ReviewOS

also looking at this

craft-native/craft

fix: validate native SDK release prerequisites before tagging

#364
Merged chris wants to merge codex/native-sdk-release-preflight into main
7 files +153 -15

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

bun.lockmodified+0-0

Binary file. Nothing to show line by line.

package.jsonmodified+3-2
Changes to package.json
@@ -61,11 +61,12 @@
6161 "changelog:generate": "bun scripts/generate-release-changelog.ts",
6262 "test:release": "bun test ./scripts/changelog-preview.test.ts ./scripts/release-workflow.test.ts ./scripts/release-manifest.test.ts ./scripts/verify-sbom.test.ts",
6363 "release": "bun scripts/release-version.ts --preflight && bun x --bun @stacksjs/bumpx prompt --recursive",
64 "release:patch": "bun scripts/release-version.ts --preflight && bun x --bun @stacksjs/bumpx patch --recursive --commit --tag --push --yes --no-changelog --execute \"bun scripts/sync-release-version.ts && bun scripts/generate-release-changelog.ts && git add packages/zig/build.zig.zon packages/zig/pantry.json CHANGELOG.md\""
64 "release:patch": "bun scripts/release-version.ts --preflight && bun x --bun @stacksjs/bumpx patch --recursive --commit --tag --push --yes --no-changelog --execute \"bun scripts/sync-release-version.ts && bun scripts/generate-release-changelog.ts && git add packages/zig/build.zig.zon packages/zig/pantry.json CHANGELOG.md\"",
65 "release:version": "bun scripts/release-version.ts --preflight && bun x --bun @stacksjs/bumpx --recursive --commit --tag --push --yes --no-changelog --execute \"bun scripts/sync-release-version.ts && bun scripts/generate-release-changelog.ts && git add packages/zig/build.zig.zon packages/zig/pantry.json CHANGELOG.md\""
6566 },
6667 "devDependencies": {
6768 "@stacksjs/buddy": "0.72.56",
68 "@stacksjs/bumpx": "^0.2.10",
69 "@stacksjs/bumpx": "^0.2.22",
6970 "@stacksjs/bunpress": "^0.2.13",
7071 "@stacksjs/env": "^0.72.50",
7172 "better-dx": "^0.2.38",
scripts/changelog-preview.test.tsmodified+15-2
Changes to scripts/changelog-preview.test.ts
@@ -109,6 +109,18 @@ test('failed release generation leaves the changelog unchanged', () => {
109109 })
110110})
111111
112test('release notes can include changes since the last published release after an abandoned tag', () => {
113 fixture((directory, git) => {
114 git('commit', '--allow-empty', '-qm', 'feat: native trail maps')
115 git('tag', 'v1.0.1')
116 writeFileSync(join(directory, 'package.json'), '{"name":"fixture","version":"1.0.2"}')
117 const content = generateReleaseChangelog(directory, 'v1.0.0')
118 expect(content).toContain('native trail maps')
119 expect(content).toContain('/compare/v1.0.0...v1.0.2)')
120 expect(git('tag', '--list', 'v1.0.1')).toBe('v1.0.1\n')
121 })
122})
123
112124test('patch release uses the guarded changelog generator before committing', () => {
113125 const project = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf8'))
114126 const command = project.scripts['release:patch'] as string
@@ -118,14 +130,15 @@ test('patch release uses the guarded changelog generator before committing', ()
118130 expect(command).toContain('git add packages/zig/build.zig.zon packages/zig/pantry.json CHANGELOG.md')
119131})
120132
121test('bumpx commits and tags the guarded changelog without its duplicate generator', () => {
133test('an explicit version commits and tags the guarded changelog without its duplicate generator', () => {
122134 fixture((directory, git) => {
123135 git('commit', '--allow-empty', '-qm', 'Merge pull request #326 from example/feature')
124136 const bumpx = Bun.resolveSync('@stacksjs/bumpx/bin/cli.js', import.meta.dir)
125137 const generator = join(import.meta.dir, 'generate-release-changelog.ts')
126138 const result = Bun.spawnSync([
127 process.execPath, bumpx, 'patch', '--commit', '--tag', '--no-push', '--yes', '--no-changelog',
139 process.execPath, bumpx, '--recursive', '--commit', '--tag', '--no-push', '--yes', '--no-changelog',
128140 '--execute', `bun ${JSON.stringify(generator)} && git add CHANGELOG.md`,
141 '1.0.2',
129142 ], { cwd: directory, stdout: 'pipe', stderr: 'pipe', timeout: 30_000 })
130143 expect(result.exitCode, result.stderr.toString()).toBe(0)
131144 expect(git('tag', '--list', 'v1.0.2')).toBe('v1.0.2\n')
scripts/generate-release-changelog.tsmodified+3-3
Changes to scripts/generate-release-changelog.ts
@@ -12,12 +12,12 @@ function previousTag(cwd: string): string {
1212}
1313
1414/** Generate the next release section before bumpx commits and tags it. */
15export function generateReleaseChangelog(cwd: string): string {
15export function generateReleaseChangelog(cwd: string, fromRef?: string): string {
1616 const version = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8')).version
1717 if (typeof version !== 'string' || !/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(version))
1818 throw new Error('The root package must have a valid release version')
1919
20 const from = previousTag(cwd)
20 const from = fromRef ?? previousTag(cwd)
2121 const to = `v${version}`
2222 if (from === to)
2323 throw new Error(`Release ${to} is already tagged`)
@@ -44,4 +44,4 @@ export function generateReleaseChangelog(cwd: string): string {
4444}
4545
4646if (import.meta.main)
47 generateReleaseChangelog(process.cwd())
47 generateReleaseChangelog(process.cwd(), process.env.CRAFT_CHANGELOG_FROM)