v0.22.0
🚀 Features
-
(config) Reject unsupported strategy values - (35709f3) by @bchatard
deploy hardcodes the atomic strategy, so a config with strategy: docker (or any
non-atomic value) was silently accepted and ignored. Validate now rejects an
explicit non-atomic strategy with a Config error (exit 2). An empty/unset strategy
is still valid — it defaults to atomic. Part of M20 (S5).Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
(config) Reject empty hook cmd at load time - (bb39dbc) by @bchatard
A hook with an empty cmd was accepted and would run 'sh -c ""' (a silent no-op).
ValidateHookCmds now rejects any hook with a blank cmd at every config level, and
Load reports it as a Config error (exit 2) alongside server-ref validation. Part of
M20 (S6).Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
(deploy) Cancel the deploy on SIGINT via cmd.Context() - (c897858) by @bchatard
deploy passed context.Background() to the deployer, so Ctrl-C couldn't interrupt a
long artifact extraction. Now that forge v0.17.0 wires fang.WithNotifySignal,
cmd.Context() is cancelled on SIGINT/SIGTERM; thread it into Deploy so cancellation
reaches Extract (which honors ctx). A guard test confirms Extract aborts with
context.Canceled. Other steps don't observe ctx yet — fuller cancellation is future
work. Part of M20 (S3).Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
(deps) Bump forge to v0.17.0 - (2f285a8) by @bchatard
cmd.Context() is now canceled on SIGINT/SIGTERM (forge cli.Run gains
fang.WithNotifySignal); previously Ctrl-C killed the process via Go's
default handler. exitcode.Interrupted (130) is also available for commands
that surface context.Canceled.Co-Authored-By:Claude Sonnet 4.6 noreply@anthropic.com
🐛 Bug Fixes
-
(deploy) Log purge-plan errors instead of discarding them - (e26a80f) by @bchatard
When PurgePlan failed, purgePlan was set to nil silently, so the dry-run preview
and JSON purged list went empty with no indication why. Log the error via the
operator-debug logger before discarding. The path is defensive (releases_root is
valid by the purge step), so no dedicated test. Part of M20 (S10).Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
(transport) Bound agent downloads with an http.Client timeout - (5832a68) by @bchatard
downloadAgent and verifyChecksum used http.Get with the default client (no
timeout), so a hung GitHub could stall a deploy indefinitely. Route both through
a shared http.Client with a 120s timeout. Part of M20 (S9).Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
(tui) Raise event-stream line limit above 64 KB - (7bad1af) by @bchatard
A hook_output event carrying a large payload (e.g. a verbose migration log)
exceeded bufio.Scanner's 64 KB default token, which dropped the line and aborted
ForwardStream with 'token too long' — truncating the rest of the deploy stream.
Raise the scanner buffer to 16 MB. Part of M20 (S7).Co-Authored-By:Claude Fable 5 noreply@anthropic.com
🚜 Refactor
-
(cmd) Extract shared config-resolution and hook helpers - (59ba701) by @bchatard
The flat-detect -> merge -> validate block was copied across deploy, release
list/activate/rollback; writeTempFlatConfig, the env-map helper, and the
interactive-confirm helper were each duplicated across the cmd and release
packages. Consolidate them: cmdutil.ResolveMergedConfig and WriteTempFlatConfig,
hooks.OSEnv, and tui.InteractiveHookConfirm. Behaviour-preserving — verified by
the full unit suite plus local-deploy, client-deploy, and release E2E tests.
Net -66 lines. Part of M20 (S1).Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
(deploy) Collapse the 8 hook-stage blocks into one helper - (eee16f7) by @bchatard
The deploy pipeline repeated the same ~7-line hook block eight times (run the
stage's hooks, wrap a failure with the stage name, render the step line). Extract
a single runHookStage closure and call it per stage. Behavior-preserving — verified
by the unit deploy tests and the hook-pipeline integration tests (firing, ordering,
JSON and error events). Part of M20 (S2).Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
(hooks) Use errors.AsType to match ssh.go - (0d599a7) by @bchatard
ssh.go uses the Go 1.26 errors.AsType generic; runner.go used the classic
errors.As. Standardize on AsType (the modernize linter's preference) so the two
error-inspection sites read the same. No behavior change. Part of M20 (S4).Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
(transport) Chmod the agent via SFTP, not a remote shell - (2c64e60) by @bchatard
Making the uploaded agent executable shelled out 'chmod +x' over SSH. Add
Staging.Chmod (delegating to the existing SFTP.Chmod) and use it instead — one
fewer remote shell exec and no command string to quote. Verified by the deploy
and release E2E tests (the agent must be executable to run). Part of M20 (S8).Co-Authored-By:Claude Fable 5 noreply@anthropic.com
📚 Documentation
-
(testing) Allow t.TempDir in deployer FS-logic tests - (f476cd6) by @bchatard
Constraint #1 forbade t.TempDir(), but the deployer's own logic tests
(deploy_logging_test, deploy_hookoutput_test, deploy_exitcode_test) need a real
local filesystem — afero is banned precisely because it can't model the symlinks
the deployer relies on. Carve a narrow exception rather than leave three files
silently contradicting a hard constraint. Co-Authored-By:Claude Fable 5 noreply@anthropic.com
🧪 Testing
-
(cmd) Assert hook output is visible over SSH (S11) - (2789e1d) by @bchatard
Extract the deploy-over-SSH scaffolding (connect, stage, exec agent, forward the
JSON stream) into testutil.DeployOverSSH so it isn't duplicated; refactor the
existing client deploy E2E onto it. Add TestDeployCmd_HookOutputVisibleOverSSH:
deploy over SSH with a post_extract hook that echoes a marker and assert the marker
appears in the rendered output — the end-to-end proof of the M19 hook-output fix
that was deferred. Closes M20 (S11).Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
(cmd) Confirm deploy error via stderr, not the exec exit code - (236e130) by @bchatard
TestPlainOutput_NoColorCodes/TestNoColor_SuppressesColors asserted the deploy
errored via the container-exec exit code. testcontainers can read that code as 0
for a fast-exiting process under CI load (the attach EOFs before the exit status
is finalized), so the tests flaked on CI even though 'bifrost deploy' exits 1
deterministically. Confirm the error via non-empty stderr (read reliably to EOF)
instead. The ANSI color-code assertions — the tests' real purpose — are unchanged.Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
(testutil) Unique known_hosts path to fix CI host-key race - (bca2aa2) by @bchatard
WriteKnownHosts wrote to a fixed /tmp/bifrost-test-known-hosts. The CMD and
RELEASE integration packages run in parallel and both write it, so under CI load
one clobbered the other's host key, failing the release E2E with 'knownhosts: key
is unknown'. S11's added SSH test raised the collision odds enough to trip it.
Write a unique per-call path instead. Full integration suite green (252).Co-Authored-By:Claude Fable 5 noreply@anthropic.com
Commit Statistics
- 15 commit(s) contributed to the release.
- 1 day(s) passed between the first and last commit.
- 15 commit(s) parsed as conventional.
- 0 linked issue(s) detected in commits.
- 1 day(s) passed between releases.