feat: extern nixosModules

This commit is contained in:
arcnmx 2024-02-19 11:29:46 -08:00
parent cb932b65cc
commit 0116ecf47f
14 changed files with 690 additions and 2 deletions

View file

@ -130,6 +130,57 @@ jobs:
command: ci-build-cache
quiet: false
stdin: ${{ runner.temp }}/ci.build.cache
extern:
name: nodes-extern
runs-on: ubuntu-latest
steps:
- id: checkout
name: git clone
uses: actions/checkout@v4
with:
submodules: false
- id: nix-install
name: nix install
uses: arcnmx/ci/actions/nix/install@v0.7
- id: ci-dirty
name: nix test dirty
uses: arcnmx/ci/actions/nix/run@v0.7
with:
attrs: ci.job.extern.run.test
command: ci-build-dirty
quiet: false
stdout: ${{ runner.temp }}/ci.build.dirty
- id: ci-test
name: nix test build
uses: arcnmx/ci/actions/nix/run@v0.7
with:
attrs: ci.job.extern.run.test
command: ci-build-realise
ignore-exit-code: true
quiet: false
stdin: ${{ runner.temp }}/ci.build.dirty
- env:
CI_EXIT_CODE: ${{ steps.ci-test.outputs.exit-code }}
id: ci-summary
name: nix test results
uses: arcnmx/ci/actions/nix/run@v0.7
with:
attrs: ci.job.extern.run.test
command: ci-build-summarise
quiet: false
stdin: ${{ runner.temp }}/ci.build.dirty
stdout: ${{ runner.temp }}/ci.build.cache
- env:
CACHIX_SIGNING_KEY: ${{ secrets.CACHIX_SIGNING_KEY }}
id: ci-cache
if: always()
name: nix test cache
uses: arcnmx/ci/actions/nix/run@v0.7
with:
attrs: ci.job.extern.run.test
command: ci-build-cache
quiet: false
stdin: ${{ runner.temp }}/ci.build.cache
hakurei:
name: nodes-hakurei
runs-on: ubuntu-latest

View file

@ -10,6 +10,7 @@
"tree.nix"
];
whitelistDirs = [
"modules/extern"
"modules/system"
"systems"
];

View file

