From 46fd15a3665a4dc3094c51bb5390bdf77ca06e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 21 Jul 2024 18:21:37 +0200 Subject: [PATCH 01/19] also fix user-password with password-store --- clanModules/root-password/default.nix | 6 +++--- clanModules/user-password/default.nix | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/clanModules/root-password/default.nix b/clanModules/root-password/default.nix index 9c901f30e..a340730e3 100644 --- a/clanModules/root-password/default.nix +++ b/clanModules/root-password/default.nix @@ -9,9 +9,9 @@ users.users.root.hashedPasswordFile = config.clan.core.facts.services.root-password.secret.password-hash.path; - sops.secrets."${config.clan.core.machineName}-password-hash".neededForUsers = lib.mkIf ( - config.clan.core.facts.secretStore == "sops" - ) true; + sops.secrets = lib.mkIf (config.clan.core.facts.secretStore == "sops") { + "${config.clan.core.machineName}-password-hash".neededForUsers = true; + }; clan.core.facts.services.root-password = { secret.password = { }; diff --git a/clanModules/user-password/default.nix b/clanModules/user-password/default.nix index f76c7f397..14b1f0177 100644 --- a/clanModules/user-password/default.nix +++ b/clanModules/user-password/default.nix @@ -23,7 +23,11 @@ users.mutableUsers = false; users.users.${config.clan.user-password.user}.hashedPasswordFile = config.clan.core.facts.services.user-password.secret.user-password-hash.path; - sops.secrets."${config.clan.core.machineName}-user-password-hash".neededForUsers = true; + + sops.secrets = lib.mkIf (config.clan.core.facts.secretStore == "sops") { + "${config.clan.core.machineName}-user-password-hash".neededForUsers = true; + }; + clan.core.facts.services.user-password = { secret.user-password = { }; secret.user-password-hash = { }; From a22286018fe522c0400de091465ad1f3ef70583a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 21 Jul 2024 19:06:09 +0200 Subject: [PATCH 02/19] borgbackup: add exclude option --- clanModules/borgbackup/default.nix | 12 +++++++++++- pkgs/clan-cli/clan_cli/inventory/classes.py | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/clanModules/borgbackup/default.nix b/clanModules/borgbackup/default.nix index fbc05db23..760e1afe8 100644 --- a/clanModules/borgbackup/default.nix +++ b/clanModules/borgbackup/default.nix @@ -106,6 +106,16 @@ in ''; }; + options.clan.borgbackup.exclude = lib.mkOption { + type = lib.types.listOf lib.types.str; + example = [ "*.pyc" ]; + default = [ ]; + description = '' + Directories/Files to exclude from the backup. + Use * as a wildcard. + ''; + }; + imports = [ (lib.mkRemovedOptionModule [ "clan" @@ -129,7 +139,7 @@ in paths = lib.unique ( lib.flatten (map (state: state.folders) (lib.attrValues config.clan.core.state)) ); - exclude = [ "*.pyc" ]; + exclude = cfg.exclude; repo = dest.repo; environment.BORG_RSH = dest.rsh; compression = "auto,zstd"; diff --git a/pkgs/clan-cli/clan_cli/inventory/classes.py b/pkgs/clan-cli/clan_cli/inventory/classes.py index 7712932a3..41d116831 100644 --- a/pkgs/clan-cli/clan_cli/inventory/classes.py +++ b/pkgs/clan-cli/clan_cli/inventory/classes.py @@ -39,6 +39,7 @@ class BorgbackupConfigDestination: @dataclass class BorgbackupConfig: destinations: dict[str, BorgbackupConfigDestination] = field(default_factory = dict) + exclude: list[str] = field(default_factory = list) @dataclass From 27cabbe8cbc4e3fa964e418b624cdc327e934280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 21 Jul 2024 21:18:53 +0200 Subject: [PATCH 03/19] CONTRIBUTING: mention browser --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ce3310fbe..04ba9226f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,3 +19,5 @@ Run a local server: ```shell-session mkdocs serve ``` + +Open http://localhost:8000/ in your browser. From 4b145e6ebd7f455543ec3b0f53764c7b4f37bd4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 21 Jul 2024 22:05:07 +0200 Subject: [PATCH 04/19] docs/setup: also mention experimental option in case someone already has nix installed --- docs/site/index.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/site/index.md b/docs/site/index.md index ee2e0297a..67ad0f166 100644 --- a/docs/site/index.md +++ b/docs/site/index.md @@ -12,6 +12,14 @@ Create your own clan with these initial steps and manage a fleet of machines wit curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install ``` + If you already have installed Nix, make sure you have the `nix-command` and `flakes` configuration enabled in your ~/.config/nix/nix.conf. + The determinate installer already comes with this configuration by default. + + ```bash + # /etc/nix/nix.conf or ~/.config/nix/nix.conf + experimental-features = nix-command flakes + ``` + === "**NixOS**" If you run NixOS the `nix` binary is already installed. From 527002bb0b08e1abd97d4f773c760b045b796a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 21 Jul 2024 22:05:27 +0200 Subject: [PATCH 05/19] docs/setup: recommends nixos configuration for nixos users --- docs/site/index.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/site/index.md b/docs/site/index.md index 67ad0f166..74cc4bb7b 100644 --- a/docs/site/index.md +++ b/docs/site/index.md @@ -24,11 +24,10 @@ Create your own clan with these initial steps and manage a fleet of machines wit If you run NixOS the `nix` binary is already installed. - You will also need to enable the `flakes` and `nix-commands` experimental features. + You will also need to enable the `flakes` and `nix-commands` experimental features in your configuration.nix: - ```bash - # /etc/nix/nix.conf or ~/.config/nix/nix.conf - experimental-features = nix-command flakes + ```nix + { nix.settings.experimental-features = [ "nix-command" "flakes" ]; } ``` === "**Other**" From 1e43a471d2dd29371b8e0fbe220a33d6c9c8279a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 21 Jul 2024 22:06:26 +0200 Subject: [PATCH 06/19] cli: default template url should come from the package itself rather than our gitea This allow easier testing and also forks. --- pkgs/clan-cli/clan_cli/clan/create.py | 17 +++++---- pkgs/clan-cli/clan_cli/dirs.py | 14 ++++++++ pkgs/clan-cli/default.nix | 1 + pkgs/clan-cli/pyproject.toml | 9 ++++- templates/flake-module.nix | 51 +++++++++++---------------- templates/flake.nix | 16 +++++++++ 6 files changed, 68 insertions(+), 40 deletions(-) create mode 100644 templates/flake.nix diff --git a/pkgs/clan-cli/clan_cli/clan/create.py b/pkgs/clan-cli/clan_cli/clan/create.py index 75921131d..3e43e70c7 100644 --- a/pkgs/clan-cli/clan_cli/clan/create.py +++ b/pkgs/clan-cli/clan_cli/clan/create.py @@ -9,12 +9,10 @@ from clan_cli.arg_actions import AppendOptionAction from clan_cli.inventory import Meta, load_inventory, save_inventory from ..cmd import CmdOut, run +from ..dirs import clan_templates from ..errors import ClanError from ..nix import nix_command, nix_shell -default_template_url: str = "git+https://git.clan.lol/clan/clan-core" -minimal_template_url: str = "git+https://git.clan.lol/clan/clan-core#templates.minimal" - @dataclass class CreateClanResponse: @@ -33,7 +31,7 @@ class CreateOptions: # Metadata can be shown with `clan show` meta: Meta | None = None # URL to the template to use. Defaults to the "minimal" template - template_url: str = minimal_template_url + template: str = "minimal" def git_command(directory: Path, *args: str) -> list[str]: @@ -43,7 +41,7 @@ def git_command(directory: Path, *args: str) -> list[str]: @API.register def create_clan(options: CreateOptions) -> CreateClanResponse: directory = Path(options.directory).resolve() - template_url = options.template_url + template_url = f"{clan_templates()}#{options.template}" if not directory.exists(): directory.mkdir() else: @@ -112,10 +110,11 @@ def create_clan(options: CreateOptions) -> CreateClanResponse: def register_create_parser(parser: argparse.ArgumentParser) -> None: parser.add_argument( - "--url", + "--template", type=str, - help="url to the clan template", - default=default_template_url, + choices=["default", "minimal"], + help="Clan template name", + default="default", ) parser.add_argument( @@ -135,7 +134,7 @@ def register_create_parser(parser: argparse.ArgumentParser) -> None: create_clan( CreateOptions( directory=args.path, - template_url=args.url, + template=args.template, ) ) diff --git a/pkgs/clan-cli/clan_cli/dirs.py b/pkgs/clan-cli/clan_cli/dirs.py index 9b687aa43..9249eab21 100644 --- a/pkgs/clan-cli/clan_cli/dirs.py +++ b/pkgs/clan-cli/clan_cli/dirs.py @@ -4,6 +4,8 @@ import sys import urllib from pathlib import Path +from .errors import ClanError + log = logging.getLogger(__name__) @@ -41,6 +43,18 @@ def find_toplevel(top_level_files: list[str]) -> Path | None: return None +def clan_templates() -> Path: + template_path = module_root().parent.parent / "templates" + if template_path.exists(): + return template_path + else: + template_path = module_root() / "templates" + if not template_path.exists(): + msg = f"BUG! clan core not found at {template_path}. This is an issue with packaging the cli" + raise ClanError(msg) + return template_path + + def user_config_dir() -> Path: if sys.platform == "win32": return Path(os.getenv("APPDATA", os.path.expanduser("~\\AppData\\Roaming\\"))) diff --git a/pkgs/clan-cli/default.nix b/pkgs/clan-cli/default.nix index a1968f850..8a29c9a0d 100644 --- a/pkgs/clan-cli/default.nix +++ b/pkgs/clan-cli/default.nix @@ -63,6 +63,7 @@ let rm $out/clan_cli/config/jsonschema ln -sf ${nixpkgs'} $out/clan_cli/nixpkgs cp -r ${../../lib/jsonschema} $out/clan_cli/config/jsonschema + cp -r ${../../templates} $out/clan_cli/templates ${classgen}/bin/classgen ${inventory-schema}/schema.json $out/clan_cli/inventory/classes.py ''; diff --git a/pkgs/clan-cli/pyproject.toml b/pkgs/clan-cli/pyproject.toml index 28fef0831..ee6d46137 100644 --- a/pkgs/clan-cli/pyproject.toml +++ b/pkgs/clan-cli/pyproject.toml @@ -18,7 +18,14 @@ Repository = "https://git.clan.lol/clan/clan-core" exclude = ["clan_cli.nixpkgs*", "result"] [tool.setuptools.package-data] -clan_cli = ["py.typed", "config/jsonschema/*", "webui/assets/**/*", "vms/mimetypes/**/*", "**/allowed-programs.json"] +clan_cli = [ + "**/allowed-programs.json", + "config/jsonschema/*", + "py.typed", + "templates/**/*", + "vms/mimetypes/**/*", + "webui/assets/**/*", +] [tool.pytest.ini_options] testpaths = "tests" diff --git a/templates/flake-module.nix b/templates/flake-module.nix index e48375b57..1caecf690 100644 --- a/templates/flake-module.nix +++ b/templates/flake-module.nix @@ -1,35 +1,26 @@ { self, inputs, ... }: { - flake.templates = { - new-clan = { - description = "Initialize a new clan flake"; - path = ./new-clan; - }; - default = self.templates.new-clan; - minimal = { - description = "for clans managed via (G)UI"; - path = ./minimal; - }; - }; - flake.checks.x86_64-linux.template-minimal = - let - path = self.templates.minimal.path; - initialized = inputs.nixpkgs.legacyPackages.x86_64-linux.runCommand "minimal-clan-flake" { } '' - mkdir $out - cp -r ${path}/* $out - mkdir -p $out/machines/foo - echo '{ "nixpkgs": { "hostPlatform": "x86_64-linux" } }' > $out/machines/foo/settings.json - ''; - evaled = (import "${initialized}/flake.nix").outputs { - self = evaled // { - outPath = initialized; + flake = (import ./flake.nix).outputs {} // { + checks.x86_64-linux.template-minimal = + let + path = self.templates.minimal.path; + initialized = inputs.nixpkgs.legacyPackages.x86_64-linux.runCommand "minimal-clan-flake" { } '' + mkdir $out + cp -r ${path}/* $out + mkdir -p $out/machines/foo + echo '{ "nixpkgs": { "hostPlatform": "x86_64-linux" } }' > $out/machines/foo/settings.json + ''; + evaled = (import "${initialized}/flake.nix").outputs { + self = evaled // { + outPath = initialized; + }; + clan-core = self; }; - clan-core = self; + in + { + type = "derivation"; + name = "minimal-clan-flake-check"; + inherit (evaled.nixosConfigurations.foo.config.system.build.vm) drvPath outPath; }; - in - { - type = "derivation"; - name = "minimal-clan-flake-check"; - inherit (evaled.nixosConfigurations.foo.config.system.build.vm) drvPath outPath; - }; + }; } diff --git a/templates/flake.nix b/templates/flake.nix new file mode 100644 index 000000000..438d8ab54 --- /dev/null +++ b/templates/flake.nix @@ -0,0 +1,16 @@ +{ + outputs = + { ... }: + { + templates = { + default = { + description = "Initialize a new clan flake"; + path = ./new-clan; + }; + minimal = { + description = "for clans managed via (G)UI"; + path = ./minimal; + }; + }; + }; +} From 927aec0db5ae626a7cae781702cd6d8cc23430e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Mon, 22 Jul 2024 06:19:06 +0200 Subject: [PATCH 07/19] don't setup json inventory for cli users --- pkgs/clan-cli/clan_cli/clan/create.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pkgs/clan-cli/clan_cli/clan/create.py b/pkgs/clan-cli/clan_cli/clan/create.py index 3e43e70c7..154854c8b 100644 --- a/pkgs/clan-cli/clan_cli/clan/create.py +++ b/pkgs/clan-cli/clan_cli/clan/create.py @@ -32,6 +32,7 @@ class CreateOptions: meta: Meta | None = None # URL to the template to use. Defaults to the "minimal" template template: str = "minimal" + setup_json_inventory: bool = True def git_command(directory: Path, *args: str) -> list[str]: @@ -87,11 +88,12 @@ def create_clan(options: CreateOptions) -> CreateClanResponse: ) # Write inventory.json file - inventory = load_inventory(directory) - if options.meta is not None: - inventory.meta = options.meta - # Persist creates a commit message for each change - save_inventory(inventory, directory, "Init inventory") + if options.setup_json_inventory: + inventory = load_inventory(directory) + if options.meta is not None: + inventory.meta = options.meta + # Persist creates a commit message for each change + save_inventory(inventory, directory, "Init inventory") flake_update = run( nix_shell(["nixpkgs#nix"], ["nix", "flake", "update"]), cwd=directory @@ -133,8 +135,7 @@ def register_create_parser(parser: argparse.ArgumentParser) -> None: def create_flake_command(args: argparse.Namespace) -> None: create_clan( CreateOptions( - directory=args.path, - template=args.template, + directory=args.path, template=args.template, setup_json_inventory=False ) ) From d21d296c5c697e479e866a8eb0adf255e0e834ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 21 Jul 2024 21:16:07 +0200 Subject: [PATCH 08/19] rework installation template based on received feedback --- docs/site/getting-started/configure.md | 151 +++++++++--------- docs/site/getting-started/deploy.md | 20 +-- docs/site/index.md | 2 +- templates/flake-module.nix | 2 +- templates/new-clan/flake.nix | 97 +++-------- .../new-clan/machines/jon/configuration.nix | 60 ++++--- .../new-clan/machines/sara/configuration.nix | 62 ++++--- templates/new-clan/modules/disko.nix | 5 + templates/new-clan/modules/gnome.nix | 5 + templates/new-clan/modules/shared.nix | 23 ++- 10 files changed, 193 insertions(+), 234 deletions(-) create mode 100644 templates/new-clan/modules/gnome.nix diff --git a/docs/site/getting-started/configure.md b/docs/site/getting-started/configure.md index c06b25cf9..8c91e987f 100644 --- a/docs/site/getting-started/configure.md +++ b/docs/site/getting-started/configure.md @@ -32,9 +32,9 @@ In the `flake.nix` file: - [x] set a unique `name`. -=== "**buildClan**" +=== "**normal flake template**" - ```nix title="clan-core.lib.buildClan" + ```nix title="flake.nix" hl_lines="3" buildClan { # Set a unique name meta.name = "Lobsters"; @@ -50,11 +50,11 @@ In the `flake.nix` file: } ``` -=== "**flakeParts**" +=== "**template using flake-parts**" !!! info "See [Clan with flake-parts](./flake-parts.md) for help migrating to flake-parts." - ```nix title="clan-core.flakeModules.default" + ```nix title="flake.nix" hl_lines="3" clan = { # Set a unique name meta.name = "Lobsters"; @@ -77,11 +77,11 @@ Adding or configuring a new machine requires two simple steps: 1. Find the remote disk id by executing: ```bash title="setup computer" - ssh root@flash-installer.local lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT + ssh root@ lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT ``` !!! Note - Replace `flash-installer.local` with the IP address of the machine if you don't have the avahi service running which resolves mDNS local domains. + Replace `` with the IP address of the machine if you don't have the avahi service running which resolves mDNS local domains. Which should show something like: @@ -97,75 +97,43 @@ Adding or configuring a new machine requires two simple steps: └─nvme0n1p3 nvme-eui.e8238fa6bf530001001b448b4aec2929-part3 swap 16.8G ``` -1. Edit the following fields inside the `flake.nix` +1. Edit the following fields inside the `./machines/jon/configuration.nix` and/or `./machines/sara/configuration.nix` - === "**buildClan**" + ```nix title="./machines//configuration.nix" hl_lines="13 18 23 27" + { + imports = [ + ./hardware-configuration.nix + # contains your disk format and partitioning configuration. + ../../modules/disko.nix + # this file is shared among all machines + ../../modules/shared.nix + # enables GNOME desktop (optional) + ../../modules/gnome.nix + ]; - ```nix title="clan-core.lib.buildClan" hl_lines="18 23" - buildClan { - # ... - machines = { - "jon" = { - imports = [ - # ... - ./modules/disko.nix - ./machines/jon/configuration.nix - ]; - # ... + # Put your username here for login + users.users.user.username = "__YOUR_USERNAME__"; - # Change this to the correct ip-address or hostname - # The hostname is the machine name by default - clan.core.networking.targetHost = pkgs.lib.mkDefault "root@jon" + # Set this for clan commands use ssh i.e. `clan machines update` + # If you change the hostname, you need to update this line to root@ + # This only works however if you have avahi running on your admin machine else use IP + clan.core.networking.targetHost = "root@__IP__"; - # Change this to the ID-LINK of the desired disk shown by 'lsblk' - disko.devices.disk.main = { - device = "/dev/disk/by-id/__CHANGE_ME__"; - } + # You can get your disk id by running the following command on the installer: + # Replace with the IP of the installer printed on the screen or by running the `ip addr` command. + # ssh root@ lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT + disko.devices.disk.main.device = "/dev/disk/by-id/__CHANGE_ME__"; - # e.g. > cat ~/.ssh/id_ed25519.pub - users.users.root.openssh.authorizedKeys.keys = [ - "" - ]; - # ... - }; - }; - } - ``` - - === "**flakeParts**" - - ```nix title="clan-core.flakeModules.default" hl_lines="18 23" - clan = { - # ... - machines = { - "jon" = { - imports = [ - # ... - ./modules/disko.nix - ./machines/jon/configuration.nix - ]; - # ... - - # Change this to the correct ip-address or hostname - # The hostname is the machine name by default - clan.core.networking.targetHost = pkgs.lib.mkDefault "root@jon" - - # Change this to the ID-LINK of the desired disk shown by 'lsblk' - disko.devices.disk.main = { - device = "/dev/disk/by-id/__CHANGE_ME__"; - } - - # e.g. > cat ~/.ssh/id_ed25519.pub - users.users.root.openssh.authorizedKeys.keys = [ - "__YOUR_SSH_KEY__" - ]; - # ... - }; - }; - }; - ``` + # IMPORTANT! Add your SSH key here + # e.g. > cat ~/.ssh/id_ed25519.pub + users.users.root.openssh.authorizedKeys.keys = "__YOUR_SSH_KEY__"; + # ... + } + ``` +!!! Info "Replace `__YOUR_USERNAME__` with the ip of your machine, if you use avahi you can also use your hostname" +!!! Info "Replace `__IP__` with the ip of your machine, if you use avahi you can also use your hostname" !!! Info "Replace `__CHANGE_ME__` with the appropriate identifier, such as `nvme-eui.e8238fa6bf530001001b448b4aec2929`" !!! Info "Replace `__YOUR_SSH_KEY__` with your personal key, like `ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILoMI0NC5eT9pHlQExrvR5ASV3iW9+BXwhfchq0smXUJ jon@jon-desktop`" @@ -179,22 +147,57 @@ Generate the `hardware-configuration.nix` file for your machine by executing the clan machines hw-generate [MACHINE_NAME] [HOSTNAME] ``` -replace `[MACHINE_NAME]` with the name of the machine i.e. `jon` and `[HOSTNAME]` with the `ip_adress` or `hostname` of the machine within the network. i.e. `flash-installer.local` +replace `[MACHINE_NAME]` with the name of the machine i.e. `jon` and `[HOSTNAME]` with the `ip_adress` or `hostname` of the machine within the network. i.e. `` !!! Example ```bash - clan machines hw-generate jon flash-installer.local + clan machines hw-generate jon ``` - This command connects to `flash-installer.local` as `root`, runs `nixos-generate-config` to detect hardware configurations (excluding filesystems), and writes them to `machines/jon/hardware-configuration.nix`. + This command connects to `` as `root`, runs `nixos-generate-config` to detect hardware configurations (excluding filesystems), and writes them to `machines/jon/hardware-configuration.nix`. ### Step 3: Custom Disk Formatting -In `./modules/disko.nix`, a simple `ext4` disk partitioning scheme is defined for the Disko module. For more complex disk partitioning setups, refer to the [Disko examples](https://github.com/nix-community/disko/tree/master/example). +In `./modules/disko.nix`, a simple `ext4` disk partitioning scheme is defined for the Disko module. For more complex disk partitioning setups, +refer to the [Disko templates](https://github.com/nix-community/disko-templates) or [Disko examples](https://github.com/nix-community/disko/tree/master/example). ### Step 4: Custom Configuration Modify `./machines/jon/configuration.nix` to personalize the system settings according to your requirements. +If you wish to name your machine to something else, do the following steps: + +``` +mv ./machines/jon/configuration.nix ./machines/newname/configuration.nix +``` + +Than rename `jon` to your preferred name in `machines` in `flake.nix` as well as the import line: + +```diff +- imports = [ ./machines/jon/configuration.nix ]; ++ imports = [ ./machines/__NEW_NAME__/configuration.nix ]; +``` + +!!! Info "Replace `__NEW_NAME__` with the name of the machine" + +Note that our clan lives inside a git repository. +Only files that have been added with `git add` are recognized by `nix`. +So for every file that you add or rename you also need to run: + +``` +git add ./path/to/my/file +``` + +For renaming jon to your own machine name, you can use the following command: + +``` +git mv ./machines/jon ./machines/newname +``` + +If you only want to setup a single machine at this point, you can delete `sara` from flake.nix as well as from the machines directory: + +``` +git rm ./machines/sara +``` ### Step 5: Check Configuration @@ -206,9 +209,9 @@ nix flake check This command helps ensure that your system configuration is correct and free from errors. -!!! Note +!!! Tip - Integrate this step into your [Continuous Integration](https://en.wikipedia.org/wiki/Continuous_integration) workflow to ensure that only valid Nix configurations are merged into your codebase. This practice helps maintain system stability and reduces integration issues. + You can integrate this step into your [Continuous Integration](https://en.wikipedia.org/wiki/Continuous_integration) workflow to ensure that only valid Nix configurations are merged into your codebase. --- diff --git a/docs/site/getting-started/deploy.md b/docs/site/getting-started/deploy.md index ff09e188c..3312a606f 100644 --- a/docs/site/getting-started/deploy.md +++ b/docs/site/getting-started/deploy.md @@ -112,7 +112,7 @@ This process involves preparing a suitable hardware and disk partitioning config 1. **SSH with Password Authentication** Run the following command to install using SSH: ```bash - clan machines install [MACHINE] flash-installer.local + clan machines install [MACHINE] ``` 2. **Scanning a QR Code for Installation Details** @@ -150,23 +150,17 @@ Clan CLI enables you to remotely update your machines over SSH. This requires se ### Setting the Target Host -Replace `root@jon` with the actual hostname or IP address of your target machine: +Replace `root@jon` with the actual hostname or IP address of your target machine in the `configuration.nix` of the machine: ```{.nix hl_lines="9" .no-copy} -buildClan { +{ # ... - machines = { - # "jon" will be the hostname of the machine - "jon" = { - # Set this for clan commands use ssh i.e. `clan machines update` - # If you change the hostname, you need to update this line to root@ - # This only works however if you have avahi running on your admin machine else use IP - clan.core.networking.targetHost = pkgs.lib.mkDefault "root@jon"; - }; - }; + # Set this for clan commands use ssh i.e. `clan machines update` + # If you change the hostname, you need to update this line to root@ + # This only works however if you have avahi running on your admin machine else use IP + clan.core.networking.targetHost = "root@jon"; }; ``` - !!! warning The use of `root@` in the target address implies SSH access as the `root` user. Ensure that the root login is secured and only used when necessary. diff --git a/docs/site/index.md b/docs/site/index.md index 74cc4bb7b..7513349c9 100644 --- a/docs/site/index.md +++ b/docs/site/index.md @@ -84,7 +84,7 @@ This should yield the following: └── modules └── shared.nix -5 directories, 6 files +5 directories, 9 files ``` ```bash diff --git a/templates/flake-module.nix b/templates/flake-module.nix index 1caecf690..5b746a563 100644 --- a/templates/flake-module.nix +++ b/templates/flake-module.nix @@ -1,6 +1,6 @@ { self, inputs, ... }: { - flake = (import ./flake.nix).outputs {} // { + flake = (import ./flake.nix).outputs { } // { checks.x86_64-linux.template-minimal = let path = self.templates.minimal.path; diff --git a/templates/new-clan/flake.nix b/templates/new-clan/flake.nix index da1d598d9..342e672f1 100644 --- a/templates/new-clan/flake.nix +++ b/templates/new-clan/flake.nix @@ -6,94 +6,24 @@ outputs = { self, clan-core, ... }: let - system = "x86_64-linux"; - pkgs = clan-core.inputs.nixpkgs.legacyPackages.${system}; # Usage see: https://docs.clan.lol clan = clan-core.lib.buildClan { directory = self; - meta.name = "__CHANGE_ME__"; # Ensure this is unique among all clans you want to use. - - # Distributed services, uncomment to enable. - # inventory = { - # services = { - # # This example configures a BorgBackup service - # # Check: https://docs.clan.lol/reference/clanModules which ones are available in Inventory - # borgbackup.instance_1 = { - # roles.server.machines = [ "jon" ]; - # roles.client.machines = [ "sara" ]; - # }; - # }; - # }; + # Ensure this is unique among all clans you want to use. + meta.name = "__CHANGE_ME__"; # Prerequisite: boot into the installer # See: https://docs.clan.lol/getting-started/installer # local> mkdir -p ./machines/machine1 - # local> Edit ./machines/machine1/configuration.nix to your liking + # local> Edit ./machines//configuration.nix to your liking machines = { # "jon" will be the hostname of the machine jon = { - imports = [ - ./modules/shared.nix - ./modules/disko.nix - ./machines/jon/configuration.nix - ]; - - nixpkgs.hostPlatform = system; - - # Set this for clan commands use ssh i.e. `clan machines update` - # If you change the hostname, you need to update this line to root@ - # This only works however if you have avahi running on your admin machine else use IP - clan.core.networking.targetHost = pkgs.lib.mkDefault "root@jon"; - - # You can get your disk id by running the following command on the installer: - # Replace with the IP of the installer printed on the screen or by running the `ip addr` command. - # ssh root@ lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT - disko.devices.disk.main.device = "/dev/disk/by-id/__CHANGE_ME__"; - - # IMPORTANT! Add your SSH key here - # e.g. > cat ~/.ssh/id_ed25519.pub - users.users.root.openssh.authorizedKeys.keys = throw '' - Don't forget to add your SSH key here! - users.users.root.openssh.authorizedKeys.keys = [ "" ] - ''; - - # Zerotier needs one controller to accept new nodes. Once accepted - # the controller can be offline and routing still works. - clan.core.networking.zerotier.controller.enable = true; + imports = [ ./machines/jon/configuration.nix ]; }; # "sara" will be the hostname of the machine sara = { - imports = [ - ./modules/shared.nix - ./modules/disko.nix - ./machines/sara/configuration.nix - ]; - - nixpkgs.hostPlatform = system; - - # Set this for clan commands use ssh i.e. `clan machines update` - # If you change the hostname, you need to update this line to root@ - # This only works however if you have avahi running on your admin machine else use IP - clan.core.networking.targetHost = pkgs.lib.mkDefault "root@sara"; - - # You can get your disk id by running the following command on the installer: - # Replace with the IP of the installer printed on the screen or by running the `ip addr` command. - # ssh root@ lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT - disko.devices.disk.main.device = "/dev/disk/by-id/__CHANGE_ME__"; - - # IMPORTANT! Add your SSH key here - # e.g. > cat ~/.ssh/id_ed25519.pub - users.users.root.openssh.authorizedKeys.keys = throw '' - Don't forget to add your SSH key here! - users.users.root.openssh.authorizedKeys.keys = [ "" ] - ''; - - /* - After jon is deployed, uncomment the following line - This will allow sara to share the VPN overlay network with jon - The networkId is generated by the first deployment of jon - */ - # clan.core.networking.zerotier.networkId = builtins.readFile ../jon/facts/zerotier-network-id; + imports = [ ./machines/sara/configuration.nix ]; }; }; }; @@ -102,8 +32,19 @@ # all machines managed by Clan inherit (clan) nixosConfigurations clanInternals; # add the Clan cli tool to the dev shell - devShells.${system}.default = pkgs.mkShell { - packages = [ clan-core.packages.${system}.clan-cli ]; - }; + # use the "nix develop" command to enter the dev shell + devShells = + clan-core.inputs.nixpkgs.lib.genAttrs + [ + "x86_64-linux" + "aarch64-linux" + "aarch64-darwin" + "x86_64-darwin" + ] + (system: { + default = clan-core.inputs.nixpkgs.legacyPackages.${system}.mkShell { + packages = [ clan-core.packages.${system}.clan-cli ]; + }; + }); }; } diff --git a/templates/new-clan/machines/jon/configuration.nix b/templates/new-clan/machines/jon/configuration.nix index 9fa9a41cd..bcfc7914b 100644 --- a/templates/new-clan/machines/jon/configuration.nix +++ b/templates/new-clan/machines/jon/configuration.nix @@ -1,38 +1,34 @@ -{ config, ... }: -let - username = config.networking.hostName; -in { - imports = [ ./hardware-configuration.nix ]; + imports = [ + ./hardware-configuration.nix + # contains your disk format and partitioning configuration. + ../../modules/disko.nix + # this file is shared among all machines + ../../modules/shared.nix + # enables GNOME desktop (optional) + ../../modules/gnome.nix + ]; - # Locale service discovery and mDNS - services.avahi.enable = true; + # This is your user login name. + users.users.user.name = ""; - services.xserver.enable = true; - services.xserver.desktopManager.gnome.enable = true; - services.xserver.displayManager.gdm.enable = true; - # Disable the default gnome apps to speed up deployment - services.gnome.core-utilities.enable = false; + # Set this for clan commands use ssh i.e. `clan machines update` + # If you change the hostname, you need to update this line to root@ + # This only works however if you have avahi running on your admin machine else use IP + clan.core.networking.targetHost = "root@"; - # Enable automatic login for the user. - services.displayManager.autoLogin = { - enable = true; - user = username; - }; + # You can get your disk id by running the following command on the installer: + # Replace with the IP of the installer printed on the screen or by running the `ip addr` command. + # ssh root@ lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT + disko.devices.disk.main.device = "/dev/disk/by-id/__CHANGE_ME__"; - users.users.${username} = { - initialPassword = username; - isNormalUser = true; - extraGroups = [ - "wheel" - "networkmanager" - "video" - "audio" - "input" - "dialout" - "disk" - ]; - uid = 1000; - openssh.authorizedKeys.keys = config.users.users.root.openssh.authorizedKeys.keys; - }; + # IMPORTANT! Add your SSH key here + # e.g. > cat ~/.ssh/id_ed25519.pub + users.users.root.openssh.authorizedKeys.keys = ['' + __YOUR_SSH_KEY__ + '']; + + # Zerotier needs one controller to accept new nodes. Once accepted + # the controller can be offline and routing still works. + clan.core.networking.zerotier.controller.enable = true; } diff --git a/templates/new-clan/machines/sara/configuration.nix b/templates/new-clan/machines/sara/configuration.nix index df02f6cad..2e8e9d9fb 100644 --- a/templates/new-clan/machines/sara/configuration.nix +++ b/templates/new-clan/machines/sara/configuration.nix @@ -1,39 +1,33 @@ -{ config, ... }: - -let - username = config.networking.hostName; -in { - imports = [ ./hardware-configuration.nix ]; + imports = [ + ./hardware-configuration.nix + ../../modules/disko.nix + ../../modules/shared.nix + # enables GNOME desktop (optional) + ../../modules/gnome.nix + ]; + # Put your username here for login + users.users.user.name = ""; - # Locale service discovery and mDNS - services.avahi.enable = true; + # Set this for clan commands use ssh i.e. `clan machines update` + # If you change the hostname, you need to update this line to root@ + # This only works however if you have avahi running on your admin machine else use IP + clan.core.networking.targetHost = "root@"; - services.xserver.enable = true; - services.xserver.desktopManager.gnome.enable = true; - services.xserver.displayManager.gdm.enable = true; - # Disable the default gnome apps to speed up deployment - services.gnome.core-utilities.enable = false; + # You can get your disk id by running the following command on the installer: + # Replace with the IP of the installer printed on the screen or by running the `ip addr` command. + # ssh root@ lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT + disko.devices.disk.main.device = "/dev/disk/by-id/__CHANGE_ME__"; - # Enable automatic login for the user. - services.displayManager.autoLogin = { - enable = true; - user = username; - }; - - users.users.${username} = { - initialPassword = username; - isNormalUser = true; - extraGroups = [ - "wheel" - "networkmanager" - "video" - "audio" - "input" - "dialout" - "disk" - ]; - uid = 1000; - openssh.authorizedKeys.keys = config.users.users.root.openssh.authorizedKeys.keys; - }; + # IMPORTANT! Add your SSH key here + # e.g. > cat ~/.ssh/id_ed25519.pub + users.users.root.openssh.authorizedKeys.keys = ['' + __YOUR_SSH_KEY__ + '']; + /* + After jon is deployed, uncomment the following line + This will allow sara to share the VPN overlay network with jon + The networkId is generated by the first deployment of jon + */ + # clan.core.networking.zerotier.networkId = builtins.readFile ../jon/facts/zerotier-network-id; } diff --git a/templates/new-clan/modules/disko.nix b/templates/new-clan/modules/disko.nix index 7a7509584..a1802a33a 100644 --- a/templates/new-clan/modules/disko.nix +++ b/templates/new-clan/modules/disko.nix @@ -1,5 +1,7 @@ { lib, ... }: { + # TO NOT EDIT THIS FILE AFTER INSTALLATION of a machine + # Otherwise your system might not boot because of missing partitions / filesystems boot.loader.grub.efiSupport = lib.mkDefault true; boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true; disko.devices = { @@ -23,6 +25,7 @@ type = "filesystem"; format = "vfat"; mountpoint = "/boot"; + mountOptions = [ "nofail" ]; }; }; root = { @@ -30,6 +33,8 @@ content = { type = "filesystem"; format = "ext4"; + # format = "btrfs"; + # format = "bcachefs"; mountpoint = "/"; }; }; diff --git a/templates/new-clan/modules/gnome.nix b/templates/new-clan/modules/gnome.nix new file mode 100644 index 000000000..bcbc5a148 --- /dev/null +++ b/templates/new-clan/modules/gnome.nix @@ -0,0 +1,5 @@ +{ + services.xserver.enable = true; + services.xserver.desktopManager.gnome.enable = true; + services.xserver.displayManager.gdm.enable = true; +} diff --git a/templates/new-clan/modules/shared.nix b/templates/new-clan/modules/shared.nix index bcd3118ec..87aead540 100644 --- a/templates/new-clan/modules/shared.nix +++ b/templates/new-clan/modules/shared.nix @@ -1,7 +1,28 @@ -{ clan-core, ... }: +{ config, clan-core, ... }: { imports = [ + # Enables the OpenSSH server for remote access clan-core.clanModules.sshd + # Set a root password clan-core.clanModules.root-password + clan-core.clanModules.user-password ]; + + # Locale service discovery and mDNS + services.avahi.enable = true; + + # generate a random password for our user below + # can be read using `clan secrets get -user-password` command + clan.user-password.user = "user"; + users.users.user = { + isNormalUser = true; + extraGroups = [ + "wheel" + "networkmanager" + "video" + "input" + ]; + uid = 1000; + openssh.authorizedKeys.keys = config.users.users.root.openssh.authorizedKeys.keys; + }; } From bba39c5c7de705f9c39778faa0c751372fdf12b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Mon, 22 Jul 2024 08:21:43 +0200 Subject: [PATCH 09/19] hw-configure: use hostname specified in the nixos configuration --- docs/site/getting-started/configure.md | 6 ++-- pkgs/clan-cli/clan_cli/machines/hardware.py | 31 +++++++++++++++------ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/docs/site/getting-started/configure.md b/docs/site/getting-started/configure.md index 8c91e987f..1341d8dde 100644 --- a/docs/site/getting-started/configure.md +++ b/docs/site/getting-started/configure.md @@ -144,17 +144,17 @@ These steps will allow you to update your machine later. Generate the `hardware-configuration.nix` file for your machine by executing the following command: ```bash -clan machines hw-generate [MACHINE_NAME] [HOSTNAME] +clan machines hw-generate [MACHINE_NAME] ``` replace `[MACHINE_NAME]` with the name of the machine i.e. `jon` and `[HOSTNAME]` with the `ip_adress` or `hostname` of the machine within the network. i.e. `` !!! Example ```bash - clan machines hw-generate jon + clan machines hw-generate jon ``` - This command connects to `` as `root`, runs `nixos-generate-config` to detect hardware configurations (excluding filesystems), and writes them to `machines/jon/hardware-configuration.nix`. + This command connects to the ip configured in the previous step, runs `nixos-generate-config` to detect hardware configurations (excluding filesystems), and writes them to `machines/jon/hardware-configuration.nix`. ### Step 3: Custom Disk Formatting diff --git a/pkgs/clan-cli/clan_cli/machines/hardware.py b/pkgs/clan-cli/clan_cli/machines/hardware.py index 23dec3016..4d9fe3393 100644 --- a/pkgs/clan-cli/clan_cli/machines/hardware.py +++ b/pkgs/clan-cli/clan_cli/machines/hardware.py @@ -5,10 +5,12 @@ import logging from pathlib import Path from clan_cli.api import API +from clan_cli.clan_uri import FlakeId from clan_cli.errors import ClanError from ..cmd import run, run_no_stdout from ..completions import add_dynamic_completer, complete_machines +from ..machines.machines import Machine from ..nix import nix_config, nix_eval, nix_shell from .types import machine_name_type @@ -88,9 +90,9 @@ def show_machine_hardware_platform( @API.register def generate_machine_hardware_info( - clan_dir: str | Path, + clan_dir: FlakeId, machine_name: str, - hostname: str, + hostname: str | None = None, password: str | None = None, force: bool | None = False, ) -> HardwareInfo: @@ -98,6 +100,13 @@ def generate_machine_hardware_info( Generate hardware information for a machine and place the resulting *.nix file in the machine's directory. """ + + machine = Machine(machine_name, flake=clan_dir) + if hostname is not None: + machine.target_host_address = hostname + + host = machine.target_host + target_host = f"{host.user or 'root'}@{host.host}" cmd = nix_shell( [ "nixpkgs#openssh", @@ -109,10 +118,14 @@ def generate_machine_hardware_info( *(["sshpass", "-p", f"{password}"] if password else []), "ssh", # Disable strict host key checking - "-o StrictHostKeyChecking=no", + "-o", + "StrictHostKeyChecking=no", # Disable known hosts file - "-o UserKnownHostsFile=/dev/null", - f"root@{hostname}", + "-o", + "UserKnownHostsFile=/dev/null", + "-p", + str(machine.target_host.port), + target_host, "nixos-generate-config", # Filesystems are managed by disko "--no-filesystems", @@ -147,9 +160,8 @@ def generate_machine_hardware_info( def hw_generate_command(args: argparse.Namespace) -> None: - flake_path = args.flake.path hw_info = generate_machine_hardware_info( - flake_path, args.machine, args.hostname, args.password, args.force + args.flake, args.machine, args.hostname, args.password, args.force ) print("----") print("Successfully generated hardware information.") @@ -166,9 +178,10 @@ def register_hw_generate(parser: argparse.ArgumentParser) -> None: type=machine_name_type, ) machine_parser = parser.add_argument( - "hostname", - help="hostname of the machine", + "target_host", type=str, + nargs="?", + help="ssh address to install to in the form of user@host:2222", ) machine_parser = parser.add_argument( "--password", From 8197bced46ecbe4bfa85a2e3aff2255cff3341a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Mon, 22 Jul 2024 08:30:24 +0200 Subject: [PATCH 10/19] add option to not create a git in flakes create --- pkgs/clan-cli/clan_cli/clan/create.py | 55 +++++++++++++++------------ 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/pkgs/clan-cli/clan_cli/clan/create.py b/pkgs/clan-cli/clan_cli/clan/create.py index 154854c8b..6bde7a348 100644 --- a/pkgs/clan-cli/clan_cli/clan/create.py +++ b/pkgs/clan-cli/clan_cli/clan/create.py @@ -17,11 +17,11 @@ from ..nix import nix_command, nix_shell @dataclass class CreateClanResponse: flake_init: CmdOut - git_init: CmdOut | None - git_add: CmdOut - git_config_username: CmdOut | None - git_config_email: CmdOut | None flake_update: CmdOut + git_init: CmdOut | None = None + git_add: CmdOut | None = None + git_config_username: CmdOut | None = None + git_config_email: CmdOut | None = None @dataclass @@ -33,6 +33,7 @@ class CreateOptions: # URL to the template to use. Defaults to the "minimal" template template: str = "minimal" setup_json_inventory: bool = True + setup_git: bool = True def git_command(directory: Path, *args: str) -> list[str]: @@ -66,24 +67,31 @@ def create_clan(options: CreateOptions) -> CreateClanResponse: ] ) flake_init = run(command, cwd=directory) + flake_update = run( + nix_shell(["nixpkgs#nix"], ["nix", "flake", "update"]), cwd=directory + ) - git_init = None - if not directory.joinpath(".git").exists(): - git_init = run(git_command(directory, "init")) - git_add = run(git_command(directory, "add", ".")) + response = CreateClanResponse( + flake_init=flake_init, + flake_update=flake_update, + ) + if not options.setup_git: + return response + + response.git_init = run(git_command(directory, "init")) + response.git_add = run(git_command(directory, "add", ".")) # check if username is set has_username = run(git_command(directory, "config", "user.name"), check=False) - git_config_username = None + response.git_config_username = None if has_username.returncode != 0: - git_config_username = run( + response.git_config_username = run( git_command(directory, "config", "user.name", "clan-tool") ) has_username = run(git_command(directory, "config", "user.email"), check=False) - git_config_email = None if has_username.returncode != 0: - git_config_email = run( + response.git_config_email = run( git_command(directory, "config", "user.email", "clan@example.com") ) @@ -95,18 +103,6 @@ def create_clan(options: CreateOptions) -> CreateClanResponse: # Persist creates a commit message for each change save_inventory(inventory, directory, "Init inventory") - flake_update = run( - nix_shell(["nixpkgs#nix"], ["nix", "flake", "update"]), cwd=directory - ) - - response = CreateClanResponse( - flake_init=flake_init, - git_init=git_init, - git_add=git_add, - git_config_username=git_config_username, - git_config_email=git_config_email, - flake_update=flake_update, - ) return response @@ -127,6 +123,12 @@ def register_create_parser(parser: argparse.ArgumentParser) -> None: action=AppendOptionAction, default=[], ) + parser.add_argument( + "--no-git", + help="Do not setup git", + action="store_true", + default=False, + ) parser.add_argument( "path", type=Path, help="Path to the clan directory", default=Path(".") @@ -135,7 +137,10 @@ def register_create_parser(parser: argparse.ArgumentParser) -> None: def create_flake_command(args: argparse.Namespace) -> None: create_clan( CreateOptions( - directory=args.path, template=args.template, setup_json_inventory=False + directory=args.path, + template=args.template, + setup_json_inventory=False, + setup_git=not args.no_git, ) ) From f9dc440b61d93df9291a3a7ce470959e42566e43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Mon, 22 Jul 2024 16:04:57 +0200 Subject: [PATCH 11/19] add .envrc --- templates/new-clan/.envrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 templates/new-clan/.envrc diff --git a/templates/new-clan/.envrc b/templates/new-clan/.envrc new file mode 100644 index 000000000..3550a30f2 --- /dev/null +++ b/templates/new-clan/.envrc @@ -0,0 +1 @@ +use flake From 7dc3a8ff2b544be3691467d6fce749ebf687ba39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Mon, 22 Jul 2024 16:09:15 +0200 Subject: [PATCH 12/19] user-password: improve prompt message --- clanModules/user-password/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clanModules/user-password/default.nix b/clanModules/user-password/default.nix index 14b1f0177..4a1bc7068 100644 --- a/clanModules/user-password/default.nix +++ b/clanModules/user-password/default.nix @@ -32,7 +32,7 @@ secret.user-password = { }; secret.user-password-hash = { }; generator.prompt = ( - lib.mkIf config.clan.user-password.prompt "Set the password for your $user: ${config.clan.user-password.user}. + lib.mkIf config.clan.user-password.prompt "Set the password for your user '${config.clan.user-password.user}'. You can autogenerate a password, if you leave this prompt blank." ); generator.path = with pkgs; [ From 21346f84acf484833b7bc90d2b63e5d9bf31424d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Mon, 22 Jul 2024 17:21:57 +0200 Subject: [PATCH 13/19] password-store: write secrets with read-only permission Otherwise we are installing secrets with the wrong permissions on the first install --- pkgs/clan-cli/clan_cli/vars/secret_modules/password_store.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkgs/clan-cli/clan_cli/vars/secret_modules/password_store.py b/pkgs/clan-cli/clan_cli/vars/secret_modules/password_store.py index c804456f3..4b748da84 100644 --- a/pkgs/clan-cli/clan_cli/vars/secret_modules/password_store.py +++ b/pkgs/clan-cli/clan_cli/vars/secret_modules/password_store.py @@ -113,5 +113,7 @@ class SecretStore(SecretStoreBase): else: # TODO: drop old format soon secret_name = secret - (output_dir / secret_name).write_bytes(self.get(service, secret_name)) + with (output_dir / secret_name).open("wb") as f: + f.chmod(0o600) + f.write(self.get(service, secret_name)) (output_dir / ".pass_info").write_bytes(self.generate_hash()) From 1907200d5894a32edb16fe65d5ddd4766ff90af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 24 Jul 2024 22:03:12 +0200 Subject: [PATCH 14/19] satisfy treefmt --- templates/flake.nix | 22 ++++++++++++---------- templates/new-clan/.envrc | 1 + 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/templates/flake.nix b/templates/flake.nix index 713701f06..438d8ab54 100644 --- a/templates/flake.nix +++ b/templates/flake.nix @@ -1,14 +1,16 @@ { - outputs = { ... }: { - templates = { - default = { - description = "Initialize a new clan flake"; - path = ./new-clan; - }; - minimal = { - description = "for clans managed via (G)UI"; - path = ./minimal; + outputs = + { ... }: + { + templates = { + default = { + description = "Initialize a new clan flake"; + path = ./new-clan; + }; + minimal = { + description = "for clans managed via (G)UI"; + path = ./minimal; + }; }; }; - }; } diff --git a/templates/new-clan/.envrc b/templates/new-clan/.envrc index 3550a30f2..0f94eedeb 100644 --- a/templates/new-clan/.envrc +++ b/templates/new-clan/.envrc @@ -1 +1,2 @@ +# shellcheck shell=bash use flake From 3cdf9b513d6df074aee094aa1d4b2f94ec8692c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 21 Aug 2024 13:53:49 +0200 Subject: [PATCH 15/19] ui: fix type errors --- pkgs/webview-ui/app/src/routes/clan/clanDetails.tsx | 10 +++++----- .../webview-ui/app/src/routes/machines/[name]/view.tsx | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkgs/webview-ui/app/src/routes/clan/clanDetails.tsx b/pkgs/webview-ui/app/src/routes/clan/clanDetails.tsx index 7daad7ccf..7c857591e 100644 --- a/pkgs/webview-ui/app/src/routes/clan/clanDetails.tsx +++ b/pkgs/webview-ui/app/src/routes/clan/clanDetails.tsx @@ -10,7 +10,7 @@ import toast from "solid-toast"; import { setActiveURI } from "@/src/App"; type CreateForm = Meta & { - template_url: string; + template: string; }; export const ClanForm = () => { @@ -18,12 +18,12 @@ export const ClanForm = () => { initialValues: { name: "", description: "", - template_url: "git+https://git.clan.lol/clan/clan-core#templates.minimal", + template: "minimal", }, }); const handleSubmit: SubmitHandler = async (values, event) => { - const { template_url, ...meta } = values; + const { template, ...meta } = values; const response = await callApi("open_file", { file_request: { mode: "save" }, @@ -44,7 +44,7 @@ export const ClanForm = () => { await callApi("create_clan", { options: { directory: target_dir[0], - template_url, + template, initial: { meta, services: {}, @@ -146,7 +146,7 @@ export const ClanForm = () => { )} - + {(field, props) => (
diff --git a/pkgs/webview-ui/app/src/routes/machines/[name]/view.tsx b/pkgs/webview-ui/app/src/routes/machines/[name]/view.tsx index 320a2756f..3e0930ef0 100644 --- a/pkgs/webview-ui/app/src/routes/machines/[name]/view.tsx +++ b/pkgs/webview-ui/app/src/routes/machines/[name]/view.tsx @@ -161,7 +161,7 @@ export const MachineDetails = () => { "generate_machine_hardware_info", { machine_name: params.id, - clan_dir: curr_uri, + clan_dir: { loc: curr_uri }, hostname: query.data.machine.deploy.targetHost, }, ); From 2fbd0b6796c2e4ce2a7152b33d1a6bab84abbe76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 21 Aug 2024 13:59:38 +0200 Subject: [PATCH 16/19] test_create_flake: use template name instead of url --- pkgs/clan-cli/tests/test_create_flake.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pkgs/clan-cli/tests/test_create_flake.py b/pkgs/clan-cli/tests/test_create_flake.py index 75f7bdd80..ee1d92c5a 100644 --- a/pkgs/clan-cli/tests/test_create_flake.py +++ b/pkgs/clan-cli/tests/test_create_flake.py @@ -17,8 +17,7 @@ def test_create_flake( ) -> None: flake_dir = temporary_home / "test-flake" - url = f"{clan_core}#default" - cli.run(["flakes", "create", str(flake_dir), f"--url={url}"]) + cli.run(["flakes", "create", str(flake_dir), "--template=default"]) assert (flake_dir / ".clan-flake").exists() # Replace the inputs.clan.url in the template flake.nix @@ -63,8 +62,7 @@ def test_ui_template( capture_output: CaptureOutput, ) -> None: flake_dir = temporary_home / "test-flake" - url = f"{clan_core}#minimal" - cli.run(["flakes", "create", str(flake_dir), f"--url={url}"]) + cli.run(["flakes", "create", str(flake_dir), "--template=minimal"]) # Replace the inputs.clan.url in the template flake.nix substitute( From d6b70f8fd784b8af9072aad704437dd915ec1915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 21 Aug 2024 15:00:04 +0200 Subject: [PATCH 17/19] remove ipdb again ipdb doesn't work with capsys and breakpoint --- pkgs/clan-app/shell.nix | 3 --- pkgs/clan-cli/shell.nix | 3 --- pkgs/clan-vm-manager/shell.nix | 3 --- 3 files changed, 9 deletions(-) diff --git a/pkgs/clan-app/shell.nix b/pkgs/clan-app/shell.nix index 74878aac6..36e034d7f 100644 --- a/pkgs/clan-app/shell.nix +++ b/pkgs/clan-app/shell.nix @@ -21,7 +21,6 @@ let ++ (with python3.pkgs; [ rope mypy - ipdb setuptools wheel pip @@ -48,8 +47,6 @@ mkShell { desktop-file-utils # verify desktop files ]); - PYTHONBREAKPOINT = "ipdb.set_trace"; - shellHook = '' export GIT_ROOT=$(git rev-parse --show-toplevel) export PKG_ROOT=$GIT_ROOT/pkgs/clan-app diff --git a/pkgs/clan-cli/shell.nix b/pkgs/clan-cli/shell.nix index 168a59649..e52bd8a5b 100644 --- a/pkgs/clan-cli/shell.nix +++ b/pkgs/clan-cli/shell.nix @@ -15,7 +15,6 @@ let rope setuptools wheel - ipdb pip ]); in @@ -27,8 +26,6 @@ mkShell { inputsFrom = [ self'.devShells.default ]; - PYTHONBREAKPOINT = "ipdb.set_trace"; - CLAN_STATIC_PROGRAMS = lib.concatStringsSep ":" ( lib.attrNames clan-cli-full.passthru.runtimeDependenciesAsSet ); diff --git a/pkgs/clan-vm-manager/shell.nix b/pkgs/clan-vm-manager/shell.nix index 1b00bd998..6b6c3c658 100644 --- a/pkgs/clan-vm-manager/shell.nix +++ b/pkgs/clan-vm-manager/shell.nix @@ -19,7 +19,6 @@ let ++ (with python3.pkgs; [ rope mypy - ipdb setuptools wheel pip @@ -41,8 +40,6 @@ mkShell { desktop-file-utils # verify desktop files ]); - PYTHONBREAKPOINT = "ipdb.set_trace"; - shellHook = '' export GIT_ROOT=$(git rev-parse --show-toplevel) export PKG_ROOT=$GIT_ROOT/pkgs/clan-vm-manager From d20287f9e34d838f47cad1466d57740f09fe92fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 21 Aug 2024 15:01:14 +0200 Subject: [PATCH 18/19] capture_output: only start capturing when we enter the with statement --- pkgs/clan-cli/tests/stdout.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkgs/clan-cli/tests/stdout.py b/pkgs/clan-cli/tests/stdout.py index 5106498db..aeda31697 100644 --- a/pkgs/clan-cli/tests/stdout.py +++ b/pkgs/clan-cli/tests/stdout.py @@ -7,8 +7,11 @@ from pytest import CaptureFixture class CaptureOutput: def __init__(self, capsys: CaptureFixture) -> None: self.capsys = capsys + self.capsys_disabled = capsys.disabled() + self.capsys_disabled.__enter__() def __enter__(self) -> "CaptureOutput": + self.capsys_disabled.__exit__(None, None, None) self.capsys.readouterr() return self @@ -17,6 +20,11 @@ class CaptureOutput: self.out = res.out self.err = res.err + # Disable capsys again + self.capsys_disabled = self.capsys.disabled() + self.capsys_disabled.__enter__() + return False + @pytest.fixture def capture_output(capsys: CaptureFixture) -> CaptureOutput: From 81c941aba69df79d798265acfc6a3da9387d1b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 21 Aug 2024 15:09:39 +0200 Subject: [PATCH 19/19] fix template path for relative clan create template --- 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 9249eab21..0c9eadb49 100644 --- a/pkgs/clan-cli/clan_cli/dirs.py +++ b/pkgs/clan-cli/clan_cli/dirs.py @@ -44,7 +44,7 @@ def find_toplevel(top_level_files: list[str]) -> Path | None: def clan_templates() -> Path: - template_path = module_root().parent.parent / "templates" + template_path = module_root().parent.parent.parent / "templates" if template_path.exists(): return template_path else: