treewide: reformat

This commit is contained in:
Michael Hoang
2025-08-08 15:27:00 +10:00
parent 6b137f21de
commit a71a5880c1
40 changed files with 738 additions and 740 deletions

View File

@@ -19,11 +19,10 @@
... ...
}: }:
let let
dependencies = dependencies = [
[ pkgs.stdenv.drvPath
pkgs.stdenv.drvPath ]
] ++ builtins.map (i: i.outPath) (builtins.attrValues (builtins.removeAttrs self.inputs [ "self" ]));
++ builtins.map (i: i.outPath) (builtins.attrValues (builtins.removeAttrs self.inputs [ "self" ]));
closureInfo = pkgs.closureInfo { rootPaths = dependencies; }; closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
in in
{ {
@@ -154,13 +153,12 @@
nixos-test-backups = self.clanLib.test.containerTest { nixos-test-backups = self.clanLib.test.containerTest {
name = "nixos-test-backups"; name = "nixos-test-backups";
nodes.machine = { nodes.machine = {
imports = imports = [
[ self.nixosModules.clanCore
self.nixosModules.clanCore # Some custom overrides for the backup tests
# Some custom overrides for the backup tests self.nixosModules.test-backup
self.nixosModules.test-backup ]
] ++
++
# import the inventory generated nixosModules # import the inventory generated nixosModules
self.clan.clanInternals.inventoryClass.machines.test-backup.machineImports; self.clan.clanInternals.inventoryClass.machines.test-backup.machineImports;
clan.core.settings.directory = ./.; clan.core.settings.directory = ./.;

View File

@@ -50,7 +50,8 @@
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.toplevel self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.toplevel
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript.drvPath self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript.drvPath
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs); ]
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
closureInfo = pkgs.closureInfo { rootPaths = dependencies; }; closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
in in
{ {

View File

@@ -158,7 +158,8 @@
pkgs.stdenv.drvPath pkgs.stdenv.drvPath
pkgs.bash.drvPath pkgs.bash.drvPath
pkgs.buildPackages.xorg.lndir pkgs.buildPackages.xorg.lndir
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs); ]
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
}; };
in in
pkgs.lib.mkIf (pkgs.stdenv.isLinux && !pkgs.stdenv.isAarch64) { pkgs.lib.mkIf (pkgs.stdenv.isLinux && !pkgs.stdenv.isAarch64) {

View File

@@ -159,7 +159,8 @@ let
pkgs.stdenv.drvPath pkgs.stdenv.drvPath
pkgs.bash.drvPath pkgs.bash.drvPath
pkgs.buildPackages.xorg.lndir pkgs.buildPackages.xorg.lndir
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs); ]
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
}; };
in in

View File

@@ -35,7 +35,8 @@
pkgs.stdenv.drvPath pkgs.stdenv.drvPath
pkgs.stdenvNoCC pkgs.stdenvNoCC
self.nixosConfigurations.test-morph-machine.config.system.build.toplevel self.nixosConfigurations.test-morph-machine.config.system.build.toplevel
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs); ]
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
closureInfo = pkgs.closureInfo { rootPaths = dependencies; }; closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
in in

View File

