@hominis/fireforge 0.19.4 → 0.19.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -640,9 +640,7 @@ The two flags can be combined — `--with-tests --xpcshell` writes both harnesse
640
640
 
641
641
  ### Mochitest stalls and `--marionette-port`
642
642
 
643
- When you pass `--marionette-port <n>`, FireForge uses that port for the stale-listener probe and for `--doctor`, and it forwards `--setpref=marionette.port=<n>` to `mach test` so the harness binds the same port. The only exception is an explicit `--mach-arg --flavor=xpcshell` (or `--flavor=xpcshell-tests`): that harness ignores the pref, so FireForge skips auto-forward and logs a short notice instead. Toolkit widget mochitests under `toolkit/content/tests/` (for example `test_*.html` next to `browser_*.js` suites) therefore stay aligned with the probe without duplicating `--mach-arg=--setpref=marionette.port=…`.
644
-
645
- Some forks see mochitests end with **`TEST_END: TIMEOUT`** after on the order of **370 seconds** with **no harness output** — including runs where mozinfo reports `headless: false`, so the failure is not explained by SWGL headless alone. When tests wait on custom chrome (for example a fork tile shell that never sets a `_readyForTesting` gate), the hang is **engine-side**; use your fork’s test docs and `browser.toml` defaults (for Hominis-style trees, see the fork’s `AGENT_RULES.md` for `hominis.testing.*` prefs). Operationally: ensure Marionette port **2828** is free, pass **`--marionette-port`** when you use a non-default port (FireForge keeps preflight and mach consistent as above), and narrow the failure with `--doctor` or by splitting a lighter smoke test that does not depend on full chrome init.
643
+ When you pass `--marionette-port <n>`, FireForge uses that port for the **stale-listener probe** (before `mach test` runs) and for **`--doctor`**, forwards **`--setpref=marionette.port=<n>`** to `mach test` so the **browser Marionette listener** binds that port, and forwards **`--marionette=127.0.0.1:<n>`** so the **mochitest harness client** connects to the same endpoint (the client otherwise defaults to `127.0.0.1:2828`). If you already pass **`--mach-arg=--marionette=...`**, FireForge does not add a second client flag. If **`--mach-arg`** already forwards the port via **`--marionette-port=...`** or **`--setpref=marionette.port=...`**, FireForge skips duplicating the setpref (existing behaviour). The exception is an explicit **`--mach-arg --flavor=xpcshell`** (or **`--flavor=xpcshell-tests`**): that harness does not use the browser Marionette path, so FireForge skips both auto-forwarded flags and logs a short notice instead. Toolkit widget mochitests under `toolkit/content/tests/` (for example `test_*.html` next to `browser_*.js` suites) therefore stay aligned with the probe without duplicating **`--mach-arg=--setpref=marionette.port=…`**.
646
644
 
647
645
  ### Stale-build preflight on `fireforge test`
648
646
 
@@ -3,7 +3,7 @@ import { join } from 'node:path';
3
3
  import { prepareBuildEnvironment } from '../core/build-prepare.js';
4
4
  import { getProjectPaths, loadConfig } from '../core/config.js';
5
5
  import { buildArtifactMismatchMessage, buildUI, hasBuildArtifacts, hasRunnableBundle, testWithOutput, } from '../core/mach.js';
6
- import { assertMarionettePortAvailable, extractForwardedMarionettePort, shouldAutoForwardMarionettePortToMach, } from '../core/marionette-port.js';
6
+ import { assertMarionettePortAvailable, extractForwardedMarionettePort, forwardedMachArgsIncludeMarionetteClient, shouldAutoForwardMarionettePortToMach, } from '../core/marionette-port.js';
7
7
  import { formatMarionettePreflightLine, reportMarionettePreflight, runMarionettePreflight, } from '../core/marionette-preflight.js';
8
8
  import { checkStaleBuildForTest, formatStaleBuildWarning } from '../core/test-stale-check.js';
9
9
  import { operatorAlreadySetAppPath, resolveXpcshellAppdirArg, } from '../core/xpcshell-appdir.js';
