11name : Publish
22
33on :
4- release :
4+ workflow_run :
5+ workflows :
6+ - Release Please
57 types :
6- - published
8+ - completed
79
810permissions :
11+ actions : read
912 contents : read
1013
1114concurrency :
@@ -15,72 +18,107 @@ concurrency:
1518jobs :
1619 verify :
1720 name : Verify the immutable release artifact
18- if : github.event_name == 'release'
21+ if : >-
22+ vars.RELEASE_PLEASE_ENABLED == 'true' &&
23+ github.event.workflow_run.conclusion == 'success' &&
24+ github.event.workflow_run.event == 'push' &&
25+ github.event.workflow_run.head_branch == 'main'
1926 runs-on : ubuntu-latest
2027 timeout-minutes : 30
2128 outputs :
2229 dist-tag : ${{ steps.version.outputs.dist-tag }}
2330 release-commit : ${{ steps.trust.outputs.release-commit }}
31+ release-tag : ${{ steps.trust.outputs.release-tag }}
2432 version : ${{ steps.version.outputs.version }}
2533 steps :
26- - name : Check out the published release tag
34+ - name : Check out the current main branch
2735 uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
2836 with :
2937 fetch-depth : 0
3038 persist-credentials : false
31- ref : refs/tags/${{ github.event.release.tag_name }}
32- - name : Reject an untrusted release target
39+ ref : refs/heads/main
40+ - name : Download the exact Release Please result
41+ uses : actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
42+ with :
43+ name : release-please-result-${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}
44+ path : release-please-result
45+ github-token : ${{ github.token }}
46+ repository : ${{ github.repository }}
47+ run-id : ${{ github.event.workflow_run.id }}
48+ - name : Reject an untrusted Release Please workflow run
3349 id : trust
3450 env :
3551 EXPECTED_BUGS_URL : https://github.com/cometapi-dev/cometapi-node/issues
3652 EXPECTED_REPOSITORY : cometapi-dev/cometapi-node
3753 EXPECTED_REPOSITORY_URL : git+https://github.com/cometapi-dev/cometapi-node.git
38- RELEASE_IMMUTABLE : ${{ github.event.release.immutable }}
39- RELEASE_TAG : ${{ github.event.release.tag_name }}
54+ EXPECTED_WORKFLOW : Release Please
55+ EXPECTED_WORKFLOW_PATH : .github/workflows/release-please.yml
56+ RELEASE_RESULT : release-please-result/result.json
57+ WORKFLOW_SHA : ${{ github.event.workflow_run.head_sha }}
4058 shell : bash
4159 run : |
4260 set -euo pipefail
43- if [[ "$GITHUB_REPOSITORY" != "$EXPECTED_REPOSITORY" ]]; then
44- echo "Publication is restricted to $EXPECTED_REPOSITORY; received $GITHUB_REPOSITORY." >&2
45- exit 1
46- fi
47- if [[ "$RELEASE_IMMUTABLE" != "true" ]]; then
48- echo "Publication requires a GitHub release with immutable=true." >&2
49- exit 1
50- fi
51-
52- release_ref="refs/tags/${RELEASE_TAG}"
53- release_commit="$(git rev-parse --verify "${release_ref}^{commit}")"
5461 head_commit="$(git rev-parse HEAD)"
55- if [[ "$head_commit" != "$release_commit " ]]; then
56- echo "Checked-out commit $head_commit does not match $release_ref ($release_commit) ." >&2
62+ if [[ "$head_commit" != "$WORKFLOW_SHA " ]]; then
63+ echo "The successful Release Please SHA is no longer the exact main tip ." >&2
5764 exit 1
5865 fi
5966
6067 git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
61- if ! git merge-base --is-ancestor "$release_commit" refs/remotes/origin/main; then
62- echo "Release commit $release_commit is not reachable from origin/main ." >&2
68+ if [[ "$( git rev-parse refs/remotes/origin/main)" != "$WORKFLOW_SHA" ]] ; then
69+ echo "origin/main moved after the successful Release Please run ." >&2
6370 exit 1
6471 fi
6572
66- node <<'EOF'
67- const manifest = require("./package.json");
68- const expectedRepository = process.env.EXPECTED_REPOSITORY_URL;
69- const expectedBugs = process.env.EXPECTED_BUGS_URL;
73+ node --input-type=module <<'EOF'
74+ import { appendFileSync, readFileSync } from "node:fs";
75+ import {
76+ validateReleasePleaseActionResult,
77+ validateReleaseWorkflowRun,
78+ } from "./scripts/release-workflow-validation.mjs";
79+
80+ const event = JSON.parse(readFileSync(process.env.GITHUB_EVENT_PATH, "utf8"));
81+ const run = validateReleaseWorkflowRun(event, {
82+ checkedOutSha: process.env.WORKFLOW_SHA,
83+ repository: process.env.EXPECTED_REPOSITORY,
84+ workflowName: process.env.EXPECTED_WORKFLOW,
85+ workflowPath: process.env.EXPECTED_WORKFLOW_PATH,
86+ });
87+ const manifest = JSON.parse(readFileSync("package.json", "utf8"));
7088 if (
7189 manifest.repository?.type !== "git" ||
72- manifest.repository?.url !== expectedRepository
90+ manifest.repository?.url !== process.env.EXPECTED_REPOSITORY_URL
7391 ) {
7492 throw new Error(
75- `package.json repository must equal ${expectedRepository }.`,
93+ `package.json repository must equal ${process.env.EXPECTED_REPOSITORY_URL }.`,
7694 );
7795 }
78- if (manifest.bugs?.url !== expectedBugs ) {
79- throw new Error(`package.json bugs.url must equal ${expectedBugs }.`);
96+ if (manifest.bugs?.url !== process.env.EXPECTED_BUGS_URL ) {
97+ throw new Error(`package.json bugs.url must equal ${process.env.EXPECTED_BUGS_URL }.`);
8098 }
99+ const actionResult = JSON.parse(
100+ readFileSync(process.env.RELEASE_RESULT, "utf8"),
101+ );
102+ const release = validateReleasePleaseActionResult(actionResult, {
103+ releaseCommit: run.releaseCommit,
104+ repository: process.env.EXPECTED_REPOSITORY,
105+ runAttempt: run.runAttempt,
106+ runId: run.runId,
107+ version: manifest.version,
108+ workflowName: process.env.EXPECTED_WORKFLOW,
109+ workflowPath: process.env.EXPECTED_WORKFLOW_PATH,
110+ });
111+ appendFileSync(
112+ process.env.GITHUB_OUTPUT,
113+ [
114+ `release-commit=${release.releaseCommit}`,
115+ `release-tag=${release.tag}`,
116+ `release-url=${release.htmlUrl}`,
117+ `release-version=${release.version}`,
118+ "",
119+ ].join("\n"),
120+ );
81121 EOF
82-
83- echo "release-commit=${release_commit}" >> "$GITHUB_OUTPUT"
84122 - name : Set up Node.js 24
85123 uses : actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
86124 with :
@@ -91,16 +129,43 @@ jobs:
91129 - name : Verify release metadata and derive the npm dist-tag
92130 id : version
93131 env :
94- RELEASE_IS_PRERELEASE : ${{ github.event.release.prerelease }}
95- RELEASE_TAG : ${{ github.event.release.tag_name }}
132+ RELEASE_TAG : ${{ steps.trust.outputs.release-tag }}
96133 shell : bash
97134 run : |
98135 set -euo pipefail
99136 node scripts/validate-release.mjs \
100137 --tag "$RELEASE_TAG" \
101- --release-prerelease "$RELEASE_IS_PRERELEASE" \
102138 --require-final \
103139 --require-releasable-docs >> "$GITHUB_OUTPUT"
140+ - name : Verify the exact immutable GitHub release and tag
141+ id : release
142+ env :
143+ GH_TOKEN : ${{ github.token }}
144+ RELEASE_COMMIT : ${{ steps.trust.outputs.release-commit }}
145+ RELEASE_HTML_URL : ${{ steps.trust.outputs.release-url }}
146+ RELEASE_TAG : ${{ steps.trust.outputs.release-tag }}
147+ shell : bash
148+ run : |
149+ set -euo pipefail
150+ release_json="$RUNNER_TEMP/github-release.json"
151+ gh api "repos/${GITHUB_REPOSITORY}/releases/tags/${RELEASE_TAG}" > "$release_json"
152+ git fetch --no-tags origin \
153+ "+refs/tags/${RELEASE_TAG}:refs/tags/${RELEASE_TAG}"
154+ tag_commit="$(git rev-parse --verify "refs/tags/${RELEASE_TAG}^{commit}")"
155+
156+ RELEASE_JSON="$release_json" TAG_COMMIT="$tag_commit" \
157+ node --input-type=module <<'EOF'
158+ import { readFileSync } from "node:fs";
159+ import { validateGitHubRelease } from "./scripts/release-workflow-validation.mjs";
160+
161+ const release = JSON.parse(readFileSync(process.env.RELEASE_JSON, "utf8"));
162+ validateGitHubRelease(release, {
163+ htmlUrl: process.env.RELEASE_HTML_URL,
164+ releaseCommit: process.env.RELEASE_COMMIT,
165+ tag: process.env.RELEASE_TAG,
166+ tagCommit: process.env.TAG_COMMIT,
167+ });
168+ EOF
104169 - name : Use a Trusted Publishing-capable npm CLI
105170 run : npm install --global npm@11.12.1
106171 - name : Install locked dependencies
@@ -134,7 +199,7 @@ jobs:
134199 run : |
135200 npm run test:package -- \
136201 --tarball "${{ steps.pack.outputs.tarball }}" \
137- --tag "${{ github.event.release.tag_name }}"
202+ --tag "${{ steps.trust.outputs.release-tag }}"
138203 npm run test:examples -- --tarball "${{ steps.pack.outputs.tarball }}"
139204 npm run test:fixtures -- --tarball "${{ steps.pack.outputs.tarball }}"
140205 - name : Upload the verified release artifact
0 commit comments