@@ -112,7 +112,8 @@
pkgs.stdenv.drvPath pkgs.stdenv.drvPath
pkgs.bash.drvPath pkgs.bash.drvPath
pkgs.buildPackages.xorg.lndir pkgs.buildPackages.xorg.lndir
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs); ]
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
}; };
in in
self.clanLib.test.containerTest { self.clanLib.test.containerTest {

View File

@@ -112,125 +112,124 @@ in
''; '';
in in
lib.mkIf (cfg.targets != { }) { lib.mkIf (cfg.targets != { }) {
environment.systemPackages = environment.systemPackages = [
[ (pkgs.writeShellScriptBin "localbackup-create" ''
(pkgs.writeShellScriptBin "localbackup-create" '' set -efu -o pipefail
set -efu -o pipefail export PATH=${
export PATH=${ lib.makeBinPath [
lib.makeBinPath [ pkgs.rsnapshot
pkgs.rsnapshot pkgs.coreutils
pkgs.coreutils pkgs.util-linux
pkgs.util-linux ]
] }
} ${lib.concatMapStringsSep "\n" (target: ''
${lib.concatMapStringsSep "\n" (target: '' ${mountHook target}
${mountHook target} echo "Creating backup '${target.name}'"
echo "Creating backup '${target.name}'"
${lib.optionalString (target.preBackupHook != null) '' ${lib.optionalString (target.preBackupHook != null) ''
( (
${target.preBackupHook} ${target.preBackupHook}
) )
''}
declare -A preCommandErrors
${lib.concatMapStringsSep "\n" (
state:
lib.optionalString (state.preBackupCommand != null) ''
echo "Running pre-backup command for ${state.name}"
if ! /run/current-system/sw/bin/${state.preBackupCommand}; then
preCommandErrors["${state.name}"]=1
fi
''
) (builtins.attrValues config.clan.core.state)}
rsnapshot -c "${pkgs.writeText "rsnapshot.conf" (rsnapshotConfig target)}" sync
rsnapshot -c "${pkgs.writeText "rsnapshot.conf" (rsnapshotConfig target)}" snapshot
'') (builtins.attrValues cfg.targets)}'')
(pkgs.writeShellScriptBin "localbackup-list" ''
set -efu -o pipefail
export PATH=${
lib.makeBinPath [
pkgs.jq
pkgs.findutils
pkgs.coreutils
pkgs.util-linux
]
}
(${
lib.concatMapStringsSep "\n" (target: ''
(
${mountHook target}
find ${lib.escapeShellArg target.directory} -mindepth 1 -maxdepth 1 -name "snapshot.*" -print0 -type d \
| jq -Rs 'split("\u0000") | .[] | select(. != "") | { "name": ("${target.name}::" + .)}'
)
'') (builtins.attrValues cfg.targets)
}) | jq -s .
'')
(pkgs.writeShellScriptBin "localbackup-restore" ''
set -efu -o pipefail
export PATH=${
lib.makeBinPath [
pkgs.rsync
pkgs.coreutils
pkgs.util-linux
pkgs.gawk
]
}
if [[ "''${NAME:-}" == "" ]]; then
echo "No backup name given via NAME environment variable"
exit 1
fi
if [[ "''${FOLDERS:-}" == "" ]]; then
echo "No folders given via FOLDERS environment variable"
exit 1
fi
name=$(awk -F'::' '{print $1}' <<< $NAME)
backupname=''${NAME#$name::}
if command -v localbackup-mount-$name; then
localbackup-mount-$name
fi
if command -v localbackup-unmount-$name; then
trap "localbackup-unmount-$name" EXIT
fi
if [[ ! -d $backupname ]]; then
echo "No backup found $backupname"
exit 1
fi
IFS=':' read -ra FOLDER <<< "''$FOLDERS"
for folder in "''${FOLDER[@]}"; do
mkdir -p "$folder"
rsync -a "$backupname/${config.networking.hostName}$folder/" "$folder"
done
'')
]
++ (lib.mapAttrsToList (
name: target:
pkgs.writeShellScriptBin ("localbackup-mount-" + name) ''
set -efu -o pipefail
${lib.optionalString (target.preMountHook != null) target.preMountHook}
${lib.optionalString (target.mountpoint != null) ''
if ! ${pkgs.util-linux}/bin/mountpoint -q ${lib.escapeShellArg target.mountpoint}; then
${pkgs.util-linux}/bin/mount -o X-mount.mkdir ${lib.escapeShellArg target.mountpoint}
fi
''} ''}
${lib.optionalString (target.postMountHook != null) target.postMountHook}
'' declare -A preCommandErrors
) cfg.targets) ${lib.concatMapStringsSep "\n" (
++ lib.mapAttrsToList ( state:
name: target: lib.optionalString (state.preBackupCommand != null) ''
pkgs.writeShellScriptBin ("localbackup-unmount-" + name) '' echo "Running pre-backup command for ${state.name}"
set -efu -o pipefail if ! /run/current-system/sw/bin/${state.preBackupCommand}; then
${lib.optionalString (target.preUnmountHook != null) target.preUnmountHook} preCommandErrors["${state.name}"]=1
${lib.optionalString ( fi
target.mountpoint != null ''
) "${pkgs.util-linux}/bin/umount ${lib.escapeShellArg target.mountpoint}"} ) (builtins.attrValues config.clan.core.state)}
${lib.optionalString (target.postUnmountHook != null) target.postUnmountHook}
'' rsnapshot -c "${pkgs.writeText "rsnapshot.conf" (rsnapshotConfig target)}" sync
) cfg.targets; rsnapshot -c "${pkgs.writeText "rsnapshot.conf" (rsnapshotConfig target)}" snapshot
'') (builtins.attrValues cfg.targets)}'')
(pkgs.writeShellScriptBin "localbackup-list" ''
set -efu -o pipefail
export PATH=${
lib.makeBinPath [
pkgs.jq
pkgs.findutils
pkgs.coreutils
pkgs.util-linux
]
}
(${
lib.concatMapStringsSep "\n" (target: ''
(
${mountHook target}
find ${lib.escapeShellArg target.directory} -mindepth 1 -maxdepth 1 -name "snapshot.*" -print0 -type d \
| jq -Rs 'split("\u0000") | .[] | select(. != "") | { "name": ("${target.name}::" + .)}'
)
'') (builtins.attrValues cfg.targets)
}) | jq -s .
'')
(pkgs.writeShellScriptBin "localbackup-restore" ''
set -efu -o pipefail
export PATH=${
lib.makeBinPath [
pkgs.rsync
pkgs.coreutils
pkgs.util-linux
pkgs.gawk
]
}
if [[ "''${NAME:-}" == "" ]]; then
echo "No backup name given via NAME environment variable"
exit 1
fi
if [[ "''${FOLDERS:-}" == "" ]]; then
echo "No folders given via FOLDERS environment variable"
exit 1
fi
name=$(awk -F'::' '{print $1}' <<< $NAME)
backupname=''${NAME#$name::}
if command -v localbackup-mount-$name; then
localbackup-mount-$name
fi
if command -v localbackup-unmount-$name; then
trap "localbackup-unmount-$name" EXIT
fi
if [[ ! -d $backupname ]]; then
echo "No backup found $backupname"
exit 1
fi
IFS=':' read -ra FOLDER <<< "''$FOLDERS"
for folder in "''${FOLDER[@]}"; do
mkdir -p "$folder"
rsync -a "$backupname/${config.networking.hostName}$folder/" "$folder"
done
'')
]
++ (lib.mapAttrsToList (
name: target:
pkgs.writeShellScriptBin ("localbackup-mount-" + name) ''
set -efu -o pipefail
${lib.optionalString (target.preMountHook != null) target.preMountHook}
${lib.optionalString (target.mountpoint != null) ''
if ! ${pkgs.util-linux}/bin/mountpoint -q ${lib.escapeShellArg target.mountpoint}; then
${pkgs.util-linux}/bin/mount -o X-mount.mkdir ${lib.escapeShellArg target.mountpoint}
fi
''}
${lib.optionalString (target.postMountHook != null) target.postMountHook}
''
) cfg.targets)
++ lib.mapAttrsToList (
name: target:
pkgs.writeShellScriptBin ("localbackup-unmount-" + name) ''
set -efu -o pipefail
${lib.optionalString (target.preUnmountHook != null) target.preUnmountHook}
${lib.optionalString (
target.mountpoint != null
) "${pkgs.util-linux}/bin/umount ${lib.escapeShellArg target.mountpoint}"}
${lib.optionalString (target.postUnmountHook != null) target.postUnmountHook}
''
) cfg.targets;
clan.core.backups.providers.localbackup = { clan.core.backups.providers.localbackup = {
# TODO list needs to run locally or on the remote machine # TODO list needs to run locally or on the remote machine

View File

@@ -116,47 +116,45 @@ in
}; };
clan.core.postgresql.databases.matrix-synapse.restore.stopOnRestore = [ "matrix-synapse" ]; clan.core.postgresql.databases.matrix-synapse.restore.stopOnRestore = [ "matrix-synapse" ];
clan.core.vars.generators = clan.core.vars.generators = {
{ "matrix-synapse" = {
"matrix-synapse" = { files."synapse-registration_shared_secret" = { };
files."synapse-registration_shared_secret" = { }; runtimeInputs = with pkgs; [
runtimeInputs = with pkgs; [ coreutils
coreutils pwgen
pwgen ];
]; migrateFact = "matrix-synapse";
migrateFact = "matrix-synapse"; script = ''
script = '' echo -n "$(pwgen -s 32 1)" > "$out"/synapse-registration_shared_secret
echo -n "$(pwgen -s 32 1)" > "$out"/synapse-registration_shared_secret '';
''; };
}; }
// lib.mapAttrs' (
name: user:
lib.nameValuePair "matrix-password-${user.name}" {
files."matrix-password-${user.name}" = { };
migrateFact = "matrix-password-${user.name}";
runtimeInputs = with pkgs; [ xkcdpass ];
script = ''
xkcdpass -n 4 -d - > "$out"/${lib.escapeShellArg "matrix-password-${user.name}"}
'';
} }
// lib.mapAttrs' ( ) cfg.users;
name: user:
lib.nameValuePair "matrix-password-${user.name}" {
files."matrix-password-${user.name}" = { };
migrateFact = "matrix-password-${user.name}";
runtimeInputs = with pkgs; [ xkcdpass ];
script = ''
xkcdpass -n 4 -d - > "$out"/${lib.escapeShellArg "matrix-password-${user.name}"}
'';
}
) cfg.users;
systemd.services.matrix-synapse = systemd.services.matrix-synapse =
let let
usersScript = usersScript = ''
'' while ! ${pkgs.netcat}/bin/nc -z -v ::1 8008; do
while ! ${pkgs.netcat}/bin/nc -z -v ::1 8008; do if ! kill -0 "$MAINPID"; then exit 1; fi
if ! kill -0 "$MAINPID"; then exit 1; fi sleep 1;
sleep 1; done
done ''
'' + lib.concatMapStringsSep "\n" (user: ''
+ lib.concatMapStringsSep "\n" (user: '' # only create user if it doesn't exist
# only create user if it doesn't exist /run/current-system/sw/bin/matrix-synapse-register_new_matrix_user --exists-ok --password-file ${
/run/current-system/sw/bin/matrix-synapse-register_new_matrix_user --exists-ok --password-file ${ config.clan.core.vars.generators."matrix-password-${user.name}".files."matrix-password-${user.name}".path
config.clan.core.vars.generators."matrix-password-${user.name}".files."matrix-password-${user.name}".path } --user "${user.name}" ${if user.admin then "--admin" else "--no-admin"}
} --user "${user.name}" ${if user.admin then "--admin" else "--no-admin"} '') (lib.attrValues cfg.users);
'') (lib.attrValues cfg.users);
in in
{ {
path = [ pkgs.curl ]; path = [ pkgs.curl ];

View File

@@ -18,13 +18,12 @@
config.clan.core.vars.generators.root-password.files.password-hash.path; config.clan.core.vars.generators.root-password.files.password-hash.path;
clan.core.vars.generators.root-password = { clan.core.vars.generators.root-password = {
files.password-hash = files.password-hash = {
{ neededFor = "users";
neededFor = "users"; }
} // (lib.optionalAttrs (_class == "nixos") {
// (lib.optionalAttrs (_class == "nixos") { restartUnits = lib.optional (config.services.userborn.enable) "userborn.service";
restartUnits = lib.optional (config.services.userborn.enable) "userborn.service"; });
});
files.password = { files.password = {
deploy = false; deploy = false;
}; };

View File

@@ -32,17 +32,16 @@ in
cfg.certificate.searchDomains != [ ] cfg.certificate.searchDomains != [ ]
) config.clan.core.vars.generators.openssh-cert.files."ssh.id_ed25519-cert.pub".path; ) config.clan.core.vars.generators.openssh-cert.files."ssh.id_ed25519-cert.pub".path;
hostKeys = hostKeys = [
[ {
{ path = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519".path;
path = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519".path; type = "ed25519";
type = "ed25519"; }
} ]
] ++ lib.optional cfg.hostKeys.rsa.enable {
++ lib.optional cfg.hostKeys.rsa.enable { path = config.clan.core.vars.generators.openssh-rsa.files."ssh.id_rsa".path;
path = config.clan.core.vars.generators.openssh-rsa.files."ssh.id_rsa".path; type = "rsa";
type = "rsa"; };
};
}; };
clan.core.vars.generators.openssh = { clan.core.vars.generators.openssh = {
@@ -62,7 +61,8 @@ in
hostNames = [ hostNames = [
"localhost" "localhost"
config.networking.hostName config.networking.hostName
] ++ (lib.optional (config.networking.domain != null) config.networking.fqdn); ]
++ (lib.optional (config.networking.domain != null) config.networking.fqdn);
publicKey = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519.pub".value; publicKey = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519.pub".value;
}; };

