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