@ -29,7 +29,11 @@ with lib; {
in
mapAttrs' (k: nameValuePair "${k}") (genAttrs nixosSystems (host: {
tasks.${host}.inputs = channels.nixfiles.nixosConfigurations.${host}.config.system.build.toplevel;
}));
})) // {
extern = {
tasks.test.inputs = channels.nixfiles.nixosConfigurations.extern-test.config.system.build.toplevel;
};
};
ci.gh-actions.checkoutOptions.submodules = false;
cache.cachix.arc = {

12
lib.nix
View file

@ -35,6 +35,16 @@
mkWinPath = replaceStrings ["/"] ["\\"];
mkBaseDn = domain: concatMapStringsSep "," (part: "dc=${part}") (splitString "." domain);
treeToModulesOutput = modules:
{
${
if modules ? __functor
then "default"
else null
} =
modules.__functor modules;
}
// builtins.removeAttrs modules ["__functor"];
in {
inherit tree nixlib inputs systems;
meta = tree.impure;
@ -42,7 +52,7 @@ in {
Std = inputs.std-fl.lib;
lib = {
domain = "gensokyo.zone";
inherit mkWinPath mkBaseDn userIs eui64 toHexStringLower hexCharToInt;
inherit treeToModulesOutput mkWinPath mkBaseDn userIs eui64 toHexStringLower hexCharToInt;
inherit (inputs.arcexprs.lib) unmerged json;
};
generate = import ./generate.nix {inherit inputs tree;};

7
modules/extern/home/args.nix vendored Normal file
View file

@ -0,0 +1,7 @@
{inputs, ...}: {...}: let
inherit (inputs.self.lib) meta;
in {
imports = [
meta.modules.extern.misc.args
];
}

27
modules/extern/misc/args.nix vendored Normal file
View file

@ -0,0 +1,27 @@
{inputs, ...}: {
config,
options,
...
}: let
hasConfigLib = options ? lib;
gensokyo-zone = {
inherit inputs;
inherit (inputs.self.lib) tree meta lib;
};
in {
config = {
${
if hasConfigLib
then "lib"
else null
} = {
inherit gensokyo-zone;
};
_module.args = {
gensokyo-zone =
if hasConfigLib
then config.lib.gensokyo-zone
else gensokyo-zone;
};
};
}

7
modules/extern/nixos/args.nix vendored Normal file
View file

@ -0,0 +1,7 @@
{inputs, ...}: {...}: let
inherit (inputs.self.lib) meta;
in {
imports = [
meta.modules.extern.misc.args
];
}

161
modules/extern/nixos/kyuuto.nix vendored Normal file
View file

@ -0,0 +1,161 @@
{
config,
lib,
gensokyo-zone,
...
}: let
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
inherit (gensokyo-zone.lib) unmerged;
cfg = config.gensokyo-zone.kyuuto;
kyuutoModule = {
gensokyo-zone,
nixosConfig,
config,
...
}: let
inherit (gensokyo-zone.lib) unmerged domain;
setFilesystemOptions = mkMerge [
(mkIf config.nfs.enable config.nfs.fstabOptions)
(mkIf config.smb.enable config.smb.fstabOptions)
(mkIf config.automount.enable config.automount.fstabOptions)
];
in {
options = with lib.types; {
enable = mkEnableOption "kyuuto";
media.enable =
mkEnableOption "/mnt/kyuuto-media"
// {
default = true;
};
transfer.enable =
mkEnableOption "/mnt/kyuuto-transfer"
// {
default = true;
};
shared.enable = mkEnableOption "/mnt/kyuuto-shared";
domain = mkOption {
type = str;
};
local.enable = mkEnableOption "LAN";
automount = {
enable =
mkEnableOption "systemd automount"
// {
default = true;
};
fstabOptions = mkOption {
type = listOf str;
};
};
nfs = {
enable =
mkEnableOption "NFS mounts"
// {
default = true;
};
fstabOptions = mkOption {
type = listOf str;
};
};
smb = {
enable = mkEnableOption "SMB mounts";
user = mkOption {
type = nullOr null;
default = null;
};
fstabOptions = mkOption {
type = listOf str;
};
};
setFilesystems = mkOption {
type = unmerged.types.attrs;
};
};
config = {
domain = mkMerge [
(mkOptionDefault (
if config.local.enable
then "local.${domain}"
else domain
))
(mkIf nixosConfig.services.tailscale.enable (
mkDefault
"tail.${domain}"
))
];
nfs.fstabOptions = [
"noauto"
"nfsvers=4"
"soft"
"retrans=2"
"timeo=60"
];
smb.fstabOptions = [
"noauto"
(mkIf (config.smb.user != null) "user=${config.smb.user}")
];
automount.fstabOptions = [
"x-systemd.automount"
"x-systemd.mount-timeout=2m"
"x-systemd.idle-timeout=10m"
];
setFilesystems = {
"/mnt/kyuuto-media" = mkIf config.media.enable {
device = mkMerge [
(mkIf config.nfs.enable "nfs.${config.domain}:/mnt/kyuuto-media")
(mkIf config.smb.enable (
if config.smb.user != null && config.local.enable
then ''\\smb.${config.domain}\kyuuto-media''
else if config.smb.user != null
then ''\\smb.${config.domain}\kyuuto-media-global''
else ''\\smb.${config.domain}\kyuuto-library-access''
))
];
fsType = mkMerge [
(mkIf config.nfs.enable "nfs4")
(mkIf config.smb.enable "smb3")
];
options = setFilesystemOptions;
};
"/mnt/kyuuto-transfer" = mkIf config.transfer.enable {
device = mkMerge [
(mkIf config.nfs.enable "nfs.${config.domain}:/mnt/kyuuto-media/transfer")
(mkIf (config.smb.enable && config.local.enable) ''\\smb.${config.domain}\kyuuto-transfer'')
];
fsType = mkMerge [
(mkIf config.nfs.enable "nfs4")
(mkIf config.smb.enable "smb3")
];
options = setFilesystemOptions;
};
"/mnt/kyuuto-shared" = mkIf (config.shared.enable && config.smb.enable) {
device = mkIf (config.smb.user != null) ''\\smb.${config.domain}\shared'';
fsType = "smb3";
options = setFilesystemOptions;
};
};
};
};
in {
options.gensokyo-zone.kyuuto = mkOption {
type = lib.types.submoduleWith {
modules = [kyuutoModule];
specialArgs = {
inherit gensokyo-zone;
inherit (gensokyo-zone) inputs;
nixosConfig = config;
};
};
default = { };
};
config = {
fileSystems = mkIf cfg.enable (
unmerged.mergeAttrs cfg.setFilesystems
);
lib.gensokyo-zone.kyuuto = {
inherit cfg kyuutoModule;
};
};
}

