Skip to content

Commit 239ecf4

Browse files
CLI release-plan recovery cannot create a previously absent exact tag with its GitHub Actions token.
1 parent 4718569 commit 239ecf4

2 files changed

Lines changed: 94 additions & 10 deletions

File tree

scripts/ci/cli-release-plan-recovery.fixture.yml

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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

scripts/ci/component-release-recovery.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
# SHA-256 of durable-workflow/cli's protected release recovery workflow.
6060
# Exact source identity is required because source-pattern matching cannot
6161
# prove that tag creation remains inside the protected repository authority.
62-
CLI_RELEASE_RECOVERY_SHA256 = "b4636ab25c27c07a3f4c1b95313abf3377447e82a9dc7632c1aa48abc3d5a97c"
62+
CLI_RELEASE_RECOVERY_SHA256 = "7ae7a13a8a292569d88ff33d79abdc70bccf666ff359d8da8cb1b62585efacfc"
6363

6464
# SHA-256 of durable-workflow/sdk-rust's prepared-plan recovery workflow. The
6565
# verifier normalizes only

0 commit comments

Comments
 (0)