From a91c419858ff377e5b1911b68cdd9afd2179d8ef Mon Sep 17 00:00:00 2001 From: Qyriad Date: Tue, 17 Feb 2026 20:17:39 +0100 Subject: [PATCH 1/4] normalize whitespace --- .editorconfig | 2 - default.nix | 28 +++--- flake.nix | 94 ++++++++++--------- package.nix | 252 +++++++++++++++++++++++++------------------------- shell.nix | 38 ++++---- 5 files changed, 208 insertions(+), 206 deletions(-) diff --git a/.editorconfig b/.editorconfig index 061699c..4753adb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,3 @@ -root = true - [*] end_of_line = lf insert_final_newline = true diff --git a/default.nix b/default.nix index 4372c35..2375445 100644 --- a/default.nix +++ b/default.nix @@ -1,18 +1,18 @@ { - pkgs ? import { }, - qpkgs ? let - src = fetchTree (builtins.parseFlakeRef "github:Qyriad/nur-packages"); - in import src { inherit pkgs; }, + pkgs ? import { }, + qpkgs ? let + src = fetchTree (builtins.parseFlakeRef "github:Qyriad/nur-packages"); + in import src { inherit pkgs; }, }: let - inherit (qpkgs) lib; - dynix = qpkgs.callPackage ./package.nix { } - |> qpkgs.stdlib.mkStdenvPretty; - byStdenv = lib.mapAttrs (stdenvName: stdenv: let - withStdenv = dynix.override { inherit stdenv; }; - dynix' = withStdenv.overrideAttrs (prev: { - pname = "${prev.pname}-${stdenvName}"; - }); - in dynix') qpkgs.validStdenvs; + inherit (qpkgs) lib; + dynix = qpkgs.callPackage ./package.nix { } + |> qpkgs.stdlib.mkStdenvPretty; + byStdenv = lib.mapAttrs (stdenvName: stdenv: let + withStdenv = dynix.override { clangStdenv = stdenv; }; + dynix' = withStdenv.overrideAttrs (prev: { + pname = "${prev.pname}-${stdenvName}"; + }); + in dynix') qpkgs.validStdenvs; in dynix.overrideAttrs (prev: lib.recursiveUpdate prev { - passthru = { inherit byStdenv; }; + passthru = { inherit byStdenv; }; }) diff --git a/flake.nix b/flake.nix index f4270f0..276cfb0 100644 --- a/flake.nix +++ b/flake.nix @@ -1,51 +1,55 @@ { - inputs = { - nixpkgs = { - url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - flake = false; - }; - flake-utils.url = "github:numtide/flake-utils"; - fenix = { - url = "github:nix-community/fenix"; - flake = false; - }; - qyriad-nur = { - url = "github:Qyriad/nur-packages"; - flake = false; - }; - }; + inputs = { + nixpkgs = { + url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + flake = false; + }; + flake-utils.url = "github:numtide/flake-utils"; + fenix = { + url = "github:nix-community/fenix"; + flake = false; + }; + qyriad-nur = { + url = "github:Qyriad/nur-packages"; + flake = false; + }; + }; - outputs = { - self, - nixpkgs, - flake-utils, - fenix, - qyriad-nur, - }: flake-utils.lib.eachDefaultSystem (system: let - pkgs = import nixpkgs { inherit system; }; - qpkgs = import qyriad-nur { inherit pkgs; }; - inherit (qpkgs) lib; - fenixLib = import fenix { inherit pkgs; }; + outputs = { + self, + nixpkgs, + flake-utils, + fenix, + qyriad-nur, + }: flake-utils.lib.eachDefaultSystem (system: let + pkgs = import nixpkgs { inherit system; }; + qpkgs = import qyriad-nur { inherit pkgs; }; + inherit (qpkgs) lib; + fenixLib = import fenix { inherit pkgs; }; - dynix = import ./default.nix { inherit pkgs qpkgs; }; - extraVersions = lib.mapAttrs' (stdenvName: value: { - name = "${stdenvName}-dynix"; - inherit value; - }) dynix.byStdenv; + dynix = import ./default.nix { inherit pkgs qpkgs; }; + extraVersions = lib.mapAttrs' (stdenvName: value: { + name = "${stdenvName}-dynix"; + inherit value; + }) dynix.byStdenv; - devShell = import ./shell.nix { inherit pkgs qpkgs dynix fenixLib; }; - extraDevShells = lib.mapAttrs' (stdenvName: value: { - name = "${stdenvName}-dynix"; - inherit value; - }) dynix.byStdenv; - in { - packages = extraVersions // { - default = dynix; - inherit dynix; - }; + devShell = import ./shell.nix { inherit pkgs qpkgs dynix fenixLib; }; + extraDevShells = lib.mapAttrs' (stdenvName: value: { + name = "${stdenvName}-dynix"; + inherit value; + }) dynix.byStdenv; + in { + packages = extraVersions // { + default = dynix; + inherit dynix; + }; - devShells = extraDevShells // { - default = devShell; - }; - }); + devShells = extraDevShells // { + default = devShell; + }; + + checks = self.packages.${system}.default.tests // { + default = self.packages.${system}.default.allTests; + }; + }); } diff --git a/package.nix b/package.nix index 448ab31..309f2d4 100644 --- a/package.nix +++ b/package.nix @@ -1,160 +1,160 @@ { - lib, - clangStdenv, - callPackage, - linkFarm, - rustHooks, - rustPackages, - versionCheckHook, + lib, + clangStdenv, + callPackage, + linkFarm, + rustHooks, + rustPackages, + versionCheckHook, }: lib.callWith' rustPackages ({ - rustPlatform, - cargo, + rustPlatform, + cargo, }: let - stdenv = clangStdenv; - cargoToml = lib.importTOML ./Cargo.toml; - cargoPackage = cargoToml.package; + stdenv = clangStdenv; + cargoToml = lib.importTOML ./Cargo.toml; + cargoPackage = cargoToml.package; in stdenv.mkDerivation (finalAttrs: let - self = finalAttrs.finalPackage; + self = finalAttrs.finalPackage; in { - pname = cargoPackage.name; - version = cargoPackage.version; + pname = cargoPackage.name; + version = cargoPackage.version; - strictDeps = true; - __structuredAttrs = true; + strictDeps = true; + __structuredAttrs = true; - outputs = [ "out" "modules" ]; + outputs = [ "out" "modules" ]; - doCheck = true; - doInstallCheck = true; + doCheck = true; + doInstallCheck = true; - phases = [ "unpackPhase" "patchPhase" "installPhase" ]; + phases = [ "unpackPhase" "patchPhase" "installPhase" ]; - src = linkFarm "dynix-source" { - inherit (self) dynixCommand dynixModules; - }; + src = linkFarm "dynix-source" { + inherit (self) dynixCommand dynixModules; + }; - installPhase = lib.dedent '' - runHook preInstall + installPhase = '' + runHook preInstall - mkdir -p "$out" - cp -r --reflink=auto "$dynixCommand/"* "$out/" - mkdir -p "$modules" - cp -r --reflink=auto "$dynixModules/"* "$modules/" + mkdir -p "$out" + cp -r --reflink=auto "$dynixCommand/"* "$out/" + mkdir -p "$modules" + cp -r --reflink=auto "$dynixModules/"* "$modules/" - runHook postInstall - ''; + runHook postInstall + ''; - # - # SUB-DERIVATONS - # + # + # SUB-DERIVATONS + # - dynixCommand = stdenv.mkDerivation (finalAttrs: let - commandSelf = finalAttrs.finalPackage; - in { - pname = "${self.pname}-command"; - inherit (self) version; - inherit (self) strictDeps __structuredAttrs; - inherit (self) doCheck doInstallCheck; + dynixCommand = stdenv.mkDerivation (finalAttrs: let + commandSelf = finalAttrs.finalPackage; + in { + pname = "${self.pname}-command"; + inherit (self) version; + inherit (self) strictDeps __structuredAttrs; + inherit (self) doCheck doInstallCheck; - src = lib.fileset.toSource { - root = ./.; - fileset = lib.fileset.unions [ - ./Cargo.toml - ./Cargo.lock - ./src - ]; - }; + src = lib.fileset.toSource { + root = ./.; + fileset = lib.fileset.unions [ + ./Cargo.toml + ./Cargo.lock + ./src + ]; + }; - cargoDeps = rustPlatform.importCargoLock { - lockFile = ./Cargo.lock; - }; + cargoDeps = rustPlatform.importCargoLock { + lockFile = ./Cargo.lock; + }; - nativeBuildInputs = rustHooks.asList ++ [ - cargo - ]; + nativeBuildInputs = rustHooks.asList ++ [ + cargo + ]; - nativeInstallCheckInputs = [ - versionCheckHook - ]; + nativeInstallCheckInputs = [ + versionCheckHook + ]; - meta = { - mainProgram = "dynix"; - }; - }); + meta = { + mainProgram = "dynix"; + }; + }); - dynixModules = stdenv.mkDerivation (finalAttrs: let - modulesSelf = finalAttrs.finalPackage; - in { - pname = "${self.pname}-modules"; - inherit (self) version; - inherit (self) strictDeps __structuredAttrs; - inherit (self) doCheck doInstallCheck; + dynixModules = stdenv.mkDerivation (finalAttrs: let + modulesSelf = finalAttrs.finalPackage; + in { + pname = "${self.pname}-modules"; + inherit (self) version; + inherit (self) strictDeps __structuredAttrs; + inherit (self) doCheck doInstallCheck; - src = lib.fileset.toSource { - root = ./modules/dynamicism; - fileset = lib.fileset.unions [ - ./modules/dynamicism - ]; - }; + src = lib.fileset.toSource { + root = ./modules/dynamicism; + fileset = lib.fileset.unions [ + ./modules/dynamicism + ]; + }; - phases = [ "unpackPhase" "patchPhase" "installPhase" ]; + phases = [ "unpackPhase" "patchPhase" "installPhase" ]; - modulesOut = "${placeholder "out"}/share/nixos/modules/dynix"; + modulesOut = "${placeholder "out"}/share/nixos/modules/dynix"; - installPhase = lib.dedent '' - runHook preInstall + installPhase = lib.dedent '' + runHook preInstall - mkdir -p "$modulesOut" - cp -r "$src/"* "$modulesOut/" + mkdir -p "$modulesOut" + cp -r "$src/"* "$modulesOut/" - runHook postInstall - ''; - }); + runHook postInstall + ''; + }); - # - # ---------------------------------------------------------------------------- - # + # + # ---------------------------------------------------------------------------- + # - passthru.mkDevShell = { - path, - mkShell, - python3Packages, - fenixToolchain, - }: let - mkShell' = mkShell.override { inherit stdenv; }; - pyEnv = python3Packages.python.withPackages (p: [ p.beartype ]); - in mkShell' { - name = "devshell-for-${self.name}"; - inputsFrom = [ self ]; - packages = [ - pyEnv - stdenv.cc - fenixToolchain - ]; - env.PYTHONPATH = [ - "${pyEnv}/${pyEnv.sitePackages}" - # Cursed. - "${path}/nixos/lib/test-driver/src" - ] |> lib.concatStringsSep ":"; - }; + passthru.mkDevShell = { + path, + mkShell, + python3Packages, + fenixToolchain, + }: let + mkShell' = mkShell.override { inherit stdenv; }; + pyEnv = python3Packages.python.withPackages (p: [ p.beartype ]); + in mkShell' { + name = "devshell-for-${self.name}"; + inputsFrom = [ self ]; + packages = [ + pyEnv + stdenv.cc + fenixToolchain + ]; + env.PYTHONPATH = [ + "${pyEnv}/${pyEnv.sitePackages}" + # Cursed. + "${path}/nixos/lib/test-driver/src" + ] |> lib.concatStringsSep ":"; + }; - passthru.modulesPath = self.modules + "/share/nixos/modules"; - passthru.dynix = self.modulesPath + "/dynix"; + passthru.modulesPath = self.modules + "/share/nixos/modules"; + passthru.dynix = self.modulesPath + "/dynix"; - passthru.tests = lib.fix (callPackage ./tests { - dynix = self; - }).packages; + passthru.tests = lib.fix (callPackage ./tests { + dynix = self; + }).packages; - passthru.allTests = linkFarm "dynix-all-tests" self.tests; + passthru.allTests = linkFarm "dynix-all-tests" self.tests; - meta = { - longDescription = lib.dedent '' - Default output contains the Rust binary. - The `modules` output contains the modules prefixed under `/share/nixos/modules/dynix`. - The `dynix` passthru attr is a shortcut for the modules output *with* the modules prefix, - and thus `dynix.dynix` can be passed directly to `imports = [`. - ''; - mainProgram = "dynix"; - outputsToInstall = [ "out" "modules" ]; - }; + meta = { + longDescription = lib.dedent '' + Default output contains the Rust binary. + The `modules` output contains the modules prefixed under `/share/nixos/modules/dynix`. + The `dynix` passthru attr is a shortcut for the modules output *with* the modules prefix, + and thus `dynix.dynix` can be passed directly to `imports = [`. + ''; + mainProgram = "dynix"; + outputsToInstall = [ "out" "modules" ]; + }; })) diff --git a/shell.nix b/shell.nix index e2af1b4..4b93b7f 100644 --- a/shell.nix +++ b/shell.nix @@ -1,26 +1,26 @@ { - pkgs ? import { - config = { - checkMeta = true; - allowAliases = false; - }; - }, - qpkgs ? let - src = fetchTarball "https://github.com/Qyriad/nur-packages/archive/main.tar.gz"; - in import src { inherit pkgs; }, - dynix ? import ./default.nix { inherit pkgs qpkgs; }, - fenixLib ? let - src = fetchTarball "https://github.com/nix-community/fenix/archive/main.tar.gz"; - in import src { inherit pkgs; }, - fenixToolchain ? fenixLib.latest.toolchain, + pkgs ? import { + config = { + checkMeta = true; + allowAliases = false; + }; + }, + qpkgs ? let + src = fetchTarball "https://github.com/Qyriad/nur-packages/archive/main.tar.gz"; + in import src { inherit pkgs; }, + dynix ? import ./default.nix { inherit pkgs qpkgs; }, + fenixLib ? let + src = fetchTarball "https://github.com/nix-community/fenix/archive/main.tar.gz"; + in import src { inherit pkgs; }, + fenixToolchain ? fenixLib.latest.toolchain, }: let - inherit (pkgs) lib; + inherit (pkgs) lib; - mkDevShell = dynix: qpkgs.callPackage dynix.mkDevShell { inherit fenixToolchain; }; - devShell = mkDevShell dynix; + mkDevShell = dynix: qpkgs.callPackage dynix.mkDevShell { inherit fenixToolchain; }; + devShell = mkDevShell dynix; - byStdenv = lib.mapAttrs (lib.const mkDevShell) dynix.byStdenv; + byStdenv = lib.mapAttrs (lib.const mkDevShell) dynix.byStdenv; in devShell.overrideAttrs (prev: { - passthru = { inherit byStdenv; }; + passthru = { inherit byStdenv; }; }) From 6931853de3970726274e7e1351e0775e3e8218f9 Mon Sep 17 00:00:00 2001 From: Qyriad Date: Wed, 18 Feb 2026 14:01:39 +0100 Subject: [PATCH 2/4] remove unused code --- default.nix | 2 +- modules/dynamicism/dynamicism.nix | 1 - tests/default.nix | 2 +- tests/distccd/test-script.py | 16 ++--- tests/gotosocial/configuration-package.nix | 7 --- tests/gotosocial/default.nix | 7 --- tests/gotosocial/hardware-configuration.nix | 7 --- tests/gotosocial/test-script.py | 7 +-- tests/harmonia/hardware-configuration.nix | 4 -- tests/harmonia/test-script.py | 7 +-- tests/tzupdate/configuration.nix | 67 --------------------- tests/tzupdate/hardware-configuration.nix | 4 -- tests/tzupdate/test-script.py | 52 ---------------- tests/tzupdate/test.nix | 24 -------- 14 files changed, 9 insertions(+), 198 deletions(-) delete mode 100644 tests/gotosocial/configuration-package.nix delete mode 100644 tests/gotosocial/default.nix delete mode 100644 tests/gotosocial/hardware-configuration.nix delete mode 100644 tests/harmonia/hardware-configuration.nix delete mode 100644 tests/tzupdate/configuration.nix delete mode 100644 tests/tzupdate/hardware-configuration.nix delete mode 100644 tests/tzupdate/test-script.py delete mode 100644 tests/tzupdate/test.nix diff --git a/default.nix b/default.nix index 2375445..64072a2 100644 --- a/default.nix +++ b/default.nix @@ -1,7 +1,7 @@ { pkgs ? import { }, qpkgs ? let - src = fetchTree (builtins.parseFlakeRef "github:Qyriad/nur-packages"); + src = fetchTarball "https://github.com/Qyriad/nur-packages/archive/main.tar.gz"; in import src { inherit pkgs; }, }: let inherit (qpkgs) lib; diff --git a/modules/dynamicism/dynamicism.nix b/modules/dynamicism/dynamicism.nix index 6f79993..75f9fcb 100644 --- a/modules/dynamicism/dynamicism.nix +++ b/modules/dynamicism/dynamicism.nix @@ -11,7 +11,6 @@ let inherit (import ./lib.nix { inherit lib; }) typeCheck - convenientAttrPath executablePathInStore concatFoldl recUpdateFoldl diff --git a/tests/default.nix b/tests/default.nix index 535c54b..02842fa 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -1,7 +1,7 @@ { pkgs ? import { }, qpkgs ? let - src = fetchTree (builtins.parseFlakeRef "github:Qyriad/nur-packages"); + src = fetchTarball "https://github.com/Qyriad/nur-packages/archive/main.tar.gz"; in import src { inherit pkgs; }, callPackage ? qpkgs.callPackage, lib ? qpkgs.lib, diff --git a/tests/distccd/test-script.py b/tests/distccd/test-script.py index d5867bf..3a9da08 100644 --- a/tests/distccd/test-script.py +++ b/tests/distccd/test-script.py @@ -1,7 +1,5 @@ import argparse import functools -#from pathlib import Path -#from pprint import pformat import shlex import textwrap from typing import Any, cast, TYPE_CHECKING @@ -64,7 +62,6 @@ def get_cli_args() -> argparse.Namespace: machine.log(f"{cmdline_args=}") print(f"{cmdline_args=}") - #return shlex.join(cmdline_args[1:]) args, rest = parser.parse_known_args(cmdline_args) return args @@ -77,12 +74,7 @@ def dynix_append(option: str, value: Any): @beartype def do_apply(): expr = textwrap.dedent(""" - let - nixos = import { }; - in nixos.config.dynamicism.applyDynamicConfiguration { - baseConfiguration = /etc/nixos/configuration.nix; - newConfiguration = /etc/nixos/dynamic.nix; - } + (import { }).config.dynamicism.applyDynamicConfiguration { } """).strip() machine.succeed(rf""" @@ -95,7 +87,6 @@ machine.log("INIT") # Config should have our initial values. args = get_cli_args() -#assert '--jobs 12' in args, f'--jobs 12 not in {args=}' assert args.jobs == 12, f'{args.jobs=} != 12' assert args.job_lifetime == 900, f'{args.job_lifetime} != 900' assert args.log_level == 'warning', f'{args.log_level=} != warning' @@ -105,10 +96,13 @@ with machine.nested("must succeed: initial nixos-rebuild switch"): # Config should not have changed. args = get_cli_args() +assert args.jobs == 12, f'{args.jobs=} != 12' +assert args.job_lifetime == 900, f'{args.job_lifetime} != 900' +assert args.log_level == 'warning', f'{args.log_level=} != warning' #machine.log(f"config.toml after first rebuild: {indent(pformat(args))}") #assert int(args['workers']) == 4, f"{args['workers']=} != 4" #assert int(args['max_connection_rate']) == 256, f"{args['max_connection_rate']=} != 256" -# + new_jobs = 4 dynix_append("services.distccd.maxJobs", new_jobs) do_apply() diff --git a/tests/gotosocial/configuration-package.nix b/tests/gotosocial/configuration-package.nix deleted file mode 100644 index 96a89d8..0000000 --- a/tests/gotosocial/configuration-package.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ - runCommand, -}: runCommand "tests-gotosocial-configuration-dot-nix" { -} '' - set -euo pipefail - install -Dm a=r ${./configuration.nix} "$out/share/nixos/configuration.nix" -'' diff --git a/tests/gotosocial/default.nix b/tests/gotosocial/default.nix deleted file mode 100644 index 7396103..0000000 --- a/tests/gotosocial/default.nix +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Convenience shortcut for running this test from the command-line. - * Normally this test is initialized from /tests/default.nix. - */ -{ - pkgs ? import { }, -}: pkgs.testers.runNixOSTest ./test.nix diff --git a/tests/gotosocial/hardware-configuration.nix b/tests/gotosocial/hardware-configuration.nix deleted file mode 100644 index f036a72..0000000 --- a/tests/gotosocial/hardware-configuration.nix +++ /dev/null @@ -1,7 +0,0 @@ -/** Dummy hardware configuration. - * Will be replaced with the real one in the test VM. - */ -{ ... }: -{ - -} diff --git a/tests/gotosocial/test-script.py b/tests/gotosocial/test-script.py index 9799dfb..c6b087b 100644 --- a/tests/gotosocial/test-script.py +++ b/tests/gotosocial/test-script.py @@ -61,12 +61,7 @@ def dynix_append(option: str, value: str): @beartype def do_apply(): expr = textwrap.dedent(""" - let - nixos = import { }; - in nixos.config.dynamicism.applyDynamicConfiguration { - baseConfiguration = /etc/nixos/configuration.nix; - newConfiguration = /etc/nixos/dynamic.nix; - } + (import { }).config.dynamicism.applyDynamicConfiguration { } """).strip() machine.succeed(rf""" diff --git a/tests/harmonia/hardware-configuration.nix b/tests/harmonia/hardware-configuration.nix deleted file mode 100644 index facb35d..0000000 --- a/tests/harmonia/hardware-configuration.nix +++ /dev/null @@ -1,4 +0,0 @@ -{ ... }: -{ - -} diff --git a/tests/harmonia/test-script.py b/tests/harmonia/test-script.py index 215ea38..be60fe3 100644 --- a/tests/harmonia/test-script.py +++ b/tests/harmonia/test-script.py @@ -64,12 +64,7 @@ def dynix_append(option: str, value: Any): @beartype def do_apply(): expr = textwrap.dedent(""" - let - nixos = import { }; - in nixos.config.dynamicism.applyDynamicConfiguration { - baseConfiguration = /etc/nixos/configuration.nix; - newConfiguration = /etc/nixos/dynamic.nix; - } + (import { }).config.dynamicism.applyDynamicConfiguration { } """).strip() machine.succeed(rf""" diff --git a/tests/tzupdate/configuration.nix b/tests/tzupdate/configuration.nix deleted file mode 100644 index 176975c..0000000 --- a/tests/tzupdate/configuration.nix +++ /dev/null @@ -1,67 +0,0 @@ -{ pkgs, lib, config, modulesPath, ... }: -let - name = config.networking.hostName; - moduleList = import "${modulesPath}/module-list.nix"; - - dynixFromSearchPath = let - res = builtins.tryEval ; - in lib.optional res.success res.value; -in -{ - imports = [ - "${modulesPath}/testing/test-instrumentation.nix" - ./hardware-configuration.nix - ] ++ lib.concatLists [ - moduleList - dynixFromSearchPath - ]; - - dynamicism.for.tzupdate.enable = true; - services.tzupdate = { - enable = true; - }; - - system.switch.enable = true; - documentation.enable = false; - - networking.hostName = "tzupdate-machine"; - - boot.loader.grub = { - enable = true; - device = "/dev/vda"; - forceInstall = true; - }; - - nix = { - package = pkgs.lixPackageSets.latest.lix; - nixPath = [ - "nixpkgs=${pkgs.path}" - "/nix/var/nix/profiles/per-user/root/profile/share/nixos/modules" - ]; - - settings = { - experimental-features = [ "nix-command" "pipe-operator" ]; - substituters = lib.mkForce [ ]; - hashed-mirrors = null; - connect-timeout = 1; - # For my debugging purposes. - show-trace = true; - }; - }; - - environment.pathsToLink = [ "/share" ]; - environment.extraOutputsToInstall = [ "modules" ]; - environment.variables = { - "NIXOS_CONFIG" = "/etc/nixos/configuration.nix"; - }; - - environment.shellAliases = { - ls = "eza --long --header --group --group-directories-first --classify --binary"; - }; - - environment.systemPackages = with pkgs; [ - eza - fd - ripgrep - ]; -} diff --git a/tests/tzupdate/hardware-configuration.nix b/tests/tzupdate/hardware-configuration.nix deleted file mode 100644 index facb35d..0000000 --- a/tests/tzupdate/hardware-configuration.nix +++ /dev/null @@ -1,4 +0,0 @@ -{ ... }: -{ - -} diff --git a/tests/tzupdate/test-script.py b/tests/tzupdate/test-script.py deleted file mode 100644 index 53762a0..0000000 --- a/tests/tzupdate/test-script.py +++ /dev/null @@ -1,52 +0,0 @@ -#from pathlib import Path -#import shlex -#import textwrap -from typing import cast, TYPE_CHECKING - -from beartype import beartype - -from test_driver.machine import Machine -from test_driver.errors import RequestedAssertionFailed - -if TYPE_CHECKING: - global machine - machine = cast(Machine, ...) - assert machine.shell is not None - -ls = "eza -lah --color=always --group-directories-first" - -@beartype -def run_log(machine: Machine, *commands: str, timeout: int | None = 60) -> str: - output = "" - for command in commands: - with machine.nested(f"must succeed: {command}"): - (status, out) = machine.execute(f"{command} | tee /dev/stderr", timeout=timeout) - if status != 0: - machine.log(f"output: {out}") - raise RequestedAssertionFailed( - f"command `{command}` failed (exit code {status})", - ) - output += out - - return output - -machine.wait_for_unit("default.target") -assert "lix" in machine.succeed("nix --version").lower() -machine.log("INIT") - - -machine.succeed("nixos-generate-config") -machine.succeed("mkdir -vp /etc/nixos") -# Dereference is required since that configuration.nix is probably a symlink to the store. -machine.succeed("cp -rv --dereference /run/current-system/sw/share/nixos/configuration.nix /etc/nixos/") - -machine.succeed("env PAGER= nixos-rebuild switch --log-format raw-with-logs --fallback") - -@beartype -def get_interval() -> str: - prop = machine.get_unit_property("tzupdate.timer", "OnCalendar") - print(f"{prop=}") - - return prop - -get_interval() diff --git a/tests/tzupdate/test.nix b/tests/tzupdate/test.nix deleted file mode 100644 index 2d42c10..0000000 --- a/tests/tzupdate/test.nix +++ /dev/null @@ -1,24 +0,0 @@ -{ ... }: - -{ - name = "nixos-test-dynamicism-tzupdate"; - - extraPythonPackages = p: [ - p.beartype - ]; - - nodes.machine = { name, pkgs, ... }: { - imports = [ ./configuration.nix ]; - - environment.systemPackages = let - configFileTree = pkgs.runCommand "${name}-configuration-dot-nix" { } '' - set -euo pipefail - install -Dm a=r ${./configuration.nix} "$out/share/nixos/configuration.nix" - ''; - in [ - configFileTree - ]; - }; - - testScript = builtins.readFile ./test-script.py; -} From f447fd0a99b0938867dd2ca6e211f0c587693abf Mon Sep 17 00:00:00 2001 From: Qyriad Date: Wed, 18 Feb 2026 14:01:39 +0100 Subject: [PATCH 3/4] cleanup in tests --- tests/distccd/configuration.nix | 2 ++ tests/distccd/test-script.py | 23 ++++++++++++++--------- tests/gotosocial/test-script.py | 7 ++++++- tests/harmonia/configuration.nix | 16 +--------------- tests/harmonia/test-script.py | 16 ++++++---------- 5 files changed, 29 insertions(+), 35 deletions(-) diff --git a/tests/distccd/configuration.nix b/tests/distccd/configuration.nix index a8fb343..b0d90b5 100644 --- a/tests/distccd/configuration.nix +++ b/tests/distccd/configuration.nix @@ -1,9 +1,11 @@ { ... }: + { services.distccd = { enable = true; jobTimeout = 900; maxJobs = 12; + logLevel = "warning"; nice = -10; }; diff --git a/tests/distccd/test-script.py b/tests/distccd/test-script.py index 3a9da08..3678155 100644 --- a/tests/distccd/test-script.py +++ b/tests/distccd/test-script.py @@ -82,8 +82,10 @@ def do_apply(): """.strip()) machine.wait_for_unit("default.target") -assert "lix" in machine.succeed("nix --version").lower() -machine.log("INIT") +machine.wait_for_unit("install-dynix.service") + +dynix_out = machine.succeed("dynix --version") +assert "dynix" in dynix_out, f"dynix not in {dynix_out=}" # Config should have our initial values. args = get_cli_args() @@ -99,17 +101,13 @@ args = get_cli_args() assert args.jobs == 12, f'{args.jobs=} != 12' assert args.job_lifetime == 900, f'{args.job_lifetime} != 900' assert args.log_level == 'warning', f'{args.log_level=} != warning' -#machine.log(f"config.toml after first rebuild: {indent(pformat(args))}") -#assert int(args['workers']) == 4, f"{args['workers']=} != 4" -#assert int(args['max_connection_rate']) == 256, f"{args['max_connection_rate']=} != 256" new_jobs = 4 dynix_append("services.distccd.maxJobs", new_jobs) do_apply() -args = get_cli_args() - # Only jobs should have changed. The others should still be default. +args = get_cli_args() assert args.jobs == new_jobs, f'{args.jobs=} != {new_jobs=}' assert args.job_lifetime == 900, f'{args.job_lifetime} != 900' assert args.log_level == 'warning', f'{args.log_level=} != warning' @@ -119,6 +117,13 @@ dynix_append("services.distccd.logLevel", f'"{new_log_level}"') do_apply() args = get_cli_args() -#assert args.jobs == new_jobs, f'{args.jobs=} != {new_jobs=}' -#assert args.job_lifetime == 900, f'{args.job_lifetime} != 900' +assert args.jobs == new_jobs, f'{args.jobs=} != {new_jobs=}' +assert args.job_lifetime == 900, f'{args.job_lifetime} != 900' assert args.log_level == new_log_level, f'{args.log_level=} != {new_log_level=}' + +# And this should set everything back. +machine.succeed("env PAGER= nixos-rebuild switch --log-format raw-with-logs --fallback") +args = get_cli_args() +assert args.jobs == 12, f'{args.jobs=} != 12' +assert args.job_lifetime == 900, f'{args.job_lifetime} != 900' +assert args.log_level == 'warning', f'{args.log_level=} != warning' diff --git a/tests/gotosocial/test-script.py b/tests/gotosocial/test-script.py index c6b087b..51a303f 100644 --- a/tests/gotosocial/test-script.py +++ b/tests/gotosocial/test-script.py @@ -70,7 +70,6 @@ def do_apply(): machine.wait_for_unit("default.target") -assert "lix" in machine.succeed("nix --version").lower() machine.wait_for_unit("install-dynix.service") dynix_out = machine.succeed("dynix --version") @@ -89,6 +88,12 @@ except StopIteration: raise AssertionError(f"no 'application-name:' found in config file: {textwrap.indent(config_text, " ")}") assert "gotosocial-for-machine" in application_name, f"'gotosocial-for-machine' should be in {application_name=}" +try: + host = next(line for line in lines if line.startswith("host:")) +except StopIteration: + raise AssertionError(f"no 'host:' found in config file: {textwrap.indent(config_text, " ")}") +assert "gotosocial-machine" in host, f"'gotosocial-machine' should be in {host=}" + new_app_name = "yay!" dynix_append("services.gotosocial.settings.application-name", f'"{new_app_name}"') do_apply() diff --git a/tests/harmonia/configuration.nix b/tests/harmonia/configuration.nix index 37494cb..d5cc833 100644 --- a/tests/harmonia/configuration.nix +++ b/tests/harmonia/configuration.nix @@ -1,20 +1,6 @@ -{ lib, modulesPath, ... }: -let - moduleList = import "${modulesPath}/module-list.nix"; +{ ... }: - dynixFromSearchPath = let - res = builtins.tryEval ; - in lib.optional res.success res.value; -in { - imports = [ - #"${modulesPath}/testing/test-instrumentation.nix" - #./hardware-configuration.nix - ] ++ lib.concatLists [ - #dynixFromSearchPath - moduleList - ]; - dynamicism.for.harmonia.enable = true; services.harmonia = { enable = true; diff --git a/tests/harmonia/test-script.py b/tests/harmonia/test-script.py index be60fe3..bdac573 100644 --- a/tests/harmonia/test-script.py +++ b/tests/harmonia/test-script.py @@ -1,6 +1,5 @@ import functools from pathlib import Path -from pprint import pformat import shlex import textwrap import tomllib @@ -72,12 +71,13 @@ def do_apply(): """.strip()) machine.wait_for_unit("default.target") -assert "lix" in machine.succeed("nix --version").lower() -machine.log("INIT") +machine.wait_for_unit("install-dynix.service") + +dynix_out = machine.succeed("dynix --version") +assert "dynix" in dynix_out, f"dynix not in {dynix_out=}" # Config should have our initial values. config_toml = get_config_file() -machine.log(f"config.toml BEFORE first rebuild (initial): {indent(pformat(config_toml))}") assert int(config_toml['workers']) == 4, f"{config_toml['workers']=} != 4" assert int(config_toml['max_connection_rate']) == 256, f"{config_toml['max_connection_rate']=} != 256" @@ -86,7 +86,6 @@ with machine.nested("must succeed: initial nixos-rebuild switch"): # Config should not have changed. config_toml = get_config_file() -machine.log(f"config.toml after first rebuild: {indent(pformat(config_toml))}") assert int(config_toml['workers']) == 4, f"{config_toml['workers']=} != 4" assert int(config_toml['max_connection_rate']) == 256, f"{config_toml['max_connection_rate']=} != 256" @@ -96,7 +95,6 @@ do_apply() # Workers, but not max connection rate, should have changed. config_toml = get_config_file() -machine.log(f"config.toml after DYNAMIC ACTIVATION: {indent(pformat(config_toml))}") assert int(config_toml['workers']) == new_workers, f"{config_toml['workers']=} != {new_workers}" assert int(config_toml['max_connection_rate']) == 256, f"{config_toml['max_connection_rate']=} != 256" @@ -104,16 +102,14 @@ new_max_connection_rate = 100 dynix_append("services.harmonia.settings.max_connection_rate", new_max_connection_rate) do_apply() -# Max connection rate should have changed, but workers should have reverted. +# Max connection rate should have changed, and workers should be the same as before. config_toml = get_config_file() -machine.log(f"config_toml after SECOND dynamic activation: {indent(pformat(config_toml))}") assert int(config_toml['max_connection_rate']) == new_max_connection_rate, f"{config_toml['max_connection_rate']=} != {new_max_connection_rate}" +assert int(config_toml['workers']) == new_workers, f"{config_toml['workers']=} != {new_workers}" # And this should set everything back. machine.succeed("env PAGER= nixos-rebuild switch --log-format raw-with-logs --fallback") -machine.systemctl("restart harmonia.service") machine.wait_for_unit("harmonia.service") config_toml = get_config_file() -machine.log(f"config_toml after NORMAL activation: {indent(pformat(config_toml))}") assert int(config_toml['max_connection_rate']) == 256, f'{config_toml["max_connection_rate"]=} != 256' assert int(config_toml['workers']) == 4, f'{config_toml["workers"]=} != 4' From 6e6948c666e0b8e9ce5ea39d69884c80a597d4ce Mon Sep 17 00:00:00 2001 From: Qyriad Date: Wed, 18 Feb 2026 14:01:39 +0100 Subject: [PATCH 4/4] add README.md --- README.md | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..c58f57e --- /dev/null +++ b/README.md @@ -0,0 +1,119 @@ +# Dynix — WIP modular dynamicism for NixOS systems + +Dynix is a prototype for modifying an append-only NixOS configuration, and dynamically + +## Running the tests + +There are currently 3 implemented dynamicism modules: `gotosocial`, `harmonia`, and `distccd`. +Each test uses the [NixOS test infrastructure](https://nixos.org/manual/nixos/unstable/#sec-nixos-tests) to: + +1. Setup a virtual machine running NixOS +2. Configure the VM's NixOS to run a given service, with certain settings +3. Verify that the running service is using those settings +4. Use Dynix to change a setting for that service +5. Verify that the running service is now using the new setting + +The tests themselves can be run with Nix. +To run the test for, e.g., Gotosocial, you can run: + +```bash +$ nix --experimental-features "nix-command flakes pipe-operator pipe-operators" build .#default.tests.gotosocial +``` + +The experimental [piping operator](https://github.com/NixOS/rfcs/pull/148) is currently used in Dynix, so you must enable the experimental feature `pipe-operator` for Lix or `pipe-operators` for CppNix (you can add both to cover both Nix implementations); flakes are used for locked inputs. + + +To run a test with fewer experimental features, and without locked inputs, you can use the old CLI: + +```bash +$ nix-build --experimental-features "pipe-operator pipe-operators" -A tests.gotosocial +``` + +All the tests at once can be run with: + +```bash +$ nix build --experimental-features "nix-command flakes pipe-operator pipe-operators" .#default.allTests +``` + +## Gotosocial + +This example, implemented in [./modules/dynamicism/gotosocial.nix](./modules/dynamicism/gotosocial.nix), is tested in [./tests/gotosocial](./tests/gotosocial). +The test sets up a VM using NixOS's `services.gotosocial` module with the following *static* [configuration](./tests/gotosocial/configuration.nix): + +```nix +{ + services.gotosocial = { + enable = true; + setupPostgresqlDB = true; + settings = { + application-name = "gotosocial-for-machine"; + host = "gotosocial-machine.local"; + }; + }; +} +``` + +The automated [test script](./tests/gotosocial/test-script.py): + +1. Asserts that that the above *static* configuration is in effect (by extracting the configuration from the running Gotosocial process) +2. Runs `dynix append "services.gotosocial.settings.application-name" "yay!"`, which modifies the append-only configuration file `/etc/nixos/dynamic.nix` in the VM filesystem +3. Runs the dynamic activation script built by `(import ).config.dynamicism.applyDynamicConfiguration { }`, which *applies* the dynamic configuration +4. Asserts that the dynamically configured `application-name` is in effect +5. Runs `nixos-rebuild switch` to re-apply the *static* configuration +6. Asserts that the dynamic configuration is *no longer* in effect, and we are back to the static configuration + +## Harmonia + +This example, implemented in [./modules/dynamicism/harmonia.nix](./modules/dynamicism/harmonia.nix), is tested in [./tests/harmonia](./tests/harmonia). +The test sets up a VM using NixOS's `services.harmonia` module with the following *static* [configuration](./tests/harmonia/configuration.nix) + +```nix +{ + services.harmonia = { + enable = true; + settings = { + workers = 4; + max_connection_rate = 256; + }; + }; +} +``` + +The VM [test script](./tests/harmonia/test-script.py): + +1. Asserts that the above *static* configuration is in effect (by extracting the configuration from the running Harmonia process) +2. Runs `dynix append "services.harmonia.settings.workers" 20`, to modify the append-only configuration file `/etc/nixos/dynamic.nix` in the VM filesystem +3. Runs the dynamic activation script built by `(import ).config.dynamicism.applyDynamicConfiguration { }`, to *apply* the dynamic configuration +4. Asserts that the dynamically configured `workers` is in effect +5. Runs `dynix append "services.harmonia.settings.max_connection_rate" 100` +6. Runs the dynamic activation script +7. Asserts that *both* `max_connection_rate` and `workers` dynamic values are in effect +8. Runs `nixos-rebuild switch` to re-apply the *static* configuration +9. Asserts that the dynamic configuration is *no longer* in effect, and we are back to the static configuration + +## Distccd + +This example, implemented in [./modules/dynamicism/distccd.nix](./modules/dynamicism/distccd.nix), is tested in [./tests/distccd](./tests/distccd). +The test sets up a VM using NixOS's `services.distccd` module with the following *static* [configuration](./tests/distccd/configuration.nix): + +```nix +{ + services.distccd = { + jobTimeout = 900; + maxJobs = 12; + logLevel = "warning"; + }; +} +``` + +The VM [test script](./tests/distccd/test-script.py): + +1. Asserts that the above *static* configuration is in effect (by extracting the configuration from the running Distccd process) +2. Runs `dynix append "services.distccd.maxJobs" 4`, to modify the append-only configuration file `/etc/nixos/dynamic.nix` in the VM filesystem +3. Runs the dynamic activation script built by `(import ).config.dynamicism.applyDynamicConfiguration { }`, to *apply* the dynamic configuration +4. Asserts that the dynamically configured `maxJobs` is in effect +5. Runs `dynix append "services.distccd.logLevel" "error"` +6. Runs the dynamic activation script +7. Asserts that *both* `maxJobs` and `logLevel` dynamic values are in effect +8. Runs `nixos-rebuild switch` to re-apply the *static* configuration +9. Asserts that the dynamic configuration is *no longer* in effect, and we are back to the static configuration.