mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-12 16:08:59 +02:00
The in-place rebrand rewrote the Chrome-for-Testing bundle's Info.plist (global name replace — which also renamed CFBundleExecutable to a binary that doesn't exist) and overwrote its Resources/*.icns, breaking the codesign seal: GPU process exit_code=5, headed mode dead on macOS 26. The mutation lived in the SHARED Playwright cache, so it also poisoned the user's other Playwright projects. Three layers land together: (1) the rebrand block is gone — branding lives in the GStack Browser.app wrapper via GSTACK_CHROMIUM_PATH, with a tombstone and a static tripwire (no plist/icns writes into the bundle; the tripwire allows the read-only probe below); (2) a launch-time self-heal detects an already-poisoned cache bundle, removes it, and errors with the exact re-fetch command — covering deploy paths that never run migrations; (3) migration v1.64.0.0 sweeps every cached bundle, removes poisoned ones, and re-fetches clean Chromium immediately (migrations run after ./setup, so without the re-fetch an upgrade would end with zero working browser). Functionally verified against fixture caches: poisoned removed, clean untouched, rerun no-op. Migration filename tracks the final VERSION at ship. The #2242 watchdog half is the absorbed PR #2565 (thanks @Screddyice). Tombstone/tripwire ported from time-attack/gstack (GStack 2); self-heal and migration are ours. Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
54 lines
2.2 KiB
TypeScript
54 lines
2.2 KiB
TypeScript
/**
|
|
* #2242 bug 1 regression tripwire: never mutate the signed Chrome-for-Testing
|
|
* bundle.
|
|
*
|
|
* The old launchHeaded() "rebrand" ran a global
|
|
* `.replace(/Google Chrome for Testing/g, 'GStack Browser')` over the
|
|
* bundle's Info.plist — which renamed CFBundleExecutable to a binary that
|
|
* doesn't exist — and overwrote Resources/*.icns. Both writes broke the
|
|
* codesign seal: GPU process exit_code=5, headed mode dead on macOS 26
|
|
* (#2242, #2138, #2139).
|
|
*
|
|
* Static invariant (same pattern as cdp-session-cleanup.test.ts): the
|
|
* browser lifecycle code must contain NO write into the Chromium .app
|
|
* bundle. Branding lives in the wrapper .app / custom GBrowser build.
|
|
* These assertions fail on the pre-fix code.
|
|
*/
|
|
|
|
import { describe, test, expect } from 'bun:test';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
|
|
const SRC = fs.readFileSync(
|
|
path.join(import.meta.dir, '..', 'src', 'browser-manager.ts'),
|
|
'utf-8',
|
|
);
|
|
|
|
describe('#2242: signed Chromium bundle is never mutated', () => {
|
|
test('no global Google-Chrome-for-Testing plist replace', () => {
|
|
expect(SRC).not.toContain("replace(/Google Chrome for Testing/g");
|
|
});
|
|
|
|
test('no Info.plist write into the Chromium bundle (reads allowed: self-heal probe)', () => {
|
|
// The old code built `Info.plist` under the bundle's Contents dir and
|
|
// wrote it back. Any reappearance of a WRITE is a regression. The
|
|
// launch-time self-heal legitimately READS the plist to detect bundles
|
|
// the old code already poisoned (EV4), so the path construction itself
|
|
// is allowed — writes into it are not.
|
|
expect(SRC).not.toMatch(/writeFileSync\(\s*chromePlist/);
|
|
const plistWrites = SRC.match(/writeFileSync\([^)]*[Pp]list/g) || [];
|
|
expect(plistWrites).toEqual([]);
|
|
});
|
|
|
|
test('no icon overwrite into the Chromium bundle Resources dir', () => {
|
|
expect(SRC).not.toMatch(/copyFileSync\([^)]*destIcon/);
|
|
expect(SRC).not.toContain("CFBundleIconFile");
|
|
});
|
|
|
|
test('the tombstone comment documenting why stays put', () => {
|
|
// If someone deletes the explanation, the next contributor reintroduces
|
|
// the mutation in good faith. Keep the why next to the where.
|
|
expect(SRC).toContain('#2242');
|
|
});
|
|
});
|