Roles from data
Host files that list roles drift: someone adds a GPU and forgets the line. Put the facts on the host instead, and let each role test them.
# Machines carry facts. No host file names its roles: the facts decide.{ yen, lib, ... }:let inherit (yen.lib) opt is;in{ yen.kinds.fleet = { spawns.host = _: yen.host; sinks.out.reduce = yen.lib.reduce.merge; }; yen.kinds.host.fields = { form = lib.types.enum [ "server" "laptop" "desktop" ]; gpu = opt (lib.types.enum [ "none" "nvidia" "amd" ]) "none"; labels = opt (is (v: lib.isList v && lib.all lib.isString v)) [ ]; };
yen.fleet.main = { }; yen.host.rack-1 = { form = "server"; labels = [ "ci-runner" ]; }; yen.host.rack-2.form = "server"; yen.host.thinkpad.form = "laptop"; yen.host.tower = { form = "desktop"; gpu = "nvidia"; }; yen.flake = { out, ... }: out;}# A role is an aspect whose `at` is a fact test. Roles build on roles.{ yen, lib, ... }:let inherit (yen.lib) all not;in{ yen.aspects = { server.at = s: (s.form or null) == "server"; workstation.at = s: lib.elem (s.form or null) [ "laptop" "desktop" ]; laptop.at = s: (s.form or null) == "laptop"; nvidia.at = s: (s.gpu or null) == "nvidia"; ci-runner.at = s: lib.elem "ci-runner" (s.labels or [ ]); gaming.at = all [ yen.aspects.workstation yen.aspects.nvidia (not yen.aspects.laptop) ]; backups.at = [ yen.aspects.server yen.aspects.workstation ]; };}Roles combine like any predicate: gaming is a workstation with an nvidia GPU that is not a laptop.
A role gives content with the usual keys (nixos, homeManager, …), and other aspects can use it in at.
Tags for deploy tools
Section titled “Tags for deploy tools”yen.lib.holds asks a predicate at a scope. One aspect turns the active roles into tags:
# Tags from active roles: `yen.lib.holds` asks an aspect at a scope. A deploy tool can read them.{ yen, lib, ... }:{ yen.aspects.tags = { at = yen.kinds.host; out = { host, scope, ... }: { tags.${host.name} = lib.filter (r: yen.lib.holds yen.aspects.${r} scope) [ "server" "workstation" "laptop" "nvidia" "ci-runner" "gaming" "backups" ]; }; };}{ rack-1 = [ "server" "ci-runner" "backups" ]; rack-2 = [ "server" "backups" ]; thinkpad = [ "workstation" "laptop" "backups" ]; tower = [ "workstation" "nvidia" "gaming" "backups" ];}