@hominis/fireforge 0.19.6 → 0.21.0

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +22 -4
  3. package/dist/src/commands/build.js +19 -1
  4. package/dist/src/commands/config.js +1 -0
  5. package/dist/src/commands/download.js +188 -185
  6. package/dist/src/commands/export-flow.js +2 -13
  7. package/dist/src/commands/furnace/chrome-doc-remove.d.ts +13 -0
  8. package/dist/src/commands/furnace/chrome-doc-remove.js +142 -0
  9. package/dist/src/commands/furnace/chrome-doc.d.ts +32 -0
  10. package/dist/src/commands/furnace/chrome-doc.js +113 -1
  11. package/dist/src/commands/furnace/create-validation.d.ts +6 -0
  12. package/dist/src/commands/furnace/create-validation.js +59 -0
  13. package/dist/src/commands/furnace/create.js +13 -88
  14. package/dist/src/commands/furnace/index.js +14 -0
  15. package/dist/src/commands/furnace/refresh.js +11 -2
  16. package/dist/src/commands/furnace/remove-state.d.ts +5 -0
  17. package/dist/src/commands/furnace/remove-state.js +14 -0
  18. package/dist/src/commands/furnace/remove.js +33 -45
  19. package/dist/src/commands/furnace/rename-browser-test.d.ts +2 -0
  20. package/dist/src/commands/furnace/rename-browser-test.js +28 -0
  21. package/dist/src/commands/furnace/rename-helpers.d.ts +13 -0
  22. package/dist/src/commands/furnace/rename-helpers.js +42 -0
  23. package/dist/src/commands/furnace/rename.js +29 -48
  24. package/dist/src/commands/status.js +22 -3
  25. package/dist/src/commands/test.js +3 -0
  26. package/dist/src/commands/watch.js +9 -2
  27. package/dist/src/core/config-paths.d.ts +1 -1
  28. package/dist/src/core/config-paths.js +1 -0
  29. package/dist/src/core/config-validate.js +5 -0
  30. package/dist/src/core/config.js +11 -7
  31. package/dist/src/core/file-lock.js +2 -2
  32. package/dist/src/core/firefox-cache.d.ts +1 -1
  33. package/dist/src/core/firefox-cache.js +43 -17
  34. package/dist/src/core/firefox-download.js +12 -4
  35. package/dist/src/core/firefox.d.ts +1 -1
  36. package/dist/src/core/firefox.js +2 -2
  37. package/dist/src/core/furnace-config.js +4 -0
  38. package/dist/src/core/furnace-refresh.js +16 -5
  39. package/dist/src/core/patch-lint-imports.d.ts +5 -0
  40. package/dist/src/core/patch-lint-imports.js +68 -0
  41. package/dist/src/core/patch-lint.js +2 -3
  42. package/dist/src/types/config.d.ts +2 -0
  43. package/dist/src/utils/fs.d.ts +5 -0
  44. package/dist/src/utils/fs.js +54 -1
  45. package/dist/src/utils/process.js +4 -1
  46. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.21.0
