Expand description
Cut-time execution of the sealed version-bump phase (release-rust-workspace- multicrate facet 2/3) — the effectful half whose pure text transforms live in
crate::release::bump.
apply_bump runs inside the coordinator’s clean checkout of the sealed commit
(release-cut-clean-checkout), before the build barrier, so every later phase
builds and publishes the bumped tree. It:
- sets
[workspace.package] versionin the root manifest; - rewrites each sealed intra-workspace
=-pin (verifying the exact old value in the tree — fail closed on zero/multiple,crate::release::bump::rewrite_pin); - refreshes
Cargo.lock(cargo update --workspace); - finalizes the CHANGELOG (
[Unreleased]→ a dated section) when the contract’s changelog mode asked for it; - runs any contract-declared
bump_hook(see the execution contract below); - commits the edits and returns the bump commit sha — the commit the tag points at (not the pre-bump sealed HEAD).
Every step fails closed: a failed edit, lockfile refresh, hook, post-hook
validation, or commit aborts the cut with a BumpExecError before the build
barrier — nothing external has happened yet (no publish, no tag).
§File I/O
Like the homebrew adapter, this reads and
writes manifests/CHANGELOG with std::fs directly (a manifest edit has no CLI to
route through CommandRunner); all writes are confined to the throwaway sealed
checkout the coordinator just materialized. Process effects (cargo, the hook,
git) go through the injected CommandRunner, so the executor is unit-testable
against a real temp checkout + a recording fake runner.
§bump_hook execution contract (supply-chain surface, schema.rs)
A declared bump_hook is arbitrary code the engine runs during the release,
equivalent in trust to a build.rs the cut already compiles. Its purpose is to
regenerate version-embedding artifacts (test snapshots that embed the version) so
they do not go stale on the bump and red CI. The contract this executor honors:
- Invocation:
sh -c "<hook>"with the hook string passed as a single, verbatim argv element — no dynamic data (version, package names) is ever interpolated into it, so there is no shell-injection surface from cut-time data (schema.rs:399). The hook is surfaced verbatim as a plan-time reviewer warning, so an approver has seen exactly what runs. - Working directory: the sealed checkout root.
- Environment: the cut’s ambient environment (inherited), the same trust
boundary as a
build.rs. Secrets the cut carries (registry tokens) are reachable — this is why the hook is an eyes-on supply-chain surface, not a sandbox. - Permitted effect: regenerating derived files. Post-hook validation re-reads
the workspace manifest and rejects the cut if the hook reverted or altered the
version bump (
BumpExecError::HookViolatedVersion) — the one invariant that must survive, since a hook that changed the version would publish the wrong number. A full permitted-path allowlist is intentionally not enforced (snapshot regen writes an open-ended set of test files); the version invariant is the load-bearing check. - Failure: a non-zero exit fails the cut closed (
BumpExecError::Hook). - Timeout: the
CommandRunnerport carries no timeout today, so the hook runs to completion (a CI-level job timeout is the backstop). A first-class per-command timeout is a tracked follow-up.
Structs§
- Bump
Outcome - The outcome of an applied bump: the commit the edits landed in (the tag target) and the CHANGELOG effective date (journalled for resume reuse).
Enums§
- Bump
Exec Error - Why the cut-time bump could not be applied. Every variant is a fail-closed refusal before the build barrier — no publish or tag has happened.
Functions§
- apply_
bump - Apply the sealed
bumpinsidectx.repo_root(the coordinator’s clean checkout), committing the edits and returning theBumpOutcome.effective_dateis theYYYY-MM-DDCHANGELOG date (freshly computed on a first run, or the journalled date on resume so it never re-dates). - civil_
date - The UTC
YYYY-MM-DDcivil date for a Unix timestamp — the CHANGELOG effective date the bump finalizes under. A self-containeddays → (y, m, d)conversion (Howard Hinnant’scivil_from_days) so the release path needs no chrono dependency; the injectedClocksupplies the timestamp, so it is deterministic under test.