some ai generated work to get services working for macos
This commit is contained in:
105
PLAN.md
Normal file
105
PLAN.md
Normal file
@@ -0,0 +1,105 @@
|
||||
Title: Add nix-darwin Support to Clan Services (clan.service)
|
||||
|
||||
Summary
|
||||
- Extend clan services so authors can ship a darwinModule alongside nixosModule.
|
||||
- Wire service results into darwin machines the same way we already do for NixOS.
|
||||
- Keep full backward compatibility: existing services that only export nixosModule continue to work unchanged.
|
||||
|
||||
Goals
|
||||
- Service authors can return perInstance/perMachine darwinModule similarly to nixosModule.
|
||||
- Darwin machines import the correct aggregated service module outputs.
|
||||
- Documentation describes the new result attribute and authoring pattern.
|
||||
|
||||
Non-Goals (initial phase)
|
||||
- No rework of service settings schema or UI beyond documenting darwinModule.
|
||||
- No OS-specific extraModules handling (we will keep extraModules affecting only nixos aggregation initially to avoid breaking existing users).
|
||||
- No sweeping updates of all services; we’ll add a concrete example (users) and leave others to be migrated incrementally.
|
||||
|
||||
Design Overview
|
||||
- Service result attributes gain darwinModule in both roles.<name>.perInstance and perMachine results.
|
||||
- The service aggregator composes both nixosModule and darwinModule per machine.
|
||||
- The machine wiring picks the correct module based on the machine’s class (nixos vs darwin).
|
||||
|
||||
Changes By File (with anchors)
|
||||
- lib/inventory/distributed-service/service-module.nix
|
||||
- Add darwinModule to per-instance return type next to nixosModule.
|
||||
- Where: lib/inventory/distributed-service/service-module.nix:536 (options.nixosModule = mkOption { … })
|
||||
- Action: Add sibling options.darwinModule = mkOption { type = types.deferredModule; default = { }; description = "A single nix-darwin module for the instance."; }.
|
||||
- Add darwinModule to per-machine return type next to nixosModule.
|
||||
- Where: lib/inventory/distributed-service/service-module.nix:666 (options.nixosModule = mkOption { … })
|
||||
- Action: Add sibling options.darwinModule = mkOption { type = types.deferredModule; default = { }; description = "A single nix-darwin module for the machine."; }.
|
||||
- Compose darwinModule per (role, instance, machine) similarly to nixosModule.
|
||||
- Where: lib/inventory/distributed-service/service-module.nix:878–893 (wrapper that builds nixosModule = { imports = [ instanceRes.nixosModule ] ++ extraModules … })
|
||||
- Action: Build darwinModule = { imports = [ instanceRes.darwinModule ]; }.
|
||||
Note: Do NOT include roles.*.extraModules here for darwin initially to avoid importing nixos-specific modules into darwin eval.
|
||||
- Aggregate darwinModules in final result.
|
||||
- Where: lib/inventory/distributed-service/service-module.nix:958–993 (instanceResults builder and final nixosModule = { imports = [ machineResult.nixosModule ] ++ instanceResults.nixosModules; })
|
||||
- Actions:
|
||||
- Track instanceResults.darwinModules in parallel to instanceResults.nixosModules.
|
||||
- Add final darwinModule = { imports = [ machineResult.darwinModule ] ++ instanceResults.darwinModules; }.
|
||||
|
||||
- modules/clan/distributed-services.nix
|
||||
- Feed the right service module to each machine based on machineClass.
|
||||
- Where: modules/clan/distributed-services.nix:147–152
|
||||
- Current: machineImports = fold over services, collecting serviceModule.result.final.${machineName}.nixosModule
|
||||
- Change: If inventory.machines.${machineName}.machineClass == "darwin" then collect .darwinModule else .nixosModule.
|
||||
|
||||
- modules/clan/module.nix
|
||||
- Ensure machineImports are included for both nixos and darwin machines.
|
||||
- Where: modules/clan/module.nix:195 (currently ++ lib.optionals (_class == "nixos") (v.machineImports or [ ]))
|
||||
- Change: Include machineImports for darwin as well (or remove the conditional and always append v.machineImports).
|
||||
|
||||
- docs/site/decisions/01-Clan-Modules.md
|
||||
- Document darwinModule as a result attribute.
|
||||
- Where: docs/site/decisions/01-Clan-Modules.md:129–146 (Result attributes and perMachine text mentioning only nixosModule)
|
||||
- Change: Add “darwinModule” to the Result attributes list and examples, mirroring nixosModule.
|
||||
|
||||
- Example service update: clanServices/users/default.nix
|
||||
- Add perInstance.darwinModule and perMachine.darwinModule mirroring nixos behavior where feasible.
|
||||
- Where: clanServices/users/default.nix:28–90 (roles.default.perInstance.nixosModule), 148–153 (perMachine.nixosModule)
|
||||
- Change: Provide minimal darwinModule that sets users.users.<name> (and any safe, cross-platform bits). If some nixos-only settings (e.g., systemd hooks) exist, keep them nixos-only.
|
||||
|
||||
Implementation Steps
|
||||
1) Service API extensions
|
||||
- Add options.darwinModule to roles.*.perInstance and perMachine (see anchors above).
|
||||
- Keep defaults to {} so services can omit it safely.
|
||||
|
||||
2) Aggregation logic
|
||||
- result.allRoles: emit darwinModule wrapper from instanceRes.darwinModule.
|
||||
- result.final:
|
||||
- Collect instanceResults.darwinModules alongside instanceResults.nixosModules.
|
||||
- Produce final darwinModule with [ machineResult.darwinModule ] ++ instanceResults.darwinModules.
|
||||
- Leave exports logic unchanged.
|
||||
|
||||
3) Machine wiring
|
||||
- modules/clan/distributed-services.nix: choose .darwinModule vs .nixosModule based on inventory.machines.<name>.machineClass.
|
||||
- modules/clan/module.nix: include v.machineImports for both OS classes.
|
||||
|
||||
4) Example migration (users)
|
||||
- Add darwinModule in clanServices/users/default.nix.
|
||||
- Validate that users service evaluates for a darwin machine and does not reference nixos-specific options.
|
||||
|
||||
5) Documentation
|
||||
- Update ADR docs to mention darwinModule in Result attributes and examples.
|
||||
- Add a short “Authoring for Darwin” snippet showing perInstance/perMachine returning both modules.
|
||||
|
||||
6) Tests and verification
|
||||
- Unit-level: extend lib/inventory/distributed-service/tests to assert presence of result.final.<machine>.darwinModule when perInstance/perMachine return it.
|
||||
- Integration-level: evaluate a sample darwin machine (e.g., inventory.json has test-darwin-machine) and assert clan.darwinModules.<machine> includes the aggregated module.
|
||||
- Sanity: ensure existing nixos-only services still evaluate unchanged.
|
||||
|
||||
Backward Compatibility
|
||||
- Existing services that only return nixosModule continue to work.
|
||||
- Darwin machines won’t import service modules until services provide darwinModule, avoiding accidental breakage.
|
||||
- extraModules remain applied only to nixos aggregation initially to prevent nixos-only modules from breaking darwin evaluation. We can add OS-specific extraModules in a follow-up (e.g., roles.*.extraModulesDarwin).
|
||||
|
||||
Acceptance Criteria
|
||||
- Services can return darwinModule in perInstance/perMachine without errors.
|
||||
- Darwin machines import aggregated darwinModule outputs from all participating services.
|
||||
- nixos behavior remains unchanged for existing services.
|
||||
- Documentation updated to reflect the new attribute and example.
|
||||
|
||||
Rollout Notes
|
||||
- Start by updating clanServices/users as a working example.
|
||||
- Encourage service authors to add darwinModule incrementally; no global migration is required.
|
||||
|
||||
@@ -120,6 +120,63 @@
|
||||
|
||||
share = settings.share;
|
||||
|
||||
script =
|
||||
(
|
||||
if settings.prompt then
|
||||
''
|
||||
prompt_value=$(cat "$prompts"/user-password)
|
||||
if [[ -n "''${prompt_value-}" ]]; then
|
||||
echo "$prompt_value" | tr -d "\n" > "$out"/user-password
|
||||
else
|
||||
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > "$out"/user-password
|
||||
fi
|
||||
''
|
||||
else
|
||||
''
|
||||
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > "$out"/user-password
|
||||
''
|
||||
)
|
||||
+ ''
|
||||
mkpasswd -s -m sha-512 < "$out"/user-password | tr -d "\n" > "$out"/user-password-hash
|
||||
'';
|
||||
};
|
||||
};
|
||||
darwinModule =
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# For darwin, we currently only generate and manage the password secret.
|
||||
# Hooking into actual macOS account management may be added later.
|
||||
clan.core.vars.generators."user-password-${settings.user}" = {
|
||||
files.user-password-hash.neededFor = "users";
|
||||
files.user-password.deploy = false;
|
||||
|
||||
prompts.user-password = lib.mkIf settings.prompt {
|
||||
display = {
|
||||
group = settings.user;
|
||||
label = "password";
|
||||
required = false;
|
||||
helperText = ''
|
||||
Your password will be encrypted and stored securely using the secret store you've configured.
|
||||
'';
|
||||
};
|
||||
type = "hidden";
|
||||
persist = true;
|
||||
description = "Leave empty to generate automatically";
|
||||
};
|
||||
|
||||
runtimeInputs = [
|
||||
pkgs.coreutils
|
||||
pkgs.xkcdpass
|
||||
pkgs.mkpasswd
|
||||
];
|
||||
|
||||
share = settings.share;
|
||||
|
||||
script =
|
||||
(
|
||||
if settings.prompt then
|
||||
@@ -149,5 +206,7 @@
|
||||
# Immutable users to ensure that this module has exclusive control over the users.
|
||||
users.mutableUsers = false;
|
||||
};
|
||||
# No-op for darwin by default; can be extended later if needed.
|
||||
darwinModule = { };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -561,6 +561,15 @@ in
|
||||
```
|
||||
'';
|
||||
};
|
||||
options.darwinModule = mkOption {
|
||||
type = types.deferredModule;
|
||||
default = { };
|
||||
description = ''
|
||||
A single nix-darwin module for the instance.
|
||||
|
||||
This mirrors `nixosModule` but targets darwin machines.
|
||||
'';
|
||||
};
|
||||
})
|
||||
];
|
||||
};
|
||||
@@ -686,6 +695,15 @@ in
|
||||
```
|
||||
'';
|
||||
};
|
||||
options.darwinModule = mkOption {
|
||||
type = types.deferredModule;
|
||||
default = { };
|
||||
description = ''
|
||||
A single nix-darwin module for the machine.
|
||||
|
||||
This mirrors `nixosModule` but targets darwin machines.
|
||||
'';
|
||||
};
|
||||
})
|
||||
];
|
||||
};
|
||||
@@ -890,6 +908,11 @@ in
|
||||
lib.setDefaultModuleLocation "via inventory.instances.${instanceName}.roles.${roleName}" s
|
||||
) instanceCfg.roles.${roleName}.extraModules);
|
||||
};
|
||||
darwinModule = {
|
||||
imports = [
|
||||
instanceRes.darwinModule
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
) instanceCfg.roles.${roleName}.machines or { };
|
||||
@@ -979,11 +1002,24 @@ in
|
||||
else
|
||||
instanceAcc.nixosModules
|
||||
);
|
||||
darwinModules = (
|
||||
if instance.allMachines.${machineName}.darwinModule or { } != { } then
|
||||
instanceAcc.darwinModules
|
||||
++ [
|
||||
(lib.setDefaultModuleLocation
|
||||
"Via instances.${instanceName}.roles.${roleName}.machines.${machineName}"
|
||||
instance.allMachines.${machineName}.darwinModule
|
||||
)
|
||||
]
|
||||
else
|
||||
instanceAcc.darwinModules
|
||||
);
|
||||
}
|
||||
) roleAcc role.allInstances
|
||||
)
|
||||
{
|
||||
nixosModules = [ ];
|
||||
darwinModules = [ ];
|
||||
# ...
|
||||
}
|
||||
config.result.allRoles;
|
||||
@@ -1021,6 +1057,12 @@ in
|
||||
]
|
||||
++ instanceResults.nixosModules;
|
||||
};
|
||||
darwinModule = {
|
||||
imports = [
|
||||
(lib.setDefaultModuleLocation "Via ${config.manifest.name}.perMachine - machine='${machineName}';" machineResult.darwinModule)
|
||||
]
|
||||
++ instanceResults.darwinModules;
|
||||
};
|
||||
}
|
||||
) config.result.allMachines;
|
||||
};
|
||||
|
||||
@@ -145,10 +145,23 @@ in
|
||||
internal = true;
|
||||
type = types.raw;
|
||||
default = lib.mapAttrs (machineName: _: {
|
||||
# This is the list of nixosModules for each machine
|
||||
# This is the list of service modules for each machine (nixos or darwin)
|
||||
machineImports = lib.foldlAttrs (
|
||||
acc: _module_ident: serviceModule:
|
||||
acc ++ [ serviceModule.result.final.${machineName}.nixosModule or { } ]
|
||||
let
|
||||
modName =
|
||||
if inventory.machines.${machineName}.machineClass == "darwin" then
|
||||
"darwinModule"
|
||||
else
|
||||
"nixosModule";
|
||||
finalForMachine = serviceModule.result.final.${machineName} or { };
|
||||
picked =
|
||||
if builtins.hasAttr modName finalForMachine then
|
||||
(builtins.getAttr modName finalForMachine)
|
||||
else
|
||||
{ };
|
||||
in
|
||||
acc ++ [ picked ]
|
||||
) [ ] config._services.mappedServices;
|
||||
}) inventory.machines or { };
|
||||
};
|
||||
|
||||
@@ -192,7 +192,7 @@ in
|
||||
# - darwinModules (_class = darwin)
|
||||
(lib.optionalAttrs (clan-core ? "${_class}Modules") clan-core."${_class}Modules".clanCore)
|
||||
]
|
||||
++ lib.optionals (_class == "nixos") (v.machineImports or [ ]);
|
||||
++ (v.machineImports or [ ]);
|
||||
|
||||
# default hostname
|
||||
networking.hostName = lib.mkDefault name;
|
||||
|
||||
Reference in New Issue
Block a user