2023-08-25 03:29:54 +00:00
|
|
|
+++
|
|
|
|
title = "Now With More Nix"
|
2023-08-28 00:27:25 +00:00
|
|
|
date = "2023-08-27T17:25:57-07:00"
|
2023-08-25 03:29:54 +00:00
|
|
|
author = "alejandro"
|
|
|
|
authorTwitter = "" #do not include @
|
|
|
|
cover = ""
|
2023-08-25 03:33:58 +00:00
|
|
|
tags = ["nix flake", "cachix", "devenv", "meta"]
|
2023-08-25 03:29:54 +00:00
|
|
|
keywords = ["nix"]
|
|
|
|
showFullContent = false
|
|
|
|
+++
|
|
|
|
|
|
|
|
It's been about a year since my last post into the void. Since [my last
|
|
|
|
post](/posts/dotfiles) I've completely overhauled how my computers are
|
|
|
|
configured. I now have [a nix
|
2024-11-16 02:01:21 +00:00
|
|
|
flake](https://github.com/alejandro-angulo/dotfiles) to manage my personal
|
2023-08-25 03:29:54 +00:00
|
|
|
machines. I'm going all in on nix and wanted to update the deployment process
|
|
|
|
for this site to use nix flakes as well.
|
|
|
|
|
2023-08-28 00:27:25 +00:00
|
|
|
## Managing Development Environments with devenv and nix flakes
|
2023-08-25 03:29:54 +00:00
|
|
|
|
|
|
|
It's been a while since I touched anything on this site and I didn't have any
|
|
|
|
of the right packages installed to work on this. I could have installed
|
|
|
|
programs like [hugo](https://gohugo.io/) system-wide. But, since I have been
|
|
|
|
tinkering with nix, I wanted to use [a flake to manage all the
|
2024-11-16 02:01:21 +00:00
|
|
|
things](https://github.com/alejandro-angulo/alejandr0angul0.dev/blob/b8174db2150f3ac9925f8450bc75264678cf06c9/flake.nix)
|
2023-08-25 03:29:54 +00:00
|
|
|
needed for development (including writing posts).
|
|
|
|
|
|
|
|
Here's what the devenv configuration looked like at the time I was writing this
|
|
|
|
post.
|
|
|
|
|
|
|
|
```nix
|
|
|
|
devShell = devenv.lib.mkShell {
|
|
|
|
inherit inputs pkgs;
|
|
|
|
modules = [
|
|
|
|
({pkgs, ...}: {
|
|
|
|
languages.javascript = {
|
|
|
|
enable = true;
|
|
|
|
npm.install.enable = true;
|
|
|
|
corepack.enable = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
packages = with pkgs; [
|
|
|
|
actionlint
|
|
|
|
alejandra
|
|
|
|
hugo
|
|
|
|
html-proofer
|
|
|
|
awscli2
|
|
|
|
];
|
|
|
|
|
|
|
|
pre-commit = {
|
|
|
|
hooks = {
|
|
|
|
actionlint.enable = true;
|
|
|
|
alejandra.enable = true;
|
|
|
|
eslint.enable = true;
|
|
|
|
markdownlint = {
|
|
|
|
enable = true;
|
|
|
|
excludes = ["node_modules"];
|
|
|
|
};
|
|
|
|
prettier = {
|
|
|
|
enable = true;
|
|
|
|
excludes = ["flake.lock"];
|
|
|
|
};
|
|
|
|
};
|
2023-08-28 00:27:25 +00:00
|
|
|
|
|
|
|
settings.markdownlint.config = {
|
|
|
|
MD013.code_blocks = false;
|
|
|
|
};
|
2023-08-25 03:29:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enterShell = ''
|
|
|
|
export PATH=./node_modules/.bin:$PATH
|
|
|
|
'';
|
|
|
|
})
|
|
|
|
];
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
This completely configures my development environment! It has all the packages
|
|
|
|
I want and sets up some pre-commit hooks for me in a single file. I don't need
|
|
|
|
to manage a `.pre-commit-config.yaml` and an `.mdlrc` file separately (these
|
|
|
|
files configure [pre-commit](https://pre-commit.com/) and
|
2023-08-28 00:27:25 +00:00
|
|
|
[markdownlint](https://github.com/markdownlint/markdownlint) respectively). The
|
|
|
|
best part is that I can easily get this development environment set up on any
|
|
|
|
machine (assuming I have [nix set up with flakes
|
|
|
|
support](https://nixos.wiki/wiki/Flakes#Enable_flakes) of course).
|
2023-08-25 03:29:54 +00:00
|
|
|
|
2023-08-28 00:27:25 +00:00
|
|
|
My `flake.nix` can accomplish what would traditionally be done with
|
2023-08-25 03:29:54 +00:00
|
|
|
[make](https://www.gnu.org/software/make/) and a `Makefile`. This section
|
|
|
|
handles building the site
|
|
|
|
|
|
|
|
```nix
|
|
|
|
packages.alejandr0angul0-dot-dev = pkgs.stdenv.mkDerivation {
|
|
|
|
name = "alejandr0angul0-dot-dev";
|
|
|
|
src = self;
|
|
|
|
|
|
|
|
buildPhase = ''
|
|
|
|
${pkgs.hugo}/bin/hugo --minify
|
|
|
|
'';
|
|
|
|
|
|
|
|
doCheck = true;
|
|
|
|
checkPhase = ''
|
|
|
|
env LOCALE_ARCHIVE=${utf8Locale}/lib/locale/locale-archive LC_ALL=en_US.UTF-8 \
|
|
|
|
${pkgs.html-proofer}/bin/htmlproofer public \
|
|
|
|
--allow-hash-href \
|
|
|
|
--ignore-empty-alt \
|
|
|
|
--disable-external \
|
|
|
|
--no-enforce-https
|
|
|
|
'';
|
|
|
|
|
|
|
|
installPhase = ''
|
|
|
|
cp -r public "$out"
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
This snippet defines how to build a
|
|
|
|
[derivation](https://nixos.org/manual/nix/stable/language/derivations.html)
|
|
|
|
that describes the site. It took me a while to make sense of all of this but
|
|
|
|
basically there are a bunch of [build
|
|
|
|
phases](https://nixos.org/manual/nixpkgs/stable/#sec-stdenv-phases). I only
|
|
|
|
needed three phases (build, check, and install). Nothing super special is going
|
|
|
|
on here.
|
|
|
|
|
|
|
|
I tell `hugo` to build a minified version of the site
|
|
|
|
|
|
|
|
```nix
|
|
|
|
buildPhase = ''
|
|
|
|
${pkgs.hugo}/bin/hugo --minify
|
|
|
|
'';
|
|
|
|
```
|
|
|
|
|
|
|
|
I enabled an optional check phase which tests the results of the build phase.
|
|
|
|
Here I run [htmlproofer](https://github.com/gjtorikian/html-proofer) to do some
|
|
|
|
quick sanity checks (like making sure I don't have broken internal links).
|
|
|
|
|
|
|
|
I did run into a small issue with this. `htmlproofer` was reading file contents
|
|
|
|
as if it were [US-ASCII](https://en.wikipedia.org/wiki/ASCII) but I have some
|
|
|
|
unicode characters in my source. The `env` below configures the
|
|
|
|
[locale](https://wiki.archlinux.org/title/Locale) to be
|
|
|
|
[UTF-8](https://en.wikipedia.org/wiki/UTF-8).
|
|
|
|
|
|
|
|
```nix
|
|
|
|
doCheck = true;
|
|
|
|
checkPhase = ''
|
|
|
|
env LOCALE_ARCHIVE=${utf8Locale}/lib/locale/locale-archive LC_ALL=en_US.UTF-8 \
|
|
|
|
${pkgs.html-proofer}/bin/htmlproofer public \
|
|
|
|
--allow-hash-href \
|
|
|
|
--ignore-empty-alt \
|
|
|
|
--disable-external \
|
|
|
|
--no-enforce-https
|
|
|
|
'';
|
|
|
|
```
|
|
|
|
|
|
|
|
The results of the build process should live in the `$out` directory. I just
|
|
|
|
need to move what `hugo` generated (it defaults to creating a `public/` folder)
|
|
|
|
into `$out`.
|
|
|
|
|
|
|
|
```nix
|
|
|
|
installPhase = ''
|
|
|
|
cp -r public "$out"
|
|
|
|
'';
|
|
|
|
```
|
|
|
|
|
|
|
|
## Updating CI/CD
|
|
|
|
|
|
|
|
This site is deployed to an S3 bucket just like before switching over to using
|
|
|
|
nix. However, I don't need to use docker containers anymore and can use nix
|
|
|
|
fully. Here's the [github actions
|
2024-11-16 02:01:21 +00:00
|
|
|
configuration](https://github.com/alejandro-angulo/alejandr0angul0.dev/blob/97a655bc0c3e18f8c8921b90f14f87f5a07ae837/.github/workflows/ci.yml)
|
2023-08-25 03:29:54 +00:00
|
|
|
at the time of writing.
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
name: "CI"
|
|
|
|
|
|
|
|
on:
|
|
|
|
pull_request:
|
|
|
|
push:
|
|
|
|
|
|
|
|
jobs:
|
|
|
|
lint:
|
|
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
|
|
- uses: actions/checkout@v3
|
|
|
|
- uses: cachix/install-nix-action@v22
|
|
|
|
- uses: cachix/cachix-action@v12
|
|
|
|
with:
|
|
|
|
name: devenv
|
|
|
|
- uses: cachix/cachix-action@v12
|
|
|
|
with:
|
|
|
|
name: alejandr0angul0-dev
|
|
|
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
|
|
|
- name: Run pre-commit hooks
|
|
|
|
run: |
|
|
|
|
git fetch origin
|
|
|
|
nix develop --accept-flake-config --impure --command bash -c \
|
|
|
|
"pre-commit run --from-ref origin/main --to-ref $GITHUB_SHA"
|
|
|
|
build:
|
|
|
|
needs: [lint]
|
|
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
|
|
- uses: actions/checkout@v3
|
|
|
|
- uses: cachix/install-nix-action@v22
|
|
|
|
- uses: cachix/cachix-action@v12
|
|
|
|
with:
|
|
|
|
name: devenv
|
|
|
|
- uses: cachix/cachix-action@v12
|
|
|
|
with:
|
|
|
|
name: alejandr0angul0-dev
|
|
|
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
|
|
|
- run: nix build --accept-flake-config -L
|
|
|
|
# Convoluted upload below is a workaround for #92
|
|
|
|
# See:
|
|
|
|
# - https://github.com/actions/upload-artifact/issues/92
|
|
|
|
# - https://github.com/actions/upload-artifact/issues/92#issuecomment-1080347032
|
|
|
|
- run: echo "UPLOAD_PATH=$(readlink -f result)" >> "$GITHUB_ENV"
|
|
|
|
- uses: actions/upload-artifact@v3
|
|
|
|
with:
|
|
|
|
name: built-site
|
|
|
|
path: ${{ env.UPLOAD_PATH }}
|
|
|
|
|
|
|
|
deploy:
|
|
|
|
needs: [build]
|
|
|
|
runs-on: ubuntu-latest
|
|
|
|
if: github.ref == 'refs/heads/main'
|
|
|
|
env:
|
|
|
|
PROD_DEPLOY_CONFIG_PATH: config/production/deployment.toml
|
|
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
|
|
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
|
|
|
|
HUGO_ENV: production
|
|
|
|
steps:
|
|
|
|
- uses: actions/checkout@v3
|
|
|
|
- uses: cachix/install-nix-action@v22
|
|
|
|
- uses: cachix/cachix-action@v12
|
|
|
|
with:
|
|
|
|
name: devenv
|
|
|
|
- uses: cachix/cachix-action@v12
|
|
|
|
with:
|
|
|
|
name: alejandr0angul0-dev
|
|
|
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
|
|
|
- uses: actions/download-artifact@v3
|
|
|
|
with:
|
|
|
|
name: built-site
|
|
|
|
path: public/
|
|
|
|
- name: Deploy
|
|
|
|
run: |
|
|
|
|
sed 's~{{S3URL}}~${{ secrets.S3URL }}~g' "${PROD_DEPLOY_CONFIG_PATH}.sample" > "${PROD_DEPLOY_CONFIG_PATH}"
|
|
|
|
sed -i 's~{{CLOUDFRONTDISTRIBUTIONID}}~${{ secrets.CLOUDFRONTDISTRIBUTIONID }}~g' "${PROD_DEPLOY_CONFIG_PATH}"
|
|
|
|
nix develop --accept-flake-config --impure --command bash \
|
|
|
|
-c 'hugo deploy --invalidateCDN'
|
|
|
|
```
|
|
|
|
|
|
|
|
[cachix](https://www.cachix.org) is a nix binary cache hosting service ran by
|
|
|
|
[Domen Kožar](https://github.com/domenkozar). (Cachix also happens to be the
|
|
|
|
entity behind devenv.) They've also provided some github actions to make that
|
|
|
|
allow me to cache the results of my nix commands to help speed up CI/CD
|
|
|
|
run times. I'm taking advantage of the
|
|
|
|
[install-nix-action](https://github.com/cachix/install-nix-action) (installs
|
|
|
|
nix on the ubuntu runners I'm using) and
|
|
|
|
[cachix-action](https://github.com/cachix/cachix-action) (gives me access to
|
|
|
|
the binaries hosted in cachix caches -- the site has its own cache) actions.
|
|
|
|
|
|
|
|
I only have three steps: lint -> build -> deploy. The lint step runs all the
|
|
|
|
pre-commit hooks I defined in my flake.nix file. I initially ran into errors
|
|
|
|
telling me that there was no `main` branch so I had to fetch origin and make
|
|
|
|
sure to explicitly reference the branch's remote (e.g. `origin/main`) .
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
- name: Run pre-commit hooks
|
|
|
|
run: |
|
|
|
|
git fetch origin
|
|
|
|
nix develop --accept-flake-config --impure --command bash -c \
|
|
|
|
"pre-commit run --from-ref origin/main --to-ref $GITHUB_SHA"
|
|
|
|
```
|
|
|
|
|
|
|
|
Notice I didn't need to explicitly install `pre-commit`. That happens
|
|
|
|
automagically when I run `nix develop`.
|
|
|
|
|
|
|
|
Once those checks are ready it's time to make sure the site can be built
|
|
|
|
successfully. I ran into another snafu with [a bug in github's
|
|
|
|
`upload-artifacts`
|
|
|
|
action](https://github.com/actions/upload-artifact/issues/92); luckily
|
2023-08-28 00:27:25 +00:00
|
|
|
[`exFalso` shared a
|
2023-08-25 03:29:54 +00:00
|
|
|
workaround](https://github.com/actions/upload-artifact/issues/92#issuecomment-1080347032).
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
- run: nix build --accept-flake-config -L
|
|
|
|
# Convoluted upload below is a workaround for #92
|
|
|
|
# See:
|
|
|
|
# - https://github.com/actions/upload-artifact/issues/92
|
|
|
|
# - https://github.com/actions/upload-artifact/issues/92#issuecomment-1080347032
|
|
|
|
- run: echo "UPLOAD_PATH=$(readlink -f result)" >> "$GITHUB_ENV"
|
|
|
|
- uses: actions/upload-artifact@v3
|
|
|
|
with:
|
|
|
|
name: built-site
|
|
|
|
path: ${{ env.UPLOAD_PATH }}
|
|
|
|
```
|
|
|
|
|
|
|
|
Building the site (running `hugo`, `htmlproofer`, and whatever else I decide to
|
|
|
|
add to my build process) is done with a single call to `nix build`. The output
|
|
|
|
lives in a `result/` directory which I upload as [a build
|
|
|
|
artifact](https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts)
|
|
|
|
so it can be deployed later (if the commit being checked is on the `main`
|
|
|
|
branch).
|
|
|
|
|
|
|
|
The deploy step configures some secrets and uses hugo's provided deploy subcommand.
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
- uses: actions/download-artifact@v3
|
|
|
|
with:
|
|
|
|
name: built-site
|
|
|
|
path: public/
|
|
|
|
- name: Deploy
|
|
|
|
run: |
|
|
|
|
sed 's~{{S3URL}}~${{ secrets.S3URL }}~g' "${PROD_DEPLOY_CONFIG_PATH}.sample" > "${PROD_DEPLOY_CONFIG_PATH}"
|
|
|
|
sed -i 's~{{CLOUDFRONTDISTRIBUTIONID}}~${{ secrets.CLOUDFRONTDISTRIBUTIONID }}~g' "${PROD_DEPLOY_CONFIG_PATH}"
|
|
|
|
nix develop --accept-flake-config --impure --command bash \
|
|
|
|
-c 'hugo deploy --invalidateCDN'
|
|
|
|
```
|
|
|
|
|
2023-08-28 00:27:25 +00:00
|
|
|
## "...cool I guess?"
|
2023-08-25 03:29:54 +00:00
|
|
|
|
2023-08-28 00:27:25 +00:00
|
|
|
So yeah, I have exactly the same site now. Nothing changes from a reader's
|
|
|
|
perspective but this scratched my tinkering itch.
|