Skip to content

Commit 5f49304

Browse files
authored
Merge pull request #39 from cometapi-dev/agent/recover-v011-workflow-dispatch
fix: dispatch recovery from immutable tag
2 parents 8a80d82 + c506ce2 commit 5f49304

5 files changed

Lines changed: 174 additions & 163 deletions

File tree

.github/workflows/publish.yml

Lines changed: 72 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,41 @@
11
name: Publish
22

33
on:
4-
deployment:
54
workflow_run:
65
workflows:
76
- Release Please
87
types:
98
- completed
9+
workflow_dispatch:
10+
inputs:
11+
recovery_task:
12+
description: Exact one-cycle recovery task.
13+
required: true
14+
type: string
15+
release_commit:
16+
description: Immutable v0.1.1 release commit.
17+
required: true
18+
type: string
19+
release_tag:
20+
description: Immutable release tag.
21+
required: true
22+
type: string
23+
release_run_id:
24+
description: Successful Release Please run ID.
25+
required: true
26+
type: string
27+
release_run_attempt:
28+
description: Successful Release Please run attempt.
29+
required: true
30+
type: string
31+
source_publish_run_id:
32+
description: Failed Publish run whose evidence is being recovered.
33+
required: true
34+
type: string
35+
source_publish_run_attempt:
36+
description: Failed Publish run attempt.
37+
required: true
38+
type: string
1039