View File

@@ -34,14 +34,15 @@ let
value = { value = {
name = machine; name = machine;
id = (lib.removeSuffix "\n" (builtins.readFile (syncthingPublicKeyPath machine))); id = (lib.removeSuffix "\n" (builtins.readFile (syncthingPublicKeyPath machine)));
addresses = addresses = [
[ "dynamic" ] "dynamic"
++ ( ]
if (lib.elem machine networkIpMachines) then ++ (
[ "tcp://[${(lib.removeSuffix "\n" (builtins.readFile (zerotierIpMachinePath machine)))}]:22000" ] if (lib.elem machine networkIpMachines) then
else [ "tcp://[${(lib.removeSuffix "\n" (builtins.readFile (zerotierIpMachinePath machine)))}]:22000" ]
[ ] else
); [ ]
);
}; };
}) syncthingPublicKeyMachines; }) syncthingPublicKeyMachines;
in in

View File

@@ -21,17 +21,16 @@ in
settings.certificateSearchDomains != [ ] settings.certificateSearchDomains != [ ]
) config.clan.core.vars.generators.openssh-cert.files."ssh.id_ed25519-cert.pub".path; ) config.clan.core.vars.generators.openssh-cert.files."ssh.id_ed25519-cert.pub".path;
hostKeys = hostKeys = [
[ {
{ path = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519".path;
path = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519".path; type = "ed25519";
type = "ed25519"; }
} ]
] ++ lib.optional settings.rsaHostKey.enable {
++ lib.optional settings.rsaHostKey.enable { path = config.clan.core.vars.generators.openssh-rsa.files."ssh.id_rsa".path;
path = config.clan.core.vars.generators.openssh-rsa.files."ssh.id_rsa".path; type = "rsa";
type = "rsa"; };
};
}; };
clan.core.vars.generators.openssh = { clan.core.vars.generators.openssh = {
@@ -51,7 +50,8 @@ in
hostNames = [ hostNames = [
"localhost" "localhost"
config.networking.hostName config.networking.hostName
] ++ (lib.optional (config.networking.domain != null) config.networking.fqdn); ]
++ (lib.optional (config.networking.domain != null) config.networking.fqdn);
publicKey = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519.pub".value; publicKey = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519.pub".value;
}; };

View File

@@ -184,24 +184,24 @@
settings.certificate.searchDomains != [ ] settings.certificate.searchDomains != [ ]
) config.clan.core.vars.generators.openssh-cert.files."ssh.id_ed25519-cert.pub".path; ) config.clan.core.vars.generators.openssh-cert.files."ssh.id_ed25519-cert.pub".path;
hostKeys = hostKeys = [
[ {
{ path = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519".path;
path = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519".path; type = "ed25519";
type = "ed25519"; }
} ]
] ++ lib.optional settings.hostKeys.rsa.enable {
++ lib.optional settings.hostKeys.rsa.enable { path = config.clan.core.vars.generators.openssh-rsa.files."ssh.id_rsa".path;
path = config.clan.core.vars.generators.openssh-rsa.files."ssh.id_rsa".path; type = "rsa";
type = "rsa"; };
};
}; };
programs.ssh.knownHosts.clan-sshd-self-ed25519 = { programs.ssh.knownHosts.clan-sshd-self-ed25519 = {
hostNames = [ hostNames = [
"localhost" "localhost"
config.networking.hostName config.networking.hostName
] ++ (lib.optional (config.networking.domain != null) config.networking.fqdn); ]
++ (lib.optional (config.networking.domain != null) config.networking.fqdn);
publicKey = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519.pub".value; publicKey = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519.pub".value;
}; };
}; };

View File

@@ -157,11 +157,11 @@
value = { value = {
name = machine; name = machine;
id = readMachineVar machine "syncthing/id/value" ""; id = readMachineVar machine "syncthing/id/value" "";
addresses = addresses = [
[ "dynamic"
"dynamic" ]
] ++
++ lib.optional (readMachineVar machine "zerotier/zerotier-ip/value" null != null) lib.optional (readMachineVar machine "zerotier/zerotier-ip/value" null != null)
"tcp://[${readMachineVar machine "zerotier/zerotier-ip/value" ""}]:22000"; "tcp://[${readMachineVar machine "zerotier/zerotier-ip/value" ""}]:22000";
}; };
}) })

View File

@@ -30,18 +30,17 @@ pkgs.stdenv.mkDerivation {
]; ];
}; };
nativeBuildInputs = nativeBuildInputs = [
[ pkgs.python3
pkgs.python3 uml-c4
uml-c4 ]
] ++ (with pkgs.python3Packages; [
++ (with pkgs.python3Packages; [ mkdocs
mkdocs mkdocs-material
mkdocs-material mkdocs-macros
mkdocs-macros mkdocs-redoc-tag
mkdocs-redoc-tag mkdocs-redirects
mkdocs-redirects ]);
]);
configurePhase = '' configurePhase = ''
pushd docs pushd docs

View File

@@ -156,7 +156,8 @@
type = types.submoduleWith { type = types.submoduleWith {
modules = [ modules = [
{ noInstanceOptions = true; } { noInstanceOptions = true; }
] ++ mapAttrsToList fakeInstanceOptions serviceModules; ]
++ mapAttrsToList fakeInstanceOptions serviceModules;
}; };
}; };
} }

View File

