I’m pretty sure I’m missing something fundamental here. Why does this standalone flake nix -L build
properly
# flake.nix
{
description = "example";
inputs = {
impermanence.url = "github:nix-community/impermanence";
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
};
outputs = { self, nixpkgs, ... }@inputs: {
packages.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.testers.runNixOSTest {
name = "test";
nodes."vm" = {
imports = [
inputs.impermanence.nixosModules.impermanence
];
};
testScript = ''
# testing here
'';
};
};
}
But if I try to modularize the runNixOSTest like so:
# flake.nix
{
description = "example";
inputs = {
impermanence.url = "github:nix-community/impermanence";
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
};
outputs = { self, nixpkgs, ... }@inputs: {
packages.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.testers.runNixOSTest ./test.nix;
};
}
# test.nix
{
name = "test";
nodes."vm" = { inputs, ...}: {
imports = [
inputs.impermanence.nixosModules.impermanence # infinite recursion
];
};
testScript = ''
# testing here
'';
}
I get an infinite recursion at inputs
? Probably something basic on my end, appreciate the help.
First thing I see is you didn’t pass inputs
into the test.nix
file.
That would then cause {inputs, ...}:
to not really make sense (because the module system doesn’t know where to get inputs
from and it infrecs on _module.args
.
I’m not familiar with the testers but does replacing ./test.nix
with import ./test.nix { inherit inputs; }
, and then putting {inputs}:
at the top of test.nix
work?
EDIT: and remove the {inputs, ...}:
bit, since I don’t see a simple way to provide that module arg here. _module.args = { inherit inputs; };
wouldn’t work because that’s a different infrec because you’ll use inputs
within imports
.
1 Like
In addition to @waffle8946 recommendation, you can also inline everything in flake like so.
When I do that, nix -L build
complains about a <<potential inifinite recursion>>
. I’m not sure what to make of that but the short exception is:
error:
… while evaluating the attribute 'config.result'
at /nix/store/fz4h8yz3qr83p6cfhisgj02knjqg6nxs-source/lib/modules.nix:334:9:
333| options = checked options;
334| config = checked (removeAttrs config [ "_module" ]);
| ^
335| _module = checked (config._module);
… while calling the 'seq' builtin
at /nix/store/fz4h8yz3qr83p6cfhisgj02knjqg6nxs-source/lib/modules.nix:334:18:
333| options = checked options;
334| config = checked (removeAttrs config [ "_module" ]);
| ^
335| _module = checked (config._module);
(stack trace truncated; use '--show-trace' to show the full, detailed trace)
error: cannot coerce a set to a string: { config = «potential infinite recursion»; lib = { __unfix__ = «lambda @ /nix/store/fz4h8yz3qr83p6cfhisgj02knjqg6nxs-source/lib/fixed-points.nix:447:7»; add = «thunk»; addContextFrom = «thunk»; addErrorContext = «thunk»; addMetaAttrs = «thunk»; all = «thunk»; allUnique = «thunk»; and = «thunk»; any = «thunk»; «438 attributes elided» }; «2 attributes elided» }
Is this maybe a round about way of saying the module system can’t find config?
Is in-lining runNixOSTest entirely inside flake.nix commonplace? If you have a large amount of tests, this would clutter the flake and seemingly conflict with the separation between flake boilerplate definitions and modules.
Is in-lining runNixOSTest entirely inside flake.nix commonplace?
That’s not the only way of doing it. It’s easy for a having a couple of checks or so, or for scratch purposes – you get inputs reference very quickly this way. Imports and separate files work well too.
These code examples work(e.g. nix run -L .\#checks.x86_64-linux.test.driverInteractive
) and are equivalent:
{
inputs = {
impermanence.url = "github:nix-community/impermanence";
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
};
outputs =
inputs@{ nixpkgs, ... }:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in
{
# Runnable via
# ❯ nix run -L .\#checks.x86_64-linux.test.driverInteractive
checks.${system}.test = pkgs.testers.runNixOSTest {
name = "test";
nodes.machine1 =
_: # { config, pkgs, ... }:
{
services.getty.autologinUser = "root";
imports = [
inputs.impermanence.nixosModules.impermanence
];
};
# If developing a proper test script, see
# https://nixos.org/manual/nixos/stable/#ssec-machine-objects
testScript = "start_all()";
};
};
}
or two files;
# flake.nix
{
inputs = {
impermanence.url = "github:nix-community/impermanence";
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
};
outputs =
inputs@{ nixpkgs, ... }:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in
{
# Runnable via
# ❯ nix run -L .\#checks.x86_64-linux.test.driverInteractive
checks.${system}.test = pkgs.testers.runNixOSTest (import ./test.nix { inherit inputs; });
};
}
# test.nix
{ inputs, ... }:
{
name = "test";
nodes.machine1 =
_: # { config, pkgs, ... }:
{
services.getty.autologinUser = "root";
imports = [
inputs.impermanence.nixosModules.impermanence
];
};
# If developing a proper test script, see
# https://nixos.org/manual/nixos/stable/#ssec-machine-objects
testScript = "start_all()";
}
1 Like
Gotcha, in my case I was missing the ( )
so the import ./test.nix { inherit inputs; }
wasn’t working correctly. Putting the runNixOSTest under checks.${system}.<name>
is really nice as well. Thank you for your help!
Yeah, import
needs to be applied first – that’s what ()
does.
This could also be the case when using lib.modules.importApply
would had produced a better error (though it still needs to be applied first):
{
inputs = {
impermanence.url = "github:nix-community/impermanence";
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
};
outputs =
inputs@{ nixpkgs, ... }:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in
{
# Runnable via
# ❯ nix run -L .\#checks.x86_64-linux.test.driverInteractive
checks.${system}.test = pkgs.testers.runNixOSTest pkgs.lib.modules.importApply ./test.nix {
inherit inputs;
};
};
}
Produces:
error: module the argument that was passed to pkgs.runNixOSTest (:anon-12:anon-1:anon-1:anon-1) does not look like a module.