feat(lib): add stable service uid registry
This commit was merged in pull request #45.
This commit is contained in:
@@ -40,8 +40,8 @@ The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overla
|
||||
It is deployed on `neogaia` and verified: a commit in a repository outside this checkout is authored `alexion <contact@alexion.dev>` with no override.
|
||||
Verify it that way rather than from this checkout, whose `.git/config` carries the same identity and would mask a broken module.
|
||||
`~/.gitconfig` (a second global file that outranks the flake-managed `~/.config/git/config`) currently holds only a `tea` credential helper and no `user.*`, so it does not shadow the identity, but it is undeclared and will not survive a reimage.
|
||||
- The primary build/verify seam for any Host is `nix flake check`, which builds `checks.x86_64-linux.<host>` (the system toplevel).
|
||||
Cheap targeted checks use `nix eval .#nixosConfigurations.<host>.config...`.
|
||||
- The primary build/verify seam is `nix flake check`, which builds project-level checks plus `checks.x86_64-linux.<host>` system toplevels.
|
||||
Cheap targeted host checks use `nix eval .#nixosConfigurations.<host>.config...`.
|
||||
- chaotic-nyx must **not** follow our `nixpkgs`, and its packages are built against chaotic's own pinned nixpkgs (its overlay defaults to `onTopOf = "flake-nixpkgs"`, the cache-friendly path).
|
||||
That is what lets the `nyx-cache.chaotic.cx` binary cache hit instead of compiling the CachyOS kernel from source.
|
||||
The tradeoff is that chaotic packages do not see our `unstable`/`stable` overlays.
|
||||
|
||||
28
flake.nix
28
flake.nix
@@ -98,13 +98,25 @@
|
||||
];
|
||||
};
|
||||
|
||||
# `nix flake check` builds each host's toplevel.
|
||||
checks.x86_64-linux = lib.mapAttrs (
|
||||
name: host:
|
||||
if host.config.warnings == [] then
|
||||
host.config.system.build.toplevel
|
||||
else
|
||||
throw "Host ${name} has evaluation warnings:\n${lib.concatStringsSep "\n" host.config.warnings}"
|
||||
) self.nixosConfigurations;
|
||||
# `nix flake check` builds host toplevels and project-level checks.
|
||||
checks.x86_64-linux =
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||
serviceIdentities = import ./lib/service-identities.nix { inherit lib; };
|
||||
serviceIdentityTestsPass = lib.all (result: result) (lib.attrValues serviceIdentities.tests);
|
||||
in
|
||||
(lib.mapAttrs (
|
||||
name: host:
|
||||
if host.config.warnings == [] then
|
||||
host.config.system.build.toplevel
|
||||
else
|
||||
throw "Host ${name} has evaluation warnings:\n${lib.concatStringsSep "\n" host.config.warnings}"
|
||||
) self.nixosConfigurations)
|
||||
// {
|
||||
service-identities = pkgs.runCommand "service-identities-check" { } ''
|
||||
${lib.optionalString (!serviceIdentityTestsPass) "exit 1"}
|
||||
touch $out
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ let
|
||||
mapAttrsToList
|
||||
;
|
||||
|
||||
serviceIdentityRegistry = import ./service-identities.nix { inherit lib; };
|
||||
|
||||
# Recursively collect every `.nix` file under `dir` as a flat list, for a
|
||||
# module's `imports`.
|
||||
collectNixFiles =
|
||||
@@ -418,4 +420,6 @@ in
|
||||
guest
|
||||
bridgeName
|
||||
;
|
||||
inherit (serviceIdentityRegistry) serviceUid;
|
||||
serviceIdentities = serviceIdentityRegistry.identities;
|
||||
}
|
||||
|
||||
59
lib/service-identities.nix
Normal file
59
lib/service-identities.nix
Normal file
@@ -0,0 +1,59 @@
|
||||
{ lib }:
|
||||
let
|
||||
defaultIdentities = {
|
||||
actualbudget = 20001;
|
||||
copyparty = 20002;
|
||||
};
|
||||
|
||||
duplicateUidMessage = duplicates: ''
|
||||
Duplicate stable service UID allocation(s): ${lib.concatStringsSep ", " (
|
||||
map (entry: "${toString entry.uid} is assigned to ${lib.concatStringsSep ", " entry.names}") duplicates
|
||||
)}
|
||||
'';
|
||||
|
||||
mk = identities:
|
||||
let
|
||||
names = lib.attrNames identities;
|
||||
namesByUid = lib.foldl' (
|
||||
acc: name:
|
||||
let
|
||||
uid = toString identities.${name};
|
||||
in
|
||||
acc // {
|
||||
${uid} = (acc.${uid} or [ ]) ++ [ name ];
|
||||
}
|
||||
) { } names;
|
||||
duplicates = lib.mapAttrsToList (uid: namesForUid: {
|
||||
uid = lib.toInt uid;
|
||||
names = namesForUid;
|
||||
}) (lib.filterAttrs (_uid: namesForUid: builtins.length namesForUid > 1) namesByUid);
|
||||
checkedIdentities =
|
||||
assert lib.assertMsg (duplicates == [ ]) (duplicateUidMessage duplicates);
|
||||
identities;
|
||||
in
|
||||
rec {
|
||||
inherit checkedIdentities;
|
||||
|
||||
serviceUid = name:
|
||||
if builtins.hasAttr name checkedIdentities then
|
||||
checkedIdentities.${name}
|
||||
else
|
||||
throw "Unknown stable service identity `${name}`. Known identities: ${lib.concatStringsSep ", " (lib.attrNames checkedIdentities)}";
|
||||
};
|
||||
|
||||
default = mk defaultIdentities;
|
||||
|
||||
tests = {
|
||||
actualbudgetUid = default.serviceUid "actualbudget" == 20001;
|
||||
copypartyUid = default.serviceUid "copyparty" == 20002;
|
||||
missingLookupFails = !(builtins.tryEval (default.serviceUid "missing-service")).success;
|
||||
duplicateUidFails = !(builtins.tryEval (mk {
|
||||
first = 20003;
|
||||
second = 20003;
|
||||
}).checkedIdentities).success;
|
||||
};
|
||||
in
|
||||
default // {
|
||||
identities = default.checkedIdentities;
|
||||
inherit tests;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
my,
|
||||
...
|
||||
}:
|
||||
let
|
||||
@@ -30,8 +31,8 @@ in
|
||||
|
||||
uid = lib.mkOption {
|
||||
type = lib.types.ints.positive;
|
||||
default = 20001;
|
||||
description = "Fixed uid for the Actual Budget service user.";
|
||||
default = my.serviceUid "actualbudget";
|
||||
description = "Stable uid for the Actual Budget service user.";
|
||||
};
|
||||
|
||||
group = lib.mkOption {
|
||||
|
||||
Reference in New Issue
Block a user