@cleartrip/frontguard 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js
CHANGED
|
@@ -5,15 +5,16 @@ import f from 'readline';
|
|
|
5
5
|
import * as tty from 'tty';
|
|
6
6
|
import { WriteStream } from 'tty';
|
|
7
7
|
import path5, { sep, normalize, delimiter, resolve, dirname } from 'path';
|
|
8
|
-
import fs from 'fs/promises';
|
|
9
|
-
import {
|
|
8
|
+
import fs, { writeFile } from 'fs/promises';
|
|
9
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
10
|
+
import fs2, { existsSync, statSync, readFileSync } from 'fs';
|
|
10
11
|
import { execFileSync, spawn } from 'child_process';
|
|
11
12
|
import { createRequire } from 'module';
|
|
12
|
-
import fs2 from 'fs';
|
|
13
13
|
import { pipeline } from 'stream/promises';
|
|
14
14
|
import { PassThrough } from 'stream';
|
|
15
15
|
import fg from 'fast-glob';
|
|
16
16
|
import * as ts from 'typescript';
|
|
17
|
+
import { Resvg } from '@resvg/resvg-js';
|
|
17
18
|
|
|
18
19
|
var __create = Object.create;
|
|
19
20
|
var __defProp = Object.defineProperty;
|
|
@@ -2496,12 +2497,52 @@ async function initFrontGuard(cwd) {
|
|
|
2496
2497
|
await fs.writeFile(tplPr, PR_TEMPLATE, "utf8");
|
|
2497
2498
|
}
|
|
2498
2499
|
}
|
|
2500
|
+
var BITBUCKET_CHECKS_IMAGE_MARKDOWN_ALT = "FrontGuard checks summary";
|
|
2501
|
+
var MAX_BITBUCKET_INLINE_IMAGE_MARKDOWN_LINE_LENGTH = 3e5;
|
|
2502
|
+
var MAX_PNG_BYTES_BITBUCKET_INLINE = 21e4;
|
|
2503
|
+
var MARKDOWN_LINE_PREFIX = ` {
|
|
2505
|
+
return buf.length >= 8 && buf[0] === 137 && buf[1] === 80 && buf[2] === 78 && buf[3] === 71 && buf[4] === 13 && buf[5] === 10 && buf[6] === 26 && buf[7] === 10;
|
|
2506
|
+
}
|
|
2507
|
+
function pngBufferToBitbucketImageMarkdownLine(png) {
|
|
2508
|
+
if (!isPngBuffer(png)) {
|
|
2509
|
+
throw new TypeError("Buffer is not a PNG (missing IHDR signature)");
|
|
2510
|
+
}
|
|
2511
|
+
if (png.length > MAX_PNG_BYTES_BITBUCKET_INLINE) {
|
|
2512
|
+
throw new RangeError(
|
|
2513
|
+
`PNG too large for Bitbucket inline markdown (${png.length} bytes; max ${MAX_PNG_BYTES_BITBUCKET_INLINE}). Host the image and use an HTTPS URL instead.`
|
|
2514
|
+
);
|
|
2515
|
+
}
|
|
2516
|
+
const b64 = png.toString("base64");
|
|
2517
|
+
const line = `${MARKDOWN_LINE_PREFIX}${b64})`;
|
|
2518
|
+
if (line.length > MAX_BITBUCKET_INLINE_IMAGE_MARKDOWN_LINE_LENGTH) {
|
|
2519
|
+
throw new RangeError(
|
|
2520
|
+
`Inline markdown line too long (${line.length} chars; max ${MAX_BITBUCKET_INLINE_IMAGE_MARKDOWN_LINE_LENGTH})`
|
|
2521
|
+
);
|
|
2522
|
+
}
|
|
2523
|
+
return line;
|
|
2524
|
+
}
|
|
2525
|
+
function tryPngFileToBitbucketImageMarkdownLine(filePath) {
|
|
2526
|
+
try {
|
|
2527
|
+
if (!existsSync(filePath)) return null;
|
|
2528
|
+
const st = statSync(filePath);
|
|
2529
|
+
if (!st.isFile() || st.size > MAX_PNG_BYTES_BITBUCKET_INLINE) return null;
|
|
2530
|
+
const buf = readFileSync(filePath);
|
|
2531
|
+
return pngBufferToBitbucketImageMarkdownLine(buf);
|
|
2532
|
+
} catch {
|
|
2533
|
+
return null;
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2499
2536
|
|
|
2500
2537
|
// src/ci/bitbucket-pr-snippet.ts
|
|
2501
2538
|
function checksImageMarkdown() {
|
|
2539
|
+
const embedPath = process.env.FRONTGUARD_EMBED_CHECKS_PNG_PATH?.trim();
|
|
2540
|
+
if (embedPath) {
|
|
2541
|
+
return tryPngFileToBitbucketImageMarkdownLine(embedPath);
|
|
2542
|
+
}
|
|
2502
2543
|
const u4 = process.env.FRONTGUARD_CHECKS_IMAGE_URL?.trim();
|
|
2503
2544
|
if (!u4) return null;
|
|
2504
|
-
return ``;
|
|
2505
2546
|
}
|
|
2506
2547
|
function bitbucketPipelineResultsUrl() {
|
|
2507
2548
|
const full = process.env.BITBUCKET_REPO_FULL_NAME?.trim();
|
|
@@ -5532,6 +5573,132 @@ function applyAiAssistedEscalation(results, pr, config) {
|
|
|
5532
5573
|
}
|
|
5533
5574
|
}
|
|
5534
5575
|
}
|
|
5576
|
+
var W3 = 920;
|
|
5577
|
+
var PAD_X = 24;
|
|
5578
|
+
var TABLE_X = 20;
|
|
5579
|
+
var TABLE_W = 880;
|
|
5580
|
+
var HEADER_H = 34;
|
|
5581
|
+
var ROW_H = 34;
|
|
5582
|
+
var COL_CHECK = 56;
|
|
5583
|
+
var COL_STATUS = 508;
|
|
5584
|
+
var COL_NUM = 748;
|
|
5585
|
+
var COL_TIME = 888;
|
|
5586
|
+
function escapeXml(s3) {
|
|
5587
|
+
return s3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
5588
|
+
}
|
|
5589
|
+
function truncate5(s3, max) {
|
|
5590
|
+
const t3 = s3.replace(/\s+/g, " ").trim();
|
|
5591
|
+
if (t3.length <= max) return t3;
|
|
5592
|
+
return `${t3.slice(0, max - 1)}\u2026`;
|
|
5593
|
+
}
|
|
5594
|
+
function formatDuration(ms) {
|
|
5595
|
+
if (ms < 1e3) return `${ms} ms`;
|
|
5596
|
+
const s3 = Math.round(ms / 1e3);
|
|
5597
|
+
if (s3 < 60) return `${s3}s`;
|
|
5598
|
+
const m3 = Math.floor(s3 / 60);
|
|
5599
|
+
const r4 = s3 % 60;
|
|
5600
|
+
return r4 ? `${m3}m ${r4}s` : `${m3}m`;
|
|
5601
|
+
}
|
|
5602
|
+
function statusText(r4) {
|
|
5603
|
+
if (r4.skipped) return `Skipped \u2014 ${truncate5(r4.skipped, 36)}`;
|
|
5604
|
+
if (r4.findings.length === 0) return "Pass";
|
|
5605
|
+
return `${r4.findings.length} issue(s)`;
|
|
5606
|
+
}
|
|
5607
|
+
function dotFill(r4) {
|
|
5608
|
+
if (r4.skipped) return "#cbd5e1";
|
|
5609
|
+
if (r4.findings.length === 0) return "#16a34a";
|
|
5610
|
+
if (r4.findings.some((x3) => x3.severity === "block")) return "#dc2626";
|
|
5611
|
+
return "#d97706";
|
|
5612
|
+
}
|
|
5613
|
+
function riskFill(risk) {
|
|
5614
|
+
if (risk === "LOW") return "#16a34a";
|
|
5615
|
+
if (risk === "MEDIUM") return "#d97706";
|
|
5616
|
+
return "#dc2626";
|
|
5617
|
+
}
|
|
5618
|
+
function buildChecksSnapshotSvg(p2) {
|
|
5619
|
+
const { riskScore, mode, results, warns, infos, blocks } = p2;
|
|
5620
|
+
const modeLabel = mode === "enforce" ? "Enforce" : "Warn only";
|
|
5621
|
+
const brandY = 20;
|
|
5622
|
+
const titleY = 42;
|
|
5623
|
+
const metaY = 66;
|
|
5624
|
+
const tableTop = 88;
|
|
5625
|
+
const bodyRows = results.length;
|
|
5626
|
+
const cardH = HEADER_H + bodyRows * ROW_H;
|
|
5627
|
+
const totalH = tableTop + cardH + 28;
|
|
5628
|
+
const headerMid = tableTop + HEADER_H / 2 + 5;
|
|
5629
|
+
const rowTextY = (i3) => tableTop + HEADER_H + i3 * ROW_H + ROW_H / 2 + 5;
|
|
5630
|
+
const headerCells = [
|
|
5631
|
+
{ x: COL_CHECK, label: "CHECK", anchor: "start" },
|
|
5632
|
+
{ x: COL_STATUS, label: "STATUS", anchor: "start" },
|
|
5633
|
+
{ x: COL_NUM, label: "#", anchor: "end" },
|
|
5634
|
+
{ x: COL_TIME, label: "TIME", anchor: "end" }
|
|
5635
|
+
];
|
|
5636
|
+
const rowLines = [];
|
|
5637
|
+
for (let i3 = 0; i3 < bodyRows; i3++) {
|
|
5638
|
+
const y4 = tableTop + HEADER_H + i3 * ROW_H;
|
|
5639
|
+
const fill = i3 % 2 === 1 ? "#f8fafc" : "#ffffff";
|
|
5640
|
+
rowLines.push(
|
|
5641
|
+
`<rect x="${TABLE_X}" y="${y4}" width="${TABLE_W}" height="${ROW_H}" fill="${fill}" stroke="none"/>`
|
|
5642
|
+
);
|
|
5643
|
+
}
|
|
5644
|
+
const bodyCells = [];
|
|
5645
|
+
for (let i3 = 0; i3 < bodyRows; i3++) {
|
|
5646
|
+
const r4 = results[i3];
|
|
5647
|
+
const cy = rowTextY(i3);
|
|
5648
|
+
const cx = 36;
|
|
5649
|
+
bodyCells.push(
|
|
5650
|
+
`<circle cx="${cx}" cy="${cy - 4}" r="4.5" fill="${dotFill(r4)}"/>`,
|
|
5651
|
+
`<text x="${COL_CHECK}" y="${cy}" font-size="13" font-weight="600" fill="#0f172a" font-family="system-ui, -apple-system, Segoe UI, sans-serif">${escapeXml(truncate5(r4.checkId, 46))}</text>`,
|
|
5652
|
+
`<text x="${COL_STATUS}" y="${cy}" font-size="13" fill="#0f172a" font-family="system-ui, -apple-system, Segoe UI, sans-serif">${escapeXml(statusText(r4))}</text>`,
|
|
5653
|
+
`<text x="${COL_NUM}" y="${cy}" font-size="13" fill="#64748b" font-family="ui-monospace, monospace" text-anchor="end">${escapeXml(r4.skipped ? "\u2014" : String(r4.findings.length))}</text>`,
|
|
5654
|
+
`<text x="${COL_TIME}" y="${cy}" font-size="13" fill="#64748b" font-family="ui-monospace, monospace" text-anchor="end">${escapeXml(formatDuration(r4.durationMs))}</text>`
|
|
5655
|
+
);
|
|
5656
|
+
}
|
|
5657
|
+
const gridLines = [];
|
|
5658
|
+
for (let i3 = 0; i3 <= bodyRows; i3++) {
|
|
5659
|
+
const y4 = tableTop + HEADER_H + i3 * ROW_H;
|
|
5660
|
+
gridLines.push(
|
|
5661
|
+
`<line x1="${TABLE_X}" y1="${y4}" x2="${TABLE_X + TABLE_W}" y2="${y4}" stroke="#e2e8f0" stroke-width="1"/>`
|
|
5662
|
+
);
|
|
5663
|
+
}
|
|
5664
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
5665
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="${W3}" height="${totalH}" viewBox="0 0 ${W3} ${totalH}">
|
|
5666
|
+
<rect width="${W3}" height="${totalH}" fill="#f8fafc"/>
|
|
5667
|
+
<text x="${PAD_X}" y="${brandY}" font-size="11" font-weight="600" fill="#64748b" letter-spacing="0.12em" font-family="system-ui, -apple-system, Segoe UI, sans-serif">FRONTGUARD</text>
|
|
5668
|
+
<text x="${PAD_X}" y="${titleY}" font-size="17" font-weight="600" fill="#0f172a" font-family="system-ui, -apple-system, Segoe UI, sans-serif">Checks</text>
|
|
5669
|
+
<text x="${PAD_X}" y="${metaY}" font-size="13" fill="#64748b" font-family="system-ui, -apple-system, Segoe UI, sans-serif">
|
|
5670
|
+
<tspan>Risk </tspan><tspan fill="${riskFill(riskScore)}" font-weight="600">${escapeXml(riskScore)}</tspan>
|
|
5671
|
+
<tspan> \xB7 ${escapeXml(modeLabel)} \xB7 Blocking </tspan><tspan fill="#0f172a" font-weight="600">${blocks}</tspan>
|
|
5672
|
+
<tspan> \xB7 Warnings </tspan><tspan fill="#0f172a" font-weight="600">${warns}</tspan>
|
|
5673
|
+
<tspan> \xB7 Info </tspan><tspan fill="#0f172a" font-weight="600">${infos}</tspan>
|
|
5674
|
+
</text>
|
|
5675
|
+
<defs>
|
|
5676
|
+
<clipPath id="fg-card">
|
|
5677
|
+
<rect x="${TABLE_X}" y="${tableTop}" width="${TABLE_W}" height="${cardH}" rx="10" ry="10"/>
|
|
5678
|
+
</clipPath>
|
|
5679
|
+
</defs>
|
|
5680
|
+
<rect x="${TABLE_X}" y="${tableTop}" width="${TABLE_W}" height="${cardH}" rx="10" ry="10" fill="#ffffff" stroke="#e2e8f0" stroke-width="1"/>
|
|
5681
|
+
<g clip-path="url(#fg-card)">
|
|
5682
|
+
<rect x="${TABLE_X}" y="${tableTop}" width="${TABLE_W}" height="${HEADER_H}" fill="#f1f5f9"/>
|
|
5683
|
+
${headerCells.map(
|
|
5684
|
+
(c4) => `<text x="${c4.x}" y="${headerMid}" font-size="11" font-weight="600" fill="#64748b" letter-spacing="0.04em" font-family="system-ui, -apple-system, Segoe UI, sans-serif" text-anchor="${c4.anchor}">${c4.label}</text>`
|
|
5685
|
+
).join("\n ")}
|
|
5686
|
+
${rowLines.join("\n ")}
|
|
5687
|
+
${gridLines.join("\n ")}
|
|
5688
|
+
${bodyCells.join("\n ")}
|
|
5689
|
+
</g>
|
|
5690
|
+
</svg>`;
|
|
5691
|
+
}
|
|
5692
|
+
async function renderChecksSnapshotPng(pngPath, input) {
|
|
5693
|
+
const svg = buildChecksSnapshotSvg(input);
|
|
5694
|
+
const resvg = new Resvg(svg, {
|
|
5695
|
+
background: "#f8fafc",
|
|
5696
|
+
fitTo: { mode: "width", value: W3 },
|
|
5697
|
+
font: { loadSystemFonts: true }
|
|
5698
|
+
});
|
|
5699
|
+
const img = resvg.render();
|
|
5700
|
+
await writeFile(pngPath, img.asPng());
|
|
5701
|
+
}
|
|
5535
5702
|
|
|
5536
5703
|
// src/report/builder.ts
|
|
5537
5704
|
var import_picocolors = __toESM(require_picocolors());
|
|
@@ -5585,7 +5752,7 @@ function sortFindings(cwd, items) {
|
|
|
5585
5752
|
return a3.f.message.localeCompare(b3.f.message);
|
|
5586
5753
|
});
|
|
5587
5754
|
}
|
|
5588
|
-
function
|
|
5755
|
+
function formatDuration2(ms) {
|
|
5589
5756
|
if (ms < 1e3) return `${ms} ms`;
|
|
5590
5757
|
const s3 = Math.round(ms / 1e3);
|
|
5591
5758
|
if (s3 < 60) return `${s3}s`;
|
|
@@ -5720,7 +5887,7 @@ function renderCheckTableRows(results) {
|
|
|
5720
5887
|
const help = escapeHtml(getCheckDescription(r4.checkId));
|
|
5721
5888
|
const ariaWhat = escapeHtml(`What does the ${r4.checkId} check do?`);
|
|
5722
5889
|
const checkTitle = `<span class="check-title-cell"><strong class="check-name">${escapeHtml(r4.checkId)}</strong><span class="check-info-wrap"><button type="button" class="check-info" title="${help}" aria-label="${ariaWhat}">i</button><span class="check-tooltip" role="tooltip">${help}</span></span></span>`;
|
|
5723
|
-
return `<tr><td class="td-icon">${statusDot(r4)}</td><td class="td-check">${checkTitle}</td><td class="td-status">${status}</td><td class="td-num">${r4.skipped ? "\u2014" : r4.findings.length}</td><td class="td-time">${
|
|
5890
|
+
return `<tr><td class="td-icon">${statusDot(r4)}</td><td class="td-check">${checkTitle}</td><td class="td-status">${status}</td><td class="td-num">${r4.skipped ? "\u2014" : r4.findings.length}</td><td class="td-time">${formatDuration2(r4.durationMs)}</td></tr>`;
|
|
5724
5891
|
}).join("\n");
|
|
5725
5892
|
}
|
|
5726
5893
|
function buildChecksSnapshotHtml(p2) {
|
|
@@ -6146,14 +6313,32 @@ function buildReport(stack, pr, results, options) {
|
|
|
6146
6313
|
lines,
|
|
6147
6314
|
llmAppendix: options?.llmAppendix ?? null
|
|
6148
6315
|
}) : null;
|
|
6149
|
-
const
|
|
6316
|
+
const llmAppendix = options?.llmAppendix ?? null;
|
|
6317
|
+
const checksSnapshotInput = options?.emitChecksSnapshot === true ? {
|
|
6318
|
+
cwd,
|
|
6150
6319
|
riskScore,
|
|
6151
6320
|
mode,
|
|
6321
|
+
stack,
|
|
6322
|
+
pr,
|
|
6152
6323
|
results,
|
|
6153
6324
|
warns,
|
|
6154
6325
|
infos,
|
|
6155
|
-
blocks
|
|
6156
|
-
|
|
6326
|
+
blocks,
|
|
6327
|
+
lines,
|
|
6328
|
+
llmAppendix
|
|
6329
|
+
} : null;
|
|
6330
|
+
const checksSnapshotHtml = checksSnapshotInput != null ? buildChecksSnapshotHtml(checksSnapshotInput) : null;
|
|
6331
|
+
return {
|
|
6332
|
+
riskScore,
|
|
6333
|
+
stack,
|
|
6334
|
+
pr,
|
|
6335
|
+
results,
|
|
6336
|
+
markdown,
|
|
6337
|
+
consoleText,
|
|
6338
|
+
html,
|
|
6339
|
+
checksSnapshotHtml,
|
|
6340
|
+
checksSnapshotInput
|
|
6341
|
+
};
|
|
6157
6342
|
}
|
|
6158
6343
|
function scoreRisk(blocks, warns, lines, files) {
|
|
6159
6344
|
let score = 0;
|
|
@@ -6201,7 +6386,7 @@ function countShieldColor(kind, n3) {
|
|
|
6201
6386
|
if (n3 <= 10) return "yellow";
|
|
6202
6387
|
return "orange";
|
|
6203
6388
|
}
|
|
6204
|
-
function
|
|
6389
|
+
function formatDuration3(ms) {
|
|
6205
6390
|
if (ms < 1e3) return `${ms} ms`;
|
|
6206
6391
|
const s3 = Math.round(ms / 1e3);
|
|
6207
6392
|
if (s3 < 60) return `${s3}s`;
|
|
@@ -6356,7 +6541,7 @@ function formatMarkdown(p2) {
|
|
|
6356
6541
|
}
|
|
6357
6542
|
const nFind = r4.skipped ? "\u2014" : String(r4.findings.length);
|
|
6358
6543
|
sb.push(
|
|
6359
|
-
`| ${he2} | **${r4.checkId}** | ${status} | **${nFind}** | ${
|
|
6544
|
+
`| ${he2} | **${r4.checkId}** | ${status} | **${nFind}** | ${formatDuration3(r4.durationMs)} |`
|
|
6360
6545
|
);
|
|
6361
6546
|
}
|
|
6362
6547
|
sb.push("");
|
|
@@ -6915,26 +7100,41 @@ async function runFrontGuard(opts) {
|
|
|
6915
7100
|
llmAppendix,
|
|
6916
7101
|
cwd: opts.cwd,
|
|
6917
7102
|
emitHtml: Boolean(opts.htmlOut),
|
|
6918
|
-
emitChecksSnapshot: Boolean(opts.checksSnapshotOut)
|
|
7103
|
+
emitChecksSnapshot: Boolean(opts.checksSnapshotOut || opts.checksPngOut)
|
|
6919
7104
|
});
|
|
6920
7105
|
if (opts.htmlOut && report.html) {
|
|
6921
7106
|
await fs.writeFile(opts.htmlOut, report.html, "utf8");
|
|
6922
7107
|
}
|
|
6923
|
-
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
`
|
|
6929
|
-
FrontGuard: wrote checks snapshot HTML to ${snapPath}
|
|
6930
|
-
|
|
6931
|
-
|
|
7108
|
+
let embedPngPath = null;
|
|
7109
|
+
if ((opts.checksSnapshotOut || opts.checksPngOut) && report.checksSnapshotHtml && report.checksSnapshotInput) {
|
|
7110
|
+
if (opts.checksSnapshotOut) {
|
|
7111
|
+
const snapPath = path5.isAbsolute(opts.checksSnapshotOut) ? opts.checksSnapshotOut : path5.join(opts.cwd, opts.checksSnapshotOut);
|
|
7112
|
+
await fs.writeFile(snapPath, report.checksSnapshotHtml, "utf8");
|
|
7113
|
+
g.stderr.write(`
|
|
7114
|
+
FrontGuard: wrote checks snapshot HTML to ${snapPath}
|
|
7115
|
+
`);
|
|
7116
|
+
}
|
|
7117
|
+
if (opts.checksPngOut) {
|
|
7118
|
+
const pngAbs = path5.isAbsolute(opts.checksPngOut) ? opts.checksPngOut : path5.join(opts.cwd, opts.checksPngOut);
|
|
7119
|
+
await renderChecksSnapshotPng(pngAbs, report.checksSnapshotInput);
|
|
7120
|
+
embedPngPath = pngAbs;
|
|
7121
|
+
g.stderr.write(`FrontGuard: wrote checks PNG to ${pngAbs}.
|
|
7122
|
+
|
|
7123
|
+
`);
|
|
7124
|
+
} else if (opts.checksSnapshotOut) {
|
|
7125
|
+
g.stderr.write(
|
|
7126
|
+
` Tip: add --checksPngOut checks.png to render the checks table to a PNG (no browser).
|
|
6932
7127
|
|
|
6933
7128
|
`
|
|
6934
|
-
|
|
7129
|
+
);
|
|
7130
|
+
}
|
|
6935
7131
|
}
|
|
6936
7132
|
if (opts.prCommentOut) {
|
|
7133
|
+
if (embedPngPath) {
|
|
7134
|
+
g.env.FRONTGUARD_EMBED_CHECKS_PNG_PATH = embedPngPath;
|
|
7135
|
+
}
|
|
6937
7136
|
const snippet = formatBitbucketPrSnippet(report);
|
|
7137
|
+
delete g.env.FRONTGUARD_EMBED_CHECKS_PNG_PATH;
|
|
6938
7138
|
const abs = path5.isAbsolute(opts.prCommentOut) ? opts.prCommentOut : path5.join(opts.cwd, opts.prCommentOut);
|
|
6939
7139
|
await fs.writeFile(abs, snippet, "utf8");
|
|
6940
7140
|
g.stderr.write(
|
|
@@ -6942,7 +7142,7 @@ FrontGuard: wrote checks snapshot HTML to ${snapPath} (screenshot this file for
|
|
|
6942
7142
|
FrontGuard: wrote Bitbucket PR comment text to ${abs} (${snippet.length} bytes).
|
|
6943
7143
|
Use ONLY this file in your POST \u2026/pullrequests/{id}/comments payload (content.raw).
|
|
6944
7144
|
Do not post frontguard-report.md or captured stdout \u2014 that is the long markdown log.
|
|
6945
|
-
|
|
7145
|
+
With --checksPngOut, the PNG is inlined (small files only) or set FRONTGUARD_CHECKS_IMAGE_URL.
|
|
6946
7146
|
|
|
6947
7147
|
`
|
|
6948
7148
|
);
|
|
@@ -6997,7 +7197,11 @@ var run = defineCommand({
|
|
|
6997
7197
|
},
|
|
6998
7198
|
checksSnapshotOut: {
|
|
6999
7199
|
type: "string",
|
|
7000
|
-
description: "Write HTML with only the Checks table (
|
|
7200
|
+
description: "Write HTML with only the Checks table (for screenshots / PR comments)"
|
|
7201
|
+
},
|
|
7202
|
+
checksPngOut: {
|
|
7203
|
+
type: "string",
|
|
7204
|
+
description: "Write PNG of the checks table (SVG raster; @resvg/resvg-js, no browser). Pair with --checksSnapshotOut or use alone"
|
|
7001
7205
|
},
|
|
7002
7206
|
prCommentOut: {
|
|
7003
7207
|
type: "string",
|
|
@@ -7012,6 +7216,7 @@ var run = defineCommand({
|
|
|
7012
7216
|
append: typeof args.append === "string" ? args.append : null,
|
|
7013
7217
|
htmlOut: typeof args.htmlOut === "string" ? args.htmlOut : null,
|
|
7014
7218
|
checksSnapshotOut: typeof args.checksSnapshotOut === "string" ? args.checksSnapshotOut : null,
|
|
7219
|
+
checksPngOut: typeof args.checksPngOut === "string" ? args.checksPngOut : null,
|
|
7015
7220
|
prCommentOut: typeof args.prCommentOut === "string" ? args.prCommentOut : null
|
|
7016
7221
|
});
|
|
7017
7222
|
}
|