fix: make pac history read large namespaces

This commit is contained in:
Codex
2026-07-06 01:14:15 +00:00
parent cfc1b5bf9f
commit 356b8bf6ac
2 changed files with 50 additions and 13 deletions
@@ -266,6 +266,7 @@ const limit = Math.max(1, Math.min(50, Number.parseInt(process.env.UNIDESK_PAC_H
const detailId = process.env.UNIDESK_PAC_HISTORY_ID || '';
const consumers = JSON.parse(Buffer.from(process.env.UNIDESK_PAC_HISTORY_CONSUMERS_B64 || 'W10=', 'base64').toString('utf8'));
const displayTimeZone = process.env.UNIDESK_PAC_DISPLAY_TIME_ZONE || 'UTC';
const kubectlJsonErrors = [];
const displayTimeFormatter = new Intl.DateTimeFormat('sv-SE', {
timeZone: displayTimeZone,
year: 'numeric',
@@ -277,17 +278,44 @@ const displayTimeFormatter = new Intl.DateTimeFormat('sv-SE', {
hour12: false,
});
function kubectlJson(args, fallback) {
function kubectlJson(args, fallback, context) {
const tmpDir = fs.mkdtempSync('/tmp/unidesk-pac-history-');
const outPath = `${tmpDir}/kubectl.json`;
let outFd = null;
try {
const out = cp.execFileSync('kubectl', args, {
outFd = fs.openSync(outPath, 'w');
const result = cp.spawnSync('kubectl', args, {
stdio: ['ignore', outFd, 'pipe'],
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore'],
timeout: 30000,
maxBuffer: 10 * 1024 * 1024,
maxBuffer: 1024 * 1024,
});
fs.closeSync(outFd);
outFd = null;
if (result.error || result.status !== 0) {
kubectlJsonErrors.push({
context: context || args.join(' '),
status: result.status,
error: result.error ? String(result.error.message || result.error) : null,
stderr: String(result.stderr || '').slice(0, 1000),
});
return fallback;
}
const out = fs.readFileSync(outPath, 'utf8');
return JSON.parse(out || JSON.stringify(fallback));
} catch {
} catch (error) {
kubectlJsonErrors.push({
context: context || args.join(' '),
status: null,
error: String(error && error.message ? error.message : error).slice(0, 1000),
stderr: null,
});
return fallback;
} finally {
if (outFd !== null) {
try { fs.closeSync(outFd); } catch {}
}
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
}
}
@@ -474,8 +502,8 @@ function rowFor(consumer, item, taskRuns) {
const rows = [];
const consumerSummaries = [];
for (const consumer of consumers) {
const pipelineRuns = kubectlJson(['-n', consumer.namespace, 'get', 'pipelinerun', '-o', 'json'], { items: [] });
const taskRuns = kubectlJson(['-n', consumer.namespace, 'get', 'taskrun', '-o', 'json'], { items: [] });
const pipelineRuns = kubectlJson(['-n', consumer.namespace, 'get', 'pipelinerun', '-o', 'json'], { items: [] }, `${consumer.id}:pipelinerun`);
const taskRuns = kubectlJson(['-n', consumer.namespace, 'get', 'taskrun', '-o', 'json'], { items: [] }, `${consumer.id}:taskrun`);
let matches = (pipelineRuns.items || []).filter((item) => {
const labels = item.metadata?.labels || {};
const name = item.metadata?.name || '';
@@ -487,10 +515,10 @@ for (const consumer of consumers) {
matches = matches.sort((a, b) => Date.parse(b.metadata?.creationTimestamp || 0) - Date.parse(a.metadata?.creationTimestamp || 0));
if (!detailId) matches = matches.slice(0, limit);
for (const item of matches) rows.push(rowFor(consumer, item, taskRuns));
consumerSummaries.push({ id: consumer.id, namespace: consumer.namespace, repository: consumer.repository, matched: matches.length });
consumerSummaries.push({ id: consumer.id, namespace: consumer.namespace, repository: consumer.repository, pipelineRunPrefix: consumer.pipelineRunPrefix, totalPipelineRuns: (pipelineRuns.items || []).length, matched: matches.length });
}
rows.sort((a, b) => Date.parse(b.triggeredAt || 0) - Date.parse(a.triggeredAt || 0));
process.stdout.write(JSON.stringify({ rows, consumers: consumerSummaries }));
process.stdout.write(JSON.stringify({ rows, consumers: consumerSummaries, errors: kubectlJsonErrors }));
NODE
}
@@ -672,7 +700,8 @@ history_action() {
history=$(history_rows)
rows=$(printf '%s' "$history" | node -e 'const fs=require("fs"); const h=JSON.parse(fs.readFileSync(0,"utf8")||"{}"); process.stdout.write(JSON.stringify(h.rows||[]));')
consumers=$(printf '%s' "$history" | node -e 'const fs=require("fs"); const h=JSON.parse(fs.readFileSync(0,"utf8")||"{}"); process.stdout.write(JSON.stringify(h.consumers||[]));')
printf '{"ok":%s,"crdPresent":%s,"controllerReady":"%s","repositoryCondition":"%s","repository":{"name":"%s","repo":"%s/%s","url":"%s"},"consumer":"%s","display":{"timeZone":"%s"},"consumerRows":%s,"rows":%s,"webhooks":%s,"source":"gitea-pac-tekton-live","historyStore":"none","valuesPrinted":false}\n' \
errors=$(printf '%s' "$history" | node -e 'const fs=require("fs"); const h=JSON.parse(fs.readFileSync(0,"utf8")||"{}"); process.stdout.write(JSON.stringify(h.errors||[]));')
printf '{"ok":%s,"crdPresent":%s,"controllerReady":"%s","repositoryCondition":"%s","repository":{"name":"%s","repo":"%s/%s","url":"%s"},"consumer":"%s","display":{"timeZone":"%s"},"consumerRows":%s,"rows":%s,"historyErrors":%s,"webhooks":%s,"source":"gitea-pac-tekton-live","historyStore":"none","valuesPrinted":false}\n' \
"$( [ -n "$crd" ] && [ "$controller_ready" != "0/0" ] && echo true || echo false )" \
"$( [ -n "$crd" ] && echo true || echo false )" \
"$(json_string "$controller_ready")" \
@@ -683,7 +712,7 @@ history_action() {
"$(json_string "$UNIDESK_PAC_REPOSITORY_URL")" \
"$(json_string "$UNIDESK_PAC_CONSUMER_ID")" \
"$(json_string "$UNIDESK_PAC_DISPLAY_TIME_ZONE")" \
"$consumers" "$rows" "$hooks"
"$consumers" "$rows" "$errors" "$hooks"
}
webhook_test_action() {
@@ -425,8 +425,9 @@ async function history(config: UniDeskConfig, options: HistoryOptions): Promise<
const result = await capture(config, target.route, ["sh"], remoteScript("history", pac, target, firstRepository, firstConsumer, { ...options, confirm: false, dryRun: true, wait: false }, secrets, "", selectedConsumers));
const parsed = parseJsonOutput(result.stdout);
const remote = parsed ?? compactCapture(result, { full: true });
const historyErrors = arrayRecords(record(remote).historyErrors);
return {
ok: result.exitCode === 0 && parsed?.ok !== false,
ok: result.exitCode === 0 && parsed?.ok !== false && historyErrors.length === 0,
action: "platform-infra-pipelines-as-code-history",
mutation: false,
target: targetSummary(target),
@@ -440,6 +441,7 @@ async function history(config: UniDeskConfig, options: HistoryOptions): Promise<
consumers: selectedConsumers.map((consumer) => consumer.id),
detailId: options.detailId,
rows: arrayRecords(record(remote).rows),
historyErrors,
remote,
next: nextCommands(target.id, options.consumerId ?? pac.defaults.consumerId, pac.defaults.consumerId),
};
@@ -786,12 +788,18 @@ function renderStatus(result: Record<string, unknown>): RenderedCliResult {
function renderHistory(result: Record<string, unknown>): RenderedCliResult {
const rows = arrayRecords(result.rows);
const config = record(result.config);
const historyErrors = arrayRecords(result.historyErrors);
const detailId = stringValue(result.detailId);
const timeHeader = `TIME(${stringValue(config.displayTimeZone)})`;
const lines = [
"PLATFORM-INFRA PIPELINES-AS-CODE HISTORY",
`DATA: ${stringValue(config.source)}`,
`ROWS: ${stringValue(rows.length)} LIMIT_PER_CONSUMER: ${stringValue(config.limitPerConsumer)}`,
`ROWS: ${stringValue(rows.length)} LIMIT_PER_CONSUMER: ${stringValue(config.limitPerConsumer)} READ_ERRORS: ${stringValue(historyErrors.length)}`,
...(historyErrors.length === 0 ? [] : [
"",
"READ ERRORS",
...table(["CONTEXT", "STATUS", "ERROR"], historyErrors.map((item) => [stringValue(item.context), stringValue(item.status), compactLine(stringValue(item.error ?? item.stderr))])),
]),
"",
"TRIGGERS",
...(rows.length === 0 ? ["-"] : table([timeHeader, "CONSUMER", "REPO", "STATUS", "DUR_S", "COMMIT", "ENV_REUSE", "ID"], rows.map((row) => [