@cleartrip/frontguard 0.2.4 → 0.2.6

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
@@ -7,9 +7,10 @@ import { WriteStream } from 'tty';
7
7
  import path5, { sep, normalize, delimiter, resolve, dirname } from 'path';
8
8
  import fs from 'fs/promises';
9
9
  import { pathToFileURL, fileURLToPath } from 'url';
10
- import { execFileSync, spawn } from 'child_process';
10
+ import { tmpdir } from 'os';
11
+ import fs2, { existsSync, statSync, readFileSync } from 'fs';
12
+ import { spawn, execFileSync } from 'child_process';
11
13
  import { createRequire } from 'module';
12
- import fs2 from 'fs';
13
14
  import { pipeline } from 'stream/promises';
14
15
  import { PassThrough } from 'stream';
15
16
  import fg from 'fast-glob';
@@ -2496,9 +2497,23 @@ async function initFrontGuard(cwd) {
2496
2497
  await fs.writeFile(tplPr, PR_TEMPLATE, "utf8");
2497
2498
  }
2498
2499
  }
2499
-
2500
- // src/ci/bitbucket-pr-snippet.ts
2500
+ var MAX_PNG_BYTES_FOR_EMBED = 22e4;
2501
2501
  function checksImageMarkdown() {
2502
+ const embedPath = process.env.FRONTGUARD_EMBED_CHECKS_PNG_PATH?.trim();
2503
+ if (embedPath) {
2504
+ try {
2505
+ if (!existsSync(embedPath)) return null;
2506
+ const st = statSync(embedPath);
2507
+ if (!st.isFile() || st.size > MAX_PNG_BYTES_FOR_EMBED) return null;
2508
+ const buf = readFileSync(embedPath);
2509
+ const b64 = buf.toString("base64");
2510
+ const md = `![FrontGuard \u2014 checks summary](data:image/png;base64,${b64})`;
2511
+ if (md.length > 45e4) return null;
2512
+ return md;
2513
+ } catch {
2514
+ return null;
2515
+ }
2516
+ }
2502
2517
  const u4 = process.env.FRONTGUARD_CHECKS_IMAGE_URL?.trim();
2503
2518
  if (!u4) return null;
2504
2519
  return `![FrontGuard \u2014 checks summary](${u4})`;
@@ -2524,13 +2539,17 @@ function bitbucketDownloadsPageUrl() {
2524
2539
  if (ws && slug) return `https://bitbucket.org/${ws}/${slug}/downloads/`;
2525
2540
  return null;
2526
2541
  }
2542
+ var DETAILED_REPORT_LINE = "For detailed check analysis, please open the full interactive report:";
2527
2543
  function formatBitbucketPrSnippet(report) {
2528
2544
  const publicReport = process.env.FRONTGUARD_PUBLIC_REPORT_URL?.trim();
2529
2545
  const linkOnly = process.env.FRONTGUARD_BITBUCKET_COMMENT_LINK_ONLY === "1";
2530
2546
  const imgMd = checksImageMarkdown();
2531
2547
  if (linkOnly && publicReport) {
2532
2548
  const parts = [];
2533
- if (imgMd) parts.push(imgMd, "");
2549
+ if (imgMd) {
2550
+ parts.push(imgMd, "");
2551
+ parts.push(DETAILED_REPORT_LINE);
2552
+ }
2534
2553
  parts.push(publicReport.endsWith("\n") ? publicReport.slice(0, -1) : publicReport);
2535
2554
  return `${parts.join("\n")}
2536
2555
  `;
@@ -2559,15 +2578,13 @@ function formatBitbucketPrSnippet(report) {
2559
2578
  ""
2560
2579
  );
2561
2580
  if (publicReport) {
2562
- out.push("Full interactive report (open in browser):");
2563
- out.push(publicReport);
2564
- out.push("");
2565
2581
  if (imgMd) {
2566
- out.push(
2567
- "The image above is a quick checks overview. Use the link for file-level findings, hints, and suggested fixes."
2568
- );
2569
- out.push("");
2582
+ out.push(DETAILED_REPORT_LINE);
2583
+ } else {
2584
+ out.push("Full interactive report (open in browser):");
2570
2585
  }
2586
+ out.push(publicReport);
2587
+ out.push("");
2571
2588
  } else if (downloadsName && downloadsPage) {
2572
2589
  out.push("HTML report is in Repository \u2192 Downloads. Open this page while logged in:");
2573
2590
  out.push(downloadsPage);
@@ -5530,6 +5547,30 @@ function applyAiAssistedEscalation(results, pr, config) {
5530
5547
  }
5531
5548
  }
5532
5549
  }
5550
+ var PLAYWRIGHT = "playwright@1.49.1";
5551
+ function runCmd(command, args) {
5552
+ return new Promise((resolve, reject) => {
5553
+ const p2 = spawn(command, args, { stdio: "inherit", env: process.env });
5554
+ p2.on("error", reject);
5555
+ p2.on("close", (code) => {
5556
+ if (code === 0) resolve();
5557
+ else reject(new Error(`${command} exited with code ${code}`));
5558
+ });
5559
+ });
5560
+ }
5561
+ async function runChecksHtmlToPng(htmlPath, pngPath) {
5562
+ const fileUrl = pathToFileURL(htmlPath).href;
5563
+ const npx = process.platform === "win32" ? "npx.cmd" : "npx";
5564
+ await runCmd(npx, ["-y", PLAYWRIGHT, "install", "chromium"]);
5565
+ await runCmd(npx, [
5566
+ "-y",
5567
+ PLAYWRIGHT,
5568
+ "screenshot",
5569
+ fileUrl,
5570
+ pngPath,
5571
+ "--viewport-size=920,1000"
5572
+ ]);
5573
+ }
5533
5574
 
5534
5575
  // src/report/builder.ts
5535
5576
  var import_picocolors = __toESM(require_picocolors());
@@ -6913,26 +6954,53 @@ async function runFrontGuard(opts) {
6913
6954
  llmAppendix,
6914
6955
  cwd: opts.cwd,
6915
6956
  emitHtml: Boolean(opts.htmlOut),
6916
- emitChecksSnapshot: Boolean(opts.checksSnapshotOut)
6957
+ emitChecksSnapshot: Boolean(opts.checksSnapshotOut || opts.checksPngOut)
6917
6958
  });
6918
6959
  if (opts.htmlOut && report.html) {
6919
6960
  await fs.writeFile(opts.htmlOut, report.html, "utf8");
6920
6961
  }
6921
- if (opts.checksSnapshotOut && report.checksSnapshotHtml) {
6922
- const snapPath = path5.isAbsolute(opts.checksSnapshotOut) ? opts.checksSnapshotOut : path5.join(opts.cwd, opts.checksSnapshotOut);
6923
- await fs.writeFile(snapPath, report.checksSnapshotHtml, "utf8");
6924
- const fileUrl = pathToFileURL(snapPath).href;
6925
- g.stderr.write(
6926
- `
6927
- FrontGuard: wrote checks snapshot HTML to ${snapPath} (screenshot this file for PR comments).
6928
- Example: npx playwright screenshot "${fileUrl}" frontguard-checks.png
6929
- Host the PNG at an HTTPS URL, then set FRONTGUARD_CHECKS_IMAGE_URL before generating the PR comment.
6962
+ let embedPngPath = null;
6963
+ let tmpDir = null;
6964
+ let htmlForPng;
6965
+ if ((opts.checksSnapshotOut || opts.checksPngOut) && report.checksSnapshotHtml) {
6966
+ if (opts.checksSnapshotOut) {
6967
+ const snapPath = path5.isAbsolute(opts.checksSnapshotOut) ? opts.checksSnapshotOut : path5.join(opts.cwd, opts.checksSnapshotOut);
6968
+ await fs.writeFile(snapPath, report.checksSnapshotHtml, "utf8");
6969
+ htmlForPng = snapPath;
6970
+ g.stderr.write(`
6971
+ FrontGuard: wrote checks snapshot HTML to ${snapPath}
6972
+ `);
6973
+ }
6974
+ if (opts.checksPngOut) {
6975
+ if (!htmlForPng) {
6976
+ tmpDir = await fs.mkdtemp(path5.join(tmpdir(), "fg-checks-"));
6977
+ htmlForPng = path5.join(tmpDir, "checks.html");
6978
+ await fs.writeFile(htmlForPng, report.checksSnapshotHtml, "utf8");
6979
+ }
6980
+ const pngAbs = path5.isAbsolute(opts.checksPngOut) ? opts.checksPngOut : path5.join(opts.cwd, opts.checksPngOut);
6981
+ await runChecksHtmlToPng(htmlForPng, pngAbs);
6982
+ embedPngPath = pngAbs;
6983
+ g.stderr.write(`FrontGuard: wrote checks PNG to ${pngAbs} (Playwright via npx).
6984
+
6985
+ `);
6986
+ if (tmpDir) {
6987
+ await fs.rm(tmpDir, { recursive: true, force: true });
6988
+ tmpDir = null;
6989
+ }
6990
+ } else if (opts.checksSnapshotOut && htmlForPng) {
6991
+ g.stderr.write(
6992
+ ` Tip: add --checksPngOut checks.png to render this HTML to PNG with Playwright (npx).
6930
6993
 
6931
6994
  `
6932
- );
6995
+ );
6996
+ }
6933
6997
  }
6934
6998
  if (opts.prCommentOut) {
6999
+ if (embedPngPath) {
7000
+ g.env.FRONTGUARD_EMBED_CHECKS_PNG_PATH = embedPngPath;
7001
+ }
6935
7002
  const snippet = formatBitbucketPrSnippet(report);
7003
+ delete g.env.FRONTGUARD_EMBED_CHECKS_PNG_PATH;
6936
7004
  const abs = path5.isAbsolute(opts.prCommentOut) ? opts.prCommentOut : path5.join(opts.cwd, opts.prCommentOut);
6937
7005
  await fs.writeFile(abs, snippet, "utf8");
6938
7006
  g.stderr.write(
@@ -6940,7 +7008,7 @@ FrontGuard: wrote checks snapshot HTML to ${snapPath} (screenshot this file for
6940
7008
  FrontGuard: wrote Bitbucket PR comment text to ${abs} (${snippet.length} bytes).
6941
7009
  Use ONLY this file in your POST \u2026/pullrequests/{id}/comments payload (content.raw).
6942
7010
  Do not post frontguard-report.md or captured stdout \u2014 that is the long markdown log.
6943
- Optional: set FRONTGUARD_CHECKS_IMAGE_URL so the comment includes a checks summary image.
7011
+ With --checksPngOut, the PNG is inlined (small files only) or set FRONTGUARD_CHECKS_IMAGE_URL.
6944
7012
 
6945
7013
  `
6946
7014
  );
@@ -6995,7 +7063,11 @@ var run = defineCommand({
6995
7063
  },
6996
7064
  checksSnapshotOut: {
6997
7065
  type: "string",
6998
- description: "Write HTML with only the Checks table (screenshot \u2192 PNG \u2192 FRONTGUARD_CHECKS_IMAGE_URL in PR comment)"
7066
+ description: "Write HTML with only the Checks table (for screenshots / PR comments)"
7067
+ },
7068
+ checksPngOut: {
7069
+ type: "string",
7070
+ description: "Write PNG of the checks table via Playwright (npx). Pair with --checksSnapshotOut or use alone"
6999
7071
  },
7000
7072
  prCommentOut: {
7001
7073
  type: "string",
@@ -7010,6 +7082,7 @@ var run = defineCommand({
7010
7082
  append: typeof args.append === "string" ? args.append : null,
7011
7083
  htmlOut: typeof args.htmlOut === "string" ? args.htmlOut : null,
7012
7084
  checksSnapshotOut: typeof args.checksSnapshotOut === "string" ? args.checksSnapshotOut : null,
7085
+ checksPngOut: typeof args.checksPngOut === "string" ? args.checksPngOut : null,
7013
7086
  prCommentOut: typeof args.prCommentOut === "string" ? args.prCommentOut : null
7014
7087
  });
7015
7088
  }