@@ -84,12 +84,17 @@ jobs:
8484 - name : Require repository publication credential
8585 env :
8686 CLI_RELEASE_DEPLOY_KEY : ${{ secrets.CLI_RELEASE_DEPLOY_KEY }}
87+ GH_TOKEN : ${{ github.token }}
8788 run : |
8889 set -euo pipefail
8990 if [ -z "$CLI_RELEASE_DEPLOY_KEY" ]; then
9091 printf 'The release-plan-publication environment has no repository write deploy key.\n' >&2
9192 exit 1
9293 fi
94+ if [ -z "$GH_TOKEN" ]; then
95+ printf 'The protected publication job has no GitHub Actions credential.\n' >&2
96+ exit 1
97+ fi
9398
9499 - uses : actions/checkout@v6
95100 with :
@@ -120,6 +125,55 @@ jobs:
120125 RELEASE_COMMIT : ${{ needs.discover.outputs.commit }}
121126 run : scripts/ci/verify-release-tag-source.sh
122127
128+ - name : Quarantine the exact tag-triggered publication run
129+ env :
130+ GH_TOKEN : ${{ github.token }}
131+ RELEASE_TAG : ${{ needs.discover.outputs.version }}
132+ RELEASE_COMMIT : ${{ needs.discover.outputs.commit }}
133+ run : |
134+ set -euo pipefail
135+ cancel_requested=false
136+ quarantined=false
137+ retained_run_id=
138+ for attempt in {1..12}; do
139+ gh run list --workflow release.yml --event push --branch "$RELEASE_TAG" --limit 100 \
140+ --json databaseId,event,displayTitle,headBranch,headSha,status,conclusion,url,workflowName \
141+ > tag-push-runs.json
142+ decision="$(python scripts/ci/component-release-recovery.py select-tag-push-run \
143+ --release-tag "$RELEASE_TAG" --release-commit "$RELEASE_COMMIT" \
144+ --runs tag-push-runs.json)"
145+ IFS=$'\t' read -r quarantine_action run_id status conclusion <<< "$decision"
146+ if [ -n "$run_id" ]; then
147+ if [ -n "$retained_run_id" ] && [ "$retained_run_id" != "$run_id" ]; then
148+ printf 'Tag-push quarantine changed from run %s to run %s.\n' \
149+ "$retained_run_id" "$run_id" >&2
150+ exit 1
151+ fi
152+ retained_run_id="$run_id"
153+ fi
154+ if [ "$quarantine_action" = cancel ] && [ "$cancel_requested" != true ]; then
155+ gh run cancel "$run_id"
156+ cancel_requested=true
157+ elif [ "$quarantine_action" = complete ]; then
158+ quarantined=true
159+ break
160+ fi
161+ [ "$attempt" -eq 12 ] || sleep 5
162+ done
163+ if [ "$quarantined" != true ] || [ -z "$retained_run_id" ]; then
164+ printf 'The exact tag-push publication run was not observed and cancelled.\n' >&2
165+ exit 1
166+ fi
167+ gh run view "$retained_run_id" \
168+ --json databaseId,event,displayTitle,headBranch,headSha,status,conclusion,url,workflowName \
169+ > tag-push-run.json
170+ python scripts/ci/component-release-recovery.py retain-tag-push-run \
171+ --repository "$GITHUB_REPOSITORY" \
172+ --release-tag "$RELEASE_TAG" --release-commit "$RELEASE_COMMIT" \
173+ --run-id "$retained_run_id" --run tag-push-run.json \
174+ --evidence release-tag-push-quarantine-evidence.json
175+
176+
123177 - name : Start or resume the exact repository-owned publication run
124178 env :
125179 GH_TOKEN : ${{ github.token }}
@@ -218,25 +272,55 @@ jobs:
218272 fi
219273 retain_publication_run success
220274
221- - name : Verify installable public CLI artifacts
222- env :
223- GITHUB_TOKEN : ${{ github.token }}
224- run : |
225- python scripts/ci/component-release-recovery.py verify \
226- --component cli --plan recovery-input/release-plan.json \
227- --attempts 6 --sleep 10 --evidence release-completion-evidence.json
228-
229275 - name : Retain publication evidence
230276 if : always()
231277 uses : actions/upload-artifact@v7
232278 with :
233279 name : cli-release-publication-${{ needs.discover.outputs.plan }}
234280 path : |
235281 release-tag-publication-evidence.json
282+ tag-push-runs.json
283+ tag-push-run.json
284+ release-tag-push-quarantine-evidence.json
236285 publication-runs.json
237286 publication-dispatch-request.json
238287 publication-dispatch.json
239288 publication-run.json
240289 release-publication-run-evidence.json
241- release-completion-evidence.json
290+ if-no-files-found : warn
291+
292+ verify-publication :
293+ name : Verify installable public CLI artifacts
294+ needs : [discover, publish]
295+ if : needs.discover.outputs.action == 'publish' && needs.publish.result == 'success'
296+ runs-on : ubuntu-latest
297+ timeout-minutes : 10
298+ permissions :
299+ attestations : read
300+ contents : read
301+ steps :
302+ - uses : actions/checkout@v6
303+ with :
304+ persist-credentials : false
305+
306+ - name : Restore the immutable release plan
307+ uses : actions/download-artifact@v8
308+ with :
309+ name : cli-release-recovery-${{ needs.discover.outputs.plan }}
310+ path : recovery-input
311+
312+ - name : Verify installable public CLI artifacts
313+ env :
314+ GITHUB_TOKEN : ${{ github.token }}
315+ run : |
316+ python scripts/ci/component-release-recovery.py verify \
317+ --component cli --plan recovery-input/release-plan.json \
318+ --attempts 6 --sleep 10 --evidence release-completion-evidence.json
319+
320+ - name : Retain installability evidence
321+ if : always()
322+ uses : actions/upload-artifact@v7
323+ with :
324+ name : cli-release-verification-${{ needs.discover.outputs.plan }}
325+ path : release-completion-evidence.json
242326 if-no-files-found : warn
0 commit comments