diff --git a/lib/test/default.nix b/lib/test/default.nix index 43f8e8936..66260142d 100644 --- a/lib/test/default.nix +++ b/lib/test/default.nix @@ -7,6 +7,10 @@ let mkOption removePrefix types + mapAttrsToList + flip + unique + flatten ; in @@ -46,10 +50,18 @@ in testSrc = lib.cleanSource test.config.clan.directory; + inputsForMachine = + machine: + flip mapAttrsToList machine.clan.core.vars.generators (_name: generator: generator.runtimeInputs); + + generatorRuntimeInputs = unique ( + flatten (flip mapAttrsToList test.config.nodes (_machineName: machine: inputsForMachine machine)) + ); + vars-check = pkgs.runCommand "update-vars-check" { - nativeBuildInputs = [ + nativeBuildInputs = generatorRuntimeInputs ++ [ pkgs.nix pkgs.git pkgs.age @@ -57,7 +69,7 @@ in pkgs.bubblewrap ]; closureInfo = pkgs.closureInfo { - rootPaths = [ + rootPaths = generatorRuntimeInputs ++ [ pkgs.bash pkgs.coreutils pkgs.jq.dev @@ -74,7 +86,9 @@ in cp -r ${testSrc} ./src chmod +w -R ./src find ./src/sops ./src/vars | sort > filesBefore - ${update-vars-script} ./src ${testName} --repo-root ${self.packages.${pkgs.system}.clan-core-flake} + ${update-vars-script} ./src ${testName} \ + --repo-root ${self.packages.${pkgs.system}.clan-core-flake} \ + --clean find ./src/sops ./src/vars | sort > filesAfter if ! diff -q filesBefore filesAfter; then echo "The update-vars script changed the files in ${testSrc}." diff --git a/pkgs/clan-cli/clan_cli/vars/generate.py b/pkgs/clan-cli/clan_cli/vars/generate.py index 9c0f71630..1356f42a7 100644 --- a/pkgs/clan-cli/clan_cli/vars/generate.py +++ b/pkgs/clan-cli/clan_cli/vars/generate.py @@ -29,7 +29,7 @@ from .graph import ( minimal_closure, requested_closure, ) -from .prompt import Prompt, ask +from .prompt import Prompt, PromptType, ask from .var import Var log = logging.getLogger(__name__) @@ -295,6 +295,26 @@ def _ask_prompts( return prompt_values +def _fake_prompts( + generator: Generator, +) -> dict[str, str]: + prompt_values: dict[str, str] = {} + for prompt in generator.prompts: + var_id = f"{generator.name}/{prompt.name}" + if prompt.prompt_type == PromptType.HIDDEN: + prompt_values[prompt.name] = "fake_hidden_value" + elif prompt.prompt_type == PromptType.MULTILINE_HIDDEN: + prompt_values[prompt.name] = "fake\nmultiline\nhidden\nvalue" + elif prompt.prompt_type == PromptType.MULTILINE: + prompt_values[prompt.name] = "fake\nmultiline\nvalue" + elif prompt.prompt_type == PromptType.LINE: + prompt_values[prompt.name] = "fake_line_value" + else: + msg = f"Unknown prompt type {prompt.prompt_type} for prompt {var_id} in generator {generator.name}" + raise ClanError(msg) + return prompt_values + + def _get_previous_value( machine: "Machine", generator: Generator, @@ -414,6 +434,7 @@ def generate_vars_for_machine_interactive( generator_name: str | None, regenerate: bool, no_sandbox: bool = False, + fake_prompts: bool = False, ) -> bool: _generator = None if generator_name: @@ -438,7 +459,10 @@ def generate_vars_for_machine_interactive( return False all_prompt_values = {} for generator in generators: - all_prompt_values[generator.name] = _ask_prompts(generator) + if fake_prompts: + all_prompt_values[generator.name] = _fake_prompts(generator) + else: + all_prompt_values[generator.name] = _ask_prompts(generator) return _generate_vars_for_machine( machine, generators, @@ -452,13 +476,18 @@ def generate_vars( generator_name: str | None = None, regenerate: bool = False, no_sandbox: bool = False, + fake_prompts: bool = False, ) -> bool: was_regenerated = False for machine in machines: errors = [] try: was_regenerated |= generate_vars_for_machine_interactive( - machine, generator_name, regenerate, no_sandbox=no_sandbox + machine, + generator_name, + regenerate, + no_sandbox=no_sandbox, + fake_prompts=fake_prompts, ) except Exception as exc: errors += [(machine, exc)] @@ -504,7 +533,11 @@ def generate_command(args: argparse.Namespace) -> None: ] ) has_changed = generate_vars( - machines, args.generator, args.regenerate, no_sandbox=args.no_sandbox + machines, + args.generator, + args.regenerate, + no_sandbox=args.no_sandbox, + fake_prompts=args.fake_prompts, ) if has_changed: args.flake.invalidate_cache() @@ -544,4 +577,11 @@ def register_generate_parser(parser: argparse.ArgumentParser) -> None: default=False, ) + parser.add_argument( + "--fake-prompts", + action="store_true", + help="automatically fill prompt responses for testing (unsafe)", + default=False, + ) + parser.set_defaults(func=generate_command) diff --git a/pkgs/generate-test-vars/generate_test_vars/cli.py b/pkgs/generate-test-vars/generate_test_vars/cli.py index 9e38073e3..61440b261 100755 --- a/pkgs/generate-test-vars/generate_test_vars/cli.py +++ b/pkgs/generate-test-vars/generate_test_vars/cli.py @@ -87,6 +87,7 @@ class Options: repo_root: Path test_dir: Path check_attr: str + clean: bool def parse_args() -> Options: @@ -107,6 +108,13 @@ def parse_args() -> Options: required=False, default=os.environ.get("PRJ_ROOT", find_git_repo_root()), ) + parser.add_argument( + "--clean", + help="wipe vars and sops directories before generating new vars", + action="store_true", + default=False, + ) + parser.add_argument( "test_dir", type=Path, @@ -125,6 +133,7 @@ def parse_args() -> Options: repo_root=args.repo_root, test_dir=args.test_dir, check_attr=args.check_attr, + clean=args.clean, ) @@ -133,8 +142,9 @@ def main() -> None: opts = parse_args() test_dir = opts.test_dir - shutil.rmtree(test_dir / "vars", ignore_errors=True) - shutil.rmtree(test_dir / "sops", ignore_errors=True) + if opts.clean: + shutil.rmtree(test_dir / "vars", ignore_errors=True) + shutil.rmtree(test_dir / "sops", ignore_errors=True) flake = Flake(str(opts.repo_root)) machine_names = get_machine_names( @@ -162,8 +172,10 @@ def main() -> None: { "publickey": sops_pub_key, "type": "age", - } + }, + indent=2, ) + + "\n" ) with NamedTemporaryFile("w") as f: f.write("# created: 2023-07-17T10:51:45+02:00\n") @@ -171,7 +183,7 @@ def main() -> None: f.write(sops_priv_key) f.seek(0) os.environ["SOPS_AGE_KEY_FILE"] = f.name - generate_vars(list(machines)) + generate_vars(list(machines), fake_prompts=True) if __name__ == "__main__":