1140
permissions:
1241
actions: read
@@ -26,17 +55,21 @@ jobs:
2655
github.event.workflow_run.conclusion == 'success' &&
2756
github.event.workflow_run.event == 'push' &&
2857
github.event.workflow_run.head_branch == 'main') ||
29-
(github.event_name == 'deployment' &&
30-
github.event.deployment.task == 'npm-publish-recovery' &&
31-
github.event.deployment.environment == 'npm' &&
32-
github.event.deployment.ref == 'v0.1.1' &&
33-
github.event.deployment.sha == 'c98b514227858cd183c781270a7f78f65b577e82'))
58+
(github.event_name == 'workflow_dispatch' &&
59+
github.ref == 'refs/tags/v0.1.1' &&
60+
inputs.recovery_task == 'npm-publish-recovery' &&
61+
inputs.release_commit == 'c98b514227858cd183c781270a7f78f65b577e82' &&
62+
inputs.release_tag == 'v0.1.1' &&
63+
inputs.release_run_id == '30469181724' &&
64+
inputs.release_run_attempt == '1' &&
65+
inputs.source_publish_run_id == '30471665743' &&
66+
inputs.source_publish_run_attempt == '1'))
3467
runs-on: ubuntu-latest
3568
timeout-minutes: 30
3669
env:
37-
SOURCE_RELEASE_COMMIT: ${{ github.event_name == 'deployment' && 'c98b514227858cd183c781270a7f78f65b577e82' || github.event.workflow_run.head_sha }}
38-
SOURCE_RELEASE_RUN_ATTEMPT: ${{ github.event_name == 'deployment' && '1' || github.event.workflow_run.run_attempt }}
39-
SOURCE_RELEASE_RUN_ID: ${{ github.event_name == 'deployment' && '30469181724' || github.event.workflow_run.id }}
70+
SOURCE_RELEASE_COMMIT: ${{ github.event_name == 'workflow_dispatch' && inputs.release_commit || github.event.workflow_run.head_sha }}
71+
SOURCE_RELEASE_RUN_ATTEMPT: ${{ github.event_name == 'workflow_dispatch' && inputs.release_run_attempt || github.event.workflow_run.run_attempt }}
72+
SOURCE_RELEASE_RUN_ID: ${{ github.event_name == 'workflow_dispatch' && inputs.release_run_id || github.event.workflow_run.id }}
4073
outputs:
4174
artifact-name: ${{ steps.artifact-name.outputs.name }}
4275
dist-tag: ${{ steps.version.outputs.dist-tag }}
@@ -50,27 +83,24 @@ jobs:
5083
with:
5184
fetch-depth: 0
5285
persist-credentials: false
53-
ref: ${{ github.event_name == 'deployment' && github.workflow_sha || github.sha }}
54-
- name: Validate the exact tag deployment recovery
55-
if: github.event_name == 'deployment'
86+
ref: ${{ github.event_name == 'workflow_dispatch' && github.workflow_sha || github.sha }}
87+
- name: Validate the exact tag workflow dispatch recovery
88+
if: github.event_name == 'workflow_dispatch'
5689
env:
5790
ACTOR: ${{ github.actor }}
5891
CHANGED_FILES: ${{ runner.temp }}/publish-recovery-files
5992
CONTROL_COMMIT: ${{ github.workflow_sha }}
60-
DEPLOYMENT_CREATOR: ${{ github.event.deployment.creator.login }}
61-
DEPLOYMENT_ENVIRONMENT: ${{ github.event.deployment.environment }}
62-
DEPLOYMENT_ID: ${{ github.event.deployment.id }}
63-
DEPLOYMENT_REF: ${{ github.event.deployment.ref }}
64-
DEPLOYMENT_RELEASE_COMMIT: ${{ github.event.deployment.payload.release_commit }}
65-
DEPLOYMENT_RELEASE_TAG: ${{ github.event.deployment.payload.release_tag }}
66-
DEPLOYMENT_SHA: ${{ github.event.deployment.sha }}
67-
DEPLOYMENT_SOURCE_RUN_ATTEMPT: ${{ github.event.deployment.payload.source_run_attempt }}
68-
DEPLOYMENT_SOURCE_RUN_ID: ${{ github.event.deployment.payload.source_run_id }}
69-
DEPLOYMENT_TASK: ${{ github.event.deployment.task }}
7093
EVENT_NAME: ${{ github.event_name }}
7194
EVENT_REF: ${{ github.ref }}
7295
EVENT_SHA: ${{ github.sha }}
7396
MAIN_COMMIT: ${{ github.workflow_sha }}
97+
RELEASE_COMMIT: ${{ inputs.release_commit }}
98+
RELEASE_TAG: ${{ inputs.release_tag }}
99+
RELEASE_RUN_ATTEMPT: ${{ inputs.release_run_attempt }}
100+
RELEASE_RUN_ID: ${{ inputs.release_run_id }}
101+
SOURCE_PUBLISH_RUN_ATTEMPT: ${{ inputs.source_publish_run_attempt }}
102+
SOURCE_PUBLISH_RUN_ID: ${{ inputs.source_publish_run_id }}
103+
TASK: ${{ inputs.recovery_task }}
74104
TRIGGERING_ACTOR: ${{ github.triggering_actor }}
75105
WORKFLOW_RUN_ATTEMPT: ${{ github.run_attempt }}
76106
shell: bash
@@ -91,50 +121,45 @@ jobs:
91121
MAIN_COMMIT="$MAIN_COMMIT" CONTROL_FIRST_PARENT="$CONTROL_FIRST_PARENT" \
92122
node --input-type=module <<'EOF'
93123
import { readFileSync } from "node:fs";
94-
import { validatePublishDeploymentRecoveryTrigger } from "./scripts/release-workflow-validation.mjs";
124+
import { validatePublishWorkflowDispatchRecoveryTrigger } from "./scripts/release-workflow-validation.mjs";
95125
96-
validatePublishDeploymentRecoveryTrigger({
126+
validatePublishWorkflowDispatchRecoveryTrigger({
97127
actor: process.env.ACTOR,
98128
changedFiles: readFileSync(process.env.CHANGED_FILES, "utf8")
99129
.split("\n")
100130
.filter((file) => file !== ""),
101131
controlCommit: process.env.CONTROL_COMMIT,
102132
controlFirstParent: process.env.CONTROL_FIRST_PARENT,
103-
deploymentCreator: process.env.DEPLOYMENT_CREATOR,
104-
deploymentEnvironment: process.env.DEPLOYMENT_ENVIRONMENT,
105-
deploymentId: Number(process.env.DEPLOYMENT_ID),
106-
deploymentRef: process.env.DEPLOYMENT_REF,
107-
deploymentReleaseCommit: process.env.DEPLOYMENT_RELEASE_COMMIT,
108-
deploymentReleaseTag: process.env.DEPLOYMENT_RELEASE_TAG,
109-
deploymentSha: process.env.DEPLOYMENT_SHA,
110-
deploymentSourceRunAttempt: Number(
111-
process.env.DEPLOYMENT_SOURCE_RUN_ATTEMPT,
112-
),
113-
deploymentSourceRunId: Number(process.env.DEPLOYMENT_SOURCE_RUN_ID),
114-
deploymentTask: process.env.DEPLOYMENT_TASK,
115133
eventName: process.env.EVENT_NAME,
116134
eventRef: process.env.EVENT_REF,
117135
eventSha: process.env.EVENT_SHA,
118136
mainCommit: process.env.MAIN_COMMIT,
137+
releaseCommit: process.env.RELEASE_COMMIT,
138+
releaseTag: process.env.RELEASE_TAG,
139+
sourcePublishRunAttempt: Number(
140+
process.env.SOURCE_PUBLISH_RUN_ATTEMPT,
141+
),
142+
sourcePublishRunId: Number(process.env.SOURCE_PUBLISH_RUN_ID),
119143
sourceReleaseCommit: process.env.SOURCE_RELEASE_COMMIT,
120144
sourceRunAttempt: Number(process.env.SOURCE_RELEASE_RUN_ATTEMPT),
121145
sourceRunId: Number(process.env.SOURCE_RELEASE_RUN_ID),
146+
task: process.env.TASK,
122147
triggeringActor: process.env.TRIGGERING_ACTOR,
123148
workflowRunAttempt: Number(process.env.WORKFLOW_RUN_ATTEMPT),
124149
});
125150
EOF
126151
- name: Validate the prior artifact and bounded live evidence
127152
id: recovery-evidence
128-
if: github.event_name == 'deployment'
153+
if: github.event_name == 'workflow_dispatch'
129154
env:
130155
GH_TOKEN: ${{ github.token }}
131156
RECOVERY_ANNOTATIONS: ${{ runner.temp }}/publish-recovery-annotations.json
132157
RECOVERY_ARTIFACTS: ${{ runner.temp }}/publish-recovery-artifacts.json
133158
RECOVERY_JOBS: ${{ runner.temp }}/publish-recovery-jobs.json
134159
RECOVERY_LIVE_LOG: ${{ runner.temp }}/publish-recovery-live.log
135160
RECOVERY_RUN: ${{ runner.temp }}/publish-recovery-run.json
136-
SOURCE_PUBLISH_ATTEMPT: ${{ github.event.deployment.payload.source_run_attempt }}
137-
SOURCE_PUBLISH_RUN_ID: ${{ github.event.deployment.payload.source_run_id }}
161+
SOURCE_PUBLISH_ATTEMPT: ${{ inputs.source_publish_run_attempt }}
162+
SOURCE_PUBLISH_RUN_ID: ${{ inputs.source_publish_run_id }}
138163
shell: bash
139164
run: |
140165
set -euo pipefail
@@ -211,7 +236,7 @@ jobs:
211236
EXPECTED_REPOSITORY_URL: git+https://github.com/cometapi-dev/cometapi-node.git
212237
EXPECTED_WORKFLOW: Release Please
213238
EXPECTED_WORKFLOW_PATH: .github/workflows/release-please.yml
214-
CONTROL_SHA: ${{ github.event_name == 'deployment' && github.workflow_sha || github.sha }}
239+
CONTROL_SHA: ${{ github.event_name == 'workflow_dispatch' && github.workflow_sha || github.sha }}
215240
EVENT_NAME: ${{ github.event_name }}
216241
RELEASE_RESULT: ${{ runner.temp }}/release-please-result/result.json
217242
SOURCE_RUN_FILE: ${{ runner.temp }}/release-please-source-run.json
@@ -230,7 +255,7 @@ jobs:
230255
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
231256
case "$EVENT_NAME" in
232257
workflow_run) expected_main="$WORKFLOW_SHA" ;;
233-
deployment) expected_main="$CONTROL_SHA" ;;
258+
workflow_dispatch) expected_main="$CONTROL_SHA" ;;
234259
*) echo "Publish received an unsupported event." >&2; exit 1 ;;
235260
esac
236261
if [[ "$(git rev-parse refs/remotes/origin/main)" != "$expected_main" ]]; then
@@ -447,7 +472,7 @@ jobs:
447472
environment: live-smoke
448473
steps:
449474
- name: Reuse the successful bounded live smoke
450-
if: github.event_name == 'deployment'
475+
if: github.event_name == 'workflow_dispatch'
451476
env:
452477
REUSE_LIVE_SMOKE: ${{ needs.verify.outputs.reuse-live-smoke }}
453478
shell: bash
@@ -458,25 +483,25 @@ jobs:
458483
exit 1
459484
fi
460485
- name: Check out the verified release tag
461-
if: github.event_name != 'deployment'
486+
if: github.event_name != 'workflow_dispatch'
462487
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
463488
with:
464489
persist-credentials: false
465490
ref: ${{ needs.verify.outputs.release-commit }}
466491
- name: Set up Node.js 24
467-
if: github.event_name != 'deployment'
492+
if: github.event_name != 'workflow_dispatch'
468493
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
469494
with:
470495
node-version: 24.x
471496
cache: npm
472497
- name: Install locked dependencies
473-
if: github.event_name != 'deployment'
498+
if: github.event_name != 'workflow_dispatch'
474499
run: npm ci
475500
- name: Build the release tag
476-
if: github.event_name != 'deployment'
501+
if: github.event_name != 'workflow_dispatch'
477502
run: npm run build
478503
- name: Run the bounded live smoke
479-
if: github.event_name != 'deployment'
504+
if: github.event_name != 'workflow_dispatch'
480505
env:
481506
COMETAPI_KEY: ${{ secrets.COMETAPI_KEY }}
482507
COMETAPI_LIVE_SMOKE: "1"

