Merge pull request 'Better install instructions for macos' (#2550) from arm64 into main

This commit is contained in:
clan-bot
2024-12-04 16:32:02 +00:00
20 changed files with 131 additions and 106 deletions

View File

@@ -83,8 +83,10 @@ def handle_io(
return b""
# Extra information passed to the logger
stdout_extra = {"command_prefix": prefix}
stderr_extra = {"command_prefix": prefix}
stdout_extra = {}
stderr_extra = {}
if prefix:
stdout_extra["command_prefix"] = stderr_extra["command_prefix"] = prefix
if msg_color and msg_color.stderr:
stdout_extra["color"] = msg_color.stderr.value
if msg_color and msg_color.stdout:
@@ -267,9 +269,6 @@ def run(
if options.cwd is None:
options.cwd = Path.cwd()
if options.prefix is None:
options.prefix = "$"
if options.input:
if any(not ch.isprintable() for ch in options.input.decode("ascii", "replace")):
filtered_input = "<<binary_blob>>"

View File

@@ -25,12 +25,9 @@ class PrefixFormatter(logging.Formatter):
print errors in red and warnings in yellow
"""
def __init__(
self, trace_prints: bool = False, default_prefix: str | None = None
) -> None:
def __init__(self, trace_prints: bool = False) -> None:
super().__init__()
self.default_prefix = default_prefix
self.trace_prints = trace_prints
self.hostnames: list[str] = []
self.hostname_color_offset = 0
@@ -51,7 +48,7 @@ class PrefixFormatter(logging.Formatter):
msg_color = AnsiColor.DEFAULT.value
# If extra["command_prefix"] is set, use that as the logging prefix.
command_prefix = getattr(record, "command_prefix", self.default_prefix)
command_prefix = getattr(record, "command_prefix", None)
# If color is disabled, don't use color.
if DISABLE_COLOR:
@@ -154,7 +151,6 @@ def print_trace(msg: str, logger: logging.Logger, prefix: str | None) -> None:
def setup_logging(
level: Any,
root_log_name: str = __name__.split(".")[0],
default_prefix: str = "clan",
) -> None:
# Get the root logger and set its level
main_logger = logging.getLogger(root_log_name)
@@ -166,5 +162,5 @@ def setup_logging(
# Create and add your custom handler
default_handler.setLevel(level)
trace_prints = bool(int(os.environ.get("TRACE_PRINT", "0")))
default_handler.setFormatter(PrefixFormatter(trace_prints, default_prefix))
default_handler.setFormatter(PrefixFormatter(trace_prints))
main_logger.addHandler(default_handler)

View File

@@ -23,17 +23,11 @@ from .list import list_possible_keymaps, list_possible_languages
log = logging.getLogger(__name__)
@dataclass
class WifiConfig:
ssid: str
@dataclass
class SystemConfig:
language: str | None = field(default=None)
keymap: str | None = field(default=None)
ssh_keys_path: list[str] | None = field(default=None)
wifi_settings: list[WifiConfig] | None = field(default=None)
@dataclass
@@ -65,12 +59,6 @@ def flash_machine(
)
generate_facts([machine])
if system_config.wifi_settings:
wifi_settings: dict[str, dict[str, str]] = {}
for wifi in system_config.wifi_settings:
wifi_settings[wifi.ssid] = {}
system_config_nix["clan"] = {"iwd": {"networks": wifi_settings}}
if system_config.language:
if system_config.language not in list_possible_languages():
msg = (

View File

@@ -10,7 +10,7 @@ from clan_cli.clan_uri import FlakeId
from clan_cli.completions import add_dynamic_completer, complete_machines
from clan_cli.machines.machines import Machine
from .flash import Disk, SystemConfig, WifiConfig, flash_machine
from .flash import Disk, SystemConfig, flash_machine
log = logging.getLogger(__name__)
@@ -69,15 +69,11 @@ def flash_command(args: argparse.Namespace) -> None:
language=args.language,
keymap=args.keymap,
ssh_keys_path=args.ssh_pubkey,
wifi_settings=None,
),
write_efi_boot_entries=args.write_efi_boot_entries,
nix_options=args.option,
)
if args.wifi:
opts.system_config.wifi_settings = [WifiConfig(ssid=ssid) for ssid in args.wifi]
machine = Machine(opts.machine, flake=opts.flake)
if opts.confirm and not opts.dry_run:
disk_str = ", ".join(f"{disk.name}={disk.device}" for disk in opts.disks)
@@ -126,13 +122,6 @@ def register_flash_write_parser(parser: argparse.ArgumentParser) -> None:
Mount is useful for updating an existing system without losing data.
"""
)
parser.add_argument(
"--wifi",
type=str,
action="append",
help="wifi ssid to connect to",
default=[],
)
parser.add_argument(
"--mode",
type=str,

View File

@@ -1,4 +1,5 @@
# DON NOT EDIT THIS FILE MANUALLY. IT IS GENERATED.
# DO NOT EDIT THIS FILE MANUALLY. IT IS GENERATED.
# This file was generated by running `pkgs/clan-cli/clan_cli/inventory/update.sh`
#
# ruff: noqa: N815
# ruff: noqa: N806

View File

@@ -1,4 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
jsonSchema=$(nix build .#schemas.inventory-schema-abstract --print-out-paths)/schema.json
nix run .#classgen "$jsonSchema" "$PKG_ROOT/clan_cli/inventory/classes.py" -- --stop-at "Service"
SCRIPT_DIR=$(dirname "$0")
cd "$SCRIPT_DIR"
nix run .#classgen -- "$jsonSchema" "../../../clan-cli/clan_cli/inventory/classes.py" --stop-at "Service"

View File

@@ -7,12 +7,12 @@ from pathlib import Path
from tempfile import TemporaryDirectory
from clan_cli.api import API
from clan_cli.clan.create import git_command
from clan_cli.clan_uri import FlakeId
from clan_cli.cmd import Log, RunOpts, run
from clan_cli.completions import add_dynamic_completer, complete_tags
from clan_cli.dirs import TemplateType, clan_templates, get_clan_flake_toplevel_or_env
from clan_cli.errors import ClanError
from clan_cli.git import commit_file
from clan_cli.inventory import Machine as InventoryMachine
from clan_cli.inventory import (
MachineDeploy,
@@ -109,10 +109,8 @@ def create_machine(opts: CreateOptions) -> None:
src = tmpdirp / "machines" / opts.template_name
if (
not (src / "configuration.nix").exists()
and not (src / "inventory.json").exists()
):
has_inventory = (dst / "inventory.json").exists()
if not (src / "configuration.nix").exists() and not has_inventory:
msg = f"Template machine '{opts.template_name}' does not contain a configuration.nix or inventory.json"
description = (
"Template machine must contain a configuration.nix or inventory.json"
@@ -126,12 +124,16 @@ def create_machine(opts: CreateOptions) -> None:
shutil.copytree(src, dst, ignore_dangling_symlinks=True, copy_function=log_copy)
run(git_command(clan_dir, "add", f"machines/{machine_name}"), RunOpts(cwd=clan_dir))
commit_file(
clan_dir / "machines" / machine_name,
repo_dir=clan_dir,
commit_message=f"Add machine {machine_name}",
)
inventory = load_inventory_json(clan_dir)
# Merge the inventory from the template
if (dst / "inventory.json").exists():
if has_inventory:
template_inventory = load_inventory_json(dst)
merge_template_inventory(inventory, template_inventory, machine_name)
@@ -141,6 +143,13 @@ def create_machine(opts: CreateOptions) -> None:
new_machine = InventoryMachine(
name=machine_name, deploy=deploy, tags=opts.machine.tags
)
if (
not has_inventory
and len(opts.machine.tags) == 0
and new_machine.deploy.targetHost is None
):
# no need to update inventory if there are no tags or target host
return
inventory.machines.update({new_machine.name: dataclass_to_dict(new_machine)})
set_inventory(inventory, clan_dir, "Imported machine from template")

View File

@@ -1,4 +1,5 @@
import json
import logging
import os
import tempfile
from pathlib import Path
@@ -8,6 +9,8 @@ from clan_cli.cmd import run, run_no_stdout
from clan_cli.dirs import nixpkgs_flake, nixpkgs_source
from clan_cli.errors import ClanError
log = logging.getLogger(__name__)
def nix_command(flags: list[str]) -> list[str]:
args = ["nix", "--extra-experimental-features", "nix-command flakes", *flags]
@@ -23,38 +26,22 @@ def nix_flake_show(flake_url: str | Path) -> list[str]:
"flake",
"show",
"--json",
"--show-trace",
f"{flake_url}",
*(["--show-trace"] if log.isEnabledFor(logging.DEBUG) else []),
str(flake_url),
]
)
def nix_build(flags: list[str], gcroot: Path | None = None) -> list[str]:
if gcroot is not None:
return (
nix_command(
[
"build",
"--out-link",
str(gcroot),
"--print-out-paths",
"--show-trace",
"--print-build-logs",
]
)
+ flags
)
return (
nix_command(
[
"build",
"--no-link",
"--print-out-paths",
"--show-trace",
"--print-build-logs",
]
)
+ flags
return nix_command(
[
"build",
"--print-out-paths",
"--print-build-logs",
*(["--show-trace"] if log.isEnabledFor(logging.DEBUG) else []),
*(["--out-root", str(gcroot)] if gcroot is not None else []),
*flags,
]
)
@@ -77,7 +64,7 @@ def nix_eval(flags: list[str]) -> list[str]:
default_flags = nix_command(
[
"eval",
"--show-trace",
*(["--show-trace"] if log.isEnabledFor(logging.DEBUG) else []),
"--json",
"--print-build-logs",
]

View File

@@ -31,7 +31,7 @@ clan_cli = [
testpaths = "tests"
faulthandler_timeout = 60
log_level = "DEBUG"
log_format = "%(levelname)s: %(message)s\n %(pathname)s:%(lineno)d::%(funcName)s"
log_format = "%(message)s"
addopts = "--cov . --cov-report term --cov-report html:.reports/html --no-cov-on-fail --durations 5 --color=yes --new-first -W error -n auto" # Add --pdb for debugging
norecursedirs = "tests/helpers"
markers = ["impure", "with_core"]

View File

@@ -20,7 +20,6 @@ struct passwd *getpwnam(const char *name) {
fprintf(stderr, "no LOGIN_SHELL set\n");
exit(1);
}
fprintf(stderr, "SHELL:%s\n", shell);
pw->pw_shell = strdup(shell);
}
return pw;

View File

@@ -15,6 +15,7 @@
vm1 =
{ lib, ... }:
{
nixpkgs.hostPlatform = "x86_64-linux";
clan.core.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
system.stateVersion = lib.version;
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";
@@ -28,6 +29,7 @@
vm2 =
{ lib, ... }:
{
nixpkgs.hostPlatform = "x86_64-linux";
imports = [
clan-core.clanModules.sshd
clan-core.clanModules.root-password

View File

@@ -10,7 +10,15 @@ def test_machine_subcommands(
capture_output: CaptureOutput,
) -> None:
cli.run(
["machines", "create", "--flake", str(test_flake_with_core.path), "machine1"]
[
"machines",
"create",
"--flake",
str(test_flake_with_core.path),
"machine1",
"--tags",
"vm",
]
)
with capture_output as output:

View File

@@ -323,6 +323,7 @@ def test_generate_secret_for_multiple_machines(
sops_setup: SopsSetup,
) -> None:
machine1_config = flake.machines["machine1"]
machine1_config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
machine1_generator = machine1_config["clan"]["core"]["vars"]["generators"][
"my_generator"
]
@@ -332,6 +333,7 @@ def test_generate_secret_for_multiple_machines(
"echo machine1 > $out/my_secret && echo machine1 > $out/my_value"
)
machine2_config = flake.machines["machine2"]
machine2_config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
machine2_generator = machine2_config["clan"]["core"]["vars"]["generators"][
"my_generator"
]
@@ -384,6 +386,7 @@ def test_dependant_generators(
flake: ClanFlake,
) -> None:
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
parent_gen = config["clan"]["core"]["vars"]["generators"]["parent_generator"]
parent_gen["files"]["my_value"]["secret"] = False
parent_gen["script"] = "echo hello > $out/my_value"
@@ -426,6 +429,7 @@ def test_prompt(
input_value: str,
) -> None:
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["files"]["my_value"]["secret"] = False
my_generator["prompts"]["prompt1"]["description"] = "dream2nix"
@@ -521,6 +525,7 @@ def test_depending_on_shared_secret_succeeds(
sops_setup: SopsSetup,
) -> None:
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
shared_generator = config["clan"]["core"]["vars"]["generators"]["shared_generator"]
shared_generator["share"] = True
shared_generator["files"]["my_secret"]["secret"] = True
@@ -550,6 +555,7 @@ def test_prompt_create_file(
Test that the createFile flag in the prompt configuration works as expected
"""
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["prompts"]["prompt1"]["createFile"] = True
my_generator["prompts"]["prompt2"]["createFile"] = False
@@ -580,6 +586,7 @@ def test_api_get_prompts(
from clan_cli.vars.list import get_prompts
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["prompts"]["prompt1"]["type"] = "line"
my_generator["files"]["prompt1"]["secret"] = False
@@ -604,6 +611,7 @@ def test_api_set_prompts(
from clan_cli.vars.list import set_prompts
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["prompts"]["prompt1"]["type"] = "line"
my_generator["files"]["prompt1"]["secret"] = False
@@ -641,6 +649,7 @@ def test_commit_message(
sops_setup: SopsSetup,
) -> None:
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["files"]["my_value"]["secret"] = False
my_generator["script"] = "echo hello > $out/my_value"
@@ -952,6 +961,7 @@ def test_vars_get(
flake: ClanFlake,
) -> None:
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["files"]["my_value"]["secret"] = False
my_generator["script"] = "echo -n hello > $out/my_value"
@@ -979,6 +989,7 @@ def test_invalidation(
flake: ClanFlake,
) -> None:
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["files"]["my_value"]["secret"] = False
my_generator["script"] = "echo -n $RANDOM > $out/my_value"

View File

@@ -61,6 +61,7 @@ def test_vm_persistence(
) -> None:
# set up a clan flake with some systemd services to test persistence
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
# logrotate-checkconf doesn't work in VM because /nix/store is owned by nobody
config["systemd"]["services"]["logrotate-checkconf"]["enable"] = False
config["services"]["getty"]["autologinUser"] = "root"