Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ filegroup(
srcs = [
"package.json",
"scripts/run-playwright-chromium-bazel.mjs",
"scripts/run-puppeteer-chromium-bazel.mjs",
],
)

Expand Down Expand Up @@ -86,12 +87,34 @@ js_test(
visibility = ["//visibility:public"],
)

js_test(
name = "puppeteer_chromium_smoke",
copy_data_to_bin = False,
data = [
":browser_smoke_srcs",
":node_modules",
],
entry_point = "scripts/run-puppeteer-chromium-bazel.mjs",
patch_node_fs = False,
tags = [
"chromium",
"gloriousflywheel-rbe-candidate",
"puppeteer",
"sveltekit",
"vite",
"web-browser-runtime-authority",
],
timeout = "moderate",
visibility = ["//visibility:public"],
)

exports_files(
[
"package-lock.json",
"package.json",
"pnpm-lock.yaml",
"scripts/run-playwright-chromium-bazel.mjs",
"scripts/run-puppeteer-chromium-bazel.mjs",
"scripts/run-vitest-bazel.mjs",
"vitest.bazel.config.ts",
],
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,12 @@ This repo still uses the npm/SvelteKit workflow for normal local development and

- `//:types_unit_tests` wraps Vitest through `vitest.bazel.config.ts` and runs the existing `src/lib/types.test.ts` slice.
- `//:playwright_chromium_smoke` launches Playwright against the pinned GloriousFlywheel Chromium runtime path. It is a browser-runtime smoke target, not the full hosted Playwright regression suite.
- `//:puppeteer_chromium_smoke` launches Puppeteer against the same pinned Chromium runtime path. It proves Puppeteer can consume browser runtime authority without lifecycle downloads.
- `package-lock.json` remains the npm dependency authority for the app. `pnpm-lock.yaml` is the generated `rules_js` lock consumed by Bazel.
- Bazel npm lifecycle hooks skip Playwright and Puppeteer browser downloads. Browser-backed RBE must use the pinned worker Chromium path rather than downloading browsers during proof actions.
- GloriousFlywheel proof runs should use the external GF REAPI proof harness against this public repo checkout.

Current boundary: this proves narrow public SvelteKit/Vite/Vitest and Playwright/Chromium target classes for remote execution evidence. It does not prove default repo-wide RBE, the full hosted Playwright suite, Puppeteer browser execution, the full SvelteKit build, or deployment.
Current boundary: this proves narrow public SvelteKit/Vite/Vitest, Playwright/Chromium, and Puppeteer/Chromium target classes for remote execution evidence. It does not prove default repo-wide RBE, the full hosted Playwright suite, the full SvelteKit build, or deployment.



Expand Down
132 changes: 132 additions & 0 deletions scripts/run-puppeteer-chromium-bazel.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { accessSync, constants, existsSync, mkdirSync, mkdtempSync, statSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import puppeteer from 'puppeteer';

const runtimeDir = mkdtempSync(join(tmpdir(), 'ghio-puppeteer-chromium-'));
ensureWritableEnvDir('HOME', join(runtimeDir, 'home'));
ensureWritableEnvDir('XDG_CONFIG_HOME', join(runtimeDir, 'xdg-config'));
ensureWritableEnvDir('XDG_CACHE_HOME', join(runtimeDir, 'xdg-cache'));

const chromiumExecutable = findChromiumExecutable();
if (!chromiumExecutable) {
console.error(
'No Chromium executable found. Set GF_RBE_CHROMIUM_EXECUTABLE, PUPPETEER_EXECUTABLE_PATH, or CHROME_BIN.',
);
process.exit(1);
}

const packageJson = JSON.parse(await readFile('package.json', 'utf8'));

let browser;
let page;
try {
browser = await puppeteer.launch({
executablePath: chromiumExecutable,
headless: 'new',
args: ['--disable-dev-shm-usage', '--no-sandbox'],
});
page = await browser.newPage();
await page.setViewport({ width: 1024, height: 640, deviceScaleFactor: 1 });
await page.setContent(renderSmokePage(packageJson.name), { waitUntil: 'load' });

const title = await page.$eval('main h1', (element) => element.textContent);
const target = await page.$eval('[data-rbe-target]', (element) => element.textContent);
if (title !== 'GloriousFlywheel Puppeteer browser RBE smoke') {
throw new Error(`unexpected smoke title: ${title}`);
}
if (target !== packageJson.name) {
throw new Error(`unexpected package marker: ${target}`);
}

console.log(`Puppeteer Chromium smoke passed with ${chromiumExecutable}`);
} catch (error) {
if (page) {
await printPageDiagnostics(page);
}
throw error;
} finally {
await browser?.close();
}

function renderSmokePage(packageName) {
return `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Puppeteer Browser RBE smoke</title>
</head>
<body>
<main>
<h1>GloriousFlywheel Puppeteer browser RBE smoke</h1>
<p data-rbe-target>${escapeHtml(packageName)}</p>
</main>
</body>
</html>`;
}

function findChromiumExecutable() {
const candidates = [
process.env.GF_RBE_CHROMIUM_EXECUTABLE,
process.env.PUPPETEER_EXECUTABLE_PATH,
process.env.CHROME_BIN,
'/bin/chromium',
'/usr/bin/chromium',
'/usr/bin/chromium-browser',
'/usr/bin/google-chrome',
'/usr/bin/google-chrome-stable',
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
'/Applications/Chromium.app/Contents/MacOS/Chromium',
].filter(Boolean);

for (const candidate of candidates) {
if (existsSync(candidate)) {
return candidate;
}
}

return '';
}

function ensureWritableEnvDir(name, fallback) {
const current = process.env[name];
if (current && isWritableDirectory(current)) {
return current;
}

mkdirSync(fallback, { recursive: true });
process.env[name] = fallback;
return fallback;
}

function isWritableDirectory(path) {
try {
if (!existsSync(path) || !statSync(path).isDirectory()) {
return false;
}
accessSync(path, constants.W_OK);
return true;
} catch {
return false;
}
}

function escapeHtml(value) {
return String(value)
.replaceAll('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&#39;');
}

async function printPageDiagnostics(page) {
const diagnostics = {
url: page.url(),
title: await page.title().catch(() => ''),
viewport: page.viewport(),
mainText: await page.$eval('main', (element) => element.textContent?.slice(0, 240)).catch(() => null),
};
console.error(`Puppeteer Chromium smoke diagnostics: ${JSON.stringify(diagnostics, null, 2)}`);
}
Loading