semver-sync 0.1.2

Keep one version across every crate in a cargo workspace; bumps are checked as docker tags before any manifest is written.
semver-sync-0.1.2 is not a library.

semver-sync

One version for every crate in a cargo workspace, moved by one command. These versions become docker tags, so they are checked as tags before anything is written.

Install

cargo install semver-sync

From a checkout:

cargo install --path .

Use

semver-sync alias   # install the shorter `ss`, then: source ~/.zshrc
ss                  # what every crate is at
ss patch            # 0.1.0 -> 0.1.1
ss minor            # 0.1.0 -> 0.2.0
ss major            # 0.1.0 -> 1.0.0
ss tag rc           # 0.1.0 -> 0.1.0-rc.0, then rc.1, rc.2 on the same tag
ss set 1.4.0-rc.0   # one exact version
ss update           # reinstall this tool from crates.io, cargo decides if it is newer
ss version          # the version of semver-sync itself
ss config           # write the config file at the repo root
ss config --global  # write it in the user config dir instead

Run it from anywhere inside the workspace; the workspace is found through cargo. Every command that writes takes --dry-run, which prints what would move and then checks the manifests were left alone:

ss patch --dry-run

What a write touches

  • [package] version and [workspace.package] version in every manifest that holds one, including a workspace root that is a package itself.
  • version = "..." pins on dependencies that point at another member of the workspace by path — the pins that stop cargo resolving when only the package versions move. Pins on crates.io dependencies are left alone.
  • Cargo.lock, by asking cargo to resolve the workspace again once the manifests are written. If it does not resolve, the manifests are already on disk: git diff -- '*Cargo.toml' says what moved.

Crates that inherit version.workspace = true need nothing of their own; cargo reports the version they resolve to, and that is what ss lists.

A level bump ignores a tag on the way: 1.2.3-rc.4 with ss patch gives 1.2.4. ss tag rc keeps the core version and starts a series — 1.2.3 gives 1.2.3-rc.0, and the same command again gives 1.2.3-rc.1. A different tag starts its own series: 1.2.3-hotfix.0.

A version has to survive as a docker tag: start with a letter or a digit, then A-Z a-z 0-9 . _ -, with no ... That rules out build metadata such as 1.2.3+g1a2b.

Configuration

ss config           # semver-sync.toml at the repo root
ss config --global  # semver-sync.toml in the user config dir

The first file that exists is the one read, so a repo can say something different from the machine it sits on:

  1. the repo root: semver-sync.toml, then .semver-sync.toml
  2. $HOME: semver-sync.toml, then .semver-sync.toml
  3. the user config dir ($XDG_CONFIG_HOME, else $HOME/.config): the same two names

The repo root is the one cargo resolves — the root every other command works on. A file that does not parse, or that names a key which is not a setting, is an error where it lies: reading the file behind it, or walking past a misspelled key, would run a bump under settings nobody asked for.

The file ss config writes names every setting below at the value a run reads when no file is there at all, and it never replaces a file already there, under either name.

git-tag

git-tag = false    # the default: a write ends at the manifests

Turn it on and a write finishes the release in git as well:

git tag -a v1.4.0 -m v1.4.0
git push
git push --tags

That runs once the manifests and Cargo.lock are written; a --dry-run prints the tag it would make instead. Each step waits for the one before, so a push that cannot reach the remote reports the tag as local rather than letting a bump pass for a release. The tag lands on the commit HEAD stands on, not on one already holding the versions just written — commit what the bump changed first when the tag has to carry them.

Completions

ss completions zsh > ~/.zfunc/_semver-sync
compdef _semver-sync ss    # the script binds `semver-sync`; this binds the alias

Development

cargo test
cargo clippy --all-targets -- -D warnings
cargo fmt --check