Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Releasing

make ci                          # fmt + clippy + test + security + binary-check
make bump VERSION=x.y.z
make release VERSION=x.y.z
make release-sha VERSION=x.y.z   # after the tag has actually built

The order, and why it is not negotiable

Cut the changelog first, then bump.

bump_version.sh does not touch CHANGELOG.md, and build.rs counts what is still uncut in the tree it compiles: the bullets under ## [Unreleased] plus the fragments in changelog.d/. Tag without folding them and the released binary tells every user [N UNRELEASED] … cut a tag. It accuses itself.

So:

make changelog-cut VERSION=x.y.z   # folds changelog.d/ into ## [x.y.z] - <date>
git commit -am "docs(changelog): cut x.y.z"
make bump VERSION=x.y.z

A correctly cut build prints omni vx.y.z [AHEAD/RC] with no UNRELEASED line. Verify that before pushing the tag.

The half of that line to trust afterwards is the missing UNRELEASED, not the label. guard::update::get_status caches the newest known release in ~/.omni/update_cache.json for 14400 seconds, so for four hours after a tag a machine that ran omni doctor beforehand still holds the previous version and reports Ahead whatever it is running. Observed on 0.7.5, where the freshly installed release printed [AHEAD/RC]. build.rs computes the unreleased count from the tree with no cache, so that half is always current; delete the cache file if you want the label to mean something.

Day to day, the entry goes in changelog.d/<issue>.<section>.md as the work merges, not into CHANGELOG.md and not at tag time. One file per entry means two branches never write the same path, which is what stopped every parallel branch conflicting on ## [Unreleased]. The format is Keep a Changelog and SemVer, and the entries here are unusually detailed on purpose: each states the measured evidence, the wrong number that was published, and the mechanism. A one-line entry is a regression in that file’s quality.

The first cut needed one manual tidy and it is done. 0.7.5 folded three fragments beside seven bullets written into ## [Unreleased] before the convention existed, and arrived with two ### Changed and two ### Fixed under one version heading. Merging those four into two was the only hand edit. Check a cut by word count rather than by eye: 2,252 words across the old section plus the fragments, 2,252 in the folded section. A reordering that drops a bullet body looks correct in a heading-level diff.

CI green does not mean the release will build

The 0.6.2 tag produced no binaries at all. release.yml asked for stable per cross target while rust-toolchain.toml pinned a version, so every cross-compile died with can't find crate for core before compiling a line. ci.yml stayed green throughout, because it only builds host-native.

The fix is that cross targets belong in rust-toolchain.toml, and its targets list has to stay in sync with the release matrix.

After tagging, watch the release workflow actually produce artifacts before running make release-sha or announcing anything.

Things that look like failures and are not

omni-release.sh ends in an interactive read -p, so an automated run has to pipe echo y | into it.

It pushes main and the tag together. main is branch-protected, and a maintainer token bypasses it: the push prints “Changes must be made through a pull request” and succeeds anyway with rc=0. That line is not an error.

The Homebrew step

update_homebrew_sha.sh pushes to two repositories: the tap, and omni.rb back to main. Check the tap clone is clean and synced with its remote first, or the run aborts partway and leaves the formula half updated.

Afterwards, verify the formula’s SHAs against the release’s published SHA256SUMS rather than trusting the script’s own success line, then confirm:

brew info fajarhide/tap/omni      # expect: x.y.z → stable <new>

Before merging anything into a release

CI green is not review-clean. Read the review comments, automated and human, validate each one against the code rather than assuming the reviewer is right or wrong, and fix or reply. A green pipeline says the tests passed. It says nothing about a correctness bug a reviewer flagged.

Branch shape

One branch per batch, not per issue. N parallel branches cost N full CI runs of about eleven minutes each, serialised. Batch a lane into one branch, one commit per issue, one pull request with several Closes #N lines.

Split only when a reviewer would genuinely need them apart, or when one is risky enough to be reverted alone.

That conflict used to be CHANGELOG.md, every time. It is gone: entries are files in changelog.d/ now, and two branches never write the same path. What remains is the CI cost, which is why batching still pays.

Closes #N must be in the pull request body before the merge. GitHub evaluates the keyword at merge time only; adding it afterwards does nothing, silently.