RELEASING.md

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -303,17 +303,42 @@ The repository maintains four independently auditable workflows:
303303
`main` deployment while the protected npm environment accepts only `v*` tags.
304304
No OIDC token or npm mutation occurred in either failed run.
305305

306-
The replacement one-cycle recovery uses a human-created GitHub deployment for
307-
the existing immutable `v0.1.1` tag. It accepts only actor and triggering actor
308-
`tensornull`, exact release commit
309-
`c98b514227858cd183c781270a7f78f65b577e82`, Release Please run
310-
`30469181724` attempt 1, failed Publish run `30471665743` attempt 1, the exact
306+
A first attempted recovery deployment [5667717157](https://github.com/cometapi-dev/cometapi-node/deployments/5667717157)
307+
was intentionally marked failed: the immutable `v0.1.1` tag predates the
308+
temporary deployment trigger, so GitHub found no workflow at that tag and no
309+
runner or npm mutation occurred. The replacement one-cycle recovery uses the
310+
documented `workflow_dispatch` API with `ref=v0.1.1`; GitHub dispatches the
311+
workflow from the reviewed default-branch definition while setting
312+
`GITHUB_REF=refs/tags/v0.1.1`, which satisfies the existing npm tag policy.
313+
The exact request is:
314+
315+
```bash
316+
gh api --method POST \
317+
repos/cometapi-dev/cometapi-node/actions/workflows/publish.yml/dispatches \
318+
--input - <<'JSON'
319+
{
320+
"ref": "v0.1.1",
321+
"inputs": {
322+
"recovery_task": "npm-publish-recovery",
323+
"release_commit": "c98b514227858cd183c781270a7f78f65b577e82",
324+
"release_tag": "v0.1.1",
325+
"release_run_id": "30469181724",
326+
"release_run_attempt": "1",
327+
"source_publish_run_id": "30471665743",
328+
"source_publish_run_attempt": "1"
329+
}
330+
}
331+
JSON
332+
```
333+
334+
The workflow accepts only actor and triggering actor `tensornull`, the exact
335+
release commit and tag, both source run IDs and attempts, the reviewed
311336
first-parent control merge, and the recorded repair files. It revalidates the
312337
successful source verify job, artifact ID and digest, branch-policy failure,
313-
and the log evidence for exactly three sequential live requests. The new tag
314-
run repeats offline package and exact-artifact gates but does not spend another
338+
and log evidence for exactly three sequential live requests. The tag run
339+
repeats offline package and exact-artifact gates but does not spend another
315340
live request budget. The protected npm environment and OIDC gate remain
316-
unchanged. The deployment trigger and exact recovery constants must be removed
341+
unchanged. The dispatch trigger and exact recovery constants must be removed
317342
in the post-release evidence PR; the `runner.temp` isolation remains permanent.
318343
319344
Third-party actions are pinned to full commit SHAs. Workflow permissions remain

0 commit comments

Comments
 (0)