My release checklist for Rust programs
Tags: rust, release, deployment, checklist
Published: 2018-10-28 14:18:12

---
title: My release checklist for Rust programs
published: true
description: An overview of my workflow for releasing new versions of my Rust projects.
tags: Rust, Release, Deployment, Checklist
---

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](https://github.com/sharkdp/fd), [hyperfine](https://github.com/sharkdp/hyperfine) and [bat](https://github.com/sharkdp/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](https://github.com/sharkdp/fd), but I have very similar lists for other projects. It is important to take the steps in the given order.

1. Check and update **dependencies**.

    a) Use [`cargo outdated`](https://github.com/kbknapp/cargo-outdated) to check for outdated dependencies. [deps.rs](https://deps.rs/repo/github/sharkdp/fd) can also be used to get the same information.
    b) Run `cargo 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).*

3. 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](https://github.com/sharkdp/fd/compare/v7.1.0...master)*

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

3. 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.*

4. **Clean up** the code base.

    a) Run `cargo clippy` and review the suggested changes [Optional]
    b) Run `cargo fmt` to auto-format your code.
    c) Run `cargo 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.*

5. Bump **version** information.

    a) Update the project version in `Cargo.toml`
    b) Run `cargo build` to update `Cargo.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](https://crates.io/) in the past.*

6. **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.*

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

8. Write **release notes**.

    While waiting for CI to finish, I already start to write the [release notes](https://github.com/sharkdp/fd/releases). 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.

9. 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).*

10. Create the **release**.

    Create the actual release on GitHub and copy over the release notes.

11. **Verify** the deployment.

     Make sure that all binaries have been uploaded. Manually test the binaries, if possible.

12. **Publish** to [crates.io](https://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 the `cargo publish` call, but `cargo publish` can not be undone if the deployment via `git push --tags` fails.*

13. Notify **package maintainers** about the update.

     *Arch Linux, for example, has the possibility to [flag packages as being "out of date"](https://www.archlinux.org/packages/community/x86_64/fd/). 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.