138
modules/extern/nixos/nix.nix vendored Normal file
View file

@ -0,0 +1,138 @@
{
config,
lib,
gensokyo-zone,
...
}: let
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkDefault;
inherit (gensokyo-zone.lib) unmerged;
cfg = config.gensokyo-zone.nix;
nixModule = {
gensokyo-zone,
nixosConfig,
config,
...
}: let
inherit (gensokyo-zone.lib) unmerged domain;
in {
options = with lib.types; {
enable = mkEnableOption "nix settings";
cache = {
arc.enable = mkEnableOption "arc cache";
infrastructure.enable =
mkEnableOption "gensokyo-infrastructure cache"
// {
default = true;
};
};
builder = {
enable = mkEnableOption "aya nixbld remote builder";
domain = mkOption {
type = str;
default = "nixbld.${domain}";
};
protocol = mkOption {
type = enum ["ssh" "ssh-ng"];
default = "ssh";
};
ssh = {
user = mkOption {
type = str;
default = "nixbld";
};
key = mkOption {
type = nullOr path;
default = null;
};
};
jobs = mkOption {
type = int;
default = 16;
};
systems = mkOption {
type = listOf str;
default = ["x86_64-linux"];
};
features = mkOption {
type = listOf str;
default = ["nixos-test" "benchmark" "big-parallel" "kvm"];
};
setBuildMachine = mkOption {
type = unmerged.types.attrs;
default = {};
};
};
setNixSettings = mkOption {
type = unmerged.types.attrs;
default = {};
};
setNixBuildMachines = mkOption {
type = unmerged.type;
default = [];
};
};
config = {
setNixSettings = mkMerge [
(mkIf config.cache.arc.enable {
extra-substituters = [
"https://arc.cachix.org"
];
extra-trusted-public-keys = [
"arc.cachix.org-1:DZmhclLkB6UO0rc0rBzNpwFbbaeLfyn+fYccuAy7YVY="
];
})
(mkIf config.cache.infrastructure.enable {
extra-substituters = [
"https://gensokyo-infrastructure.cachix.org"
];
extra-trusted-public-keys = [
"gensokyo-infrastructure.cachix.org-1:CY6ChfQ8KTUdwWoMbo8ZWr2QCLMXUQspHAxywnS2FyI="
];
})
];
builder = {
domain = mkIf nixosConfig.services.tailscale.enable (
mkDefault
"nixbld.tail.${domain}"
);
setBuildMachine = {
hostName = config.builder.domain;
protocol = config.builder.protocol;
sshUser = config.builder.ssh.user;
sshKey = config.builder.ssh.key;
maxJobs = config.builder.jobs;
systems = config.builder.systems;
supportedFeatures = config.builder.features;
};
};
setNixBuildMachines = mkIf config.builder.enable [
(
unmerged.mergeAttrs config.builder.setBuildMachine
)
];
};
};
in {
options.gensokyo-zone.nix = mkOption {
type = lib.types.submoduleWith {
modules = [nixModule];
specialArgs = {
inherit gensokyo-zone;
inherit (gensokyo-zone) inputs;
nixosConfig = config;
};
};
default = { };
};
config = {
nix = mkIf cfg.enable {
settings = unmerged.mergeAttrs cfg.setNixSettings;
buildMachines = unmerged.merge cfg.setNixBuildMachines;
};
lib.gensokyo-zone.nix = {
inherit cfg nixModule;
};
};
}

