Reformat with nixfmt

This commit is contained in:
r-vdp 2024-09-30 11:59:31 +02:00
parent 3aab6ab23a
commit 8441081b6b
No known key found for this signature in database
11 changed files with 837 additions and 674 deletions

View file

@ -1,6 +1,4 @@
(import (import (fetchTarball {
(fetchTarball { url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz";
url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz"; sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2";
sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2"; }) { src = ./.; }).defaultNix
})
{ src = ./.; }).defaultNix

View file

@ -1,4 +1,5 @@
{ lib, pkgs, ... }: { { lib, pkgs, ... }:
{
config = { config = {
nixpkgs.hostPlatform = "x86_64-linux"; nixpkgs.hostPlatform = "x86_64-linux";
@ -56,28 +57,28 @@
}; };
}; };
systemd.services = systemd.services = lib.listToAttrs (
lib.listToAttrs lib.flip lib.genList 10 (
(lib.flip lib.genList 10 (ix: ix:
lib.nameValuePair "service-${toString ix}" lib.nameValuePair "service-${toString ix}" {
{ enable = true;
enable = true; description = "service-${toString ix}";
description = "service-${toString ix}"; wants = [ "network-online.target" ];
wants = [ "network-online.target" ]; after = [
after = [ "network-online.target"
"network-online.target" ];
]; serviceConfig = {
serviceConfig = { Type = "oneshot";
Type = "oneshot"; RemainAfterExit = true;
RemainAfterExit = true; };
}; wantedBy = [ "system-manager.target" ];
wantedBy = [ "system-manager.target" ]; requiredBy = lib.mkIf (ix > 5) [ "service-0.service" ];
requiredBy = lib.mkIf (ix > 5) [ "service-0.service" ]; script = ''
script = '' sleep ${if ix > 5 then "2" else "1"}
sleep ${if ix > 5 then "2" else "1"} '';
''; }
}) )
); );
systemd.tmpfiles.rules = [ "D /var/tmp/system-manager 0755 root root -" ]; systemd.tmpfiles.rules = [ "D /var/tmp/system-manager 0755 root root -" ];
}; };
} }

406
flake.nix
View file

