From 8ca0a2aee4b72f07b22e6bb49f80155a09d41d75 Mon Sep 17 00:00:00 2001 From: DavHau Date: Sat, 26 Aug 2023 10:32:04 +0200 Subject: [PATCH 01/40] clan-config: introduce --machine + add tests --- pkgs/clan-cli/clan_cli/config/__init__.py | 190 ++++++++++++-------- pkgs/clan-cli/clan_cli/config/machine.py | 2 +- pkgs/clan-cli/clan_cli/nix.py | 26 +++ pkgs/clan-cli/default.nix | 2 +- pkgs/clan-cli/tests/machine_flake/flake.nix | 9 +- pkgs/clan-cli/tests/test_config.py | 32 +++- 6 files changed, 178 insertions(+), 83 deletions(-) diff --git a/pkgs/clan-cli/clan_cli/config/__init__.py b/pkgs/clan-cli/clan_cli/config/__init__.py index fbfad5163..8016ea082 100644 --- a/pkgs/clan-cli/clan_cli/config/__init__.py +++ b/pkgs/clan-cli/clan_cli/config/__init__.py @@ -5,9 +5,11 @@ import os import subprocess import sys from pathlib import Path -from typing import Any, Optional, Type, Union +from typing import Any, Optional, Type +from clan_cli.dirs import get_clan_flake_toplevel from clan_cli.errors import ClanError +from clan_cli.nix import nix_eval script_dir = Path(__file__).parent @@ -91,16 +93,61 @@ def cast(value: Any, type: Type, opt_description: str) -> Any: ) -def read_option(option: str) -> str: +def options_for_machine(machine_name: str, flake: Optional[Path] = None) -> dict: + if flake is None: + flake = get_clan_flake_toplevel() + # use nix eval to lib.evalModules .#clanModules.machine-{machine_name} + proc = subprocess.run( + nix_eval( + flags=[ + "--json", + "--show-trace", + "--impure", + "--expr", + f""" + let + flake = builtins.getFlake (toString {flake}); + lib = flake.inputs.nixpkgs.lib; + module = flake.nixosModules.machine-{machine_name}; + evaled = lib.evalModules {{ + modules = [module]; + }}; + + # this is actually system independent as it uses toFile + docs = flake.inputs.nixpkgs.legacyPackages.x86_64-linux.nixosOptionsDoc {{ + inherit (evaled) options; + }}; + options = builtins.fromJSON (builtins.readFile docs.optionsJSON.options); + in + options + """, + ], + ), + capture_output=True, + text=True, + ) + if proc.returncode != 0: + print(proc.stderr, file=sys.stderr) + raise Exception( + f"Failed to read options for machine {machine_name}:\n{proc.stderr}" + ) + options = json.loads(proc.stdout) + return options + + +def read_machine_option_value(machine_name: str, option: str) -> str: # use nix eval to read from .#nixosConfigurations.default.config.{option} # this will give us the evaluated config with the options attribute proc = subprocess.run( - [ - "nix", - "eval", - "--json", - f".#nixosConfigurations.default.config.{option}", - ], + nix_eval( + flags=[ + "--json", + "--show-trace", + "--extra-experimental-features", + "nix-command flakes", + f".#nixosConfigurations.{machine_name}.config.{option}", + ], + ), capture_output=True, text=True, ) @@ -119,18 +166,44 @@ def read_option(option: str) -> str: return out -def process_args( +def get_or_set_option(args: argparse.Namespace) -> None: + if args.value == []: + print(read_machine_option_value(args.machine, args.option)) + else: + # load options + print(args.options_file) + if args.options_file is None: + options = options_for_machine(machine_name=args.machine) + else: + with open(args.options_file) as f: + options = json.load(f) + # compute settings json file location + if args.settings_file is None: + flake = get_clan_flake_toplevel() + settings_file = flake / "machines" / f"{args.machine}.json" + else: + settings_file = args.settings_file + # set the option with the given value + set_option( + option=args.option, + value=args.value, + options=options, + settings_file=settings_file, + option_description=args.option, + ) + if not args.quiet: + new_value = read_machine_option_value(args.machine, args.option) + print(f"New Value for {args.option}:") + print(new_value) + + +def set_option( option: str, value: Any, options: dict, settings_file: Path, - quiet: bool = False, option_description: str = "", ) -> None: - if value == []: - print(read_option(option)) - return - option_path = option.split(".") # if the option cannot be found, then likely the type is attrs and we need to @@ -140,12 +213,11 @@ def process_args( raise ClanError(f"Option {option_description} not found") option_parent = option_path[:-1] attr = option_path[-1] - return process_args( + return set_option( option=".".join(option_parent), value={attr: value}, options=options, settings_file=settings_file, - quiet=quiet, option_description=option, ) @@ -170,45 +242,14 @@ def process_args( current_config = {} # merge and save the new config file new_config = merge(current_config, result) + settings_file.parent.mkdir(parents=True, exist_ok=True) with open(settings_file, "w") as f: json.dump(new_config, f, indent=2) - if not quiet: - new_value = read_option(option) - print(f"New Value for {option}:") - print(new_value) - - -def register_parser( - parser: argparse.ArgumentParser, - options_file: Optional[Union[str, Path]] = os.environ.get("CLAN_OPTIONS_FILE"), -) -> None: - if not options_file: - # use nix eval to evaluate .#clanOptions - # this will give us the evaluated config with the options attribute - proc = subprocess.run( - [ - "nix", - "eval", - "--raw", - ".#clanOptions", - ], - check=True, - capture_output=True, - text=True, - ) - file = proc.stdout.strip() - with open(file) as f: - options = json.load(f) - else: - with open(options_file) as f: - options = json.load(f) - return _register_parser(parser, options) # takes a (sub)parser and configures it -def _register_parser( +def register_parser( parser: Optional[argparse.ArgumentParser], - options: dict[str, Any], ) -> None: if parser is None: parser = argparse.ArgumentParser( @@ -216,31 +257,35 @@ def _register_parser( ) # inject callback function to process the input later - parser.set_defaults( - func=lambda args: process_args( - option=args.option, - value=args.value, - options=options, - quiet=args.quiet, - settings_file=args.settings_file, - ) - ) + parser.set_defaults(func=get_or_set_option) - # add --quiet option + # add --machine argument parser.add_argument( - "--quiet", - "-q", - help="Suppress output", - action="store_true", + "--machine", + "-m", + help="Machine to configure", + type=str, + default="default", ) - # add argument to pass output file + # add --options-file argument + parser.add_argument( + "--options-file", + help="JSON file with options", + type=Path, + ) + + # add --settings-file argument parser.add_argument( "--settings-file", - "-o", - help="Output file", + help="JSON file with settings", type=Path, - default=Path("clan-settings.json"), + ) + # add --quiet argument + parser.add_argument( + "--quiet", + help="Do not print the value", + action="store_true", ) # add single positional argument for the option (e.g. "foo.bar") @@ -248,7 +293,6 @@ def _register_parser( "option", help="Option to configure", type=str, - choices=AllContainer(list(options.keys())), ) # add a single optional argument for the value @@ -264,14 +308,8 @@ def main(argv: Optional[list[str]] = None) -> None: if argv is None: argv = sys.argv parser = argparse.ArgumentParser() - parser.add_argument( - "schema", - help="The schema to use for the configuration", - type=Path, - ) - args = parser.parse_args(argv[1:2]) - register_parser(parser, args.schema) - parser.parse_args(argv[2:]) + register_parser(parser) + parser.parse_args(argv[1:]) if __name__ == "__main__": diff --git a/pkgs/clan-cli/clan_cli/config/machine.py b/pkgs/clan-cli/clan_cli/config/machine.py index 8580a56be..6463bba17 100644 --- a/pkgs/clan-cli/clan_cli/config/machine.py +++ b/pkgs/clan-cli/clan_cli/config/machine.py @@ -55,7 +55,7 @@ def schema_for_machine(machine_name: str, flake: Optional[Path] = None) -> dict: let flake = builtins.getFlake (toString {flake}); lib = import {nixpkgs()}/lib; - module = builtins.trace (builtins.attrNames flake) flake.nixosModules.machine-{machine_name}; + module = flake.nixosModules.machine-{machine_name}; evaled = lib.evalModules {{ modules = [module]; }}; diff --git a/pkgs/clan-cli/clan_cli/nix.py b/pkgs/clan-cli/clan_cli/nix.py index b34db8d1d..6f866225f 100644 --- a/pkgs/clan-cli/clan_cli/nix.py +++ b/pkgs/clan-cli/clan_cli/nix.py @@ -1,8 +1,34 @@ import os +import tempfile from .dirs import flake_registry, unfree_nixpkgs +def nix_eval(flags: list[str]) -> list[str]: + if os.environ.get("IN_NIX_SANDBOX"): + with tempfile.TemporaryDirectory() as nix_store: + return [ + "nix", + "eval", + "--show-trace", + "--extra-experimental-features", + "nix-command flakes", + "--flake-registry", + str(flake_registry()), + # --store is required to prevent this error: + # error: cannot unlink '/nix/store/6xg259477c90a229xwmb53pdfkn6ig3g-default-builder.sh': Operation not permitted + "--store", + nix_store, + ] + flags + return [ + "nix", + "eval", + "--show-trace", + "--extra-experimental-features", + "nix-command flakes", + ] + flags + + def nix_shell(packages: list[str], cmd: list[str]) -> list[str]: # we cannot use nix-shell inside the nix sandbox # in our tests we just make sure we have all the packages diff --git a/pkgs/clan-cli/default.nix b/pkgs/clan-cli/default.nix index 684136fe9..6ff778dd4 100644 --- a/pkgs/clan-cli/default.nix +++ b/pkgs/clan-cli/default.nix @@ -77,7 +77,7 @@ python3.pkgs.buildPythonPackage { ]; propagatedBuildInputs = dependencies; - passthru.tests.clan-pytest = runCommand "clan-tests" + passthru.tests.clan-pytest = runCommand "clan-pytest" { nativeBuildInputs = [ age zerotierone bubblewrap sops nix openssh rsync stdenv.cc ]; } '' diff --git a/pkgs/clan-cli/tests/machine_flake/flake.nix b/pkgs/clan-cli/tests/machine_flake/flake.nix index 32aebce29..82afd3e06 100644 --- a/pkgs/clan-cli/tests/machine_flake/flake.nix +++ b/pkgs/clan-cli/tests/machine_flake/flake.nix @@ -4,7 +4,14 @@ nixpkgs.url = "__NIXPKGS__"; }; - outputs = _inputs: { + outputs = inputs: { nixosModules.machine-machine1 = ./nixosModules/machine1.nix; + nixosConfigurations.machine1 = inputs.nixpkgs.lib.nixosSystem { + modules = [ + inputs.self.nixosModules.machine-machine1 + (builtins.fromJSON (builtins.readFile ./machines/machine1.json)) + { nixpkgs.hostPlatform = "x86_64-linux"; } + ]; + }; }; } diff --git a/pkgs/clan-cli/tests/test_config.py b/pkgs/clan-cli/tests/test_config.py index e0185d298..79fc2d883 100644 --- a/pkgs/clan-cli/tests/test_config.py +++ b/pkgs/clan-cli/tests/test_config.py @@ -28,20 +28,44 @@ example_options = f"{Path(config.__file__).parent}/jsonschema/options.json" def test_set_some_option( args: list[str], expected: dict[str, Any], - monkeypatch: pytest.MonkeyPatch, ) -> None: - monkeypatch.setenv("CLAN_OPTIONS_FILE", example_options) - # create temporary file for out_file with tempfile.NamedTemporaryFile() as out_file: with open(out_file.name, "w") as f: json.dump({}, f) cli = Cli() - cli.run(["config", "--quiet", "--settings-file", out_file.name] + args) + cli.run( + [ + "config", + "--quiet", + "--options-file", + example_options, + "--settings-file", + out_file.name, + ] + + args + ) json_out = json.loads(open(out_file.name).read()) assert json_out == expected +def test_configure_machine( + machine_flake: Path, + temporary_dir: Path, + capsys: pytest.CaptureFixture, + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.setenv("HOME", str(temporary_dir)) + cli = Cli() + cli.run(["config", "-m", "machine1", "clan.jitsi.enable", "true"]) + # clear the output buffer + capsys.readouterr() + # read a option value + cli.run(["config", "-m", "machine1", "clan.jitsi.enable"]) + # read the output + assert capsys.readouterr().out == "true\n" + + def test_walk_jsonschema_all_types() -> None: schema = dict( type="object", From 414033392e1e36922c4ae09e946c41dfadf89506 Mon Sep 17 00:00:00 2001 From: DavHau Date: Sat, 2 Sep 2023 16:12:37 +0200 Subject: [PATCH 02/40] new-clan: update template and add test --- checks/impure/flake-module.nix | 17 ++++++++++++++++- templates/new-clan/flake.nix | 20 ++++++++++++++++---- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/checks/impure/flake-module.nix b/checks/impure/flake-module.nix index 5a7c8d3de..9add525bf 100644 --- a/checks/impure/flake-module.nix +++ b/checks/impure/flake-module.nix @@ -1,5 +1,5 @@ { self, ... }: { - perSystem = { pkgs, lib, ... }: + perSystem = { pkgs, lib, self', ... }: let impureChecks = { check-clan-template = pkgs.writeShellScriptBin "check-clan-template" '' @@ -9,6 +9,8 @@ trap "${pkgs.coreutils}/bin/chmod -R +w '$TMPDIR'; ${pkgs.coreutils}/bin/rm -rf '$TMPDIR'" EXIT export PATH="${lib.makeBinPath [ pkgs.gitMinimal + pkgs.gnugrep + pkgs.jq pkgs.openssh pkgs.nix ]}" @@ -18,8 +20,21 @@ echo initialize new clan nix flake init -t ${self}#new-clan + echo override clan input to the current version + nix flake lock --override-input clan-core ${self} + nix flake lock --override-input nixpkgs ${self.inputs.nixpkgs} + echo ensure flake outputs can be listed nix flake show + + echo create a machine + ${self'.packages.clan-cli}/bin/clan machines create machine1 + + echo check machine1 exists + ${self'.packages.clan-cli}/bin/clan machines list | grep -q machine1 + + echo check machine1 appears in nixosConfigurations + nix flake show --json | jq '.nixosConfigurations' | grep -q machine1 ''; }; in diff --git a/templates/new-clan/flake.nix b/templates/new-clan/flake.nix index c710d9ef2..7687075e2 100644 --- a/templates/new-clan/flake.nix +++ b/templates/new-clan/flake.nix @@ -3,9 +3,21 @@ inputs.clan-core.url = "git+https://git.clan.lol/clan/clan-core"; - outputs = { clan-core, ... }: { - nixosConfigurations = clan-core.lib.buildClan { - directory = ./.; + outputs = { clan-core, nixpkgs, ... }: + let + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.x86_64-linux; + in + { + # all machines managed by cLAN + nixosConfigurations = clan-core.lib.buildClan { + directory = ./.; + }; + # add the cLAN cli tool to the dev shell + devShells.${system}.default = pkgs.mkShell { + packages = [ + clan-core.packages.${system}.clan-cli + ]; + }; }; - }; } From a1d825d0a01251268b58ed436020761e7524404f Mon Sep 17 00:00:00 2001 From: DavHau Date: Sat, 2 Sep 2023 16:35:59 +0200 Subject: [PATCH 03/40] clan-config: fix: read options from nixosConfigurations instead of nixosModules --- pkgs/clan-cli/clan_cli/config/__init__.py | 11 ++--- pkgs/clan-cli/clan_cli/config/machine.py | 46 ++++++++++----------- pkgs/clan-cli/tests/machine_flake/flake.nix | 7 ++-- 3 files changed, 30 insertions(+), 34 deletions(-) diff --git a/pkgs/clan-cli/clan_cli/config/__init__.py b/pkgs/clan-cli/clan_cli/config/__init__.py index 8016ea082..f73a9dc12 100644 --- a/pkgs/clan-cli/clan_cli/config/__init__.py +++ b/pkgs/clan-cli/clan_cli/config/__init__.py @@ -108,18 +108,15 @@ def options_for_machine(machine_name: str, flake: Optional[Path] = None) -> dict let flake = builtins.getFlake (toString {flake}); lib = flake.inputs.nixpkgs.lib; - module = flake.nixosModules.machine-{machine_name}; - evaled = lib.evalModules {{ - modules = [module]; - }}; + options = flake.nixosConfigurations.{machine_name}.options; # this is actually system independent as it uses toFile docs = flake.inputs.nixpkgs.legacyPackages.x86_64-linux.nixosOptionsDoc {{ - inherit (evaled) options; + inherit options; }}; - options = builtins.fromJSON (builtins.readFile docs.optionsJSON.options); + opts = builtins.fromJSON (builtins.readFile docs.optionsJSON.options); in - options + opts """, ], ), diff --git a/pkgs/clan-cli/clan_cli/config/machine.py b/pkgs/clan-cli/clan_cli/config/machine.py index 6463bba17..83c56be9d 100644 --- a/pkgs/clan-cli/clan_cli/config/machine.py +++ b/pkgs/clan-cli/clan_cli/config/machine.py @@ -8,6 +8,7 @@ from fastapi import HTTPException from clan_cli.dirs import get_clan_flake_toplevel, nixpkgs from clan_cli.machines.folders import machine_folder, machine_settings_file +from clan_cli.nix import nix_eval def config_for_machine(machine_name: str) -> dict: @@ -42,30 +43,27 @@ def schema_for_machine(machine_name: str, flake: Optional[Path] = None) -> dict: flake = get_clan_flake_toplevel() # use nix eval to lib.evalModules .#nixosModules.machine-{machine_name} proc = subprocess.run( - [ - "nix", - "eval", - "--json", - "--impure", - "--show-trace", - "--extra-experimental-features", - "nix-command flakes", - "--expr", - f""" - let - flake = builtins.getFlake (toString {flake}); - lib = import {nixpkgs()}/lib; - module = flake.nixosModules.machine-{machine_name}; - evaled = lib.evalModules {{ - modules = [module]; - }}; - clanOptions = evaled.options.clan; - jsonschemaLib = import {Path(__file__).parent / "jsonschema"} {{ inherit lib; }}; - jsonschema = jsonschemaLib.parseOptions clanOptions; - in - jsonschema - """, - ], + nix_eval( + flags=[ + "--json", + "--impure", + "--show-trace", + "--extra-experimental-features", + "nix-command flakes", + "--expr", + f""" + let + flake = builtins.getFlake (toString {flake}); + lib = import {nixpkgs()}/lib; + options = flake.nixosConfigurations.{machine_name}.options; + clanOptions = options.clan; + jsonschemaLib = import {Path(__file__).parent / "jsonschema"} {{ inherit lib; }}; + jsonschema = jsonschemaLib.parseOptions clanOptions; + in + jsonschema + """, + ], + ), capture_output=True, text=True, ) diff --git a/pkgs/clan-cli/tests/machine_flake/flake.nix b/pkgs/clan-cli/tests/machine_flake/flake.nix index 82afd3e06..0f76e47fc 100644 --- a/pkgs/clan-cli/tests/machine_flake/flake.nix +++ b/pkgs/clan-cli/tests/machine_flake/flake.nix @@ -5,11 +5,12 @@ }; outputs = inputs: { - nixosModules.machine-machine1 = ./nixosModules/machine1.nix; nixosConfigurations.machine1 = inputs.nixpkgs.lib.nixosSystem { modules = [ - inputs.self.nixosModules.machine-machine1 - (builtins.fromJSON (builtins.readFile ./machines/machine1.json)) + ./nixosModules/machine1.nix + (if builtins.pathExists ./machines/machine1.json + then builtins.fromJSON (builtins.readFile ./machines/machine1.json) + else { }) { nixpkgs.hostPlatform = "x86_64-linux"; } ]; }; From 10c4d26b5858b5dd59f353fef465bf0cd1cba765 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Sat, 2 Sep 2023 17:06:22 +0200 Subject: [PATCH 04/40] add: dynamic form generation based on json-schema --- pkgs/ui/nix/pdefs.nix | 530 +++++++++++++++++++++++-- pkgs/ui/package-lock.json | 224 ++++++++++- pkgs/ui/package.json | 4 + pkgs/ui/src/app/machines/add/page.tsx | 395 +++--------------- pkgs/ui/src/app/machines/add/schema.ts | 111 ++++++ 5 files changed, 863 insertions(+), 401 deletions(-) create mode 100644 pkgs/ui/src/app/machines/add/schema.ts diff --git a/pkgs/ui/nix/pdefs.nix b/pkgs/ui/nix/pdefs.nix index 09ce05601..5224db1ff 100644 --- a/pkgs/ui/nix/pdefs.nix +++ b/pkgs/ui/nix/pdefs.nix @@ -254,7 +254,7 @@ }; }; "@babel/runtime" = { - "7.22.10" = { + "7.22.11" = { depInfo = { regenerator-runtime = { descriptor = "^0.14.0"; @@ -263,13 +263,13 @@ }; }; fetchInfo = { - narHash = "sha256-5ecEDXI/B/XZUtU3VFGYjC1yAMqmmoqb9Jyu03CI1rQ="; + narHash = "sha256-u4IYeznySCACZfl7/j6Fwdz0J5eRLYRntlijjEtZQb0="; type = "tarball"; - url = "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz"; + url = "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz"; }; ident = "@babel/runtime"; ltype = "file"; - version = "7.22.10"; + version = "7.22.11"; }; }; "@babel/types" = { @@ -311,7 +311,7 @@ }; "@babel/runtime" = { descriptor = "^7.18.3"; - pin = "7.22.10"; + pin = "7.22.11"; runtime = true; }; "@emotion/hash" = { @@ -459,7 +459,7 @@ depInfo = { "@babel/runtime" = { descriptor = "^7.18.3"; - pin = "7.22.10"; + pin = "7.22.11"; runtime = true; }; "@emotion/babel-plugin" = { @@ -574,7 +574,7 @@ depInfo = { "@babel/runtime" = { descriptor = "^7.18.3"; - pin = "7.22.10"; + pin = "7.22.11"; runtime = true; }; "@emotion/babel-plugin" = { @@ -1104,7 +1104,7 @@ depInfo = { "@babel/runtime" = { descriptor = "^7.22.6"; - pin = "7.22.10"; + pin = "7.22.11"; runtime = true; }; "@emotion/is-prop-valid" = { @@ -1119,7 +1119,7 @@ }; "@mui/utils" = { descriptor = "^5.14.5"; - pin = "5.14.5"; + pin = "5.14.7"; runtime = true; }; "@popperjs/core" = { @@ -1183,7 +1183,7 @@ depInfo = { "@babel/runtime" = { descriptor = "^7.22.6"; - pin = "7.22.10"; + pin = "7.22.11"; runtime = true; }; }; @@ -1214,7 +1214,7 @@ depInfo = { "@babel/runtime" = { descriptor = "^7.22.6"; - pin = "7.22.10"; + pin = "7.22.11"; runtime = true; }; "@mui/base" = { @@ -1239,7 +1239,7 @@ }; "@mui/utils" = { descriptor = "^5.14.5"; - pin = "5.14.5"; + pin = "5.14.7"; runtime = true; }; "@types/react-transition-group" = { @@ -1308,12 +1308,12 @@ depInfo = { "@babel/runtime" = { descriptor = "^7.22.6"; - pin = "7.22.10"; + pin = "7.22.11"; runtime = true; }; "@mui/utils" = { descriptor = "^5.14.5"; - pin = "5.14.5"; + pin = "5.14.7"; runtime = true; }; prop-types = { @@ -1346,7 +1346,7 @@ depInfo = { "@babel/runtime" = { descriptor = "^7.21.0"; - pin = "7.22.10"; + pin = "7.22.11"; runtime = true; }; "@emotion/cache" = { @@ -1393,7 +1393,7 @@ depInfo = { "@babel/runtime" = { descriptor = "^7.22.6"; - pin = "7.22.10"; + pin = "7.22.11"; runtime = true; }; "@mui/private-theming" = { @@ -1413,7 +1413,7 @@ }; "@mui/utils" = { descriptor = "^5.14.5"; - pin = "5.14.5"; + pin = "5.14.7"; runtime = true; }; clsx = { @@ -1479,11 +1479,11 @@ }; }; "@mui/utils" = { - "5.14.5" = { + "5.14.7" = { depInfo = { "@babel/runtime" = { - descriptor = "^7.22.6"; - pin = "7.22.10"; + descriptor = "^7.22.10"; + pin = "7.22.11"; runtime = true; }; "@types/prop-types" = { @@ -1508,9 +1508,9 @@ }; }; fetchInfo = { - narHash = "sha256-mym+STz4KseB2TDlXB8qkcPKpvNQDU4r+9xTC99m84U="; + narHash = "sha256-bvWlZoYxVVHqprNjDYZQtl6vrpx6BZNUe/t8J+REcHk="; type = "tarball"; - url = "https://registry.npmjs.org/@mui/utils/-/utils-5.14.5.tgz"; + url = "https://registry.npmjs.org/@mui/utils/-/utils-5.14.7.tgz"; }; ident = "@mui/utils"; ltype = "file"; @@ -1519,7 +1519,7 @@ descriptor = "^17.0.0 || ^18.0.0"; }; }; - version = "5.14.5"; + version = "5.14.7"; }; }; "@next/env" = { @@ -2080,6 +2080,172 @@ version = "2.11.8"; }; }; + "@rjsf/core" = { + "5.12.1" = { + depInfo = { + lodash = { + descriptor = "^4.17.21"; + pin = "4.17.21"; + runtime = true; + }; + lodash-es = { + descriptor = "^4.17.21"; + pin = "4.17.21"; + runtime = true; + }; + markdown-to-jsx = { + descriptor = "^7.3.2"; + pin = "7.3.2"; + runtime = true; + }; + nanoid = { + descriptor = "^3.3.6"; + pin = "3.3.6"; + runtime = true; + }; + prop-types = { + descriptor = "^15.8.1"; + pin = "15.8.1"; + runtime = true; + }; + }; + fetchInfo = { + narHash = "sha256-TYa/k9q0Au9+0l7qyLaa2XMyQ6bHZqRniGzzo7BFDWk="; + type = "tarball"; + url = "https://registry.npmjs.org/@rjsf/core/-/core-5.12.1.tgz"; + }; + ident = "@rjsf/core"; + ltype = "file"; + peerInfo = { + "@rjsf/utils" = { + descriptor = "^5.8.x"; + }; + react = { + descriptor = "^16.14.0 || >=17"; + }; + }; + version = "5.12.1"; + }; + }; + "@rjsf/mui" = { + "5.12.1" = { + fetchInfo = { + narHash = "sha256-HS37nzO3SsWJycV8yvqrEjtcb9w8GxtivBBWArBhziU="; + type = "tarball"; + url = "https://registry.npmjs.org/@rjsf/mui/-/mui-5.12.1.tgz"; + }; + ident = "@rjsf/mui"; + ltype = "file"; + peerInfo = { + "@emotion/react" = { + descriptor = "^11.7.0"; + }; + "@emotion/styled" = { + descriptor = "^11.6.0"; + }; + "@mui/icons-material" = { + descriptor = "^5.2.0"; + }; + "@mui/material" = { + descriptor = "^5.2.2"; + }; + "@rjsf/core" = { + descriptor = "^5.8.x"; + }; + "@rjsf/utils" = { + descriptor = "^5.8.x"; + }; + react = { + descriptor = ">=17"; + }; + }; + treeInfo = { }; + version = "5.12.1"; + }; + }; + "@rjsf/utils" = { + "5.12.1" = { + depInfo = { + json-schema-merge-allof = { + descriptor = "^0.8.1"; + pin = "0.8.1"; + runtime = true; + }; + jsonpointer = { + descriptor = "^5.0.1"; + pin = "5.0.1"; + runtime = true; + }; + lodash = { + descriptor = "^4.17.21"; + pin = "4.17.21"; + runtime = true; + }; + lodash-es = { + descriptor = "^4.17.21"; + pin = "4.17.21"; + runtime = true; + }; + react-is = { + descriptor = "^18.2.0"; + pin = "18.2.0"; + runtime = true; + }; + }; + fetchInfo = { + narHash = "sha256-CR5Jmw9hCiLFWgoxBDdhuzItZz/Y60pAX0T0IxMQKJM="; + type = "tarball"; + url = "https://registry.npmjs.org/@rjsf/utils/-/utils-5.12.1.tgz"; + }; + ident = "@rjsf/utils"; + ltype = "file"; + peerInfo = { + react = { + descriptor = "^16.14.0 || >=17"; + }; + }; + version = "5.12.1"; + }; + }; + "@rjsf/validator-ajv8" = { + "5.12.1" = { + depInfo = { + ajv = { + descriptor = "^8.12.0"; + pin = "8.12.0"; + runtime = true; + }; + ajv-formats = { + descriptor = "^2.1.1"; + pin = "2.1.1"; + runtime = true; + }; + lodash = { + descriptor = "^4.17.21"; + pin = "4.17.21"; + runtime = true; + }; + lodash-es = { + descriptor = "^4.17.21"; + pin = "4.17.21"; + runtime = true; + }; + }; + fetchInfo = { + narHash = "sha256-w28JPlFA1Pnc3K/qBmPqwnlgQf6Pa/b7e7UY1zCvJjg="; + type = "tarball"; + url = "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.12.1.tgz"; + }; + ident = "@rjsf/validator-ajv8"; + ltype = "file"; + peerInfo = { + "@rjsf/utils" = { + descriptor = "^5.8.x"; + }; + }; + version = "5.12.1"; + }; + }; "@rollup/plugin-commonjs" = { "22.0.2" = { depInfo = { @@ -4627,7 +4793,7 @@ depInfo = { "@babel/runtime" = { descriptor = "^7.12.5"; - pin = "7.22.10"; + pin = "7.22.11"; runtime = true; }; cosmiconfig = { @@ -5209,6 +5375,69 @@ version = "4.1.4"; }; }; + compute-gcd = { + "1.2.1" = { + depInfo = { + "validate.io-array" = { + descriptor = "^1.0.3"; + pin = "1.0.6"; + runtime = true; + }; + "validate.io-function" = { + descriptor = "^1.0.2"; + pin = "1.0.2"; + runtime = true; + }; + "validate.io-integer-array" = { + descriptor = "^1.0.0"; + pin = "1.0.0"; + runtime = true; + }; + }; + fetchInfo = { + narHash = "sha256-d0KQIsd8wClVDFno5ovxwZeZrxT8Eds/EZeee1vP9tk="; + type = "tarball"; + url = "https://registry.npmjs.org/compute-gcd/-/compute-gcd-1.2.1.tgz"; + }; + ident = "compute-gcd"; + ltype = "file"; + version = "1.2.1"; + }; + }; + compute-lcm = { + "1.1.2" = { + depInfo = { + compute-gcd = { + descriptor = "^1.2.1"; + pin = "1.2.1"; + runtime = true; + }; + "validate.io-array" = { + descriptor = "^1.0.3"; + pin = "1.0.6"; + runtime = true; + }; + "validate.io-function" = { + descriptor = "^1.0.2"; + pin = "1.0.2"; + runtime = true; + }; + "validate.io-integer-array" = { + descriptor = "^1.0.0"; + pin = "1.0.0"; + runtime = true; + }; + }; + fetchInfo = { + narHash = "sha256-1KY8MWyNiiL/EbcaST1NDtJ/EVlphHN1zvMkEkEBUDA="; + type = "tarball"; + url = "https://registry.npmjs.org/compute-lcm/-/compute-lcm-1.1.2.tgz"; + }; + ident = "compute-lcm"; + ltype = "file"; + version = "1.1.2"; + }; + }; concat-map = { "0.0.1" = { fetchInfo = { @@ -5834,7 +6063,7 @@ depInfo = { "@babel/runtime" = { descriptor = "^7.1.2"; - pin = "7.22.10"; + pin = "7.22.11"; runtime = true; }; }; @@ -5851,7 +6080,7 @@ depInfo = { "@babel/runtime" = { descriptor = "^7.8.7"; - pin = "7.22.10"; + pin = "7.22.11"; runtime = true; }; csstype = { @@ -7430,7 +7659,7 @@ depInfo = { "@babel/runtime" = { descriptor = "^7.20.7"; - pin = "7.22.10"; + pin = "7.22.11"; runtime = true; }; aria-query = { @@ -9857,6 +10086,54 @@ version = "2.3.1"; }; }; + json-schema-compare = { + "0.2.2" = { + depInfo = { + lodash = { + descriptor = "^4.17.4"; + pin = "4.17.21"; + runtime = true; + }; + }; + fetchInfo = { + narHash = "sha256-C0qcy7sHg0SseMH51wBxWKNSOuMKIsdYJrKZiorAD6g="; + type = "tarball"; + url = "https://registry.npmjs.org/json-schema-compare/-/json-schema-compare-0.2.2.tgz"; + }; + ident = "json-schema-compare"; + ltype = "file"; + version = "0.2.2"; + }; + }; + json-schema-merge-allof = { + "0.8.1" = { + depInfo = { + compute-lcm = { + descriptor = "^1.1.2"; + pin = "1.1.2"; + runtime = true; + }; + json-schema-compare = { + descriptor = "^0.2.2"; + pin = "0.2.2"; + runtime = true; + }; + lodash = { + descriptor = "^4.17.20"; + pin = "4.17.21"; + runtime = true; + }; + }; + fetchInfo = { + narHash = "sha256-XjBzD/iGKHCog9JktMJ7IV/hD3y/B7P7GPpCx+z3Ah4="; + type = "tarball"; + url = "https://registry.npmjs.org/json-schema-merge-allof/-/json-schema-merge-allof-0.8.1.tgz"; + }; + ident = "json-schema-merge-allof"; + ltype = "file"; + version = "0.8.1"; + }; + }; json-schema-ref-parser = { "5.1.3" = { depInfo = { @@ -10223,6 +10500,19 @@ version = "4.17.21"; }; }; + lodash-es = { + "4.17.21" = { + fetchInfo = { + narHash = "sha256-2l4E89z3xMFn6MP9E0rVVNFWnB1oUINVGzno0F9CL3g="; + type = "tarball"; + url = "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz"; + }; + ident = "lodash-es"; + ltype = "file"; + treeInfo = { }; + version = "4.17.21"; + }; + }; "lodash.get" = { "4.4.2" = { fetchInfo = { @@ -10402,6 +10692,24 @@ version = "0.25.9"; }; }; + markdown-to-jsx = { + "7.3.2" = { + fetchInfo = { + narHash = "sha256-9sSiMN7r0b//8QFL72wsY4tkOpztRB0yDqV+1RUN97Q="; + type = "tarball"; + url = "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.3.2.tgz"; + }; + ident = "markdown-to-jsx"; + ltype = "file"; + peerInfo = { + react = { + descriptor = ">= 0.14.0"; + }; + }; + treeInfo = { }; + version = "7.3.2"; + }; + }; matcher = { "1.1.1" = { depInfo = { @@ -10584,6 +10892,26 @@ pin = "5.14.5"; runtime = true; }; + "@rjsf/core" = { + descriptor = "^5.12.1"; + pin = "5.12.1"; + runtime = true; + }; + "@rjsf/mui" = { + descriptor = "^5.12.1"; + pin = "5.12.1"; + runtime = true; + }; + "@rjsf/validator-ajv8" = { + descriptor = "^5.12.1"; + pin = "5.12.1"; + runtime = true; + }; + "@types/json-schema" = { + descriptor = "^7.0.12"; + pin = "7.0.12"; + runtime = true; + }; "@types/node" = { descriptor = "20.4.7"; pin = "20.4.7"; @@ -10810,7 +11138,7 @@ key = "supports-color/5.5.0"; }; "node_modules/@babel/runtime" = { - key = "@babel/runtime/7.22.10"; + key = "@babel/runtime/7.22.11"; }; "node_modules/@babel/types" = { key = "@babel/types/7.22.10"; @@ -10956,7 +11284,7 @@ key = "@mui/types/7.2.4"; }; "node_modules/@mui/utils" = { - key = "@mui/utils/5.14.5"; + key = "@mui/utils/5.14.7"; }; "node_modules/@next/env" = { key = "@next/env/13.4.12"; @@ -11049,6 +11377,24 @@ "node_modules/@popperjs/core" = { key = "@popperjs/core/2.11.8"; }; + "node_modules/@rjsf/core" = { + key = "@rjsf/core/5.12.1"; + }; + "node_modules/@rjsf/mui" = { + key = "@rjsf/mui/5.12.1"; + }; + "node_modules/@rjsf/utils" = { + key = "@rjsf/utils/5.12.1"; + }; + "node_modules/@rjsf/validator-ajv8" = { + key = "@rjsf/validator-ajv8/5.12.1"; + }; + "node_modules/@rjsf/validator-ajv8/node_modules/ajv" = { + key = "ajv/8.12.0"; + }; + "node_modules/@rjsf/validator-ajv8/node_modules/json-schema-traverse" = { + key = "json-schema-traverse/1.0.0"; + }; "node_modules/@rollup/plugin-commonjs" = { dev = true; key = "@rollup/plugin-commonjs/22.0.2"; @@ -11256,7 +11602,6 @@ key = "@types/estree/0.0.39"; }; "node_modules/@types/json-schema" = { - dev = true; key = "@types/json-schema/7.0.12"; }; "node_modules/@types/json5" = { @@ -11334,15 +11679,12 @@ key = "ajv/6.12.6"; }; "node_modules/ajv-formats" = { - dev = true; key = "ajv-formats/2.1.1"; }; "node_modules/ajv-formats/node_modules/ajv" = { - dev = true; key = "ajv/8.12.0"; }; "node_modules/ajv-formats/node_modules/json-schema-traverse" = { - dev = true; key = "json-schema-traverse/1.0.0"; }; "node_modules/ansi-colors" = { @@ -11542,6 +11884,12 @@ dev = true; key = "compare-versions/4.1.4"; }; + "node_modules/compute-gcd" = { + key = "compute-gcd/1.2.1"; + }; + "node_modules/compute-lcm" = { + key = "compute-lcm/1.1.2"; + }; "node_modules/concat-map" = { key = "concat-map/0.0.1"; }; @@ -11936,7 +12284,6 @@ key = "execa/5.1.1"; }; "node_modules/fast-deep-equal" = { - dev = true; key = "fast-deep-equal/3.1.3"; }; "node_modules/fast-equals" = { @@ -12323,6 +12670,12 @@ "node_modules/json-parse-even-better-errors" = { key = "json-parse-even-better-errors/2.3.1"; }; + "node_modules/json-schema-compare" = { + key = "json-schema-compare/0.2.2"; + }; + "node_modules/json-schema-merge-allof" = { + key = "json-schema-merge-allof/0.8.1"; + }; "node_modules/json-schema-ref-parser" = { dev = true; key = "json-schema-ref-parser/5.1.3"; @@ -12364,7 +12717,6 @@ key = "jsonpath-plus/7.1.0"; }; "node_modules/jsonpointer" = { - dev = true; key = "jsonpointer/5.0.1"; }; "node_modules/jsonschema" = { @@ -12404,6 +12756,9 @@ "node_modules/lodash" = { key = "lodash/4.17.21"; }; + "node_modules/lodash-es" = { + key = "lodash-es/4.17.21"; + }; "node_modules/lodash.get" = { dev = true; key = "lodash.get/4.4.2"; @@ -12451,6 +12806,9 @@ dev = true; key = "magic-string/0.25.9"; }; + "node_modules/markdown-to-jsx" = { + key = "markdown-to-jsx/7.3.2"; + }; "node_modules/matcher" = { dev = true; key = "matcher/1.1.1"; @@ -12742,7 +13100,6 @@ key = "proxy-from-env/1.1.0"; }; "node_modules/punycode" = { - dev = true; key = "punycode/2.3.0"; }; "node_modules/queue-microtask" = { @@ -12822,7 +13179,6 @@ key = "require-directory/2.1.1"; }; "node_modules/require-from-string" = { - dev = true; key = "require-from-string/2.0.2"; }; "node_modules/reserved" = { @@ -13100,7 +13456,6 @@ key = "update-browserslist-db/1.0.11"; }; "node_modules/uri-js" = { - dev = true; key = "uri-js/4.4.1"; }; "node_modules/urijs" = { @@ -13121,6 +13476,21 @@ dev = true; key = "validate-npm-package-name/3.0.0"; }; + "node_modules/validate.io-array" = { + key = "validate.io-array/1.0.6"; + }; + "node_modules/validate.io-function" = { + key = "validate.io-function/1.0.2"; + }; + "node_modules/validate.io-integer" = { + key = "validate.io-integer/1.0.5"; + }; + "node_modules/validate.io-integer-array" = { + key = "validate.io-integer-array/1.0.0"; + }; + "node_modules/validate.io-number" = { + key = "validate.io-number/1.0.3"; + }; "node_modules/validator" = { dev = true; key = "validator/13.11.0"; @@ -15143,7 +15513,7 @@ depInfo = { "@babel/runtime" = { descriptor = "^7.5.5"; - pin = "7.22.10"; + pin = "7.22.11"; runtime = true; }; dom-helpers = { @@ -17311,6 +17681,88 @@ version = "3.0.0"; }; }; + "validate.io-array" = { + "1.0.6" = { + fetchInfo = { + narHash = "sha256-hTj+pWYWlZgbr1jdb6kfr7k2vnYZAyN8d1VwQdBITjg="; + type = "tarball"; + url = "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz"; + }; + ident = "validate.io-array"; + ltype = "file"; + treeInfo = { }; + version = "1.0.6"; + }; + }; + "validate.io-function" = { + "1.0.2" = { + fetchInfo = { + narHash = "sha256-MG3+IDs5WavAbTvbFkZczGZ/NfcAG3QP94E2r2bnchQ="; + type = "tarball"; + url = "https://registry.npmjs.org/validate.io-function/-/validate.io-function-1.0.2.tgz"; + }; + ident = "validate.io-function"; + ltype = "file"; + treeInfo = { }; + version = "1.0.2"; + }; + }; + "validate.io-integer" = { + "1.0.5" = { + depInfo = { + "validate.io-number" = { + descriptor = "^1.0.3"; + pin = "1.0.3"; + runtime = true; + }; + }; + fetchInfo = { + narHash = "sha256-yf1eZKbJtm4w+AwPpBtwiCOgIk08joKjkqAmXDjPj3k="; + type = "tarball"; + url = "https://registry.npmjs.org/validate.io-integer/-/validate.io-integer-1.0.5.tgz"; + }; + ident = "validate.io-integer"; + ltype = "file"; + version = "1.0.5"; + }; + }; + "validate.io-integer-array" = { + "1.0.0" = { + depInfo = { + "validate.io-array" = { + descriptor = "^1.0.3"; + pin = "1.0.6"; + runtime = true; + }; + "validate.io-integer" = { + descriptor = "^1.0.4"; + pin = "1.0.5"; + runtime = true; + }; + }; + fetchInfo = { + narHash = "sha256-2yabi9Qb/A7B2T29xrl2nxTfgV97SCQe9eOl8GE36gQ="; + type = "tarball"; + url = "https://registry.npmjs.org/validate.io-integer-array/-/validate.io-integer-array-1.0.0.tgz"; + }; + ident = "validate.io-integer-array"; + ltype = "file"; + version = "1.0.0"; + }; + }; + "validate.io-number" = { + "1.0.3" = { + fetchInfo = { + narHash = "sha256-tlQD45K0CSB8ih58xTdP8blRdYk1fzLWF3+2r8VEAXw="; + type = "tarball"; + url = "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz"; + }; + ident = "validate.io-number"; + ltype = "file"; + treeInfo = { }; + version = "1.0.3"; + }; + }; validator = { "13.11.0" = { fetchInfo = { diff --git a/pkgs/ui/package-lock.json b/pkgs/ui/package-lock.json index 7e1de42c3..5462475ba 100644 --- a/pkgs/ui/package-lock.json +++ b/pkgs/ui/package-lock.json @@ -12,6 +12,10 @@ "@emotion/styled": "^11.11.0", "@mui/icons-material": "^5.14.3", "@mui/material": "^5.14.3", + "@rjsf/core": "^5.12.1", + "@rjsf/mui": "^5.12.1", + "@rjsf/validator-ajv8": "^5.12.1", + "@types/json-schema": "^7.0.12", "autoprefixer": "10.4.14", "axios": "^1.4.0", "classnames": "^2.3.2", @@ -353,9 +357,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz", - "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz", + "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -955,11 +959,11 @@ } }, "node_modules/@mui/utils": { - "version": "5.14.5", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.5.tgz", - "integrity": "sha512-6Hzw63VR9C5xYv+CbjndoRLU6Gntal8rJ5W+GUzkyHrGWIyYPWZPa6AevnyGioySNETATe1H9oXS8f/7qgIHJA==", + "version": "5.14.7", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.7.tgz", + "integrity": "sha512-RtheP/aBoPogVdi8vj8Vo2IFnRa4mZVmnD0RGlVZ49yF60rZs+xP4/KbpIrTr83xVs34QmHQ2aQ+IX7I0a0dDw==", "dependencies": { - "@babel/runtime": "^7.22.6", + "@babel/runtime": "^7.22.10", "@types/prop-types": "^15.7.5", "@types/react-is": "^18.2.1", "prop-types": "^15.8.1", @@ -1277,6 +1281,98 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@rjsf/core": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@rjsf/core/-/core-5.12.1.tgz", + "integrity": "sha512-1YFhZ90/uHRx1akQmDdIjBxGMjs/5gtuTLUFwl6GbOwTm2fhZRh3qXRFyTXz81Oy6TGcbrxBJEYvFg2iHjYKCA==", + "dependencies": { + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "markdown-to-jsx": "^7.3.2", + "nanoid": "^3.3.6", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@rjsf/utils": "^5.8.x", + "react": "^16.14.0 || >=17" + } + }, + "node_modules/@rjsf/mui": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@rjsf/mui/-/mui-5.12.1.tgz", + "integrity": "sha512-d7cNFIdt6N24m5NPrNSqfCe2SUyUjX48Goo7z4J9vOHWxo5kdDfBEa3UwNA/DR9lo+9cYY7QTvKbgrTkxWU58A==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@emotion/react": "^11.7.0", + "@emotion/styled": "^11.6.0", + "@mui/icons-material": "^5.2.0", + "@mui/material": "^5.2.2", + "@rjsf/core": "^5.8.x", + "@rjsf/utils": "^5.8.x", + "react": ">=17" + } + }, + "node_modules/@rjsf/utils": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.12.1.tgz", + "integrity": "sha512-/k8+7WdLwhaYsOQvH5BQINipj2IJvjEW3QQv4jQQ7sXtkpdUjieZayRfaE8DHfRdm9HjgJURJFDy3EODkWPl6A==", + "peer": true, + "dependencies": { + "json-schema-merge-allof": "^0.8.1", + "jsonpointer": "^5.0.1", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.14.0 || >=17" + } + }, + "node_modules/@rjsf/validator-ajv8": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.12.1.tgz", + "integrity": "sha512-m4QO44yp60LTIfd4RPUu/h07B8U9umbD3I4Nh4iv9oyUudncaZFFXRopKcBm08v30VkN0tjMwuu0SxGDpzMtHA==", + "dependencies": { + "ajv": "^8.12.0", + "ajv-formats": "^2.1.1", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@rjsf/utils": "^5.8.x" + } + }, + "node_modules/@rjsf/validator-ajv8/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@rjsf/validator-ajv8/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/@rollup/plugin-commonjs": { "version": "22.0.2", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.2.tgz", @@ -1987,8 +2083,7 @@ "node_modules/@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", - "dev": true + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==" }, "node_modules/@types/json5": { "version": "0.0.29", @@ -2218,7 +2313,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, "dependencies": { "ajv": "^8.0.0" }, @@ -2235,7 +2329,6 @@ "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -2250,8 +2343,7 @@ "node_modules/ajv-formats/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/ansi-colors": { "version": "4.1.3", @@ -2869,6 +2961,29 @@ "integrity": "sha512-FemMreK9xNyL8gQevsdRMrvO4lFCkQP7qbuktn1q8ndcNk1+0mz7lgE7b/sNvbhVgY4w6tMN1FDp6aADjqw2rw==", "dev": true }, + "node_modules/compute-gcd": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/compute-gcd/-/compute-gcd-1.2.1.tgz", + "integrity": "sha512-TwMbxBNz0l71+8Sc4czv13h4kEqnchV9igQZBi6QUaz09dnz13juGnnaWWJTRsP3brxOoxeB4SA2WELLw1hCtg==", + "peer": true, + "dependencies": { + "validate.io-array": "^1.0.3", + "validate.io-function": "^1.0.2", + "validate.io-integer-array": "^1.0.0" + } + }, + "node_modules/compute-lcm": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/compute-lcm/-/compute-lcm-1.1.2.tgz", + "integrity": "sha512-OFNPdQAXnQhDSKioX8/XYT6sdUlXwpeMjfd6ApxMJfyZ4GxmLR1xvMERctlYhlHwIiz6CSpBc2+qYKjHGZw4TQ==", + "peer": true, + "dependencies": { + "compute-gcd": "^1.2.1", + "validate.io-array": "^1.0.3", + "validate.io-function": "^1.0.2", + "validate.io-integer-array": "^1.0.0" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4226,8 +4341,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-equals": { "version": "5.0.1", @@ -5353,6 +5467,29 @@ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, + "node_modules/json-schema-compare": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/json-schema-compare/-/json-schema-compare-0.2.2.tgz", + "integrity": "sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==", + "peer": true, + "dependencies": { + "lodash": "^4.17.4" + } + }, + "node_modules/json-schema-merge-allof": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/json-schema-merge-allof/-/json-schema-merge-allof-0.8.1.tgz", + "integrity": "sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w==", + "peer": true, + "dependencies": { + "compute-lcm": "^1.1.2", + "json-schema-compare": "^0.2.2", + "lodash": "^4.17.20" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/json-schema-ref-parser": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-5.1.3.tgz", @@ -5452,7 +5589,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -5551,6 +5687,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -5637,6 +5778,17 @@ "sourcemap-codec": "^1.4.8" } }, + "node_modules/markdown-to-jsx": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.3.2.tgz", + "integrity": "sha512-B+28F5ucp83aQm+OxNrPkS8z0tMKaeHiy0lHJs3LqCyDQFtWuenaIrkaVTgAm1pf1AU85LXltva86hlaT17i8Q==", + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, "node_modules/matcher": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/matcher/-/matcher-1.1.1.tgz", @@ -6688,7 +6840,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, "engines": { "node": ">=6" } @@ -6962,7 +7113,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -7875,7 +8025,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -7917,6 +8066,43 @@ "builtins": "^1.0.3" } }, + "node_modules/validate.io-array": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz", + "integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==", + "peer": true + }, + "node_modules/validate.io-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/validate.io-function/-/validate.io-function-1.0.2.tgz", + "integrity": "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==", + "peer": true + }, + "node_modules/validate.io-integer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/validate.io-integer/-/validate.io-integer-1.0.5.tgz", + "integrity": "sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ==", + "peer": true, + "dependencies": { + "validate.io-number": "^1.0.3" + } + }, + "node_modules/validate.io-integer-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/validate.io-integer-array/-/validate.io-integer-array-1.0.0.tgz", + "integrity": "sha512-mTrMk/1ytQHtCY0oNO3dztafHYyGU88KL+jRxWuzfOmQb+4qqnWmI+gykvGp8usKZOM0H7keJHEbRaFiYA0VrA==", + "peer": true, + "dependencies": { + "validate.io-array": "^1.0.3", + "validate.io-integer": "^1.0.4" + } + }, + "node_modules/validate.io-number": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz", + "integrity": "sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg==", + "peer": true + }, "node_modules/validator": { "version": "13.11.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", diff --git a/pkgs/ui/package.json b/pkgs/ui/package.json index fdd813308..6ae1b83ee 100644 --- a/pkgs/ui/package.json +++ b/pkgs/ui/package.json @@ -16,6 +16,10 @@ "@emotion/styled": "^11.11.0", "@mui/icons-material": "^5.14.3", "@mui/material": "^5.14.3", + "@rjsf/core": "^5.12.1", + "@rjsf/mui": "^5.12.1", + "@rjsf/validator-ajv8": "^5.12.1", + "@types/json-schema": "^7.0.12", "autoprefixer": "10.4.14", "axios": "^1.4.0", "classnames": "^2.3.2", diff --git a/pkgs/ui/src/app/machines/add/page.tsx b/pkgs/ui/src/app/machines/add/page.tsx index 5f580c236..6413c3c88 100644 --- a/pkgs/ui/src/app/machines/add/page.tsx +++ b/pkgs/ui/src/app/machines/add/page.tsx @@ -1,355 +1,64 @@ "use client"; -import React, { ReactNode, useEffect, useMemo, useState } from "react"; -import { - Box, - Button, - MenuItem, - Select, - Step, - StepLabel, - Stepper, - Typography, -} from "@mui/material"; -import { - Control, - Controller, - Form, - useForm, - UseFormWatch, -} from "react-hook-form"; -import { DashboardCard } from "@/components/card"; -import Info from "@mui/icons-material/Info"; -import { Check, Usb } from "@mui/icons-material"; -import toast from "react-hot-toast"; -import { buffer } from "stream/consumers"; +import { useForm } from "react-hook-form"; -type StepId = "select" | "create" | "install"; +import { JSONSchema7 } from "json-schema"; +import { useMemo, useState } from "react"; +import { schema } from "./schema"; +import Form from "@rjsf/mui"; +import validator from "@rjsf/validator-ajv8"; +import { Button } from "@mui/material"; -type Step = { - id: StepId; - label: string; - children?: ReactNode; -}; - -const steps: Step[] = [ - { - id: "select", - label: "Image", - }, - { - id: "create", - label: "Customize new template", - }, - { - id: "install", - label: "Install", - }, -]; - -const serverImagesData = [ - { - id: "1", - name: "Cassies Gaming PC", - }, - { - id: "2", - name: "Ivern office", - }, - { - id: "3", - name: "Dad's working pc", - }, - { - id: "4", - name: "Sisters's pony preset", - }, -]; - -interface StepContentProps { - id: StepId; - control: Control; - watch: UseFormWatch; +interface CreateMachineFormProps { + schema: JSONSchema7; + initialValues: any; } -const StepContent = (props: StepContentProps) => { - const { id, control, watch } = props; - const [hasWebUsb, setHasWebUsb] = useState(false); - useEffect(() => { - setHasWebUsb(Boolean(navigator?.usb)); - }, []); - - const content: Record = { - select: ( -
-
- - Select an image - - ( - - )} - /> -
- }> -
- - {watch("image") === "new" - ? `You selected the option to create a new system image. Configure your predefined options, such as programs, clans, etc. in - the following steps.` - : `You selected the option to reuse an existing system image. Please select one - from the list below`} - - {watch("image") === "existing" && ( - ( - - )} - /> - )} -
-
-
-
-
- ), - create: ( -
-
- Formular generated from nix flake jsonschema -
-
- ), - install: ( -
- -
- ), - }; - return ( -
-
- - {watch("image") == "new" - ? "Create system template" - : "Choose existing"} - -
{content[id]}
-
-
- ); -}; - -type FormValues = { - image: ImageOption; - source: string; -}; -type ImageOption = "new" | "existing"; - -type ImageOptions = { - id: ImageOption; - label: string; -}[]; -const imageOptions: ImageOptions = [ - { - id: "new", - label: "New image", +const defaultValues = Object.entries(schema.properties || {}).reduce( + (acc, [key, value]) => { + /*@ts-ignore*/ + const init: any = value?.default; + if (init) { + return { + ...acc, + [key]: init, + }; + } + return acc; }, - { - id: "existing", - label: "Previously created image", - }, -]; + {}, +); -const defaultValues: FormValues = { - image: "new", - source: serverImagesData[0].id, -}; +function CreateMachineForm(props: CreateMachineFormProps) { + const { schema, initialValues } = props; -export default function AddNode() { - const { handleSubmit, control, watch, reset, formState } = - useForm({ - defaultValues, - }); - - const [activeStep, setActiveStep] = useState(0); - const [usb, setUsb] = useState(undefined); - useEffect(() => { - setUsb(navigator?.usb); - }, []); - - const handleNext = () => { - if (activeStep < visibleSteps.length - 1) { - setActiveStep((prevActiveStep) => prevActiveStep + 1); - } - }; - - const handleBack = () => { - if (activeStep > 0) { - setActiveStep((prevActiveStep) => prevActiveStep - 1); - } - }; - - const handleReset = () => { - setActiveStep(0); - reset(); - }; - - async function onSubmit(data: any) { - console.log({ data }, "To be submitted"); - if (usb) { - let device; - try { - device = await usb.requestDevice({ - filters: [{}], - }); - toast.success(`Connected to '${device.productName}'`); - } catch (error) { - console.log({ error }); - toast.error("Couldn't connect to usb device"); - } - if (device) { - // await device.open(); - // await device.selectConfiguration(1); - // await device.claimInterface(0); - // const data = new Uint8Array([1, 2, 3]); - // device.transferOut(2, data); - } - } else { - //Offer the image as download - - const blob = new Blob(["data"]); - let url = window.URL.createObjectURL(blob); - let a = document.createElement("a"); - a.href = url; - a.download = "image.iso"; - a.click(); - } - return true; - } - - const imageValue = watch("image"); - const visibleSteps = useMemo( - () => - steps.filter((s) => { - if (imageValue == "existing" && s.id == "create") { - return false; - } - return true; - }), - [imageValue], - ); - // console.log({}) - const currentStep = visibleSteps.at(activeStep); return ( -
- - - {visibleSteps.map(({ label }, index) => { - const stepProps: { completed?: boolean } = {}; - const labelProps: { - optional?: React.ReactNode; - } = {}; - return ( - - {label} - - ); - })} - - {activeStep === visibleSteps.length ? ( - <> - - Image succesfully downloaded - - - - - - - ) : ( - <> - {currentStep && ( - - )} - - - - - {activeStep !== visibleSteps.length - 1 && ( - - )} - {activeStep === visibleSteps.length - 1 && ( - - )} - - - )} - - +
( + + ), + }, + }} + /> ); } + +export default function CreateMachine() { + return ; +} diff --git a/pkgs/ui/src/app/machines/add/schema.ts b/pkgs/ui/src/app/machines/add/schema.ts new file mode 100644 index 000000000..5c311daba --- /dev/null +++ b/pkgs/ui/src/app/machines/add/schema.ts @@ -0,0 +1,111 @@ +import { RJSFSchema } from "@rjsf/utils"; +export const schema: RJSFSchema = { + type: "object", + properties: { + name: { + type: "string", + default: "John-nixi", + description: "The name of the machine", + }, + age: { + type: "integer", + default: 42, + description: "The age of the user", + maximum: 40, + }, + role: { + enum: ["New York", "Amsterdam", "Hong Kong"], + description: "Role of the user", + }, + kernelModules: { + type: "array", + items: { + type: "string", + }, + default: ["nvme", "xhci_pci", "ahci"], + description: "A list of enabled kernel modules", + }, + userIds: { + type: "array", + items: { + type: "object", + properties: { + user: { + type: "string", + }, + id: { + type: "integer", + }, + }, + }, + default: [ + { + user: "John", + id: 12, + }, + ], + description: "Some attributes", + }, + xdg: { + type: "object", + properties: { + portal: { + type: "object", + properties: { + xdgOpenUsePortal: { + type: "boolean", + default: false, + }, + enable: { + type: "boolean", + default: false, + }, + lxqt: { + type: "object", + properties: { + enable: { + type: "boolean", + default: false, + }, + styles: { + type: "array", + items: { + type: "string", + }, + }, + }, + }, + extraPortals: { + type: "array", + items: { + type: "string", + }, + }, + wlr: { + type: "object", + properties: { + enable: { + type: "boolean", + default: false, + }, + settings: { + type: "object", + default: { + screencast: { + output_name: "HDMI-A-1", + max_fps: 30, + exec_before: "disable_notifications.sh", + exec_after: "enable_notifications.sh", + chooser_type: "simple", + chooser_cmd: "${pkgs.slurp}/bin/slurp -f %o -or", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +}; From 03808d9fbcb8500d4e672e0d149be5fa373b4d32 Mon Sep 17 00:00:00 2001 From: DavHau Date: Sat, 2 Sep 2023 18:24:34 +0200 Subject: [PATCH 05/40] buildClan: fix missing argument clan --- lib/default.nix | 4 ++-- lib/flake-module.nix | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/default.nix b/lib/default.nix index 89ac0cc3f..dcd09edb7 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -1,4 +1,4 @@ -{ lib, clan, nixpkgs, ... }: +{ lib, self, nixpkgs, ... }: { findNixFiles = folder: lib.mapAttrs' @@ -14,5 +14,5 @@ jsonschema = import ./jsonschema { inherit lib; }; - buildClan = import ./build-clan { inherit lib clan nixpkgs; }; + buildClan = import ./build-clan { inherit lib self nixpkgs; }; } diff --git a/lib/flake-module.nix b/lib/flake-module.nix index 13855fbc6..1062e92c1 100644 --- a/lib/flake-module.nix +++ b/lib/flake-module.nix @@ -1,5 +1,6 @@ { lib , inputs +, self , ... }: { imports = [ @@ -7,6 +8,7 @@ ]; flake.lib = import ./default.nix { inherit lib; - inherit (inputs) nixpkgs clan; + inherit self; + inherit (inputs) nixpkgs; }; } From ac7e3f6407e9cedc69d0b3114d1b76e7730b2667 Mon Sep 17 00:00:00 2001 From: DavHau Date: Sat, 2 Sep 2023 18:25:27 +0200 Subject: [PATCH 06/40] sops: fix module to accept missing ./sops dir --- nixosModules/clanCore/secrets/sops.nix | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nixosModules/clanCore/secrets/sops.nix b/nixosModules/clanCore/secrets/sops.nix index 7df0b31d2..2c1042f98 100644 --- a/nixosModules/clanCore/secrets/sops.nix +++ b/nixosModules/clanCore/secrets/sops.nix @@ -45,13 +45,17 @@ ''; sops.secrets = let + secretsDir = config.clanCore.clanDir + "/sops/secrets"; encryptedForThisMachine = name: type: let - symlink = config.clanCore.clanDir + "/sops/secrets/${name}/machines/${config.clanCore.machineName}"; + symlink = secretsDir + "/${name}/machines/${config.clanCore.machineName}"; in # WTF, nix bug, my symlink is in the nixos module detected as a directory also it works in the repl type == "directory" && (builtins.readFileType symlink == "directory" || builtins.readFileType symlink == "symlink"); - secrets = lib.filterAttrs encryptedForThisMachine (builtins.readDir (config.clanCore.clanDir + "/sops/secrets")); + secrets = + if !builtins.pathExists secretsDir + then { } + else lib.filterAttrs encryptedForThisMachine (builtins.readDir secretsDir); in builtins.mapAttrs (name: _: { From 75cfd8494982675e51a596e72cdc578f63eea21e Mon Sep 17 00:00:00 2001 From: DavHau Date: Sat, 2 Sep 2023 18:26:45 +0200 Subject: [PATCH 07/40] buildClan: set clanCore.directory and hostPlatform --- lib/build-clan/default.nix | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/build-clan/default.nix b/lib/build-clan/default.nix index 4da48dafd..6539a366c 100644 --- a/lib/build-clan/default.nix +++ b/lib/build-clan/default.nix @@ -1,4 +1,4 @@ -{ nixpkgs, clan, lib }: +{ nixpkgs, self, lib }: { directory # The directory containing the machines subdirectory , 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} = { ... } @@ -18,9 +18,12 @@ let (name: _: nixpkgs.lib.nixosSystem { modules = [ - clan.nixosModules.clanCore + self.nixosModules.clanCore (machineSettings name) (machines.${name} or { }) + { clanCore.clanDir = directory; } + # TODO: remove this once we have a hardware-config mechanism + { nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; } ]; specialArgs = specialArgs; }) From a0d1b09b1da1f5a2165b9e4ee9c5c6e05925c615 Mon Sep 17 00:00:00 2001 From: DavHau Date: Sat, 2 Sep 2023 18:28:31 +0200 Subject: [PATCH 08/40] clanCore module: add clanSchema top level option --- checks/impure/flake-module.nix | 7 +++++++ nixosModules/clanCore/flake-module.nix | 19 ++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/checks/impure/flake-module.nix b/checks/impure/flake-module.nix index 9add525bf..d8018061b 100644 --- a/checks/impure/flake-module.nix +++ b/checks/impure/flake-module.nix @@ -5,9 +5,13 @@ check-clan-template = pkgs.writeShellScriptBin "check-clan-template" '' #!${pkgs.bash}/bin/bash set -euo pipefail + export TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d) trap "${pkgs.coreutils}/bin/chmod -R +w '$TMPDIR'; ${pkgs.coreutils}/bin/rm -rf '$TMPDIR'" EXIT + export PATH="${lib.makeBinPath [ + pkgs.coreutils + pkgs.curl pkgs.gitMinimal pkgs.gnugrep pkgs.jq @@ -35,6 +39,9 @@ echo check machine1 appears in nixosConfigurations nix flake show --json | jq '.nixosConfigurations' | grep -q machine1 + + echo check machine1 jsonschema can be evaluated + nix eval .#nixosConfigurations.machine1.config.clanSchema ''; }; in diff --git a/nixosModules/clanCore/flake-module.nix b/nixosModules/clanCore/flake-module.nix index da8fa0e63..437f54874 100644 --- a/nixosModules/clanCore/flake-module.nix +++ b/nixosModules/clanCore/flake-module.nix @@ -1,8 +1,18 @@ { self, inputs, lib, ... }: { - flake.nixosModules.clanCore = { pkgs, ... }: { + flake.nixosModules.clanCore = { pkgs, options, ... }: { + imports = [ + ./secrets + ./zerotier.nix + inputs.sops-nix.nixosModules.sops + ]; + options.clanSchema = lib.mkOption { + type = lib.types.attrs; + description = "The json schema for the .clan options namespace"; + default = self.lib.jsonschema.parseOptions options.clan; + }; options.clanCore = { clanDir = lib.mkOption { - type = lib.types.str; + type = lib.types.either lib.types.path lib.types.str; description = '' the location of the flake repo, used to calculate the location of facts and secrets ''; @@ -23,10 +33,5 @@ utility outputs for clan management of this machine ''; }; - imports = [ - ./secrets - ./zerotier.nix - inputs.sops-nix.nixosModules.sops - ]; }; } From 2a11b1c5d1dfa90c505b2e470bd363a9473b3b85 Mon Sep 17 00:00:00 2001 From: DavHau Date: Sat, 2 Sep 2023 18:29:12 +0200 Subject: [PATCH 09/40] machines api: always create empty settings.json --- pkgs/clan-cli/clan_cli/machines/create.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkgs/clan-cli/clan_cli/machines/create.py b/pkgs/clan-cli/clan_cli/machines/create.py index adc1ee9fa..54b70705a 100644 --- a/pkgs/clan-cli/clan_cli/machines/create.py +++ b/pkgs/clan-cli/clan_cli/machines/create.py @@ -6,6 +6,9 @@ from .folders import machine_folder def create_machine(name: str) -> None: folder = machine_folder(name) folder.mkdir(parents=True, exist_ok=True) + # create empty settings.json file inside the folder + with open(folder / "settings.json", "w") as f: + f.write("{}") def create_command(args: argparse.Namespace) -> None: From a56073b0519264aae0b9af01f2a1540b140fb363 Mon Sep 17 00:00:00 2001 From: DavHau Date: Sat, 2 Sep 2023 18:37:40 +0200 Subject: [PATCH 10/40] machines delete: fix - delete all existing files --- pkgs/clan-cli/clan_cli/machines/delete.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkgs/clan-cli/clan_cli/machines/delete.py b/pkgs/clan-cli/clan_cli/machines/delete.py index 20dc3c087..6fd5cf6ec 100644 --- a/pkgs/clan-cli/clan_cli/machines/delete.py +++ b/pkgs/clan-cli/clan_cli/machines/delete.py @@ -1,4 +1,5 @@ import argparse +import shutil from ..errors import ClanError from .folders import machine_folder @@ -7,7 +8,7 @@ from .folders import machine_folder def delete_command(args: argparse.Namespace) -> None: folder = machine_folder(args.host) if folder.exists(): - folder.rmdir() + shutil.rmtree(folder) else: raise ClanError(f"Machine {args.host} does not exist") From c9bfd0a5b54f5136de9683053bb1d637b63cba4e Mon Sep 17 00:00:00 2001 From: DavHau Date: Sat, 2 Sep 2023 19:15:29 +0200 Subject: [PATCH 11/40] clanCore: add example options --- lib/jsonschema/example-interface.nix | 5 +++++ nixosModules/clanCore/bloatware/default.nix | 9 +++++++++ nixosModules/clanCore/flake-module.nix | 2 ++ 3 files changed, 16 insertions(+) create mode 100644 nixosModules/clanCore/bloatware/default.nix diff --git a/lib/jsonschema/example-interface.nix b/lib/jsonschema/example-interface.nix index 1370d3cd9..4a8cfe702 100644 --- a/lib/jsonschema/example-interface.nix +++ b/lib/jsonschema/example-interface.nix @@ -3,16 +3,19 @@ */ { lib, ... }: { options = { + # str name = lib.mkOption { type = lib.types.str; default = "John Doe"; description = "The name of the user"; }; + # int age = lib.mkOption { type = lib.types.int; default = 42; description = "The age of the user"; }; + # bool isAdmin = lib.mkOption { type = lib.types.bool; default = false; @@ -28,6 +31,7 @@ }; }; }; + # attrs of int userIds = lib.mkOption { type = lib.types.attrsOf lib.types.int; description = "Some attributes"; @@ -37,6 +41,7 @@ albrecht = 3; }; }; + # list of str kernelModules = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ "nvme" "xhci_pci" "ahci" ]; diff --git a/nixosModules/clanCore/bloatware/default.nix b/nixosModules/clanCore/bloatware/default.nix new file mode 100644 index 000000000..0c85221f6 --- /dev/null +++ b/nixosModules/clanCore/bloatware/default.nix @@ -0,0 +1,9 @@ +{ lib, ... }: { + options.clan.bloatware = lib.mkOption { + type = lib.types.submodule { + imports = [ + ../../../lib/jsonschema/example-interface.nix + ]; + }; + }; +} diff --git a/nixosModules/clanCore/flake-module.nix b/nixosModules/clanCore/flake-module.nix index 437f54874..da174fff9 100644 --- a/nixosModules/clanCore/flake-module.nix +++ b/nixosModules/clanCore/flake-module.nix @@ -4,6 +4,8 @@ ./secrets ./zerotier.nix inputs.sops-nix.nixosModules.sops + # just some example options. Can be removed later + ./bloatware ]; options.clanSchema = lib.mkOption { type = lib.types.attrs; From e079627f0fe933fc0837a35dfc0e4e4a76e84520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 08:37:38 +0200 Subject: [PATCH 12/40] secrets/sops-import: check for key.json to exist --- pkgs/clan-cli/clan_cli/secrets/import_sops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/clan-cli/clan_cli/secrets/import_sops.py b/pkgs/clan-cli/clan_cli/secrets/import_sops.py index a83556063..bd1d56b9c 100644 --- a/pkgs/clan-cli/clan_cli/secrets/import_sops.py +++ b/pkgs/clan-cli/clan_cli/secrets/import_sops.py @@ -36,7 +36,7 @@ def import_sops(args: argparse.Namespace) -> None: file=sys.stderr, ) continue - if (sops_secrets_folder() / k).exists(): + if (sops_secrets_folder() / k / "key.json").exists(): print( f"WARNING: {k} already exists, skipping", file=sys.stderr, From 6c7c9b95402a6b60dce419def7acd1e31707dab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 08:37:38 +0200 Subject: [PATCH 13/40] secrets/sops-import: check correct secret for conflicts --- pkgs/clan-cli/clan_cli/secrets/import_sops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/clan-cli/clan_cli/secrets/import_sops.py b/pkgs/clan-cli/clan_cli/secrets/import_sops.py index bd1d56b9c..bfac4565e 100644 --- a/pkgs/clan-cli/clan_cli/secrets/import_sops.py +++ b/pkgs/clan-cli/clan_cli/secrets/import_sops.py @@ -36,7 +36,7 @@ def import_sops(args: argparse.Namespace) -> None: file=sys.stderr, ) continue - if (sops_secrets_folder() / k / "key.json").exists(): + if (sops_secrets_folder() / k / "secret").exists(): print( f"WARNING: {k} already exists, skipping", file=sys.stderr, From 4e19b7c085cf852dad6695b7407976b8ff6c6604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 09:40:31 +0200 Subject: [PATCH 14/40] secrets/sops: set dummy default sops file --- nixosModules/clanCore/secrets/sops.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nixosModules/clanCore/secrets/sops.nix b/nixosModules/clanCore/secrets/sops.nix index 2c1042f98..011754f3f 100644 --- a/nixosModules/clanCore/secrets/sops.nix +++ b/nixosModules/clanCore/secrets/sops.nix @@ -63,5 +63,7 @@ format = "binary"; }) secrets; + # 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" ""))); }; } From 949b72bd0b4c1fe6341345fd4984e72471ffd888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 11:02:48 +0200 Subject: [PATCH 15/40] actual implement secret rename function --- pkgs/clan-cli/clan_cli/secrets/secrets.py | 12 ++++++++---- pkgs/clan-cli/tests/test_secrets_cli.py | 6 ++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/pkgs/clan-cli/clan_cli/secrets/secrets.py b/pkgs/clan-cli/clan_cli/secrets/secrets.py index 89925755c..27378e9fa 100644 --- a/pkgs/clan-cli/clan_cli/secrets/secrets.py +++ b/pkgs/clan-cli/clan_cli/secrets/secrets.py @@ -212,7 +212,13 @@ def set_command(args: argparse.Namespace) -> None: def rename_command(args: argparse.Namespace) -> None: - pass + old_path = sops_secrets_folder() / args.secret + new_path = sops_secrets_folder() / args.new_name + if not old_path.exists(): + raise ClanError(f"Secret '{args.secret}' does not exist") + if new_path.exists(): + raise ClanError(f"Secret '{args.new_name}' already exists") + os.rename(old_path, new_path) def register_secrets_parser(subparser: argparse._SubParsersAction) -> None: @@ -250,9 +256,7 @@ def register_secrets_parser(subparser: argparse._SubParsersAction) -> None: parser_rename = subparser.add_parser("rename", help="rename a secret") add_secret_argument(parser_rename) - parser_rename.add_argument( - "new_name", help="the new name of the secret", type=secret_name_type - ) + parser_rename.add_argument("new_name", type=str, help="the new name of the secret") parser_rename.set_defaults(func=rename_command) parser_remove = subparser.add_parser("remove", help="remove a secret") diff --git a/pkgs/clan-cli/tests/test_secrets_cli.py b/pkgs/clan-cli/tests/test_secrets_cli.py index ccdffd799..80bf1e752 100644 --- a/pkgs/clan-cli/tests/test_secrets_cli.py +++ b/pkgs/clan-cli/tests/test_secrets_cli.py @@ -129,9 +129,9 @@ def test_secrets( with pytest.raises(ClanError): # does not exist yet cli.run(["secrets", "get", "nonexisting"]) - cli.run(["secrets", "set", "key"]) + cli.run(["secrets", "set", "initialkey"]) capsys.readouterr() - cli.run(["secrets", "get", "key"]) + cli.run(["secrets", "get", "initialkey"]) assert capsys.readouterr().out == "foo" capsys.readouterr() cli.run(["secrets", "users", "list"]) @@ -139,6 +139,8 @@ def test_secrets( assert len(users) == 1, f"users: {users}" owner = users[0] + cli.run(["secrets", "rename", "initialkey", "key"]) + capsys.readouterr() # empty the buffer cli.run(["secrets", "list"]) assert capsys.readouterr().out == "key\n" From 5dd318e637598a039c66d3932e3b28c3d4976396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 13:09:35 +0200 Subject: [PATCH 16/40] buildClan: machineName --- lib/build-clan/default.nix | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/build-clan/default.nix b/lib/build-clan/default.nix index 6539a366c..6186783d2 100644 --- a/lib/build-clan/default.nix +++ b/lib/build-clan/default.nix @@ -21,11 +21,14 @@ let self.nixosModules.clanCore (machineSettings name) (machines.${name} or { }) - { clanCore.clanDir = directory; } - # TODO: remove this once we have a hardware-config mechanism - { nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; } + { + clanCore.machineName = name; + clanCore.clanDir = directory; + # TODO: remove this once we have a hardware-config mechanism + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + } ]; - specialArgs = specialArgs; + inherit specialArgs; }) (machinesDirs // machines); in From 0a3982d0ac37b87a77ff19832344c4d78e1bf4cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 13:24:36 +0200 Subject: [PATCH 17/40] fix machines folder not beeing present yet --- lib/build-clan/default.nix | 8 ++++---- nixosModules/clanCore/secrets/sops.nix | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/build-clan/default.nix b/lib/build-clan/default.nix index 6186783d2..265a1c584 100644 --- a/lib/build-clan/default.nix +++ b/lib/build-clan/default.nix @@ -5,13 +5,13 @@ }: let machinesDirs = - if builtins.pathExists (directory + /machines) - then builtins.readDir (directory + /machines) + if builtins.pathExists "${directory}/machines" + then builtins.readDir "${directory}/machines" else { }; machineSettings = machineName: - if builtins.pathExists (directory + /machines/${machineName}/settings.json) - then builtins.fromJSON (builtins.readFile (directory + /machines/${machineName}/settings.json)) + if builtins.pathExists "${directory}/machines/${machineName}/settings.json" + then builtins.fromJSON (builtins.readFile "${directory}/machines/${machineName}/settings.json") else { }; nixosConfigurations = lib.mapAttrs diff --git a/nixosModules/clanCore/secrets/sops.nix b/nixosModules/clanCore/secrets/sops.nix index 011754f3f..05973cc4f 100644 --- a/nixosModules/clanCore/secrets/sops.nix +++ b/nixosModules/clanCore/secrets/sops.nix @@ -48,18 +48,18 @@ secretsDir = config.clanCore.clanDir + "/sops/secrets"; encryptedForThisMachine = name: type: let - symlink = secretsDir + "/${name}/machines/${config.clanCore.machineName}"; + symlink = "${secretsDir}/${name}/machines/${config.clanCore.machineName}"; in # WTF, nix bug, my symlink is in the nixos module detected as a directory also it works in the repl - type == "directory" && (builtins.readFileType symlink == "directory" || builtins.readFileType symlink == "symlink"); + type == "directory" && builtins.pathExists symlink && (builtins.readFileType symlink == "directory" || builtins.readFileType symlink == "symlink"); secrets = - if !builtins.pathExists secretsDir + if !(builtins.pathExists secretsDir) then { } else lib.filterAttrs encryptedForThisMachine (builtins.readDir secretsDir); in builtins.mapAttrs (name: _: { - sopsFile = config.clanCore.clanDir + "/sops/secrets/${name}/secret"; + sopsFile = "${config.clanCore.clanDir}/sops/secrets/${name}/secret"; format = "binary"; }) secrets; From 14335ae5765a8e58f3c04ab8231e5c79c71745df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 13:24:36 +0200 Subject: [PATCH 18/40] fix machines folder not beeing present yet --- lib/build-clan/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/build-clan/default.nix b/lib/build-clan/default.nix index 265a1c584..658c60a1f 100644 --- a/lib/build-clan/default.nix +++ b/lib/build-clan/default.nix @@ -23,7 +23,7 @@ let (machines.${name} or { }) { clanCore.machineName = name; - clanCore.clanDir = directory; + clanCore.clanDir = builtins.toString directory; # TODO: remove this once we have a hardware-config mechanism nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; } From f1e80704127dba02034e61415f74fae620afb653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 13:53:20 +0200 Subject: [PATCH 19/40] Revert "fix machines folder not beeing present yet" This reverts commit 14335ae5765a8e58f3c04ab8231e5c79c71745df. --- lib/build-clan/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/build-clan/default.nix b/lib/build-clan/default.nix index 658c60a1f..265a1c584 100644 --- a/lib/build-clan/default.nix +++ b/lib/build-clan/default.nix @@ -23,7 +23,7 @@ let (machines.${name} or { }) { clanCore.machineName = name; - clanCore.clanDir = builtins.toString directory; + clanCore.clanDir = directory; # TODO: remove this once we have a hardware-config mechanism nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; } From cfec69fec493e447b8651b96f1aa0309dfed50c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 14:04:18 +0200 Subject: [PATCH 20/40] different fix for missing secrets --- lib/build-clan/default.nix | 4 ++-- nixosModules/clanCore/secrets/sops.nix | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/build-clan/default.nix b/lib/build-clan/default.nix index 265a1c584..405934a2c 100644 --- a/lib/build-clan/default.nix +++ b/lib/build-clan/default.nix @@ -6,12 +6,12 @@ let machinesDirs = if builtins.pathExists "${directory}/machines" - then builtins.readDir "${directory}/machines" + then builtins.readDir directory + /machines else { }; machineSettings = machineName: if builtins.pathExists "${directory}/machines/${machineName}/settings.json" - then builtins.fromJSON (builtins.readFile "${directory}/machines/${machineName}/settings.json") + then builtins.fromJSON (builtins.readFile directory + /machines/${machineName}/settings.json) else { }; nixosConfigurations = lib.mapAttrs diff --git a/nixosModules/clanCore/secrets/sops.nix b/nixosModules/clanCore/secrets/sops.nix index 05973cc4f..58a6eef6d 100644 --- a/nixosModules/clanCore/secrets/sops.nix +++ b/nixosModules/clanCore/secrets/sops.nix @@ -59,7 +59,7 @@ in builtins.mapAttrs (name: _: { - sopsFile = "${config.clanCore.clanDir}/sops/secrets/${name}/secret"; + sopsFile = config.clanCore.clanDir + "/sops/secrets/${name}/secret"; format = "binary"; }) secrets; From 598dd776ec70e309936efc38765e2895997fc088 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Sun, 3 Sep 2023 14:15:10 +0200 Subject: [PATCH 21/40] add: dynamic schema form with pure/impure seperation --- pkgs/ui/src/app/machines/add/page.tsx | 61 +------ .../createMachineForm/customConfig.tsx | 165 ++++++++++++++++++ .../components/createMachineForm/index.tsx | 160 +++++++++++++++++ .../createMachineForm/interfaces.ts | 24 +++ .../createMachineForm/saveConfig.tsx | 0 .../createMachineForm/selectModules.tsx | 0 .../createMachineForm/selectTemplate.tsx | 0 pkgs/ui/src/data/_schema.ts | 88 ++++++++++ .../add/schema.ts => data/_schema2.ts} | 0 9 files changed, 439 insertions(+), 59 deletions(-) create mode 100644 pkgs/ui/src/components/createMachineForm/customConfig.tsx create mode 100644 pkgs/ui/src/components/createMachineForm/index.tsx create mode 100644 pkgs/ui/src/components/createMachineForm/interfaces.ts create mode 100644 pkgs/ui/src/components/createMachineForm/saveConfig.tsx create mode 100644 pkgs/ui/src/components/createMachineForm/selectModules.tsx create mode 100644 pkgs/ui/src/components/createMachineForm/selectTemplate.tsx create mode 100644 pkgs/ui/src/data/_schema.ts rename pkgs/ui/src/{app/machines/add/schema.ts => data/_schema2.ts} (100%) diff --git a/pkgs/ui/src/app/machines/add/page.tsx b/pkgs/ui/src/app/machines/add/page.tsx index 6413c3c88..3581d0006 100644 --- a/pkgs/ui/src/app/machines/add/page.tsx +++ b/pkgs/ui/src/app/machines/add/page.tsx @@ -1,64 +1,7 @@ "use client"; -import { useForm } from "react-hook-form"; - -import { JSONSchema7 } from "json-schema"; -import { useMemo, useState } from "react"; -import { schema } from "./schema"; -import Form from "@rjsf/mui"; -import validator from "@rjsf/validator-ajv8"; -import { Button } from "@mui/material"; - -interface CreateMachineFormProps { - schema: JSONSchema7; - initialValues: any; -} - -const defaultValues = Object.entries(schema.properties || {}).reduce( - (acc, [key, value]) => { - /*@ts-ignore*/ - const init: any = value?.default; - if (init) { - return { - ...acc, - [key]: init, - }; - } - return acc; - }, - {}, -); - -function CreateMachineForm(props: CreateMachineFormProps) { - const { schema, initialValues } = props; - - return ( - ( - - ), - }, - }} - /> - ); -} +import { CreateMachineForm } from "@/components/createMachineForm"; export default function CreateMachine() { - return ; + return ; } diff --git a/pkgs/ui/src/components/createMachineForm/customConfig.tsx b/pkgs/ui/src/components/createMachineForm/customConfig.tsx new file mode 100644 index 000000000..7b9e76f23 --- /dev/null +++ b/pkgs/ui/src/components/createMachineForm/customConfig.tsx @@ -0,0 +1,165 @@ +"use client"; +import { useGetMachineSchema } from "@/api/default/default"; +import { Check, Error } from "@mui/icons-material"; +import { + Box, + Button, + LinearProgress, + List, + ListItem, + ListItemIcon, + ListItemText, + Paper, + Typography, +} from "@mui/material"; +import { IChangeEvent, FormProps } from "@rjsf/core"; +import { Form } from "@rjsf/mui"; +import validator from "@rjsf/validator-ajv8"; +import toast from "react-hot-toast"; +import { JSONSchema7 } from "json-schema"; +import { useMemo, useRef } from "react"; +import { FormStepContentProps } from "./interfaces"; +import { + ErrorListProps, + FormContextType, + RJSFSchema, + StrictRJSFSchema, + TranslatableString, +} from "@rjsf/utils"; + +interface PureCustomConfigProps extends FormStepContentProps { + schema: JSONSchema7; + initialValues: any; +} +export function CustomConfig(props: FormStepContentProps) { + const { formHooks, handleNext } = props; + const { data, isLoading, error } = useGetMachineSchema("mama"); + const schema = useMemo(() => { + if (!isLoading && !error?.message && data?.data) { + return data?.data.schema; + } + return {}; + }, [data, isLoading, error]); + + const initialValues = useMemo( + () => + Object.entries(schema?.properties || {}).reduce((acc, [key, value]) => { + /*@ts-ignore*/ + const init: any = value?.default; + if (init) { + return { + ...acc, + [key]: init, + }; + } + return acc; + }, {}), + [schema], + ); + + return isLoading ? ( + + ) : error?.message ? ( +
{error?.message}
+ ) : ( + + ); +} + +function ErrorList< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any, +>({ errors, registry }: ErrorListProps) { + const { translateString } = registry; + return ( + + + + {translateString(TranslatableString.ErrorsLabel)} + + + {errors.map((error, i: number) => { + return ( + + + + + + + ); + })} + + + + ); +} + +function PureCustomConfig(props: PureCustomConfigProps) { + const { schema, initialValues, formHooks, handleNext } = props; + const { setValue, watch } = formHooks; + + console.log({ schema }); + + const configData = watch("config") as IChangeEvent; + + console.log({ configData }); + + const setConfig = (data: IChangeEvent) => { + console.log({ data }); + setValue("config", data); + }; + + const formRef = useRef(); + + const validate = () => { + const isValid: boolean = formRef?.current?.validateForm(); + console.log({ isValid }, formRef.current); + if (!isValid) { + formHooks.setError("config", { + message: "invalid config", + }); + toast.error( + "Configuration is invalid. Please check the highlighted fields for details.", + ); + } else { + formHooks.clearErrors("config"); + toast.success("Config seems valid"); + } + }; + + return ( + ( +
+ +
+ ), + }, + }} + /> + ); +} diff --git a/pkgs/ui/src/components/createMachineForm/index.tsx b/pkgs/ui/src/components/createMachineForm/index.tsx new file mode 100644 index 000000000..3c5186a63 --- /dev/null +++ b/pkgs/ui/src/components/createMachineForm/index.tsx @@ -0,0 +1,160 @@ +import { + Box, + Button, + MobileStepper, + Step, + StepLabel, + Stepper, + Typography, + useMediaQuery, + useTheme, +} from "@mui/material"; +import React, { ReactNode, useState } from "react"; +import { useForm, UseFormReturn } from "react-hook-form"; +import { CustomConfig } from "./customConfig"; +import { CreateMachineForm, FormStep } from "./interfaces"; + +const SC = (props: { children: ReactNode }) => { + return <>{props.children}; +}; + +export function CreateMachineForm() { + const formHooks = useForm({ + defaultValues: { + name: "", + config: {}, + }, + }); + const { handleSubmit, control, watch, reset, formState } = formHooks; + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const [activeStep, setActiveStep] = useState(0); + + const steps: FormStep[] = [ + { + id: "template", + label: "Template", + content:
, + }, + { + id: "modules", + label: "Modules", + content:
, + }, + { + id: "config", + label: "Customize", + content: , + }, + { + id: "save", + label: "Save", + content:
, + }, + ]; + + const handleNext = () => { + if (activeStep < steps.length - 1) { + setActiveStep((prevActiveStep) => prevActiveStep + 1); + } + }; + + const handleBack = () => { + if (activeStep > 0) { + setActiveStep((prevActiveStep) => prevActiveStep - 1); + } + }; + + const handleReset = () => { + setActiveStep(0); + reset(); + }; + const currentStep = steps.at(activeStep); + + async function onSubmit(data: any) { + console.log({ data }, "Aggregated Data; creating machine from"); + } + + const BackButton = () => ( + + ); + + const NextButton = () => ( + <> + {activeStep !== steps.length - 1 && ( + + )} + {activeStep === steps.length - 1 && ( + + )} + + ); + return ( + + + {isMobile && ( + } + nextButton={} + steps={steps.length} + /> + )} + {!isMobile && ( + + {steps.map(({ label }, index) => { + const stepProps: { completed?: boolean } = {}; + const labelProps: { + optional?: React.ReactNode; + } = {}; + return ( + + {label} + + ); + })} + + )} + {/* */} + {/* The step Content */} + {currentStep && currentStep.content} + + {/* Desktop step controls */} + {!isMobile && ( + + + + + + )} + + + ); +} diff --git a/pkgs/ui/src/components/createMachineForm/interfaces.ts b/pkgs/ui/src/components/createMachineForm/interfaces.ts new file mode 100644 index 000000000..93d730fcb --- /dev/null +++ b/pkgs/ui/src/components/createMachineForm/interfaces.ts @@ -0,0 +1,24 @@ +import { ReactElement, ReactNode } from "react"; +import { UseFormReturn } from "react-hook-form"; + +export type StepId = "template" | "modules" | "config" | "save"; + +export type CreateMachineForm = { + name: string; + config: any; +}; + +export type FormHooks = UseFormReturn; + +export type FormStep = { + id: StepId; + label: string; + content: FormStepContent; +}; + +export interface FormStepContentProps { + formHooks: FormHooks; + handleNext: () => void; +} + +export type FormStepContent = ReactElement; diff --git a/pkgs/ui/src/components/createMachineForm/saveConfig.tsx b/pkgs/ui/src/components/createMachineForm/saveConfig.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/pkgs/ui/src/components/createMachineForm/selectModules.tsx b/pkgs/ui/src/components/createMachineForm/selectModules.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/pkgs/ui/src/components/createMachineForm/selectTemplate.tsx b/pkgs/ui/src/components/createMachineForm/selectTemplate.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/pkgs/ui/src/data/_schema.ts b/pkgs/ui/src/data/_schema.ts new file mode 100644 index 000000000..7a3b8938d --- /dev/null +++ b/pkgs/ui/src/data/_schema.ts @@ -0,0 +1,88 @@ +import { RJSFSchema } from "@rjsf/utils"; +export const schema: RJSFSchema = { + properties: { + bloatware: { + properties: { + age: { + default: 42, + description: "The age of the user", + type: "integer", + }, + isAdmin: { + default: false, + description: "Is the user an admin?", + type: "boolean", + }, + kernelModules: { + default: ["nvme", "xhci_pci", "ahci"], + description: "A list of enabled kernel modules", + items: { + type: "string", + }, + type: "array", + }, + name: { + default: "John Doe", + description: "The name of the user", + type: "string", + }, + services: { + properties: { + opt: { + default: "foo", + description: "A submodule option", + type: "string", + }, + }, + type: "object", + }, + userIds: { + additionalProperties: { + type: "integer", + }, + default: { + albrecht: 3, + horst: 1, + peter: 2, + }, + description: "Some attributes", + type: "object", + }, + }, + type: "object", + }, + networking: { + properties: { + zerotier: { + properties: { + controller: { + properties: { + enable: { + default: false, + description: + "Whether to enable turn this machine into the networkcontroller.", + type: "boolean", + }, + public: { + default: false, + description: + "everyone can join a public network without having the administrator to accept\n", + type: "boolean", + }, + }, + type: "object", + }, + networkId: { + description: "zerotier networking id\n", + type: "string", + }, + }, + required: ["networkId"], + type: "object", + }, + }, + type: "object", + }, + }, + type: "object", +}; diff --git a/pkgs/ui/src/app/machines/add/schema.ts b/pkgs/ui/src/data/_schema2.ts similarity index 100% rename from pkgs/ui/src/app/machines/add/schema.ts rename to pkgs/ui/src/data/_schema2.ts From 2a9be18d31178e9dbde4f0033cd701aab938e5a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 14:17:00 +0200 Subject: [PATCH 22/40] generate-secrets: use - instead of _ as file seperator --- nixosModules/clanCore/secrets/sops.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixosModules/clanCore/secrets/sops.nix b/nixosModules/clanCore/secrets/sops.nix index 58a6eef6d..fb06c393d 100644 --- a/nixosModules/clanCore/secrets/sops.nix +++ b/nixosModules/clanCore/secrets/sops.nix @@ -1,7 +1,7 @@ { config, lib, pkgs, ... }: { config = { - system.clan.generateSecrets = pkgs.writeScript "generate_secrets" '' + system.clan.generateSecrets = pkgs.writeScript "generate-secrets" '' #!/bin/sh set -efu set -x # remove for prod From 5cb136d32cc5285526fdc92d80b8ff6fc055aa0a Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Sun, 3 Sep 2023 14:23:31 +0200 Subject: [PATCH 23/40] fix malformed step interface --- pkgs/ui/src/components/createMachineForm/customConfig.tsx | 5 ++--- pkgs/ui/src/components/createMachineForm/interfaces.ts | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pkgs/ui/src/components/createMachineForm/customConfig.tsx b/pkgs/ui/src/components/createMachineForm/customConfig.tsx index 7b9e76f23..fd005fbf3 100644 --- a/pkgs/ui/src/components/createMachineForm/customConfig.tsx +++ b/pkgs/ui/src/components/createMachineForm/customConfig.tsx @@ -32,7 +32,7 @@ interface PureCustomConfigProps extends FormStepContentProps { initialValues: any; } export function CustomConfig(props: FormStepContentProps) { - const { formHooks, handleNext } = props; + const { formHooks } = props; const { data, isLoading, error } = useGetMachineSchema("mama"); const schema = useMemo(() => { if (!isLoading && !error?.message && data?.data) { @@ -66,7 +66,6 @@ export function CustomConfig(props: FormStepContentProps) { formHooks={formHooks} initialValues={initialValues} schema={schema} - handleNext={handleNext} /> ); } @@ -101,7 +100,7 @@ function ErrorList< } function PureCustomConfig(props: PureCustomConfigProps) { - const { schema, initialValues, formHooks, handleNext } = props; + const { schema, initialValues, formHooks } = props; const { setValue, watch } = formHooks; console.log({ schema }); diff --git a/pkgs/ui/src/components/createMachineForm/interfaces.ts b/pkgs/ui/src/components/createMachineForm/interfaces.ts index 93d730fcb..e83c9a9e5 100644 --- a/pkgs/ui/src/components/createMachineForm/interfaces.ts +++ b/pkgs/ui/src/components/createMachineForm/interfaces.ts @@ -18,7 +18,6 @@ export type FormStep = { export interface FormStepContentProps { formHooks: FormHooks; - handleNext: () => void; } export type FormStepContent = ReactElement; From 89cdbdd62a3df9789f4ac5d20fb6cdeb0f61cea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 14:55:53 +0200 Subject: [PATCH 24/40] also auto-import group secrets --- checks/secrets/default.nix | 6 ++- .../foo => groups/group}/machines/machine | 0 .../sops/secrets/group-secret/groups/group | 1 + .../secrets/sops/secrets/group-secret/secret | 20 ++++++++ .../sops/secrets/secret/machines/machine | 1 + .../sops/secrets/{foo => secret}/secret | 0 .../sops/secrets/{foo => secret}/users/admin | 0 nixosModules/clanCore/secrets/sops.nix | 47 +++++++++++-------- 8 files changed, 54 insertions(+), 21 deletions(-) rename checks/secrets/sops/{secrets/foo => groups/group}/machines/machine (100%) create mode 120000 checks/secrets/sops/secrets/group-secret/groups/group create mode 100644 checks/secrets/sops/secrets/group-secret/secret create mode 120000 checks/secrets/sops/secrets/secret/machines/machine rename checks/secrets/sops/secrets/{foo => secret}/secret (100%) rename checks/secrets/sops/secrets/{foo => secret}/users/admin (100%) diff --git a/checks/secrets/default.nix b/checks/secrets/default.nix index c6b1a8b2b..8f050bf7b 100644 --- a/checks/secrets/default.nix +++ b/checks/secrets/default.nix @@ -5,13 +5,17 @@ imports = [ (self.nixosModules.clanCore) ]; - environment.etc."secret".source = config.sops.secrets.foo.path; + environment.etc."secret".source = config.sops.secrets.secret.path; + environment.etc."group-secret".source = config.sops.secrets.group-secret.path; sops.age.keyFile = ./key.age; + clanCore.clanDir = "${./.}"; clanCore.machineName = "machine"; + networking.hostName = "machine"; }; testScript = '' machine.succeed("cat /etc/secret >&2") + machine.succeed("cat /etc/group-secret >&2") ''; } diff --git a/checks/secrets/sops/secrets/foo/machines/machine b/checks/secrets/sops/groups/group/machines/machine similarity index 100% rename from checks/secrets/sops/secrets/foo/machines/machine rename to checks/secrets/sops/groups/group/machines/machine diff --git a/checks/secrets/sops/secrets/group-secret/groups/group b/checks/secrets/sops/secrets/group-secret/groups/group new file mode 120000 index 000000000..ad3ef6eac --- /dev/null +++ b/checks/secrets/sops/secrets/group-secret/groups/group @@ -0,0 +1 @@ +../../../groups/group \ No newline at end of file diff --git a/checks/secrets/sops/secrets/group-secret/secret b/checks/secrets/sops/secrets/group-secret/secret new file mode 100644 index 000000000..fc575a972 --- /dev/null +++ b/checks/secrets/sops/secrets/group-secret/secret @@ -0,0 +1,20 @@ +{ + "data": "ENC[AES256_GCM,data:FgF3,iv:QBbnqZ6405qmwGKhbolPr9iobngXt8rtfUwCBOnmwRA=,tag:7gqI1zLVnTkZ0xrNn/LEkA==,type:str]", + "sops": { + "kms": null, + "gcp_kms": null, + "azure_kv": null, + "hc_vault": null, + "age": [ + { + "recipient": "age15x8u838dwqflr3t6csf4tlghxm4tx77y379ncqxav7y2n8qp7yzqgrwt00", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArMHcxKzhUZzNHQmQrb28x\nRC9UMlZMeDN3S1l1eHdUWmV4VUVReHhhQ0RnCjAyUXVlY1FmclVmL2lEdFZuTmll\nVENpa3AwbjlDck5zdGdHUTRnNEdEOUkKLS0tIER3ZlNMSVFnRElkRDcxajZnVmFl\nZThyYzcvYUUvaWJYUmlwQ3dsSDdjSjgK+tj34yBzrsIjm6V+T9wTgz5FdNGOR7I/\nVB4fh8meW0vi/PCK/rajC8NbqmK8qq/lwsF/JwfZKDSdG0FOJUB1AA==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2023-09-03T12:44:56Z", + "mac": "ENC[AES256_GCM,data:d5a0WfE5ZRLKF1NZkBfOl+cVI8ZZHd2rC+qX/giALjyrzk09rLxBeY4lO827GFfMmVy/oC7ceH9pjv2O7ibUiQtcbGIQVBg/WP+dVn8fRMWtF0jpv9BhYTutkVk3kiddqPGhp3mpwvls2ot5jtCRczTPk3JSxN3B1JSJCmj9GfQ=,iv:YmlkTYFNUaFRWozO8+OpEVKaSQmh+N9zpatwUNMPNyw=,tag:mEGQ4tdo82qlhKWalQuufg==,type:str]", + "pgp": null, + "unencrypted_suffix": "_unencrypted", + "version": "3.7.3" + } +} \ No newline at end of file diff --git a/checks/secrets/sops/secrets/secret/machines/machine b/checks/secrets/sops/secrets/secret/machines/machine new file mode 120000 index 000000000..4cef1e1fa --- /dev/null +++ b/checks/secrets/sops/secrets/secret/machines/machine @@ -0,0 +1 @@ +../../../machines/machine \ No newline at end of file diff --git a/checks/secrets/sops/secrets/foo/secret b/checks/secrets/sops/secrets/secret/secret similarity index 100% rename from checks/secrets/sops/secrets/foo/secret rename to checks/secrets/sops/secrets/secret/secret diff --git a/checks/secrets/sops/secrets/foo/users/admin b/checks/secrets/sops/secrets/secret/users/admin similarity index 100% rename from checks/secrets/sops/secrets/foo/users/admin rename to checks/secrets/sops/secrets/secret/users/admin diff --git a/nixosModules/clanCore/secrets/sops.nix b/nixosModules/clanCore/secrets/sops.nix index fb06c393d..ab9772282 100644 --- a/nixosModules/clanCore/secrets/sops.nix +++ b/nixosModules/clanCore/secrets/sops.nix @@ -1,4 +1,25 @@ { config, lib, pkgs, ... }: +let + secretsDir = config.clanCore.clanDir + "/sops/secrets"; + 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? + containsSymlink = path: + builtins.pathExists path && (builtins.readFileType path == "directory" || builtins.readFileType path == "symlink"); + + containsMachine = parent: name: type: + type == "directory" && containsSymlink "${parent}/${name}/machines/${config.clanCore.machineName}"; + + containsMachineOrGroups = name: type: + (containsMachine secretsDir name type) || lib.any (group: type == "directory" && containsSymlink "${secretsDir}/${name}/groups/${group}") groups; + + filterDir = filter: dir: + lib.optionalAttrs (builtins.pathExists dir) + (lib.filterAttrs filter (builtins.readDir dir)); + + groups = builtins.attrNames (filterDir (containsMachine groupsDir) groupsDir); + secrets = filterDir containsMachineOrGroups secretsDir; +in { config = { system.clan.generateSecrets = pkgs.writeScript "generate-secrets" '' @@ -43,26 +64,12 @@ fi) '') "" config.clanCore.secrets} ''; - sops.secrets = - let - secretsDir = config.clanCore.clanDir + "/sops/secrets"; - encryptedForThisMachine = name: type: - let - symlink = "${secretsDir}/${name}/machines/${config.clanCore.machineName}"; - in - # WTF, nix bug, my symlink is in the nixos module detected as a directory also it works in the repl - type == "directory" && builtins.pathExists symlink && (builtins.readFileType symlink == "directory" || builtins.readFileType symlink == "symlink"); - secrets = - if !(builtins.pathExists secretsDir) - then { } - else lib.filterAttrs encryptedForThisMachine (builtins.readDir secretsDir); - in - builtins.mapAttrs - (name: _: { - sopsFile = config.clanCore.clanDir + "/sops/secrets/${name}/secret"; - format = "binary"; - }) - secrets; + sops.secrets = builtins.mapAttrs + (name: _: { + sopsFile = config.clanCore.clanDir + "/sops/secrets/${name}/secret"; + format = "binary"; + }) + secrets; # 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" ""))); }; From 618fb4b8a7b2b03f5c9b30e01b54cc82f087003c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 15:17:28 +0200 Subject: [PATCH 25/40] impureTest: add clan to $PATH for debugging --- checks/impure/flake-module.nix | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/checks/impure/flake-module.nix b/checks/impure/flake-module.nix index d8018061b..4d571c4d0 100644 --- a/checks/impure/flake-module.nix +++ b/checks/impure/flake-module.nix @@ -17,6 +17,7 @@ pkgs.jq pkgs.openssh pkgs.nix + self'.packages.clan-cli ]}" cd $TMPDIR @@ -32,10 +33,10 @@ nix flake show echo create a machine - ${self'.packages.clan-cli}/bin/clan machines create machine1 + clan machines create machine1 echo check machine1 exists - ${self'.packages.clan-cli}/bin/clan machines list | grep -q machine1 + clan machines list | grep -q machine1 echo check machine1 appears in nixosConfigurations nix flake show --json | jq '.nixosConfigurations' | grep -q machine1 From d51dd39ae3caba9281bb3987632efdd1ee48accb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 15:18:29 +0200 Subject: [PATCH 26/40] buildClan: fix importing machines from settings --- lib/build-clan/default.nix | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/build-clan/default.nix b/lib/build-clan/default.nix index 405934a2c..f931b8d6f 100644 --- a/lib/build-clan/default.nix +++ b/lib/build-clan/default.nix @@ -4,15 +4,12 @@ , machines ? { } # allows to include machine-specific modules i.e. machines.${name} = { ... } }: let - machinesDirs = - if builtins.pathExists "${directory}/machines" - then builtins.readDir directory + /machines - else { }; + machinesDirs = lib.optionalAttrs (builtins.pathExists "${directory}/machines") (builtins.readDir (directory + /machines)); machineSettings = machineName: - if builtins.pathExists "${directory}/machines/${machineName}/settings.json" - then builtins.fromJSON (builtins.readFile directory + /machines/${machineName}/settings.json) - else { }; + lib.optionalAttrs (builtins.pathExists "${directory}/machines/${machineName}/settings.json") + builtins.fromJSON + (builtins.readFile (directory + /machines/${machineName}/settings.json)); nixosConfigurations = lib.mapAttrs (name: _: From 40ce518b7ef94b8e80d7149f28f30b00a881d1e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 15:35:14 +0200 Subject: [PATCH 27/40] template/buildClan: use self as directory paths somehow don't end up in the store for some reason, which breaks sops-nix --- templates/new-clan/flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/new-clan/flake.nix b/templates/new-clan/flake.nix index 7687075e2..17c6154b8 100644 --- a/templates/new-clan/flake.nix +++ b/templates/new-clan/flake.nix @@ -11,7 +11,7 @@ { # all machines managed by cLAN nixosConfigurations = clan-core.lib.buildClan { - directory = ./.; + directory = self; }; # add the cLAN cli tool to the dev shell devShells.${system}.default = pkgs.mkShell { From efce138b2462bb91f2c334142403aa329f6cf125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 15:36:00 +0200 Subject: [PATCH 28/40] template: do not rely on flake registry for nixpkgs otherwise we end up downloading nixpkgs twice. --- templates/new-clan/flake.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/new-clan/flake.nix b/templates/new-clan/flake.nix index 17c6154b8..4493397b6 100644 --- a/templates/new-clan/flake.nix +++ b/templates/new-clan/flake.nix @@ -3,10 +3,10 @@ inputs.clan-core.url = "git+https://git.clan.lol/clan/clan-core"; - outputs = { clan-core, nixpkgs, ... }: + outputs = { self, clan-core, ... }: let system = "x86_64-linux"; - pkgs = nixpkgs.legacyPackages.x86_64-linux; + pkgs = clan-core.inputs.nixpkgs.legacyPackages.${system}; in { # all machines managed by cLAN From 83533720de8ec11c0140e64a0525bf0c4d2d0990 Mon Sep 17 00:00:00 2001 From: Clan Merge Bot Date: Mon, 4 Sep 2023 00:00:13 +0000 Subject: [PATCH 29/40] update flake lock - 2023-09-04T00:00+00:00 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'disko': 'github:nix-community/disko/4eed2457b053c4bbad7d90d2b3a1d539c2c9009c' (2023-08-16) → 'github:nix-community/disko/06481a9836c37b7c1aba784092a984c2d2ef5431' (2023-09-02) • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/59cf3f1447cfc75087e7273b04b31e689a8599fb' (2023-08-01) → 'github:hercules-ci/flake-parts/7f53fdb7bdc5bb237da7fefef12d099e4fd611ca' (2023-09-01) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/5690c4271f2998c304a45c91a0aeb8fb69feaea7' (2023-08-25) → 'github:NixOS/nixpkgs/e56990880811a451abd32515698c712788be5720' (2023-09-02) • Updated input 'sops-nix': 'github:Mic92/sops-nix/0618c8f0ed5255ad74ee08d1618841ff5af85c86' (2023-08-27) → 'github:Mic92/sops-nix/d9c5dc41c4b1f74c77f0dbffd0f3a4ebde447b7a' (2023-08-30) • Updated input 'treefmt-nix': 'github:numtide/treefmt-nix/843e1e1b01ac7c9e858368fffd1692cbbdbe4a0e' (2023-08-25) → 'github:numtide/treefmt-nix/e3e0f9f6d47f8fc68aff15150eda1224fb46f4d4' (2023-09-02) --- flake.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/flake.lock b/flake.lock index 6d94dcaed..76ab44698 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1692199161, - "narHash": "sha256-GqKApvQ1JCf5DzH/Q+P4nwuHb6MaQGaWTu41lYzveF4=", + "lastModified": 1693677537, + "narHash": "sha256-F8ozidIQV4Sp/IfTE54U+qIOuC88b9WskFWK5VrHBs4=", "owner": "nix-community", "repo": "disko", - "rev": "4eed2457b053c4bbad7d90d2b3a1d539c2c9009c", + "rev": "06481a9836c37b7c1aba784092a984c2d2ef5431", "type": "github" }, "original": { @@ -27,11 +27,11 @@ ] }, "locked": { - "lastModified": 1690933134, - "narHash": "sha256-ab989mN63fQZBFrkk4Q8bYxQCktuHmBIBqUG1jl6/FQ=", + "lastModified": 1693611461, + "narHash": "sha256-aPODl8vAgGQ0ZYFIRisxYG5MOGSkIczvu2Cd8Gb9+1Y=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "59cf3f1447cfc75087e7273b04b31e689a8599fb", + "rev": "7f53fdb7bdc5bb237da7fefef12d099e4fd611ca", "type": "github" }, "original": { @@ -98,11 +98,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1693003285, - "narHash": "sha256-5nm4yrEHKupjn62MibENtfqlP6pWcRTuSKrMiH9bLkc=", + "lastModified": 1693663421, + "narHash": "sha256-ImMIlWE/idjcZAfxKK8sQA7A1Gi/O58u5/CJA+mxvl8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5690c4271f2998c304a45c91a0aeb8fb69feaea7", + "rev": "e56990880811a451abd32515698c712788be5720", "type": "github" }, "original": { @@ -131,11 +131,11 @@ "nixpkgs-stable": [] }, "locked": { - "lastModified": 1693105804, - "narHash": "sha256-nlqNjW7dfucUJQqRGuG08MKPOSME8fLOCx/bd9hiEPs=", + "lastModified": 1693404499, + "narHash": "sha256-cx/7yvM/AP+o/3wPJmA9W9F+WHemJk5t+Xcr+Qwkqhg=", "owner": "Mic92", "repo": "sops-nix", - "rev": "0618c8f0ed5255ad74ee08d1618841ff5af85c86", + "rev": "d9c5dc41c4b1f74c77f0dbffd0f3a4ebde447b7a", "type": "github" }, "original": { @@ -151,11 +151,11 @@ ] }, "locked": { - "lastModified": 1692972530, - "narHash": "sha256-LG+M7TjlLJ1lx2qbD1yaexvue1VAatpVandtHVEN5Lc=", + "lastModified": 1693689099, + "narHash": "sha256-NuilTRYMH+DDR/uBWQjDbX5mWCA05lwo2Sg9iTkkEs4=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "843e1e1b01ac7c9e858368fffd1692cbbdbe4a0e", + "rev": "e3e0f9f6d47f8fc68aff15150eda1224fb46f4d4", "type": "github" }, "original": { From 9f033aef55b830cf21ed769cfe4584e338e94505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 15:55:57 +0200 Subject: [PATCH 30/40] fix quoting --- lib/build-clan/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/build-clan/default.nix b/lib/build-clan/default.nix index f931b8d6f..416f1982c 100644 --- a/lib/build-clan/default.nix +++ b/lib/build-clan/default.nix @@ -8,8 +8,8 @@ let machineSettings = machineName: lib.optionalAttrs (builtins.pathExists "${directory}/machines/${machineName}/settings.json") - builtins.fromJSON - (builtins.readFile (directory + /machines/${machineName}/settings.json)); + (builtins.fromJSON + (builtins.readFile (directory + /machines/${machineName}/settings.json))); nixosConfigurations = lib.mapAttrs (name: _: From bef27a2c67393c614c9b46c8d33474df3da31b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 6 Sep 2023 14:03:21 +0200 Subject: [PATCH 31/40] fix cors headers in development --- pkgs/clan-cli/clan_cli/webui/app.py | 14 +--------- pkgs/clan-cli/clan_cli/webui/config.py | 38 -------------------------- pkgs/clan-cli/clan_cli/webui/server.py | 36 +++++++++++++++++------- 3 files changed, 27 insertions(+), 61 deletions(-) delete mode 100644 pkgs/clan-cli/clan_cli/webui/config.py diff --git a/pkgs/clan-cli/clan_cli/webui/app.py b/pkgs/clan-cli/clan_cli/webui/app.py index f1742d8c6..e8396549b 100644 --- a/pkgs/clan-cli/clan_cli/webui/app.py +++ b/pkgs/clan-cli/clan_cli/webui/app.py @@ -1,10 +1,8 @@ from fastapi import FastAPI -from fastapi.middleware.cors import CORSMiddleware from fastapi.routing import APIRoute from fastapi.staticfiles import StaticFiles from .assets import asset_path -from .config import settings from .routers import health, machines, root @@ -14,17 +12,7 @@ def setup_app() -> FastAPI: app.include_router(machines.router) app.include_router(root.router) - if settings.env.is_development(): - # TODO make this configurable - app.add_middleware( - CORSMiddleware, - allow_origins="http://${settings.dev_host}:${settings.dev_port}", - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], - ) - else: - app.mount("/static", StaticFiles(directory=asset_path()), name="static") + app.mount("/static", StaticFiles(directory=asset_path()), name="static") for route in app.routes: if isinstance(route, APIRoute): diff --git a/pkgs/clan-cli/clan_cli/webui/config.py b/pkgs/clan-cli/clan_cli/webui/config.py deleted file mode 100644 index d64c23b38..000000000 --- a/pkgs/clan-cli/clan_cli/webui/config.py +++ /dev/null @@ -1,38 +0,0 @@ -# config.py -import logging -import os -from enum import Enum - -from pydantic import BaseSettings - -logger = logging.getLogger(__name__) - - -class EnvType(Enum): - production = "production" - development = "development" - - @staticmethod - def from_environment() -> "EnvType": - t = os.environ.get("CLAN_WEBUI_ENV", "production") - try: - return EnvType[t] - except KeyError: - logger.warning(f"Invalid environment type: {t}, fallback to production") - return EnvType.production - - def is_production(self) -> bool: - return self == EnvType.production - - def is_development(self) -> bool: - return self == EnvType.development - - -class Settings(BaseSettings): - env: EnvType = EnvType.from_environment() - dev_port: int = int(os.environ.get("CLAN_WEBUI_DEV_PORT", 3000)) - dev_host: str = os.environ.get("CLAN_WEBUI_DEV_HOST", "localhost") - - -# global instance -settings = Settings() diff --git a/pkgs/clan-cli/clan_cli/webui/server.py b/pkgs/clan-cli/clan_cli/webui/server.py index e594ed3a3..7d915a743 100644 --- a/pkgs/clan-cli/clan_cli/webui/server.py +++ b/pkgs/clan-cli/clan_cli/webui/server.py @@ -1,6 +1,5 @@ import argparse import logging -import os import subprocess import time import urllib.request @@ -27,11 +26,23 @@ def defer_open_browser(base_url: str) -> None: @contextmanager -def spawn_node_dev_server() -> Iterator[None]: +def spawn_node_dev_server(host: str, port: int) -> Iterator[None]: logger.info("Starting node dev server...") path = Path(__file__).parent.parent.parent.parent / "ui" with subprocess.Popen( - ["direnv", "exec", path, "npm", "run", "dev"], + [ + "direnv", + "exec", + path, + "npm", + "run", + "dev", + "--", + "--hostname", + host, + "--port", + str(port), + ], cwd=path, ) as proc: try: @@ -42,16 +53,21 @@ def spawn_node_dev_server() -> Iterator[None]: def start_server(args: argparse.Namespace) -> None: with ExitStack() as stack: + headers: list[tuple[str, str]] = [] if args.dev: - os.environ["CLAN_WEBUI_ENV"] = "development" - os.environ["CLAN_WEBUI_DEV_PORT"] = str(args.dev_port) - os.environ["CLAN_WEBUI_DEV_HOST"] = args.dev_host - - stack.enter_context(spawn_node_dev_server()) + stack.enter_context(spawn_node_dev_server(args.dev_host, args.dev_port)) open_url = f"http://{args.dev_host}:{args.dev_port}" + host = args.dev_host + if ":" in host: + host = f"[{host}]" + headers = [ + ( + "Access-Control-Allow-Origin", + f"http://{host}:{args.dev_port}", + ) + ] else: - os.environ["CLAN_WEBUI_ENV"] = "production" open_url = f"http://[{args.host}]:{args.port}" if not args.no_open: @@ -63,5 +79,5 @@ def start_server(args: argparse.Namespace) -> None: port=args.port, log_level=args.log_level, reload=args.reload, - headers=[("Access-Control-Allow-Origin", "*")], + headers=headers, ) From fcbc3ec8995523a1b4b65a74c25b1b33627dde6f Mon Sep 17 00:00:00 2001 From: DavHau Date: Wed, 6 Sep 2023 14:40:00 +0200 Subject: [PATCH 32/40] clan-cli: use dependency flake instead of registry --- pkgs/clan-cli/clan_cli/dirs.py | 10 +++++++--- pkgs/clan-cli/clan_cli/nix.py | 11 ++++++----- pkgs/clan-cli/default.nix | 22 ++++++++++------------ pkgs/clan-cli/deps-flake.nix | 9 +++++++++ pkgs/clan-cli/shell.nix | 8 ++++---- pkgs/clan-cli/tests/test_ssh_cli.py | 12 ++---------- 6 files changed, 38 insertions(+), 34 deletions(-) create mode 100644 pkgs/clan-cli/deps-flake.nix diff --git a/pkgs/clan-cli/clan_cli/dirs.py b/pkgs/clan-cli/clan_cli/dirs.py index 316448a25..2ac14e8cc 100644 --- a/pkgs/clan-cli/clan_cli/dirs.py +++ b/pkgs/clan-cli/clan_cli/dirs.py @@ -1,3 +1,4 @@ +import json import os import sys from pathlib import Path @@ -30,12 +31,15 @@ def module_root() -> Path: return Path(__file__).parent -def flake_registry() -> Path: - return module_root() / "nixpkgs" / "flake-registry.json" +def deps_flake() -> Path: + return module_root() / "deps_flake" def nixpkgs() -> Path: - return (module_root() / "nixpkgs" / "path").resolve() + # load the flake.lock json file from the deps_flake and return nodes.nixpkgs.path + with open(deps_flake() / "flake.lock") as f: + flake_lock = json.load(f) + return Path(flake_lock["nodes"]["nixpkgs"]["locked"]["path"]) def unfree_nixpkgs() -> Path: diff --git a/pkgs/clan-cli/clan_cli/nix.py b/pkgs/clan-cli/clan_cli/nix.py index 6f866225f..a46e627f5 100644 --- a/pkgs/clan-cli/clan_cli/nix.py +++ b/pkgs/clan-cli/clan_cli/nix.py @@ -1,7 +1,7 @@ import os import tempfile -from .dirs import flake_registry, unfree_nixpkgs +from .dirs import nixpkgs, unfree_nixpkgs def nix_eval(flags: list[str]) -> list[str]: @@ -13,8 +13,9 @@ def nix_eval(flags: list[str]) -> list[str]: "--show-trace", "--extra-experimental-features", "nix-command flakes", - "--flake-registry", - str(flake_registry()), + "--override-input", + "nixpkgs", + str(nixpkgs()), # --store is required to prevent this error: # error: cannot unlink '/nix/store/6xg259477c90a229xwmb53pdfkn6ig3g-default-builder.sh': Operation not permitted "--store", @@ -41,8 +42,8 @@ def nix_shell(packages: list[str], cmd: list[str]) -> list[str]: "shell", "--extra-experimental-features", "nix-command flakes", - "--flake-registry", - str(flake_registry()), + "--inputs-from", + ".#", ] + wrapped_packages + ["-c"] diff --git a/pkgs/clan-cli/default.nix b/pkgs/clan-cli/default.nix index 6ff778dd4..890a2452e 100644 --- a/pkgs/clan-cli/default.nix +++ b/pkgs/clan-cli/default.nix @@ -49,19 +49,17 @@ let cp -r ${./.} $out chmod -R +w $out rm $out/clan_cli/config/jsonschema - ln -sTf ${nixpkgs} $out/clan_cli/nixpkgs + cp -r ${depsFlake} $out/clan_cli/deps_flake cp -r ${../../lib/jsonschema} $out/clan_cli/config/jsonschema ln -s ${ui-assets} $out/clan_cli/webui/assets ''; - nixpkgs = runCommand "nixpkgs" { } '' - mkdir -p $out/unfree - cat > $out/unfree/default.nix < $out/flake-registry.json < Date: Wed, 6 Sep 2023 15:18:22 +0200 Subject: [PATCH 33/40] clan-cli: fix nix_shell --- pkgs/clan-cli/clan_cli/nix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/clan-cli/clan_cli/nix.py b/pkgs/clan-cli/clan_cli/nix.py index a46e627f5..50b2435fb 100644 --- a/pkgs/clan-cli/clan_cli/nix.py +++ b/pkgs/clan-cli/clan_cli/nix.py @@ -43,7 +43,7 @@ def nix_shell(packages: list[str], cmd: list[str]) -> list[str]: "--extra-experimental-features", "nix-command flakes", "--inputs-from", - ".#", + f"{str(nixpkgs())}", ] + wrapped_packages + ["-c"] From 0108d84fe3855f21d987b9d1036ea6be4c5b8963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 6 Sep 2023 16:03:40 +0200 Subject: [PATCH 34/40] speed up machine_flake eval --- pkgs/clan-cli/tests/machine_flake/flake.nix | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkgs/clan-cli/tests/machine_flake/flake.nix b/pkgs/clan-cli/tests/machine_flake/flake.nix index 0f76e47fc..ff220a590 100644 --- a/pkgs/clan-cli/tests/machine_flake/flake.nix +++ b/pkgs/clan-cli/tests/machine_flake/flake.nix @@ -11,7 +11,12 @@ (if builtins.pathExists ./machines/machine1.json then builtins.fromJSON (builtins.readFile ./machines/machine1.json) else { }) - { nixpkgs.hostPlatform = "x86_64-linux"; } + { + nixpkgs.hostPlatform = "x86_64-linux"; + # speed up by not instantiating nixpkgs twice and disable documentation + nixpkgs.pkgs = inputs.nixpkgs.legacyPackages.x86_64-linux; + documentation.enable = false; + } ]; }; }; From 4ac91ccdcb5ce04b54c1cfa52f4968071f816480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 6 Sep 2023 16:04:00 +0200 Subject: [PATCH 35/40] drop clan flake monkeypatch --- pkgs/clan-cli/tests/conftest.py | 2 -- pkgs/clan-cli/tests/machine_flake/.clan-flake | 0 2 files changed, 2 deletions(-) create mode 100644 pkgs/clan-cli/tests/machine_flake/.clan-flake diff --git a/pkgs/clan-cli/tests/conftest.py b/pkgs/clan-cli/tests/conftest.py index f7f750ade..f5db6eb26 100644 --- a/pkgs/clan-cli/tests/conftest.py +++ b/pkgs/clan-cli/tests/conftest.py @@ -48,6 +48,4 @@ def machine_flake(monkeymodule: pytest.MonkeyPatch) -> Generator[Path, None, Non ) # check that an empty config is returned if no json file exists monkeymodule.chdir(flake) - # monkeypatch get_clan_flake_toplevel to return the flake - monkeymodule.setattr("clan_cli.dirs.get_clan_flake_toplevel", lambda: flake) yield flake diff --git a/pkgs/clan-cli/tests/machine_flake/.clan-flake b/pkgs/clan-cli/tests/machine_flake/.clan-flake new file mode 100644 index 000000000..e69de29bb From 802a86715d1a3644b5d4ad267fc7f092f1e1d094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 6 Sep 2023 16:04:56 +0200 Subject: [PATCH 36/40] gitignore deps_flake --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7650c9587..296b9b39f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .direnv result* -pkgs/clan-cli/clan_cli/nixpkgs +pkgs/clan-cli/clan_cli/deps_flake pkgs/clan-cli/clan_cli/webui/assets # python From e7dcc0d260fc5fb7298b092f74b9863fb489f0fe Mon Sep 17 00:00:00 2001 From: DavHau Date: Wed, 6 Sep 2023 16:13:27 +0200 Subject: [PATCH 37/40] clan-cli: fix nix_shell --inputs from --- pkgs/clan-cli/clan_cli/nix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/clan-cli/clan_cli/nix.py b/pkgs/clan-cli/clan_cli/nix.py index 50b2435fb..36e0265f4 100644 --- a/pkgs/clan-cli/clan_cli/nix.py +++ b/pkgs/clan-cli/clan_cli/nix.py @@ -1,7 +1,7 @@ import os import tempfile -from .dirs import nixpkgs, unfree_nixpkgs +from .dirs import deps_flake, nixpkgs, unfree_nixpkgs def nix_eval(flags: list[str]) -> list[str]: @@ -43,7 +43,7 @@ def nix_shell(packages: list[str], cmd: list[str]) -> list[str]: "--extra-experimental-features", "nix-command flakes", "--inputs-from", - f"{str(nixpkgs())}", + f"{str(deps_flake())}", ] + wrapped_packages + ["-c"] From 35c5f248d19e6f87cb7278a663c52e636f5c777b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 6 Sep 2023 17:05:21 +0200 Subject: [PATCH 38/40] cli/deps_flake: resolve symlink prior to execution --- pkgs/clan-cli/clan_cli/dirs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/clan-cli/clan_cli/dirs.py b/pkgs/clan-cli/clan_cli/dirs.py index 2ac14e8cc..7aeb33c97 100644 --- a/pkgs/clan-cli/clan_cli/dirs.py +++ b/pkgs/clan-cli/clan_cli/dirs.py @@ -32,7 +32,7 @@ def module_root() -> Path: def deps_flake() -> Path: - return module_root() / "deps_flake" + return (module_root() / "deps_flake").resolve() def nixpkgs() -> Path: From 4d9a59c792dda2c885ccff4ca7f4f45ac91cf893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Sep 2023 08:29:15 +0200 Subject: [PATCH 39/40] move facts to machine subdirectory This makes it easier to delete facts when removing machines --- nixosModules/clanCore/secrets/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixosModules/clanCore/secrets/default.nix b/nixosModules/clanCore/secrets/default.nix index f1128a327..48fef2956 100644 --- a/nixosModules/clanCore/secrets/default.nix +++ b/nixosModules/clanCore/secrets/default.nix @@ -49,7 +49,7 @@ description = '' path to a fact which is generated by the generator ''; - default = "${config.clanCore.clanDir}/facts/${config.clanCore.machineName}/${fact.config._module.args.name}"; + default = "${config.clanCore.clanDir}/machines/${config.clanCore.machineName}/facts/${fact.config._module.args.name}"; }; value = lib.mkOption { default = builtins.readFile fact.config.path; From 3bdd3af24807980508b77abd233efceab6c72999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 6 Sep 2023 17:29:49 +0200 Subject: [PATCH 40/40] cli: fix nixpkgs unfree import --- .gitignore | 2 +- pkgs/clan-cli/clan_cli/config/machine.py | 4 ++-- pkgs/clan-cli/clan_cli/dirs.py | 12 ++++------ pkgs/clan-cli/clan_cli/nix.py | 6 ++--- pkgs/clan-cli/default.nix | 28 ++++++++++++++++++------ pkgs/clan-cli/shell.nix | 2 +- pkgs/clan-cli/tests/conftest.py | 4 ++-- 7 files changed, 34 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 296b9b39f..7650c9587 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .direnv result* -pkgs/clan-cli/clan_cli/deps_flake +pkgs/clan-cli/clan_cli/nixpkgs pkgs/clan-cli/clan_cli/webui/assets # python diff --git a/pkgs/clan-cli/clan_cli/config/machine.py b/pkgs/clan-cli/clan_cli/config/machine.py index 83c56be9d..0dec9c138 100644 --- a/pkgs/clan-cli/clan_cli/config/machine.py +++ b/pkgs/clan-cli/clan_cli/config/machine.py @@ -6,7 +6,7 @@ from typing import Optional from fastapi import HTTPException -from clan_cli.dirs import get_clan_flake_toplevel, nixpkgs +from clan_cli.dirs import get_clan_flake_toplevel, nixpkgs_source from clan_cli.machines.folders import machine_folder, machine_settings_file from clan_cli.nix import nix_eval @@ -54,7 +54,7 @@ def schema_for_machine(machine_name: str, flake: Optional[Path] = None) -> dict: f""" let flake = builtins.getFlake (toString {flake}); - lib = import {nixpkgs()}/lib; + lib = import {nixpkgs_source()}/lib; options = flake.nixosConfigurations.{machine_name}.options; clanOptions = options.clan; jsonschemaLib = import {Path(__file__).parent / "jsonschema"} {{ inherit lib; }}; diff --git a/pkgs/clan-cli/clan_cli/dirs.py b/pkgs/clan-cli/clan_cli/dirs.py index 7aeb33c97..064f52f0c 100644 --- a/pkgs/clan-cli/clan_cli/dirs.py +++ b/pkgs/clan-cli/clan_cli/dirs.py @@ -1,4 +1,3 @@ -import json import os import sys from pathlib import Path @@ -31,15 +30,12 @@ def module_root() -> Path: return Path(__file__).parent -def deps_flake() -> Path: - return (module_root() / "deps_flake").resolve() +def nixpkgs_flake() -> Path: + return (module_root() / "nixpkgs").resolve() -def nixpkgs() -> Path: - # load the flake.lock json file from the deps_flake and return nodes.nixpkgs.path - with open(deps_flake() / "flake.lock") as f: - flake_lock = json.load(f) - return Path(flake_lock["nodes"]["nixpkgs"]["locked"]["path"]) +def nixpkgs_source() -> Path: + return (module_root() / "nixpkgs" / "path").resolve() def unfree_nixpkgs() -> Path: diff --git a/pkgs/clan-cli/clan_cli/nix.py b/pkgs/clan-cli/clan_cli/nix.py index 36e0265f4..a90b29b1f 100644 --- a/pkgs/clan-cli/clan_cli/nix.py +++ b/pkgs/clan-cli/clan_cli/nix.py @@ -1,7 +1,7 @@ import os import tempfile -from .dirs import deps_flake, nixpkgs, unfree_nixpkgs +from .dirs import nixpkgs_flake, nixpkgs_source, unfree_nixpkgs def nix_eval(flags: list[str]) -> list[str]: @@ -15,7 +15,7 @@ def nix_eval(flags: list[str]) -> list[str]: "nix-command flakes", "--override-input", "nixpkgs", - str(nixpkgs()), + str(nixpkgs_source()), # --store is required to prevent this error: # error: cannot unlink '/nix/store/6xg259477c90a229xwmb53pdfkn6ig3g-default-builder.sh': Operation not permitted "--store", @@ -43,7 +43,7 @@ def nix_shell(packages: list[str], cmd: list[str]) -> list[str]: "--extra-experimental-features", "nix-command flakes", "--inputs-from", - f"{str(deps_flake())}", + f"{str(nixpkgs_flake())}", ] + wrapped_packages + ["-c"] diff --git a/pkgs/clan-cli/default.nix b/pkgs/clan-cli/default.nix index 890a2452e..c73810b29 100644 --- a/pkgs/clan-cli/default.nix +++ b/pkgs/clan-cli/default.nix @@ -44,19 +44,33 @@ let checkPython = python3.withPackages (_ps: dependencies ++ testDependencies); # - vendor the jsonschema nix lib (copy instead of symlink). - # - lib.cleanSource prevents unnecessary rebuilds when `self` changes. source = runCommand "clan-cli-source" { } '' cp -r ${./.} $out chmod -R +w $out rm $out/clan_cli/config/jsonschema - cp -r ${depsFlake} $out/clan_cli/deps_flake + cp -r ${nixpkgs} $out/clan_cli/nixpkgs cp -r ${../../lib/jsonschema} $out/clan_cli/config/jsonschema ln -s ${ui-assets} $out/clan_cli/webui/assets ''; - depsFlake = runCommand "deps-flake" { } '' + nixpkgs = runCommand "nixpkgs" { nativeBuildInputs = [ pkgs.nix ]; } '' mkdir $out - cp ${./deps-flake.nix} $out/flake.nix - ${pkgs.nix}/bin/nix flake lock $out \ + mkdir -p $out/unfree + cat > $out/unfree/default.nix < $out/flake.nix << EOF + { + description = "dependencies for the clan-cli"; + + inputs = { + nixpkgs.url = "nixpkgs"; + }; + + outputs = _inputs: { }; + } + EOF + ln -s ${pkgs.path} $out/path + nix flake lock $out \ --store ./. \ --experimental-features 'nix-command flakes' \ --override-input nixpkgs ${pkgs.path} @@ -94,7 +108,7 @@ python3.pkgs.buildPythonPackage { ${checkPython}/bin/python ./bin/gen-openapi --out $out/openapi.json --app-dir . clan_cli.webui.app:app touch $out ''; - passthru.depsFlake = depsFlake; + passthru.nixpkgs = nixpkgs; passthru.devDependencies = [ setuptools @@ -104,7 +118,7 @@ python3.pkgs.buildPythonPackage { passthru.testDependencies = dependencies ++ testDependencies; postInstall = '' - cp -r ${depsFlake} $out/${python3.sitePackages}/clan_cli/deps_flake + cp -r ${nixpkgs} $out/${python3.sitePackages}/clan_cli/nixpkgs installShellCompletion --bash --name clan \ <(${argcomplete}/bin/register-python-argcomplete --shell bash clan) installShellCompletion --fish --name clan.fish \ diff --git a/pkgs/clan-cli/shell.nix b/pkgs/clan-cli/shell.nix index 0c9644ec1..d3ebaab6c 100644 --- a/pkgs/clan-cli/shell.nix +++ b/pkgs/clan-cli/shell.nix @@ -27,7 +27,7 @@ mkShell { tmp_path=$(realpath ./.direnv) rm -f clan_cli/nixpkgs clan_cli/assets - ln -sf ${clan-cli.depsFlake} clan_cli/deps_flake + ln -sf ${clan-cli.nixpkgs} clan_cli/nixpkgs ln -sf ${ui-assets} clan_cli/webui/assets export PATH="$tmp_path/bin:${checkScript}/bin:$PATH" diff --git a/pkgs/clan-cli/tests/conftest.py b/pkgs/clan-cli/tests/conftest.py index f5db6eb26..70ebd8271 100644 --- a/pkgs/clan-cli/tests/conftest.py +++ b/pkgs/clan-cli/tests/conftest.py @@ -6,7 +6,7 @@ from typing import Generator import pytest -from clan_cli.dirs import nixpkgs +from clan_cli.dirs import nixpkgs_source sys.path.append(os.path.join(os.path.dirname(__file__), "helpers")) @@ -44,7 +44,7 @@ def machine_flake(monkeymodule: pytest.MonkeyPatch) -> Generator[Path, None, Non # provided by get_clan_flake_toplevel flake_nix = flake / "flake.nix" flake_nix.write_text( - flake_nix.read_text().replace("__NIXPKGS__", str(nixpkgs())) + flake_nix.read_text().replace("__NIXPKGS__", str(nixpkgs_source())) ) # check that an empty config is returned if no json file exists monkeymodule.chdir(flake)