refactor: split control-plane cli modules
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. install module for scripts/src/artifact-registry.ts.
|
||||
|
||||
// Moved mechanically from scripts/src/artifact-registry.ts:2117-2231 for #903.
|
||||
|
||||
import { createHash } from "node:crypto";
|
||||
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { runCommand, type CommandResult } from "../command";
|
||||
import { readConfig, type UniDeskConfig, repoRoot, rootPath } from "../config";
|
||||
import { startJob } from "../jobs";
|
||||
import {
|
||||
compareDeployJsonExecutorMirrors,
|
||||
deployJsonCommitImage,
|
||||
deployJsonDriftResult,
|
||||
deployJsonSourceOfTruth,
|
||||
hasDeployJsonExecutorContract,
|
||||
k3sManifestExecutorMirror,
|
||||
parseDeployJsonServiceContractBase64,
|
||||
readDeployJsonServiceContractFromFile,
|
||||
type DeployJsonExecutorMirror,
|
||||
type DeployJsonServiceContract,
|
||||
} from "../deploy-json-contract";
|
||||
import { d601K3sGuardShellLines } from "../d601-k3s-guard";
|
||||
import { composeRuntimeEnvValue } from "../runtime-env";
|
||||
|
||||
import type { ArtifactRegistryOptions, RenderedFile, RuntimeSecretPresence, RuntimeSecretRequirement } from "./types";
|
||||
import { renderBundle, rootExecPrelude, shellQuote } from "./bundle";
|
||||
import { commandTail } from "./consumer";
|
||||
import { runReadonlyStatus } from "./readonly";
|
||||
import { runRemoteScript } from "./remote";
|
||||
import { plan } from "./status";
|
||||
|
||||
export function remoteWriteFileCommand(item: RenderedFile): string {
|
||||
const encoded = Buffer.from(item.content, "utf8").toString("base64");
|
||||
const rootOwned = item.path.startsWith("/etc/");
|
||||
return [
|
||||
`target=${shellQuote(item.path)}`,
|
||||
"tmp=$(mktemp /tmp/unidesk-artifact-registry.XXXXXX)",
|
||||
"trap 'rm -f \"$tmp\"' EXIT",
|
||||
`printf %s ${shellQuote(encoded)} | base64 -d > "$tmp"`,
|
||||
`if [ ${rootOwned ? "1" : "0"} = 1 ]; then`,
|
||||
" root_exec mkdir -p \"$(dirname \"$target\")\"",
|
||||
` root_exec install -m ${shellQuote(item.mode)} "$tmp" "$target"`,
|
||||
"else",
|
||||
" mkdir -p \"$(dirname \"$target\")\"",
|
||||
` install -m ${shellQuote(item.mode)} "$tmp" "$target"`,
|
||||
"fi",
|
||||
`echo ${shellQuote(`artifact_registry_file_written path=${item.path} sha256=${item.sha256}`)}`,
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
export function install(options: ArtifactRegistryOptions): Record<string, unknown> {
|
||||
const bundle = renderBundle(options);
|
||||
const script = [
|
||||
"set -euo pipefail",
|
||||
"command -v docker >/dev/null",
|
||||
"docker compose version >/dev/null",
|
||||
"command -v systemctl >/dev/null",
|
||||
rootExecPrelude(),
|
||||
`mkdir -p ${shellQuote(bundle.paths.baseDir)} ${shellQuote(bundle.paths.storage)}`,
|
||||
...bundle.files.map(remoteWriteFileCommand),
|
||||
"root_exec systemctl daemon-reload",
|
||||
`root_exec systemctl enable --now ${shellQuote(options.unitName)}`,
|
||||
"sleep 2",
|
||||
`curl -fsS http://${options.host}:${options.port}/v2/ >/dev/null`,
|
||||
].join("\n");
|
||||
const command = runRemoteScript(options, script, Math.max(options.timeoutMs, 120_000));
|
||||
const status = runReadonlyStatus(options, true);
|
||||
return {
|
||||
ok: command.exitCode === 0 && !command.timedOut && status.ok === true,
|
||||
dryRun: false,
|
||||
mutation: true,
|
||||
providerId: options.providerId,
|
||||
render: {
|
||||
paths: bundle.paths,
|
||||
files: bundle.files.map((item) => ({ path: item.path, mode: item.mode, sha256: item.sha256 })),
|
||||
},
|
||||
installCommand: commandTail(command),
|
||||
health: status,
|
||||
};
|
||||
}
|
||||
|
||||
export function installDryRun(options: ArtifactRegistryOptions): Record<string, unknown> {
|
||||
const bundle = renderBundle(options);
|
||||
return {
|
||||
ok: true,
|
||||
dryRun: true,
|
||||
readonly: true,
|
||||
mutation: false,
|
||||
providerId: options.providerId,
|
||||
plan: plan(options),
|
||||
render: bundle,
|
||||
intendedRemoteActions: [
|
||||
`mkdir -p ${options.baseDir} ${options.storageDir}`,
|
||||
`write ${bundle.paths.config}`,
|
||||
`write ${bundle.paths.compose}`,
|
||||
`write ${bundle.paths.unit}`,
|
||||
"systemctl daemon-reload",
|
||||
`systemctl enable --now ${options.unitName}`,
|
||||
`curl -fsS http://${options.host}:${options.port}/v2/`,
|
||||
],
|
||||
note: "Dry run only; no D601 runtime files or services were changed.",
|
||||
};
|
||||
}
|
||||
|
||||
export function composeLockScript(innerScript: string): string {
|
||||
const lockDir = rootPath(".state", "locks");
|
||||
const lockPath = rootPath(".state", "locks", "server-compose.lock");
|
||||
return [
|
||||
"set -euo pipefail",
|
||||
`mkdir -p ${shellQuote(lockDir)}`,
|
||||
`echo ${shellQuote(`compose_lock_wait ${lockPath}`)}`,
|
||||
`flock ${shellQuote(lockPath)} bash -lc ${shellQuote(innerScript)}`,
|
||||
].join("; ");
|
||||
}
|
||||
|
||||
export function findEnvValueLength(raw: string, key: string): number {
|
||||
for (const line of raw.split(/\r?\n/u)) {
|
||||
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
||||
const index = line.indexOf("=");
|
||||
if (index <= 0) continue;
|
||||
if (line.slice(0, index) !== key) continue;
|
||||
let value = line.slice(index + 1);
|
||||
if (value.startsWith("\"") && value.endsWith("\"")) {
|
||||
try {
|
||||
value = JSON.parse(value) as string;
|
||||
} catch {
|
||||
value = value.slice(1, -1);
|
||||
}
|
||||
}
|
||||
return value.length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function runtimeSecretPresenceFromEnvText(envText: string, requirements: RuntimeSecretRequirement[]): RuntimeSecretPresence[] {
|
||||
return requirements.map((requirement) => {
|
||||
const length = findEnvValueLength(envText, requirement.sourceEnvName);
|
||||
return {
|
||||
sourceEnvName: requirement.sourceEnvName,
|
||||
containerEnvName: requirement.containerEnvName,
|
||||
present: length > 0,
|
||||
length,
|
||||
};
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user