4
+
5
+ ### Features
6
+
7
+ - **Chrome-doc previews and cleanup.** `furnace chrome-doc create` now supports `--dry-run`, validating the same target files and jar registrations without writing. New `furnace chrome-doc remove <name>` removes scaffolded chrome-doc files, jar entries, and optional xpcshell packaging-test directories, with `--dry-run` and `--yes` support.
8
+ - **Versioned `status --json` schema.** The JSON output is now an object with `schemaVersion`, `summary`, and `files` instead of a bare array. Error paths also emit versioned JSON objects with `code` and `error`.
9
+
10
+ ### Hardening
11
+
12
+ - **Override removal demotes back to stock.** Removing a Furnace override restores engine files, deletes the override workspace, clears override checksums, and re-adds the component to `stock` tracking instead of dropping it from `furnace.json`. Optional Furnace config fields, including `platformPrefixes`, are preserved across the write.
13
+ - **Rename updates browser-chrome test bodies.** `furnace rename` now rewrites generated browser-chrome mochitest contents as well as filenames and `browser.toml`, preventing stale `waitForElement("<old>")` references after a component rename.
14
+ - **UI build preflight is stricter.** `fireforge build --ui` now refuses before `mach build faster` when the current objdir lacks a completed launchable bundle, guiding fresh imports and partial builds through a full `fireforge build` first.
15
+ - **Interrupt and diagnostics polish.** Signal-driven Furnace preview teardown has regression coverage for stale lock cleanup, `chrome-doc-rollback` markers round-trip through Furnace state validation, watch-mode permission failures name the macOS privacy remediation, and `test --doctor` prints the probed objdir, binary/app path, port, and elapsed time.
16
+
17
+ ### Documentation
18
+
19
+ - **README — Storybook first-run and audit posture.** The Furnace preview docs now call out the upstream Storybook npm install and audit output as Firefox Storybook workspace dependency state, not FireForge package dependency state.
20
+ - **README — chrome-doc lifecycle and JSON schema.** The Furnace and status sections document chrome-doc dry-runs/removal, UI-build preconditions, watch privacy guidance, and the new versioned `status --json` object.
21
+
22
+ ## 0.20.0
23
+
24
+ ### Features
25
+
26
+ - **Pinned Firefox archive verification.** `FirefoxConfig` now accepts optional `firefox.sha256`, and `fireforge config firefox.sha256 <digest>` is a supported config path. The digest must be a 64-character SHA-256 hex string; FireForge verifies both cached and freshly downloaded source archives against it before extraction, so reproducible workflows can fail fast on a mismatched upstream archive instead of discovering the problem after engine mutation has begun.
27
+ - **`fireforge patch compact`.** Patch queues with ordinal gaps after deletes, splits, or manual repair can now be compacted back to contiguous numbering under the existing patch manifest lock. The command supports dry-run previews, confirmation/`--yes`, lock-time recomputation, history entries, and the same two-phase rename/rollback safety as other patch renumbering paths.
28
+ - **Furnace xpcshell scaffolding and rename helpers.** Furnace test scaffolding now covers xpcshell packaging probes for storage/module-loading code paths, and rename logic is split into focused helpers for component filenames and config rewrites so custom and override renames share the same behaviour.
29
+
30
+ ### Hardening
31
+
32
+ - **`fireforge download` is locked end-to-end.** The command now takes a project-level `.fireforge/` sidecar lock around the full engine mutation sequence: engine existence checks, forced removal, source download/extraction, git init or resume, patch cleanup, and state updates. Parallel invocations queue with explicit timeout/stale-lock messaging instead of racing over `engine/`.
33
+ - **Firefox archive cache mutation is locked per archive.** Cache validation, invalidation, download promotion, and metadata writes now run under a per-archive cache lock. A failed downloader removes only its own unique `.part-*` file unless it already promoted the tarball, so a failing peer can no longer delete another process's valid final cache entry.
34
+ - **Download stream timers clean up on all terminal paths.** The stall detector now clears its timer from `destroy(error, callback)` as well as normal `flush`, avoiding late timer errors and unnecessary event-loop retention after pipeline failures.
35
+ - **Relative import linting is AST-backed.** `fireforge lint` now uses Acorn/ESTree traversal to detect relative ES imports, side-effect imports, dynamic `import()`, relative re-exports, and `ChromeUtils` / `Cu.import` calls, with a narrow stripped-text fallback only when parsing fails. This closes the false negatives left by the old regex pass while keeping malformed-file diagnostics useful.
36
+ - **Cross-platform process and lock probes are stricter.** `findExecutable` now parses Windows `where` output with CRLF-safe per-line trimming and returns the first non-empty candidate. File-lock stale recovery treats only `ESRCH` from `process.kill(pid, 0)` as dead; `EPERM` and unknown errors are treated as live/unknown so FireForge does not reclaim a lock owned by another live process.
37
+ - **Filesystem writes get low-risk durability checks.** `writeFileAtomic` preserves the existing mode-preservation behaviour and now best-effort fsyncs the parent directory after rename where the platform supports directory handles. `pathExistsStrict` is available for user-facing probes that should surface `EACCES` / `EPERM` instead of collapsing them into "missing".
38
+ - **Furnace mutations use fresh locked state.** `furnace create`, `furnace remove`, and `furnace rename` re-read `furnace.json` inside the mutation lock before validation and writeback, preserving sibling entries created by concurrent commands instead of writing back stale outer snapshots. `furnace remove` also shares cleanup state for custom browser-chrome, xpcshell, and MochiKit scaffolds.
39
+ - **Furnace refresh distinguishes conflicts from fatal merge failures.** `git merge-file` result handling now treats normal conflict exits separately from fatal/error output or high exit codes. `furnace refresh --all` continues through later overrides after a per-component failure, reports the failed count, and exits non-zero with the failed override names once the rest of the selection has been attempted.
40
+ - **Shared naming and coverage drift guards.** Export placement now reuses `sanitizeName` from patch export instead of maintaining a duplicate slug helper, and the dedicated coverage-threshold script now enrols newly critical modules including Furnace refresh, patch compact, and Furnace xpcshell rename handling.
41
+
42
+ ### Documentation
43
+
44
+ - **README — download locks and pinned archive checksums.** The quick-start and configuration sections now describe the project download lock, per-archive cache lock, and `firefox.sha256` workflow.
45
+ - **README — concurrent Furnace mutations.** The Furnace section now documents locked fresh-state writeback for create/remove/rename and the `refresh --all` failure summary behaviour.
46
+
3
47
  ## 0.19.0
