ReviewOS

also looking at this

stacks/bumpx

fix: stop releases when a required command fails

#2014
Merged chris wants to merge codex/fail-fast-release-execute into main
3 files +40 -34

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.

packages/bumpx/src/version-bump.tsmodified+2-0
Changes to packages/bumpx/src/version-bump.ts
@@ -684,6 +684,8 @@ export async function versionBump(options: VersionBumpOptions): Promise<void> {
684684
685685 // Create a backup of the file content before modification
686686 const originalContent = readFileSync(filePath, 'utf-8')
687 fileBackups.set(filePath, { content: originalContent, version: fileCurrentVersion })
688 hasStartedUpdates = true
687689 // Always call updateVersionInFile to ensure mocks are triggered in tests
688690 const fileInfo = updateVersionInFile(filePath, fileCurrentVersion, fileNewVersion, forceUpdate)
689691 // If in dry run mode, restore the original content after the operation
packages/bumpx/test/git-push.integration.test.tsmodified+4-2
Changes to packages/bumpx/test/git-push.integration.test.ts
@@ -1,5 +1,5 @@
11import { afterEach, beforeEach, describe, expect, it } from 'bun:test'
2import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
2import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
33import { tmpdir } from 'node:os'
44import { join } from 'node:path'
55
@@ -124,15 +124,17 @@ describe('Git push & tag integration (local bare remote)', () => {
124124 it('does not commit, tag or push when a required release command fails', async () => {
125125 const localHead = runGit(['rev-parse', 'HEAD'], workDir).trim()
126126 const remoteHead = runGit(['--git-dir', bareDir, 'rev-parse', 'refs/heads/main'], tempDir).trim()
127 const manifest = readFileSync(join(workDir, 'package.json'), 'utf8')
127128 const res = await runCLI(['patch', '--commit', '--tag', '--push', '--yes', '--no-changelog', '--execute', 'bun -e "process.exit(23)"'], workDir)
128129
129130 expect({
130131 succeeded: res.code === 0,
132 manifest: readFileSync(join(workDir, 'package.json'), 'utf8'),
131133 localHead: runGit(['rev-parse', 'HEAD'], workDir).trim(),
132134 remoteHead: runGit(['--git-dir', bareDir, 'rev-parse', 'refs/heads/main'], tempDir).trim(),
133135 localTags: runGit(['tag', '--list'], workDir).trim(),
134136 remoteTags: runGit(['--git-dir', bareDir, 'tag', '--list'], tempDir).trim(),
135 }).toEqual({ succeeded: false, localHead, remoteHead, localTags: '', remoteTags: '' })
137 }).toEqual({ succeeded: false, manifest, localHead, remoteHead, localTags: '', remoteTags: '' })
136138 })
137139
138140 it('pushes commit and tag to local bare remote (single package)', async () => {