vars: eval finalScript lazy

This commit is contained in:
lassulus
2024-12-13 18:30:54 +01:00
parent a364a5b800
commit c888f01823
6 changed files with 67 additions and 31 deletions

View File

@@ -41,7 +41,6 @@ in
inherit (generator) inherit (generator)
name name
dependencies dependencies
finalScript
validationHash validationHash
migrateFact migrateFact
prompts prompts

View File

@@ -24,35 +24,37 @@ let
filePromptNames = attrNames (filterAttrs (_name: prompt: prompt.createFile) config.prompts); filePromptNames = attrNames (filterAttrs (_name: prompt: prompt.createFile) config.prompts);
in in
{ {
finalScript = mkOptionDefault '' finalScript = mkOptionDefault (
set -eu -o pipefail pkgs.writeScript "generator-${config.name}" ''
set -eu -o pipefail
export PATH="${makeBinPath config.runtimeInputs}:${pkgs.coreutils}/bin" export PATH="${makeBinPath config.runtimeInputs}:${pkgs.coreutils}/bin"
${optionalString (pkgs.stdenv.hostPlatform.isLinux) '' ${optionalString (pkgs.stdenv.hostPlatform.isLinux) ''
# prepare sandbox user on platforms where this is supported # prepare sandbox user on platforms where this is supported
mkdir -p /etc mkdir -p /etc
cat > /etc/group <<EOF cat > /etc/group <<EOF
root:x:0: root:x:0:
nixbld:!:$(id -g): nixbld:!:$(id -g):
nogroup:x:65534: nogroup:x:65534:
EOF EOF
cat > /etc/passwd <<EOF cat > /etc/passwd <<EOF
root:x:0:0:Nix build user:/build:/noshell root:x:0:0:Nix build user:/build:/noshell
nixbld:x:$(id -u):$(id -g):Nix build user:/build:/noshell nixbld:x:$(id -u):$(id -g):Nix build user:/build:/noshell
nobody:x:65534:65534:Nobody:/:/noshell nobody:x:65534:65534:Nobody:/:/noshell
EOF EOF
cat > /etc/hosts <<EOF cat > /etc/hosts <<EOF
127.0.0.1 localhost 127.0.0.1 localhost
::1 localhost ::1 localhost
EOF EOF
''} ''}
${promptsToFilesScript filePromptNames} ${promptsToFilesScript filePromptNames}
${config.script} ${config.script}
''; ''
);
files = genAttrs filePromptNames (_name: { }); files = genAttrs filePromptNames (_name: { });
} }

View File

@@ -322,7 +322,7 @@ in
- all required programs are in PATH - all required programs are in PATH
- sandbox is set up correctly - sandbox is set up correctly
''; '';
type = lib.types.str; type = lib.types.path;
readOnly = true; readOnly = true;
internal = true; internal = true;
}; };

View File

@@ -0,0 +1,25 @@
{
config,
lib,
pkgs,
...
}:
let
sortedGenerators = lib.toposort (a: b: builtins.elem a.name b.dependencies) (
lib.attrValues config.clan.core.vars.generators
);
generateSecrets = ''
${lib.concatStringsSep "\n" (_gen: ''
v
'') sortedGenerators}
'';
in
{
config = lib.mkIf (config.clan.core.vars.settings.secretStore == "on-machine") {
environment.systemPackages = [
(pkgs.writeShellApplication {
text = generateSecrets;
})
];
};
}

View File

@@ -166,7 +166,11 @@ class Machine:
generators: dict[str, Any] = clan_vars.get("generators") generators: dict[str, Any] = clan_vars.get("generators")
if generators is None: if generators is None:
return [] return []
return [Generator.from_json(gen) for gen in generators.values()] _generators = [Generator.from_json(gen) for gen in generators.values()]
for gen in _generators:
gen.machine(self)
return _generators
@property @property
def secrets_upload_directory(self) -> str: def secrets_upload_directory(self) -> str:

View File

@@ -40,7 +40,6 @@ class Generator:
files: list[Var] = field(default_factory=list) files: list[Var] = field(default_factory=list)
share: bool = False share: bool = False
validation: str | None = None validation: str | None = None
final_script: str = ""
prompts: list[Prompt] = field(default_factory=list) prompts: list[Prompt] = field(default_factory=list)
dependencies: list[str] = field(default_factory=list) dependencies: list[str] = field(default_factory=list)
@@ -62,7 +61,6 @@ class Generator:
return cls( return cls(
name=data["name"], name=data["name"],
share=data["share"], share=data["share"],
final_script=data["finalScript"],
files=[Var.from_json(data["name"], f) for f in data["files"].values()], files=[Var.from_json(data["name"], f) for f in data["files"].values()],
validation=data["validationHash"], validation=data["validationHash"],
dependencies=data["dependencies"], dependencies=data["dependencies"],
@@ -70,6 +68,14 @@ class Generator:
prompts=[Prompt.from_json(p) for p in data["prompts"].values()], prompts=[Prompt.from_json(p) for p in data["prompts"].values()],
) )
@property
def final_script(self) -> Path:
assert self._machine is not None
final_script = self._machine.build_nix(
f"config.clan.core.vars.generators.{self.name}.finalScript"
)
return final_script
def bubblewrap_cmd(generator: str, tmpdir: Path) -> list[str]: def bubblewrap_cmd(generator: str, tmpdir: Path) -> list[str]:
# fmt: off # fmt: off
@@ -188,7 +194,7 @@ def execute_generator(
prompt_file.write_text(value) prompt_file.write_text(value)
if sys.platform == "linux": if sys.platform == "linux":
cmd = bubblewrap_cmd(generator.final_script, tmpdir) cmd = bubblewrap_cmd(str(generator.final_script), tmpdir)
else: else:
cmd = ["bash", "-c", generator.final_script] cmd = ["bash", "-c", generator.final_script]
run(cmd, RunOpts(env=env)) run(cmd, RunOpts(env=env))
@@ -201,7 +207,7 @@ def execute_generator(
secret_file = tmpdir_out / file.name secret_file = tmpdir_out / file.name
if not secret_file.is_file(): if not secret_file.is_file():
msg = f"did not generate a file for '{file.name}' when running the following command:\n" msg = f"did not generate a file for '{file.name}' when running the following command:\n"
msg += generator.final_script msg += str(generator.final_script)
raise ClanError(msg) raise ClanError(msg)
if file.secret: if file.secret:
file_path = secret_vars_store.set( file_path = secret_vars_store.set(