diff --git a/.vscode/ink.code-snippets b/.vscode/ink.code-snippets
new file mode 100644
index 000000000..74dd266b8
--- /dev/null
+++ b/.vscode/ink.code-snippets
@@ -0,0 +1,21 @@
+{
+ "ink-comp": {
+ "scope": "typescriptreact,javascriptreact",
+ "prefix": "ink-comp",
+ "body": [
+ "import React from \"react\";",
+ "import { Box } from \"ink\";",
+
+ "",
+ "const ${1:$TM_FILENAME_BASE} = ({ setConfig, next }: {setConfig: SetConfig; next: Next; }) => {",
+ "\treturn (",
+ "\t\t",
+ "\t\t\t${0}",
+ "\t\t",
+ "\t);",
+ "}",
+ "",
+ "export default ${1:$TM_FILENAME_BASE};"
+ ]
+ },
+}
\ No newline at end of file
diff --git a/cli/src/App.tsx b/cli/src/App.tsx
index 786db9514..31187d70e 100644
--- a/cli/src/App.tsx
+++ b/cli/src/App.tsx
@@ -8,6 +8,8 @@ import Intro from "./components/Intro";
import Setup from "./components/Setup";
import Periphery from "./components/Periphery";
import Mongo from "./components/mongo/Mongo";
+import { dockerNotInstalled } from "./monitor-cli";
+import Docker from "./components/docker/Docker";
const App = () => {
const [current, next, prev] = useSequence();
@@ -27,6 +29,7 @@ const App = () => {
const pages: ReactNode[] = [
,
+ ...(dockerNotInstalled ? [] : []),
,
...(periphery === true ? peripheryPages : []),
...(periphery === false ? corePages : []),
diff --git a/cli/src/components/docker/Docker.tsx b/cli/src/components/docker/Docker.tsx
new file mode 100644
index 000000000..6351f202a
--- /dev/null
+++ b/cli/src/components/docker/Docker.tsx
@@ -0,0 +1,37 @@
+import React, { useState } from "react";
+import { Text } from "ink";
+import { Next } from "../../types";
+import YesNo from "../util/YesNo";
+import InstallDocker from "./InstallDocker";
+
+const Docker = ({ next }: { next: Next }) => {
+ const [installDocker, setInstallDocker] = useState();
+ if (installDocker === undefined) {
+ return (
+
+ Would you like to install Docker? This will begin the{" "}
+
+ Docker Install Helper
+
+
+ }
+ onSelect={(res) => {
+ if (res === "yes") {
+ setInstallDocker(true);
+ } else {
+ next();
+ }
+ }}
+ direction="vertical"
+ />
+ );
+ } else if (installDocker) {
+ return
+ } else {
+ return null;
+ }
+};
+
+export default Docker;
diff --git a/cli/src/components/docker/InstallDocker.tsx b/cli/src/components/docker/InstallDocker.tsx
new file mode 100644
index 000000000..b50ebbfcf
--- /dev/null
+++ b/cli/src/components/docker/InstallDocker.tsx
@@ -0,0 +1,157 @@
+import React, { Fragment, useState } from "react";
+import { Box, Newline, Text, useInput } from "ink";
+import { Next, SetConfig } from "../../types";
+import YesNo from "../util/YesNo";
+import { installDockerUbuntu, InstallLog } from "../../helpers/docker";
+
+const InstallDocker = ({ next }: { next: Next }) => {
+ const [stage, setStage] = useState<
+ | "userGroup"
+ | "sysCtlEnable"
+ | "confirm"
+ | "install"
+ | "installing"
+ | "finish"
+ | "error"
+ >("userGroup");
+ const [addToUG, setAddToUG] = useState<"yes" | "no">();
+ const [sysCtlEnable, setSysCtlEnable] = useState<"yes" | "no">();
+ const [logs, setLogs] = useState([]);
+ useInput(async (_, key) => {
+ if (key.return) {
+ switch (stage) {
+ case "confirm":
+ const log = await installDockerUbuntu(
+ (log) => setLogs((logs) => [...logs, log]),
+ addToUG === "yes",
+ sysCtlEnable === "yes"
+ );
+ if (log) {
+ // there was some error
+ setLogs((logs) => [...logs, log])
+ setStage("error");
+ } else {
+ setStage("finish");
+ }
+ break;
+
+ case "finish":
+ next();
+ break;
+
+ case "error":
+ setAddToUG(undefined);
+ setSysCtlEnable(undefined);
+ setStage("userGroup");
+ break;
+
+ default:
+ break;
+ }
+ } else if (key.leftArrow) {
+ switch (stage) {
+ case "sysCtlEnable":
+ setAddToUG(undefined);
+ setStage("userGroup");
+ break;
+
+ case "confirm":
+ setSysCtlEnable(undefined);
+ setStage("sysCtlEnable");
+ break;
+
+ default:
+ break;
+ }
+ }
+ });
+ return (
+
+
+ Docker Install Helper
+
+
+ {addToUG === undefined && (
+ {
+ setAddToUG(res);
+ setStage("sysCtlEnable");
+ }}
+ direction="vertical"
+ />
+ )}
+ {addToUG !== undefined && (
+
+ add to user group: {addToUG}
+
+ )}
+ {stage === "sysCtlEnable" && sysCtlEnable === undefined && (
+ {
+ setSysCtlEnable(res);
+ setStage("confirm");
+ }}
+ direction="vertical"
+ />
+ )}
+ {sysCtlEnable !== undefined && (
+
+ start on boot: {sysCtlEnable}
+
+ )}
+ {stage === "confirm" && (
+
+
+
+ press enter to install docker
+
+
+ )}
+ {(stage === "installing" || stage === "finish") && (
+
+ {stage === "installing" && installing...}
+
+ {logs.map(({ stage, log }) => {
+
+
+ {stage}
+
+
+ command: {log.command}
+
+ {log.log.stdout ? (
+
+ stdout: {log.log.stdout}
+
+ ) : undefined}
+ {log.log.stderr ? (
+
+ stderr: {log.log.stderr}
+
+ ) : undefined}
+
+ ;
+ })}
+
+ )}
+ {stage === "finish" && (
+
+ Docker has finished installing. Press enter{" "}
+ to continue.
+
+ )}
+ {stage === "error" && (
+
+ There was an error during install. Press{" "}
+ enter to try again.
+
+ )}
+
+ );
+};
+
+export default InstallDocker;
diff --git a/cli/src/components/util/YesNo.tsx b/cli/src/components/util/YesNo.tsx
index 3d941484c..cead4cdfe 100644
--- a/cli/src/components/util/YesNo.tsx
+++ b/cli/src/components/util/YesNo.tsx
@@ -5,12 +5,14 @@ const YesNo = ({
label,
onYes,
onNo,
+ onSelect,
direction,
labelColor
}: {
label: ReactNode;
onYes?: () => void;
onNo?: () => void;
+ onSelect?: (res: "yes" | "no") => void;
direction?: "vertical" | "horizontal";
labelColor?: "green" | "white"
}) => {
@@ -22,8 +24,9 @@ const YesNo = ({
if (item === "yes") {
onYes && onYes();
} else {
- onNo && onNo()
+ onNo && onNo();
}
+ onSelect && onSelect(item as "yes" | "no");
}}
direction={direction}
labelColor={labelColor}
diff --git a/cli/src/helpers/docker.ts b/cli/src/helpers/docker.ts
new file mode 100644
index 000000000..16968d2a5
--- /dev/null
+++ b/cli/src/helpers/docker.ts
@@ -0,0 +1,116 @@
+import { CommandLogError } from "@monitor/types";
+import { execute } from "./execute";
+
+export type InstallLog = {
+ stage: string;
+ log: CommandLogError;
+};
+
+export async function installDockerUbuntu(
+ onCommandEnd: (log: InstallLog) => void,
+ addToUserGroup?: boolean,
+ systemCtlEnable?: boolean
+) {
+ const total = 5 + (addToUserGroup ? 1 : 0) + (systemCtlEnable ? 1 : 0);
+ const update = await execute("sudo apt-get update");
+ if (update.isError) return {
+ stage: "error updating system",
+ log: update
+ };
+ onCommandEnd({
+ stage: `updated system (1 of ${total})`,
+ log: update,
+ });
+
+
+ const installDeps = await execute(`
+ sudo apt-get install \
+ ca-certificates \
+ curl \
+ gnupg \
+ lsb-release
+ `);
+ if (installDeps.isError) return {
+ stage: "error installing dependencies",
+ log: installDeps
+ };
+ onCommandEnd({
+ stage: `installed dependencies (2 of ${total})`,
+ log: installDeps,
+ });
+
+
+ const addKey = await execute(
+ "curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg"
+ );
+ if (addKey.isError) return {
+ stage: "error adding docker key",
+ log: addKey,
+ };
+ onCommandEnd({
+ stage: `added docker key (3 of ${total})`,
+ log: addKey,
+ });
+
+
+ const setStableRepository = await execute(`
+ echo \
+ "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
+ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
+ `);
+ if (setStableRepository.isError) return {
+ stage: "error adding stable repository",
+ log: setStableRepository,
+ };
+ onCommandEnd({
+ stage: `set docker stable repository (4 of ${total})`,
+ log: setStableRepository,
+ });
+
+ const installDocker = await execute(
+ "sudo apt-get udpate && sudo apt-get install docker-ce docker-ce-cli containerd.io"
+ );
+ if (installDocker.isError) return {
+ stage: "error installing docker",
+ log: installDocker,
+ };
+ onCommandEnd({
+ stage: `installed docker (5 of ${total})`,
+ log: installDocker,
+ });
+
+ if (addToUserGroup) {
+ const addUser = await execute(
+ "sudo groupadd docker && sudo usermod -aG docker $USER && newgrp docker"
+ );
+ if (addUser.isError) return {
+ stage: "error adding user to docker group",
+ log: addUser,
+ };
+ onCommandEnd({
+ stage: `added user to docker user group (6 of ${total})`,
+ log: addUser,
+ });
+ }
+
+ if (systemCtlEnable) {
+ const startOnBoot = await execute(
+ "sudo systemctl enable docker.service && sudo systemctl enable containerd.service"
+ );
+ if (startOnBoot.isError) return {
+ stage: "error configuring to start on boot",
+ log: startOnBoot,
+ };
+ onCommandEnd({
+ stage: `configured to start on boot (7 of ${total})`,
+ log: startOnBoot,
+ });
+ }
+
+ return;
+}
+
+export async function checkDockerNotInstalled() {
+ const res = await execute("docker ps");
+ return res.isError
+}
diff --git a/cli/src/monitor-cli.tsx b/cli/src/monitor-cli.tsx
index bf3cfe660..80ea13ca0 100644
--- a/cli/src/monitor-cli.tsx
+++ b/cli/src/monitor-cli.tsx
@@ -3,6 +3,7 @@ import React from "react";
import { render } from "ink";
// import meow from "meow";
import App from "./App";
+import { checkDockerNotInstalled } from "./helpers/docker";
// const cli = meow(
// `
@@ -24,5 +25,8 @@ import App from "./App";
// },
// }
// );
-
-render();
+export let dockerNotInstalled = true;
+checkDockerNotInstalled().then((res) => {
+ dockerNotInstalled = res;
+ render();
+});
\ No newline at end of file
diff --git a/cli/src/types.d.ts b/cli/src/types.d.ts
index db02ed88b..a2c718a31 100644
--- a/cli/src/types.d.ts
+++ b/cli/src/types.d.ts
@@ -7,3 +7,5 @@ export type SetConfig = (
field: keyof Config,
val: string | number | boolean
) => void;
+
+export type Next = () => void;