Migrate Code Queue infra target to G14
This commit is contained in:
+11
-6
@@ -115,11 +115,11 @@ export function checkHelp(): Record<string, unknown> {
|
||||
{ name: "--compose", description: "Render Docker Compose config." },
|
||||
{ name: "--logs", description: "Check unified log rotation policy." },
|
||||
{ name: "--recovery-guardrails", description: "Run D601 k3s/Code Queue reboot recovery diagnostics in read-only mode." },
|
||||
{ name: "--rust", description: "Run cargo check only when UNIDESK_D601_RUST_CHECK=1 is set inside D601 CI/dev execution." },
|
||||
{ name: "--rust", description: "Run cargo check only when UNIDESK_D601_RUST_CHECK=1 or UNIDESK_NATIVE_K3S_RUST_CHECK=1 is set inside an approved native k3s CI/dev execution." },
|
||||
],
|
||||
rustBoundary: {
|
||||
masterServer: "do not run cargo check/build here",
|
||||
d601: "use deploy apply --env dev --service backend-core and CI with UNIDESK_D601_RUST_CHECK=1",
|
||||
nativeK3sCi: "use deploy apply --env dev --service backend-core and CI with UNIDESK_NATIVE_K3S_RUST_CHECK=1",
|
||||
},
|
||||
recoveryGuardrailsBoundary: {
|
||||
command: "bun scripts/cli.ts check recovery-guardrails",
|
||||
@@ -282,14 +282,16 @@ function codeQueueMgrHealthcheckItem(): CheckItem {
|
||||
}
|
||||
|
||||
function rustCheckItem(): CheckItem {
|
||||
if (process.env.UNIDESK_D601_RUST_CHECK !== "1") {
|
||||
const rustCheckAllowed = process.env.UNIDESK_D601_RUST_CHECK === "1" || process.env.UNIDESK_NATIVE_K3S_RUST_CHECK === "1";
|
||||
if (!rustCheckAllowed) {
|
||||
return {
|
||||
name: "rust:backend-core",
|
||||
ok: false,
|
||||
detail: {
|
||||
skipped: true,
|
||||
reason: "Rust compilation is intentionally not allowed on the master server; run it from D601 CI/dev deploy.",
|
||||
enableOnD601: "UNIDESK_D601_RUST_CHECK=1 bun scripts/cli.ts check --rust",
|
||||
reason: "Rust compilation is intentionally not allowed on the master server; run it from an approved native k3s CI/dev execution plane.",
|
||||
enableOnNativeK3s: "UNIDESK_NATIVE_K3S_RUST_CHECK=1 bun scripts/cli.ts check --rust",
|
||||
legacyEnableOnD601: "UNIDESK_D601_RUST_CHECK=1 bun scripts/cli.ts check --rust",
|
||||
deployPath: "bun scripts/cli.ts deploy apply --env dev --service backend-core",
|
||||
},
|
||||
};
|
||||
@@ -397,6 +399,9 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
|
||||
fileItem("src/components/microservices/auth-broker/src/main.rs"),
|
||||
fileItem("scripts/artifact-consumer-dry-run-matrix-test.ts"),
|
||||
fileItem("src/components/microservices/k3sctl-adapter/k3s/ci/unidesk-ci.pipeline.yaml"),
|
||||
fileItem("src/components/microservices/k3sctl-adapter/k3s/ci/unidesk-ci.pipeline.g14.yaml"),
|
||||
fileItem("src/components/microservices/k3sctl-adapter/k3s/code-queue.g14.k3s.json"),
|
||||
fileItem("src/components/microservices/k3sctl-adapter/k3s/code-queue.g14.k8s.yaml"),
|
||||
);
|
||||
} else {
|
||||
items.push(skippedItem("files:required-entrypoints", "required file presence scan is opt-in", "--files or --full"));
|
||||
@@ -531,7 +536,7 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
|
||||
if (options.rust) {
|
||||
items.push(rustCheckItem());
|
||||
} else {
|
||||
items.push(skippedItem("rust:backend-core", "Rust check/build must run through D601 CI artifact publication, not on the master server or CD runtime target", "--rust inside D601 CI with UNIDESK_D601_RUST_CHECK=1"));
|
||||
items.push(skippedItem("rust:backend-core", "Rust check/build must run through an approved native k3s CI artifact publication, not on the master server or CD runtime target", "--rust inside native k3s CI with UNIDESK_NATIVE_K3S_RUST_CHECK=1"));
|
||||
}
|
||||
return { ok: items.every((item) => item.ok), mode: options.full ? "full" : "basic", options, items };
|
||||
}
|
||||
|
||||
+201
-69
@@ -27,6 +27,8 @@ const providerGatewayWsEgressProxyUrl = "http://127.0.0.1:18789";
|
||||
const ciCodeQueueImage = "unidesk-code-queue:dev";
|
||||
const codeQueueDirectDockerBaseImage = "unidesk-code-queue:d601";
|
||||
const providerDispatchCompletionLagMs = 45_000;
|
||||
const defaultCiPipelineManifest = "src/components/microservices/k3sctl-adapter/k3s/ci/unidesk-ci.pipeline.yaml";
|
||||
const g14CiPipelineManifest = "src/components/microservices/k3sctl-adapter/k3s/ci/unidesk-ci.pipeline.g14.yaml";
|
||||
const ciRuntimeImages = [
|
||||
"rancher/mirrored-pause:3.6",
|
||||
"rancher/mirrored-library-busybox:1.36.1",
|
||||
@@ -40,10 +42,56 @@ const ciRuntimeImages = [
|
||||
"alpine/git:2.45.2",
|
||||
ciCodeQueueImage,
|
||||
];
|
||||
|
||||
function ciTarget(providerId: string | null): CiTarget {
|
||||
const normalized = providerId ?? d601ProviderId;
|
||||
if (normalized === d601ProviderId) {
|
||||
return {
|
||||
providerId: d601ProviderId,
|
||||
kubeconfig: d601Kubeconfig,
|
||||
hostCwd: "/home/ubuntu",
|
||||
homeDir: "/home/ubuntu",
|
||||
pipelineManifest: defaultCiPipelineManifest,
|
||||
codeQueueImage: ciCodeQueueImage,
|
||||
guardName: "d601_native_k3s_guard",
|
||||
requiredNodeName: "d601",
|
||||
};
|
||||
}
|
||||
if (normalized === "G14") {
|
||||
return {
|
||||
providerId: "G14",
|
||||
kubeconfig: d601Kubeconfig,
|
||||
hostCwd: "/root",
|
||||
homeDir: "/root",
|
||||
pipelineManifest: g14CiPipelineManifest,
|
||||
codeQueueImage: ciCodeQueueImage,
|
||||
guardName: "g14_native_k3s_guard",
|
||||
requiredNodeLabel: { key: "unidesk.ai/node-id", value: "G14" },
|
||||
};
|
||||
}
|
||||
throw new Error(`ci --provider-id currently supports D601 or G14, got ${normalized}`);
|
||||
}
|
||||
|
||||
function providerIdOption(args: string[]): string | null {
|
||||
return stringOption(args, "--provider-id") ?? stringOption(args, "--provider");
|
||||
}
|
||||
interface CiOptions {
|
||||
repoUrl: string;
|
||||
revision: string;
|
||||
waitMs: number;
|
||||
target: CiTarget;
|
||||
}
|
||||
|
||||
interface CiTarget {
|
||||
providerId: string;
|
||||
kubeconfig: string;
|
||||
hostCwd: string;
|
||||
homeDir: string;
|
||||
pipelineManifest: string;
|
||||
codeQueueImage: string;
|
||||
guardName: string;
|
||||
requiredNodeName?: string;
|
||||
requiredNodeLabel?: { key: string; value: string };
|
||||
}
|
||||
|
||||
interface CiPublishBackendCoreOptions {
|
||||
@@ -251,6 +299,70 @@ function shellQuote(value: string): string {
|
||||
return `'${value.replace(/'/gu, "'\\''")}'`;
|
||||
}
|
||||
|
||||
function ciTargetGuardShellLines(target: CiTarget, options: { passOutput?: "stdout" | "stderr" | "quiet" } = {}): string[] {
|
||||
if (target.providerId === d601ProviderId) {
|
||||
return d601K3sGuardShellLines(target.kubeconfig, options);
|
||||
}
|
||||
const passOutput = options.passOutput ?? "stdout";
|
||||
const labelSelector = target.requiredNodeLabel === undefined ? "" : `${target.requiredNodeLabel.key}=${target.requiredNodeLabel.value}`;
|
||||
const passLine = passOutput === "quiet"
|
||||
? ":"
|
||||
: passOutput === "stderr"
|
||||
? `printf '${target.guardName}=pass kubeconfig=%s context=%s server=%s nodes=%s\\n' "$required_kubeconfig" "$context" "$server" "$(printf '%s' "$nodes" | tr '\\n' ',')" >&2`
|
||||
: `printf '${target.guardName}=pass kubeconfig=%s context=%s server=%s nodes=%s\\n' "$required_kubeconfig" "$context" "$server" "$(printf '%s' "$nodes" | tr '\\n' ',')"`;
|
||||
return [
|
||||
`export KUBECONFIG=${shellQuote(target.kubeconfig)}`,
|
||||
"unidesk_ci_k3s_guard() {",
|
||||
` required_kubeconfig=${shellQuote(target.kubeconfig)}`,
|
||||
` required_node=${shellQuote(target.requiredNodeName ?? "")}`,
|
||||
` required_selector=${shellQuote(labelSelector)}`,
|
||||
` guard_name=${shellQuote(target.guardName)}`,
|
||||
" if [ \"${KUBECONFIG:-}\" != \"$required_kubeconfig\" ]; then",
|
||||
" printf '%s=blocked reason=wrong-kubeconfig expected=%s actual=%s\\n' \"$guard_name\" \"$required_kubeconfig\" \"${KUBECONFIG:-<unset>}\" >&2",
|
||||
" return 1",
|
||||
" fi",
|
||||
" if ! command -v kubectl >/dev/null 2>&1; then",
|
||||
" printf '%s=blocked reason=kubectl-missing\\n' \"$guard_name\" >&2",
|
||||
" return 1",
|
||||
" fi",
|
||||
" if ! context=$(kubectl config current-context 2>&1); then",
|
||||
" printf '%s=blocked reason=context-read-failed detail=%s\\n' \"$guard_name\" \"$context\" >&2",
|
||||
" return 1",
|
||||
" fi",
|
||||
" if ! server=$(kubectl config view --minify -o 'jsonpath={.clusters[0].cluster.server}' 2>&1); then",
|
||||
" printf '%s=blocked reason=server-read-failed detail=%s\\n' \"$guard_name\" \"$server\" >&2",
|
||||
" return 1",
|
||||
" fi",
|
||||
" if ! nodes=$(kubectl get nodes -o 'jsonpath={range .items[*]}{.metadata.name}{\"\\n\"}{end}' 2>&1); then",
|
||||
" printf '%s=blocked reason=nodes-read-failed detail=%s\\n' \"$guard_name\" \"$nodes\" >&2",
|
||||
" return 1",
|
||||
" fi",
|
||||
" combined=$(printf '%s\\n%s\\n%s\\n' \"$context\" \"$server\" \"$nodes\")",
|
||||
" if printf '%s\\n' \"$combined\" | grep -Eiq 'docker-desktop|desktop-control-plane|127\\.0\\.0\\.1:11700'; then",
|
||||
" printf '%s=refused reason=forbidden-control-plane context=%s server=%s nodes=%s\\n' \"$guard_name\" \"$context\" \"$server\" \"$(printf '%s' \"$nodes\" | tr '\\n' ',')\" >&2",
|
||||
" return 1",
|
||||
" fi",
|
||||
" if [ -n \"$required_node\" ] && ! printf '%s\\n' \"$nodes\" | grep -Fx \"$required_node\" >/dev/null; then",
|
||||
" printf '%s=blocked reason=missing-required-node expected=%s nodes=%s\\n' \"$guard_name\" \"$required_node\" \"$(printf '%s' \"$nodes\" | tr '\\n' ',')\" >&2",
|
||||
" return 1",
|
||||
" fi",
|
||||
" if [ -n \"$required_selector\" ]; then",
|
||||
" if ! labeled_nodes=$(kubectl get nodes -l \"$required_selector\" -o 'jsonpath={range .items[*]}{.metadata.name}{\"\\n\"}{end}' 2>&1); then",
|
||||
" printf '%s=blocked reason=label-query-failed selector=%s detail=%s\\n' \"$guard_name\" \"$required_selector\" \"$labeled_nodes\" >&2",
|
||||
" return 1",
|
||||
" fi",
|
||||
" if [ -z \"$(printf '%s' \"$labeled_nodes\" | tr -d '\\n')\" ]; then",
|
||||
" printf '%s=blocked reason=missing-required-label selector=%s nodes=%s\\n' \"$guard_name\" \"$required_selector\" \"$(printf '%s' \"$nodes\" | tr '\\n' ',')\" >&2",
|
||||
" return 1",
|
||||
" fi",
|
||||
" nodes=\"$labeled_nodes\"",
|
||||
" fi",
|
||||
` ${passLine}`,
|
||||
"}",
|
||||
"unidesk_ci_k3s_guard",
|
||||
];
|
||||
}
|
||||
|
||||
function safePathToken(value: string): string {
|
||||
return value.replace(/[^a-z0-9._-]/giu, "-").toLowerCase().replace(/^-+|-+$/gu, "").slice(0, 80) || "artifact";
|
||||
}
|
||||
@@ -679,18 +791,18 @@ function requireCiScriptPath(value: unknown): string {
|
||||
return scriptPath;
|
||||
}
|
||||
|
||||
async function dispatchSsh(command: string, waitMs: number, remoteTimeoutMs: number, pollCompletion = true): Promise<DispatchResult> {
|
||||
async function dispatchSsh(command: string, waitMs: number, remoteTimeoutMs: number, pollCompletion = true, target = ciTarget(null)): Promise<DispatchResult> {
|
||||
const dispatchResponse = coreInternalFetch("/api/dispatch", {
|
||||
method: "POST",
|
||||
body: {
|
||||
providerId: d601ProviderId,
|
||||
providerId: target.providerId,
|
||||
command: "host.ssh",
|
||||
payload: {
|
||||
source: "ci-cli",
|
||||
mode: "exec",
|
||||
command,
|
||||
timeoutMs: remoteTimeoutMs,
|
||||
cwd: "/home/ubuntu",
|
||||
cwd: target.hostCwd,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -753,48 +865,48 @@ async function dispatchSsh(command: string, waitMs: number, remoteTimeoutMs: num
|
||||
};
|
||||
}
|
||||
|
||||
async function runRemoteKubectl(script: string, waitMs = 60_000, remoteTimeoutMs = 45_000): Promise<DispatchResult> {
|
||||
const result = await runRemoteKubectlRaw(script, waitMs, remoteTimeoutMs);
|
||||
async function runRemoteKubectl(script: string, waitMs = 60_000, remoteTimeoutMs = 45_000, target = ciTarget(null)): Promise<DispatchResult> {
|
||||
const result = await runRemoteKubectlRaw(script, waitMs, remoteTimeoutMs, target);
|
||||
if (!result.ok) {
|
||||
throw new Error(`D601 kubectl command failed: ${result.stderr || result.stdout || JSON.stringify(result.raw)}`);
|
||||
throw new Error(`${target.providerId} kubectl command failed: ${result.stderr || result.stdout || JSON.stringify(result.raw)}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function runRemoteKubectlRaw(script: string, waitMs = 60_000, remoteTimeoutMs = 45_000): Promise<DispatchResult> {
|
||||
async function runRemoteKubectlRaw(script: string, waitMs = 60_000, remoteTimeoutMs = 45_000, target = ciTarget(null)): Promise<DispatchResult> {
|
||||
const command = [
|
||||
"set -euo pipefail",
|
||||
...d601K3sGuardShellLines(d601Kubeconfig, { passOutput: "stderr" }),
|
||||
...ciTargetGuardShellLines(target, { passOutput: "stderr" }),
|
||||
script,
|
||||
].join("\n");
|
||||
return dispatchSsh(command, waitMs, remoteTimeoutMs);
|
||||
return dispatchSsh(command, waitMs, remoteTimeoutMs, true, target);
|
||||
}
|
||||
|
||||
async function uploadRemoteBase64(path: string, encoded: string): Promise<DispatchResult> {
|
||||
async function uploadRemoteBase64(path: string, encoded: string, target = ciTarget(null)): Promise<DispatchResult> {
|
||||
const init = await dispatchSsh([
|
||||
"set -euo pipefail",
|
||||
`target=${shellQuote(path)}`,
|
||||
"rm -f \"$target\"",
|
||||
": > \"$target\"",
|
||||
"chmod 600 \"$target\"",
|
||||
].join("\n"), 20_000, 10_000);
|
||||
].join("\n"), 20_000, 10_000, true, target);
|
||||
if (!init.ok) return init;
|
||||
for (const chunk of chunks(encoded, 950)) {
|
||||
const append = await dispatchSsh([
|
||||
"set -euo pipefail",
|
||||
`target=${shellQuote(path)}`,
|
||||
`printf %s ${shellQuote(chunk)} >> "$target"`,
|
||||
].join("\n"), 20_000, 10_000);
|
||||
].join("\n"), 20_000, 10_000, true, target);
|
||||
if (!append.ok) return append;
|
||||
}
|
||||
return dispatchSsh([
|
||||
"set -euo pipefail",
|
||||
`target=${shellQuote(path)}`,
|
||||
"wc -c \"$target\"",
|
||||
].join("\n"), 20_000, 10_000);
|
||||
].join("\n"), 20_000, 10_000, true, target);
|
||||
}
|
||||
|
||||
async function runRemoteBackground(label: string, script: string, timeoutMs: number): Promise<DispatchResult> {
|
||||
async function runRemoteBackground(label: string, script: string, timeoutMs: number, target = ciTarget(null)): Promise<DispatchResult> {
|
||||
const token = randomUUID().replace(/-/gu, "").slice(0, 12);
|
||||
const safeLabel = label.replace(/[^a-z0-9-]/giu, "-").toLowerCase().slice(0, 48);
|
||||
const base = `/tmp/unidesk-ci-${safeLabel}-${token}`;
|
||||
@@ -802,7 +914,7 @@ async function runRemoteBackground(label: string, script: string, timeoutMs: num
|
||||
const logPath = `${base}.log`;
|
||||
const donePath = `${base}.done`;
|
||||
const encoded = Buffer.from(script, "utf8").toString("base64");
|
||||
const upload = await uploadRemoteBase64(`${scriptPath}.b64`, encoded);
|
||||
const upload = await uploadRemoteBase64(`${scriptPath}.b64`, encoded, target);
|
||||
if (!upload.ok) return upload;
|
||||
const start = await dispatchSsh([
|
||||
"set -euo pipefail",
|
||||
@@ -815,7 +927,7 @@ async function runRemoteBackground(label: string, script: string, timeoutMs: num
|
||||
"chmod 700 \"$script_path\"",
|
||||
"nohup bash -lc \"bash '$script_path' >'$log_path' 2>&1; code=\\$?; printf '%s\\n' \\\"\\$code\\\" >'$done_path'\" >/tmp/unidesk-ci-nohup.log 2>&1 &",
|
||||
"printf 'remote_job_pid=%s\\nlog=%s\\ndone=%s\\n' \"$!\" \"$log_path\" \"$done_path\"",
|
||||
].join("\n"), 20_000, 10_000);
|
||||
].join("\n"), 20_000, 10_000, true, target);
|
||||
if (!start.ok) return start;
|
||||
|
||||
const deadline = Date.now() + timeoutMs;
|
||||
@@ -833,7 +945,7 @@ async function runRemoteBackground(label: string, script: string, timeoutMs: num
|
||||
" printf 'REMOTE_RUNNING\\n'",
|
||||
"fi",
|
||||
"tail -n 160 \"$log_path\" 2>/dev/null || true",
|
||||
].join("\n"), 75_000, 12_000);
|
||||
].join("\n"), 75_000, 12_000, true, target);
|
||||
if (!latest.ok) {
|
||||
if (latest.status === "timeout" || latest.stderr.includes("did not finish within")) {
|
||||
continue;
|
||||
@@ -860,32 +972,32 @@ async function runRemoteBackground(label: string, script: string, timeoutMs: num
|
||||
};
|
||||
}
|
||||
|
||||
async function remoteApplyManifest(path: string): Promise<void> {
|
||||
async function remoteApplyManifest(path: string, target = ciTarget(null)): Promise<void> {
|
||||
const absolute = rootPath(path);
|
||||
if (!existsSync(absolute)) throw new Error(`manifest not found: ${path}`);
|
||||
const encoded = Buffer.from(readFileSync(absolute, "utf8"), "utf8").toString("base64");
|
||||
const token = randomUUID().replace(/-/gu, "").slice(0, 12);
|
||||
const b64Path = `/tmp/unidesk-ci-apply-${token}.b64`;
|
||||
const upload = await uploadRemoteBase64(b64Path, encoded);
|
||||
const upload = await uploadRemoteBase64(b64Path, encoded, target);
|
||||
if (!upload.ok) throw new Error(`failed to upload manifest ${path}: ${upload.stderr || upload.stdout}`);
|
||||
const script = [
|
||||
"set -euo pipefail",
|
||||
...d601K3sGuardShellLines(d601Kubeconfig),
|
||||
...ciTargetGuardShellLines(target),
|
||||
"tmp=$(mktemp /tmp/unidesk-ci-apply.XXXXXX.yaml)",
|
||||
`b64_path=${shellQuote(b64Path)}`,
|
||||
"trap 'rm -f \"$tmp\" \"$b64_path\"' EXIT",
|
||||
"base64 -d \"$b64_path\" > \"$tmp\"",
|
||||
"kubectl apply -f \"$tmp\"",
|
||||
].join("\n");
|
||||
const result = await runRemoteBackground(`apply-${path.split("/").pop() ?? "manifest"}`, script, 180_000);
|
||||
const result = await runRemoteBackground(`apply-${path.split("/").pop() ?? "manifest"}`, script, 180_000, target);
|
||||
if (!result.ok) throw new Error(`kubectl apply failed for ${path}: ${result.stderr || result.stdout}`);
|
||||
}
|
||||
|
||||
async function prewarmCiRuntimeImages(): Promise<void> {
|
||||
async function prewarmCiRuntimeImages(target = ciTarget(null)): Promise<void> {
|
||||
const images = ciRuntimeImages.map(shellQuote).join(" ");
|
||||
const script = [
|
||||
"set -euo pipefail",
|
||||
...d601K3sGuardShellLines(d601Kubeconfig),
|
||||
...ciTargetGuardShellLines(target),
|
||||
"export DOCKER_CONFIG=/tmp/unidesk-ci-docker-config",
|
||||
"mkdir -p \"$DOCKER_CONFIG\"",
|
||||
"printf '{}\\n' > \"$DOCKER_CONFIG/config.json\"",
|
||||
@@ -900,7 +1012,13 @@ async function prewarmCiRuntimeImages(): Promise<void> {
|
||||
"done",
|
||||
"pause_entrypoint=$(docker image inspect rancher/mirrored-pause:3.6 --format '{{json .Config.Entrypoint}}' 2>/dev/null || true)",
|
||||
"if ! printf '%s' \"$pause_entrypoint\" | grep -q '\"/pause\"'; then echo native_k3s_pause_image_invalid_entrypoint=$pause_entrypoint >&2; exit 1; fi",
|
||||
"containerd_images=$(/mnt/c/Windows/System32/wsl.exe -u root -- ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images ls 2>/tmp/unidesk-ci-containerd-images.err || true)",
|
||||
"root_exec() {",
|
||||
" if [ \"$(id -u)\" = \"0\" ]; then \"$@\"; return $?; fi",
|
||||
" if command -v sudo >/dev/null 2>&1; then sudo \"$@\"; return $?; fi",
|
||||
" if [ -x /mnt/c/Windows/System32/wsl.exe ]; then /mnt/c/Windows/System32/wsl.exe -u root -- \"$@\"; return $?; fi",
|
||||
" \"$@\"",
|
||||
"}",
|
||||
"containerd_images=$(root_exec ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images ls 2>/tmp/unidesk-ci-containerd-images.err || true)",
|
||||
"containerd_ready=1",
|
||||
"for image in \"${images[@]}\"; do",
|
||||
" case \"$image\" in",
|
||||
@@ -919,17 +1037,17 @@ async function prewarmCiRuntimeImages(): Promise<void> {
|
||||
"fi",
|
||||
"rm -f /tmp/unidesk-ci-runtime-images.tar",
|
||||
"docker save \"${images[@]}\" -o /tmp/unidesk-ci-runtime-images.tar",
|
||||
"/mnt/c/Windows/System32/wsl.exe -u root -- ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images import --digests --all-platforms /tmp/unidesk-ci-runtime-images.tar >/tmp/unidesk-ci-runtime-images-import.log",
|
||||
"/mnt/c/Windows/System32/wsl.exe -u root -- ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images ls | grep -F 'docker.io/rancher/mirrored-pause:3.6' >/dev/null",
|
||||
"/mnt/c/Windows/System32/wsl.exe -u root -- ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images ls | grep -F 'docker.io/oven/bun:1-debian' >/dev/null",
|
||||
"/mnt/c/Windows/System32/wsl.exe -u root -- ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images ls | grep -F 'docker.io/alpine/git:2.45.2' >/dev/null",
|
||||
`/mnt/c/Windows/System32/wsl.exe -u root -- ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images ls | grep -F ${shellQuote(`docker.io/library/${ciCodeQueueImage}`)} >/dev/null`,
|
||||
"root_exec ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images import --digests --all-platforms /tmp/unidesk-ci-runtime-images.tar >/tmp/unidesk-ci-runtime-images-import.log",
|
||||
"root_exec ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images ls | grep -F 'docker.io/rancher/mirrored-pause:3.6' >/dev/null",
|
||||
"root_exec ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images ls | grep -F 'docker.io/oven/bun:1-debian' >/dev/null",
|
||||
"root_exec ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images ls | grep -F 'docker.io/alpine/git:2.45.2' >/dev/null",
|
||||
`root_exec ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images ls | grep -F ${shellQuote(`docker.io/library/${target.codeQueueImage}`)} >/dev/null`,
|
||||
].join("\n");
|
||||
const result = await runRemoteBackground("prewarm-runtime-images", script, 900_000);
|
||||
const result = await runRemoteBackground("prewarm-runtime-images", script, 900_000, target);
|
||||
if (!result.ok) throw new Error(`CI runtime image prewarm failed: ${result.stderr || result.stdout}`);
|
||||
}
|
||||
|
||||
async function status(): Promise<Record<string, unknown>> {
|
||||
async function status(target = ciTarget(null)): Promise<Record<string, unknown>> {
|
||||
const summary = await runRemoteKubectl([
|
||||
"set -euo pipefail",
|
||||
"printf 'tekton_pipelines='",
|
||||
@@ -939,10 +1057,10 @@ async function status(): Promise<Record<string, unknown>> {
|
||||
"printf '\\nunidesk_ci='",
|
||||
"kubectl get pipeline,task,pipelinerun,eventlistener,svc -n unidesk-ci -o name 2>/dev/null | tr '\\n' ' ' || true",
|
||||
"printf '\\n'",
|
||||
].join("\n"));
|
||||
].join("\n"), 60_000, 45_000, target);
|
||||
return {
|
||||
ok: true,
|
||||
providerId: d601ProviderId,
|
||||
providerId: target.providerId,
|
||||
orchestrator: "native-k3s",
|
||||
tekton: {
|
||||
pipelineVersion: tektonPipelineVersion,
|
||||
@@ -952,14 +1070,14 @@ async function status(): Promise<Record<string, unknown>> {
|
||||
};
|
||||
}
|
||||
|
||||
async function install(): Promise<Record<string, unknown>> {
|
||||
if (!existsSync(rootPath("src/components/microservices/k3sctl-adapter/k3s/ci/unidesk-ci.pipeline.yaml"))) {
|
||||
async function install(target = ciTarget(null)): Promise<Record<string, unknown>> {
|
||||
if (!existsSync(rootPath(target.pipelineManifest))) {
|
||||
throw new Error("CI manifests are missing");
|
||||
}
|
||||
await prewarmCiRuntimeImages();
|
||||
await prewarmCiRuntimeImages(target);
|
||||
const installTektonScript = [
|
||||
"set -euo pipefail",
|
||||
...d601K3sGuardShellLines(d601Kubeconfig),
|
||||
...ciTargetGuardShellLines(target),
|
||||
`kubectl apply -f ${shellQuote(tektonPipelineReleaseUrl)}`,
|
||||
"kubectl wait --for=condition=Available deployment --all -n tekton-pipelines --timeout=900s",
|
||||
`kubectl apply -f ${shellQuote(tektonTriggersReleaseUrl)}`,
|
||||
@@ -967,12 +1085,12 @@ async function install(): Promise<Record<string, unknown>> {
|
||||
"kubectl wait --for=condition=Available deployment --all -n tekton-pipelines --timeout=900s",
|
||||
"kubectl wait --for=condition=Available deployment --all -n tekton-pipelines-resolvers --timeout=900s",
|
||||
].join("\n");
|
||||
const installTekton = await runRemoteBackground("install-tekton", installTektonScript, 1_200_000);
|
||||
const installTekton = await runRemoteBackground("install-tekton", installTektonScript, 1_200_000, target);
|
||||
if (!installTekton.ok) throw new Error(`Tekton install failed: ${installTekton.stderr || installTekton.stdout}`);
|
||||
await remoteApplyManifest("src/components/microservices/k3sctl-adapter/k3s/ci/tekton-install.yaml");
|
||||
await remoteApplyManifest("src/components/microservices/k3sctl-adapter/k3s/ci/unidesk-ci.pipeline.yaml");
|
||||
await remoteApplyManifest("src/components/microservices/k3sctl-adapter/k3s/ci/unidesk-ci.triggers.yaml");
|
||||
return status();
|
||||
await remoteApplyManifest("src/components/microservices/k3sctl-adapter/k3s/ci/tekton-install.yaml", target);
|
||||
await remoteApplyManifest(target.pipelineManifest, target);
|
||||
await remoteApplyManifest("src/components/microservices/k3sctl-adapter/k3s/ci/unidesk-ci.triggers.yaml", target);
|
||||
return status(target);
|
||||
}
|
||||
|
||||
function pipelineRunManifest(options: CiOptions): string {
|
||||
@@ -1314,11 +1432,11 @@ async function prepareClaudeqqArtifactSource(config: UniDeskConfig, options: CiP
|
||||
};
|
||||
}
|
||||
|
||||
async function remoteCreatePipelineRun(manifest: string): Promise<string> {
|
||||
async function remoteCreatePipelineRun(manifest: string, target = ciTarget(null)): Promise<string> {
|
||||
const encoded = Buffer.from(manifest, "utf8").toString("base64");
|
||||
const token = randomUUID().replace(/-/gu, "").slice(0, 12);
|
||||
const b64Path = `/tmp/unidesk-ci-pipelinerun-${token}.b64`;
|
||||
const upload = await uploadRemoteBase64(b64Path, encoded);
|
||||
const upload = await uploadRemoteBase64(b64Path, encoded, target);
|
||||
if (!upload.ok) throw new Error(`failed to upload PipelineRun manifest: ${upload.stderr || upload.stdout}`);
|
||||
const result = await runRemoteKubectl([
|
||||
"tmp=$(mktemp /tmp/unidesk-ci-run.XXXXXX.yaml)",
|
||||
@@ -1326,15 +1444,15 @@ async function remoteCreatePipelineRun(manifest: string): Promise<string> {
|
||||
"trap 'rm -f \"$tmp\" \"$b64_path\"' EXIT",
|
||||
"base64 -d \"$b64_path\" > \"$tmp\"",
|
||||
"kubectl create -f \"$tmp\" -o jsonpath='{.metadata.name}'",
|
||||
].join("\n"), 60_000, 45_000);
|
||||
].join("\n"), 60_000, 45_000, target);
|
||||
return result.stdout.trim();
|
||||
}
|
||||
|
||||
async function waitForPipelineRun(name: string, waitMs: number): Promise<DispatchResult | null> {
|
||||
async function waitForPipelineRun(name: string, waitMs: number, target = ciTarget(null)): Promise<DispatchResult | null> {
|
||||
if (waitMs <= 0) return null;
|
||||
const command = [
|
||||
"set -euo pipefail",
|
||||
...d601K3sGuardShellLines(d601Kubeconfig),
|
||||
...ciTargetGuardShellLines(target),
|
||||
`printf 'waiting_pipelinerun=%s\\n' ${shellQuote(name)}`,
|
||||
`deadline=$((SECONDS + ${Math.ceil(waitMs / 1000)}))`,
|
||||
"while [ \"$SECONDS\" -lt \"$deadline\" ]; do",
|
||||
@@ -1357,15 +1475,15 @@ async function waitForPipelineRun(name: string, waitMs: number): Promise<Dispatc
|
||||
`kubectl get pipelinerun/${shellQuote(name)} -n unidesk-ci -o json`,
|
||||
"exit 124",
|
||||
].join("\n");
|
||||
return dispatchSsh(command, waitMs + 30_000, waitMs + 20_000);
|
||||
return dispatchSsh(command, waitMs + 30_000, waitMs + 20_000, true, target);
|
||||
}
|
||||
|
||||
async function readPipelineRunCondition(name: string): Promise<PipelineRunCondition> {
|
||||
async function readPipelineRunCondition(name: string, target = ciTarget(null)): Promise<PipelineRunCondition> {
|
||||
const result = await runRemoteKubectlRaw([
|
||||
"set -euo pipefail",
|
||||
`condition="$(kubectl get pipelinerun/${shellQuote(name)} -n unidesk-ci -o jsonpath='{range .status.conditions[?(@.type==\"Succeeded\")]}{.status}{\"\\t\"}{.reason}{\"\\t\"}{.message}{end}' 2>/dev/null || true)"`,
|
||||
"printf '%s\\n' \"$condition\"",
|
||||
].join("\n"), 30_000, 15_000);
|
||||
].join("\n"), 30_000, 15_000, target);
|
||||
const [status = "", reason = "", ...messageParts] = result.stdout.trim().split("\t");
|
||||
const message = messageParts.join("\t");
|
||||
return {
|
||||
@@ -1973,23 +2091,24 @@ async function readArtifactSummaryFromPipelineRun(name: string, context: Artifac
|
||||
return completeArtifactSummaryFromRegistry(parseArtifactSummaryFromOutput(await readPipelineRunLogText(name), context), context);
|
||||
}
|
||||
|
||||
async function readPipelineRunLogText(name: string): Promise<string> {
|
||||
async function readPipelineRunLogText(name: string, target = ciTarget(null)): Promise<string> {
|
||||
const result = await runRemoteKubectlRaw([
|
||||
"set -euo pipefail",
|
||||
`kubectl get pipelinerun/${shellQuote(name)} -n unidesk-ci -o wide`,
|
||||
`kubectl get taskrun -n unidesk-ci -l tekton.dev/pipelineRun=${shellQuote(name)} -o wide`,
|
||||
`for pod in $(kubectl get pods -n unidesk-ci -l tekton.dev/pipelineRun=${shellQuote(name)} -o name); do echo "===== $pod"; kubectl logs -n unidesk-ci "$pod" --all-containers=true --tail=240 || true; done`,
|
||||
].join("\n"), 60_000, 45_000);
|
||||
].join("\n"), 60_000, 45_000, target);
|
||||
return `${result.stdout}\n${result.stderr}`.trim();
|
||||
}
|
||||
|
||||
async function run(options: CiOptions): Promise<Record<string, unknown>> {
|
||||
const name = await remoteCreatePipelineRun(pipelineRunManifest(options));
|
||||
const wait = await waitForPipelineRun(name, options.waitMs);
|
||||
const condition = wait === null ? null : await readPipelineRunCondition(name);
|
||||
const name = await remoteCreatePipelineRun(pipelineRunManifest(options), options.target);
|
||||
const wait = await waitForPipelineRun(name, options.waitMs, options.target);
|
||||
const condition = wait === null ? null : await readPipelineRunCondition(name, options.target);
|
||||
const waitSucceeded = pipelineRunWaitSucceeded(wait, condition);
|
||||
return {
|
||||
ok: waitSucceeded,
|
||||
providerId: options.target.providerId,
|
||||
pipelineRun: name,
|
||||
namespace: "unidesk-ci",
|
||||
repoUrl: options.repoUrl,
|
||||
@@ -2004,8 +2123,8 @@ async function run(options: CiOptions): Promise<Record<string, unknown>> {
|
||||
},
|
||||
condition,
|
||||
next: [
|
||||
`bun scripts/cli.ts ci logs ${name}`,
|
||||
"bun scripts/cli.ts ci status",
|
||||
`bun scripts/cli.ts ci logs ${name} --provider-id ${options.target.providerId}`,
|
||||
`bun scripts/cli.ts ci status --provider-id ${options.target.providerId}`,
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -2582,13 +2701,14 @@ async function runDevE2E(options: CiDevE2EOptions): Promise<Record<string, unkno
|
||||
};
|
||||
}
|
||||
|
||||
async function logs(name: string): Promise<Record<string, unknown>> {
|
||||
async function logs(name: string, target = ciTarget(null)): Promise<Record<string, unknown>> {
|
||||
if (name.length === 0) throw new Error("ci logs requires run id or PipelineRun name");
|
||||
if (/^[a-z0-9]([-a-z0-9]{0,46}[a-z0-9])?$/u.test(name)) {
|
||||
const result = await dispatchSsh([
|
||||
"set -euo pipefail",
|
||||
`run_id=${shellQuote(name)}`,
|
||||
"result_dir=\"/home/ubuntu/.unidesk/runs/$run_id\"",
|
||||
`result_root=${shellQuote(`${target.homeDir}/.unidesk/runs`)}`,
|
||||
"result_dir=\"$result_root/$run_id\"",
|
||||
"printf 'result_dir=%s\\n' \"$result_dir\"",
|
||||
"found=0",
|
||||
"if [ -f \"$result_dir/result.json\" ]; then found=1; echo '===== result.json'; cat \"$result_dir/result.json\"; fi",
|
||||
@@ -2596,7 +2716,7 @@ async function logs(name: string): Promise<Record<string, unknown>> {
|
||||
"if [ -f \"$result_dir/runner.log\" ]; then found=1; echo '===== runner.log'; tail -n 240 \"$result_dir/runner.log\"; fi",
|
||||
"if [ -f \"$result_dir/pods.log\" ]; then found=1; echo '===== pods.log'; tail -n 240 \"$result_dir/pods.log\"; fi",
|
||||
"if [ \"$found\" = \"0\" ]; then echo \"no_run_files=$result_dir\" >&2; exit 42; fi",
|
||||
].join("\n"), 60_000, 45_000);
|
||||
].join("\n"), 60_000, 45_000, true, target);
|
||||
if (result.ok || (result.exitCode !== 42 && !result.stderr.includes("no_run_files="))) {
|
||||
return {
|
||||
ok: result.ok,
|
||||
@@ -2612,9 +2732,10 @@ async function logs(name: string): Promise<Record<string, unknown>> {
|
||||
`kubectl get pipelinerun/${shellQuote(name)} -n unidesk-ci -o wide`,
|
||||
`kubectl get taskrun -n unidesk-ci -l tekton.dev/pipelineRun=${shellQuote(name)} -o wide`,
|
||||
`for pod in $(kubectl get pods -n unidesk-ci -l tekton.dev/pipelineRun=${shellQuote(name)} -o name); do echo "===== $pod"; kubectl logs -n unidesk-ci "$pod" --all-containers=true --tail=160 || true; done`,
|
||||
].join("\n"), 60_000, 45_000);
|
||||
].join("\n"), 60_000, 45_000, target);
|
||||
return {
|
||||
ok: true,
|
||||
providerId: target.providerId,
|
||||
pipelineRun: name,
|
||||
output: result.stdout,
|
||||
stderr: result.stderr,
|
||||
@@ -2649,10 +2770,12 @@ export function ciHelp(): Record<string, unknown> {
|
||||
const catalog = loadCiCatalog();
|
||||
return {
|
||||
command: "ci install|status|run|publish-backend-core|publish-user-service|run-dev-e2e|logs",
|
||||
description: "Manage the D601 k3s Tekton CI gate. CI may publish commit-pinned image artifacts, but it intentionally does not deploy CD.",
|
||||
description: "Manage native k3s Tekton CI on D601 or G14. CI may publish commit-pinned image artifacts, but it intentionally does not deploy CD.",
|
||||
examples: [
|
||||
"bun scripts/cli.ts ci install",
|
||||
"bun scripts/cli.ts ci install --provider-id G14",
|
||||
"bun scripts/cli.ts ci run --revision <commit>",
|
||||
"bun scripts/cli.ts ci run --provider-id G14 --revision <commit>",
|
||||
"bun scripts/cli.ts ci publish-backend-core --commit <full-sha>",
|
||||
"bun scripts/cli.ts ci publish-user-service --service baidu-netdisk --commit <full-sha>",
|
||||
"bun scripts/cli.ts ci publish-user-service --service mdtodo --commit <full-sha>",
|
||||
@@ -2661,7 +2784,7 @@ export function ciHelp(): Record<string, unknown> {
|
||||
"bun scripts/cli.ts ci publish-user-service --service decision-center --commit <full-sha>",
|
||||
"bun scripts/cli.ts ci publish-user-service --service frontend --commit <full-sha>",
|
||||
"bun scripts/cli.ts ci run-dev-e2e --wait-ms 600000",
|
||||
"bun scripts/cli.ts ci logs <runId>",
|
||||
"bun scripts/cli.ts ci logs <runId> [--provider-id G14]",
|
||||
],
|
||||
tekton: {
|
||||
pipelineVersion: tektonPipelineVersion,
|
||||
@@ -2671,6 +2794,14 @@ export function ciHelp(): Record<string, unknown> {
|
||||
triggers: tektonTriggersReleaseUrl,
|
||||
interceptors: tektonTriggersInterceptorsUrl,
|
||||
},
|
||||
targets: {
|
||||
default: "D601",
|
||||
g14: {
|
||||
providerId: "G14",
|
||||
pipelineManifest: g14CiPipelineManifest,
|
||||
nodeSelector: "unidesk.ai/node-id=G14",
|
||||
},
|
||||
},
|
||||
},
|
||||
backendCoreArtifact: {
|
||||
producer: "D601 CI",
|
||||
@@ -2716,13 +2847,14 @@ function requireRunId(value: string): string {
|
||||
export async function runCiCommand(config: UniDeskConfig, args: string[]): Promise<Record<string, unknown>> {
|
||||
const [action = "status", nameArg] = args;
|
||||
if (isHelpArg(action) || args.slice(1).some(isHelpArg)) return ciHelp();
|
||||
if (action === "install") return install();
|
||||
if (action === "status") return status();
|
||||
if (action === "install") return install(ciTarget(providerIdOption(args)));
|
||||
if (action === "status") return status(ciTarget(providerIdOption(args)));
|
||||
if (action === "run") {
|
||||
const target = ciTarget(providerIdOption(args));
|
||||
const repoUrl = stringOption(args, "--repo") ?? stringOption(args, "--repo-url") ?? "https://github.com/pikasTech/unidesk";
|
||||
const revision = requireRevision(stringOption(args, "--revision") ?? stringOption(args, "--commit"));
|
||||
const waitMs = numberOption(args, "--wait-ms", 0);
|
||||
return run({ repoUrl, revision, waitMs });
|
||||
return run({ repoUrl, revision, waitMs, target });
|
||||
}
|
||||
if (action === "publish-backend-core") {
|
||||
if (stringOption(args, "--repo") !== null || stringOption(args, "--repo-url") !== null) {
|
||||
@@ -2799,11 +2931,11 @@ export async function runCiCommand(config: UniDeskConfig, args: string[]): Promi
|
||||
waitMs,
|
||||
});
|
||||
}
|
||||
if (action === "logs") return logs(nameArg ?? "");
|
||||
if (action === "logs") return logs(nameArg ?? "", ciTarget(providerIdOption(args)));
|
||||
throw new Error("ci command must be one of: install, status, run, publish-backend-core, publish-user-service, run-dev-e2e, logs");
|
||||
}
|
||||
|
||||
export function startCiInstallJob(): Record<string, unknown> {
|
||||
const job = startJob("ci_install", ["bun", "scripts/cli.ts", "ci", "install"], "Install/refresh Tekton CI on D601 k3s");
|
||||
export function startCiInstallJob(providerId = d601ProviderId): Record<string, unknown> {
|
||||
const job = startJob("ci_install", ["bun", "scripts/cli.ts", "ci", "install", "--provider-id", providerId], `Install/refresh Tekton CI on ${providerId} native k3s`);
|
||||
return { ok: true, job };
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@ const defaultPrivateKeyPath = "/root/.ssh/id_ed25519";
|
||||
const defaultKnownHostsPath = "/root/.ssh/known_hosts";
|
||||
const providerGatewayWsEgressProxyUrl = "http://127.0.0.1:18789";
|
||||
|
||||
function providerHomeDir(providerId: string): string {
|
||||
return providerId === "G14" ? "/root" : "/home/ubuntu";
|
||||
}
|
||||
|
||||
function pgLiteral(value: string): string {
|
||||
return `'${value.replace(/'/gu, "''")}'`;
|
||||
}
|
||||
@@ -202,7 +206,8 @@ import subprocess
|
||||
import sys
|
||||
|
||||
data = json.load(sys.stdin)
|
||||
ssh_dir = pathlib.Path("/home/ubuntu/.ssh")
|
||||
home_dir = pathlib.Path(str(data.get("homeDir") or os.environ.get("HOME") or "/home/ubuntu"))
|
||||
ssh_dir = home_dir / ".ssh"
|
||||
private_key = str(data.get("privateKey") or "")
|
||||
public_key = str(data.get("publicKey") or "").strip()
|
||||
known_hosts = str(data.get("knownHosts") or "")
|
||||
@@ -321,6 +326,7 @@ function distributeGithubIdentity(providerId: string, identity: GithubSshIdentit
|
||||
privateKey: identity.privateKey,
|
||||
publicKey: identity.publicKey,
|
||||
knownHosts: identity.knownHosts,
|
||||
homeDir: providerHomeDir(providerId),
|
||||
});
|
||||
const remotePython = remoteInstallPythonSource();
|
||||
const proxyPython = gitSshHttpConnectProxySource();
|
||||
|
||||
+14
-6
@@ -50,7 +50,7 @@ export function unsupportedRebuildService(value: string | undefined): Record<str
|
||||
},
|
||||
"code-queue": {
|
||||
classification: "standard-artifact",
|
||||
reason: "code-queue execution plane is D601 k3s-managed and only supports the dev artifact consumer in this phase",
|
||||
reason: "code-queue execution plane is native-k3s-managed and only supports the dev artifact consumer in this phase",
|
||||
replacement: "deploy apply --env dev --service code-queue or artifact-registry deploy-service --env dev --service code-queue",
|
||||
deleteAllowedLater: false,
|
||||
},
|
||||
@@ -138,6 +138,14 @@ export function writeComposeEnv(config: UniDeskConfig, freshLogPrefix: boolean):
|
||||
const labels = JSON.stringify(config.providerGateway.labels);
|
||||
const microservices = JSON.stringify(config.microservices);
|
||||
const restrictedHostBind = config.network.restrictedHostAccess?.bindHost || "127.0.0.1";
|
||||
const codeQueueService = config.microservices.find((service) => service.id === "code-queue");
|
||||
const codeQueueProviderId = codeQueueService?.providerId || "D601";
|
||||
const codeQueueRemoteWorkdir = codeQueueService?.development.worktreePath || "/home/ubuntu";
|
||||
const codeQueueExecutionProviderIds = codeQueueService?.deployment.expectedNodeIds?.join(",") || codeQueueProviderId;
|
||||
const codeQueueEnvValue = (key: string, defaultValue: string): string => {
|
||||
const value = process.env[key];
|
||||
return value === undefined || value.length === 0 ? defaultValue : value;
|
||||
};
|
||||
const lines = {
|
||||
UNIDESK_PUBLIC_HOST: config.network.publicHost,
|
||||
UNIDESK_CORE_PORT: String(config.network.core.port),
|
||||
@@ -233,16 +241,16 @@ export function writeComposeEnv(config: UniDeskConfig, freshLogPrefix: boolean):
|
||||
UNIDESK_CODE_QUEUE_MINIMAX_MODEL: runtimeSecret("UNIDESK_CODE_QUEUE_MINIMAX_MODEL") || runtimeSecret("MINIMAX_MODEL") || "MiniMax-M2.7",
|
||||
UNIDESK_CODE_QUEUE_MINIMAX_API_BASE: runtimeSecret("UNIDESK_CODE_QUEUE_MINIMAX_API_BASE") || runtimeSecret("MINIMAX_API_BASE") || "https://api.minimaxi.com/v1",
|
||||
UNIDESK_CODE_QUEUE_MINIMAX_JUDGE_TIMEOUT_MS: runtimeSecretWithDefault("UNIDESK_CODE_QUEUE_MINIMAX_JUDGE_TIMEOUT_MS", "90000", "60000"),
|
||||
UNIDESK_CODE_QUEUE_REMOTE_WORKDIR: runtimeSecret("UNIDESK_CODE_QUEUE_REMOTE_WORKDIR") || "/home/ubuntu",
|
||||
UNIDESK_CODE_QUEUE_MAIN_PROVIDER_ID: runtimeSecret("UNIDESK_CODE_QUEUE_MAIN_PROVIDER_ID") || "D601",
|
||||
UNIDESK_CODE_QUEUE_REMOTE_WORKDIR: codeQueueEnvValue("UNIDESK_CODE_QUEUE_REMOTE_WORKDIR", codeQueueRemoteWorkdir),
|
||||
UNIDESK_CODE_QUEUE_MAIN_PROVIDER_ID: codeQueueEnvValue("UNIDESK_CODE_QUEUE_MAIN_PROVIDER_ID", codeQueueProviderId),
|
||||
UNIDESK_CODE_QUEUE_TRACE_DATABASE_URL: runtimeSecret("UNIDESK_CODE_QUEUE_TRACE_DATABASE_URL")
|
||||
|| `postgres://${config.database.user}:${config.database.password}@database:${config.network.database.containerPort}/${config.database.name}`,
|
||||
UNIDESK_CODE_QUEUE_MGR_DATABASE_POOL_MAX: runtimeSecret("UNIDESK_CODE_QUEUE_MGR_DATABASE_POOL_MAX") || "2",
|
||||
UNIDESK_CODE_QUEUE_TRACE_DATABASE_POOL_MAX: runtimeSecret("UNIDESK_CODE_QUEUE_TRACE_DATABASE_POOL_MAX") || "1",
|
||||
UNIDESK_CODE_QUEUE_EXECUTION_PROVIDER_IDS: runtimeSecret("UNIDESK_CODE_QUEUE_EXECUTION_PROVIDER_IDS") || "D601",
|
||||
UNIDESK_CODE_QUEUE_DEV_CONTAINER_DEFAULT_PROVIDER_ID: runtimeSecret("UNIDESK_CODE_QUEUE_DEV_CONTAINER_DEFAULT_PROVIDER_ID") || "D601",
|
||||
UNIDESK_CODE_QUEUE_EXECUTION_PROVIDER_IDS: codeQueueEnvValue("UNIDESK_CODE_QUEUE_EXECUTION_PROVIDER_IDS", codeQueueExecutionProviderIds),
|
||||
UNIDESK_CODE_QUEUE_DEV_CONTAINER_DEFAULT_PROVIDER_ID: codeQueueEnvValue("UNIDESK_CODE_QUEUE_DEV_CONTAINER_DEFAULT_PROVIDER_ID", codeQueueService?.development.providerId || codeQueueProviderId),
|
||||
UNIDESK_CODE_QUEUE_DEV_CONTAINER_IMAGE: runtimeSecret("UNIDESK_CODE_QUEUE_DEV_CONTAINER_IMAGE"),
|
||||
UNIDESK_CODE_QUEUE_DEV_CONTAINER_WORKDIR: runtimeSecret("UNIDESK_CODE_QUEUE_DEV_CONTAINER_WORKDIR") || "/home/ubuntu",
|
||||
UNIDESK_CODE_QUEUE_DEV_CONTAINER_WORKDIR: codeQueueEnvValue("UNIDESK_CODE_QUEUE_DEV_CONTAINER_WORKDIR", codeQueueRemoteWorkdir),
|
||||
UNIDESK_OA_EVENT_FLOW_BASE_URL: runtimeSecret("UNIDESK_OA_EVENT_FLOW_BASE_URL") || "http://oa-event-flow:4255",
|
||||
UNIDESK_OA_EVENT_FLOW_PORT: runtimeSecret("UNIDESK_OA_EVENT_FLOW_PORT") || "4255",
|
||||
UNIDESK_OA_EVENT_FLOW_BIND_HOST: runtimeSecretWithDefault("UNIDESK_OA_EVENT_FLOW_BIND_HOST", restrictedHostBind, "127.0.0.1"),
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ export function rootHelp(): unknown {
|
||||
{ command: "debug dispatch [providerId] [docker.ps|provider.upgrade|host.ssh|microservice.http|echo] [--wait-ms N]", description: "Submit a real internal-core dispatch request for CLI debugging." },
|
||||
{ command: "debug task <taskId|latest>", description: "Read a dispatched task record from internal core for CLI debugging." },
|
||||
{ command: "network perf [--service code-queue --path /api/tasks/overview?limit=30 --count N --concurrency N --label before|after]", description: "Benchmark frontend -> backend-core -> provider/adapter user-service networking and report latency/proxy-mode distributions." },
|
||||
{ command: "ci install|status|run|publish-backend-core|publish-user-service|run-dev-e2e|logs", description: "Manage D601 k3s Tekton CI; artifact publish commands build commit-pinned images in CI without deploying CD." },
|
||||
{ command: "ci install|status|run|publish-backend-core|publish-user-service|run-dev-e2e|logs", description: "Manage D601/G14 native k3s Tekton CI; artifact publish commands build commit-pinned images in CI without deploying CD." },
|
||||
{ command: "e2e run [--only pattern[,pattern...]] [--skip pattern[,pattern...]]", description: "Run selected public/internal/Playwright E2E checks; use --only for focused iteration and rerun without filters for final regression." },
|
||||
{ command: "bun scripts/playwright-cli.ts screenshot|open|eval ...", description: "Repo-owned Playwright wrapper for commander browser checks; headless by default, supports storage-state --session, and returns JSON guidance for unsupported interactive daemon commands." },
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user