Interactive. Run locally. Produces a release PR — never publishes, never writes to main.
otf-release version [--dry-run] [--first-release]
| Flag | Effect |
|---|---|
--dry-run |
Compute and print the plan (summary), write nothing. |
--first-release |
Permit publishable packages with no prior tag matching tag_format. Curated mode still requires release notes for packages you want to release. |
Implemented in crates/core/src/version.rs.
- Discover packages via the adapter; build the internal dependency graph.
- Strict preflight (preflight.md) — abort the entire run on any violation, before mutating anything. All violations are printed at once.
- Parse
[Unreleased]from the configured changelog scope; flag packages with content as pending. - Prompt — multi-select the packages to release, then pick a bump (major / minor / patch) per selected package.
- Cascade — for each bumped package, walk its dependents. Each dependent's bump is
adapter.dependent_bump(dep_bump, kind). This is transitive (every newly bumped dependent is re-fed into the walk) and takes the max bump when a package is reached by multiple paths. The cascade terminates at private packages — they are graph leaves and are never versioned or published. See graph. - Compute new versions and the internal dependency-range updates
(
adapter.format_range). - Plan — render the computed version and range changes.
--dry-runstops here and writes nothing. - Branch — assert a clean working tree and that you are on
main, thengit checkout -b release/<date-or-versions>. Release changes are never committed ontomaindirectly (CI publish triggers onmain). - Apply on the branch:
adapter.write_versionfor every affected publishable package.adapter.update_dep_rangefor every changed internal range — including private apps (so they stay buildable) — but private apps get no version bump and no publish.- Changelog rewrite: move
[Unreleased]→## [x.y.z] - YYYY-MM-DD, leaving a fresh empty[Unreleased]. Packages that were auto-bumped only (no curated notes) get the stub_Dependency updates._. See changelog-format.md. adapter.update_lockfile— refresh the lockfile in the same commit, or a CI install will drift.
- Final review / confirm — print the actual
git diff --stat, then ask whether to commit, push, and open the PR. On cancel, generated release-branch changes are discarded and the command returns to the original branch. - Commit (
chore(release): …), push, and open a PR viagh.
Merging that PR is what triggers CI publish.
The plan is shown by --dry-run and is included in the final review:
Version Bumps (Direct & Indirect):
Package | Old | New | Reason
@opentf/core | 1.2.0 | 2.0.0 | major, selected
Internal Range Updates:
Consumer | Dependency | Old | New | Notes
playground | @opentf/core | ^1.2.0 | ^2.0.0 | private app (not published)
Changed Files:
Cargo.toml | 2 +-
CHANGELOG.md | 8 +++++++-
Three blocks: explicitly selected packages, auto-bumped dependents (with the reason), and internal range updates (private apps flagged "range updated, NOT published").
- No release commit is created and nothing is pushed before the final diff confirmation.
- If the user cancels at the final confirmation, generated release changes are discarded.
- Private apps: ranges updated, never bumped or published.
- The working tree must be clean and on
main; all release writes land onrelease/*. - Preflight runs to completion (and can abort) before the first prompt.
- preflight.md — the gate that runs in step 2.
- changelog-format.md — the rewrite rules in step 9.
- publish.md — what the merged PR triggers.