Skip to content

v0.21.0

Choose a tag to compare

@github-actions github-actions released this 14 Jun 10:19
Immutable release. Only release title and notes can be modified.
v0.21.0
b78cda7

🚀 Features

  • (transport) Verify agent binary checksum before caching - (c332ddb) by @bchatard

    The downloaded agent runs on the target (often via sudo) but was cached and
    executed with no integrity check. verifyChecksum now fetches the release
    checksums.txt and verifies the binary's SHA-256 before the cache write, failing
    on a mismatch or a missing entry for the platform asset. --agent-binary still
    bypasses both download and verification.

    Part of M19 (agent binary integrity verification). ADR-0011 updated.

    Co-Authored-By:Claude Fable 5 noreply@anthropic.com

🐛 Bug Fixes

  • (deploy) Emit hook output as JSON events in --output json mode - (a3675e2) by @bchatard

    In JSON mode the deployer wrote raw hook stdout/stderr to the same stream as the
    JSON events, so over SSH the client's NDJSON parser silently dropped all hook
    output (migrations, build logs) and a local --output json deploy produced invalid
    NDJSON. A new tui.HookOutputWriter wraps each write as a hook_output event;
    ForwardStream renders it; the deployer routes hook output through it in JSON mode
    and writes raw to stdout as before in human/plain mode. A WithRunner seam lets a
    fake runner drive the deployer test without real shell execution.

    Part of M19 (hook output over SSH / JSON mode).

    Co-Authored-By:Claude Fable 5 noreply@anthropic.com

  • (deploy) Classify hook and deploy runtime failures as exit 3 - (11755f3) by @bchatard

    Hook, template, extraction, link, symlink, and purge failures returned bare
    errors that forge resolved to exit 1 (Usage), but specs 01/03/05 all document
    exit 3 (Runtime) for these — a contract CI/orchestration relies on. The hooks
    runner now wraps hook-exec and template failures as Runtime, the deployer wraps
    its extract/link/symlink/purge/release-dir failures, and emitError reports the
    resolved code in the JSON error event instead of a hardcoded 1. cmderr re-exports
    Wrap/Resolve. The specs already matched; only the code changed.

    Part of M19 (exit-code classification).

    Co-Authored-By:Claude Fable 5 noreply@anthropic.com

  • (transport) Download agent as a raw binary, not a tar.gz - (ec289ad) by @bchatard

    goreleaser ships raw binaries (formats: [binary], asset
    bifrost_{version}{os}{arch}), but the downloader requested a .tar.gz and
    re-prefixed an already-v-prefixed version, so every agent auto-download 404'd
    and only --agent-binary worked. downloadURL now targets the raw asset and
    strips a leading v; downloadAgent caches the bytes directly (no gzip/tar).

    Part of M19 (agent binary download). ADR-0011 and spec 07 updated to match.

    Co-Authored-By:Claude Fable 5 noreply@anthropic.com

  • (transport) Shell-quote interpolated values in remote commands - (689e901) by @bchatard

    Agent invocation strings interpolated env, app, release name, and the artifact
    basename straight into a remote shell via session.Run, unquoted — they broke on
    spaces and were injectable from semi-trusted filenames. A new ShellQuote helper
    single-quote-escapes each value; it is now applied at every interpolation site in
    client_deploy, client_activate, client_list, client_rollback, and staging's rm -rf
    and chmod calls. An integration test proves a space/semicolon payload round-trips
    literally over a real SSH shell without injecting.

    Part of M19 (quote remote command arguments).

    Co-Authored-By:Claude Fable 5 noreply@anthropic.com

