diff --git a/scripts/src/deploy.ts b/scripts/src/deploy.ts index 7ede1add..277d9613 100644 --- a/scripts/src/deploy.ts +++ b/scripts/src/deploy.ts @@ -235,6 +235,40 @@ function asString(value: unknown): string { return typeof value === "string" ? value : ""; } +function parseJsonObjectFromText(text: string): unknown | null { + const start = text.indexOf("{"); + if (start < 0) return null; + let depth = 0; + let inString = false; + let escaped = false; + for (let index = start; index < text.length; index += 1) { + const char = text[index]; + if (inString) { + if (escaped) { + escaped = false; + } else if (char === "\\") { + escaped = true; + } else if (char === "\"") { + inString = false; + } + continue; + } + if (char === "\"") { + inString = true; + continue; + } + if (char === "{") { + depth += 1; + continue; + } + if (char === "}") { + depth -= 1; + if (depth === 0) return JSON.parse(text.slice(start, index + 1)) as unknown; + } + } + return null; +} + function parseFullCommit(value: string): string { const match = value.match(/\b[0-9a-f]{40}\b/iu); return match?.[0]?.toLowerCase() ?? ""; @@ -1688,7 +1722,38 @@ function devK3sServiceHealthScript(service: UniDeskMicroserviceConfig): string { `port=${shellQuote(String(port))}`, "case \"$path\" in /*) ;; *) path=\"/$path\" ;; esac", "proxy_path=\"/api/v1/namespaces/$namespace/services/http:$service_name:$port/proxy$path\"", - `KUBECONFIG=${shellQuote(k8sKubeconfig)} kubectl get --raw "$proxy_path"`, + "tmp_health=$(mktemp)", + "trap 'rm -f \"$tmp_health\"' EXIT", + `KUBECONFIG=${shellQuote(k8sKubeconfig)} kubectl get --raw "$proxy_path" > "$tmp_health"`, + "python3 - \"$tmp_health\" <<'PY'", + "import json", + "import sys", + "", + "text = open(sys.argv[1], encoding='utf-8', errors='replace').read()", + "try:", + " body = json.loads(text)", + "except Exception:", + " print(text[:4000])", + " raise SystemExit(0)", + "", + "def compact(mapping, keys):", + " if not isinstance(mapping, dict):", + " return None", + " return {key: mapping.get(key) for key in keys if key in mapping}", + "", + "summary = compact(body, ['ok', 'service', 'instanceId', 'role', 'environment', 'namespace', 'databaseName', 'serviceId', 'status', 'startedAt', 'deployRef']) or {}", + "deploy = compact(body.get('deploy'), ['repo', 'commit', 'requestedCommit', 'serviceId'])", + "if deploy is not None:", + " summary['deploy'] = deploy", + "queue = body.get('queue')", + "if isinstance(queue, dict):", + " queue_summary = compact(queue, ['total', 'queueCount', 'defaultQueueId', 'processing']) or {}", + " storage = compact(queue.get('storage'), ['primary', 'postgresReady'])", + " if storage is not None:", + " queue_summary['storage'] = storage", + " summary['queue'] = queue_summary", + "print(json.dumps(summary, ensure_ascii=False, separators=(',', ':')))", + "PY", ].join("\n"); } @@ -1698,12 +1763,14 @@ async function serviceHealth(config: UniDeskConfig, service: UniDeskMicroservice } if (isDevK3sDeployService(service)) { const result = await runTargetCommand(config, service, devK3sServiceHealthScript(service), "/home/ubuntu", 30_000, 20_000); + const stdout = result.stdout.trim(); let body: unknown = null; try { - body = result.stdout.trim().length > 0 ? JSON.parse(result.stdout.trim()) : null; + body = stdout.length > 0 ? parseJsonObjectFromText(stdout) : null; } catch { - body = { text: result.stdout.trim() }; + body = null; } + if (body === null && stdout.length > 0) body = { text: stdout }; return { ok: result.ok, status: result.ok ? 200 : 502,