diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..0b17e3c --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,15 @@ +# `Cargo.toml` sets `publish = ["kellnr"]`, so `cargo publish` targets the +# private registry and refuses crates.io. Cargo needs that registry's index +# declared to resolve the name. +# +# Committed rather than left to a per-user `~/.cargo/config.toml` so the repo +# is self-contained: a fresh clone, a new machine, or CI would otherwise fail +# with +# +# error: registry index was not found in any configuration: `kellnr` +# +# Index URL only — it is not a secret. Publish tokens live in +# `~/.cargo/credentials.toml` (per-user, never committed) or, in CI, in +# `CARGO_REGISTRIES_KELLNR_TOKEN`. +[registries.kellnr] +index = "sparse+https://crates.aceofba.se/api/v1/crates/" diff --git a/Cargo.toml b/Cargo.toml index 2774717..24815fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,11 @@ edition = "2024" rust-version = "1.85" description = "TrueSkill Through Time: Bayesian skill rating that tracks how skill evolves over time, via Gaussian message passing" repository = "https://git.aceofba.se/logaritmisk/trueskill-tt" +authors = ["Anders Olsson"] +# Publishing is restricted to the private kellnr registry; this also makes +# an accidental `cargo publish` to crates.io a hard error rather than a +# irreversible mistake. Index is declared in `.cargo/config.toml`. +publish = ["kellnr"] readme = "README.md" keywords = ["trueskill", "rating", "bayesian", "elo", "skill"] categories = ["algorithms", "science", "game-development"] diff --git a/Justfile b/Justfile index 5a22ca3..aea461b 100644 --- a/Justfile +++ b/Justfile @@ -43,3 +43,49 @@ bench: flame: cargo flamegraph --root --example atp + +# --------------------------------------------------------------------------- +# Release workflow +# +# Publishing goes to the private kellnr registry only: `Cargo.toml` sets +# `publish = ["kellnr"]`, so an accidental `cargo publish` to crates.io is a +# hard error rather than an irreversible mistake. The index is declared in the +# committed `.cargo/config.toml`; the token is per-user and lives in +# `~/.cargo/credentials.toml` (`cargo login --registry kellnr`). +# +# Step 1: just release-plan [level] — dry run, no writes +# Step 2: just release [level] — bump, changelog, tag, publish, push +# +# LEVEL is the cargo-release bump level (default `minor`). On 0.x: +# minor -> breaking bump (0.1.2 -> 0.2.0) <- any public-API change +# patch -> additive only (0.1.2 -> 0.1.3) +# major -> reserved for the 1.0.0 jump +# +# `release.toml` regenerates CHANGELOG.md with git-cliff in a pre-release hook +# and keeps push = false; this recipe pushes last, after publish has succeeded. +# --------------------------------------------------------------------------- + +# Dry-run preview of the next release. Inspect the version bump and the +# "Publishing ..." line before running `just release`. +release-plan level="minor": + cargo release {{level}} + +# Cut a release from a clean main: gate -> bump -> tag -> publish -> push. +release level="minor": + #!/usr/bin/env bash + set -euo pipefail + + if [[ "$(git branch --show-current)" != "main" ]]; then + echo "error: run 'just release' from the 'main' branch" >&2; exit 1 + fi + if [[ -n "$(git status --porcelain)" ]]; then + echo "error: working tree is dirty — commit or stash first" >&2; exit 1 + fi + + # cargo-release only verify-compiles the packaged crate; it does not run the + # suite, and publishing is irreversible. Run the same gate CI does, which + # includes the release profile where debug_assert! is compiled out. + just ci + + cargo release {{level}} --execute --no-confirm + git push --follow-tags diff --git a/cliff.toml b/cliff.toml index 6953f7f..5dce1aa 100644 --- a/cliff.toml +++ b/cliff.toml @@ -44,6 +44,11 @@ split_commits = false # Assigns commits to groups. # Optionally sets the commit's scope and can decide to exclude commits from further processing. commit_parsers = [ + # Must precede the type parsers below: a `feat!`/`fix!`/`refactor!` subject + # matches those too, and the first match wins. Without this a breaking + # change renders as an ordinary line of its own type. + { message = "^[a-z]+(\\(.+\\))?!:", group = "Breaking Changes" }, + { body = "BREAKING CHANGE", group = "Breaking Changes" }, { message = "^feat", group = "Features" }, { message = "^fix", group = "Bug Fixes" }, { message = "^doc", group = "Documentation" }, diff --git a/release.toml b/release.toml index e32cc02..9fcce4e 100644 --- a/release.toml +++ b/release.toml @@ -1,2 +1,6 @@ -publish = false +# Publish to the registry named in Cargo.toml's `publish` list (kellnr). +publish = true +# Hold off pushing until tags and publish have both succeeded; `just release` +# pushes last. +push = false pre-release-hook = ["sh", "-c", "git cliff -o CHANGELOG.md --tag {{version}} && git add CHANGELOG.md"]