@ -39,15 +39,16 @@
}; };
outputs = outputs =
{ self {
, nixpkgs self,
, flake-utils nixpkgs,
, rust-overlay flake-utils,
, crane rust-overlay,
, devshell crane,
, treefmt-nix devshell,
, pre-commit-hooks treefmt-nix,
, ... pre-commit-hooks,
...
}@inputs: }@inputs:
{ {
lib = import ./nix/lib.nix { lib = import ./nix/lib.nix {
@ -60,201 +61,228 @@
modules = [ ./examples/example.nix ]; modules = [ ./examples/example.nix ];
}; };
} }
// // (flake-utils.lib.eachSystem
(flake-utils.lib.eachSystem
[ [
flake-utils.lib.system.x86_64-linux flake-utils.lib.system.x86_64-linux
flake-utils.lib.system.aarch64-linux flake-utils.lib.system.aarch64-linux
] ]
(system: (
let system:
pkgs = import nixpkgs { let
inherit system; pkgs = import nixpkgs {
overlays = [ (import rust-overlay) devshell.overlays.default ]; inherit system;
}; overlays = [
# TODO Pin the version for release (import rust-overlay)
rust = pkgs.rust-bin.stable.latest; devshell.overlays.default
];
};
# TODO Pin the version for release
rust = pkgs.rust-bin.stable.latest;
craneLib = (crane.mkLib pkgs).overrideToolchain rust.default; craneLib = (crane.mkLib pkgs).overrideToolchain rust.default;
# Common derivation arguments used for all builds # Common derivation arguments used for all builds
commonArgs = { dbus, pkg-config }: { commonArgs =
src = craneLib.cleanCargoSource ./.; { dbus, pkg-config }:
buildInputs = [
dbus
];
nativeBuildInputs = [
pkg-config
];
# https://github.com/ipetkov/crane/issues/385
doNotLinkInheritedArtifacts = true;
};
# Build only the cargo dependencies
cargoArtifacts = { dbus, pkg-config }:
craneLib.buildDepsOnly ((commonArgs { inherit dbus pkg-config; }) // {
pname = "system-manager";
});
system-manager-unwrapped =
{ dbus
, pkg-config
}:
craneLib.buildPackage ((commonArgs { inherit dbus pkg-config; }) // {
pname = "system-manager";
cargoArtifacts = cargoArtifacts { inherit dbus pkg-config; };
});
system-manager =
{ dbus
, makeBinaryWrapper
, nix
, pkg-config
, runCommand
}:
let
unwrapped = system-manager-unwrapped { inherit dbus pkg-config; };
in
runCommand "system-manager"
{ {
nativeBuildInputs = [ makeBinaryWrapper ]; src = craneLib.cleanCargoSource ./.;
} buildInputs = [
'' dbus
makeWrapper \ ];
${unwrapped}/bin/system-manager \ nativeBuildInputs = [
$out/bin/system-manager \ pkg-config
--prefix PATH : ${nixpkgs.lib.makeBinPath [ nix ]} ];
''; # https://github.com/ipetkov/crane/issues/385
doNotLinkInheritedArtifacts = true;
};
system-manager-clippy = # Build only the cargo dependencies
{ dbus cargoArtifacts =
, pkg-config { dbus, pkg-config }:
}: craneLib.buildDepsOnly (
craneLib.cargoClippy ((commonArgs { inherit dbus pkg-config; }) // { (commonArgs { inherit dbus pkg-config; })
cargoArtifacts = cargoArtifacts { inherit dbus pkg-config; }; // {
cargoClippyExtraArgs = "--all-targets -- --deny warnings"; pname = "system-manager";
}); }
);
system-manager-test = system-manager-unwrapped =
{ dbus {
, pkg-config dbus,
}: pkg-config,
craneLib.cargoTest ((commonArgs { inherit dbus pkg-config; }) // { }:
cargoArtifacts = cargoArtifacts { inherit dbus pkg-config; }; craneLib.buildPackage (
}); (commonArgs { inherit dbus pkg-config; })
// {
pname = "system-manager";
cargoArtifacts = cargoArtifacts { inherit dbus pkg-config; };
}
);
# treefmt-nix configuration system-manager =
treefmt.config = { {
projectRootFile = "flake.nix"; dbus,
programs = { makeBinaryWrapper,
nixfmt.enable = true; nix,
rustfmt = { pkg-config,
enable = true; runCommand,
package = rust.rustfmt; }:
let
unwrapped = system-manager-unwrapped { inherit dbus pkg-config; };
in
runCommand "system-manager"
{
nativeBuildInputs = [ makeBinaryWrapper ];
}
''
makeWrapper \
${unwrapped}/bin/system-manager \
$out/bin/system-manager \
--prefix PATH : ${nixpkgs.lib.makeBinPath [ nix ]}
'';
system-manager-clippy =
{
dbus,
pkg-config,
}:
craneLib.cargoClippy (
(commonArgs { inherit dbus pkg-config; })
// {
cargoArtifacts = cargoArtifacts { inherit dbus pkg-config; };
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
}
);
system-manager-test =
{
dbus,
pkg-config,
}:
craneLib.cargoTest (
(commonArgs { inherit dbus pkg-config; })
// {
cargoArtifacts = cargoArtifacts { inherit dbus pkg-config; };
}
);
# treefmt-nix configuration
treefmt.config = {
projectRootFile = "flake.nix";
programs = {
nixfmt.enable = true;
rustfmt = {
enable = true;
package = rust.rustfmt;
};
}; };
}; };
}; in
in {
{ packages = {
packages = { # The unwrapped version takes nix from the PATH, it will fail if nix
# The unwrapped version takes nix from the PATH, it will fail if nix # cannot be found.
# cannot be found. # The wrapped version has a reference to the nix store path, so nix is
# The wrapped version has a reference to the nix store path, so nix is # part of its runtime closure.
# part of its runtime closure. system-manager-unwrapped = pkgs.callPackage system-manager-unwrapped { };
system-manager-unwrapped = pkgs.callPackage system-manager-unwrapped { }; system-manager = pkgs.callPackage system-manager { };
system-manager = pkgs.callPackage system-manager { };
system-manager-clippy = pkgs.callPackage system-manager-clippy { }; system-manager-clippy = pkgs.callPackage system-manager-clippy { };
system-manager-test = pkgs.callPackage system-manager-test { }; system-manager-test = pkgs.callPackage system-manager-test { };
default = self.packages.${system}.system-manager; default = self.packages.${system}.system-manager;
};
devShells.default =
let
llvm = pkgs.llvmPackages_latest;
in
pkgs.devshell.mkShell {
packages = with pkgs; [
llvm.clang
pkg-config
(rust.default.override {
extensions = [ "rust-src" ];
})
(treefmt-nix.lib.mkWrapper pkgs treefmt.config)
];
env = [
{
name = "PKG_CONFIG_PATH";
value = pkgs.lib.makeSearchPath "lib/pkgconfig" [
pkgs.dbus.dev
pkgs.systemdMinimal.dev
];
}
{
name = "LIBCLANG_PATH";
value = "${llvm.libclang}/lib";
}
{
# for rust-analyzer
name = "RUST_SRC_PATH";
value = "${rust.rust-src}";
}
{
name = "RUST_BACKTRACE";
value = "1";
}
{
name = "RUSTFLAGS";
value =
let
getLib = pkg: "${pkgs.lib.getLib pkg}/lib";
in
pkgs.lib.concatStringsSep " " [
"-L${getLib pkgs.systemdMinimal} -lsystemd"
];
}
{
name = "DEVSHELL_NO_MOTD";
value = "1";
}
];
devshell.startup.pre-commit.text = (pre-commit-hooks.lib.${system}.run {
src = ./.;
hooks = {
check-format = {
enable = true;
entry = "treefmt --fail-on-change";
};
cargo-clippy = {
enable = true;
description = "Lint Rust code.";
entry = "cargo-clippy --workspace -- -D warnings";
files = "\\.rs$";
pass_filenames = false;
};
};
}).shellHook;
}; };
checks = devShells.default =
let let
# The Aarch64 VM tests seem to hang on garnix, we disable them for now llvm = pkgs.llvmPackages_latest;
enableVmTests = system != flake-utils.lib.system.aarch64-linux; in
in pkgs.devshell.mkShell {
{ packages = with pkgs; [
inherit (self.packages.${system}) llvm.clang
# Build the crate as part of `nix flake check` for convenience pkg-config
system-manager (rust.default.override {
system-manager-clippy extensions = [ "rust-src" ];
system-manager-test; })
} // (treefmt-nix.lib.mkWrapper pkgs treefmt.config)
pkgs.lib.optionalAttrs enableVmTests (import ./test/nix/modules { ];
inherit system; env = [
inherit (pkgs) lib; {
inherit (inputs) nix-vm-test; name = "PKG_CONFIG_PATH";
system-manager = self; value = pkgs.lib.makeSearchPath "lib/pkgconfig" [
}); pkgs.dbus.dev
}) pkgs.systemdMinimal.dev
];
}
{
name = "LIBCLANG_PATH";
value = "${llvm.libclang}/lib";
}
{
# for rust-analyzer
name = "RUST_SRC_PATH";
value = "${rust.rust-src}";
}
{
name = "RUST_BACKTRACE";
value = "1";
}
{
name = "RUSTFLAGS";
value =
let
getLib = pkg: "${pkgs.lib.getLib pkg}/lib";
in
pkgs.lib.concatStringsSep " " [
"-L${getLib pkgs.systemdMinimal} -lsystemd"
];
}
{
name = "DEVSHELL_NO_MOTD";
value = "1";
}
];
devshell.startup.pre-commit.text =
(pre-commit-hooks.lib.${system}.run {
src = ./.;
hooks = {
check-format = {
enable = true;
entry = "treefmt --fail-on-change";
};
cargo-clippy = {
enable = true;
description = "Lint Rust code.";
entry = "cargo-clippy --workspace -- -D warnings";
files = "\\.rs$";
pass_filenames = false;
};
};
}).shellHook;
};
checks =
let
# The Aarch64 VM tests seem to hang on garnix, we disable them for now
enableVmTests = system != flake-utils.lib.system.aarch64-linux;
in
{
inherit (self.packages.${system})
# Build the crate as part of `nix flake check` for convenience
system-manager
system-manager-clippy
system-manager-test
;
}
// pkgs.lib.optionalAttrs enableVmTests (
import ./test/nix/modules {
inherit system;
inherit (pkgs) lib;
inherit (inputs) nix-vm-test;
system-manager = self;
}
);
}
)
); );
} }

View file

