Files
nixos-at-home/Taskfile.yml
2026-05-31 09:33:14 +00:00

244 lines
9.1 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ------------------------------------------------------------
# Taskfile Dockerbased Nix development & Colmena deployment
# ------------------------------------------------------------
version: "3"
vars:
# Base directory of the repository pass it when you invoke `task`,
# e.g. `task -v ROOT_DIR=$(pwd) …`. You can also export it in your shell.
ROOT_DIR: "{{ .ROOT_DIR }}"
# Convenience paths (expanded by `task` before being passed to Docker)
FLAKE_DIR: "{{ .ROOT_DIR }}"
AGE_KEY_PATH: "{{ .ROOT_DIR }}/age.key"
SECRETS_YAML_PATH: "{{ .ROOT_DIR }}/hosts/blade/secrets.yaml"
# -----------------------------------------------------------------
# Docker helpers (the Nix container always gets the repo mounted at
# /workspace). `DOCKER_NIX_CMD` is used for *any* command that runs
# inside the container.
# -----------------------------------------------------------------
DOCKER_COMPOSE_CMD: "docker compose -f {{ .ROOT_DIR }}/docker-compose.yml"
DOCKER_NIX_CMD: >-
{{ .DOCKER_COMPOSE_CMD }} run --rm
-v "{{ .ROOT_DIR }}:/workspace"
nixos
# -----------------------------------------------------------------
# Docker management
# -----------------------------------------------------------------
tasks:
docker-build:
desc: Build the Docker container for Nix
cmds:
- "{{ .DOCKER_COMPOSE_CMD }} build"
dir: "{{ .ROOT_DIR }}"
# ---------------------------------------------------------------
# NOTE: we now use DOCKER_NIX_CMD (which already has the bindmount)
# ---------------------------------------------------------------
docker-shell:
desc: Enter a shell in the Docker container (repo is mounted)
cmds:
- "{{ .DOCKER_NIX_CMD }} bash"
dir: "{{ .ROOT_DIR }}"
docker-up:
desc: Start the Docker container (detached)
cmds:
- "{{ .DOCKER_COMPOSE_CMD }} up -d"
dir: "{{ .ROOT_DIR }}"
docker-down:
desc: Stop the Docker container
cmds:
- "{{ .DOCKER_COMPOSE_CMD }} down"
dir: "{{ .ROOT_DIR }}"
# -----------------------------------------------------------------
# Agekey handling (SOPS)
# -----------------------------------------------------------------
generate-age-key:
desc: Generate a new age key for a host (default - all hosts without a key)
vars:
# If HOST is supplied we generate only for that host; otherwise we
# iterate over every directory under `hosts/` that lacks an age.key.
TARGET_HOSTS: |
{{- if .HOST -}}
{{ .HOST }}
{{- else -}}
{{- range $dir := (files (printf "%s/hosts/*" .ROOT_DIR)) -}}
{{- $name := base $dir -}}
{{- if not (fileExists (printf "%s/age.key" $dir)) -}}
{{ $name }}
{{- end -}}
{{- end -}}
{{- end }}
cmds:
- |
{{- $list := splitList "\n" .TARGET_HOSTS -}}
{{- range $h := $list }}
echo "🔑 Generating age.key for host '{{ $h }}' ..."
{{ .DOCKER_NIX_CMD }} -v "{{ .ROOT_DIR }}/hosts/{{ $h }}:/workspace/hosts/{{ $h }}" \
nixos age-keygen -o /workspace/hosts/{{ $h }}/age.key
{{- end }}
dir: "{{ .ROOT_DIR }}"
# -----------------------------------------------------------------
# Encrypt / Decrypt (loop over hosts)
# -----------------------------------------------------------------
encrypt-secrets:
desc: |
Encrypt `secrets.yaml` for every host that has an age.key.
Pass HOST=<name> to limit to a single host.
vars:
# Spaceseparated list of hosts that have both age.key & secrets.yaml
TARGET_HOSTS: |
{{- if .HOST -}}
{{ .HOST }}
{{- else -}}
{{- $list := slice -}}
{{- range $dir := (files (printf "%s/hosts/*" .ROOT_DIR)) -}}
{{- $name := base $dir -}}
{{- $age := printf "%s/age.key" $dir -}}
{{- $sec := printf "%s/secrets.yaml" $dir -}}
{{- if (and (fileExists $age) (fileExists $sec)) -}}
{{- $list = append $list $name -}}
{{- end -}}
{{- end -}}
{{- join " " $list -}}
{{- end }}
preconditions:
- sh: test -n "{{ .TARGET_HOSTS }}"
msg: "No hosts with both age.key and secrets.yaml were found."
cmds:
- |
for host in {{ .TARGET_HOSTS }}; do
echo "🔐 Encrypting secrets for host '$host' ..."
{{ .DOCKER_NIX_CMD }} -v "{{ .ROOT_DIR }}/hosts/$host:/workspace/hosts/$host" \
nixos sh -c "\
pub=\$(grep 'public key' /workspace/hosts/$host/age.key | awk '{print \$NF}') && \
sops --encrypt --age=\$pub --in-place /workspace/hosts/$host/secrets.yaml"
done
dir: "{{ .ROOT_DIR }}"
decrypt-secrets:
desc: |
Decrypt `secrets.yaml` for every host that has an age.key.
Decrypted output is written to `secrets.yaml.decrypted`.
Pass HOST=<name> to limit to a single host.
vars:
TARGET_HOSTS: |
{{- if .HOST -}}
{{ .HOST }}
{{- else -}}
{{- $list := slice -}}
{{- range $dir := (files (printf "%s/hosts/*" .ROOT_DIR)) -}}
{{- $name := base $dir -}}
{{- $age := printf "%s/age.key" $dir -}}
{{- $sec := printf "%s/secrets.yaml" $dir -}}
{{- if (and (fileExists $age) (fileExists $sec)) -}}
{{- $list = append $list $name -}}
{{- end -}}
{{- end -}}
{{- join " " $list -}}
{{- end }}
preconditions:
- sh: test -n "{{ .TARGET_HOSTS }}"
msg: "No hosts with both age.key and secrets.yaml were found."
cmds:
- |
for host in {{ .TARGET_HOSTS }}; do
echo "🔓 Decrypting secrets for host '$host' ..."
{{ .DOCKER_NIX_CMD }} -v "{{ .ROOT_DIR }}/hosts/$host:/workspace/hosts/$host" \
nixos sh -c "\
pub=\$(grep '# public key' /workspace/hosts/$host/age.key | awk '{print \$NF}') && \
sops --decrypt --age=\$pub /workspace/hosts/$host/secrets.yaml \
> /workspace/hosts/$host/secrets.yaml.decrypted"
done
dir: "{{ .ROOT_DIR }}"
# -----------------------------------------------------------------
# Nix flake & build
# -----------------------------------------------------------------
flake-check:
desc: Run `nix flake check` on the repository
cmds:
- "{{ .DOCKER_NIX_CMD }} nix flake check --flake /workspace"
dir: "{{ .ROOT_DIR }}"
build-blade:
desc: Build the NixOS configuration for the *blade* host
cmds:
- "{{ .DOCKER_NIX_CMD }} nix build /workspace#nixosConfigurations.blade"
dir: "{{ .ROOT_DIR }}"
# -----------------------------------------------------------------
# Colmena deployment
# -----------------------------------------------------------------
deploy:
desc: Deploy to an arbitrary node (usage - task deploy NODE=blade)
cmds:
- "{{ .DOCKER_NIX_CMD }} colmena apply --on {{ .NODE }}"
dir: "{{ .ROOT_DIR }}"
preconditions:
- sh: test -n "{{ .NODE }}"
msg: "NODE variable is required. Example: task deploy NODE=blade"
- sh: test -f "{{ .ROOT_DIR }}/age.key"
- sh: test -f "{{ .ROOT_DIR }}/hosts/{{ .NODE }}/secrets.yaml"
deploy-blade:
desc: Deploy the configuration to the *blade* host
cmds:
- "{{ .DOCKER_NIX_CMD }} colmena apply --on blade"
dir: "{{ .ROOT_DIR }}"
preconditions:
- sh: test -f "{{ .ROOT_DIR }}/age.key"
- sh: test -f "{{ .ROOT_DIR }}/hosts/blade/secrets.yaml"
# -----------------------------------------------------------------
# Helper: add a new node
# -----------------------------------------------------------------
add-node:
desc: Create a new host directory with skeleton Nix files
prompt: "Enter the name of the new node (e.g., node2):"
cmds:
- mkdir -p "{{ .FLAKE_DIR }}/hosts/{{ .CLI_ARGS }}"
- |
cat > "{{ .FLAKE_DIR }}/hosts/{{ .CLI_ARGS }}/configuration.nix" <<'EOF'
{ config, lib, pkgs, ... }:
{
imports = [
./hardware-configuration.nix
../../common.nix
];
networking.hostName = "{{ .CLI_ARGS }}";
}
EOF
- |
cat > "{{ .FLAKE_DIR }}/hosts/{{ .CLI_ARGS }}/hardware-configuration.nix" <<'EOF'
# Add hardwarespecific configurations here
{ config, lib, pkgs, modulesPath, ... }:
{
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
}
EOF
- |
cat > "{{ .FLAKE_DIR }}/hosts/{{ .CLI_ARGS }}/secrets.yaml" <<'EOF'
# Add nodespecific secrets here
EOF
- |
cat > "{{ .FLAKE_DIR }}/hosts/{{ .CLI_ARGS }}/default.nix" <<'EOF'
{ ... }: { imports = [ ./configuration.nix ./hardware-configuration.nix ../../common.nix ]; }
EOF
dir: "{{ .ROOT_DIR }}"
# -----------------------------------------------------------------
# Housekeeping
# -----------------------------------------------------------------
clean:
desc: Remove temporary/decrypted secret files for all hosts
cmds:
- |
find "{{ .ROOT_DIR }}/hosts" -name 'secrets.yaml.decrypted' -delete
silent: true