4
48
 
5
49
  ### Features
package/README.md CHANGED
@@ -36,7 +36,7 @@ Inspired by [fern.js](https://github.com/ghostery/user-agent-desktop) and [Melon
36
36
  - **Python 3** (required by Firefox's `mach` build system).
37
37
  - **Git**
38
38
  - Platform build tools: Xcode on macOS, `build-essential` on Linux, Visual Studio Build Tools on Windows.
39
- - **Watchman** (optional, only required by `fireforge watch`). Install via `brew install watchman` (macOS), `dnf install watchman` (Fedora), or follow the upstream [Meta docs](https://facebook.github.io/watchman/). `fireforge doctor` surfaces a warning row when it is not on `PATH` so the dependency is visible during the usual onboarding sweep rather than at the watch-mode failure site. `fireforge watch` resolves watchman's absolute path via `which` / `where` and prepends its directory to the subprocess `PATH` it hands mach, so a homebrew-installed watchman at `/opt/homebrew/bin/watchman` (absent from the Node subprocess's default `PATH` on macOS) is still visible to `mach watch` without the operator having to re-export `PATH` manually.
39
+ - **Watchman** (optional, only required by `fireforge watch`). Install via `brew install watchman` (macOS), `dnf install watchman` (Fedora), or follow the upstream [Meta docs](https://facebook.github.io/watchman/). `fireforge doctor` surfaces a warning row when it is not on `PATH` so the dependency is visible during the usual onboarding sweep rather than at the watch-mode failure site. `fireforge watch` resolves watchman's absolute path via `which` / `where` and prepends its directory to the subprocess `PATH` it hands mach, so a homebrew-installed watchman at `/opt/homebrew/bin/watchman` (absent from the Node subprocess's default `PATH` on macOS) is still visible to `mach watch` without the operator having to re-export `PATH` manually. If `mach watch` reports `Operation not permitted` / `EPERM` on macOS, FireForge points at the usual privacy fix: grant Full Disk Access or Files and Folders access to the terminal/Codex app and watchman, then restart watchman with `watchman shutdown-server`.
40
40
 
41
41
  ### Setup
42
42
 
@@ -87,6 +87,8 @@ npx fireforge rebase
87
87
 
88
88
  `fireforge download` indexes the extracted Firefox source into a fresh git repository — a one-time 1–3 minute pass on a cold SSD, longer on slow or loaded disks. The monolithic `git add -A` is capped at 10 minutes by default and falls back to a per-directory chunked pass (30 minutes per chunk) when the cap hits. If indexing still times out, the command now raises `GitIndexingTimeoutError` with recovery guidance: extend the cap via `FIREFORGE_GIT_ADD_TIMEOUT_MS` (monolithic) and/or `FIREFORGE_GIT_ADD_CHUNK_TIMEOUT_MS` (chunked) in milliseconds, e.g. `FIREFORGE_GIT_ADD_TIMEOUT_MS=1800000 fireforge download --force` for a 30-minute monolithic budget, then re-run `fireforge download --force` — the resume path picks up from the partial git state so the repeat is not wasted work.
89
89
 
90
+ `fireforge download` is serialised by a project lock under `.fireforge/`, covering the engine-exists check, forced replacement, extraction, git initialisation/resume, patch cleanup and state update. Archive cache entries are also guarded by per-archive locks, so parallel downloads queue instead of racing over the same `engine/` directory or deleting each other's cache results. For reproducible workflows, set `firefox.sha256` to the expected 64-character archive SHA-256; FireForge verifies both cached and freshly downloaded archives before extraction and refuses a mismatch before touching the engine.
91
+
90
92
  `fireforge rebase --dry-run` refuses when the engine has no baseline commit yet (e.g. the aftermath of an aborted `download --force`), so dry-run and real-run preconditions stay in sync.
91
93
 
92
94
  ## Patch Workflow
@@ -288,9 +290,11 @@ When a patch queue drifts, e.g. due to overlapping new-file creations, forward i
288
290
  fireforge verify # fsck: manifest + cross-patch lint
289
291
  fireforge lint # includes the same cross-patch rules
290
292
  fireforge status --ownership # flat path → owning patch table
291
- fireforge status --json # machine-readable classified output
293
+ fireforge status --json # machine-readable classified object
292
294
  ```
293
295
 
296
+ `status --json` emits a versioned object: `{ "schemaVersion": 1, "summary": { "total": <n>, "byClassification": { ... } }, "files": [...] }`. Error paths also emit a JSON object with `schemaVersion`, `code`, and `error` before exiting non-zero, so scripts can parse both clean and failing runs.
297
+
294
298
  Then fix with the appropriate primitive:
295
299
 
296
300
  | Problem | Fix |
@@ -377,6 +381,9 @@ Custom elements live under `toolkit/content/widgets`, but a fork's top-level chr
377
381
  fireforge furnace chrome-doc create mybrowser # full chrome (titlebar + windowtype)
378
382
  fireforge furnace chrome-doc create overlay --no-titlebar # frameless overlay
379
383
  fireforge furnace chrome-doc create mybrowser --with-tests # + xpcshell packaging-verification test
384
+ fireforge furnace chrome-doc create mybrowser --dry-run # preview without writing
385
+ fireforge furnace chrome-doc remove mybrowser --dry-run # preview cleanup
386
+ fireforge furnace chrome-doc remove mybrowser --yes # remove files + registrations
380
387
  ```
381
388
 
382
389
  The command writes:
@@ -388,7 +395,7 @@ The command writes:
388
395
  - Appends the corresponding `jar.mn` / `jar.inc.mn` entries. The locales/jar.mn append is suppressed when the fork's existing `engine/browser/locales/jar.mn` already carries a `[localization] (%browser/**/*.ftl)` (or `(%browser/*.ftl)`) wildcard that would already pick up the scaffolded FTL — on those forks a per-file `locale/<name>.ftl` entry would be dead weight at best and an outright build break when the fork has dropped the `% locale browser …` registration the per-file entry depends on. Forks still on the legacy registration get the per-file entry as before.
389
396
  - 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.
390
397
 
391
- Writes are transactional: a SIGINT mid-scaffold rolls back every touched file. Requires an existing engine — run `fireforge download` first.
398
+ Writes are transactional: a SIGINT mid-scaffold rolls back every touched file. `--dry-run` validates the same paths and registrations without acquiring the mutation lock or writing. `furnace chrome-doc remove <name>` removes the scaffolded source files, jar registrations, and optional xpcshell packaging-test directory; use its `--dry-run` first when cleaning an experimental document. Requires an existing engine — run `fireforge download` first.
392
399
 
393
400
  #### Platform module compatibility
394
401
 
@@ -435,6 +442,10 @@ Three styles are available via `--test-style`:
435
442
 
436
443
  `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.
437
444
 
445
+ `furnace create`, `furnace remove`, and `furnace rename` re-read `furnace.json` inside the mutation lock before writing, so concurrent component edits preserve sibling entries instead of writing back a stale outer snapshot. `furnace refresh --all` continues past per-component refresh failures, reports the failed count, and exits non-zero with the failed override names after finishing the rest of the selection.
446
+
447
+ `furnace preview` starts Firefox's upstream Storybook workspace. On the first run, `mach storybook` may install roughly a thousand npm packages under `engine/browser/components/storybook/` and may print npm audit counts from Storybook's transitive dependencies. FireForge frames that output as upstream Storybook dependency state; it does not mean FireForge's own package dependencies were installed into the project.
448
+
438
449
  ## Additional Commands
439
450
 
440
451
  The commands below cover project configuration, patch queue management, build packaging and development utilities. Run `fireforge <command> --help` for full option details.
@@ -448,6 +459,9 @@ fireforge config firefox.version
448
459
  # Set a config value
449
460
  fireforge config firefox.version 145.0.0esr
450
461
 
462
+ # Pin the resolved Firefox source archive checksum
463
+ fireforge config firefox.sha256 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
464
+
451
465
  # Set a value at a non-standard path (requires --force)
452
466
  fireforge config customKey "value" --force
453
467
  ```
@@ -456,6 +470,8 @@ Writes are serialised behind a sidecar lock — two concurrent `fireforge config
456
470
 
457
471
  Re-setting a key to its current value is a no-op: `fireforge.json` is not rewritten, key ordering is preserved, and the success log surfaces `<key> = <value> (unchanged)` instead of a fresh `Set …` line. This means automation that idempotently runs `fireforge config <key> <value>` no longer produces spurious diffs in `fireforge.json`.
458
472
 
473
+ `firefox.sha256` is optional. When set, it must be a 64-character hex SHA-256 for the Firefox source archive resolved from `firefox.product` + `firefox.version`; cached archives and fresh downloads are verified against it before extraction. Omit it to keep the default cache-integrity-only behaviour.
474
+
459
475
  ### Patch queue management
460
476
 
461
477
  ```bash
@@ -531,6 +547,8 @@ Aggregate patch-size findings (`large-patch-files`, `large-patch-lines`) describ
531
547
 
532
548
  `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.
533
549
 
550
+ `fireforge build --ui` is intentionally a fast rebuild path, not a bootstrap path. It now refuses before invoking `mach build faster` unless the current objdir has a completed launchable bundle; fresh imports and partial builds should run a full `fireforge build` first.
551
+
534
552
  The audit applies seven routing rules to suppress false positives that previously trained operators to ignore its warnings:
535
553
 
536
554
  - **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.
@@ -605,7 +623,7 @@ fireforge test --doctor
605
623
  fireforge test --doctor browser/base/content/test/foo/browser_bar.js
606
624
  ```
607
625
 
608
- Spawns the built browser headless, waits for a marionette handshake on `127.0.0.1:2828`, and reports PASS/FAIL with the tail of the browser's stderr on FAIL. Distinguishes "marionette wedged" (socket silent) from "mach test discovery failed" — both otherwise surface as a silent 360-second hang followed by `Passed: 0, Failed: 0`. Useful as a prefix on routine `fireforge test` invocations when marionette has been flaky.
626
+ Spawns the built browser headless, waits for a marionette handshake on `127.0.0.1:2828`, and reports PASS/FAIL with the tail of the browser's stderr on FAIL. The success output also names the objdir, binary, app path, port, and elapsed probe time so CI logs show exactly what was probed. Distinguishes "marionette wedged" (socket silent) from "mach test discovery failed" — both otherwise surface as a silent 360-second hang followed by `Passed: 0, Failed: 0`. Useful as a prefix on routine `fireforge test` invocations when marionette has been flaky.
609
627
 
610
628
  The probe is a cascade of six layered checks — engine-present → mach-available → python-available → profile-creatable → browser-spawns → marionette-handshake. Each failure is tagged `[layer N/6: <name>]` so the first broken layer is surfaced immediately instead of the whole cascade blocking on the final socket poll. When the browser binary crashes at startup (missing dylib, wrong CPU arch, corrupt profile) the cascade fails at layer 5 within the settle window, not after the full socket timeout.
611
629
 
@@ -5,7 +5,7 @@ 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 { attemptMozinfoRewrite, build, buildArtifactMismatchMessage, buildUI, hasBuildArtifacts, runMach, withBuildLock, } from '../core/mach.js';
8
+ import { attemptMozinfoRewrite, build, buildArtifactMismatchMessage, buildUI, hasBuildArtifacts, hasRunnableBundle, runMach, withBuildLock, } 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';
@@ -106,6 +106,24 @@ export async function buildCommand(projectRoot, options) {
106
106
  throw new GeneralError(mismatchMessage);
107
107
  }
108
108
  }
109
+ if (options.ui) {
110
+ if (!buildCheck.exists || !buildCheck.objDir) {
111
+ const detail = buildCheck.objDir
112
+ ? `Build artifacts incomplete in ${buildCheck.objDir}/`
113
+ : 'No completed obj-* build artifacts found.';
114
+ throw new GeneralError(`UI-only builds require a completed full build first. ${detail}\n\n` +
115
+ 'Run "fireforge build" and let it finish, then retry "fireforge build --ui".');
116
+ }
117
+ const bundleCheck = await hasRunnableBundle(paths.engine, config.binaryName, buildCheck.objDir);
118
+ if (!bundleCheck.runnable) {
119
+ const expectedSuffix = bundleCheck.expectedPath
120
+ ? ` Expected launchable binary at engine/${bundleCheck.expectedPath}.`
121
+ : '';
122
+ throw new GeneralError(`UI-only builds require a completed full build first.${expectedSuffix}\n\n` +
123
+ 'Freshly imported or partially built trees cannot use `mach build faster` yet. ' +
124
+ 'Run "fireforge build" and let it finish, then retry "fireforge build --ui".');
125
+ }
126
+ }
109
127
  // Log brand info if specified
110
128
  if (options.brand) {
111
129
  verbose(`Building with brand: ${options.brand}`);
@@ -31,6 +31,7 @@ const STRING_TYPED_KEYS = new Set([
31
31
  'binaryName',
32
32
  'firefox.version',
33
33
  'firefox.product',
34
+ 'firefox.sha256',
34
35
  'license',
35
36
  'wire.subscriptDir',
36
37
  ]);