Steps for Releasing a Haskell Package to Hackage

2018-07-16

Releasing a new version of a Haskell package to Hackage requires quite a few steps. I wanted to make a list of the steps here on my blog so that it is easy for people to refer to them when trying make a new release of a package. It is better to have a simple checklist to follow than to have to remember all the steps each time.1

The steps below make the following assumptions:

  1. git is used for version control.
  2. The source code is hosted on Github.

However, the steps can easily be adapted to work with any version control system and code-hosting provider.

The (short) Steps

Here is an abbreviated version of the steps. They are arranged in the order they should be performed. Additional explanation is given in the next section:

  1. Make sure CI is succeeding on master branch.
  2. Decide what the next version of the package should be based on the PVP.
  3. Update the .cabal or package.yaml file with the new package version.
  4. Make sure the CHANGELOG.md file has been updated with the differences between the old version and the current version.
  5. Upload the code from the master branch to Hackage with the command stack upload.
  6. Create a Git tag for the new version and push it to Github with the command git tag v1.2.3.4 && git push --tags.

The (long) Steps

Here is a longer version of the steps above.

  1. Make sure all open pull requests on Github have been merged into the master branch.

    We are going to focus on the master branch for the following steps. The code in the master branch is what is eventually going to be released to Hackage.

    When making a new release to Hackage, it isn't strictly necessary to merge in all open pull requests, but it is generally a good idea to check just in case. If someone went through the work of adding a feature or fixing a bug they think is important, it is a good idea to make a release with their change in a timely fashion.

  2. Make sure continuous integration (CI) is passing for the master branch.

    Before making a release to Hackage, it is a good idea to make sure all the CI tests are passing.

    As of July 2018, I am using Travis CI to do CI for most of my packages. You should double check the build status of the master branch on Travis CI.

    For example, take the pretty-simple package. The build status of the master branch can be found at the following URL:

    https://travis-ci.org/cdepillabout/pretty-simple/branches

    In practice, the build status of the master branch is usually determined by the build status of the most recent pull request. Since most releases are usually performed immediately after the merge of a pull request, all you need to do is check the build status of the most recently merged-in pull request.

    (Since most pull requests are only merged in if they pass CI, this step 2 isn't normally necessary. However, it is generally a good idea to take a second and check.)

  3. Decide the next version of the package based on the PVP.

    This is often the trickest step. Most Haskell packages have a version with the following format: MAJOR1.MAJOR2.MINOR.EXTRA. For example, given the package foo-1.2.3.4, the MAJOR version is 1.2, the MINOR version is 3, and the EXTRA version is 4. The PVP page linked above gives rules for when each version needs to be bumped, as well as explaining why it is done this way.

    The explanations on the page linked above are somewhat hard to follow, so I recommend you look at the flow chart on the same page.

    If you're still not sure which version component to bump, here are a couple rules of thumb:

    1. If a change was made that the end user generally won't care about, it is safe to bump the EXTRA version. This includes adding documentation, adding tests, and refactoring (as long as it doesn't change the API).

      For example, foo-1.2.3.4 becomes foo-1.2.3.5.

    2. If new functions or types were added, you need to bump the MINOR version. The package should continue working for all existing users with NO changes to their own code.

      For example, foo-1.2.3.4 becomes foo-1.2.4.0.

    3. If existing functions or types were changed or deleted, you need to bump the MAJOR2 version. Existing users of the package may need to make changes to their code to work with the new version of the package.

      For example, foo-1.2.3.4 becomes foo-1.3.0.0.

      It may be worthwhile to announce the new version somewhere like Twitter.

    4. If the package was radically rewritten, or really exciting new functionality was added, you can bump the MAJOR1 version.

      For example, foo-1.2.3.4 becomes foo-2.0.0.0.

      It would be a good idea to create a blog post about the new features and submit it somewhere like the /r/haskell or the Haskell mailing list.2

    5. If you're still not sure which version component to bump, you should generally bump the higher component. For example, if you're not sure whether to bump the MINOR component or the MAJOR2 component, you should bump the MAJOR2 component just to be safe.

      Users generally won't get mad at you for being too conservative.

    You can look through the git commit log to figure out exactly what was changed from the previous version. You can also look at a git diff with the previous version.

    For example, imagine we are working on the package foo. The previous version was foo-1.2.3.4. There should be a git tag called v1.2.3.4. If you are on the master branch, you should be able to get a diff between the current code and the previous version with the following command:

    $ git diff v1.2.3.4..
  4. Update the .cabal or package.yaml file with the new package version.

    If there is a package.yaml file in the current directory, you should bump the version number there.3 If not, then you should bump the version number in the .cabal file.

  5. Make sure the CHANGELOG.md file has been updated with the differences between the old version and the current version.

    This is important so that users can easily figure out what has changed between versions.

    Ideally, everyone sending in a pull request will add a new entry to the CHANGELOG.md. However, sometimes this gets missed. It is a good idea to make one last check before you release the package. Make sure to commit your additions to the CHANGELOG.md before moving on to the next step.

  6. Upload the package to Hackage.

    You should upload the code from the master branch. You can do this with the command stack upload or cabal upload, depending on whether you are using stack or cabal.4

  7. Create a git tag for the new version of the package and push it to Github.

    The git tag should match the version of the package. It should start with a leading v.

    For example, given the package foo-1.2.3.4, you can create a tag and push it to Github with the following commands:

    $ git tag v1.2.3.4
    $ git push --tags

Conclusion

Except for figuring out the PVP version, following the steps above is not hard. However, since there are multiple steps, it is convenient to have a simple checklist to run through. Hopefully this blog post is helpful next time you are releasing a Haskell package to Hackage.

Footnotes


  1. This idea is discussed extensively in the book The Checklist Manifesto.↩︎

  2. However, this is definitely not a requirement.↩︎

  3. package.yaml files are for hpack.↩︎

  4. I generally recommend you use stack, unless you personally dislike it. nix is also nice, but I don't provide nix expressions for most of my packages.↩︎

tags: haskell