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.
cargo outdatedto check for outdated dependencies. deps.rs can also be used to get the same information.
cargo updateto 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.
Update the documentation.
a) Review and update the
b) Update the README (program usage, document new features, update minimum required Rust version)
c) Update the man page.
Install the latest
masterlocally and test new features.
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.
cargo clippyand review the suggested changes [Optional]
cargo fmtto auto-format your code.
cargo testto 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 buildto update
c) Search the whole repository for the old version and update as required (README, install instructions, build scripts, ..)
Forgetting to also update
Cargo.lockhas prevented me from successfully publishing to crates.io in the past.
Dry run for
cargo publish --dry-run --allow-dirty
cargo publishis 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 pushall 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
Do this after the
git push --tagsstep. A git tag can be deleted if something goes wrong with the
cargo publishcall, but
cargo publishcan not be undone if the deployment via
git push --tagsfails.
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.
As software gets more and more integrated into our lives, the industrialization of its crafting process becomes inevitable. But the over-generalization of software engineering can be crushing the creative side of programming.