Skip to content

Commit 91ce99c

Browse files
authored
Merge pull request #30 from cometapi-dev/agent/fix-options-release-please-0.1.1
fix: enforce options boundary and patch release flow
2 parents 9c7149f + f72f185 commit 91ce99c

20 files changed

Lines changed: 2066 additions & 126 deletions

.github/workflows/publish.yml

Lines changed: 102 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
name: Publish
22

33
on:
4-
release:
4+
workflow_run:
5+
workflows:
6+
- Release Please
57
types:
6-
- published
8+
- completed
79

810
permissions:
11+
actions: read
912
contents: read
1013

1114
concurrency:
@@ -15,72 +18,107 @@ concurrency:
1518
jobs:
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

Comments
 (0)