just: run colmena, nvd and nixos-anywhere through the dev shell, so recipes work from any shell
Recipes needed to run inside nix develop, or just deploy failed with 'colmena: command not found'. The three tools only the dev shell has are now called as {{ dev }} <tool> (nix develop <fleet> --command); jq, ssh, nix and secrets come from the system PATH. Checked from a plain shell: eval, diff-host, deploy dry-activate.
2 files changed,  +15, -11
M Justfile
+14, -10
 1@@ -1,20 +1,24 @@
 2-# Fleet workflow (README.md, docs/fleet-migration-plan.md). Run inside
 3-# `nix develop`.
 4+# Fleet workflow (README.md, docs/fleet-migration-plan.md). Runs from any
 5+# shell: the tools only the dev shell has (colmena, nvd, nixos-anywhere) are
 6+# called through `dev`; jq, ssh, nix and `secrets` come from the system.
 7 
 8 # The `secrets` store (age-encrypted, one file per secret).
 9 export SECRETS_DIR := env("SECRETS_DIR", home_directory() / "projects/data")
10 
11+# Runs a command from this flake's dev shell (the pinned colmena 0.5.0 etc.).
12+dev := "nix develop " + quote(justfile_directory()) + " --command"
13+
14 # List recipes.
15 default:
16     @just --list
17 
18 # Evaluate every host (fast sanity check; no builds).
19 eval:
20-    colmena eval -E '{ nodes, ... }: builtins.mapAttrs (n: v: v.config.system.build.toplevel.drvPath) nodes'
21+    {{ dev }} colmena eval -E '{ nodes, ... }: builtins.mapAttrs (n: v: v.config.system.build.toplevel.drvPath) nodes'
22 
23 # Build host systems without deploying (all hosts, or one).
24 build HOST="":
25-    colmena build {{ if HOST == "" { "" } else { "--on " + HOST } }}
26+    {{ dev }} colmena build {{ if HOST == "" { "" } else { "--on " + HOST } }}
27 
28 # Builds HOST's system, fetches the one it runs, and compares them with nvd.
29 # "identical" means a deploy would change nothing.
30@@ -31,14 +35,14 @@ diff-host HOST:
31       exit 0
32     fi
33     nix copy --no-check-sigs --from "ssh-ng://root@{{ HOST }}" "$running"
34-    nvd diff "$running" "$new"
35+    {{ dev }} nvd diff "$running" "$new"
36 
37 # Refuses to start if any secret HOST declares is missing from the store.
38 # Extra args go to `colmena apply`: `just deploy HOST dry-activate`,
39 # `just deploy HOST --reboot`.
40 # Deploy HOST with Colmena (build, copy, upload keys, switch).
41 deploy HOST *ARGS: (check-secrets HOST)
42-    colmena apply {{ ARGS }} --on {{ HOST }}
43+    {{ dev }} colmena apply {{ ARGS }} --on {{ HOST }}
44 
45 # Every host with a role from PROJECT ("moods" or "sovrn") gets the new
46 # version; the lock bump is the deploy log, so commit it afterwards.
47@@ -50,7 +54,7 @@ deploy-project PROJECT *ARGS:
48     hosts="$(jq -r --arg p "{{ PROJECT }}" '[to_entries[] | select(any(.value.roles[]?; . == $p or startswith($p + "-"))) | .key] | join(",")' hosts.json)"
49     [ -n "$hosts" ] || { echo "no host in hosts.json runs a {{ PROJECT }} role" >&2; exit 1; }
50     for h in ${hosts//,/ }; do just check-secrets "$h"; done
51-    colmena apply {{ ARGS }} --on "$hosts"
52+    {{ dev }} colmena apply {{ ARGS }} --on "$hosts"
53     echo "deployed {{ PROJECT }} $(jq -r '.nodes["{{ PROJECT }}"].locked.rev' flake.lock) to $hosts; commit flake.lock"
54 
55 # Covers every project's secrets: reads the `secrets decrypt <path>` key
56@@ -111,8 +115,8 @@ new-host IP HOSTNAME PROVIDER ROLES:
57     [[ "$HOST" =~ ^[a-z0-9-]+(\.[a-z0-9-]+)+$ ]] || fail "HOSTNAME must be a fully qualified lowercase name, got: $HOST"
58     case "$PROVIDER" in hetzner|netcup) ;; *) fail "PROVIDER must be hetzner or netcup, got: $PROVIDER" ;; esac
59     [[ "$ROLES" =~ ^[a-z0-9-]+(,[a-z0-9-]+)*$ ]] || fail "ROLES must be comma-separated role names, got: $ROLES"
60-    for bin in nixos-anywhere secrets jq ssh ssh-keygen ssh-copy-id; do
61-      command -v "$bin" >/dev/null || fail "$bin not on PATH (run inside nix develop)"
62+    for bin in secrets jq ssh ssh-keygen ssh-copy-id; do
63+      command -v "$bin" >/dev/null || fail "$bin not on PATH"
64     done
65     if jq -e --arg h "$HOST" '.[$h].layout == "legacy"' "$INVENTORY" >/dev/null; then
66       fail "$HOST is a legacy-layout host; new-host installs the standard layout (see plan 2.8)"
67@@ -206,7 +210,7 @@ new-host IP HOSTNAME PROVIDER ROLES:
68     jj st >/dev/null 2>&1 || true
69 
70     # 4. Install (reboots into NixOS at the end).
71-    nixos-anywhere --flake ".#$HOST" -i "$SSH_KEY" --extra-files "$tmp/extra" --target-host "root@$IP"
72+    {{ dev }} nixos-anywhere --flake ".#$HOST" -i "$SSH_KEY" --extra-files "$tmp/extra" --target-host "root@$IP"
73 
74     # 5. Verify the pinned key, then trust it.
75     echo "$HOST,$IP $PUB" > "$tmp/known_hosts"
M README.md
+1, -1
1@@ -22,7 +22,7 @@ The fleet: every NixOS host, deployed with [Colmena](https://github.com/zhaofeng
2 
3 ## Workflow
4 
5-Everything runs inside `nix develop` (colmena, nvd, nixos-anywhere, just). `secrets` (the age-encrypted store CLI) must be on `PATH`, with `SECRETS_DIR` (default `~/projects/data`).
6+The recipes run from any shell: the tools only the dev shell provides (colmena, nvd, nixos-anywhere) are called through `nix develop --command` inside the `Justfile`. On `PATH` you need `just`, `nix`, `jq`, `ssh`/`ssh-keygen`/`ssh-copy-id`, and `secrets` (the age-encrypted store CLI) with `SECRETS_DIR` (default `~/projects/data`). `nix develop` is only for running those tools by hand.
7 
8 | Command | What |
9 | --- | --- |