@@ -320,17 +320,21 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
320
320
  extraArgs.push(...options.machArg);
321
321
  }
322
322
  // Auto-forward the Marionette port to mach when `--marionette-port` is
323
- // set. We use `--setpref=marionette.port=<n>` because the marionette
324
- // listener reads that pref before binding (browser-chrome / mochitest
325
- // path); xpcshell never reads it, so the pref is a no-op there.
323
+ // set. `--setpref=marionette.port=<n>` configures where the browser
324
+ // listener binds; `--marionette=127.0.0.1:<n>` tells the mochitest harness
325
+ // client to connect there (default client is 127.0.0.1:2828). xpcshell
326
+ // ignores both for browser Marionette.
326
327
  //
327
- // Skip forwarding when the operator already supplied an equivalent arg
328
- // via `--mach-arg` — duplicates would be confusing without changing
328
+ // Skip setpref forwarding when the operator already supplied an equivalent
329
+ // arg via `--mach-arg` — duplicates would be confusing without changing
329
330
  // semantics. Skip when mach args explicitly request `--flavor=xpcshell`
330
331
  // (or `xpcshell-tests`): the preflight still honours `--marionette-port`,
331
332
  // but mach does not use the marionette.port pref on that harness. Any
332
333
  // other arg shape still forwards so toolkit widget paths and mixed suites
333
334
  // stay aligned with the probe without duplicate `--mach-arg` flags.
335
+ //
336
+ // Skip auto `--marionette=...` when `--mach-arg` already includes a client
337
+ // `--marionette=...` (or two-token `--marionette host:port`).
334
338
  if (options.marionettePort !== undefined) {
335
339
  const operatorAlreadyForwarded = forwardedPort !== undefined;
336
340
  const machArgs = options.machArg ?? [];
@@ -341,7 +345,11 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
341
345
  extraArgs.push(`--setpref=marionette.port=${options.marionettePort}`);
342
346
  }
343
347
  else {
344
- info(`--marionette-port=${options.marionettePort} applied to the preflight probe, but --flavor=xpcshell is set — mach is not auto-configured with --setpref=marionette.port (xpcshell ignores that pref). Pass --mach-arg --setpref=marionette.port=${options.marionettePort} explicitly if you still need mach to see the port.`);
348
+ info(`--marionette-port=${options.marionettePort} applied to the preflight probe, but --flavor=xpcshell is set — mach is not auto-configured with --setpref=marionette.port or --marionette (xpcshell ignores the browser Marionette path). Pass --mach-arg --setpref=marionette.port=${options.marionettePort} explicitly if you still need mach to see the port.`);
349
+ }
350
+ if (shouldAutoForwardMarionettePortToMach(machArgs) &&
351
+ !forwardedMachArgsIncludeMarionetteClient(machArgs)) {
352
+ extraArgs.push(`--marionette=127.0.0.1:${options.marionettePort}`);
345
353
  }
346
354
  }
347
355
  // xpcshell appdir auto-injection — see src/core/xpcshell-appdir.ts for the
@@ -409,7 +417,7 @@ export function registerTest(program, { getProjectRoot, withErrorHandling }) {
409
417
  acc.push(value);
410
418
  return acc;
411
419
  }, [])
