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
30 changes: 29 additions & 1 deletion apps/server/src/browser-preview/browser-preview-manager.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ThreadId } from "@t3tools/contracts";
import { describe, expect, it } from "vitest";

import { BrowserPreviewManager } from "./browser-preview-manager.ts";
import { BrowserPreviewManager, playwright_chrome_candidates } from "./browser-preview-manager.ts";

const threadId = ThreadId.make("thread-browser-preview");

Expand Down Expand Up @@ -75,3 +75,31 @@ describe("BrowserPreviewManager", () => {
expect(tickets.map((ticket) => manager.resolve_ticket(ticket))).toEqual([null, null]);
});
});

describe("playwright_chrome_candidates", () => {
it("prefers the newest revision and ignores unrelated cache entries", async () => {
const fs = await import("node:fs/promises");
const os = await import("node:os");
const path = await import("node:path");
const cacheDir = await fs.mkdtemp(path.join(os.tmpdir(), "gits-playwright-cache-"));
try {
for (const entry of ["chromium-1208", "chromium-1228", "firefox-1489", ".links"]) {
await fs.mkdir(path.join(cacheDir, entry), { recursive: true });
}

const candidates = playwright_chrome_candidates(cacheDir);

expect(candidates[0]).toBe(path.join(cacheDir, "chromium-1228", "chrome-linux64", "chrome"));
expect(candidates).toContain(
path.join(cacheDir, "chromium-1208", "chrome-linux64", "chrome"),
);
expect(candidates.join(" ")).not.toContain("firefox");
} finally {
await fs.rm(cacheDir, { recursive: true, force: true });
}
});

it("returns nothing when the cache directory is absent", () => {
expect(playwright_chrome_candidates("/nonexistent/ms-playwright")).toEqual([]);
});
});
38 changes: 35 additions & 3 deletions apps/server/src/browser-preview/browser-preview-manager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @effect-diagnostics nodeBuiltinImport:off globalDate:off - external CLI boundary and expiring viewer tickets.
import { execFile } from "node:child_process";
import { existsSync } from "node:fs";
import { existsSync, readdirSync } from "node:fs";
import { join } from "node:path";
import { randomUUID } from "node:crypto";
import { promisify } from "node:util";

Expand Down Expand Up @@ -55,6 +56,36 @@ export interface BrowserPreviewManagerOptions {
readonly run?: BrowserPreviewRun;
}

// Playwright installs each Chromium build under its own `chromium-<revision>` directory and
// prunes old ones, so pinning revisions goes stale on the next `playwright install`.
const PLAYWRIGHT_CHROME_RELATIVE_PATHS = [
"chrome-linux64/chrome",
"chrome-linux/chrome",
"chrome-mac/Chromium.app/Contents/MacOS/Chromium",
] as const;

export function playwright_chrome_candidates(cache_dir: string): ReadonlyArray<string> {
let entries: ReadonlyArray<string>;
try {
entries = readdirSync(cache_dir);
} catch {
return [];
}

return (
entries
.flatMap((entry) => {
const revision = /^chromium(?:_headless_shell)?-(\d+)$/u.exec(entry)?.[1];
return revision === undefined ? [] : [{ entry, revision: Number(revision) }];
})
// Newest revision first: Playwright keeps older builds around until they are pruned.
.sort((left, right) => right.revision - left.revision)
.flatMap(({ entry }) =>
PLAYWRIGHT_CHROME_RELATIVE_PATHS.map((relative) => join(cache_dir, entry, relative)),
)
);
}

function resolve_browser_path(): string | undefined {
if (process.env.GSD_BROWSER_BROWSER_PATH) {
return process.env.GSD_BROWSER_BROWSER_PATH;
Expand All @@ -64,8 +95,9 @@ function resolve_browser_path(): string | undefined {
"/usr/bin/google-chrome",
"/usr/bin/google-chrome-stable",
"/usr/bin/chromium",
`${process.env.HOME ?? ""}/.cache/ms-playwright/chromium-1223/chrome-linux64/chrome`,
`${process.env.HOME ?? ""}/.cache/ms-playwright/chromium-1208/chrome-linux64/chrome`,
...(process.env.HOME
? playwright_chrome_candidates(join(process.env.HOME, ".cache", "ms-playwright"))
: []),
];
return candidates.find((candidate) => existsSync(candidate));
}
Expand Down
Loading