Files
Michel de Graaf f09d550e4b ci: attach .deb and .rpm packages to releases and nightlies
Built by nfpm from the static musl binaries, with the man page and the
bash, zsh and fish completions. The names carry no version, so
releases/latest/download/soulseek-rs-amd64.deb stays a working link; the
nightly pair replaces itself on the rolling prerelease.
2026-09-21 12:58:09 +02:00

121 lines
6.4 KiB
Markdown

# Releasing
Releases are automated by [release-plz](https://release-plz.dev). You do not run
`cargo publish`, edit versions, write changelog entries, or push tags by hand.
## How it works
1. Merge a PR to `master` using [conventional commits](https://www.conventionalcommits.org)
(`feat:`, `fix:`, `perf:`, `refactor:`, `docs:`, `chore:`, or a `!` / `BREAKING CHANGE:`
footer for a major bump).
2. `.github/workflows/release.yml` opens or updates a **`chore: release vX.Y.Z`** PR. It
contains the semver bump in `[workspace.package]`, the refreshed `Cargo.lock`, and the
generated `CHANGELOG.md` entry. Review it like any other PR.
3. Merging that PR releases, but only once CI has passed on the merge commit — a published
crates.io version can be yanked but never withdrawn, so the `Wait for CI` job blocks the
publish until the CI run for that exact commit is green:
- tags `vX.Y.Z` and creates the GitHub release with the changelog as its notes,
- publishes `soulseek-rs-lib` then `soulseek-rs` to crates.io (dependency order, waiting
for the index between them),
- builds and attaches binaries with `.sha256` checksums for six targets:
| Archive | Notes |
| ------------------------------------------------ | ------------------------------ |
| `soulseek-rs-vX.Y.Z-x86_64-unknown-linux-musl.tar.gz` | static, any glibc |
| `soulseek-rs-vX.Y.Z-aarch64-unknown-linux-musl.tar.gz` | static, any glibc |
| `soulseek-rs-vX.Y.Z-x86_64-apple-darwin.tar.gz` | Intel Mac |
| `soulseek-rs-vX.Y.Z-aarch64-apple-darwin.tar.gz` | Apple Silicon |
| `soulseek-rs-vX.Y.Z-x86_64-pc-windows-msvc.zip` | |
| `soulseek-rs-vX.Y.Z-aarch64-pc-windows-msvc.zip` | |
- packages the two Linux binaries as `soulseek-rs-amd64.deb`, `soulseek-rs-arm64.deb`,
`soulseek-rs-x86_64.rpm` and `soulseek-rs-aarch64.rpm`, with the man page and the
bash, zsh and fish completions. The names carry no version, so
`releases/latest/download/soulseek-rs-amd64.deb` is a link that keeps working,
- rewrites `Formula/soulseek-rs.rb` in the `michel/homebrew-tap` repository so
`brew install michel/tap/soulseek-rs` picks up the new macOS and Linux archives,
- merges `master` back into `develop`, so the branch the work happens on carries the
new version and changelog rather than drifting behind them.
Both crates always share one version, one tag, and one changelog — see `release-plz.toml`.
The version lives in one place, `[workspace.package]` in the root `Cargo.toml`, and
release-plz owns it. Do not edit it by hand on `develop`: the back-merge relies on
`develop` never touching that line, and an edit there turns every release into a
conflict.
## Nightly builds
`.github/workflows/nightly.yml` builds the tip of `develop` at 02:17 UTC every
day. It resolves the commit once, builds the same six targets as a numbered
release, packages the two Linux ones as `soulseek-rs-nightly-*.deb` and `.rpm`,
verifies all sixteen files as one set, then replaces the mutable `nightly`
prerelease and tag. Every nightly binary reports `X.Y.Z+git<commit time, UTC>.<sha>`
instead of the crate version. The published release is smoke-tested through the
curl and wget installer paths, and by installing the `.deb` and the `.rpm`.
Never enable GitHub's Immutable Releases on this repository: nightly replaces
its assets and moves its tag, and a tag name burnt by that setting cannot be
reused.
Use **Actions → Nightly → Run workflow** to force a build. The workflow always
checks out `develop`, regardless of the ref selected in the dispatch form. The
equivalent command is:
```bash
gh workflow run nightly.yml --ref develop
```
Nightly does not publish crates or update Homebrew. If a target fails, the
previous nightly release stays available; use a manual run to retry.
## One-time repository setup
- **Settings → Actions → General → Workflow permissions**: enable *Allow GitHub Actions to
create and approve pull requests*. Without it the release PR is never opened.
- **Settings → Secrets and variables → Actions**: add `CARGO_REGISTRY_TOKEN`, a
[crates.io token](https://crates.io/settings/tokens) scoped to `publish-new` and
`publish-update`. Without it the tag and GitHub release still happen, but the crates.io
publish fails.
- **Homebrew tap**: create a public `michel/homebrew-tap` repository, then add a
`HOMEBREW_TAP_TOKEN` secret — a fine-grained PAT with *Contents: read and write* on that
repository only. `GITHUB_TOKEN` cannot reach another repository. The formula itself is
generated by the workflow; nothing in the tap needs editing by hand.
## Notes
- A merge with no releasable commits (only `docs:`/`chore:`/`test:`) opens no release PR.
That is intended.
- The release PR does not run CI on its own, because GitHub does not trigger workflows for
PRs opened with `GITHUB_TOKEN`. Pushing any commit to that branch yourself does start it —
handy if you want the version bump validated before merging.
- If one target's build fails, the other five still upload (`fail-fast: false`). To retry,
run the **Release** workflow manually and set `tag` to the existing tag (e.g. `v6.0.0`);
it rebuilds and re-uploads every target for that tag. Re-running the failed job from the
Actions UI replays the *old* workflow file, so it is no use when the workflow is the thing
that needs fixing.
- To skip a release for a commit that would otherwise trigger one, use a `chore:` type.
- **If CI flakes on the release commit the publish is blocked, by design.** The e2e suite
talks to a real soulfind server over the network and does occasionally time out. To
recover: re-run the failed CI job, wait for that run to go green, then re-run the
**Release** workflow. The gate reads the *newest* CI run for the commit, so it will then
pass. Never work around it by publishing by hand.
- Release binaries are stripped via `[profile.release] strip = true` in the workspace
`Cargo.toml`, so `cargo install` benefits too — not just the release archives.
## Installing a release
```bash
cargo install soulseek-rs
```
Or download an archive from the [releases page](https://github.com/michel/soulseek-rs/releases),
verify it against the adjacent `.sha256`, and extract the binary.
## Using the library
```bash
cargo add soulseek-rs-lib
```