Releasing new versions of your projects is one of the more laborious tasks of an open source maintainer. There are many great tools that automate part of this process, but typically there are still a lot of manual steps involved. In addition, there are lots of things that can go wrong. New bugs might have been introduced, dependency updates can go wrong, the automatic deployment might not work anymore.
After some practice with three of my Rust projects (fd, hyperfine and bat), my workflow has converged to something that works quite well and avoids many pitfalls that I have walked into in the past. My hope in writing this post is that this process can be useful for others as well.
The following is my release checklist for fd, but I have very similar lists for other projects. It is important to take the steps in the given order.
-
Check and update dependencies.
a) Use
cargo outdated
to check for outdated dependencies. deps.rs can also be used to get the same information.
b) Runcargo update
to update dependencies to the latest compatible (minor) version.
c) If possible and useful, manually update to new major versions.As for updates to new major versions, take a look at the upstream changes and carefully evaluate if an update is necessary (now).
-
Get the list of updates since the last release.
Go to GitHub -> Releases -> "XX commits to master since this release" to get an overview of all changes since the last release.
Example: fd/compare/v7.1.0...master
-
Update the documentation.
a) Review and update the
-h
and--help
text.
b) Update the README (program usage, document new features, update minimum required Rust version)
c) Update the man page. -
Install the latest
master
locally and test new features.a) Run
cargo install -f
.
b) Test the new features manually.
c) Run benchmarks to avoid performance regressions.In an ideal world, we have written tests for all of the new code. These tests also run in our CI pipeline, so there is nothing to worry about, right? In my experience, there are always things that need to be reviewed manually. This is especially true for CLI tools that are more difficult to test due to their intricate dependencies on the interactive terminal environment.
-
Clean up the code base.
a) Run
cargo clippy
and review the suggested changes [Optional]
b) Runcargo fmt
to auto-format your code.
c) Runcargo test
to make sure that all tests still pass.The last two steps are typically automated in my CI pipeline. They are included here for completeness.
-
Bump version information.
a) Update the project version in
Cargo.toml
b) Runcargo build
to updateCargo.lock
c) Search the whole repository for the old version and update as required (README, install instructions, build scripts, ..)Forgetting to also update
Cargo.lock
has prevented me from successfully publishing to crates.io in the past. -
Dry run for
cargo publish
.cargo publish --dry-run --allow-dirty
Running
cargo publish
is one of the last steps in the release process. Using the dry-run functionality at this stage can avoid later surprises. -
Commit, push, and wait for CI to succeed.
git push
all the updates from the last steps and wait until CI has passed.I used to immediately tag my "version update" commit to start the automated deployment. Having this intermediate "wait for CI" step has definitely prevented some failed releases.
-
Write release notes.
While waiting for CI to finish, I already start to write the release notes. I go through the list of updates and categorize changes into "Feature", "Change", "Bugfix" or "Other". I typically include links to the relevant Github issues and try to credit the original authors.
-
Tag the latest commit and start deployment.
git tag vX.Y.Z; git push --tags
This assumes that the CI pipeline has been set up to take care about the actual deployment (upload binaries to GitHub).
-
Create the release.
Create the actual release on GitHub and copy over the release notes.
-
Verify the deployment.
Make sure that all binaries have been uploaded. Manually test the binaries, if possible.
-
Publish to crates.io.
Make sure that your repository is clean or clone a fresh copy of the repository. Then run
cargo publish
Do this after the
git push --tags
step. A git tag can be deleted if something goes wrong with thecargo publish
call, butcargo publish
can not be undone if the deployment viagit push --tags
fails. -
Notify package maintainers about the update.
Arch Linux, for example, has the possibility to flag packages as being "out of date". Include the link to your release notes and highlight changes that are relevant for package maintainers (new files that need to be installed, new dependencies, changes in the build process)
Do you maintain similar release-checklists? If so, I'd love to hear about things you do differently or steps I might have missed.
Top comments (3)
Some extra tips:
cargo outdated
instead of deps.rs to get the info from your terminal.cargo audit
in your CI pipeline.cargo fmt -- --check
in your git commit hook.Thank you very much for these tips. I have updated the post to mention "cargo outdated" in favor of deps.rs.
Thanks for this awesome list -- I'm using it everytime :-)
I suggest adding the option
--all-features
tocargo clippy
andcargo test
.