@@ -87,34 +87,35 @@
}; };
}; };
systems = import systems; systems = import systems;
imports = imports = [
[ flake-parts.flakeModules.modules ] flake-parts.flakeModules.modules
++ ]
# only importing existing paths allows to minimize the flake for test ++
# by removing files # only importing existing paths allows to minimize the flake for test
filter pathExists [ # by removing files
./checks/flake-module.nix filter pathExists [
./clanModules/flake-module.nix ./checks/flake-module.nix
./clanServices/flake-module.nix ./clanModules/flake-module.nix
./devShell.nix ./clanServices/flake-module.nix
./docs/nix/flake-module.nix ./devShell.nix
./flakeModules/flake-module.nix ./docs/nix/flake-module.nix
./flakeModules/demo_iso.nix ./flakeModules/flake-module.nix
./lib/filter-clan-core/flake-module.nix ./flakeModules/demo_iso.nix
./lib/flake-module.nix ./lib/filter-clan-core/flake-module.nix
./lib/flake-parts/clan-nixos-test.nix ./lib/flake-module.nix
./nixosModules/clanCore/vars/flake-module.nix ./lib/flake-parts/clan-nixos-test.nix
./nixosModules/flake-module.nix ./nixosModules/clanCore/vars/flake-module.nix
./pkgs/flake-module.nix ./nixosModules/flake-module.nix
./templates/flake-module.nix ./pkgs/flake-module.nix
] ./templates/flake-module.nix
++ [
(if pathExists ./flakeModules/clan.nix then import ./flakeModules/clan.nix inputs.self else { })
] ]
# Make treefmt-nix optional ++ [
# This only works if you set inputs.clan-core.inputs.treefmt-nix.follows (if pathExists ./flakeModules/clan.nix then import ./flakeModules/clan.nix inputs.self else { })
# to a non-empty input that doesn't export a flakeModule ]
++ optional (pathExists ./formatter.nix && inputs.treefmt-nix ? flakeModule) ./formatter.nix; # Make treefmt-nix optional
# This only works if you set inputs.clan-core.inputs.treefmt-nix.follows
# to a non-empty input that doesn't export a flakeModule
++ optional (pathExists ./formatter.nix && inputs.treefmt-nix ? flakeModule) ./formatter.nix;
} }
); );
} }

View File

@@ -95,39 +95,38 @@
"*/asciinema-player/*" "*/asciinema-player/*"
]; ];
}; };
treefmt.programs.mypy.directories = treefmt.programs.mypy.directories = {
{ "clan-cli" = {
"clan-cli" = { directory = "pkgs/clan-cli";
directory = "pkgs/clan-cli"; extraPythonPackages = (self'.packages.clan-cli.devshellPyDeps pkgs.python3Packages);
extraPythonPackages = (self'.packages.clan-cli.devshellPyDeps pkgs.python3Packages); };
}; "clan-app" = {
"clan-app" = { directory = "pkgs/ui/clan-app";
directory = "pkgs/ui/clan-app"; extraPythonPackages = (self'.packages.clan-app.devshellPyDeps pkgs.python3Packages);
extraPythonPackages = (self'.packages.clan-app.devshellPyDeps pkgs.python3Packages); extraPythonPaths = [ "../../clan-cli" ];
extraPythonPaths = [ "../../clan-cli" ]; };
}; "generate-test-vars" = {
"generate-test-vars" = { directory = "pkgs/generate-test-vars";
directory = "pkgs/generate-test-vars"; extraPythonPackages = [
extraPythonPackages = [ (pkgs.python3.withPackages (ps: self'.packages.clan-cli.devshellPyDeps ps))
(pkgs.python3.withPackages (ps: self'.packages.clan-cli.devshellPyDeps ps)) ];
]; extraPythonPaths = [ "../clan-cli" ];
extraPythonPaths = [ "../clan-cli" ]; };
}; }
} // (
// ( if pkgs.stdenv.isLinux then
if pkgs.stdenv.isLinux then {
{ "clan-vm-manager" = {
"clan-vm-manager" = { directory = "pkgs/clan-vm-manager";
directory = "pkgs/clan-vm-manager"; extraPythonPackages = self'.packages.clan-vm-manager.externalTestDeps ++ [
extraPythonPackages = self'.packages.clan-vm-manager.externalTestDeps ++ [ (pkgs.python3.withPackages (ps: self'.packages.clan-cli.devshellPyDeps ps))
(pkgs.python3.withPackages (ps: self'.packages.clan-cli.devshellPyDeps ps)) ];
]; extraPythonPaths = [ "../clan-cli" ];
extraPythonPaths = [ "../clan-cli" ]; };
}; }
} else
else { }
{ } );
);
treefmt.programs.ruff.check = true; treefmt.programs.ruff.check = true;
treefmt.programs.ruff.format = true; treefmt.programs.ruff.format = true;
}; };

View File

@@ -252,7 +252,8 @@ rec {
// { // {
oneOf = [ oneOf = [
{ type = "null"; } { type = "null"; }
] ++ (lib.optional (!isExcludedOption nestedOption) (parseOption nestedOption)); ]
++ (lib.optional (!isExcludedOption nestedOption) (parseOption nestedOption));
} }
# parse bool # parse bool
else if else if

View File

@@ -61,7 +61,8 @@ let
modules = [ (config.outputs.moduleForMachine.${name} or { }) ]; modules = [ (config.outputs.moduleForMachine.${name} or { }) ];
specialArgs = { specialArgs = {
inherit clan-core; inherit clan-core;
} // specialArgs; }
// specialArgs;
} }
) allMachines; ) allMachines;
@@ -193,7 +194,8 @@ in
# - nixosModules (_class = nixos) # - nixosModules (_class = nixos)
# - darwinModules (_class = darwin) # - darwinModules (_class = darwin)
(lib.optionalAttrs (clan-core ? "${_class}Modules") clan-core."${_class}Modules".clanCore) (lib.optionalAttrs (clan-core ? "${_class}Modules") clan-core."${_class}Modules".clanCore)
] ++ lib.optionals (_class == "nixos") (v.machineImports or [ ]); ]
++ lib.optionals (_class == "nixos") (v.machineImports or [ ]);
# default hostname # default hostname
networking.hostName = lib.mkDefault name; networking.hostName = lib.mkDefault name;

View File

@@ -73,7 +73,8 @@ in
}; };
}; };
} }
] ++ lib.mapAttrsToList (_: service: service.exports) config.mappedServices; ]
++ lib.mapAttrsToList (_: service: service.exports) config.mappedServices;
}; };
default = { }; default = { };
}; };

View File

@@ -15,16 +15,15 @@
lib.evalModules { lib.evalModules {
class = "clan.service"; class = "clan.service";
specialArgs._ctx = prefix; specialArgs._ctx = prefix;
modules = modules = [
[ # Base module
# Base module ./service-module.nix
./service-module.nix # Feature modules
# Feature modules (lib.modules.importApply ./api-feature.nix {
(lib.modules.importApply ./api-feature.nix { inherit clanLib prefix;
inherit clanLib prefix; })
}) ]
] ++
++
# Modules of caller # Modules of caller
modules; modules;
} }

View File

@@ -130,7 +130,8 @@ in
}; };
modules = [ modules = [
(import ./all-services-wrapper.nix { inherit directory; }) (import ./all-services-wrapper.nix { inherit directory; })
] ++ modules; ]
++ modules;
}; };
servicesEval = evalServices { servicesEval = evalServices {
@@ -139,15 +140,14 @@ in
{ {
inherit exportsModule; inherit exportsModule;
mappedServices = lib.mapAttrs (_module_ident: instances: { mappedServices = lib.mapAttrs (_module_ident: instances: {
imports = imports = [
[ # Import the resolved module.
# Import the resolved module. # i.e. clan.modules.admin
# i.e. clan.modules.admin (builtins.head instances).instance.resolvedModule
(builtins.head instances).instance.resolvedModule ] # Include all the instances that correlate to the resolved module
] # Include all the instances that correlate to the resolved module ++ (builtins.map (v: {
++ (builtins.map (v: { instances.${v.instanceName}.roles = v.instance.instanceRoles;
instances.${v.instanceName}.roles = v.instance.instanceRoles; }) instances);
}) instances);
}) grouped; }) grouped;
} }
]; ];

