1
0
forked from clan/clan-core

re-format with nixfmt

This commit is contained in:
Jörg Thalheim 2024-03-17 19:48:49 +01:00
parent 916e4dff84
commit e296a3019d
87 changed files with 2122 additions and 1650 deletions

View File

@ -14,21 +14,27 @@ let
}; };
in in
{ {
flake.nixosConfigurations = { inherit (clan.nixosConfigurations) test_backup_client; }; flake.nixosConfigurations = {
inherit (clan.nixosConfigurations) test_backup_client;
};
flake.clanInternals = clan.clanInternals; flake.clanInternals = clan.clanInternals;
flake.nixosModules = { flake.nixosModules = {
test_backup_server = { ... }: { test_backup_server =
imports = [ { ... }:
self.clanModules.borgbackup {
]; imports = [ self.clanModules.borgbackup ];
services.sshd.enable = true; services.sshd.enable = true;
services.borgbackup.repos.testrepo = { services.borgbackup.repos.testrepo = {
authorizedKeys = [ authorizedKeys = [ (builtins.readFile ../lib/ssh/pubkey) ];
(builtins.readFile ../lib/ssh/pubkey)
];
}; };
}; };
test_backup_client = { pkgs, lib, config, ... }: test_backup_client =
{
pkgs,
lib,
config,
...
}:
let let
dependencies = [ dependencies = [
self self
@ -38,14 +44,10 @@ in
closureInfo = pkgs.closureInfo { rootPaths = dependencies; }; closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
in in
{ {
imports = [ imports = [ self.clanModules.borgbackup ];
self.clanModules.borgbackup
];
networking.hostName = "client"; networking.hostName = "client";
services.sshd.enable = true; services.sshd.enable = true;
users.users.root.openssh.authorizedKeys.keyFiles = [ users.users.root.openssh.authorizedKeys.keyFiles = [ ../lib/ssh/pubkey ];
../lib/ssh/pubkey
];
systemd.tmpfiles.settings."vmsecrets" = { systemd.tmpfiles.settings."vmsecrets" = {
"/etc/secrets/borgbackup.ssh" = { "/etc/secrets/borgbackup.ssh" = {
@ -78,11 +80,11 @@ in
clan.borgbackup.destinations.test_backup_server.repo = "borg@server:."; clan.borgbackup.destinations.test_backup_server.repo = "borg@server:.";
}; };
}; };
perSystem = { nodes, pkgs, ... }: { perSystem =
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) { { nodes, pkgs, ... }:
test-backups =
(import ../lib/test-base.nix)
{ {
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) {
test-backups = (import ../lib/test-base.nix) {
name = "test-backups"; name = "test-backups";
nodes.server = { nodes.server = {
imports = [ imports = [
@ -135,8 +137,7 @@ in
client.succeed(f"clan --debug --flake ${../..} backups restore test_backup_client borgbackup {backup_id}") client.succeed(f"clan --debug --flake ${../..} backups restore test_backup_client borgbackup {backup_id}")
assert(client.succeed("cat /var/test-backups/somefile").strip() == "testing") assert(client.succeed("cat /var/test-backups/somefile").strip() == "testing")
''; '';
} } { inherit pkgs self; };
{ inherit pkgs self; };
}; };
}; };
} }

View File

@ -1,16 +1,18 @@
(import ../lib/test-base.nix) ({ ... }: { (import ../lib/test-base.nix) (
{ ... }:
{
name = "borgbackup"; name = "borgbackup";
nodes.machine = { self, pkgs, ... }: { nodes.machine =
{ self, pkgs, ... }:
{
imports = [ imports = [
self.clanModules.borgbackup self.clanModules.borgbackup
self.nixosModules.clanCore self.nixosModules.clanCore
{ {
services.openssh.enable = true; services.openssh.enable = true;
services.borgbackup.repos.testrepo = { services.borgbackup.repos.testrepo = {
authorizedKeys = [ authorizedKeys = [ (builtins.readFile ../lib/ssh/pubkey) ];
(builtins.readFile ../lib/ssh/pubkey)
];
}; };
} }
{ {
@ -45,4 +47,5 @@
machine.systemctl("start --wait borgbackup-job-test.service") machine.systemctl("start --wait borgbackup-job-test.service")
assert "machine-test" in machine.succeed("BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes /run/current-system/sw/bin/borg-job-test list") assert "machine-test" in machine.succeed("BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes /run/current-system/sw/bin/borg-job-test list")
''; '';
}) }
)

View File

@ -1,7 +1,11 @@
(import ../lib/container-test.nix) ({ ... }: { (import ../lib/container-test.nix) (
{ ... }:
{
name = "secrets"; name = "secrets";
nodes.machine = { ... }: { nodes.machine =
{ ... }:
{
networking.hostName = "machine"; networking.hostName = "machine";
services.openssh.enable = true; services.openssh.enable = true;
services.openssh.startWhenNeeded = false; services.openssh.startWhenNeeded = false;
@ -11,4 +15,5 @@
machine.succeed("systemctl status sshd") machine.succeed("systemctl status sshd")
machine.wait_for_unit("sshd") machine.wait_for_unit("sshd")
''; '';
}) }
)

View File

@ -1,7 +1,11 @@
(import ../lib/container-test.nix) ({ pkgs, ... }: { (import ../lib/container-test.nix) (
{ pkgs, ... }:
{
name = "secrets"; name = "secrets";
nodes.machine = { self, ... }: { nodes.machine =
{ self, ... }:
{
imports = [ imports = [
self.clanModules.deltachat self.clanModules.deltachat
self.nixosModules.clanCore self.nixosModules.clanCore
@ -21,4 +25,5 @@
# smtp # smtp
machine.succeed("${pkgs.netcat}/bin/nc -z -v ::1 25") machine.succeed("${pkgs.netcat}/bin/nc -z -v ::1 25")
''; '';
}) }
)

View File

@ -1,11 +1,19 @@
{ self, ... }: { { self, ... }:
{
imports = [ imports = [
./impure/flake-module.nix ./impure/flake-module.nix
./backups/flake-module.nix ./backups/flake-module.nix
./installation/flake-module.nix ./installation/flake-module.nix
./flash/flake-module.nix ./flash/flake-module.nix
]; ];
perSystem = { pkgs, lib, self', ... }: { perSystem =
{
pkgs,
lib,
self',
...
}:
{
checks = checks =
let let
nixosTestArgs = { nixosTestArgs = {
@ -24,14 +32,17 @@
syncthing = import ./syncthing nixosTestArgs; syncthing = import ./syncthing nixosTestArgs;
wayland-proxy-virtwl = import ./wayland-proxy-virtwl nixosTestArgs; wayland-proxy-virtwl = import ./wayland-proxy-virtwl nixosTestArgs;
}; };
schemaTests = pkgs.callPackages ./schemas.nix { schemaTests = pkgs.callPackages ./schemas.nix { inherit self; };
inherit self;
};
flakeOutputs = lib.mapAttrs' (name: config: lib.nameValuePair "nixos-${name}" config.config.system.build.toplevel) self.nixosConfigurations flakeOutputs =
lib.mapAttrs' (
name: config: lib.nameValuePair "nixos-${name}" config.config.system.build.toplevel
) self.nixosConfigurations
// lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages // lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages
// lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") self'.devShells // lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") self'.devShells
// lib.mapAttrs' (name: config: lib.nameValuePair "home-manager-${name}" config.activation-script) (self'.legacyPackages.homeConfigurations or { }); // lib.mapAttrs' (name: config: lib.nameValuePair "home-manager-${name}" config.activation-script) (
self'.legacyPackages.homeConfigurations or { }
);
in in
nixosTests // schemaTests // flakeOutputs; nixosTests // schemaTests // flakeOutputs;
legacyPackages = { legacyPackages = {

View File

@ -1,6 +1,12 @@
{ self, ... }: { self, ... }:
{ {
perSystem = { nodes, pkgs, lib, ... }: perSystem =
{
nodes,
pkgs,
lib,
...
}:
let let
dependencies = [ dependencies = [
self self
@ -14,9 +20,7 @@
in in
{ {
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) { checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) {
flash = flash = (import ../lib/test-base.nix) {
(import ../lib/test-base.nix)
{
name = "flash"; name = "flash";
nodes.target = { nodes.target = {
virtualisation.emptyDiskImages = [ 4096 ]; virtualisation.emptyDiskImages = [ 4096 ];
@ -39,8 +43,7 @@
start_all() start_all()
machine.succeed("clan --flake ${../..} flash --debug --yes --disk main /dev/vdb test_install_machine") machine.succeed("clan --flake ${../..} flash --debug --yes --disk main /dev/vdb test_install_machine")
''; '';
} } { inherit pkgs self; };
{ inherit pkgs self; };
}; };
}; };
} }

View File

@ -1,15 +1,19 @@
{ {
perSystem = { pkgs, lib, ... }: { perSystem =
{ pkgs, lib, ... }:
{
# a script that executes all other checks # a script that executes all other checks
packages.impure-checks = pkgs.writeShellScriptBin "impure-checks" '' packages.impure-checks = pkgs.writeShellScriptBin "impure-checks" ''
#!${pkgs.bash}/bin/bash #!${pkgs.bash}/bin/bash
set -euo pipefail set -euo pipefail
export PATH="${lib.makeBinPath [ export PATH="${
lib.makeBinPath [
pkgs.gitMinimal pkgs.gitMinimal
pkgs.nix pkgs.nix
pkgs.rsync # needed to have rsync installed on the dummy ssh server pkgs.rsync # needed to have rsync installed on the dummy ssh server
]}" ]
}"
ROOT=$(git rev-parse --show-toplevel) ROOT=$(git rev-parse --show-toplevel)
cd "$ROOT/pkgs/clan-cli" cd "$ROOT/pkgs/clan-cli"
nix develop "$ROOT#clan-cli" -c bash -c "TMPDIR=/tmp python -m pytest -s -m impure ./tests $@" nix develop "$ROOT#clan-cli" -c bash -c "TMPDIR=/tmp python -m pytest -s -m impure ./tests $@"

View File

@ -12,10 +12,14 @@ let
}; };
in in
{ {
flake.nixosConfigurations = { inherit (clan.nixosConfigurations) test_install_machine; }; flake.nixosConfigurations = {
inherit (clan.nixosConfigurations) test_install_machine;
};
flake.clanInternals = clan.clanInternals; flake.clanInternals = clan.clanInternals;
flake.nixosModules = { flake.nixosModules = {
test_install_machine = { lib, modulesPath, ... }: { test_install_machine =
{ lib, modulesPath, ... }:
{
imports = [ imports = [
self.clanModules.diskLayouts self.clanModules.diskLayouts
(modulesPath + "/testing/test-instrumentation.nix") # we need these 2 modules always to be able to run the tests (modulesPath + "/testing/test-instrumentation.nix") # we need these 2 modules always to be able to run the tests
@ -26,12 +30,16 @@ in
environment.etc."install-successful".text = "ok"; environment.etc."install-successful".text = "ok";
boot.consoleLogLevel = lib.mkForce 100; boot.consoleLogLevel = lib.mkForce 100;
boot.kernelParams = [ boot.kernelParams = [ "boot.shell_on_fail" ];
"boot.shell_on_fail"
];
}; };
}; };
perSystem = { nodes, pkgs, lib, ... }: perSystem =
{
nodes,
pkgs,
lib,
...
}:
let let
dependencies = [ dependencies = [
self self
@ -45,15 +53,11 @@ in
in in
{ {
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) { checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) {
test-installation = test-installation = (import ../lib/test-base.nix) {
(import ../lib/test-base.nix)
{
name = "test-installation"; name = "test-installation";
nodes.target = { nodes.target = {
services.openssh.enable = true; services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keyFiles = [ users.users.root.openssh.authorizedKeys.keyFiles = [ ../lib/ssh/pubkey ];
../lib/ssh/pubkey
];
system.nixos.variant_id = "installer"; system.nixos.variant_id = "installer";
virtualisation.emptyDiskImages = [ 4096 ]; virtualisation.emptyDiskImages = [ 4096 ];
nix.settings = { nix.settings = {
@ -111,8 +115,7 @@ in
new_machine = create_test_machine(oldmachine=target, args={ "name": "new_machine" }) new_machine = create_test_machine(oldmachine=target, args={ "name": "new_machine" })
assert(new_machine.succeed("cat /etc/install-successful").strip() == "ok") assert(new_machine.succeed("cat /etc/install-successful").strip() == "ok")
''; '';
} } { inherit pkgs self; };
{ inherit pkgs self; };
}; };
}; };
} }

View File

@ -1,17 +1,23 @@
{ hostPkgs, lib, config, ... }: {
hostPkgs,
lib,
config,
...
}:
let let
testDriver = hostPkgs.python3.pkgs.callPackage ./package.nix { testDriver = hostPkgs.python3.pkgs.callPackage ./package.nix {
inherit (config) extraPythonPackages; inherit (config) extraPythonPackages;
inherit (hostPkgs.pkgs) util-linux systemd; inherit (hostPkgs.pkgs) util-linux systemd;
}; };
containers = map (m: m.system.build.toplevel) (lib.attrValues config.nodes); containers = map (m: m.system.build.toplevel) (lib.attrValues config.nodes);
pythonizeName = name: pythonizeName =
name:
let let
head = lib.substring 0 1 name; head = lib.substring 0 1 name;
tail = lib.substring 1 (-1) name; tail = lib.substring 1 (-1) name;
in in
(if builtins.match "[A-z_]" head == null then "_" else head) + (if builtins.match "[A-z_]" head == null then "_" else head)
lib.stringAsChars (c: if builtins.match "[A-z0-9_]" c == null then "_" else c) tail; + lib.stringAsChars (c: if builtins.match "[A-z0-9_]" c == null then "_" else c) tail;
nodeHostNames = nodeHostNames =
let let
nodesList = map (c: c.system.name) (lib.attrValues config.nodes); nodesList = map (c: c.system.name) (lib.attrValues config.nodes);
@ -21,7 +27,8 @@ let
pythonizedNames = map pythonizeName nodeHostNames; pythonizedNames = map pythonizeName nodeHostNames;
in in
{ {
driver = lib.mkForce (hostPkgs.runCommand "nixos-test-driver-${config.name}" driver = lib.mkForce (
hostPkgs.runCommand "nixos-test-driver-${config.name}"
{ {
nativeBuildInputs = [ nativeBuildInputs = [
hostPkgs.makeWrapper hostPkgs.makeWrapper
@ -61,9 +68,11 @@ in
wrapProgram $out/bin/nixos-test-driver \ wrapProgram $out/bin/nixos-test-driver \
${lib.concatStringsSep " " (map (name: "--add-flags '--container ${name}'") containers)} \ ${lib.concatStringsSep " " (map (name: "--add-flags '--container ${name}'") containers)} \
--add-flags "--test-script '$out/test-script'" --add-flags "--test-script '$out/test-script'"
''); ''
);
test = lib.mkForce (lib.lazyDerivation { test = lib.mkForce (
lib.lazyDerivation {
# lazyDerivation improves performance when only passthru items and/or meta are used. # lazyDerivation improves performance when only passthru items and/or meta are used.
derivation = hostPkgs.stdenv.mkDerivation { derivation = hostPkgs.stdenv.mkDerivation {
name = "vm-test-run-${config.name}"; name = "vm-test-run-${config.name}";
@ -84,5 +93,6 @@ in
meta = config.meta; meta = config.meta;
}; };
inherit (config) passthru meta; inherit (config) passthru meta;
}); }
);
} }

View File

@ -1,8 +1,18 @@
{ extraPythonPackages, python3Packages, buildPythonApplication, setuptools, util-linux, systemd }: {
extraPythonPackages,
python3Packages,
buildPythonApplication,
setuptools,
util-linux,
systemd,
}:
buildPythonApplication { buildPythonApplication {
pname = "test-driver"; pname = "test-driver";
version = "0.0.1"; version = "0.0.1";
propagatedBuildInputs = [ util-linux systemd ] ++ extraPythonPackages python3Packages; propagatedBuildInputs = [
util-linux
systemd
] ++ extraPythonPackages python3Packages;
nativeBuildInputs = [ setuptools ]; nativeBuildInputs = [ setuptools ];
format = "pyproject"; format = "pyproject";
src = ./.; src = ./.;

View File

@ -1,13 +1,12 @@
test: test:
{ pkgs { pkgs, self, ... }:
, self
, ...
}:
let let
inherit (pkgs) lib; inherit (pkgs) lib;
nixos-lib = import (pkgs.path + "/nixos/lib") { }; nixos-lib = import (pkgs.path + "/nixos/lib") { };
in in
(nixos-lib.runTest ({ hostPkgs, ... }: { (nixos-lib.runTest (
{ hostPkgs, ... }:
{
hostPkgs = pkgs; hostPkgs = pkgs;
# speed-up evaluation # speed-up evaluation
defaults = { defaults = {
@ -30,4 +29,5 @@ in
test test
./container-driver/module.nix ./container-driver/module.nix
]; ];
})).config.result }
)).config.result

View File

@ -1,8 +1,5 @@
test: test:
{ pkgs { pkgs, self, ... }:
, self
, ...
}:
let let
inherit (pkgs) lib; inherit (pkgs) lib;
nixos-lib = import (pkgs.path + "/nixos/lib") { }; nixos-lib = import (pkgs.path + "/nixos/lib") { };

View File

@ -1,35 +1,48 @@
{ self, runCommand, check-jsonschema, pkgs, lib, ... }: {
self,
runCommand,
check-jsonschema,
pkgs,
lib,
...
}:
let let
clanModules.clanCore = self.nixosModules.clanCore; clanModules.clanCore = self.nixosModules.clanCore;
baseModule = { baseModule = {
imports = imports = (import (pkgs.path + "/nixos/modules/module-list.nix")) ++ [
(import (pkgs.path + "/nixos/modules/module-list.nix")) {
++ [{
nixpkgs.hostPlatform = "x86_64-linux"; nixpkgs.hostPlatform = "x86_64-linux";
clanCore.clanName = "dummy"; clanCore.clanName = "dummy";
}]; }
];
}; };
optionsFromModule = module: optionsFromModule =
module:
let let
evaled = lib.evalModules { evaled = lib.evalModules {
modules = [ module baseModule ]; modules = [
module
baseModule
];
}; };
in in
evaled.options.clan; evaled.options.clan;
clanModuleSchemas = lib.mapAttrs (_: module: self.lib.jsonschema.parseOptions (optionsFromModule module)) clanModules; clanModuleSchemas = lib.mapAttrs (
_: module: self.lib.jsonschema.parseOptions (optionsFromModule module)
) clanModules;
mkTest = name: schema: runCommand "schema-${name}" { } '' mkTest =
name: schema:
runCommand "schema-${name}" { } ''
${check-jsonschema}/bin/check-jsonschema \ ${check-jsonschema}/bin/check-jsonschema \
--check-metaschema ${builtins.toFile "schema-${name}" (builtins.toJSON schema)} --check-metaschema ${builtins.toFile "schema-${name}" (builtins.toJSON schema)}
touch $out touch $out
''; '';
in in
lib.mapAttrs' lib.mapAttrs' (name: schema: {
(name: schema: {
name = "schema-${name}"; name = "schema-${name}";
value = mkTest name schema; value = mkTest name schema;
}) }) clanModuleSchemas
clanModuleSchemas

View File

@ -1,10 +1,10 @@
(import ../lib/test-base.nix) { (import ../lib/test-base.nix) {
name = "secrets"; name = "secrets";
nodes.machine = { self, config, ... }: { nodes.machine =
imports = [ { self, config, ... }:
(self.nixosModules.clanCore) {
]; imports = [ (self.nixosModules.clanCore) ];
environment.etc."secret".source = config.sops.secrets.secret.path; environment.etc."secret".source = config.sops.secrets.secret.path;
environment.etc."group-secret".source = config.sops.secrets.group-secret.path; environment.etc."group-secret".source = config.sops.secrets.group-secret.path;
sops.age.keyFile = ./key.age; sops.age.keyFile = ./key.age;

View File

@ -1,7 +1,16 @@
import ../lib/test-base.nix ({ config, pkgs, lib, ... }: { import ../lib/test-base.nix (
{
config,
pkgs,
lib,
...
}:
{
name = "wayland-proxy-virtwl"; name = "wayland-proxy-virtwl";
nodes.machine = { self, ... }: { nodes.machine =
{ self, ... }:
{
imports = [ imports = [
self.nixosModules.clanCore self.nixosModules.clanCore
{ {
@ -22,4 +31,5 @@ import ../lib/test-base.nix ({ config, pkgs, lib, ... }: {
# use machinectl # use machinectl
machine.succeed("machinectl shell .host ${config.nodes.machine.systemd.package}/bin/systemctl --user start wayland-proxy-virtwl >&2") machine.succeed("machinectl shell .host ${config.nodes.machine.systemd.package}/bin/systemctl --user start wayland-proxy-virtwl >&2")
''; '';
}) }
)

View File

@ -1,7 +1,11 @@
(import ../lib/container-test.nix) ({ pkgs, ... }: { (import ../lib/container-test.nix) (
{ pkgs, ... }:
{
name = "zt-tcp-relay"; name = "zt-tcp-relay";
nodes.machine = { self, ... }: { nodes.machine =
{ self, ... }:
{
imports = [ imports = [
self.nixosModules.clanCore self.nixosModules.clanCore
self.clanModules.zt-tcp-relay self.clanModules.zt-tcp-relay
@ -17,4 +21,5 @@
out = machine.succeed("${pkgs.netcat}/bin/nc -z -v localhost 4443") out = machine.succeed("${pkgs.netcat}/bin/nc -z -v localhost 4443")
print(out) print(out)
''; '';
}) }
)

View File

@ -1,10 +1,18 @@
{ config, lib, pkgs, ... }: {
config,
lib,
pkgs,
...
}:
let let
cfg = config.clan.borgbackup; cfg = config.clan.borgbackup;
in in
{ {
options.clan.borgbackup.destinations = lib.mkOption { options.clan.borgbackup.destinations = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: { type = lib.types.attrsOf (
lib.types.submodule (
{ name, ... }:
{
options = { options = {
name = lib.mkOption { name = lib.mkOption {
type = lib.types.str; type = lib.types.str;
@ -17,23 +25,31 @@ in
}; };
rsh = lib.mkOption { rsh = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = "ssh -i ${config.clanCore.secrets.borgbackup.secrets."borgbackup.ssh".path} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"; default = "ssh -i ${
config.clanCore.secrets.borgbackup.secrets."borgbackup.ssh".path
} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null";
description = "the rsh to use for the backup"; description = "the rsh to use for the backup";
}; };
}; };
})); }
)
);
default = { }; default = { };
description = '' description = ''
destinations where the machine should be backuped to destinations where the machine should be backuped to
''; '';
}; };
imports = [ (lib.mkRemovedOptionModule [ "clan" "borgbackup" "enable" ] "Just define clan.borgbackup.destinations to enable it") ]; imports = [
(lib.mkRemovedOptionModule [
"clan"
"borgbackup"
"enable"
] "Just define clan.borgbackup.destinations to enable it")
];
config = lib.mkIf (cfg.destinations != { }) { config = lib.mkIf (cfg.destinations != { }) {
services.borgbackup.jobs = lib.mapAttrs services.borgbackup.jobs = lib.mapAttrs (_: dest: {
(_: dest: {
paths = lib.flatten (map (state: state.folders) (lib.attrValues config.clanCore.state)); paths = lib.flatten (map (state: state.folders) (lib.attrValues config.clanCore.state));
exclude = [ "*.pyc" ]; exclude = [ "*.pyc" ];
repo = dest.repo; repo = dest.repo;
@ -56,14 +72,17 @@ in
weekly = 4; weekly = 4;
monthly = 0; monthly = 0;
}; };
}) }) cfg.destinations;
cfg.destinations;
clanCore.secrets.borgbackup = { clanCore.secrets.borgbackup = {
facts."borgbackup.ssh.pub" = { }; facts."borgbackup.ssh.pub" = { };
secrets."borgbackup.ssh" = { }; secrets."borgbackup.ssh" = { };
secrets."borgbackup.repokey" = { }; secrets."borgbackup.repokey" = { };
generator.path = [ pkgs.openssh pkgs.coreutils pkgs.xkcdpass ]; generator.path = [
pkgs.openssh
pkgs.coreutils
pkgs.xkcdpass
];
generator.script = '' generator.script = ''
ssh-keygen -t ed25519 -N "" -f "$secrets"/borgbackup.ssh ssh-keygen -t ed25519 -N "" -f "$secrets"/borgbackup.ssh
mv "$secrets"/borgbackup.ssh.pub "$facts"/borgbackup.ssh.pub mv "$secrets"/borgbackup.ssh.pub "$facts"/borgbackup.ssh.pub
@ -75,8 +94,9 @@ in
# TODO list needs to run locally or on the remote machine # TODO list needs to run locally or on the remote machine
list = '' list = ''
# we need yes here to skip the changed url verification # we need yes here to skip the changed url verification
${lib.concatMapStringsSep "\n" (dest: ''yes y | borg-job-${dest.name} list --json | jq -r '. + {"job-name": "${dest.name}"}' '') ${lib.concatMapStringsSep "\n" (
(lib.attrValues cfg.destinations)} dest: ''yes y | borg-job-${dest.name} list --json | jq -r '. + {"job-name": "${dest.name}"}' ''
) (lib.attrValues cfg.destinations)}
''; '';
create = '' create = ''
${lib.concatMapStringsSep "\n" (dest: '' ${lib.concatMapStringsSep "\n" (dest: ''

View File

@ -1,4 +1,5 @@
{ config, pkgs, ... }: { { config, pkgs, ... }:
{
networking.firewall.interfaces."zt+".allowedTCPPorts = [ 25 ]; # smtp with other hosts networking.firewall.interfaces."zt+".allowedTCPPorts = [ 25 ]; # smtp with other hosts
environment.systemPackages = [ pkgs.deltachat-desktop ]; environment.systemPackages = [ pkgs.deltachat-desktop ];
@ -134,9 +135,7 @@
storage &local_mailboxes storage &local_mailboxes
} }
''; '';
ensureAccounts = [ ensureAccounts = [ "user@${domain}" ];
"user@${domain}"
];
ensureCredentials = { ensureCredentials = {
"user@${domain}".passwordFile = pkgs.writeText "dummy" "foobar"; "user@${domain}".passwordFile = pkgs.writeText "dummy" "foobar";
}; };

View File

@ -41,4 +41,3 @@
}; };
}; };
} }

View File

@ -1,4 +1,5 @@
{ inputs, ... }: { { inputs, ... }:
{
flake.clanModules = { flake.clanModules = {
diskLayouts = { diskLayouts = {
imports = [ imports = [

View File

@ -1,4 +1 @@
_: _: { fonts.enableDefaultPackages = true; }
{
fonts.enableDefaultPackages = true;
}

View File

@ -1,7 +1,8 @@
{ config {
, pkgs config,
, lib pkgs,
, ... lib,
...
}: }:
{ {
# Integration can be improved, if the following issues get implemented: # Integration can be improved, if the following issues get implemented:

View File

@ -1,4 +1,5 @@
{ pkgs, ... }: { { pkgs, ... }:
{
hardware.opengl.enable = true; hardware.opengl.enable = true;
environment.systemPackages = [ pkgs.moonlight-qt ]; environment.systemPackages = [ pkgs.moonlight-qt ];
} }

View File

@ -1,15 +1,21 @@
{ config, pkgs, ... }: { { config, pkgs, ... }:
{
services.openssh.enable = true; services.openssh.enable = true;
services.openssh.hostKeys = [{ services.openssh.hostKeys = [
{
path = config.clanCore.secrets.openssh.secrets."ssh.id_ed25519".path; path = config.clanCore.secrets.openssh.secrets."ssh.id_ed25519".path;
type = "ed25519"; type = "ed25519";
}]; }
];
clanCore.secrets.openssh = { clanCore.secrets.openssh = {
secrets."ssh.id_ed25519" = { }; secrets."ssh.id_ed25519" = { };
facts."ssh.id_ed25519.pub" = { }; facts."ssh.id_ed25519.pub" = { };
generator.path = [ pkgs.coreutils pkgs.openssh ]; generator.path = [
pkgs.coreutils
pkgs.openssh
];
generator.script = '' generator.script = ''
ssh-keygen -t ed25519 -N "" -f $secrets/ssh.id_ed25519 ssh-keygen -t ed25519 -N "" -f $secrets/ssh.id_ed25519
mv $secrets/ssh.id_ed25519.pub $facts/ssh.id_ed25519.pub mv $secrets/ssh.id_ed25519.pub $facts/ssh.id_ed25519.pub

View File

@ -1,7 +1,7 @@
{ pkgs, options, ... }: { pkgs, options, ... }:
let let
apps = pkgs.writeText "apps.json" (builtins.toJSON apps = pkgs.writeText "apps.json" (
{ builtins.toJSON {
env = { env = {
PATH = "$(PATH):$(HOME)/.local/bin:/run/current-system/sw/bin"; PATH = "$(PATH):$(HOME)/.local/bin:/run/current-system/sw/bin";
}; };
@ -22,13 +22,12 @@ let
} }
{ {
name = "Steam Big Picture"; name = "Steam Big Picture";
detached = [ detached = [ "setsid steam steam://open/bigpicture" ];
"setsid steam steam://open/bigpicture"
];
image-path = "steam.png"; image-path = "steam.png";
} }
]; ];
}); }
);
sunshineConfiguration = pkgs.writeText "sunshine.conf" '' sunshineConfiguration = pkgs.writeText "sunshine.conf" ''
address_family = both address_family = both
channels = 5 channels = 5
@ -78,11 +77,9 @@ in
environment.systemPackages = [ environment.systemPackages = [
pkgs.sunshine pkgs.sunshine
(pkgs.writers.writeDashBin "sun" '' (pkgs.writers.writeDashBin "sun" ''
${pkgs.sunshine}/bin/sunshine -1 ${ ${pkgs.sunshine}/bin/sunshine -1 ${pkgs.writeText "sunshine.conf" ''
pkgs.writeText "sunshine.conf" ''
address_family = both address_family = both
'' ''} "$@"
} "$@"
'') '')
# Create a dummy account, for easier setup, # Create a dummy account, for easier setup,
# don't use this account in actual production yet. # don't use this account in actual production yet.
@ -113,11 +110,7 @@ in
}; };
}; };
systemd.tmpfiles.rules = [ "d '/var/lib/sunshine' 0770 'user' 'users' - -" ];
systemd.tmpfiles.rules = [
"d '/var/lib/sunshine' 0770 'user' 'users' - -"
];
systemd.user.services.sunshine = { systemd.user.services.sunshine = {
enable = true; enable = true;
@ -128,9 +121,7 @@ in
serviceConfig = { serviceConfig = {
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "5s"; RestartSec = "5s";
ReadWritePaths = [ ReadWritePaths = [ "/var/lib/sunshine" ];
"/var/lib/sunshine"
];
}; };
wantedBy = [ "graphical-session.target" ]; wantedBy = [ "graphical-session.target" ];
}; };

View File

@ -1,7 +1,8 @@
{ config {
, pkgs config,
, lib pkgs,
, ... lib,
...
}: }:
{ {
options.clan.syncthing = { options.clan.syncthing = {
@ -53,9 +54,9 @@
assertions = [ assertions = [
{ {
assertion = assertion = lib.all (
lib.all (attr: builtins.hasAttr attr config.services.syncthing.settings.folders) attr: builtins.hasAttr attr config.services.syncthing.settings.folders
config.clan.syncthing.autoShares; ) config.clan.syncthing.autoShares;
message = '' message = ''
Syncthing: If you want to AutoShare a folder, you need to have it configured on the sharing device. Syncthing: If you want to AutoShare a folder, you need to have it configured on the sharing device.
''; '';
@ -80,12 +81,8 @@
group = "syncthing"; group = "syncthing";
key = key = lib.mkDefault config.clan.secrets.syncthing.secrets."syncthing.key".path or null;
lib.mkDefault cert = lib.mkDefault config.clan.secrets.syncthing.secrets."syncthing.cert".path or null;
config.clan.secrets.syncthing.secrets."syncthing.key".path or null;
cert =
lib.mkDefault
config.clan.secrets.syncthing.secrets."syncthing.cert".path or null;
settings = { settings = {
options = { options = {
@ -127,38 +124,24 @@
set -x set -x
# query pending deviceID's # query pending deviceID's
APIKEY=$(cat ${apiKey}) APIKEY=$(cat ${apiKey})
PENDING=$(${ PENDING=$(${lib.getExe pkgs.curl} -X GET -H "X-API-Key: $APIKEY" ${baseAddress}${getPendingDevices})
lib.getExe pkgs.curl
} -X GET -H "X-API-Key: $APIKEY" ${baseAddress}${getPendingDevices})
PENDING=$(echo $PENDING | ${lib.getExe pkgs.jq} keys[]) PENDING=$(echo $PENDING | ${lib.getExe pkgs.jq} keys[])
# accept pending deviceID's # accept pending deviceID's
for ID in $PENDING;do for ID in $PENDING;do
${ ${lib.getExe pkgs.curl} -X POST -d "{\"deviceId\": $ID}" -H "Content-Type: application/json" -H "X-API-Key: $APIKEY" ${baseAddress}${postNewDevice}
lib.getExe pkgs.curl
} -X POST -d "{\"deviceId\": $ID}" -H "Content-Type: application/json" -H "X-API-Key: $APIKEY" ${baseAddress}${postNewDevice}
# get all shared folders by their ID # get all shared folders by their ID
for folder in ${builtins.toString config.clan.syncthing.autoShares}; do for folder in ${builtins.toString config.clan.syncthing.autoShares}; do
SHARED_IDS=$(${ SHARED_IDS=$(${lib.getExe pkgs.curl} -X GET -H "X-API-Key: $APIKEY" ${baseAddress}${SharedFolderById}"$folder" | ${lib.getExe pkgs.jq} ."devices")
lib.getExe pkgs.curl PATCHED_IDS=$(echo $SHARED_IDS | ${lib.getExe pkgs.jq} ".+= [{\"deviceID\": $ID, \"introducedBy\": \"\", \"encryptionPassword\": \"\"}]")
} -X GET -H "X-API-Key: $APIKEY" ${baseAddress}${SharedFolderById}"$folder" | ${ ${lib.getExe pkgs.curl} -X PATCH -d "{\"devices\": $PATCHED_IDS}" -H "X-API-Key: $APIKEY" ${baseAddress}${SharedFolderById}"$folder"
lib.getExe pkgs.jq
} ."devices")
PATCHED_IDS=$(echo $SHARED_IDS | ${
lib.getExe pkgs.jq
} ".+= [{\"deviceID\": $ID, \"introducedBy\": \"\", \"encryptionPassword\": \"\"}]")
${
lib.getExe pkgs.curl
} -X PATCH -d "{\"devices\": $PATCHED_IDS}" -H "X-API-Key: $APIKEY" ${baseAddress}${SharedFolderById}"$folder"
done done
done done
''; '';
}; };
systemd.timers.syncthing-auto-accept = systemd.timers.syncthing-auto-accept = lib.mkIf config.clan.syncthing.autoAcceptDevices {
lib.mkIf config.clan.syncthing.autoAcceptDevices
{
description = "Syncthing Auto Accept"; description = "Syncthing Auto Accept";
wantedBy = [ "syncthing-auto-accept.service" ]; wantedBy = [ "syncthing-auto-accept.service" ];
@ -182,9 +165,7 @@
set -efu pipefail set -efu pipefail
APIKEY=$(cat ${apiKey}) APIKEY=$(cat ${apiKey})
${ ${lib.getExe pkgs.gnused} -i "s/<apikey>.*<\/apikey>/<apikey>$APIKEY<\/apikey>/" /var/lib/syncthing/config.xml
lib.getExe pkgs.gnused
} -i "s/<apikey>.*<\/apikey>/<apikey>$APIKEY<\/apikey>/" /var/lib/syncthing/config.xml
# sudo systemctl restart syncthing.service # sudo systemctl restart syncthing.service
systemctl restart syncthing.service systemctl restart syncthing.service
''; '';

View File

@ -1,7 +1,8 @@
{ pkgs {
, lib pkgs,
, config lib,
, ... config,
...
}: }:
{ {
options.clan.services.waypipe = { options.clan.services.waypipe = {
@ -49,7 +50,10 @@
isNormalUser = true; isNormalUser = true;
uid = 1000; uid = 1000;
password = ""; password = "";
extraGroups = [ "wheel" "video" ]; extraGroups = [
"wheel"
"video"
];
shell = "/run/current-system/sw/bin/bash"; shell = "/run/current-system/sw/bin/bash";
}; };

View File

@ -1,4 +1,10 @@
{ pkgs, lib, config, ... }: { {
pkgs,
lib,
config,
...
}:
{
options.clan.zt-tcp-relay = { options.clan.zt-tcp-relay = {
port = lib.mkOption { port = lib.mkOption {
type = lib.types.port; type = lib.types.port;
@ -13,7 +19,9 @@
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
after = [ "network.target" ]; after = [ "network.target" ];
serviceConfig = { serviceConfig = {
ExecStart = "${pkgs.callPackage ../pkgs/zt-tcp-relay {}}/bin/zt-tcp-relay --listen [::]:${builtins.toString config.clan.zt-tcp-relay.port}"; ExecStart = "${
pkgs.callPackage ../pkgs/zt-tcp-relay { }
}/bin/zt-tcp-relay --listen [::]:${builtins.toString config.clan.zt-tcp-relay.port}";
Restart = "always"; Restart = "always";
RestartSec = "5"; RestartSec = "5";
dynamicUsers = true; dynamicUsers = true;

View File

@ -1,9 +1,10 @@
{ {
perSystem = perSystem =
{ pkgs {
, self' pkgs,
, lib self',
, ... lib,
...
}: }:
let let
python3 = pkgs.python3; python3 = pkgs.python3;
@ -20,9 +21,7 @@
ps.pygobject3 ps.pygobject3
] ]
); );
linuxOnlyPackages = lib.optionals pkgs.stdenv.isLinux [ linuxOnlyPackages = lib.optionals pkgs.stdenv.isLinux [ pkgs.xdg-utils ];
pkgs.xdg-utils
];
in in
{ {
devShells.python = pkgs.mkShell { devShells.python = pkgs.mkShell {

View File

@ -1,9 +1,10 @@
{ {
perSystem = perSystem =
{ pkgs {
, self' pkgs,
, config self',
, ... config,
...
}: }:
let let
writers = pkgs.callPackage ./pkgs/builders/script-writers.nix { }; writers = pkgs.callPackage ./pkgs/builders/script-writers.nix { };
@ -16,8 +17,7 @@
# A python program to switch between dev-shells # A python program to switch between dev-shells
# usage: select-shell shell-name # usage: select-shell shell-name
# the currently enabled dev-shell gets stored in ./.direnv/selected-shell # the currently enabled dev-shell gets stored in ./.direnv/selected-shell
select-shell = writers.writePython3Bin "select-shell" select-shell = writers.writePython3Bin "select-shell" {
{
flakeIgnore = [ "E501" ]; flakeIgnore = [ "E501" ];
} ./pkgs/scripts/select-shell.py; } ./pkgs/scripts/select-shell.py;
in in

View File

@ -2,7 +2,9 @@
description = "clan.lol base operating system"; description = "clan.lol base operating system";
nixConfig.extra-substituters = [ "https://cache.clan.lol" ]; nixConfig.extra-substituters = [ "https://cache.clan.lol" ];
nixConfig.extra-trusted-public-keys = [ "cache.clan.lol-1:3KztgSAB5R1M+Dz7vzkBGzXdodizbgLXGXKXlcQLA28=" ]; nixConfig.extra-trusted-public-keys = [
"cache.clan.lol-1:3KztgSAB5R1M+Dz7vzkBGzXdodizbgLXGXKXlcQLA28="
];
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable-small"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable-small";
@ -20,8 +22,11 @@
treefmt-nix.inputs.nixpkgs.follows = "nixpkgs"; treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
}; };
outputs = inputs @ { flake-parts, ... }: outputs =
flake-parts.lib.mkFlake { inherit inputs; } ({ lib, ... }: { inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } (
{ lib, ... }:
{
systems = [ systems = [
"x86_64-linux" "x86_64-linux"
"aarch64-linux" "aarch64-linux"
@ -44,20 +49,15 @@
clanInternals = lib.mkOption { clanInternals = lib.mkOption {
type = lib.types.submodule { type = lib.types.submodule {
options = { options = {
all-machines-json = lib.mkOption { all-machines-json = lib.mkOption { type = lib.types.attrsOf lib.types.str; };
type = lib.types.attrsOf lib.types.str; machines = lib.mkOption { type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified); };
}; machinesFunc = lib.mkOption { type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified); };
machines = lib.mkOption {
type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified);
};
machinesFunc = lib.mkOption {
type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified);
};
}; };
}; };
}; };
}; };
} }
]; ];
}); }
);
} }

View File

@ -1,11 +1,9 @@
{ lib { lib, inputs, ... }:
, inputs {
, ... imports = [ inputs.treefmt-nix.flakeModule ];
}: { perSystem =
imports = [ { self', pkgs, ... }:
inputs.treefmt-nix.flakeModule {
];
perSystem = { self', pkgs, ... }: {
treefmt.projectRootFile = "flake.nix"; treefmt.projectRootFile = "flake.nix";
treefmt.programs.shellcheck.enable = true; treefmt.programs.shellcheck.enable = true;

View File

@ -1,38 +1,51 @@
{ clan-core, nixpkgs, lib }: {
{ directory # The directory containing the machines subdirectory clan-core,
, specialArgs ? { } # Extra arguments to pass to nixosSystem i.e. useful to make self available nixpkgs,
, machines ? { } # allows to include machine-specific modules i.e. machines.${name} = { ... } lib,
, clanName # Needs to be (globally) unique, as this determines the folder name where the flake gets downloaded to. }:
, clanIcon ? null # A path to an icon to be used for the clan, should be the same for all machines {
, pkgsForSystem ? (_system: null) # A map from arch to pkgs, if specified this nixpkgs will be only imported once for each system. directory, # The directory containing the machines subdirectory
# This improves performance, but all nipxkgs.* options will be ignored. specialArgs ? { }, # Extra arguments to pass to nixosSystem i.e. useful to make self available
machines ? { }, # allows to include machine-specific modules i.e. machines.${name} = { ... }
clanName, # Needs to be (globally) unique, as this determines the folder name where the flake gets downloaded to.
clanIcon ? null, # A path to an icon to be used for the clan, should be the same for all machines
pkgsForSystem ? (_system: null), # 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.
}: }:
let let
machinesDirs = lib.optionalAttrs (builtins.pathExists "${directory}/machines") (builtins.readDir (directory + /machines)); machinesDirs = lib.optionalAttrs (builtins.pathExists "${directory}/machines") (
builtins.readDir (directory + /machines)
);
machineSettings = machineName: machineSettings =
machineName:
# CLAN_MACHINE_SETTINGS_FILE allows to override the settings file temporarily # CLAN_MACHINE_SETTINGS_FILE allows to override the settings file temporarily
# This is useful for doing a dry-run before writing changes into the settings.json # This is useful for doing a dry-run before writing changes into the settings.json
# Using CLAN_MACHINE_SETTINGS_FILE requires passing --impure to nix eval # Using CLAN_MACHINE_SETTINGS_FILE requires passing --impure to nix eval
if builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE" != "" if builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE" != "" then
then builtins.fromJSON (builtins.readFile (builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE")) builtins.fromJSON (builtins.readFile (builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE"))
else else
lib.optionalAttrs (builtins.pathExists "${directory}/machines/${machineName}/settings.json") lib.optionalAttrs (builtins.pathExists "${directory}/machines/${machineName}/settings.json") (
(builtins.fromJSON builtins.fromJSON (builtins.readFile (directory + /machines/${machineName}/settings.json))
(builtins.readFile (directory + /machines/${machineName}/settings.json))); );
# Read additional imports specified via a config option in settings.json # Read additional imports specified via a config option in settings.json
# This is not an infinite recursion, because the imports are discovered here # This is not an infinite recursion, because the imports are discovered here
# before calling evalModules. # before calling evalModules.
# It is still useful to have the imports as an option, as this allows for type # It is still useful to have the imports as an option, as this allows for type
# checking and easy integration with the config frontend(s) # checking and easy integration with the config frontend(s)
machineImports = machineSettings: machineImports =
map machineSettings: map (module: clan-core.clanModules.${module}) (machineSettings.clanImports or [ ]);
(module: clan-core.clanModules.${module})
(machineSettings.clanImports or [ ]);
# TODO: remove default system once we have a hardware-config mechanism # TODO: remove default system once we have a hardware-config mechanism
nixosConfiguration = { system ? "x86_64-linux", name, pkgs ? null, extraConfig ? { } }: nixpkgs.lib.nixosSystem { nixosConfiguration =
{
system ? "x86_64-linux",
name,
pkgs ? null,
extraConfig ? { },
}:
nixpkgs.lib.nixosSystem {
modules = modules =
let let
settings = machineSettings name; settings = machineSettings name;
@ -43,7 +56,8 @@ let
clan-core.nixosModules.clanCore clan-core.nixosModules.clanCore
extraConfig extraConfig
(machines.${name} or { }) (machines.${name} or { })
({ (
{
clanCore.clanName = clanName; clanCore.clanName = clanName;
clanCore.clanIcon = clanIcon; clanCore.clanIcon = clanIcon;
clanCore.clanDir = directory; clanCore.clanDir = directory;
@ -55,9 +69,9 @@ let
type = "path"; type = "path";
path = lib.mkDefault nixpkgs; path = lib.mkDefault nixpkgs;
}; };
} // lib.optionalAttrs (pkgs != null) { }
nixpkgs.pkgs = lib.mkForce pkgs; // lib.optionalAttrs (pkgs != null) { nixpkgs.pkgs = lib.mkForce pkgs; }
}) )
]; ];
inherit specialArgs; inherit specialArgs;
}; };
@ -77,27 +91,38 @@ let
# This instantiates nixos for each system that we support: # This instantiates nixos for each system that we support:
# configPerSystem = <system>.<machine>.nixosConfiguration # configPerSystem = <system>.<machine>.nixosConfiguration
# We need this to build nixos secret generators for each system # We need this to build nixos secret generators for each system
configsPerSystem = builtins.listToAttrs configsPerSystem = builtins.listToAttrs (
(builtins.map builtins.map (
(system: lib.nameValuePair system system:
(lib.mapAttrs lib.nameValuePair system (
(name: _: nixosConfiguration { lib.mapAttrs (
name: _:
nixosConfiguration {
inherit name system; inherit name system;
pkgs = pkgsForSystem system; pkgs = pkgsForSystem system;
}) }
allMachines)) ) allMachines
supportedSystems); )
) supportedSystems
);
configsFuncPerSystem = builtins.listToAttrs configsFuncPerSystem = builtins.listToAttrs (
(builtins.map builtins.map (
(system: lib.nameValuePair system system:
(lib.mapAttrs lib.nameValuePair system (
(name: _: args: nixosConfiguration (args // { lib.mapAttrs (
name: _: args:
nixosConfiguration (
args
// {
inherit name system; inherit name system;
pkgs = pkgsForSystem system; pkgs = pkgsForSystem system;
})) }
allMachines)) )
supportedSystems); ) allMachines
)
) supportedSystems
);
in in
{ {
inherit nixosConfigurations; inherit nixosConfigurations;
@ -105,8 +130,11 @@ in
clanInternals = { clanInternals = {
machines = configsPerSystem; machines = configsPerSystem;
machinesFunc = configsFuncPerSystem; machinesFunc = configsFuncPerSystem;
all-machines-json = lib.mapAttrs all-machines-json = lib.mapAttrs (
(system: configs: nixpkgs.legacyPackages.${system}.writers.writeJSON "machines.json" (lib.mapAttrs (_: m: m.config.system.clan.deployment.data) configs)) system: configs:
configsPerSystem; nixpkgs.legacyPackages.${system}.writers.writeJSON "machines.json" (
lib.mapAttrs (_: m: m.config.system.clan.deployment.data) configs
)
) configsPerSystem;
}; };
} }

View File

@ -1,4 +1,9 @@
{ lib, clan-core, nixpkgs, ... }: {
lib,
clan-core,
nixpkgs,
...
}:
{ {
jsonschema = import ./jsonschema { inherit lib; }; jsonschema = import ./jsonschema { inherit lib; };

View File

@ -1,11 +1,11 @@
{ lib {
, inputs lib,
, self inputs,
, ... self,
}: { ...
imports = [ }:
./jsonschema/flake-module.nix {
]; imports = [ ./jsonschema/flake-module.nix ];
flake.lib = import ./default.nix { flake.lib = import ./default.nix {
inherit lib; inherit lib;
inherit (inputs) nixpkgs; inherit (inputs) nixpkgs;

View File

@ -1,65 +1,72 @@
{ lib ? import <nixpkgs/lib> {
, excludedTypes ? [ lib ? import <nixpkgs/lib>,
excludedTypes ? [
"functionTo" "functionTo"
"package" "package"
] ],
}: }:
let let
# remove _module attribute from options # remove _module attribute from options
clean = opts: builtins.removeAttrs opts [ "_module" ]; clean = opts: builtins.removeAttrs opts [ "_module" ];
# throw error if option type is not supported # throw error if option type is not supported
notSupported = option: lib.trace option throw '' notSupported =
option:
lib.trace option throw ''
option type '${option.type.name}' ('${option.type.description}') not supported by jsonschema converter option type '${option.type.name}' ('${option.type.description}') not supported by jsonschema converter
location: ${lib.concatStringsSep "." option.loc} location: ${lib.concatStringsSep "." option.loc}
''; '';
isExcludedOption = option: (lib.elem (option.type.name or null) excludedTypes); isExcludedOption = option: (lib.elem (option.type.name or null) excludedTypes);
filterExcluded = lib.filter (opt: ! isExcludedOption opt); filterExcluded = lib.filter (opt: !isExcludedOption opt);
filterExcludedAttrs = lib.filterAttrs (_name: opt: ! isExcludedOption opt); filterExcludedAttrs = lib.filterAttrs (_name: opt: !isExcludedOption opt);
allBasicTypes =
[ "boolean" "integer" "number" "string" "array" "object" "null" ];
allBasicTypes = [
"boolean"
"integer"
"number"
"string"
"array"
"object"
"null"
];
in in
rec { rec {
# parses a nixos module to a jsonschema # parses a nixos module to a jsonschema
parseModule = module: parseModule =
module:
let let
evaled = lib.evalModules { evaled = lib.evalModules { modules = [ module ]; };
modules = [ module ];
};
in in
parseOptions evaled.options; parseOptions evaled.options;
# parses a set of evaluated nixos options to a jsonschema # parses a set of evaluated nixos options to a jsonschema
parseOptions = options': parseOptions =
options':
let let
options = filterExcludedAttrs (clean options'); options = filterExcludedAttrs (clean options');
# parse options to jsonschema properties # parse options to jsonschema properties
properties = lib.mapAttrs (_name: option: parseOption option) options; properties = lib.mapAttrs (_name: option: parseOption option) options;
# TODO: figure out how to handle if prop.anyOf is used # TODO: figure out how to handle if prop.anyOf is used
isRequired = prop: ! (prop ? default || prop.type or null == "object"); isRequired = prop: !(prop ? default || prop.type or null == "object");
requiredProps = lib.filterAttrs (_: prop: isRequired prop) properties; requiredProps = lib.filterAttrs (_: prop: isRequired prop) properties;
required = lib.optionalAttrs (requiredProps != { }) { required = lib.optionalAttrs (requiredProps != { }) { required = lib.attrNames requiredProps; };
required = lib.attrNames requiredProps;
};
in in
# return jsonschema # return jsonschema
required // { required
// {
type = "object"; type = "object";
inherit properties; inherit properties;
}; };
# parses and evaluated nixos option to a jsonschema property definition # parses and evaluated nixos option to a jsonschema property definition
parseOption = option: parseOption =
option:
let let
default = lib.optionalAttrs (option ? default) { default = lib.optionalAttrs (option ? default) { inherit (option) default; };
inherit (option) default;
};
description = lib.optionalAttrs (option ? description) { description = lib.optionalAttrs (option ? description) {
description = option.description.text or option.description; description = option.description.text or option.description;
}; };
@ -67,177 +74,217 @@ rec {
# either type # either type
# TODO: if all nested optiosn are excluded, the parent sould be excluded too # TODO: if all nested optiosn are excluded, the parent sould be excluded too
if option.type.name or null == "either" if
option.type.name or null == "either"
# return jsonschema property definition for either # return jsonschema property definition for either
then then
let let
optionsList' = [ optionsList' = [
{ type = option.type.nestedTypes.left; _type = "option"; loc = option.loc; } {
{ type = option.type.nestedTypes.right; _type = "option"; loc = option.loc; } type = option.type.nestedTypes.left;
_type = "option";
loc = option.loc;
}
{
type = option.type.nestedTypes.right;
_type = "option";
loc = option.loc;
}
]; ];
optionsList = filterExcluded optionsList'; optionsList = filterExcluded optionsList';
in in
default // description // { default // description // { anyOf = map parseOption optionsList; }
anyOf = map parseOption optionsList;
}
# handle nested options (not a submodule) # handle nested options (not a submodule)
else if ! option ? _type else if !option ? _type then
then parseOptions option parseOptions option
# throw if not an option # throw if not an option
else if option._type != "option" && option._type != "option-type" else if option._type != "option" && option._type != "option-type" then
then throw "parseOption: not an option" throw "parseOption: not an option"
# parse nullOr # parse nullOr
else if option.type.name == "nullOr" else if
option.type.name == "nullOr"
# return jsonschema property definition for nullOr # return jsonschema property definition for nullOr
then then
let let
nestedOption = nestedOption = {
{ type = option.type.nestedTypes.elemType; _type = "option"; loc = option.loc; }; type = option.type.nestedTypes.elemType;
_type = "option";
loc = option.loc;
};
in in
default // description // { default
anyOf = // description
[{ type = "null"; }] // {
++ ( anyOf = [
lib.optional (! isExcludedOption nestedOption) { type = "null"; }
(parseOption nestedOption) ] ++ (lib.optional (!isExcludedOption nestedOption) (parseOption nestedOption));
);
} }
# parse bool # parse bool
else if option.type.name == "bool" else if
option.type.name == "bool"
# return jsonschema property definition for bool # return jsonschema property definition for bool
then default // description // { then
type = "boolean"; default // description // { type = "boolean"; }
}
# parse float # parse float
else if option.type.name == "float" else if
option.type.name == "float"
# return jsonschema property definition for float # return jsonschema property definition for float
then default // description // { then
type = "number"; default // description // { type = "number"; }
}
# parse int # parse int
else if (option.type.name == "int" || option.type.name == "positiveInt") else if
(option.type.name == "int" || option.type.name == "positiveInt")
# return jsonschema property definition for int # return jsonschema property definition for int
then default // description // { then
type = "integer"; default // description // { type = "integer"; }
}
# parse string # parse string
else if option.type.name == "str" else if
option.type.name == "str"
# return jsonschema property definition for string # return jsonschema property definition for string
then default // description // { then
type = "string"; default // description // { type = "string"; }
}
# parse string # parse string
else if option.type.name == "path" else if
option.type.name == "path"
# return jsonschema property definition for path # return jsonschema property definition for path
then default // description // { then
type = "string"; default // description // { type = "string"; }
}
# parse anything # parse anything
else if option.type.name == "anything" else if
option.type.name == "anything"
# return jsonschema property definition for anything # return jsonschema property definition for anything
then default // description // { then
type = allBasicTypes; default // description // { type = allBasicTypes; }
}
# parse unspecified # parse unspecified
else if option.type.name == "unspecified" else if
option.type.name == "unspecified"
# return jsonschema property definition for unspecified # return jsonschema property definition for unspecified
then default // description // { then
type = allBasicTypes; default // description // { type = allBasicTypes; }
}
# parse raw # parse raw
else if option.type.name == "raw" else if
option.type.name == "raw"
# return jsonschema property definition for raw # return jsonschema property definition for raw
then default // description // { then
type = allBasicTypes; default // description // { type = allBasicTypes; }
}
# parse enum # parse enum
else if option.type.name == "enum" else if
option.type.name == "enum"
# return jsonschema property definition for enum # return jsonschema property definition for enum
then default // description // { then
enum = option.type.functor.payload; default // description // { enum = option.type.functor.payload; }
}
# parse listOf submodule # parse listOf submodule
else if option.type.name == "listOf" && option.type.functor.wrapped.name == "submodule" else if
option.type.name == "listOf" && option.type.functor.wrapped.name == "submodule"
# return jsonschema property definition for listOf submodule # return jsonschema property definition for listOf submodule
then default // description // { then
default
// description
// {
type = "array"; type = "array";
items = parseOptions (option.type.functor.wrapped.getSubOptions option.loc); items = parseOptions (option.type.functor.wrapped.getSubOptions option.loc);
} }
# parse list # parse list
else if (option.type.name == "listOf") else if
(option.type.name == "listOf")
# return jsonschema property definition for list # return jsonschema property definition for list
then then
let let
nestedOption = { type = option.type.functor.wrapped; _type = "option"; loc = option.loc; }; nestedOption = {
type = option.type.functor.wrapped;
_type = "option";
loc = option.loc;
};
in in
default // description // { default
// description
// {
type = "array"; type = "array";
} }
// (lib.optionalAttrs (! isExcludedOption nestedOption) { // (lib.optionalAttrs (!isExcludedOption nestedOption) { items = parseOption nestedOption; })
items = parseOption nestedOption;
})
# parse list of unspecified # parse list of unspecified
else if else if
(option.type.name == "listOf") (option.type.name == "listOf") && (option.type.functor.wrapped.name == "unspecified")
&& (option.type.functor.wrapped.name == "unspecified")
# return jsonschema property definition for list # return jsonschema property definition for list
then default // description // { then
type = "array"; default // description // { type = "array"; }
}
# parse attrsOf submodule # parse attrsOf submodule
else if option.type.name == "attrsOf" && option.type.nestedTypes.elemType.name == "submodule" else if
option.type.name == "attrsOf" && option.type.nestedTypes.elemType.name == "submodule"
# return jsonschema property definition for attrsOf submodule # return jsonschema property definition for attrsOf submodule
then default // description // { then
default
// description
// {
type = "object"; type = "object";
additionalProperties = parseOptions (option.type.nestedTypes.elemType.getSubOptions option.loc); additionalProperties = parseOptions (option.type.nestedTypes.elemType.getSubOptions option.loc);
} }
# parse attrs # parse attrs
else if option.type.name == "attrs" else if
option.type.name == "attrs"
# return jsonschema property definition for attrs # return jsonschema property definition for attrs
then default // description // { then
default
// description
// {
type = "object"; type = "object";
additionalProperties = true; additionalProperties = true;
} }
# parse attrsOf # parse attrsOf
# TODO: if nested option is excluded, the parent sould be excluded too # TODO: if nested option is excluded, the parent sould be excluded too
else if option.type.name == "attrsOf" || option.type.name == "lazyAttrsOf" else if
option.type.name == "attrsOf" || option.type.name == "lazyAttrsOf"
# return jsonschema property definition for attrs # return jsonschema property definition for attrs
then then
let let
nestedOption = { type = option.type.nestedTypes.elemType; _type = "option"; loc = option.loc; }; nestedOption = {
type = option.type.nestedTypes.elemType;
_type = "option";
loc = option.loc;
};
in in
default // description // { default
// description
// {
type = "object"; type = "object";
additionalProperties = additionalProperties =
if ! isExcludedOption nestedOption if !isExcludedOption nestedOption then
then parseOption { type = option.type.nestedTypes.elemType; _type = "option"; loc = option.loc; } parseOption {
else false; type = option.type.nestedTypes.elemType;
_type = "option";
loc = option.loc;
}
else
false;
} }
# parse submodule # parse submodule
else if option.type.name == "submodule" else if
option.type.name == "submodule"
# return jsonschema property definition for submodule # return jsonschema property definition for submodule
# then (lib.attrNames (option.type.getSubOptions option.loc).opt) # then (lib.attrNames (option.type.getSubOptions option.loc).opt)
then parseOptions (option.type.getSubOptions option.loc) then
parseOptions (option.type.getSubOptions option.loc)
# throw error if option type is not supported # throw error if option type is not supported
else notSupported option; else
notSupported option;
} }

View File

@ -1,7 +1,6 @@
/* # An example nixos module declaring an interface.
An example nixos module declaring an interface. { lib, ... }:
*/ {
{ lib, ... }: {
options = { options = {
# str # str
name = lib.mkOption { name = lib.mkOption {
@ -44,7 +43,11 @@
# list of str # list of str
kernelModules = lib.mkOption { kernelModules = lib.mkOption {
type = lib.types.listOf lib.types.str; type = lib.types.listOf lib.types.str;
default = [ "nvme" "xhci_pci" "ahci" ]; default = [
"nvme"
"xhci_pci"
"ahci"
];
description = "A list of enabled kernel modules"; description = "A list of enabled kernel modules";
}; };
}; };

View File

@ -1,5 +1,7 @@
{ {
perSystem = { pkgs, ... }: { perSystem =
{ pkgs, ... }:
{
checks = { checks = {
# check if the `clan config` example jsonschema and data is valid # check if the `clan config` example jsonschema and data is valid

View File

@ -1,6 +1,7 @@
# run these tests via `nix-unit ./test.nix` # run these tests via `nix-unit ./test.nix`
{ lib ? (import <nixpkgs> { }).lib {
, slib ? import ./. { inherit lib; } lib ? (import <nixpkgs> { }).lib,
slib ? import ./. { inherit lib; },
}: }:
{ {
parseOption = import ./test_parseOption.nix { inherit lib slib; }; parseOption = import ./test_parseOption.nix { inherit lib slib; };

View File

@ -1,21 +1,25 @@
# tests for the nixos options to jsonschema converter # tests for the nixos options to jsonschema converter
# run these tests via `nix-unit ./test.nix` # run these tests via `nix-unit ./test.nix`
{ lib ? (import <nixpkgs> { }).lib {
, slib ? import ./. { inherit lib; } lib ? (import <nixpkgs> { }).lib,
slib ? import ./. { inherit lib; },
}: }:
let let
description = "Test Description"; description = "Test Description";
evalType = type: default: evalType =
type: default:
let let
evaledConfig = lib.evalModules { evaledConfig = lib.evalModules {
modules = [{ modules = [
{
options.opt = lib.mkOption { options.opt = lib.mkOption {
inherit type; inherit type;
inherit default; inherit default;
inherit description; inherit description;
}; };
}]; }
];
}; };
in in
evaledConfig.options.opt; evaledConfig.options.opt;
@ -25,11 +29,7 @@ in
testNoDefaultNoDescription = testNoDefaultNoDescription =
let let
evaledConfig = lib.evalModules { evaledConfig = lib.evalModules {
modules = [{ modules = [ { options.opt = lib.mkOption { type = lib.types.bool; }; } ];
options.opt = lib.mkOption {
type = lib.types.bool;
};
}];
}; };
in in
{ {
@ -42,7 +42,8 @@ in
testDescriptionIsAttrs = testDescriptionIsAttrs =
let let
evaledConfig = lib.evalModules { evaledConfig = lib.evalModules {
modules = [{ modules = [
{
options.opt = lib.mkOption { options.opt = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
description = { description = {
@ -50,7 +51,8 @@ in
text = description; text = description;
}; };
}; };
}]; }
];
}; };
in in
{ {
@ -112,7 +114,11 @@ in
testEnum = testEnum =
let let
default = "foo"; default = "foo";
values = [ "foo" "bar" "baz" ]; values = [
"foo"
"bar"
"baz"
];
in in
{ {
expr = slib.parseOption (evalType (lib.types.enum values) default); expr = slib.parseOption (evalType (lib.types.enum values) default);
@ -124,7 +130,11 @@ in
testListOfInt = testListOfInt =
let let
default = [ 1 2 3 ]; default = [
1
2
3
];
in in
{ {
expr = slib.parseOption (evalType (lib.types.listOf lib.types.int) default); expr = slib.parseOption (evalType (lib.types.listOf lib.types.int) default);
@ -139,14 +149,26 @@ in
testListOfUnspecified = testListOfUnspecified =
let let
default = [ 1 2 3 ]; default = [
1
2
3
];
in in
{ {
expr = slib.parseOption (evalType (lib.types.listOf lib.types.unspecified) default); expr = slib.parseOption (evalType (lib.types.listOf lib.types.unspecified) default);
expected = { expected = {
type = "array"; type = "array";
items = { items = {
type = [ "boolean" "integer" "number" "string" "array" "object" "null" ]; type = [
"boolean"
"integer"
"number"
"string"
"array"
"object"
"null"
];
}; };
inherit default description; inherit default description;
}; };
@ -154,7 +176,11 @@ in
testAttrs = testAttrs =
let let
default = { foo = 1; bar = 2; baz = 3; }; default = {
foo = 1;
bar = 2;
baz = 3;
};
in in
{ {
expr = slib.parseOption (evalType (lib.types.attrs) default); expr = slib.parseOption (evalType (lib.types.attrs) default);
@ -167,7 +193,11 @@ in
testAttrsOfInt = testAttrsOfInt =
let let
default = { foo = 1; bar = 2; baz = 3; }; default = {
foo = 1;
bar = 2;
baz = 3;
};
in in
{ {
expr = slib.parseOption (evalType (lib.types.attrsOf lib.types.int) default); expr = slib.parseOption (evalType (lib.types.attrsOf lib.types.int) default);
@ -182,7 +212,11 @@ in
testLazyAttrsOfInt = testLazyAttrsOfInt =
let let
default = { foo = 1; bar = 2; baz = 3; }; default = {
foo = 1;
bar = 2;
baz = 3;
};
in in
{ {
expr = slib.parseOption (evalType (lib.types.lazyAttrsOf lib.types.int) default); expr = slib.parseOption (evalType (lib.types.lazyAttrsOf lib.types.int) default);
@ -286,7 +320,10 @@ in
inherit description; inherit description;
}; };
}; };
default = { foo.opt = false; bar.opt = true; }; default = {
foo.opt = false;
bar.opt = true;
};
in in
{ {
expr = slib.parseOption (evalType (lib.types.attrsOf (lib.types.submodule subModule)) default); expr = slib.parseOption (evalType (lib.types.attrsOf (lib.types.submodule subModule)) default);
@ -315,7 +352,10 @@ in
inherit description; inherit description;
}; };
}; };
default = [{ opt = false; } { opt = true; }]; default = [
{ opt = false; }
{ opt = true; }
];
in in
{ {
expr = slib.parseOption (evalType (lib.types.listOf (lib.types.submodule subModule)) default); expr = slib.parseOption (evalType (lib.types.listOf (lib.types.submodule subModule)) default);
@ -358,7 +398,15 @@ in
expr = slib.parseOption (evalType lib.types.anything default); expr = slib.parseOption (evalType lib.types.anything default);
expected = { expected = {
inherit default description; inherit default description;
type = [ "boolean" "integer" "number" "string" "array" "object" "null" ]; type = [
"boolean"
"integer"
"number"
"string"
"array"
"object"
"null"
];
}; };
}; };
@ -370,7 +418,15 @@ in
expr = slib.parseOption (evalType lib.types.unspecified default); expr = slib.parseOption (evalType lib.types.unspecified default);
expected = { expected = {
inherit default description; inherit default description;
type = [ "boolean" "integer" "number" "string" "array" "object" "null" ]; type = [
"boolean"
"integer"
"number"
"string"
"array"
"object"
"null"
];
}; };
}; };
@ -382,7 +438,15 @@ in
expr = slib.parseOption (evalType lib.types.raw default); expr = slib.parseOption (evalType lib.types.raw default);
expected = { expected = {
inherit default description; inherit default description;
type = [ "boolean" "integer" "number" "string" "array" "object" "null" ]; type = [
"boolean"
"integer"
"number"
"string"
"array"
"object"
"null"
];
}; };
}; };
} }

View File

@ -1,14 +1,13 @@
# tests for the nixos options to jsonschema converter # tests for the nixos options to jsonschema converter
# run these tests via `nix-unit ./test.nix` # run these tests via `nix-unit ./test.nix`
{ lib ? (import <nixpkgs> { }).lib {
, slib ? import ./. { inherit lib; } lib ? (import <nixpkgs> { }).lib,
slib ? import ./. { inherit lib; },
}: }:
let let
evaledOptions = evaledOptions =
let let
evaledConfig = lib.evalModules { evaledConfig = lib.evalModules { modules = [ ./example-interface.nix ]; };
modules = [ ./example-interface.nix ];
};
in in
evaledConfig.options; evaledConfig.options;
in in
@ -21,11 +20,7 @@ in
testParseNestedOptions = testParseNestedOptions =
let let
evaled = lib.evalModules { evaled = lib.evalModules {
modules = [{ modules = [ { options.foo.bar = lib.mkOption { type = lib.types.bool; }; } ];
options.foo.bar = lib.mkOption {
type = lib.types.bool;
};
}];
}; };
in in
{ {
@ -34,7 +29,9 @@ in
properties = { properties = {
foo = { foo = {
properties = { properties = {
bar = { type = "boolean"; }; bar = {
type = "boolean";
};
}; };
required = [ "bar" ]; required = [ "bar" ];
type = "object"; type = "object";

View File

@ -1,11 +1,12 @@
{ lib, ... }: { lib, ... }:
{ {
imports = [ imports = [ ./state.nix ];
./state.nix
];
options.clanCore.backups = { options.clanCore.backups = {
providers = lib.mkOption { providers = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: { type = lib.types.attrsOf (
lib.types.submodule (
{ name, ... }:
{
options = { options = {
name = lib.mkOption { name = lib.mkOption {
type = lib.types.str; type = lib.types.str;
@ -39,7 +40,9 @@
''; '';
}; };
}; };
})); }
)
);
default = { }; default = { };
description = '' description = ''
Configured backup providers which are used by this machine Configured backup providers which are used by this machine

View File

@ -1,6 +1,5 @@
{ lib { lib, ... }:
, ... {
}: {
/* /*
Declaring imports inside the module system does not trigger an infinite Declaring imports inside the module system does not trigger an infinite
recursion in this case because buildClan generates the imports from the recursion in this case because buildClan generates the imports from the

View File

@ -1 +1,4 @@
{ pkgs, ... }: { documentation.nixos.enable = pkgs.lib.mkDefault false; } { pkgs, ... }:
{
documentation.nixos.enable = pkgs.lib.mkDefault false;
}

View File

@ -1,4 +1,5 @@
{ lib, pkgs, ... }: { { lib, pkgs, ... }:
{
options.clanCore = { options.clanCore = {
clanName = lib.mkOption { clanName = lib.mkOption {
type = lib.types.str; type = lib.types.str;

View File

@ -49,7 +49,18 @@
}; };
imports = [ imports = [
(lib.mkRenamedOptionModule [ "clan" "networking" "deploymentAddress" ] [ "clan" "networking" "targetHost" ]) (lib.mkRenamedOptionModule
[
"clan"
"networking"
"deploymentAddress"
]
[
"clan"
"networking"
"targetHost"
]
)
]; ];
config = { config = {
# conflicts with systemd-resolved # conflicts with systemd-resolved
@ -64,7 +75,9 @@
systemd.network.wait-online.enable = false; systemd.network.wait-online.enable = false;
# Provide a default network configuration but don't compete with network-manager or dhcpcd # Provide a default network configuration but don't compete with network-manager or dhcpcd
systemd.network.networks."50-uplink" = lib.mkIf (!(config.networking.networkmanager.enable || config.networking.dhcpcd.enable)) { systemd.network.networks."50-uplink" =
lib.mkIf (!(config.networking.networkmanager.enable || config.networking.dhcpcd.enable))
{
matchConfig.Type = "ether"; matchConfig.Type = "ether";
networkConfig = { networkConfig = {
DHCP = "yes"; DHCP = "yes";

View File

@ -1,4 +1,10 @@
{ pkgs, options, lib, ... }: { {
pkgs,
options,
lib,
...
}:
{
options.clanCore.optionsNix = lib.mkOption { options.clanCore.optionsNix = lib.mkOption {
type = lib.types.raw; type = lib.types.raw;
internal = true; internal = true;

View File

@ -1,4 +1,10 @@
{ config, lib, pkgs, ... }: { {
config,
lib,
pkgs,
...
}:
{
# TODO: factor these out into a separate interface.nix. # TODO: factor these out into a separate interface.nix.
# Also think about moving these options out of `system.clan`. # Also think about moving these options out of `system.clan`.
# Maybe we should not re-use the already polluted confg.system namespace # Maybe we should not re-use the already polluted confg.system namespace
@ -90,6 +96,8 @@
inherit (config.clan.deployment) requireExplicitUpdate; inherit (config.clan.deployment) requireExplicitUpdate;
inherit (config.clanCore) secretsUploadDirectory; inherit (config.clanCore) secretsUploadDirectory;
}; };
system.clan.deployment.file = pkgs.writeText "deployment.json" (builtins.toJSON config.system.clan.deployment.data); system.clan.deployment.file = pkgs.writeText "deployment.json" (
builtins.toJSON config.system.clan.deployment.data
);
}; };
} }

View File

@ -1,4 +1,5 @@
{ pkgs, ... }: { { pkgs, ... }:
{
# essential debugging tools for networked services # essential debugging tools for networked services
environment.systemPackages = [ environment.systemPackages = [
pkgs.dnsutils pkgs.dnsutils

View File

@ -1,7 +1,17 @@
{ config, lib, pkgs, ... }: {
config,
lib,
pkgs,
...
}:
{ {
options.clanCore.secretStore = lib.mkOption { options.clanCore.secretStore = lib.mkOption {
type = lib.types.enum [ "sops" "password-store" "vm" "custom" ]; type = lib.types.enum [
"sops"
"password-store"
"vm"
"custom"
];
default = "sops"; default = "sops";
description = '' description = ''
method to store secrets method to store secrets
@ -34,8 +44,8 @@
options.clanCore.secrets = lib.mkOption { options.clanCore.secrets = lib.mkOption {
default = { }; default = { };
type = lib.types.attrsOf type = lib.types.attrsOf (
(lib.types.submodule (service: { lib.types.submodule (service: {
options = { options = {
name = lib.mkOption { name = lib.mkOption {
type = lib.types.str; type = lib.types.str;
@ -45,7 +55,9 @@
''; '';
}; };
generator = lib.mkOption { generator = lib.mkOption {
type = lib.types.submodule ({ config, ... }: { type = lib.types.submodule (
{ config, ... }:
{
options = { options = {
path = lib.mkOption { path = lib.mkOption {
type = lib.types.listOf (lib.types.either lib.types.path lib.types.package); type = lib.types.listOf (lib.types.either lib.types.path lib.types.package);
@ -83,17 +95,20 @@
# prepare sandbox user # prepare sandbox user
mkdir -p /etc mkdir -p /etc
cp ${pkgs.runCommand "fake-etc" {} '' cp ${
pkgs.runCommand "fake-etc" { } ''
export PATH="${pkgs.coreutils}/bin" export PATH="${pkgs.coreutils}/bin"
mkdir -p $out mkdir -p $out
cp /etc/* $out/ cp /etc/* $out/
''}/* /etc/ ''
}/* /etc/
${config.script} ${config.script}
''; '';
}; };
}; };
}); }
);
}; };
secrets = secrets =
let let
@ -101,8 +116,12 @@
in in
lib.mkOption { lib.mkOption {
default = { }; default = { };
type = lib.types.attrsOf (lib.types.submodule ({ config, name, ... }: { type = lib.types.attrsOf (
options = { lib.types.submodule (
{ config, name, ... }:
{
options =
{
name = lib.mkOption { name = lib.mkOption {
type = lib.types.str; type = lib.types.str;
description = '' description = ''
@ -117,7 +136,8 @@
''; '';
default = "${config'.clanCore.secretsDirectory}/${config'.clanCore.secretsPrefix}${config.name}"; default = "${config'.clanCore.secretsDirectory}/${config'.clanCore.secretsPrefix}${config.name}";
}; };
} // lib.optionalAttrs (config'.clanCore.secretStore == "sops") { }
// lib.optionalAttrs (config'.clanCore.secretStore == "sops") {
groups = lib.mkOption { groups = lib.mkOption {
type = lib.types.listOf lib.types.str; type = lib.types.listOf lib.types.str;
default = config'.clanCore.sops.defaultGroups; default = config'.clanCore.sops.defaultGroups;
@ -126,14 +146,17 @@
''; '';
}; };
}; };
})); }
)
);
description = '' description = ''
path where the secret is located in the filesystem path where the secret is located in the filesystem
''; '';
}; };
facts = lib.mkOption { facts = lib.mkOption {
default = { }; default = { };
type = lib.types.attrsOf (lib.types.submodule (fact: { type = lib.types.attrsOf (
lib.types.submodule (fact: {
options = { options = {
name = lib.mkOption { name = lib.mkOption {
type = lib.types.str; type = lib.types.str;
@ -147,22 +170,23 @@
description = '' description = ''
path to a fact which is generated by the generator path to a fact which is generated by the generator
''; '';
default = config.clanCore.clanDir + "/machines/${config.clanCore.machineName}/facts/${fact.config._module.args.name}"; default =
config.clanCore.clanDir
+ "/machines/${config.clanCore.machineName}/facts/${fact.config._module.args.name}";
}; };
value = lib.mkOption { value = lib.mkOption {
defaultText = lib.literalExpression "\${config.clanCore.clanDir}/\${fact.config.path}"; defaultText = lib.literalExpression "\${config.clanCore.clanDir}/\${fact.config.path}";
type = lib.types.nullOr lib.types.str; type = lib.types.nullOr lib.types.str;
default = default =
if builtins.pathExists fact.config.path then if builtins.pathExists fact.config.path then lib.strings.fileContents fact.config.path else null;
lib.strings.fileContents fact.config.path
else
null;
}; };
}; };
})); })
);
}; };
}; };
})); })
);
}; };
imports = [ imports = [
./sops.nix ./sops.nix

View File

@ -13,4 +13,3 @@
system.clan.secretsModule = "clan_cli.secrets.modules.password_store"; system.clan.secretsModule = "clan_cli.secrets.modules.password_store";
}; };
} }

View File

@ -1,22 +1,33 @@
{ config, lib, pkgs, ... }: {
config,
lib,
pkgs,
...
}:
let let
secretsDir = config.clanCore.clanDir + "/sops/secrets"; secretsDir = config.clanCore.clanDir + "/sops/secrets";
groupsDir = config.clanCore.clanDir + "/sops/groups"; groupsDir = config.clanCore.clanDir + "/sops/groups";
# My symlink is in the nixos module detected as a directory also it works in the repl. Is this because of pure evaluation? # My symlink is in the nixos module detected as a directory also it works in the repl. Is this because of pure evaluation?
containsSymlink = path: containsSymlink =
builtins.pathExists path && (builtins.readFileType path == "directory" || builtins.readFileType path == "symlink"); path:
builtins.pathExists path
&& (builtins.readFileType path == "directory" || builtins.readFileType path == "symlink");
containsMachine = parent: name: type: containsMachine =
parent: name: type:
type == "directory" && containsSymlink "${parent}/${name}/machines/${config.clanCore.machineName}"; type == "directory" && containsSymlink "${parent}/${name}/machines/${config.clanCore.machineName}";
containsMachineOrGroups = name: type: containsMachineOrGroups =
(containsMachine secretsDir name type) || lib.any (group: type == "directory" && containsSymlink "${secretsDir}/${name}/groups/${group}") groups; name: type:
(containsMachine secretsDir name type)
|| lib.any (
group: type == "directory" && containsSymlink "${secretsDir}/${name}/groups/${group}"
) groups;
filterDir = filter: dir: filterDir =
lib.optionalAttrs (builtins.pathExists dir) filter: dir:
(lib.filterAttrs filter (builtins.readDir dir)); lib.optionalAttrs (builtins.pathExists dir) (lib.filterAttrs filter (builtins.readDir dir));
groups = builtins.attrNames (filterDir (containsMachine groupsDir) groupsDir); groups = builtins.attrNames (filterDir (containsMachine groupsDir) groupsDir);
secrets = filterDir containsMachineOrGroups secretsDir; secrets = filterDir containsMachineOrGroups secretsDir;
@ -34,17 +45,18 @@ in
clanCore.secretsDirectory = "/run/secrets"; clanCore.secretsDirectory = "/run/secrets";
clanCore.secretsPrefix = config.clanCore.machineName + "-"; clanCore.secretsPrefix = config.clanCore.machineName + "-";
system.clan.secretsModule = "clan_cli.secrets.modules.sops"; system.clan.secretsModule = "clan_cli.secrets.modules.sops";
sops.secrets = builtins.mapAttrs sops.secrets = builtins.mapAttrs (name: _: {
(name: _: {
sopsFile = config.clanCore.clanDir + "/sops/secrets/${name}/secret"; sopsFile = config.clanCore.clanDir + "/sops/secrets/${name}/secret";
format = "binary"; format = "binary";
}) }) secrets;
secrets;
# To get proper error messages about missing secrets we need a dummy secret file that is always present # To get proper error messages about missing secrets we need a dummy secret file that is always present
sops.defaultSopsFile = lib.mkIf config.sops.validateSopsFiles (lib.mkDefault (builtins.toString (pkgs.writeText "dummy.yaml" ""))); sops.defaultSopsFile = lib.mkIf config.sops.validateSopsFiles (
lib.mkDefault (builtins.toString (pkgs.writeText "dummy.yaml" ""))
);
sops.age.keyFile = lib.mkIf (builtins.pathExists (config.clanCore.clanDir + "/sops/secrets/${config.clanCore.machineName}-age.key/secret")) sops.age.keyFile = lib.mkIf (builtins.pathExists (
(lib.mkDefault "/var/lib/sops-nix/key.txt"); config.clanCore.clanDir + "/sops/secrets/${config.clanCore.machineName}-age.key/secret"
)) (lib.mkDefault "/var/lib/sops-nix/key.txt");
clanCore.secretsUploadDirectory = lib.mkDefault "/var/lib/sops-nix"; clanCore.secretsUploadDirectory = lib.mkDefault "/var/lib/sops-nix";
}; };
} }

View File

@ -7,4 +7,3 @@
system.clan.factsModule = "clan_cli.facts.modules.vm"; system.clan.factsModule = "clan_cli.facts.modules.vm";
}; };
} }

View File

@ -1,15 +1,15 @@
{ lib, ... }: { lib, ... }:
{ {
# defaults # defaults
config.clanCore.state.HOME.folders = [ config.clanCore.state.HOME.folders = [ "/home" ];
"/home"
];
# interface # interface
options.clanCore.state = lib.mkOption { options.clanCore.state = lib.mkOption {
default = { }; default = { };
type = lib.types.attrsOf type = lib.types.attrsOf (
(lib.types.submodule ({ ... }: { lib.types.submodule (
{ ... }:
{
options = { options = {
folders = lib.mkOption { folders = lib.mkOption {
type = lib.types.listOf lib.types.str; type = lib.types.listOf lib.types.str;
@ -36,6 +36,8 @@
''; '';
}; };
}; };
})); }
)
);
}; };
} }

View File

@ -1,12 +1,15 @@
{ lib, config, pkgs, options, extendModules, modulesPath, ... }: {
lib,
config,
pkgs,
options,
extendModules,
modulesPath,
...
}:
let let
# Flatten the list of state folders into a single list # Flatten the list of state folders into a single list
stateFolders = lib.flatten ( stateFolders = lib.flatten (lib.mapAttrsToList (_item: attrs: attrs.folders) config.clanCore.state);
lib.mapAttrsToList
(_item: attrs: attrs.folders)
config.clanCore.state
);
vmModule = { vmModule = {
imports = [ imports = [
@ -32,7 +35,10 @@ let
# currently needed for system.etc.overlay.enable # currently needed for system.etc.overlay.enable
boot.kernelPackages = pkgs.linuxPackages_latest; boot.kernelPackages = pkgs.linuxPackages_latest;
boot.initrd.systemd.storePaths = [ pkgs.util-linux pkgs.e2fsprogs ]; boot.initrd.systemd.storePaths = [
pkgs.util-linux
pkgs.e2fsprogs
];
boot.initrd.systemd.emergencyAccess = true; boot.initrd.systemd.emergencyAccess = true;
# sysusers is faster than nixos's perl scripts # sysusers is faster than nixos's perl scripts
@ -43,22 +49,39 @@ let
boot.initrd.kernelModules = [ "virtiofs" ]; boot.initrd.kernelModules = [ "virtiofs" ];
virtualisation.writableStore = false; virtualisation.writableStore = false;
virtualisation.fileSystems = lib.mkForce ({ virtualisation.fileSystems = lib.mkForce (
{
"/nix/store" = { "/nix/store" = {
device = "nix-store"; device = "nix-store";
options = [ "x-systemd.requires=systemd-modules-load.service" "ro" ]; options = [
"x-systemd.requires=systemd-modules-load.service"
"ro"
];
fsType = "virtiofs"; fsType = "virtiofs";
}; };
"/" = { "/" = {
device = "/dev/vda"; device = "/dev/vda";
fsType = "ext4"; fsType = "ext4";
options = [ "defaults" "x-systemd.makefs" "nobarrier" "noatime" "nodiratime" "data=writeback" "discard" ]; options = [
"defaults"
"x-systemd.makefs"
"nobarrier"
"noatime"
"nodiratime"
"data=writeback"
"discard"
];
}; };
"/vmstate" = { "/vmstate" = {
device = "/dev/vdb"; device = "/dev/vdb";
options = [ "x-systemd.makefs" "noatime" "nodiratime" "discard" ]; options = [
"x-systemd.makefs"
"noatime"
"nodiratime"
"discard"
];
noCheck = true; noCheck = true;
fsType = "ext4"; fsType = "ext4";
}; };
@ -67,26 +90,31 @@ let
device = "secrets"; device = "secrets";
fsType = "9p"; fsType = "9p";
neededForBoot = true; neededForBoot = true;
options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ]; options = [
"trans=virtio"
"version=9p2000.L"
"cache=loose"
];
}; };
}
} // lib.listToAttrs (map // lib.listToAttrs (
(folder: map (
folder:
lib.nameValuePair folder { lib.nameValuePair folder {
device = "/vmstate${folder}"; device = "/vmstate${folder}";
fsType = "none"; fsType = "none";
options = [ "bind" ]; options = [ "bind" ];
}) }
stateFolders)); ) stateFolders
)
);
}; };
# We cannot simply merge the VM config into the current system config, because # We cannot simply merge the VM config into the current system config, because
# it is not necessarily a VM. # it is not necessarily a VM.
# Instead we use extendModules to create a second instance of the current # Instead we use extendModules to create a second instance of the current
# system configuration, and then merge the VM config into that. # system configuration, and then merge the VM config into that.
vmConfig = extendModules { vmConfig = extendModules { modules = [ vmModule ]; };
modules = [ vmModule ];
};
in in
{ {
options = { options = {
@ -210,12 +238,14 @@ in
}; };
# for clan vm create # for clan vm create
system.clan.vm = { system.clan.vm = {
create = pkgs.writeText "vm.json" (builtins.toJSON { create = pkgs.writeText "vm.json" (
builtins.toJSON {
initrd = "${vmConfig.config.system.build.initialRamdisk}/${vmConfig.config.system.boot.loader.initrdFile}"; initrd = "${vmConfig.config.system.build.initialRamdisk}/${vmConfig.config.system.boot.loader.initrdFile}";
toplevel = vmConfig.config.system.build.toplevel; toplevel = vmConfig.config.system.build.toplevel;
regInfo = (pkgs.closureInfo { rootPaths = vmConfig.config.virtualisation.additionalPaths; }); regInfo = (pkgs.closureInfo { rootPaths = vmConfig.config.virtualisation.additionalPaths; });
inherit (config.clan.virtualisation) memorySize cores graphics; inherit (config.clan.virtualisation) memorySize cores graphics;
}); }
);
}; };
virtualisation = lib.optionalAttrs (options.virtualisation ? cores) { virtualisation = lib.optionalAttrs (options.virtualisation ? cores) {

View File

@ -1,4 +1,9 @@
{ pkgs, config, lib, ... }: {
pkgs,
config,
lib,
...
}:
{ {
options = { options = {
# maybe upstream this? # maybe upstream this?

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }: {
config,
lib,
pkgs,
...
}:
let let
cfg = config.clan.networking.zerotier; cfg = config.clan.networking.zerotier;
facts = config.clanCore.secrets.zerotier.facts or { }; facts = config.clanCore.secrets.zerotier.facts or { };
@ -76,16 +81,18 @@ in
}; };
settings = lib.mkOption { settings = lib.mkOption {
description = lib.mdDoc "override the network config in /var/lib/zerotier/bla/$network.json"; description = lib.mdDoc "override the network config in /var/lib/zerotier/bla/$network.json";
type = lib.types.submodule { type = lib.types.submodule { freeformType = (pkgs.formats.json { }).type; };
freeformType = (pkgs.formats.json { }).type;
};
}; };
}; };
config = lib.mkMerge [ config = lib.mkMerge [
({ ({
# Override license so that we can build zerotierone without # Override license so that we can build zerotierone without
# having to re-import nixpkgs. # having to re-import nixpkgs.
services.zerotierone.package = lib.mkDefault (pkgs.zerotierone.overrideAttrs (_old: { meta = { }; })); services.zerotierone.package = lib.mkDefault (
pkgs.zerotierone.overrideAttrs (_old: {
meta = { };
})
);
}) })
(lib.mkIf ((facts.zerotier-ip.value or null) != null) { (lib.mkIf ((facts.zerotier-ip.value or null) != null) {
environment.etc."zerotier/ip".text = facts.zerotier-ip.value; environment.etc."zerotier/ip".text = facts.zerotier-ip.value;
@ -111,7 +118,7 @@ in
mkdir -p /var/lib/zerotier-one/controller.d/network mkdir -p /var/lib/zerotier-one/controller.d/network
ln -sfT ${pkgs.writeText "net.json" (builtins.toJSON cfg.settings)} /var/lib/zerotier-one/controller.d/network/${cfg.networkId}.json ln -sfT ${pkgs.writeText "net.json" (builtins.toJSON cfg.settings)} /var/lib/zerotier-one/controller.d/network/${cfg.networkId}.json
''} ''}
${lib.optionalString (cfg.moon.stableEndpoints != []) '' ${lib.optionalString (cfg.moon.stableEndpoints != [ ]) ''
if [[ ! -f /var/lib/zerotier-one/moon.json ]]; then if [[ ! -f /var/lib/zerotier-one/moon.json ]]; then
zerotier-idtool initmoon /var/lib/zerotier-one/identity.public > /var/lib/zerotier-one/moon.json zerotier-idtool initmoon /var/lib/zerotier-one/identity.public > /var/lib/zerotier-one/moon.json
fi fi
@ -123,7 +130,11 @@ in
find /var/lib/zerotier-one/networks.d \ find /var/lib/zerotier-one/networks.d \
-type f \ -type f \
-name "*.conf" \ -name "*.conf" \
-not \( ${lib.concatMapStringsSep " -o " (netId: ''-name "${netId}.conf"'') config.services.zerotierone.joinNetworks} \) \ -not \( ${
lib.concatMapStringsSep " -o " (
netId: ''-name "${netId}.conf"''
) config.services.zerotierone.joinNetworks
} \) \
-delete -delete
fi fi
''}" ''}"
@ -172,7 +183,11 @@ in
facts.zerotier-ip = { }; facts.zerotier-ip = { };
facts.zerotier-network-id = { }; facts.zerotier-network-id = { };
secrets.zerotier-identity-secret = { }; secrets.zerotier-identity-secret = { };
generator.path = [ config.services.zerotierone.package pkgs.fakeroot pkgs.python3 ]; generator.path = [
config.services.zerotierone.package
pkgs.fakeroot
pkgs.python3
];
generator.script = '' generator.script = ''
python3 ${./generate.py} --mode network \ python3 ${./generate.py} --mode network \
--ip "$facts/zerotier-ip" \ --ip "$facts/zerotier-ip" \
@ -188,7 +203,10 @@ in
clanCore.secrets.zerotier = { clanCore.secrets.zerotier = {
facts.zerotier-ip = { }; facts.zerotier-ip = { };
secrets.zerotier-identity-secret = { }; secrets.zerotier-identity-secret = { };
generator.path = [ config.services.zerotierone.package pkgs.python3 ]; generator.path = [
config.services.zerotierone.package
pkgs.python3
];
generator.script = '' generator.script = ''
python3 ${./generate.py} --mode identity \ python3 ${./generate.py} --mode identity \
--ip "$facts/zerotier-ip" \ --ip "$facts/zerotier-ip" \
@ -200,9 +218,7 @@ in
(lib.mkIf (cfg.controller.enable && (facts.zerotier-network-id.value or null) != null) { (lib.mkIf (cfg.controller.enable && (facts.zerotier-network-id.value or null) != null) {
clan.networking.zerotier.networkId = facts.zerotier-network-id.value; clan.networking.zerotier.networkId = facts.zerotier-network-id.value;
clan.networking.zerotier.settings = { clan.networking.zerotier.settings = {
authTokens = [ authTokens = [ null ];
null
];
authorizationEndpoint = ""; authorizationEndpoint = "";
capabilities = [ ]; capabilities = [ ];
clientId = ""; clientId = "";
@ -242,7 +258,9 @@ in
environment.etc."zerotier/network-id".text = facts.zerotier-network-id.value; environment.etc."zerotier/network-id".text = facts.zerotier-network-id.value;
systemd.services.zerotierone.serviceConfig.ExecStartPost = [ systemd.services.zerotierone.serviceConfig.ExecStartPost = [
"+${pkgs.writeShellScript "whitelist-controller" '' "+${pkgs.writeShellScript "whitelist-controller" ''
${config.clanCore.clanPkgs.zerotier-members}/bin/zerotier-members allow ${builtins.substring 0 10 cfg.networkId} ${config.clanCore.clanPkgs.zerotier-members}/bin/zerotier-members allow ${
builtins.substring 0 10 cfg.networkId
}
''}" ''}"
]; ];
}) })

View File

@ -1,4 +1,5 @@
{ inputs, self, ... }: { { inputs, self, ... }:
{
flake.nixosModules = { flake.nixosModules = {
hidden-ssh-announce.imports = [ ./hidden-ssh-announce.nix ]; hidden-ssh-announce.imports = [ ./hidden-ssh-announce.nix ];
installer.imports = [ installer.imports = [
@ -10,9 +11,12 @@
inputs.sops-nix.nixosModules.sops inputs.sops-nix.nixosModules.sops
./clanCore ./clanCore
./iso ./iso
({ pkgs, lib, ... }: { (
{ pkgs, lib, ... }:
{
clanCore.clanPkgs = lib.mkDefault self.packages.${pkgs.hostPlatform.system}; clanCore.clanPkgs = lib.mkDefault self.packages.${pkgs.hostPlatform.system};
}) }
)
]; ];
}; };
} }

View File

@ -1,8 +1,10 @@
{ config {
, lib config,
, pkgs lib,
, ... pkgs,
}: { ...
}:
{
options.hidden-ssh-announce = { options.hidden-ssh-announce = {
enable = lib.mkEnableOption "hidden-ssh-announce"; enable = lib.mkEnableOption "hidden-ssh-announce";
script = lib.mkOption { script = lib.mkOption {
@ -32,8 +34,14 @@
}; };
systemd.services.hidden-ssh-announce = { systemd.services.hidden-ssh-announce = {
description = "announce hidden ssh"; description = "announce hidden ssh";
after = [ "tor.service" "network-online.target" ]; after = [
wants = [ "tor.service" "network-online.target" ]; "tor.service"
"network-online.target"
];
wants = [
"tor.service"
"network-online.target"
];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
serviceConfig = { serviceConfig = {
# ${pkgs.tor}/bin/torify # ${pkgs.tor}/bin/torify

View File

@ -1,11 +1,11 @@
{ lib {
, pkgs lib,
, modulesPath pkgs,
, ... modulesPath,
}: { ...
systemd.tmpfiles.rules = [ }:
"d /var/shared 0777 root root - -" {
]; systemd.tmpfiles.rules = [ "d /var/shared 0777 root root - -" ];
imports = [ imports = [
(modulesPath + "/profiles/installation-device.nix") (modulesPath + "/profiles/installation-device.nix")
(modulesPath + "/profiles/all-hardware.nix") (modulesPath + "/profiles/all-hardware.nix")
@ -21,7 +21,17 @@
enable = true; enable = true;
script = pkgs.writeShellScript "write-hostname" '' script = pkgs.writeShellScript "write-hostname" ''
set -efu set -efu
export PATH=${lib.makeBinPath (with pkgs; [ iproute2 coreutils jq qrencode ])} export PATH=${
lib.makeBinPath (
with pkgs;
[
iproute2
coreutils
jq
qrencode
]
)
}
mkdir -p /var/shared mkdir -p /var/shared
echo "$1" > /var/shared/onion-hostname echo "$1" > /var/shared/onion-hostname

View File

@ -1,4 +1,10 @@
{ config, extendModules, lib, pkgs, ... }: {
config,
extendModules,
lib,
pkgs,
...
}:
let let
# Generates a fileSystems entry for bind mounting a given state folder path # Generates a fileSystems entry for bind mounting a given state folder path
# It binds directories from /var/clanstate/{some-path} to /{some-path}. # It binds directories from /var/clanstate/{some-path} to /{some-path}.
@ -13,27 +19,19 @@ let
}; };
# Flatten the list of state folders into a single list # Flatten the list of state folders into a single list
stateFolders = lib.flatten ( stateFolders = lib.flatten (lib.mapAttrsToList (_item: attrs: attrs.folders) config.clanCore.state);
lib.mapAttrsToList
(_item: attrs: attrs.folders)
config.clanCore.state
);
# A module setting up bind mounts for all state folders # A module setting up bind mounts for all state folders
stateMounts = { stateMounts = {
fileSystems = fileSystems = lib.listToAttrs (map mkBindMount stateFolders);
lib.listToAttrs
(map mkBindMount stateFolders);
}; };
isoModule = { config, ... }: { isoModule =
imports = [ { config, ... }:
stateMounts {
]; imports = [ stateMounts ];
options.clan.iso.disko = lib.mkOption { options.clan.iso.disko = lib.mkOption {
type = lib.types.submodule { type = lib.types.submodule { freeformType = (pkgs.formats.json { }).type; };
freeformType = (pkgs.formats.json { }).type;
};
default = { default = {
disk = { disk = {
iso = { iso = {
@ -78,9 +76,7 @@ let
}; };
}; };
isoConfig = extendModules { isoConfig = extendModules { modules = [ isoModule ]; };
modules = [ isoModule ];
};
in in
{ {
config = { config = {

View File

@ -1,36 +1,37 @@
{ age {
, lib age,
, argcomplete lib,
, installShellFiles argcomplete,
, nix installShellFiles,
, openssh nix,
, pytest openssh,
, pytest-cov pytest,
, pytest-xdist pytest-cov,
, pytest-subprocess pytest-xdist,
, pytest-timeout pytest-subprocess,
, remote-pdb pytest-timeout,
, ipdb remote-pdb,
, python3 ipdb,
, runCommand python3,
, setuptools runCommand,
, sops setuptools,
, stdenv sops,
, wheel stdenv,
, fakeroot wheel,
, rsync fakeroot,
, bash rsync,
, sshpass bash,
, zbar sshpass,
, tor zbar,
, git tor,
, nixpkgs git,
, qemu nixpkgs,
, gnupg qemu,
, e2fsprogs gnupg,
, mypy e2fsprogs,
, rope mypy,
, clan-core-path rope,
clan-core-path,
}: }:
let let
@ -38,7 +39,10 @@ let
argcomplete # optional dependency: if not enabled, shell completion will not work argcomplete # optional dependency: if not enabled, shell completion will not work
]; ];
pytestDependencies = runtimeDependencies ++ dependencies ++ [ pytestDependencies =
runtimeDependencies
++ dependencies
++ [
pytest pytest
pytest-cov pytest-cov
pytest-subprocess pytest-subprocess
@ -70,7 +74,9 @@ let
e2fsprogs e2fsprogs
]; ];
runtimeDependenciesAsSet = builtins.listToAttrs (builtins.map (p: lib.nameValuePair (lib.getName p.name) p) runtimeDependencies); runtimeDependenciesAsSet = builtins.listToAttrs (
builtins.map (p: lib.nameValuePair (lib.getName p.name) p) runtimeDependencies
);
checkPython = python3.withPackages (_ps: pytestDependencies); checkPython = python3.withPackages (_ps: pytestDependencies);
@ -121,8 +127,12 @@ python3.pkgs.buildPythonApplication {
propagatedBuildInputs = dependencies; propagatedBuildInputs = dependencies;
# also re-expose dependencies so we test them in CI # also re-expose dependencies so we test them in CI
passthru.tests = (lib.mapAttrs' (n: lib.nameValuePair "clan-dep-${n}") runtimeDependenciesAsSet) // rec { passthru.tests =
clan-pytest-without-core = runCommand "clan-pytest-without-core" { nativeBuildInputs = [ checkPython ] ++ pytestDependencies; } '' (lib.mapAttrs' (n: lib.nameValuePair "clan-dep-${n}") runtimeDependenciesAsSet)
// rec {
clan-pytest-without-core =
runCommand "clan-pytest-without-core" { nativeBuildInputs = [ checkPython ] ++ pytestDependencies; }
''
cp -r ${source} ./src cp -r ${source} ./src
chmod +w -R ./src chmod +w -R ./src
cd ./src cd ./src
@ -132,7 +142,9 @@ python3.pkgs.buildPythonApplication {
touch $out touch $out
''; '';
# separate the tests that can never be cached # separate the tests that can never be cached
clan-pytest-with-core = runCommand "clan-pytest-with-core" { nativeBuildInputs = [ checkPython ] ++ pytestDependencies; } '' clan-pytest-with-core =
runCommand "clan-pytest-with-core" { nativeBuildInputs = [ checkPython ] ++ pytestDependencies; }
''
cp -r ${source} ./src cp -r ${source} ./src
chmod +w -R ./src chmod +w -R ./src
cd ./src cd ./src

View File

@ -1,37 +1,44 @@
{ inputs, self, lib, ... }:
{ {
perSystem = { self', pkgs, ... }: inputs,
self,
lib,
...
}:
{
perSystem =
{ self', pkgs, ... }:
let let
flakeLock = lib.importJSON (self + /flake.lock); flakeLock = lib.importJSON (self + /flake.lock);
flakeInputs = (builtins.removeAttrs inputs [ "self" ]); flakeInputs = (builtins.removeAttrs inputs [ "self" ]);
flakeLockVendoredDeps = flakeLock // { flakeLockVendoredDeps = flakeLock // {
nodes = flakeLock.nodes // ( nodes =
lib.flip lib.mapAttrs flakeInputs (name: _: flakeLock.nodes.${name} // { flakeLock.nodes
// (lib.flip lib.mapAttrs flakeInputs (
name: _:
flakeLock.nodes.${name}
// {
locked = { locked = {
inherit (flakeLock.nodes.${name}.locked) narHash; inherit (flakeLock.nodes.${name}.locked) narHash;
lastModified = lastModified =
# lol, nixpkgs has a different timestamp on the fs??? # lol, nixpkgs has a different timestamp on the fs???
if name == "nixpkgs" if name == "nixpkgs" then 0 else 1;
then 0
else 1;
path = "${inputs.${name}}"; path = "${inputs.${name}}";
type = "path"; type = "path";
}; };
}) }
); ));
}; };
flakeLockFile = builtins.toFile "clan-core-flake.lock" flakeLockFile = builtins.toFile "clan-core-flake.lock" (builtins.toJSON flakeLockVendoredDeps);
(builtins.toJSON flakeLockVendoredDeps); clanCoreWithVendoredDeps =
clanCoreWithVendoredDeps = lib.trace flakeLockFile pkgs.runCommand "clan-core-with-vendored-deps" { } '' lib.trace flakeLockFile pkgs.runCommand "clan-core-with-vendored-deps" { }
''
cp -r ${self} $out cp -r ${self} $out
chmod +w -R $out chmod +w -R $out
cp ${flakeLockFile} $out/flake.lock cp ${flakeLockFile} $out/flake.lock
''; '';
in in
{ {
devShells.clan-cli = pkgs.callPackage ./shell.nix { devShells.clan-cli = pkgs.callPackage ./shell.nix { inherit (self'.packages) clan-cli; };
inherit (self'.packages) clan-cli;
};
packages = { packages = {
clan-cli = pkgs.python3.pkgs.callPackage ./default.nix { clan-cli = pkgs.python3.pkgs.callPackage ./default.nix {
inherit (inputs) nixpkgs; inherit (inputs) nixpkgs;
@ -42,5 +49,4 @@
checks = self'.packages.clan-cli.tests; checks = self'.packages.clan-cli.tests;
}; };
} }

View File

@ -1,16 +1,20 @@
{ nix-unit, clan-cli, system, mkShell, writeScriptBin, openssh, ruff, python3 }: {
nix-unit,
clan-cli,
system,
mkShell,
writeScriptBin,
openssh,
ruff,
python3,
}:
let let
checkScript = writeScriptBin "check" '' checkScript = writeScriptBin "check" ''
nix build .#checks.${system}.{treefmt,clan-pytest} -L "$@" nix build .#checks.${system}.{treefmt,clan-pytest} -L "$@"
''; '';
pythonWithDeps = python3.withPackages ( pythonWithDeps = python3.withPackages (
ps: ps: clan-cli.propagatedBuildInputs ++ clan-cli.devDependencies ++ [ ps.pip ]
clan-cli.propagatedBuildInputs
++ clan-cli.devDependencies
++ [
ps.pip
]
); );
in in
mkShell { mkShell {

View File

@ -1,4 +1,5 @@
{ lib, ... }: { { lib, ... }:
{
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__"; clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
system.stateVersion = lib.version; system.stateVersion = lib.version;
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__"; sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";

View File

@ -1,4 +1,5 @@
{ lib, ... }: { { lib, ... }:
{
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__"; clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
system.stateVersion = lib.version; system.stateVersion = lib.version;
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__"; sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";

View File

@ -1,4 +1,5 @@
{ lib, ... }: { { lib, ... }:
{
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__"; clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
system.stateVersion = lib.version; system.stateVersion = lib.version;
clan.virtualisation.graphics = false; clan.virtualisation.graphics = false;

View File

@ -1,6 +1,5 @@
{ lib { lib, ... }:
, ... {
}: {
options.clan.fake-module.fake-flag = lib.mkOption { options.clan.fake-module.fake-flag = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = false;

View File

@ -2,32 +2,41 @@
# this placeholder is replaced by the path to nixpkgs # this placeholder is replaced by the path to nixpkgs
inputs.nixpkgs.url = "__NIXPKGS__"; inputs.nixpkgs.url = "__NIXPKGS__";
outputs = inputs': outputs =
inputs':
let let
# fake clan-core input # fake clan-core input
fake-clan-core = { fake-clan-core = {
clanModules.fake-module = ./fake-module.nix; clanModules.fake-module = ./fake-module.nix;
}; };
inputs = inputs' // { clan-core = fake-clan-core; }; inputs = inputs' // {
clan-core = fake-clan-core;
};
machineSettings = ( machineSettings = (
if builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE" != "" if builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE" != "" then
then builtins.fromJSON (builtins.readFile (builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE")) builtins.fromJSON (builtins.readFile (builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE"))
else if builtins.pathExists ./machines/machine1/settings.json else if builtins.pathExists ./machines/machine1/settings.json then
then builtins.fromJSON (builtins.readFile ./machines/machine1/settings.json) builtins.fromJSON (builtins.readFile ./machines/machine1/settings.json)
else { } else
{ }
);
machineImports = map (module: fake-clan-core.clanModules.${module}) (
machineSettings.clanImports or [ ]
); );
machineImports =
map
(module: fake-clan-core.clanModules.${module})
(machineSettings.clanImports or [ ]);
in in
{ {
nixosConfigurations.machine1 = inputs.nixpkgs.lib.nixosSystem { nixosConfigurations.machine1 = inputs.nixpkgs.lib.nixosSystem {
modules = modules = machineImports ++ [
machineImports ++ [
./nixosModules/machine1.nix ./nixosModules/machine1.nix
machineSettings machineSettings
({ lib, options, pkgs, ... }: { (
{
lib,
options,
pkgs,
...
}:
{
config = { config = {
nixpkgs.hostPlatform = "x86_64-linux"; nixpkgs.hostPlatform = "x86_64-linux";
# speed up by not instantiating nixpkgs twice and disable documentation # speed up by not instantiating nixpkgs twice and disable documentation
@ -51,7 +60,8 @@
The buildClan function will automatically import these modules for the current machine. The buildClan function will automatically import these modules for the current machine.
''; '';
}; };
}) }
)
]; ];
}; };
}; };

View File

@ -1,4 +1,5 @@
{ lib, ... }: { { lib, ... }:
{
options.clan.jitsi.enable = lib.mkOption { options.clan.jitsi.enable = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = false;

View File

@ -5,13 +5,16 @@
# this placeholder is replaced by the path to nixpkgs # this placeholder is replaced by the path to nixpkgs
inputs.clan-core.url = "__CLAN_CORE__"; inputs.clan-core.url = "__CLAN_CORE__";
outputs = { self, clan-core }: outputs =
{ self, clan-core }:
let let
clan = clan-core.lib.buildClan { clan = clan-core.lib.buildClan {
directory = self; directory = self;
clanName = "test_flake_with_core"; clanName = "test_flake_with_core";
machines = { machines = {
vm1 = { lib, ... }: { vm1 =
{ lib, ... }:
{
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__"; clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
system.stateVersion = lib.version; system.stateVersion = lib.version;
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__"; sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";
@ -32,7 +35,9 @@
''; '';
}; };
}; };
vm2 = { lib, ... }: { vm2 =
{ lib, ... }:
{
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__"; clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
system.stateVersion = lib.version; system.stateVersion = lib.version;
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__"; sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";

View File

@ -5,13 +5,16 @@
# this placeholder is replaced by the path to clan-core # this placeholder is replaced by the path to clan-core
inputs.clan-core.url = "__CLAN_CORE__"; inputs.clan-core.url = "__CLAN_CORE__";
outputs = { self, clan-core }: outputs =
{ self, clan-core }:
let let
clan = clan-core.lib.buildClan { clan = clan-core.lib.buildClan {
directory = self; directory = self;
clanName = "test_flake_with_core_and_pass"; clanName = "test_flake_with_core_and_pass";
machines = { machines = {
vm1 = { lib, ... }: { vm1 =
{ lib, ... }:
{
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__"; clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
system.stateVersion = lib.version; system.stateVersion = lib.version;
clanCore.secretStore = "password-store"; clanCore.secretStore = "password-store";

View File

@ -5,7 +5,8 @@
# this placeholder is replaced by the path to nixpkgs # this placeholder is replaced by the path to nixpkgs
inputs.clan-core.url = "__CLAN_CORE__"; inputs.clan-core.url = "__CLAN_CORE__";
outputs = { self, clan-core }: outputs =
{ self, clan-core }:
let let
clan = clan-core.lib.buildClan { clan = clan-core.lib.buildClan {
directory = self; directory = self;
@ -14,9 +15,7 @@
let let
machineModules = builtins.readDir (self + "/machines"); machineModules = builtins.readDir (self + "/machines");
in in
builtins.mapAttrs builtins.mapAttrs (name: _type: import (self + "/machines/${name}")) machineModules;
(name: _type: import (self + "/machines/${name}"))
machineModules;
}; };
in in
{ {

View File

@ -1,16 +1,17 @@
{ python3 {
, runCommand python3,
, setuptools runCommand,
, copyDesktopItems setuptools,
, pygobject3 copyDesktopItems,
, wrapGAppsHook pygobject3,
, gtk4 wrapGAppsHook,
, gnome gtk4,
, pygobject-stubs gnome,
, gobject-introspection pygobject-stubs,
, clan-cli gobject-introspection,
, makeDesktopItem clan-cli,
, libadwaita makeDesktopItem,
libadwaita,
}: }:
let let
source = ./.; source = ./.;
@ -41,7 +42,11 @@ python3.pkgs.buildPythonApplication {
gobject-introspection gobject-introspection
]; ];
buildInputs = [ gtk4 libadwaita gnome.adwaita-icon-theme ]; buildInputs = [
gtk4
libadwaita
gnome.adwaita-icon-theme
];
# We need to propagate the build inputs to nix fmt / treefmt # We need to propagate the build inputs to nix fmt / treefmt
propagatedBuildInputs = [ propagatedBuildInputs = [
@ -73,7 +78,5 @@ python3.pkgs.buildPythonApplication {
checkPhase = '' checkPhase = ''
PYTHONPATH= $out/bin/clan-vm-manager --help PYTHONPATH= $out/bin/clan-vm-manager --help
''; '';
desktopItems = [ desktopItems = [ desktop-file ];
desktop-file
];
} }

View File

@ -1,5 +1,8 @@
{ ... }: { { ... }:
perSystem = { config, pkgs, ... }: { {
perSystem =
{ config, pkgs, ... }:
{
devShells.clan-vm-manager = pkgs.callPackage ./shell.nix { devShells.clan-vm-manager = pkgs.callPackage ./shell.nix {
inherit (config.packages) clan-cli clan-vm-manager; inherit (config.packages) clan-cli clan-vm-manager;
}; };

View File

@ -1,7 +1,33 @@
{ lib, runCommand, makeWrapper, stdenv, clan-vm-manager, gdb, gtk4, libadwaita, clan-cli, mkShell, ruff, desktop-file-utils, xdg-utils, mypy, python3, python3Packages }: {
lib,
runCommand,
makeWrapper,
stdenv,
clan-vm-manager,
gdb,
gtk4,
libadwaita,
clan-cli,
mkShell,
ruff,
desktop-file-utils,
xdg-utils,
mypy,
python3,
python3Packages,
}:
mkShell ( mkShell (
let let
pygdb = runCommand "pygdb" { buildInputs = [ gdb python3 makeWrapper ]; } '' pygdb =
runCommand "pygdb"
{
buildInputs = [
gdb
python3
makeWrapper
];
}
''
mkdir -p "$out/bin" mkdir -p "$out/bin"
makeWrapper "${gdb}/bin/gdb" "$out/bin/pygdb" \ makeWrapper "${gdb}/bin/gdb" "$out/bin/pygdb" \
--add-flags '-ex "source ${python3}/share/gdb/libpython.py"' --add-flags '-ex "source ${python3}/share/gdb/libpython.py"'
@ -15,7 +41,6 @@ mkShell (
pygdb pygdb
]; ];
# To debug clan-vm-manger execute pygdb --args python ./bin/clan-vm-manager # To debug clan-vm-manger execute pygdb --args python ./bin/clan-vm-manager
nativeBuildInputs = [ nativeBuildInputs = [
ruff ruff

View File

@ -1,23 +1,30 @@
{ ... }: { { ... }:
{
imports = [ imports = [
./clan-cli/flake-module.nix ./clan-cli/flake-module.nix
./clan-vm-manager/flake-module.nix ./clan-vm-manager/flake-module.nix
./installer/flake-module.nix ./installer/flake-module.nix
]; ];
perSystem = { pkgs, config, lib, ... }: { perSystem =
packages = { {
pkgs,
config,
lib,
...
}:
{
packages =
{
tea-create-pr = pkgs.callPackage ./tea-create-pr { }; tea-create-pr = pkgs.callPackage ./tea-create-pr { };
zerotier-members = pkgs.callPackage ./zerotier-members { }; zerotier-members = pkgs.callPackage ./zerotier-members { };
zt-tcp-relay = pkgs.callPackage ./zt-tcp-relay { }; zt-tcp-relay = pkgs.callPackage ./zt-tcp-relay { };
merge-after-ci = pkgs.callPackage ./merge-after-ci { merge-after-ci = pkgs.callPackage ./merge-after-ci { inherit (config.packages) tea-create-pr; };
inherit (config.packages) tea-create-pr;
};
pending-reviews = pkgs.callPackage ./pending-reviews { }; pending-reviews = pkgs.callPackage ./pending-reviews { };
} // lib.optionalAttrs pkgs.stdenv.isLinux { }
// lib.optionalAttrs pkgs.stdenv.isLinux {
wayland-proxy-virtwl = pkgs.callPackage ./wayland-proxy-virtwl { }; wayland-proxy-virtwl = pkgs.callPackage ./wayland-proxy-virtwl { };
waypipe = pkgs.waypipe.overrideAttrs waypipe = pkgs.waypipe.overrideAttrs (_old: {
(_old: {
# https://gitlab.freedesktop.org/mstoeckl/waypipe # https://gitlab.freedesktop.org/mstoeckl/waypipe
src = pkgs.fetchFromGitLab { src = pkgs.fetchFromGitLab {
domain = "gitlab.freedesktop.org"; domain = "gitlab.freedesktop.org";

View File

@ -1,7 +1,7 @@
{ lib {
, buildGoModule lib,
, fetchFromGitHub buildGoModule,
, fetchFromGitHub,
}: }:
buildGoModule rec { buildGoModule rec {
pname = "go-ssb"; pname = "go-ssb";
@ -17,7 +17,10 @@ buildGoModule rec {
vendorHash = "sha256-ZytuWFre7Cz6Qt01tLQoPEuNzDIyoC938OkdIrU8nZo="; vendorHash = "sha256-ZytuWFre7Cz6Qt01tLQoPEuNzDIyoC938OkdIrU8nZo=";
ldflags = [ "-s" "-w" ]; ldflags = [
"-s"
"-w"
];
# take very long # take very long
doCheck = false; doCheck = false;

View File

@ -1,6 +1,8 @@
{ self, lib, ... }: { self, lib, ... }:
let let
installerModule = { config, pkgs, ... }: { installerModule =
{ config, pkgs, ... }:
{
imports = [ imports = [
self.nixosModules.installer self.nixosModules.installer
self.inputs.nixos-generators.nixosModules.all-formats self.inputs.nixos-generators.nixosModules.all-formats
@ -27,7 +29,9 @@ in
flake.packages.x86_64-linux.install-iso = self.inputs.disko.lib.makeDiskImages { flake.packages.x86_64-linux.install-iso = self.inputs.disko.lib.makeDiskImages {
nixosConfig = installer; nixosConfig = installer;
}; };
flake.nixosConfigurations = { inherit (clan.nixosConfigurations) installer; }; flake.nixosConfigurations = {
inherit (clan.nixosConfigurations) installer;
};
flake.clanInternals = clan.clanInternals; flake.clanInternals = clan.clanInternals;
flake.apps.x86_64-linux.install-vm.program = installer.config.formats.vm.outPath; flake.apps.x86_64-linux.install-vm.program = installer.config.formats.vm.outPath;
flake.apps.x86_64-linux.install-vm-nogui.program = installer.config.formats.vm-nogui.outPath; flake.apps.x86_64-linux.install-vm-nogui.program = installer.config.formats.vm-nogui.outPath;

View File

@ -1,19 +1,19 @@
{ bash {
, callPackage bash,
, coreutils callPackage,
, git coreutils,
, lib git,
, nix lib,
, openssh nix,
, tea openssh,
, tea-create-pr tea,
, ... tea-create-pr,
...
}: }:
let let
writers = callPackage ../builders/script-writers.nix { }; writers = callPackage ../builders/script-writers.nix { };
in in
writers.writePython3Bin "merge-after-ci" writers.writePython3Bin "merge-after-ci" {
{
makeWrapperArgs = [ makeWrapperArgs = [
"--prefix" "--prefix"
"PATH" "PATH"
@ -28,6 +28,4 @@ writers.writePython3Bin "merge-after-ci"
tea-create-pr tea-create-pr
]) ])
]; ];
} } ./merge-after-ci.py
./merge-after-ci.py

View File

@ -1,6 +1,7 @@
{ writeShellApplication {
, bash writeShellApplication,
, curl bash,
curl,
}: }:
writeShellApplication { writeShellApplication {
name = "pending-reviews"; name = "pending-reviews";

View File

@ -1,9 +1,10 @@
{ writeShellApplication {
, bash writeShellApplication,
, coreutils bash,
, git coreutils,
, tea git,
, openssh tea,
openssh,
}: }:
writeShellApplication { writeShellApplication {
name = "tea-create-pr"; name = "tea-create-pr";

View File

@ -1,4 +1,9 @@
{ wayland-proxy-virtwl, fetchFromGitHub, libdrm, ocaml-ng }: {
wayland-proxy-virtwl,
fetchFromGitHub,
libdrm,
ocaml-ng,
}:
let let
ocaml-wayland = ocaml-ng.ocamlPackages_5_0.wayland.overrideAttrs (_old: { ocaml-wayland = ocaml-ng.ocamlPackages_5_0.wayland.overrideAttrs (_old: {
src = fetchFromGitHub { src = fetchFromGitHub {
@ -16,7 +21,9 @@ wayland-proxy-virtwl.overrideAttrs (_old: {
rev = "652fca9d4e006a2bdeba920dfaf53190c5373a7d"; rev = "652fca9d4e006a2bdeba920dfaf53190c5373a7d";
hash = "sha256-VgpqxjHgueK9eQSX987PF0KvscpzkScOzFkW3haYCOw="; hash = "sha256-VgpqxjHgueK9eQSX987PF0KvscpzkScOzFkW3haYCOw=";
}; };
buildInputs = [ libdrm ] ++ (with ocaml-ng.ocamlPackages_5_0; [ buildInputs =
[ libdrm ]
++ (with ocaml-ng.ocamlPackages_5_0; [
ocaml-wayland ocaml-wayland
dune-configurator dune-configurator
eio_main eio_main

View File

@ -1,4 +1,8 @@
{ stdenv, python3, lib }: {
stdenv,
python3,
lib,
}:
stdenv.mkDerivation { stdenv.mkDerivation {
name = "zerotier-members"; name = "zerotier-members";

View File

@ -1,6 +1,7 @@
{ lib {
, rustPlatform lib,
, fetchFromGitHub rustPlatform,
fetchFromGitHub,
}: }:
rustPlatform.buildRustPackage { rustPlatform.buildRustPackage {

View File

@ -1,4 +1,5 @@
{ self, ... }: { { self, ... }:
{
flake.templates = { flake.templates = {
new-clan = { new-clan = {
description = "Initialize a new clan flake"; description = "Initialize a new clan flake";

View File

@ -3,7 +3,8 @@
inputs.clan-core.url = "git+https://git.clan.lol/clan/clan-core"; inputs.clan-core.url = "git+https://git.clan.lol/clan/clan-core";
outputs = { self, clan-core, ... }: outputs =
{ self, clan-core, ... }:
let let
system = "x86_64-linux"; system = "x86_64-linux";
pkgs = clan-core.inputs.nixpkgs.legacyPackages.${system}; pkgs = clan-core.inputs.nixpkgs.legacyPackages.${system};
@ -17,9 +18,7 @@
inherit (clan) nixosConfigurations clanInternals; inherit (clan) nixosConfigurations clanInternals;
# add the cLAN cli tool to the dev shell # add the cLAN cli tool to the dev shell
devShells.${system}.default = pkgs.mkShell { devShells.${system}.default = pkgs.mkShell {
packages = [ packages = [ clan-core.packages.${system}.clan-cli ];
clan-core.packages.${system}.clan-cli
];
}; };
}; };
} }