412
- .option('--marionette-port <port>', 'Override the Marionette control port (default 2828) for the stale-browser probe, the --doctor preflight, and (unless --mach-arg includes --flavor=xpcshell) the auto-forwarded --setpref=marionette.port=<n> passed to mach. Use this when a stale process holds 2828 or a CI runner reserves a different port.', (raw) => {
420
+ .option('--marionette-port <port>', 'Override the Marionette control port (default 2828) for the stale-browser probe, the --doctor preflight, and (unless --mach-arg includes --flavor=xpcshell) auto-forwarded mach args: --setpref=marionette.port=<n> (browser listener) and --marionette=127.0.0.1:<n> (mochitest client). Omits the client flag when --mach-arg already sets --marionette. Use when 2828 is busy or CI assigns another port.', (raw) => {
413
421
  const n = Number.parseInt(raw, 10);
414
422
  if (!Number.isFinite(n) || n < 1 || n > 65535) {
415
423
  throw new GeneralError(`--marionette-port must be an integer in 1..65535 (got "${raw}")`);
@@ -64,6 +64,15 @@ export declare function assertMarionettePortAvailable(port?: number, options?: {
64
64
  * `undefined`.
65
65
  */
66
66
  export declare function extractForwardedMarionettePort(machArgs: string[]): number | undefined;
67
+ /**
68
+ * True when forwarded mach args already set a Marionette **client** address
69
+ * for mach/mochitest (`--marionette=host:port` or `--marionette host:port`).
70
+ * Used only to avoid duplicating FireForge's auto-injected
71
+ * `--marionette=127.0.0.1:<n>`; this is not a full URL validator (IPv6, etc.).
72
+ *
73
+ * Deliberately does **not** treat `--marionette-port` as a client endpoint.
74
+ */
75
+ export declare function forwardedMachArgsIncludeMarionetteClient(machArgs: string[]): boolean;
67
76
  /**
68
77
  * True when forwarded mach args explicitly select the xpcshell harness.
69
78
  * Used so `--marionette-port` auto-forward skips `--setpref=marionette.port`
@@ -71,11 +80,12 @@ export declare function extractForwardedMarionettePort(machArgs: string[]): numb
71
80
  */
72
81
  export declare function hasExplicitXpcshellFlavor(machArgs: string[]): boolean;
73
82
  /**
74
- * Whether `fireforge test` should append `--setpref=marionette.port=<n>` when
75
- * the operator passed `--marionette-port`. Forwards for every harness except
76
- * an explicit `--flavor=xpcshell` / `xpcshell-tests` (toolkit widget mochitests
77
- * under `toolkit/content/tests/` do not match the older path-only heuristic
78
- * but still launch a Marionette-driven browser).
83
+ * Whether `fireforge test` should append `--setpref=marionette.port=<n>` and
84
+ * `--marionette=127.0.0.1:<n>` when the operator passed `--marionette-port`.
85
+ * Forwards for every harness except an explicit `--flavor=xpcshell` /
86
+ * `xpcshell-tests` (toolkit widget mochitests under `toolkit/content/tests/`
87
+ * do not match the older path-only heuristic but still launch a
88
+ * Marionette-driven browser).
79
89
  */
80
90
  export declare function shouldAutoForwardMarionettePortToMach(machArgs: string[]): boolean;
81
91
  /**
@@ -88,7 +98,7 @@ export declare function shouldAutoForwardMarionettePortToMach(machArgs: string[]
88
98
  * Note: `fireforge test` auto-forward of `--marionette-port` to mach uses
89
99
  * {@link shouldAutoForwardMarionettePortToMach} (mach-arg flavor gate) rather
90
100
  * than this function alone, so toolkit paths without `/mochitest/` still get
91
- * the pref when appropriate.
101
+ * the listener pref and harness `--marionette=127.0.0.1:<n>` when appropriate.
92
102
  *
93
103
  * @param testPaths - Engine-relative paths after `stripEnginePrefix`.
94
104
  * @param machArgs - Forwarded mach args (post-`--mach-arg`).
@@ -260,6 +260,31 @@ export function extractForwardedMarionettePort(machArgs) {
260
260
  }
261
261
  return undefined;
262
262
  }
263
+ /**
264
+ * True when forwarded mach args already set a Marionette **client** address
265
+ * for mach/mochitest (`--marionette=host:port` or `--marionette host:port`).
266
+ * Used only to avoid duplicating FireForge's auto-injected
267
+ * `--marionette=127.0.0.1:<n>`; this is not a full URL validator (IPv6, etc.).
268
+ *
269
+ * Deliberately does **not** treat `--marionette-port` as a client endpoint.
270
+ */
271
+ export function forwardedMachArgsIncludeMarionetteClient(machArgs) {
272
+ const valueLooksLikeHostPort = (token) => /:[0-9]+$/.test(token) || /^\[[^]]+\]:[0-9]+$/.test(token);
273
+ for (let i = 0; i < machArgs.length; i++) {
274
+ const arg = machArgs[i];
275
+ if (arg === undefined)
276
+ continue;
277
+ if (arg.startsWith('--marionette=') && !arg.startsWith('--marionette-port=')) {
278
+ return true;
279
+ }
280
+ if (arg === '--marionette') {
281
+ const next = machArgs[i + 1];
282
+ if (next !== undefined && valueLooksLikeHostPort(next))
283
+ return true;
284
+ }
285
+ }
286
+ return false;
287
+ }
263
288
  /**
264
289
  * True when forwarded mach args explicitly select the xpcshell harness.
265
290
  * Used so `--marionette-port` auto-forward skips `--setpref=marionette.port`
@@ -273,11 +298,12 @@ export function hasExplicitXpcshellFlavor(machArgs) {
273
298
  return false;
274
299
  }
275
300
  /**
276
- * Whether `fireforge test` should append `--setpref=marionette.port=<n>` when
277
- * the operator passed `--marionette-port`. Forwards for every harness except
278
- * an explicit `--flavor=xpcshell` / `xpcshell-tests` (toolkit widget mochitests
279
- * under `toolkit/content/tests/` do not match the older path-only heuristic
280
- * but still launch a Marionette-driven browser).
301
+ * Whether `fireforge test` should append `--setpref=marionette.port=<n>` and
302
+ * `--marionette=127.0.0.1:<n>` when the operator passed `--marionette-port`.
303
+ * Forwards for every harness except an explicit `--flavor=xpcshell` /
304
+ * `xpcshell-tests` (toolkit widget mochitests under `toolkit/content/tests/`
305
+ * do not match the older path-only heuristic but still launch a
306
+ * Marionette-driven browser).
281
307
  */
282
308
  export function shouldAutoForwardMarionettePortToMach(machArgs) {
283
309
  return !hasExplicitXpcshellFlavor(machArgs);
@@ -292,7 +318,7 @@ export function shouldAutoForwardMarionettePortToMach(machArgs) {
292
318
  * Note: `fireforge test` auto-forward of `--marionette-port` to mach uses
293
319
  * {@link shouldAutoForwardMarionettePortToMach} (mach-arg flavor gate) rather
294
320
  * than this function alone, so toolkit paths without `/mochitest/` still get
295
- * the pref when appropriate.
321
+ * the listener pref and harness `--marionette=127.0.0.1:<n>` when appropriate.
296
322
  *
297
323
  * @param testPaths - Engine-relative paths after `stripEnginePrefix`.
298
324
  * @param machArgs - Forwarded mach args (post-`--mach-arg`).
@@ -318,11 +318,12 @@ export interface TestOptions {
318
318
  machArg?: string[];
319
319
  /**
320
320
  * Override the Marionette control port (default 2828) used by the
321
- * stale-browser probe, the `--doctor` preflight, and the auto-forwarded
322
- * `--setpref=marionette.port=<n>` arg passed to mach (omitted when mach
323
- * args explicitly set `--flavor=xpcshell` / `xpcshell-tests`). Set this
324
- * when a stale process holds the default port and `kill` is not an option,
325
- * or when a CI runner reserves a different port for parallel test runs.
321
+ * stale-browser probe, the `--doctor` preflight, and (unless mach args set
322
+ * `--flavor=xpcshell` / `xpcshell-tests`) auto-forwarded mach flags:
323
+ * `--setpref=marionette.port=<n>` for the browser listener and
324
+ * `--marionette=127.0.0.1:<n>` for the mochitest harness client. Omits the
325
+ * client flag when `--mach-arg` already passes `--marionette`. Set when a
326
+ * stale process holds the default port or CI uses another port.
326
327
  */
327
328
  marionettePort?: number;
328
329
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hominis/fireforge",
3
- "version": "0.19.4",
3
+ "version": "0.19.5",
4
4
  "description": "FireForge — a build tool for customizing Firefox",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",