diff --git a/pkgs/clan-cli/clan_cli/tests/test_vars.py b/pkgs/clan-cli/clan_cli/tests/test_vars.py index ad51563f1..62713c781 100644 --- a/pkgs/clan-cli/clan_cli/tests/test_vars.py +++ b/pkgs/clan-cli/clan_cli/tests/test_vars.py @@ -12,7 +12,12 @@ from clan_cli.tests.age_keys import SopsSetup from clan_cli.tests.fixtures_flakes import ClanFlake from clan_cli.tests.helpers import cli from clan_cli.vars.check import check_vars -from clan_cli.vars.generate import Generator, generate_vars_for_machine_interactive +from clan_cli.vars.generate import ( + Generator, + generate_vars_for_machine, + generate_vars_for_machine_interactive, + get_generators_closure, +) from clan_cli.vars.get import get_var from clan_cli.vars.graph import all_missing_closure, requested_closure from clan_cli.vars.list import stringify_all_vars @@ -640,9 +645,6 @@ def test_api_set_prompts( monkeypatch: pytest.MonkeyPatch, flake: ClanFlake, ) -> None: - from clan_cli.vars._types import GeneratorUpdate - from clan_cli.vars.list import get_generators, set_prompts - config = flake.machines["my_machine"] config["nixpkgs"]["hostPlatform"] = "x86_64-linux" my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"] @@ -652,33 +654,39 @@ def test_api_set_prompts( flake.refresh() monkeypatch.chdir(flake.path) - params = {"machine_name": "my_machine", "base_dir": str(flake.path)} - set_prompts( - **params, - updates=[ - GeneratorUpdate( - generator="my_generator", - prompt_values={"prompt1": "input1"}, - ) - ], + generate_vars_for_machine( + machine_name="my_machine", + base_dir=flake.path, + generators=["my_generator"], + all_prompt_values={ + "my_generator": { + "prompt1": "input1", + } + }, ) machine = Machine(name="my_machine", flake=Flake(str(flake.path))) store = in_repo.FactStore(machine) assert store.exists(Generator("my_generator"), "prompt1") assert store.get(Generator("my_generator"), "prompt1").decode() == "input1" - set_prompts( - **params, - updates=[ - GeneratorUpdate( - generator="my_generator", - prompt_values={"prompt1": "input2"}, - ) - ], + generate_vars_for_machine( + machine_name="my_machine", + base_dir=flake.path, + generators=["my_generator"], + all_prompt_values={ + "my_generator": { + "prompt1": "input2", + } + }, ) assert store.get(Generator("my_generator"), "prompt1").decode() == "input2" - generators = get_generators(**params) + generators = get_generators_closure( + machine_name="my_machine", + base_dir=flake.path, + regenerate=True, + include_previous_values=True, + ) assert len(generators) == 1 assert generators[0].name == "my_generator" assert generators[0].prompts[0].name == "prompt1" diff --git a/pkgs/clan-cli/clan_cli/vars/generate.py b/pkgs/clan-cli/clan_cli/vars/generate.py index cadbdc4f7..c515f7967 100644 --- a/pkgs/clan-cli/clan_cli/vars/generate.py +++ b/pkgs/clan-cli/clan_cli/vars/generate.py @@ -294,10 +294,28 @@ def _ask_prompts( return prompt_values +def _get_previous_value( + machine: "Machine", + generator: Generator, + prompt: Prompt, +) -> str | None: + if not prompt.persist: + return None + + pub_store = machine.public_vars_store + if pub_store.exists(generator, prompt.name): + return pub_store.get(generator, prompt.name).decode() + sec_store = machine.secret_vars_store + if sec_store.exists(generator, prompt.name): + return sec_store.get(generator, prompt.name).decode() + return None + + def get_closure( machine: "Machine", generator_name: str | None, regenerate: bool, + include_previous_values: bool = False, ) -> list[Generator]: from .graph import all_missing_closure, full_closure @@ -310,14 +328,24 @@ def get_closure( for generator in vars_generators: generator.machine(machine) + result_closure = [] if generator_name is None: # all generators selected if regenerate: - return full_closure(generators) - return all_missing_closure(generators) + result_closure = full_closure(generators) + else: + result_closure = all_missing_closure(generators) # specific generator selected - if regenerate: - return requested_closure([generator_name], generators) - return minimal_closure([generator_name], generators) + elif regenerate: + result_closure = requested_closure([generator_name], generators) + else: + result_closure = minimal_closure([generator_name], generators) + + if include_previous_values: + for generator in result_closure: + for prompt in generator.prompts: + prompt.previous_value = _get_previous_value(machine, generator, prompt) + + return result_closure @API.register @@ -325,6 +353,7 @@ def get_generators_closure( machine_name: str, base_dir: Path, regenerate: bool = False, + include_previous_values: bool = False, ) -> list[Generator]: from clan_cli.machines.machines import Machine @@ -332,13 +361,14 @@ def get_generators_closure( machine=Machine(name=machine_name, flake=Flake(str(base_dir))), generator_name=None, regenerate=regenerate, + include_previous_values=include_previous_values, ) def _generate_vars_for_machine( machine: "Machine", generators: list[Generator], - all_prompt_values: dict[str, dict], + all_prompt_values: dict[str, dict[str, str]], no_sandbox: bool = False, ) -> bool: for generator in generators: @@ -350,7 +380,7 @@ def _generate_vars_for_machine( generator=generator, secret_vars_store=machine.secret_vars_store, public_vars_store=machine.public_vars_store, - prompt_values=all_prompt_values[generator.name], + prompt_values=all_prompt_values.get(generator.name, {}), no_sandbox=no_sandbox, ) return True diff --git a/pkgs/webview-ui/app/src/index.tsx b/pkgs/webview-ui/app/src/index.tsx index 155294f66..94b6ba375 100644 --- a/pkgs/webview-ui/app/src/index.tsx +++ b/pkgs/webview-ui/app/src/index.tsx @@ -21,6 +21,8 @@ import { ModuleDetails as AddModule } from "./routes/modules/add"; import { ApiTester } from "./api_test"; import { IconVariant } from "./components/icon"; import { Components } from "./routes/components"; +import { activeURI } from "./App"; +import { VarsForMachine, VarsStep } from "./routes/machines/install/vars-step"; export const client = new QueryClient(); @@ -31,7 +33,6 @@ if (import.meta.env.DEV && !(root instanceof HTMLElement)) { "Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got misspelled?", ); } - if (import.meta.env.DEV) { console.log("Development mode"); // Load the debugger in development mode @@ -73,6 +74,12 @@ export const routes: AppRoute[] = [ hidden: true, component: () => , }, + { + path: "/:id/vars", + label: "Vars", + hidden: true, + component: () => , + }, ], }, { diff --git a/pkgs/webview-ui/app/src/routes/machines/details.tsx b/pkgs/webview-ui/app/src/routes/machines/details.tsx index c5742fd1e..9b64691a1 100644 --- a/pkgs/webview-ui/app/src/routes/machines/details.tsx +++ b/pkgs/webview-ui/app/src/routes/machines/details.tsx @@ -11,9 +11,9 @@ import { getValues, setValue, } from "@modular-forms/solid"; -import { useParams } from "@solidjs/router"; -import { createQuery } from "@tanstack/solid-query"; -import { createSignal, For, Match, Show, Switch } from "solid-js"; +import { useNavigate, useParams, useSearchParams } from "@solidjs/router"; +import { createQuery, useQueryClient } from "@tanstack/solid-query"; +import { createEffect, createSignal, For, Match, Show, Switch } from "solid-js"; import toast from "solid-toast"; import { MachineAvatar } from "./avatar"; import { Header } from "@/src/layout/header"; @@ -316,21 +316,7 @@ const InstallMachine = (props: InstallMachineProps) => { /> - } - handleNext={(data) => { - // const prev = getValue(formStore, "2"); - // setValue(formStore, "2", { ...prev, ...data }); - handleNext(); - }} - initial={{ - ...getValue(formStore, "3"), - }} - /> +
TODO: vars
{ const [installModalOpen, setInstallModalOpen] = createSignal(false); + const navigate = useNavigate(); + const [searchParams, setSearchParams] = useSearchParams(); + const queryClient = useQueryClient(); + const handleSubmit = async (values: MachineFormInterface) => { console.log("submitting", values); @@ -447,7 +437,40 @@ const MachineForm = (props: MachineDetailsProps) => { return null; }; + const generatorsQuery = createQuery(() => ({ + queryKey: [activeURI(), machineName(), "generators"], + queryFn: async () => { + const machine_name = machineName(); + const base_dir = activeURI(); + if (!machine_name || !base_dir) { + return []; + } + const result = await callApi("get_generators_closure", { + base_dir: base_dir, + machine_name: machine_name, + }); + if (result.status === "error") throw new Error("Failed to fetch data"); + return result.data; + }, + })); + + const handleUpdateButton = async () => { + const t = toast.loading("Checking for generators..."); + await generatorsQuery.refetch(); + toast.dismiss(t); + if (generatorsQuery.data?.length !== 0) { + navigate(`/machines/${machineName()}/vars`); + } else { + handleUpdate(); + } + }; + + const [isUpdating, setIsUpdating] = createSignal(false); + const handleUpdate = async () => { + if (isUpdating()) { + return; + } const curr_uri = activeURI(); if (!curr_uri) { return; @@ -461,6 +484,7 @@ const MachineForm = (props: MachineDetailsProps) => { const target = targetHost(); const loading_toast = toast.loading("Updating machine..."); + setIsUpdating(true); const r = await callApi("update_machines", { base_path: curr_uri, machines: [ @@ -472,6 +496,7 @@ const MachineForm = (props: MachineDetailsProps) => { }, ], }); + setIsUpdating(false); toast.dismiss(loading_toast); if (r.status === "error") { @@ -481,6 +506,15 @@ const MachineForm = (props: MachineDetailsProps) => { toast.success("Machine updated successfully"); } }; + + createEffect(() => { + const action = searchParams.action; + if (action === "update") { + setSearchParams({ action: undefined }); + handleUpdate(); + } + }); + return ( <>
@@ -626,7 +660,7 @@ const MachineForm = (props: MachineDetailsProps) => {
- {(f) => ( + {(prompt) => ( - {!f.previous_value ? "Required" : "Optional"} + {!prompt.previous_value ? "Required" : "Optional"} - {f.name} + {prompt.name} + {/* Avoid nesting issue in case of a "." */} + + {(field, props) => ( + + )} + )} @@ -80,7 +141,17 @@ export const VarsStep = (props: StepProps) => { - {props.footer} + ); }; + +export const VarsForMachine = () => { + const params = useParams(); + + return ( + + {(uri) => } + + ); +};