Comments (4)
I think I could make something like this work. Needs some additions, possibly changes to the variants module, which can be moved to flake-parts.flakeModules.variants
.
Here's how it may look when nixosConfigurations.*
decides the nixpkgs input. It should also be possible for the module to decide the variant, which will look similar and can hopefully be combined.
{
inputs.nixpkgs = ...; # default nixpkgs
inputs.nixpkgs-foo = ...; # alternate nixpkgs
outputs = inputs@{ flake-parts, ... }: flake-parts.lib.mkFlake { inherit inputs; } ({ config, inputs, moduleWithSystem, fromVariant, ... }: {
# declare override
variants.foo.inputs.nixpkgs = lib.mkForce inputs.nixpkgs-foo;
# define as usual with inputs.nixpkgs, implicitly via perSystem, moduleWithSystem, etc,
# but avoid `self`, `self'`; use `config` directly
flake.nixosModules.bar = ...;
flake.nixosConfigurations.foohost = fromVariant "foo" (c: c.flake.nixosConfigurations.foohost) (
inputs.nixpkgs.lib.nixosSystem {
modules = [ ./foohost.nix config.nixosModules.bar ];
}
);
});
}
from flake-parts.
I hacked together a quick PoC of something like this, with my limited understanding of the module system. It looks like you could potentially make a pretty nice API, but avoiding infinite recursion is a little tricky with extendModules
.
I'll post an example below. Mind you this is just quickly hacked together and not well thought out. It has problems like not being able to call withVariant.<variant>
multiple times and prefixing the flake outputs like that is really sketchy. But I imagine most of those could be fixed.
Example flake:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs-22-11.url = "github:NixOS/nixpkgs/nixos-22.11";
};
outputs = inputs@{ self, flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } ({ lib, ... }: {
systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
imports = [
./variants.nix
];
variants.nixpkgs-22-11.inputs.nixpkgs = inputs.nixpkgs-22-11;
variants.nixpkgs-unstable.inputs.nixpkgs = inputs.nixpkgs;
perSystem = { config, self', inputs', pkgs, system, ... }: {
packages = {
version = pkgs.writeShellScriptBin "version" "echo ${pkgs.lib.version}";
};
};
# define `flake.nixosConfigurations.a`, executed in the context of `variants.nixpkgs-unstable`
withVariant.nixpkgs-unstable = { withSystem, ... }: {
flake.nixosConfigurations.a = withSystem "x86_64-linux" ({ pkgs, config, ... }:
pkgs.nixos {
boot.isContainer = true;
environment.systemPackages = [ config.packages.version ];
system.stateVersion = "22.11";
}
);
};
# define `flake.nixosConfigurations.b`, executed in the context of `variants.nixpkgs-22-11`
withVariant.nixpkgs-22-11 = { withSystem, ... }: {
flake.nixosConfigurations.b = withSystem "x86_64-linux" ({ pkgs, config, ... }:
pkgs.nixos {
boot.isContainer = true;
environment.systemPackages = [ config.packages.version ];
system.stateVersion = "22.11";
}
);
};
});
}
Variants module:
{ config, inputs, lib, extendModules, ... }:
let
inherit (lib) mkOption types;
flakeVariants = config.variants;
in
{
options = {
withVariant = mkOption {
type = types.lazyAttrsOf types.raw;
default = { };
};
variants = mkOption {
type = types.lazyAttrsOf (types.submodule ({ config, ... }: {
options = {
inputs = mkOption {
type = types.lazyAttrsOf types.raw;
default = { };
};
configuration = mkOption {
type = (extendModules {
# Override inputs in the variant
specialArgs.inputs = inputs // config.inputs;
modules = [
# Copy `options` from root to `internal` so we can have an isolated namespace where we can safely define our config without infinite recursion
# Infinite recursion usually happens when you define both `flake.<x>` and `variants.<variant>.configuration.flake.<x>`.
({ options, ... }: { options.internal = options; })
# Tbh I'm not really sure why but `inputs'` remains stale in the extended module. There's probably a better way to fix this
({ inputs, config, ... }: {
perSystem = { system, inputs', ... }: {
_module.args.inputs' = lib.mkForce (lib.attrsets.mapAttrs (k: config.perInput system) inputs);
};
internal.perSystem = { system, inputs', ... }: {
_module.args.inputs' = lib.mkForce (lib.attrsets.mapAttrs (k: config.internal.perInput system) inputs);
};
})
];
}).type;
default = { };
};
};
}));
default = { };
};
};
config = {
perSystem = { system, ... }: {
options = {
variants = mkOption {
default = lib.mapAttrs (_: variant: variant.configuration.allSystems.${system} or (variant.configuration.perSystem system)) flakeVariants;
readOnly = true;
};
};
};
# Execute withVariant on variants, namespacing the output to `internal`.
variants = lib.mapAttrs
(name: variant: {
configuration.imports = [
(lib.setFunctionArgs (args: { internal = variant args; }) (lib.functionArgs variant))
];
}
)
config.withVariant;
# Assign variant flake ouputs from `internal` to the actual flake
flake = lib.mkMerge (lib.mapAttrsToList
(name: variant: config.variants.${name}.configuration.internal.flake)
config.withVariant);
};
}
$ nix build .#nixosConfigurations.a.config.system.build.toplevel
$ cat result/sw/bin/version
#!/nix/store/4xw8n979xpivdc46a9ndcvyhwgif00hz-bash-5.1-p16/bin/bash
echo 23.05pre-git
$ nix build .#nixosConfigurations.b.config.system.build.toplevel
$ cat result/sw/bin/version
#!/nix/store/dsd5gz46hdbdk2rfdimqddhq6m8m8fqs-bash-5.1-p16/bin/bash
echo 22.11pre-git
from flake-parts.
Yeah, that looks super convenient! This way you can parameterize over multiple inputs (e.g. it might be useful for home-manager as well). And you can still decide whether you want the flake version (through self'
) or the variant version (through config
) which is great!
Though I don't understand why fromVariant
would need to fetch the output it's working with (c: c.flake.nixosConfigurations.foohost
). I assumed that fromVariant
would just have to set the config
arg to point to the varient's config
. But I may be misunderstanding how flake-parts
works.
from flake-parts.
Hi everyone!
I mulled over this issue, thinking about how I'd deal with this problem myself, and this is how I solved it, just using normal features of flake-parts
. I liked how it turned out. Really, I just use an overlay, which I use to extend whatever Nixpkgs I chose in any given context:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs-22-11.url = "github:NixOS/nixpkgs/nixos-22.11";
};
outputs = inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } ({ config, withSystem, ... }: {
systems = [ "x86_64-linux" "aarch64-darwin" ];
perSystem = { pkgs, ... }:
let build = pkgs.extend config.flake.overlays.default;
in { packages = { inherit (build) version; }; };
flake.overlays.default = final: prev: {
version =
final.writeShellScriptBin "version" "echo ${final.lib.version}";
};
flake.nixosConfigurations.a = inputs.nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
({ pkgs, ... }: {
nixpkgs.overlays = [ config.flake.overlays.default ];
boot.isContainer = true;
environment.systemPackages = [ pkgs.version ];
system.stateVersion = "22.11";
})
];
};
flake.nixosConfigurations.b = inputs.nixpkgs-22-11.lib.nixosSystem {
system = "x86_64-linux";
modules = [
({ pkgs, ... }: {
nixpkgs.overlays = [ config.flake.overlays.default ];
boot.isContainer = true;
environment.systemPackages = [ pkgs.version ];
system.stateVersion = "22.11";
})
];
};
});
}
And here's proof it works (though, honestly, I think it's not a surprise):
❯ nix build .#nixosConfigurations.a.config.system.build.toplevel
❯ result/sw/bin/version
23.05pre-git
❯ nix build .#nixosConfigurations.b.config.system.build.toplevel
❯ result/sw/bin/version
22.11pre-git
Note, this technique doesn't work with the easyOverlay
module. I got an infinite recursion trying. But making an explicit overlay doesn't seem too high a tax to me.
What do you all think? Is there something above-and-beyond that still warrants the complexity of the variant solution?
from flake-parts.
Related Issues (20)
- Provide a way to easily run doc tests HOT 1
- Explicitly pass default nixpkgs to mkFlake
- Document, how to declare packages only for concrete system
- perSystem.packages cannot be used with recursive packages HOT 1
- NixOS options not 1:1 available HOT 2
- nixosConfigurations example does not invoke nixosConfigurations HOT 2
- infinite recursion when `self` assigned to option HOT 6
- Add `templates` to `flake` HOT 2
- Infinite recursion error when unknown option is set HOT 3
- nix flake show broken HOT 2
- Set `_file` and `_class` for modules defined under `flake.modules.${className}.${moduleName}` HOT 1
- How to define a custom flake attribute for a reusable module HOT 2
- Attribute set is expected to be unique HOT 2
- Generate nixosConfigurations HOT 1
- Per system option with a different name? HOT 4
- Flakestry releases HOT 2
- Q: How to correctly implement a home-manager? HOT 1
- Do we really need nixpkgs in inputs instead of the lightweight nix-community/nixpkgs.lib? HOT 2
- `flake-parts` doesn't evaluate. HOT 3
- Declaring per-system functions as flake outputs HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from flake-parts.