diff --git a/pkgs/clan-app/ui/src/components/Select/Select.module.css b/pkgs/clan-app/ui/src/components/Select/Select.module.css index 2f720449d..d4d798c64 100644 --- a/pkgs/clan-app/ui/src/components/Select/Select.module.css +++ b/pkgs/clan-app/ui/src/components/Select/Select.module.css @@ -4,7 +4,7 @@ &[data-expanded] { @apply outline-def-2 outline-1 outline; - z-index: var(--z-index + 5); + z-index: calc(var(--z-index) + 5); } &[data-highlighted] { diff --git a/pkgs/clan-app/ui/src/components/Select/Select.tsx b/pkgs/clan-app/ui/src/components/Select/Select.tsx index 325078c1f..3c7595bbc 100644 --- a/pkgs/clan-app/ui/src/components/Select/Select.tsx +++ b/pkgs/clan-app/ui/src/components/Select/Select.tsx @@ -146,6 +146,7 @@ export const Select = (props: SelectProps) => { > diff --git a/pkgs/clan-app/ui/src/routes/Onboarding/Onboarding.tsx b/pkgs/clan-app/ui/src/routes/Onboarding/Onboarding.tsx index cd0dfceaf..f81a65449 100644 --- a/pkgs/clan-app/ui/src/routes/Onboarding/Onboarding.tsx +++ b/pkgs/clan-app/ui/src/routes/Onboarding/Onboarding.tsx @@ -251,6 +251,10 @@ export const Onboarding: Component = (props) => { }, }).result; + await client.fetch("create_secrets_user", { + flake_dir: path, + }).result; + if (resp.status === "error") { setWelcomeError(resp.errors[0].message); setState("welcome"); diff --git a/pkgs/clan-app/ui/src/workflows/Install/Install.stories.tsx b/pkgs/clan-app/ui/src/workflows/Install/Install.stories.tsx index 87ac1b84a..1c949087e 100644 --- a/pkgs/clan-app/ui/src/workflows/Install/Install.stories.tsx +++ b/pkgs/clan-app/ui/src/workflows/Install/Install.stories.tsx @@ -18,29 +18,64 @@ type ResultDataMap = { [K in OperationNames]: SuccessQuery["data"]; }; -export const mockFetcher: Fetcher = ( +const mockFetcher: Fetcher = ( name: K, _args: unknown, ): ApiCall => { // TODO: Make this configurable for every story const resultData: Partial = { - get_machine_flash_options: { - keymaps: ["DE_de", "US_en"], - languages: ["en", "de"], - }, get_system_file: ["id_rsa.pub"], list_system_storage_devices: { blockdevices: [ { name: "sda_bla_bla", path: "/dev/sda", + id_link: "sda_bla_bla", }, { name: "sdb_foo_foo", path: "/dev/sdb", + id_link: "sdb_foo_foo", }, ] as SuccessQuery<"list_system_storage_devices">["data"]["blockdevices"], }, + get_machine_disk_schemas: { + "single-disk": { + readme: "This is a single disk installation schema", + frontmatter: { + description: "Single disk installation schema", + }, + name: "single-disk", + placeholders: { + mainDisk: { + label: "Main Disk", + required: true, + options: ["disk1", "usb1"], + }, + }, + }, + }, + get_generators: [ + { + name: "funny.gritty", + prompts: [ + { + name: "gritty.name", + description: "Name of the gritty", + prompt_type: "line", + display: { + label: "Gritty Name", + group: "User", + required: true, + }, + }, + ], + }, + ], + run_generators: true, + get_machine_hardware_summary: { + hardware_config: "nixos-facter", + }, }; return { diff --git a/pkgs/clan-app/ui/src/workflows/Install/steps/Initial.tsx b/pkgs/clan-app/ui/src/workflows/Install/steps/Initial.tsx index 8f8354246..9e709955f 100644 --- a/pkgs/clan-app/ui/src/workflows/Install/steps/Initial.tsx +++ b/pkgs/clan-app/ui/src/workflows/Install/steps/Initial.tsx @@ -7,7 +7,7 @@ import { StepLayout } from "../../Steps"; const ChoiceLocalOrRemote = () => { const stepSignal = useStepper(); return ( -
+
diff --git a/pkgs/clan-app/ui/src/workflows/Install/steps/createInstaller.tsx b/pkgs/clan-app/ui/src/workflows/Install/steps/createInstaller.tsx index 3bae7fc1f..c8fd3ac4d 100644 --- a/pkgs/clan-app/ui/src/workflows/Install/steps/createInstaller.tsx +++ b/pkgs/clan-app/ui/src/workflows/Install/steps/createInstaller.tsx @@ -16,10 +16,7 @@ import { Alert } from "@/src/components/Alert/Alert"; import { LoadingBar } from "@/src/components/LoadingBar/LoadingBar"; import { Button } from "@/src/components/Button/Button"; import Icon from "@/src/components/Icon/Icon"; -import { - useMachineFlashOptions, - useSystemStorageOptions, -} from "@/src/hooks/queries"; +import { useSystemStorageOptions } from "@/src/hooks/queries"; import { useApiClient } from "@/src/hooks/ApiClient"; import { onMount } from "solid-js"; @@ -144,8 +141,6 @@ const ConfigureImage = () => { throw new Error("No data returned from api call"); }; - const optionsQuery = useMachineFlashOptions(); - let content: Node; return ( @@ -309,15 +304,17 @@ const FlashProgress = () => { }); const handleCancel = async () => { - const progress = store.flash.progress; - if (progress) { - await progress.cancel(); + if (store.flash) { + const progress = store.flash.progress; + if (progress) { + await progress.cancel(); + } } stepSignal.previous(); }; return ( -
+
{ const stepSignal = useStepper(); return (
-
+
diff --git a/pkgs/clan-app/ui/src/workflows/Install/steps/installSteps.tsx b/pkgs/clan-app/ui/src/workflows/Install/steps/installSteps.tsx index d413c6fe9..5e66db2e4 100644 --- a/pkgs/clan-app/ui/src/workflows/Install/steps/installSteps.tsx +++ b/pkgs/clan-app/ui/src/workflows/Install/steps/installSteps.tsx @@ -70,7 +70,6 @@ const ConfigureAddress = () => { // Here you would typically trigger the ISO creation process stepSignal.next(); - console.log("Shit doesnt work", values); }; return ( @@ -318,6 +317,25 @@ interface PromptForm extends FieldValues { promptValues: PromptValues; } +const sanitize = (name: string) => { + return name.replace(".", "__dot__"); +}; +const restore = (name: string) => { + return name.replace("__dot__", "."); +}; + +const transformPromptValues = ( + values: PromptValues, + transform: (s: string) => string, +): PromptValues => + Object.fromEntries( + Object.entries(values).map(([key, value]) => [ + transform(key), + Object.fromEntries( + Object.entries(value).map(([k, v]) => [transform(k), v]), + ), + ]), + ); interface PromptsFieldsProps { generators: MachineGenerators; } @@ -338,8 +356,11 @@ const PromptsFields = (props: PromptsFieldsProps) => { if (!acc[groupName]) acc[groupName] = { fields: [], name: groupName }; acc[groupName].fields.push({ - prompt, - generator: generator.name, + prompt: { + ...prompt, + name: sanitize(prompt.name), + }, + generator: sanitize(generator.name), value: prompt.previous_value, }); } @@ -351,16 +372,24 @@ const PromptsFields = (props: PromptsFieldsProps) => { const [formStore, { Form, Field }] = createForm({ initialValues: { - promptValues: store.install?.promptValues || {}, + promptValues: transformPromptValues( + store.install?.promptValues || {}, + sanitize, + ), }, }); console.log(groups); const handleSubmit: SubmitHandler = (values, event) => { - console.log("vars submitted", values); + const restoredValues: PromptValues = transformPromptValues( + values.promptValues, + restore, + ); - set("install", (s) => ({ ...s, promptValues: values.promptValues })); + console.log("vars submitted", restoredValues); + + set("install", (s) => ({ ...s, promptValues: restoredValues })); console.log("vars preloaded"); // Here you would typically trigger the ISO creation process stepSignal.next(); @@ -545,7 +574,14 @@ const InstallSummary = () => { ); }; -type InstallTopic = "generators" | "upload-secrets" | "nixos-anywhere"; +type InstallTopic = [ + "generators", + "upload-secrets", + "nixos-anywhere", + "formatting", + "rebooting", + "installing", +][number]; const InstallProgress = () => { const stepSignal = useStepper(); @@ -563,7 +599,7 @@ const InstallProgress = () => { ); return ( -
+
{ Running nixos-anywhere ... + + Formatting ... + + + Installing ... + + + Rebooting ... + @@ -625,7 +670,7 @@ const InstallDone = (props: InstallDoneProps) => { const [store, get] = getStepStore(stepSignal); return ( -
+
diff --git a/pkgs/clan-cli/clan_lib/machines/install.py b/pkgs/clan-cli/clan_lib/machines/install.py index a04849d34..57b6f6fef 100644 --- a/pkgs/clan-cli/clan_lib/machines/install.py +++ b/pkgs/clan-cli/clan_lib/machines/install.py @@ -22,7 +22,14 @@ log = logging.getLogger(__name__) BuildOn = Literal["auto", "local", "remote"] -Step = Literal["generators", "upload-secrets", "nixos-anywhere"] +Step = Literal[ + "generators", + "upload-secrets", + "nixos-anywhere", + "formatting", + "rebooting", + "installing", +] def notify_install_step(current: Step) -> None: @@ -195,4 +202,22 @@ def run_machine_install(opts: InstallOptions, target_host: Remote) -> None: ) notify_install_step("nixos-anywhere") - run(cmd, RunOpts(log=Log.BOTH, prefix=machine.name, needs_user_terminal=True)) + run( + [*cmd, "--phases", "kexec"], + RunOpts(log=Log.BOTH, prefix=machine.name, needs_user_terminal=True), + ) + notify_install_step("formatting") + run( + [*cmd, "--phases", "disko"], + RunOpts(log=Log.BOTH, prefix=machine.name, needs_user_terminal=True), + ) + notify_install_step("installing") + run( + [*cmd, "--phases", "install"], + RunOpts(log=Log.BOTH, prefix=machine.name, needs_user_terminal=True), + ) + notify_install_step("rebooting") + run( + [*cmd, "--phases", "reboot"], + RunOpts(log=Log.BOTH, prefix=machine.name, needs_user_terminal=True), + )