As the title suggests, this is a “Hello World” blog post indicating two things:
- The pipeline for building and deploying this blog is operational.
- I’ve finally set up (yet another) blog, after a long period of procrastination :)
To make up a proper post, I’ll talk about the first one in this post.
hakyll blog with Nix
This blog is powered by
hakyll. Simply type
stack install hakyll or
cabal new-install hakyll into the terminal, then go enjoy the aftertoon tea; after
hakyll and its dependencies finish compiling, we can come back and use
hakyll-init to bring up a sample site, then modify the sample site to make our own.
We won’t be happy if the same building process is triggered on the CI service with every single commit, after all,
hakyll has heavy dependencies (e.g.
pandoc and its friends). We can take a bit of trouble to cache the build files, but a simpler option is: take advantage of the pre-built Haskell packages in
nixpkgs, and let Nix build the site for us.
We don’t have to be a Nix ninja to build a Haskell project with Nix. There’s an excellent Nix/Haskell tutorial series written by Gabriel Gonsolez here, and we only need to go through the Project 0 for our use case.
Using Nix on CircleCI
After testing builds locally, we shall associate the repository with a CI service, so when new commits are pushed, the blog site is automatically built and deployed. Since “evaluation of CI services in the wild” should be worth a standalone blog post, I’m directly jumping to my biased conclusion here: use CircleCI.
When writing a build config for CircleCI, one needs to specify a Docker image as the build environment, and CircleCI expects some common tools like
openssh to be present in the environment. There are quite some Docker images for Nix out there, a non-comprehensive list:
Just pick anyone and write the config. Nix encourages dependency pinning for fully deterministic builds, so before installing anything, we can add a specific revision of
nix-channel with something like
nix-channel --add https://releases.nixos.org/nixpkgs/nixpkgs-19.03pre159144.0249f7d48af nixpkgs.
Travis CI has built-in support for Nix projects nowadays. Probably worth a try.
Deploying the site to Netlify
The canonical choice of hosting static sites was GitHub pages, but I’d like to try something new here: Netlify. They have a GitHub pages versus Netlify comparison here.
Netlify has its own CI/CD service which works out of the box for Node.js/Python/Ruby blogs. But since we’re using CircleCI to build a Haskell blog, we want a “manual” deployment workflow which pushes generated pages to Netlify. This can be done using Netlify’s own CLI tool, see docs here for instructions. Basically, you just need to:
- Log in Netlify and start a site. Easiest way is dragging the site folder into the browser tab, then modifying the settings (adding your shiny custom domains, etc) via the Netlify settings page.
- Add the
NETLIFY_SITE_IDsecret environment variables via CircleCI’s web panel. Don’t put them in a config file and submit it!
- Build the site, then push it via
netlify deploy --dir=_site --message=".." --prod. Without
--prod, the deployment won’t take effect in the primary domain, but you can still preview the built site via a
xxx.netlify.comURL generated by
When working with corporate blogs with multiple authors, remember to add the
--prod flag to
netlify deploy only when building on the
master branch. When an author start working on a new post via opening a pull request, the preview deploys aren’t pushed, but can still be accessed in the browser, and when the administrator confirms the post looks good, he/she can merge the pull request and the real deployment happens. Since this is just a personal blog, I won’t bother starting a WIP branch and opening a pull request for every single post, so deployment happens with every commit.
The full source code of this site is available here. It is a combination of
hakyll blog site with RSS feeds, Disqus comments, Nix-based building and Netlify deployment.
In case any fellow Haskeller want to set up a site using a similar workflow, this post and the code might be useful :)