omnixient/lib/mksystem.nix
Padreug fa467a346a feat(mksystem): inject home-manager sharedModules OmniXY home needs
home.nix depends on nix-colors (colorScheme), lazyvim, and walker HM
modules; previously every host wired them by hand via `modules`, which
external consumers couldn't know to do. Inject them in mkSystem so any
host built through it gets them; re-importing from a host is a no-op.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 10:40:55 +02:00

185 lines
6.8 KiB
Nix

# mksystem — uniform NixOS host constructor.
#
# Inspired by mitchellh/nixos-config's lib/mksystem.nix, adapted for our
# context. Replaces the boilerplate of repeated `nixpkgs.lib.nixosSystem`
# calls in flake.nix with a single function that:
#
# - dispatches to nixpkgs.lib.nixosSystem
# - applies overlays uniformly
# - imports `hosts/<name>.nix` (or `hosts/<name>/default.nix`) by convention
# - imports `users/<user>/nixos.nix` if it exists
# - wires home-manager and imports `users/<user>/home-manager.nix`
# - opt-in dev-env via `devEnv = true` (imports modules/dev-env)
# - exposes `currentSystemName`, `currentSystemUser`, `inputs` to every
# module via specialArgs so downstream code can introspect host context
#
# Usage in flake.nix:
#
# let
# overlays = import ./lib/overlays.nix { inherit inputs; };
# mkSystem = import ./lib/mksystem.nix { inherit nixpkgs overlays inputs; };
# in
# {
# nixosConfigurations = {
# my-laptop = mkSystem "my-laptop" {
# user = "padreug";
# devEnv = true;
# };
#
# # Multiple users on the same machine, no dev-env:
# lab-shared = mkSystem "lab-shared" {
# user = "padreug";
# };
# };
# }
#
# Differences from mitchellh's version:
#
# - NixOS only (no darwin, no WSL — we don't need those right now and
# the dispatch is easy to add later if we do).
# - dev-env module is opt-in; not every host wants it.
# - `modules` parameter lets a host inject extra modules without
# creating a wrapper file.
# - Uses specialArgs (not _module.args inside a module) so the values
# are visible to imported file paths, which is required for things
# like `(let path = currentSystemName; in ./hosts/${path})`.
{
nixpkgs,
overlays ? [ ],
inputs,
}:
name:
{
system ? "x86_64-linux",
user,
devEnv ? false,
modules ? [ ],
# Home-manager config for `user`, as a PATH. External consumers pass
# their own (e.g. `home = ./home.nix`). When null, fall back to this
# repo's users/<user>/home-manager.nix, then to Omnixient's home.nix.
home ? null,
# Extra args merged into the system-level specialArgs. Used by hosts
# whose configuration.nix expects values beyond the defaults (e.g.
# omni's configuration.nix takes `settings` from settings.nix).
extraSpecialArgs ? { },
# Same, for home-manager.extraSpecialArgs.
extraHmArgs ? { },
}:
let
# Resolve the host's config file. Prefer `hosts/<name>/default.nix`
# (lets a host have its own subdirectory for hardware files etc.),
# fall back to a flat `hosts/<name>.nix`.
hostDir = ../hosts/${name};
hostFile = ../hosts + "/${name}.nix";
# Internal hosts (this repo's hosts/<name>/) are imported automatically.
# External consumers using this flake as an input have no host dir here,
# so they pass their host config through `modules` and we add nothing.
hostConfigModules =
if builtins.pathExists hostDir then
[ hostDir ]
else if builtins.pathExists hostFile then
[ hostFile ]
else if modules != [ ] then
[ ]
else
throw "mksystem: no host config for '${name}' add hosts/${name}/ or pass one via `modules` (looked at ${toString hostDir} and ${toString hostFile})";
userNixosConfig = ../users/${user}/nixos.nix;
hasUserNixosConfig = builtins.pathExists userNixosConfig;
# Home-manager config for `user`, resolved in priority order so both
# internal hosts and external consumers get a working home:
# 1. caller-supplied `home` (consumer override), else
# 2. this repo's users/<user>/home-manager.nix (internal hosts), else
# 3. Omnixient's default home.nix (consumer whose user isn't in this tree).
internalUserHM = ../users/${user}/home-manager.nix;
userHMConfig =
if home != null then
home
else if builtins.pathExists internalUserHM then
internalUserHM
else
../home.nix;
in
nixpkgs.lib.nixosSystem {
# specialArgs flow into every module's function signature AND can be
# used in `imports = [ ... ]` paths. _module.args cannot, which is
# why we don't use it for these.
specialArgs = {
inherit inputs;
currentSystemName = name;
currentSystemUser = user;
}
// extraSpecialArgs;
modules = [
# Apply overlays first so subsequent modules see our pinned packages.
{ nixpkgs.hostPlatform = system; }
{ nixpkgs.overlays = overlays; }
{ nixpkgs.config.allowUnfree = true; }
]
# Per-host configuration: this repo's hosts/<name>/ when present;
# external consumers supply their host through `modules`.
++ hostConfigModules
# Per-user system-level config (account, shell, sudo) — only if the
# user file exists. Skipping silently is fine; some hosts share users
# defined elsewhere (e.g. omni's users.nix).
++ nixpkgs.lib.optional hasUserNixosConfig userNixosConfig
++ [
# sops-nix — declarative age-encrypted secrets, decrypted at
# activation. Module always included; host modules opt in by
# declaring `sops.secrets.<name>`. Per-host wiring (defaultSopsFile,
# age.keyFile) lives in modules/secrets.nix.
inputs.sops-nix.nixosModules.sops
]
++ [
# Opt-in packs — always imported (so the omni.packs.<name>.* option
# namespace exists) but inert until `omni.packs.<name>.enable = true`.
# Single wire-in point; the ISO bypasses mksystem and so stays
# pack-free. See modules/packs/default.nix.
../modules/packs
]
++ [
# Home-manager wiring.
inputs.home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.extraSpecialArgs = {
inherit inputs;
currentSystemName = name;
currentSystemUser = user;
}
// extraHmArgs;
# Omnixient's home.nix depends on these home-manager modules
# (colorScheme via nix-colors, programs.lazyvim, programs.walker).
# Inject them for every host so consumers don't wire them by hand;
# re-importing the same module from a host's `modules` is a no-op.
home-manager.sharedModules = [
inputs.nix-colors.homeManagerModules.default
inputs.lazyvim.homeManagerModules.default
inputs.walker.homeManagerModules.default
];
}
# Pre-create the home-manager activation script's expected
# directory tree. See modules/home-manager-bootstrap.nix for
# the full rationale and the bugs it fixes. Imported here
# rather than inlined so the live ISO (which bypasses
# mksystem) can import the same module and get the same
# bootstrap.
../modules/home-manager-bootstrap.nix
]
++ [
# Home config for `user` — always wired now that userHMConfig always
# resolves (caller `home`, internal shim, or Omnixient's home.nix).
{ home-manager.users.${user} = import userHMConfig; }
]
# dev-env is opt-in.
++ nixpkgs.lib.optional devEnv ../modules/dev-env
# Caller-supplied extras.
++ modules;
}