@hominis/fireforge 0.43.1 → 0.44.1
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 +153 -0
- package/README.md +3 -3
- package/dist/bin/fireforge.d.ts +4 -9
- package/dist/bin/fireforge.js +42 -40
- package/dist/build-info.json +4 -4
- package/dist/src/cli.js +74 -8
- package/dist/src/commands/bootstrap.js +17 -20
- package/dist/src/commands/build.js +25 -32
- package/dist/src/commands/config.js +34 -41
- package/dist/src/commands/discard.js +8 -9
- package/dist/src/commands/doctor-check-core.d.ts +13 -18
- package/dist/src/commands/doctor-check-core.js +8 -35
- package/dist/src/commands/doctor-external-toolchains.js +2 -2
- package/dist/src/commands/doctor-furnace-config-sync.d.ts +18 -0
- package/dist/src/commands/{doctor-furnace-manifest-sync.js → doctor-furnace-config-sync.js} +27 -26
- package/dist/src/commands/doctor-furnace-jar.d.ts +7 -9
- package/dist/src/commands/doctor-furnace-jar.js +7 -9
- package/dist/src/commands/doctor-furnace.js +21 -24
- package/dist/src/commands/doctor-orphaned-harness.d.ts +16 -20
- package/dist/src/commands/doctor-orphaned-harness.js +18 -44
- package/dist/src/commands/doctor-patch-manifest.d.ts +8 -0
- package/dist/src/commands/doctor-patch-manifest.js +155 -0
- package/dist/src/commands/doctor-post-rebase-audit.d.ts +2 -0
- package/dist/src/commands/{doctor/post-rebase-audit.js → doctor-post-rebase-audit.js} +3 -3
- package/dist/src/commands/doctor-working-tree.d.ts +10 -12
- package/dist/src/commands/doctor-working-tree.js +11 -18
- package/dist/src/commands/doctor.d.ts +1 -1
- package/dist/src/commands/doctor.js +64 -96
- package/dist/src/commands/download.js +32 -48
- package/dist/src/commands/export-all.js +29 -33
- package/dist/src/commands/export-flow.js +6 -6
- package/dist/src/commands/export-placement-conflicts.js +10 -9
- package/dist/src/commands/export-placement-gate.js +5 -5
- package/dist/src/commands/export-shared.d.ts +30 -33
- package/dist/src/commands/export-shared.js +45 -43
- package/dist/src/commands/export.js +21 -27
- package/dist/src/commands/furnace/apply.js +14 -23
- package/dist/src/commands/furnace/chrome-doc-remove.js +14 -21
- package/dist/src/commands/furnace/chrome-doc-templates.d.ts +23 -23
- package/dist/src/commands/furnace/chrome-doc-templates.js +23 -23
- package/dist/src/commands/furnace/chrome-doc-tests.js +26 -29
- package/dist/src/commands/furnace/chrome-doc.d.ts +16 -17
- package/dist/src/commands/furnace/chrome-doc.js +31 -41
- package/dist/src/commands/furnace/create-browser-test.d.ts +4 -4
- package/dist/src/commands/furnace/create-browser-test.js +8 -8
- package/dist/src/commands/furnace/create-mochikit.d.ts +6 -7
- package/dist/src/commands/furnace/create-mochikit.js +8 -9
- package/dist/src/commands/furnace/create-readback.d.ts +12 -13
- package/dist/src/commands/furnace/create-readback.js +12 -13
- package/dist/src/commands/furnace/create-templates.d.ts +22 -28
- package/dist/src/commands/furnace/create-templates.js +35 -30
- package/dist/src/commands/furnace/create-xpcshell.js +2 -2
- package/dist/src/commands/furnace/create.js +33 -23
- package/dist/src/commands/furnace/deploy.js +16 -24
- package/dist/src/commands/furnace/diff.js +15 -10
- package/dist/src/commands/furnace/init.js +33 -36
- package/dist/src/commands/furnace/override.js +23 -25
- package/dist/src/commands/furnace/preview.js +47 -67
- package/dist/src/commands/furnace/refresh.js +12 -16
- package/dist/src/commands/furnace/remove-mutations.d.ts +3 -6
- package/dist/src/commands/furnace/remove-mutations.js +14 -17
- package/dist/src/commands/furnace/remove-state.js +1 -1
- package/dist/src/commands/furnace/remove.js +33 -38
- package/dist/src/commands/furnace/rename-xpcshell.d.ts +8 -11
- package/dist/src/commands/furnace/rename-xpcshell.js +8 -11
- package/dist/src/commands/furnace/rename.js +59 -69
- package/dist/src/commands/furnace/scan.js +19 -33
- package/dist/src/commands/furnace/status.js +12 -9
- package/dist/src/commands/furnace/sync.js +9 -16
- package/dist/src/commands/furnace/validate.js +20 -20
- package/dist/src/commands/import.js +48 -37
- package/dist/src/commands/lint-per-patch.js +15 -10
- package/dist/src/commands/lint-per-run-checkjs.d.ts +13 -13
- package/dist/src/commands/lint-per-run-checkjs.js +13 -13
- package/dist/src/commands/lint-report.js +6 -7
- package/dist/src/commands/lint.d.ts +15 -16
- package/dist/src/commands/lint.js +86 -102
- package/dist/src/commands/package.js +6 -11
- package/dist/src/commands/patch/compact.js +1 -1
- package/dist/src/commands/patch/delete.js +24 -33
- package/dist/src/commands/patch/index.d.ts +5 -6
- package/dist/src/commands/patch/index.js +10 -12
- package/dist/src/commands/patch/lint-ignore.js +14 -13
- package/dist/src/commands/patch/move-files-create.d.ts +6 -6
- package/dist/src/commands/patch/move-files-create.js +8 -8
- package/dist/src/commands/patch/move-files-into.d.ts +6 -6
- package/dist/src/commands/patch/move-files-into.js +8 -8
- package/dist/src/commands/patch/patch-context.d.ts +2 -3
- package/dist/src/commands/patch/patch-context.js +2 -3
- package/dist/src/commands/patch/rename.d.ts +10 -11
- package/dist/src/commands/patch/rename.js +11 -12
- package/dist/src/commands/patch/reorder.js +8 -9
- package/dist/src/commands/patch/split-plan.js +1 -1
- package/dist/src/commands/patch/split.d.ts +8 -10
- package/dist/src/commands/patch/split.js +9 -11
- package/dist/src/commands/patch/staged-dependency-validate.js +8 -8
- package/dist/src/commands/patch/staged-dependency.js +7 -3
- package/dist/src/commands/patch/tier.d.ts +9 -9
- package/dist/src/commands/patch/tier.js +18 -15
- package/dist/src/commands/re-export-adjacent.js +9 -8
- package/dist/src/commands/re-export-bulk-scan.d.ts +9 -9
- package/dist/src/commands/re-export-bulk-scan.js +22 -21
- package/dist/src/commands/re-export-drift.js +10 -10
- package/dist/src/commands/re-export-files.d.ts +6 -7
- package/dist/src/commands/re-export-files.js +34 -44
- package/dist/src/commands/re-export-lint.d.ts +10 -13
- package/dist/src/commands/re-export-lint.js +16 -14
- package/dist/src/commands/re-export-options.d.ts +6 -6
- package/dist/src/commands/re-export-options.js +6 -6
- package/dist/src/commands/re-export-register.d.ts +4 -10
- package/dist/src/commands/re-export-register.js +5 -11
- package/dist/src/commands/re-export-scan.js +1 -1
- package/dist/src/commands/re-export-single.d.ts +3 -3
- package/dist/src/commands/re-export-single.js +33 -25
- package/dist/src/commands/re-export.js +13 -21
- package/dist/src/commands/rebase/abort.js +3 -3
- package/dist/src/commands/rebase/confirm.js +2 -1
- package/dist/src/commands/rebase/continue.d.ts +1 -1
- package/dist/src/commands/rebase/continue.js +14 -9
- package/dist/src/commands/rebase/index.js +11 -11
- package/dist/src/commands/rebase/patch-loop.d.ts +7 -1
- package/dist/src/commands/rebase/patch-loop.js +60 -39
- package/dist/src/commands/rebase/summary.js +1 -1
- package/dist/src/commands/register.js +5 -7
- package/dist/src/commands/reset.js +2 -1
- package/dist/src/commands/resolve.d.ts +16 -13
- package/dist/src/commands/resolve.js +30 -33
- package/dist/src/commands/run.js +33 -35
- package/dist/src/commands/setup-support.js +15 -19
- package/dist/src/commands/setup.js +13 -9
- package/dist/src/commands/status-check.d.ts +6 -6
- package/dist/src/commands/status-json.d.ts +6 -6
- package/dist/src/commands/status-json.js +5 -0
- package/dist/src/commands/status-output.d.ts +5 -5
- package/dist/src/commands/status-output.js +6 -6
- package/dist/src/commands/status-ownership.d.ts +16 -14
- package/dist/src/commands/status-ownership.js +16 -14
- package/dist/src/commands/status.js +42 -34
- package/dist/src/commands/test-build-phase.js +26 -13
- package/dist/src/commands/test-diagnose.js +44 -44
- package/dist/src/commands/test-register.js +1 -0
- package/dist/src/commands/test-run.d.ts +14 -16
- package/dist/src/commands/test-run.js +21 -13
- package/dist/src/commands/test-stale-gate.d.ts +15 -16
- package/dist/src/commands/test-stale-gate.js +15 -16
- package/dist/src/commands/test-verdict.d.ts +20 -3
- package/dist/src/commands/test-verdict.js +30 -0
- package/dist/src/commands/test.js +83 -76
- package/dist/src/commands/token-coverage.js +3 -4
- package/dist/src/commands/token.js +16 -18
- package/dist/src/commands/tree.js +47 -31
- package/dist/src/commands/typecheck.d.ts +10 -12
- package/dist/src/commands/typecheck.js +24 -27
- package/dist/src/commands/verify.js +13 -15
- package/dist/src/commands/watch.js +21 -23
- package/dist/src/commands/wire.js +52 -62
- package/dist/src/core/ast-utils.d.ts +8 -7
- package/dist/src/core/ast-utils.js +8 -7
- package/dist/src/core/branding.d.ts +9 -14
- package/dist/src/core/branding.js +7 -11
- package/dist/src/core/browser-wire.d.ts +1 -1
- package/dist/src/core/browser-wire.js +14 -19
- package/dist/src/core/build-audit-platform.js +25 -33
- package/dist/src/core/build-audit-registration.js +17 -25
- package/dist/src/core/build-audit-resolve.d.ts +15 -17
- package/dist/src/core/build-audit-resolve.js +39 -36
- package/dist/src/core/build-audit-transforms.js +18 -19
- package/dist/src/core/build-audit.js +28 -42
- package/dist/src/core/build-baseline-types.d.ts +29 -35
- package/dist/src/core/build-baseline.d.ts +5 -4
- package/dist/src/core/build-baseline.js +14 -14
- package/dist/src/core/build-overwrite-guard.d.ts +7 -14
- package/dist/src/core/build-overwrite-guard.js +17 -27
- package/dist/src/core/build-prepare.d.ts +2 -2
- package/dist/src/core/build-prepare.js +8 -9
- package/dist/src/core/config-mutate.js +20 -19
- package/dist/src/core/config-state.js +12 -4
- package/dist/src/core/config-validate.js +8 -5
- package/dist/src/core/config.d.ts +5 -8
- package/dist/src/core/config.js +7 -16
- package/dist/src/core/coverage-extend.d.ts +15 -13
- package/dist/src/core/coverage-extend.js +15 -13
- package/dist/src/core/destructive.d.ts +31 -9
- package/dist/src/core/destructive.js +29 -26
- package/dist/src/core/diff-hunks.js +10 -12
- package/dist/src/core/discard-baseline.d.ts +8 -8
- package/dist/src/core/discard-baseline.js +16 -15
- package/dist/src/core/display-state.d.ts +12 -14
- package/dist/src/core/display-state.js +12 -14
- package/dist/src/core/engine-changes.d.ts +2 -4
- package/dist/src/core/engine-changes.js +2 -4
- package/dist/src/core/engine-precondition.d.ts +28 -13
- package/dist/src/core/engine-precondition.js +25 -10
- package/dist/src/core/engine-session-lock.d.ts +47 -21
- package/dist/src/core/engine-session-lock.js +168 -33
- package/dist/src/core/export-moved-code-shape.d.ts +10 -10
- package/dist/src/core/export-moved-code-shape.js +10 -10
- package/dist/src/core/file-lock.d.ts +62 -5
- package/dist/src/core/file-lock.js +196 -59
- package/dist/src/core/firefox-cache.js +1 -3
- package/dist/src/core/firefox-extract.js +10 -14
- package/dist/src/core/firefox.d.ts +7 -5
- package/dist/src/core/firefox.js +4 -5
- package/dist/src/core/furnace-apply-dry-run.d.ts +2 -2
- package/dist/src/core/furnace-apply-dry-run.js +3 -2
- package/dist/src/core/furnace-apply-ftl.d.ts +7 -9
- package/dist/src/core/furnace-apply-ftl.js +16 -13
- package/dist/src/core/furnace-apply-helpers.d.ts +7 -7
- package/dist/src/core/furnace-apply-helpers.js +30 -21
- package/dist/src/core/furnace-apply-overwrite-warn.d.ts +8 -9
- package/dist/src/core/furnace-apply-overwrite-warn.js +8 -9
- package/dist/src/core/furnace-apply-undeploy-actions.d.ts +3 -4
- package/dist/src/core/furnace-apply-undeploy-actions.js +3 -4
- package/dist/src/core/furnace-apply.d.ts +5 -8
- package/dist/src/core/furnace-apply.js +19 -24
- package/dist/src/core/furnace-checksum-utils.d.ts +14 -0
- package/dist/src/core/furnace-checksum-utils.js +16 -0
- package/dist/src/core/furnace-config.d.ts +21 -30
- package/dist/src/core/furnace-config.js +32 -54
- package/dist/src/core/furnace-constants.d.ts +8 -0
- package/dist/src/core/furnace-constants.js +10 -2
- package/dist/src/core/furnace-css-fragments.d.ts +5 -5
- package/dist/src/core/furnace-css-fragments.js +5 -5
- package/dist/src/core/furnace-jsconfig.d.ts +11 -12
- package/dist/src/core/furnace-jsconfig.js +11 -12
- package/dist/src/core/furnace-marker.d.ts +7 -8
- package/dist/src/core/furnace-marker.js +7 -8
- package/dist/src/core/furnace-operation.d.ts +31 -0
- package/dist/src/core/furnace-operation.js +54 -17
- package/dist/src/core/furnace-precondition.d.ts +61 -0
- package/dist/src/core/furnace-precondition.js +67 -0
- package/dist/src/core/furnace-registration-remove.d.ts +5 -6
- package/dist/src/core/furnace-registration-remove.js +6 -7
- package/dist/src/core/furnace-registration-validate.d.ts +13 -0
- package/dist/src/core/furnace-registration-validate.js +20 -1
- package/dist/src/core/furnace-registration.d.ts +5 -6
- package/dist/src/core/furnace-registration.js +10 -12
- package/dist/src/core/furnace-rollback.js +7 -7
- package/dist/src/core/furnace-scanner.d.ts +1 -0
- package/dist/src/core/furnace-scanner.js +1 -2
- package/dist/src/core/furnace-stale-export.d.ts +10 -10
- package/dist/src/core/furnace-stale-export.js +10 -10
- package/dist/src/core/furnace-state-persist.d.ts +6 -8
- package/dist/src/core/furnace-state-persist.js +6 -8
- package/dist/src/core/furnace-validate-accessibility.d.ts +22 -0
- package/dist/src/core/furnace-validate-accessibility.js +310 -1
- package/dist/src/core/furnace-validate-compatibility.js +141 -1
- package/dist/src/core/furnace-validate-helpers.d.ts +5 -116
- package/dist/src/core/furnace-validate-helpers.js +0 -491
- package/dist/src/core/furnace-validate-registration.d.ts +7 -0
- package/dist/src/core/furnace-validate-registration.js +115 -41
- package/dist/src/core/furnace-validate.d.ts +16 -0
- package/dist/src/core/furnace-validate.js +188 -145
- package/dist/src/core/furnace-version-drift.d.ts +10 -11
- package/dist/src/core/furnace-version-drift.js +10 -11
- package/dist/src/core/git-base.d.ts +21 -9
- package/dist/src/core/git-base.js +24 -9
- package/dist/src/core/git-diff.d.ts +20 -9
- package/dist/src/core/git-diff.js +148 -63
- package/dist/src/core/git-file-ops.d.ts +19 -19
- package/dist/src/core/git-file-ops.js +19 -19
- package/dist/src/core/git-readonly-index.d.ts +53 -19
- package/dist/src/core/git-readonly-index.js +75 -31
- package/dist/src/core/git-status.d.ts +5 -6
- package/dist/src/core/git-status.js +5 -6
- package/dist/src/core/git.d.ts +9 -10
- package/dist/src/core/git.js +57 -71
- package/dist/src/core/license-headers.d.ts +17 -23
- package/dist/src/core/license-headers.js +17 -23
- package/dist/src/core/mach-build-artifacts.d.ts +9 -9
- package/dist/src/core/mach-build-artifacts.js +57 -19
- package/dist/src/core/mach-error-hints.js +42 -29
- package/dist/src/core/mach-known-noise-filter.d.ts +15 -16
- package/dist/src/core/mach-known-noise-filter.js +18 -20
- package/dist/src/core/mach-mozconfig.d.ts +14 -0
- package/dist/src/core/mach-mozconfig.js +30 -0
- package/dist/src/core/mach-resource-shim.d.ts +43 -58
- package/dist/src/core/mach-resource-shim.js +50 -67
- package/dist/src/core/mach.d.ts +37 -41
- package/dist/src/core/mach.js +50 -51
- package/dist/src/core/marionette-port.d.ts +36 -1
- package/dist/src/core/marionette-port.js +96 -31
- package/dist/src/core/marionette-preflight.d.ts +8 -12
- package/dist/src/core/marionette-preflight.js +86 -52
- package/dist/src/core/mochitest-server-port.d.ts +42 -0
- package/dist/src/core/mochitest-server-port.js +119 -0
- package/dist/src/core/module-resolution-preflight.d.ts +9 -11
- package/dist/src/core/module-resolution-preflight.js +9 -11
- package/dist/src/core/moz-manifest-helpers.d.ts +79 -0
- package/dist/src/core/moz-manifest-helpers.js +163 -0
- package/dist/src/core/{manifest-register.d.ts → moz-manifest-register.d.ts} +1 -1
- package/dist/src/core/{manifest-register.js → moz-manifest-register.js} +13 -28
- package/dist/src/core/{manifest-rules.d.ts → moz-manifest-rules.d.ts} +10 -1
- package/dist/src/core/{manifest-rules.js → moz-manifest-rules.js} +31 -24
- package/dist/src/core/{manifest-tokenizers.d.ts → moz-manifest-tokenizers.d.ts} +3 -5
- package/dist/src/core/{manifest-tokenizers.js → moz-manifest-tokenizers.js} +9 -12
- package/dist/src/core/ownership-table.d.ts +14 -16
- package/dist/src/core/ownership-table.js +14 -16
- package/dist/src/core/parser-fallback.d.ts +15 -1
- package/dist/src/core/parser-fallback.js +22 -3
- package/dist/src/core/patch-apply-fuzz.d.ts +10 -12
- package/dist/src/core/patch-apply-fuzz.js +15 -18
- package/dist/src/core/patch-apply.d.ts +13 -34
- package/dist/src/core/patch-apply.js +16 -76
- package/dist/src/core/patch-artifact-normalize.d.ts +13 -15
- package/dist/src/core/patch-artifact-normalize.js +13 -15
- package/dist/src/core/patch-export-metadata.d.ts +12 -2
- package/dist/src/core/patch-export-metadata.js +13 -4
- package/dist/src/core/patch-export-update.d.ts +13 -1
- package/dist/src/core/patch-export-update.js +25 -8
- package/dist/src/core/patch-export.d.ts +15 -8
- package/dist/src/core/patch-export.js +10 -11
- package/dist/src/core/patch-files.d.ts +5 -6
- package/dist/src/core/patch-files.js +5 -6
- package/dist/src/core/patch-identifier-suggest.d.ts +7 -12
- package/dist/src/core/patch-identifier-suggest.js +14 -19
- package/dist/src/core/patch-lint-binary.d.ts +51 -0
- package/dist/src/core/patch-lint-binary.js +39 -0
- package/dist/src/core/{lint-cache.d.ts → patch-lint-cache.d.ts} +5 -5
- package/dist/src/core/{lint-cache.js → patch-lint-cache.js} +21 -23
- package/dist/src/core/patch-lint-checkjs.d.ts +16 -16
- package/dist/src/core/patch-lint-checkjs.js +41 -20
- package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +6 -9
- package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -14
- package/dist/src/core/patch-lint-creators.d.ts +8 -8
- package/dist/src/core/patch-lint-creators.js +8 -8
- package/dist/src/core/patch-lint-cross.d.ts +17 -20
- package/dist/src/core/patch-lint-cross.js +16 -16
- package/dist/src/core/patch-lint-css.d.ts +3 -5
- package/dist/src/core/patch-lint-css.js +17 -22
- package/dist/src/core/patch-lint-diff-tag.d.ts +14 -16
- package/dist/src/core/patch-lint-diff-tag.js +14 -16
- package/dist/src/core/patch-lint-jsdoc.d.ts +7 -8
- package/dist/src/core/patch-lint-jsdoc.js +26 -19
- package/dist/src/core/patch-lint-mozbuild.js +20 -20
- package/dist/src/core/patch-lint-observer.d.ts +7 -7
- package/dist/src/core/patch-lint-observer.js +7 -7
- package/dist/src/core/patch-lint.d.ts +38 -29
- package/dist/src/core/patch-lint.js +89 -121
- package/dist/src/core/patch-lock.d.ts +1 -1
- package/dist/src/core/patch-lock.js +45 -9
- package/dist/src/core/patch-manifest-consistency.d.ts +57 -6
- package/dist/src/core/patch-manifest-consistency.js +111 -40
- package/dist/src/core/patch-manifest-files-affected.d.ts +43 -0
- package/dist/src/core/patch-manifest-files-affected.js +77 -0
- package/dist/src/core/patch-manifest-io.js +14 -18
- package/dist/src/core/patch-manifest-resolve.d.ts +9 -12
- package/dist/src/core/patch-manifest-resolve.js +9 -12
- package/dist/src/core/patch-manifest-validate.d.ts +6 -1
- package/dist/src/core/patch-manifest-validate.js +25 -13
- package/dist/src/core/patch-manifest.d.ts +2 -1
- package/dist/src/core/patch-manifest.js +2 -1
- package/dist/src/core/patch-parse.d.ts +24 -13
- package/dist/src/core/patch-parse.js +19 -14
- package/dist/src/core/patch-policy.d.ts +2 -2
- package/dist/src/core/patch-policy.js +2 -2
- package/dist/src/core/patch-registration-refs.d.ts +7 -8
- package/dist/src/core/patch-registration-refs.js +9 -9
- package/dist/src/core/process-cpu-time.d.ts +12 -0
- package/dist/src/core/process-cpu-time.js +52 -0
- package/dist/src/core/rebase-session.d.ts +38 -26
- package/dist/src/core/rebase-session.js +55 -28
- package/dist/src/core/register-browser-content.js +9 -74
- package/dist/src/core/{register-test-manifest.js → register-browser-test.js} +7 -42
- package/dist/src/core/register-module.js +10 -43
- package/dist/src/core/register-scaffold.d.ts +8 -8
- package/dist/src/core/register-scaffold.js +10 -10
- package/dist/src/core/register-shared-css.d.ts +9 -12
- package/dist/src/core/register-shared-css.js +17 -87
- package/dist/src/core/register-xpcshell-test.d.ts +6 -7
- package/dist/src/core/register-xpcshell-test.js +7 -8
- package/dist/src/core/shared-ftl.js +12 -4
- package/dist/src/core/state-file.d.ts +45 -0
- package/dist/src/core/state-file.js +34 -0
- package/dist/src/core/status-binary.d.ts +18 -11
- package/dist/src/core/status-binary.js +19 -12
- package/dist/src/core/status-classify.d.ts +30 -36
- package/dist/src/core/status-classify.js +21 -26
- package/dist/src/core/test-changed-prefs.d.ts +30 -0
- package/dist/src/core/test-changed-prefs.js +78 -0
- package/dist/src/core/test-harness-crash.d.ts +25 -63
- package/dist/src/core/test-harness-crash.js +121 -142
- package/dist/src/core/test-harness-signature.d.ts +18 -0
- package/dist/src/core/test-harness-signature.js +13 -0
- package/dist/src/core/test-path-scope.d.ts +12 -15
- package/dist/src/core/test-path-scope.js +15 -18
- package/dist/src/core/test-stale-check.d.ts +17 -17
- package/dist/src/core/test-stale-check.js +50 -43
- package/dist/src/core/test-stall-triage.d.ts +51 -0
- package/dist/src/core/test-stall-triage.js +126 -0
- package/dist/src/core/token-category.js +66 -14
- package/dist/src/core/token-coverage.js +9 -9
- package/dist/src/core/token-dark-mode.d.ts +9 -11
- package/dist/src/core/token-dark-mode.js +29 -13
- package/dist/src/core/token-docs.d.ts +2 -0
- package/dist/src/core/token-docs.js +3 -1
- package/dist/src/core/token-manager.d.ts +10 -2
- package/dist/src/core/token-manager.js +17 -8
- package/dist/src/core/token-scaffold.d.ts +7 -7
- package/dist/src/core/token-scaffold.js +7 -7
- package/dist/src/core/token-variant.d.ts +4 -4
- package/dist/src/core/token-variant.js +28 -6
- package/dist/src/core/toolchain-preflight.d.ts +20 -21
- package/dist/src/core/toolchain-preflight.js +28 -31
- package/dist/src/core/tree-guard.js +12 -2
- package/dist/src/core/tree-store.d.ts +41 -27
- package/dist/src/core/tree-store.js +68 -43
- package/dist/src/core/typecheck-shim.d.ts +23 -24
- package/dist/src/core/typecheck-shim.js +23 -24
- package/dist/src/core/typecheck.js +15 -9
- package/dist/src/core/wire-destroy.d.ts +3 -3
- package/dist/src/core/wire-destroy.js +7 -15
- package/dist/src/core/wire-dom-fragment.d.ts +9 -17
- package/dist/src/core/wire-dom-fragment.js +11 -44
- package/dist/src/core/wire-init.d.ts +2 -3
- package/dist/src/core/wire-init.js +8 -19
- package/dist/src/core/wire-subscript.d.ts +2 -3
- package/dist/src/core/wire-subscript.js +4 -13
- package/dist/src/core/wire-utils.d.ts +7 -8
- package/dist/src/core/wire-utils.js +7 -8
- package/dist/src/core/xpcshell-appdir.d.ts +38 -45
- package/dist/src/core/xpcshell-appdir.js +38 -45
- package/dist/src/errors/base.d.ts +37 -16
- package/dist/src/errors/base.js +37 -16
- package/dist/src/errors/build.d.ts +13 -1
- package/dist/src/errors/build.js +14 -2
- package/dist/src/errors/codes.d.ts +16 -0
- package/dist/src/errors/codes.js +16 -0
- package/dist/src/errors/furnace.d.ts +4 -4
- package/dist/src/errors/git.d.ts +6 -10
- package/dist/src/errors/git.js +6 -10
- package/dist/src/errors/patch.d.ts +7 -7
- package/dist/src/errors/patch.js +7 -7
- package/dist/src/errors/rebase.d.ts +4 -4
- package/dist/src/errors/rebase.js +4 -4
- package/dist/src/index.d.ts +7 -1
- package/dist/src/index.js +5 -0
- package/dist/src/types/commands/index.d.ts +2 -2
- package/dist/src/types/commands/options.d.ts +111 -70
- package/dist/src/types/commands/patches.d.ts +24 -34
- package/dist/src/types/commands/project.d.ts +5 -34
- package/dist/src/types/config.d.ts +25 -23
- package/dist/src/types/config.js +6 -1
- package/dist/src/types/furnace.d.ts +37 -13
- package/dist/src/types/furnace.js +19 -1
- package/dist/src/types/index.d.ts +3 -3
- package/dist/src/utils/build-info.d.ts +7 -8
- package/dist/src/utils/build-info.js +12 -11
- package/dist/src/utils/errors.d.ts +6 -8
- package/dist/src/utils/errors.js +6 -8
- package/dist/src/utils/logger.d.ts +27 -10
- package/dist/src/utils/logger.js +28 -16
- package/dist/src/utils/machine-output.d.ts +12 -0
- package/dist/src/utils/machine-output.js +33 -0
- package/dist/src/utils/options.d.ts +31 -14
- package/dist/src/utils/options.js +86 -30
- package/dist/src/utils/paths.d.ts +10 -8
- package/dist/src/utils/paths.js +10 -8
- package/dist/src/utils/platform.d.ts +0 -25
- package/dist/src/utils/platform.js +1 -42
- package/dist/src/utils/process-group.d.ts +9 -4
- package/dist/src/utils/process-group.js +19 -5
- package/dist/src/utils/process.d.ts +42 -7
- package/dist/src/utils/process.js +44 -17
- package/dist/src/utils/ps-duration.d.ts +31 -0
- package/dist/src/utils/ps-duration.js +70 -0
- package/dist/src/utils/regex.d.ts +9 -10
- package/dist/src/utils/regex.js +9 -10
- package/dist/src/utils/validation.d.ts +58 -28
- package/dist/src/utils/validation.js +60 -33
- package/package.json +3 -3
- package/dist/src/commands/doctor/post-rebase-audit.d.ts +0 -2
- package/dist/src/commands/doctor-furnace-manifest-sync.d.ts +0 -18
- package/dist/src/core/manifest-helpers.d.ts +0 -38
- package/dist/src/core/manifest-helpers.js +0 -120
- /package/dist/src/core/{lint-projection.d.ts → patch-lint-projection.d.ts} +0 -0
- /package/dist/src/core/{lint-projection.js → patch-lint-projection.js} +0 -0
- /package/dist/src/core/{register-test-manifest.d.ts → register-browser-test.d.ts} +0 -0
|
@@ -6,17 +6,20 @@ import { getCurrentBranch, getHead, isGitRepository, isMissingHeadError } from '
|
|
|
6
6
|
import { ensureGit } from '../core/git-base.js';
|
|
7
7
|
import { ensureMach, ensurePython } from '../core/mach.js';
|
|
8
8
|
import { countPatches } from '../core/patch-apply.js';
|
|
9
|
-
import {
|
|
9
|
+
import { validatePatchIntegrity } from '../core/patch-manifest.js';
|
|
10
|
+
import { InvalidArgumentError } from '../errors/base.js';
|
|
10
11
|
import { ExitCode } from '../errors/codes.js';
|
|
11
12
|
import { toError } from '../utils/errors.js';
|
|
12
13
|
import { pathExists } from '../utils/fs.js';
|
|
13
14
|
import { error, info, intro, outro, success, warn } from '../utils/logger.js';
|
|
15
|
+
import { addWaitLockOption } from '../utils/options.js';
|
|
14
16
|
import { findExecutable } from '../utils/process.js';
|
|
15
|
-
import { POST_REBASE_AUDIT_CHECK } from './doctor/post-rebase-audit.js';
|
|
16
17
|
import { failure, ok, resolveDoctorSeverity, warning } from './doctor-check-core.js';
|
|
17
18
|
import { EXTERNAL_TOOLCHAIN_DOCTOR_CHECK } from './doctor-external-toolchains.js';
|
|
18
19
|
import { FURNACE_DOCTOR_CHECKS } from './doctor-furnace.js';
|
|
19
20
|
import { ORPHANED_HARNESS_DOCTOR_CHECK } from './doctor-orphaned-harness.js';
|
|
21
|
+
import { PATCH_MANIFEST_CONSISTENCY_CHECK } from './doctor-patch-manifest.js';
|
|
22
|
+
import { POST_REBASE_AUDIT_CHECK } from './doctor-post-rebase-audit.js';
|
|
20
23
|
import { inspectEngineWorkingTree } from './doctor-working-tree.js';
|
|
21
24
|
import { collectPatchQueueHealth } from './verify.js';
|
|
22
25
|
/**
|
|
@@ -236,31 +239,21 @@ const DOCTOR_CHECKS = [
|
|
|
236
239
|
fix: 'Firefox source may be corrupted. Re-download with "fireforge download --force"',
|
|
237
240
|
},
|
|
238
241
|
{
|
|
239
|
-
// `fireforge watch` has an undeclared hard dependency on watchman
|
|
240
|
-
//
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
// watchman should not fail `doctor` outright, but the information
|
|
245
|
-
// needs to be visible ahead of time rather than at the watch-mode
|
|
246
|
-
// failure site.
|
|
242
|
+
// `fireforge watch` has an undeclared hard dependency on watchman, and
|
|
243
|
+
// without a doctor row operators only discover the gap when they try to
|
|
244
|
+
// start watch mode. Warning severity is the right shape: most projects
|
|
245
|
+
// never run watch, so a missing watchman should not fail `doctor`
|
|
246
|
+
// outright, but the information needs to be visible ahead of time.
|
|
247
247
|
name: 'Watchman available',
|
|
248
248
|
run: async () => {
|
|
249
249
|
// Resolve the absolute path so the OK row names what doctor actually
|
|
250
|
-
// found.
|
|
251
|
-
//
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
// subprocess, and surfacing the resolved path makes the discrepancy
|
|
255
|
-
// visible without users having to re-run with a verbose flag.
|
|
250
|
+
// found. A PATH-export discrepancy between the operator's interactive
|
|
251
|
+
// shell and the spawned subprocess otherwise reads as doctor printing
|
|
252
|
+
// "OK" for a binary `which watchman` cannot find; surfacing the
|
|
253
|
+
// resolved path makes the discrepancy visible without a verbose flag.
|
|
256
254
|
const path = await findExecutable('watchman');
|
|
257
255
|
if (path) {
|
|
258
|
-
return {
|
|
259
|
-
name: 'Watchman available',
|
|
260
|
-
passed: true,
|
|
261
|
-
severity: 'ok',
|
|
262
|
-
message: `OK (${path})`,
|
|
263
|
-
};
|
|
256
|
+
return ok('Watchman available', `OK (${path})`);
|
|
264
257
|
}
|
|
265
258
|
return warning('Watchman available', 'watchman is not installed or not on PATH. "fireforge watch" requires it.', 'Install watchman (brew install watchman / dnf install watchman / https://facebook.github.io/watchman/), then re-run doctor.');
|
|
266
259
|
},
|
|
@@ -270,12 +263,7 @@ const DOCTOR_CHECKS = [
|
|
|
270
263
|
name: 'Patches directory exists',
|
|
271
264
|
run: async (ctx) => {
|
|
272
265
|
const patchesExist = await pathExists(ctx.paths.patches);
|
|
273
|
-
return
|
|
274
|
-
name: 'Patches directory exists',
|
|
275
|
-
passed: true,
|
|
276
|
-
severity: 'ok',
|
|
277
|
-
message: patchesExist ? 'OK' : 'No patches/ directory (optional)',
|
|
278
|
-
};
|
|
266
|
+
return ok('Patches directory exists', patchesExist ? 'OK' : 'No patches/ directory (optional)');
|
|
279
267
|
},
|
|
280
268
|
},
|
|
281
269
|
{
|
|
@@ -285,70 +273,10 @@ const DOCTOR_CHECKS = [
|
|
|
285
273
|
return [];
|
|
286
274
|
}
|
|
287
275
|
const patchCount = await countPatches(ctx.paths.patches);
|
|
288
|
-
return {
|
|
289
|
-
name: 'Patches found',
|
|
290
|
-
passed: true,
|
|
291
|
-
severity: 'ok',
|
|
292
|
-
message: `${patchCount} patch${patchCount === 1 ? '' : 'es'} found`,
|
|
293
|
-
};
|
|
294
|
-
},
|
|
295
|
-
},
|
|
296
|
-
{
|
|
297
|
-
name: 'Patch manifest consistency',
|
|
298
|
-
dependsOn: ['fireforge.json is valid'],
|
|
299
|
-
run: async (ctx) => {
|
|
300
|
-
if (!(await pathExists(ctx.paths.patches))) {
|
|
301
|
-
return [];
|
|
302
|
-
}
|
|
303
|
-
const manifestConsistencyIssues = await validatePatchesManifestConsistency(ctx.paths.patches);
|
|
304
|
-
if (manifestConsistencyIssues.length === 0) {
|
|
305
|
-
return ok('Patch manifest consistency');
|
|
306
|
-
}
|
|
307
|
-
if (!ctx.options.repairPatchesManifest) {
|
|
308
|
-
return failure('Patch manifest consistency', manifestConsistencyIssues.map((issue) => issue.message).join(' '), 'Run "fireforge doctor --repair-patches-manifest" to rebuild patches.json from patch files.');
|
|
309
|
-
}
|
|
310
|
-
// Repair stamps sourceEsrVersion into every recovered entry. If the
|
|
311
|
-
// earlier "fireforge.json is valid" check failed, ctx.config is
|
|
312
|
-
// undefined and we must refuse rather than fabricate a fallback —
|
|
313
|
-
// persisting 'unknown' into manifest metadata is hard to reverse
|
|
314
|
-
// and would mislead every later command that reads it.
|
|
315
|
-
if (!ctx.config) {
|
|
316
|
-
return failure('Patch manifest consistency', 'Cannot repair patches.json: fireforge.json could not be loaded, so the Firefox version to stamp into recovered manifest entries is unknown.', 'Fix the fireforge.json errors reported above and re-run "fireforge doctor --repair-patches-manifest".');
|
|
317
|
-
}
|
|
318
|
-
try {
|
|
319
|
-
const repaired = await rebuildPatchesManifest(ctx.paths.patches, ctx.config.firefox.version);
|
|
320
|
-
// 2026-04-21 eval (Finding #17): the repair path silently
|
|
321
|
-
// overwrote useful human-written descriptions on recovered
|
|
322
|
-
// entries, leaving the queue less trustworthy as an audit
|
|
323
|
-
// trail. The rebuilder now returns the list of filenames
|
|
324
|
-
// whose metadata was entirely invented, and we name them
|
|
325
|
-
// explicitly here so the operator knows exactly which
|
|
326
|
-
// patches to review. Names that DID have a preserved entry
|
|
327
|
-
// (only `filesAffected` / ordering drifted) are not flagged.
|
|
328
|
-
if (repaired.recoveredFilenames.length > 0) {
|
|
329
|
-
for (const filename of repaired.recoveredFilenames) {
|
|
330
|
-
// 2026-04-24 eval Finding 6: the repair path used to tell the
|
|
331
|
-
// operator to hand-edit patches.json, which contradicts the
|
|
332
|
-
// README + downstream docs that treat the manifest as
|
|
333
|
-
// FireForge-owned. Point at the existing `re-export` /
|
|
334
|
-
// `export` workflow instead so the fix stays inside the tool:
|
|
335
|
-
// re-exporting the same files with an explicit `--description`
|
|
336
|
-
// overwrites the recovered entry with operator-supplied
|
|
337
|
-
// metadata and supersedes the mtime-based createdAt stamp.
|
|
338
|
-
warn(`Recovered manifest entry for ${filename} with generic description and mtime-based createdAt. ` +
|
|
339
|
-
'Re-export the affected files with `fireforge re-export <filename> --description "<your description>"` ' +
|
|
340
|
-
'(or `fireforge export <paths...> --name <name> --category <category> --description "<your description>"`) ' +
|
|
341
|
-
'to overwrite the reconstructed metadata, or accept the generic description if the original text is not recoverable. ' +
|
|
342
|
-
'Avoid hand-editing patches.json — FireForge owns that file and will regenerate it on the next manifest consistency pass.');
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
return warning('Patch manifest consistency', `Rebuilt patches.json from ${repaired.manifest.patches.length} patch${repaired.manifest.patches.length === 1 ? '' : 'es'}${repaired.recoveredFilenames.length > 0 ? ` (${repaired.recoveredFilenames.length} with reconstructed metadata — see warnings above)` : ''}. Review recovered metadata before release.`);
|
|
346
|
-
}
|
|
347
|
-
catch (err) {
|
|
348
|
-
return failure('Patch manifest consistency', toError(err).message, 'Repair failed. Fix the underlying patch metadata issue and retry the doctor command.');
|
|
349
|
-
}
|
|
276
|
+
return ok('Patches found', `${patchCount} patch${patchCount === 1 ? '' : 'es'} found`);
|
|
350
277
|
},
|
|
351
278
|
},
|
|
279
|
+
PATCH_MANIFEST_CONSISTENCY_CHECK,
|
|
352
280
|
{
|
|
353
281
|
name: 'Patch integrity',
|
|
354
282
|
skipIf: (ctx) => !ctx.engineExists,
|
|
@@ -400,7 +328,7 @@ export const DOCTOR_CHECK_ORDER = DOCTOR_CHECKS.map((check) => check.name);
|
|
|
400
328
|
* @param checks - The check results to display
|
|
401
329
|
* @returns The exit code reflecting the overall result
|
|
402
330
|
*/
|
|
403
|
-
export function reportDoctorResults(checks) {
|
|
331
|
+
export function reportDoctorResults(checks, mutations = []) {
|
|
404
332
|
info('');
|
|
405
333
|
let passedCount = 0;
|
|
406
334
|
let warningCount = 0;
|
|
@@ -427,6 +355,16 @@ export function reportDoctorResults(checks) {
|
|
|
427
355
|
}
|
|
428
356
|
}
|
|
429
357
|
info('');
|
|
358
|
+
// Printed in every branch, including the failing one: a repair that wrote
|
|
359
|
+
// and then met an unrelated failing check must not be reported as a run
|
|
360
|
+
// where nothing happened.
|
|
361
|
+
if (mutations.length > 0) {
|
|
362
|
+
warn('Repairs applied this run:');
|
|
363
|
+
for (const mutation of mutations) {
|
|
364
|
+
warn(` ${mutation}`);
|
|
365
|
+
}
|
|
366
|
+
info('');
|
|
367
|
+
}
|
|
430
368
|
if (failedCount === 0 && warningCount === 0) {
|
|
431
369
|
outro(`All ${passedCount} checks passed!`);
|
|
432
370
|
}
|
|
@@ -439,12 +377,38 @@ export function reportDoctorResults(checks) {
|
|
|
439
377
|
}
|
|
440
378
|
return ExitCode.SUCCESS;
|
|
441
379
|
}
|
|
380
|
+
/**
|
|
381
|
+
* Rejects repair-flag combinations that cannot mean what they say.
|
|
382
|
+
*
|
|
383
|
+
* The two manifest repairs are different operations on the same file, and
|
|
384
|
+
* `--dry-run` with nothing to project is a flag that silently does nothing —
|
|
385
|
+
* the shape most likely to be mistaken for a preview that reported "no
|
|
386
|
+
* changes".
|
|
387
|
+
*/
|
|
388
|
+
function assertRepairOptions(options) {
|
|
389
|
+
if (options.repairPatchesManifest === true && options.repairFilesAffected === true) {
|
|
390
|
+
throw new InvalidArgumentError('--repair-patches-manifest and --repair-files-affected are mutually exclusive. ' +
|
|
391
|
+
'Use --repair-files-affected for filesAffected drift; use --repair-patches-manifest ' +
|
|
392
|
+
'when rows are missing, untracked or duplicated.', '--repair-files-affected');
|
|
393
|
+
}
|
|
394
|
+
if (options.allowMetadataLoss === true && options.repairPatchesManifest !== true) {
|
|
395
|
+
throw new InvalidArgumentError('--allow-metadata-loss only applies to --repair-patches-manifest.', '--allow-metadata-loss');
|
|
396
|
+
}
|
|
397
|
+
if (options.dryRun === true &&
|
|
398
|
+
options.repairPatchesManifest !== true &&
|
|
399
|
+
options.repairFilesAffected !== true) {
|
|
400
|
+
throw new InvalidArgumentError('--dry-run needs a manifest repair to project. Pass it with ' +
|
|
401
|
+
'--repair-patches-manifest or --repair-files-affected. ' +
|
|
402
|
+
'(--repair-furnace has no dry-run: it reconciles engine state, not a manifest.)', '--dry-run');
|
|
403
|
+
}
|
|
404
|
+
}
|
|
442
405
|
/**
|
|
443
406
|
* Runs the doctor command to diagnose issues.
|
|
444
407
|
* @param projectRoot - Root directory of the project
|
|
445
408
|
*/
|
|
446
409
|
export async function doctorCommand(projectRoot, options = {}) {
|
|
447
410
|
intro('FireForge Doctor');
|
|
411
|
+
assertRepairOptions(options);
|
|
448
412
|
const paths = getProjectPaths(projectRoot);
|
|
449
413
|
const state = await loadState(projectRoot);
|
|
450
414
|
const engineExists = await pathExists(paths.engine);
|
|
@@ -458,24 +422,28 @@ export async function doctorCommand(projectRoot, options = {}) {
|
|
|
458
422
|
config: undefined,
|
|
459
423
|
furnaceConfigExists: furnaceConfigExistsFlag,
|
|
460
424
|
furnaceConfig: undefined,
|
|
425
|
+
mutations: [],
|
|
461
426
|
};
|
|
462
427
|
const checks = [];
|
|
463
428
|
for (const definition of DOCTOR_CHECKS) {
|
|
464
429
|
checks.push(...(await executeCheck(definition, ctx)));
|
|
465
430
|
}
|
|
466
|
-
const exitCode = reportDoctorResults(checks);
|
|
431
|
+
const exitCode = reportDoctorResults(checks, ctx.mutations);
|
|
467
432
|
return { checks, exitCode };
|
|
468
433
|
}
|
|
469
434
|
/** Registers the doctor command on the CLI program. */
|
|
470
435
|
export function registerDoctor(program, { getProjectRoot, withErrorHandling }) {
|
|
471
|
-
program
|
|
436
|
+
const command = program
|
|
472
437
|
.command('doctor')
|
|
473
438
|
.description('Diagnose project issues')
|
|
474
|
-
.option('--repair-patches-manifest', 'Rebuild patches/patches.json from the current patch files before reporting results')
|
|
439
|
+
.option('--repair-patches-manifest', 'Rebuild patches/patches.json from the current patch files before reporting results. Only filesAffected and order are recomputed; every other field on an existing entry is preserved')
|
|
440
|
+
.option('--repair-files-affected', 'Repair only the filesAffected lists that disagree with their patch body, leaving every other manifest field untouched')
|
|
441
|
+
.option('--allow-metadata-loss', 'With --repair-patches-manifest, rebuild even when patches.json cannot be parsed — every descriptive field on every entry is then reinvented')
|
|
442
|
+
.option('--dry-run', 'Show what a requested repair would change without writing anything')
|
|
475
443
|
.option('--repair-furnace', 'Reconcile furnace state: clear stale furnace-state.json entries, re-run furnace apply to fix engine drift, and clear the pending-repair marker set by a failed preview teardown')
|
|
476
444
|
.option('--clear-resolution', 'Clear stale pendingResolution state after the patch queue health check reports no errors')
|
|
477
|
-
.option('--post-rebase-audit', 'Check common registration surfaces after a Firefox source rebase')
|
|
478
|
-
|
|
445
|
+
.option('--post-rebase-audit', 'Check common registration surfaces after a Firefox source rebase');
|
|
446
|
+
addWaitLockOption(command).action(withErrorHandling(async (options) => {
|
|
479
447
|
const result = await doctorCommand(getProjectRoot(), options);
|
|
480
448
|
if (result.exitCode !== 0) {
|
|
481
449
|
process.exitCode = result.exitCode;
|
|
@@ -76,13 +76,12 @@ async function cleanPatchTouchedFiles(engineDir, patchesDir, preExistingDirty) {
|
|
|
76
76
|
}
|
|
77
77
|
/**
|
|
78
78
|
* Prints a one-line nudge pointing at `fireforge import` when the project
|
|
79
|
-
* carries a non-empty patch queue but the just-downloaded engine has not
|
|
80
|
-
*
|
|
79
|
+
* carries a non-empty patch queue but the just-downloaded engine has not yet
|
|
80
|
+
* had any patches applied. The post-download spinner closes with
|
|
81
81
|
* "Patch-touched files already match baseline" because a fresh tree IS at
|
|
82
|
-
* baseline,
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* stay quiet.
|
|
82
|
+
* baseline, which reads as "patches are restored" and invites skipping the
|
|
83
|
+
* import step. Suppressed when patches/ is missing or the manifest is empty
|
|
84
|
+
* so unconfigured projects stay quiet.
|
|
86
85
|
*/
|
|
87
86
|
async function noteUnappliedPatches(patchesDir) {
|
|
88
87
|
if (!(await pathExists(patchesDir)))
|
|
@@ -97,12 +96,8 @@ async function noteUnappliedPatches(patchesDir) {
|
|
|
97
96
|
* Stops `restoreSpinner` with a message that reflects what actually
|
|
98
97
|
* happened. Three branches: empty queue → explicit no-op; queue present but
|
|
99
98
|
* nothing dirty → "already clean"; queue with dirty files → the usual
|
|
100
|
-
* "Patch-touched files restored" success line.
|
|
101
|
-
*
|
|
102
|
-
* Before 0.16.0 the spinner always closed with "Patch-touched files
|
|
103
|
-
* restored", so a fresh project with zero patches saw a claim of restore
|
|
104
|
-
* work that had not happened — misleading and easy to mistake for a
|
|
105
|
-
* silent retry.
|
|
99
|
+
* "Patch-touched files restored" success line. Always closing with the third
|
|
100
|
+
* claims restore work that did not happen on a project with zero patches.
|
|
106
101
|
*/
|
|
107
102
|
function closeRestoreSpinner(restoreSpinner, result) {
|
|
108
103
|
if (!result.hadQueue) {
|
|
@@ -192,12 +187,11 @@ async function downloadAndExtractFirefox(args) {
|
|
|
192
187
|
}
|
|
193
188
|
}
|
|
194
189
|
/**
|
|
195
|
-
* Prints the major-version-hop toolchain nudge when this download moved
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
* re-downloads.
|
|
190
|
+
* Prints the major-version-hop toolchain nudge when this download moved the
|
|
191
|
+
* engine across a Firefox MAJOR version — the first post-hop build otherwise
|
|
192
|
+
* dies in `mach configure` on a moved cbindgen minimum with nothing in the
|
|
193
|
+
* download output suggesting `fireforge bootstrap`. Quiet on first downloads
|
|
194
|
+
* and same-major re-downloads.
|
|
201
195
|
*/
|
|
202
196
|
function noteMajorVersionHop(previousVersion, version) {
|
|
203
197
|
const hopNotice = formatMajorVersionHopNotice(previousVersion, version);
|
|
@@ -207,9 +201,9 @@ function noteMajorVersionHop(previousVersion, version) {
|
|
|
207
201
|
}
|
|
208
202
|
async function initializeDownloadedEngine(args) {
|
|
209
203
|
const { projectRoot, patchesDir, version, previousVersion, engineDir, replacementActivated, backupEngineDir, } = args;
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
//
|
|
204
|
+
// The git indexing phase of `download` can block for minutes on a ~600 MB
|
|
205
|
+
// Firefox tree. Emit a one-line heads-up banner before the spinner starts
|
|
206
|
+
// so CI logs show the expected duration.
|
|
213
207
|
try {
|
|
214
208
|
info('Indexing downloaded source into git (one-time; typically 3–5 minutes on a ~600 MB Firefox tree)...');
|
|
215
209
|
info('Git phase: initializing/resetting source repository metadata.');
|
|
@@ -280,11 +274,11 @@ export async function downloadCommand(projectRoot, options) {
|
|
|
280
274
|
info(`Firefox version: ${version}`);
|
|
281
275
|
await checkDiskSpace(projectRoot, 5 * 1024 * 1024 * 1024, warn);
|
|
282
276
|
// A legitimate holder of this lock runs for 10+ minutes (download +
|
|
283
|
-
// extract + git indexing).
|
|
284
|
-
//
|
|
285
|
-
//
|
|
286
|
-
//
|
|
287
|
-
//
|
|
277
|
+
// extract + git indexing). A 30 s timeout fails the second invocation with
|
|
278
|
+
// "remove the lock directory if it is stale" advice while the lock is
|
|
279
|
+
// actively held — dangerous guidance mid-extraction. Wait generously
|
|
280
|
+
// instead: a dead holder is reaped within seconds by the PID-based stale
|
|
281
|
+
// probe, so a long timeout only ever waits on real work.
|
|
288
282
|
const downloadLockOptions = {
|
|
289
283
|
timeoutMs: 30 * 60_000,
|
|
290
284
|
onTimeoutMessage: 'Timed out waiting for another FireForge download to finish. ' +
|
|
@@ -329,11 +323,9 @@ export async function downloadCommand(projectRoot, options) {
|
|
|
329
323
|
// The non-TTY spinner fallback in `src/utils/logger.ts`
|
|
330
324
|
// already calls `p.log.step(msg)` from `message()`, so
|
|
331
325
|
// forwarding the progress message is the single authority
|
|
332
|
-
// in both TTY and non-TTY modes.
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
// twice in CI logs (once from the fallback, once from
|
|
336
|
-
// the explicit call).
|
|
326
|
+
// in both TTY and non-TTY modes. Calling `step(message)`
|
|
327
|
+
// explicitly here as well prints the same line twice in
|
|
328
|
+
// CI logs.
|
|
337
329
|
onProgress: (message) => {
|
|
338
330
|
resumeSpinner.message(message);
|
|
339
331
|
},
|
|
@@ -359,17 +351,14 @@ export async function downloadCommand(projectRoot, options) {
|
|
|
359
351
|
}
|
|
360
352
|
catch (error) {
|
|
361
353
|
resumeSpinner.error('Resume failed');
|
|
362
|
-
// Preserve the underlying cause so the
|
|
363
|
-
// resume failed (timeout, permission denied, corrupted
|
|
364
|
-
// disk full
|
|
365
|
-
// exists" story.
|
|
366
|
-
//
|
|
367
|
-
|
|
368
|
-
verbose
|
|
369
|
-
|
|
370
|
-
verbose(cause.stack);
|
|
371
|
-
}
|
|
372
|
-
throw new PartialEngineExistsError(paths.engine, cause);
|
|
354
|
+
// Preserve the underlying cause so the operator sees *why*
|
|
355
|
+
// the resume failed (timeout, permission denied, corrupted
|
|
356
|
+
// object, disk full) instead of only the generic "partial
|
|
357
|
+
// engine exists" story. The cause reaches them through the
|
|
358
|
+
// CLI boundary's chain walk under --verbose (src/cli.ts),
|
|
359
|
+
// which is what makes PartialEngineExistsError's "re-run
|
|
360
|
+
// with --verbose" promise true.
|
|
361
|
+
throw new PartialEngineExistsError(paths.engine, toError(error));
|
|
373
362
|
}
|
|
374
363
|
}
|
|
375
364
|
// Re-throw unexpected git errors (corrupted objects, permission
|
|
@@ -377,12 +366,7 @@ export async function downloadCommand(projectRoot, options) {
|
|
|
377
366
|
// both narratives: "we detected a partial engine and attempted
|
|
378
367
|
// resume" AND the underlying git failure. Without the wrap the
|
|
379
368
|
// raw git error loses the context that resume was in flight.
|
|
380
|
-
|
|
381
|
-
verbose(`Partial-engine probe failed with unexpected error: ${cause.message}`);
|
|
382
|
-
if (cause.stack) {
|
|
383
|
-
verbose(cause.stack);
|
|
384
|
-
}
|
|
385
|
-
throw new PartialEngineExistsError(paths.engine, cause);
|
|
369
|
+
throw new PartialEngineExistsError(paths.engine, toError(error));
|
|
386
370
|
}
|
|
387
371
|
}
|
|
388
372
|
throw new EngineExistsError(paths.engine);
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import { isBrandingManagedPath } from '../core/branding.js';
|
|
4
4
|
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
5
|
+
import { stdioIsInteractive } from '../core/destructive.js';
|
|
6
|
+
import { assertEngineGitReady } from '../core/engine-precondition.js';
|
|
5
7
|
import { collectFurnaceManagedPrefixes, furnaceConfigExists, loadFurnaceConfig, } from '../core/furnace-config.js';
|
|
6
|
-
import { hasChanges
|
|
8
|
+
import { hasChanges } from '../core/git.js';
|
|
7
9
|
import { getAllDiff, getDiffForFilesAgainstHead } from '../core/git-diff.js';
|
|
8
10
|
import { expandUntrackedDirectoryEntries, getWorkingTreeStatus } from '../core/git-status.js';
|
|
9
11
|
import { extractAffectedFiles } from '../core/patch-apply.js';
|
|
@@ -14,7 +16,7 @@ import { buildPatchSourceMetadata } from '../core/patch-source-metadata.js';
|
|
|
14
16
|
import { GeneralError } from '../errors/base.js';
|
|
15
17
|
import { ensureDir, pathExists } from '../utils/fs.js';
|
|
16
18
|
import { info, intro, outro, spinner } from '../utils/logger.js';
|
|
17
|
-
import { pickDefined } from '../utils/options.js';
|
|
19
|
+
import { addWaitLockOption, pickDefined, resolveWaitLockSeconds } from '../utils/options.js';
|
|
18
20
|
import { renderDryRunPreview } from './export-flow.js';
|
|
19
21
|
import { autoFixLicenseHeaders, promptExportPatchMetadata, runPatchLint, runSupersedeAndOverlapGates, } from './export-shared.js';
|
|
20
22
|
async function checkBrandingManagedFiles(paths, config) {
|
|
@@ -46,9 +48,7 @@ async function resolveFurnaceExclusionPolicy(paths, projectRoot, excludeFurnace)
|
|
|
46
48
|
// Expand collapsed `?? dir/` entries before matching against Furnace
|
|
47
49
|
// prefixes — otherwise a Furnace-introduced directory slips past the
|
|
48
50
|
// filter and later lands in the non-Furnace path list that feeds the
|
|
49
|
-
// aggregate diff, where `getDiffForFilesAgainstHead` crashes with
|
|
50
|
-
// EISDIR (eval finding: export-all unusable on a fresh project with
|
|
51
|
-
// Furnace scaffolding).
|
|
51
|
+
// aggregate diff, where `getDiffForFilesAgainstHead` crashes with EISDIR.
|
|
52
52
|
const rawStatus = await getWorkingTreeStatus(paths.engine);
|
|
53
53
|
const changedFiles = await expandUntrackedDirectoryEntries(paths.engine, rawStatus);
|
|
54
54
|
const furnaceManagedFiles = changedFiles
|
|
@@ -66,20 +66,19 @@ async function resolveFurnaceExclusionPolicy(paths, projectRoot, excludeFurnace)
|
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
68
|
* Refuses the export when the resulting patch would register furnace
|
|
69
|
-
* component source files it does not itself carry.
|
|
70
|
-
*
|
|
71
|
-
* `
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* before exporting.
|
|
69
|
+
* component source files it does not itself carry.
|
|
70
|
+
*
|
|
71
|
+
* `export-all --exclude-furnace` after `furnace create --localized
|
|
72
|
+
* --with-tests` otherwise produces a patch that adds
|
|
73
|
+
* `toolkit/content/widgets/<tag>/*` via jar.mn / customElements.js / locale
|
|
74
|
+
* jar.mn while excluding the component source files themselves — a
|
|
75
|
+
* structurally broken queue that `fireforge verify` stays silent about. The
|
|
76
|
+
* operator is asked to either include the component sources (skip
|
|
77
|
+
* `--exclude-furnace`) or revert the furnace changes before exporting.
|
|
79
78
|
*
|
|
80
79
|
* The check runs against the synthesised patch body before
|
|
81
|
-
* `commitExportedPatch` writes anything, so no broken patch is left on
|
|
82
|
-
*
|
|
80
|
+
* `commitExportedPatch` writes anything, so no broken patch is left on disk
|
|
81
|
+
* when the refusal fires.
|
|
83
82
|
*/
|
|
84
83
|
async function checkDanglingFurnaceRegistrations(projectRoot, diff, furnaceExcluded) {
|
|
85
84
|
if (furnaceExcluded.size === 0)
|
|
@@ -171,14 +170,7 @@ export async function exportAllCommand(projectRoot, options = {}) {
|
|
|
171
170
|
const isDryRun = options.dryRun === true;
|
|
172
171
|
intro(isDryRun ? 'FireForge Export All (dry run)' : 'FireForge Export All');
|
|
173
172
|
const paths = getProjectPaths(projectRoot);
|
|
174
|
-
|
|
175
|
-
if (!(await pathExists(paths.engine))) {
|
|
176
|
-
throw new GeneralError('Firefox source not found. Run "fireforge download" first.');
|
|
177
|
-
}
|
|
178
|
-
// Check if it's a git repository
|
|
179
|
-
if (!(await isGitRepository(paths.engine))) {
|
|
180
|
-
throw new GeneralError('Engine directory is not a git repository. Run "fireforge download" to initialize.');
|
|
181
|
-
}
|
|
173
|
+
await assertEngineGitReady(paths.engine);
|
|
182
174
|
// Check for changes
|
|
183
175
|
if (!(await hasChanges(paths.engine))) {
|
|
184
176
|
info('No changes to export');
|
|
@@ -228,13 +220,13 @@ export async function exportAllCommand(projectRoot, options = {}) {
|
|
|
228
220
|
// the aggregate would newly create, so it runs here instead of alongside
|
|
229
221
|
// the branding / furnace guards that operate on the raw status list.
|
|
230
222
|
await checkDuplicateNewFileCreations(paths, diff);
|
|
231
|
-
// Dangling-furnace-registration preflight
|
|
232
|
-
//
|
|
233
|
-
//
|
|
234
|
-
//
|
|
223
|
+
// Dangling-furnace-registration preflight. Runs after the diff is
|
|
224
|
+
// assembled so it can inspect the exact hunks the operator is about to
|
|
225
|
+
// land, and BEFORE any write so a refusal leaves the patches directory
|
|
226
|
+
// untouched.
|
|
235
227
|
await checkDanglingFurnaceRegistrations(projectRoot, diff, furnaceExcluded);
|
|
236
228
|
// Check for non-interactive mode
|
|
237
|
-
const isInteractive =
|
|
229
|
+
const isInteractive = stdioIsInteractive();
|
|
238
230
|
// Auto-fix missing license headers on new files (interactive only;
|
|
239
231
|
// report-only under --dry-run so the preview never mutates engine/)
|
|
240
232
|
const headersAdded = await autoFixLicenseHeaders(paths.engine, diff, config, isInteractive, isDryRun);
|
|
@@ -308,6 +300,10 @@ export async function exportAllCommand(projectRoot, options = {}) {
|
|
|
308
300
|
config,
|
|
309
301
|
policyCommand: 'export-all',
|
|
310
302
|
forceUnsafe: options.forceUnsafe === true,
|
|
303
|
+
lockOptions: {
|
|
304
|
+
waitLockSeconds: resolveWaitLockSeconds(options.waitLock),
|
|
305
|
+
command: 'export-all',
|
|
306
|
+
},
|
|
311
307
|
});
|
|
312
308
|
for (const oldPatch of superseded) {
|
|
313
309
|
info(`Superseded: ${oldPatch.filename}`);
|
|
@@ -326,7 +322,7 @@ export async function exportAllCommand(projectRoot, options = {}) {
|
|
|
326
322
|
}
|
|
327
323
|
/** Registers the export-all command on the CLI program. */
|
|
328
324
|
export function registerExportAll(program, { getProjectRoot, withErrorHandling }) {
|
|
329
|
-
program
|
|
325
|
+
const command = program
|
|
330
326
|
.command('export-all')
|
|
331
327
|
.description('Export all changes as a patch')
|
|
332
328
|
.option('--name <name>', 'Name for the patch')
|
|
@@ -337,8 +333,8 @@ export function registerExportAll(program, { getProjectRoot, withErrorHandling }
|
|
|
337
333
|
.option('--exclude-furnace', 'Export the non-Furnace subset of the aggregate diff instead of refusing when Furnace-managed files are modified. Furnace-managed files are still deployed by "fireforge furnace apply"; this flag only changes whether export-all aborts or filters in their presence.')
|
|
338
334
|
.option('--allow-overlap', 'Acknowledge cross-patch ownership overlap with non-superseded patches (the resulting queue fails verify). Does not bypass the new-file creation guard — two patches creating the same path is structurally unrecoverable, so that case still refuses regardless of this flag.')
|
|
339
335
|
.option('--dry-run', 'Print the export-all plan (filename, metadata, files affected, supersede preview) without writing anything to patches/. Lint still runs so the operator sees the same lint output a real run would produce.')
|
|
340
|
-
.option('--force-unsafe', 'Bypass force-mode patchPolicy refusals')
|
|
341
|
-
|
|
336
|
+
.option('--force-unsafe', 'Bypass force-mode patchPolicy refusals');
|
|
337
|
+
addWaitLockOption(command).action(withErrorHandling(async (options) => {
|
|
342
338
|
const { category, ...rest } = options;
|
|
343
339
|
await exportAllCommand(getProjectRoot(), {
|
|
344
340
|
...pickDefined(rest),
|
|
@@ -195,12 +195,12 @@ export async function projectPlacementForLint(patchesDir, plan, diff, config) {
|
|
|
195
195
|
return entry;
|
|
196
196
|
return { ...entry, filename: rename.newFilename, order: rename.newOrder };
|
|
197
197
|
});
|
|
198
|
-
// Synthetic entry for the pending patch, populated with both its
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
//
|
|
198
|
+
// Synthetic entry for the pending patch, populated with both its new-file
|
|
199
|
+
// content AND its added-line content for files it modifies, so the
|
|
200
|
+
// forward-import rule can inspect imports the patch *itself* authors —
|
|
201
|
+
// whether they live in a brand-new file or are added to an existing one.
|
|
202
|
+
// Leaving either map empty lets a patch land before one of its own
|
|
203
|
+
// dependencies and still pass the gate.
|
|
204
204
|
projectedEntries.push({
|
|
205
205
|
filename: plan.newFilename,
|
|
206
206
|
order: plan.insertionOrder,
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
/**
|
|
3
3
|
* Attribution of projected placement lint errors, split out of
|
|
4
|
-
* export-flow.ts for the line budget.
|
|
5
|
-
* merged queue — the renumbered existing patches plus a synthetic entry
|
|
6
|
-
* for the pending patch — so its errors used to render as one flat list
|
|
7
|
-
* that read as though the NEW patch caused every one of them. A
|
|
8
|
-
* renumbering that re-evaluates pre-existing staged relationships (455
|
|
9
|
-
* importing a module created by 456, legal only at their current
|
|
10
|
-
* ordinals) then sent the operator auditing a perfectly fine export.
|
|
4
|
+
* export-flow.ts for the line budget.
|
|
11
5
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
6
|
+
* A placement projection lints one merged queue — the renumbered existing
|
|
7
|
+
* patches plus a synthetic entry for the pending patch — so a flat error
|
|
8
|
+
* list reads as though the NEW patch caused every one of them. A renumbering
|
|
9
|
+
* that re-evaluates pre-existing staged relationships (patch 455 importing a
|
|
10
|
+
* module created by 456, legal only at their current ordinals) then sends
|
|
11
|
+
* the operator auditing a perfectly fine export.
|
|
12
|
+
*
|
|
13
|
+
* Consumed by export-flow.ts; no top-level registrar is exported and none is
|
|
14
|
+
* wanted.
|
|
14
15
|
*/
|
|
15
16
|
import { formatPatchLintIssue } from '../core/patch-lint.js';
|
|
16
17
|
/**
|
|
@@ -70,10 +70,10 @@ export async function gatePlacementPlan(args) {
|
|
|
70
70
|
const summary = placementSummary(placementPlan);
|
|
71
71
|
const renameCount = placementPlan.renameMap.size;
|
|
72
72
|
// Route through confirmDestructive when the operation is destructive
|
|
73
|
-
// enough to warrant a prompt (more than one rename) OR when the user
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
73
|
+
// enough to warrant a prompt (more than one rename) OR when the user asked
|
|
74
|
+
// for a dry-run. The dry-run branch must always print the placement
|
|
75
|
+
// summary; otherwise single-rename and no-rename dry-runs exit silently
|
|
76
|
+
// with no filename or projected layout.
|
|
77
77
|
if (renameCount > 1 || isDryRun) {
|
|
78
78
|
s.stop();
|
|
79
79
|
const decision = await confirmDestructive({
|
|
@@ -89,7 +89,7 @@ export async function gatePlacementPlan(args) {
|
|
|
89
89
|
outro('Dry run complete — no changes made');
|
|
90
90
|
return 'stop';
|
|
91
91
|
}
|
|
92
|
-
if (decision === '
|
|
92
|
+
if (decision === 'declined') {
|
|
93
93
|
outro('Export cancelled');
|
|
94
94
|
return 'stop';
|
|
95
95
|
}
|