Skip to content

Commit 6c598d2

Browse files
committed
fix: pin release publishing to merge commit
1 parent 2339757 commit 6c598d2

3 files changed

Lines changed: 46 additions & 5 deletions

File tree

.github/workflows/publish.yml

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,26 @@ jobs:
3535
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
3636
with:
3737
fetch-depth: 0
38-
# On manual dispatch, check out the existing tag so we publish the
39-
# exact commit that was tagged — not whatever is currently on main.
38+
# Pin every release to an immutable ref. In particular, a merged
39+
# release PR event exposes main as github.ref, which may advance
40+
# before this job starts; use the event's exact merge commit instead.
4041
ref: >-
41-
${{ github.event_name == 'workflow_dispatch'
42+
${{ github.event_name == 'pull_request'
43+
&& github.event.pull_request.merge_commit_sha
44+
|| github.event_name == 'workflow_dispatch'
4245
&& format('refs/tags/v{0}', inputs.version)
43-
|| github.ref }}
46+
|| github.sha }}
47+
48+
- name: Verify merged release checkout
49+
if: github.event_name == 'pull_request'
50+
env:
51+
EXPECTED_MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }}
52+
run: |
53+
ACTUAL_SHA="$(git rev-parse HEAD)"
54+
if [ "$ACTUAL_SHA" != "$EXPECTED_MERGE_SHA" ]; then
55+
echo "::error::Expected merged release commit $EXPECTED_MERGE_SHA, checked out $ACTUAL_SHA"
56+
exit 1
57+
fi
4458
4559
- name: Resolve version
4660
id: version

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"player:perf": "bun run --filter @hyperframes/player perf",
4848
"format:check": "oxfmt --check .",
4949
"knip": "knip",
50-
"test:scripts": "node --import tsx --test scripts/check-tracked-artifacts.test.mjs scripts/check-workspace-contracts.test.mjs scripts/check-package-cycles.test.mjs scripts/check-cli-process-ownership.test.mjs scripts/package-subpaths.test.mjs scripts/validate-release-channel.test.mjs scripts/draft-changelog.test.ts scripts/set-version.test.ts scripts/release-prepare.test.ts scripts/cli-options.test.ts scripts/changelog-weekly.test.ts scripts/claude-plugin-compression.test.ts scripts/studio-runtime-smoke.test.mjs scripts/verify-packed-manifests.test.mjs scripts/lint-skills.test.mjs packages/gcp-cloud-run/check-dockerfile-workspaces.test.mjs",
50+
"test:scripts": "node --import tsx --test scripts/check-tracked-artifacts.test.mjs scripts/check-workspace-contracts.test.mjs scripts/check-package-cycles.test.mjs scripts/check-cli-process-ownership.test.mjs scripts/package-subpaths.test.mjs scripts/validate-release-channel.test.mjs scripts/publish-workflow.test.mjs scripts/draft-changelog.test.ts scripts/set-version.test.ts scripts/release-prepare.test.ts scripts/cli-options.test.ts scripts/changelog-weekly.test.ts scripts/claude-plugin-compression.test.ts scripts/studio-runtime-smoke.test.mjs scripts/verify-packed-manifests.test.mjs scripts/lint-skills.test.mjs packages/gcp-cloud-run/check-dockerfile-workspaces.test.mjs",
5151
"test:skills": "node --test 'skills/**/*.test.mjs'",
5252
"generate:previews": "tsx scripts/generate-template-previews.ts",
5353
"generate:catalog-previews": "tsx scripts/generate-catalog-previews.ts",

scripts/publish-workflow.test.mjs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import assert from "node:assert/strict";
2+
import { readFileSync } from "node:fs";
3+
import test from "node:test";
4+
5+
const workflow = readFileSync(new URL("../.github/workflows/publish.yml", import.meta.url), "utf8");
6+
7+
test("merged release PRs publish the immutable merge commit", () => {
8+
assert.match(
9+
workflow,
10+
/github\.event_name == 'pull_request'\s+&& github\.event\.pull_request\.merge_commit_sha/,
11+
);
12+
assert.match(workflow, /Verify merged release checkout/);
13+
assert.match(
14+
workflow,
15+
/EXPECTED_MERGE_SHA: \$\{\{ github\.event\.pull_request\.merge_commit_sha \}\}/,
16+
);
17+
assert.match(workflow, /ACTUAL_SHA="\$\(git rev-parse HEAD\)"/);
18+
assert.match(workflow, /\[ "\$ACTUAL_SHA" != "\$EXPECTED_MERGE_SHA" \]/);
19+
});
20+
21+
test("tag pushes use the immutable event SHA", () => {
22+
assert.match(
23+
workflow,
24+
/github\.event_name == 'workflow_dispatch'[\s\S]*format\('refs\/tags\/v\{0\}', inputs\.version\)[\s\S]*\|\| github\.sha/,
25+
);
26+
assert.doesNotMatch(workflow, /\|\| github\.ref/);
27+
});

0 commit comments

Comments
 (0)