@ -1,7 +1,7 @@
{ nixpkgs # The nixpkgs flake {
, self # The system-manager flake nixpkgs, # The nixpkgs flake
, nixos # The path to the nixos dir from nixpkgs self, # The system-manager flake
, nixos, # The path to the nixos dir from nixpkgs
}: }:
let let
inherit (nixpkgs) lib; inherit (nixpkgs) lib;
@ -11,45 +11,64 @@ in
# reporting in module-system errors. # reporting in module-system errors.
# Usage example: # Usage example:
# { _file = "${printAttrPos (builtins.unsafeGetAttrPos "a" { a = null; })}: inline module"; } # { _file = "${printAttrPos (builtins.unsafeGetAttrPos "a" { a = null; })}: inline module"; }
printAttrPos = { file, line, column }: "${file}:${toString line}:${toString column}"; printAttrPos =
{
file,
line,
column,
}:
"${file}:${toString line}:${toString column}";
makeSystemConfig = makeSystemConfig =
{ modules {
, extraSpecialArgs ? { } modules,
extraSpecialArgs ? { },
}: }:
let let
# Module that sets additional module arguments # Module that sets additional module arguments
extraArgsModule = { lib, config, pkgs, ... }: { extraArgsModule =
_file = "${self.lib.printAttrPos (builtins.unsafeGetAttrPos "a" { a = null; })}: inline module"; {
_module.args = { lib,
pkgs = nixpkgs.legacyPackages.${config.nixpkgs.hostPlatform}; config,
utils = import "${nixos}/lib/utils.nix" { pkgs,
inherit lib config pkgs; ...
}:
{
_file = "${self.lib.printAttrPos (builtins.unsafeGetAttrPos "a" { a = null; })}: inline module";
_module.args = {
pkgs = nixpkgs.legacyPackages.${config.nixpkgs.hostPlatform};
utils = import "${nixos}/lib/utils.nix" {
inherit lib config pkgs;
};
# Pass the wrapped system-manager binary down
inherit (self.packages.${config.nixpkgs.hostPlatform}) system-manager;
}; };
# Pass the wrapped system-manager binary down
inherit (self.packages.${config.nixpkgs.hostPlatform}) system-manager;
}; };
};
config = (lib.evalModules { config =
specialArgs = { nixosModulesPath = "${nixos}/modules"; } // extraSpecialArgs; (lib.evalModules {
modules = [ specialArgs = {
extraArgsModule nixosModulesPath = "${nixos}/modules";
./modules } // extraSpecialArgs;
] ++ modules; modules = [
}).config; extraArgsModule
./modules
] ++ modules;
}).config;
# Get the system as it was defined in the modules. # Get the system as it was defined in the modules.
system = config.nixpkgs.hostPlatform; system = config.nixpkgs.hostPlatform;
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
returnIfNoAssertions = drv: returnIfNoAssertions =
drv:
let let
failedAssertions = map (x: x.message) (lib.filter (x: !x.assertion) config.assertions); failedAssertions = map (x: x.message) (lib.filter (x: !x.assertion) config.assertions);
in in
if failedAssertions != [ ] if failedAssertions != [ ] then
then throw "\nFailed assertions:\n${lib.concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}" throw "\nFailed assertions:\n${lib.concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}"
else lib.showWarnings config.warnings drv; else
lib.showWarnings config.warnings drv;
servicesPath = pkgs.writeTextFile { servicesPath = pkgs.writeTextFile {
name = "services"; name = "services";
@ -72,45 +91,50 @@ in
toplevel = toplevel =
let let
scripts = lib.mapAttrsToList scripts = lib.mapAttrsToList (_: script: linkFarmBinEntryFromDrv script) config.build.scripts;
(_: script: linkFarmBinEntryFromDrv script)
config.build.scripts;
entries = [ entries = [
(linkFarmEntryFromDrv servicesPath) (linkFarmEntryFromDrv servicesPath)
(linkFarmEntryFromDrv etcPath) (linkFarmEntryFromDrv etcPath)
] ++ scripts; ] ++ scripts;
addPassthru = drv: drv.overrideAttrs (prevAttrs: { addPassthru =
passthru = (prevAttrs.passthru or { }) // { drv:
inherit config; drv.overrideAttrs (prevAttrs: {
}; passthru = (prevAttrs.passthru or { }) // {
}); inherit config;
};
});
in in
addPassthru (pkgs.linkFarm "system-manager" entries); addPassthru (pkgs.linkFarm "system-manager" entries);
in in
returnIfNoAssertions toplevel; returnIfNoAssertions toplevel;
mkTestPreamble = mkTestPreamble =
{ node {
, profile node,
, action profile,
}: '' action,
}:
''
${node}.succeed("${profile}/bin/${action} 2>&1 | tee /tmp/output.log") ${node}.succeed("${profile}/bin/${action} 2>&1 | tee /tmp/output.log")
${node}.succeed("! grep -F 'ERROR' /tmp/output.log") ${node}.succeed("! grep -F 'ERROR' /tmp/output.log")
''; '';
activateProfileSnippet = { node, profile }: activateProfileSnippet =
{ node, profile }:
self.lib.mkTestPreamble { self.lib.mkTestPreamble {
inherit node profile; inherit node profile;
action = "activate"; action = "activate";
}; };
deactivateProfileSnippet = { node, profile }: deactivateProfileSnippet =
{ node, profile }:
self.lib.mkTestPreamble { self.lib.mkTestPreamble {
inherit node profile; inherit node profile;
action = "deactivate"; action = "deactivate";
}; };
prepopulateProfileSnippet = { node, profile }: prepopulateProfileSnippet =
{ node, profile }:
self.lib.mkTestPreamble { self.lib.mkTestPreamble {
inherit node profile; inherit node profile;
action = "prepopulate"; action = "prepopulate";

View file

@ -1,8 +1,9 @@
{ lib {
, config lib,
, pkgs config,
, system-manager pkgs,
, ... system-manager,
...
}: }:
{ {
imports = [ imports = [
@ -31,7 +32,12 @@
type = types.listOf types.unspecified; type = types.listOf types.unspecified;
internal = true; internal = true;
default = [ ]; default = [ ];
example = [{ assertion = false; message = "you can't enable this for that reason"; }]; example = [
{
assertion = false;
message = "you can't enable this for that reason";
}
];
description = lib.mdDoc '' description = lib.mdDoc ''
This option allows modules to express conditions that must This option allows modules to express conditions that must
hold for the evaluation of the system configuration to hold for the evaluation of the system configuration to
@ -95,20 +101,27 @@
allowAnyDistro = lib.mkEnableOption "the usage of system-manager on untested distributions"; allowAnyDistro = lib.mkEnableOption "the usage of system-manager on untested distributions";
preActivationAssertions = lib.mkOption { preActivationAssertions = lib.mkOption {
type = with lib.types; attrsOf (submodule ({ name, ... }: { type =
options = { with lib.types;
enable = lib.mkEnableOption "the assertion"; attrsOf (
submodule (
{ name, ... }:
{
options = {
enable = lib.mkEnableOption "the assertion";
name = lib.mkOption { name = lib.mkOption {
type = types.str; type = types.str;
default = name; default = name;
}; };
script = lib.mkOption { script = lib.mkOption {
type = types.str; type = types.str;
}; };
}; };
})); }
)
);
default = { }; default = { };
}; };
}; };
@ -140,17 +153,22 @@
system-manager.preActivationAssertions = { system-manager.preActivationAssertions = {
osVersion = osVersion =
let let
supportedIds = [ "nixos" "ubuntu" ]; supportedIds = [
"nixos"
"ubuntu"
];
in in
{ {
enable = !config.system-manager.allowAnyDistro; enable = !config.system-manager.allowAnyDistro;
script = '' script = ''
source /etc/os-release source /etc/os-release
${lib.concatStringsSep "\n" (lib.flip map supportedIds (supportedId: '' ${lib.concatStringsSep "\n" (
if [ $ID = "${supportedId}" ]; then lib.flip map supportedIds (supportedId: ''
exit 0 if [ $ID = "${supportedId}" ]; then
fi exit 0
''))} fi
'')
)}
echo "This OS is not currently supported." echo "This OS is not currently supported."
echo "Supported OSs are: ${lib.concatStringsSep ", " supportedIds}" echo "Supported OSs are: ${lib.concatStringsSep ", " supportedIds}"
exit 1 exit 1
@ -184,27 +202,27 @@
preActivationAssertionScript = preActivationAssertionScript =
let let
mkAssertion = { name, script, ... }: '' mkAssertion =
# ${name} { name, script, ... }:
''
# ${name}
echo -e "Evaluating pre-activation assertion ${name}...\n" echo -e "Evaluating pre-activation assertion ${name}...\n"
( (
set +e set +e
${script} ${script}
)
assertion_result=$?
if [ $assertion_result -ne 0 ]; then
failed_assertions+=${name}
fi
'';
mkAssertions = assertions:
lib.concatStringsSep "\n" (
lib.mapAttrsToList (name: mkAssertion) (
lib.filterAttrs (name: cfg: cfg.enable)
assertions
) )
assertion_result=$?
if [ $assertion_result -ne 0 ]; then
failed_assertions+=${name}
fi
'';
mkAssertions =
assertions:
lib.concatStringsSep "\n" (
lib.mapAttrsToList (name: mkAssertion) (lib.filterAttrs (name: cfg: cfg.enable) assertions)
); );
in in
pkgs.writeShellScript "preActivationAssertions" '' pkgs.writeShellScript "preActivationAssertions" ''
@ -230,43 +248,40 @@
# TODO: handle globbing # TODO: handle globbing
etc = etc =
let let
addToStore = name: file: pkgs.runCommandLocal "${name}-etc-link" { } '' addToStore =
mkdir -p "$out/$(dirname "${file.target}")" name: file:
ln -s "${file.source}" "$out/${file.target}" pkgs.runCommandLocal "${name}-etc-link" { } ''
mkdir -p "$out/$(dirname "${file.target}")"
ln -s "${file.source}" "$out/${file.target}"
if [ "${file.mode}" != symlink ]; then if [ "${file.mode}" != symlink ]; then
echo "${file.mode}" > "$out/${file.target}.mode" echo "${file.mode}" > "$out/${file.target}.mode"
echo "${file.user}" > "$out/${file.target}.uid" echo "${file.user}" > "$out/${file.target}.uid"
echo "${file.group}" > "$out/${file.target}.gid" echo "${file.group}" > "$out/${file.target}.gid"
fi fi
''; '';
filteredEntries = lib.filterAttrs filteredEntries = lib.filterAttrs (_name: etcFile: etcFile.enable) config.environment.etc;
(_name: etcFile: etcFile.enable)
config.environment.etc;
srcDrvs = lib.mapAttrs addToStore filteredEntries; srcDrvs = lib.mapAttrs addToStore filteredEntries;
entries = lib.mapAttrs entries = lib.mapAttrs (name: file: file // { source = "${srcDrvs.${name}}"; }) filteredEntries;
(name: file: file // { source = "${srcDrvs.${name}}"; })
filteredEntries;
staticEnv = pkgs.buildEnv { staticEnv = pkgs.buildEnv {
name = "etc-static-env"; name = "etc-static-env";
paths = lib.attrValues srcDrvs; paths = lib.attrValues srcDrvs;
}; };
in in
{ inherit entries staticEnv; }; {
inherit entries staticEnv;
};
services = services = lib.mapAttrs' (
lib.mapAttrs' unitName: unit:
(unitName: unit: lib.nameValuePair unitName {
lib.nameValuePair unitName { storePath = ''${unit.unit}/${unitName}'';
storePath = }
''${unit.unit}/${unitName}''; ) (lib.filterAttrs (_: unit: unit.enable) config.systemd.units);
})
(lib.filterAttrs (_: unit: unit.enable)
config.systemd.units);
}; };
}; };
} }

View file

@ -1,4 +1,9 @@
{ lib, config, pkgs, ... }: {
lib,
config,
pkgs,
...
}:
{ {
options.environment = { options.environment = {
@ -24,10 +29,9 @@
]; ];
etc = { etc = {
"profile.d/system-manager-path.sh".source = "profile.d/system-manager-path.sh".source = pkgs.writeText "system-manager-path.sh" ''
pkgs.writeText "system-manager-path.sh" '' export PATH=${pathDir}/bin/:''${PATH}
export PATH=${pathDir}/bin/:''${PATH} '';
'';
# TODO: figure out how to properly add fish support. We could start by # TODO: figure out how to properly add fish support. We could start by
# looking at what NixOS and HM do to set up the fish env. # looking at what NixOS and HM do to set up the fish env.

View file

@ -1,6 +1,7 @@
{ lib {
, pkgs lib,
, ... pkgs,
...
}: }:
{ {
options = { options = {
@ -18,99 +19,108 @@
Set of files that have to be linked in {file}`/etc`. Set of files that have to be linked in {file}`/etc`.
''; '';
type = lib.types.attrsOf (lib.types.submodule ( type = lib.types.attrsOf (
{ name, config, options, ... }: lib.types.submodule (
{ {
options = { name,
config,
options,
...
}:
{
options = {
enable = lib.mkOption { enable = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = true; default = true;
description = lib.mdDoc '' description = lib.mdDoc ''
Whether this /etc file should be generated. This Whether this /etc file should be generated. This
option allows specific /etc files to be disabled. option allows specific /etc files to be disabled.
''; '';
};
target = lib.mkOption {
type = lib.types.str;
description = lib.mdDoc ''
Name of symlink (relative to
{file}`/etc`). Defaults to the attribute
name.
'';
};
text = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.lines;
description = lib.mdDoc "Text of the file.";
};
source = lib.mkOption {
type = lib.types.path;
description = lib.mdDoc "Path of the source file.";
};
mode = lib.mkOption {
type = lib.types.str;
default = "symlink";
example = "0600";
description = lib.mdDoc ''
If set to something else than `symlink`,
the file is copied instead of symlinked, with the given
file mode.
'';
};
uid = lib.mkOption {
default = 0;
type = lib.types.int;
description = lib.mdDoc ''
UID of created file. Only takes effect when the file is
copied (that is, the mode is not 'symlink').
'';
};
gid = lib.mkOption {
default = 0;
type = lib.types.int;
description = lib.mdDoc ''
GID of created file. Only takes effect when the file is
copied (that is, the mode is not 'symlink').
'';
};
user = lib.mkOption {
default = "+${toString config.uid}";
type = lib.types.str;
description = lib.mdDoc ''
User name of created file.
Only takes effect when the file is copied (that is, the mode is not 'symlink').
Changing this option takes precedence over `uid`.
'';
};
group = lib.mkOption {
default = "+${toString config.gid}";
type = lib.types.str;
description = lib.mdDoc ''
Group name of created file.
Only takes effect when the file is copied (that is, the mode is not 'symlink').
Changing this option takes precedence over `gid`.
'';
};
}; };
target = lib.mkOption { config = {
type = lib.types.str; target = lib.mkDefault name;
description = lib.mdDoc '' source = lib.mkIf (config.text != null) (
Name of symlink (relative to let
{file}`/etc`). Defaults to the attribute name' = "etc-" + baseNameOf name;
name. in
''; lib.mkDerivedConfig options.text (pkgs.writeText name')
);
}; };
}
text = lib.mkOption { )
default = null; );
type = lib.types.nullOr lib.types.lines;
description = lib.mdDoc "Text of the file.";
};
source = lib.mkOption {
type = lib.types.path;
description = lib.mdDoc "Path of the source file.";
};
mode = lib.mkOption {
type = lib.types.str;
default = "symlink";
example = "0600";
description = lib.mdDoc ''
If set to something else than `symlink`,
the file is copied instead of symlinked, with the given
file mode.
'';
};
uid = lib.mkOption {
default = 0;
type = lib.types.int;
description = lib.mdDoc ''
UID of created file. Only takes effect when the file is
copied (that is, the mode is not 'symlink').
'';
};
gid = lib.mkOption {
default = 0;
type = lib.types.int;
description = lib.mdDoc ''
GID of created file. Only takes effect when the file is
copied (that is, the mode is not 'symlink').
'';
};
user = lib.mkOption {
default = "+${toString config.uid}";
type = lib.types.str;
description = lib.mdDoc ''
User name of created file.
Only takes effect when the file is copied (that is, the mode is not 'symlink').
Changing this option takes precedence over `uid`.
'';
};
group = lib.mkOption {
default = "+${toString config.gid}";
type = lib.types.str;
description = lib.mdDoc ''
Group name of created file.
Only takes effect when the file is copied (that is, the mode is not 'symlink').
Changing this option takes precedence over `gid`.
'';
};
};
config = {
target = lib.mkDefault name;
source = lib.mkIf (config.text != null) (
let name' = "etc-" + baseNameOf name;
in lib.mkDerivedConfig options.text (pkgs.writeText name')
);
};
}
));
}; };
}; };
} }

View file

@ -1,8 +1,9 @@
{ lib {
, config lib,
, pkgs config,
, utils pkgs,
, ... utils,
...
}: }:
let let
@ -19,14 +20,28 @@ in
# We could consider copying the systemd lib from NixOS and removing the bits # We could consider copying the systemd lib from NixOS and removing the bits
# that are not relevant to us, like this option. # that are not relevant to us, like this option.
package = lib.mkOption { package = lib.mkOption {
type = lib.types.oneOf [ lib.types.str lib.types.path lib.types.package ]; type = lib.types.oneOf [
lib.types.str
lib.types.path
lib.types.package
];
default = pkgs.systemdMinimal; default = pkgs.systemdMinimal;
}; };
globalEnvironment = lib.mkOption { globalEnvironment = lib.mkOption {
type = with lib.types; attrsOf (nullOr (oneOf [ str path package ])); type =
with lib.types;
attrsOf (
nullOr (oneOf [
str
path
package
])
);
default = { }; default = { };
example = { TZ = "CET"; }; example = {
TZ = "CET";
};
description = lib.mdDoc '' description = lib.mdDoc ''
Environment variables passed to *all* systemd units. Environment variables passed to *all* systemd units.
''; '';
@ -104,7 +119,9 @@ in
generators = lib.mkOption { generators = lib.mkOption {
type = lib.types.attrsOf lib.types.path; type = lib.types.attrsOf lib.types.path;
default = { }; default = { };
example = { systemd-gpt-auto-generator = "/dev/null"; }; example = {
systemd-gpt-auto-generator = "/dev/null";
};
description = lib.mdDoc '' description = lib.mdDoc ''
Definition of systemd generators. Definition of systemd generators.
For each `NAME = VALUE` pair of the attrSet, a link is generated from For each `NAME = VALUE` pair of the attrSet, a link is generated from
@ -129,14 +146,10 @@ in
wantedBy = [ "default.target" ]; wantedBy = [ "default.target" ];
}; };
timers = timers = lib.mapAttrs (name: service: {
lib.mapAttrs wantedBy = [ "timers.target" ];
(name: service: timerConfig.OnCalendar = service.startAt;
{ }) (lib.filterAttrs (name: service: service.enable && service.startAt != [ ]) cfg.services);
wantedBy = [ "timers.target" ];
timerConfig.OnCalendar = service.startAt;
})
(lib.filterAttrs (name: service: service.enable && service.startAt != [ ]) cfg.services);
units = units =
lib.mapAttrs' (n: v: lib.nameValuePair "${n}.path" (systemd-lib.pathToUnit v)) cfg.paths lib.mapAttrs' (n: v: lib.nameValuePair "${n}.path" (systemd-lib.pathToUnit v)) cfg.paths
@ -145,16 +158,24 @@ in
// lib.mapAttrs' (n: v: lib.nameValuePair "${n}.socket" (systemd-lib.socketToUnit v)) cfg.sockets // lib.mapAttrs' (n: v: lib.nameValuePair "${n}.socket" (systemd-lib.socketToUnit v)) cfg.sockets
// lib.mapAttrs' (n: v: lib.nameValuePair "${n}.target" (systemd-lib.targetToUnit v)) cfg.targets // lib.mapAttrs' (n: v: lib.nameValuePair "${n}.target" (systemd-lib.targetToUnit v)) cfg.targets
// lib.mapAttrs' (n: v: lib.nameValuePair "${n}.timer" (systemd-lib.timerToUnit v)) cfg.timers // lib.mapAttrs' (n: v: lib.nameValuePair "${n}.timer" (systemd-lib.timerToUnit v)) cfg.timers
// lib.listToAttrs (map // lib.listToAttrs (
(v: map (
let n = utils.escapeSystemdPath v.where; v:
in lib.nameValuePair "${n}.mount" (systemd-lib.mountToUnit v)) let
cfg.mounts) n = utils.escapeSystemdPath v.where;
// lib.listToAttrs (map in
(v: lib.nameValuePair "${n}.mount" (systemd-lib.mountToUnit v)
let n = utils.escapeSystemdPath v.where; ) cfg.mounts
in lib.nameValuePair "${n}.automount" (systemd-lib.automountToUnit v)) )
cfg.automounts); // lib.listToAttrs (
map (
v:
let
n = utils.escapeSystemdPath v.where;
in
lib.nameValuePair "${n}.automount" (systemd-lib.automountToUnit v)
) cfg.automounts
);
}; };
environment.etc = environment.etc =
@ -164,49 +185,59 @@ in
enabledUnits = lib.filterAttrs (_: unit: unit.enable) cfg.units; enabledUnits = lib.filterAttrs (_: unit: unit.enable) cfg.units;
in in
{ {
"systemd/system".source = pkgs.runCommand "system-manager-units" "systemd/system".source =
{ pkgs.runCommand "system-manager-units"
preferLocalBuild = true; {
allowSubstitutes = false; preferLocalBuild = true;
} allowSubstitutes = false;
'' }
mkdir -p $out ''
mkdir -p $out
for i in ${toString (lib.mapAttrsToList (n: v: v.unit) enabledUnits)}; do for i in ${toString (lib.mapAttrsToList (n: v: v.unit) enabledUnits)}; do
fn=$(basename $i/*) fn=$(basename $i/*)
if [ -e $out/$fn ]; then if [ -e $out/$fn ]; then
if [ "$(readlink -f $i/$fn)" = /dev/null ]; then if [ "$(readlink -f $i/$fn)" = /dev/null ]; then
ln -sfn /dev/null $out/$fn ln -sfn /dev/null $out/$fn
else
${
if allowCollisions then
''
mkdir -p $out/$fn.d
ln -s $i/$fn $out/$fn.d/overrides.conf
''
else
''
echo "Found multiple derivations configuring $fn!"
exit 1
''
}
fi
else else
${if allowCollisions then '' ln -fs $i/$fn $out/
mkdir -p $out/$fn.d
ln -s $i/$fn $out/$fn.d/overrides.conf
'' else ''
echo "Found multiple derivations configuring $fn!"
exit 1
''}
fi fi
else done
ln -fs $i/$fn $out/
fi
done
${lib.concatStrings ( ${lib.concatStrings (
lib.mapAttrsToList (name: unit: lib.mapAttrsToList (
lib.concatMapStrings (name2: '' name: unit:
mkdir -p $out/'${name2}.wants' lib.concatMapStrings (name2: ''
ln -sfn '../${name}' $out/'${name2}.wants'/ mkdir -p $out/'${name2}.wants'
'') (unit.wantedBy or []) ln -sfn '../${name}' $out/'${name2}.wants'/
) enabledUnits)} '') (unit.wantedBy or [ ])
) enabledUnits
)}
${lib.concatStrings ( ${lib.concatStrings (
lib.mapAttrsToList (name: unit: lib.mapAttrsToList (
lib.concatMapStrings (name2: '' name: unit:
mkdir -p $out/'${name2}.requires' lib.concatMapStrings (name2: ''
ln -sfn '../${name}' $out/'${name2}.requires'/ mkdir -p $out/'${name2}.requires'
'') (unit.requiredBy or []) ln -sfn '../${name}' $out/'${name2}.requires'/
) enabledUnits)} '') (unit.requiredBy or [ ])
''; ) enabledUnits
)}
'';
}; };
}; };
} }

View file

@ -1,19 +1,21 @@
{ nixosModulesPath {
, lib nixosModulesPath,
, ... lib,
...
}: }:
{ {
imports = [ imports =
./nginx.nix [
] ++ ./nginx.nix
# List of imported NixOS modules ]
# TODO: how will we manage this in the long term? ++
map (path: nixosModulesPath + path) [ # List of imported NixOS modules
"/misc/meta.nix" # TODO: how will we manage this in the long term?
"/security/acme/" map (path: nixosModulesPath + path) [
"/services/web-servers/nginx/" "/misc/meta.nix"
]; "/security/acme/"
"/services/web-servers/nginx/"
];
options = options =
# We need to ignore a bunch of options that are used in NixOS modules but # We need to ignore a bunch of options that are used in NixOS modules but

View file

@ -5,4 +5,3 @@ let
}; };
in in
(import compat { src = ./.; }).shellNix.default (import compat { src = ./.; }).shellNix.default

View file

@ -1,42 +1,55 @@
{ lib {
, system-manager lib,
, system system-manager,
, nix-vm-test system,
nix-vm-test,
}: }:
let let
forEachUbuntuImage = forEachUbuntuImage =
name: name:
{ modules {
, testScriptFunction modules,
, extraPathsToRegister ? [ ] testScriptFunction,
, projectTest ? test: test.sandboxed extraPathsToRegister ? [ ],
projectTest ? test: test.sandboxed,
}: }:
let let
ubuntu = nix-vm-test.lib.${system}.ubuntu; ubuntu = nix-vm-test.lib.${system}.ubuntu;
in in
lib.listToAttrs (lib.flip map (lib.attrNames ubuntu.images) lib.listToAttrs (
(imageVersion: lib.flip map (lib.attrNames ubuntu.images) (
let imageVersion:
toplevel = (system-manager.lib.makeSystemConfig { let
modules = modules ++ [ toplevel = (
({ lib, pkgs, ... }: { system-manager.lib.makeSystemConfig {
options.hostPkgs = lib.mkOption { type = lib.types.raw; readOnly = true; }; modules = modules ++ [
config.hostPkgs = pkgs; (
}) { lib, pkgs, ... }:
]; {
}); options.hostPkgs = lib.mkOption {
inherit (toplevel.config) hostPkgs; type = lib.types.raw;
in readOnly = true;
lib.nameValuePair "ubuntu-${imageVersion}-${name}" };
(projectTest config.hostPkgs = pkgs;
(ubuntu.${imageVersion} { }
testScript = testScriptFunction { inherit toplevel hostPkgs; }; )
extraPathsToRegister = extraPathsToRegister ++ [ ];
toplevel }
]; );
sharedDirs = { }; inherit (toplevel.config) hostPkgs;
})) in
lib.nameValuePair "ubuntu-${imageVersion}-${name}" (
projectTest (
ubuntu.${imageVersion} {
testScript = testScriptFunction { inherit toplevel hostPkgs; };
extraPathsToRegister = extraPathsToRegister ++ [
toplevel
];
sharedDirs = { };
}
)
)
) )
); );
@ -44,90 +57,100 @@ let
# and one that cannot. # and one that cannot.
# The id parameter is a string that can be used to force reloading the services # The id parameter is a string that can be used to force reloading the services
# between two configs by changing their contents. # between two configs by changing their contents.
testModule = id: { lib, pkgs, ... }: { testModule =
systemd.services = { id:
has-reload = { { lib, pkgs, ... }:
enable = true; {
description = "service-reload"; systemd.services = {
serviceConfig = { has-reload = {
Type = "oneshot"; enable = true;
RemainAfterExit = true; description = "service-reload";
ExecReload = '' serviceConfig = {
${lib.getBin pkgs.coreutils}/bin/true Type = "oneshot";
RemainAfterExit = true;
ExecReload = ''
${lib.getBin pkgs.coreutils}/bin/true
'';
};
wantedBy = [ "system-manager.target" ];
script = ''
echo "I can be reloaded (id: ${id})"
'';
};
has-no-reload = {
enable = true;
description = "service-no-reload";
serviceConfig.Type = "simple";
wantedBy = [ "system-manager.target" ];
script = ''
while true; do
echo "I cannot be reloaded (id: ${id})"
done
''; '';
}; };
wantedBy = [ "system-manager.target" ];
script = ''
echo "I can be reloaded (id: ${id})"
'';
};
has-no-reload = {
enable = true;
description = "service-no-reload";
serviceConfig.Type = "simple";
wantedBy = [ "system-manager.target" ];
script = ''
while true; do
echo "I cannot be reloaded (id: ${id})"
done
'';
}; };
}; };
};
newConfig = system-manager.lib.makeSystemConfig { newConfig = system-manager.lib.makeSystemConfig {
modules = [ modules = [
(testModule "new") (testModule "new")
({ lib, pkgs, ... }: { (
config = { { lib, pkgs, ... }:
nixpkgs.hostPlatform = system; {
config = {
nixpkgs.hostPlatform = system;
services.nginx.enable = false; services.nginx.enable = false;
environment = { environment = {
etc = { etc = {
foo_new = { foo_new = {
text = '' text = ''
This is just a test! This is just a test!
'';
};
};
systemPackages = [
pkgs.fish
];
};
systemd.services = {
new-service = {
enable = true;
description = "new-service";
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecReload = "${lib.getBin pkgs.coreutils}/bin/true";
};
wantedBy = [
"system-manager.target"
"default.target"
];
script = ''
sleep 2
''; '';
}; };
}; };
systemPackages = [
pkgs.fish
];
}; };
}
systemd.services = { )
new-service = {
enable = true;
description = "new-service";
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecReload = "${lib.getBin pkgs.coreutils}/bin/true";
};
wantedBy = [ "system-manager.target" "default.target" ];
script = ''
sleep 2
'';
};
};
};
})
]; ];
}; };
in in
forEachUbuntuImage "example" forEachUbuntuImage "example" {
{ modules = [
modules = [ (testModule "old")
(testModule "old") ../../../examples/example.nix
../../../examples/example.nix ];
]; extraPathsToRegister = [ newConfig ];
extraPathsToRegister = [ newConfig ]; testScriptFunction =
testScriptFunction = { toplevel, ... }: '' { toplevel, ... }:
''
# Start all machines in parallel # Start all machines in parallel
start_all() start_all()
@ -138,7 +161,10 @@ forEachUbuntuImage "example"
vm.succeed("grep -F 'Error while creating file in /etc: Unmanaged path already exists in filesystem, please remove it and run system-manager again: /etc/foo_test' /tmp/output.log") vm.succeed("grep -F 'Error while creating file in /etc: Unmanaged path already exists in filesystem, please remove it and run system-manager again: /etc/foo_test' /tmp/output.log")
vm.succeed("rm /etc/foo_test") vm.succeed("rm /etc/foo_test")
${system-manager.lib.activateProfileSnippet { node = "vm"; profile = toplevel; }} ${system-manager.lib.activateProfileSnippet {
node = "vm";
profile = toplevel;
}}
vm.wait_for_unit("system-manager.target") vm.wait_for_unit("system-manager.target")
vm.succeed("systemctl status service-9.service") vm.succeed("systemctl status service-9.service")
@ -150,7 +176,10 @@ forEachUbuntuImage "example"
vm.succeed("test -d /var/tmp/system-manager") vm.succeed("test -d /var/tmp/system-manager")
${system-manager.lib.activateProfileSnippet { node = "vm"; profile = newConfig; }} ${system-manager.lib.activateProfileSnippet {
node = "vm";
profile = newConfig;
}}
vm.succeed("systemctl status new-service.service") vm.succeed("systemctl status new-service.service")
vm.fail("systemctl status service-9.service") vm.fail("systemctl status service-9.service")
vm.fail("test -f /etc/a/nested/example/foo3") vm.fail("test -f /etc/a/nested/example/foo3")
@ -177,89 +206,111 @@ forEachUbuntuImage "example"
vm.fail("test -f /etc/baz/bar/foo2") vm.fail("test -f /etc/baz/bar/foo2")
vm.succeed("test -f /etc/foo_new") vm.succeed("test -f /etc/foo_new")
${system-manager.lib.deactivateProfileSnippet { node = "vm"; profile = newConfig; }} ${system-manager.lib.deactivateProfileSnippet {
node = "vm";
profile = newConfig;
}}
vm.fail("systemctl status new-service.service") vm.fail("systemctl status new-service.service")
vm.fail("test -f /etc/foo_new") vm.fail("test -f /etc/foo_new")
#vm.fail("test -f /var/tmp/system-manager/foo1") #vm.fail("test -f /var/tmp/system-manager/foo1")
''; '';
}
//
forEachUbuntuImage "prepopulate" {
modules = [
(testModule "old")
../../../examples/example.nix
];
extraPathsToRegister = [ newConfig ];
testScriptFunction =
{ toplevel, ... }:
''
# Start all machines in parallel
start_all()
vm.wait_for_unit("default.target")
${system-manager.lib.prepopulateProfileSnippet {
node = "vm";
profile = toplevel;
}}
vm.systemctl("daemon-reload")
# Simulate a reboot, to check that the services defined with
# system-manager start correctly after a reboot.
# TODO: can we find an easy way to really reboot the VM and not
# loose the root FS state?
vm.systemctl("isolate rescue.target")
# We need to send a return character to dismiss the rescue-mode prompt
vm.send_key("ret")
vm.systemctl("isolate default.target")
vm.wait_for_unit("system-manager.target")
vm.succeed("systemctl status service-9.service")
vm.succeed("test -f /etc/baz/bar/foo2")
vm.succeed("test -f /etc/a/nested/example/foo3")
vm.succeed("test -f /etc/foo.conf")
vm.succeed("grep -F 'launch_the_rockets = true' /etc/foo.conf")
vm.fail("grep -F 'launch_the_rockets = false' /etc/foo.conf")
${system-manager.lib.activateProfileSnippet {
node = "vm";
profile = newConfig;
}}
vm.succeed("systemctl status new-service.service")
vm.fail("systemctl status service-9.service")
vm.fail("test -f /etc/a/nested/example/foo3")
vm.fail("test -f /etc/baz/bar/foo2")
vm.succeed("test -f /etc/foo_new")
${system-manager.lib.deactivateProfileSnippet {
node = "vm";
profile = newConfig;
}}
vm.fail("systemctl status new-service.service")
vm.fail("test -f /etc/foo_new")
'';
} }
// //
forEachUbuntuImage "prepopulate" { forEachUbuntuImage "system-path" {
modules = [ modules = [
(testModule "old") (testModule "old")
../../../examples/example.nix ../../../examples/example.nix
]; ];
extraPathsToRegister = [ newConfig ]; extraPathsToRegister = [ newConfig ];
testScriptFunction = { toplevel, ... }: '' testScriptFunction =
# Start all machines in parallel { toplevel, hostPkgs, ... }:
start_all() ''
# Start all machines in parallel
start_all()
vm.wait_for_unit("default.target")
vm.wait_for_unit("default.target") vm.fail("bash --login -c '$(which rg)'")
vm.fail("bash --login -c '$(which fd)'")
${system-manager.lib.prepopulateProfileSnippet { node = "vm"; profile = toplevel; }} ${system-manager.lib.activateProfileSnippet {
vm.systemctl("daemon-reload") node = "vm";
profile = toplevel;
}}
# Simulate a reboot, to check that the services defined with vm.wait_for_unit("system-manager.target")
# system-manager start correctly after a reboot. vm.wait_for_unit("system-manager-path.service")
# TODO: can we find an easy way to really reboot the VM and not
# loose the root FS state?
vm.systemctl("isolate rescue.target")
# We need to send a return character to dismiss the rescue-mode prompt
vm.send_key("ret")
vm.systemctl("isolate default.target")
vm.wait_for_unit("system-manager.target")
vm.succeed("systemctl status service-9.service") #vm.fail("bash --login -c '$(which fish)'")
vm.succeed("test -f /etc/baz/bar/foo2") vm.succeed("bash --login -c 'realpath $(which rg) | grep -F ${hostPkgs.ripgrep}/bin/rg'")
vm.succeed("test -f /etc/a/nested/example/foo3") vm.succeed("bash --login -c 'realpath $(which fd) | grep -F ${hostPkgs.fd}/bin/fd'")
vm.succeed("test -f /etc/foo.conf")
vm.succeed("grep -F 'launch_the_rockets = true' /etc/foo.conf")
vm.fail("grep -F 'launch_the_rockets = false' /etc/foo.conf")
${system-manager.lib.activateProfileSnippet { node = "vm"; profile = newConfig; }} ${system-manager.lib.activateProfileSnippet {
vm.succeed("systemctl status new-service.service") node = "vm";
vm.fail("systemctl status service-9.service") profile = newConfig;
vm.fail("test -f /etc/a/nested/example/foo3") }}
vm.fail("test -f /etc/baz/bar/foo2")
vm.succeed("test -f /etc/foo_new")
${system-manager.lib.deactivateProfileSnippet { node = "vm"; profile = newConfig; }} vm.fail("bash --login -c '$(which rg)'")
vm.fail("systemctl status new-service.service") vm.fail("bash --login -c '$(which fd)'")
vm.fail("test -f /etc/foo_new") vm.succeed("bash --login -c 'realpath $(which fish) | grep -F ${hostPkgs.fish}/bin/fish'")
''; '';
} }
//
forEachUbuntuImage "system-path" {
modules = [
(testModule "old")
../../../examples/example.nix
];
extraPathsToRegister = [ newConfig ];
testScriptFunction = { toplevel, hostPkgs, ... }: ''
# Start all machines in parallel
start_all()
vm.wait_for_unit("default.target")
vm.fail("bash --login -c '$(which rg)'")
vm.fail("bash --login -c '$(which fd)'")
${system-manager.lib.activateProfileSnippet { node = "vm"; profile = toplevel; }}
vm.wait_for_unit("system-manager.target")
vm.wait_for_unit("system-manager-path.service")
#vm.fail("bash --login -c '$(which fish)'")
vm.succeed("bash --login -c 'realpath $(which rg) | grep -F ${hostPkgs.ripgrep}/bin/rg'")
vm.succeed("bash --login -c 'realpath $(which fd) | grep -F ${hostPkgs.fd}/bin/fd'")
${system-manager.lib.activateProfileSnippet { node = "vm"; profile = newConfig; }}
vm.fail("bash --login -c '$(which rg)'")
vm.fail("bash --login -c '$(which fd)'")
vm.succeed("bash --login -c 'realpath $(which fish) | grep -F ${hostPkgs.fish}/bin/fish'")
'';
}