@hominis/fireforge 0.15.6 → 0.15.7
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/CHANGELOG.md +46 -0
- package/README.md +68 -5
- package/dist/src/commands/build.js +60 -3
- package/dist/src/commands/furnace/chrome-doc-templates.d.ts +17 -0
- package/dist/src/commands/furnace/chrome-doc-templates.js +18 -0
- package/dist/src/commands/furnace/chrome-doc-tests.d.ts +23 -0
- package/dist/src/commands/furnace/chrome-doc-tests.js +120 -0
- package/dist/src/commands/furnace/chrome-doc.d.ts +11 -0
- package/dist/src/commands/furnace/chrome-doc.js +37 -4
- package/dist/src/commands/furnace/create-dry-run.d.ts +31 -0
- package/dist/src/commands/furnace/create-dry-run.js +95 -0
- package/dist/src/commands/furnace/create-templates.js +14 -0
- package/dist/src/commands/furnace/create.js +28 -24
- package/dist/src/commands/furnace/index.js +3 -1
- package/dist/src/commands/lint.d.ts +17 -2
- package/dist/src/commands/lint.js +25 -2
- package/dist/src/commands/register.d.ts +1 -1
- package/dist/src/commands/register.js +30 -7
- package/dist/src/commands/test.js +16 -1
- package/dist/src/core/build-audit-registration.d.ts +80 -0
- package/dist/src/core/build-audit-registration.js +187 -0
- package/dist/src/core/build-audit-transforms.d.ts +23 -0
- package/dist/src/core/build-audit-transforms.js +94 -0
- package/dist/src/core/build-audit.js +107 -7
- package/dist/src/core/furnace-validate-registration.d.ts +6 -4
- package/dist/src/core/furnace-validate-registration.js +66 -6
- package/dist/src/core/mach-build-artifacts.d.ts +44 -0
- package/dist/src/core/mach-build-artifacts.js +104 -3
- package/dist/src/core/mach.d.ts +1 -1
- package/dist/src/core/mach.js +1 -1
- package/dist/src/core/test-stale-check.d.ts +42 -0
- package/dist/src/core/test-stale-check.js +114 -0
- package/dist/src/types/commands/options.d.ts +16 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,52 @@
|
|
|
2
2
|
|
|
3
3
|
## 0.15.0
|
|
4
4
|
|
|
5
|
+
### Furnace create
|
|
6
|
+
|
|
7
|
+
- `furnace create` now accepts `--dry-run` so operators can preview the planned file set, test scaffold, and `furnace.json` mutation without touching the workspace. Previously the only way to preview a create was `fireforge furnace create --help` followed by running the real command and inspecting the result after the fact — a workflow that stranded a component directory, a furnace.json entry, and (with `--with-tests`) test sources under `engine/` behind every aborted preview. The dry-run path runs every validation the real command would run (name shape, name conflicts, engine pre-existence, `--compose` target existence + cycle detection, prefix warning) BEFORE emitting the plan, so a preview that fails matches a real run that would fail. A new `src/commands/furnace/create-dry-run.ts` owns the plan formatter and the success-note formatter so the two renderings stay in lock-step when the scaffolded layout changes.
|
|
8
|
+
|
|
9
|
+
### Furnace validate — `missing-token-link` auto-detection
|
|
10
|
+
|
|
11
|
+
- `missing-token-link` now auto-detects chrome host documents that mount a component without requiring the operator to configure `tokenHostDocuments`. The validator scans `browser/base/content/*.xhtml` for any document that references the component's tag name and adds those to the scan set alongside the configured (or default) host documents. Previously a fork that mounted `moz-mybrowser-canvas` from its own `mybrowser.xhtml` chrome document had to either configure `tokenHostDocuments` explicitly or ignore every `missing-token-link` warning for Hominis-hosted components; the warning false-fired on `browser.xhtml` (the upstream default) while the component's actual host document linked the tokens CSS correctly. The warning message now reads "none of the scanned chrome host documents" to reflect that the set can include both configured and auto-detected entries. Auto-detected documents that are already in `tokenHostDocuments` are deduplicated so they never render twice in the warning list.
|
|
12
|
+
|
|
13
|
+
### Build audit — known packaging transforms
|
|
14
|
+
|
|
15
|
+
- The post-build audit now applies known source→chrome packaging transforms after jar.mn registration lookup and before the similarity scorer. Motivating case: `engine/browser/base/content/hominis.js` packages to `chrome/browser/content/browser/hominis.js` under dist/, but an unrelated patch placed a `browser/defaults/preferences/hominis.js` pref file in the same build. The source's jar.mn entry has no `(source)` annotation — just a bare target line — so the registration-aware resolver could not anchor the match. The scorer then tied both candidates at score=10 (every intermediate segment of the source is in the "generic" list so no bonus applies), `resolveBestArtifact` picked whichever the directory walk hit first, and the structural-relation check rejected every candidate as unrelated. The audit reported "missing" on the correctly-packaged chrome resource every build. The new `src/core/build-audit-transforms.ts` maps well-known source prefixes (`browser/base/content/`, `toolkit/content/widgets/`, `toolkit/content/`) to their upstream chrome suffixes and picks the first dist candidate whose absolute path ends with the expected suffix — treating the match as high-confidence (bypasses the structural-relation check). Rules are intentionally narrow to the subtrees whose packaging target is stable across every fork we know about; a fork that reroutes a known subtree can still win via `(source)` annotations in its own jar.mn.
|
|
16
|
+
|
|
17
|
+
### Register
|
|
18
|
+
|
|
19
|
+
- `fireforge register <path>` now accepts both repo-root-relative paths (e.g. `engine/browser/base/content/foo.xhtml`) and engine-relative paths (e.g. `browser/base/content/foo.xhtml`). Previously passing the former — a form operators commonly produce by copying paths from `git status` or shell tab completion — failed with `Invalid Argument: File not found in engine: engine/browser/base/content/foo.xhtml`, which named a doubled path that gave no hint the fix was to drop the `engine/` prefix. A leading `engine/` segment is now stripped before the existence check and before the manifest writer runs; the `[dry-run] Would register`, `Already registered`, and `Registered` log lines all render the normalised engine-relative path so the operator sees exactly what the manifest writer received.
|
|
20
|
+
|
|
21
|
+
### Test — stale-build preflight
|
|
22
|
+
|
|
23
|
+
- `fireforge test` now runs a stale-build preflight when `--build` was NOT passed. The probe diffs engine HEAD (and the workdir) against the last-build baseline (`.fireforge/last-build.json`), filters to paths that imply packaging, and — when any match — emits a single up-front warning naming the changed files and pointing at `fireforge test --build`. Previously the mismatch only surfaced as a cryptic `NS_ERROR_FILE_NOT_FOUND` against a `chrome://browser/content/…` URI AFTER xpcshell or mach test launched; the motivating case was scaffolding a new top-level chrome document plus a BrowserGlue-style xpcshell test, where the test file existed, the manifests were registered, but `obj-*/dist/` still held the pre-edit bundle and chrome URIs resolved to nothing. The preflight is warn-only (never blocks) because a fork that rebuilt out-of-band — a direct `./mach build` invocation, an IDE plugin, a separate CI stage — can legitimately have a fresh `dist/` with no FireForge-recorded baseline update. Lives in the new `src/core/test-stale-check.ts`.
|
|
24
|
+
|
|
25
|
+
### Lint — `--only-introduced` exit-code scope
|
|
26
|
+
|
|
27
|
+
- New `fireforge lint --only-introduced` flag scopes the exit code to issues tagged `[introduced]` by `--since`. Cumulative pre-existing queue errors still print (the operator retains full visibility into queue state), but do not fail lint — so a branch whose own diff is clean passes CI even when the repo carries unrelated `raw-color` / license-header errors from older patches. Requires `--since`; without a revision there is no introduced-vs-cumulative distinction and the combination is rejected up-front with an actionable message rather than silently treating every error as cumulative. The failure message reports the count of cumulative errors suppressed by the flag (`N cumulative error(s) suppressed by --only-introduced`) so a branch that turned clean only by virtue of the flag still tells the operator what was hidden. Pre-flag behaviour — any error fails lint — is unchanged when the flag is absent.
|
|
28
|
+
|
|
29
|
+
### Furnace create — xpcshell chrome-URI documentation
|
|
30
|
+
|
|
31
|
+
- `furnace create --xpcshell` scaffolds a generated test that now carries an inline explanation of the xpcshell chrome-URI boundary: toolkit chrome (`chrome://global/*`) IS registered and resolvable from the harness, but browser chrome (`chrome://browser/*`) is NOT registered even when `firefox-appdir = "browser"` is set, and the manifest set xpcshell loads lags what the real browser loads. The comment points at `furnace create --test-style=browser-chrome` as the correct harness for tests that need browser chrome. The `--xpcshell` help text picks up the same note so operators reading `fireforge furnace create --help` see the constraint before they scaffold. Previously the scaffolded test and the help text only described xpcshell as "headless, no tabbrowser", which read as "it just won't render UI" — not "it can't fetch browser chrome URIs". Motivating case: `test_browserGlue_hominis_startup.js` trying to fetch `chrome://browser/content/hominis.xhtml` via `NetUtil.asyncFetch` and hitting `NS_ERROR_FILE_NOT_FOUND` despite the file being present under `obj-*/dist/`.
|
|
32
|
+
|
|
33
|
+
### Furnace chrome-doc — platform-module compatibility + packaging verification
|
|
34
|
+
|
|
35
|
+
- Every `furnace chrome-doc create`-scaffolded root element now carries a `data-furnace-chrome-doc="<name>"` sentinel attribute. Fork-side patches to upstream platform modules that observe `browser-delayed-startup-finished` and walk INTO the window assuming `browser.xhtml`'s DOM — `DevToolsStartup`, `PageActions`, `SessionStore`, `DownloadsButton`, and the growing set of modules that treat every `navigator:browser` window as a main browser window — can guard on this attribute cheaply via `document.documentElement.hasAttribute("data-furnace-chrome-doc")` to skip the walk on a custom chrome doc. The attribute name is fork-neutral so a fork upgrading across FireForge versions does not have to rewrite every guard; the name carried in the value distinguishes multiple chrome docs when a patch needs finer-grained routing. Motivating case: Hominis' `hominis.xhtml` launch fired `TypeError: can't access property "addEventListener", menu is null` from `DevToolsStartup.sys.mjs` and `TypeError: can't access property "placeAllActionsInUrlbar", bpa is undefined` from `PageActions.sys.mjs` on every window-open, because both modules assumed structure that only `browser.xhtml` provides. Both fire as non-fatal Browser Console noise today, but the set of walking modules grows each upstream release, and chasing them with per-element stubs (`<xul:keyset id="mainKeyset"/>`, `<menubar>` placeholders, …) is whack-a-mole compared to a single guard attribute. Exported as `FURNACE_CHROME_DOC_SENTINEL` so test code and external checks can reference the exact name without hardcoding the string.
|
|
36
|
+
- New `furnace chrome-doc create --with-tests` flag scaffolds an xpcshell packaging-verification test (`test_<name>_packaging.js` + `xpcshell.toml`) under `engine/browser/base/content/test/<binary>-xpcshell/<name>/`. The generated test probes the packaged app directory via `Services.dirsvc.get("XCurProcD", Ci.nsIFile)` and navigates to `<AppDir>/chrome/browser/content/browser/<name>.xhtml` (and the corresponding `skin/classic/browser/<name>-chrome.css`), asserting each file exists and is non-empty. Crucially the test does NOT go through `chrome://` URI resolution — the xpcshell harness's browser-chrome manifest set lags the real browser's even with `firefox-appdir = "browser"` set, so `NetUtil.asyncFetch` on a packaged chrome URI returns `NS_ERROR_FILE_NOT_FOUND` against a file that IS correctly packaged (the Hominis startup-verification failure mode). Direct filesystem probing sidesteps the chrome-registration gap entirely. The generated test also carries an inline comment flagging the omni.ja-packed-build limitation so an operator running a packed-tree configuration sees that the scaffold assumes an unpacked `mach build` layout before it fails. `XPCSHELL_TESTS_MANIFESTS` registration is left to the operator because the owning `moz.build` depends on the fork's layout. Lives in the new `src/commands/furnace/chrome-doc-tests.ts`. Writes go through the same rollback journal as the chrome-doc scaffolder itself, so a SIGINT mid-scaffold restores both the source files and the test scaffold.
|
|
37
|
+
- `furnace chrome-doc create`'s "Next steps" note now calls out the platform-module-compatibility sentinel explicitly and points at the README's "Platform module compatibility" section, plus the manual registration step when `--with-tests` is set, so operators see the relevant context before they run `fireforge build`.
|
|
38
|
+
|
|
39
|
+
### Build audit — registration-aware resolution
|
|
40
|
+
|
|
41
|
+
- `fireforge build`'s post-build audit now anchors artifact resolution to the `(source)` references in `jar.mn`. When a source file is claimed by a `jar.mn` entry, the audit walks the registration to compute the expected target path (e.g. `content/browser/mybrowser.js`) and probes the dist tree for a candidate whose absolute path ends with that suffix — rather than basename-similarity scoring, which could not distinguish a correctly-registered chrome resource from an unrelated same-basename file elsewhere in the tree. Motivating case: a fork that added `engine/browser/base/content/mybrowser.js` (registered in `browser/base/jar.mn`, packaged to `chrome/browser/content/browser/mybrowser.js`) alongside an unrelated `browser/defaults/preferences/mybrowser.js` pref from an earlier patch. The heuristic locked onto whichever the directory walk hit first, awarded both candidates an equal trailing-overlap score (basename only), and reported the chrome resource as "missing" in the `Packaged: ...` summary — even though packaging had landed it correctly. Registration-aware resolution in the new `src/core/build-audit-registration.ts` picks the correct artifact without consulting the scorer; the similarity heuristic only runs when no `jar.mn` registration is found (moz.build-registered sources such as `FINAL_TARGET_FILES` and `JS_PREFERENCE_FILES`).
|
|
42
|
+
- When a source IS registered in `jar.mn` but the registered target is absent from dist, the audit now reports "missing" with a warning that names the `jar.mn` entry (`is registered in engine/browser/base/jar.mn as "content/browser/foo.js (content/foo.js)" but no packaged artifact ending in "/content/browser/foo.js" was found under dist/`). This is distinguishable from an unregistered miss and tells the operator "registration is intact, packaging dropped the file" — not "check the jar.mn again".
|
|
43
|
+
- When the heuristic fallback downgrades to `missing` (unrelated same-basename hit, no structural relation to the source), the warning now enumerates EVERY same-basename candidate in dist/ up to five entries, then truncates with a `(+N more)` tail. Previously only the scorer's single pick was surfaced, which often misled operators by naming a file that had nothing to do with the source; the full set lets triage see the real artifact alongside the confounders at a glance.
|
|
44
|
+
|
|
45
|
+
### Build preflight — `--rewrite-mozinfo` for safe relocations
|
|
46
|
+
|
|
47
|
+
- New `fireforge build --rewrite-mozinfo` option handles the class of problem where a workspace was simply moved to a new path and `obj-*/mozinfo.json` still records the old `topsrcdir` / `topobjdir`. Without the flag, the stale-objdir preflight aborts with a "delete and rebuild" instruction; the full rebuild takes ~20 minutes and discards ~14 GB of otherwise-intact obj artefacts. With the flag, FireForge patches `mozinfo.json`'s recorded paths in place and runs `mach configure` so the recursive-make backend regenerates against the corrected paths — no obj-\* scrub, no fresh compile.
|
|
48
|
+
- The rewriter refuses any change that is not a pure prefix-move: mozinfo must record both `topsrcdir` and `topobjdir`, `topobjdir` must resolve to `<topsrcdir>/<objDir>` (out-of-tree builds are rejected), and the detected obj-\* directory name must match the one mozinfo recorded. On any refusal the command falls back to the original clean-rebuild guidance with the refusal reason appended (`mozinfo rewrite refused: …`), so an unsafe relocation is never silently misrepaired. An external mozconfig (one that lives outside the old topsrcdir — e.g. a shared `$HOME/configs/shared-mozconfig`) is left untouched by the rewriter; a relocated workspace that also moved its mozconfig still falls back to clean-rebuild.
|
|
49
|
+
- The `buildArtifactMismatchMessage` copy now points operators at the new flag: "If the workspace was simply moved (same tree, different prefix), "fireforge build --rewrite-mozinfo" will patch mozinfo.json paths in place and run mach configure instead of scrubbing the whole tree." A failed `mach configure` after a successful rewrite surfaces as a `BuildError` so the operator sees "rewrote mozinfo but configure failed" distinctly from "rewrite refused".
|
|
50
|
+
|
|
5
51
|
### Furnace registration
|
|
6
52
|
|
|
7
53
|
- `furnace apply` idempotency check is marker-comment-tolerant. Previously the single-line substring match (`content.includes('["tag",')`) missed multi-line entries, and the standalone-line regex anchored on `\s*$`, which did not allow trailing `// <marker>:` comments an operator may have appended to a previously-written entry. A duplicate tag was then inserted on every re-apply, and the second `setElementCreationCallback` invocation threw `NotSupportedError: Operation is not supported` at every window-load. The idempotency check now matches on tag-name column 0 (both single- and multi-line array shapes) and tolerates trailing `//` comments on the line.
|
package/README.md
CHANGED
|
@@ -246,8 +246,10 @@ Every destructive command defaults to an interactive confirmation with a change
|
|
|
246
246
|
# Wire a subscript with init/destroy lifecycle
|
|
247
247
|
fireforge wire my-widget --init "MyWidget.init()" --destroy "MyWidget.destroy()"
|
|
248
248
|
|
|
249
|
-
# Register a file in the correct build manifest
|
|
249
|
+
# Register a file in the correct build manifest (engine-relative or
|
|
250
|
+
# repo-root-relative — a leading `engine/` segment is stripped)
|
|
250
251
|
fireforge register browser/modules/mybrowser/MyStore.sys.mjs
|
|
252
|
+
fireforge register engine/browser/modules/mybrowser/MyStore.sys.mjs
|
|
251
253
|
|
|
252
254
|
# Both support --dry-run to preview changes
|
|
253
255
|
```
|
|
@@ -308,6 +310,7 @@ Custom elements live under `toolkit/content/widgets`, but a fork's top-level chr
|
|
|
308
310
|
```bash
|
|
309
311
|
fireforge furnace chrome-doc create mybrowser # full chrome (titlebar + windowtype)
|
|
310
312
|
fireforge furnace chrome-doc create overlay --no-titlebar # frameless overlay
|
|
313
|
+
fireforge furnace chrome-doc create mybrowser --with-tests # + xpcshell packaging-verification test
|
|
311
314
|
```
|
|
312
315
|
|
|
313
316
|
The command writes:
|
|
@@ -317,9 +320,37 @@ The command writes:
|
|
|
317
320
|
- `engine/browser/themes/shared/<name>-chrome.css` — scoped CSS; emits the macOS `.titlebar-button { display: none }` carve-out under `--no-titlebar`.
|
|
318
321
|
- `engine/browser/locales/en-US/browser/<name>.ftl` — Fluent stub keyed on `<name>-window-title`.
|
|
319
322
|
- Appends the corresponding `jar.mn` / `jar.inc.mn` / `locales/jar.mn` entries.
|
|
323
|
+
- When `--with-tests` is set, also scaffolds an xpcshell test + `xpcshell.toml` under `engine/browser/base/content/test/<binary>-xpcshell/<name>/` that probes the packaged app directory (`Services.dirsvc.get("XCurProcD")/chrome/browser/...`) directly rather than going through `chrome://` URI resolution — see "Platform module compatibility" and the xpcshell chrome-URI note further down for why direct filesystem probing is the reliable way to verify chrome-doc packaging. Registration in `XPCSHELL_TESTS_MANIFESTS` is left to the operator because the owning moz.build depends on the fork layout.
|
|
320
324
|
|
|
321
325
|
Writes are transactional: a SIGINT mid-scaffold rolls back every touched file. Requires an existing engine — run `fireforge download` first.
|
|
322
326
|
|
|
327
|
+
#### Platform module compatibility
|
|
328
|
+
|
|
329
|
+
A custom chrome document with `windowtype="navigator:browser"` is treated as a main browser window by every upstream platform module that observes `browser-delayed-startup-finished` — `DevToolsStartup`, `PageActions`, `SessionStore`, `DownloadsButton`, Sync UI, and more. Those modules walk INTO the window assuming `browser.xhtml`'s DOM (`<menu>` entries, `window.BrowserPageActions`, the `cmd_*` command set, the tabbrowser, …) and throw a `TypeError` on anything else. The errors are non-fatal but noisy, and the matrix of "which modules walk in" grows with every Firefox release.
|
|
330
|
+
|
|
331
|
+
Every `furnace chrome-doc create`-scaffolded root element now carries a `data-furnace-chrome-doc="<name>"` sentinel attribute. Fork-side patches to the offending platform modules can guard on this attribute cheaply:
|
|
332
|
+
|
|
333
|
+
```js
|
|
334
|
+
// DevToolsStartup.sys.mjs (fork patch)
|
|
335
|
+
observe(subject, topic) {
|
|
336
|
+
if (topic === "browser-delayed-startup-finished") {
|
|
337
|
+
const win = subject.QueryInterface(Ci.nsIDOMWindow);
|
|
338
|
+
if (win.document.documentElement.hasAttribute("data-furnace-chrome-doc")) {
|
|
339
|
+
return; // fork's custom chrome doc — skip DevTools menubar wiring
|
|
340
|
+
}
|
|
341
|
+
// ... upstream body ...
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
The sentinel is fork-neutral (the attribute name is stable across projects) so a fork upgrading from one FireForge version to the next does not have to rewrite every guard. The name carried in the attribute value distinguishes multiple chrome docs in the same fork when a patch needs finer-grained routing.
|
|
347
|
+
|
|
348
|
+
#### Harness matrix gap
|
|
349
|
+
|
|
350
|
+
`furnace chrome-doc create` generates a top-level chrome document, but neither of the two existing test harnesses (`furnace create --test-style=mochikit`, `--test-style=browser-chrome`) covers it well: mochikit targets widgets loaded via `chrome://global/` (no tabbrowser, but also no chrome-doc-level interactive behaviors like titlebar drag / focus-ring / window sizing), and browser-chrome mochitest requires a working `tabbrowser` which a fork-authored chrome document that replaces `browser.xhtml` deliberately does not carry. Running a tabbrowser-less window through the mochikit harness crashes the harness itself (on `URILoadingHelper.openLinkIn`), not the chrome doc.
|
|
351
|
+
|
|
352
|
+
The packaging-verification test that `--with-tests` scaffolds is what FireForge can offer cleanly from inside the current harness matrix: it asserts the packaged files landed, not that they behave correctly at runtime. Interactive assertions (dot-grid background painting, titlebar drag region, focus ring) are out of scope for this scaffold and require manual verification against a built browser until the upstream harness matrix catches up.
|
|
353
|
+
|
|
323
354
|
### Picking a test harness for `furnace create`
|
|
324
355
|
|
|
325
356
|
`furnace create --with-tests` defaults to a MochiKit test at `engine/toolkit/content/tests/widgets/test_<tag>.html`. MochiKit tests load the component module via `chrome://global/` and don't need a `tabbrowser`, so they run against any fork — including bespoke chrome documents (`mybrowser.xhtml`-class) that deliberately omit the upstream browser chrome.
|
|
@@ -334,6 +365,8 @@ Three styles are available via `--test-style`:
|
|
|
334
365
|
|
|
335
366
|
`--xpcshell` is preserved as an alias for `--test-style=xpcshell`; conflicting flag combinations (`--xpcshell --test-style=mochikit`) are rejected.
|
|
336
367
|
|
|
368
|
+
`furnace create --dry-run` previews the planned file set, test scaffold, and `furnace.json` mutation without writing anything. Every validation the real command runs (tag-name shape, name conflicts, engine pre-existence of the component, `--compose` target existence + cycle detection) fires BEFORE the plan is emitted, so a failed preview matches a failed real run.
|
|
369
|
+
|
|
337
370
|
## Additional Commands
|
|
338
371
|
|
|
339
372
|
The commands below cover project configuration, patch queue management, build packaging and development utilities. Run `fireforge <command> --help` for full option details.
|
|
@@ -392,17 +425,26 @@ fireforge lint --since main # everything since main
|
|
|
392
425
|
fireforge lint --since abc1234 # since a specific SHA
|
|
393
426
|
```
|
|
394
427
|
|
|
395
|
-
The summary line splits counts — e.g. `Lint: 2 introduced error(s), 0 introduced warning(s); 5 cumulative error(s), 1 cumulative warning(s)` — so triage of "did my diff introduce any of these?" is a one-glance check on a large patch series. Exit code still fails on any error (introduced or cumulative)
|
|
428
|
+
The summary line splits counts — e.g. `Lint: 2 introduced error(s), 0 introduced warning(s); 5 cumulative error(s), 1 cumulative warning(s)` — so triage of "did my diff introduce any of these?" is a one-glance check on a large patch series. Exit code still fails on any error (introduced or cumulative) unless `--only-introduced` is set; without `--since`, output is unchanged.
|
|
429
|
+
|
|
430
|
+
Pass `--only-introduced` together with `--since` to scope the exit code to issues the current diff introduced. Cumulative pre-existing errors still print, but do not fail lint — useful in CI when a branch's own diff is clean but the repo already carries unrelated errors from older patches:
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
fireforge lint --since main --only-introduced
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
The failure message reports how many cumulative errors were suppressed by the flag so a branch that passed only because of the flag still tells the operator what was hidden. Without `--since`, `--only-introduced` is rejected up-front — there is no introduced-vs-cumulative distinction to scope to.
|
|
396
437
|
|
|
397
438
|
### Post-build audit and auto-configure
|
|
398
439
|
|
|
399
440
|
`fireforge build` is a transactional step: after a successful mach build it audits the dist bundle against engine-relative paths touched since the last successful build, and warns per file that is packageable-by-convention (`.js`/`.mjs`/`.css`/`.ftl`/`.xhtml`/`app/profile/…`) but has no matching artifact or whose dist mtime is older than the source. Ends every build with a `Packaged: N updated, M stale, K missing, S skipped` summary. The audit is warn-only — it never fails a build that mach reported green.
|
|
400
441
|
|
|
401
|
-
The audit applies
|
|
442
|
+
The audit applies seven routing rules to suppress false positives that previously trained operators to ignore its warnings:
|
|
402
443
|
|
|
444
|
+
- **jar.mn registrations are authoritative.** When the source under audit is claimed by a `(source)` reference in an ancestor `jar.mn`, the audit walks the registration to compute the expected target path (e.g. `content/browser/mybrowser.js`) and probes the dist tree for a candidate whose absolute path ends with that suffix. Picking the correct artifact from a same-basename collision no longer depends on path-similarity scoring. If the registration target is missing from dist, the warning names the `jar.mn` entry so "registration is intact, packaging dropped the file" is distinguishable from "source is unregistered". This is the fix for the class of false positive where `engine/browser/base/content/<name>.js` (registered in `browser/base/jar.mn`) collided with an unrelated `browser/defaults/preferences/<name>.js` added by a separate patch; the heuristic could not distinguish them, so the audit falsely reported the correctly-packaged chrome resource as missing.
|
|
403
445
|
- **Build inputs are excluded.** `jar.mn`, `moz.build`, `moz.configure`, `Makefile.in`, and `mozbuild.in` are consumed by the build to produce chrome registrations / make targets but never themselves ship. They are skipped before the dist lookup, so editing them no longer fires a "missing packaged artifact" warning.
|
|
404
|
-
- **Same-basename collisions in `dist/` are disambiguated by trailing-segment overlap.** A branding override at `engine/browser/branding/<name>/content/aboutDialog.css` ships at `chrome/<area>/content/branding/aboutDialog.css`. A naive basename match would tie that against the unrelated upstream `chrome/<area>/content/browser/aboutDialog.css`; the audit now scores candidates by trailing path-segment match plus a small bonus for non-generic source segments (`branding`, the branding directory name) appearing in the candidate path, so re-rooted artifacts win over coincidentally-named ones.
|
|
405
|
-
- **Unrelated same-basename hits never surface as "stale".** When the best-scoring candidate shares only the basename with the source and no meaningful intermediate segment (common on sparsely-populated `_tests/` trees where an upstream helper like `head.js` is the only same-basename file left from a prior build), the audit classifies the file as `missing` rather than emitting a misleading stale-comparison warning against the unrelated candidate. The warning
|
|
446
|
+
- **Same-basename collisions in `dist/` are disambiguated by trailing-segment overlap.** A branding override at `engine/browser/branding/<name>/content/aboutDialog.css` ships at `chrome/<area>/content/branding/aboutDialog.css`. A naive basename match would tie that against the unrelated upstream `chrome/<area>/content/browser/aboutDialog.css`; the audit now scores candidates by trailing path-segment match plus a small bonus for non-generic source segments (`branding`, the branding directory name) appearing in the candidate path, so re-rooted artifacts win over coincidentally-named ones. Applies only to sources that are not registered in jar.mn (registration-aware lookup runs first).
|
|
447
|
+
- **Unrelated same-basename hits never surface as "stale".** When the best-scoring candidate shares only the basename with the source and no meaningful intermediate segment (common on sparsely-populated `_tests/` trees where an upstream helper like `head.js` is the only same-basename file left from a prior build), the audit classifies the file as `missing` rather than emitting a misleading stale-comparison warning against the unrelated candidate. The warning enumerates every same-basename hit so the operator can see the full set of confounders at a glance — not just the scorer's pick.
|
|
406
448
|
- **Test sources are looked up under `_tests/`, not `dist/`.** Anything under `/test(s)/` directories, plus `browser_*.js` / `test_*.js` / `xpcshell.toml` / `browser.ini`, is resolved against the `_tests/` tree under the active `obj-*` directory. Mochitest and xpcshell harnesses copy registered tests there, never into the packaged bundle. Misses still warn — but they point at `_tests/`, directing the operator to `BROWSER_CHROME_MANIFESTS` / `XPCSHELL_TESTS_MANIFESTS` instead of `package-manifest.in`.
|
|
407
449
|
- **Test-path audits are gated on `_tests/all-tests.json`.** Plain `mach build` populates a partial `_tests/` subtree and stops — full test packaging only runs under `mach package-tests` / `mach test <target>` (or `fireforge test <name>`). The audit now checks for the `all-tests.json` marker written by the packaged-tests make target and silently skips test-path sources when the marker is absent, so every registered mochitest / xpcshell source no longer false-flags as "missing" on the common build-only path. Run `cd engine && ./mach package-tests` (or a scoped `fireforge test`) after a build to green-check test registrations.
|
|
408
450
|
- **Files inside an `if CONFIG[…]:` block in their owning `moz.build` are skipped on hosts where the gate is off.** Windows-only stubinstaller CSS on a macOS build, Darwin-only artwork on Linux, etc. The detection walks up to the closest `moz.build`, scans for the basename inside a Python-style indented `if CONFIG[…]:` block, and matches the gate against the host platform. Negation expressions are conservatively NOT treated as single-OS gates so a warning is never wrongly suppressed for a file that should ship on the current host. Subtrees packaged through platform-specific `Makefile.in` recipes that live outside the `moz.build` graph — `/stubinstaller/` (NSIS), `browser/installer/windows/`, `browser/installer/macosx/`, `browser/installer/linux/` — are also gated by path convention so branding stubinstaller CSS no longer warns on every non-Windows build.
|
|
@@ -411,6 +453,21 @@ The build also auto-runs `mach configure` before the mach build step when any `m
|
|
|
411
453
|
|
|
412
454
|
Mach build failures with known-cryptic mozbuild errors now print actionable hints. Example: a `JS_PREFERENCE_PP_FILES` entry with no `#filter` / `#expand` directives now prints `Hint: ...use JS_PREFERENCE_FILES instead, or add at least one #filter / #expand directive to the file.` alongside the raw mach traceback.
|
|
413
455
|
|
|
456
|
+
### Relocated workspaces: `fireforge build --rewrite-mozinfo`
|
|
457
|
+
|
|
458
|
+
When a workspace is moved to a new path (e.g. the project directory was renamed or relocated on disk), `obj-*/mozinfo.json` still records the old `topsrcdir` / `topobjdir`. The pre-flight detects the mismatch and aborts with a "delete and rebuild" instruction — correct but expensive; a fresh clean build typically runs ~20 minutes and discards ~14 GB of intact obj artefacts on a moved checkout.
|
|
459
|
+
|
|
460
|
+
`fireforge build --rewrite-mozinfo` offers a shortcut for the pure path-relocation case. The rewriter patches `topsrcdir` / `topobjdir` / `mozconfig` inside `mozinfo.json` to match the current checkout, then runs `mach configure` so the recursive-make backend regenerates against the corrected paths. No obj-\* scrubbing, no fresh compile.
|
|
461
|
+
|
|
462
|
+
The rewriter refuses any change it cannot prove safe:
|
|
463
|
+
|
|
464
|
+
- `mozinfo.json` must record both `topsrcdir` and `topobjdir`.
|
|
465
|
+
- `topobjdir` must resolve to `<topsrcdir>/<objDir>` — out-of-tree builds are rejected.
|
|
466
|
+
- The detected `obj-*` directory name must match the one recorded in mozinfo — if the objdir name itself changed, the configure shape changed and a full rebuild is required.
|
|
467
|
+
- `mozinfo.json` must be valid JSON describing an object.
|
|
468
|
+
|
|
469
|
+
On any refusal the command falls back to the original clean-rebuild guidance with the refusal reason appended, so an unsafe relocation is never silently misrepaired.
|
|
470
|
+
|
|
414
471
|
## Configuration
|
|
415
472
|
|
|
416
473
|
`fireforge.json` at your project root:
|
|
@@ -471,8 +528,14 @@ Both rules compose with the existing `tokenPrefix` / `tokenAllowlist` checks and
|
|
|
471
528
|
|
|
472
529
|
`fireforge furnace create --xpcshell` scaffolds an **xpcshell test harness** instead. Use this when the component's code path is storage-only, observer-driven, or module-loading logic that does not touch a `tabbrowser`. xpcshell runs headless without browser chrome, so forks without an upstream tab strip can still cover these paths. The scaffolder writes `test_<name>_module_loads.js` + `xpcshell.toml` into `engine/browser/base/content/test/<binary-name>-xpcshell/<component-name>/` and prints a note: registration in `XPCSHELL_TESTS_MANIFESTS` is the operator's call (the moz.build that should own the entry depends on where the component actually lives).
|
|
473
530
|
|
|
531
|
+
xpcshell has a chrome-URI boundary that is worth knowing before writing assertions: `chrome://global/*` (toolkit chrome) IS registered and resolvable from the harness, but `chrome://browser/*` (browser chrome) is NOT — even when `firefox-appdir = "browser"` is set in the xpcshell.toml, the manifest set xpcshell loads lags what the real browser loads, so `NetUtil.asyncFetch("chrome://browser/content/…")` can still fail with `NS_ERROR_FILE_NOT_FOUND` against an artifact that IS present in `obj-*/dist/`. Assertions that need browser chrome URIs belong in a browser-chrome mochitest (`furnace create --test-style=browser-chrome`). The scaffolded xpcshell test carries the same note inline so the constraint is obvious before the operator extends the test.
|
|
532
|
+
|
|
474
533
|
The two flags can be combined — `--with-tests --xpcshell` writes both harnesses.
|
|
475
534
|
|
|
535
|
+
### Stale-build preflight on `fireforge test`
|
|
536
|
+
|
|
537
|
+
`fireforge test <path>` (without `--build`) now runs a preflight that diffs engine HEAD and the workdir against the last successful `fireforge build` (recorded at `.fireforge/last-build.json`). When packageable engine files have changed since that baseline, the command prints a single up-front warning naming the paths and pointing at `fireforge test --build`. This catches the class of failure where a newly scaffolded chrome resource or pref file is registered correctly but `obj-*/dist/` still holds the pre-edit bundle, so the test reads stale packaged artifacts and errors out with a cryptic `NS_ERROR_FILE_NOT_FOUND` inside xpcshell / mach test. The preflight is warn-only — a fork that rebuilt out-of-band (direct `./mach build`, IDE plugin, separate CI stage) is not blocked. Passing `--build` skips the preflight because the rebuild just refreshed the bundle.
|
|
538
|
+
|
|
476
539
|
## Roadmap
|
|
477
540
|
|
|
478
541
|
Planned but not yet implemented:
|
|
@@ -5,12 +5,12 @@ import { auditBuildArtifacts } from '../core/build-audit.js';
|
|
|
5
5
|
import { readBuildBaseline, writeBuildBaseline } from '../core/build-baseline.js';
|
|
6
6
|
import { prepareBuildEnvironment } from '../core/build-prepare.js';
|
|
7
7
|
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
8
|
-
import { build, buildArtifactMismatchMessage, buildUI, hasBuildArtifacts } from '../core/mach.js';
|
|
8
|
+
import { attemptMozinfoRewrite, build, buildArtifactMismatchMessage, buildUI, hasBuildArtifacts, runMach, } from '../core/mach.js';
|
|
9
9
|
import { GeneralError } from '../errors/base.js';
|
|
10
10
|
import { AmbiguousBuildArtifactsError, BuildError } from '../errors/build.js';
|
|
11
11
|
import { toError } from '../utils/errors.js';
|
|
12
12
|
import { checkDiskSpace, pathExists } from '../utils/fs.js';
|
|
13
|
-
import { error, info, intro, outro, verbose, warn } from '../utils/logger.js';
|
|
13
|
+
import { error, info, intro, outro, spinner, verbose, warn } from '../utils/logger.js';
|
|
14
14
|
import { pickDefined } from '../utils/options.js';
|
|
15
15
|
import { isPositiveInteger } from '../utils/validation.js';
|
|
16
16
|
function parseJobCount(value) {
|
|
@@ -20,6 +20,45 @@ function parseJobCount(value) {
|
|
|
20
20
|
}
|
|
21
21
|
return parsed;
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Patches `mozinfo.json` under the active obj-* directory so its recorded
|
|
25
|
+
* paths match the current engine checkout, then runs `mach configure` to
|
|
26
|
+
* regenerate every other generated path alongside it. Throws with the
|
|
27
|
+
* original mismatch guidance when the rewriter refuses to proceed — that
|
|
28
|
+
* always covers the unsafe cases, so the operator still gets the correct
|
|
29
|
+
* fallback recovery instruction.
|
|
30
|
+
*/
|
|
31
|
+
async function rewriteAndReconfigure(engineDir, objDir, mismatchMessage) {
|
|
32
|
+
const rewriteSpinner = spinner('Rewriting mozinfo.json paths...');
|
|
33
|
+
let rewrite;
|
|
34
|
+
try {
|
|
35
|
+
rewrite = await attemptMozinfoRewrite(engineDir, objDir);
|
|
36
|
+
}
|
|
37
|
+
catch (rewriteError) {
|
|
38
|
+
rewriteSpinner.error('mozinfo rewrite failed');
|
|
39
|
+
throw new GeneralError(`${mismatchMessage}\n\nmozinfo rewrite failed: ${toError(rewriteError).message}`);
|
|
40
|
+
}
|
|
41
|
+
if (!rewrite.rewritten) {
|
|
42
|
+
rewriteSpinner.error('mozinfo rewrite refused');
|
|
43
|
+
throw new GeneralError(`${mismatchMessage}\n\nmozinfo rewrite refused: ${rewrite.reason ?? 'unspecified reason'}`);
|
|
44
|
+
}
|
|
45
|
+
rewriteSpinner.stop(`mozinfo.json patched (topsrcdir → ${rewrite.newTopsrcdir})`);
|
|
46
|
+
const configureSpinner = spinner('Running mach configure against rewritten mozinfo.json...');
|
|
47
|
+
let exitCode;
|
|
48
|
+
try {
|
|
49
|
+
exitCode = await runMach(['configure'], engineDir);
|
|
50
|
+
}
|
|
51
|
+
catch (configureError) {
|
|
52
|
+
configureSpinner.error('mach configure failed');
|
|
53
|
+
throw new BuildError('mach configure failed after mozinfo rewrite', 'mach configure', configureError instanceof Error ? configureError : undefined);
|
|
54
|
+
}
|
|
55
|
+
if (exitCode !== 0) {
|
|
56
|
+
configureSpinner.error('mach configure exited non-zero');
|
|
57
|
+
throw new BuildError(`mach configure exited non-zero (${exitCode}) after mozinfo rewrite; a clean rebuild is required.`, 'mach configure');
|
|
58
|
+
}
|
|
59
|
+
configureSpinner.stop('mach configure regenerated the backend');
|
|
60
|
+
info('Backend path-rewrite complete; continuing with the build.');
|
|
61
|
+
}
|
|
23
62
|
function resolveJobCount(options, configJobs) {
|
|
24
63
|
const jobs = options.jobs ?? configJobs;
|
|
25
64
|
if (jobs === undefined) {
|
|
@@ -55,7 +94,17 @@ export async function buildCommand(projectRoot, options) {
|
|
|
55
94
|
}
|
|
56
95
|
const mismatchMessage = buildArtifactMismatchMessage(paths.engine, buildCheck, 'Build');
|
|
57
96
|
if (mismatchMessage) {
|
|
58
|
-
|
|
97
|
+
if (options.rewriteMozinfo && buildCheck.objDir) {
|
|
98
|
+
// Safe-relocation rewrite path: patch mozinfo.json paths in place so
|
|
99
|
+
// `mach configure` can regenerate the backend without scrubbing the
|
|
100
|
+
// whole obj tree. The rewriter refuses anything that is not a pure
|
|
101
|
+
// prefix-move; on refusal we surface the refusal reason alongside
|
|
102
|
+
// the original mismatch guidance and abort.
|
|
103
|
+
await rewriteAndReconfigure(paths.engine, buildCheck.objDir, mismatchMessage);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
throw new GeneralError(mismatchMessage);
|
|
107
|
+
}
|
|
59
108
|
}
|
|
60
109
|
// Log brand info if specified
|
|
61
110
|
if (options.brand) {
|
|
@@ -127,6 +176,7 @@ export function registerBuild(program, { getProjectRoot, withErrorHandling }) {
|
|
|
127
176
|
.option('--ui', 'Fast UI-only rebuild')
|
|
128
177
|
.option('-j, --jobs <n>', 'Number of parallel jobs', parseJobCount)
|
|
129
178
|
.option('--brand <name>', 'Build specific brand')
|
|
179
|
+
.option('--rewrite-mozinfo', 'On a mozinfo path mismatch, patch mozinfo.json paths in place and run mach configure instead of aborting with a clean-rebuild instruction. Refuses anything that is not a pure prefix-move.')
|
|
130
180
|
.addHelpText('after', [
|
|
131
181
|
'',
|
|
132
182
|
'Furnace apply runs automatically before the build step, so edits in',
|
|
@@ -137,6 +187,13 @@ export function registerBuild(program, { getProjectRoot, withErrorHandling }) {
|
|
|
137
187
|
'If you want to preview the engine state without triggering a build,',
|
|
138
188
|
'run `fireforge furnace apply` directly. For source-change-driven',
|
|
139
189
|
'rebuild loops during development, use `fireforge watch`.',
|
|
190
|
+
'',
|
|
191
|
+
'--rewrite-mozinfo: when a workspace is moved to a new path, mozinfo.json',
|
|
192
|
+
'still records the old topsrcdir/topobjdir and the build aborts with a',
|
|
193
|
+
'delete-and-rebuild instruction. This flag patches those paths in place',
|
|
194
|
+
'and runs mach configure — preserving up to ~20 GB of obj-* artefacts on',
|
|
195
|
+
'a relocation. The rewriter refuses any change that is not a pure prefix',
|
|
196
|
+
'move, in which case a clean rebuild is still required.',
|
|
140
197
|
].join('\n'))
|
|
141
198
|
.action(withErrorHandling(async (options) => {
|
|
142
199
|
await buildCommand(getProjectRoot(), pickDefined(options));
|
|
@@ -7,6 +7,19 @@
|
|
|
7
7
|
* upstream Firefox (browser.xhtml, privatebrowsing/aboutPrivateBrowsing.html,
|
|
8
8
|
* etc.) minus the fork-specific wiring. A fork author fills in the body.
|
|
9
9
|
*/
|
|
10
|
+
/**
|
|
11
|
+
* Sentinel attribute emitted on every `furnace chrome-doc create`-scaffolded
|
|
12
|
+
* root element. Platform modules (`DevToolsStartup`, `PageActions`,
|
|
13
|
+
* `SessionStore`, `DownloadsButton`, …) that observe
|
|
14
|
+
* `browser-delayed-startup-finished` and walk INTO the window assume the
|
|
15
|
+
* `browser.xhtml` DOM and throw on anything else. A fork-authored patch
|
|
16
|
+
* to such a module can use `hasAttribute(...)` against this sentinel as
|
|
17
|
+
* a cheap, fork-neutral guard to skip the walk on a custom chrome doc.
|
|
18
|
+
*
|
|
19
|
+
* Exposed as a named constant so test code and external checks can
|
|
20
|
+
* reference the exact attribute name without hardcoding the string.
|
|
21
|
+
*/
|
|
22
|
+
export declare const FURNACE_CHROME_DOC_SENTINEL = "data-furnace-chrome-doc";
|
|
10
23
|
/**
|
|
11
24
|
* XHTML shell for a top-level chrome document.
|
|
12
25
|
*
|
|
@@ -17,6 +30,10 @@
|
|
|
17
30
|
* platform-native window controls render.
|
|
18
31
|
* - Links the per-document CSS at `chrome://browser/content/<name>-chrome.css`
|
|
19
32
|
* and the Fluent bundle `browser/<name>.ftl`.
|
|
33
|
+
* - Carries the `data-furnace-chrome-doc="<name>"` sentinel so fork-side
|
|
34
|
+
* patches to upstream platform modules (DevToolsStartup, PageActions, …)
|
|
35
|
+
* that assume `browser.xhtml`'s DOM can guard against it cheaply. See
|
|
36
|
+
* the README "Platform module compatibility" section for the pattern.
|
|
20
37
|
*/
|
|
21
38
|
export declare function generateChromeDocXhtml(name: string, withTitlebar: boolean, license: string): string;
|
|
22
39
|
/**
|
|
@@ -8,6 +8,19 @@
|
|
|
8
8
|
* upstream Firefox (browser.xhtml, privatebrowsing/aboutPrivateBrowsing.html,
|
|
9
9
|
* etc.) minus the fork-specific wiring. A fork author fills in the body.
|
|
10
10
|
*/
|
|
11
|
+
/**
|
|
12
|
+
* Sentinel attribute emitted on every `furnace chrome-doc create`-scaffolded
|
|
13
|
+
* root element. Platform modules (`DevToolsStartup`, `PageActions`,
|
|
14
|
+
* `SessionStore`, `DownloadsButton`, …) that observe
|
|
15
|
+
* `browser-delayed-startup-finished` and walk INTO the window assume the
|
|
16
|
+
* `browser.xhtml` DOM and throw on anything else. A fork-authored patch
|
|
17
|
+
* to such a module can use `hasAttribute(...)` against this sentinel as
|
|
18
|
+
* a cheap, fork-neutral guard to skip the walk on a custom chrome doc.
|
|
19
|
+
*
|
|
20
|
+
* Exposed as a named constant so test code and external checks can
|
|
21
|
+
* reference the exact attribute name without hardcoding the string.
|
|
22
|
+
*/
|
|
23
|
+
export const FURNACE_CHROME_DOC_SENTINEL = 'data-furnace-chrome-doc';
|
|
11
24
|
/**
|
|
12
25
|
* XHTML shell for a top-level chrome document.
|
|
13
26
|
*
|
|
@@ -18,6 +31,10 @@
|
|
|
18
31
|
* platform-native window controls render.
|
|
19
32
|
* - Links the per-document CSS at `chrome://browser/content/<name>-chrome.css`
|
|
20
33
|
* and the Fluent bundle `browser/<name>.ftl`.
|
|
34
|
+
* - Carries the `data-furnace-chrome-doc="<name>"` sentinel so fork-side
|
|
35
|
+
* patches to upstream platform modules (DevToolsStartup, PageActions, …)
|
|
36
|
+
* that assume `browser.xhtml`'s DOM can guard against it cheaply. See
|
|
37
|
+
* the README "Platform module compatibility" section for the pattern.
|
|
21
38
|
*/
|
|
22
39
|
export function generateChromeDocXhtml(name, withTitlebar, license) {
|
|
23
40
|
const windowAttr = withTitlebar ? ' windowtype="navigator:browser"' : '';
|
|
@@ -34,6 +51,7 @@ export function generateChromeDocXhtml(name, withTitlebar, license) {
|
|
|
34
51
|
xmlns="http://www.w3.org/1999/xhtml"
|
|
35
52
|
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
|
36
53
|
id="${name}-window"${windowAttr}
|
|
54
|
+
${FURNACE_CHROME_DOC_SENTINEL}="${name}"
|
|
37
55
|
data-l10n-id="${name}-window-title"
|
|
38
56
|
role="application">
|
|
39
57
|
<head>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the canonical xpcshell test basename for a chrome doc packaging
|
|
3
|
+
* check (`test_<name>_packaging.js`). Hyphens in `<name>` are preserved —
|
|
4
|
+
* xpcshell permits them in file basenames even though the derived task
|
|
5
|
+
* function names replace them with underscores.
|
|
6
|
+
*/
|
|
7
|
+
export declare function chromeDocPackagingTestFileName(name: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Emits an xpcshell test that verifies the scaffolded chrome doc's
|
|
10
|
+
* `.xhtml`, `-chrome.css`, and `.ftl` are all present under the packaged
|
|
11
|
+
* app directory. Each assertion carries the exact probed path in its
|
|
12
|
+
* failure message so an operator reading a red CI run knows which
|
|
13
|
+
* jar.mn entry or build step dropped the file.
|
|
14
|
+
*/
|
|
15
|
+
export declare function generateChromeDocPackagingTest(name: string, header: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Emits the `xpcshell.toml` manifest for the packaging test directory.
|
|
18
|
+
* Sets `firefox-appdir = "browser"` so XCurProcD resolves to the browser
|
|
19
|
+
* subdir rather than the generic gecko runtime dir — without that, the
|
|
20
|
+
* path probe walks the wrong tree on every fork whose app directory is
|
|
21
|
+
* not the default.
|
|
22
|
+
*/
|
|
23
|
+
export declare function generateChromeDocPackagingManifest(name: string, header: string): string;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/*
|
|
3
|
+
* Packaging-verification test templates for
|
|
4
|
+
* `fireforge furnace chrome-doc create --with-tests`.
|
|
5
|
+
*
|
|
6
|
+
* Motivation: an operator who scaffolds a top-level chrome document wants
|
|
7
|
+
* to know "did the file actually land in the packaged bundle?" before
|
|
8
|
+
* relying on it at runtime. Both natural test harnesses have gaps for
|
|
9
|
+
* this question:
|
|
10
|
+
*
|
|
11
|
+
* - xpcshell's `chrome://browser/*` URI registration lags what the real
|
|
12
|
+
* browser loads even with `firefox-appdir = "browser"` set, so
|
|
13
|
+
* `NetUtil.asyncFetch("chrome://browser/content/<name>.xhtml")` can
|
|
14
|
+
* fail with `NS_ERROR_FILE_NOT_FOUND` against a file that IS
|
|
15
|
+
* correctly packaged (the motivating case).
|
|
16
|
+
* - Browser-chrome mochitests require a `tabbrowser`, which a
|
|
17
|
+
* fork-authored chrome doc that replaces `browser.xhtml` deliberately
|
|
18
|
+
* does not carry (the URILoadingHelper crash path).
|
|
19
|
+
*
|
|
20
|
+
* This scaffold threads the needle by probing the filesystem directly:
|
|
21
|
+
* `Services.dirsvc.get("XCurProcD", Ci.nsIFile)` returns the current
|
|
22
|
+
* process directory (the browser app dir when `firefox-appdir = "browser"`),
|
|
23
|
+
* and the packaged chrome layout for a jar.mn entry
|
|
24
|
+
* `content/browser/<name>.xhtml` is stable across platforms at
|
|
25
|
+
* `<AppDir>/chrome/browser/content/browser/<name>.xhtml` on an unpacked
|
|
26
|
+
* tree (the default for `mach build` without `MOZ_CHROME_MULTILOCALE`
|
|
27
|
+
* / omnijar). A tree that packs omni.ja would need a different probe;
|
|
28
|
+
* the scaffold notes that out-of-scope case explicitly rather than
|
|
29
|
+
* silently producing a test that fails on packed builds.
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Returns the canonical xpcshell test basename for a chrome doc packaging
|
|
33
|
+
* check (`test_<name>_packaging.js`). Hyphens in `<name>` are preserved —
|
|
34
|
+
* xpcshell permits them in file basenames even though the derived task
|
|
35
|
+
* function names replace them with underscores.
|
|
36
|
+
*/
|
|
37
|
+
export function chromeDocPackagingTestFileName(name) {
|
|
38
|
+
return `test_${name}_packaging.js`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Emits an xpcshell test that verifies the scaffolded chrome doc's
|
|
42
|
+
* `.xhtml`, `-chrome.css`, and `.ftl` are all present under the packaged
|
|
43
|
+
* app directory. Each assertion carries the exact probed path in its
|
|
44
|
+
* failure message so an operator reading a red CI run knows which
|
|
45
|
+
* jar.mn entry or build step dropped the file.
|
|
46
|
+
*/
|
|
47
|
+
export function generateChromeDocPackagingTest(name, header) {
|
|
48
|
+
const taskSuffix = name.replace(/-/g, '_');
|
|
49
|
+
return `${header}
|
|
50
|
+
|
|
51
|
+
"use strict";
|
|
52
|
+
|
|
53
|
+
// Packaging verification for the "${name}" chrome document.
|
|
54
|
+
//
|
|
55
|
+
// Scope: reads the packaged tree under XCurProcD (the browser app dir
|
|
56
|
+
// when firefox-appdir = "browser") and asserts that the three
|
|
57
|
+
// scaffolded files landed where the jar.mn / jar.inc.mn / locales/jar.mn
|
|
58
|
+
// entries promised. Does NOT go through chrome:// URI resolution —
|
|
59
|
+
// xpcshell's chrome manifest set lags the real browser's even with
|
|
60
|
+
// firefox-appdir = "browser", so NetUtil.asyncFetch on
|
|
61
|
+
// chrome://browser/content/${name}.xhtml can fail with
|
|
62
|
+
// NS_ERROR_FILE_NOT_FOUND against a file that IS packaged.
|
|
63
|
+
//
|
|
64
|
+
// Out of scope: builds that pack omni.ja (MOZ_CHROME_MULTILOCALE, some
|
|
65
|
+
// release configs). The probe below assumes an unpacked tree, which is
|
|
66
|
+
// what "mach build" produces by default. A packed build would need to
|
|
67
|
+
// unzip omni.ja to verify the same files.
|
|
68
|
+
|
|
69
|
+
add_task(async function test_${taskSuffix}_files_packaged() {
|
|
70
|
+
const appDir = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
|
|
71
|
+
|
|
72
|
+
function probe(segments, description) {
|
|
73
|
+
const file = appDir.clone();
|
|
74
|
+
for (const segment of segments) {
|
|
75
|
+
file.append(segment);
|
|
76
|
+
}
|
|
77
|
+
Assert.ok(
|
|
78
|
+
file.exists(),
|
|
79
|
+
description + " missing at " + file.path +
|
|
80
|
+
' — run "fireforge build --ui" and retry. If the file is present under ' +
|
|
81
|
+
"obj-*/dist/ but absent from this path, the xpcshell harness is probing " +
|
|
82
|
+
"a stale build tree; the post-build audit should flag the same miss.",
|
|
83
|
+
);
|
|
84
|
+
Assert.greater(
|
|
85
|
+
file.fileSize,
|
|
86
|
+
0,
|
|
87
|
+
description + " is zero-length at " + file.path +
|
|
88
|
+
" — packaging copied an empty file, check the source template.",
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
probe(
|
|
93
|
+
["chrome", "browser", "content", "browser", "${name}.xhtml"],
|
|
94
|
+
"${name}.xhtml",
|
|
95
|
+
);
|
|
96
|
+
probe(
|
|
97
|
+
["chrome", "browser", "skin", "classic", "browser", "${name}-chrome.css"],
|
|
98
|
+
"${name}-chrome.css",
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Emits the `xpcshell.toml` manifest for the packaging test directory.
|
|
105
|
+
* Sets `firefox-appdir = "browser"` so XCurProcD resolves to the browser
|
|
106
|
+
* subdir rather than the generic gecko runtime dir — without that, the
|
|
107
|
+
* path probe walks the wrong tree on every fork whose app directory is
|
|
108
|
+
* not the default.
|
|
109
|
+
*/
|
|
110
|
+
export function generateChromeDocPackagingManifest(name, header) {
|
|
111
|
+
return `${header}
|
|
112
|
+
|
|
113
|
+
[DEFAULT]
|
|
114
|
+
head = ""
|
|
115
|
+
firefox-appdir = "browser"
|
|
116
|
+
|
|
117
|
+
["${chromeDocPackagingTestFileName(name)}"]
|
|
118
|
+
`;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=chrome-doc-tests.js.map
|
|
@@ -24,6 +24,17 @@ export interface FurnaceChromeDocCreateOptions {
|
|
|
24
24
|
* `--no-titlebar`.
|
|
25
25
|
*/
|
|
26
26
|
titlebar?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Scaffold an xpcshell packaging-verification test alongside the chrome
|
|
29
|
+
* document. The generated test probes `XCurProcD/chrome/browser/...` on
|
|
30
|
+
* disk rather than going through `chrome://` URI resolution — that
|
|
31
|
+
* bypasses xpcshell's limited browser-chrome manifest set (the
|
|
32
|
+
* motivating failure mode where `NetUtil.asyncFetch` returns
|
|
33
|
+
* `NS_ERROR_FILE_NOT_FOUND` against a file that IS packaged).
|
|
34
|
+
* Registration in `XPCSHELL_TESTS_MANIFESTS` is left to the operator
|
|
35
|
+
* because the owning moz.build depends on the fork's layout.
|
|
36
|
+
*/
|
|
37
|
+
withTests?: boolean;
|
|
27
38
|
}
|
|
28
39
|
/**
|
|
29
40
|
* Runs `furnace chrome-doc create <name>`.
|