View File

@@ -756,14 +756,13 @@ in
instanceRes instanceRes
// { // {
nixosModule = { nixosModule = {
imports = imports = [
[ # Result of the applied 'perInstance = {...}: { nixosModule = { ... }; }'
# Result of the applied 'perInstance = {...}: { nixosModule = { ... }; }' instanceRes.nixosModule
instanceRes.nixosModule ]
] ++ (map (
++ (map ( s: if builtins.typeOf s == "string" then "${directory}/${s}" else s
s: if builtins.typeOf s == "string" then "${directory}/${s}" else s ) instanceCfg.roles.${roleName}.extraModules);
) instanceCfg.roles.${roleName}.extraModules);
}; };
} }
@@ -877,7 +876,8 @@ in
} }
) )
(lib.setDefaultModuleLocation "Via ${config.manifest.name}.perMachine - machine='${machineName}';" machineResult.nixosModule) (lib.setDefaultModuleLocation "Via ${config.manifest.name}.perMachine - machine='${machineName}';" machineResult.nixosModule)
] ++ instanceResults.nixosModules; ]
++ instanceResults.nixosModules;
}; };
} }
) config.result.allMachines; ) config.result.allMachines;

View File

@@ -35,7 +35,8 @@ let
clan.core.settings.directory = clan-core; clan.core.settings.directory = clan-core;
} }
clan-core.nixosModules.clanCore clan-core.nixosModules.clanCore
] ++ modules; ]
++ modules;
}; };
in in
# lib.warn '' # lib.warn ''

View File

@@ -45,7 +45,8 @@ lib.mkIf (config.clan.test.useContainers or true) {
{ {
nativeBuildInputs = [ nativeBuildInputs = [
hostPkgs.makeWrapper hostPkgs.makeWrapper
] ++ lib.optionals (!config.skipTypeCheck) [ hostPkgs.mypy ]; ]
++ lib.optionals (!config.skipTypeCheck) [ hostPkgs.mypy ];
buildInputs = [ testDriver ]; buildInputs = [ testDriver ];
testScript = config.testScriptString; testScript = config.testScriptString;
preferLocalBuild = true; preferLocalBuild = true;

View File

@@ -21,7 +21,8 @@ let
colorama colorama
junit-xml junit-xml
nix nix
] ++ extraPythonPackages python3Packages; ]
++ extraPythonPackages python3Packages;
nativeBuildInputs = [ setuptools ]; nativeBuildInputs = [ setuptools ];
format = "pyproject"; format = "pyproject";
src = ./.; src = ./.;

View File

@@ -1,28 +1,27 @@
{ _class, lib, ... }: { _class, lib, ... }:
{ {
imports = imports = [
[ ./backups.nix
./backups.nix ./defaults.nix
./defaults.nix ./facts
./facts ./inventory
./inventory ./meta/interface.nix
./meta/interface.nix ./metadata.nix
./metadata.nix ./networking.nix
./networking.nix ./nix-settings.nix
./nix-settings.nix ./options.nix
./options.nix ./outputs.nix
./outputs.nix ./sops.nix
./sops.nix ./vars
./vars ]
] ++ lib.optionals (_class == "nixos") [
++ lib.optionals (_class == "nixos") [ ./nixos-facter.nix
./nixos-facter.nix ./vm.nix
./vm.nix ./postgresql
./postgresql ./machine-id
./machine-id ./state-version
./state-version ./wayland-proxy-virtwl.nix
./wayland-proxy-virtwl.nix ./zerotier
./zerotier ./zfs.nix
./zfs.nix ];
];
} }

View File

@@ -90,35 +90,34 @@
default = { }; default = { };
type = lib.types.attrsOf ( type = lib.types.attrsOf (
lib.types.submodule (secret: { lib.types.submodule (secret: {
options = options = {
{ name = lib.mkOption {
name = lib.mkOption { type = lib.types.str;
type = lib.types.str; description = ''
description = '' name of the secret
name of the secret '';
''; default = secret.config._module.args.name;
default = secret.config._module.args.name; defaultText = "attribute name of the secret";
defaultText = "attribute name of the secret";
};
path = lib.mkOption {
type = lib.types.path;
description = ''
path to a secret which is generated by the generator
'';
default = config.clan.core.facts.secretPathFunction secret;
defaultText = lib.literalExpression "config.clan.core.facts.secretPathFunction secret";
};
}
// lib.optionalAttrs (config.clan.core.facts.secretStore == "sops") {
groups = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = config.clan.core.sops.defaultGroups;
defaultText = lib.literalExpression "config.clan.core.sops.defaultGroups";
description = ''
Groups to decrypt the secret for. By default we always use the user's key.
'';
};
}; };
path = lib.mkOption {
type = lib.types.path;
description = ''
path to a secret which is generated by the generator
'';
default = config.clan.core.facts.secretPathFunction secret;
defaultText = lib.literalExpression "config.clan.core.facts.secretPathFunction secret";
};
}
// lib.optionalAttrs (config.clan.core.facts.secretStore == "sops") {
groups = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = config.clan.core.sops.defaultGroups;
defaultText = lib.literalExpression "config.clan.core.sops.defaultGroups";
description = ''
Groups to decrypt the secret for. By default we always use the user's key.
'';
};
};
}) })
); );
description = '' description = ''

View File

@@ -175,35 +175,34 @@
default = { }; default = { };
type = lib.types.attrsOf ( type = lib.types.attrsOf (
lib.types.submodule (secret: { lib.types.submodule (secret: {
options = options = {
{ name = lib.mkOption {
name = lib.mkOption { type = lib.types.str;
type = lib.types.str; description = ''
description = '' name of the secret
name of the secret '';
''; default = secret.config._module.args.name;
default = secret.config._module.args.name; defaultText = "attribute name of the secret";
defaultText = "attribute name of the secret";
};
path = lib.mkOption {
type = lib.types.str;
description = ''
path to a secret which is generated by the generator
'';
default = config.clan.core.facts.secretPathFunction secret;
defaultText = lib.literalExpression "config.clan.core.facts.secretPathFunction secret";
};
}
// lib.optionalAttrs (config.clan.core.facts.secretModule == "clan_cli.facts.secret_modules.sops") {
groups = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = config.clan.core.sops.defaultGroups;
defaultText = lib.literalExpression "config.clan.core.sops.defaultGroups";
description = ''
Groups to decrypt the secret for. By default we always use the user's key.
'';
};
}; };
path = lib.mkOption {
type = lib.types.str;
description = ''
path to a secret which is generated by the generator
'';
default = config.clan.core.facts.secretPathFunction secret;
defaultText = lib.literalExpression "config.clan.core.facts.secretPathFunction secret";
};
}
// lib.optionalAttrs (config.clan.core.facts.secretModule == "clan_cli.facts.secret_modules.sops") {
groups = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = config.clan.core.sops.defaultGroups;
defaultText = lib.literalExpression "config.clan.core.sops.defaultGroups";
description = ''
Groups to decrypt the secret for. By default we always use the user's key.
'';
};
};
}) })
); );
}; };

View File

