fix: move branch follower targets to jd01
This commit is contained in:
@@ -39,7 +39,7 @@ Use configRef summaries in plan/status; do not create a `full.md` or super Markd
|
|||||||
## First Followers
|
## First Followers
|
||||||
|
|
||||||
- `hwlab-jd01-v03`: follows `pikasTech/HWLAB@v0.3`, adapter `hwlab-node-runtime`, trigger `hwlab nodes control-plane trigger-current --node JD01 --lane v03 --confirm --wait`.
|
- `hwlab-jd01-v03`: follows `pikasTech/HWLAB@v0.3`, adapter `hwlab-node-runtime`, trigger `hwlab nodes control-plane trigger-current --node JD01 --lane v03 --confirm --wait`.
|
||||||
- `agentrun-d601-v02`: follows `pikasTech/agentrun@v0.2`, adapter `agentrun-yaml-lane`, trigger `agentrun control-plane trigger-current --node D601 --lane v02 --confirm --wait`.
|
- `agentrun-jd01-v02`: follows `pikasTech/agentrun@v0.2`, adapter `agentrun-yaml-lane`, trigger `agentrun control-plane trigger-current --node JD01 --lane jd01-v02 --confirm --wait`.
|
||||||
- `web-probe-sentinel-master`: follows `pikasTech/unidesk@master`, adapter `web-probe-sentinel-cicd`, trigger `web-probe sentinel publish-current --node JD01 --lane v03 --sentinel jd01-web-probe-sentinel --confirm --wait`.
|
- `web-probe-sentinel-master`: follows `pikasTech/unidesk@master`, adapter `web-probe-sentinel-cicd`, trigger `web-probe sentinel publish-current --node JD01 --lane v03 --sentinel jd01-web-probe-sentinel --confirm --wait`.
|
||||||
|
|
||||||
## Status Contract
|
## Status Contract
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ metadata:
|
|||||||
|
|
||||||
controller:
|
controller:
|
||||||
namespace: devops-infra
|
namespace: devops-infra
|
||||||
kubeRoute: D601:k3s
|
kubeRoute: JD01:k3s
|
||||||
fieldManager: unidesk-cicd-branch-follower
|
fieldManager: unidesk-cicd-branch-follower
|
||||||
serviceAccountName: unidesk-cicd-branch-follower
|
serviceAccountName: unidesk-cicd-branch-follower
|
||||||
deploymentName: unidesk-cicd-branch-follower
|
deploymentName: unidesk-cicd-branch-follower
|
||||||
@@ -89,28 +89,28 @@ followers:
|
|||||||
closeout:
|
closeout:
|
||||||
checks: ["sourceSnapshot", "pipelineRun", "gitMirrorPostFlush", "gitops", "argo", "runtime", "publicHealth"]
|
checks: ["sourceSnapshot", "pipelineRun", "gitMirrorPostFlush", "gitops", "argo", "runtime", "publicHealth"]
|
||||||
|
|
||||||
- id: agentrun-d601-v02
|
- id: agentrun-jd01-v02
|
||||||
enabled: true
|
enabled: true
|
||||||
adapter: agentrun-yaml-lane
|
adapter: agentrun-yaml-lane
|
||||||
description: Follow AgentRun v0.2 into the D601 YAML-only runtime lane.
|
description: Follow AgentRun v0.2 into the JD01 YAML-only runtime lane.
|
||||||
source:
|
source:
|
||||||
repository: pikasTech/agentrun
|
repository: pikasTech/agentrun
|
||||||
branch: v0.2
|
branch: v0.2
|
||||||
branchRef: config/agentrun.yaml#controlPlane.lanes.v02.source.branch
|
branchRef: config/agentrun.yaml#controlPlane.lanes.jd01-v02.source.branch
|
||||||
authorityRef: config/agentrun.yaml#controlPlane.lanes.v02.source.sourceAuthority
|
authorityRef: config/agentrun.yaml#controlPlane.lanes.jd01-v02.source.sourceAuthority
|
||||||
snapshotPrefix: refs/unidesk/snapshots/agentrun-yaml-lane/v0.2
|
snapshotPrefix: refs/unidesk/snapshots/agentrun-yaml-lane/v0.2
|
||||||
snapshotRef: config/agentrun.yaml#controlPlane.lanes.v02.source.sourceSnapshot.stageRefPrefix
|
snapshotRef: config/agentrun.yaml#controlPlane.lanes.jd01-v02.source.sourceSnapshot.stageRefPrefix
|
||||||
target:
|
target:
|
||||||
node: D601
|
node: JD01
|
||||||
lane: v02
|
lane: jd01-v02
|
||||||
namespace: agentrun-v02
|
namespace: agentrun-v02
|
||||||
configRefs:
|
configRefs:
|
||||||
lane: config/agentrun.yaml#controlPlane.lanes.v02
|
lane: config/agentrun.yaml#controlPlane.lanes.jd01-v02
|
||||||
source: config/agentrun.yaml#controlPlane.lanes.v02.source
|
source: config/agentrun.yaml#controlPlane.lanes.jd01-v02.source
|
||||||
runtime: config/agentrun.yaml#controlPlane.lanes.v02.runtime
|
runtime: config/agentrun.yaml#controlPlane.lanes.jd01-v02.runtime
|
||||||
pipeline: config/agentrun.yaml#controlPlane.lanes.v02.ci.pipeline
|
pipeline: config/agentrun.yaml#controlPlane.lanes.jd01-v02.ci.pipeline
|
||||||
pipelineRunPrefix: config/agentrun.yaml#controlPlane.lanes.v02.ci.pipelineRunPrefix
|
pipelineRunPrefix: config/agentrun.yaml#controlPlane.lanes.jd01-v02.ci.pipelineRunPrefix
|
||||||
argoApplication: config/agentrun.yaml#controlPlane.lanes.v02.gitops.argoApplication
|
argoApplication: config/agentrun.yaml#controlPlane.lanes.jd01-v02.gitops.argoApplication
|
||||||
budgets:
|
budgets:
|
||||||
endToEndSeconds: 120
|
endToEndSeconds: 120
|
||||||
statusSeconds: 35
|
statusSeconds: 35
|
||||||
@@ -118,19 +118,19 @@ followers:
|
|||||||
sourceSyncSeconds: 20
|
sourceSyncSeconds: 20
|
||||||
commands:
|
commands:
|
||||||
plan:
|
plan:
|
||||||
argv: ["bun", "scripts/cli.ts", "agentrun", "control-plane", "trigger-current", "--node", "D601", "--lane", "v02", "--dry-run", "--raw"]
|
argv: ["bun", "scripts/cli.ts", "agentrun", "control-plane", "trigger-current", "--node", "JD01", "--lane", "jd01-v02", "--dry-run", "--raw"]
|
||||||
timeoutSeconds: 35
|
timeoutSeconds: 35
|
||||||
status:
|
status:
|
||||||
argv: ["bun", "scripts/cli.ts", "agentrun", "control-plane", "status", "--node", "D601", "--lane", "v02", "--raw"]
|
argv: ["bun", "scripts/cli.ts", "agentrun", "control-plane", "status", "--node", "JD01", "--lane", "jd01-v02", "--raw"]
|
||||||
timeoutSeconds: 35
|
timeoutSeconds: 35
|
||||||
trigger:
|
trigger:
|
||||||
argv: ["bun", "scripts/cli.ts", "agentrun", "control-plane", "trigger-current", "--node", "D601", "--lane", "v02", "--confirm", "--wait", "--raw"]
|
argv: ["bun", "scripts/cli.ts", "agentrun", "control-plane", "trigger-current", "--node", "JD01", "--lane", "jd01-v02", "--confirm", "--wait", "--raw"]
|
||||||
timeoutSeconds: 120
|
timeoutSeconds: 120
|
||||||
events:
|
events:
|
||||||
argv: ["bun", "scripts/cli.ts", "agentrun", "control-plane", "status", "--node", "D601", "--lane", "v02", "--full"]
|
argv: ["bun", "scripts/cli.ts", "agentrun", "control-plane", "status", "--node", "JD01", "--lane", "jd01-v02", "--full"]
|
||||||
timeoutSeconds: 35
|
timeoutSeconds: 35
|
||||||
logs:
|
logs:
|
||||||
argv: ["bun", "scripts/cli.ts", "agentrun", "control-plane", "status", "--node", "D601", "--lane", "v02", "--full"]
|
argv: ["bun", "scripts/cli.ts", "agentrun", "control-plane", "status", "--node", "JD01", "--lane", "jd01-v02", "--full"]
|
||||||
timeoutSeconds: 35
|
timeoutSeconds: 35
|
||||||
closeout:
|
closeout:
|
||||||
checks: ["sourceSnapshot", "pipelineRun", "gitops", "argo", "manager", "runtimeHealth"]
|
checks: ["sourceSnapshot", "pipelineRun", "gitops", "argo", "manager", "runtimeHealth"]
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ SPEC: PJ2026-01060703 CI/CD branch follower draft-2026-07-03-p0-branch-follower
|
|||||||
首批 follower:
|
首批 follower:
|
||||||
|
|
||||||
- `hwlab-jd01-v03`: 跟随 `pikasTech/HWLAB@v0.3`,触发 `hwlab-node-runtime` 控制面。
|
- `hwlab-jd01-v03`: 跟随 `pikasTech/HWLAB@v0.3`,触发 `hwlab-node-runtime` 控制面。
|
||||||
- `agentrun-d601-v02`: 跟随 `pikasTech/agentrun@v0.2`,触发 `agentrun-yaml-lane` 控制面。
|
- `agentrun-jd01-v02`: 跟随 `pikasTech/agentrun@v0.2`,触发 JD01 `agentrun-yaml-lane` 控制面。
|
||||||
- `web-probe-sentinel-master`: 跟随 `pikasTech/unidesk@master`,触发 YAML 选中的 web-probe sentinel CI/CD。
|
- `web-probe-sentinel-master`: 跟随 `pikasTech/unidesk@master`,触发 YAML 选中的 web-probe sentinel CI/CD。
|
||||||
|
|
||||||
## 2. 强约束
|
## 2. 强约束
|
||||||
@@ -80,7 +80,7 @@ bun scripts/cli.ts cicd branch-follower run-once --all --confirm --wait --contro
|
|||||||
每个 adapter 复用既有受控 CLI:
|
每个 adapter 复用既有受控 CLI:
|
||||||
|
|
||||||
- `hwlab-node-runtime`: `hwlab nodes control-plane status|trigger-current --node JD01 --lane v03`
|
- `hwlab-node-runtime`: `hwlab nodes control-plane status|trigger-current --node JD01 --lane v03`
|
||||||
- `agentrun-yaml-lane`: `agentrun control-plane status|trigger-current --node D601 --lane v02`
|
- `agentrun-yaml-lane`: `agentrun control-plane status|trigger-current --node JD01 --lane jd01-v02`
|
||||||
- `web-probe-sentinel-cicd`: `web-probe sentinel publish-current|control-plane status --node JD01 --lane v03 --sentinel jd01-web-probe-sentinel`
|
- `web-probe-sentinel-cicd`: `web-probe sentinel publish-current|control-plane status --node JD01 --lane v03 --sentinel jd01-web-probe-sentinel`
|
||||||
|
|
||||||
branch follower 不直接操作 Tekton、Argo、kubectl 或 GitHub。它只通过 adapter 命令读取 compact status 或触发已存在的控制面。
|
branch follower 不直接操作 Tekton、Argo、kubectl 或 GitHub。它只通过 adapter 命令读取 compact status 或触发已存在的控制面。
|
||||||
|
|||||||
+47
-6
@@ -1,7 +1,7 @@
|
|||||||
// SPEC: PJ2026-01060703 CI/CD branch follower draft-2026-07-03-p0-branch-follower.
|
// SPEC: PJ2026-01060703 CI/CD branch follower draft-2026-07-03-p0-branch-follower.
|
||||||
// Responsibility: YAML-first K8s branch-follower controller, status, and adapter orchestration.
|
// Responsibility: YAML-first K8s branch-follower controller, status, and adapter orchestration.
|
||||||
import { createHash } from "node:crypto";
|
import { createHash } from "node:crypto";
|
||||||
import { readFileSync } from "node:fs";
|
import { existsSync, readFileSync } from "node:fs";
|
||||||
import { isAbsolute } from "node:path";
|
import { isAbsolute } from "node:path";
|
||||||
import { repoRoot, rootPath, type UniDeskConfig } from "./config";
|
import { repoRoot, rootPath, type UniDeskConfig } from "./config";
|
||||||
import { runCommand, type CommandResult } from "./command";
|
import { runCommand, type CommandResult } from "./command";
|
||||||
@@ -579,11 +579,14 @@ function buildPlan(registry: BranchFollowerRegistry, options: ParsedOptions): Re
|
|||||||
async function applyController(registry: BranchFollowerRegistry, options: ParsedOptions): Promise<Record<string, unknown>> {
|
async function applyController(registry: BranchFollowerRegistry, options: ParsedOptions): Promise<Record<string, unknown>> {
|
||||||
const manifests = renderControllerManifests(registry);
|
const manifests = renderControllerManifests(registry);
|
||||||
const manifestYaml = `${manifests.map((item) => Bun.YAML.stringify(item).trim()).join("\n---\n")}\n`;
|
const manifestYaml = `${manifests.map((item) => Bun.YAML.stringify(item).trim()).join("\n---\n")}\n`;
|
||||||
|
const manifestBase64 = Buffer.from(manifestYaml, "utf8").toString("base64");
|
||||||
const waitSeconds = options.timeoutSeconds ?? registry.controller.budgets.applyWaitSeconds;
|
const waitSeconds = options.timeoutSeconds ?? registry.controller.budgets.applyWaitSeconds;
|
||||||
const script = [
|
const script = [
|
||||||
"set -eu",
|
"set -eu",
|
||||||
"tmp=$(mktemp)",
|
"tmp=$(mktemp)",
|
||||||
"cat >\"$tmp\"",
|
"base64 -d >\"$tmp\" <<'UNIDESK_CICD_BRANCH_FOLLOWER_MANIFEST_B64'",
|
||||||
|
manifestBase64,
|
||||||
|
"UNIDESK_CICD_BRANCH_FOLLOWER_MANIFEST_B64",
|
||||||
options.dryRun
|
options.dryRun
|
||||||
? `kubectl apply --dry-run=server --field-manager=${shQuote(registry.controller.fieldManager)} -f "$tmp"`
|
? `kubectl apply --dry-run=server --field-manager=${shQuote(registry.controller.fieldManager)} -f "$tmp"`
|
||||||
: `kubectl apply --server-side --force-conflicts --field-manager=${shQuote(registry.controller.fieldManager)} -f "$tmp"`,
|
: `kubectl apply --server-side --force-conflicts --field-manager=${shQuote(registry.controller.fieldManager)} -f "$tmp"`,
|
||||||
@@ -592,7 +595,7 @@ async function applyController(registry: BranchFollowerRegistry, options: Parsed
|
|||||||
: "true",
|
: "true",
|
||||||
`kubectl -n ${shQuote(registry.controller.namespace)} get deploy/${shQuote(registry.controller.deploymentName)} cm/${shQuote(registry.controller.configMapName)} cm/${shQuote(registry.controller.stateConfigMapName)} lease/${shQuote(registry.controller.leaseName)} -o wide 2>/dev/null || true`,
|
`kubectl -n ${shQuote(registry.controller.namespace)} get deploy/${shQuote(registry.controller.deploymentName)} cm/${shQuote(registry.controller.configMapName)} cm/${shQuote(registry.controller.stateConfigMapName)} lease/${shQuote(registry.controller.leaseName)} -o wide 2>/dev/null || true`,
|
||||||
].join("\n");
|
].join("\n");
|
||||||
const result = runKubeScript(registry, options, script, manifestYaml, (waitSeconds + 15) * 1000);
|
const result = runKubeScript(registry, options, script, "", (waitSeconds + 15) * 1000);
|
||||||
return {
|
return {
|
||||||
ok: result.exitCode === 0,
|
ok: result.exitCode === 0,
|
||||||
action: "apply",
|
action: "apply",
|
||||||
@@ -849,7 +852,8 @@ async function readAdapterStatus(follower: FollowerSpec, options: ParsedOptions)
|
|||||||
const spec = follower.commands.status;
|
const spec = follower.commands.status;
|
||||||
const timeoutSeconds = options.timeoutSeconds ?? spec.timeoutSeconds;
|
const timeoutSeconds = options.timeoutSeconds ?? spec.timeoutSeconds;
|
||||||
const result = runCommand(spec.argv, repoRoot, { timeoutMs: timeoutSeconds * 1000 });
|
const result = runCommand(spec.argv, repoRoot, { timeoutMs: timeoutSeconds * 1000 });
|
||||||
const payload = parseJsonObject(result.stdout) ?? parseJsonObject(result.stderr);
|
const rawPayload = parseJsonObject(result.stdout) ?? parseJsonObject(result.stderr);
|
||||||
|
const payload = recoverDumpPayload(rawPayload) ?? rawPayload;
|
||||||
const body = payload === null ? null : unwrapEnvelope(payload);
|
const body = payload === null ? null : unwrapEnvelope(payload);
|
||||||
const observedSha = firstStringPath(body, [
|
const observedSha = firstStringPath(body, [
|
||||||
"summary.sourceCommit",
|
"summary.sourceCommit",
|
||||||
@@ -864,6 +868,7 @@ async function readAdapterStatus(follower: FollowerSpec, options: ParsedOptions)
|
|||||||
"selectedCommit",
|
"selectedCommit",
|
||||||
"sourceCommit",
|
"sourceCommit",
|
||||||
"observedSha",
|
"observedSha",
|
||||||
|
"alignment.sourceCommit",
|
||||||
], ["sourceCommit", "observedSha", "observedCommit", "selectedCommit", "selectedSourceCommit"]);
|
], ["sourceCommit", "observedSha", "observedCommit", "selectedCommit", "selectedSourceCommit"]);
|
||||||
const targetSha = firstStringPath(body, [
|
const targetSha = firstStringPath(body, [
|
||||||
"summary.targetCommit",
|
"summary.targetCommit",
|
||||||
@@ -878,6 +883,10 @@ async function readAdapterStatus(follower: FollowerSpec, options: ParsedOptions)
|
|||||||
"deployedCommit",
|
"deployedCommit",
|
||||||
"currentCommit",
|
"currentCommit",
|
||||||
"targetSha",
|
"targetSha",
|
||||||
|
"summary.runtimeAlignment.managerSourceCommit",
|
||||||
|
"alignment.runtimeAlignment.managerSourceCommit",
|
||||||
|
"runtime.manager.sourceCommit",
|
||||||
|
"manager.sourceCommit",
|
||||||
], ["targetCommit", "targetSha", "runtimeCommit", "gitopsCommit", "deployedCommit", "currentCommit"]);
|
], ["targetCommit", "targetSha", "runtimeCommit", "gitopsCommit", "deployedCommit", "currentCommit"]);
|
||||||
const lastTriggeredSha = firstStringPath(body, [
|
const lastTriggeredSha = firstStringPath(body, [
|
||||||
"summary.lastTriggeredSha",
|
"summary.lastTriggeredSha",
|
||||||
@@ -894,10 +903,12 @@ async function readAdapterStatus(follower: FollowerSpec, options: ParsedOptions)
|
|||||||
], ["lastSucceededSha", "lastSucceededCommit", "succeededCommit"]);
|
], ["lastSucceededSha", "lastSucceededCommit", "succeededCommit"]);
|
||||||
const pipelineRun = firstStringPath(body, [
|
const pipelineRun = firstStringPath(body, [
|
||||||
"summary.pipelineRun",
|
"summary.pipelineRun",
|
||||||
|
"summary.expectedPipelineRun",
|
||||||
|
"alignment.expectedPipelineRun",
|
||||||
"pipelineRun.name",
|
"pipelineRun.name",
|
||||||
"pipelineRun",
|
"pipelineRun",
|
||||||
"latestPipelineRun.name",
|
"latestPipelineRun.name",
|
||||||
], ["pipelineRun", "pipelineRunName"]);
|
], ["pipelineRun", "pipelineRunName", "expectedPipelineRun"]);
|
||||||
const inFlightJob = firstStringPath(body, [
|
const inFlightJob = firstStringPath(body, [
|
||||||
"summary.inFlightJob",
|
"summary.inFlightJob",
|
||||||
"job.id",
|
"job.id",
|
||||||
@@ -1001,7 +1012,7 @@ function readK8sState(registry: BranchFollowerRegistry, options: ParsedOptions):
|
|||||||
const deploymentResult = kubeJson(registry, options, `kubectl -n ${shQuote(registry.controller.namespace)} get deploy ${shQuote(registry.controller.deploymentName)} -o json`, 10_000);
|
const deploymentResult = kubeJson(registry, options, `kubectl -n ${shQuote(registry.controller.namespace)} get deploy ${shQuote(registry.controller.deploymentName)} -o json`, 10_000);
|
||||||
const leaseResult = kubeJson(registry, options, `kubectl -n ${shQuote(registry.controller.namespace)} get lease ${shQuote(registry.controller.leaseName)} -o json`, 10_000);
|
const leaseResult = kubeJson(registry, options, `kubectl -n ${shQuote(registry.controller.namespace)} get lease ${shQuote(registry.controller.leaseName)} -o json`, 10_000);
|
||||||
const podSelector = labelSelector(registry.controller.labels);
|
const podSelector = labelSelector(registry.controller.labels);
|
||||||
const podsResult = kubeJson(registry, options, `kubectl -n ${shQuote(registry.controller.namespace)} get pods -l ${shQuote(podSelector)} -o json`, 10_000);
|
const podsResult = kubePodList(registry, options, podSelector);
|
||||||
if (!stateResult.ok && !isNotFoundText(stateResult.error)) errors.push(`state configmap: ${stateResult.error}`);
|
if (!stateResult.ok && !isNotFoundText(stateResult.error)) errors.push(`state configmap: ${stateResult.error}`);
|
||||||
if (!deploymentResult.ok && !isNotFoundText(deploymentResult.error)) errors.push(`deployment: ${deploymentResult.error}`);
|
if (!deploymentResult.ok && !isNotFoundText(deploymentResult.error)) errors.push(`deployment: ${deploymentResult.error}`);
|
||||||
if (!leaseResult.ok && !isNotFoundText(leaseResult.error)) errors.push(`lease: ${leaseResult.error}`);
|
if (!leaseResult.ok && !isNotFoundText(leaseResult.error)) errors.push(`lease: ${leaseResult.error}`);
|
||||||
@@ -1037,6 +1048,21 @@ function kubeJson(registry: BranchFollowerRegistry, options: ParsedOptions, comm
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function kubePodList(registry: BranchFollowerRegistry, options: ParsedOptions, selector: string): { ok: boolean; value: Record<string, unknown> | null; error: string } {
|
||||||
|
const command = `kubectl -n ${shQuote(registry.controller.namespace)} get pods -l ${shQuote(selector)} -o name`;
|
||||||
|
const result = runKubeScript(registry, options, `set -eu\n${command}`, "", 10_000);
|
||||||
|
const names = result.stdout
|
||||||
|
.split(/\r?\n/u)
|
||||||
|
.map((line) => line.trim())
|
||||||
|
.filter((line) => line.length > 0)
|
||||||
|
.map((line) => line.replace(/^pod\//u, ""));
|
||||||
|
return {
|
||||||
|
ok: result.exitCode === 0,
|
||||||
|
value: result.exitCode === 0 ? { items: names.map((name) => ({ metadata: { name } })) } : null,
|
||||||
|
error: redactText(tailText(result.stderr || result.stdout, 800)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function runKubeScript(registry: BranchFollowerRegistry, options: ParsedOptions, script: string, input: string, timeoutMs: number): CommandResult {
|
function runKubeScript(registry: BranchFollowerRegistry, options: ParsedOptions, script: string, input: string, timeoutMs: number): CommandResult {
|
||||||
if (options.controller) {
|
if (options.controller) {
|
||||||
return runCommand(["sh", "-lc", script], repoRoot, { input, timeoutMs });
|
return runCommand(["sh", "-lc", script], repoRoot, { input, timeoutMs });
|
||||||
@@ -1166,12 +1192,14 @@ function controllerLoopScript(): string {
|
|||||||
"while true; do",
|
"while true; do",
|
||||||
" started_at=$(date -Iseconds)",
|
" started_at=$(date -Iseconds)",
|
||||||
" echo \"branch-follower loop started ${started_at}\"",
|
" echo \"branch-follower loop started ${started_at}\"",
|
||||||
|
" cd /work",
|
||||||
" rm -rf /work/unidesk",
|
" rm -rf /work/unidesk",
|
||||||
" git clone --depth=1 --branch \"${UNIDESK_CONTROLLER_SOURCE_BRANCH}\" \"${UNIDESK_CONTROLLER_GIT_MIRROR_READ_URL}\" /work/unidesk",
|
" git clone --depth=1 --branch \"${UNIDESK_CONTROLLER_SOURCE_BRANCH}\" \"${UNIDESK_CONTROLLER_GIT_MIRROR_READ_URL}\" /work/unidesk",
|
||||||
" cp /etc/unidesk-cicd-branch-follower/cicd-branch-followers.yaml /work/unidesk/config/cicd-branch-followers.yaml",
|
" cp /etc/unidesk-cicd-branch-follower/cicd-branch-followers.yaml /work/unidesk/config/cicd-branch-followers.yaml",
|
||||||
" cd /work/unidesk",
|
" cd /work/unidesk",
|
||||||
" bun scripts/cli.ts cicd branch-follower run-once --all --confirm --wait --controller --config config/cicd-branch-followers.yaml --timeout-seconds \"${timeout}\" --json || true",
|
" bun scripts/cli.ts cicd branch-follower run-once --all --confirm --wait --controller --config config/cicd-branch-followers.yaml --timeout-seconds \"${timeout}\" --json || true",
|
||||||
" echo \"branch-follower loop finished $(date -Iseconds)\"",
|
" echo \"branch-follower loop finished $(date -Iseconds)\"",
|
||||||
|
" cd /work",
|
||||||
" sleep \"${interval}\"",
|
" sleep \"${interval}\"",
|
||||||
"done",
|
"done",
|
||||||
].join("\n");
|
].join("\n");
|
||||||
@@ -1278,6 +1306,19 @@ function unwrapEnvelope(payload: Record<string, unknown>): Record<string, unknow
|
|||||||
return data ?? payload;
|
return data ?? payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function recoverDumpPayload(payload: Record<string, unknown> | null): Record<string, unknown> | null {
|
||||||
|
if (payload === null) return null;
|
||||||
|
const data = asOptionalRecord(payload.data);
|
||||||
|
const dump = asOptionalRecord(data?.dump) ?? asOptionalRecord(payload.dump);
|
||||||
|
const path = stringOrNull(dump?.path);
|
||||||
|
if (path === null || !existsSync(path)) return payload;
|
||||||
|
try {
|
||||||
|
return parseJsonObject(readFileSync(path, "utf8")) ?? payload;
|
||||||
|
} catch {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function firstStringPath(root: Record<string, unknown> | null, paths: string[], fallbackKeys: string[] = []): string | null {
|
function firstStringPath(root: Record<string, unknown> | null, paths: string[], fallbackKeys: string[] = []): string | null {
|
||||||
if (root === null) return null;
|
if (root === null) return null;
|
||||||
for (const path of paths) {
|
for (const path of paths) {
|
||||||
|
|||||||
Reference in New Issue
Block a user