Files
soulseek-rs-modded/RELEASE.md
T
Michel de Graaf 1b898adeeb docs: stop pinning a library version the instructions cannot keep current
Four places told people to write `soulseek-rs-lib = "8"` (RELEASE.md said "6"),
and every one of them had been left behind by ten majors or more. The failure is
silent rather than loud: 6.x and 8.0.0 are both real published versions, so cargo
resolves the snippet happily and hands over a library from August instead of
refusing it, and one of the four is soulseek-rs-lib's own README, which is the
page crates.io renders.

`cargo add` writes whatever version is current at the time it runs, so there is
no longer a number in the docs to go stale. The manifest pin in
soulseek-rs/Cargo.toml stays as it is — release-plz owns that one and keeps it
in step with the release.
2026-09-10 12:08:00 +02:00

5.7 KiB

Releasing

Releases are automated by release-plz. 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 (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
    • 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, verifies all twelve archives and checksum files as one set, then replaces the mutable nightly prerelease and tag. The published release is smoke-tested through both the curl and wget installer paths.

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:

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

cargo install soulseek-rs

Or download an archive from the releases page, verify it against the adjacent .sha256, and extract the binary.

Using the library

cargo add soulseek-rs-lib