@@ -16,16 +16,15 @@ let
}; };
in in
{ {
imports = imports = [
[ ./public/in_repo.nix
./public/in_repo.nix ./secret/fs.nix
./secret/fs.nix ./secret/sops
./secret/sops ./secret/vm.nix
./secret/vm.nix ]
] ++ lib.optionals (_class == "nixos") [
++ lib.optionals (_class == "nixos") [ ./secret/password-store.nix
./secret/password-store.nix ];
];
options.clan.core.vars = lib.mkOption { options.clan.core.vars = lib.mkOption {
description = '' description = ''

View File

@@ -198,136 +198,135 @@ in
] ]
) )
]; ];
options = options = {
{ name = mkOption {
name = mkOption { type = str;
type = str; description = ''
description = '' name of the public fact
name of the public fact '';
''; readOnly = true;
readOnly = true; default = file.config._module.args.name;
default = file.config._module.args.name; defaultText = "Name of the file";
defaultText = "Name of the file"; };
}; generatorName = mkOption {
generatorName = mkOption { type = str;
type = str; description = ''
description = '' name of the generator
name of the generator '';
''; readOnly = true;
readOnly = true; default = generator.config._module.args.name;
default = generator.config._module.args.name; defaultText = "Name of the generator that generates this file";
defaultText = "Name of the generator that generates this file"; };
}; share = mkOption {
share = mkOption { type = bool;
type = bool; description = ''
description = '' Whether the generated vars should be shared between machines.
Whether the generated vars should be shared between machines. Shared vars are only generated once, when the first machine using it is deployed.
Shared vars are only generated once, when the first machine using it is deployed. Subsequent machines will re-use the already generated values.
Subsequent machines will re-use the already generated values. '';
''; readOnly = true;
readOnly = true; internal = true;
internal = true; default = generator.config.share;
default = generator.config.share; defaultText = "Mirror of the share flag of the generator";
defaultText = "Mirror of the share flag of the generator"; };
}; deploy = mkOption {
deploy = mkOption { description = ''
description = '' Whether the file should be deployed to the target machine.
Whether the file should be deployed to the target machine.
Disable this if the generated file is only used as an input to other generators. Disable this if the generated file is only used as an input to other generators.
''; '';
type = bool; type = bool;
default = true; default = true;
}; };
secret = mkOption { secret = mkOption {
description = '' description = ''
Whether the file should be treated as a secret. Whether the file should be treated as a secret.
''; '';
type = bool; type = bool;
default = true; default = true;
}; };
flakePath = mkOption { flakePath = mkOption {
description = '' description = ''
The path to the file containing the content of the generated value. The path to the file containing the content of the generated value.
This will be set automatically This will be set automatically
''; '';
type = nullOr path; type = nullOr path;
default = null; default = null;
}; };
path = mkOption { path = mkOption {
description = '' description = ''
The path to the file containing the content of the generated value. The path to the file containing the content of the generated value.
This will be set automatically This will be set automatically
''; '';
type = str; type = str;
defaultText = '' defaultText = ''
builtins.path { builtins.path {
name = "$${generator.config._module.args.name}_$${file.config._module.args.name}"; name = "$${generator.config._module.args.name}_$${file.config._module.args.name}";
path = file.config.flakePath;
}
'';
default = builtins.path {
name = "${generator.config._module.args.name}_${file.config._module.args.name}";
path = file.config.flakePath; path = file.config.flakePath;
};
};
neededFor = mkOption {
description = ''
This option determines when the secret will be decrypted and deployed to the target machine.
By setting this to `partitioning`, the secret will be deployed prior to running `disko` allowing
you to manage filesystem encryption keys. These will only be deployed when installing the system.
By setting this to `activation`, the secret will be deployed prior to running `nixos-rebuild` or `nixos-install`.
By setting this to `user`, the secret will be deployed prior to users and groups are created, allowing
users' passwords to be managed by vars. The secret will be stored in `/run/secrets-for-users` and `owner` and `group` must be `root`.
'';
type = enum [
"partitioning"
"activation"
"users"
"services"
];
default = "services";
};
owner = mkOption {
description = "The user name or id that will own the file.";
default = "root";
};
group = mkOption {
description = "The group name or id that will own the file.";
default = if _class == "darwin" then "wheel" else "root";
defaultText = lib.literalExpression ''if _class == "darwin" then "wheel" else "root"'';
};
mode = mkOption {
type = strMatching "^[0-7]{4}$";
description = "The unix file mode of the file. Must be a 4-digit octal number.";
default = "0400";
};
value =
mkOption {
description = ''
The content of the generated value.
Only available if the file is not secret.
'';
type = str;
defaultText = "Throws error because the value of a secret file is not accessible";
} }
// lib.optionalAttrs file.config.secret { '';
default = throw "Cannot access value of secret file"; default = builtins.path {
}; name = "${generator.config._module.args.name}_${file.config._module.args.name}";
} path = file.config.flakePath;
// (lib.optionalAttrs (_class == "nixos") {
restartUnits = mkOption {
description = ''
A list of systemd units that should be restarted after the file is deployed.
This is useful for services that need to reload their configuration after the file is updated.
WARNING: currently only sops-nix implements this option.
'';
type = listOf str;
default = [ ];
}; };
}); };
neededFor = mkOption {
description = ''
This option determines when the secret will be decrypted and deployed to the target machine.
By setting this to `partitioning`, the secret will be deployed prior to running `disko` allowing
you to manage filesystem encryption keys. These will only be deployed when installing the system.
By setting this to `activation`, the secret will be deployed prior to running `nixos-rebuild` or `nixos-install`.
By setting this to `user`, the secret will be deployed prior to users and groups are created, allowing
users' passwords to be managed by vars. The secret will be stored in `/run/secrets-for-users` and `owner` and `group` must be `root`.
'';
type = enum [
"partitioning"
"activation"
"users"
"services"
];
default = "services";
};
owner = mkOption {
description = "The user name or id that will own the file.";
default = "root";
};
group = mkOption {
description = "The group name or id that will own the file.";
default = if _class == "darwin" then "wheel" else "root";
defaultText = lib.literalExpression ''if _class == "darwin" then "wheel" else "root"'';
};
mode = mkOption {
type = strMatching "^[0-7]{4}$";
description = "The unix file mode of the file. Must be a 4-digit octal number.";
default = "0400";
};
value =
mkOption {
description = ''
The content of the generated value.
Only available if the file is not secret.
'';
type = str;
defaultText = "Throws error because the value of a secret file is not accessible";
}
// lib.optionalAttrs file.config.secret {
default = throw "Cannot access value of secret file";
};
}
// (lib.optionalAttrs (_class == "nixos") {
restartUnits = mkOption {
description = ''
A list of systemd units that should be restarted after the file is deployed.
This is useful for services that need to reload their configuration after the file is updated.
WARNING: currently only sops-nix implements this option.
'';
type = listOf str;
default = [ ];
};
});
}) })
); );
}; };

View File

@@ -43,23 +43,22 @@ in
secrets = lib.listToAttrs ( secrets = lib.listToAttrs (
map (secret: { map (secret: {
name = "vars/${secret.generator}/${secret.name}"; name = "vars/${secret.generator}/${secret.name}";
value = value = {
{ inherit (secret)
inherit (secret) owner
owner group
group mode
mode neededForUsers
neededForUsers ;
; sopsFile = builtins.path {
sopsFile = builtins.path { name = "${secret.generator}_${secret.name}";
name = "${secret.generator}_${secret.name}"; path = secretPath secret;
path = secretPath secret; };
}; format = "binary";
format = "binary"; }
} // (lib.optionalAttrs (_class == "nixos") {
// (lib.optionalAttrs (_class == "nixos") { inherit (secret) restartUnits;
inherit (secret) restartUnits; });
});
}) (builtins.filter (x: builtins.pathExists (secretPath x)) vars) }) (builtins.filter (x: builtins.pathExists (secretPath x)) vars)
); );

View File

