Repository navigation
ci: release preflight, separate release outcomes, duration-balanced shards, narrow scope checks #5653
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ci: release preflight, separate release outcomes, duration-balanced shards, narrow scope checks #5653
Changes from all commits
e8db154
3fd90dd
73e21ff
256b93b
6458c16
ea8665e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -74,8 +74,52 @@ jobs: | |
| process.exit(1); | ||
| } | ||
| NODE | ||
| package-standalone: | ||
|
|
||
| # Every publication precondition the dispatch can already decide, checked before any runner | ||
| # starts packaging: channel and dist-tag, every version source, the tag, the GitHub release, | ||
| # npm, the global tag ordering and the dev pre-move (scripts/ci/release-preflight.sh). | ||
| # | ||
| # Run 35783865160 packaged 2.62.0 for nineteen minutes and then failed the ordering gate in | ||
| # `publish` on v2.63.0-preview.20260923. That tag already existed when the run's first job | ||
| # started: the workflow-level `release` concurrency group above is one constant slot for every | ||
| # ref, so the stable run had waited for the preview run to finish. The runs were serialised; | ||
| # the check was in the wrong place. Because of that shared slot, this job sees whatever the | ||
| # previous release run published. | ||
| # | ||
| # It is an early answer, not the final one. Tags, releases and registry state can still move | ||
| # while a run packages (a hand-pushed tag, a first local publish), so `publish` repeats every | ||
| # one of these checks immediately before `npm publish`. | ||
| preflight: | ||
| name: release preflight | ||
| needs: validate-dispatch | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 5 | ||
| permissions: | ||
| contents: read | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 | ||
| with: | ||
| persist-credentials: false | ||
| fetch-tags: true | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
On a release from Useful? React with 👍 / 👎. |
||
|
|
||
| - name: Setup project Bun | ||
| uses: ./.github/actions/setup-project-bun | ||
|
|
||
| - name: Fetch the dev line | ||
| run: git fetch --no-tags --depth=1 origin +refs/heads/dev:refs/remotes/origin/dev | ||
|
|
||
| - name: Refuse a release that cannot publish | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| RELEASE_VERSION: ${{ inputs.version }} | ||
| NPM_DIST_TAG: ${{ inputs.tag }} | ||
| DRY_RUN: ${{ inputs.dry-run }} | ||
| RESUME: ${{ inputs.resume-after-npm-publish }} | ||
| run: bash scripts/ci/release-preflight.sh | ||
|
|
||
| package-standalone: | ||
| needs: [validate-dispatch, preflight] | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
|
|
@@ -181,7 +225,7 @@ jobs: | |
| retention-days: 7 | ||
|
|
||
| package-desktop: | ||
| needs: validate-dispatch | ||
| needs: [validate-dispatch, preflight] | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
|
|
@@ -604,10 +648,47 @@ jobs: | |
| gh release edit "$release_tag" --draft=false | ||
| fi | ||
|
|
||
| # One row per fact a release run can establish: the public GitHub release, the npm version read | ||
| # back from the registry, and the npm dist-tag. A green run used to read the same whichever of | ||
| # them were true, because the registry smoke continues to the GitHub release when its reads stay | ||
| # pending, which is the intended publishing behaviour. This job only reports; it never changes the | ||
| # run's result. | ||
| # | ||
| # A job of its own, not a step in attach-release: a failed publish skips attach-release entirely, | ||
| # and that is when the rows matter most. It reads with the job token at contents: read, so a draft | ||
| # release is invisible to it and reads as not public, which is the question the row answers. | ||
| release-outcomes: | ||
| name: release outcomes | ||
| needs: [publish, attach-release] | ||
| if: ${{ always() && inputs.dry-run != true }} | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 5 | ||
| permissions: | ||
| contents: read | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Report release outcomes | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| RELEASE_VERSION: ${{ inputs.version }} | ||
| NPM_DIST_TAG: ${{ inputs.tag }} | ||
| NPM_VERSION_STATE: ${{ needs.publish.outputs.npm_version }} | ||
| NPM_DIST_TAG_STATE: ${{ needs.publish.outputs.npm_dist_tag }} | ||
| PUBLISH_RESULT: ${{ needs.publish.result }} | ||
| ATTACH_RESULT: ${{ needs.attach-release.result }} | ||
| run: bash scripts/ci/release-outcome-report.sh | ||
|
|
||
| publish: | ||
| needs: [validate-dispatch, verify-release] | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 15 | ||
| outputs: | ||
| npm_version: ${{ steps.registry-smoke.outputs.npm_version }} | ||
| npm_dist_tag: ${{ steps.registry-smoke.outputs.npm_dist_tag }} | ||
| permissions: | ||
| contents: write | ||
| actions: read | ||
|
|
@@ -933,6 +1014,7 @@ jobs: | |
| if: ${{ inputs.dry-run != true && steps.publication.outputs.published == 'true' }} | ||
| env: | ||
| RELEASE_VERSION: ${{ inputs.version }} | ||
| NPM_DIST_TAG: ${{ inputs.tag }} | ||
| PUBLISHED: ${{ steps.publication.outputs.published }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
@@ -949,14 +1031,35 @@ jobs: | |
| fi | ||
| echo "registry version=$VERSION" | ||
| echo "verification=verified" >> "$GITHUB_OUTPUT" | ||
| echo "npm_version=confirmed" >> "$GITHUB_OUTPUT" | ||
| echo "Registry verified ${pkg_name}@${RELEASE_VERSION}." >> "$GITHUB_STEP_SUMMARY" | ||
| timeout --kill-after=2s 10s npm dist-tag ls "$pkg_name" --fetch-retries=0 --fetch-timeout=8000 || echo "::warning::Could not read npm dist-tags; exact version was verified" | ||
| # The dist-tag is its own outcome: a version can be on the registry while the tag | ||
| # still names the previous release. | ||
| dist_tag_state="unconfirmed" | ||
| if dist_tags="$(timeout --kill-after=2s 10s npm dist-tag ls "$pkg_name" --fetch-retries=0 --fetch-timeout=8000)"; then | ||
| printf '%s\n' "$dist_tags" | ||
| tagged="$(printf '%s\n' "$dist_tags" | awk -F': ' -v tag="$NPM_DIST_TAG" '$1 == tag { print $2; exit }')" | ||
| if [ "$tagged" = "$RELEASE_VERSION" ]; then | ||
| dist_tag_state="confirmed" | ||
| elif [ -n "$tagged" ]; then | ||
| dist_tag_state="mismatch" | ||
| echo "::warning::npm dist-tag ${NPM_DIST_TAG} points at ${tagged}, not ${RELEASE_VERSION}" | ||
| else | ||
| echo "::warning::npm dist-tag ${NPM_DIST_TAG} is not listed for ${pkg_name}" | ||
| fi | ||
| else | ||
| echo "::warning::Could not read npm dist-tags; exact version was verified" | ||
| fi | ||
| echo "npm_dist_tag=${dist_tag_state}" >> "$GITHUB_OUTPUT" | ||
| echo "npm dist-tag ${NPM_DIST_TAG}: ${dist_tag_state}." >> "$GITHUB_STEP_SUMMARY" | ||
| exit 0 | ||
| fi | ||
| echo "::notice::Registry lookup not confirmed (attempt $attempt/6)" | ||
| if [ "$attempt" -lt 6 ]; then sleep 5; fi | ||
| done | ||
| echo "verification=pending" >> "$GITHUB_OUTPUT" | ||
| echo "npm_version=unconfirmed" >> "$GITHUB_OUTPUT" | ||
| echo "npm_dist_tag=unconfirmed" >> "$GITHUB_OUTPUT" | ||
| echo "::warning::npm publish succeeded, but registry verification remains pending; continuing GitHub release creation without republishing" | ||
| echo "Publication acknowledged for ${pkg_name}@${RELEASE_VERSION}; registry verification pending after bounded reads. Inspect the registry before announcing availability. Do not republish this version." >> "$GITHUB_STEP_SUMMARY" | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: lidge-jun/opencodex
Length of output: 202
🏁 Script executed:
Repository: lidge-jun/opencodex
Length of output: 12991
Restrict
setup_actiontosetup-project-bun.The filter at
.github/workflows/ci.yml:312-314matches every path under.github/actions/**, but the job at lines 1098-1116 executes only.github/actions/setup-project-bun. If a pull request adds or changes another composite action, the CI gate can pass without executing that action.Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents
Source: Path instructions