diff --git a/.agents/skills/unidesk-cicd/SKILL.md b/.agents/skills/unidesk-cicd/SKILL.md index 046171ee..d98e093d 100644 --- a/.agents/skills/unidesk-cicd/SKILL.md +++ b/.agents/skills/unidesk-cicd/SKILL.md @@ -171,7 +171,8 @@ bun scripts/cli.ts hwlab nodes control-plane legacy-cicd --help - PaC `status|closeout|history` 的 `--json` 是有界 machine contract;`--full` 展开单条详情,`--raw` 才展开目标侧原始 payload。`history --id --full` 不得重复嵌套同一 rows/consumer payload或依赖 `/tmp/unidesk-cli-output` 才可读。 - PaC `status` 必须在 `observability.readOnlyCapture.timeoutMs` 内完成: - 目标侧通过 `pac-status-progress` 披露最后阶段,CLI 将其投影为 `observation.remoteStage`; - - PipelineRun 与 TaskRun 原始对象各只读取一次并复用,禁止 status 为同一 run 串行读取每个 Pod 日志; + - PipelineRun 按 YAML 选中的 Repository CR label 做服务端 selector;TaskRun 与 Argo 原始对象各只读取一次并复用,禁止 status 为同一 run 串行读取每个 Pod 日志或重复读取 Argo; + - 单次 Kubernetes/Gitea/registry/runtime 读取上限必须从 YAML 总 capture 预算派生;慢调用返回 unknown/false 和阶段证据,不得吞完整体预算; - status 的 TaskRun 日志证据模式固定为 `taskrun-status-only`,需要日志时使用 id-specific 下钻; - 不得通过提高 YAML timeout 掩盖重复采集、同步日志或无界远端查询。 - PaC `debug-step --id ` 必须读取目标侧真实 PipelineRun、TaskRun condition/result 与合同 Task 日志: diff --git a/.agents/skills/unidesk-cicd/references/incident-recovery.md b/.agents/skills/unidesk-cicd/references/incident-recovery.md index 521b09ed..7e79d384 100644 --- a/.agents/skills/unidesk-cicd/references/incident-recovery.md +++ b/.agents/skills/unidesk-cicd/references/incident-recovery.md @@ -134,6 +134,12 @@ - history collector 收敛为一次 selector 聚合,保留失败日志下钻; - 目标侧输出无敏感值的 `pac-status-progress`,CLI 在 timeout observation 中显示 `remoteStage`; - PipelineRun 成功、Argo `Synced/Healthy` 且 runtime ready 时,缺少同步日志证据降级为 `pac-artifact-log-evidence-deferred` 非阻断告警。 +- 首次合并后的补充恢复: + - 首个修复 PR 合并后,canary 又在 `remoteStage=argo` 间歇超过 `25000ms`; + - 给 PipelineRun 查询直接设置过短的单次上限会返回空列表并制造 `pipeline-missing` false negative,不能以“命令不再超时”冒充恢复; + - 最终按 Repository CR label 在服务端筛选 PipelineRun,同时保留 consumer classifier 区分共享 repository lanes; + - Kubernetes、Gitea、registry 和 runtime 的单次读取上限从 YAML 总预算派生;Argo 原始 JSON 只读一次并由 Argo 摘要与 runtime 共同复用; + - 阶段事件同时输出 elapsed 与 step timeout,连续两轮 canary 分别在 `16.3s` 与 `17.9s` 完成且保持 `ok=true`。 - 验收结论: - 原 `status --target NC01 --consumer selfmedia-nc01 --json` 在 `14.5s` 内返回合法 JSON; - 输出 `ok=true`、`remoteStage=complete`、PipelineRun `Succeeded`、Argo `Synced/Healthy`、runtime `1/1`; diff --git a/scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts b/scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts index 3b81f0a3..b68ad367 100644 --- a/scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts +++ b/scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts @@ -220,6 +220,10 @@ test("PaC apply checks the Gitea repository before dry-run return and cluster mu const bootstrapReadyAssignment = statusAction.split("\n").find((line) => line.includes("bootstrap_ready=$(")); expect(bootstrapReadyAssignment).toBeDefined(); expect(remote).toContain("status_progress() {"); + expect(remote).toContain('stepTimeoutSeconds'); + expect(remote).toContain('pac_read kubectl'); + expect(remote).toContain('export UNIDESK_PAC_ARGO_FILE="$argo_file"'); + expect(remote).toContain('get pipelinerun -l "pipelinesascode.tekton.dev/repository=$UNIDESK_PAC_REPOSITORY_NAME"'); expect(statusAction).toContain("status_progress artifact-evidence"); expect(statusAction).toContain("status_progress diagnostics"); expect(statusAction).toContain("status_progress complete"); diff --git a/scripts/src/platform-infra-pipelines-as-code-remote.sh b/scripts/src/platform-infra-pipelines-as-code-remote.sh index 230c3b3c..70959e9e 100644 --- a/scripts/src/platform-infra-pipelines-as-code-remote.sh +++ b/scripts/src/platform-infra-pipelines-as-code-remote.sh @@ -5,6 +5,12 @@ UNIDESK_PAC_EVALUATOR_PATH=$(mktemp) printf '%s' "$UNIDESK_PAC_EVALUATOR_B64" | base64 -d >"$UNIDESK_PAC_EVALUATOR_PATH" export UNIDESK_PAC_EVALUATOR_PATH trap 'rm -f "$UNIDESK_PAC_EVALUATOR_PATH"' 0 1 2 15 +UNIDESK_PAC_HTTP_TIMEOUT_SECONDS="$UNIDESK_PAC_WAIT_TIMEOUT_SECONDS" +if [ "$UNIDESK_PAC_ACTION" = status ]; then UNIDESK_PAC_HTTP_TIMEOUT_SECONDS="$UNIDESK_PAC_READ_STEP_TIMEOUT_SECONDS"; fi + +pac_read() { + if [ "$UNIDESK_PAC_ACTION" = status ]; then timeout "$UNIDESK_PAC_READ_STEP_TIMEOUT_SECONDS" "$@"; else "$@"; fi +} json_escape() { sed 's/\\/\\\\/g; s/"/\\"/g; s/ /\\t/g' | tr '\n' ' ' @@ -67,7 +73,7 @@ prepare_gitea_api_base() { service=${host%%.*} rest=${host#*.} namespace=${rest%%.*} - cluster_ip=$(kubectl -n "$namespace" get svc "$service" -o jsonpath='{.spec.clusterIP}') + cluster_ip=$(pac_read kubectl -n "$namespace" get svc "$service" -o jsonpath='{.spec.clusterIP}') UNIDESK_PAC_GITEA_API_BASE_URL="http://$cluster_ip:$port" else UNIDESK_PAC_GITEA_API_BASE_URL="$base" @@ -81,13 +87,13 @@ gitea_api() { body="${3:-}" tmp=$(mktemp) if [ -n "$body" ]; then - code=$(curl -sS -o "$tmp" -w '%{http_code}' -u "$UNIDESK_PAC_GITEA_ADMIN_USERNAME:$UNIDESK_PAC_GITEA_ADMIN_PASSWORD" \ + code=$(curl -sS --max-time "$UNIDESK_PAC_HTTP_TIMEOUT_SECONDS" -o "$tmp" -w '%{http_code}' -u "$UNIDESK_PAC_GITEA_ADMIN_USERNAME:$UNIDESK_PAC_GITEA_ADMIN_PASSWORD" \ -H 'Content-Type: application/json' \ -X "$method" \ --data "$body" \ "$(api_url "$path")" || true) else - code=$(curl -sS -o "$tmp" -w '%{http_code}' -u "$UNIDESK_PAC_GITEA_ADMIN_USERNAME:$UNIDESK_PAC_GITEA_ADMIN_PASSWORD" \ + code=$(curl -sS --max-time "$UNIDESK_PAC_HTTP_TIMEOUT_SECONDS" -o "$tmp" -w '%{http_code}' -u "$UNIDESK_PAC_GITEA_ADMIN_USERNAME:$UNIDESK_PAC_GITEA_ADMIN_PASSWORD" \ -X "$method" \ "$(api_url "$path")" || true) fi @@ -103,7 +109,7 @@ gitea_api() { gitea_api_status() { method="$1" path="$2" - status=$(curl -sS -o /dev/null -w '%{http_code}' \ + status=$(curl -sS --max-time "$UNIDESK_PAC_HTTP_TIMEOUT_SECONDS" -o /dev/null -w '%{http_code}' \ -u "$UNIDESK_PAC_GITEA_ADMIN_USERNAME:$UNIDESK_PAC_GITEA_ADMIN_PASSWORD" \ -X "$method" \ "$(api_url "$path")" 2>/dev/null) || status=000 @@ -378,7 +384,7 @@ apply_action() { } condition_status() { - kubectl -n "$1" get "$2" "$3" -o jsonpath='{range .status.conditions[*]}{.type}={.status}:{.reason}{";"}{end}' 2>/dev/null || true + pac_read kubectl -n "$1" get "$2" "$3" -o jsonpath='{range .status.conditions[*]}{.type}={.status}:{.reason}{";"}{end}' 2>/dev/null || true } admission_provenance_state() { @@ -390,14 +396,14 @@ admission_provenance_state() { binding_file=$(mktemp) role_file=$(mktemp) role_binding_file=$(mktemp) - kubectl get validatingadmissionpolicy "$UNIDESK_PAC_ADMISSION_POLICY_NAME" -o json >"$policy_file" 2>/dev/null || printf '{}' >"$policy_file" - kubectl get validatingadmissionpolicybinding "$UNIDESK_PAC_ADMISSION_BINDING_NAME" -o json >"$binding_file" 2>/dev/null || printf '{}' >"$binding_file" - kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get role unidesk-pac-consumer-runner -o json >"$role_file" 2>/dev/null || printf '{}' >"$role_file" - kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get rolebinding unidesk-pac-consumer-runner-default -o json >"$role_binding_file" 2>/dev/null || printf '{}' >"$role_binding_file" + pac_read kubectl get validatingadmissionpolicy "$UNIDESK_PAC_ADMISSION_POLICY_NAME" -o json >"$policy_file" 2>/dev/null || printf '{}' >"$policy_file" + pac_read kubectl get validatingadmissionpolicybinding "$UNIDESK_PAC_ADMISSION_BINDING_NAME" -o json >"$binding_file" 2>/dev/null || printf '{}' >"$binding_file" + pac_read kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get role unidesk-pac-consumer-runner -o json >"$role_file" 2>/dev/null || printf '{}' >"$role_file" + pac_read kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get rolebinding unidesk-pac-consumer-runner-default -o json >"$role_binding_file" 2>/dev/null || printf '{}' >"$role_binding_file" default_can_mutate=false for verb in create patch update delete deletecollection; do for resource in pipelineruns.tekton.dev taskruns.tekton.dev; do - if answer=$(timeout "$UNIDESK_PAC_WAIT_TIMEOUT_SECONDS" kubectl auth can-i --as="system:serviceaccount:$UNIDESK_PAC_TARGET_NAMESPACE:default" "$verb" "$resource" -n "$UNIDESK_PAC_TARGET_NAMESPACE" 2>/dev/null); then :; else :; fi + if answer=$(pac_read kubectl auth can-i --as="system:serviceaccount:$UNIDESK_PAC_TARGET_NAMESPACE:default" "$verb" "$resource" -n "$UNIDESK_PAC_TARGET_NAMESPACE" 2>/dev/null); then :; else :; fi case "$answer" in yes) default_can_mutate=true ;; no) ;; @@ -440,7 +446,7 @@ prepare_admission_provenance_state() { pipeline_rows() { payload_file=$(mktemp) - kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get pipelinerun -o json >"$payload_file" 2>/dev/null || printf '{"items":[]}' >"$payload_file" + pac_read kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get pipelinerun -l "pipelinesascode.tekton.dev/repository=$UNIDESK_PAC_REPOSITORY_NAME" -o json >"$payload_file" 2>/dev/null || printf '{"items":[]}' >"$payload_file" node - "$payload_file" <<'NODE' const fs = require('node:fs'); const { classifyPacPipelineRun, pipelineRunMatchesConsumer } = require(process.env.UNIDESK_PAC_EVALUATOR_PATH); @@ -1072,7 +1078,7 @@ task_rows() { if [ -z "$payload_file" ]; then payload_file=$(mktemp) remove_payload=true - kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get taskrun -o json >"$payload_file" 2>/dev/null || printf '{"items":[]}' >"$payload_file" + pac_read kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get taskrun -o json >"$payload_file" 2>/dev/null || printf '{"items":[]}' >"$payload_file" fi node - "$payload_file" <<'NODE' const fs = require('node:fs'); @@ -1127,7 +1133,7 @@ artifact_summary() { if [ -z "$task_file" ]; then task_file=$(mktemp) remove_task_file=true - kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get taskrun -l "tekton.dev/pipelineRun=$UNIDESK_PAC_TARGET_PIPELINERUN" -o json >"$task_file" 2>/dev/null || printf '{"items":[]}' >"$task_file" + pac_read kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get taskrun -l "tekton.dev/pipelineRun=$UNIDESK_PAC_TARGET_PIPELINERUN" -o json >"$task_file" 2>/dev/null || printf '{"items":[]}' >"$task_file" fi node - "$task_file" <<'NODE' const fs = require('node:fs'); @@ -1198,9 +1204,14 @@ NODE } runtime_summary() { - app_file=$(mktemp) + app_file="${UNIDESK_PAC_ARGO_FILE:-}" + remove_app_file=false + if [ -z "$app_file" ]; then + app_file=$(mktemp) + remove_app_file=true + pac_read kubectl -n "$UNIDESK_PAC_ARGO_NAMESPACE" get application "$UNIDESK_PAC_ARGO_APPLICATION" -o json >"$app_file" 2>/dev/null || printf '{}' >"$app_file" + fi params_file=$(mktemp) - kubectl -n "$UNIDESK_PAC_ARGO_NAMESPACE" get application "$UNIDESK_PAC_ARGO_APPLICATION" -o json >"$app_file" 2>/dev/null || printf '{}' >"$app_file" printf '%s' "$UNIDESK_PAC_PARAMS_JSON" >"$params_file" target=$(node - "$app_file" "$params_file" <<'NODE' const fs = require('node:fs'); @@ -1216,7 +1227,8 @@ const resource = (app.status?.resources || []).find((item) => item.kind === 'Dep if (resource) process.stdout.write(`${resource.namespace}\t${resource.name}`); NODE ) - rm -f "$app_file" "$params_file" + if [ "$remove_app_file" = true ]; then rm -f "$app_file"; fi + rm -f "$params_file" if [ -z "$target" ]; then printf '{}' return @@ -1224,7 +1236,7 @@ NODE namespace=$(printf '%s' "$target" | cut -f1) name=$(printf '%s' "$target" | cut -f2) deploy_file=$(mktemp) - kubectl -n "$namespace" get deploy "$name" -o json >"$deploy_file" 2>/dev/null || printf '{}' >"$deploy_file" + pac_read kubectl -n "$namespace" get deploy "$name" -o json >"$deploy_file" 2>/dev/null || printf '{}' >"$deploy_file" node - "$deploy_file" <<'NODE' || printf '{}' const fs = require('node:fs'); const input = fs.readFileSync(process.argv[2], 'utf8').trim(); @@ -1261,7 +1273,7 @@ NODE service=${host%%.*} rest=${host#*.} namespace=${rest%%.*} - cluster_ip=$(kubectl -n "$namespace" get svc "$service" -o jsonpath='{.spec.clusterIP}' 2>/dev/null || true) + cluster_ip=$(pac_read kubectl -n "$namespace" get svc "$service" -o jsonpath='{.spec.clusterIP}' 2>/dev/null || true) if [ -z "$cluster_ip" ]; then return 1 fi @@ -1468,7 +1480,7 @@ NODE registry_digest="" if [ -n "$registry_url" ]; then header_file=$(mktemp) - if curl -fsS -D "$header_file" -o /dev/null -H 'Accept: application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.list.v2+json' "$registry_url" >/dev/null 2>&1; then + if curl -fsS --max-time "$UNIDESK_PAC_READ_STEP_TIMEOUT_SECONDS" -D "$header_file" -o /dev/null -H 'Accept: application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.list.v2+json' "$registry_url" >/dev/null 2>&1; then registry_present=true fi headers=$(cat "$header_file" 2>/dev/null || true) @@ -1482,7 +1494,7 @@ NODE if [ -n "$health_url" ]; then if [ -n "$runtime_namespace" ] && [ -n "$runtime_service" ] && [ -n "$runtime_service_port" ] && [ -n "$health_path" ]; then health_file=$(mktemp) - if kubectl get --raw "/api/v1/namespaces/$runtime_namespace/services/$runtime_service:$runtime_service_port/proxy$health_path" >"$health_file" 2>/dev/null \ + if pac_read kubectl get --raw "/api/v1/namespaces/$runtime_namespace/services/$runtime_service:$runtime_service_port/proxy$health_path" >"$health_file" 2>/dev/null \ && node - "$health_file" <<'NODE' const fs = require('node:fs'); const body = JSON.parse(fs.readFileSync(process.argv[2], 'utf8')); @@ -1495,7 +1507,7 @@ NODE fi rm -f "$health_file" else - health_status=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 8 "$health_url" 2>/dev/null || true) + health_status=$(curl -sS -o /dev/null -w '%{http_code}' --max-time "$UNIDESK_PAC_READ_STEP_TIMEOUT_SECONDS" "$health_url" 2>/dev/null || true) fi fi export UNIDESK_PAC_DIAG_IMAGE_REPOSITORY="$image_repository" @@ -1570,14 +1582,14 @@ consumer_bootstrap_state() { role_binding_file=$(mktemp) argo_secret_file=$(mktemp) if [ -n "${UNIDESK_PAC_RUNNER_SERVICE_ACCOUNT_NAME:-}" ]; then - kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get serviceaccount "$UNIDESK_PAC_RUNNER_SERVICE_ACCOUNT_NAME" -o json >"$sa_file" 2>/dev/null || printf '{}' >"$sa_file" - kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get rolebinding "$UNIDESK_PAC_RUNNER_ROLE_BINDING_NAME" -o json >"$role_binding_file" 2>/dev/null || printf '{}' >"$role_binding_file" + pac_read kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get serviceaccount "$UNIDESK_PAC_RUNNER_SERVICE_ACCOUNT_NAME" -o json >"$sa_file" 2>/dev/null || printf '{}' >"$sa_file" + pac_read kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get rolebinding "$UNIDESK_PAC_RUNNER_ROLE_BINDING_NAME" -o json >"$role_binding_file" 2>/dev/null || printf '{}' >"$role_binding_file" else printf '{}' >"$sa_file" printf '{}' >"$role_binding_file" fi if [ -n "${UNIDESK_PAC_ARGO_REPOSITORY_SECRET_NAME:-}" ]; then - kubectl -n "$UNIDESK_PAC_ARGO_NAMESPACE" get secret "$UNIDESK_PAC_ARGO_REPOSITORY_SECRET_NAME" -o json >"$argo_secret_file" 2>/dev/null || printf '{}' >"$argo_secret_file" + pac_read kubectl -n "$UNIDESK_PAC_ARGO_NAMESPACE" get secret "$UNIDESK_PAC_ARGO_REPOSITORY_SECRET_NAME" -o json >"$argo_secret_file" 2>/dev/null || printf '{}' >"$argo_secret_file" else printf '{}' >"$argo_secret_file" fi @@ -1673,13 +1685,15 @@ NODE } status_progress() { - printf '{"event":"pac-status-progress","stage":"%s","valuesPrinted":false}\n' "$1" >&2 + elapsed_seconds=$(( $(now_epoch) - UNIDESK_PAC_STATUS_STARTED_EPOCH )) + printf '{"event":"pac-status-progress","stage":"%s","elapsedSeconds":%s,"stepTimeoutSeconds":%s,"valuesPrinted":false}\n' "$1" "$elapsed_seconds" "$UNIDESK_PAC_READ_STEP_TIMEOUT_SECONDS" >&2 } status_action() { + UNIDESK_PAC_STATUS_STARTED_EPOCH=$(now_epoch) status_progress control-plane - crd=$(kubectl get crd repositories.pipelinesascode.tekton.dev -o name 2>/dev/null || true) - controller_ready=$(kubectl -n "$UNIDESK_PAC_RELEASE_NAMESPACE" get deploy "$UNIDESK_PAC_CONTROLLER_SERVICE_NAME" -o jsonpath='{.status.readyReplicas}/{.spec.replicas}' 2>/dev/null || echo "0/0") + crd=$(pac_read kubectl get crd repositories.pipelinesascode.tekton.dev -o name 2>/dev/null || true) + controller_ready=$(pac_read kubectl -n "$UNIDESK_PAC_RELEASE_NAMESPACE" get deploy "$UNIDESK_PAC_CONTROLLER_SERVICE_NAME" -o jsonpath='{.status.readyReplicas}/{.spec.replicas}' 2>/dev/null || echo "0/0") repository_condition=$(condition_status "$UNIDESK_PAC_TARGET_NAMESPACE" repository "$UNIDESK_PAC_REPOSITORY_NAME") status_progress admission-provenance prepare_admission_provenance_state @@ -1693,7 +1707,7 @@ status_action() { status_progress task-runs task_file=$(mktemp) if [ -n "$latest" ]; then - kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get taskrun -l "tekton.dev/pipelineRun=$latest" -o json >"$task_file" 2>/dev/null || printf '{"items":[]}' >"$task_file" + pac_read kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get taskrun -l "tekton.dev/pipelineRun=$latest" -o json >"$task_file" 2>/dev/null || printf '{"items":[]}' >"$task_file" else printf '{"items":[]}' >"$task_file" fi @@ -1706,7 +1720,10 @@ status_action() { status_progress webhooks hooks=$(hook_summary) status_progress argo - argo=$(kubectl -n "$UNIDESK_PAC_ARGO_NAMESPACE" get application "$UNIDESK_PAC_ARGO_APPLICATION" -o json 2>/dev/null | node -e 'const fs=require("fs"); const s=fs.readFileSync(0,"utf8").trim(); if(!s){process.stdout.write("{}"); process.exit(0)} const a=JSON.parse(s); const source=a.spec?.source || a.spec?.sources?.[0] || {}; let repoURL=source.repoURL||null; try { const url=new URL(repoURL); url.username=""; url.password=""; repoURL=url.toString(); } catch {} process.stdout.write(JSON.stringify({sync:a.status?.sync?.status||null, health:a.status?.health?.status||null, revision:a.status?.sync?.revision||null, repoURL, targetRevision:source.targetRevision||null}))' || echo '{}') + argo_file=$(mktemp) + pac_read kubectl -n "$UNIDESK_PAC_ARGO_NAMESPACE" get application "$UNIDESK_PAC_ARGO_APPLICATION" -o json >"$argo_file" 2>/dev/null || printf '{}' >"$argo_file" + export UNIDESK_PAC_ARGO_FILE="$argo_file" + argo=$(node -e 'const fs=require("fs"); const s=fs.readFileSync(0,"utf8").trim(); if(!s){process.stdout.write("{}"); process.exit(0)} const a=JSON.parse(s); const source=a.spec?.source || a.spec?.sources?.[0] || {}; let repoURL=source.repoURL||null; try { const url=new URL(repoURL); url.username=""; url.password=""; repoURL=url.toString(); } catch {} process.stdout.write(JSON.stringify({sync:a.status?.sync?.status||null, health:a.status?.health?.status||null, revision:a.status?.sync?.revision||null, repoURL, targetRevision:source.targetRevision||null}))' <"$argo_file" || echo '{}') argo=$(json_normalize "$argo") status_progress gitops-revision revision_relation=$(gitops_revision_relation "$artifact" "$argo") @@ -1724,6 +1741,8 @@ NODE ) status_progress runtime runtime=$(json_normalize "$(runtime_summary)") + unset UNIDESK_PAC_ARGO_FILE + rm -f "$argo_file" status_progress diagnostics diagnostics=$(cicd_diagnostics "$pipelines" "$artifact" "$argo" "$runtime") diagnostics=$(UNIDESK_PAC_DIAGNOSTICS="$diagnostics" UNIDESK_PAC_STATE="$UNIDESK_PAC_ADMISSION_STATE_JSON" UNIDESK_PAC_BOOTSTRAP_STATE="$consumer_bootstrap" UNIDESK_PAC_PIPELINES_JSON="$pipelines" UNIDESK_PAC_ARTIFACT_JSON="$artifact" UNIDESK_PAC_ARGO_JSON="$argo" UNIDESK_PAC_RUNTIME_JSON="$runtime" node <<'NODE' diff --git a/scripts/src/platform-infra-pipelines-as-code.ts b/scripts/src/platform-infra-pipelines-as-code.ts index 8703bc2a..4f7f6592 100644 --- a/scripts/src/platform-infra-pipelines-as-code.ts +++ b/scripts/src/platform-infra-pipelines-as-code.ts @@ -2059,6 +2059,7 @@ function remoteScript(action: "apply" | "status" | "history" | "diagnose-regress UNIDESK_PAC_RELEASE_MANIFEST_B64: Buffer.from(releaseManifest, "utf8").toString("base64"), UNIDESK_PAC_CONTROLLER_SERVICE_NAME: pac.release.controllerServiceName, UNIDESK_PAC_WAIT_TIMEOUT_SECONDS: String(pac.release.waitTimeoutSeconds), + UNIDESK_PAC_READ_STEP_TIMEOUT_SECONDS: String(Math.max(1, Math.floor(pac.observability.readOnlyCapture.timeoutMs / 8_000))), UNIDESK_PAC_DRY_RUN: "dryRun" in options && options.dryRun ? "1" : "0", UNIDESK_PAC_GITEA_BASE_URL: pac.gitea.internalBaseUrl.replace(/\/+$/u, ""), UNIDESK_PAC_GITEA_ADMIN_USERNAME: secrets.adminUsername,