📚 Documentation

  • (tasks) Add M19 hardening and M20 cleanup milestones - (bdd04a4) by @bchatard

    Record the findings from the full code/doc review as roadmap tasks. M19
    covers the critical agent-download fix, command-injection hardening, agent
    integrity verification, CI integration tests, the hook-output protocol, and
    exit-code classification. M20 collects the minor cleanups (S1-S10).

    Co-Authored-By:Claude Fable 5 noreply@anthropic.com

  • (tasks) Record the container-readiness fix under M19 - (03b248c) by @bchatard

    The integration-test CI job (M19) exposed and led to fixing the SSH container
    readiness race (commit e837a96). Record it on the roadmap rather than only in
    session memory, per the project's roadmap discipline.

    Co-Authored-By:Claude Fable 5 noreply@anthropic.com

  • (testing) Require container readiness to gate on a setup marker - (639c94d) by @bchatard

    Record the forward-facing rule from the M19 container-readiness fix: SSH
    integration containers must signal readiness via a setup-complete marker, not
    wait.ForListeningPort (which the Docker port proxy can report open before the
    startup script finishes). Complements the inline comment in ssh_container.go.

    Co-Authored-By:Claude Fable 5 noreply@anthropic.com

  • Reconcile specs and rules with shipped behaviour - (1ed87d6) by @bchatard

    Fixes from the full review: spec 05 no longer claims interactive hooks are
    unsupported in v0 (they prompt/skip); specs/README indexes specs 07 and 08; the
    --config default in spec 03 and coding.md now states the real resolution chain;
    spec 07's release list/activate/rollback agent invocations match the actual client
    commands (--env/--app passed, activate uses --no-confirm); versions.md covers
    M11-M19; and the .goreleaser.yml ADR reference is qualified as a forge ADR.

    Part of M19 (documentation reconciliation). Moving the working-doc artifacts out
    of docs/ is deferred pending confirmation.

    Co-Authored-By:Claude Fable 5 noreply@anthropic.com

  • Move M18 working-doc artifacts to .claude/plans - (08d7b6e) by @bchatard

    The dated hook-lifecycle plan and design doc were working artifacts cluttering
    docs/ (the plan ~2,000 lines, the design doc unindexed in specs/README). Move
    them to .claude/plans/ as m18-hook-lifecycle.md and m18-hook-lifecycle-design.md,
    matching the existing m0/m2 plan artifacts and the descriptive-naming convention.
    The durable links to the design notes (M18 roadmap entry, ADR-0012) are repointed;
    docs/ now holds only specs, ADRs, tasks, and the sample config.

    Completes M19 (documentation reconciliation).

    Co-Authored-By:Claude Fable 5 noreply@anthropic.com

🧪 Testing

  • (testutil) Wait for container setup to finish, not just the port - (e837a96) by @bchatard

    wait.ForListeningPort('22/tcp') can return before the container's setup script
    finishes: Docker's port proxy on Linux binds the mapped port early, so tests ran
    container-side setup (e.g. 'chown deploy:deploy') before 'useradd deploy' had run,
    failing with exit 1. macOS Docker networking hid this; the M19 CI integration job
    exposed it on GitHub. The setup script now touches /tmp/.setup-complete after all
    setup (just before exec sshd), and readiness waits on that marker instead — so the
    deploy user and keys are guaranteed present before any test runs.

    Co-Authored-By:Claude Fable 5 noreply@anthropic.com

⚙️ Miscellaneous Tasks

  • Run the integration test suite in CI - (98d8254) by @bchatard

    The reusable forge go-ci workflow runs go test ./... without -tags integration,
    and the build job only compiled, so the entire testcontainers suite (real deploy,
    SSH transport, multi-server) never ran in CI — which is how the broken agent
    download shipped. A new integration job runs go test -tags integration ./... on a
    Docker-enabled ubuntu runner. Verified locally: 238 tests pass.

    Part of M19 (run integration tests in CI).

    Co-Authored-By:Claude Fable 5 noreply@anthropic.com

Commit Statistics

  • 12 commit(s) contributed to the release.
  • 1 day(s) passed between the first and last commit.
  • 12 commit(s) parsed as conventional.
  • 0 linked issue(s) detected in commits.
  • 1 day(s) passed between releases.