Merge pull request 'pkgs/clan/lib(install): implement separate nixos-anywhere install phases' (#4710) from ke-install-phases into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4710
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
&[data-expanded] {
|
&[data-expanded] {
|
||||||
@apply outline-def-2 outline-1 outline;
|
@apply outline-def-2 outline-1 outline;
|
||||||
z-index: var(--z-index + 5);
|
z-index: calc(var(--z-index) + 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
&[data-highlighted] {
|
&[data-highlighted] {
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ export const Select = (props: SelectProps) => {
|
|||||||
<KSelect.HiddenSelect {...selectProps} />
|
<KSelect.HiddenSelect {...selectProps} />
|
||||||
<KSelect.Trigger
|
<KSelect.Trigger
|
||||||
class={cx(styles.trigger)}
|
class={cx(styles.trigger)}
|
||||||
|
style={{ "--z-index": zIndex() }}
|
||||||
data-loading={loading() || undefined}
|
data-loading={loading() || undefined}
|
||||||
>
|
>
|
||||||
<KSelect.Value<Option>>
|
<KSelect.Value<Option>>
|
||||||
|
|||||||
@@ -251,6 +251,10 @@ export const Onboarding: Component<RouteSectionProps> = (props) => {
|
|||||||
},
|
},
|
||||||
}).result;
|
}).result;
|
||||||
|
|
||||||
|
await client.fetch("create_secrets_user", {
|
||||||
|
flake_dir: path,
|
||||||
|
}).result;
|
||||||
|
|
||||||
if (resp.status === "error") {
|
if (resp.status === "error") {
|
||||||
setWelcomeError(resp.errors[0].message);
|
setWelcomeError(resp.errors[0].message);
|
||||||
setState("welcome");
|
setState("welcome");
|
||||||
|
|||||||
@@ -18,29 +18,64 @@ type ResultDataMap = {
|
|||||||
[K in OperationNames]: SuccessQuery<K>["data"];
|
[K in OperationNames]: SuccessQuery<K>["data"];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mockFetcher: Fetcher = <K extends OperationNames>(
|
const mockFetcher: Fetcher = <K extends OperationNames>(
|
||||||
name: K,
|
name: K,
|
||||||
_args: unknown,
|
_args: unknown,
|
||||||
): ApiCall<K> => {
|
): ApiCall<K> => {
|
||||||
// TODO: Make this configurable for every story
|
// TODO: Make this configurable for every story
|
||||||
const resultData: Partial<ResultDataMap> = {
|
const resultData: Partial<ResultDataMap> = {
|
||||||
get_machine_flash_options: {
|
|
||||||
keymaps: ["DE_de", "US_en"],
|
|
||||||
languages: ["en", "de"],
|
|
||||||
},
|
|
||||||
get_system_file: ["id_rsa.pub"],
|
get_system_file: ["id_rsa.pub"],
|
||||||
list_system_storage_devices: {
|
list_system_storage_devices: {
|
||||||
blockdevices: [
|
blockdevices: [
|
||||||
{
|
{
|
||||||
name: "sda_bla_bla",
|
name: "sda_bla_bla",
|
||||||
path: "/dev/sda",
|
path: "/dev/sda",
|
||||||
|
id_link: "sda_bla_bla",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "sdb_foo_foo",
|
name: "sdb_foo_foo",
|
||||||
path: "/dev/sdb",
|
path: "/dev/sdb",
|
||||||
|
id_link: "sdb_foo_foo",
|
||||||
},
|
},
|
||||||
] as SuccessQuery<"list_system_storage_devices">["data"]["blockdevices"],
|
] 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 {
|
return {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { StepLayout } from "../../Steps";
|
|||||||
const ChoiceLocalOrRemote = () => {
|
const ChoiceLocalOrRemote = () => {
|
||||||
const stepSignal = useStepper<InstallSteps>();
|
const stepSignal = useStepper<InstallSteps>();
|
||||||
return (
|
return (
|
||||||
<div class="flex flex-col gap-3">
|
<div class="flex flex-col gap-3 size-full">
|
||||||
<div class="flex flex-col gap-6 rounded-md px-4 py-6 text-fg-def-1 bg-def-2">
|
<div class="flex flex-col gap-6 rounded-md px-4 py-6 text-fg-def-1 bg-def-2">
|
||||||
<div class="flex justify-between gap-2">
|
<div class="flex justify-between gap-2">
|
||||||
<div class="flex flex-col justify-center gap-1 px-1">
|
<div class="flex flex-col justify-center gap-1 px-1">
|
||||||
|
|||||||
@@ -16,10 +16,7 @@ import { Alert } from "@/src/components/Alert/Alert";
|
|||||||
import { LoadingBar } from "@/src/components/LoadingBar/LoadingBar";
|
import { LoadingBar } from "@/src/components/LoadingBar/LoadingBar";
|
||||||
import { Button } from "@/src/components/Button/Button";
|
import { Button } from "@/src/components/Button/Button";
|
||||||
import Icon from "@/src/components/Icon/Icon";
|
import Icon from "@/src/components/Icon/Icon";
|
||||||
import {
|
import { useSystemStorageOptions } from "@/src/hooks/queries";
|
||||||
useMachineFlashOptions,
|
|
||||||
useSystemStorageOptions,
|
|
||||||
} from "@/src/hooks/queries";
|
|
||||||
import { useApiClient } from "@/src/hooks/ApiClient";
|
import { useApiClient } from "@/src/hooks/ApiClient";
|
||||||
import { onMount } from "solid-js";
|
import { onMount } from "solid-js";
|
||||||
|
|
||||||
@@ -144,8 +141,6 @@ const ConfigureImage = () => {
|
|||||||
throw new Error("No data returned from api call");
|
throw new Error("No data returned from api call");
|
||||||
};
|
};
|
||||||
|
|
||||||
const optionsQuery = useMachineFlashOptions();
|
|
||||||
|
|
||||||
let content: Node;
|
let content: Node;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -309,15 +304,17 @@ const FlashProgress = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleCancel = async () => {
|
const handleCancel = async () => {
|
||||||
const progress = store.flash.progress;
|
if (store.flash) {
|
||||||
if (progress) {
|
const progress = store.flash.progress;
|
||||||
await progress.cancel();
|
if (progress) {
|
||||||
|
await progress.cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
stepSignal.previous();
|
stepSignal.previous();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex size-full h-60 flex-col items-center justify-end bg-inv-4">
|
<div class="flex size-full flex-col items-center justify-center bg-inv-4">
|
||||||
<div class="mb-6 flex w-full max-w-md flex-col items-center gap-3 fg-inv-1">
|
<div class="mb-6 flex w-full max-w-md flex-col items-center gap-3 fg-inv-1">
|
||||||
<Typography
|
<Typography
|
||||||
hierarchy="title"
|
hierarchy="title"
|
||||||
@@ -344,7 +341,7 @@ const FlashDone = () => {
|
|||||||
const stepSignal = useStepper<InstallSteps>();
|
const stepSignal = useStepper<InstallSteps>();
|
||||||
return (
|
return (
|
||||||
<div class="flex size-full flex-col items-center justify-between bg-inv-4">
|
<div class="flex size-full flex-col items-center justify-between bg-inv-4">
|
||||||
<div class="flex w-full max-w-md flex-col items-center gap-3 py-6 fg-inv-1">
|
<div class="flex size-full max-w-md flex-col items-center justify-center gap-3 py-6 fg-inv-1">
|
||||||
<div class="rounded-full bg-semantic-success-4">
|
<div class="rounded-full bg-semantic-success-4">
|
||||||
<Icon icon="Checkmark" class="size-9" />
|
<Icon icon="Checkmark" class="size-9" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ const ConfigureAddress = () => {
|
|||||||
|
|
||||||
// Here you would typically trigger the ISO creation process
|
// Here you would typically trigger the ISO creation process
|
||||||
stepSignal.next();
|
stepSignal.next();
|
||||||
console.log("Shit doesnt work", values);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -318,6 +317,25 @@ interface PromptForm extends FieldValues {
|
|||||||
promptValues: PromptValues;
|
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 {
|
interface PromptsFieldsProps {
|
||||||
generators: MachineGenerators;
|
generators: MachineGenerators;
|
||||||
}
|
}
|
||||||
@@ -338,8 +356,11 @@ const PromptsFields = (props: PromptsFieldsProps) => {
|
|||||||
if (!acc[groupName]) acc[groupName] = { fields: [], name: groupName };
|
if (!acc[groupName]) acc[groupName] = { fields: [], name: groupName };
|
||||||
|
|
||||||
acc[groupName].fields.push({
|
acc[groupName].fields.push({
|
||||||
prompt,
|
prompt: {
|
||||||
generator: generator.name,
|
...prompt,
|
||||||
|
name: sanitize(prompt.name),
|
||||||
|
},
|
||||||
|
generator: sanitize(generator.name),
|
||||||
value: prompt.previous_value,
|
value: prompt.previous_value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -351,16 +372,24 @@ const PromptsFields = (props: PromptsFieldsProps) => {
|
|||||||
|
|
||||||
const [formStore, { Form, Field }] = createForm<PromptForm>({
|
const [formStore, { Form, Field }] = createForm<PromptForm>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
promptValues: store.install?.promptValues || {},
|
promptValues: transformPromptValues(
|
||||||
|
store.install?.promptValues || {},
|
||||||
|
sanitize,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(groups);
|
console.log(groups);
|
||||||
|
|
||||||
const handleSubmit: SubmitHandler<PromptForm> = (values, event) => {
|
const handleSubmit: SubmitHandler<PromptForm> = (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");
|
console.log("vars preloaded");
|
||||||
// Here you would typically trigger the ISO creation process
|
// Here you would typically trigger the ISO creation process
|
||||||
stepSignal.next();
|
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 InstallProgress = () => {
|
||||||
const stepSignal = useStepper<InstallSteps>();
|
const stepSignal = useStepper<InstallSteps>();
|
||||||
@@ -563,7 +599,7 @@ const InstallProgress = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex size-full h-60 flex-col items-center justify-end bg-inv-4">
|
<div class="flex size-full flex-col items-center justify-center bg-inv-4">
|
||||||
<div class="mb-6 flex w-full max-w-md flex-col items-center gap-3 fg-inv-1">
|
<div class="mb-6 flex w-full max-w-md flex-col items-center gap-3 fg-inv-1">
|
||||||
<Typography
|
<Typography
|
||||||
hierarchy="title"
|
hierarchy="title"
|
||||||
@@ -599,6 +635,15 @@ const InstallProgress = () => {
|
|||||||
<Match when={installState()?.topic === "nixos-anywhere"}>
|
<Match when={installState()?.topic === "nixos-anywhere"}>
|
||||||
Running nixos-anywhere ...
|
Running nixos-anywhere ...
|
||||||
</Match>
|
</Match>
|
||||||
|
<Match when={installState()?.topic === "formatting"}>
|
||||||
|
Formatting ...
|
||||||
|
</Match>
|
||||||
|
<Match when={installState()?.topic === "installing"}>
|
||||||
|
Installing ...
|
||||||
|
</Match>
|
||||||
|
<Match when={installState()?.topic === "rebooting"}>
|
||||||
|
Rebooting ...
|
||||||
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
@@ -625,7 +670,7 @@ const InstallDone = (props: InstallDoneProps) => {
|
|||||||
const [store, get] = getStepStore<InstallStoreType>(stepSignal);
|
const [store, get] = getStepStore<InstallStoreType>(stepSignal);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex w-full flex-col items-center bg-inv-4">
|
<div class="flex size-full flex-col items-center justify-center bg-inv-4">
|
||||||
<div class="flex w-full max-w-md flex-col items-center gap-3 py-6 fg-inv-1">
|
<div class="flex w-full max-w-md flex-col items-center gap-3 py-6 fg-inv-1">
|
||||||
<div class="rounded-full bg-semantic-success-4">
|
<div class="rounded-full bg-semantic-success-4">
|
||||||
<Icon icon="Checkmark" class="size-9" />
|
<Icon icon="Checkmark" class="size-9" />
|
||||||
|
|||||||
@@ -22,7 +22,14 @@ log = logging.getLogger(__name__)
|
|||||||
BuildOn = Literal["auto", "local", "remote"]
|
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:
|
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")
|
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),
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user