Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions apps/cli/src/cli/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ import { makeStyler } from "./styler.ts";
// `dv version` per specs/cli.md § dv version. Consumes pending Records
// and (per Package) applies the aggregated Bump, rewrites the manifest
// Version, prepends a CHANGELOG section, deletes the consumed Records,
// and stages everything into one commit (the Release PR).
// and stages everything into one commit (the version-bump commit; the
// Release PR in a review-gated workflow).
//
// Plan-then-execute is the spine (specs/language.md Algebra §7): the
// same buildVersionPlan call powers `dv status`, `dv version --dry-run`,
Expand Down Expand Up @@ -147,7 +148,8 @@ export interface CascadedUpdate {
* Runs `dv version`: consumes pending Records and, per Package, applies the
* aggregated Bump, rewrites the manifest Version, prepends a CHANGELOG section,
* cascades dependent constraints, deletes the consumed Records, and stages
* everything into one commit (the Release PR). Plan-then-execute is the spine
* everything into one commit (the version-bump commit; the Release PR in a
* review-gated workflow). Plan-then-execute is the spine
* (specs/language.md Algebra §7): the same `buildVersionPlan` call powers
* `dv status`, `--dry-run`, and the real run, with the dry-run path invoking
* zero write-side plugin Ops and touching nothing on disk.
Expand Down
3 changes: 2 additions & 1 deletion apps/cli/src/subtools/git/commit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import type { GitSign } from "../../domain/config.ts";
import { DvError } from "../../domain/errors.ts";

// Creates a commit from currently-staged changes. `dv version` produces
// exactly one such commit (the Release PR) per specs/cli.md § dv version.
// exactly one such commit — the version-bump commit (the Release PR in a
// review-gated workflow) per specs/cli.md § dv version.
//
// Signing follows the git.sign config option (specs/config-format.md §
// git.sign): `"auto"` honors git's own commit.gpgsign config (no flag
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/src/subtools/git/mod.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Public surface of the git Substrate (specs/design.md § Capability
// decomposition: git is a substrate, not a capability of its own). Used
// by `dv version` to gate the run, stage the changes, and produce the
// Release PR commit.
// version-bump commit (the Release PR in a review-gated workflow).

export { type AssertCleanTreeArgs, assertCleanTree } from "./clean-tree.ts";
export {
Expand Down
73 changes: 57 additions & 16 deletions apps/docs/content/guides/cut-a-release.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,43 @@
# Cut a release

This guide walks through running a real release end-to-end, including
the things you'll actually do in practice — previewing first, gating
on review, handling failures. If you've never used dv, start with the
the things you'll actually do in practice — previewing first, handling
failures, and (if your team wants it) gating the bump on review. If
you've never used dv, start with the
[Getting started tutorial](/getting-started) first; this assumes you
already know what Records are.

Skim the page top-to-bottom the first time. After that, the **TL;DR**
at the top is what most days look like.

The default workflow is **release-on-merge** (plain GitHub Flow):
merging a feature PR to `main` runs both phases automatically, because
the bump is derived from the Records that PR carried. Teams that want a
human to approve the bump before it lands can route the `dv version`
commit through a **Release PR** instead — same two commands, one extra
review gate. This guide shows release-on-merge first, then the Release
PR variant.

## TL;DR

```sh
# 1. Verify the plan
$ dv status

# 2. Land the Release PR
# 2. Bump versions + write CHANGELOGs
$ dv version --dry-run # preview
$ dv version # for real (one commit)
# → review, merge
$ dv version # for real (one commit, straight to main
# under release-on-merge)

# 3. Tag + publish
$ dv release --dry-run # preview
$ dv release # for real
```

Three commands, two phases, plus the merge. The rest of this page is
the *why* and the edge cases.
Three commands, two phases. Under release-on-merge both phases run in
CI on merge to `main`; if you gate behind a Release PR, the `dv version`
commit is reviewed and merged before `dv release` runs. The rest of this
page is the *why* and the edge cases.

## Step 1: see what's pending with `dv status`

Expand Down Expand Up @@ -100,7 +111,7 @@ The `--json` shape is documented in the [Plan schema](https://github.com/ben-lai
and is stable across the three commands (`dv status`, `dv version
--dry-run`, `dv release --dry-run`).

## Step 3: land the Release PR with `dv version`
## Step 3: bump versions with `dv version`

```sh
$ dv version
Expand All @@ -125,11 +136,16 @@ What dv did:
7. **Refreshed lockfiles** via the plugin's `finalize` op.
8. **Staged everything** into one commit.

That commit is the **Release PR**. Open it as a PR, review it, merge it.
Under **release-on-merge**, this commit lands on `main` as part of the
CI job that ran on the merge — there's no separate step. If you prefer
to gate the bump on review, that same commit becomes a **Release PR**:
open it as a PR, review it, and merge it before `dv release` runs (see
[the Release PR variant](#the-release-pr-variant) below).

### Reviewing the Release PR
### What to review (either workflow)

Things to look at:
Whether the bump commit lands automatically or you review it as a
Release PR, these are the things worth a look:

- **The CHANGELOG entries** read as you'd want them to. Each Record's
`notes` field became one CHANGELOG bullet. If the bullet reads
Expand Down Expand Up @@ -191,12 +207,37 @@ right call when a package was renamed and old Records should resolve
to the new name. See [Packages and plugins](/concepts/packages-and-plugins)
for more on the rename ledger.

## Step 4: the merge
## Step 4: how the bump reaches `main`

After `dv version`, the manifests and CHANGELOGs are committed; the git
tags don't exist yet. How that commit gets to `main` is your team's
call — dv works the same either way.

- **Release-on-merge (default).** The `dv version` commit is produced by
the CI job running on merge to `main`, so it's already on `main` — the
feature PR you merged *was* the review. `dv release` runs next in the
same job. There's no second PR. See the
[CI integration guide](/guides/ci-integration) for the workflow.
- **Release PR (variant).** If you want a human to approve the bump
itself, route the `dv version` commit through a PR — see below.

### The Release PR variant

Some teams want the version bump and CHANGELOG reviewed before anything
is tagged. To do that, run `dv version` so its commit lands on a branch,
open that branch as a PR, and merge it before running `dv release`:

```sh
$ dv version # one commit on a release branch
# → open as a PR, review the bump + CHANGELOG, merge to main
$ dv release # only after the Release PR merges
```

Nothing dv-specific here. Whatever your team's PR workflow is —
review, approval, merge button — applies. After the Release PR
merges to `main`, the manifests and CHANGELOGs are committed; the
git tags don't exist yet.
The commit `dv version` produces is the **Release PR** in this workflow.
Nothing dv-specific happens at merge time — whatever your team's PR
workflow is (review, approval, merge button) applies. Once it merges to
`main`, proceed to `dv release`. (`--no-commit`, below, is handy if you
want to shape the commit yourself before opening the PR.)

## Step 5: tag + publish with `dv release`

Expand Down
5 changes: 3 additions & 2 deletions specs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,9 @@ entries (and HISTORY entries when `history.enabled: true` in config),
updates dependents' constraints (constraint-only cascade), and deletes the
consumed Records. Auto-commits the result by default with a templated
message (CC-shaped — an emission convention only; `dv` never parses
contributor commits). **Does not push.** The commit it produces is the
Release PR.
contributor commits). **Does not push.** The commit it produces lands on
`main` under release-on-merge, or is reviewed as a Release PR when a team
gates the bump.

Halts on an Unresolved Reference (a Record pointing at a Package that no
longer exists with no Rename edge); `--prune` drops such Records instead.
Expand Down
12 changes: 8 additions & 4 deletions specs/language.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,11 @@ output; distinct from the Records that feed it.
computed before any mutation. The single artifact shared by `dv status`
and `--dry-run`. *Not: preview, diff.*

**Release PR** — the reviewable commit `dv version` produces, merged before
`dv release` runs. *Not: version commit (in user-facing text).*
**Release PR** — the reviewable commit `dv version` produces. One of two
workflows: under **release-on-merge** (the default) it lands on `main`
automatically as part of the merge; routed through a **Release PR** it is
reviewed and merged before `dv release` runs. The term names the commit in
the latter, gated workflow. *Not: version commit (in user-facing text).*

---

Expand Down Expand Up @@ -226,8 +229,9 @@ version : Records → ΔManifests + ΔCHANGELOGs + Commit
release : Manifests × Tags → ΔTags + Publishes
```

This is why the phases can be separated by a PR review (the Release PR) and
why each is independently runnable, dry-runnable, and resumable.
This is why the phases can run back-to-back on merge (release-on-merge) or
be separated by a PR review (the Release PR), and why each is independently
runnable, dry-runnable, and resumable.

### 7. Plan determinism

Expand Down
7 changes: 5 additions & 2 deletions specs/v1-scope.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ Not v1, but the architecture leaves room for each:
- **CC-accelerator affordances** — `dv record from-commit <sha>` /
`from-range <range>` to draft Records from Conventional Commits
history. See `design.md` § Records over commit messages.
- **GitHub Actions companion** — bot/action that maintains a Release PR.
- **GitHub Actions companion** — release-on-merge CI that runs both phases
on merge to `main` (the default), or a bot/action that maintains a Release
PR for teams gating the bump on review. See `guides/ci-integration.md`.

## Non-goals (probably forever)

Expand Down Expand Up @@ -158,7 +160,8 @@ A user can:
2. Write a small shell-script plugin per ecosystem (or use whatever
built-ins ship by then).
3. Have contributors file records via `dv add`.
4. Get a clean Release PR via `dv version`.
4. Get a clean version-bump commit via `dv version` — landed on `main` by
release-on-merge CI, or reviewed as a Release PR first.
5. Cut tags and trigger publishes via `dv release`.

With SemVer adherence enforced and per-package CHANGELOGs maintained.
Expand Down
7 changes: 5 additions & 2 deletions specs/walkthrough.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,14 @@ $ dv version
```

The consumed Records are gone from `.dv/records/`, and the whole thing
is one commit — the **Release PR**. Open it, review it, merge it.
is one commit. Under **release-on-merge** (the default workflow) that
commit lands on `main` automatically; teams that gate the bump on review
open it as a **Release PR** and merge it first.

## 6. Release (phase two)

After the Release PR merges, cut Tags and publish. `dv release` is
Once the bump commit is on `main` (directly under release-on-merge, or
after the Release PR merges), cut Tags and publish. `dv release` is
**stateless**: it compares each Package's current Version to existing Tags
and acts on whatever is untagged.

Expand Down