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

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

```sh
cargo install semver-sync
```

From a checkout:

```sh
cargo install --path .
```

## Use

```sh
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:

```sh
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

```sh
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`

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

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

```sh
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

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

## Development

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