@controlplane/cli
Version:
Control Plane Corporation CLI
154 lines • 6.45 kB
JavaScript
"use strict";
// ANCHOR - Constants
Object.defineProperty(exports, "__esModule", { value: true });
exports.RemoteBuildLogFilter = void 0;
// Whole vertices to drop: registry auth, cache-manifest imports, internal builder steps.
const DROP_VERTEX = /\[auth\]|importing cache manifest|railpack/;
// Cache export is collapsed to its header and DONE — its per-layer writes are noise.
// Any phrase here classifies the vertex, so a log that begins mid-vertex still collapses it.
const CACHE_EXPORT_VERTEX = /exporting cache to registry|preparing build cache|writing cache image manifest|writing (?:layer|config) sha256:/;
const CACHE_EXPORT_HEADER = /exporting cache to registry/;
// Error-context blocks for dropped cache steps; real build-step failures do not match.
const NOISE_BLOCK = /importing cache manifest|exporting cache to registry/;
// A ------ line opens and closes a failed step's error-context block.
const SEPARATOR = '------';
// Every log line of a build step starts with the step's vertex number, e.g. "#15 npm ci".
const VERTEX = /^#(\d+)\b/;
// The line that closes a vertex, e.g. "#29 DONE 36.2s".
const VERTEX_DONE = /^#\d+ DONE\b/;
// ANCHOR - RemoteBuildLogFilter
/**
* Trims BuildKit plain-progress noise from the streamed build log while leaving real
* build steps and failures untouched. Unrecognized lines always pass through, so a
* change in BuildKit's wording shows more, never less.
*/
class RemoteBuildLogFilter {
constructor() {
// Start "blank" so a leading blank left by a dropped opening vertex is suppressed.
this.lastBlank = true;
this.block = null;
this.verdicts = new Map();
this.headerShown = new Set();
}
// Public Methods //
/**
* Filters one log line, returning the lines to actually print — usually zero or
* one, or several when a buffered error-context block is released.
*
* @param {string} line - One BuildKit log line.
* @returns {string[]} The lines to print.
*/
accept(line) {
// Inside a ------ block, every line is buffered until the closing separator arrives.
if (this.block !== null) {
this.block.push(line);
// The closing separator seals the block: a cache-noise block vanishes, any other block prints whole.
if (line.trim() === SEPARATOR) {
const buffered = this.block;
this.block = null;
return NOISE_BLOCK.test(buffered.join('\n')) ? [] : this.emit(buffered);
}
return [];
}
// An opening separator starts buffering — whether the block is noise is only known from its content.
if (line.trim() === SEPARATOR) {
this.block = [line];
return [];
}
const match = line.match(VERTEX);
// A line outside any vertex — a blank separator or a closing error summary — passes through.
if (!match) {
return this.emit([line]);
}
// Vertex lines are judged per build step, keyed by the vertex number.
return this.acceptVertexLine(Number(match[1]), line);
}
/**
* Flushes any buffered error-context block that never closed, so no line is lost
* when the log ends mid-block.
*
* @returns {string[]} The remaining lines to print.
*/
finish() {
// Nothing buffered — the log ended cleanly.
if (this.block === null) {
return [];
}
// A block with no closing separator is incomplete; it prints rather than risk hiding a real failure.
const buffered = this.block;
this.block = null;
return this.emit(buffered);
}
// Private Methods //
/**
* Classifies a vertex line and returns the lines to print for it. A vertex is
* classified once, on its first line — a build step whose own output mentions a
* marker phrase is never reclassified and swallowed mid-stream.
*
* @param {number} vertex - The vertex number.
* @param {string} line - The vertex line.
* @returns {string[]} The lines to print.
*/
acceptVertexLine(vertex, line) {
// The first line seen for a vertex decides its fate; later lines never re-judge it.
if (!this.verdicts.has(vertex)) {
this.verdicts.set(vertex, judgeVertex(line));
}
const verdict = this.verdicts.get(vertex);
// A dropped vertex vanishes entirely, its DONE line included.
if (verdict === 'drop') {
return [];
}
// A normal build step prints verbatim.
if (verdict !== 'collapse') {
return this.emit([line]);
}
// The collapsed cache export keeps its DONE line — it carries the step's total time.
if (VERTEX_DONE.test(line)) {
return this.emit([line]);
}
// The header line prints once, even when the vertex restarts and repeats it.
if (CACHE_EXPORT_HEADER.test(line) && !this.headerShown.has(vertex)) {
this.headerShown.add(vertex);
return this.emit([line]);
}
// Everything else in the collapsed vertex is per-layer progress noise.
return [];
}
/**
* Passes lines through, collapsing runs of blank lines that suppression can leave behind.
*
* @param {string[]} lines - The candidate lines.
* @returns {string[]} The lines to print.
*/
emit(lines) {
const out = [];
for (const line of lines) {
const blank = line.trim() === '';
// Dropping a vertex makes the blanks around it adjacent; only the first survives.
if (blank && this.lastBlank) {
continue;
}
this.lastBlank = blank;
out.push(line);
}
return out;
}
}
exports.RemoteBuildLogFilter = RemoteBuildLogFilter;
// SECTION - Functions
/**
* Judges one build step from its first line: pure machinery is dropped outright, the
* cache export is kept but reduced to its header and DONE, everything else prints.
*
* @param {string} line - The vertex's first line.
* @returns {VertexVerdict} What to do with every line of this vertex.
*/
function judgeVertex(line) {
if (DROP_VERTEX.test(line)) {
return 'drop';
}
return CACHE_EXPORT_VERTEX.test(line) ? 'collapse' : 'keep';
}
// !SECTION
//# sourceMappingURL=log-filter.js.map