clan-core: add clan meta for ui usage

This commit is contained in:
Johannes Kirschbauer 2024-05-31 17:22:38 +02:00 committed by hsjobeki
parent 481f926b17
commit 83fe58e003
9 changed files with 167 additions and 19 deletions

View File

@ -24,10 +24,14 @@
}; };
outputs = outputs =
inputs@{ flake-parts, ... }: inputs@{ flake-parts, self, ... }:
flake-parts.lib.mkFlake { inherit inputs; } ( flake-parts.lib.mkFlake { inherit inputs; } (
{ ... }: { ... }:
{ {
clan = {
# meta.name = "clan-core";
directory = self;
};
systems = [ systems = [
"x86_64-linux" "x86_64-linux"
"aarch64-linux" "aarch64-linux"

View File

@ -17,6 +17,33 @@ let
cfg = config.clan; cfg = config.clan;
in in
{ {
imports = [
# TODO: figure out how to print the deprecation warning
# "${inputs.nixpkgs}/nixos/modules/misc/assertions.nix"
(lib.mkRenamedOptionModule
[
"clan"
"clanName"
]
[
"clan"
"meta"
"name"
]
)
(lib.mkRenamedOptionModule
[
"clan"
"clanIcon"
]
[
"clan"
"meta"
"icon"
]
)
];
options.clan = { options.clan = {
directory = mkOption { directory = mkOption {
type = types.path; type = types.path;
@ -33,15 +60,27 @@ in
default = { }; default = { };
description = "Allows to include machine-specific modules i.e. machines.\${name} = { ... }"; description = "Allows to include machine-specific modules i.e. machines.\${name} = { ... }";
}; };
clanName = mkOption {
type = types.str; # Checks are performed in 'buildClan'
# Not everyone uses flake-parts
meta = {
name = lib.mkOption {
type = types.nullOr types.str;
default = null;
description = "Needs to be (globally) unique, as this determines the folder name where the flake gets downloaded to."; description = "Needs to be (globally) unique, as this determines the folder name where the flake gets downloaded to.";
}; };
clanIcon = mkOption { icon = mkOption {
type = types.nullOr types.path; type = types.nullOr types.path;
default = null; default = null;
description = "A path to an icon to be used for the clan, should be the same for all machines"; description = "A path to an icon to be used for the clan in the GUI";
}; };
description = mkOption {
type = types.nullOr types.str;
default = null;
description = "A short description of the clan";
};
};
pkgsForSystem = mkOption { pkgsForSystem = mkOption {
type = types.functionTo types.raw; type = types.functionTo types.raw;
default = _system: null; default = _system: null;
@ -52,6 +91,7 @@ in
clanInternals = lib.mkOption { clanInternals = lib.mkOption {
type = lib.types.submodule { type = lib.types.submodule {
options = { options = {
meta = lib.mkOption { type = lib.types.attrsOf lib.types.unspecified; };
all-machines-json = lib.mkOption { type = lib.types.attrsOf lib.types.unspecified; }; all-machines-json = lib.mkOption { type = lib.types.attrsOf lib.types.unspecified; };
machines = lib.mkOption { type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified); }; machines = lib.mkOption { type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified); };
machinesFunc = lib.mkOption { type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified); }; machinesFunc = lib.mkOption { type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified); };
@ -65,9 +105,8 @@ in
directory directory
specialArgs specialArgs
machines machines
clanName
clanIcon
pkgsForSystem pkgsForSystem
meta
; ;
}; };
}; };

View File

@ -7,16 +7,58 @@
directory, # The directory containing the machines subdirectory directory, # The directory containing the machines subdirectory
specialArgs ? { }, # Extra arguments to pass to nixosSystem i.e. useful to make self available specialArgs ? { }, # Extra arguments to pass to nixosSystem i.e. useful to make self available
machines ? { }, # allows to include machine-specific modules i.e. machines.${name} = { ... } machines ? { }, # allows to include machine-specific modules i.e. machines.${name} = { ... }
clanName, # Needs to be (globally) unique, as this determines the folder name where the flake gets downloaded to. # DEPRECATED: use meta.name instead
clanName ? null, # Needs to be (globally) unique, as this determines the folder name where the flake gets downloaded to.
# DEPRECATED: use meta.icon instead
clanIcon ? null, # A path to an icon to be used for the clan, should be the same for all machines clanIcon ? null, # A path to an icon to be used for the clan, should be the same for all machines
meta ? { }, # A set containing clan meta: name :: string, icon :: string, description :: string
pkgsForSystem ? (_system: null), # A map from arch to pkgs, if specified this nixpkgs will be only imported once for each system. pkgsForSystem ? (_system: null), # A map from arch to pkgs, if specified this nixpkgs will be only imported once for each system.
# This improves performance, but all nipxkgs.* options will be ignored. # This improves performance, but all nipxkgs.* options will be ignored.
}: }:
let let
deprecationWarnings = [
(lib.warnIf (
clanName != null
) "clanName is deprecated, please use meta.name instead. ${clanName}" null)
(lib.warnIf (clanIcon != null) "clanIcon is deprecated, please use meta.icon instead" null)
];
machinesDirs = lib.optionalAttrs (builtins.pathExists "${directory}/machines") ( machinesDirs = lib.optionalAttrs (builtins.pathExists "${directory}/machines") (
builtins.readDir (directory + /machines) builtins.readDir (directory + /machines)
); );
mergedMeta =
let
metaFromFile =
if (builtins.pathExists "${directory}/clan/meta.json") then
let
settings = builtins.fromJSON (builtins.readFile "${directory}/clan/meta.json");
in
settings
else
{ };
legacyMeta = lib.filterAttrs (_: v: v != null) {
name = clanName;
icon = clanIcon;
};
optionsMeta = lib.filterAttrs (_: v: v != null) meta;
warnings =
builtins.map (
name:
if
metaFromFile.${name} or null != optionsMeta.${name} or null && optionsMeta.${name} or null != null
then
lib.warn "meta.${name} is set in different places. (exlicit option meta.${name} overrides ${directory}/clan/meta.json)" null
else
null
) (builtins.attrNames metaFromFile)
++ [ (if (res.name or null == null) then (throw "meta.name should be set") else null) ];
res = metaFromFile // legacyMeta // optionsMeta;
in
# Print out warnings before returning the merged result
builtins.deepSeq warnings res;
machineSettings = machineSettings =
machineName: machineName:
# CLAN_MACHINE_SETTINGS_FILE allows to override the settings file temporarily # CLAN_MACHINE_SETTINGS_FILE allows to override the settings file temporarily
@ -58,11 +100,15 @@ let
(machines.${name} or { }) (machines.${name} or { })
( (
{ {
networking.hostName = lib.mkDefault name; # Settings
clanCore.clanName = clanName;
clanCore.clanIcon = clanIcon;
clanCore.clanDir = directory; clanCore.clanDir = directory;
# Inherited from clan wide settings
clanCore.clanName = meta.name or clanName;
clanCore.clanIcon = meta.icon or clanIcon;
# Machine specific settings
clanCore.machineName = name; clanCore.machineName = name;
networking.hostName = lib.mkDefault name;
nixpkgs.hostPlatform = lib.mkDefault system; nixpkgs.hostPlatform = lib.mkDefault system;
# speeds up nix commands by using the nixpkgs from the host system (especially useful in VMs) # speeds up nix commands by using the nixpkgs from the host system (especially useful in VMs)
@ -127,10 +173,15 @@ let
) supportedSystems ) supportedSystems
); );
in in
{ builtins.deepSeq deprecationWarnings {
inherit nixosConfigurations; inherit nixosConfigurations;
clanInternals = { clanInternals = {
# Evaluated clan meta
# Merged /clan/meta.json with overrides from buildClan
meta = mergedMeta;
# machine specifics
machines = configsPerSystem; machines = configsPerSystem;
machinesFunc = configsFuncPerSystem; machinesFunc = configsFuncPerSystem;
all-machines-json = lib.mapAttrs ( all-machines-json = lib.mapAttrs (

View File

@ -47,3 +47,33 @@ def test_create_flake(
flake_outputs["nixosConfigurations"]["machine1"] flake_outputs["nixosConfigurations"]["machine1"]
except KeyError: except KeyError:
pytest.fail("nixosConfigurations.machine1 not found in flake outputs") pytest.fail("nixosConfigurations.machine1 not found in flake outputs")
@pytest.mark.impure
def test_ui_template(
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture,
temporary_home: Path,
cli: Cli,
) -> None:
flake_dir = temporary_home / "test-flake"
url = "git+https://git.clan.lol/clan/clan-core#empty"
cli.run(["flakes", "create", str(flake_dir), "--url", url])
assert (flake_dir / ".clan-flake").exists()
monkeypatch.chdir(flake_dir)
cli.run(["machines", "create", "machine1"])
capsys.readouterr() # flush cache
cli.run(["machines", "list"])
assert "machine1" in capsys.readouterr().out
flake_show = subprocess.run(
["nix", "flake", "show", "--json"],
check=True,
capture_output=True,
text=True,
)
flake_outputs = json.loads(flake_show.stdout)
try:
flake_outputs["nixosConfigurations"]["machine1"]
except KeyError:
pytest.fail("nixosConfigurations.machine1 not found in flake outputs")

View File

@ -98,9 +98,6 @@ let
in in
{ {
clan = { clan = {
clanName = "clan-core";
directory = self;
# To build a generic installer image (without ssh pubkeys), # To build a generic installer image (without ssh pubkeys),
# use the following command: # use the following command:
# $ nix build .#iso-installer # $ nix build .#iso-installer

View File

@ -0,0 +1,2 @@
# DO NOT DELETE
# This file is used by the clan cli to discover a clan flake

View File

@ -0,0 +1,5 @@
{
"name": "My Empty Clan",
"description": "some nice description",
"icon": "A path to the png"
}

16
templates/empty/flake.nix Normal file
View File

@ -0,0 +1,16 @@
# Clan configuration file
# TODO: This file is used as a template for the simple GUI workflow
{
inputs.clan-core.url = "git+file:///home/johannes/git/clan-core";
outputs =
{ self, clan-core, ... }:
let
clan = clan-core.lib.buildClan {
# This clan builds all its configuration out of the current directory
directory = self;
};
in
{
inherit (clan) nixosConfigurations clanInternals;
};
}

View File

@ -5,6 +5,10 @@
description = "Initialize a new clan flake"; description = "Initialize a new clan flake";
path = ./new-clan; path = ./new-clan;
}; };
empty = {
description = "A empty clan template. Primarily for usage with the clan ui";
path = ./empty;
};
default = self.templates.new-clan; default = self.templates.new-clan;
}; };
} }