I have been trying out google cloud run with a simple haskell + purescript web app. In order to deploy the app, I need to containerize the app and push the docker image to google container registry. I wanted to figure out whether this can be done with nix and some simple github actions. Turns out: with skopep, it is pretty easy!
skopeo copy
After some searching, I found this discourse thread mentions skopeo, and I found this repo using nix flakes and skopeo to push docker image into a private docker registry in github action. So I decided to try out skopeo.
skopeo can push an image from one location to another. It supports Google Container Registry (GCR)
So we can push our image like this
install skopeo in github action
I am using install-nix-action in my build step already, so I added a shell script bin in my nix flake, and make it as nix flake app using flakes-utils, which can be run using nix run ".#script"
Authenticate skopeo with GCR
I was not able to find too much documentation on this step. So I went to some trail and error.
GCR supports different authentication methods. I thought the easiest would to create a service account key with Cloud Storage Admin role, and upload the key content as a secret in github actions.
Of course, we want to replace docker with skopeo. This works locally, but not in github action due to how skope loginworks.
But turns out, we don’t have to do skope login, we can just using skopeo copy --dest-creds.
Put everything together
So I put my service account key, google cloud project name, and the name of the GCR docker image name into github actions as secret, and pass them as env variable in the step, and do nix run ".#upload-script"
I have been learning nix for a while. Overall, I am really happy with
Nix ecosystem. With Nix, home-manager, cachix, you have setup a reliable
and efficient development environment and CI. For demonstration, I want
to show you how I use nix to publish my blog:
If you are new to Nix, I hope you can find something useful in this
blog.
Why Nix ecosystem
Hopefully, at this point you are already sold on nix. If not, maybe
checkout build with nix or
nix.dev. For me, the biggest selling point of is: you
can use a declarative language to set up a reproducible environment for
your local development and CI.
Why Hakyll
This is probably a tough sell if you don’t care about Haskell. Nowadays,
you use tools like Hugo, WordPress, or Jekyll to effortless setup a
static site. I love Haskell, and Hakyll gives me an opportunity to use
Haskell.
Why Firebase
There are lots of decent alternative to Firebase for hosting a static
site. Just to list few
Both templates are very opinionated.
getting-started-nix-template
is a more generic nix template. hakyll-nix-template is Hakyll specified,
it has sitemap, tag support.
scaffold hakyll project using hakyll-init
Hakyll comes with a command line too haskyll-init to scaffold a Hakyll
project. Since we only need haskell-init once, normal nix workflow
would be
but depend on which nix channel is your default channel, you might get
an error about pkgs.haskellPackages.hakyll is broken. We could use
-I flag to pin our one-time shell package to stable version. The
pattern is
-I nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/{rev}.tar.gz~
The project is just a plain cabal project. We can edit project or
executable name. We can use normal Nix Haskell development workflow.
Nix Haskell Workflow
Currently, there are two major Nix Haskell workflows:
iohk-haskell.nix
and your traditional
nixpgs.
People usually recommend iohk one for complex project, I haven’t used it
at all.
Most of the YAML configuration is copied from
getting-started-nix-template. My default.nix only build the Hakyll
program, it doesn’t generate the site. So I added
result/bin/site build in run command. (site is the name of my Hakyll
executable). We need pass generated site directory as
artifacts
between build steps
dist is the directory name for the generated site, by default Hakyll
uses _site.
Publish to Firebase
I use w9jds firebase action
to publish the generated static site directory to Firebase. There are
publish actions for netlify and Github Page. Of course, we have to store
our Firebase token as encrypted secret and pass them as environment
variables into the build step.