patch-release.js: non-interactive mode (--yes, --cm-trigger, --json) - #638
Conversation
… use
Unblocks automated release-cutting from the dispatch app, which runs the
script headlessly. In interactive mode prompt 2 (CM deploy trigger) defaults
YES on EOF — a known deploy footgun. --yes inverts this: CM deploy is skipped
unless --cm-trigger is also passed.
Flag semantics:
--yes Auto-confirm all prompts. Prompt 1 (proceed) → y. Prompts 3/4
(branch return) → y. Prompt 2 (CM deploy) → n unless
--cm-trigger is also set.
--cm-trigger Force CM deploy. With --yes: opt-in deploy. Without --yes:
skips prompt 2 and answers y.
--json Print a final "RESULT: {...}" line with ok, target, coreVersion,
proVersion, coreBumped, pushed, cmTriggered, dryRun. Fatal
error paths emit RESULT: {"ok":false,"error":"..."} before exit.
Also introduces die() helper to avoid repeating the err+JSON+exit pattern
across all process.exit(1) sites.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Reviewed; no blockers found. |
There was a problem hiding this comment.
Code Review
This pull request adds non-interactive execution support to the patch-release.js script via new --yes, --cm-trigger, and --json flags, allowing for automated release flows and machine-readable JSON output. The feedback highlights two important improvements: first, using fs.writeSync instead of process.stdout.write in the die helper to ensure JSON output is fully flushed before process.exit terminates the process; second, safely handling caught exceptions in the global catch block to prevent secondary crashes when the error is not an instance of Error.
- die() and the final RESULT block now write via fs.writeSync(1, ...)
instead of process.stdout.write() — the latter races process.exit()
when stdout is a pipe (the standard --json setup), which can drop the
RESULT line once output crosses ~64 KiB.
- main().catch() no longer assumes the rejection is an Error (e?.message
?? String(e)).
- Declining the first confirmation prompt now emits an explicit
RESULT: {"ok":false,"error":"aborted",...} line under --json instead
of exiting silently.
- --cm-trigger no longer bypasses the deploy-confirmation prompt when
--yes isn't also set — it only changes the prompt's wording. Only
--cm-trigger + --yes auto-confirms.
- A CM-trigger failure is now terminal (nonzero exit, RESULT ok:false)
when --cm-trigger was explicitly requested, preserving pushed/version
state — previously it was swallowed to stderr and the run reported
ok:true with cmTriggered:false, so a dispatch caller couldn't tell
"deploy skipped" from "deploy attempted and failed".
Extracted the decision logic (resolveDeployAnswer, buildAbortedResult,
buildCmFailureResult) into pure, exported functions with unit test
coverage in unitTests/scripts/patchRelease.test.mjs, since main() itself
has no seams for testing without live git/gh state.
Addresses review feedback on PR #638 from gemini-code-assist, cb1kenobi,
and kriszyp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…oc the new flags Follow-up to the independent pre-push review of the prior commit: - Deferred the CM-trigger fatal die() until after Step 7 (branch restore). Making a requested CM failure terminal meant it previously short-circuited before the repos were returned to their original branches, leaving a reused dispatch worktree stuck on the release branch. - Extracted the synchronous RESULT-line write (die(), abort, and success paths) into a shared writeResult(result, fd) so the truncation-safe write mechanism itself has direct test coverage via a real fd, not just the objects it serializes. - Documented --yes/--cm-trigger/--json in CONTRIBUTING.md's options table. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Pushed fixes for all 8 open threads (Gemini, cb1kenobi, and Kris's comments) in f782b1b:
Extracted the decision logic ( I ran this change through an independent pre-push review (Codex + Grok) across two rounds. One real regression it caught in round 1 — making the CM failure terminal via One finding I'm flagging rather than fixing here (both Codex and Grok raised it, consistently across rounds): 🤖 Generated by Claude Sonnet 5 |
- die() and the final RESULT block now write via fs.writeSync(1, ...)
instead of process.stdout.write() — the latter races process.exit()
when stdout is a pipe (the standard --json setup), which can drop the
RESULT line once output crosses ~64 KiB.
- main().catch() no longer assumes the rejection is an Error (e?.message
?? String(e)).
- Declining the first confirmation prompt now emits an explicit
RESULT: {"ok":false,"error":"aborted",...} line under --json instead
of exiting silently.
- --cm-trigger no longer bypasses the deploy-confirmation prompt when
--yes isn't also set — it only changes the prompt's wording. Only
--cm-trigger + --yes auto-confirms.
- A CM-trigger failure is now terminal (nonzero exit, RESULT ok:false)
when --cm-trigger was explicitly requested, preserving pushed/version
state — previously it was swallowed to stderr and the run reported
ok:true with cmTriggered:false, so a dispatch caller couldn't tell
"deploy skipped" from "deploy attempted and failed".
Extracted the decision logic (resolveDeployAnswer, buildAbortedResult,
buildCmFailureResult) into pure, exported functions with unit test
coverage in unitTests/scripts/patchRelease.test.mjs, since main() itself
has no seams for testing without live git/gh state.
Addresses review feedback on PR #638 from gemini-code-assist, cb1kenobi,
and kriszyp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Motivation
The dispatch release-automation app runs
patch-release.jsheadlessly from a dev-agent.Two problems blocked that:
the script exits early or is piped without input. This is a known footgun.
Changes
Three new flags; existing interactive behavior is unchanged when flags are absent.
--yesNon-interactive mode. All prompts auto-answered:
The CM-deploy default is deliberately inverted from interactive mode. In interactive use,
pressing Enter (or EOF) triggers a deploy. In
--yesmode the safe default is to skip thedeploy unless the caller explicitly opts in with
--cm-trigger.--cm-triggerTriggers the CM release-to-environments workflow (step 6). With
--yes: opt-in deploy.Without
--yes: skips prompt 2 and answers yes directly.--jsonPrints a final machine-readable line on stdout:
Also emitted on fatal error paths before exit:
A
die()helper consolidates theerr()+JSON+process.exit()pattern that was scatteredacross all error-exit sites.
Dry-run verification
Ran against the real repo: completed with no prompt hangs, no CM workflow triggered
(dry-run + no --cm-trigger), ended with a parseable RESULT line.
Output (abridged):
🤖 Generated with Claude Code