Compare commits
5 commits
3e29615db9
...
f4dca25c28
| Author | SHA1 | Date | |
|---|---|---|---|
| f4dca25c28 | |||
| 16cfbe4da1 | |||
| f819933c8d | |||
| d891a92357 | |||
| b1b1743414 |
34 changed files with 718 additions and 1219 deletions
21
flake.lock
generated
21
flake.lock
generated
|
|
@ -185,9 +185,30 @@
|
||||||
"microvm": "microvm",
|
"microvm": "microvm",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"nt": "nt",
|
"nt": "nt",
|
||||||
|
"sops-nix": "sops-nix",
|
||||||
"systems": "systems_3"
|
"systems": "systems_3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"sops-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1773096132,
|
||||||
|
"narHash": "sha256-M3zEnq9OElB7zqc+mjgPlByPm1O5t2fbUrH3t/Hm5Ag=",
|
||||||
|
"owner": "Mic92",
|
||||||
|
"repo": "sops-nix",
|
||||||
|
"rev": "d1ff3b1034d5bab5d7d8086a7803c5a5968cd784",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "Mic92",
|
||||||
|
"repo": "sops-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"spectrum": {
|
"spectrum": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
nt,
|
nt,
|
||||||
...
|
...
|
||||||
} @ inputs:
|
} @ inputs:
|
||||||
import ./cerulean
|
import ./nix
|
||||||
{
|
{
|
||||||
inherit inputs self nt;
|
inherit inputs self nt;
|
||||||
inherit (nt) mix;
|
inherit (nt) mix;
|
||||||
|
|
|
||||||
|
|
@ -18,15 +18,14 @@
|
||||||
} @ args: let
|
} @ args: let
|
||||||
inherit (nt) findImport;
|
inherit (nt) findImport;
|
||||||
in
|
in
|
||||||
mix.newMixture args (mixture: let
|
mix.newMixture args (mixture: {
|
||||||
inherit (mixture) mapNodes;
|
|
||||||
in {
|
|
||||||
includes = {
|
includes = {
|
||||||
private = [
|
private = [
|
||||||
./lib/nodes.nix
|
./lib/nodes.nix
|
||||||
];
|
];
|
||||||
public = [
|
public = [
|
||||||
./flake
|
./flake
|
||||||
|
./lib.nix
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,33 +4,53 @@
|
||||||
systems,
|
systems,
|
||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
|
inherit
|
||||||
|
(builtins)
|
||||||
|
attrNames
|
||||||
|
concatStringsSep
|
||||||
|
filter
|
||||||
|
length
|
||||||
|
warn
|
||||||
|
;
|
||||||
|
|
||||||
inherit (inputs.nixpkgs) lib;
|
inherit (inputs.nixpkgs) lib;
|
||||||
in {
|
in {
|
||||||
# snow.flake
|
# snow.flake
|
||||||
flake = flakeInputs: root: let
|
flake = flakeInputs: root: let
|
||||||
snowflake = lib.evalModules {
|
snowflake = lib.evalModules {
|
||||||
class = "snowflake";
|
class = "snowflake";
|
||||||
# XXX: TODO: abort if inputs contains reserved names
|
specialArgs = let
|
||||||
specialArgs =
|
reservedSpecialArgs = {
|
||||||
(flakeInputs
|
|
||||||
// {
|
|
||||||
inherit (this) snow;
|
inherit (this) snow;
|
||||||
inherit systems root;
|
inherit systems root;
|
||||||
inputs = flakeInputs;
|
inputs = flakeInputs;
|
||||||
})
|
};
|
||||||
# XXX: TODO:
|
|
||||||
# |> (x: builtins.removeAttrs x ["self" "nodes"]);
|
warnIfReserved = let
|
||||||
|> (x: builtins.removeAttrs x ["self"]);
|
getReservedNames = names:
|
||||||
|
reservedSpecialArgs
|
||||||
|
|> attrNames
|
||||||
|
|> filter (name: names?${name});
|
||||||
|
|
||||||
|
reservedNames =
|
||||||
|
flakeInputs
|
||||||
|
|> attrNames
|
||||||
|
|> getReservedNames;
|
||||||
|
in
|
||||||
|
(length reservedNames == 0)
|
||||||
|
|| warn ''
|
||||||
|
[snow] Your `flake.nix` declares inputs with reserved names!
|
||||||
|
[snow] These will be accessible only via `inputs.''${NAME}`
|
||||||
|
[snow] Please rename the following:
|
||||||
|
[snow] ${concatStringsSep reservedNames ", "}
|
||||||
|
''
|
||||||
|
true;
|
||||||
|
in
|
||||||
|
assert warnIfReserved;
|
||||||
|
flakeInputs // reservedSpecialArgs;
|
||||||
|
|
||||||
modules = [
|
modules = [
|
||||||
./module.nix
|
./module.nix
|
||||||
({config, ...}: {
|
|
||||||
_module.args = {
|
|
||||||
self = config;
|
|
||||||
# XXX: TODO:
|
|
||||||
# nodes = config.nodes.nodes;
|
|
||||||
};
|
|
||||||
})
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
|
|
||||||
|
|
@ -1,318 +0,0 @@
|
||||||
{ lib
|
|
||||||
# Optionally a string with extra version info to be included in the error message
|
|
||||||
# in case is lib is out of date. Empty or starts with space.
|
|
||||||
, revInfo ? ""
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
inherit (lib)
|
|
||||||
mkOption
|
|
||||||
mkOptionType
|
|
||||||
defaultFunctor
|
|
||||||
isAttrs
|
|
||||||
isFunction
|
|
||||||
showOption
|
|
||||||
throwIf
|
|
||||||
types
|
|
||||||
warnIf
|
|
||||||
getAttrFromPath
|
|
||||||
setAttrByPath
|
|
||||||
attrByPath
|
|
||||||
optionalAttrs
|
|
||||||
;
|
|
||||||
inherit (lib.modules)
|
|
||||||
mkAliasAndWrapDefsWithPriority;
|
|
||||||
inherit (lib.types)
|
|
||||||
path
|
|
||||||
submoduleWith
|
|
||||||
;
|
|
||||||
|
|
||||||
# Polyfill isFlake until Nix with https://github.com/NixOS/nix/pull/7207 is common
|
|
||||||
isFlake = maybeFlake:
|
|
||||||
if maybeFlake ? _type
|
|
||||||
then maybeFlake._type == "flake"
|
|
||||||
else maybeFlake ? inputs && maybeFlake ? outputs && maybeFlake ? sourceInfo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Deprecated for any use except type-merging into `perSystem`.
|
|
||||||
Use `lib.types.deferredModuleWith` instead, and add `apply = m: [ m ];` if needed.
|
|
||||||
|
|
||||||
The deferredModule type was pioneered in flake-parts for the `perSystem` option.
|
|
||||||
The Nixpkgs version has an improved merge function that returns a single module,
|
|
||||||
whereas this version returns a list. The flake-parts version was not updated to
|
|
||||||
match this improvement in Nixpkgs.
|
|
||||||
|
|
||||||
# History
|
|
||||||
|
|
||||||
This predates `lib.types.deferredModuleWith`, added in Nixpkgs 22.11
|
|
||||||
(https://github.com/NixOS/nixpkgs/pull/163617).
|
|
||||||
Documented as deprecated in flake-parts in January 2026.
|
|
||||||
*/
|
|
||||||
deferredModuleWith =
|
|
||||||
attrs@{ staticModules ? [ ] }: mkOptionType {
|
|
||||||
name = "deferredModule";
|
|
||||||
description = "module";
|
|
||||||
check = x: isAttrs x || isFunction x || path.check x;
|
|
||||||
merge = loc: defs: staticModules ++ map (def: lib.setDefaultModuleLocation "${def.file}, via option ${showOption loc}" def.value) defs;
|
|
||||||
inherit (submoduleWith { modules = staticModules; })
|
|
||||||
getSubOptions
|
|
||||||
getSubModules;
|
|
||||||
substSubModules = m: deferredModuleWith (attrs // {
|
|
||||||
staticModules = m;
|
|
||||||
});
|
|
||||||
functor = defaultFunctor "deferredModuleWith" // {
|
|
||||||
type = deferredModuleWith;
|
|
||||||
payload = {
|
|
||||||
inherit staticModules;
|
|
||||||
};
|
|
||||||
binOp = lhs: rhs: {
|
|
||||||
staticModules = lhs.staticModules ++ rhs.staticModules;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Internal: preserves legacy list-merge behavior for perSystem type-merging.
|
|
||||||
mkLegacyDeferredModuleType =
|
|
||||||
module:
|
|
||||||
deferredModuleWith {
|
|
||||||
staticModules = [ module ];
|
|
||||||
};
|
|
||||||
|
|
||||||
errorExample = ''
|
|
||||||
For example:
|
|
||||||
|
|
||||||
outputs = inputs@{ flake-parts, ... }:
|
|
||||||
flake-parts.lib.mkFlake { inherit inputs; } { /* module */ };
|
|
||||||
|
|
||||||
To avoid an infinite recursion, *DO NOT* pass `self.inputs` and
|
|
||||||
*DO NOT* pass `inherit (self) inputs`, but pass the output function
|
|
||||||
arguments as `inputs` like above.
|
|
||||||
'';
|
|
||||||
|
|
||||||
flake-parts-lib = rec {
|
|
||||||
evalFlakeModule =
|
|
||||||
args@
|
|
||||||
{ inputs ? self.inputs
|
|
||||||
, specialArgs ? { }
|
|
||||||
|
|
||||||
# legacy
|
|
||||||
, self ? inputs.self or (throw ''
|
|
||||||
When invoking flake-parts, you must pass all the flake output arguments,
|
|
||||||
and not just `self.inputs`.
|
|
||||||
|
|
||||||
${errorExample}
|
|
||||||
'')
|
|
||||||
, moduleLocation ? "${self.outPath}/flake.nix"
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
inputsPos = builtins.unsafeGetAttrPos "inputs" args;
|
|
||||||
errorLocation =
|
|
||||||
# Best case: user makes it explicit
|
|
||||||
args.moduleLocation or (
|
|
||||||
# Slightly worse: Nix does not technically commit to unsafeGetAttrPos semantics
|
|
||||||
if inputsPos != null
|
|
||||||
then inputsPos.file
|
|
||||||
# Slightly worse: self may not be valid when an error occurs
|
|
||||||
else if args?inputs.self.outPath
|
|
||||||
then args.inputs.self.outPath + "/flake.nix"
|
|
||||||
# Fallback
|
|
||||||
else "<mkFlake argument>"
|
|
||||||
);
|
|
||||||
in
|
|
||||||
throwIf
|
|
||||||
(!args?self && !args?inputs) ''
|
|
||||||
When invoking flake-parts, you must pass in the flake output arguments.
|
|
||||||
|
|
||||||
${errorExample}
|
|
||||||
''
|
|
||||||
warnIf
|
|
||||||
(!args?inputs) ''
|
|
||||||
When invoking flake-parts, it is recommended to pass all the flake output
|
|
||||||
arguments in the `inputs` parameter. If you only pass `self`, it's not
|
|
||||||
possible to use the `inputs` module argument in the module `imports`.
|
|
||||||
|
|
||||||
Please pass the output function arguments. ${errorExample}
|
|
||||||
''
|
|
||||||
|
|
||||||
(module:
|
|
||||||
lib.evalModules {
|
|
||||||
specialArgs = {
|
|
||||||
inherit self flake-parts-lib moduleLocation;
|
|
||||||
inputs = args.inputs or /* legacy, warned above */ self.inputs;
|
|
||||||
} // specialArgs;
|
|
||||||
modules = [ ./all-modules.nix (lib.setDefaultModuleLocation errorLocation module) ];
|
|
||||||
class = "flake";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
# Function to extract the default flakeModule from
|
|
||||||
# what may be a flake, returning the argument unmodified
|
|
||||||
# if it's not a flake.
|
|
||||||
#
|
|
||||||
# Useful to map over an 'imports' list to make it less
|
|
||||||
# verbose in the common case.
|
|
||||||
defaultModule = maybeFlake:
|
|
||||||
if isFlake maybeFlake
|
|
||||||
then maybeFlake.flakeModules.default or maybeFlake
|
|
||||||
else maybeFlake;
|
|
||||||
|
|
||||||
mkFlake = args: module:
|
|
||||||
let
|
|
||||||
eval = flake-parts-lib.evalFlakeModule args module;
|
|
||||||
in
|
|
||||||
eval.config.flake;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Deprecated. Declare options directly, e.g. `options.foo.bar = mkOption { ... }`,
|
|
||||||
provided that `foo` is already declared as a submodule option.
|
|
||||||
|
|
||||||
In flake-parts, `flake` is declared as a submodule option by the core modules,
|
|
||||||
so `options.flake.<name>` declarations work directly.
|
|
||||||
|
|
||||||
This function wraps option declarations in a submodule, allowing them to
|
|
||||||
be merged into an existing submodule option. For example, if `foo` is
|
|
||||||
already declared as a submodule option, using
|
|
||||||
`options.foo = mkSubmoduleOptions { bar = mkOption {...}; }` would add
|
|
||||||
`bar` to the `foo` submodule.
|
|
||||||
|
|
||||||
# History
|
|
||||||
|
|
||||||
This was a workaround for https://github.com/NixOS/nixpkgs/issues/146882,
|
|
||||||
fixed in Nixpkgs 22.05 by https://github.com/NixOS/nixpkgs/pull/156533.
|
|
||||||
With the fix, declaring `options.foo.bar` directly works when `foo` is
|
|
||||||
already a submodule option. Documented as deprecated in flake-parts in January 2026.
|
|
||||||
*/
|
|
||||||
mkSubmoduleOptions =
|
|
||||||
options:
|
|
||||||
mkOption {
|
|
||||||
type = types.submoduleWith {
|
|
||||||
modules = [{ inherit options; }];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
Deprecated. Use mkPerSystemType/mkPerSystemOption for `perSystem` type-merging, or
|
|
||||||
use Nixpkgs `types.deferredModule` directly, noting the lack of list wrapping;
|
|
||||||
see `deferredModuleWith` docs.
|
|
||||||
*/
|
|
||||||
mkDeferredModuleType = mkLegacyDeferredModuleType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Given a module, construct an option type suitable for type-merging into `perSystem`'s type.
|
|
||||||
*/
|
|
||||||
mkPerSystemType = mkLegacyDeferredModuleType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Deprecated. Use mkPerSystemOption for `perSystem` type-merging, or
|
|
||||||
use `mkOption` and Nixpkgs `types.deferredModule` directly, noting the
|
|
||||||
lack of list wrapping; see `deferredModuleWith` docs.
|
|
||||||
*/
|
|
||||||
mkDeferredModuleOption =
|
|
||||||
module:
|
|
||||||
mkOption {
|
|
||||||
type = flake-parts-lib.mkPerSystemType module;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
Given a module, construct an option declaration suitable for merging into the core `perSystem` module.
|
|
||||||
*/
|
|
||||||
mkPerSystemOption = mkDeferredModuleOption;
|
|
||||||
|
|
||||||
# Polyfill https://github.com/NixOS/nixpkgs/pull/344216
|
|
||||||
# Nixpkgs master 2024-12-09, Nixpkgs 25.05
|
|
||||||
attrsWith = types.attrsWith or ({ elemType, lazy ? false, placeholder ? "name" }:
|
|
||||||
if lazy then types.attrsOf elemType else types.lazyAttrsOf elemType);
|
|
||||||
|
|
||||||
# Helper function for defining a per-system option that
|
|
||||||
# gets transposed by the usual flake system logic to a
|
|
||||||
# top-level flake attribute.
|
|
||||||
mkTransposedPerSystemModule = { name, option, file }: {
|
|
||||||
_file = file;
|
|
||||||
|
|
||||||
options = {
|
|
||||||
flake.${name} = mkOption {
|
|
||||||
type = attrsWith {
|
|
||||||
elemType = option.type;
|
|
||||||
lazy = true;
|
|
||||||
placeholder = "system";
|
|
||||||
};
|
|
||||||
default = { };
|
|
||||||
description = ''
|
|
||||||
See {option}`perSystem.${name}` for description and examples.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
perSystem = flake-parts-lib.mkPerSystemOption {
|
|
||||||
_file = file;
|
|
||||||
|
|
||||||
options.${name} = option;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
transposition.${name} = { };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Needed pending https://github.com/NixOS/nixpkgs/pull/198450
|
|
||||||
mkAliasOptionModule = from: to: { config, options, ... }:
|
|
||||||
let
|
|
||||||
fromOpt = getAttrFromPath from options;
|
|
||||||
toOf = attrByPath to
|
|
||||||
(abort "Renaming error: option `${showOption to}' does not exist.");
|
|
||||||
toType = let opt = attrByPath to { } options; in opt.type or (types.submodule { });
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = setAttrByPath from (mkOption
|
|
||||||
{
|
|
||||||
visible = true;
|
|
||||||
description = "Alias of {option}`${showOption to}`.";
|
|
||||||
apply = x: (toOf config);
|
|
||||||
} // optionalAttrs (toType != null) {
|
|
||||||
type = toType;
|
|
||||||
});
|
|
||||||
config = mkAliasAndWrapDefsWithPriority (setAttrByPath to) fromOpt;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Helper function for importing while preserving module location. To be added
|
|
||||||
# in nixpkgs: https://github.com/NixOS/nixpkgs/pull/230588
|
|
||||||
# I expect these functions to remain identical. This one will stick around
|
|
||||||
# for a while to support older nixpkgs-lib.
|
|
||||||
importApply =
|
|
||||||
modulePath: staticArgs:
|
|
||||||
lib.setDefaultModuleLocation modulePath (import modulePath staticArgs);
|
|
||||||
|
|
||||||
inherit (import ./lib/memoize/memoize.nix {
|
|
||||||
inherit lib;
|
|
||||||
}) memoizeStr;
|
|
||||||
|
|
||||||
/**
|
|
||||||
`importAndPublish name module` returns a module that both imports the `module`, and exposes it as flake attribute `modules.flake.${name}`.
|
|
||||||
|
|
||||||
This also imports the optional [`modules`](https://flake.parts/options/flake-parts-modules.html) module to support that.
|
|
||||||
*/
|
|
||||||
importAndPublish = name: module: { lib, ... }: {
|
|
||||||
_class = "flake";
|
|
||||||
imports = [
|
|
||||||
module
|
|
||||||
./extras/modules.nix
|
|
||||||
];
|
|
||||||
flake.modules.flake.${name} = module;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# A best effort, lenient estimate. Please use a recent nixpkgs lib if you
|
|
||||||
# override it at all.
|
|
||||||
minVersion = "23.05pre-git";
|
|
||||||
|
|
||||||
in
|
|
||||||
|
|
||||||
if builtins.compareVersions lib.version minVersion < 0
|
|
||||||
then
|
|
||||||
abort ''
|
|
||||||
The nixpkgs-lib dependency of flake-parts was overridden but is too old.
|
|
||||||
The minimum supported version of nixpkgs-lib is ${minVersion},
|
|
||||||
but the actual version is ${lib.version}${revInfo}.
|
|
||||||
''
|
|
||||||
else
|
|
||||||
|
|
||||||
flake-parts-lib
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
}: {
|
}: {
|
||||||
imports = [
|
imports = [
|
||||||
./nodes
|
./nodes
|
||||||
|
./modules
|
||||||
(snow.findImport /${root}/snow)
|
(snow.findImport /${root}/snow)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
nix/snow/flake/modules/README.md
Normal file
3
nix/snow/flake/modules/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Snow Module Backend
|
||||||
|
This source code was tedious so it's just a modified version of the module backend of
|
||||||
|
[github:hercules-ci/flake-parts](https://github.com/hercules-ci/flake-parts/tree/main/modules).
|
||||||
|
|
@ -1,23 +1,31 @@
|
||||||
{ lib, flake-parts-lib, ... }:
|
{
|
||||||
let
|
lib,
|
||||||
inherit (lib)
|
snow,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit
|
||||||
|
(lib)
|
||||||
mkOption
|
mkOption
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
inherit (flake-parts-lib)
|
|
||||||
mkTransposedPerSystemModule
|
inherit
|
||||||
|
(snow)
|
||||||
|
mkPerSystemFlakeOutput
|
||||||
;
|
;
|
||||||
|
|
||||||
programType = lib.types.coercedTo derivationType lib.getExe lib.types.str;
|
derivationType =
|
||||||
|
lib.types.package
|
||||||
derivationType = lib.types.package // {
|
// {
|
||||||
check = lib.isDerivation;
|
check = lib.isDerivation;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
programType = lib.types.coercedTo derivationType lib.getExe lib.types.str;
|
||||||
|
|
||||||
appType = lib.types.submodule {
|
appType = lib.types.submodule {
|
||||||
options = {
|
options = {
|
||||||
type = mkOption {
|
type = mkOption {
|
||||||
type = lib.types.enum [ "app" ];
|
type = lib.types.enum ["app"];
|
||||||
default = "app";
|
default = "app";
|
||||||
description = ''
|
description = ''
|
||||||
A type tag for `apps` consumers.
|
A type tag for `apps` consumers.
|
||||||
|
|
@ -31,7 +39,7 @@ let
|
||||||
};
|
};
|
||||||
meta = mkOption {
|
meta = mkOption {
|
||||||
type = types.lazyAttrsOf lib.types.raw;
|
type = types.lazyAttrsOf lib.types.raw;
|
||||||
default = { };
|
default = {};
|
||||||
# TODO refer to Nix manual 2.25
|
# TODO refer to Nix manual 2.25
|
||||||
description = ''
|
description = ''
|
||||||
Metadata information about the app.
|
Metadata information about the app.
|
||||||
|
|
@ -43,11 +51,11 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
mkTransposedPerSystemModule {
|
mkPerSystemFlakeOutput {
|
||||||
name = "apps";
|
name = "apps";
|
||||||
option = mkOption {
|
option = mkOption {
|
||||||
type = types.lazyAttrsOf appType;
|
type = types.lazyAttrsOf appType;
|
||||||
default = { };
|
default = {};
|
||||||
description = ''
|
description = ''
|
||||||
Programs runnable with nix run `<name>`.
|
Programs runnable with nix run `<name>`.
|
||||||
'';
|
'';
|
||||||
|
|
@ -58,4 +66,4 @@ mkTransposedPerSystemModule {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
file = ./apps.nix;
|
file = ./apps.nix;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,26 @@
|
||||||
{ lib, flake-parts-lib, ... }:
|
{
|
||||||
let
|
lib,
|
||||||
inherit (lib)
|
snow,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit
|
||||||
|
(lib)
|
||||||
mkOption
|
mkOption
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
inherit (flake-parts-lib)
|
inherit
|
||||||
mkTransposedPerSystemModule
|
(snow)
|
||||||
|
mkPerSystemFlakeOutput
|
||||||
;
|
;
|
||||||
in
|
in
|
||||||
mkTransposedPerSystemModule {
|
mkPerSystemFlakeOutput {
|
||||||
name = "checks";
|
name = "checks";
|
||||||
option = mkOption {
|
option = mkOption {
|
||||||
type = types.lazyAttrsOf types.package;
|
type = types.lazyAttrsOf types.package;
|
||||||
default = { };
|
default = {};
|
||||||
description = ''
|
description = ''
|
||||||
Derivations to be built by [`nix flake check`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake-check.html).
|
Derivations to be built by [`nix flake check`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake-check.html).
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
file = ./checks.nix;
|
file = ./checks.nix;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
{ config, flake-parts-lib, lib, options, getSystem, extendModules, ... }:
|
|
||||||
let
|
|
||||||
inherit (lib)
|
|
||||||
mapAttrs
|
|
||||||
mkIf
|
|
||||||
mkOption
|
|
||||||
optionalAttrs
|
|
||||||
types
|
|
||||||
;
|
|
||||||
inherit (flake-parts-lib)
|
|
||||||
mkPerSystemOption
|
|
||||||
;
|
|
||||||
|
|
||||||
mkDebugConfig = { config, options, extendModules }: config // {
|
|
||||||
inherit config;
|
|
||||||
inherit (config) _module;
|
|
||||||
inherit options;
|
|
||||||
inherit extendModules;
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
debug = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
description = ''
|
|
||||||
Whether to add the attributes `debug`, `allSystems` and `currentSystem`
|
|
||||||
to the flake output. When `true`, this allows inspection of options via
|
|
||||||
`nix repl`.
|
|
||||||
|
|
||||||
```
|
|
||||||
$ nix repl
|
|
||||||
nix-repl> :lf .
|
|
||||||
nix-repl> currentSystem._module.args.pkgs.hello
|
|
||||||
«derivation /nix/store/7vf0d0j7majv1ch1xymdylyql80cn5fp-hello-2.12.1.drv»
|
|
||||||
```
|
|
||||||
|
|
||||||
Each of `debug`, `allSystems.<system>` and `currentSystem` is an
|
|
||||||
attribute set consisting of the `config` attributes, plus the extra
|
|
||||||
attributes `_module`, `config`, `options`, `extendModules`. So note that
|
|
||||||
these are not part of the `config` parameter, but are merged in for
|
|
||||||
debugging convenience.
|
|
||||||
|
|
||||||
- `debug`: The top-level options
|
|
||||||
- `allSystems`: The `perSystem` submodule applied to the configured `systems`.
|
|
||||||
- `currentSystem`: Shortcut into `allSystems`. Only available in impure mode.
|
|
||||||
Works for arbitrary system values.
|
|
||||||
|
|
||||||
See [Expore and debug option values](../debug.html) for more examples.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
perSystem = mkPerSystemOption
|
|
||||||
({ options, config, extendModules, ... }: {
|
|
||||||
_file = ./formatter.nix;
|
|
||||||
options = {
|
|
||||||
debug = mkOption {
|
|
||||||
description = ''
|
|
||||||
Values to return in e.g. `allSystems.<system>` when
|
|
||||||
[`debug = true`](#opt-debug).
|
|
||||||
'';
|
|
||||||
type = types.lazyAttrsOf types.raw;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
config = {
|
|
||||||
debug = mkDebugConfig { inherit config options extendModules; };
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
config = mkIf config.debug {
|
|
||||||
flake = {
|
|
||||||
debug = mkDebugConfig { inherit config options extendModules; };
|
|
||||||
allSystems = mapAttrs (_s: c: c.debug) config.allSystems;
|
|
||||||
} // optionalAttrs (builtins?currentSystem) {
|
|
||||||
currentSystem = (getSystem builtins.currentSystem).debug;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
15
nix/snow/flake/modules/default.nix
Normal file
15
nix/snow/flake/modules/default.nix
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./outputs.nix
|
||||||
|
|
||||||
|
./apps.nix
|
||||||
|
./checks.nix
|
||||||
|
./devShells.nix
|
||||||
|
./formatter.nix
|
||||||
|
./legacyPackages.nix
|
||||||
|
./nixosConfigurations.nix
|
||||||
|
./nixosModules.nix
|
||||||
|
./overlays.nix
|
||||||
|
./packages.nix
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
@ -1,19 +1,24 @@
|
||||||
{ lib, flake-parts-lib, ... }:
|
{
|
||||||
let
|
lib,
|
||||||
inherit (lib)
|
snow,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit
|
||||||
|
(lib)
|
||||||
mkOption
|
mkOption
|
||||||
types
|
types
|
||||||
literalExpression
|
literalExpression
|
||||||
;
|
;
|
||||||
inherit (flake-parts-lib)
|
inherit
|
||||||
mkTransposedPerSystemModule
|
(snow)
|
||||||
|
mkPerSystemFlakeOutput
|
||||||
;
|
;
|
||||||
in
|
in
|
||||||
mkTransposedPerSystemModule {
|
mkPerSystemFlakeOutput {
|
||||||
name = "devShells";
|
name = "devShells";
|
||||||
option = mkOption {
|
option = mkOption {
|
||||||
type = types.lazyAttrsOf types.package;
|
type = types.lazyAttrsOf types.package;
|
||||||
default = { };
|
default = {};
|
||||||
description = ''
|
description = ''
|
||||||
An attribute set of packages to be used as shells.
|
An attribute set of packages to be used as shells.
|
||||||
[`nix develop .#<name>`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-develop.html) will run `devShells.<name>`.
|
[`nix develop .#<name>`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-develop.html) will run `devShells.<name>`.
|
||||||
|
|
@ -27,4 +32,4 @@ mkTransposedPerSystemModule {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
file = ./devShells.nix;
|
file = ./devShells.nix;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,26 @@
|
||||||
{ config, lib, flake-parts-lib, ... }:
|
{
|
||||||
let
|
lib,
|
||||||
inherit (lib)
|
snow,
|
||||||
filterAttrs
|
...
|
||||||
mapAttrs
|
}: let
|
||||||
|
inherit
|
||||||
|
(lib)
|
||||||
mkOption
|
mkOption
|
||||||
optionalAttrs
|
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
inherit (flake-parts-lib)
|
inherit
|
||||||
mkPerSystemOption
|
(snow)
|
||||||
|
mkPerSystemFlakeOutput
|
||||||
;
|
;
|
||||||
in
|
in
|
||||||
{
|
mkPerSystemFlakeOutput {
|
||||||
options = {
|
name = "formatter";
|
||||||
flake.formatter = mkOption {
|
option = mkOption {
|
||||||
type = types.lazyAttrsOf types.package;
|
|
||||||
default = { };
|
|
||||||
description = ''
|
|
||||||
An attribute set of per system a package used by [`nix fmt`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-fmt.html).
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
perSystem = mkPerSystemOption {
|
|
||||||
_file = ./formatter.nix;
|
|
||||||
options = {
|
|
||||||
formatter = mkOption {
|
|
||||||
type = types.nullOr types.package;
|
type = types.nullOr types.package;
|
||||||
default = null;
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
A package used by [`nix fmt`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-fmt.html).
|
A package used by [`nix fmt`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-fmt.html).
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
file = ./apps.nix;
|
||||||
};
|
}
|
||||||
};
|
|
||||||
config = {
|
|
||||||
flake.formatter =
|
|
||||||
mapAttrs
|
|
||||||
(k: v: v.formatter)
|
|
||||||
(filterAttrs
|
|
||||||
(k: v: v.formatter != null)
|
|
||||||
config.allSystems
|
|
||||||
);
|
|
||||||
|
|
||||||
perInput = system: flake:
|
|
||||||
optionalAttrs (flake?formatter.${system}) {
|
|
||||||
formatter = flake.formatter.${system};
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,26 @@
|
||||||
{ lib, flake-parts-lib, ... }:
|
{
|
||||||
let
|
lib,
|
||||||
inherit (lib)
|
snow,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit
|
||||||
|
(lib)
|
||||||
mkOption
|
mkOption
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
inherit (flake-parts-lib)
|
inherit
|
||||||
mkTransposedPerSystemModule
|
(snow)
|
||||||
|
mkPerSystemFlakeOutput
|
||||||
;
|
;
|
||||||
in
|
in
|
||||||
mkTransposedPerSystemModule {
|
mkPerSystemFlakeOutput {
|
||||||
name = "legacyPackages";
|
name = "legacyPackages";
|
||||||
option = mkOption {
|
option = mkOption {
|
||||||
type = types.lazyAttrsOf types.raw;
|
type = types.lazyAttrsOf types.raw;
|
||||||
default = { };
|
default = {};
|
||||||
description = ''
|
description = ''
|
||||||
An attribute set of unmergeable values. This is also used by [`nix build .#<attrpath>`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-build.html).
|
Used for nixpkgs packages, also accessible via `nix build .#<name>` [`nix build .#<name>`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-build.html).
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
file = ./legacyPackages.nix;
|
file = ./legacyPackages.nix;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
{ withSystem, ... }:
|
|
||||||
{
|
|
||||||
config = {
|
|
||||||
_module.args = {
|
|
||||||
moduleWithSystem =
|
|
||||||
module:
|
|
||||||
|
|
||||||
{ config, ... }:
|
|
||||||
let
|
|
||||||
system =
|
|
||||||
config._module.args.system or
|
|
||||||
config._module.args.pkgs.stdenv.hostPlatform.system or
|
|
||||||
(throw "moduleWithSystem: Could not determine the configuration's system parameter for this module system application.");
|
|
||||||
|
|
||||||
allArgs = withSystem system (args: args);
|
|
||||||
|
|
||||||
lazyArgsPerParameter = f: builtins.mapAttrs
|
|
||||||
(k: v: allArgs.${k} or (throw "moduleWithSystem: module argument `${k}` does not exist."))
|
|
||||||
(builtins.functionArgs f);
|
|
||||||
|
|
||||||
# Use reflection to make the call lazy in the argument.
|
|
||||||
# Restricts args to the ones declared.
|
|
||||||
callLazily = f: a: f (lazyArgsPerParameter f);
|
|
||||||
in
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
(callLazily module allArgs)
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
{ lib, ... }:
|
{lib, ...}: let
|
||||||
let
|
inherit
|
||||||
inherit (lib)
|
(lib)
|
||||||
mkOption
|
mkOption
|
||||||
types
|
types
|
||||||
literalExpression
|
literalExpression
|
||||||
;
|
;
|
||||||
in
|
in {
|
||||||
{
|
|
||||||
options = {
|
options = {
|
||||||
flake.nixosConfigurations = mkOption {
|
outputs.nixosConfigurations = mkOption {
|
||||||
type = types.lazyAttrsOf types.raw;
|
type = types.lazyAttrsOf types.raw;
|
||||||
default = { };
|
default = {};
|
||||||
description = ''
|
description = ''
|
||||||
Instantiated NixOS configurations. Used by `nixos-rebuild`.
|
Instantiated NixOS configurations. Used by `nixos-rebuild`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,23 @@
|
||||||
{ self, lib, moduleLocation, ... }:
|
{
|
||||||
let
|
lib,
|
||||||
inherit (lib)
|
moduleLocation,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit
|
||||||
|
(lib)
|
||||||
mapAttrs
|
mapAttrs
|
||||||
mkOption
|
mkOption
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
in
|
in {
|
||||||
{
|
|
||||||
options = {
|
options = {
|
||||||
flake.nixosModules = mkOption {
|
outputs.nixosModules = mkOption {
|
||||||
type = types.lazyAttrsOf types.deferredModule;
|
type = types.lazyAttrsOf types.deferredModule;
|
||||||
default = { };
|
default = {};
|
||||||
apply = mapAttrs (k: v: {
|
apply = mapAttrs (k: v: {
|
||||||
_class = "nixos";
|
_class = "nixos";
|
||||||
_file = "${toString moduleLocation}#nixosModules.${k}";
|
_file = "${toString moduleLocation}#nixosModules.${k}";
|
||||||
imports = [ v ];
|
imports = [v];
|
||||||
});
|
});
|
||||||
description = ''
|
description = ''
|
||||||
NixOS modules.
|
NixOS modules.
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
#
|
|
||||||
# Nixpkgs module. The only exception to the rule.
|
|
||||||
#
|
|
||||||
# Provides a `pkgs` argument in `perSystem`.
|
|
||||||
#
|
|
||||||
# Arguably, this shouldn't be in flake-parts, but in nixpkgs.
|
|
||||||
# Nixpkgs could define its own module that does this, which would be
|
|
||||||
# a more consistent UX, but for now this will do.
|
|
||||||
#
|
|
||||||
# The existence of this module does not mean that other flakes' logic
|
|
||||||
# will be accepted into flake-parts, because it's against the
|
|
||||||
# spirit of Flakes.
|
|
||||||
#
|
|
||||||
{
|
|
||||||
config = {
|
|
||||||
perSystem = { inputs', lib, ... }: {
|
|
||||||
config = {
|
|
||||||
_module.args.pkgs = lib.mkOptionDefault (
|
|
||||||
builtins.seq
|
|
||||||
(inputs'.nixpkgs or (throw "flake-parts: The flake does not have a `nixpkgs` input. Please add it, or set `perSystem._module.args.pkgs` yourself."))
|
|
||||||
inputs'.nixpkgs.legacyPackages
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
|
|
||||||
flake = mkOption {
|
outputs = mkOption {
|
||||||
type = types.submoduleWith {
|
type = types.submoduleWith {
|
||||||
modules = [
|
modules = [
|
||||||
{
|
{
|
||||||
|
|
@ -21,7 +21,6 @@
|
||||||
No option has been declared for this flake output attribute, so its definitions can't be merged automatically.
|
No option has been declared for this flake output attribute, so its definitions can't be merged automatically.
|
||||||
Possible solutions:
|
Possible solutions:
|
||||||
- Load a module that defines this flake output attribute
|
- Load a module that defines this flake output attribute
|
||||||
Many modules are listed at https://flake.parts
|
|
||||||
- Declare an option for this flake output attribute
|
- Declare an option for this flake output attribute
|
||||||
- Make sure the output attribute is spelled correctly
|
- Make sure the output attribute is spelled correctly
|
||||||
- Define the value only once, with a single definition in a single module
|
- Define the value only once, with a single definition in a single module
|
||||||
|
|
@ -39,8 +38,7 @@
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
options = {
|
options = {
|
||||||
inherit flake;
|
inherit outputs;
|
||||||
output = {inherit flake;};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
config = {inherit (config) flake;};
|
config = {inherit (config) flake;};
|
||||||
|
|
@ -1,19 +1,18 @@
|
||||||
{ lib, ... }:
|
{lib, ...}: let
|
||||||
let
|
inherit
|
||||||
inherit (lib)
|
(lib)
|
||||||
mkOption
|
mkOption
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
in
|
in {
|
||||||
{
|
|
||||||
options = {
|
options = {
|
||||||
flake.overlays = mkOption {
|
outputs.overlays = mkOption {
|
||||||
# uniq -> ordered: https://github.com/NixOS/nixpkgs/issues/147052
|
# uniq -> ordered: https://github.com/NixOS/nixpkgs/issues/147052
|
||||||
# also update description when done
|
# also update description when done
|
||||||
type = types.lazyAttrsOf (types.uniq (types.functionTo (types.functionTo (types.lazyAttrsOf types.unspecified))));
|
type = types.lazyAttrsOf (types.uniq (types.functionTo (types.functionTo (types.lazyAttrsOf types.unspecified))));
|
||||||
# This eta expansion exists for the sole purpose of making nix flake check happy.
|
# This eta expansion exists for the sole purpose of making nix flake check happy.
|
||||||
apply = lib.mapAttrs (_k: f: final: prev: f final prev);
|
apply = lib.mapAttrs (_k: f: final: prev: f final prev);
|
||||||
default = { };
|
default = {};
|
||||||
example = lib.literalExpression ''
|
example = lib.literalExpression ''
|
||||||
{
|
{
|
||||||
default = final: prev: {};
|
default = final: prev: {};
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,24 @@
|
||||||
{ lib, flake-parts-lib, ... }:
|
{
|
||||||
let
|
lib,
|
||||||
inherit (lib)
|
snow,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit
|
||||||
|
(lib)
|
||||||
mkOption
|
mkOption
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
inherit (flake-parts-lib)
|
|
||||||
mkTransposedPerSystemModule
|
inherit
|
||||||
|
(snow)
|
||||||
|
mkPerSystemFlakeOutput
|
||||||
;
|
;
|
||||||
in
|
in
|
||||||
mkTransposedPerSystemModule {
|
mkPerSystemFlakeOutput {
|
||||||
name = "packages";
|
name = "packages";
|
||||||
option = mkOption {
|
option = mkOption {
|
||||||
type = types.lazyAttrsOf types.package;
|
type = types.lazyAttrsOf types.package;
|
||||||
default = { };
|
default = {};
|
||||||
description = ''
|
description = ''
|
||||||
An attribute set of packages to be built by [`nix build`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-build.html).
|
An attribute set of packages to be built by [`nix build`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-build.html).
|
||||||
|
|
||||||
|
|
@ -20,4 +26,4 @@ mkTransposedPerSystemModule {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
file = ./packages.nix;
|
file = ./packages.nix;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,159 +0,0 @@
|
||||||
{ config, lib, flake-parts-lib, self, ... }:
|
|
||||||
let
|
|
||||||
inherit (lib)
|
|
||||||
genAttrs
|
|
||||||
mapAttrs
|
|
||||||
mkOption
|
|
||||||
types
|
|
||||||
;
|
|
||||||
inherit (lib.strings)
|
|
||||||
escapeNixIdentifier
|
|
||||||
;
|
|
||||||
inherit (flake-parts-lib)
|
|
||||||
mkPerSystemType
|
|
||||||
;
|
|
||||||
|
|
||||||
rootConfig = config;
|
|
||||||
|
|
||||||
# Stubs for self and inputs. While it'd be possible to define aliases
|
|
||||||
# inside perSystem, that is not a general solution, and it would make
|
|
||||||
# top.config harder to discover, stretching the learning curve rather
|
|
||||||
# than flattening it.
|
|
||||||
|
|
||||||
throwAliasError' = param:
|
|
||||||
throw ''
|
|
||||||
`${param}` (without `'`) is not a `perSystem` module argument, but a
|
|
||||||
module argument of the top level config.
|
|
||||||
|
|
||||||
The following is an example usage of `${param}`. Note that its binding
|
|
||||||
is in the `top` parameter list, which is declared by the top level module
|
|
||||||
rather than the `perSystem` module.
|
|
||||||
|
|
||||||
top@{ config, lib, ${param}, ... }: {
|
|
||||||
perSystem = { config, ${param}', ... }: {
|
|
||||||
# in scope here:
|
|
||||||
# - ${param}
|
|
||||||
# - ${param}'
|
|
||||||
# - config (of perSystem)
|
|
||||||
# - top.config (note the `top@` pattern)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
|
|
||||||
throwAliasError = param:
|
|
||||||
throw ''
|
|
||||||
`${param}` is not a `perSystem` module argument, but a module argument of
|
|
||||||
the top level config.
|
|
||||||
|
|
||||||
The following is an example usage of `${param}`. Note that its binding
|
|
||||||
is in the `top` parameter list, which is declared by the top level module
|
|
||||||
rather than the `perSystem` module.
|
|
||||||
|
|
||||||
top@{ config, lib, ${param}, ... }: {
|
|
||||||
perSystem = { config, ... }: {
|
|
||||||
# in scope here:
|
|
||||||
# - ${param}
|
|
||||||
# - config (of perSystem)
|
|
||||||
# - top.config (note the `top@` pattern)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
|
|
||||||
/**
|
|
||||||
We primarily use `systems` to help memoize the per system context, but that
|
|
||||||
doesn't extend to arbitrary `system`s.
|
|
||||||
For that, we use the slightly less efficient, but perfectly acceptable
|
|
||||||
`memoizeStr` function.
|
|
||||||
*/
|
|
||||||
otherMemoizedSystems = flake-parts-lib.memoizeStr config.perSystem;
|
|
||||||
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
systems = mkOption {
|
|
||||||
description = ''
|
|
||||||
All the system types to enumerate in the flake output subattributes.
|
|
||||||
|
|
||||||
In other words, all valid values for `system` in e.g. `packages.<system>.foo`.
|
|
||||||
'';
|
|
||||||
type = types.listOf types.str;
|
|
||||||
};
|
|
||||||
|
|
||||||
perInput = mkOption {
|
|
||||||
description = ''
|
|
||||||
A function that pre-processes flake inputs.
|
|
||||||
|
|
||||||
It is called for users of `perSystem` such that `inputs'.''${name} = config.perInput system inputs.''${name}`.
|
|
||||||
|
|
||||||
This is used for [`inputs'`](../module-arguments.html#inputs) and [`self'`](../module-arguments.html#self).
|
|
||||||
|
|
||||||
The attributes returned by the `perInput` function definitions are merged into a single namespace (per input),
|
|
||||||
so each module should return an attribute set with usually only one or two predictable attribute names. Otherwise,
|
|
||||||
the `inputs'` namespace gets polluted.
|
|
||||||
'';
|
|
||||||
type = types.functionTo (types.functionTo (types.lazyAttrsOf types.unspecified));
|
|
||||||
};
|
|
||||||
|
|
||||||
perSystem = mkOption {
|
|
||||||
description = ''
|
|
||||||
A function from system to flake-like attributes omitting the `<system>` attribute.
|
|
||||||
|
|
||||||
Modules defined here have access to the suboptions and [some convenient module arguments](../module-arguments.html).
|
|
||||||
'';
|
|
||||||
type = mkPerSystemType ({ config, system, ... }: {
|
|
||||||
_file = ./perSystem.nix;
|
|
||||||
config = {
|
|
||||||
_module.args.inputs' =
|
|
||||||
mapAttrs
|
|
||||||
(inputName: input:
|
|
||||||
builtins.addErrorContext "while retrieving system-dependent attributes for input ${escapeNixIdentifier inputName}" (
|
|
||||||
if input._type or null == "flake"
|
|
||||||
then rootConfig.perInput system input
|
|
||||||
else
|
|
||||||
throw "Trying to retrieve system-dependent attributes for input ${escapeNixIdentifier inputName}, but this input is not a flake. Perhaps flake = false was added to the input declarations by mistake, or you meant to use a different input, or you meant to use plain old inputs, not inputs'."
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.inputs;
|
|
||||||
_module.args.self' =
|
|
||||||
builtins.addErrorContext "while retrieving system-dependent attributes for a flake's own outputs" (
|
|
||||||
rootConfig.perInput system self
|
|
||||||
);
|
|
||||||
|
|
||||||
# Custom error messages
|
|
||||||
_module.args.self = throwAliasError' "self";
|
|
||||||
_module.args.inputs = throwAliasError' "inputs";
|
|
||||||
_module.args.getSystem = throwAliasError "getSystem";
|
|
||||||
_module.args.withSystem = throwAliasError "withSystem";
|
|
||||||
_module.args.moduleWithSystem = throwAliasError "moduleWithSystem";
|
|
||||||
};
|
|
||||||
});
|
|
||||||
apply = modules: system:
|
|
||||||
(lib.evalModules {
|
|
||||||
inherit modules;
|
|
||||||
prefix = [ "perSystem" system ];
|
|
||||||
specialArgs = {
|
|
||||||
inherit system;
|
|
||||||
};
|
|
||||||
class = "perSystem";
|
|
||||||
}).config;
|
|
||||||
};
|
|
||||||
|
|
||||||
allSystems = mkOption {
|
|
||||||
type = types.lazyAttrsOf types.unspecified;
|
|
||||||
description = "The system-specific config for each of systems.";
|
|
||||||
internal = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
allSystems = genAttrs config.systems config.perSystem;
|
|
||||||
_module.args.getSystem = system: config.allSystems.${system} or (otherMemoizedSystems system);
|
|
||||||
|
|
||||||
# The warning is there for a reason. Only use this in situations where the
|
|
||||||
# performance cost has already been incurred, such as in `flakeModules.easyOverlay`,
|
|
||||||
# where we run in the context of an overlay, and the performance cost of the
|
|
||||||
# extra `pkgs` makes the cost of running `perSystem` probably negligible.
|
|
||||||
_module.args.getSystemIgnoreWarning = system: config.allSystems.${system} or (config.perSystem system);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,132 +0,0 @@
|
||||||
{ config, lib, flake-parts-lib, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
inherit (lib)
|
|
||||||
filterAttrs
|
|
||||||
mapAttrs
|
|
||||||
mkOption
|
|
||||||
types
|
|
||||||
;
|
|
||||||
inherit (lib.strings)
|
|
||||||
escapeNixIdentifier
|
|
||||||
;
|
|
||||||
|
|
||||||
transpositionModule = {
|
|
||||||
options = {
|
|
||||||
adHoc = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
description = ''
|
|
||||||
Whether to provide a stub option declaration for {option}`perSystem.<name>`.
|
|
||||||
|
|
||||||
The stub option declaration does not support merging and lacks
|
|
||||||
documentation, so you are recommended to declare the {option}`perSystem.<name>`
|
|
||||||
option yourself and avoid {option}`adHoc`.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
perInputAttributeError = { flake, attrName, system, attrConfig }:
|
|
||||||
# This uses flake.outPath for lack of a better identifier.
|
|
||||||
# Consider adding a perInput variation that has a normally-redundant argument for the input name.
|
|
||||||
# Tested manually with
|
|
||||||
# perSystem = { inputs', ... }: {
|
|
||||||
# packages.extra = inputs'.nixpkgs.extra;
|
|
||||||
# packages.default = inputs'.nixpkgs.packages.default;
|
|
||||||
# packages.veryWrong = (top.config.perInput "x86_64-linux" inputs'.nixpkgs.legacyPackages.hello).packages.default;
|
|
||||||
# };
|
|
||||||
# transposition.extra = {};
|
|
||||||
let
|
|
||||||
attrPath = "${escapeNixIdentifier attrName}.${escapeNixIdentifier system}";
|
|
||||||
flakeIdentifier =
|
|
||||||
if flake._type or null != "flake"
|
|
||||||
then
|
|
||||||
throw "An attempt was made to access attribute ${attrPath} on a value that's supposed to be a flake, but may not be a proper flake."
|
|
||||||
else
|
|
||||||
builtins.addErrorContext "while trying to find out how to describe what is supposedly a flake, whose attribute ${attrPath} was accessed but does not exist" (
|
|
||||||
toString flake.outPath
|
|
||||||
);
|
|
||||||
# This ought to be generalized by extending attrConfig, but this is the only known and common mistake for now.
|
|
||||||
alternateAttrNameHint =
|
|
||||||
if attrName == "packages" && flake?legacyPackages
|
|
||||||
then # Unfortunately we can't just switch them out, because that will put packages *sets* where single packages are expected in user code, resulting in potentially much worse and more confusing errors down the line.
|
|
||||||
"\nIt does define legacyPackages; try that instead?"
|
|
||||||
else "";
|
|
||||||
in
|
|
||||||
if flake?${attrName}
|
|
||||||
then
|
|
||||||
throw ''
|
|
||||||
Attempt to access ${attrPath} of flake ${flakeIdentifier}, but it does not have it.
|
|
||||||
It does have attribute ${escapeNixIdentifier attrName}, so it appears that it does not support system type ${escapeNixIdentifier system}.
|
|
||||||
''
|
|
||||||
else
|
|
||||||
throw ''
|
|
||||||
Attempt to access ${attrPath} of flake ${flakeIdentifier}, but it does not have attribute ${escapeNixIdentifier attrName}.${alternateAttrNameHint}
|
|
||||||
'';
|
|
||||||
|
|
||||||
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
transposition = lib.mkOption {
|
|
||||||
description = ''
|
|
||||||
A helper that defines transposed attributes in the flake outputs.
|
|
||||||
|
|
||||||
When you define `transposition.foo = { };`, definitions are added to the effect of (pseudo-code):
|
|
||||||
|
|
||||||
```nix
|
|
||||||
flake.foo.''${system} = (perSystem system).foo;
|
|
||||||
perInput = system: inputFlake: inputFlake.foo.''${system};
|
|
||||||
```
|
|
||||||
|
|
||||||
Transposition is the operation that swaps the indices of a data structure.
|
|
||||||
Here it refers specifically to the transposition between
|
|
||||||
|
|
||||||
```plain
|
|
||||||
perSystem: .''${system}.''${attribute}
|
|
||||||
outputs: .''${attribute}.''${system}
|
|
||||||
```
|
|
||||||
|
|
||||||
It also defines the reverse operation in [{option}`perInput`](#opt-perInput).
|
|
||||||
'';
|
|
||||||
type =
|
|
||||||
types.lazyAttrsOf
|
|
||||||
(types.submoduleWith { modules = [ transpositionModule ]; });
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
flake =
|
|
||||||
lib.mapAttrs
|
|
||||||
(attrName: attrConfig:
|
|
||||||
mapAttrs
|
|
||||||
(system: v: v.${attrName} or (
|
|
||||||
abort ''
|
|
||||||
Could not find option ${attrName} in the perSystem module. It is required to declare such an option whenever transposition.<name> is defined (and in this instance <name> is ${attrName}).
|
|
||||||
''))
|
|
||||||
config.allSystems
|
|
||||||
)
|
|
||||||
config.transposition;
|
|
||||||
|
|
||||||
perInput =
|
|
||||||
system: flake:
|
|
||||||
mapAttrs
|
|
||||||
(attrName: attrConfig:
|
|
||||||
flake.${attrName}.${system} or (
|
|
||||||
throw (perInputAttributeError { inherit system flake attrName attrConfig; })
|
|
||||||
)
|
|
||||||
)
|
|
||||||
config.transposition;
|
|
||||||
|
|
||||||
perSystem = {
|
|
||||||
options =
|
|
||||||
mapAttrs
|
|
||||||
(k: v: lib.mkOption { })
|
|
||||||
(filterAttrs
|
|
||||||
(k: v: v.adHoc)
|
|
||||||
config.transposition
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
{ lib, flake-parts-lib, getSystem, ... }:
|
|
||||||
let
|
|
||||||
inherit (lib)
|
|
||||||
mkOption
|
|
||||||
types
|
|
||||||
;
|
|
||||||
inherit (flake-parts-lib)
|
|
||||||
mkPerSystemOption
|
|
||||||
;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
perSystem = mkPerSystemOption ({ config, options, specialArgs, ... }: {
|
|
||||||
_file = ./perSystem.nix;
|
|
||||||
options = {
|
|
||||||
allModuleArgs = mkOption {
|
|
||||||
type = types.lazyAttrsOf (types.raw or types.unspecified);
|
|
||||||
internal = true;
|
|
||||||
readOnly = true;
|
|
||||||
description = "Internal option that exposes _module.args, for use by withSystem.";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
config = {
|
|
||||||
allModuleArgs = config._module.args // specialArgs // { inherit config options; };
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
_module.args = {
|
|
||||||
withSystem =
|
|
||||||
system: f:
|
|
||||||
f
|
|
||||||
(getSystem system).allModuleArgs;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
{
|
{
|
||||||
|
_snow,
|
||||||
lib,
|
lib,
|
||||||
specialArgs,
|
specialArgs,
|
||||||
...
|
...
|
||||||
|
|
@ -25,38 +26,20 @@
|
||||||
in
|
in
|
||||||
mkOption {
|
mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
Cerulean node declarations.
|
Snowflake node declarations.
|
||||||
'';
|
'';
|
||||||
type = types.submoduleWith {
|
type = types.submoduleWith {
|
||||||
inherit specialArgs;
|
inherit specialArgs;
|
||||||
|
|
||||||
modules = [
|
modules = [
|
||||||
{
|
./nodes.nix
|
||||||
imports = [./shared.nix];
|
|
||||||
|
|
||||||
options = {
|
|
||||||
groups = mkOption {
|
|
||||||
type = types.attrs;
|
|
||||||
default = {};
|
|
||||||
example = lib.literalExpression "{ servers = { staging = {}; production = {}; }; }";
|
|
||||||
description = ''
|
|
||||||
Hierarchical groups that nodes can be a member of.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
nodes = mkOption {
|
|
||||||
type = types.attrsOf (types.submoduleWith {
|
|
||||||
inherit specialArgs;
|
|
||||||
modules = [(import ./submodule.nix)];
|
|
||||||
});
|
|
||||||
# example = { ... }; # TODO
|
|
||||||
description = ''
|
|
||||||
Node (host systems) declarations.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
nodes = {
|
||||||
|
base = _snow.inputs.nixpkgs;
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
73
nix/snow/flake/nodes/groups.nix
Normal file
73
nix/snow/flake/nodes/groups.nix
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
# # Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
||||||
|
# #
|
||||||
|
# # Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# # you may not use this file except in compliance with the License.
|
||||||
|
# # You may obtain a copy of the License at
|
||||||
|
# #
|
||||||
|
# # http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
# #
|
||||||
|
# # Unless required by applicable law or agreed to in writing, software
|
||||||
|
# # distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# # See the License for the specific language governing permissions and
|
||||||
|
# # limitations under the License.
|
||||||
|
{nt, ...}: let
|
||||||
|
inherit
|
||||||
|
(builtins)
|
||||||
|
concatLists
|
||||||
|
elem
|
||||||
|
filter
|
||||||
|
isAttrs
|
||||||
|
mapAttrs
|
||||||
|
pathExists
|
||||||
|
typeOf
|
||||||
|
;
|
||||||
|
|
||||||
|
rootGroupName = "all";
|
||||||
|
in {
|
||||||
|
parseGroupsDecl = groups: let
|
||||||
|
validGroup = g:
|
||||||
|
isAttrs g
|
||||||
|
|| throw ''
|
||||||
|
Snow node groups must be provided as attribute sets, got "${typeOf g}" instead!
|
||||||
|
Ensure all the group definitions are attribute sets under your call to `snow.flake`.
|
||||||
|
NOTE: Groups can be accessed via `self.groups.PATH.TO.YOUR.GROUP`
|
||||||
|
'';
|
||||||
|
delegate = parent: gName: g: let
|
||||||
|
result =
|
||||||
|
(g
|
||||||
|
// {
|
||||||
|
_name = gName;
|
||||||
|
_parent = parent;
|
||||||
|
})
|
||||||
|
|> mapAttrs (name: value:
|
||||||
|
if elem name ["_name" "_parent"]
|
||||||
|
# ignore metadata fields
|
||||||
|
then value
|
||||||
|
else assert validGroup value; (delegate result name value));
|
||||||
|
in
|
||||||
|
result;
|
||||||
|
in
|
||||||
|
assert validGroup groups;
|
||||||
|
delegate null rootGroupName groups;
|
||||||
|
|
||||||
|
getGroupModules = root: groups:
|
||||||
|
# ensure root group is always added
|
||||||
|
groups
|
||||||
|
# add all inherited groups via _parent
|
||||||
|
|> map (let
|
||||||
|
delegate = g:
|
||||||
|
if g._parent == null
|
||||||
|
then [g]
|
||||||
|
else [g] ++ delegate (g._parent);
|
||||||
|
in
|
||||||
|
delegate)
|
||||||
|
# flatten recursion result
|
||||||
|
|> concatLists
|
||||||
|
# find import location
|
||||||
|
|> map (group: nt.findImport /${root}/groups/${group._name})
|
||||||
|
# filter by uniqueness
|
||||||
|
|> nt.prim.unique
|
||||||
|
# ignore missing groups
|
||||||
|
|> filter pathExists;
|
||||||
|
}
|
||||||
|
|
@ -14,16 +14,18 @@
|
||||||
{
|
{
|
||||||
lib,
|
lib,
|
||||||
systems,
|
systems,
|
||||||
|
config,
|
||||||
|
nodesConfig,
|
||||||
...
|
...
|
||||||
}: {
|
}: {
|
||||||
imports = [./shared.nix];
|
|
||||||
|
|
||||||
options = let
|
options = let
|
||||||
inherit
|
inherit
|
||||||
(lib)
|
(lib)
|
||||||
mkOption
|
mkOption
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
|
|
||||||
|
flakeRef = types.either types.str types.path;
|
||||||
in {
|
in {
|
||||||
enabled = lib.mkOption {
|
enabled = lib.mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
|
|
@ -43,6 +45,65 @@
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
base = lib.mkOption {
|
||||||
|
# In newer Nix versions, particularly with lazy trees, outPath of
|
||||||
|
# flakes becomes a Nix-language path object. We deliberately allow this
|
||||||
|
# to gracefully come through the interface in discussion with @roberth.
|
||||||
|
#
|
||||||
|
# See: https://github.com/NixOS/nixpkgs/pull/278522#discussion_r1460292639
|
||||||
|
type = types.nullOr flakeRef;
|
||||||
|
|
||||||
|
default = nodesConfig.base;
|
||||||
|
defaultText = "nodes.base";
|
||||||
|
|
||||||
|
example = lib.literalExpression "inputs.nixpkgs";
|
||||||
|
|
||||||
|
description = ''
|
||||||
|
The path to the nixpkgs source used to build a system. A `base` package set
|
||||||
|
is required to be set, and can be specified via either:
|
||||||
|
1. `options.nodes.base` (default `base` used for all systems)
|
||||||
|
2. `options.nodes.nodes.<name>.base` (takes prescedence over `options.nodes.base`)
|
||||||
|
|
||||||
|
This can also be optionally set if the NixOS system is not built with a flake but still uses
|
||||||
|
pinned sources: set this to the store path for the nixpkgs sources used to build the system,
|
||||||
|
as may be obtained by `fetchTarball`, for example.
|
||||||
|
|
||||||
|
Note: the name of the store path must be "source" due to
|
||||||
|
<https://github.com/NixOS/nix/issues/7075>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
homeManager = mkOption {
|
||||||
|
type = types.nullOr flakeRef;
|
||||||
|
default = nodesConfig.homeManager;
|
||||||
|
defaultText = "nodes.homeManager";
|
||||||
|
example = lib.literalExpression "inputs.home-manager";
|
||||||
|
description = ''
|
||||||
|
The path to the home-manager source. A `homeManager` flake reference
|
||||||
|
is required to be set for `homes/` to be evaluated, and can be specified via either:
|
||||||
|
1. `options.nodes.homeManager` (default `homManager` used for all systems)
|
||||||
|
2. `options.nodes.nodes.<name>.homeManager` (takes prescedence over `options.nodes.homeManager`)
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
modules = mkOption {
|
||||||
|
type = types.listOf types.raw;
|
||||||
|
default = [];
|
||||||
|
example = lib.literalExpression "[ { environment.systemPackages = [ pkgs.git ]; } ]";
|
||||||
|
description = ''
|
||||||
|
Shared modules to import; equivalent to the NixOS module system's `extraModules`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
args = mkOption {
|
||||||
|
type = types.attrs;
|
||||||
|
default = {};
|
||||||
|
example = lib.literalExpression "{ inherit inputs; }";
|
||||||
|
description = ''
|
||||||
|
Shared args to provided for each node; equivalent to the NixOS module system's `specialArgs`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
groups = mkOption {
|
groups = mkOption {
|
||||||
# TODO: write a custom group type that validates better than types.attrs lol
|
# TODO: write a custom group type that validates better than types.attrs lol
|
||||||
type = types.functionTo (types.listOf types.attrs);
|
type = types.functionTo (types.listOf types.attrs);
|
||||||
|
|
@ -51,6 +112,9 @@
|
||||||
description = ''
|
description = ''
|
||||||
A function from the `groups` hierarchy to a list of groups this node inherits from.
|
A function from the `groups` hierarchy to a list of groups this node inherits from.
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
apply = groupsFn:
|
||||||
|
groupsFn nodesConfig.groups;
|
||||||
};
|
};
|
||||||
|
|
||||||
deploy = {
|
deploy = {
|
||||||
|
|
@ -91,7 +155,7 @@
|
||||||
example = false;
|
example = false;
|
||||||
description = ''
|
description = ''
|
||||||
Whether to enable interactive sudo (password based sudo).
|
Whether to enable interactive sudo (password based sudo).
|
||||||
NOT RECOMMENDED. Use one of Cerulean's recommended auth methods instead.
|
NOT RECOMMENDED. Use one of Snowflake's recommended auth methods instead.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -164,7 +228,7 @@
|
||||||
|
|
||||||
user = mkOption {
|
user = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = "cerubld";
|
default = "snowbld";
|
||||||
example = "custom-user";
|
example = "custom-user";
|
||||||
description = ''
|
description = ''
|
||||||
The user to connect to over ssh during deployment.
|
The user to connect to over ssh during deployment.
|
||||||
|
|
@ -183,7 +247,7 @@
|
||||||
publicKeys = mkOption {
|
publicKeys = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
default = [];
|
default = [];
|
||||||
example = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIeyZuUUmyUYrYaEJwEMvcXqZFYm1NaZab8klOyK6Imr me@puter"];
|
example = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIeyZuUUmyUYrYaEJwEMvcXqZFYm1NaZab8klOyK6Imr me@myputer"];
|
||||||
description = ''
|
description = ''
|
||||||
SSH public keys that will be authorized to the deployment user.
|
SSH public keys that will be authorized to the deployment user.
|
||||||
This key is intended solely for deployment, allowing for fine-grained permission control.
|
This key is intended solely for deployment, allowing for fine-grained permission control.
|
||||||
|
|
@ -201,4 +265,33 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
config = let
|
||||||
|
throwGotNull = name:
|
||||||
|
throw ''
|
||||||
|
[snow] `nodes.<name>.${name}` must be set for all nodes! (got: <null>)
|
||||||
|
'';
|
||||||
|
givenSystem =
|
||||||
|
(config.system != null)
|
||||||
|
|| throwGotNull "system";
|
||||||
|
|
||||||
|
givenBase =
|
||||||
|
(config.base != null)
|
||||||
|
|| throwGotNull "base";
|
||||||
|
|
||||||
|
givenHomeManager =
|
||||||
|
(config.homeManager != null)
|
||||||
|
|| throwGotNull "homeManager";
|
||||||
|
|
||||||
|
givenDeployHost =
|
||||||
|
(config.deploy.ssh.host != null)
|
||||||
|
|| throwGotNull "deploy.ssh.host";
|
||||||
|
in
|
||||||
|
assert givenSystem
|
||||||
|
&& givenBase
|
||||||
|
&& givenHomeManager
|
||||||
|
&& givenDeployHost; {
|
||||||
|
# extend these from the nodes configuration
|
||||||
|
inherit (nodesConfig) modules args;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -11,7 +11,13 @@
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
{lib, ...}: let
|
{
|
||||||
|
_snow,
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
specialArgs,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
inherit
|
inherit
|
||||||
(lib)
|
(lib)
|
||||||
mkOption
|
mkOption
|
||||||
|
|
@ -19,6 +25,8 @@
|
||||||
;
|
;
|
||||||
|
|
||||||
flakeRef = types.either types.str types.path;
|
flakeRef = types.either types.str types.path;
|
||||||
|
|
||||||
|
groupLibs = import ./groups.nix {inherit (_snow.inputs) nt;};
|
||||||
in {
|
in {
|
||||||
options = {
|
options = {
|
||||||
base = lib.mkOption {
|
base = lib.mkOption {
|
||||||
|
|
@ -49,6 +57,18 @@ in {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
homeManager = mkOption {
|
||||||
|
type = types.nullOr flakeRef;
|
||||||
|
default = null;
|
||||||
|
example = lib.literalExpression "inputs.home-manager";
|
||||||
|
description = ''
|
||||||
|
The path to the home-manager source. A `homeManager` flake reference
|
||||||
|
is required to be set for `homes/` to be evaluated, and can be specified via either:
|
||||||
|
1. `options.nodes.homeManager` (default `homManager` used for all systems)
|
||||||
|
2. `options.nodes.nodes.<name>.homeManager` (takes prescedence over `options.nodes.homeManager`)
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
modules = mkOption {
|
modules = mkOption {
|
||||||
type = types.listOf types.raw;
|
type = types.listOf types.raw;
|
||||||
default = [];
|
default = [];
|
||||||
|
|
@ -67,15 +87,28 @@ in {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
homeManager = mkOption {
|
groups = mkOption {
|
||||||
type = types.nullOr flakeRef;
|
type = types.attrs;
|
||||||
default = null;
|
default = {};
|
||||||
example = lib.literalExpression "inputs.home-manager";
|
example = lib.literalExpression "{ servers = { staging = {}; production = {}; }; }";
|
||||||
description = ''
|
description = ''
|
||||||
The path to the home-manager source. A `homeManager` flake reference
|
Hierarchical groups that nodes can be a member of.
|
||||||
is required to be set for `homes/` to be evaluated, and can be specified via either:
|
'';
|
||||||
1. `options.nodes.homeManager` (default `homManager` used for all systems)
|
|
||||||
2. `options.nodes.nodes.<name>.homeManager` (takes prescedence over `options.nodes.homeManager`)
|
apply = groupLibs.parseGroupsDecl;
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes = mkOption {
|
||||||
|
type = types.attrsOf (types.submoduleWith {
|
||||||
|
specialArgs =
|
||||||
|
specialArgs
|
||||||
|
// {
|
||||||
|
nodeConfig = config;
|
||||||
|
};
|
||||||
|
modules = [./node.nix];
|
||||||
|
});
|
||||||
|
description = ''
|
||||||
|
Node (host systems) declarations.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
checks =
|
{
|
||||||
inputs.deploy-rs.lib
|
config,
|
||||||
|> mapAttrs (system: deployLib:
|
_snow,
|
||||||
deployLib.deployChecks deploy);
|
...
|
||||||
|
}: {
|
||||||
|
outputs.checks =
|
||||||
|
_snow.inputs.deploy-rs.lib
|
||||||
|
|> builtins.mapAttrs (system: deployLib:
|
||||||
|
deployLib.deployChecks config.outputs.deploy);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,37 @@
|
||||||
deploy.nodes = mapNodes nodes ({
|
{
|
||||||
|
_snow,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit
|
||||||
|
(builtins)
|
||||||
|
mapAttrs
|
||||||
|
;
|
||||||
|
|
||||||
|
mapNodes = nodes: f:
|
||||||
|
nodes.nodes
|
||||||
|
|> mapAttrs (name: node: let
|
||||||
|
# use per-node base or default to nodes' base
|
||||||
|
base =
|
||||||
|
if node.base != null
|
||||||
|
then node.base
|
||||||
|
else if nodes.base != null
|
||||||
|
then nodes.base
|
||||||
|
else
|
||||||
|
abort ''
|
||||||
|
snow cannot construct nodes node "${name}" without a base package source.
|
||||||
|
Ensure `nodes.nodes.*.base` or `nodes.base` is a flake reference to the github:NixOS/nixpkgs repository.
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
f rec {
|
||||||
|
inherit name node base;
|
||||||
|
inherit (base) lib;
|
||||||
|
|
||||||
|
groups = node.groups (parseGroupsDecl nodes.groups);
|
||||||
|
groupModules = root: getGroupModules root groups;
|
||||||
|
});
|
||||||
|
in {
|
||||||
|
outputs.deploy.nodes = mapNodes config.nodes ({
|
||||||
name,
|
name,
|
||||||
node,
|
node,
|
||||||
...
|
...
|
||||||
|
|
@ -16,7 +49,7 @@
|
||||||
confirmTimeout
|
confirmTimeout
|
||||||
;
|
;
|
||||||
|
|
||||||
nixosFor = system: inputs.deploy-rs.lib.${system}.activate.nixos;
|
nixosFor = system: _snow.inputs.deploy-rs.lib.${system}.activate.nixos;
|
||||||
in {
|
in {
|
||||||
hostname =
|
hostname =
|
||||||
if ssh.host != null
|
if ssh.host != null
|
||||||
|
|
@ -25,7 +58,7 @@
|
||||||
|
|
||||||
profilesOrder = ["default"]; # profiles priority
|
profilesOrder = ["default"]; # profiles priority
|
||||||
profiles.default = {
|
profiles.default = {
|
||||||
path = nixosFor node.system nixosConfigurations.${name};
|
path = nixosFor node.system config.outputs.nixosConfigurations.${name};
|
||||||
|
|
||||||
user = user;
|
user = user;
|
||||||
sudo = "sudo -u";
|
sudo = "sudo -u";
|
||||||
|
|
@ -43,15 +76,15 @@
|
||||||
sshOpts =
|
sshOpts =
|
||||||
ssh.opts
|
ssh.opts
|
||||||
++ (
|
++ (
|
||||||
if elem "-p" ssh.opts
|
if builtins.elem "-p" ssh.opts
|
||||||
then []
|
then []
|
||||||
else ["-p" (toString ssh.port)]
|
else ["-p" (toString ssh.port)]
|
||||||
)
|
)
|
||||||
++ (
|
++ (
|
||||||
if elem "-A" ssh.opts
|
if builtins.elem "-A" ssh.opts
|
||||||
then []
|
then []
|
||||||
else ["-A"]
|
else ["-A"]
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,26 @@
|
||||||
# options = { ... };
|
# options = { ... };
|
||||||
# type = { ... };
|
# type = { ... };
|
||||||
# }
|
# }
|
||||||
nixosConfigurations = mapNodes nodes (
|
{
|
||||||
|
snow,
|
||||||
|
config,
|
||||||
|
systems,
|
||||||
|
root,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit
|
||||||
|
(builtins)
|
||||||
|
all
|
||||||
|
attrNames
|
||||||
|
warn
|
||||||
|
;
|
||||||
|
|
||||||
|
inherit
|
||||||
|
(config)
|
||||||
|
nodes
|
||||||
|
;
|
||||||
|
in {
|
||||||
|
outputs.nixosConfigurations = mapNodes nodes (
|
||||||
{
|
{
|
||||||
base,
|
base,
|
||||||
lib,
|
lib,
|
||||||
|
|
@ -30,38 +49,37 @@
|
||||||
null;
|
null;
|
||||||
|
|
||||||
userArgs = nodes.args // node.args;
|
userArgs = nodes.args // node.args;
|
||||||
ceruleanArgs = {
|
snowArgs = {
|
||||||
inherit systems root base nodes node;
|
inherit systems snow root base nodes node;
|
||||||
inherit (node) system;
|
inherit (node) system;
|
||||||
inherit (this) snow;
|
|
||||||
hostname = name;
|
hostname = name;
|
||||||
|
|
||||||
_cerulean = {
|
_snow = {
|
||||||
inherit inputs userArgs ceruleanArgs homeManager;
|
inherit inputs userArgs snowArgs homeManager;
|
||||||
specialArgs = userArgs // ceruleanArgs;
|
specialArgs = userArgs // snowArgs;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
specialArgs = assert (userArgs
|
specialArgs = assert (userArgs
|
||||||
|> attrNames
|
|> attrNames
|
||||||
|> all (argName:
|
|> all (argName:
|
||||||
! ceruleanArgs ? argName
|
! snowArgs ? argName
|
||||||
|| abort ''
|
|| abort ''
|
||||||
`specialArgs` are like super important to Cerulean my love... </3
|
`specialArgs` are like super important to Snow my love... </3
|
||||||
But `args.${argName}` is a reserved argument name :(
|
But `args.${argName}` is a reserved argument name :(
|
||||||
''));
|
''));
|
||||||
ceruleanArgs._cerulean.specialArgs;
|
snowArgs._snow.specialArgs;
|
||||||
in
|
in
|
||||||
lib.nixosSystem {
|
lib.nixosSystem {
|
||||||
inherit (node) system;
|
inherit (node) system;
|
||||||
inherit specialArgs;
|
inherit specialArgs;
|
||||||
modules =
|
modules =
|
||||||
[
|
[
|
||||||
self.nixosModules.default
|
snow.nixosModules.default
|
||||||
(this.findImport /${root}/hosts/${name})
|
(snow.findImport /${root}/hosts/${name})
|
||||||
]
|
]
|
||||||
++ (groupModules root)
|
++ (groupModules root)
|
||||||
++ node.modules
|
++ node.modules
|
||||||
++ nodes.modules;
|
++ nodes.modules;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
27
nix/snow/lib/default.nix
Normal file
27
nix/snow/lib/default.nix
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
{
|
||||||
|
nt,
|
||||||
|
mix,
|
||||||
|
...
|
||||||
|
} @ args: let
|
||||||
|
inherit (nt) findImport;
|
||||||
|
in
|
||||||
|
mix.newMixture args (mixture: {
|
||||||
|
includes.public = [
|
||||||
|
./nixpkgs.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
inherit findImport;
|
||||||
|
})
|
||||||
51
nix/snow/lib/nixpkgs.nix
Normal file
51
nix/snow/lib/nixpkgs.nix
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
{
|
||||||
|
inputs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit
|
||||||
|
(lib)
|
||||||
|
mkOption
|
||||||
|
types
|
||||||
|
;
|
||||||
|
|
||||||
|
# A best effort, lenient estimate. Please use a recent nixpkgs lib if you
|
||||||
|
# override it at all.
|
||||||
|
minVersion = "23.05pre-git";
|
||||||
|
|
||||||
|
isNixpkgsValidVersion = let
|
||||||
|
revInfo = lib.optional (inputs.nixpkgs?rev) " (nixpkgs-lib.rev: ${inputs.nixpkgs.rev})";
|
||||||
|
in
|
||||||
|
(builtins.compareVersions lib.version minVersion < 0)
|
||||||
|
|| abort ''
|
||||||
|
The nixpkgs dependency of snow was overridden but is too old.
|
||||||
|
The minimum supported version of nixpkgs-lib is ${minVersion},
|
||||||
|
but the actual version is ${lib.version}${revInfo}.
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
assert isNixpkgsValidVersion; {
|
||||||
|
# Helper function for defining a per-system option that
|
||||||
|
# gets transposed by the usual flake system logic to a
|
||||||
|
# top-level outputs attribute.
|
||||||
|
mkPerSystemFlakeOutput = {
|
||||||
|
name,
|
||||||
|
option,
|
||||||
|
file,
|
||||||
|
}: {
|
||||||
|
_file = file;
|
||||||
|
|
||||||
|
options = {
|
||||||
|
outputs.${name} = mkOption {
|
||||||
|
type = types.attrsWith {
|
||||||
|
elemType = option.type;
|
||||||
|
lazy = true;
|
||||||
|
placeholder = "system";
|
||||||
|
};
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
See {option}`perSystem.${name}` for description and examples.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
{nt, ...}: let
|
|
||||||
inherit
|
|
||||||
(builtins)
|
|
||||||
concatLists
|
|
||||||
elem
|
|
||||||
filter
|
|
||||||
isAttrs
|
|
||||||
mapAttrs
|
|
||||||
pathExists
|
|
||||||
typeOf
|
|
||||||
;
|
|
||||||
|
|
||||||
rootGroupName = "all";
|
|
||||||
|
|
||||||
parseGroupsDecl = groups: let
|
|
||||||
validGroup = g:
|
|
||||||
isAttrs g
|
|
||||||
|| throw ''
|
|
||||||
Cerulean Nexus groups must be provided as attribute sets, got "${typeOf g}" instead!
|
|
||||||
Ensure all the group definitions are attribute sets under your call to `cerulean.mkNexus`.
|
|
||||||
NOTE: Groups can be accessed via `self.groups.PATH.TO.YOUR.GROUP`
|
|
||||||
'';
|
|
||||||
delegate = parent: gName: g: let
|
|
||||||
result =
|
|
||||||
(g
|
|
||||||
// {
|
|
||||||
_name = gName;
|
|
||||||
_parent = parent;
|
|
||||||
})
|
|
||||||
|> mapAttrs (name: value:
|
|
||||||
if elem name ["_name" "_parent"]
|
|
||||||
# ignore metadata fields
|
|
||||||
then value
|
|
||||||
else assert validGroup value; (delegate result name value));
|
|
||||||
in
|
|
||||||
result;
|
|
||||||
in
|
|
||||||
assert validGroup groups;
|
|
||||||
delegate null rootGroupName groups;
|
|
||||||
|
|
||||||
getGroupModules = root: groups:
|
|
||||||
# ensure root group is always added
|
|
||||||
groups
|
|
||||||
# add all inherited groups via _parent
|
|
||||||
|> map (let
|
|
||||||
delegate = g:
|
|
||||||
if g._parent == null
|
|
||||||
then [g]
|
|
||||||
else [g] ++ delegate (g._parent);
|
|
||||||
in
|
|
||||||
delegate)
|
|
||||||
# flatten recursion result
|
|
||||||
|> concatLists
|
|
||||||
# find import location
|
|
||||||
|> map (group: nt.findImport /${root}/groups/${group._name})
|
|
||||||
# filter by uniqueness
|
|
||||||
|> nt.prim.unique
|
|
||||||
# ignore missing groups
|
|
||||||
|> filter pathExists;
|
|
||||||
in {
|
|
||||||
mapNodes = nodes: f:
|
|
||||||
nodes.nodes
|
|
||||||
|> mapAttrs (name: node: let
|
|
||||||
# use per-node base or default to nodes' base
|
|
||||||
base =
|
|
||||||
if node.base != null
|
|
||||||
then node.base
|
|
||||||
else if nodes.base != null
|
|
||||||
then nodes.base
|
|
||||||
else
|
|
||||||
abort ''
|
|
||||||
Cerulean cannot construct nodes node "${name}" without a base package source.
|
|
||||||
Ensure `nodes.nodes.*.base` or `nodes.base` is a flake reference to the github:NixOS/nixpkgs repository.
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
f rec {
|
|
||||||
inherit name node base;
|
|
||||||
inherit (base) lib;
|
|
||||||
|
|
||||||
groups = node.groups (parseGroupsDecl nodes.groups);
|
|
||||||
groupModules = root: getGroupModules root groups;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue