diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b160fb7..5fe7c7d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,12 +1,41 @@ name: Publish on: - deployment: workflow_run: workflows: - Release Please types: - completed + workflow_dispatch: + inputs: + recovery_task: + description: Exact one-cycle recovery task. + required: true + type: string + release_commit: + description: Immutable v0.1.1 release commit. + required: true + type: string + release_tag: + description: Immutable release tag. + required: true + type: string + release_run_id: + description: Successful Release Please run ID. + required: true + type: string + release_run_attempt: + description: Successful Release Please run attempt. + required: true + type: string + source_publish_run_id: + description: Failed Publish run whose evidence is being recovered. + required: true + type: string + source_publish_run_attempt: + description: Failed Publish run attempt. + required: true + type: string permissions: actions: read @@ -26,17 +55,21 @@ jobs: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'main') || - (github.event_name == 'deployment' && - github.event.deployment.task == 'npm-publish-recovery' && - github.event.deployment.environment == 'npm' && - github.event.deployment.ref == 'v0.1.1' && - github.event.deployment.sha == 'c98b514227858cd183c781270a7f78f65b577e82')) + (github.event_name == 'workflow_dispatch' && + github.ref == 'refs/tags/v0.1.1' && + inputs.recovery_task == 'npm-publish-recovery' && + inputs.release_commit == 'c98b514227858cd183c781270a7f78f65b577e82' && + inputs.release_tag == 'v0.1.1' && + inputs.release_run_id == '30469181724' && + inputs.release_run_attempt == '1' && + inputs.source_publish_run_id == '30471665743' && + inputs.source_publish_run_attempt == '1')) runs-on: ubuntu-latest timeout-minutes: 30 env: - SOURCE_RELEASE_COMMIT: ${{ github.event_name == 'deployment' && 'c98b514227858cd183c781270a7f78f65b577e82' || github.event.workflow_run.head_sha }} - SOURCE_RELEASE_RUN_ATTEMPT: ${{ github.event_name == 'deployment' && '1' || github.event.workflow_run.run_attempt }} - SOURCE_RELEASE_RUN_ID: ${{ github.event_name == 'deployment' && '30469181724' || github.event.workflow_run.id }} + SOURCE_RELEASE_COMMIT: ${{ github.event_name == 'workflow_dispatch' && inputs.release_commit || github.event.workflow_run.head_sha }} + SOURCE_RELEASE_RUN_ATTEMPT: ${{ github.event_name == 'workflow_dispatch' && inputs.release_run_attempt || github.event.workflow_run.run_attempt }} + SOURCE_RELEASE_RUN_ID: ${{ github.event_name == 'workflow_dispatch' && inputs.release_run_id || github.event.workflow_run.id }} outputs: artifact-name: ${{ steps.artifact-name.outputs.name }} dist-tag: ${{ steps.version.outputs.dist-tag }} @@ -50,27 +83,24 @@ jobs: with: fetch-depth: 0 persist-credentials: false - ref: ${{ github.event_name == 'deployment' && github.workflow_sha || github.sha }} - - name: Validate the exact tag deployment recovery - if: github.event_name == 'deployment' + ref: ${{ github.event_name == 'workflow_dispatch' && github.workflow_sha || github.sha }} + - name: Validate the exact tag workflow dispatch recovery + if: github.event_name == 'workflow_dispatch' env: ACTOR: ${{ github.actor }} CHANGED_FILES: ${{ runner.temp }}/publish-recovery-files CONTROL_COMMIT: ${{ github.workflow_sha }} - DEPLOYMENT_CREATOR: ${{ github.event.deployment.creator.login }} - DEPLOYMENT_ENVIRONMENT: ${{ github.event.deployment.environment }} - DEPLOYMENT_ID: ${{ github.event.deployment.id }} - DEPLOYMENT_REF: ${{ github.event.deployment.ref }} - DEPLOYMENT_RELEASE_COMMIT: ${{ github.event.deployment.payload.release_commit }} - DEPLOYMENT_RELEASE_TAG: ${{ github.event.deployment.payload.release_tag }} - DEPLOYMENT_SHA: ${{ github.event.deployment.sha }} - DEPLOYMENT_SOURCE_RUN_ATTEMPT: ${{ github.event.deployment.payload.source_run_attempt }} - DEPLOYMENT_SOURCE_RUN_ID: ${{ github.event.deployment.payload.source_run_id }} - DEPLOYMENT_TASK: ${{ github.event.deployment.task }} EVENT_NAME: ${{ github.event_name }} EVENT_REF: ${{ github.ref }} EVENT_SHA: ${{ github.sha }} MAIN_COMMIT: ${{ github.workflow_sha }} + RELEASE_COMMIT: ${{ inputs.release_commit }} + RELEASE_TAG: ${{ inputs.release_tag }} + RELEASE_RUN_ATTEMPT: ${{ inputs.release_run_attempt }} + RELEASE_RUN_ID: ${{ inputs.release_run_id }} + SOURCE_PUBLISH_RUN_ATTEMPT: ${{ inputs.source_publish_run_attempt }} + SOURCE_PUBLISH_RUN_ID: ${{ inputs.source_publish_run_id }} + TASK: ${{ inputs.recovery_task }} TRIGGERING_ACTOR: ${{ github.triggering_actor }} WORKFLOW_RUN_ATTEMPT: ${{ github.run_attempt }} shell: bash @@ -91,41 +121,36 @@ jobs: MAIN_COMMIT="$MAIN_COMMIT" CONTROL_FIRST_PARENT="$CONTROL_FIRST_PARENT" \ node --input-type=module <<'EOF' import { readFileSync } from "node:fs"; - import { validatePublishDeploymentRecoveryTrigger } from "./scripts/release-workflow-validation.mjs"; + import { validatePublishWorkflowDispatchRecoveryTrigger } from "./scripts/release-workflow-validation.mjs"; - validatePublishDeploymentRecoveryTrigger({ + validatePublishWorkflowDispatchRecoveryTrigger({ actor: process.env.ACTOR, changedFiles: readFileSync(process.env.CHANGED_FILES, "utf8") .split("\n") .filter((file) => file !== ""), controlCommit: process.env.CONTROL_COMMIT, controlFirstParent: process.env.CONTROL_FIRST_PARENT, - deploymentCreator: process.env.DEPLOYMENT_CREATOR, - deploymentEnvironment: process.env.DEPLOYMENT_ENVIRONMENT, - deploymentId: Number(process.env.DEPLOYMENT_ID), - deploymentRef: process.env.DEPLOYMENT_REF, - deploymentReleaseCommit: process.env.DEPLOYMENT_RELEASE_COMMIT, - deploymentReleaseTag: process.env.DEPLOYMENT_RELEASE_TAG, - deploymentSha: process.env.DEPLOYMENT_SHA, - deploymentSourceRunAttempt: Number( - process.env.DEPLOYMENT_SOURCE_RUN_ATTEMPT, - ), - deploymentSourceRunId: Number(process.env.DEPLOYMENT_SOURCE_RUN_ID), - deploymentTask: process.env.DEPLOYMENT_TASK, eventName: process.env.EVENT_NAME, eventRef: process.env.EVENT_REF, eventSha: process.env.EVENT_SHA, mainCommit: process.env.MAIN_COMMIT, + releaseCommit: process.env.RELEASE_COMMIT, + releaseTag: process.env.RELEASE_TAG, + sourcePublishRunAttempt: Number( + process.env.SOURCE_PUBLISH_RUN_ATTEMPT, + ), + sourcePublishRunId: Number(process.env.SOURCE_PUBLISH_RUN_ID), sourceReleaseCommit: process.env.SOURCE_RELEASE_COMMIT, sourceRunAttempt: Number(process.env.SOURCE_RELEASE_RUN_ATTEMPT), sourceRunId: Number(process.env.SOURCE_RELEASE_RUN_ID), + task: process.env.TASK, triggeringActor: process.env.TRIGGERING_ACTOR, workflowRunAttempt: Number(process.env.WORKFLOW_RUN_ATTEMPT), }); EOF - name: Validate the prior artifact and bounded live evidence id: recovery-evidence - if: github.event_name == 'deployment' + if: github.event_name == 'workflow_dispatch' env: GH_TOKEN: ${{ github.token }} RECOVERY_ANNOTATIONS: ${{ runner.temp }}/publish-recovery-annotations.json @@ -133,8 +158,8 @@ jobs: RECOVERY_JOBS: ${{ runner.temp }}/publish-recovery-jobs.json RECOVERY_LIVE_LOG: ${{ runner.temp }}/publish-recovery-live.log RECOVERY_RUN: ${{ runner.temp }}/publish-recovery-run.json - SOURCE_PUBLISH_ATTEMPT: ${{ github.event.deployment.payload.source_run_attempt }} - SOURCE_PUBLISH_RUN_ID: ${{ github.event.deployment.payload.source_run_id }} + SOURCE_PUBLISH_ATTEMPT: ${{ inputs.source_publish_run_attempt }} + SOURCE_PUBLISH_RUN_ID: ${{ inputs.source_publish_run_id }} shell: bash run: | set -euo pipefail @@ -211,7 +236,7 @@ jobs: EXPECTED_REPOSITORY_URL: git+https://github.com/cometapi-dev/cometapi-node.git EXPECTED_WORKFLOW: Release Please EXPECTED_WORKFLOW_PATH: .github/workflows/release-please.yml - CONTROL_SHA: ${{ github.event_name == 'deployment' && github.workflow_sha || github.sha }} + CONTROL_SHA: ${{ github.event_name == 'workflow_dispatch' && github.workflow_sha || github.sha }} EVENT_NAME: ${{ github.event_name }} RELEASE_RESULT: ${{ runner.temp }}/release-please-result/result.json SOURCE_RUN_FILE: ${{ runner.temp }}/release-please-source-run.json @@ -230,7 +255,7 @@ jobs: git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main case "$EVENT_NAME" in workflow_run) expected_main="$WORKFLOW_SHA" ;; - deployment) expected_main="$CONTROL_SHA" ;; + workflow_dispatch) expected_main="$CONTROL_SHA" ;; *) echo "Publish received an unsupported event." >&2; exit 1 ;; esac if [[ "$(git rev-parse refs/remotes/origin/main)" != "$expected_main" ]]; then @@ -447,7 +472,7 @@ jobs: environment: live-smoke steps: - name: Reuse the successful bounded live smoke - if: github.event_name == 'deployment' + if: github.event_name == 'workflow_dispatch' env: REUSE_LIVE_SMOKE: ${{ needs.verify.outputs.reuse-live-smoke }} shell: bash @@ -458,25 +483,25 @@ jobs: exit 1 fi - name: Check out the verified release tag - if: github.event_name != 'deployment' + if: github.event_name != 'workflow_dispatch' uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false ref: ${{ needs.verify.outputs.release-commit }} - name: Set up Node.js 24 - if: github.event_name != 'deployment' + if: github.event_name != 'workflow_dispatch' uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24.x cache: npm - name: Install locked dependencies - if: github.event_name != 'deployment' + if: github.event_name != 'workflow_dispatch' run: npm ci - name: Build the release tag - if: github.event_name != 'deployment' + if: github.event_name != 'workflow_dispatch' run: npm run build - name: Run the bounded live smoke - if: github.event_name != 'deployment' + if: github.event_name != 'workflow_dispatch' env: COMETAPI_KEY: ${{ secrets.COMETAPI_KEY }} COMETAPI_LIVE_SMOKE: "1" diff --git a/RELEASING.md b/RELEASING.md index 5b6235b..74adb7e 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -303,17 +303,42 @@ The repository maintains four independently auditable workflows: `main` deployment while the protected npm environment accepts only `v*` tags. No OIDC token or npm mutation occurred in either failed run. - The replacement one-cycle recovery uses a human-created GitHub deployment for - the existing immutable `v0.1.1` tag. It accepts only actor and triggering actor - `tensornull`, exact release commit - `c98b514227858cd183c781270a7f78f65b577e82`, Release Please run - `30469181724` attempt 1, failed Publish run `30471665743` attempt 1, the exact + A first attempted recovery deployment [5667717157](https://github.com/cometapi-dev/cometapi-node/deployments/5667717157) + was intentionally marked failed: the immutable `v0.1.1` tag predates the + temporary deployment trigger, so GitHub found no workflow at that tag and no + runner or npm mutation occurred. The replacement one-cycle recovery uses the + documented `workflow_dispatch` API with `ref=v0.1.1`; GitHub dispatches the + workflow from the reviewed default-branch definition while setting + `GITHUB_REF=refs/tags/v0.1.1`, which satisfies the existing npm tag policy. + The exact request is: + + ```bash + gh api --method POST \ + repos/cometapi-dev/cometapi-node/actions/workflows/publish.yml/dispatches \ + --input - <<'JSON' + { + "ref": "v0.1.1", + "inputs": { + "recovery_task": "npm-publish-recovery", + "release_commit": "c98b514227858cd183c781270a7f78f65b577e82", + "release_tag": "v0.1.1", + "release_run_id": "30469181724", + "release_run_attempt": "1", + "source_publish_run_id": "30471665743", + "source_publish_run_attempt": "1" + } + } + JSON + ``` + + The workflow accepts only actor and triggering actor `tensornull`, the exact + release commit and tag, both source run IDs and attempts, the reviewed first-parent control merge, and the recorded repair files. It revalidates the successful source verify job, artifact ID and digest, branch-policy failure, - and the log evidence for exactly three sequential live requests. The new tag - run repeats offline package and exact-artifact gates but does not spend another + and log evidence for exactly three sequential live requests. The tag run + repeats offline package and exact-artifact gates but does not spend another live request budget. The protected npm environment and OIDC gate remain - unchanged. The deployment trigger and exact recovery constants must be removed + unchanged. The dispatch trigger and exact recovery constants must be removed in the post-release evidence PR; the `runner.temp` isolation remains permanent. Third-party actions are pinned to full commit SHAs. Workflow permissions remain diff --git a/scripts/release-workflow-validation.mjs b/scripts/release-workflow-validation.mjs index f5d479b..1d07135 100644 --- a/scripts/release-workflow-validation.mjs +++ b/scripts/release-workflow-validation.mjs @@ -24,9 +24,8 @@ const PUBLISH_RECOVERY = Object.freeze({ "tests/release-workflow-validation.test.mjs", "tests/workflow-contract.test.mjs", ]), - controlParent: "22c313d4f80c53ba01672dd35cc27b621d5ec9ce", - deploymentEnvironment: "npm", - deploymentTask: "npm-publish-recovery", + dispatchControlParent: "8a80d8272a490ed6a7b47eede45aaeccae03c819", + dispatchTask: "npm-publish-recovery", failedPublishJobId: 90643868523, liveJobId: 90643725110, releaseCommit: "c98b514227858cd183c781270a7f78f65b577e82", @@ -89,28 +88,23 @@ function stablePatch(version, label) { return Number(match[1]); } -export function validatePublishDeploymentRecoveryTrigger({ +export function validatePublishWorkflowDispatchRecoveryTrigger({ actor, changedFiles, controlCommit, controlFirstParent, - deploymentCreator, - deploymentEnvironment, - deploymentId, - deploymentRef, - deploymentReleaseCommit, - deploymentReleaseTag, - deploymentSha, - deploymentSourceRunAttempt, - deploymentSourceRunId, - deploymentTask, eventName, eventRef, eventSha, mainCommit, + releaseCommit, + releaseTag, + sourcePublishRunAttempt, + sourcePublishRunId, sourceReleaseCommit, sourceRunAttempt, sourceRunId, + task, triggeringActor, workflowRunAttempt, }) { @@ -120,7 +114,7 @@ export function validatePublishDeploymentRecoveryTrigger({ PUBLISH_RECOVERY.actor, "publish recovery triggering actor", ); - requireEqual(eventName, "deployment", "publish recovery event"); + requireEqual(eventName, "workflow_dispatch", "publish recovery event"); requireEqual( eventRef, `refs/tags/${PUBLISH_RECOVERY.releaseTag}`, @@ -143,9 +137,19 @@ export function validatePublishDeploymentRecoveryTrigger({ ); requireEqual( controlFirstParent, - PUBLISH_RECOVERY.controlParent, + PUBLISH_RECOVERY.dispatchControlParent, "publish recovery control first parent", ); + requireEqual( + releaseCommit, + PUBLISH_RECOVERY.releaseCommit, + "publish recovery input release commit", + ); + requireEqual( + releaseTag, + PUBLISH_RECOVERY.releaseTag, + "publish recovery input release tag", + ); requireEqual( sourceReleaseCommit, PUBLISH_RECOVERY.releaseCommit, @@ -166,65 +170,25 @@ export function validatePublishDeploymentRecoveryTrigger({ PUBLISH_RECOVERY.releaseRunAttempt, "publish recovery source run attempt", ); - requirePositiveInteger(deploymentId, "publish recovery deployment ID"); - requireEqual( - deploymentCreator, - PUBLISH_RECOVERY.actor, - "publish recovery deployment creator", - ); - requireEqual( - deploymentEnvironment, - PUBLISH_RECOVERY.deploymentEnvironment, - "publish recovery deployment environment", - ); - requireEqual( - deploymentRef, - PUBLISH_RECOVERY.releaseTag, - "publish recovery deployment ref", - ); - requireCommit(deploymentSha, "publish recovery deployment SHA"); - requireEqual( - deploymentSha, - PUBLISH_RECOVERY.releaseCommit, - "publish recovery deployment SHA", - ); - requireEqual( - deploymentTask, - PUBLISH_RECOVERY.deploymentTask, - "publish recovery deployment task", - ); - requireCommit( - deploymentReleaseCommit, - "publish recovery deployment payload release commit", - ); - requireEqual( - deploymentReleaseCommit, - PUBLISH_RECOVERY.releaseCommit, - "publish recovery deployment payload release commit", - ); - requireEqual( - deploymentReleaseTag, - PUBLISH_RECOVERY.releaseTag, - "publish recovery deployment payload release tag", - ); requirePositiveInteger( - deploymentSourceRunId, - "publish recovery deployment payload source run ID", + sourcePublishRunId, + "publish recovery source Publish run ID", ); requireEqual( - deploymentSourceRunId, + sourcePublishRunId, PUBLISH_RECOVERY.sourcePublishRunId, - "publish recovery deployment payload source run ID", + "publish recovery source Publish run ID", ); requirePositiveInteger( - deploymentSourceRunAttempt, - "publish recovery deployment payload source run attempt", + sourcePublishRunAttempt, + "publish recovery source Publish run attempt", ); requireEqual( - deploymentSourceRunAttempt, + sourcePublishRunAttempt, PUBLISH_RECOVERY.sourcePublishRunAttempt, - "publish recovery deployment payload source run attempt", + "publish recovery source Publish run attempt", ); + requireEqual(task, PUBLISH_RECOVERY.dispatchTask, "publish recovery task"); requirePositiveInteger(workflowRunAttempt, "publish recovery run attempt"); if (!Array.isArray(changedFiles)) { fail("Release workflow publish recovery changed files must be an array."); diff --git a/tests/release-workflow-validation.test.mjs b/tests/release-workflow-validation.test.mjs index 07ebd2a..3f60154 100644 --- a/tests/release-workflow-validation.test.mjs +++ b/tests/release-workflow-validation.test.mjs @@ -11,7 +11,7 @@ import { validateMergedReleasePullRequest, validateOpenReleasePullRequestCollisions, validatePostActionPullRequestSnapshot, - validatePublishDeploymentRecoveryTrigger, + validatePublishWorkflowDispatchRecoveryTrigger, validatePublishRecoveryEvidence, validateReleasePleaseCommitMessages, validateReleasePleaseCompletion, @@ -161,9 +161,9 @@ describe("Release Please generated files", () => { }); }); -describe("Publish deployment recovery trigger", () => { +describe("Publish workflow dispatch recovery trigger", () => { const recoveryCommit = "c98b514227858cd183c781270a7f78f65b577e82"; - const controlParent = "22c313d4f80c53ba01672dd35cc27b621d5ec9ce"; + const controlParent = "8a80d8272a490ed6a7b47eede45aaeccae03c819"; const recoveryFiles = [ ".github/workflows/publish.yml", "RELEASING.md", @@ -178,23 +178,18 @@ describe("Publish deployment recovery trigger", () => { changedFiles: recoveryFiles, controlCommit: BRANCH_SHA, controlFirstParent: controlParent, - deploymentCreator: "tensornull", - deploymentEnvironment: "npm", - deploymentId: 456789, - deploymentRef: "v0.1.1", - deploymentReleaseCommit: recoveryCommit, - deploymentReleaseTag: "v0.1.1", - deploymentSha: recoveryCommit, - deploymentSourceRunAttempt: 1, - deploymentSourceRunId: 30471665743, - deploymentTask: "npm-publish-recovery", - eventName: "deployment", + eventName: "workflow_dispatch", eventRef: "refs/tags/v0.1.1", eventSha: recoveryCommit, mainCommit: BRANCH_SHA, + releaseCommit: recoveryCommit, + releaseTag: "v0.1.1", + sourcePublishRunAttempt: 1, + sourcePublishRunId: 30471665743, sourceReleaseCommit: recoveryCommit, sourceRunAttempt: 1, sourceRunId: 30469181724, + task: "npm-publish-recovery", triggeringActor: "tensornull", workflowRunAttempt: 1, ...overrides, @@ -202,34 +197,30 @@ describe("Publish deployment recovery trigger", () => { } it("accepts only the reviewed one-cycle recovery merge", () => { - expect(validatePublishDeploymentRecoveryTrigger(recoveryTrigger())).toEqual( - { - releaseCommit: recoveryCommit, - releaseRunAttempt: 1, - releaseRunId: 30469181724, - sourcePublishRunAttempt: 1, - sourcePublishRunId: 30471665743, - }, - ); + expect( + validatePublishWorkflowDispatchRecoveryTrigger(recoveryTrigger()), + ).toEqual({ + releaseCommit: recoveryCommit, + releaseRunAttempt: 1, + releaseRunId: 30469181724, + sourcePublishRunAttempt: 1, + sourcePublishRunId: 30471665743, + }); }); it.each([ ["actor", { actor: "github-actions[bot]" }], ["triggering actor", { triggeringActor: "other-maintainer" }], - ["event", { eventName: "push" }], + ["event", { eventName: "deployment" }], ["ref", { eventRef: "refs/heads/main" }], ["event SHA", { eventSha: RELEASE_SHA }], ["control commit", { controlCommit: RELEASE_SHA }], ["first parent", { controlFirstParent: RELEASE_SHA }], - ["deployment creator", { deploymentCreator: "github-actions[bot]" }], - ["deployment environment", { deploymentEnvironment: "production" }], - ["deployment ref", { deploymentRef: "main" }], - ["deployment SHA", { deploymentSha: RELEASE_SHA }], - ["deployment task", { deploymentTask: "deploy" }], - ["payload release commit", { deploymentReleaseCommit: RELEASE_SHA }], - ["payload release tag", { deploymentReleaseTag: "v0.1.0" }], - ["payload source run", { deploymentSourceRunId: 30471665744 }], - ["payload source attempt", { deploymentSourceRunAttempt: 2 }], + ["release input", { releaseCommit: RELEASE_SHA }], + ["release tag input", { releaseTag: "v0.1.0" }], + ["source Publish run", { sourcePublishRunId: 30471665744 }], + ["source Publish attempt", { sourcePublishRunAttempt: 2 }], + ["task", { task: "publish" }], ["source commit", { sourceReleaseCommit: RELEASE_SHA }], ["source run ID", { sourceRunId: 30469181725 }], ["source attempt", { sourceRunAttempt: 2 }], @@ -237,13 +228,15 @@ describe("Publish deployment recovery trigger", () => { ["extra file", { changedFiles: [...recoveryFiles, "package.json"] }], ])("rejects recovery trigger drift in %s", (_name, overrides) => { expect(() => - validatePublishDeploymentRecoveryTrigger(recoveryTrigger(overrides)), + validatePublishWorkflowDispatchRecoveryTrigger( + recoveryTrigger(overrides), + ), ).toThrow(/release workflow/i); }); it("accepts a rerun of the same immutable recovery event", () => { expect( - validatePublishDeploymentRecoveryTrigger( + validatePublishWorkflowDispatchRecoveryTrigger( recoveryTrigger({ workflowRunAttempt: 2 }), ), ).toMatchObject({ releaseRunId: 30469181724 }); diff --git a/tests/workflow-contract.test.mjs b/tests/workflow-contract.test.mjs index c6a2cd6..f91b130 100644 --- a/tests/workflow-contract.test.mjs +++ b/tests/workflow-contract.test.mjs @@ -129,7 +129,8 @@ describe("GitHub Actions workflow contract", () => { "ref: ${{ needs.verify.outputs.release-commit }}", ); expect(publish).toContain("run: bash scripts/publish-artifact.sh"); - expect(publishWorkflow).not.toContain("workflow_dispatch"); + expect(publishWorkflow).toContain("workflow_dispatch:"); + expect(publishWorkflow).toContain("inputs.recovery_task"); expect(publishWorkflow).not.toContain("NPM_ALPHA1_BOOTSTRAP"); expect(publishWorkflow).not.toContain("recover-verify"); expect(publishWorkflow).not.toContain("recover-publish"); @@ -319,14 +320,13 @@ describe("GitHub Actions workflow contract", () => { } }); - it("starts publication from Release Please or the exact tag deployment recovery", () => { + it("starts publication from Release Please or the exact tag workflow dispatch recovery", () => { const publish = workflow("publish.yml"); expect(publish).toMatch( /workflow_run:\n {4}workflows:\n {6}- Release Please\n {4}types:\n {6}- completed/, ); expect(publish).not.toMatch(/^ {2}release:/m); - expect(publish).not.toMatch(/^ {2}workflow_dispatch:/m); - expect(publishWorkflow.on.deployment).toBeNull(); + expect(publishWorkflow.on.workflow_dispatch).toBeDefined(); expect(publishWorkflow.on.push).toBeUndefined(); const verify = job(publish, "verify"); @@ -338,11 +338,14 @@ describe("GitHub Actions workflow contract", () => { expect(verify).toContain("SOURCE_RELEASE_RUN_ID:"); expect(verify).toContain("30469181724"); expect(verify).toContain("SOURCE_RELEASE_RUN_ATTEMPT:"); - expect(verify).toContain("github.event.deployment.ref == 'v0.1.1'"); + expect(verify).toContain("github.ref == 'refs/tags/v0.1.1'"); expect(verify).toContain( - "github.event.deployment.sha == 'c98b514227858cd183c781270a7f78f65b577e82'", + "inputs.release_commit == 'c98b514227858cd183c781270a7f78f65b577e82'", ); - expect(verify).toContain("validatePublishDeploymentRecoveryTrigger"); + expect(verify).toContain("inputs.release_tag == 'v0.1.1'"); + expect(verify).toContain("inputs.release_run_id == '30469181724'"); + expect(verify).toContain("inputs.source_publish_run_id == '30471665743'"); + expect(verify).toContain("validatePublishWorkflowDispatchRecoveryTrigger"); expect(verify).toContain("validatePublishRecoveryEvidence"); expect(verify).toContain("github.triggering_actor"); expect(releaseWorkflowValidation).toContain("30471665743"); @@ -400,7 +403,8 @@ describe("GitHub Actions workflow contract", () => { ); const liveSmoke = job(publish, "live-smoke"); expect(liveSmoke).toContain("Reuse the successful bounded live smoke"); - expect(liveSmoke).toContain("if: github.event_name != 'deployment'"); + expect(liveSmoke).toContain("if: github.event_name != 'workflow_dispatch'"); + expect(publish).not.toContain("github.event.deployment"); }); it("rejects an unrelated divergent Release Please branch", () => {