From 8b0fa9943bc4caceb40eea427aa69562aa66463b Mon Sep 17 00:00:00 2001 From: Gale W Date: Sun, 7 Jun 2026 17:12:21 -0400 Subject: [PATCH] docs: sync guidance and maintainer toolkit --- .codex/environments/swift-package.toml | 21 +++++++++ .../workflows/validate-repo-maintenance.yml | 1 + scripts/repo-maintenance/lib/common.sh | 44 +++++++++++++++++++ scripts/repo-maintenance/release.sh | 21 +++++---- .../release/40-github-release.sh | 9 +++- 5 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 .codex/environments/swift-package.toml diff --git a/.codex/environments/swift-package.toml b/.codex/environments/swift-package.toml new file mode 100644 index 0000000..0d26432 --- /dev/null +++ b/.codex/environments/swift-package.toml @@ -0,0 +1,21 @@ +# Copy to .codex/environments/swift-package.toml and adjust action names if needed. +version = 1 +name = "swift-package" + +[setup] +script = "swift package resolve" + +[[actions]] +name = "resolve" +icon = "tool" +command = "swift package resolve" + +[[actions]] +name = "build" +icon = "tool" +command = "swift build" + +[[actions]] +name = "test" +icon = "tool" +command = "swift test" diff --git a/.github/workflows/validate-repo-maintenance.yml b/.github/workflows/validate-repo-maintenance.yml index afd62d7..5e05590 100644 --- a/.github/workflows/validate-repo-maintenance.yml +++ b/.github/workflows/validate-repo-maintenance.yml @@ -14,6 +14,7 @@ jobs: name: validate runs-on: macos-26 steps: + # This is a validated floor, not a ceiling; update to newer stable official versions when validated. - uses: actions/checkout@v6.0.2 - name: Report selected Xcode run: xcode-select --print-path diff --git a/scripts/repo-maintenance/lib/common.sh b/scripts/repo-maintenance/lib/common.sh index b456429..b0afa95 100755 --- a/scripts/repo-maintenance/lib/common.sh +++ b/scripts/repo-maintenance/lib/common.sh @@ -55,6 +55,50 @@ positive_integer_or_default() { esac } +is_semver_prerelease_tag() { + tag_name="$1" + case "$tag_name" in + v[0-9]*.[0-9]*.[0-9]*-*) + return 0 + ;; + *) + return 1 + ;; + esac +} + +expected_github_prerelease_value() { + tag_name="$1" + if is_semver_prerelease_tag "$tag_name"; then + printf '%s\n' "true" + else + printf '%s\n' "false" + fi +} + +github_release_create_prerelease_flag() { + tag_name="$1" + if is_semver_prerelease_tag "$tag_name"; then + printf '%s\n' "--prerelease" + fi +} + +verify_github_release_prerelease_metadata() { + tag_name="$1" + expected_value="$(expected_github_prerelease_value "$tag_name")" + + actual_value="$(gh release view "$tag_name" --json isPrerelease --jq .isPrerelease 2>/dev/null || true)" + case "$actual_value" in + true|false) + ;; + *) + die "GitHub release $tag_name exists, but its prerelease metadata was not readable. Confirm gh can read release JSON metadata before rerunning release.sh." + ;; + esac + + [ "$actual_value" = "$expected_value" ] || die "GitHub release $tag_name prerelease metadata mismatch: tag implies isPrerelease=$expected_value but GitHub reports isPrerelease=$actual_value. Update the release metadata or delete and recreate the release before rerunning release.sh." +} + github_wait_timeout() { value="$1" default_timeout="$(positive_integer_or_default "${REPO_MAINTENANCE_GH_WAIT_TIMEOUT_SECONDS:-120}" 120)" diff --git a/scripts/repo-maintenance/release.sh b/scripts/repo-maintenance/release.sh index 9449e99..7151e19 100755 --- a/scripts/repo-maintenance/release.sh +++ b/scripts/repo-maintenance/release.sh @@ -126,15 +126,15 @@ ensure_branch_release_context() { run_version_bump() { release_version="${RELEASE_TAG#v}" version_bump_script="$SELF_DIR/version-bump.sh" - version_bump_subject="release: bump versions for $RELEASE_TAG" + head_subject="$(git -C "$REPO_ROOT" log -1 --format=%s 2>/dev/null || true)" if [ "$skip_version_bump" = "true" ]; then log "Skipping repo version bump because --skip-version-bump was requested." return 0 fi - if git -C "$REPO_ROOT" log --format=%s "$base_branch"..HEAD | grep -Fxq "$version_bump_subject"; then - log "Version bump commit for $RELEASE_TAG is already on this branch; continuing the release resume path." + if [ "$head_subject" = "release: bump versions for $RELEASE_TAG" ]; then + log "Version bump commit for $RELEASE_TAG is already at HEAD; continuing the release resume path." return 0 fi @@ -287,8 +287,8 @@ wait_for_initial_pr_checks() { log "Waiting up to ${timeout_seconds}s for GitHub to report initial checks on PR #$pr_number." while :; do - last_state="$(gh pr view "$pr_number" --json statusCheckRollup --jq '[.statusCheckRollup[]? | .name + ":" + ((.status // .state // .conclusion // "") | ascii_downcase)] | join(", ")' 2>/dev/null || printf 'no checks reported')" - check_count="$(gh pr view "$pr_number" --json statusCheckRollup --jq '(.statusCheckRollup // []) | length' 2>/dev/null || printf '0')" + last_state="$(gh pr checks "$pr_number" --json name,state,workflow --jq 'map(.name + ":" + .state) | join(", ")' 2>/dev/null || printf 'no checks reported')" + check_count="$(gh pr checks "$pr_number" --json name,state,workflow --jq 'length' 2>/dev/null || printf '0')" case "$check_count" in ''|*[!0-9]*) check_count="0" @@ -349,7 +349,7 @@ check_pr_comments() { wait_for_pr_review_state "$pr_number" review_decision="$(gh pr view "$pr_number" --json reviewDecision --jq '.reviewDecision // ""')" - comment_count="$(gh pr view "$pr_number" --json comments,reviews --jq '([.comments[]?, (.reviews[]? | select(.state == "COMMENTED" or ((.body // "") | length > 0)))] | length)')" + comment_count="$(gh pr view "$pr_number" --json comments,reviews --jq '([.comments[]?, (.reviews[]? | select(.state == "COMMENTED"))] | length)')" if [ "$review_decision" = "CHANGES_REQUESTED" ]; then gh pr view "$pr_number" --comments @@ -398,18 +398,23 @@ create_github_release() { fi if [ "$REPO_MAINTENANCE_DRY_RUN" = "true" ]; then - log "Would create a GitHub release for $RELEASE_TAG with gh release create --verify-tag." + prerelease_flag="$(github_release_create_prerelease_flag "$RELEASE_TAG")" + log "Would create a GitHub release for $RELEASE_TAG with gh release create --verify-tag${prerelease_flag:+ $prerelease_flag}." return 0 fi if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then + verify_github_release_prerelease_metadata "$RELEASE_TAG" log "GitHub release $RELEASE_TAG already exists." return 0 fi - gh release create "$RELEASE_TAG" --verify-tag --generate-notes + prerelease_flag="$(github_release_create_prerelease_flag "$RELEASE_TAG")" + # shellcheck disable=SC2086 + gh release create "$RELEASE_TAG" --verify-tag --generate-notes $prerelease_flag log "Created GitHub release $RELEASE_TAG." wait_for_github_release "$RELEASE_TAG" + verify_github_release_prerelease_metadata "$RELEASE_TAG" } cleanup_merged_branches() { diff --git a/scripts/repo-maintenance/release/40-github-release.sh b/scripts/repo-maintenance/release/40-github-release.sh index 0be674d..2220e09 100755 --- a/scripts/repo-maintenance/release/40-github-release.sh +++ b/scripts/repo-maintenance/release/40-github-release.sh @@ -16,15 +16,20 @@ if ! command -v gh >/dev/null 2>&1; then fi if [ "${REPO_MAINTENANCE_DRY_RUN:-false}" = "true" ]; then - log "Would create a GitHub release for $RELEASE_TAG with gh release create --verify-tag." + prerelease_flag="$(github_release_create_prerelease_flag "$RELEASE_TAG")" + log "Would create a GitHub release for $RELEASE_TAG with gh release create --verify-tag${prerelease_flag:+ $prerelease_flag}." exit 0 fi if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then + verify_github_release_prerelease_metadata "$RELEASE_TAG" log "GitHub release $RELEASE_TAG already exists." exit 0 fi -gh release create "$RELEASE_TAG" --verify-tag --generate-notes +prerelease_flag="$(github_release_create_prerelease_flag "$RELEASE_TAG")" +# shellcheck disable=SC2086 +gh release create "$RELEASE_TAG" --verify-tag --generate-notes $prerelease_flag log "Created GitHub release $RELEASE_TAG." wait_for_github_release "$RELEASE_TAG" +verify_github_release_prerelease_metadata "$RELEASE_TAG"