230
modules/extern/nixos/users.nix vendored Normal file
View file

@ -0,0 +1,230 @@
{
config,
lib,
gensokyo-zone,
...
}: let
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkDefault mkOptionDefault mkOverride;
inherit (lib.lists) filter elem;
inherit (lib.attrsets) nameValuePair attrValues;
inherit (gensokyo-zone.lib) unmerged;
inherit (gensokyo-zone) meta;
cfg = config.gensokyo-zone.users;
userModule = {
gensokyo-zone,
nixosConfig,
config,
name,
...
}: let
inherit (gensokyo-zone.lib) json unmerged;
cfg = nixosConfig.gensokyo-zone.users;
isValidGroup = group: ! elem group cfg.excludeGroups && cfg.groups.${group}.enable;
mapGroupToSystem = group: cfg.groups.${group}.systemName;
in {
freeformType = json.types.attrs;
options = with lib.types; {
enable =
mkEnableOption "user"
// {
default = true;
};
name = mkOption {
type = str;
default = name;
};
systemName = mkOption {
type = str;
default = config.name;
};
systemUser = mkOption {
type = unspecified;
readOnly = true;
};
uid = mkOption {
type = int;
};
group = mkOption {
type = str;
};
extraGroups = mkOption {
type = listOf str;
default = [];
};
systemGroup = mkOption {
type = str;
};
systemGroups = mkOption {
type = listOf str;
};
setUser = mkOption {
type = unmerged.type;
};
};
config = {
systemUser = nixosConfig.users.users.${config.systemName};
systemGroup = mkOptionDefault (mapGroupToSystem config.group);
systemGroups = mkOptionDefault (map mapGroupToSystem (
filter isValidGroup config.extraGroups
));
setUser = {
uid = mkDefault config.uid;
name = mkDefault config.systemName;
autoSubUidGidRange = mkDefault false;
group = mkIf (isValidGroup config.group) (
mkDefault (mapGroupToSystem config.group)
);
isSystemUser = mkOverride 1250 (!config.systemUser.isNormalUser);
extraGroups = config.systemGroups;
openssh.authorizedKeys = mkIf (config.systemUser.isNormalUser && config.openssh.authorizedKeys or {} != {}) (
config.openssh.authorizedKeys
);
};
};
};
groupModule = {
gensokyo-zone,
nixosConfig,
config,
name,
...
}: let
inherit (gensokyo-zone.lib) json unmerged;
cfg = nixosConfig.gensokyo-zone.users;
isValidUser = user: ! elem user cfg.excludeUsers && cfg.users.${user}.enable;
mapUserToSystem = user: cfg.users.${user}.systemName;
in {
freeformType = json.types.attrs;
options = with lib.types; {
enable =
mkEnableOption "group"
// {
default = true;
};
name = mkOption {
type = str;
default = name;
};
systemName = mkOption {
type = str;
default = config.name;
};
systemGroup = mkOption {
type = unspecified;
readOnly = true;
};
gid = mkOption {
type = int;
};
members = mkOption {
type = listOf str;
};
systemMembers = mkOption {
type = listOf str;
};
setGroup = mkOption {
type = unmerged.type;
};
};
config = {
systemGroup = nixosConfig.users.groups.${config.systemName};
systemMembers = mkOptionDefault (map mapUserToSystem (
filter isValidUser config.members
));
setGroup = {
gid = mkDefault config.gid;
name = mkDefault config.systemName;
members = config.systemMembers;
openssh.authorizedKeys = mkIf (config.systemUser.isNormalUser && config.openssh.authorizedKeys or {} != {}) (
config.openssh.authorizedKeys
);
};
};
};
usersModule = {
gensokyo-zone,
nixosConfig,
config,
...
}: let
inherit (gensokyo-zone.lib) unmerged;
specialArgs = {
inherit gensokyo-zone nixosConfig;
};
enabledUsers = filter (user: user.enable) (attrValues config.users);
enabledGroups = filter (group: group.enable) (attrValues config.groups);
in {
options = with lib.types; {
enable = mkEnableOption "gensokyo-zone users";
users = mkOption {
type = attrsOf (submoduleWith {
modules = [userModule];
inherit specialArgs;
});
default = { };
};
excludeUsers = mkOption {
type = listOf str;
};
groups = mkOption {
type = attrsOf (submoduleWith {
modules = [groupModule];
inherit specialArgs;
});
default = { };
};
excludeGroups = mkOption {
type = listOf str;
};
setUsers = mkOption {
type = unmerged.types.attrs;
internal = true;
};
};
config = {
excludeUsers = [];
excludeGroups = [
"users"
"wheel"
];
setUsers = {
users = map (user:
nameValuePair user.systemName (
unmerged.merge user.setUser
))
enabledUsers;
groups = map (group:
nameValuePair group.systemName (
unmerged.merge group.setGroup
))
enabledGroups;
};
};
};
in {
options.gensokyo-zone.users = mkOption {
type = lib.types.submoduleWith {
modules = [usersModule];
specialArgs = {
inherit gensokyo-zone;
inherit (gensokyo-zone) inputs;
nixosConfig = config;
};
};
};
config = {
gensokyo-zone.users = {...}: {
imports = [
meta.nixos.users
];
};
users = mkIf cfg.enable (
unmerged.mergeAttrs cfg.setUsers
);
lib.gensokyo-zone.users = {
inherit cfg usersModule userModule groupModule;
};
};
}

