After few months I spent learning Haskell, I finally finished my very first real world Haskell project called Headroom. It’s basically a manager for license headers located in source code files (more about it in next blog post). When I reached some reasonable stability of the codebase, I decided it would be also nice to have it released on Hackage. I found the process not to be as straightforward as I expected, so I decided to sum up my experience in this blog post, both for future myself and for anyone else interested.

1 Prerequisities

1.1 Project Configuration

Hackage needs know some additional metadata about your package, so you need to put these into either package.yaml or .cabal file. You can do that by performing

$ cabal check

and following the error messages that will lead you which fields need to be added. This step will make sure that Hackage won’t reject your package.

1.2 Version Bounds

Every package uploaded to Hackage should have correctly set version bounds for its dependencies to avoid troubles when building using Cabal. If you use hpack instead of .cabal file, then the generated .cabal file will lack these version restrictions. Fortunately you can tell Stack to generate them for you by adding the pvp-bounds option to stack.yaml:

pvp-bounds: both

Then when you pack the distribution archive using stack sdist, the generated .cabal file will contain correct version bounds, based on the Stackage version you choose, as shown in snippet below:

build-depends:
    aeson >=1.4.6.0 && <1.5,
    base >=4.7 && <5,
    either >=5.0.1.1 && <5.1,
    file-embed >=0.0.11.1 && <0.1,
    lens >=4.17.1 && <4.18,
    mustache >=2.3.1 && <2.4,

1.3 Haddock Documentation

You should document all the public API using the Haddock tool, it will help potential users with orientation in your codebase. Also consider adding some example code snippets and make sure they will compile using the awesome Doctest tool. Example of such Haddock comment with Doctest example is below:

-- | Parses 'License' from the raw string representation, formatted as
-- @licenseType:fileType@.
--
-- >>> parseLicense "bsd3:haskell"
-- Just (License BSD3 Haskell)
parseLicense :: Text          -- ^ raw string representation
             -> Maybe License -- ^ parsed 'License'

1.4 Setup CI

You should also consider setting up CI as it will make sure your project is building and all tests are passing, so you won’t upload broken stuff to Hackage. There are many CI tools to choose from, I decided to go with the Travis CI.

As a first step, you need Travis account. Follow these steps describing how to do that. In order to let Travis run your build, you need to add appropriate .travis.yml configuration file to your project root. If you use Stack tool to build your project, you can check the Stack documentation with example configuration for Travis, or see the actual configuration file I use for one of my projects.

2 Hackage User Account

Now it’s time to register user account and ask Hackage trustees to add you to group with upload rights (this seems to be some kind of anti-spam protection).

You can register new account here, then follow the e-mail with confirmation link. After that, you need to send e-mail to Hackage trustees, asking them to add you to uploader group. In my case it took one or two days before I got any answer, so be patient.

3 Upload to Hackage

Your project should be ready for upload. Distribution tarball can be generated by running following command:

$ stack sdist

and it should be stored on location similar to this one

.stack-work/dist/x86_64-osx/Cabal-3.0.1.0/package-x.y.z.tar.gz

3.1 Package Candidate

Before you publish your package to the public index, you may want to upload the package candidate first. Package candidate is not released to the main index and contrary to published package, it can be reuploaded as many times as you wish, so you can check that everything is fine and do any necessary fixes. Unfortunately the package candidate workflow is not finished, for example the Haddock documentation is not generated properly.

3.2 Publishing Package

Once you are sure that your package is ready to be published, you can do that by either using the upload form or executing following command:

$ cabal upload path/to/package-x.y.z.tar.gz

That should be all, but for example in my case, the Haddock documentation on project Hackage page was not generated for some reason. You can fix this by uploading the Haddock documentation tarball manually using these commands:

$ cabal haddock --haddock-html-location='https://hackage.haskell.org/package/$pkg-$version/docs' --haddock-hyperlink-source --haddock-quickjump --haddock-for-hackage
$ cabal upload -d  --publish path/to/package-x.y.z-docs.tar.gz

If this happens to you, don’t forget to check the result of the build that Hackage attempts to do on following address:

https://matrix.hackage.haskell.org/package/<YOUR_PACKAGE>

4 Summary

The process of uploading package to Hackage is not difficult, but there are some gotchas that may make it more cumbersome for someone who does that for very first time. One thing that will be hopefully improved is the workflow of package candidates, which is not fully implemented yet. Also the process of registering your account on Hackage is relatively slow as it needs some human admin to verify your registration, but I understand this is because of some issues with fake accounts. Hopefully this blog post will help anyone in same situation as I was.