Tutorial: Building our Astro Starlight page with Nix & flake.parts
Recently, I migrated the unwieldy README.md of crate2nix to a much more structured GitHub page. After some research, I settled on Astro with the Starlight Theme since I had head good things about Astro at the NixCon 2023 in Darmstadt (happy times!).
This is an especially appealing choice if you want to
- start easy with mostly simple documentation in
*.md
files, but - potentially later extend it with some client-side logic with React.
But on we go! Let's get our GitHub Page up and running.
What you need to get started
This is a follow up on Tutorial: Using flake-parts to set up a nodejs devshell. We assume that you already have set up
- a flake-based project using flake.parts, and
- setup a devshell with the right version of
npm
.
Creating the docs directory
Now that we have npm
at hand, we can follow the instructions of Starlight
on how to get started and answer the prompts as they come:
❯ npm create astro@latest -- --template starlight
astro Launch sequence initiated.
dir Where should we create your new project?
./docs
◼ tmpl Using starlight as project template
deps Install dependencies?
Yes
ts Do you plan to write TypeScript?
Yes
use How strict should TypeScript be?
Strict
git Initialize a new git repository?
No
◼ Sounds good! You can always run git init manually.
✔ Project initialized!
■ Template copied
■ Dependencies installed
■ TypeScript customized
next Liftoff confirmed. Explore your project!
Enter your project directory using cd ./docs
Run npm run dev to start the dev server. CTRL+C to stop.
Add frameworks like react or tailwind using astro add.
Stuck? Join us at https://astro.build/chat
╭──🎁─╮ Houston:
│ ◠ ◡ ◠ Good luck out there, astronaut! 🚀
╰─────╯
Local development
For local development, you can use a fast turn-around npm-based workflow:
❯ npm run dev
> docs@0.0.1 dev
> astro dev
astro v4.1.1 ready in 590 ms
┃ Local http://localhost:4321/
┃ Network use --host to expose
23:13:21 [WARN] [content] The i18n collection is defined but no content/i18n folder exists in the content directory. Create a new folder for the collection, or check your content configuration file for typos.
23:13:21 watching for file changes...
The exposed web server will update as you edit or display any potential errors.
The npm
and node
are supplied by nix and all
collaborators with nix and direnv will seamlessly
have the same version available.
But what about reproducible production builds?
Hermetic build with nix
Let's say we are satisfied with our edits and want a reproducible
build of our docs. Copy the following as flake-module.nix
into your docs folder:
# docs/flake-module.nix
{
perSystem = { config, self', inputs', pkgs, lib, system, ... }: {
packages.docs = pkgs.buildNpmPackage {
pname = "docs";
version = "0.1.0";
inherit (config.packages) nodejs;
src = ./.;
buildInputs = [
pkgs.vips
];
nativeBuildInputs = [
pkgs.pkg-config
];
installPhase = ''
runHook preInstall
cp -pr --reflink=auto dist $out/
runHook postInstall
'';
npmDepsHash = "";
};
};
}
You remember our drill from the last article? You have to
add the new ./docs/flake-module.nix
to the imports
in your flake.nix
and ensure that all relevant files
have been added to the git index with git add
.
You can now try to build your docs like this:
nix build -L .#docs
Did you notice the empty string for the npmDepsHash
before? Well, now nix will complain about this while also
telling us the expected value:
...
warning: found empty hash, assuming 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='
error: hash mismatch in fixed-output derivation '/nix/store/mpnydavlxdww9fv2n2aic5mwnapnch7f-docs-0.1.0-npm-deps.drv':
specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
got: sha256-iyz7+GeVYNDa0cLlz8PGmTNAQVetnt87ndfP0vUjxLw=
error: 1 dependencies of derivation '/nix/store/dcrayn9gy58hk0raq53ssca3h2js3alp-docs-0.1.0.drv' failed to build
We'll update npmDepsHash
accordingly in our flake-module.nix
:
{
# ...
npmDepsHash = "sha256-iyz7+GeVYNDa0cLlz8PGmTNAQVetnt87ndfP0vUjxLw=";
# ...
}
NOTE: Your hash value will be different if any of the dependencies have a new version in the starlight template! Use the hash that nix has output behind "got."
Now nix build .#docs
should build your docs, store them in the /nix/store
and provide a symlink called result
to them:
❯ ls -al result/
total 72
dr-xr-xr-x 9 root wheel 288 Jan 1 1970 .
drwxrwxr-t@ 65535 root nixbld 5130176 Jan 11 23:33 ..
-r--r--r-- 1 root wheel 8474 Jan 1 1970 404.html
dr-xr-xr-x 10 root wheel 320 Jan 1 1970 _astro
-r--r--r-- 1 root wheel 696 Jan 1 1970 favicon.svg
dr-xr-xr-x 3 root wheel 96 Jan 1 1970 guides
-r--r--r-- 1 root wheel 17758 Jan 1 1970 index.html
dr-xr-xr-x 14 root wheel 448 Jan 1 1970 pagefind
dr-xr-xr-x 3 root wheel 96 Jan 1 1970 reference
Woooo! Our docs!
.gitignore
Well, when committing our changes, we'll see the "result" symlink.
We should add it to .gitignore
:
echo "result*" >>.gitignore
The star makes it also cover builds with multiple results.
Why didn't we create a .gitignore
for the npm_modules
etc? It
was already done for us by the starlight template.
Conclusion
It was easy to build our Starlight page with nix. But this is mostly useful, if we also deploy it somewhere!
Let's do that in the next episode of this tutorial.
Feel free to give me feedback/ask questions at discourse or in a GitHub issue. I want to hear your thoughts: so feel free to err on the side of commenting too much.