Compare commits
148 Commits
agit-4042
...
generate-t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11bd629597 | ||
|
|
f28b5a4760 | ||
|
|
43df2c9a14 | ||
|
|
65ee951b72 | ||
|
|
b251275063 | ||
|
|
6afe4305b4 | ||
|
|
553d13b05a | ||
|
|
6ab5171b5b | ||
|
|
e152740017 | ||
|
|
bc3ba8eeff | ||
|
|
5d5bb32970 | ||
|
|
3035752d00 | ||
|
|
0799c72095 | ||
|
|
b5ef05f9ad | ||
|
|
a624cb34ee | ||
|
|
c0d4db6a8d | ||
|
|
a88f3e21a6 | ||
|
|
e248b1f551 | ||
|
|
89e8673e82 | ||
|
|
3e70e30b6b | ||
|
|
0a56f60005 | ||
|
|
9de5b97802 | ||
|
|
8789ebeb59 | ||
|
|
6f1a5286db | ||
|
|
7372063579 | ||
|
|
c3c9a8d082 | ||
|
|
d011b1ab02 | ||
|
|
b34e5b30e5 | ||
|
|
b1376f2669 | ||
|
|
79ec0f07eb | ||
|
|
7833a56723 | ||
|
|
50f6b4a095 | ||
|
|
e195a77476 | ||
|
|
0271af0dcb | ||
|
|
cfce6d9845 | ||
|
|
6929700c77 | ||
|
|
7d755d04b5 | ||
|
|
4f8421def0 | ||
|
|
0cabce6e5f | ||
|
|
264b010a89 | ||
|
|
28147fb5d5 | ||
|
|
a95d39923e | ||
|
|
acaf44e2c5 | ||
|
|
e77769bd20 | ||
|
|
11a94893fb | ||
|
|
c079d6b65f | ||
|
|
1568950410 | ||
|
|
5ae6636126 | ||
|
|
6c460db016 | ||
|
|
47b338f539 | ||
|
|
f314fbb1b9 | ||
|
|
9196de993d | ||
|
|
fcd2124936 | ||
|
|
06711a09c5 | ||
|
|
2f5793a2bc | ||
|
|
76e8cf10cb | ||
|
|
a01ece8742 | ||
|
|
a37bdd9fac | ||
|
|
0b459f64e6 | ||
|
|
e09deaf42c | ||
|
|
4ea7a10a79 | ||
|
|
1955a8171e | ||
|
|
0bb5ed923e | ||
|
|
c91b5fb3db | ||
|
|
6188583885 | ||
|
|
68ed393c87 | ||
|
|
af27f43491 | ||
|
|
16392622c5 | ||
|
|
1819243123 | ||
|
|
4204381edc | ||
|
|
4e35ab2e47 | ||
|
|
332f844ec6 | ||
|
|
a4bcaa8854 | ||
|
|
d0ae75e5cc | ||
|
|
ed7162635b | ||
|
|
aa96a11369 | ||
|
|
3e030a5199 | ||
|
|
b14a15407c | ||
|
|
d1163bc2c5 | ||
|
|
75ce3fcb84 | ||
|
|
9299c83822 | ||
|
|
2b73671f18 | ||
|
|
4421ce006e | ||
|
|
ae1a139818 | ||
|
|
c6dce03c58 | ||
|
|
b1dcef4862 | ||
|
|
6558a915ad | ||
|
|
92918719e9 | ||
|
|
81b87a6437 | ||
|
|
96cf48a8a8 | ||
|
|
6831dc9f72 | ||
|
|
60d2837ddd | ||
|
|
1bec31b371 | ||
|
|
5c6c848dea | ||
|
|
d14a5d34fd | ||
|
|
d3d2cb8723 | ||
|
|
e6a7efafcf | ||
|
|
ef5a4ab122 | ||
|
|
c8e6a6c6b8 | ||
|
|
bbebf67eb0 | ||
|
|
724aa17faa | ||
|
|
9fcbb6d688 | ||
|
|
b53975684e | ||
|
|
a1b1f3e9de | ||
|
|
d4a4f61f74 | ||
|
|
e79b926566 | ||
|
|
d85277a077 | ||
|
|
29a5fbed53 | ||
|
|
e53a490edc | ||
|
|
48ea1c757f | ||
|
|
f81c2254e1 | ||
|
|
03fe06285b | ||
|
|
b476eb2f92 | ||
|
|
ae73428ba5 | ||
|
|
ca17fb0ee8 | ||
|
|
f288b8c1ef | ||
|
|
62199e5ec9 | ||
|
|
add15a1a3e | ||
|
|
5a14bd3993 | ||
|
|
eb26ccaed0 | ||
|
|
1b6a3ba335 | ||
|
|
18d45da9d6 | ||
|
|
b26aad3619 | ||
|
|
cb69eea68f | ||
|
|
7eb90acfc4 | ||
|
|
26a2b45c74 | ||
|
|
5b04cfc06a | ||
|
|
e0b7b3329a | ||
|
|
afc001cc54 | ||
|
|
a6f94987f9 | ||
|
|
f0d8974d03 | ||
|
|
5dc80e43cd | ||
|
|
247eb46b5e | ||
|
|
fa843569c7 | ||
|
|
c37651f2fe | ||
|
|
046d13bd50 | ||
|
|
c3a631de7e | ||
|
|
b3c04ccd53 | ||
|
|
217f55adec | ||
|
|
c4b3b26fa6 | ||
|
|
f1b886f04c | ||
|
|
c65bd82e98 | ||
|
|
c4ce1e7962 | ||
|
|
6ff64f7e0a | ||
|
|
df5de44ba3 | ||
|
|
36e2f25b57 | ||
|
|
2ed5d29c89 | ||
|
|
4586b0d17d |
@@ -22,7 +22,7 @@
|
||||
dependencies = [
|
||||
self
|
||||
pkgs.stdenv.drvPath
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-backup.config.system.clan.deployment.file
|
||||
self.clan.clanInternals.machines.${pkgs.hostPlatform.system}.test-backup.config.system.clan.deployment.file
|
||||
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
||||
in
|
||||
@@ -162,7 +162,7 @@
|
||||
]
|
||||
++
|
||||
# import the inventory generated nixosModules
|
||||
self.clanInternals.inventoryClass.machines.test-backup.machineImports;
|
||||
self.clan.clanInternals.inventoryClass.machines.test-backup.machineImports;
|
||||
clan.core.settings.directory = ./.;
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ nixosLib.runTest (
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
clan-core.modules.nixosVmTest.clanTest
|
||||
clan-core.modules.nixosTest.clanTest
|
||||
];
|
||||
|
||||
hostPkgs = pkgs;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ fetchgit }:
|
||||
fetchgit {
|
||||
url = "https://git.clan.lol/clan/clan-core.git";
|
||||
rev = "13a9b1719835ef4510e4adb6941ddfe9a91d41cb";
|
||||
sha256 = "sha256-M+pLnpuX+vIsxTFtbBZaNA1OwGQPeSbsMbTiDl1t4vY=";
|
||||
rev = "28131afbbcd379a8ff04c79c66c670ef655ed889";
|
||||
sha256 = "1294cwjlnc341fl6zbggn4rgq8z33gqkcyggjfvk9cf7zdgygrf6";
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ nixosLib.runTest (
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
clan-core.modules.nixosVmTest.clanTest
|
||||
clan-core.modules.nixosTest.clanTest
|
||||
];
|
||||
|
||||
hostPkgs = pkgs;
|
||||
|
||||
@@ -9,7 +9,7 @@ nixosLib.runTest (
|
||||
{ hostPkgs, config, ... }:
|
||||
{
|
||||
imports = [
|
||||
clan-core.modules.nixosVmTest.clanTest
|
||||
clan-core.modules.nixosTest.clanTest
|
||||
];
|
||||
|
||||
hostPkgs = pkgs;
|
||||
|
||||
@@ -8,7 +8,7 @@ nixosLib.runTest (
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
clan-core.modules.nixosVmTest.clanTest
|
||||
clan-core.modules.nixosTest.clanTest
|
||||
];
|
||||
|
||||
hostPkgs = pkgs;
|
||||
|
||||
@@ -9,6 +9,7 @@ in
|
||||
{
|
||||
imports = filter pathExists [
|
||||
./backups/flake-module.nix
|
||||
../nixosModules/clanCore/machine-id/tests/flake-module.nix
|
||||
./devshell/flake-module.nix
|
||||
./flash/flake-module.nix
|
||||
./impure/flake-module.nix
|
||||
@@ -48,6 +49,8 @@ in
|
||||
zt-tcp-relay = self.clanLib.test.containerTest ./zt-tcp-relay nixosTestArgs;
|
||||
matrix-synapse = self.clanLib.test.containerTest ./matrix-synapse nixosTestArgs;
|
||||
postgresql = self.clanLib.test.containerTest ./postgresql nixosTestArgs;
|
||||
user-firewall-iptables = self.clanLib.test.containerTest ./user-firewall/iptables.nix nixosTestArgs;
|
||||
user-firewall-nftables = self.clanLib.test.containerTest ./user-firewall/nftables.nix nixosTestArgs;
|
||||
|
||||
dummy-inventory-test = import ./dummy-inventory-test nixosTestArgs;
|
||||
dummy-inventory-test-from-flake = import ./dummy-inventory-test-from-flake nixosTestArgs;
|
||||
@@ -80,7 +83,7 @@ in
|
||||
_n: m:
|
||||
let
|
||||
schema =
|
||||
(self.clanLib.inventory.evalClanService {
|
||||
(self.clanLib.evalService {
|
||||
modules = [ m ];
|
||||
prefix = [
|
||||
"checks"
|
||||
|
||||
@@ -8,9 +8,9 @@ let
|
||||
{ modulesPath, pkgs, ... }:
|
||||
let
|
||||
dependencies = [
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.toplevel
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.diskoScript
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.clan.deployment.file
|
||||
self.clan.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.toplevel
|
||||
self.clan.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.diskoScript
|
||||
self.clan.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.clan.deployment.file
|
||||
pkgs.stdenv.drvPath
|
||||
pkgs.bash.drvPath
|
||||
pkgs.nixos-anywhere
|
||||
|
||||
@@ -8,7 +8,7 @@ nixosLib.runTest (
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
clan-core.modules.nixosVmTest.clanTest
|
||||
clan-core.modules.nixosTest.clanTest
|
||||
];
|
||||
|
||||
hostPkgs = pkgs;
|
||||
|
||||
@@ -9,7 +9,7 @@ nixosLib.runTest (
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
clan-core.modules.nixosVmTest.clanTest
|
||||
clan-core.modules.nixosTest.clanTest
|
||||
];
|
||||
|
||||
hostPkgs = pkgs;
|
||||
|
||||
100
checks/user-firewall/common.nix
Normal file
100
checks/user-firewall/common.nix
Normal file
@@ -0,0 +1,100 @@
|
||||
# Shared configuration for user firewall tests
|
||||
{ self, pkgs, ... }:
|
||||
{
|
||||
imports = [
|
||||
self.nixosModules.user-firewall
|
||||
];
|
||||
|
||||
networking.firewall.enable = true;
|
||||
|
||||
# Configure the user firewall module
|
||||
# Test with default allowedInterfaces (which includes wg*)
|
||||
networking.user-firewall = {
|
||||
# Use defaults for allowedInterfaces to test that wg* is included by default
|
||||
exemptUsers = [
|
||||
"root"
|
||||
"alice"
|
||||
];
|
||||
};
|
||||
|
||||
# Create test users
|
||||
users.users = {
|
||||
alice = {
|
||||
isNormalUser = true;
|
||||
uid = 1001;
|
||||
initialPassword = "test";
|
||||
};
|
||||
|
||||
bob = {
|
||||
isNormalUser = true;
|
||||
uid = 1002;
|
||||
initialPassword = "test";
|
||||
};
|
||||
};
|
||||
|
||||
# Add tools for testing
|
||||
environment.systemPackages = with pkgs; [
|
||||
curl
|
||||
netcat
|
||||
iproute2
|
||||
];
|
||||
|
||||
# Add a local web server for testing
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
virtualHosts = {
|
||||
"localhost" = {
|
||||
listen = [
|
||||
{
|
||||
addr = "127.0.0.1";
|
||||
port = 8080;
|
||||
}
|
||||
];
|
||||
locations."/" = {
|
||||
return = "200 'test server response'";
|
||||
extraConfig = "add_header Content-Type text/plain;";
|
||||
};
|
||||
};
|
||||
"wg0-test" = {
|
||||
listen = [
|
||||
{
|
||||
addr = "10.100.0.2";
|
||||
port = 8081;
|
||||
}
|
||||
{
|
||||
addr = "[fd00::2]";
|
||||
port = 8081;
|
||||
}
|
||||
];
|
||||
locations."/" = {
|
||||
return = "200 'wg0 interface test response'";
|
||||
extraConfig = "add_header Content-Type text/plain;";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Create a dummy interface to test allowed interface patterns
|
||||
systemd.services.setup-wg0-interface = {
|
||||
description = "Setup wg0 dummy interface";
|
||||
after = [ "network-pre.target" ];
|
||||
before = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script = ''
|
||||
${pkgs.iproute2}/bin/ip link add wg0 type dummy || true
|
||||
${pkgs.iproute2}/bin/ip addr add 10.100.0.2/24 dev wg0 || true
|
||||
${pkgs.iproute2}/bin/ip addr add fd00::2/64 dev wg0 || true
|
||||
${pkgs.iproute2}/bin/ip link set wg0 up || true
|
||||
'';
|
||||
};
|
||||
|
||||
# Make nginx wait for the wg0 interface
|
||||
systemd.services.nginx = {
|
||||
after = [ "setup-wg0-interface.service" ];
|
||||
requires = [ "setup-wg0-interface.service" ];
|
||||
};
|
||||
}
|
||||
82
checks/user-firewall/iptables.nix
Normal file
82
checks/user-firewall/iptables.nix
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
name = "user-firewall-iptables";
|
||||
|
||||
nodes = {
|
||||
router =
|
||||
{ ... }:
|
||||
{
|
||||
imports = [ ./router.nix ];
|
||||
};
|
||||
|
||||
machine =
|
||||
{ ... }:
|
||||
{
|
||||
imports = [ ./common.nix ];
|
||||
|
||||
# Force iptables backend
|
||||
networking.nftables.enable = false;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
router.wait_for_unit("multi-user.target")
|
||||
router.wait_for_unit("nginx.service")
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.wait_for_unit("nginx.service")
|
||||
|
||||
# Get router IPs (both IPv4 and IPv6)
|
||||
router_ip = router.succeed("ip -4 addr show eth1 | grep -oP '(?<=inet\\s)\\d+(\\.\\d+){3}'").strip()
|
||||
router_ip6 = router.succeed("ip -6 addr show eth1 | grep -oP '(?<=inet6\\s)[0-9a-f:]+' | grep -v '^fe80' | head -1").strip()
|
||||
print(f"Router IPv4: {router_ip}")
|
||||
print(f"Router IPv6: {router_ip6}")
|
||||
|
||||
# Test firewall restart
|
||||
machine.succeed("systemctl restart firewall")
|
||||
machine.wait_for_unit("firewall.service")
|
||||
|
||||
# Verify rules are loaded
|
||||
machine.succeed("iptables -L user-firewall-output >&2")
|
||||
|
||||
# Test alice (exempt user) - should succeed both locally and to router
|
||||
machine.wait_until_succeeds("runuser -u alice -- curl -s http://127.0.0.1:8080")
|
||||
machine.succeed(f"runuser -u alice -- curl -s http://{router_ip}")
|
||||
machine.succeed(f"runuser -u alice -- curl -s http://[{router_ip6}]")
|
||||
|
||||
# Test bob (restricted user) - localhost should work, external should fail
|
||||
machine.succeed("runuser -u bob -- curl -s http://127.0.0.1:8080")
|
||||
# This should be blocked by firewall - IPv4
|
||||
result = machine.succeed(f"runuser -u bob -- curl -s --connect-timeout 2 http://{router_ip} 2>&1 || echo 'EXIT_CODE='$?")
|
||||
assert "EXIT_CODE=7" in result, f"Bob should be blocked from external IPv4 access (expected EXIT_CODE=7) but got: {result}"
|
||||
# This should be blocked by firewall - IPv6
|
||||
result6 = machine.succeed(f"runuser -u bob -- curl -s --connect-timeout 2 http://[{router_ip6}] 2>&1 || echo 'EXIT_CODE='$?")
|
||||
assert "EXIT_CODE=7" in result6, f"Bob should be blocked from external IPv6 access (expected EXIT_CODE=7) but got: {result6}"
|
||||
|
||||
# Verify the rules are actually present for both IPv4 and IPv6
|
||||
rules4 = machine.succeed("iptables -L user-firewall-output -n -v")
|
||||
assert "REJECT" in rules4, "REJECT rule not found in iptables"
|
||||
rules6 = machine.succeed("ip6tables -L user-firewall-output -n -v")
|
||||
assert "REJECT" in rules6, "REJECT rule not found in ip6tables"
|
||||
|
||||
# Wait for the dummy interface to be created
|
||||
machine.wait_for_unit("setup-wg0-interface.service")
|
||||
machine.wait_for_unit("nginx.service")
|
||||
machine.wait_for_open_port(8081, "10.100.0.2")
|
||||
|
||||
# Check that wg0 interface exists
|
||||
machine.succeed("ip link show wg0")
|
||||
machine.succeed("ip addr show wg0")
|
||||
|
||||
# The key test: users should be able to connect via wg0 interface
|
||||
# For alice (exempt user) - should work
|
||||
machine.succeed("runuser -u alice -- curl -s --interface wg0 http://10.100.0.2:8081/")
|
||||
machine.succeed("runuser -u alice -- curl -s --interface wg0 http://[fd00::2]:8081/") # IPv6 test
|
||||
|
||||
# For bob (restricted user) - should also work because wg* is in default allowedInterfaces
|
||||
machine.succeed("runuser -u bob -- curl -s --interface wg0 http://10.100.0.2:8081/")
|
||||
machine.succeed("runuser -u bob -- curl -s --interface wg0 http://[fd00::2]:8081/") # IPv6 test
|
||||
|
||||
# Verify that wg* interfaces are allowed in the firewall rules
|
||||
machine.succeed("iptables -L user-firewall-output -n -v | grep -E 'wg0|wg\\+' >&2")
|
||||
'';
|
||||
}
|
||||
77
checks/user-firewall/nftables.nix
Normal file
77
checks/user-firewall/nftables.nix
Normal file
@@ -0,0 +1,77 @@
|
||||
{
|
||||
name = "user-firewall-nftables";
|
||||
|
||||
nodes = {
|
||||
router = {
|
||||
imports = [ ./router.nix ];
|
||||
};
|
||||
|
||||
machine = {
|
||||
imports = [ ./common.nix ];
|
||||
|
||||
# Force nftables backend
|
||||
networking.nftables.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
router.wait_for_unit("multi-user.target")
|
||||
router.wait_for_unit("nginx.service")
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.wait_for_unit("nginx.service")
|
||||
|
||||
# Get router IPs (both IPv4 and IPv6)
|
||||
router_ip = router.succeed("ip -4 addr show eth1 | grep -oP '(?<=inet\\s)\\d+(\\.\\d+){3}'").strip()
|
||||
router_ip6 = router.succeed("ip -6 addr show eth1 | grep -oP '(?<=inet6\\s)[0-9a-f:]+' | grep -v '^fe80' | head -1").strip()
|
||||
print(f"Router IPv4: {router_ip}")
|
||||
print(f"Router IPv6: {router_ip6}")
|
||||
|
||||
# Test nftables restart
|
||||
machine.succeed("systemctl restart nftables")
|
||||
machine.wait_for_unit("nftables.service")
|
||||
|
||||
# Verify rules are loaded
|
||||
machine.succeed("nft list table inet user-firewall >&2")
|
||||
|
||||
# Test alice (exempt user) - should succeed both locally and to router
|
||||
machine.wait_until_succeeds("runuser -u alice -- curl -s http://127.0.0.1:8080")
|
||||
machine.succeed(f"runuser -u alice -- curl -s http://{router_ip}")
|
||||
machine.succeed(f"runuser -u alice -- curl -s http://[{router_ip6}]")
|
||||
|
||||
# Test bob (restricted user) - localhost should work, external should fail
|
||||
machine.succeed("runuser -u bob -- curl -s http://127.0.0.1:8080")
|
||||
# This should be blocked by firewall - IPv4
|
||||
result = machine.succeed(f"runuser -u bob -- curl -s --connect-timeout 2 http://{router_ip} 2>&1 || echo 'EXIT_CODE='$?")
|
||||
assert "EXIT_CODE=7" in result, f"Bob should be blocked from external IPv4 access (expected EXIT_CODE=7) but got: {result}"
|
||||
# This should be blocked by firewall - IPv6
|
||||
result6 = machine.succeed(f"runuser -u bob -- curl -s --connect-timeout 2 http://[{router_ip6}] 2>&1 || echo 'EXIT_CODE='$?")
|
||||
assert "EXIT_CODE=7" in result6, f"Bob should be blocked from external IPv6 access (expected EXIT_CODE=7) but got: {result6}"
|
||||
|
||||
# Verify the rules are actually present
|
||||
rules = machine.succeed("nft list table inet user-firewall")
|
||||
assert 'meta skuid 1002' in rules and 'reject' in rules, f"Reject rule for bob (uid 1002) not found in nftables. Actual rules:\n{rules}"
|
||||
assert "oifname" in rules, f"Interface rules not found in nftables. Actual rules:\n{rules}"
|
||||
|
||||
# Wait for the dummy interface to be created
|
||||
machine.wait_for_unit("setup-wg0-interface.service")
|
||||
machine.wait_for_unit("nginx.service")
|
||||
machine.wait_for_open_port(8081, "10.100.0.2")
|
||||
|
||||
# Check that wg0 interface exists
|
||||
machine.succeed("ip link show wg0")
|
||||
machine.succeed("ip addr show wg0")
|
||||
|
||||
# The key test: users should be able to connect via wg0 interface
|
||||
# For alice (exempt user) - should work
|
||||
machine.succeed("runuser -u alice -- curl -s --interface wg0 http://10.100.0.2:8081/")
|
||||
machine.succeed("runuser -u alice -- curl -s --interface wg0 http://[fd00::2]:8081/") # IPv6 test
|
||||
|
||||
# For bob (restricted user) - should also work because wg* is in default allowedInterfaces
|
||||
machine.succeed("runuser -u bob -- curl -s --interface wg0 http://10.100.0.2:8081/")
|
||||
machine.succeed("runuser -u bob -- curl -s --interface wg0 http://[fd00::2]:8081/") # IPv6 test
|
||||
|
||||
# Verify that wg* interfaces are allowed in the nftables rules
|
||||
rules_with_wg = machine.succeed("nft list table inet user-firewall | grep -E 'oifname.*wg' >&2")
|
||||
'';
|
||||
}
|
||||
32
checks/user-firewall/router.nix
Normal file
32
checks/user-firewall/router.nix
Normal file
@@ -0,0 +1,32 @@
|
||||
# Shared router configuration for user firewall tests
|
||||
{ ... }:
|
||||
{
|
||||
networking.firewall.enable = false;
|
||||
networking.useNetworkd = true;
|
||||
|
||||
# Simple web server to test connectivity
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
virtualHosts."router" = {
|
||||
listen = [
|
||||
{
|
||||
addr = "0.0.0.0";
|
||||
port = 80;
|
||||
}
|
||||
{
|
||||
addr = "[::]";
|
||||
port = 80;
|
||||
}
|
||||
{
|
||||
addr = "10.100.0.1";
|
||||
port = 80;
|
||||
}
|
||||
];
|
||||
locations."/" = {
|
||||
return = "200 'router response'";
|
||||
extraConfig = "add_header Content-Type text/plain;";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -18,6 +18,13 @@
|
||||
../../root-password
|
||||
];
|
||||
config = {
|
||||
|
||||
warnings = [
|
||||
"The clan.admin module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
|
||||
users.users.root.openssh.authorizedKeys.keys = builtins.attrValues config.clan.admin.allowedKeys;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,8 +15,11 @@ in
|
||||
};
|
||||
|
||||
config = {
|
||||
|
||||
warnings = [
|
||||
"The clan.auto-upgrade module is deprecated and will be removed on 2025-07-15. Please migrate to using the system.autoUpgrade NixOS option directly."
|
||||
"The clan.auto-upgrade module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
|
||||
system.autoUpgrade = {
|
||||
|
||||
@@ -90,6 +90,12 @@ in
|
||||
|
||||
config = {
|
||||
|
||||
warnings = [
|
||||
"The clan.borgbackup module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
|
||||
# Destinations
|
||||
clan.borgbackup.destinations =
|
||||
let
|
||||
|
||||
@@ -27,6 +27,13 @@ in
|
||||
};
|
||||
|
||||
config = {
|
||||
|
||||
warnings = [
|
||||
"The clan.admin module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
|
||||
services.data-mesher.initNetwork =
|
||||
let
|
||||
# for a given machine, read it's public key and remove any new lines
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
}:
|
||||
{
|
||||
warnings = [
|
||||
"The clan.deltachat module is deprecated and will be removed on 2025-07-15. Please migrate to user-maintained configuration."
|
||||
"The clan.deltachat module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
|
||||
networking.firewall.interfaces."zt+".allowedTCPPorts = [ 25 ]; # smtp with other hosts
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
{
|
||||
|
||||
config = {
|
||||
|
||||
warnings = [
|
||||
"The clan.disk-id module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
clan.core.vars.generators.disk-id = {
|
||||
files.diskId.secret = false;
|
||||
runtimeInputs = [
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
_: {
|
||||
|
||||
warnings = [
|
||||
"The clan.ergochat module is deprecated and will be removed on 2025-07-15. Please migrate to user-maintained configuration."
|
||||
"The clan.ergochat module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
|
||||
services.ergochat = {
|
||||
|
||||
@@ -23,7 +23,6 @@ in
|
||||
iwd = ./iwd;
|
||||
localbackup = ./localbackup;
|
||||
localsend = ./localsend;
|
||||
machine-id = ./machine-id;
|
||||
matrix-synapse = ./matrix-synapse;
|
||||
moonlight = ./moonlight;
|
||||
mumble = ./mumble;
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
|
||||
warnings = [
|
||||
"The clan.ergochat module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
|
||||
systemd.services.garage.serviceConfig = {
|
||||
LoadCredential = [
|
||||
"rpc_secret_path:${config.clan.core.vars.generators.garage-shared.files.rpc_secret.path}"
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
];
|
||||
config = {
|
||||
warnings = [
|
||||
"The clan.heisenbridge module is deprecated and will be removed on 2025-07-15. Please migrate to user-maintained configuration."
|
||||
"The clan.heisenbridge module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
services.heisenbridge = {
|
||||
enable = true;
|
||||
|
||||
@@ -39,7 +39,9 @@ in
|
||||
];
|
||||
config = {
|
||||
warnings = [
|
||||
"The clan.localsend module is deprecated and will be removed on 2025-07-15. Please migrate to user-maintained configuration."
|
||||
"The clan.localsend module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
|
||||
clan.core.state.localsend.folders = [
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
description = "Sets the /etc/machine-id and exposes it as a nix option"
|
||||
features = [ "inventory" ]
|
||||
---
|
||||
@@ -1,6 +0,0 @@
|
||||
# Dont import this file
|
||||
# It is only here for backwards compatibility.
|
||||
# Dont author new modules with this file.
|
||||
{
|
||||
imports = [ ./roles/default.nix ];
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
var = config.clan.core.vars.generators.machine-id.files.machineId or { };
|
||||
in
|
||||
{
|
||||
config = lib.mkMerge [
|
||||
(lib.mkIf ((var.value or null) != null) {
|
||||
assertions = [
|
||||
{
|
||||
assertion = lib.stringLength var.value == 32;
|
||||
message = "machineId must be exactly 32 characters long.";
|
||||
}
|
||||
];
|
||||
boot.kernelParams = [
|
||||
''systemd.machine_id=${var.value}''
|
||||
];
|
||||
environment.etc."machine-id" = {
|
||||
text = var.value;
|
||||
};
|
||||
})
|
||||
{
|
||||
clan.core.vars.generators.machine-id = {
|
||||
files.machineId.secret = false;
|
||||
runtimeInputs = [
|
||||
pkgs.coreutils
|
||||
pkgs.bash
|
||||
];
|
||||
script = ''
|
||||
uuid=$(bash ${./uuid4.sh})
|
||||
|
||||
# Remove the hyphens from the UUID
|
||||
uuid_no_hyphens=$(echo -n "$uuid" | tr -d '-')
|
||||
|
||||
echo -n "$uuid_no_hyphens" > "$out/machineId"
|
||||
'';
|
||||
};
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -37,8 +37,11 @@ in
|
||||
};
|
||||
|
||||
config = {
|
||||
|
||||
warnings = [
|
||||
"The clan.mumble module is deprecated and will be removed on 2025-07-15. Please migrate to user-maintained configuration."
|
||||
"The clan.mumble module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
|
||||
services.murmur = {
|
||||
|
||||
@@ -19,6 +19,12 @@
|
||||
};
|
||||
};
|
||||
|
||||
config.warnings = [
|
||||
"The clan.mycelium module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
|
||||
config.services.mycelium = {
|
||||
enable = true;
|
||||
addHostedPublicNodes = lib.mkDefault config.clan.mycelium.addHostedPublicNodes;
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
};
|
||||
};
|
||||
config = {
|
||||
|
||||
warnings = [
|
||||
"The clan.packages module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
environment.systemPackages = map (
|
||||
pName: lib.getAttrFromPath (lib.splitString "." pName) pkgs
|
||||
) config.clan.packages.packages;
|
||||
|
||||
@@ -6,6 +6,13 @@
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
warnings = [
|
||||
"The clan.root-password module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
|
||||
users.mutableUsers = false;
|
||||
users.users.root.hashedPasswordFile =
|
||||
config.clan.core.vars.generators.root-password.files.password-hash.path;
|
||||
|
||||
@@ -17,6 +17,13 @@ in
|
||||
clan.sshd.hostKeys.rsa.enable = lib.mkEnableOption "Generate RSA host key";
|
||||
};
|
||||
config = {
|
||||
|
||||
warnings = [
|
||||
"The clan.sshd module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings.PasswordAuthentication = false;
|
||||
|
||||
@@ -3,6 +3,13 @@ let
|
||||
var = config.clan.core.vars.generators.state-version.files.version or { };
|
||||
in
|
||||
{
|
||||
|
||||
warnings = [
|
||||
"The clan.state-version module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
|
||||
system.stateVersion = lib.mkDefault (lib.removeSuffix "\n" var.value);
|
||||
|
||||
clan.core.vars.generators.state-version = {
|
||||
|
||||
@@ -11,6 +11,7 @@ in
|
||||
imports = [
|
||||
../shared.nix
|
||||
];
|
||||
|
||||
clan.syncthing.introducer = lib.strings.removeSuffix "\n" (
|
||||
if builtins.pathExists introducerId then
|
||||
builtins.readFile introducerId
|
||||
|
||||
@@ -26,6 +26,13 @@ in
|
||||
};
|
||||
|
||||
config = {
|
||||
|
||||
warnings = [
|
||||
"The clan.user-password module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
|
||||
users.mutableUsers = false;
|
||||
users.users.${cfg.user} = {
|
||||
hashedPasswordFile = config.clan.core.vars.generators.user-password.files.user-password-hash.path;
|
||||
|
||||
@@ -17,6 +17,13 @@ in
|
||||
../shared.nix
|
||||
];
|
||||
config = {
|
||||
|
||||
warnings = [
|
||||
"The clan.zerotier module is deprecated and will be removed on 2025-07-15.
|
||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
||||
(https://docs.clan.lol/reference/clanServices)."
|
||||
];
|
||||
|
||||
systemd.services.zerotier-inventory-autoaccept =
|
||||
let
|
||||
machines = uniqueStrings (roles.moon.machines ++ roles.controller.machines ++ roles.peer.machines);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
let
|
||||
testFlake = clanLib.buildClan {
|
||||
# Point to the folder of the module
|
||||
# TODO: make this optional in buildClan
|
||||
# TODO: make this optional
|
||||
directory = ./..;
|
||||
|
||||
# Create some test machines
|
||||
|
||||
@@ -8,13 +8,13 @@ inventory.instances = {
|
||||
|
||||
zone1 = {
|
||||
module.name = "@clan/importer";
|
||||
roles.default.tags = [ "zone1" ];
|
||||
roles.default.tags.zone1 = {};
|
||||
roles.default.extraModules = [ "modules/zone1.nix" ];
|
||||
};
|
||||
|
||||
base = {
|
||||
module.name = "@clan/importer";
|
||||
roles.default.tags = [ "all" ];
|
||||
roles.default.tags.all = {};
|
||||
roles.default.extraModules = [ "modules/base.nix" ];
|
||||
};
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
clan.core.vars.generators."user-password-${settings.user}" = {
|
||||
|
||||
# files.user-password-hash.neededFor = "users";
|
||||
files.user-password-hash.neededFor = "users";
|
||||
files.user-password-hash.restartUnits = lib.optional (config.services.userborn.enable) "userborn.service";
|
||||
files.user-password.deploy = false;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
let
|
||||
testFlake = clanLib.buildClan {
|
||||
# Point to the folder of the module
|
||||
# TODO: make this optional in buildClan
|
||||
# TODO: make this optional
|
||||
directory = ./..;
|
||||
|
||||
# Create some test machines
|
||||
@@ -46,7 +46,7 @@ in
|
||||
inherit testFlake;
|
||||
|
||||
expr =
|
||||
testFlake.clanInternals.inventoryClass.distributedServices.importedModulesEvaluated.self-wifi.config;
|
||||
testFlake.clan.clanInternals.inventoryClass.distributedServices.importedModulesEvaluated.self-wifi.config;
|
||||
expected = 1;
|
||||
|
||||
# expr = {
|
||||
|
||||
@@ -16,7 +16,7 @@ inventory.instances = {
|
||||
The input should be named according to your flake input.
|
||||
All machines will be peers and connected to the zerotier network.
|
||||
Jon is the controller machine, which will will accept other machines into the network.
|
||||
Sara is a moon and sets the `stableEndpoint` setting with a publically reachable IP, the moon is optional.
|
||||
Sara is a moon and sets the `stableEndpoint` setting with a publicly reachable IP, the moon is optional.
|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -44,6 +44,9 @@
|
||||
# vendoring / needed for impure tests
|
||||
ln -sfT ${self'.packages.clan-cli.nixpkgs} "$PRJ_ROOT/pkgs/clan-cli/clan_lib/nixpkgs"
|
||||
ln -sfT ${inputs.nix-select} "$PRJ_ROOT/pkgs/clan-cli/clan_lib/select"
|
||||
|
||||
# Generate classes.py from schemas
|
||||
${self'.packages.classgen}/bin/classgen ${self'.legacyPackages.schemas.clan-schema-abstract}/schema.json $PRJ_ROOT/pkgs/clan-cli/clan_lib/nix_models/clan.py
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
1
docs/.gitignore
vendored
1
docs/.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
/site/reference
|
||||
/site/static
|
||||
/site/options-page
|
||||
!/site/static/extra.css
|
||||
|
||||
@@ -116,7 +116,6 @@ nav:
|
||||
- reference/clanModules/iwd.md
|
||||
- reference/clanModules/localbackup.md
|
||||
- reference/clanModules/localsend.md
|
||||
- reference/clanModules/machine-id.md
|
||||
- reference/clanModules/matrix-synapse.md
|
||||
- reference/clanModules/moonlight.md
|
||||
- reference/clanModules/mumble.md
|
||||
@@ -169,7 +168,7 @@ nav:
|
||||
- reference/clan.core/state.md
|
||||
- reference/clan.core/vars.md
|
||||
- Nix API:
|
||||
- buildClan: reference/nix-api/buildclan.md
|
||||
- clan: reference/nix-api/clan.md
|
||||
- Inventory: reference/nix-api/inventory.md
|
||||
- Glossary: reference/glossary.md
|
||||
- Decisions:
|
||||
@@ -180,6 +179,7 @@ nav:
|
||||
- 04-fetching-nix-from-python: decisions/04-fetching-nix-from-python.md
|
||||
- 05-deployment-parameters: decisions/05-deployment-parameters.md
|
||||
- Template: decisions/_template.md
|
||||
- Options: options.md
|
||||
|
||||
docs_dir: site
|
||||
site_dir: out
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
asciinema-player-css,
|
||||
roboto,
|
||||
fira-code,
|
||||
docs-options,
|
||||
...
|
||||
}:
|
||||
let
|
||||
@@ -35,6 +36,11 @@ pkgs.stdenv.mkDerivation {
|
||||
mkdir -p ./site/reference/cli
|
||||
cp -af ${module-docs}/* ./site/reference/
|
||||
cp -af ${clan-cli-docs}/* ./site/reference/cli/
|
||||
chmod -R +w ./site/reference
|
||||
echo "Generated API documentation in './site/reference/' "
|
||||
|
||||
cp -r ${docs-options} ./site/options-page
|
||||
chmod -R +w ./site/options-page
|
||||
|
||||
mkdir -p ./site/static/asciinema-player
|
||||
ln -snf ${asciinema-player-js} ./site/static/asciinema-player/asciinema-player.min.js
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{ inputs, self, ... }:
|
||||
{
|
||||
imports = [
|
||||
./options/flake-module.nix
|
||||
];
|
||||
perSystem =
|
||||
{
|
||||
config,
|
||||
@@ -94,7 +97,6 @@
|
||||
# Frontmatter format for clanModules
|
||||
export CLAN_MODULES_FRONTMATTER_DOCS=${clanModulesFrontmatter}/share/doc/nixos/options.json
|
||||
|
||||
# buildClan options
|
||||
export BUILD_CLAN_PATH=${buildClanOptions}/share/doc/nixos/options.json
|
||||
|
||||
mkdir $out
|
||||
@@ -112,19 +114,20 @@
|
||||
clanModulesViaService
|
||||
;
|
||||
};
|
||||
devShells.docs = pkgs.callPackage ./shell.nix {
|
||||
inherit (self'.packages) docs clan-cli-docs inventory-api-docs;
|
||||
inherit
|
||||
asciinema-player-js
|
||||
asciinema-player-css
|
||||
module-docs
|
||||
self'
|
||||
;
|
||||
};
|
||||
devShells.docs = self'.packages.docs.overrideAttrs (_old: {
|
||||
nativeBuildInputs =
|
||||
self'.devShells.default.nativeBuildInputs ++ self'.packages.docs.nativeBuildInputs;
|
||||
shellHook = ''
|
||||
${self'.devShells.default.shellHook}
|
||||
git_root=$(git rev-parse --show-toplevel)
|
||||
cd "$git_root"
|
||||
runPhase configurePhase
|
||||
'';
|
||||
});
|
||||
packages = {
|
||||
docs = pkgs.python3.pkgs.callPackage ./default.nix {
|
||||
clan-core = self;
|
||||
inherit (self'.packages) clan-cli-docs inventory-api-docs;
|
||||
inherit (self'.packages) clan-cli-docs docs-options inventory-api-docs;
|
||||
inherit (inputs) nixpkgs;
|
||||
inherit module-docs;
|
||||
inherit asciinema-player-js;
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
clanModulesViaService = lib.mapAttrs (
|
||||
_moduleName: moduleValue:
|
||||
let
|
||||
evaluatedService = clan-core.clanLib.inventory.evalClanService {
|
||||
evaluatedService = clan-core.clanLib.evalService {
|
||||
modules = [ moduleValue ];
|
||||
prefix = [ ];
|
||||
};
|
||||
|
||||
167
docs/nix/options/flake-module.nix
Normal file
167
docs/nix/options/flake-module.nix
Normal file
@@ -0,0 +1,167 @@
|
||||
{ self, config, ... }:
|
||||
{
|
||||
perSystem =
|
||||
{
|
||||
inputs',
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib)
|
||||
mapAttrsToList
|
||||
flip
|
||||
mapAttrs
|
||||
mkOption
|
||||
types
|
||||
splitString
|
||||
stringLength
|
||||
substring
|
||||
;
|
||||
inherit (self) clanLib;
|
||||
|
||||
serviceModules = self.clan.modules;
|
||||
|
||||
baseHref = "/options-page/";
|
||||
|
||||
getRoles =
|
||||
module:
|
||||
(clanLib.evalService {
|
||||
modules = [ module ];
|
||||
prefix = [ ];
|
||||
}).config.roles;
|
||||
|
||||
getManifest =
|
||||
module:
|
||||
(clanLib.evalService {
|
||||
modules = [ module ];
|
||||
prefix = [ ];
|
||||
}).config.manifest;
|
||||
|
||||
loadFile = file: if builtins.pathExists file then builtins.readFile file else "";
|
||||
|
||||
settingsModules =
|
||||
module: flip mapAttrs (getRoles module) (_roleName: roleConfig: roleConfig.interface);
|
||||
|
||||
# Map each letter to its capitalized version
|
||||
capitalizeChar =
|
||||
char:
|
||||
{
|
||||
a = "A";
|
||||
b = "B";
|
||||
c = "C";
|
||||
d = "D";
|
||||
e = "E";
|
||||
f = "F";
|
||||
g = "G";
|
||||
h = "H";
|
||||
i = "I";
|
||||
j = "J";
|
||||
k = "K";
|
||||
l = "L";
|
||||
m = "M";
|
||||
n = "N";
|
||||
o = "O";
|
||||
p = "P";
|
||||
q = "Q";
|
||||
r = "R";
|
||||
s = "S";
|
||||
t = "T";
|
||||
u = "U";
|
||||
v = "V";
|
||||
w = "W";
|
||||
x = "X";
|
||||
y = "Y";
|
||||
z = "Z";
|
||||
}
|
||||
.${char};
|
||||
|
||||
title =
|
||||
name:
|
||||
let
|
||||
# split by -
|
||||
parts = splitString "-" name;
|
||||
# capitalize first letter of each part
|
||||
capitalize = part: (capitalizeChar (substring 0 1 part)) + substring 1 (stringLength part) part;
|
||||
capitalizedParts = map capitalize parts;
|
||||
in
|
||||
builtins.concatStringsSep " " capitalizedParts;
|
||||
|
||||
fakeInstanceOptions =
|
||||
name: module:
|
||||
let
|
||||
manifest = getManifest module;
|
||||
description = ''
|
||||
# ${title name} (Clan Service)
|
||||
|
||||
**${manifest.description}**
|
||||
|
||||
${loadFile (module._file + "/../README.md")}
|
||||
|
||||
${
|
||||
if manifest.categories != [ ] then
|
||||
"Categories: " + builtins.concatStringsSep ", " manifest.categories
|
||||
else
|
||||
"No categories defined"
|
||||
}
|
||||
|
||||
'';
|
||||
in
|
||||
{
|
||||
options = {
|
||||
_ = mkOption {
|
||||
type = types.raw;
|
||||
};
|
||||
instances.${name} = lib.mkOption {
|
||||
inherit description;
|
||||
type = types.submodule {
|
||||
options.roles = flip mapAttrs (settingsModules module) (
|
||||
roleName: roleSettingsModule:
|
||||
mkOption {
|
||||
type = types.submodule {
|
||||
_file = "docs flake-module";
|
||||
imports = [
|
||||
{ _module.args = { inherit clanLib; }; }
|
||||
(import ../../../lib/modules/inventoryClass/roles-interface.nix {
|
||||
nestedSettingsOption = mkOption {
|
||||
type = types.raw;
|
||||
description = ''
|
||||
See [instances.${name}.roles.${roleName}.settings](${baseHref}?option_scope=0&option=instances.${name}.roles.${roleName}.settings)
|
||||
'';
|
||||
};
|
||||
settingsOption = mkOption {
|
||||
type = types.submoduleWith {
|
||||
modules = [ roleSettingsModule ];
|
||||
};
|
||||
};
|
||||
})
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mkScope = name: modules: {
|
||||
inherit name;
|
||||
modules = [
|
||||
{
|
||||
_module.args = { inherit clanLib; };
|
||||
_file = "docs mkScope";
|
||||
}
|
||||
{ noInstanceOptions = true; }
|
||||
../../../lib/modules/inventoryClass/interface.nix
|
||||
] ++ mapAttrsToList fakeInstanceOptions modules;
|
||||
urlPrefix = "https://github.com/nix-community/dream2nix/blob/main/";
|
||||
};
|
||||
in
|
||||
{
|
||||
packages.docs-options = inputs'.nuschtos.packages.mkMultiSearch {
|
||||
inherit baseHref;
|
||||
title = "Clan Options";
|
||||
# scopes = mapAttrsToList mkScope serviceModules;
|
||||
scopes = [ (mkScope "Clan Inventory" serviceModules) ];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -383,36 +383,16 @@ For more information, see the [inventory guide](../../guides/inventory.md).
|
||||
|
||||
`clan.admin.allowedkeys`
|
||||
|
||||
This means there are two equivalent ways to set the `allowedkeys` option.
|
||||
Either via a nixos module or via the inventory interface.
|
||||
**But it is recommended to keep together `imports` and `config` to preserve
|
||||
locality of the module configuration.**
|
||||
|
||||
=== "Inventory"
|
||||
|
||||
```nix
|
||||
clan-core.lib.buildClan {
|
||||
inventory.services = {
|
||||
admin.me = {
|
||||
roles.default.machines = [ "jon" ];
|
||||
config.allowedkeys = [ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQD..." ];
|
||||
};
|
||||
```nix
|
||||
clan-core.lib.clan {
|
||||
inventory.services = {
|
||||
admin.me = {
|
||||
roles.default.machines = [ "jon" ];
|
||||
config.allowedkeys = [ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQD..." ];
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
=== "NixOS"
|
||||
|
||||
```nix
|
||||
clan-core.lib.buildClan {
|
||||
machines = {
|
||||
jon = {
|
||||
clan.admin.allowedkeys = [ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQD..." ];
|
||||
imports = [ clanModules.admin ];
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
};
|
||||
```
|
||||
"""
|
||||
)
|
||||
return ""
|
||||
@@ -708,18 +688,18 @@ def produce_build_clan_docs() -> None:
|
||||
msg = f"Environment variables are not set correctly: $out={OUT}"
|
||||
raise ClanError(msg)
|
||||
|
||||
output = """# BuildClan
|
||||
output = """# Clan
|
||||
|
||||
This provides an overview of the available arguments of the `clan` interface.
|
||||
|
||||
Each attribute is documented below
|
||||
|
||||
- **buildClan**: A function that takes an attribute set.`.
|
||||
- **clan-core.lib.clan**: A function that takes an attribute set.
|
||||
|
||||
??? example "buildClan Example"
|
||||
??? example "clan Example"
|
||||
|
||||
```nix
|
||||
buildClan {
|
||||
clan {
|
||||
self = self;
|
||||
machines = {
|
||||
jon = { };
|
||||
@@ -728,7 +708,13 @@ Each attribute is documented below
|
||||
};
|
||||
```
|
||||
|
||||
- **flake-parts**: Each attribute can be defined via `clan.<attribute name>`. See our [flake-parts](../../guides/flake-parts.md) guide.
|
||||
- **clan with flake-parts**: Import the FlakeModule
|
||||
|
||||
After importing the FlakeModule you can define your `clan` as a flake attribute
|
||||
|
||||
All attribute can be defined via `clan.*`
|
||||
|
||||
Further information see: [flake-parts](../../guides/flake-parts.md) guide.
|
||||
|
||||
??? example "flake-parts Example"
|
||||
|
||||
@@ -767,7 +753,7 @@ Each attribute is documented below
|
||||
for option in root.suboptions:
|
||||
output += options_docs_from_tree(option, init_level=2)
|
||||
|
||||
outfile = Path(OUT) / "nix-api/buildclan.md"
|
||||
outfile = Path(OUT) / "nix-api/clan.md"
|
||||
outfile.parent.mkdir(parents=True, exist_ok=True)
|
||||
with Path.open(outfile, "w") as of:
|
||||
of.write(output)
|
||||
@@ -821,8 +807,8 @@ Typically needed by module authors to define roles, behavior and metadata for di
|
||||
|
||||
See: [clanService Authoring Guide](../../guides/authoring/clanServices/index.md)
|
||||
"""
|
||||
# Inventory options are already included under the buildClan attribute
|
||||
# We just omitted them in the buildClan docs, because we want a separate output for the inventory model
|
||||
# Inventory options are already included under the clan attribute
|
||||
# We just omitted them in the clan docs, because we want a separate output for the inventory model
|
||||
with Path(CLAN_SERVICE_INTERFACE).open() as f:
|
||||
options: dict[str, dict[str, Any]] = json.load(f)
|
||||
|
||||
@@ -860,11 +846,11 @@ def produce_inventory_docs() -> None:
|
||||
output = """# Inventory
|
||||
This provides an overview of the available attributes of the `inventory` model.
|
||||
|
||||
It can be set via the `inventory` attribute of the [`buildClan`](./buildclan.md#inventory) function, or via the [`clan.inventory`](./buildclan.md#inventory) attribute of flake-parts.
|
||||
It can be set via the `inventory` attribute of the [`clan`](./clan.md#inventory) function, or via the [`clan.inventory`](./clan.md#inventory) attribute of flake-parts.
|
||||
|
||||
"""
|
||||
# Inventory options are already included under the buildClan attribute
|
||||
# We just omitted them in the buildClan docs, because we want a separate output for the inventory model
|
||||
# Inventory options are already included under the clan attribute
|
||||
# We just omitted them in the clan docs, because we want a separate output for the inventory model
|
||||
with Path(BUILD_CLAN_PATH).open() as f:
|
||||
options: dict[str, dict[str, Any]] = json.load(f)
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
docs,
|
||||
pkgs,
|
||||
module-docs,
|
||||
clan-cli-docs,
|
||||
asciinema-player-js,
|
||||
asciinema-player-css,
|
||||
roboto,
|
||||
fira-code,
|
||||
self',
|
||||
...
|
||||
}:
|
||||
pkgs.mkShell {
|
||||
name = "clan-docs";
|
||||
inputsFrom = [
|
||||
docs
|
||||
self'.devShells.default
|
||||
];
|
||||
shellHook = ''
|
||||
git_root=$(git rev-parse --show-toplevel)
|
||||
cd ''${git_root}/docs
|
||||
|
||||
mkdir -p ./site/reference/cli
|
||||
cp -af ${module-docs}/* ./site/reference/
|
||||
cp -af ${clan-cli-docs}/* ./site/reference/cli/
|
||||
|
||||
chmod -R +w ./site/reference/*
|
||||
|
||||
echo "Generated API documentation in './site/reference/' "
|
||||
|
||||
mkdir -p ./site/static/asciinema-player
|
||||
|
||||
ln -snf ${asciinema-player-js} ./site/static/asciinema-player/asciinema-player.min.js
|
||||
ln -snf ${asciinema-player-css} ./site/static/asciinema-player/asciinema-player.css
|
||||
|
||||
# Link to fonts
|
||||
ln -snf ${roboto}/share/fonts/truetype/Roboto-Regular.ttf ./site/static/
|
||||
ln -snf ${fira-code}/share/fonts/truetype/FiraCode-VF.ttf ./site/static/
|
||||
'';
|
||||
}
|
||||
14
docs/overrides/options.html
Normal file
14
docs/overrides/options.html
Normal file
@@ -0,0 +1,14 @@
|
||||
{% extends "base.html" %} {% block extrahead %}
|
||||
<style>
|
||||
.md-main__inner {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
.md-content {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
.md-main__inner {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %} {% block site_nav %}{% endblock %} {% block content %} {{
|
||||
page.content }} {% endblock %}
|
||||
@@ -52,7 +52,7 @@ clanModules/borgbackup
|
||||
|
||||
```nix title="flake.nix"
|
||||
# ...
|
||||
buildClan {
|
||||
clan-core.lib.clan {
|
||||
# 1. Add the module to the available clanModules with inventory support
|
||||
inventory.modules = {
|
||||
custom-module = ./modules/my_module;
|
||||
@@ -175,7 +175,7 @@ The following shows how to add options to your module.
|
||||
Configuration can be set as follows.
|
||||
|
||||
```nix title="flake.nix"
|
||||
buildClan {
|
||||
clan-core.lib.clan {
|
||||
inventory.services = {
|
||||
custom-module.instance_1 = {
|
||||
roles.default.machines = [ "machineA" ];
|
||||
|
||||
@@ -27,7 +27,7 @@ inputs = {
|
||||
|
||||
## 2. Import the Clan flake-parts Module
|
||||
|
||||
After updating your flake inputs, the next step is to import the Clan flake-parts module. This will make the [Clan options](../reference/nix-api/buildclan.md) available within `mkFlake`.
|
||||
After updating your flake inputs, the next step is to import the Clan flake-parts module. This will make the [Clan options](../reference/nix-api/clan.md) available within `mkFlake`.
|
||||
|
||||
```nix
|
||||
{
|
||||
@@ -62,7 +62,7 @@ Next you'll need to configure Clan wide settings and define machines, here's an
|
||||
];
|
||||
|
||||
# Define your Clan
|
||||
# See: https://docs.clan.lol/reference/nix-api/buildclan/
|
||||
# See: https://docs.clan.lol/reference/nix-api/clan/
|
||||
clan = {
|
||||
# Clan wide settings
|
||||
meta.name = ""; # This is required and must be unique
|
||||
|
||||
@@ -8,7 +8,7 @@ Clan currently offers the following methods to configure machines:
|
||||
|
||||
!!! Success "Recommended for advanced Nix users"
|
||||
|
||||
- flake.nix (i.e. via `buildClan`)
|
||||
- flake.nix (i.e. via `clan-core.lib.clan`)
|
||||
- `machine` argument
|
||||
- `inventory` argument
|
||||
|
||||
@@ -30,7 +30,7 @@ In the `flake.nix` file:
|
||||
=== "**normal flake template**"
|
||||
|
||||
```nix title="flake.nix" hl_lines="3"
|
||||
buildClan {
|
||||
clan-core.lib.clan {
|
||||
# Set a unique name
|
||||
meta.name = "Lobsters";
|
||||
# Necessary for importing external Clan services
|
||||
@@ -60,7 +60,7 @@ Adding or configuring a new machine requires two simple steps:
|
||||
|
||||
???+ Note "Cloud Machines"
|
||||
NixOS can cause strange issues when booting in certain cloud environments.
|
||||
|
||||
|
||||
- If on Linode: Make sure that the system uses Direct Disk boot kernel (found in the configuration pannel)
|
||||
|
||||
### Step 1. Identify Target Disk-ID
|
||||
|
||||
@@ -175,7 +175,7 @@ During an update, the CLI will SSH into the build host and run `nixos-rebuild` f
|
||||
|
||||
|
||||
```{.nix hl_lines="5" .no-copy}
|
||||
buildClan {
|
||||
clan {
|
||||
# ...
|
||||
machines = {
|
||||
"jon" = {
|
||||
@@ -191,7 +191,7 @@ To exclude machines from being updated when running `clan machines update` witho
|
||||
one can set the `clan.deployment.requireExplicitUpdate` option to true:
|
||||
|
||||
```{.nix hl_lines="5" .no-copy}
|
||||
buildClan {
|
||||
clan {
|
||||
# ...
|
||||
machines = {
|
||||
"jon" = {
|
||||
|
||||
@@ -82,7 +82,7 @@ are loaded when using Clan:
|
||||
outputs =
|
||||
{ self, clan-core, ... }:
|
||||
let
|
||||
clan = clan-core.clanLib.buildClan {
|
||||
clan = clan-core.clanLib.clan {
|
||||
inherit self;
|
||||
|
||||
meta.name = "myclan";
|
||||
|
||||
@@ -47,7 +47,7 @@ Each service can still be customized and configured according to the modules opt
|
||||
See also: [Multiple Service Instances](#multiple-service-instances)
|
||||
|
||||
```{.nix hl_lines="6-7"}
|
||||
buildClan {
|
||||
clan-core.lib.clan {
|
||||
inventory = {
|
||||
services = {
|
||||
borgbackup.instance_1 = {
|
||||
@@ -69,7 +69,7 @@ It is possible to add services to multiple machines via tags as shown
|
||||
!!! Example "Tags Example"
|
||||
|
||||
```{.nix hl_lines="5 8 14"}
|
||||
buildClan {
|
||||
clan-core.lib.clan {
|
||||
inventory = {
|
||||
machines = {
|
||||
"jon" = {
|
||||
@@ -101,7 +101,7 @@ It is possible to add services to multiple machines via tags as shown
|
||||
In this example `backup_server` has role `client` and `server` in different instances.
|
||||
|
||||
```{.nix hl_lines="11 14"}
|
||||
buildClan {
|
||||
clan-core.lib.clan {
|
||||
inventory = {
|
||||
machines = {
|
||||
"jon" = {};
|
||||
|
||||
@@ -13,10 +13,10 @@ Currently, Clan supports the following features for macOS:
|
||||
|
||||
In this example, we'll name the machine `yourmachine`. Replace this with your preferred machine name.
|
||||
|
||||
=== "**If using core.lib.buildClan**"
|
||||
=== "**If using clan-core.lib.clan**"
|
||||
|
||||
```nix
|
||||
buildClan {
|
||||
clan-core.lib.clan {
|
||||
inventory = {
|
||||
machines.yourmachine.machineClass = "darwin";
|
||||
};
|
||||
|
||||
@@ -52,7 +52,7 @@ This guide shows you how to configure `zerotier` either through `NixOS Options`
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## 3. Apply the Configuration
|
||||
Update the `controller` machine:
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ output parameters.
|
||||
|
||||
The existing `nixosConfigurations` output of your flake will be created by
|
||||
clan. In addition, a new `clanInternals` output will be added. Since both of
|
||||
these are provided by the output of `lib.buildClan`, a common syntax is to use a
|
||||
these are provided by the output of `clan-core.lib.clan`, a common syntax is to use a
|
||||
`let...in` statement to create your clan and access it's parameters in the flake
|
||||
outputs.
|
||||
|
||||
@@ -85,7 +85,7 @@ For the provide flake example, your flake should now look like this:
|
||||
|
||||
outputs = { self, nixpkgs, clan-core, ... }:
|
||||
let
|
||||
clan = clan-core.lib.buildClan {
|
||||
clan = clan-core.lib.clan {
|
||||
self = self; # this needs to point at the repository root
|
||||
specialArgs = {};
|
||||
meta.name = throw "Change me to something unique";
|
||||
|
||||
@@ -17,19 +17,14 @@ Every folder `machines/{machineName}` will be registered automatically as a Clan
|
||||
- [x] `machines/{machineName}/facter.json` Automatically configured, for further information see [nixos-facter](https://clan.lol/blog/nixos-facter/)
|
||||
- [x] `machines/{machineName}/disko.nix` Automatically loaded, for further information see the [disko docs](https://github.com/nix-community/disko/blob/master/docs/quickstart.md).
|
||||
|
||||
|
||||
|
||||
## Manual declaration
|
||||
|
||||
Machines can also be added manually under `buildClan`, `clan.*` in flake-parts or via [`inventory`](../guides/inventory.md).
|
||||
|
||||
!!! Note
|
||||
It is possible to use `inventory` and `buildClan` together at the same time.
|
||||
Machines can be added via [`clan.inventory.machines`](../guides/inventory.md) or in `clan.machines`, which allows for defining NixOS options.
|
||||
|
||||
=== "**Individual Machine Configuration**"
|
||||
|
||||
```{.nix}
|
||||
buildClan {
|
||||
clan-core.lib.clan {
|
||||
machines = {
|
||||
"jon" = {
|
||||
# Any valid nixos config
|
||||
@@ -41,12 +36,13 @@ Machines can also be added manually under `buildClan`, `clan.*` in flake-parts o
|
||||
=== "**Inventory Configuration**"
|
||||
|
||||
```{.nix}
|
||||
buildClan {
|
||||
clan-core.lib.clan {
|
||||
inventory = {
|
||||
machines = {
|
||||
"jon" = {
|
||||
# Inventory machines can set tags
|
||||
# Inventory can set tags and other metadata
|
||||
tags = [ "zone1" ];
|
||||
deploy.targetHost = "root@jon";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ At the moment, NixOS/Clan does not support [Secure Boot](https://wiki.gentoo.org
|
||||
|
||||
### Step 2: Access the UEFI/BIOS Menu
|
||||
- Restart your computer.
|
||||
- As your computer restarts, press the appropriate key to enter the UEFI/BIOS settings.
|
||||
- As your computer restarts, press the appropriate key to enter the UEFI/BIOS settings.
|
||||
??? tip "The key depends on your laptop or motherboard manufacturer. Click to see a reference list:"
|
||||
|
||||
| Manufacturer | UEFI/BIOS Key(s) |
|
||||
|
||||
6
docs/site/options.md
Normal file
6
docs/site/options.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
template: options.html
|
||||
---
|
||||
|
||||
|
||||
<iframe src="/options-page/" height="1000" width="100%"></iframe>
|
||||
@@ -21,7 +21,7 @@ Flake URL-like syntax used to link to clans.
|
||||
Required to connect the `url-open` application to the `clan-app`.
|
||||
|
||||
## facts *(deprecated)*
|
||||
System for creating secrets and public files in a declarative way.
|
||||
System for creating secrets and public files in a declarative way.
|
||||
**Note:** Deprecated, use `vars` instead.
|
||||
|
||||
## inventory
|
||||
|
||||
@@ -7,7 +7,7 @@ This section of the site provides an overview of available options and commands
|
||||
- Learn how to use the [Clan CLI](./cli/index.md)
|
||||
- Explore available services and application [modules](./clanModules/index.md)
|
||||
- Discover [configuration options](./clan.core/index.md) that manage essential features
|
||||
- Find descriptions of the [Nix interfaces](./nix-api/buildclan.md) for defining a Clan
|
||||
- Find descriptions of the [Nix interfaces](./nix-api/clan.md) for defining a Clan
|
||||
|
||||
---
|
||||
|
||||
|
||||
92
flake.lock
generated
92
flake.lock
generated
@@ -16,11 +16,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1750183842,
|
||||
"narHash": "sha256-znYkJ+9GUNQCmFtEhGvMRZPRP3fdGmbiuTyyrJRKUGA=",
|
||||
"rev": "cb75111e4c99c7a960cfdd0d743f75663e36cbfa",
|
||||
"lastModified": 1750933274,
|
||||
"narHash": "sha256-Aq7mRVv+mws5U+6RmQSVxiO6dKQi/r6MlO4caKwZdK0=",
|
||||
"rev": "32b1ff6978758dda91a1e48d54f4f523ce9d1a98",
|
||||
"type": "tarball",
|
||||
"url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/cb75111e4c99c7a960cfdd0d743f75663e36cbfa.tar.gz"
|
||||
"url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/32b1ff6978758dda91a1e48d54f4f523ce9d1a98.tar.gz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
@@ -34,11 +34,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1750040002,
|
||||
"narHash": "sha256-KrC9iOVYIn6ukpVlHbqSA4hYCZ6oDyJKrcLqv4c5v84=",
|
||||
"lastModified": 1750903843,
|
||||
"narHash": "sha256-Ng9+f0H5/dW+mq/XOKvB9uwvGbsuiiO6HrPdAcVglCs=",
|
||||
"owner": "nix-community",
|
||||
"repo": "disko",
|
||||
"rev": "7f1857b31522062a6a00f88cbccf86b43acceed1",
|
||||
"rev": "83c4da299c1d7d300f8c6fd3a72ac46cb0d59aae",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -67,6 +67,52 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": [
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"ixx": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"nuschtos",
|
||||
"flake-utils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nuschtos",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1748294338,
|
||||
"narHash": "sha256-FVO01jdmUNArzBS7NmaktLdGA5qA3lUMJ4B7a05Iynw=",
|
||||
"owner": "NuschtOS",
|
||||
"repo": "ixx",
|
||||
"rev": "cc5f390f7caf265461d4aab37e98d2292ebbdb85",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NuschtOS",
|
||||
"ref": "v0.0.8",
|
||||
"repo": "ixx",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-darwin": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -128,15 +174,41 @@
|
||||
"url": "https://nixos.org/channels/nixpkgs-unstable/nixexprs.tar.xz"
|
||||
}
|
||||
},
|
||||
"nuschtos": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"flake-utils"
|
||||
],
|
||||
"ixx": "ixx",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1749730855,
|
||||
"narHash": "sha256-L3x2nSlFkXkM6tQPLJP3oCBMIsRifhIDPMQQdHO5xWo=",
|
||||
"owner": "NuschtOS",
|
||||
"repo": "search",
|
||||
"rev": "8dfe5879dd009ff4742b668d9c699bc4b9761742",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NuschtOS",
|
||||
"repo": "search",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"data-mesher": "data-mesher",
|
||||
"disko": "disko",
|
||||
"flake-parts": "flake-parts",
|
||||
"flake-utils": "flake-utils",
|
||||
"nix-darwin": "nix-darwin",
|
||||
"nix-select": "nix-select",
|
||||
"nixos-facter-modules": "nixos-facter-modules",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nuschtos": "nuschtos",
|
||||
"sops-nix": "sops-nix",
|
||||
"systems": "systems",
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
@@ -184,11 +256,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1749194973,
|
||||
"narHash": "sha256-eEy8cuS0mZ2j/r/FE0/LYBSBcIs/MKOIVakwHVuqTfk=",
|
||||
"lastModified": 1750931469,
|
||||
"narHash": "sha256-0IEdQB1nS+uViQw4k3VGUXntjkDp7aAlqcxdewb/hAc=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "a05be418a1af1198ca0f63facb13c985db4cb3c5",
|
||||
"rev": "ac8e6f32e11e9c7f153823abc3ab007f2a65d3e1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
46
flake.nix
46
flake.nix
@@ -34,6 +34,13 @@
|
||||
treefmt-nix.follows = "treefmt-nix";
|
||||
};
|
||||
};
|
||||
|
||||
# dependencies needed for nuschtos
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
flake-utils.inputs.systems.follows = "systems";
|
||||
nuschtos.url = "github:NuschtOS/search";
|
||||
nuschtos.inputs.nixpkgs.follows = "nixpkgs";
|
||||
nuschtos.inputs.flake-utils.follows = "flake-utils";
|
||||
};
|
||||
|
||||
outputs =
|
||||
@@ -56,27 +63,28 @@
|
||||
clan = {
|
||||
meta.name = "clan-core";
|
||||
};
|
||||
|
||||
systems = import systems;
|
||||
imports =
|
||||
# only importing existing paths allows to minimize the flake for test
|
||||
# by removing files
|
||||
filter pathExists [
|
||||
./checks/flake-module.nix
|
||||
./clanModules/flake-module.nix
|
||||
./clanServices/flake-module.nix
|
||||
./devShell.nix
|
||||
./docs/nix/flake-module.nix
|
||||
./flakeModules/flake-module.nix
|
||||
./flakeModules/demo_iso.nix
|
||||
./lib/filter-clan-core/flake-module.nix
|
||||
./lib/flake-module.nix
|
||||
./lib/flake-parts/clan-nixos-test.nix
|
||||
./nixosModules/clanCore/vars/flake-module.nix
|
||||
./nixosModules/flake-module.nix
|
||||
./pkgs/flake-module.nix
|
||||
./templates/flake-module.nix
|
||||
]
|
||||
[ flake-parts.flakeModules.modules ]
|
||||
++
|
||||
# only importing existing paths allows to minimize the flake for test
|
||||
# by removing files
|
||||
filter pathExists [
|
||||
./checks/flake-module.nix
|
||||
./clanModules/flake-module.nix
|
||||
./clanServices/flake-module.nix
|
||||
./devShell.nix
|
||||
./docs/nix/flake-module.nix
|
||||
./flakeModules/flake-module.nix
|
||||
./flakeModules/demo_iso.nix
|
||||
./lib/filter-clan-core/flake-module.nix
|
||||
./lib/flake-module.nix
|
||||
./lib/flake-parts/clan-nixos-test.nix
|
||||
./nixosModules/clanCore/vars/flake-module.nix
|
||||
./nixosModules/flake-module.nix
|
||||
./pkgs/flake-module.nix
|
||||
./templates/flake-module.nix
|
||||
]
|
||||
++ [
|
||||
(if pathExists ./flakeModules/clan.nix then import ./flakeModules/clan.nix inputs.self else { })
|
||||
]
|
||||
|
||||
@@ -1,55 +1,78 @@
|
||||
clan-core:
|
||||
# Downstream flake arguments
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
self,
|
||||
inputs,
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib) types;
|
||||
in
|
||||
{
|
||||
# Backwards compatibility
|
||||
imports = [
|
||||
(lib.mkRenamedOptionModule [ "clan" ] [ "flake" "clan" ])
|
||||
];
|
||||
options.flake = {
|
||||
# CLI compat
|
||||
clanInternals = lib.mkOption {
|
||||
description = "Stable nix interface interacted by the clan cli.";
|
||||
default = config.flake.clan.clanInternals;
|
||||
};
|
||||
# The clan module
|
||||
clan = lib.mkOption {
|
||||
description = "Clan module. Define your clan inside here";
|
||||
default = { };
|
||||
type = types.submoduleWith {
|
||||
class = "clan";
|
||||
specialArgs =
|
||||
# TODO: make these explizit options and deduplicate with lib.clan function
|
||||
let
|
||||
nixpkgs = inputs.nixpkgs or clan-core.inputs.nixpkgs;
|
||||
nix-darwin = inputs.nix-darwin or clan-core.inputs.nix-darwin;
|
||||
in
|
||||
{
|
||||
inherit self;
|
||||
inherit nixpkgs nix-darwin;
|
||||
};
|
||||
modules = [
|
||||
clan-core.modules.clan.default
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
buildClanModule = clan-core.clanLib.buildClanModule;
|
||||
|
||||
publicAttrs = import ../lib/build-clan/public.nix;
|
||||
# Create output options only for listed attributes
|
||||
# TODO: Refactor this into an explicit module, so we can have description and other attributes to be listed in flake-parts
|
||||
outputModule = {
|
||||
clan = lib.genAttrs publicAttrs.clan (
|
||||
name: config.clan.${name} or (throw "Output: clan.${name} not found.")
|
||||
);
|
||||
topLevel = {
|
||||
options = lib.genAttrs publicAttrs.topLevel (_: lib.mkOption { });
|
||||
config = lib.genAttrs publicAttrs.topLevel (
|
||||
name: config.clan.${name} or (throw "Output: clan.${name} not found.")
|
||||
# Mapped flake toplevel outputs
|
||||
darwinConfigurations = lib.mkOption {
|
||||
type = types.lazyAttrsOf types.raw;
|
||||
description = "darwinConfigurations produced by clan for a specific machine";
|
||||
apply = lib.mapAttrs (
|
||||
k: v: {
|
||||
_file = "#nixosModules.${k}";
|
||||
imports = [ v ];
|
||||
}
|
||||
);
|
||||
};
|
||||
darwinModules = lib.mkOption {
|
||||
type = types.lazyAttrsOf types.deferredModule;
|
||||
description = "darwinModules produced by clan for a specific machine";
|
||||
apply = lib.mapAttrs (
|
||||
k: v: {
|
||||
_file = "#nixosModules.${k}";
|
||||
imports = [ v ];
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
options.clan = lib.mkOption {
|
||||
default = { };
|
||||
type = types.submoduleWith {
|
||||
specialArgs = {
|
||||
inherit clan-core self;
|
||||
inherit (inputs) nixpkgs nix-darwin;
|
||||
# TODO: inject the inventory interface
|
||||
# inventoryInterface = {};
|
||||
};
|
||||
modules = [
|
||||
buildClanModule.flakePartsModule
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
options.flake = {
|
||||
clan = lib.mkOption { type = types.raw; };
|
||||
} // outputModule.topLevel.options;
|
||||
|
||||
config = {
|
||||
flake = {
|
||||
clan = outputModule.clan;
|
||||
} // outputModule.topLevel.config;
|
||||
# Use normal prio, to allow merging with user values
|
||||
config.flake = {
|
||||
inherit (config.flake.clan)
|
||||
nixosConfigurations
|
||||
nixosModules
|
||||
darwinConfigurations
|
||||
darwinModules
|
||||
;
|
||||
};
|
||||
|
||||
_file = __curPos.file;
|
||||
|
||||
@@ -106,6 +106,13 @@
|
||||
extraPythonPackages = (self'.packages.clan-app.devshellPyDeps pkgs.python3Packages);
|
||||
extraPythonPaths = [ "../../clan-cli" ];
|
||||
};
|
||||
"generate-test-vars" = {
|
||||
directory = "pkgs/generate-test-vars";
|
||||
extraPythonPackages = [
|
||||
(pkgs.python3.withPackages (ps: self'.packages.clan-cli.devshellPyDeps ps))
|
||||
];
|
||||
extraPythonPaths = [ "../clan-cli" ];
|
||||
};
|
||||
}
|
||||
// (
|
||||
if pkgs.stdenv.isLinux then
|
||||
|
||||
105
inventory.json
105
inventory.json
@@ -9,110 +9,5 @@
|
||||
"test-darwin-machine": {
|
||||
"machineClass": "darwin"
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"zerotier": {
|
||||
"one": {
|
||||
"roles": {
|
||||
"controller": {
|
||||
"machines": ["test-inventory-machine"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"borgbackup": {
|
||||
"simple": {
|
||||
"roles": {
|
||||
"client": {
|
||||
"machines": ["test-inventory-machine"]
|
||||
},
|
||||
"server": {
|
||||
"tags": ["2"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"double": {
|
||||
"roles": {
|
||||
"server": {
|
||||
"machines": ["test-inventory-machine"]
|
||||
},
|
||||
"client": {
|
||||
"tags": ["1"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"packages": {
|
||||
"editors": {
|
||||
"meta": {
|
||||
"name": "Some editor packages"
|
||||
},
|
||||
"roles": {
|
||||
"default": {
|
||||
"extraModules": [],
|
||||
"machines": ["test-inventory-machine"],
|
||||
"tags": []
|
||||
}
|
||||
},
|
||||
"config": {},
|
||||
"extraModules": [],
|
||||
"machines": {
|
||||
"test-inventory-machine": {
|
||||
"config": {
|
||||
"packages": ["hello"]
|
||||
},
|
||||
"extraModules": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"browsing": {
|
||||
"meta": {
|
||||
"name": "Web browsing packages"
|
||||
},
|
||||
"roles": {
|
||||
"default": {
|
||||
"config": {},
|
||||
"extraModules": [],
|
||||
"machines": ["test-inventory-machine"],
|
||||
"tags": []
|
||||
}
|
||||
},
|
||||
"config": {},
|
||||
"extraModules": [],
|
||||
"machines": {
|
||||
"test-inventory-machine": {
|
||||
"config": {
|
||||
"packages": ["chromium"]
|
||||
},
|
||||
"extraModules": []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"single-disk": {
|
||||
"default": {
|
||||
"meta": {
|
||||
"name": "single-disk"
|
||||
},
|
||||
"roles": {
|
||||
"default": {
|
||||
"config": {},
|
||||
"extraModules": [],
|
||||
"machines": ["test-inventory-machine"],
|
||||
"tags": []
|
||||
}
|
||||
},
|
||||
"config": {},
|
||||
"extraModules": [],
|
||||
"machines": {
|
||||
"test-inventory-machine": {
|
||||
"config": {
|
||||
"device": "/dev/null"
|
||||
},
|
||||
"extraModules": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
## WARNING: Do not add core logic here.
|
||||
## This is only a wrapper such that buildClan can be called as a function.
|
||||
## Add any logic to ./module.nix
|
||||
{
|
||||
lib,
|
||||
clanLib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
flakePartsModule = {
|
||||
imports = [
|
||||
(lib.modules.importApply ./interface.nix { inherit clanLib; })
|
||||
./module.nix
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
A function that takes some arguments such as 'clan-core' and returns the 'buildClan' function.
|
||||
|
||||
# Arguments of the first function
|
||||
- clan-core: Self, provided by our flake-parts module
|
||||
- publicAttrs: { clan :: List Str, topLevel :: List Str } Publicly exported attribute names
|
||||
|
||||
# Arguments of the second function (aka 'buildClan')
|
||||
- self: Reference to the users flake
|
||||
- inventory: An "Inventory" attribute set, see the docs, for how to construct one
|
||||
- specialArgs: Extra arguments to pass to nixosSystem i.e. useful to make self available
|
||||
- ...: Any other argument of the 'clan' submodule. See the docs for all available options
|
||||
|
||||
# Returns
|
||||
|
||||
Public attributes of buildClan. As specified in publicAttrs.
|
||||
*/
|
||||
buildClanWith =
|
||||
{
|
||||
clan-core,
|
||||
# TODO: Below should be module options such that the user can override them?
|
||||
nixpkgs,
|
||||
publicAttrs ? import ./public.nix,
|
||||
nix-darwin ? null,
|
||||
}:
|
||||
{
|
||||
## Inputs
|
||||
self ? lib.warn "Argument: 'self' must be set when using 'buildClan'." null, # Reference to the current flake
|
||||
# allows to include machine-specific modules i.e. machines.${name} = { ... }
|
||||
# A map from arch to pkgs, if specified this nixpkgs will be only imported once for each system.
|
||||
# This improves performance, but all nipxkgs.* options will be ignored.
|
||||
# deadnix: skip
|
||||
inventory ? { },
|
||||
## Special inputs (not passed to the module system as config)
|
||||
specialArgs ? { }, # Extra arguments to pass to nixosSystem i.e. useful to make self available
|
||||
##
|
||||
...
|
||||
}@attrs:
|
||||
let
|
||||
eval = import ./function-adapter.nix {
|
||||
inherit
|
||||
lib
|
||||
nixpkgs
|
||||
nix-darwin
|
||||
clan-core
|
||||
self
|
||||
;
|
||||
inherit specialArgs;
|
||||
};
|
||||
rest = builtins.removeAttrs attrs [ "specialArgs" ];
|
||||
result = eval {
|
||||
imports = [
|
||||
rest
|
||||
# implementation
|
||||
./module.nix
|
||||
];
|
||||
};
|
||||
in
|
||||
{
|
||||
clan = lib.genAttrs publicAttrs.clan (
|
||||
name:
|
||||
result.clanInternals.${name}
|
||||
or (throw "Output: clanInternals.${name} not found. Check: ${result.file}")
|
||||
);
|
||||
}
|
||||
// lib.filterAttrs (name: _v: builtins.elem name publicAttrs.topLevel) result;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
lib,
|
||||
nixpkgs,
|
||||
nix-darwin ? null,
|
||||
clan-core,
|
||||
self,
|
||||
specialArgs ? { },
|
||||
}:
|
||||
# Returns a function that takes self, which should point to the directory of the flake
|
||||
module:
|
||||
(lib.evalModules {
|
||||
specialArgs = {
|
||||
inherit
|
||||
self
|
||||
clan-core
|
||||
nixpkgs
|
||||
nix-darwin
|
||||
;
|
||||
};
|
||||
modules = [
|
||||
(lib.modules.importApply ./interface.nix { inherit (clan-core) clanLib; })
|
||||
module
|
||||
{
|
||||
inherit specialArgs;
|
||||
}
|
||||
];
|
||||
}).config
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
Publicly exported attribute names
|
||||
These are mapped from 'options.clan.{name}' into 'flake.{name}'
|
||||
For example "clanInternals" will be exposed as "flake.clan.clanInternals"
|
||||
This list is used to guarantee equivalent attribute sets for both flake-parts and buildClan users.
|
||||
*/
|
||||
{
|
||||
# flake.clan.{name} <- clan.{name}
|
||||
clan = [
|
||||
"templates"
|
||||
"modules"
|
||||
];
|
||||
# flake.{name} <- clan.{name}
|
||||
topLevel = [
|
||||
"clanInternals"
|
||||
"nixosConfigurations"
|
||||
"nixosModules"
|
||||
"darwinConfigurations"
|
||||
"darwinModules"
|
||||
];
|
||||
}
|
||||
@@ -20,7 +20,7 @@ in
|
||||
{
|
||||
# A function that returns an extension to runTest
|
||||
# TODO: remove this from clanLib, add to legacyPackages, simplify signature
|
||||
flake.modules.nixosVmTest.clanTest =
|
||||
flake.modules.nixosTest.clanTest =
|
||||
{ config, hostPkgs, ... }:
|
||||
let
|
||||
testName = config.name;
|
||||
@@ -87,7 +87,7 @@ in
|
||||
self.packages.${hostPkgs.system}.generate-test-vars
|
||||
}/bin/generate-test-vars";
|
||||
|
||||
relativeDir = removePrefix ("${self}/") (toString config.clan.directory);
|
||||
relativeDir = removePrefix "${self}/" (toString config.clan.directory);
|
||||
|
||||
update-vars = hostPkgs.writeShellScriptBin "update-vars" ''
|
||||
${update-vars-script} $PRJ_ROOT/${relativeDir} ${testName}
|
||||
@@ -158,18 +158,31 @@ in
|
||||
'';
|
||||
|
||||
# the test's flake.nix with locked clan-core input
|
||||
flakeForSandbox = hostPkgs.runCommand "offline-flake-for-test-${config.name}" { } ''
|
||||
cp -r ${config.clan.directory} $out
|
||||
chmod +w -R $out
|
||||
substituteInPlace $out/flake.nix \
|
||||
--replace-fail \
|
||||
"https://git.clan.lol/clan/clan-core/archive/main.tar.gz" \
|
||||
"${clan-core.packages.${hostPkgs.system}.clan-core-flake}"
|
||||
'';
|
||||
flakeForSandbox =
|
||||
hostPkgs.runCommand "offline-flake-for-test-${config.name}"
|
||||
{
|
||||
nativeBuildInputs = [ hostPkgs.nix ];
|
||||
}
|
||||
''
|
||||
cp -r ${config.clan.directory} $out
|
||||
chmod +w -R $out
|
||||
substituteInPlace $out/flake.nix \
|
||||
--replace-fail \
|
||||
"https://git.clan.lol/clan/clan-core/archive/main.tar.gz" \
|
||||
"${clan-core.packages.${hostPkgs.system}.clan-core-flake}"
|
||||
|
||||
# Create a proper lock file for the test flake
|
||||
export HOME=$(mktemp -d)
|
||||
nix flake lock $out \
|
||||
--extra-experimental-features 'nix-command flakes' \
|
||||
--override-input clan-core ${clan-core.packages.${hostPkgs.system}.clan-core-flake} \
|
||||
--override-input nixpkgs ${clan-core.inputs.nixpkgs}
|
||||
'';
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
../test/container-test-driver/driver-module.nix
|
||||
|
||||
];
|
||||
options = {
|
||||
clanSettings = mkOption {
|
||||
@@ -197,7 +210,7 @@ in
|
||||
self = throw "set clan.directory in the test";
|
||||
};
|
||||
modules = [
|
||||
clanLib.buildClanModule.flakePartsModule
|
||||
clan-core.modules.clan.default
|
||||
{
|
||||
_prefix = [
|
||||
"checks"
|
||||
|
||||
@@ -1,62 +1,54 @@
|
||||
{
|
||||
lib,
|
||||
self,
|
||||
nixpkgs,
|
||||
nix-darwin ? null,
|
||||
...
|
||||
}:
|
||||
# Produces the
|
||||
# 'clanLib' attribute set
|
||||
# Wrapped with fix, so we can depend on other clanLib functions without passing the whole flake
|
||||
lib.fix (clanLib: {
|
||||
/**
|
||||
Like callPackage, but doesn't try to automatically detect arguments
|
||||
'lib' and 'clanLib' are always passed, plus the additional arguments
|
||||
*/
|
||||
callLib = file: args: import file ({ inherit lib clanLib; } // args);
|
||||
lib.fix (
|
||||
clanLib:
|
||||
let
|
||||
buildClanLib = (
|
||||
clanLib.callLib ./modules {
|
||||
clan-core = self;
|
||||
}
|
||||
);
|
||||
in
|
||||
{
|
||||
|
||||
# ------------------------------------
|
||||
buildClan = clanLib.buildClanModule.buildClanWith {
|
||||
clan-core = self;
|
||||
inherit nixpkgs nix-darwin;
|
||||
};
|
||||
evalServiceSchema =
|
||||
self:
|
||||
{
|
||||
moduleSpec,
|
||||
flakeInputs ? self.inputs,
|
||||
localModuleSet ? self.clan.modules,
|
||||
}:
|
||||
let
|
||||
resolvedModule = clanLib.inventory.resolveModule {
|
||||
inherit moduleSpec flakeInputs localModuleSet;
|
||||
};
|
||||
in
|
||||
(clanLib.inventory.evalClanService {
|
||||
modules = [ resolvedModule ];
|
||||
prefix = [ ];
|
||||
}).config.result.api.schema;
|
||||
# ------------------------------------
|
||||
# ClanLib functions
|
||||
evalClan = clanLib.callLib ./inventory/eval-clan-modules { };
|
||||
buildClanModule = clanLib.callLib ./build-clan { };
|
||||
inventory = clanLib.callLib ./inventory { };
|
||||
modules = clanLib.callLib ./inventory/frontmatter { };
|
||||
test = clanLib.callLib ./test { };
|
||||
# Custom types
|
||||
types = clanLib.callLib ./types { };
|
||||
inherit (buildClanLib)
|
||||
buildClan
|
||||
clan
|
||||
;
|
||||
/**
|
||||
Like callPackage, but doesn't try to automatically detect arguments
|
||||
'lib' and 'clanLib' are always passed, plus the additional arguments
|
||||
*/
|
||||
callLib = file: args: import file ({ inherit lib clanLib; } // args);
|
||||
|
||||
# Plain imports.
|
||||
introspection = import ./introspection { inherit lib; };
|
||||
jsonschema = import ./jsonschema { inherit lib; };
|
||||
facts = import ./facts.nix { inherit lib; };
|
||||
evalService = clanLib.callLib ./modules/inventory/distributed-service/evalService.nix { };
|
||||
# ------------------------------------
|
||||
# ClanLib functions
|
||||
evalClan = clanLib.callLib ./modules/inventory/eval-clan-modules { };
|
||||
inventory = clanLib.callLib ./modules/inventory { };
|
||||
modules = clanLib.callLib ./modules/inventory/frontmatter { };
|
||||
test = clanLib.callLib ./test { };
|
||||
# Custom types
|
||||
types = clanLib.callLib ./types { };
|
||||
|
||||
# flakes
|
||||
flakes = clanLib.callLib ./flakes.nix {
|
||||
clan-core = self;
|
||||
};
|
||||
# Plain imports.
|
||||
introspection = import ./introspection { inherit lib; };
|
||||
jsonschema = import ./jsonschema { inherit lib; };
|
||||
facts = import ./facts.nix { inherit lib; };
|
||||
|
||||
# deprecated
|
||||
# remove when https://git.clan.lol/clan/clan-core/pulls/3212 is implemented
|
||||
inherit (self.inputs.nix-select.lib) select;
|
||||
})
|
||||
# flakes
|
||||
flakes = clanLib.callLib ./flakes.nix {
|
||||
clan-core = self;
|
||||
};
|
||||
|
||||
# deprecated
|
||||
# remove when https://git.clan.lol/clan/clan-core/pulls/3212 is implemented
|
||||
inherit (self.inputs.nix-select.lib) select;
|
||||
}
|
||||
)
|
||||
|
||||
@@ -7,16 +7,15 @@
|
||||
rec {
|
||||
# TODO: automatically generate this from the directory conventions
|
||||
imports = [
|
||||
./build-clan/flake-module.nix
|
||||
./modules/flake-module.nix
|
||||
./clanTest/flake-module.nix
|
||||
./introspection/flake-module.nix
|
||||
./inventory/flake-module.nix
|
||||
./modules/inventory/flake-module.nix
|
||||
./jsonschema/flake-module.nix
|
||||
./types/flake-module.nix
|
||||
];
|
||||
flake.clanLib = import ./default.nix {
|
||||
inherit lib inputs self;
|
||||
inherit (inputs) nixpkgs nix-darwin;
|
||||
};
|
||||
# TODO: remove this legacy alias
|
||||
flake.lib = flake.clanLib;
|
||||
|
||||
@@ -40,7 +40,7 @@ in
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
self.modules.nixosVmTest.clanTest
|
||||
self.modules.nixosTest.clanTest
|
||||
testModule
|
||||
];
|
||||
|
||||
@@ -68,7 +68,7 @@ in
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
self.modules.nixosVmTest.clanTest
|
||||
self.modules.nixosTest.clanTest
|
||||
testModule
|
||||
];
|
||||
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
# Inventory
|
||||
|
||||
The inventory is our concept for distributed services. Users can configure multiple machines with minimal effort.
|
||||
|
||||
- The inventory acts as a declarative source of truth for all machine configurations.
|
||||
- Users can easily add or remove machines to/from services.
|
||||
- Ensures that all machines and services are configured consistently, across multiple nixosConfigs.
|
||||
- Defaults and predefined roles in our modules minimizes the need for manual configuration.
|
||||
|
||||
Open questions:
|
||||
|
||||
- [ ] How do we set default role, description and other metadata?
|
||||
- It must be accessible from Python.
|
||||
- It must set the value in the module system.
|
||||
|
||||
- [ ] Inventory might use assertions. Should each machine inherit the inventory assertions ?
|
||||
|
||||
- [ ] Is the service config interface the same as the module config interface ?
|
||||
|
||||
- [ ] As a user do I want to see borgbackup as the high level category?
|
||||
|
||||
|
||||
Architecture
|
||||
|
||||
```
|
||||
nixosConfig < machine_module < inventory
|
||||
---------------------------------------------
|
||||
nixos < borgbackup <- inventory <-> UI
|
||||
|
||||
creates the config Maps from high level services to the borgbackup clan module
|
||||
for ONE machine Inventory is completely serializable.
|
||||
UI can interact with the inventory to define machines, and services
|
||||
Defining Users is out of scope for the first prototype.
|
||||
```
|
||||
|
||||
## Provides a specification for the inventory
|
||||
|
||||
It is used for design phase and as validation helper.
|
||||
|
||||
> Cue is less verbose and easier to understand and maintain than json-schema.
|
||||
> Json-schema, if needed can be easily generated on-the fly.
|
||||
|
||||
## Checking validity
|
||||
|
||||
Directly check a json against the schema
|
||||
|
||||
`cue vet inventory.json root.cue -d '#Root'`
|
||||
|
||||
## Json schema
|
||||
|
||||
Export the json-schema i.e. for usage in python / javascript / nix
|
||||
|
||||
`cue export --out openapi root.cue`
|
||||
|
||||
## Usage
|
||||
|
||||
Comments are rendered as descriptions in the json schema.
|
||||
|
||||
```cue
|
||||
// A name of the clan (primarily shown by the UI)
|
||||
name: string
|
||||
```
|
||||
|
||||
Cue open sets. In the following `foo = {...}` means that the key `foo` can contain any arbitrary json object.
|
||||
|
||||
```cue
|
||||
foo: { ... }
|
||||
```
|
||||
|
||||
Cue dynamic keys.
|
||||
|
||||
```cue
|
||||
[string]: {
|
||||
attr: string
|
||||
}
|
||||
```
|
||||
|
||||
This is the schema of
|
||||
|
||||
```json
|
||||
{
|
||||
"a": {
|
||||
"attr": "foo"
|
||||
},
|
||||
"b": {
|
||||
"attr": "bar"
|
||||
}
|
||||
// ... Indefinitely more dynamic keys of type "string"
|
||||
}
|
||||
```
|
||||
@@ -1,49 +0,0 @@
|
||||
# Generate partial NixOS configurations for every machine in the inventory
|
||||
# This function is responsible for generating the module configuration for every machine in the inventory.
|
||||
{ lib, clanLib }:
|
||||
let
|
||||
/*
|
||||
Returns a set with NixOS configuration for every machine in the inventory.
|
||||
|
||||
machinesFromInventory :: Inventory -> { ${machine_name} :: NixOSConfiguration }
|
||||
*/
|
||||
buildInventory =
|
||||
{
|
||||
inventory,
|
||||
directory,
|
||||
flakeInputs,
|
||||
prefix ? [ ],
|
||||
localModuleSet ? { },
|
||||
}:
|
||||
(lib.evalModules {
|
||||
# TODO: remove clanLib from specialArgs
|
||||
specialArgs = {
|
||||
inherit clanLib;
|
||||
};
|
||||
modules = [
|
||||
./builder
|
||||
(lib.modules.importApply ./service-list-from-inputs.nix {
|
||||
inherit flakeInputs clanLib localModuleSet;
|
||||
})
|
||||
{ inherit directory inventory; }
|
||||
(
|
||||
# config.distributedServices.allMachines.${name} or [ ];
|
||||
{ config, ... }:
|
||||
{
|
||||
distributedServices = clanLib.inventory.mapInstances {
|
||||
inherit (config) inventory;
|
||||
inherit localModuleSet;
|
||||
inherit flakeInputs;
|
||||
prefix = prefix ++ [ "distributedServices" ];
|
||||
};
|
||||
machines = config.distributedServices.allMachines;
|
||||
|
||||
}
|
||||
)
|
||||
(lib.modules.importApply ./inventory-introspection.nix { inherit clanLib; })
|
||||
];
|
||||
}).config;
|
||||
in
|
||||
{
|
||||
inherit buildInventory;
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
{ machines, ... }:
|
||||
{
|
||||
# Only compute the default value
|
||||
# The option MUST be defined in ./build-inventory/interface.nix
|
||||
# The option MUST be defined in inventoryClass/interface.nix
|
||||
all = lib.mkDefault (builtins.attrNames machines);
|
||||
nixos = lib.mkDefault (
|
||||
builtins.attrNames (lib.filterAttrs (_n: m: m.machineClass == "nixos") machines)
|
||||
12
lib/modules/clan/default.nix
Normal file
12
lib/modules/clan/default.nix
Normal file
@@ -0,0 +1,12 @@
|
||||
{ clan-core }:
|
||||
{
|
||||
_class = "clan";
|
||||
_module.args = {
|
||||
inherit clan-core;
|
||||
inherit (clan-core) clanLib;
|
||||
};
|
||||
imports = [
|
||||
./module.nix
|
||||
./interface.nix
|
||||
];
|
||||
}
|
||||
4
lib/modules/clan/flake-module.nix
Normal file
4
lib/modules/clan/flake-module.nix
Normal file
@@ -0,0 +1,4 @@
|
||||
{ self, lib, ... }:
|
||||
{
|
||||
flake.modules.clan.default = lib.modules.importApply ./default.nix { clan-core = self; };
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{ clanLib }:
|
||||
{
|
||||
lib,
|
||||
clanLib,
|
||||
self,
|
||||
# TODO: Use dependency injection to allow for testing
|
||||
# inventoryInterface,
|
||||
@@ -90,7 +90,7 @@ in
|
||||
};
|
||||
|
||||
templates = lib.mkOption {
|
||||
type = types.submodule { imports = [ ./templates/interface.nix ]; };
|
||||
type = types.submodule { imports = [ ./templates.nix ]; };
|
||||
default = { };
|
||||
description = ''
|
||||
Define Clan templates.
|
||||
@@ -100,7 +100,11 @@ in
|
||||
inventory = lib.mkOption {
|
||||
type = types.submodule {
|
||||
imports = [
|
||||
(lib.modules.importApply ../inventory/build-inventory/interface.nix { inherit clanLib; })
|
||||
{
|
||||
_module.args = { inherit clanLib; };
|
||||
_file = "clan interface";
|
||||
}
|
||||
../inventoryClass/interface.nix
|
||||
];
|
||||
};
|
||||
description = ''
|
||||
@@ -116,13 +120,13 @@ in
|
||||
Global information about the clan.
|
||||
'';
|
||||
type = types.deferredModuleWith {
|
||||
staticModules = [ ../inventory/build-inventory/meta-interface.nix ];
|
||||
staticModules = [ ../inventoryClass/meta-interface.nix ];
|
||||
};
|
||||
default = { };
|
||||
};
|
||||
|
||||
secrets = lib.mkOption {
|
||||
type = types.submodule { imports = [ ./secrets/interface.nix ]; };
|
||||
type = types.submodule { imports = [ ./secrets.nix ]; };
|
||||
description = ''
|
||||
Secrets related options such as AGE plugins required to encrypt/decrypt secrets using the CLI.
|
||||
'';
|
||||
@@ -203,7 +207,11 @@ in
|
||||
clanModules = lib.mkOption { type = lib.types.raw; };
|
||||
|
||||
# The machine 'imports' generated by the inventory per machine
|
||||
inventoryClass = lib.mkOption { type = lib.types.raw; };
|
||||
inventoryClass = lib.mkOption {
|
||||
type = types.submoduleWith {
|
||||
modules = [ ];
|
||||
};
|
||||
};
|
||||
|
||||
# TODO: remove all dependents in python, delete this option
|
||||
inventory = lib.mkOption {
|
||||
@@ -19,8 +19,6 @@ let
|
||||
specialArgs
|
||||
;
|
||||
|
||||
inherit (clan-core.clanLib.inventory) buildInventory;
|
||||
|
||||
supportedSystems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
@@ -43,18 +41,7 @@ let
|
||||
if pkgs != null then pkgs else nixpkgs.legacyPackages.${system}
|
||||
);
|
||||
|
||||
# map from machine name to service configuration
|
||||
# { ${machineName} :: Config }
|
||||
inventoryClass = (
|
||||
buildInventory {
|
||||
inherit inventory directory;
|
||||
flakeInputs = config.self.inputs;
|
||||
prefix = config._prefix ++ [ "inventoryClass" ];
|
||||
# TODO: remove inventory.modules, this is here for backwards compatibility
|
||||
localModuleSet =
|
||||
lib.filterAttrs (n: _: !inventory._legacyModules ? ${n}) inventory.modules // config.modules;
|
||||
}
|
||||
);
|
||||
inherit (clan-core) clanLib;
|
||||
|
||||
moduleSystemConstructor = {
|
||||
# TODO: remove default system once we have a hardware-config mechanism
|
||||
@@ -62,7 +49,7 @@ let
|
||||
darwin = nix-darwin.lib.darwinSystem;
|
||||
};
|
||||
|
||||
allMachines = inventoryClass.machines; # <- inventory.machines <- clan.machines
|
||||
allMachines = config.clanInternals.inventoryClass.machines; # <- inventory.machines <- clan.machines
|
||||
|
||||
machineClasses = lib.mapAttrs (
|
||||
name: _: inventory.machines.${name}.machineClass or "nixos"
|
||||
@@ -120,7 +107,7 @@ let
|
||||
moduleSystemConstructor.${machineClasses.${name}} {
|
||||
modules = [
|
||||
(config.outputs.moduleForMachine.${name} or { })
|
||||
(lib.modules.importApply ./machineModules/overridePkgs.nix {
|
||||
(lib.modules.importApply ../machineModules/overridePkgs.nix {
|
||||
pkgs = pkgsFor.${system};
|
||||
})
|
||||
];
|
||||
@@ -178,7 +165,6 @@ in
|
||||
config = {
|
||||
inventory.modules = clan-core.clanModules;
|
||||
inventory._legacyModules = clan-core.clanModules;
|
||||
# Merge the meta attributes from the buildClan function
|
||||
inventory.meta = config.meta;
|
||||
|
||||
outputs.moduleForMachine = lib.mkMerge [
|
||||
@@ -201,7 +187,7 @@ in
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(lib.modules.importApply ./machineModules/forName.nix {
|
||||
(lib.modules.importApply ../machineModules/forName.nix {
|
||||
inherit (config.inventory) meta;
|
||||
inherit
|
||||
name
|
||||
@@ -219,7 +205,7 @@ in
|
||||
networking.hostName = lib.mkDefault name;
|
||||
}
|
||||
)
|
||||
) inventoryClass.machines)
|
||||
) config.clanInternals.inventoryClass.machines)
|
||||
|
||||
# The user can define some machine config here
|
||||
# i.e. 'clan.machines.jon = ...'
|
||||
@@ -241,7 +227,39 @@ in
|
||||
inherit darwinConfigurations;
|
||||
|
||||
clanInternals = {
|
||||
inherit inventoryClass;
|
||||
inventoryClass =
|
||||
let
|
||||
localModuleSet =
|
||||
lib.filterAttrs (n: _: !inventory._legacyModules ? ${n}) inventory.modules // config.modules;
|
||||
flakeInputs = config.self.inputs;
|
||||
in
|
||||
{
|
||||
_module.args = {
|
||||
inherit clanLib;
|
||||
};
|
||||
imports = [
|
||||
../inventoryClass/builder/default.nix
|
||||
(lib.modules.importApply ../inventoryClass/service-list-from-inputs.nix {
|
||||
inherit localModuleSet flakeInputs clanLib;
|
||||
})
|
||||
{
|
||||
inherit inventory directory;
|
||||
}
|
||||
(
|
||||
{ config, ... }:
|
||||
{
|
||||
distributedServices = clanLib.inventory.mapInstances {
|
||||
inherit (config) inventory;
|
||||
inherit localModuleSet flakeInputs;
|
||||
prefix = [ "distributedServices" ];
|
||||
};
|
||||
machines = config.distributedServices.allMachines;
|
||||
}
|
||||
)
|
||||
../inventoryClass/inventory-introspection.nix
|
||||
];
|
||||
};
|
||||
|
||||
# TODO: remove this after a month or so
|
||||
# This is here for backwards compatibility for older CLI versions
|
||||
inventory = config.inventory;
|
||||
38
lib/modules/default.nix
Normal file
38
lib/modules/default.nix
Normal file
@@ -0,0 +1,38 @@
|
||||
## WARNING: Do not add core logic here.
|
||||
## This is only a wrapper such that 'clan' can be called as a function.
|
||||
{
|
||||
lib,
|
||||
clan-core,
|
||||
...
|
||||
}:
|
||||
rec {
|
||||
buildClan =
|
||||
# TODO: Once all templates and docs are migrated add: lib.warn "'buildClan' is deprecated. Use 'clan-core.lib.clan' instead"
|
||||
module: (clan module).config;
|
||||
|
||||
clan =
|
||||
{
|
||||
self ? lib.warn "Argument: 'self' must be set" null, # Reference to the current flake
|
||||
...
|
||||
}@m:
|
||||
let
|
||||
nixpkgs = self.inputs.nixpkgs or clan-core.inputs.nixpkgs;
|
||||
nix-darwin = self.inputs.nix-darwin or clan-core.inputs.nix-darwin;
|
||||
in
|
||||
lib.evalModules {
|
||||
class = "clan";
|
||||
specialArgs = {
|
||||
inherit
|
||||
self
|
||||
;
|
||||
inherit
|
||||
nixpkgs
|
||||
nix-darwin
|
||||
;
|
||||
};
|
||||
modules = [
|
||||
m
|
||||
clan-core.modules.clan.default
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
clanLib,
|
||||
clan-core,
|
||||
}:
|
||||
let
|
||||
eval = lib.evalModules {
|
||||
class = "nixos";
|
||||
modules = [
|
||||
(lib.modules.importApply ./interface.nix { inherit clanLib; })
|
||||
clan-core.modules.clan.default
|
||||
];
|
||||
};
|
||||
evalDocs = pkgs.nixosOptionsDoc {
|
||||
@@ -9,6 +9,9 @@ let
|
||||
);
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./clan/flake-module.nix
|
||||
];
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
@@ -19,7 +22,7 @@ in
|
||||
let
|
||||
jsonDocs = import ./eval-docs.nix {
|
||||
inherit pkgs lib;
|
||||
inherit (self) clanLib;
|
||||
clan-core = self;
|
||||
};
|
||||
in
|
||||
{
|
||||
@@ -3,9 +3,14 @@ let
|
||||
services = clanLib.callLib ./distributed-service/inventory-adapter.nix { };
|
||||
in
|
||||
{
|
||||
inherit (services) evalClanService mapInstances resolveModule;
|
||||
inherit (import ./build-inventory { inherit lib clanLib; }) buildInventory;
|
||||
interface = lib.modules.importApply ./build-inventory/interface.nix { inherit clanLib; };
|
||||
inherit (services) mapInstances;
|
||||
interface = {
|
||||
_file = "clanLib.inventory.interface";
|
||||
imports = [
|
||||
../inventoryClass/interface.nix
|
||||
];
|
||||
_module.args = { inherit clanLib; };
|
||||
};
|
||||
# Returns the list of machine names
|
||||
# { ... } -> [ string ]
|
||||
resolveTags =
|
||||
30
lib/modules/inventory/distributed-service/evalService.nix
Normal file
30
lib/modules/inventory/distributed-service/evalService.nix
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Example usage:
|
||||
|
||||
```nix
|
||||
evalService = import /this/file.nix { inherit lib clanLib; };
|
||||
result = evalService { modules = []; prefix = []; };
|
||||
|
||||
=> result.config
|
||||
=> result.options
|
||||
```
|
||||
*/
|
||||
{ lib, clanLib }:
|
||||
# <lambda evalService>
|
||||
{ modules, prefix }:
|
||||
lib.evalModules {
|
||||
class = "clan.service";
|
||||
specialArgs._ctx = prefix;
|
||||
modules =
|
||||
[
|
||||
# Base module
|
||||
./service-module.nix
|
||||
# Feature modules
|
||||
(lib.modules.importApply ./api-feature.nix {
|
||||
inherit clanLib prefix;
|
||||
})
|
||||
]
|
||||
++
|
||||
# Modules of caller
|
||||
modules;
|
||||
}
|
||||
@@ -15,67 +15,9 @@
|
||||
...
|
||||
}:
|
||||
let
|
||||
evalClanService =
|
||||
{ modules, prefix }:
|
||||
(lib.evalModules {
|
||||
class = "clan.service";
|
||||
specialArgs._ctx = prefix;
|
||||
modules = [
|
||||
./service-module.nix
|
||||
# feature modules
|
||||
(lib.modules.importApply ./api-feature.nix {
|
||||
inherit clanLib prefix;
|
||||
})
|
||||
] ++ modules;
|
||||
});
|
||||
|
||||
resolveModule =
|
||||
{
|
||||
moduleSpec,
|
||||
flakeInputs,
|
||||
localModuleSet,
|
||||
}:
|
||||
let
|
||||
# TODO:
|
||||
resolvedModuleSet =
|
||||
# If the module.name is self then take the modules defined in the flake
|
||||
# Otherwise its an external input which provides the modules via 'clan.modules' attribute
|
||||
if moduleSpec.input == null then
|
||||
localModuleSet
|
||||
else
|
||||
let
|
||||
input =
|
||||
flakeInputs.${moduleSpec.input} or (throw ''
|
||||
Flake doesn't provide input with name '${moduleSpec.input}'
|
||||
|
||||
Choose one of the following inputs:
|
||||
- ${
|
||||
builtins.concatStringsSep "\n- " (
|
||||
lib.attrNames (lib.filterAttrs (_name: input: input ? clan) flakeInputs)
|
||||
)
|
||||
}
|
||||
|
||||
To import a local module from 'clan.modules' remove the 'input' attribute from the module definition
|
||||
Remove the following line from the module definition:
|
||||
|
||||
...
|
||||
- module.input = "${moduleSpec.input}"
|
||||
|
||||
'');
|
||||
clanAttrs =
|
||||
input.clan
|
||||
or (throw "It seems the flake input ${moduleSpec.input} doesn't export any clan resources");
|
||||
in
|
||||
clanAttrs.modules;
|
||||
|
||||
resolvedModule =
|
||||
resolvedModuleSet.${moduleSpec.name}
|
||||
or (throw "flake doesn't provide clan-module with name ${moduleSpec.name}");
|
||||
in
|
||||
resolvedModule;
|
||||
resolveModule = import ./resolveModule.nix { inherit lib; };
|
||||
in
|
||||
{
|
||||
inherit evalClanService resolveModule;
|
||||
mapInstances =
|
||||
{
|
||||
# This is used to resolve the module imports from 'flake.inputs'
|
||||
@@ -151,7 +93,7 @@ in
|
||||
# TODO: Eagerly check the _class of the resolved module
|
||||
importedModulesEvaluated = lib.mapAttrs (
|
||||
module_ident: instances:
|
||||
evalClanService {
|
||||
clanLib.evalService {
|
||||
prefix = prefix ++ [ module_ident ];
|
||||
modules =
|
||||
[
|
||||
@@ -201,5 +143,4 @@ in
|
||||
importedModulesEvaluated
|
||||
;
|
||||
};
|
||||
|
||||
}
|
||||
43
lib/modules/inventory/distributed-service/resolveModule.nix
Normal file
43
lib/modules/inventory/distributed-service/resolveModule.nix
Normal file
@@ -0,0 +1,43 @@
|
||||
{ lib }:
|
||||
{
|
||||
moduleSpec,
|
||||
flakeInputs,
|
||||
localModuleSet,
|
||||
}:
|
||||
let
|
||||
resolvedModuleSet =
|
||||
# If the module.name is self then take the modules defined in the flake
|
||||
# Otherwise its an external input which provides the modules via 'clan.modules' attribute
|
||||
if moduleSpec.input == null then
|
||||
localModuleSet
|
||||
else
|
||||
let
|
||||
input =
|
||||
flakeInputs.${moduleSpec.input} or (throw ''
|
||||
Flake doesn't provide input with name '${moduleSpec.input}'
|
||||
|
||||
Choose one of the following inputs:
|
||||
- ${
|
||||
builtins.concatStringsSep "\n- " (
|
||||
lib.attrNames (lib.filterAttrs (_name: input: input ? clan) flakeInputs)
|
||||
)
|
||||
}
|
||||
|
||||
To import a local module from 'clan.modules' remove the 'input' attribute from the module definition
|
||||
Remove the following line from the module definition:
|
||||
|
||||
...
|
||||
- module.input = "${moduleSpec.input}"
|
||||
|
||||
'');
|
||||
clanAttrs =
|
||||
input.clan
|
||||
or (throw "It seems the flake input ${moduleSpec.input} doesn't export any clan resources");
|
||||
in
|
||||
clanAttrs.modules;
|
||||
|
||||
resolvedModule =
|
||||
resolvedModuleSet.${moduleSpec.name}
|
||||
or (throw "flake doesn't provide clan-module with name ${moduleSpec.name}");
|
||||
in
|
||||
resolvedModule
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user