diff --git a/.github/workflows/nix-pr-check.yml b/.github/workflows/nix-pr-check.yml index c964de65..545c2956 100644 --- a/.github/workflows/nix-pr-check.yml +++ b/.github/workflows/nix-pr-check.yml @@ -1,4 +1,4 @@ -name: Check nix flake +name: Nix flake and NixOS tests on: pull_request: @@ -9,6 +9,7 @@ on: jobs: check-flake: runs-on: ubuntu-latest + timeout-minutes: 120 steps: - name: Checkout @@ -18,6 +19,25 @@ jobs: - name: Install Nix uses: cachix/install-nix-action@v31 + with: + enable_kvm: true + extra_nix_config: | + system-features = nixos-test benchmark big-parallel kvm - name: Check the flake - run: nix flake check + run: nix flake check -L + + - name: Run NixOS module test + run: nix build .#nixosTests.x86_64-linux.nixos-module -L + + - name: Run NixOS service start test + run: nix build .#nixosTests.x86_64-linux.nixos-service-start-module -L + + - name: Run greeter niri test + run: nix build .#nixosTests.x86_64-linux.greeter-niri-module -L + + - name: Run home-manager module test + run: nix build .#nixosTests.x86_64-linux.home-manager-module -L + + - name: Run niri home-manager module test + run: nix build .#nixosTests.x86_64-linux.niri-home-module -L diff --git a/distro/nix/tests/default.nix b/distro/nix/tests/default.nix new file mode 100644 index 00000000..a22afdae --- /dev/null +++ b/distro/nix/tests/default.nix @@ -0,0 +1,52 @@ +{ + self, + pkgs, + ... +}: +rec { + all = pkgs.symlinkJoin { + name = "dms-nixos-tests"; + paths = [ + nixos-module + nixos-service-start-module + greeter-niri-module + niri-home-module + home-manager-module + ]; + }; + + nixos-module = import ./nixos-module.nix { + inherit + self + pkgs + ; + }; + + nixos-service-start-module = import ./nixos-service-start-module.nix { + inherit + self + pkgs + ; + }; + + greeter-niri-module = import ./greeter-niri-module.nix { + inherit + self + pkgs + ; + }; + + niri-home-module = import ./niri-home-module.nix { + inherit + self + pkgs + ; + }; + + home-manager-module = import ./home-manager-module.nix { + inherit + self + pkgs + ; + }; +} diff --git a/distro/nix/tests/greeter-niri-module.nix b/distro/nix/tests/greeter-niri-module.nix new file mode 100644 index 00000000..be5f05be --- /dev/null +++ b/distro/nix/tests/greeter-niri-module.nix @@ -0,0 +1,60 @@ +{ + self, + pkgs, + ... +}: +pkgs.testers.runNixOSTest { + name = "dms-greeter-niri-module"; + + nodes.machine = { + imports = [ + self.nixosModules.greeter + ]; + + users.groups.greeter = { }; + users.users.greeter = { + isSystemUser = true; + group = "greeter"; + }; + + services.greetd.settings.default_session.user = "greeter"; + + programs.niri.enable = true; + + programs.dank-material-shell.greeter = { + enable = true; + compositor.name = "niri"; + }; + + system.stateVersion = "25.11"; + }; + + testScript = '' + import re + + machine.wait_for_unit("multi-user.target") + machine.wait_for_unit("greetd.service") + + machine.succeed("systemctl is-enabled greetd.service") + machine.succeed("systemctl is-active greetd.service") + + greetd_unit = machine.succeed("cat /etc/systemd/system/greetd.service") + config_match = re.search(r'--config (/nix/store[^ ]+-greetd.toml)', greetd_unit) + if config_match is None: + raise AssertionError(greetd_unit) + + greetd_config_path = config_match.group(1) + greetd_config = machine.succeed(f"cat {greetd_config_path}") + t.assertIn("dms-greeter", greetd_config) + + script_match = re.search(r'command\s*=\s*"([^"]+/bin/dms-greeter)"', greetd_config) + if script_match is None: + raise AssertionError(greetd_config) + + script_path = script_match.group(1) + script = machine.succeed(f"cat {script_path}") + t.assertIn("--command", script) + t.assertIn("niri", script) + t.assertIn("/share/quickshell/dms", script) + ''; +} diff --git a/distro/nix/tests/home-manager-module.nix b/distro/nix/tests/home-manager-module.nix new file mode 100644 index 00000000..b0ca912b --- /dev/null +++ b/distro/nix/tests/home-manager-module.nix @@ -0,0 +1,107 @@ +{ + self, + pkgs, + ... +}: +let + homeManagerNixosModule = + (fetchTarball { + url = "https://github.com/nix-community/home-manager/archive/e82d4a4ecd18363aa2054cbaa3e32e4134c3dbf4.tar.gz"; + sha256 = "sha256-ZTYDofOM3/PJhRF1EuBh6uibm+DmkhU7Wor6mMN7YTc="; + }) + + "/nixos"; +in +pkgs.testers.runNixOSTest { + name = "dms-home-manager-module"; + + nodes.machine = { + ... + }: { + imports = [ + homeManagerNixosModule + ]; + + users.users.danklinux = { + isNormalUser = true; + createHome = true; + home = "/home/danklinux"; + extraGroups = [ "wheel" ]; + }; + + home-manager.useGlobalPkgs = true; + home-manager.useUserPackages = true; + + home-manager.users.danklinux = { + pkgs, + ... + }: { + imports = [ + self.homeModules.dank-material-shell + ]; + + home.username = "danklinux"; + home.homeDirectory = "/home/danklinux"; + home.stateVersion = "25.11"; + + programs.dank-material-shell = { + enable = true; + systemd = { + enable = true; + target = "default.target"; + }; + + settings = { + theme = "integration-test"; + }; + + clipboardSettings = { + maxItems = 10; + }; + + session = { + startedFrom = "nixos-test"; + }; + + plugins.TestPlugin = { + enable = true; + src = pkgs.runCommand "dms-test-plugin" { } '' + mkdir -p "$out" + echo plugin > "$out/plugin.txt" + ''; + settings = { + enabled = true; + source = "test"; + }; + }; + }; + }; + + system.stateVersion = "25.11"; + }; + + testScript = '' + import json + + machine.wait_for_unit("multi-user.target") + + machine.succeed("su -- danklinux -c 'command -v dms'") + machine.succeed("su -- danklinux -c 'test -f ~/.config/DankMaterialShell/settings.json'") + machine.succeed("su -- danklinux -c 'test -f ~/.config/DankMaterialShell/clsettings.json'") + machine.succeed("su -- danklinux -c 'test -f ~/.config/DankMaterialShell/plugin_settings.json'") + machine.succeed("su -- danklinux -c 'test -e ~/.config/DankMaterialShell/plugins/TestPlugin'") + machine.succeed("su -- danklinux -c 'test -f ~/.local/state/DankMaterialShell/session.json'") + + settings = json.loads(machine.succeed("su -- danklinux -c 'cat ~/.config/DankMaterialShell/settings.json'")) + clipboard = json.loads(machine.succeed("su -- danklinux -c 'cat ~/.config/DankMaterialShell/clsettings.json'")) + session = json.loads(machine.succeed("su -- danklinux -c 'cat ~/.local/state/DankMaterialShell/session.json'")) + plugins = json.loads(machine.succeed("su -- danklinux -c 'cat ~/.config/DankMaterialShell/plugin_settings.json'")) + doctor = json.loads(machine.succeed("su -- danklinux -c 'dms doctor --json'")) + + t.assertEqual(settings["theme"], "integration-test") + t.assertEqual(clipboard["maxItems"], 10) + t.assertEqual(session["startedFrom"], "nixos-test") + t.assertTrue(plugins["TestPlugin"]["enabled"]) + t.assertEqual(plugins["TestPlugin"]["source"], "test") + t.assertIsInstance(doctor.get("results"), list) + ''; +} diff --git a/distro/nix/tests/niri-home-module.nix b/distro/nix/tests/niri-home-module.nix new file mode 100644 index 00000000..e283a544 --- /dev/null +++ b/distro/nix/tests/niri-home-module.nix @@ -0,0 +1,84 @@ +{ + self, + pkgs, + ... +}: +let + homeManagerNixosModule = + (fetchTarball { + url = "https://github.com/nix-community/home-manager/archive/e82d4a4ecd18363aa2054cbaa3e32e4134c3dbf4.tar.gz"; + sha256 = "sha256-ZTYDofOM3/PJhRF1EuBh6uibm+DmkhU7Wor6mMN7YTc="; + }) + + "/nixos"; + + niriFlake = builtins.getFlake "github:sodiboo/niri-flake/2bb22af2985e5f3cfd051b3d977ebfbf81126280?narHash=sha256-ooPmu%2B8tqOGh4kozPW4rJC7Y7WM/FHtEY3OK1PoNW7g%3D"; + + fakeNiri = (pkgs.writeScriptBin "niri" "") // { + cargoBuildNoDefaultFeatures = false; + }; +in +pkgs.testers.runNixOSTest { + name = "dms-niri-home-module"; + + nodes.machine = { + ... + }: { + imports = [ + homeManagerNixosModule + ]; + + users.users.danklinux = { + isNormalUser = true; + createHome = true; + home = "/home/danklinux"; + extraGroups = [ "wheel" ]; + }; + + home-manager.useGlobalPkgs = true; + home-manager.useUserPackages = true; + + environment.pathsToLink = [ + "/share/applications" + "/share/xdg-desktop-portal" + ]; + + home-manager.users.danklinux = { + ... + }: { + imports = [ + self.homeModules.dank-material-shell + niriFlake.homeModules.niri + self.homeModules.niri + ]; + + home.username = "danklinux"; + home.homeDirectory = "/home/danklinux"; + home.stateVersion = "25.11"; + + programs.niri = { + enable = true; + package = fakeNiri; # avoids niri from being compiled in the CI + }; + + programs.dank-material-shell = { + enable = true; + niri = { + enableKeybinds = false; + enableSpawn = true; + }; + }; + }; + + system.stateVersion = "25.11"; + }; + + testScript = '' + machine.wait_for_unit("multi-user.target") + + machine.succeed("su -- danklinux -c 'test -f ~/.config/niri/config.kdl'") + machine.succeed("su -- danklinux -c 'grep -F \"include \\\"dms/binds.kdl\\\"\" ~/.config/niri/config.kdl'") + machine.succeed("su -- danklinux -c 'grep -F \"include \\\"hm.kdl\\\"\" ~/.config/niri/config.kdl'") + machine.succeed("su -- danklinux -c 'grep -F \"spawn-at-startup\" ~/.config/niri/hm.kdl'") + machine.succeed("su -- danklinux -c 'grep -F \"\\\"dms\\\" \\\"run\\\"\" ~/.config/niri/hm.kdl'") + ''; +} diff --git a/distro/nix/tests/nixos-module.nix b/distro/nix/tests/nixos-module.nix new file mode 100644 index 00000000..083cafd7 --- /dev/null +++ b/distro/nix/tests/nixos-module.nix @@ -0,0 +1,47 @@ +{ + self, + pkgs, + ... +}: +pkgs.testers.runNixOSTest { + name = "dms-nixos-module"; + + nodes.machine = { + imports = [ + self.nixosModules.dank-material-shell + ]; + + users.users.danklinux = { + isNormalUser = true; + extraGroups = [ "wheel" ]; + }; + + programs.dank-material-shell = { + enable = true; + systemd.enable = true; + plugins = { + TestPlugin = { + src = pkgs.emptyDirectory; + }; + }; + }; + + system.stateVersion = "25.11"; + }; + + testScript = '' + import json + + machine.wait_for_unit("multi-user.target") + + machine.succeed("command -v dms") + machine.succeed("command -v quickshell") + machine.succeed("su -- danklinux -c 'dms --help >/dev/null'") + machine.succeed("test -d /etc/xdg/quickshell/dms-plugins") + machine.succeed("test -f /run/current-system/sw/lib/systemd/user/dms.service") + + payload = json.loads(machine.succeed("su -- danklinux -c 'dms doctor --json'")) + t.assertIn("summary", payload) + t.assertIsInstance(payload.get("results"), list) + ''; +} diff --git a/distro/nix/tests/nixos-service-start-module.nix b/distro/nix/tests/nixos-service-start-module.nix new file mode 100644 index 00000000..a9b8a70b --- /dev/null +++ b/distro/nix/tests/nixos-service-start-module.nix @@ -0,0 +1,48 @@ +{ + self, + pkgs, + ... +}: +let + fakeDms = pkgs.writeShellScriptBin "dms" '' + printf '%s\n' "$@" > /tmp/dms-service-args + exec ${pkgs.coreutils}/bin/sleep 300 + ''; +in +pkgs.testers.runNixOSTest { + name = "dms-nixos-service-start-module"; + + nodes.machine = { + imports = [ + self.nixosModules.dank-material-shell + ]; + + users.users.danklinux = { + isNormalUser = true; + linger = true; + extraGroups = [ "wheel" ]; + }; + + programs.dank-material-shell = { + enable = true; + package = fakeDms; + systemd = { + enable = true; + target = "default.target"; + }; + }; + + system.stateVersion = "25.11"; + }; + + testScript = '' + machine.wait_for_unit("multi-user.target") + machine.wait_for_unit("user@1000.service") + + machine.succeed("systemctl --machine=danklinux@ --user start dms.service") + machine.wait_until_succeeds("systemctl --machine=danklinux@ --user is-active dms.service") + machine.wait_until_succeeds("test -f /tmp/dms-service-args") + machine.succeed("grep -Fx run /tmp/dms-service-args") + machine.succeed("grep -Fx -- --session /tmp/dms-service-args") + ''; +} diff --git a/flake.nix b/flake.nix index fed35232..fabd3090 100644 --- a/flake.nix +++ b/flake.nix @@ -45,6 +45,11 @@ nixpkgs.lib.genAttrs [ "aarch64-darwin" "aarch64-linux" "x86_64-darwin" "x86_64-linux" ] ( system: fn system nixpkgs.legacyPackages.${system} ); + forEachLinuxSystem = + fn: + nixpkgs.lib.genAttrs [ "aarch64-linux" "x86_64-linux" ] ( + system: fn system nixpkgs.legacyPackages.${system} + ); buildDmsPkgs = pkgs: { dms-shell = self.packages.${pkgs.stdenv.hostPlatform.system}.default; quickshell = quickshell.packages.${pkgs.stdenv.hostPlatform.system}.default; @@ -240,5 +245,16 @@ }; } ); + + nixosTests = forEachLinuxSystem ( + system: pkgs: + import ./distro/nix/tests { + inherit + self + pkgs + ; + lib = pkgs.lib; + } + ); }; }