ALPHA: wire is a tool to deploy nixos systems
wire.althaea.zone/
1# SPDX-License-Identifier: AGPL-3.0-or-later
2# Copyright 2024-2025 wire Contributors
3
4{
5 self,
6 config,
7 lib,
8 inputs,
9 ...
10}:
11let
12 inherit (lib)
13 mkOption
14 mapAttrsToList
15 flatten
16 cartesianProduct
17 ;
18 inherit (lib.types)
19 submodule
20 lines
21 attrsOf
22 anything
23 lazyAttrsOf
24 ;
25 cfg = config.wire.testing;
26
27 stripTyping =
28 value:
29 let
30 split = builtins.split "(from typing import TYPE_CHECKING|# typing-end)" value;
31 in
32 (builtins.elemAt split 0) + (builtins.elemAt split 4);
33in
34{
35 imports = [
36 ./suite/test_remote_deploy
37 ./suite/test_local_deploy
38 ./suite/test_keys
39 ./suite/test_stdin
40 ];
41 options.wire.testing = mkOption {
42 type = attrsOf (
43 submodule (
44 { name, ... }:
45 {
46 options = {
47 nodes = mkOption {
48 type = lazyAttrsOf anything;
49 };
50 testScript = mkOption {
51 type = lines;
52 default = '''';
53 description = "test script for runNixOSTest";
54 apply = stripTyping;
55 };
56 testDir = mkOption {
57 default = "${self}/tests/nix/suite/${name}";
58 readOnly = true;
59 };
60 };
61 }
62 )
63 );
64 description = "A set of test cases for wire VM testing suite";
65 };
66
67 config.perSystem =
68 {
69 pkgs,
70 self',
71 inputs',
72 ...
73 }:
74 let
75 nixNixpkgsCombos = cartesianProduct {
76 nixpkgs = [
77 inputs'.nixpkgs
78 inputs'.nixpkgs_current_stable
79 # inputs'.nixpkgs_prev_stable
80 ];
81 # TODO: Update once #126 is solved.
82 nix = [
83 # "nix"
84 "lix"
85 ];
86 testName = builtins.attrNames cfg;
87 };
88 mkTest =
89 {
90 testName,
91 opts,
92 nixpkgs,
93 }:
94 let
95 # TODO: Update once #126 is solved.
96 nixPackage = nixpkgs.legacyPackages.lix;
97 sanitiseName =
98 str: lib.strings.sanitizeDerivationName (builtins.replaceStrings [ "." ] [ "_" ] str);
99 identifier = sanitiseName "${nixpkgs.legacyPackages.lib.trivial.release}-${nixPackage.name}";
100 path = "tests/nix/suite/${testName}";
101
102 flakeDirFileset = lib.fileset.toSource {
103 root = ../..;
104 fileset = lib.fileset.union ./. (
105 lib.fileset.fileFilter (file: (file.hasExt "nix") || (file.hasExt "lock")) ../..
106 );
107 };
108
109 injectedFlakeDir = pkgs.runCommand "injected-flake-dir" { } ''
110 cp -r ${flakeDirFileset} $out
111 chmod -R +w $out
112 substituteInPlace $out/${path}/hive.nix --replace-fail @IDENT@ ${identifier}
113 '';
114 in
115 rec {
116 name = "vm-${testName}-${identifier}";
117 value = pkgs.testers.runNixOSTest {
118 inherit (opts) nodes;
119 inherit name;
120 defaults =
121 {
122 pkgs,
123 ...
124 }:
125 let
126 hive = builtins.scopedImport {
127 __nixPath = _b: null;
128 __findFile = _path: name: if name == "nixpkgs" then pkgs.path else throw "oops!!";
129 } "${injectedFlakeDir}/${path}/hive.nix";
130 nodes = mapAttrsToList (_: val: val.config.system.build.toplevel.drvPath) hive.nodes;
131 # fetch **all** dependencies of a flake
132 # it's called fetchLayer because my naming skills are awful
133 fetchLayer =
134 input:
135 let
136 subLayers = if input ? inputs then map fetchLayer (builtins.attrValues input.inputs) else [ ];
137 in
138 [
139 input.outPath
140 ]
141 ++ subLayers;
142 in
143 {
144 imports = [ ./test-opts.nix ];
145 nix = {
146 nixPath = [ "nixpkgs=${pkgs.path}" ];
147 settings.substituters = lib.mkForce [ ];
148 package = nixPackage;
149 };
150
151 environment.systemPackages = [ pkgs.ripgrep ];
152 environment.variables.XDG_RUNTIME_DIR = "/tmp";
153 virtualisation.memorySize = 4096;
154 virtualisation.additionalPaths = flatten [
155 injectedFlakeDir
156 nodes
157 (mapAttrsToList (_: fetchLayer) inputs)
158 ];
159 };
160 node.specialArgs = {
161 testName = name;
162 snakeOil = import "${pkgs.path}/nixos/tests/ssh-keys.nix" pkgs;
163 inherit (opts) testDir;
164 inherit (self'.packages) wire-small-dev;
165 };
166 # NOTE: there is surely a better way of doing this in a more
167 # "controlled" manner, but until a need is asked for, this will remain
168 # as is.
169 testScript = ''
170 start_all()
171
172 TEST_DIR="${injectedFlakeDir}/${path}"
173
174 ${stripTyping (builtins.readFile ./tools/__init__.py)}
175 ''
176 + lib.concatStringsSep "\n" (mapAttrsToList (_: value: value._wire.testScript) value.nodes)
177 + opts.testScript;
178 };
179 };
180 in
181 {
182 checks = builtins.listToAttrs (
183 builtins.map (
184 {
185 nixpkgs,
186 testName,
187 ...
188 }:
189 let
190 opts = cfg.${testName};
191 in
192 mkTest {
193 inherit
194 testName
195 opts
196 nixpkgs
197 ;
198 }
199 ) nixNixpkgsCombos
200 );
201 };
202}