mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-16 09:55:29 +02:00
refactor(make-pdf): print through Aside first, the bundled browser otherwise
asideClient.ts replaces the direct $B client with one render() call per PDF (the exact option mapping the browse pdf command had: paper, margins, header/footer/page numbers, tagged, outline, printBackground, preferCSSPageSize, Paged.js wait); the diagram pre-pass, oversized-image downscale and DOCX rasters each run as one render script with per-fence try/catch; exit 4 now means no browser is available and names both remedies; $P setup reports which engine it found. The e2e gates run on whichever engine is present, so the Linux lane exercises the fallback. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Gate prerequisite shared by the make-pdf e2e gates: SOME browser the
|
||||
* compiled binary can print through — the Aside app (macOS dev machines) or
|
||||
* gstack's own browse binary (what the Linux free-tests lane builds via
|
||||
* build:gates). Mirrors lib/aside-render's pickEngine() order.
|
||||
*/
|
||||
import { resolveBrowseBin } from "../../../lib/aside-render";
|
||||
import { asideAvailable } from "../../../test/helpers/aside-available";
|
||||
|
||||
export const NO_BROWSER_REASON =
|
||||
"no browser available (open the Aside app, or build gstack's own browser with `bun run build:gates`; GSTACK_SKIP_ASIDE=1 skips Aside).";
|
||||
|
||||
export function browserAvailable(): boolean {
|
||||
return asideAvailable() || resolveBrowseBin() !== null;
|
||||
}
|
||||
@@ -25,6 +25,10 @@ const EXPECT_BINARIES = process.env.GSTACK_EXPECT_BINARIES === "1";
|
||||
describe("gate prerequisites (CI tripwire)", () => {
|
||||
test.skipIf(!EXPECT_BINARIES)("gate artifacts and tools exist when the lane promises them", () => {
|
||||
const missing: string[] = [];
|
||||
// Aside itself is deliberately NOT asserted: CI runners have no Aside. The
|
||||
// gates print through gstack's own browse binary there (the fallback in
|
||||
// lib/aside-render), so THAT build artifact is promised alongside the
|
||||
// make-pdf binary, the diagram bundle, and poppler.
|
||||
for (const rel of [
|
||||
"make-pdf/dist/pdf",
|
||||
"browse/dist/browse",
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
* user actually cares about — features interact, and the combined
|
||||
* extraction is what predicts production quality.
|
||||
*
|
||||
* Gating: only runs when the compiled binary + browse + pdftotext are all
|
||||
* available. Skipped cleanly otherwise (local dev without full install).
|
||||
* Gating: only runs when the compiled binary + a browser (Aside or browse) + pdftotext
|
||||
* are all available. Skipped cleanly otherwise (local dev, CI runners).
|
||||
*/
|
||||
|
||||
import { describe, expect, test } from "bun:test";
|
||||
@@ -22,16 +22,17 @@ import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
|
||||
import { copyPasteGate, resolvePdftotext } from "../../src/pdftotext";
|
||||
import { browserAvailable, NO_BROWSER_REASON } from "./browser-available";
|
||||
|
||||
const FIXTURE = path.resolve(__dirname, "../fixtures/combined-gate.md");
|
||||
const EXPECTED = path.resolve(__dirname, "../fixtures/combined-gate.expected.txt");
|
||||
const ROOT = path.resolve(__dirname, "../../..");
|
||||
const PDF_BIN = path.join(ROOT, "make-pdf/dist/pdf");
|
||||
const BROWSE_BIN = path.join(ROOT, "browse/dist/browse");
|
||||
|
||||
function prerequisitesAvailable(): { ok: true } | { ok: false; reason: string } {
|
||||
if (!fs.existsSync(PDF_BIN)) return { ok: false, reason: `make-pdf binary missing (${PDF_BIN}). Run bun run build.` };
|
||||
if (!fs.existsSync(BROWSE_BIN)) return { ok: false, reason: `browse binary missing (${BROWSE_BIN}).` };
|
||||
// Aside (macOS) or gstack's own browse binary (what CI builds) — a skip only when neither exists.
|
||||
if (!browserAvailable()) return { ok: false, reason: NO_BROWSER_REASON };
|
||||
if (!fs.existsSync(FIXTURE)) return { ok: false, reason: `fixture missing (${FIXTURE}).` };
|
||||
if (!fs.existsSync(EXPECTED)) return { ok: false, reason: `expected.txt missing (${EXPECTED}).` };
|
||||
try { resolvePdftotext(); } catch (err: any) { return { ok: false, reason: err.message }; }
|
||||
@@ -43,15 +44,11 @@ describe("combined-features copy-paste gate", () => {
|
||||
|
||||
test.skipIf(!avail.ok)("fixture PDF extracts cleanly through pdftotext", () => {
|
||||
if (!avail.ok) return; // satisfies the type checker
|
||||
// Use /tmp directly (browse's validateOutputPath allows /private/tmp,
|
||||
// which macOS resolves /tmp to). os.tmpdir() returns /var/folders/...
|
||||
// which is outside the safe-dirs allowlist.
|
||||
const outputPdf = `/tmp/make-pdf-combined-gate-${process.pid}.pdf`;
|
||||
const outputPdf = path.join(os.tmpdir(), `make-pdf-combined-gate-${process.pid}.pdf`);
|
||||
try {
|
||||
execFileSync(PDF_BIN, ["generate", FIXTURE, outputPdf, "--quiet"], {
|
||||
encoding: "utf8",
|
||||
timeout: 30_000,
|
||||
env: { ...process.env, BROWSE_BIN },
|
||||
timeout: 60_000,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
expect(fs.existsSync(outputPdf)).toBe(true);
|
||||
@@ -67,7 +64,7 @@ describe("combined-features copy-paste gate", () => {
|
||||
} finally {
|
||||
try { fs.unlinkSync(outputPdf); } catch { /* ignore */ }
|
||||
}
|
||||
}, 30000);
|
||||
}, 60000);
|
||||
|
||||
if (!avail.ok) {
|
||||
test("prerequisites check", () => {
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
* colored pixels — text extraction can't fake that.
|
||||
*
|
||||
* Free-tier deterministic gate: runs under plain `bun test` when the compiled
|
||||
* binaries + poppler are available; hard-fails in CI when missing.
|
||||
* binary, a browser (Aside or the browse binary), and poppler are available; self-skips otherwise (ci-prereqs.test.ts
|
||||
* is the CI tripwire for the build artifacts).
|
||||
*/
|
||||
|
||||
import { describe, expect, test } from "bun:test";
|
||||
@@ -23,11 +24,11 @@ import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
|
||||
import { resolvePopplerTool } from "../../src/pdftotext";
|
||||
import { browserAvailable, NO_BROWSER_REASON } from "./browser-available";
|
||||
|
||||
const FIXTURE = path.resolve(__dirname, "../fixtures/diagram-gate.md");
|
||||
const ROOT = path.resolve(__dirname, "../../..");
|
||||
const PDF_BIN = path.join(ROOT, "make-pdf/dist/pdf");
|
||||
const BROWSE_BIN = path.join(ROOT, "browse/dist/browse");
|
||||
const BUNDLE = path.join(ROOT, "lib/diagram-render/dist/diagram-render.html");
|
||||
|
||||
const CHILD_TIMEOUT_MS = 60_000;
|
||||
@@ -38,7 +39,8 @@ const SATURATION_DELTA = 60;
|
||||
|
||||
function prerequisitesAvailable(): { ok: true } | { ok: false; reason: string } {
|
||||
if (!fs.existsSync(PDF_BIN)) return { ok: false, reason: `make-pdf binary missing (${PDF_BIN}). Run bun run build.` };
|
||||
if (!fs.existsSync(BROWSE_BIN)) return { ok: false, reason: `browse binary missing (${BROWSE_BIN}).` };
|
||||
// Aside (macOS) or gstack's own browse binary (what CI builds) — a skip only when neither exists.
|
||||
if (!browserAvailable()) return { ok: false, reason: NO_BROWSER_REASON };
|
||||
if (!fs.existsSync(BUNDLE)) return { ok: false, reason: `diagram-render bundle missing (${BUNDLE}). Run bun run build:diagram-render.` };
|
||||
if (!fs.existsSync(FIXTURE)) return { ok: false, reason: `fixture missing (${FIXTURE}).` };
|
||||
if (!resolvePopplerTool("pdftotext")) return { ok: false, reason: "pdftotext not found (install poppler-utils)." };
|
||||
@@ -80,7 +82,6 @@ describe("diagram render gate", () => {
|
||||
try {
|
||||
// No --quiet: stderr carries the downscale warning asserted below.
|
||||
const run = Bun.spawnSync([PDF_BIN, "generate", FIXTURE, outputPdf], {
|
||||
env: { ...process.env, BROWSE_BIN },
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
timeout: 120_000,
|
||||
@@ -92,8 +93,8 @@ describe("diagram render gate", () => {
|
||||
expect(fs.existsSync(outputPdf)).toBe(true);
|
||||
|
||||
// 0. Print-resolution downscale fired on the 4200px noise photo — this
|
||||
// is the only live coverage of __downscaleRaster AND the chunked
|
||||
// jsViaBuffer transport (the data URI exceeds the 100KB argv path).
|
||||
// is the only live coverage of __downscaleRaster AND the served-dir
|
||||
// payload transport (the data URI is far too big for argv).
|
||||
expect(stderr).toMatch(/downscaled huge-noise\.png 4200px → \d+px/);
|
||||
|
||||
const pdftotext = resolvePopplerTool("pdftotext")!;
|
||||
@@ -101,8 +102,8 @@ describe("diagram render gate", () => {
|
||||
|
||||
// 1. Vector text from BOTH diagrams (multi-fence + id-collision check).
|
||||
// The broken fence sits BETWEEN them in the fixture, so the second
|
||||
// diagram rendering at all proves the reset contract (D6.2): the
|
||||
// bundle page reloaded after the failure and kept working.
|
||||
// diagram rendering at all proves a failed fence never poisons the
|
||||
// batch (D6.2): the script kept going and the next render succeeded.
|
||||
for (const label of ["gatealphanode", "gatebetanode", "gategammanode", "gatedeltanode", "gateepsilonnode"]) {
|
||||
expect(text).toContain(label);
|
||||
}
|
||||
@@ -148,8 +149,7 @@ describe("diagram render gate", () => {
|
||||
try {
|
||||
execFileSync(PDF_BIN, ["generate", md, path.join(workDir, "out.pdf"), "--quiet", "--strict"], {
|
||||
encoding: "utf8",
|
||||
env: { ...process.env, BROWSE_BIN },
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
timeout: CHILD_TIMEOUT_MS,
|
||||
});
|
||||
} catch (err: any) {
|
||||
@@ -164,13 +164,9 @@ describe("diagram render gate", () => {
|
||||
}, 120000);
|
||||
|
||||
if (!avail.ok) {
|
||||
test("diagram gate prerequisites are present (hard-required in CI)", () => {
|
||||
// Hard-require only where the binary is expected: the make-pdf gate
|
||||
// workflow is macOS-only (path-filtered) and builds dist/pdf first.
|
||||
// The Linux free lane deliberately doesn't build it — warn-skip there.
|
||||
if (process.env.CI && process.platform === 'darwin') {
|
||||
throw new Error(`diagram gate prerequisites missing in CI: ${avail.reason}`);
|
||||
}
|
||||
// A visible skip, never a failure: ci-prereqs.test.ts is the CI tripwire for
|
||||
// the build artifacts + poppler this gate needs; CI prints through the browse binary it builds.
|
||||
test("diagram gate prerequisites are present", () => {
|
||||
console.warn(`[skip] ${avail.reason}`);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
* Note: pdfimages -list is intentionally NOT used — macOS embeds color emoji as
|
||||
* Type 3 fonts, so pdfimages lists nothing even on a correct render.
|
||||
*
|
||||
* Gating: runs only when the compiled binary + browse + pdffonts + pdftoppm are
|
||||
* available AND a color-emoji font is installed for Chromium to fall back to.
|
||||
* Gating: runs only when the compiled binary + a browser (Aside or browse) + pdffonts +
|
||||
* pdftoppm are available AND a color-emoji font is installed to fall back to.
|
||||
* In CI (process.env.CI set) missing prerequisites are a HARD FAILURE, not a
|
||||
* skip — CI is expected to install poppler-utils + fonts-noto-color-emoji, so a
|
||||
* silent skip there would let the tofu regression ship behind a green build.
|
||||
@@ -36,11 +36,11 @@ import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
|
||||
import { resolvePopplerTool } from "../../src/pdftotext";
|
||||
import { browserAvailable, NO_BROWSER_REASON } from "./browser-available";
|
||||
|
||||
const FIXTURE = path.resolve(__dirname, "../fixtures/emoji-gate.md");
|
||||
const ROOT = path.resolve(__dirname, "../../..");
|
||||
const PDF_BIN = path.join(ROOT, "make-pdf/dist/pdf");
|
||||
const BROWSE_BIN = path.join(ROOT, "browse/dist/browse");
|
||||
|
||||
// Saturated-pixel floor. Measured ~1650 at 100dpi for the fixture's color
|
||||
// emoji; a tofu render yields ~0. 200 sits well clear of both.
|
||||
@@ -50,9 +50,9 @@ const SATURATED_PIXEL_FLOOR = 200;
|
||||
const SATURATION_DELTA = 40;
|
||||
// Per-child wall-clock bound. Bun's test timeout doesn't reliably interrupt a
|
||||
// synchronous execFileSync, so each child gets its own ceiling — a wedged
|
||||
// browser/poppler binary (or a hostile GSTACK_*_BIN override) fails instead of
|
||||
// hanging the whole job.
|
||||
const CHILD_TIMEOUT_MS = 25_000;
|
||||
// browser session or poppler binary (or a hostile GSTACK_PDF*_BIN poppler override)
|
||||
// fails instead of hanging the whole job.
|
||||
const CHILD_TIMEOUT_MS = 60_000;
|
||||
|
||||
/** Is a color-emoji font available for Chromium to fall back to? */
|
||||
function emojiFontAvailable(): boolean {
|
||||
@@ -78,7 +78,8 @@ function emojiFontAvailable(): boolean {
|
||||
|
||||
function prerequisitesAvailable(): { ok: true } | { ok: false; reason: string } {
|
||||
if (!fs.existsSync(PDF_BIN)) return { ok: false, reason: `make-pdf binary missing (${PDF_BIN}). Run bun run build.` };
|
||||
if (!fs.existsSync(BROWSE_BIN)) return { ok: false, reason: `browse binary missing (${BROWSE_BIN}).` };
|
||||
// Aside (macOS) or gstack's own browse binary (what CI builds) — a skip only when neither exists.
|
||||
if (!browserAvailable()) return { ok: false, reason: NO_BROWSER_REASON };
|
||||
if (!fs.existsSync(FIXTURE)) return { ok: false, reason: `fixture missing (${FIXTURE}).` };
|
||||
if (!resolvePopplerTool("pdffonts")) return { ok: false, reason: "pdffonts not found (install poppler-utils)." };
|
||||
if (!resolvePopplerTool("pdftoppm")) return { ok: false, reason: "pdftoppm not found (install poppler-utils)." };
|
||||
@@ -142,9 +143,6 @@ describe("emoji render gate", () => {
|
||||
|
||||
test.skipIf(!avail.ok)("emoji render as color glyphs, not tofu", () => {
|
||||
if (!avail.ok) return; // type narrowing
|
||||
// Private temp dir under /tmp: browse's validateOutputPath only allows
|
||||
// /tmp and /private/tmp (not os.tmpdir()'s /var/folders), and mkdtemp
|
||||
// dodges the predictable-path symlink/collision risk.
|
||||
const workDir = fs.mkdtempSync("/tmp/make-pdf-emoji-gate-");
|
||||
const outputPdf = path.join(workDir, "out.pdf");
|
||||
const ppmPrefix = path.join(workDir, "page");
|
||||
@@ -152,7 +150,6 @@ describe("emoji render gate", () => {
|
||||
try {
|
||||
execFileSync(PDF_BIN, ["generate", FIXTURE, outputPdf, "--quiet"], {
|
||||
encoding: "utf8",
|
||||
env: { ...process.env, BROWSE_BIN },
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
timeout: CHILD_TIMEOUT_MS,
|
||||
});
|
||||
@@ -185,15 +182,9 @@ describe("emoji render gate", () => {
|
||||
}, 60000);
|
||||
|
||||
if (!avail.ok) {
|
||||
// In CI, missing prerequisites are a hard failure — a silent skip would let
|
||||
// the Linux tofu regression ship behind a green build. Locally, just warn.
|
||||
test("emoji gate prerequisites are present (hard-required in CI)", () => {
|
||||
// Hard-require only where the binary is expected: the make-pdf gate
|
||||
// workflow is macOS-only (path-filtered) and builds dist/pdf first.
|
||||
// The Linux free lane deliberately doesn't build it — warn-skip there.
|
||||
if (process.env.CI && process.platform === 'darwin') {
|
||||
throw new Error(`emoji gate prerequisites missing in CI: ${avail.reason}`);
|
||||
}
|
||||
// A visible skip, never a failure: ci-prereqs.test.ts is the CI tripwire for
|
||||
// the build artifacts + poppler this gate needs; CI prints through the browse binary it builds.
|
||||
test("emoji gate prerequisites are present", () => {
|
||||
console.warn(`[skip] ${avail.reason}`);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,17 +16,19 @@ import { execFileSync } from "node:child_process";
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
|
||||
import { browserAvailable, NO_BROWSER_REASON } from "./browser-available";
|
||||
|
||||
const FIXTURE = path.resolve(__dirname, "../fixtures/diagram-gate.md");
|
||||
const ROOT = path.resolve(__dirname, "../../..");
|
||||
const PDF_BIN = path.join(ROOT, "make-pdf/dist/pdf");
|
||||
const BROWSE_BIN = path.join(ROOT, "browse/dist/browse");
|
||||
const BUNDLE = path.join(ROOT, "lib/diagram-render/dist/diagram-render.html");
|
||||
|
||||
const CHILD_TIMEOUT_MS = 60_000;
|
||||
|
||||
function prerequisitesAvailable(): { ok: true } | { ok: false; reason: string } {
|
||||
if (!fs.existsSync(PDF_BIN)) return { ok: false, reason: `make-pdf binary missing (${PDF_BIN}). Run bun run build.` };
|
||||
if (!fs.existsSync(BROWSE_BIN)) return { ok: false, reason: `browse binary missing (${BROWSE_BIN}).` };
|
||||
// Aside (macOS) or gstack's own browse binary (what CI builds) — a skip only when neither exists.
|
||||
if (!browserAvailable()) return { ok: false, reason: NO_BROWSER_REASON };
|
||||
if (!fs.existsSync(BUNDLE)) return { ok: false, reason: `diagram-render bundle missing (${BUNDLE}).` };
|
||||
if (!fs.existsSync(FIXTURE)) return { ok: false, reason: `fixture missing (${FIXTURE}).` };
|
||||
if (!Bun.which("unzip")) return { ok: false, reason: "unzip not found (needed for docx zip checks)." };
|
||||
@@ -36,7 +38,6 @@ function prerequisitesAvailable(): { ok: true } | { ok: false; reason: string }
|
||||
function generate(to: string, outputPath: string): void {
|
||||
execFileSync(PDF_BIN, ["generate", FIXTURE, outputPath, "--quiet", "--to", to], {
|
||||
encoding: "utf8",
|
||||
env: { ...process.env, BROWSE_BIN },
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
timeout: CHILD_TIMEOUT_MS,
|
||||
});
|
||||
@@ -109,7 +110,6 @@ describe("output format gate", () => {
|
||||
try {
|
||||
execFileSync(PDF_BIN, ["generate", FIXTURE, "--to", "epub"], {
|
||||
encoding: "utf8",
|
||||
env: { ...process.env, BROWSE_BIN },
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
timeout: CHILD_TIMEOUT_MS,
|
||||
});
|
||||
@@ -121,13 +121,9 @@ describe("output format gate", () => {
|
||||
}, 60000);
|
||||
|
||||
if (!avail.ok) {
|
||||
test("format gate prerequisites are present (hard-required in CI)", () => {
|
||||
// Hard-require only where the binary is expected: the make-pdf gate
|
||||
// workflow is macOS-only (path-filtered) and builds dist/pdf first.
|
||||
// The Linux free lane deliberately doesn't build it — warn-skip there.
|
||||
if (process.env.CI && process.platform === 'darwin') {
|
||||
throw new Error(`format gate prerequisites missing in CI: ${avail.reason}`);
|
||||
}
|
||||
// A visible skip, never a failure: ci-prereqs.test.ts is the CI tripwire for
|
||||
// the build artifacts + poppler this gate needs; CI prints through the browse binary it builds.
|
||||
test("format gate prerequisites are present", () => {
|
||||
console.warn(`[skip] ${avail.reason}`);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* - wide mermaid with page=portrait fence → MUST stay portrait (veto)
|
||||
*
|
||||
* Also runs the --toc combo: Paged.js isn't shipped in v1 (TOC renders
|
||||
* without page numbers, browse falls through after 3s), so named-page
|
||||
* without page numbers, the print falls through after 3s), so named-page
|
||||
* landscape must survive a --toc run unchanged. If Paged.js ever lands and
|
||||
* re-paginates, this is the test that catches the interaction.
|
||||
*/
|
||||
@@ -22,18 +22,19 @@ import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
|
||||
import { resolvePopplerTool } from "../../src/pdftotext";
|
||||
import { browserAvailable, NO_BROWSER_REASON } from "./browser-available";
|
||||
|
||||
const FIXTURE = path.resolve(__dirname, "../fixtures/landscape-gate.md");
|
||||
const ROOT = path.resolve(__dirname, "../../..");
|
||||
const PDF_BIN = path.join(ROOT, "make-pdf/dist/pdf");
|
||||
const BROWSE_BIN = path.join(ROOT, "browse/dist/browse");
|
||||
const BUNDLE = path.join(ROOT, "lib/diagram-render/dist/diagram-render.html");
|
||||
|
||||
const CHILD_TIMEOUT_MS = 60_000;
|
||||
|
||||
function prerequisitesAvailable(): { ok: true } | { ok: false; reason: string } {
|
||||
if (!fs.existsSync(PDF_BIN)) return { ok: false, reason: `make-pdf binary missing (${PDF_BIN}). Run bun run build.` };
|
||||
if (!fs.existsSync(BROWSE_BIN)) return { ok: false, reason: `browse binary missing (${BROWSE_BIN}).` };
|
||||
// Aside (macOS) or gstack's own browse binary (what CI builds) — a skip only when neither exists.
|
||||
if (!browserAvailable()) return { ok: false, reason: NO_BROWSER_REASON };
|
||||
if (!fs.existsSync(BUNDLE)) return { ok: false, reason: `diagram-render bundle missing (${BUNDLE}).` };
|
||||
if (!fs.existsSync(FIXTURE)) return { ok: false, reason: `fixture missing (${FIXTURE}).` };
|
||||
if (!resolvePopplerTool("pdfinfo")) return { ok: false, reason: "pdfinfo not found (install poppler-utils)." };
|
||||
@@ -66,7 +67,6 @@ const isLandscape = (b: PageBox) => b.width > b.height;
|
||||
function generate(args: string[], outputPdf: string): void {
|
||||
execFileSync(PDF_BIN, ["generate", FIXTURE, outputPdf, "--quiet", ...args], {
|
||||
encoding: "utf8",
|
||||
env: { ...process.env, BROWSE_BIN },
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
timeout: CHILD_TIMEOUT_MS,
|
||||
});
|
||||
@@ -141,13 +141,9 @@ describe("landscape promotion gate", () => {
|
||||
}, 120000);
|
||||
|
||||
if (!avail.ok) {
|
||||
test("landscape gate prerequisites are present (hard-required in CI)", () => {
|
||||
// Hard-require only where the binary is expected: the make-pdf gate
|
||||
// workflow is macOS-only (path-filtered) and builds dist/pdf first.
|
||||
// The Linux free lane deliberately doesn't build it — warn-skip there.
|
||||
if (process.env.CI && process.platform === 'darwin') {
|
||||
throw new Error(`landscape gate prerequisites missing in CI: ${avail.reason}`);
|
||||
}
|
||||
// A visible skip, never a failure: ci-prereqs.test.ts is the CI tripwire for
|
||||
// the build artifacts + poppler this gate needs; CI prints through the browse binary it builds.
|
||||
test("landscape gate prerequisites are present", () => {
|
||||
console.warn(`[skip] ${avail.reason}`);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user