fix: surface playwright fallback remediation (#852)
Co-authored-by: Codex Agent <codex@hwlab.local>
This commit is contained in:
@@ -102,7 +102,9 @@
|
||||
|
||||
Cloud Web 的默认校验入口是 `bun run --cwd web/hwlab-cloud-web check`。该入口必须在 v0.2 CI 的 `hwlab-cloud-web` 镜像发布前执行,且保持秒级或低十秒级,不引入浏览器、Playwright、公网或真实 provider 依赖。
|
||||
|
||||
Browser/layout/live smoke 属于显式专项诊断,不进入默认 Cloud Web check。所有 repo-owned Playwright smoke、layout smoke 和专项截图脚本必须通过 `scripts/src/browser-launcher.mjs` 启动 Chromium;不得直接 `chromium.launch()` 绑定到偶然存在的 Playwright browser cache。launcher 的浏览器来源判定顺序是显式 env(`HWLAB_PLAYWRIGHT_CHROMIUM` / `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH` / `CHROMIUM_PATH`)、G14 系统 Chromium(优先 `/snap/bin/chromium`)、最后才是 Playwright cache。诊断入口是 `npm run web:browser:doctor`,输出必须包含 `playwrightPackageSource`、`browser.browserSource`、`browser.executablePath`、`browser.fallbackUsed`、`failureCode` 和 remediation;浏览器不可用、auth bootstrap 未完成和真实 layout 失败不得再统一表现成 `#command-input hidden` 超时。
|
||||
Browser/layout/live smoke 属于显式专项诊断,不进入默认 Cloud Web check。所有 repo-owned Playwright smoke、layout smoke 和专项截图脚本必须通过 `scripts/src/browser-launcher.mjs` 启动 Chromium;不得直接 `chromium.launch()` 绑定到偶然存在的 Playwright browser cache。launcher 的浏览器来源判定顺序是显式 env(`HWLAB_PLAYWRIGHT_CHROMIUM` / `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH` / `CHROMIUM_PATH`)、G14 系统 Chromium(优先 `/snap/bin/chromium`)、最后才是 Playwright cache。诊断入口是 `npm run web:browser:doctor`,输出必须包含 `playwrightPackageSource`、`platform`、`managedBrowser.cacheStatus`、`installCheck.status`、`browser.browserSource`、`browser.executablePath`、`browser.fallbackUsed`、`browser.candidatePaths`、`failureCode` 和 remediation;浏览器不可用、auth bootstrap 未完成和真实 layout 失败不得再统一表现成 `#command-input hidden` 超时。
|
||||
|
||||
临时 live DOM closeout probe 也必须使用 repo-owned 入口,不得在 `/tmp` 或 issue 验收脚本里手写 Playwright import/launch。标准入口是 `npm run web:dom-probe -- start --url http://74.48.78.17:19666/ --fresh-session`,它会从 workspace 解析依赖、复用统一 launcher、把 PID/stdout/stderr/result/screenshot 写入 `.state/web-live-dom-probe/`,并立即返回 `status` 短查询命令;`npm run web:dom-probe -- status <jobId>` 用于轮询结果。前端-only DOM/renderer/scroll 验收默认不提交 Code Agent;只有显式 `--message` 才经 UI 提交一条真实消息,并按默认策略尝试取消运行中的 turn。`npm run web:browser:guard` 是最小 grep guard,用于确认 `scripts/`、`tools/`、`web/` 下没有新增绕过 launcher 的直接 Chromium launch 调用,唯一允许文件是 `scripts/src/browser-launcher.mjs`。
|
||||
|
||||
Live smoke 登录前必须等待前端 auth bootstrap 结束(`body[data-auth-state]` 不再是 `checking`,且 login submit 已可见/可用)再填表;登录后必须断言 URL query 不含 `username` 或 `password`,防止原生 form submit 泄漏凭据并伪装成 layout 超时。
|
||||
|
||||
|
||||
@@ -153,6 +153,7 @@ test("workbench smoke CLI treats skipped browser evidence as blocked and refuses
|
||||
|
||||
test("repo-owned browser launcher classifies cache misses and exposes system Chromium fallback metadata", () => {
|
||||
assert.equal(classifyBrowserLaunchFailure(new Error("Executable doesn't exist at /root/.cache/ms-playwright/chromium_headless_shell-1217")), "browser-cache-missing");
|
||||
assert.equal(classifyBrowserLaunchFailure(new Error("ERROR: Playwright does not support chromium on ubuntu26.04-x64")), "browser-install-unsupported-platform");
|
||||
assert.equal(classifyBrowserLaunchFailure(new Error("spawn /missing/chromium ENOENT")), "browser-executable-unavailable");
|
||||
const plan = chromiumLaunchPlan({ HWLAB_PLAYWRIGHT_CHROMIUM: "/definitely/missing/chromium" });
|
||||
assert.ok(["system-chromium", "playwright-cache"].includes(plan.browserSource));
|
||||
@@ -161,6 +162,12 @@ test("repo-owned browser launcher classifies cache misses and exposes system Chr
|
||||
const managed = playwrightManagedBrowserPlan({ executablePath: () => "/definitely/missing/playwright-cache" });
|
||||
assert.equal(managed.cacheStatus, "missing");
|
||||
assert.equal(managed.exists, false);
|
||||
const fallbackRemediation = browserLaunchRemediation({ executablePath: "/snap/bin/chromium" }, {
|
||||
managedBrowser: managed,
|
||||
installCheck: { status: "unsupported" }
|
||||
});
|
||||
assert.ok(fallbackRemediation.some((line) => line.includes("Playwright-managed browser cache is missing")));
|
||||
assert.ok(fallbackRemediation.some((line) => line.includes("status=unsupported")));
|
||||
const platform = browserRuntimePlatform();
|
||||
assert.equal(platform.platform, process.platform);
|
||||
assert.equal(platform.arch, process.arch);
|
||||
|
||||
@@ -110,7 +110,7 @@ export async function browserDoctor() {
|
||||
managedBrowser,
|
||||
installCheck,
|
||||
browser: browser.hwlabLaunchPlan ?? chromiumLaunchPlan(),
|
||||
remediation: []
|
||||
remediation: browserLaunchRemediation(browser.hwlabLaunchPlan ?? chromiumLaunchPlan(), { managedBrowser, installCheck })
|
||||
};
|
||||
}
|
||||
|
||||
@@ -196,17 +196,31 @@ export function chromiumLaunchPlan(env = process.env) {
|
||||
|
||||
export function classifyBrowserLaunchFailure(error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
if (/does not support chromium on/iu.test(message)) return "browser-install-unsupported-platform";
|
||||
if (/Executable doesn't exist|download new browsers|playwright install/iu.test(message)) return "browser-cache-missing";
|
||||
if (/spawn|ENOENT|permission denied|EACCES/iu.test(message)) return "browser-executable-unavailable";
|
||||
return "browser-launch-failed";
|
||||
}
|
||||
|
||||
export function browserLaunchRemediation(plan = chromiumLaunchPlan()) {
|
||||
if (plan.executablePath) return [`using ${plan.executablePath}`];
|
||||
return [
|
||||
export function browserLaunchRemediation(plan = chromiumLaunchPlan(), context = {}) {
|
||||
const remediation = [];
|
||||
if (plan.executablePath) {
|
||||
remediation.push(`using ${plan.executablePath}`);
|
||||
if (context.managedBrowser?.cacheStatus === "missing") {
|
||||
remediation.push("Playwright-managed browser cache is missing; the repo launcher selected system Chromium fallback.");
|
||||
}
|
||||
if (context.installCheck?.status) {
|
||||
remediation.push(`Playwright install dry-run status=${context.installCheck.status}; keep using the repo launcher/system Chromium fallback when managed browser install is unavailable or unnecessary.`);
|
||||
}
|
||||
return remediation;
|
||||
}
|
||||
remediation.push(
|
||||
"Set HWLAB_PLAYWRIGHT_CHROMIUM=/snap/bin/chromium when system Chromium exists.",
|
||||
"Install Playwright browsers with npx playwright install when no system Chromium is available."
|
||||
];
|
||||
context.installCheck?.status === "unsupported"
|
||||
? "Playwright install is unsupported on this platform; install a system Chromium package or use a runner image that already contains Chromium."
|
||||
: "Install Playwright browsers with npx playwright install when no system Chromium is available."
|
||||
);
|
||||
return remediation;
|
||||
}
|
||||
|
||||
function normalizePlaywrightModule(module) {
|
||||
|
||||
Reference in New Issue
Block a user