View file

@ -46,9 +46,13 @@
};
checks = legacyPackages.deploy-rs.deployChecks inputs.self.deploy;
});
inherit (inputs.self.lib.lib) treeToModulesOutput;
in {
inherit (outputs) devShells legacyPackages packages checks;
inherit (systems) deploy nixosConfigurations;
nixosModules = treeToModulesOutput tree.impure.modules.extern.nixos;
homeModules = treeToModulesOutput tree.impure.modules.extern.home;
miscModules = treeToModulesOutput tree.impure.modules.extern.misc;
lib = import ./lib.nix {
inherit tree inputs;
inherit (systems) systems;

View file

@ -0,0 +1,15 @@
{ inputs, lib, ... }: let
inherit (lib.modules) mkForce;
in {
arch = "x86_64";
type = "NixOS";
modules = mkForce [
./nixos.nix
];
builder = mkForce ({ modules, system, specialArgs, ... }: inputs.nixpkgs.lib.nixosSystem {
inherit modules system;
specialArgs = {
extern'test'inputs = specialArgs.inputs;
};
});
}

View file

@ -0,0 +1,28 @@
{
extern'test'inputs,
...
}: let
inherit (extern'test'inputs.self) nixosModules;
in {
imports = [
nixosModules.default
];
config = {
gensokyo-zone = {
nix = {
enable = true;
builder.enable = true;
};
kyuuto = {
enable = true;
shared.enable = true;
};
# TODO: users?
};
# this isn't a real machine...
boot.isContainer = true;
system.stateVersion = "23.11";
};
}

View file

@ -64,6 +64,11 @@
"modules/system/extern".functor.enable = true;
"modules/home".functor.enable = true;
"modules/type".functor.enable = true;
"modules/extern/home".functor.enable = true;
"modules/extern/home/args".evaluate = true;
"modules/extern/nixos".functor.enable = true;
"modules/extern/nixos/args".evaluate = true;
"modules/extern/misc/args".evaluate = true;
"nixos/*".functor = {
enable = true;
};