244 lines
9.1 KiB
YAML
244 lines
9.1 KiB
YAML
# ------------------------------------------------------------
|
||
# Taskfile – Docker‑based 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 bind‑mount)
|
||
# ---------------------------------------------------------------
|
||
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 }}"
|
||
|
||
# -----------------------------------------------------------------
|
||
# Age‑key 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:
|
||
# Space‑separated 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 hardware‑specific configurations here
|
||
{ config, lib, pkgs, modulesPath, ... }:
|
||
{
|
||
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
|
||
}
|
||
EOF
|
||
- |
|
||
cat > "{{ .FLAKE_DIR }}/hosts/{{ .CLI_ARGS }}/secrets.yaml" <<'EOF'
|
||
# Add node‑specific secrets here
|
||
EOF
|
||
- |
|
||
cat > "{{ .FLAKE_DIR }}/hosts/{{ .CLI_ARGS }}/default.nix" <<'EOF'
|
||
{ ... }: { imports = [ ./configuration.nix ./hardware-configuration.nix ../../common.nix ]; }
|
||
EOF
|
||
dir: "{{ .ROOT_DIR }}"
|
||
|
||
# -----------------------------------------------------------------
|
||
# House‑keeping
|
||
# -----------------------------------------------------------------
|
||
clean:
|
||
desc: Remove temporary/decrypted secret files for all hosts
|
||
cmds:
|
||
- |
|
||
find "{{ .ROOT_DIR }}/hosts" -name 'secrets.yaml.decrypted' -delete
|
||
silent: true |