This commit is contained in:
2026-03-14 07:19:46 +01:00
parent 89975d96cb
commit cec584c177
54 changed files with 774 additions and 313 deletions
@@ -2,18 +2,15 @@ import { AppContext } from "@/src/pages/_app";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
} from "@/src/types";
} from "@/src/types/turboci";
import ArrowedLink from "@/twui/components/layout/ArrowedLink";
import Button from "@/twui/components/layout/Button";
import H2 from "@/twui/components/layout/H2";
import Row from "@/twui/components/layout/Row";
import Stack from "@/twui/components/layout/Stack";
import { useContext, useEffect, useRef, useState } from "react";
import ServiceClusterServer from "../service/(partials)/cluster-server";
import { twMerge } from "tailwind-merge";
import Select from "@/twui/components/form/Select";
import useStatus from "@/twui/components/hooks/useStatus";
import Loading from "@/twui/components/elements/Loading";
import _ from "lodash";
type Props = {
@@ -1,5 +1,5 @@
import { Dispatch, Fragment, SetStateAction, useContext, useRef } from "react";
import { ParsedDeploymentServiceConfig } from "@/src/types";
import { ParsedDeploymentServiceConfig } from "@/src/types/turboci";
import Select, { TWUISelectOptionObject } from "@/twui/components/form/Select";
import Row from "@/twui/components/layout/Row";
import Button from "@/twui/components/layout/Button";
@@ -3,7 +3,7 @@ import { Dispatch, SetStateAction } from "react";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
} from "@/src/types";
} from "@/src/types/turboci";
import Row from "@/twui/components/layout/Row";
import ServiceClusterServerLogSelectorSetCustomLog from "./cluster-server-log-selector-set-custom-log";
import ServiceClusterServerLogSelectorSelectLog from "./cluster-server-log-selector-select-log";
@@ -2,8 +2,6 @@ import Stack from "@/twui/components/layout/Stack";
import { RefObject, useContext, useEffect, useRef, useState } from "react";
import { AppContext } from "@/src/pages/_app";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
ServerTerminalTargets,
TtydInfoObject,
WebSocketDataType,
@@ -15,6 +13,10 @@ import Center from "@/twui/components/layout/Center";
import TtydIframe from "@/src/components/general/ttyd-iframe";
import useIntersectionObserver from "@/twui/components/hooks/useIntersectionObserver";
import useStatus from "@/twui/components/hooks/useStatus";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
} from "@/src/types/turboci";
type Props = {
service: ParsedDeploymentServiceConfig;
@@ -4,8 +4,7 @@ import { AppContext } from "@/src/pages/_app";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
ServerTerminalTargets,
} from "@/src/types";
} from "@/src/types/turboci";
import useIntersectionObserver from "@/twui/components/hooks/useIntersectionObserver";
import Center from "@/twui/components/layout/Center";
import Loading from "@/twui/components/elements/Loading";
@@ -14,6 +13,7 @@ import Row from "@/twui/components/layout/Row";
import Button from "@/twui/components/layout/Button";
import ServiceClusterServerLogSelector from "./cluster-server-log-selector";
import { twMerge } from "tailwind-merge";
import { ServerTerminalTargets } from "@/src/types";
type Props = {
service: ParsedDeploymentServiceConfig;
@@ -51,6 +51,7 @@ export default function ServiceClusterServer({
<Row className="w-full justify-between p-4 -mb-6">
<Row>
<code>{server.private_ip}</code>
{server.public_ip ? <code>{server.public_ip}</code> : null}
</Row>
<Row className="">
@@ -4,7 +4,7 @@ import { AppContext } from "@/src/pages/_app";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
} from "@/src/types";
} from "@/src/types/turboci";
import Row from "@/twui/components/layout/Row";
import ServiceClusterServer from "./cluster-server";
@@ -2,15 +2,9 @@ import Stack from "@/twui/components/layout/Stack";
import { useContext, useState } from "react";
import { AppContext } from "@/src/pages/_app";
import ServiceCluster from "../(partials)/cluster";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
} from "@/src/types";
import Button from "@/twui/components/layout/Button";
import { ParsedDeploymentServiceConfig } from "@/src/types/turboci";
import Row from "@/twui/components/layout/Row";
import { twMerge } from "tailwind-merge";
import Select from "@/twui/components/form/Select";
import ServiceClusterServer from "../(partials)/cluster-server";
import H2 from "@/twui/components/layout/H2";
import _ from "lodash";
-77
View File
@@ -1,77 +0,0 @@
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
} from "@/src/types";
import execSSH from "@/src/utils/exec-ssh";
import serviceFlight from "@/src/utils/flight";
import grabTurboCiConfig from "@/src/utils/grab-turboci-config";
export default async function cronCheckServices() {
const config = grabTurboCiConfig();
for (let i = 0; i < config.services.length; i++) {
const service = config.services[i];
if (!service.servers) {
continue;
}
for (let srv = 0; srv < service.servers.length; srv++) {
const server = service.servers[srv];
if (service.healthcheck) {
const test = await healthcheck({ server, service });
if (!test) {
console.log(
`Server ${server.private_ip} down. Restarting ...`,
);
const MAX_RETRIES = 5;
let retries = 0;
while (retries < MAX_RETRIES) {
console.log(`Retryig #${retries + 1} ...`);
await serviceFlight({
deployment: config,
servers: [server],
service,
});
await Bun.sleep(4000);
const retest = await healthcheck({ server, service });
if (retest) {
break;
} else {
retries++;
}
}
}
}
}
}
}
async function healthcheck({
server,
service,
}: {
service: ParsedDeploymentServiceConfig;
server: NormalizedServerObject;
}) {
if (!service.healthcheck?.cmd || !server.private_ip) {
return false;
}
const res = await execSSH({
cmd: service.healthcheck.cmd,
ip: server.private_ip,
});
const test = Boolean(res?.match(service.healthcheck.test));
return test;
}
+38
View File
@@ -0,0 +1,38 @@
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
TCIGlobalConfig,
} from "@/src/types/turboci";
import execSSH from "@/src/utils/exec-ssh";
import serviceFlight from "@/src/utils/flight";
type Params = {
service: ParsedDeploymentServiceConfig;
config: TCIGlobalConfig;
};
export default async function cronCheckServicesGit({
config,
service,
}: Params) {
if (service.git) {
const service_git_array = Array.isArray(service.git)
? service.git
: [service.git];
for (let i = 0; i < service_git_array.length; i++) {
const service_git = service_git_array[i];
if (!service_git.keep_updated) {
continue;
}
const work_dir = service_git.work_dir || "/turboci/app";
let cmd = ``;
cmd += `set -e\n`;
cmd += `\n`;
}
}
}
@@ -0,0 +1,71 @@
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
TCIGlobalConfig,
} from "@/src/types/turboci";
import execSSH from "@/src/utils/exec-ssh";
import serviceFlight from "@/src/utils/flight";
type Params = {
service: ParsedDeploymentServiceConfig;
server: NormalizedServerObject;
config: TCIGlobalConfig;
};
export default async function cronCheckServicesHealtcheck({
config,
server,
service,
}: Params) {
if (service.healthcheck) {
const test = await healthcheck({ server, service });
if (!test) {
console.log(`Server ${server.private_ip} down. Restarting ...`);
const MAX_RETRIES = 5;
let retries = 0;
while (retries < MAX_RETRIES) {
console.log(`Retryig #${retries + 1} ...`);
await serviceFlight({
deployment: config,
servers: [server],
service,
});
await Bun.sleep(4000);
const retest = await healthcheck({ server, service });
if (retest) {
break;
} else {
retries++;
}
}
}
}
}
async function healthcheck({
server,
service,
}: {
service: ParsedDeploymentServiceConfig;
server: NormalizedServerObject;
}) {
if (!service.healthcheck?.cmd || !server.private_ip) {
return false;
}
const res = await execSSH({
cmd: service.healthcheck.cmd,
ip: server.private_ip,
});
const test = Boolean(res?.match(service.healthcheck.test));
return test;
}
@@ -0,0 +1,22 @@
import grabTurboCiConfig from "@/src/utils/grab-turboci-config";
import cronCheckServicesHealtcheck from "./healthcheck";
import serviceGitCheck from "../../utils/service-git-check";
export default async function cronCheckServices() {
const config = grabTurboCiConfig();
for (let i = 0; i < config.services.length; i++) {
const service = config.services[i];
if (!service.servers) {
continue;
}
await serviceGitCheck({ deployment: config, service });
for (let srv = 0; srv < service.servers.length; srv++) {
const server = service.servers[srv];
await cronCheckServicesHealtcheck({ config, server, service });
}
}
}
+112
View File
@@ -0,0 +1,112 @@
import {
ParsedDeploymentServiceConfig,
ResponseObject,
TCIGlobalConfig,
} from "@/src/types/turboci";
import bunGrabBulkSyncScripts from "@/src/utils/bun-grab-bulk-sync-script";
import serviceFlight from "@/src/utils/flight";
import grabGitRepoName from "@/src/utils/grab-git-repo-name";
import relayExecSSH from "@/src/utils/relay-exec-ssh";
import turboCIPkgrabDirNames from "@/src/utils/turboci-pkg-grab-dir-names";
import _ from "lodash";
import path from "path";
const { relayServerRsyncDir } = turboCIPkgrabDirNames();
type Params = {
service: ParsedDeploymentServiceConfig;
deployment: TCIGlobalConfig;
};
export default async function serviceGitCheck(
params: Params,
): Promise<ResponseObject> {
const { service, deployment } = params;
if (!service.git || !service.servers?.[0]) {
return { success: true };
}
const servers_private_ips = service.servers
.map((srv) => srv.private_ip)
.filter((ip) => Boolean(ip)) as string[];
const git_array = Array.isArray(service.git) ? service.git : [service.git];
for (let i = 0; i < git_array.length; i++) {
const service_git = git_array[i];
if (!service_git) continue;
const git_url = service_git.repo_url;
const repo_name = grabGitRepoName({ git_url });
if (!repo_name) continue;
const relay_dst = path.join(
relayServerRsyncDir,
service.service_name,
"git",
repo_name,
);
let git_pull_cmd = ``;
git_pull_cmd += `cd ${relay_dst}\n`;
git_pull_cmd += `git pull\n`;
const git_pull_check = await relayExecSSH({
cmd: git_pull_cmd,
log_error: true,
});
if (git_pull_check?.match(/Already up to date./i)) {
continue;
}
let cmd = ``;
const src = relay_dst + "/";
const dst = (service_git.work_dir || "/turboci/app") + "/";
const sync_cmd = await relayExecSSH({
cmd: bunGrabBulkSyncScripts({
dst,
src,
private_server_ips: servers_private_ips,
parrallel: true,
relay_ignore: [".git"],
}),
bun: true,
return_cmd_only: true,
});
cmd += `${sync_cmd}\n`;
cmd += `echo "Git Setup Success!"\n`;
const res = await relayExecSSH({
cmd,
log_error: true,
debug: true,
});
if (!res?.match(/Git Setup Success/)) {
console.error(
`\`${service.service_name}\` service git prep failed!`,
);
continue;
}
await serviceFlight({
deployment,
servers: service.servers,
service,
});
}
return {
success: true,
};
}
+2 -4
View File
@@ -2,13 +2,11 @@ import {
NSQLITE_TURBOCI_ADMIN_USERS_PORTS,
NSQLiteTables,
} from "@/src/db/types";
import { PrivateServerTtydParadigms, TtydInfoObject, User } from "@/src/types";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
PrivateServerTtydParadigms,
TtydInfoObject,
User,
} from "@/src/types";
} from "@/src/types/turboci";
import grabDirNames from "@/src/utils/grab-dir-names";
import getNextAvailablePort from "@/src/utils/grab-next-available-port";
import grabSSHPrefix from "@/src/utils/grab-ssh-prefix";
+5 -167
View File
@@ -4,178 +4,16 @@ import useAppInit from "../hooks/use-app-init";
import { ServerWebSocket } from "bun";
import { ChildProcess } from "child_process";
import { NSQLITE_TURBOCI_ADMIN_USERS } from "../db/types";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
TCIGlobalConfig,
} from "./turboci";
export type User = DATASQUIREL_LoggedInUser & {
super_admin?: boolean;
};
export const CloudProviders = [
{
title: "Hetzner",
value: "hetzner",
},
{
title: "Amazon Web Services",
value: "aws",
},
{
title: "Google Cloud Platform",
value: "gcp",
},
{
title: "Microsoft Azure",
value: "azure",
},
] as const;
export type ParsedDeploymentServiceConfig = TCIConfigServiceConfig & {
service_name: string;
parent_service_name?: string;
servers?: NormalizedServerObject[];
};
export type NormalizedServerObject = {
public_ip?: string;
private_ip?: string;
};
export type TCIConfig = {
deployment_name: string;
duplicate_deployment_name?: string;
description?: string;
location?: string;
availability_zone?: string;
provider: (typeof CloudProviders)[number]["value"];
services: TCIConfigService;
env?: { [k: string]: string };
env_file?: string;
pre_deployment?: TCIRunObj;
};
export type TCIGlobalConfig = Omit<TCIConfig, "services"> & {
services: ParsedDeploymentServiceConfig[];
relay_server_ip?: string;
};
export type TCIConfigService = {
[k: string]: TCIConfigServiceConfig;
};
export const TCIServiceTypes = [
{
title: "Default Service",
value: "default",
},
{
title: "Docker",
value: "docker",
},
{
title: "Load Balancer",
value: "load_balancer",
},
] as const;
export const TCIServiceOS = [
{
title: "Debian 12 Bookworm",
value: "debian_12",
},
{
title: "Debian 13 Buster",
value: "debian_13",
},
{
title: "Ubuntu 23.0.4",
value: "ubuntu_23_0_4",
},
] as const;
export const TCIServiceDependecyTypes = [
{
title: "Debian APT",
value: "apt",
},
{
title: "Turbo CI",
value: "turboci",
},
] as const;
export type TCIConfigServiceConfig = {
type?: (typeof TCIServiceTypes)[number]["value"];
os?: string;
server_type?: string;
enable_public_ip?: boolean;
instances?: number;
clusters?: number;
dir_mappings?: TCIConfigServiceConfigDirMApping[];
dependencies?: {
[k in (typeof TCIServiceDependecyTypes)[number]["value"]]?: string[];
};
env?: { [k: string]: string };
env_file?: string;
target_services?: TCIConfigServiceConfigLBTarget[];
run?: TCIConfigServiceConfigRun;
ssl?: TCIConfigServiceSSL;
duplicate_service_name?: string;
healthcheck?: TCIConfigServiceHealthcheck;
/**
* Commoands to Run on first run
*/
init?: string[];
logs?: TCIConfigServiceConfigLog[];
};
export type TCIConfigServiceConfigLog =
| string
| {
cmd: string;
};
export type TCIConfigServiceHealthcheck = {
cmd: string;
test: string;
};
export type TCIConfigServiceDomain = {
domain_name: string;
};
export type TCIConfigServiceSSL = {
email: string;
};
export type TCIConfigServiceConfigLBTarget = {
service_name: string;
port: number;
weight?: number;
backup?: boolean;
domains?: (string | TCIConfigServiceDomain)[];
};
export type TCIConfigServiceConfigRun = {
preflight?: TCIRunObj;
start?: TCIRunObj;
postflight?: TCIRunObj;
work_dir?: string;
};
export type TCIRunObj = {
cmds?: string[];
work_dir?: string;
file?: string;
};
export type TCIConfigServiceConfigDirMApping = {
src: string;
dst: string;
ignore_file?: string;
ignore_patterns?: string[];
use_gitignore?: boolean;
relay_ignore?: string[];
};
export type ServiceScriptObject = {
sh: string;
service_name: string;
+363
View File
@@ -0,0 +1,363 @@
import type { ExecSyncOptions } from "child_process";
export const TCICommands = [
{
name: "up",
description: "Deploy Stack",
},
{
name: "down",
description: "Destroy Stack",
},
] as const;
export interface PackageJson {
name?: string;
version?: string;
description?: string;
bin?: Record<string, string>;
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
[key: string]: any;
}
export const CloudProviders = [
{
title: "Hetzner",
value: "hetzner",
},
{
title: "Amazon Web Services",
value: "aws",
},
{
title: "Google Cloud Platform",
value: "gcp",
},
{
title: "Microsoft Azure",
value: "azure",
},
] as const;
export type GrabConfigReturn = {
deployments: TCIGlobalConfig[];
envs?: string[];
};
export type TCIConfig =
| TCIConfigDeployment[]
| {
deployments: TCIConfigDeployment[];
envs?: string[];
};
export type TCIConfigDeployment = {
deployment_name: string;
duplicate_deployment_name?: string;
description?: string;
location?: string;
availability_zone?: string;
provider: (typeof CloudProviders)[number]["value"];
services: TCIConfigService;
env?: { [k: string]: string };
env_file?: string;
pre_deployment?: TCIRunObj;
relay_server_options?: TCIConfigRelayServerOptions;
};
export type TCIConfigRelayServerOptions = {
server_type?: string;
};
export type TCIGlobalConfig = Omit<TCIConfigDeployment, "services"> & {
services: ParsedDeploymentServiceConfig[];
relay_server_ip?: string;
};
export type TCIConfigService = {
[k: string]: TCIConfigServiceConfig;
};
export const TCIServiceTypes = [
{
title: "Default Service",
value: "default",
},
{
title: "Docker",
value: "docker",
},
{
title: "Load Balancer",
value: "load_balancer",
},
] as const;
export const TCIServiceOS = [
{
title: "Debian 12 Bookworm",
value: "debian_12",
},
{
title: "Debian 13 Buster",
value: "debian_13",
},
{
title: "Ubuntu 23.0.4",
value: "ubuntu_23_0_4",
},
] as const;
export const TCIServiceDependecyTypes = [
{
title: "Debian APT",
value: "apt",
},
{
title: "Turbo CI",
value: "turboci",
},
] as const;
export const TCIGitParadigms = [
{
title: "Github",
value: "github",
},
{
title: "Gitlab",
value: "gitlab",
},
{
title: "Gitea",
value: "gitea",
},
] as const;
export const TCIContainerRegistryParadigms = [
{
title: "Docker Hub",
value: "dockerhub",
},
{
title: "Github Container Registry",
value: "ghcr",
},
] as const;
export type TCIConfigServiceConfig = {
type?: (typeof TCIServiceTypes)[number]["value"];
os?: string;
server_type?: string;
enable_public_ip?: boolean;
instances?: number;
clusters?: number;
dir_mappings?: TCIConfigServiceConfigDirMApping[];
dependencies?: {
[k in (typeof TCIServiceDependecyTypes)[number]["value"]]?: string[];
};
env?: { [k: string]: string };
env_file?: string;
target_services?: TCIConfigServiceConfigLBTarget[];
run?: TCIConfigServiceConfigRun;
ssl?: TCIConfigServiceSSL;
duplicate_service_name?: string;
healthcheck?: TCIConfigServiceHealthcheck;
/**
* Commoands to Run on first run
*/
init?: string[];
logs?: TCIConfigServiceConfigLog[];
git?: TCIConfigServiceConfigGit | TCIConfigServiceConfigGit[];
};
export type TCIConfigServiceConfigGit = {
paradigm?: (typeof TCIGitParadigms)[number]["value"];
repo_url: string;
branch?: string;
/**
* Directory in target servers where the repo should
* live. Defaults to `/app`
*/
work_dir?: string;
public_repo?: boolean;
username?: string;
api_key?: string;
/**
* If true, this will continuosly pull from the git source
* and rerun the flight commands
*/
keep_updated?: boolean;
};
export type TCIConfigServiceConfigDocker = {
container_registry_paradigm?: (typeof TCIContainerRegistryParadigms)[number]["value"];
container_registry_url?: string;
compose?: { [k: string]: any };
/**
* Location of the dockerfile in the target servers
*/
docker_file_path?: { [k: string]: any };
/**
* Directory in target servers which is the docker
* reference for files like `docker-compose.yaml` or
* `Dockerfile`.
*/
work_dir?: string;
};
export type TCIConfigServiceConfigLog =
| string
| {
cmd: string;
};
export type TCIConfigServiceHealthcheck = {
cmd: string;
test: string;
};
export type TCIConfigServiceDomain = {
domain_name: string;
};
export type TCIConfigServiceSSL = {
email: string;
};
export type TCIConfigServiceConfigLBTarget = {
service_name: string;
port: number;
weight?: number;
backup?: boolean;
domains?: (string | TCIConfigServiceDomain)[];
};
export type TCIConfigServiceConfigRun = {
preflight?: TCIRunObj;
start?: TCIRunObj;
postflight?: TCIRunObj;
work_dir?: string;
};
export type TCIRunObj = {
cmds?: string[];
work_dir?: string;
file?: string;
};
export type TCIConfigServiceConfigDirMApping = {
src: string;
dst: string;
ignore_file?: string;
ignore_patterns?: string[];
use_gitignore?: boolean;
relay_ignore?: string[];
};
export type TCICommandOptions = {
file?: string;
};
export type TCIOptions = {
config?: TCIConfig;
};
export const TurboCIPreferedOS = ["debian", "ubuntu"] as const;
export const TurboCIOsPreferenceRegexp = new RegExp(
`${TurboCIPreferedOS.join("|")}`,
`i`,
);
export type SSHRelayServerReturn = {
ip: string;
private_ip: string;
};
export type NormalizedServerObject = {
public_ip?: string;
private_ip?: string;
};
export type SyncRemoteDirsParams = {
ip?: string;
ips?: string[];
user?: string;
src: string;
dst: string;
ignore_path?: string;
ignore_patterns?: string[];
use_gitignore?: boolean;
delete?: boolean;
debug?: boolean;
use_relay_server?: boolean;
deployment?: Omit<TCIConfigDeployment, "services">;
options?: ExecSyncOptions;
service?: TCIConfigServiceConfig;
service_name?: string;
relay_ignore?: string[];
};
export type DefaultPrepParams = {
service: ParsedDeploymentServiceConfig;
deployment: TCIGlobalConfig;
servers: NormalizedServerObject[];
};
export type ServiceScriptObject = {
sh: string;
service_name: string;
deployment_name: string;
work_dir?: string;
};
export type ParsedDeploymentServiceConfig = TCIConfigServiceConfig & {
service_name: string;
parent_service_name?: string;
servers?: NormalizedServerObject[];
};
export type DefaultDeploymentParams = {
service: ParsedDeploymentServiceConfig;
deployment: TCIGlobalConfig;
};
export type CommanderDefaultOptions = {
skip?: string[];
target?: string[];
};
export type TurbociControlServer = NormalizedServerObject & {
service_name?: "__relay" | (string & {});
deployment_name?: string;
};
export type TurbociControlReturn = {
servers?: TurbociControlServer[];
};
export const TurboCIDependencies = [
{
package_name: "bun",
},
{
package_name: "node",
},
{
package_name: "docker",
},
] as const;
export type DeploymentAndServicesToUpdate = {
deployment: TCIGlobalConfig;
services: ParsedDeploymentServiceConfig[];
skipped_services: ParsedDeploymentServiceConfig[];
};
export type ResponseObject = {
success: boolean;
msg?: string;
};
+86
View File
@@ -0,0 +1,86 @@
import { statSync } from "fs";
import _ from "lodash";
import path from "path";
import turboCIPkgrabDirNames from "./turboci-pkg-grab-dir-names";
import { AppData } from "../data/app-data";
type Params = {
private_server_ips: string[];
parrallel?: boolean;
/**
* Source on the relay server
*/
src: string;
/**
* Destination on private servers
*/
dst: string;
relay_ignore?: string[];
};
export default function bunGrabBulkSyncScripts({
private_server_ips,
parrallel,
src,
dst,
relay_ignore,
}: Params) {
const { relayServerSshPrivateKeyFile } = turboCIPkgrabDirNames();
// const srcStats = statSync(src);
// const isSrcFile = srcStats.isFile();
const dst_dir = src.match(/\.{1,5}$/)
? path.dirname(dst)
: path.normalize(dst);
let bunCmd = "";
bunCmd += `import _ from "lodash";\n`;
bunCmd += `import { execSync } from "child_process";\n`;
bunCmd += `\n`;
bunCmd += `const SSH_KEY = "${relayServerSshPrivateKeyFile}";\n`;
bunCmd += `const SSH_OPTS = \`-i \${SSH_KEY} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -C -c aes128-ctr\`;\n`;
bunCmd += `const REMOTE_HOSTS = [${private_server_ips
.map((h) => `"${h.replace(/\"/g, "")}"`)
.join(", ")}];\n`;
bunCmd += `const DEFAULT_SSH_USER = "root";\n`;
bunCmd += `const BATCH_SIZE = ${AppData["private_server_batch_exec_size"]};\n`;
bunCmd += `\n`;
bunCmd += `async function run(host: string) {\n`;
bunCmd += ` let execCmd = \`ssh \${SSH_OPTS} \${DEFAULT_SSH_USER}@\${host} mkdir -p ${dst_dir}\`;\n`;
bunCmd += ` execCmd += \` && rsync -avz -e 'ssh \${SSH_OPTS}' --delete\`;\n`;
if (relay_ignore) {
bunCmd += relay_ignore
.map((patt) => ` execCmd += \` --exclude='${patt}'\`;\n`)
.join("");
}
bunCmd += ` execCmd += \` ${src} \${DEFAULT_SSH_USER}@\${host}:${dst}\`;\n`;
bunCmd += ` try {\n`;
bunCmd += ` execSync(execCmd);\n`;
bunCmd += ` console.log("Sync Success!");\n`;
bunCmd += ` } catch (error) {\n`;
bunCmd += ` process.exit(1);\n`;
bunCmd += ` }\n`;
bunCmd += `}\n`;
bunCmd += `\n`;
if (parrallel) {
bunCmd += `const first_host = REMOTE_HOSTS.splice(0,1)[0];\n`;
bunCmd += `await run(first_host)\n`;
bunCmd += `\n`;
bunCmd += `const chunks = _.chunk(REMOTE_HOSTS, BATCH_SIZE);\n`;
bunCmd += `for (let i = 0; i < chunks.length; i++) {\n`;
bunCmd += ` const chunk = chunks[i];\n`;
bunCmd += ` const runChunk = await Promise.all(chunk.map(h => run(h)));\n`;
bunCmd += `}\n`;
} else {
bunCmd += `for (let i = 0; i < REMOTE_HOSTS.length; i++) {\n`;
bunCmd += ` const host = REMOTE_HOSTS[i];\n`;
bunCmd += ` const runHost = await run(host);\n`;
bunCmd += `}\n`;
}
return bunCmd;
}
+1 -1
View File
@@ -6,7 +6,7 @@ import {
ParsedDeploymentServiceConfig,
ServiceScriptObject,
TCIGlobalConfig,
} from "../types";
} from "@/src/types/turboci";
import { AppNames } from "./app-names";
import grabSHEnvs from "./grab-sh-env";
import bunGrabPrivateIPsBulkScripts from "./bun-grab-private-ips-bulk-scripts";
+12
View File
@@ -0,0 +1,12 @@
export default function grabGitRepoName({
git_url,
}: {
git_url: string;
}): string | undefined {
const git_arr = git_url.split("/");
const repo_name = git_arr.pop()?.replace(/\.git$/, "");
const repo_user_name = git_arr.pop();
return `${repo_user_name}/${repo_name}`;
}
+4 -1
View File
@@ -1,6 +1,9 @@
import _ from "lodash";
import path from "path";
import { ParsedDeploymentServiceConfig, TCIGlobalConfig } from "../types";
import {
ParsedDeploymentServiceConfig,
TCIGlobalConfig,
} from "@/src/types/turboci";
import parseEnv from "./parse-env";
type Params = {
+1 -1
View File
@@ -1,6 +1,6 @@
import fs from "fs";
import grabDirNames from "./grab-dir-names";
import { TCIGlobalConfig } from "@/src/types";
import { TCIGlobalConfig } from "@/src/types/turboci";
export default function grabTurboCiConfig() {
const { TURBOCI_CONFIG_JSON_FILE } = grabDirNames();