Release-plz: release Rust packages from CI

Releasing Rust packages is tedious and error-prone, just like most IT manual tasks. For every package you want to release, you need to:

  • Increase the version in Cargo.toml.
  • Update the changelog.
  • Publish the package in the cargo registry (for example, crates.io).
  • Create and push a git tag.

Meet release-plz: a Rust open-source project that automates these tasks, allowing developers to release Rust packages without the command line.

logo

Features

  • Version update based on conventional commits.
  • Changelog update with git-cliff.
  • Cargo workspaces support.
  • No configuration required.
  • Optional cargo update before releasing.
  • Git tag created for every released package.
  • Package published to any cargo registry.

How does it work

The recommended way to run release-plz is via the GitHub action.

By default, every time you merge a commit to the main branch, the GitHub action runs two commands: release-plz release-pr and release-plz release.

Creating a release pull request

The release-plz release-pr command opens a pull request that prepares the next release. The command:

  • Downloads the packages of the project from the cargo registry.
  • Compares the local packages with the downloaded ones to determine the new commits.
  • Updates the packages versions based on the messages of the new commits (based on conventional commits and semantic versioning).
  • Updates the packages changelogs with the messages of the new commits.
  • Updates all dependencies by running cargo update (disabled by default).
  • Raises a pull request with the above changes.

When the project maintainer merges the release pull request, the packages are ready to be published.

Here’s an example of a PR opened by release-plz in the release-plz GitHub project itself:

pr

Releasing all updated packages

The release-plz release command releases all the packages with a new version.

For example, let’s say you have a workspace with two packages: pkg-a (version 0.3.1) and pkg-b (version 0.2.2). The crates.io registry contains pkg-a version 0.3.1, but it doesn’t contain pkg-b version 0.2.2 because you didn’t publish this version yet. In this case, release-plz would release pkg-b.

For every release, release-plz:

  • Creates a git tag named <package_name>-v<version> (e.g. tokio-v1.8.1).
  • Publishes the package to the cargo registry by running cargo publish.

Releases made easy

In short, release-plz makes releasing Rust packages child’s play:

  1. For every change, release-plz creates a release pull request.
  2. The release pull request reminds the maintainer about the unpublished changes.
  3. When the maintainer reviews and merges the pull request, release-plz releases the updated packages.

Release-plz makes releasing Rust packages as easy as clicking the pull request “merge” button.

Why yet another release tool

New Rust apps and rewrites are mushrooming every day. Choosing which tools to add to your developer toolbox is becoming harder and harder.

I feel obliged to explain why I created this project and how it compares with similar tools.

Differences with release-please

I learned about the “pull request driven release flow” from the article My ideal Rust workflow by fasterthanlime, where he talks about release-please. I immediately felt like release-please could fit great with the way I work, so I decided to try it. Like release-plz, release-please is an open-source project that manages releases via pull requests. I tried release-please both with the GitHub action and the CLI, but it didn’t work for me:

  • I got a “not found” error, which wasn’t very descriptive.
  • Two configuration files were required: .release-please-manifest.json and release-please-config.json. Most of the fields I had to write in these two files were already in the Cargo.toml files. I thought it was a shame that Rust developers needed to duplicate these fields.
  • Release-please considers a package “published” when the relative git tag exists. Release-please doesn’t interact at all with cargo registries. However, some Rust projects don’t create git tags. Instead, they just publish the package to crates.io, which is the ultimate source of truth.

Most of the problems listed above mainly exist because release-please aims to support different programming languages. So I thought:

What if I write a tool specifically optimized for Rust that solves all the problems I have experienced? A tool that works out-of-the-box with nearly every Rust project, without any extra configuration.

It took six months of my spare time to write release-plz, but here we are!

Differences with other Rust release tools

These are the release tools in the Rust ecosystem I am aware of:

  • cargo-release:
    • Bumps the version and publishes Rust packages from the CLI.
    • Doesn’t support automatic releases from CI yet. See this open issue.
  • cargo-workspaces:
    • It’s a set of commands to manage cargo workspaces and their crates.
    • The publish command releases the packages from the CLI similarly to cargo-release.
  • cargo-smart-release:
    • Updates and publishes packages from the CLI.
    • It’s very similar to release-plz, but it is not meant to be run in CI (see this comment from the author).

To respect the “Unix philosophy”, I tried to delegate the release flow to cargo-release and cargo-workspaces. However, after discussing with the maintainers here and here, I decided to implement the release-plz release command by myself.

Overall, I wasn’t satisfied with the existing Rust release tools because:

  • They use git tags to determine if a package is already published — release-plz uses cargo registries instead.
  • They primarily focus on the CLI use case — release-plz focuses mainly on CI.

Conclusion

Using release-plz increased my productivity. Having an automated release pipeline helps you deliver at a faster pace, with a lower risk of manual mistakes.

If you try release-plz, please let me know on Twitter. Also, make sure to open issues for feature requests or bug reports!