Compare commits
4 Commits
f1cc0fa25c
...
90cc2d53f1
| Author | SHA1 | Date | |
|---|---|---|---|
| 90cc2d53f1 | |||
| b044504938 | |||
| 98ccc23ef5 | |||
| d7e8ca52a0 |
@@ -7,10 +7,10 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
name: Check, build and cache nixfiles
|
name: Check, build and cache nixfiles
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-26.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: cachix/install-nix-action@v27
|
- uses: cachix/install-nix-action@v31
|
||||||
with:
|
with:
|
||||||
# Gitea will supply a token in GITHUB_TOKEN, which this action will
|
# Gitea will supply a token in GITHUB_TOKEN, which this action will
|
||||||
# try to pass to Nix when downloading from GitHub
|
# try to pass to Nix when downloading from GitHub
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
This file provides guidance to coding agents when working with code in this repository.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Personal Nix flake managing NixOS systems and home-manager configurations for a fleet of
|
||||||
|
machines (servers, home boxes, routers). It is built around a **custom module system** layered
|
||||||
|
on top of NixOS/home-manager, not the stock flake `nixosConfigurations` pattern.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
This repo provides a `numtide/devshell` (entered via `direnv` / `use flake`). The shell defines
|
||||||
|
named commands — prefer them over raw `nix` invocations. Run a bare command name with no args to
|
||||||
|
see its help, or browse `devshell/commands.nix` / `devshell/install.nix` / `devshell/vm-tasks.nix`.
|
||||||
|
|
||||||
|
Common ones:
|
||||||
|
- `fmt` — format Nix with `nixpkgs-fmt` (the canonical formatter here).
|
||||||
|
- `build-system <host> [nix args]` — build a NixOS system's `toplevel`.
|
||||||
|
- `build-n-switch <args>` — wraps `doas nixos-rebuild --flake .`.
|
||||||
|
- `build-home <name>` / `home-switch` — build / switch a home-manager config.
|
||||||
|
- `run-vm <host>` — build & boot a system as a dev VM (installs `.keys/dev.key` into the VM).
|
||||||
|
- `build-iso` / `build-kexec` / `build-netboot <host>` — alternate build outputs via `config.my.buildAs.*`.
|
||||||
|
- `check-system <host> [nix args]` — evaluate a system (catches eval errors without a full build).
|
||||||
|
**Prefer this over `build-system` to validate a config change** — evaluation surfaces module/option
|
||||||
|
errors quickly and cheaply; only do a full build when you specifically need the built artifact.
|
||||||
|
- `deploy <host>` and `deploy-multi <hosts...>` — deploy-rs deployment (uses `.keys/deploy.key`, `--skip-checks`).
|
||||||
|
Pass the flake-qualified node, e.g. `deploy .#git`. The deploy node name is **always** the system
|
||||||
|
name (`deploy-rs.nix` keys nodes directly off `nixos.systems` / `home-manager.homes`); a system is
|
||||||
|
only a deploy target when `config.my.deploy.enable` is true (defaults true; auto-disabled for dev
|
||||||
|
VMs and containers).
|
||||||
|
- `ssh-machine <name> [cmd]` — SSH to a NixOS system or home-manager config by name. Resolves the
|
||||||
|
target and ssh options (identity, port) from its deploy-rs node, so it needs `my.deploy.enable`
|
||||||
|
(same gate as `deploy`).
|
||||||
|
- `ragenix` — edit age secrets using `.keys/dev.key` as identity (see Secrets).
|
||||||
|
- `repl` — `nix repl .#`.
|
||||||
|
- `update-nixpkgs` / `update-home-manager` — bump pinned inputs.
|
||||||
|
|
||||||
|
Check everything (what CI runs): `nix flake check --no-build`.
|
||||||
|
CI builds each attr of `.#ci.x86_64-linux` (systems, homes, packages, shell) and pushes to the
|
||||||
|
Harmonia binary cache; see `.gitea/workflows/ci.yaml` and `ci/push-to-cache.sh`.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### The custom module system
|
||||||
|
`flake.nix` does **not** call `nixosSystem` per host directly. Instead it `evalModules` over
|
||||||
|
`./nixos`, `./home-manager`, `./deploy-rs.nix`, and the per-host files listed in the `configs`
|
||||||
|
list in `flake.nix`. That evaluation produces a top-level config (`self.nixfiles`) from which the
|
||||||
|
real flake outputs are derived:
|
||||||
|
- `nixos.systems.<name>` → `nixosConfigurations.<name>`
|
||||||
|
- `home-manager.homes.<name>` → `homeConfigurations.<name>`
|
||||||
|
- `nixos.modules` / `home-manager.modules` → `nixosModules` / `homeModules`
|
||||||
|
- `deploy-rs.rendered` → `deploy`
|
||||||
|
|
||||||
|
`nixos/default.nix` and `home-manager/default.nix` define the `systemOpts` / `homeOpts` submodules
|
||||||
|
and the `mkSystem` / `mkHome` functions that actually invoke `eval-config.nix` /
|
||||||
|
`homeManagerConfiguration`. **To add a new host:** create a box file that sets
|
||||||
|
`nixos.systems.<name> = { ... }`, then add its path to the `configs` list in `flake.nix`.
|
||||||
|
|
||||||
|
### Multiple nixpkgs channels
|
||||||
|
Four pkgs sets are threaded everywhere as `pkgsFlakes` / `pkgs'` (and `hmFlakes` for home-manager):
|
||||||
|
`unstable`, `stable`, `mine` (a personal nixpkgs fork), `mine-stable`. Each system/home picks its
|
||||||
|
channel via `nixpkgs` / `home-manager` / `hmNixpkgs` options (e.g. `nixpkgs = "mine-stable"`).
|
||||||
|
Modules receive `pkgs'` = an attrset of all channels for the current system.
|
||||||
|
|
||||||
|
### `lib.my` and the `my` option namespace
|
||||||
|
`lib/default.nix` extends `lib` with a `my` attrset (helpers like `mkOpt'`, `mkBoolOpt'`,
|
||||||
|
`mkDefault'`, `inlineModule'`, `mkDefaultSystemsPkgs`, `homeStateVersion`). It also pulls in:
|
||||||
|
- `lib.my.net` — network/CIDR helpers from the `libnetRepo` input. Used heavily for IP math.
|
||||||
|
- `lib.my.c` — shared constants from `lib/constants.nix` (UIDs/GIDs, kernel package selection,
|
||||||
|
nginx snippets, per-network domains/prefixes, etc.). Reuse these rather than hardcoding.
|
||||||
|
- `lib.my.dns` — DNS helpers (`lib/dns.nix`).
|
||||||
|
|
||||||
|
Custom modules add options under the `my.*` namespace (e.g. `my.secrets`, `my.build`,
|
||||||
|
`my.tmproot`, `my.server`). Use `mkOpt'`/`mkBoolOpt'` for option declarations to match style.
|
||||||
|
|
||||||
|
### Modules and module lists
|
||||||
|
Module sets are registered in `nixos/modules/_list.nix` and `home-manager/modules/_list.nix`
|
||||||
|
(name → path), which become `nixos.modules` / `home-manager.modules` and are applied to every
|
||||||
|
system/home. To add a shared module, drop the file in `nixos/modules/` (or `home-manager/modules/`)
|
||||||
|
and add an entry to the relevant `_list.nix`.
|
||||||
|
|
||||||
|
### Network assignments
|
||||||
|
Each system declares `assignments.<name>` (in its `nixos.systems.<host>` block) with IPv4/IPv6
|
||||||
|
addresses, gateways, domains, MTU, etc. These are aggregated into `allAssignments` (passed to every
|
||||||
|
module) and there is an assertion that fails on duplicate IPs. Host networking
|
||||||
|
(`networking.hostName`, `domain`) defaults from the `internal` assignment.
|
||||||
|
|
||||||
|
### Hosts / "boxes"
|
||||||
|
Per-host configs live under `nixos/boxes/<host>` (some are single `.nix` files, some directories
|
||||||
|
with nested VMs/containers under e.g. `colony/vms`). Many "systems" are VMs or containers managed
|
||||||
|
via the `vms` / `containers` modules and the `l2mesh` VXLAN module.
|
||||||
|
|
||||||
|
## Secrets
|
||||||
|
|
||||||
|
age-encrypted secrets in `secrets/`, managed with **ragenix**. Each module declares
|
||||||
|
`my.secrets.files.<name>` and `my.secrets.key` (the host pubkey to encrypt for). `secrets.nix`
|
||||||
|
(the ragenix rules file) is generated by reading every system's declared secrets and computing the
|
||||||
|
recipient key list (always including `.keys/dev.pub`). Edit secrets with the `ragenix` devshell
|
||||||
|
command, which supplies `.keys/dev.key` as the identity. The `.keys/` directory (dev + deploy
|
||||||
|
private keys) is required for editing secrets, deploying, and running dev VMs.
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
- Format with `nixpkgs-fmt` (`fmt`). 2-space indent, `inherit (...)` blocks at the top of `let`.
|
||||||
|
- Prefer `lib.my` helpers (`mkOpt'`, `mkBoolOpt'`, `mkDefault'`) and `lib.my.c` constants over
|
||||||
|
reimplementing.
|
||||||
|
- New shared functionality → a module in `*/modules/` + entry in `_list.nix`, options under `my.*`.
|
||||||
|
- New host → box file under `nixos/boxes/` + entry in the `configs` list in `flake.nix`.
|
||||||
|
- Custom packages live in `pkgs/` and are registered in `pkgs/default.nix`; the overlay is exposed
|
||||||
|
as `overlays.default`.
|
||||||
|
- In prose and commit messages, quote code-like identifiers (commands, options, paths, package and
|
||||||
|
attribute names) in backticks.
|
||||||
@@ -48,6 +48,25 @@ in
|
|||||||
help = "Print the ed25519 pubkey for a host";
|
help = "Print the ed25519 pubkey for a host";
|
||||||
command = "${pkgs.openssh}/bin/ssh-keyscan -t ed25519 \"$1\" 2> /dev/null | awk '{ print $2 \" \" $3 }'";
|
command = "${pkgs.openssh}/bin/ssh-keyscan -t ed25519 \"$1\" 2> /dev/null | awk '{ print $2 \" \" $3 }'";
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
name = "ssh-machine";
|
||||||
|
category = "utilities";
|
||||||
|
help = "SSH to a machine by NixOS system or home-manager config name";
|
||||||
|
command = ''
|
||||||
|
name="$1"
|
||||||
|
shift
|
||||||
|
# Run from the project root so deploy's relative identity path (.keys/deploy.key) resolves
|
||||||
|
cd "$PRJ_ROOT"
|
||||||
|
# deploy-rs node names are the system/home name, with `@` mangled to `-at-`
|
||||||
|
node="''${name//@/-at-}"
|
||||||
|
# Single eval: resolve `user@host` plus merged (global + node) deploy-rs ssh options
|
||||||
|
info="$(nix eval --raw .#deploy --apply 'd: let n = d.nodes."'"$node"'"; in "''${n.sshUser}@''${n.hostname} ''${builtins.concatStringsSep " " (d.sshOpts ++ n.sshOpts)}"')"
|
||||||
|
target="''${info%% *}"
|
||||||
|
opts="''${info#* }"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
exec ${pkgs.openssh}/bin/ssh $opts "$target" "$@"
|
||||||
|
'';
|
||||||
|
}
|
||||||
{
|
{
|
||||||
name = "json2nix";
|
name = "json2nix";
|
||||||
category = "utilities";
|
category = "utilities";
|
||||||
@@ -73,6 +92,12 @@ in
|
|||||||
help = "Build NixOS configuration";
|
help = "Build NixOS configuration";
|
||||||
command = ''nix build "''${@:2}" ".#nixosConfigurations.\"$1\".config.system.build.toplevel"'';
|
command = ''nix build "''${@:2}" ".#nixosConfigurations.\"$1\".config.system.build.toplevel"'';
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
name = "check-system";
|
||||||
|
category = "utilities";
|
||||||
|
help = "Evaluate NixOS configuration (check validity without building)";
|
||||||
|
command = ''nix eval "''${@:2}" ".#nixosConfigurations.\"$1\".config.system.build.toplevel.drvPath"'';
|
||||||
|
}
|
||||||
{
|
{
|
||||||
name = "build-n-switch";
|
name = "build-n-switch";
|
||||||
category = "tasks";
|
category = "tasks";
|
||||||
|
|||||||
@@ -1,19 +1,7 @@
|
|||||||
{ lib, pkgs, config, ... }:
|
{ lib, pkgs, config, ... }:
|
||||||
let
|
let
|
||||||
inherit (builtins) toJSON;
|
|
||||||
inherit (lib) mkForce;
|
inherit (lib) mkForce;
|
||||||
inherit (lib.my.c) pubDomain;
|
inherit (lib.my.c) pubDomain;
|
||||||
|
|
||||||
cfgFile = pkgs.writeText "gitea-actions-runner.yaml" (toJSON {
|
|
||||||
container = {
|
|
||||||
network = "podman";
|
|
||||||
privileged = true;
|
|
||||||
};
|
|
||||||
cache = {
|
|
||||||
enabled = true;
|
|
||||||
dir = "/var/cache/gitea-runner";
|
|
||||||
};
|
|
||||||
});
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = {
|
config = {
|
||||||
@@ -30,8 +18,8 @@ in
|
|||||||
enable = true;
|
enable = true;
|
||||||
name = "main-docker";
|
name = "main-docker";
|
||||||
labels = [
|
labels = [
|
||||||
"debian-node-bullseye:docker://node:18-bullseye"
|
"debian-node-trixie:docker://node:24-trixie"
|
||||||
"ubuntu-22.04:docker://git.nul.ie/dev/actions-ubuntu:22.04"
|
"ubuntu-26.04:docker://git.nul.ie/dev/actions-ubuntu:26.04"
|
||||||
];
|
];
|
||||||
url = "https://git.${pubDomain}";
|
url = "https://git.${pubDomain}";
|
||||||
tokenFile = config.age.secrets."gitea/actions-runner.env".path;
|
tokenFile = config.age.secrets."gitea/actions-runner.env".path;
|
||||||
@@ -39,6 +27,14 @@ in
|
|||||||
runner = {
|
runner = {
|
||||||
timeout = "8h";
|
timeout = "8h";
|
||||||
};
|
};
|
||||||
|
container = {
|
||||||
|
network = "podman";
|
||||||
|
privileged = true;
|
||||||
|
};
|
||||||
|
cache = {
|
||||||
|
enabled = true;
|
||||||
|
dir = "/var/cache/gitea-runner";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -66,7 +62,6 @@ in
|
|||||||
DynamicUser = mkForce false;
|
DynamicUser = mkForce false;
|
||||||
User = "gitea-runner";
|
User = "gitea-runner";
|
||||||
Group = "gitea-runner";
|
Group = "gitea-runner";
|
||||||
ExecStart = mkForce "${config.services.gitea-actions-runner.package}/bin/act_runner -c ${cfgFile} daemon";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user