@@ -8,16 +8,15 @@ let
... ...
}: }:
{ {
imports = imports = [
[ ./clanCore
./clanCore inputs.sops-nix."${_class}Modules".sops
inputs.sops-nix."${_class}Modules".sops ]
] ++ lib.optionals (_class == "nixos") [
++ lib.optionals (_class == "nixos") [ inputs.nixos-facter-modules.nixosModules.facter
inputs.nixos-facter-modules.nixosModules.facter inputs.disko.nixosModules.default
inputs.disko.nixosModules.default inputs.data-mesher.nixosModules.data-mesher
inputs.data-mesher.nixosModules.data-mesher ];
];
config = { config = {
clan.core.clanPkgs = lib.mkDefault self.packages.${pkgs.hostPlatform.system}; clan.core.clanPkgs = lib.mkDefault self.packages.${pkgs.hostPlatform.system};
}; };

View File

@@ -90,7 +90,8 @@ pythonRuntime.pkgs.buildPythonApplication {
# gtk4 deps # gtk4 deps
wrapGAppsHook4 wrapGAppsHook4
] ++ runtimeDependencies; ]
++ runtimeDependencies;
# The necessity of setting buildInputs and propagatedBuildInputs to the # The necessity of setting buildInputs and propagatedBuildInputs to the
# same values for your Python package within Nix largely stems from ensuring # same values for your Python package within Nix largely stems from ensuring
@@ -98,7 +99,8 @@ pythonRuntime.pkgs.buildPythonApplication {
# at build time and runtime, # at build time and runtime,
propagatedBuildInputs = [ propagatedBuildInputs = [
(pythonRuntime.withPackages (ps: clan-cli-module ++ (pyDeps ps))) (pythonRuntime.withPackages (ps: clan-cli-module ++ (pyDeps ps)))
] ++ runtimeDependencies; ]
++ runtimeDependencies;
# also re-expose dependencies so we test them in CI # also re-expose dependencies so we test them in CI
passthru = { passthru = {

View File

@@ -35,15 +35,14 @@ mkShell {
clan-app-ui clan-app-ui
]; ];
packages = packages = [
[ # required for reload-python-api.sh script
# required for reload-python-api.sh script json2ts
json2ts ]
] ++ (lib.optionals stdenv.hostPlatform.isLinux [
++ (lib.optionals stdenv.hostPlatform.isLinux [ # for viewing the storybook in a webkit-based browser to match webview
# for viewing the storybook in a webkit-based browser to match webview luakit
luakit ]);
]);
inherit (clan-app) propagatedBuildInputs; inherit (clan-app) propagatedBuildInputs;
@@ -63,64 +62,64 @@ mkShell {
++ (clan-app.devshellPyDeps ps) ++ (clan-app.devshellPyDeps ps)
)) ))
ruff ruff
] ++ clan-app.runtimeDeps; ]
++ clan-app.runtimeDeps;
shellHook = shellHook = ''
'' export CLAN_CORE_PATH=$(git rev-parse --show-toplevel)
export CLAN_CORE_PATH=$(git rev-parse --show-toplevel)
## Clan app ## Clan app
pushd "$CLAN_CORE_PATH/pkgs/clan-app" pushd "$CLAN_CORE_PATH/pkgs/clan-app"
# Add clan-app command to PATH # Add clan-app command to PATH
export PATH="$(pwd)/bin":"$PATH" export PATH="$(pwd)/bin":"$PATH"
# Add current package to PYTHONPATH # Add current package to PYTHONPATH
export PYTHONPATH="$(pwd)''${PYTHONPATH:+:$PYTHONPATH:}" export PYTHONPATH="$(pwd)''${PYTHONPATH:+:$PYTHONPATH:}"
popd popd
# Add clan-cli to the python path so that we can import it without building it in nix first # Add clan-cli to the python path so that we can import it without building it in nix first
export PYTHONPATH="$CLAN_CORE_PATH/pkgs/clan-cli":"$PYTHONPATH" export PYTHONPATH="$CLAN_CORE_PATH/pkgs/clan-cli":"$PYTHONPATH"
export XDG_DATA_DIRS=$GSETTINGS_SCHEMAS_PATH:$XDG_DATA_DIRS export XDG_DATA_DIRS=$GSETTINGS_SCHEMAS_PATH:$XDG_DATA_DIRS
export WEBVIEW_LIB_DIR=${webview-lib}/lib export WEBVIEW_LIB_DIR=${webview-lib}/lib
export OPENAPI_FILE="${clan-lib-openapi}" export OPENAPI_FILE="${clan-lib-openapi}"
export SWAGGER_UI_DIST="${swagger-ui-dist}/dist" export SWAGGER_UI_DIST="${swagger-ui-dist}/dist"
## Webview UI ## Webview UI
# Add clan-app-ui scripts to PATH # Add clan-app-ui scripts to PATH
pushd "$CLAN_CORE_PATH/pkgs/clan-app/ui" pushd "$CLAN_CORE_PATH/pkgs/clan-app/ui"
export NODE_PATH="$(pwd)/node_modules" export NODE_PATH="$(pwd)/node_modules"
export PATH="$NODE_PATH/.bin:$(pwd)/bin:$PATH" export PATH="$NODE_PATH/.bin:$(pwd)/bin:$PATH"
cp -r ${self'.packages.fonts} .fonts cp -r ${self'.packages.fonts} .fonts
chmod -R +w .fonts chmod -R +w .fonts
mkdir -p api mkdir -p api
cp -r ${clan-ts-api}/* api cp -r ${clan-ts-api}/* api
chmod -R +w api chmod -R +w api
popd popd
# configure process-compose # configure process-compose
if test -f "$CLAN_CORE_PATH/pkgs/clan-app/.local.env"; then if test -f "$CLAN_CORE_PATH/pkgs/clan-app/.local.env"; then
source "$CLAN_CORE_PATH/pkgs/clan-app/.local.env" source "$CLAN_CORE_PATH/pkgs/clan-app/.local.env"
fi fi
export PC_CONFIG_FILES="$CLAN_CORE_PATH/pkgs/clan-app/process-compose.yaml" export PC_CONFIG_FILES="$CLAN_CORE_PATH/pkgs/clan-app/process-compose.yaml"
echo -e "${GREEN}To launch a qemu VM for testing, run:\n start-vm <number of VMs>${NC}" echo -e "${GREEN}To launch a qemu VM for testing, run:\n start-vm <number of VMs>${NC}"
'' ''
+ +
# todo darwin support needs some work # todo darwin support needs some work
(lib.optionalString stdenv.hostPlatform.isLinux '' (lib.optionalString stdenv.hostPlatform.isLinux ''
# configure playwright for storybook snapshot testing # configure playwright for storybook snapshot testing
export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
export PLAYWRIGHT_BROWSERS_PATH=${ export PLAYWRIGHT_BROWSERS_PATH=${
playwright-driver.browsers.override { playwright-driver.browsers.override {
withFfmpeg = false; withFfmpeg = false;
withFirefox = false; withFirefox = false;
withChromium = false; withChromium = false;
withChromiumHeadlessShell = true; withChromiumHeadlessShell = true;
}
} }
export PLAYWRIGHT_HOST_PLATFORM_OVERRIDE="ubuntu-24.04" }
''); export PLAYWRIGHT_HOST_PLATFORM_OVERRIDE="ubuntu-24.04"
'');
} }

View File

@@ -156,93 +156,92 @@ pythonRuntime.pkgs.buildPythonApplication {
propagatedBuildInputs = [ pythonRuntimeWithDeps ] ++ bundledRuntimeDependencies; propagatedBuildInputs = [ pythonRuntimeWithDeps ] ++ bundledRuntimeDependencies;
passthru.tests = passthru.tests = {
{ clan-deps = pkgs.runCommand "clan-deps" { } ''
clan-deps = pkgs.runCommand "clan-deps" { } '' # ${builtins.toString (builtins.attrValues testRuntimeDependenciesMap)}
# ${builtins.toString (builtins.attrValues testRuntimeDependenciesMap)} touch $out
touch $out '';
''; clan-pytest-without-core =
clan-pytest-without-core = runCommand "clan-pytest-without-core"
runCommand "clan-pytest-without-core" {
{ nativeBuildInputs = testDependencies;
nativeBuildInputs = testDependencies; closureInfo = pkgs.closureInfo {
closureInfo = pkgs.closureInfo { rootPaths = [
rootPaths = [ templateDerivation
templateDerivation ];
]; };
}; }
} ''
'' set -euo pipefail
set -euo pipefail cp -r ${sourceWithTests} ./src
cp -r ${sourceWithTests} ./src chmod +w -R ./src
chmod +w -R ./src cd ./src
cd ./src
export NIX_STATE_DIR=$TMPDIR/nix IN_NIX_SANDBOX=1 PYTHONWARNINGS=error export NIX_STATE_DIR=$TMPDIR/nix IN_NIX_SANDBOX=1 PYTHONWARNINGS=error
# required to prevent concurrent 'nix flake lock' operations # required to prevent concurrent 'nix flake lock' operations
export CLAN_TEST_STORE=$TMPDIR/store export CLAN_TEST_STORE=$TMPDIR/store
export LOCK_NIX=$TMPDIR/nix_lock export LOCK_NIX=$TMPDIR/nix_lock
mkdir -p "$CLAN_TEST_STORE/nix/store" mkdir -p "$CLAN_TEST_STORE/nix/store"
# limit build cores to 16 # limit build cores to 16
jobs="$((NIX_BUILD_CORES>16 ? 16 : NIX_BUILD_CORES))" jobs="$((NIX_BUILD_CORES>16 ? 16 : NIX_BUILD_CORES))"
python -m pytest -m "not impure and not with_core" -n $jobs ./clan_cli ./clan_lib python -m pytest -m "not impure and not with_core" -n $jobs ./clan_cli ./clan_lib
touch $out touch $out
''; '';
} }
// lib.optionalAttrs (!stdenv.isDarwin) { // lib.optionalAttrs (!stdenv.isDarwin) {
# disabled on macOS until we fix all remaining issues # disabled on macOS until we fix all remaining issues
clan-pytest-with-core = clan-pytest-with-core =
runCommand "clan-pytest-with-core" runCommand "clan-pytest-with-core"
{ {
nativeBuildInputs = testDependencies; nativeBuildInputs = testDependencies;
buildInputs = [ buildInputs = [
pkgs.bash
pkgs.coreutils
pkgs.nix
];
closureInfo = pkgs.closureInfo {
rootPaths = [
templateDerivation
pkgs.bash pkgs.bash
pkgs.coreutils pkgs.coreutils
pkgs.nix pkgs.jq.dev
pkgs.stdenv
pkgs.stdenvNoCC
pkgs.openssh
pkgs.shellcheck-minimal
pkgs.mkpasswd
pkgs.xkcdpass
pkgs.pass
nix-select
]; ];
closureInfo = pkgs.closureInfo { };
rootPaths = [ }
templateDerivation ''
pkgs.bash set -euo pipefail
pkgs.coreutils cp -r ${sourceWithTests} ./src
pkgs.jq.dev chmod +w -R ./src
pkgs.stdenv cd ./src
pkgs.stdenvNoCC
pkgs.openssh
pkgs.shellcheck-minimal
pkgs.mkpasswd
pkgs.xkcdpass
pkgs.pass
nix-select
];
};
}
''
set -euo pipefail
cp -r ${sourceWithTests} ./src
chmod +w -R ./src
cd ./src
${setupNixInNix} ${setupNixInNix}
export CLAN_CORE_PATH=${clan-core-path} export CLAN_CORE_PATH=${clan-core-path}
export PYTHONWARNINGS=error export PYTHONWARNINGS=error
# used for tests without flakes # used for tests without flakes
export NIXPKGS=${nixpkgs} export NIXPKGS=${nixpkgs}
export NIX_SELECT=${nix-select} export NIX_SELECT=${nix-select}
# limit build cores to 16 # limit build cores to 16
jobs="$((NIX_BUILD_CORES>16 ? 16 : NIX_BUILD_CORES))" jobs="$((NIX_BUILD_CORES>16 ? 16 : NIX_BUILD_CORES))"
# Run all tests with core marker # Run all tests with core marker
python -m pytest -m "not impure and with_core" -n $jobs ./clan_cli ./clan_lib python -m pytest -m "not impure and with_core" -n $jobs ./clan_cli ./clan_lib
touch $out touch $out
''; '';
}; };
passthru.nixpkgs = nixpkgs'; passthru.nixpkgs = nixpkgs';
passthru.devshellPyDeps = ps: (pyTestDeps ps) ++ (pyDeps ps) ++ (devDeps ps); passthru.devshellPyDeps = ps: (pyTestDeps ps) ++ (pyDeps ps) ++ (devDeps ps);

View File

@@ -22,7 +22,8 @@ mkShell {
)) ))
ruff ruff
nix-unit nix-unit
] ++ clan-cli.runtimeDependencies; ]
++ clan-cli.runtimeDependencies;
inputsFrom = [ self'.devShells.default ]; inputsFrom = [ self'.devShells.default ];

View File

@@ -34,18 +34,17 @@ let
}; };
# Dependencies that are directly used in the project but nor from internal python packages # Dependencies that are directly used in the project but nor from internal python packages
externalPythonDeps = externalPythonDeps = [
[ pygobject3
pygobject3 pygobject-stubs
pygobject-stubs gtk4
gtk4 libadwaita
libadwaita adwaita-icon-theme
adwaita-icon-theme ]
] ++ clan-cli.propagatedBuildInputs
++ clan-cli.propagatedBuildInputs ++ lib.optionals (!stdenv.isDarwin) [
++ lib.optionals (!stdenv.isDarwin) [ webkitgtk_6_0
webkitgtk_6_0 ];
];
# Deps including python packages from the local project # Deps including python packages from the local project
allPythonDeps = [ (python.pkgs.toPythonModule clan-cli) ] ++ externalPythonDeps; allPythonDeps = [ (python.pkgs.toPythonModule clan-cli) ] ++ externalPythonDeps;

View File

@@ -27,19 +27,18 @@ in
mkShell { mkShell {
inherit (clan-vm-manager) nativeBuildInputs; inherit (clan-vm-manager) nativeBuildInputs;
name = "clan-vm-manager"; name = "clan-vm-manager";
buildInputs = buildInputs = [
[ ruff
ruff gtk4.dev # has the demo called 'gtk4-widget-factory'
gtk4.dev # has the demo called 'gtk4-widget-factory' libadwaita.devdoc # has the demo called 'adwaita-1-demo'
libadwaita.devdoc # has the demo called 'adwaita-1-demo' ]
] ++ devshellTestDeps
++ devshellTestDeps
# Dependencies for testing for linux hosts # Dependencies for testing for linux hosts
++ (lib.optionals stdenv.isLinux [ ++ (lib.optionals stdenv.isLinux [
xdg-utils # install desktop files xdg-utils # install desktop files
desktop-file-utils # verify desktop files desktop-file-utils # verify desktop files
]); ]);
shellHook = '' shellHook = ''
export GIT_ROOT=$(git rev-parse --show-toplevel) export GIT_ROOT=$(git rev-parse --show-toplevel)