This commit is contained in:
mbecker20
2022-03-29 23:48:08 -07:00
parent 78814198d2
commit eface85e1f
18 changed files with 266 additions and 86 deletions

View File

@@ -1,6 +1,6 @@
FROM node:alpine
RUN apk update && apk add docker git openrc yarn --no-cache
RUN apk update && apk add docker git openrc yarn python3 --no-cache
RUN addgroup root docker && rc-update add docker boot
COPY ./frontend /frontend

View File

@@ -40,7 +40,11 @@ async function build(
const { cliBuild, dockerBuildArgs } = build;
try {
const cli = cliBuild
? await execute(`cd ${cliBuild.path} && ${cliBuild.command}`)
? await execute(
`cd ${BUILD_REPO_PATH}${
cliBuild.path ? (cliBuild.path[0] === "/" ? "" : "/") : ""
}${cliBuild.path} && ${cliBuild.command}`
)
: undefined;
const docker = dockerBuildArgs
? await dockerBuild(

View File

@@ -1,7 +1,8 @@
import { Deployment, User } from "@monitor/types";
import { deploymentChangelog, UPDATE_DEPLOYMENT } from "@monitor/util";
import { FastifyInstance } from "fastify";
import { PERMISSIONS_DENY_LOG } from "../../config";
import { remove } from "fs-extra";
import { DEPLOYMENT_REPO_PATH, PERMISSIONS_DENY_LOG } from "../../config";
import { clonePeriphery } from "../../util/periphery/git";
import { addDeploymentUpdate } from "../../util/updates";
import cloneRepo from "./clone";
@@ -36,10 +37,18 @@ async function updateDeployment(
deployment.serverID === app.core._id
? undefined
: await app.servers.findById(deployment.serverID!);
if (server) {
await clonePeriphery(server, deployment);
if (deployment.repo) {
if (server) {
await clonePeriphery(server, deployment);
} else {
await cloneRepo(app, user, deployment);
}
} else {
await cloneRepo(app, user, deployment);
if (server) {
// need to make this route
} else {
await remove(DEPLOYMENT_REPO_PATH + deployment.containerName); // need to have this on periphery as well
}
}
}
// make sure owners cant be updated this way

View File

@@ -1,4 +1,5 @@
import { Component, Show } from "solid-js";
import { BUILD } from "../../state/actions";
import { useAppState } from "../../state/StateProvider";
import { combineClasses } from "../../util/helpers";
import ConfirmButton from "../util/ConfirmButton";
@@ -8,21 +9,23 @@ import Grid from "../util/layout/Grid";
import s from "./build.module.css";
const Actions: Component<{}> = (p) => {
const { builds, selected } = useAppState();
const { builds, selected, ws } = useAppState();
const build = () => builds.get(selected.id())!;
return (
<Show when={build()}>
<Grid class={combineClasses(s.Card, "shadow")}>
<h1>actions</h1>
<Flex class={combineClasses(s.Action, "shadow")}>
build{" "}
<ConfirmButton color="green">
<Icon type="build" />
</ConfirmButton>
</Flex>
</Grid>
</Show>
);
}
return (
<Show when={build()}>
<Grid class={combineClasses(s.Card, "shadow")}>
<h1>actions</h1>
<Flex class={combineClasses(s.Action, "shadow")}>
build{" "}
<ConfirmButton color="green" onConfirm={() => {
ws.send(BUILD, {})
}}>
<Icon type="build" />
</ConfirmButton>
</Flex>
</Grid>
</Show>
);
};
export default Actions;
export default Actions;

View File

@@ -1,11 +1,11 @@
import { Build, Update } from "@monitor/types";
import { Component, createContext, createEffect, onCleanup, useContext } from "solid-js";
import { Accessor, Component, createContext, createEffect, createSignal, onCleanup, useContext } from "solid-js";
import { createStore, DeepReadonly, SetStoreFunction } from "solid-js/store";
import { ADD_UPDATE, UPDATE_BUILD } from "../../../../state/actions";
import { useAppState } from "../../../../state/StateProvider";
import { getBuild } from "../../../../util/query";
import { ADD_UPDATE, UPDATE_BUILD } from "../../../state/actions";
import { useAppState } from "../../../state/StateProvider";
import { getBuild } from "../../../util/query";
type ConfigBuild = Build & { loaded: boolean; updated: boolean };
type ConfigBuild = Build & { loaded: boolean; updated: boolean; saving: boolean };
type State = {
build: DeepReadonly<ConfigBuild>;
@@ -22,12 +22,14 @@ export const ConfigProvider: Component<{ build: Build }> = (p) => {
...p.build,
loaded: false,
updated: false,
saving: false,
});
const setBuild = (...args: any) => {
const setBuild: SetStoreFunction<ConfigBuild> = (...args: any) => {
// @ts-ignore
set(...args);
set("updated", true);
};
const load = () => {
console.log("load server");
getBuild(p.build._id!).then((build) => {
@@ -35,11 +37,14 @@ export const ConfigProvider: Component<{ build: Build }> = (p) => {
...build,
loaded: true,
updated: false,
saving: false
});
});
};
createEffect(load);
const save = () => {
setBuild("saving", true);
ws.send(UPDATE_BUILD, { build });
};

View File

@@ -3,8 +3,9 @@ import { useAppState } from "../../../state/StateProvider";
import { combineClasses } from "../../../util/helpers";
import Tabs from "../../util/tabs/Tabs";
import s from "../build.module.css";
import Config from "./config/Config";
import { ConfigProvider } from "./config/Provider";
import BuildConfig from "./build-config/BuildConfig";
import GitConfig from "./git-config/GitConfig";
import { ConfigProvider } from "./Provider";
const BuildTabs: Component<{}> = (p) => {
const { builds, selected } = useAppState();
@@ -16,9 +17,13 @@ const BuildTabs: Component<{}> = (p) => {
containerClass={combineClasses(s.Card, s.Tabs, "shadow")}
tabs={[
{
title: "config",
element: <Config />,
title: "repo",
element: <GitConfig />,
},
{
title: "build",
element: <BuildConfig />
}
]}
localStorageKey="build-tab"
/>

View File

@@ -7,18 +7,16 @@ import Grid from "../../../util/layout/Grid";
import s from "../../build.module.css";
import CliBuild from "./CliBuild";
import Docker from "./Docker";
import Git from "./Git";
import { useConfig } from "./Provider";
import { useConfig } from "../Provider";
const Config: Component<{}> = (p) => {
const { build, reset, save } = useConfig();
return (
const BuildConfig: Component<{}> = (p) => {
const { build, reset, save } = useConfig();
return (
<Show when={build.loaded}>
<Grid class={s.Config}>
<Grid class={combineClasses(s.ConfigItems, "scroller")}>
<Git />
<CliBuild />
<Docker />
<Docker />
</Grid>
<Show when={build.updated}>
<Flex style={{ "place-self": "center", padding: "1rem" }}>
@@ -35,6 +33,6 @@ const Config: Component<{}> = (p) => {
</Grid>
</Show>
);
}
};
export default Config;
export default BuildConfig;

View File

@@ -4,7 +4,7 @@ import Input from "../../../util/Input";
import Flex from "../../../util/layout/Flex";
import Grid from "../../../util/layout/Grid";
import s from "../../build.module.css";
import { useConfig } from "./Provider";
import { useConfig } from "../Provider";
const CliBuild: Component<{}> = (p) => {
const { build, setBuild } = useConfig();
@@ -17,7 +17,7 @@ const CliBuild: Component<{}> = (p) => {
<Input
placeholder="from root of repo"
value={build.cliBuild?.path}
onEdit={(value) => setBuild("cliBuild", "path", value)}
onEdit={(path) => setBuild("cliBuild", { path })}
/>
</Flex>
<Flex justifyContent="space-between" alignItems="center">
@@ -25,7 +25,7 @@ const CliBuild: Component<{}> = (p) => {
<Input
placeholder="ie. yarn build"
value={build.cliBuild?.command}
onEdit={(value) => setBuild("cliBuild", "command", value)}
onEdit={(command) => setBuild("cliBuild", { command })}
/>
</Flex>
</Grid>

View File

@@ -4,7 +4,7 @@ import Input from "../../../util/Input";
import Flex from "../../../util/layout/Flex";
import Grid from "../../../util/layout/Grid";
import s from "../../build.module.css";
import { useConfig } from "./Provider";
import { useConfig } from "../Provider";
const Docker: Component<{}> = (p) => {
const { build, setBuild } = useConfig();

View File

@@ -1,7 +1,7 @@
import { Component } from "solid-js";
import { combineClasses } from "../../../../util/helpers";
import Grid from "../../../util/layout/Grid";
import { useConfig } from "./Provider";
import { useConfig } from "../Provider";
import s from "../../build.module.css";
import Flex from "../../../util/layout/Flex";
import Input from "../../../util/Input";
@@ -39,4 +39,4 @@ const Git: Component<{}> = (p) => {
);
};
export default Git;
export default Git;

View File

@@ -0,0 +1,43 @@
import { Component, Show } from "solid-js";
import { combineClasses } from "../../../../util/helpers";
import ConfirmButton from "../../../util/ConfirmButton";
import Icon from "../../../util/icons/Icon";
import Flex from "../../../util/layout/Flex";
import Grid from "../../../util/layout/Grid";
import Loading from "../../../util/loading/Loading";
import s from "../../build.module.css";
import { useConfig } from "../Provider";
import Git from "./Git";
import OnClone from "./OnClone";
const GitConfig: Component<{}> = (p) => {
const { build, reset, save } = useConfig();
return (
<Show when={build.loaded}>
<Grid class={s.Config}>
<Grid class={combineClasses(s.ConfigItems, "scroller")}>
<Git />
<OnClone />
</Grid>
<Show when={build.updated}>
<Show when={!build.saving} fallback={<button class="green">
<Loading type="spinner" />
</button>}>
<Flex style={{ "place-self": "center", padding: "1rem" }}>
<button onClick={reset}>
reset
<Icon type="reset" />
</button>
<ConfirmButton onConfirm={save} color="green">
save
<Icon type="floppy-disk" />
</ConfirmButton>
</Flex>
</Show>
</Show>
</Grid>
</Show>
);
};
export default GitConfig;

View File

@@ -0,0 +1,31 @@
import { Component } from "solid-js";
import { combineClasses } from "../../../../util/helpers";
import Input from "../../../util/Input";
import Grid from "../../../util/layout/Grid";
import { useConfig } from "../Provider";
import s from "../../build.module.css";
const OnClone: Component = () => {
const { build, setBuild } = useConfig();
return (
<Grid class={combineClasses(s.ConfigItem, "shadow")}>
<h1>on clone</h1>
<Input
placeholder="path"
value={build.onClone?.path}
onEdit={(path) => {
setBuild("onClone", { path });
}}
/>
<Input
placeholder="command"
value={build.onClone?.command}
onEdit={(command) => {
setBuild("onClone", { command });
}}
/>
</Grid>
);
};
export default OnClone;

View File

@@ -89,7 +89,7 @@ const Update: Component<{ update: UpdateType; showName: boolean }> = (p) => {
</pre>
<Show when={p.update.log.stderr}>
<div>stderr</div>
<pre class={s.Log}>{p.update.log.stderr}</pre>
<pre class={combineClasses(s.Log, "scroller")}>{p.update.log.stderr}</pre>
</Show>
<Show when={p.update.log.stdout}>
<div>stdout</div>

View File

@@ -0,0 +1,26 @@
import { Component, JSXElement } from "solid-js";
import Flex from "../layout/Flex";
const Toggle: Component<{
label?: JSXElement;
toggled: boolean;
onChange?: (toggled: boolean) => void;
}> = (p) => {
return (
<Flex alignItems="center" justifyContent="space-between">
{p.label}
<label class="switch">
<input
type="checkbox"
checked={p.toggled}
onInput={() => {
p.onChange && p.onChange(!p.toggled);
}}
/>
<span class="slider round"></span>
</label>
</Flex>
);
};
export default Toggle;

View File

@@ -0,0 +1,65 @@
export function toggleCss(scale = 0.75) {
return `
.toggle {
position: relative;
display: inline-block;
width: ${60 * scale}px;
height: ${34 * scale}px;
}
/* Hide default HTML checkbox */
.toggle input {
opacity: 0;
width: 0;
height: 0;
}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #2196F3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(${26 * scale}px);
-ms-transform: translateX(${26 * scale}px);
transform: translateX(${26 * scale}px);
}
/* Rounded sliders */
.slider.round {
border-radius: ${30 * scale}px;
}
.slider.round:before {
border-radius: 50%;
}
`;
}

2
types/types.d.ts vendored
View File

@@ -60,7 +60,7 @@ export type DockerBuildArgs = {
// maybe best for admins to add premade curated command strings, so user dev can't input them directly, only give path to run in.
export type Command = {
name: string;
path: string;
path?: string;
command: string; // no cd and no sudo
};

View File

@@ -1,7 +1,13 @@
import { Build, Conversion, Deployment, EnvironmentVar, Server } from "@monitor/types";
import {
Build,
Conversion,
Deployment,
EnvironmentVar,
Server,
} from "@monitor/types";
import { objFrom2Arrays } from "./helpers";
const deploymentViewFields = [
const deploymentViewFields: (keyof Deployment)[] = [
"name",
"image",
"buildID",
@@ -9,10 +15,7 @@ const deploymentViewFields = [
"volumes",
"environment",
"network",
"useServerRoot",
"restart",
"logTail",
"autoDeploy",
"containerUser",
"postImage",
]; // the fields shown in the update log
@@ -196,18 +199,12 @@ function volumesChangelog(oldVols?: Conversion[], newVols?: Conversion[]) {
return "";
} else {
return `Added Volumes:\n${newVols
.map(
(vol) =>
`\t${vol.local}: ${vol.container},\n`
)
.map((vol) => `\t${vol.local}: ${vol.container},\n`)
.reduce((prev, curr) => prev + curr)}`;
}
} else if (!newVols) {
return `Removed Volumes:\n${oldVols
.map(
(vol) =>
`\t${vol.local}: ${vol.container},\n`
)
.map((vol) => `\t${vol.local}: ${vol.container},\n`)
.reduce((prev, curr) => prev + curr)}`;
} else {
const oldLocal = oldVols.map((vol) => vol.local);
@@ -221,10 +218,7 @@ function volumesChangelog(oldVols?: Conversion[], newVols?: Conversion[]) {
const deletions: string[] = [];
oldLocal.forEach((local) => {
if (newLocal.includes(local)) {
if (
oldObj[local] !== newObj[local]
)
changes.push(local);
if (oldObj[local] !== newObj[local]) changes.push(local);
} else {
deletions.push(local);
}
@@ -239,10 +233,7 @@ function volumesChangelog(oldVols?: Conversion[], newVols?: Conversion[]) {
additions.length > 0
? `\tAdditions:\n` +
additions
.map(
(addition) =>
`\t\t${addition}: ${newObj[addition]},\n`
)
.map((addition) => `\t\t${addition}: ${newObj[addition]},\n`)
.reduce((prev, curr) => prev + curr)
: "";
const changesString =
@@ -259,10 +250,7 @@ function volumesChangelog(oldVols?: Conversion[], newVols?: Conversion[]) {
deletions.length > 0
? `\tDeletions:\n` +
deletions
.map(
(deletion) =>
`\t\t${deletion}: ${oldObj[deletion]},\n`
)
.map((deletion) => `\t\t${deletion}: ${oldObj[deletion]},\n`)
.reduce((prev, curr) => prev + curr)
: "";
const show =
@@ -275,14 +263,13 @@ function volumesChangelog(oldVols?: Conversion[], newVols?: Conversion[]) {
}
}
const buildViewFields = [
const buildViewFields: (keyof Build)[] = [
"name",
"repoURL",
"repoName",
"repo",
"branch",
"buildPath",
"dockerfilePath",
"owner",
"onClone",
"cliBuild",
"dockerBuildArgs",
]; // the fields shown in the update log
export function buildChangelog(oldBuild: Build, newBuild: Build) {

View File

@@ -69,12 +69,12 @@ export function generateQuery(query?: Collection<string | number | undefined>) {
}
export function mergeCommandLogError(
...cle: ({ name: string; cle: CommandLogError | undefined })[]
...cle: { name: string; cle: CommandLogError | undefined }[]
) {
const _cle = cle.filter((cle) => cle.cle) as {
name: string;
cle: CommandLogError;
}[];
}[];
const command = _cle.reduce((prev, curr) => {
return prev + (prev && "\n\n") + `${curr.name}: ${curr.cle.command}`;
}, "");
@@ -82,12 +82,16 @@ export function mergeCommandLogError(
(log, curr) => {
log.stdout =
log.stdout +
(log.stdout && "\n\n") +
`${curr.name}:\n${curr.cle.log.stdout}`;
(log.stdout && (curr.cle.log.stdout ? "\n\n" : "")) +
curr.cle.log.stdout
? `${curr.name}:\n${curr.cle.log.stdout}`
: "";
log.stderr =
log.stderr +
(log.stderr && "\n\n") +
`${curr.name}:\n${curr.cle.log.stderr}`;
(log.stderr && (curr.cle.log.stderr ? "\n\n" : "")) +
curr.cle.log.stderr
? `${curr.name}:\n${curr.cle.log.stderr}`
: "";
return log;
},
{ stdout: "", stderr: "" }