@hominis/fireforge 0.44.4 → 0.46.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.
- package/CHANGELOG.md +120 -0
- package/dist/bin/fireforge.js +6 -0
- package/dist/build-info.json +4 -4
- package/dist/src/cli-usage-error.d.ts +46 -0
- package/dist/src/cli-usage-error.js +81 -0
- package/dist/src/cli.d.ts +5 -1
- package/dist/src/cli.js +20 -21
- package/dist/src/commands/bootstrap.js +1 -2
- package/dist/src/commands/build.js +15 -6
- package/dist/src/commands/destructive-decision.d.ts +19 -0
- package/dist/src/commands/destructive-decision.js +31 -0
- package/dist/src/commands/doctor-check-core.d.ts +0 -6
- package/dist/src/commands/doctor-check-core.js +0 -8
- package/dist/src/commands/doctor-orphaned-harness.d.ts +0 -6
- package/dist/src/commands/doctor-orphaned-harness.js +4 -12
- package/dist/src/commands/doctor-post-rebase-audit.js +1 -1
- package/dist/src/commands/doctor-source-pin.d.ts +29 -0
- package/dist/src/commands/doctor-source-pin.js +81 -0
- package/dist/src/commands/doctor.d.ts +2 -0
- package/dist/src/commands/doctor.js +9 -8
- package/dist/src/commands/download.js +35 -24
- package/dist/src/commands/export-all.js +21 -4
- package/dist/src/commands/export-flow.js +15 -60
- package/dist/src/commands/export-placement-conflicts.js +3 -3
- package/dist/src/commands/export-placement-gate.js +2 -8
- package/dist/src/commands/export-placement-policy.js +3 -5
- package/dist/src/commands/export-shared.d.ts +42 -23
- package/dist/src/commands/export-shared.js +4 -19
- package/dist/src/commands/export.js +10 -1
- package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
- package/dist/src/commands/furnace/authoring-config.js +19 -0
- package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
- package/dist/src/commands/furnace/create-browser-test.js +3 -8
- package/dist/src/commands/furnace/create-dry-run.js +2 -5
- package/dist/src/commands/furnace/create-templates.d.ts +17 -1
- package/dist/src/commands/furnace/create-templates.js +2 -1
- package/dist/src/commands/furnace/create.js +25 -19
- package/dist/src/commands/furnace/deploy.js +11 -2
- package/dist/src/commands/furnace/init.js +2 -1
- package/dist/src/commands/furnace/override.js +28 -19
- package/dist/src/commands/furnace/refresh.js +9 -1
- package/dist/src/commands/furnace/remove-mutations.d.ts +6 -7
- package/dist/src/commands/furnace/remove-mutations.js +0 -1
- package/dist/src/commands/furnace/remove.js +2 -6
- package/dist/src/commands/furnace/rename-browser-test.js +1 -7
- package/dist/src/commands/furnace/rename.js +29 -29
- package/dist/src/commands/furnace/status.js +12 -9
- package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
- package/dist/src/commands/furnace/test-file-name.js +39 -0
- package/dist/src/commands/furnace/validation-output.d.ts +21 -9
- package/dist/src/commands/furnace/validation-output.js +3 -8
- package/dist/src/commands/import.js +2 -9
- package/dist/src/commands/lint-per-patch.d.ts +2 -2
- package/dist/src/commands/lint-per-patch.js +19 -4
- package/dist/src/commands/lint-per-run-checkjs.d.ts +2 -2
- package/dist/src/commands/lint.d.ts +2 -1
- package/dist/src/commands/lint.js +1 -1
- package/dist/src/commands/manifest.js +1 -1
- package/dist/src/commands/package.js +2 -4
- package/dist/src/commands/patch/compact.js +14 -15
- package/dist/src/commands/patch/delete.js +15 -25
- package/dist/src/commands/patch/entry-projection.d.ts +13 -0
- package/dist/src/commands/patch/entry-projection.js +24 -0
- package/dist/src/commands/patch/lint-ignore.d.ts +0 -8
- package/dist/src/commands/patch/lint-ignore.js +19 -29
- package/dist/src/commands/patch/move-files-create.js +24 -10
- package/dist/src/commands/patch/move-files-into.js +38 -37
- package/dist/src/commands/patch/move-files.js +4 -13
- package/dist/src/commands/patch/rename.js +27 -41
- package/dist/src/commands/patch/reorder.js +25 -38
- package/dist/src/commands/patch/split-plan.d.ts +4 -5
- package/dist/src/commands/patch/split-plan.js +4 -11
- package/dist/src/commands/patch/split.js +45 -43
- package/dist/src/commands/patch/staged-dependency.js +16 -22
- package/dist/src/commands/patch/tier.js +15 -25
- package/dist/src/commands/patch-rename-map.d.ts +19 -0
- package/dist/src/commands/patch-rename-map.js +36 -0
- package/dist/src/commands/pending-resolution.d.ts +15 -0
- package/dist/src/commands/pending-resolution.js +25 -0
- package/dist/src/commands/re-export-adjacent.d.ts +32 -2
- package/dist/src/commands/re-export-adjacent.js +51 -10
- package/dist/src/commands/re-export-bulk-scan.js +6 -10
- package/dist/src/commands/re-export-drift.d.ts +8 -0
- package/dist/src/commands/re-export-drift.js +65 -4
- package/dist/src/commands/re-export-files.d.ts +2 -3
- package/dist/src/commands/re-export-files.js +38 -32
- package/dist/src/commands/re-export-lint.d.ts +3 -4
- package/dist/src/commands/re-export-lint.js +9 -2
- package/dist/src/commands/re-export-options.js +19 -5
- package/dist/src/commands/re-export-register.js +5 -1
- package/dist/src/commands/re-export-scan.js +8 -8
- package/dist/src/commands/re-export-single.d.ts +22 -3
- package/dist/src/commands/re-export-single.js +19 -11
- package/dist/src/commands/re-export.js +44 -27
- package/dist/src/commands/rebase/abort.js +3 -8
- package/dist/src/commands/rebase/continue.js +12 -12
- package/dist/src/commands/rebase/index.js +2 -2
- package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
- package/dist/src/commands/rebase/patch-loop.js +2 -7
- package/dist/src/commands/resolve.js +18 -10
- package/dist/src/commands/run.d.ts +0 -13
- package/dist/src/commands/run.js +12 -25
- package/dist/src/commands/setup-support.d.ts +16 -4
- package/dist/src/commands/setup-support.js +16 -16
- package/dist/src/commands/setup.js +3 -9
- package/dist/src/commands/source.js +8 -1
- package/dist/src/commands/status-check.js +2 -2
- package/dist/src/commands/status-output.js +2 -2
- package/dist/src/commands/status.js +3 -4
- package/dist/src/commands/test-build-phase.d.ts +2 -2
- package/dist/src/commands/test-build-phase.js +33 -8
- package/dist/src/commands/test-diagnose.js +14 -5
- package/dist/src/commands/test-modes.d.ts +2 -2
- package/dist/src/commands/test-modes.js +0 -1
- package/dist/src/commands/test-register.js +6 -3
- package/dist/src/commands/test-run.js +29 -21
- package/dist/src/commands/test-stale-gate.js +8 -5
- package/dist/src/commands/test-verdict.d.ts +16 -2
- package/dist/src/commands/test-verdict.js +18 -3
- package/dist/src/commands/test.js +51 -7
- package/dist/src/commands/token-list.d.ts +26 -0
- package/dist/src/commands/token-list.js +153 -0
- package/dist/src/commands/token.js +17 -1
- package/dist/src/commands/tree.js +7 -2
- package/dist/src/commands/typecheck.js +9 -9
- package/dist/src/commands/wire.js +4 -3
- package/dist/src/core/branding.d.ts +0 -7
- package/dist/src/core/branding.js +3 -2
- package/dist/src/core/browser-wire.js +6 -3
- package/dist/src/core/build-audit-platform.d.ts +29 -0
- package/dist/src/core/build-audit-platform.js +140 -0
- package/dist/src/core/build-audit-registration.js +3 -2
- package/dist/src/core/build-audit-skip.d.ts +57 -0
- package/dist/src/core/build-audit-skip.js +112 -0
- package/dist/src/core/build-audit.d.ts +15 -2
- package/dist/src/core/build-audit.js +154 -12
- package/dist/src/core/build-baseline.d.ts +45 -28
- package/dist/src/core/build-baseline.js +6 -29
- package/dist/src/core/build-jar-escalation.d.ts +64 -0
- package/dist/src/core/build-jar-escalation.js +128 -0
- package/dist/src/core/build-overwrite-guard.js +1 -1
- package/dist/src/core/build-prepare.d.ts +5 -2
- package/dist/src/core/build-prepare.js +34 -6
- package/dist/src/core/config-paths.d.ts +2 -6
- package/dist/src/core/config-paths.js +4 -2
- package/dist/src/core/config-state.d.ts +0 -6
- package/dist/src/core/config-state.js +1 -19
- package/dist/src/core/config-validate-build-audit.d.ts +8 -0
- package/dist/src/core/config-validate-build-audit.js +80 -0
- package/dist/src/core/config-validate-file-size.d.ts +12 -0
- package/dist/src/core/config-validate-file-size.js +65 -0
- package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
- package/dist/src/core/config-validate-patch-policy.js +3 -3
- package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
- package/dist/src/core/config-validate-test-toolchains.js +4 -5
- package/dist/src/core/config-validate.js +20 -3
- package/dist/src/core/config.d.ts +2 -2
- package/dist/src/core/config.js +11 -15
- package/dist/src/core/coverage-extend.js +4 -4
- package/dist/src/core/discard-baseline.js +7 -7
- package/dist/src/core/engine-precondition.d.ts +1 -9
- package/dist/src/core/engine-precondition.js +3 -8
- package/dist/src/core/engine-session-lock.js +13 -17
- package/dist/src/core/export-moved-code-shape.js +2 -2
- package/dist/src/core/file-lock-owner.d.ts +103 -0
- package/dist/src/core/file-lock-owner.js +259 -0
- package/dist/src/core/file-lock.d.ts +2 -7
- package/dist/src/core/file-lock.js +40 -91
- package/dist/src/core/firefox-cache.d.ts +13 -1
- package/dist/src/core/firefox-cache.js +59 -34
- package/dist/src/core/firefox-download.d.ts +15 -0
- package/dist/src/core/firefox-download.js +37 -21
- package/dist/src/core/firefox-extract.d.ts +6 -5
- package/dist/src/core/firefox-extract.js +36 -18
- package/dist/src/core/firefox.d.ts +30 -32
- package/dist/src/core/firefox.js +5 -36
- package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
- package/dist/src/core/furnace-apply-dry-run.js +1 -7
- package/dist/src/core/furnace-apply-helpers.d.ts +0 -11
- package/dist/src/core/furnace-apply-helpers.js +8 -19
- package/dist/src/core/furnace-apply-overwrite-warn.js +4 -2
- package/dist/src/core/furnace-apply.d.ts +1 -1
- package/dist/src/core/furnace-apply.js +30 -10
- package/dist/src/core/furnace-config.d.ts +1 -1
- package/dist/src/core/furnace-config.js +23 -36
- package/dist/src/core/furnace-constants.js +3 -2
- package/dist/src/core/furnace-dir-entry.d.ts +20 -0
- package/dist/src/core/furnace-dir-entry.js +21 -0
- package/dist/src/core/furnace-operation.d.ts +2 -0
- package/dist/src/core/furnace-operation.js +3 -1
- package/dist/src/core/furnace-refresh.d.ts +18 -6
- package/dist/src/core/furnace-refresh.js +3 -6
- package/dist/src/core/furnace-registration-ast.d.ts +0 -1
- package/dist/src/core/furnace-registration-ast.js +0 -3
- package/dist/src/core/furnace-registration.d.ts +2 -1
- package/dist/src/core/furnace-registration.js +22 -29
- package/dist/src/core/furnace-scanner.js +3 -6
- package/dist/src/core/furnace-state-persist.d.ts +2 -2
- package/dist/src/core/furnace-validate-registration.js +5 -7
- package/dist/src/core/git-base.d.ts +3 -1
- package/dist/src/core/git-base.js +25 -3
- package/dist/src/core/git-diff-new-file.d.ts +40 -0
- package/dist/src/core/git-diff-new-file.js +119 -0
- package/dist/src/core/git-diff.d.ts +9 -9
- package/dist/src/core/git-diff.js +88 -157
- package/dist/src/core/git-file-ops.d.ts +0 -7
- package/dist/src/core/git-file-ops.js +4 -28
- package/dist/src/core/git-readonly-index.d.ts +0 -2
- package/dist/src/core/git-readonly-index.js +0 -4
- package/dist/src/core/git-status.d.ts +7 -0
- package/dist/src/core/git-status.js +21 -9
- package/dist/src/core/git.d.ts +0 -6
- package/dist/src/core/git.js +14 -43
- package/dist/src/core/harness-orphans.d.ts +73 -0
- package/dist/src/core/harness-orphans.js +187 -0
- package/dist/src/core/history-log.d.ts +19 -0
- package/dist/src/core/history-log.js +29 -0
- package/dist/src/core/mach-error-hints.d.ts +0 -5
- package/dist/src/core/mach-error-hints.js +1 -1
- package/dist/src/core/mach-known-noise-filter.d.ts +25 -3
- package/dist/src/core/mach-known-noise-filter.js +31 -10
- package/dist/src/core/mach-mozconfig.js +2 -1
- package/dist/src/core/mach-python.d.ts +2 -0
- package/dist/src/core/mach-python.js +12 -7
- package/dist/src/core/mach-resource-shim.d.ts +13 -1
- package/dist/src/core/mach-resource-shim.js +112 -9
- package/dist/src/core/mach.d.ts +46 -53
- package/dist/src/core/mach.js +31 -83
- package/dist/src/core/marionette-port.d.ts +0 -17
- package/dist/src/core/marionette-port.js +12 -58
- package/dist/src/core/marionette-preflight.js +9 -27
- package/dist/src/core/mochitest-server-port.js +3 -3
- package/dist/src/core/moz-manifest-rules.js +37 -33
- package/dist/src/core/patch-apply-fuzz.js +4 -4
- package/dist/src/core/patch-apply.d.ts +4 -5
- package/dist/src/core/patch-apply.js +55 -32
- package/dist/src/core/patch-export-coverage.d.ts +0 -9
- package/dist/src/core/patch-export-coverage.js +1 -30
- package/dist/src/core/patch-export-update.d.ts +18 -1
- package/dist/src/core/patch-export-update.js +4 -3
- package/dist/src/core/patch-export.d.ts +11 -21
- package/dist/src/core/patch-export.js +44 -66
- package/dist/src/core/patch-files.d.ts +0 -4
- package/dist/src/core/patch-files.js +0 -11
- package/dist/src/core/patch-lint-binary.d.ts +6 -12
- package/dist/src/core/patch-lint-cache.d.ts +17 -1
- package/dist/src/core/patch-lint-cache.js +3 -6
- package/dist/src/core/patch-lint-checkjs.d.ts +33 -16
- package/dist/src/core/patch-lint-checkjs.js +43 -25
- package/dist/src/core/patch-lint-creators.d.ts +2 -6
- package/dist/src/core/patch-lint-cross.d.ts +16 -8
- package/dist/src/core/patch-lint-cross.js +49 -92
- package/dist/src/core/patch-lint-css.js +10 -6
- package/dist/src/core/patch-lint-file-size.d.ts +52 -0
- package/dist/src/core/patch-lint-file-size.js +53 -0
- package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
- package/dist/src/core/patch-lint-forward-registration.js +210 -0
- package/dist/src/core/patch-lint-jsdoc.js +36 -36
- package/dist/src/core/patch-lint-module-registration.d.ts +2 -8
- package/dist/src/core/patch-lint-prettier.d.ts +36 -0
- package/dist/src/core/patch-lint-prettier.js +126 -0
- package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
- package/dist/src/core/patch-lint-queue-types.js +12 -0
- package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
- package/dist/src/core/patch-lint-staged-registration.js +90 -0
- package/dist/src/core/patch-lint-unmanaged-companion.d.ts +106 -0
- package/dist/src/core/patch-lint-unmanaged-companion.js +214 -0
- package/dist/src/core/patch-lint.d.ts +14 -9
- package/dist/src/core/patch-lint.js +27 -27
- package/dist/src/core/patch-lock.js +3 -3
- package/dist/src/core/patch-manifest-io.js +8 -7
- package/dist/src/core/patch-parse.d.ts +19 -8
- package/dist/src/core/patch-parse.js +25 -0
- package/dist/src/core/patch-policy.js +15 -27
- package/dist/src/core/patch-source-metadata.d.ts +1 -3
- package/dist/src/core/patch-source-metadata.js +0 -4
- package/dist/src/core/patch-transform.d.ts +21 -8
- package/dist/src/core/patch-transform.js +38 -10
- package/dist/src/core/process-cpu-time.js +1 -1
- package/dist/src/core/rebase-session.d.ts +2 -8
- package/dist/src/core/rebase-session.js +5 -17
- package/dist/src/core/register-browser-content.js +3 -2
- package/dist/src/core/register-browser-test.js +2 -1
- package/dist/src/core/register-module.js +2 -1
- package/dist/src/core/register-scaffold.js +5 -4
- package/dist/src/core/register-shared-css.js +2 -1
- package/dist/src/core/run-log-redact.d.ts +38 -0
- package/dist/src/core/run-log-redact.js +69 -0
- package/dist/src/core/run-log.d.ts +13 -2
- package/dist/src/core/run-log.js +67 -18
- package/dist/src/core/signal-critical.js +2 -4
- package/dist/src/core/smoke-patterns.d.ts +0 -1
- package/dist/src/core/smoke-patterns.js +18 -11
- package/dist/src/core/state-file.d.ts +12 -1
- package/dist/src/core/state-file.js +14 -1
- package/dist/src/core/status-classify.js +2 -1
- package/dist/src/core/test-changed-prefs.js +26 -11
- package/dist/src/core/test-harness-crash.js +61 -48
- package/dist/src/core/test-harness-verdict-notes.d.ts +55 -0
- package/dist/src/core/test-harness-verdict-notes.js +48 -0
- package/dist/src/core/test-stale-check.d.ts +23 -8
- package/dist/src/core/test-stale-check.js +77 -4
- package/dist/src/core/test-stall-triage.js +37 -19
- package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
- package/dist/src/core/test-xpcshell-retry.js +14 -11
- package/dist/src/core/token-category.d.ts +33 -0
- package/dist/src/core/token-category.js +56 -48
- package/dist/src/core/token-dark-mode.js +2 -14
- package/dist/src/core/token-inventory.d.ts +56 -0
- package/dist/src/core/token-inventory.js +109 -0
- package/dist/src/core/token-manager.d.ts +1 -1
- package/dist/src/core/token-manager.js +1 -1
- package/dist/src/core/token-variant.js +21 -22
- package/dist/src/core/toolchain-preflight.js +1 -1
- package/dist/src/core/tree-cow.d.ts +0 -2
- package/dist/src/core/tree-cow.js +2 -2
- package/dist/src/core/tree-store.js +4 -5
- package/dist/src/core/typecheck.js +2 -2
- package/dist/src/core/wire-dom-fragment.js +6 -17
- package/dist/src/core/wire-utils.js +5 -17
- package/dist/src/core/xpcshell-appdir.js +2 -4
- package/dist/src/errors/base.d.ts +53 -1
- package/dist/src/errors/base.js +79 -11
- package/dist/src/errors/build.d.ts +28 -0
- package/dist/src/errors/build.js +51 -10
- package/dist/src/errors/config.d.ts +0 -14
- package/dist/src/errors/config.js +13 -34
- package/dist/src/errors/download.d.ts +13 -0
- package/dist/src/errors/download.js +53 -24
- package/dist/src/errors/furnace.js +8 -9
- package/dist/src/errors/git.d.ts +8 -8
- package/dist/src/errors/git.js +20 -30
- package/dist/src/errors/patch.js +8 -10
- package/dist/src/errors/rebase.js +7 -7
- package/dist/src/types/commands/options.d.ts +96 -123
- package/dist/src/types/commands/patches.d.ts +17 -6
- package/dist/src/types/config.d.ts +75 -2
- package/dist/src/types/index.d.ts +0 -1
- package/dist/src/utils/assert.d.ts +3 -15
- package/dist/src/utils/assert.js +1 -18
- package/dist/src/utils/build-info.d.ts +1 -1
- package/dist/src/utils/child-env.d.ts +2 -1
- package/dist/src/utils/elapsed.js +10 -1
- package/dist/src/utils/fs.d.ts +4 -2
- package/dist/src/utils/fs.js +11 -14
- package/dist/src/utils/hash.d.ts +12 -0
- package/dist/src/utils/hash.js +16 -0
- package/dist/src/utils/line-dispatch.d.ts +15 -0
- package/dist/src/utils/line-dispatch.js +48 -0
- package/dist/src/utils/options.d.ts +1 -7
- package/dist/src/utils/options.js +6 -7
- package/dist/src/utils/package-root.js +3 -12
- package/dist/src/utils/platform.d.ts +0 -4
- package/dist/src/utils/process-group.d.ts +2 -1
- package/dist/src/utils/process-group.js +20 -19
- package/dist/src/utils/process.d.ts +40 -26
- package/dist/src/utils/process.js +115 -166
- package/dist/src/utils/ps-duration.js +3 -3
- package/dist/src/utils/sleep.d.ts +26 -0
- package/dist/src/utils/sleep.js +31 -0
- package/dist/src/utils/validation.d.ts +2 -38
- package/dist/src/utils/validation.js +7 -16
- package/docs/configuration.md +136 -1
- package/docs/exit-codes.md +34 -20
- package/docs/lifecycle-invariants.md +18 -12
- package/docs/machine-output.md +31 -8
- package/docs/patch-workflow.md +2 -1
- package/docs/run-logs.md +44 -4
- package/docs/testing.md +70 -11
- package/package.json +8 -7
- package/dist/src/commands/rebase.d.ts +0 -4
- package/dist/src/commands/rebase.js +0 -5
- package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
- package/dist/src/core/patch-artifact-normalize.js +0 -24
- package/dist/src/core/patch-lint-reexports.d.ts +0 -7
- package/dist/src/core/patch-lint-reexports.js +0 -8
- package/dist/src/core/wire-targets.d.ts +0 -7
- package/dist/src/core/wire-targets.js +0 -8
|
@@ -9,8 +9,9 @@ import { loadPatchesManifest, resolvePatchIdentifier, stampPatchVersions, } from
|
|
|
9
9
|
import { GeneralError, InvalidArgumentError } from '../errors/base.js';
|
|
10
10
|
import { elapsedSince } from '../utils/elapsed.js';
|
|
11
11
|
import { toError } from '../utils/errors.js';
|
|
12
|
-
import { cancel, info, intro, isCancel, outro, spinner, success, warn } from '../utils/logger.js';
|
|
12
|
+
import { cancel, info, intro, isCancel, outro, spinner, success, warn, } from '../utils/logger.js';
|
|
13
13
|
import { loadScanFilesAssignments, withDryRunPurityGuard, withDryRunReExportLock, } from './re-export-bulk-scan.js';
|
|
14
|
+
import { warnUnseenExpectedDrift } from './re-export-drift.js';
|
|
14
15
|
import { reExportFilesInPlace } from './re-export-files.js';
|
|
15
16
|
import { buildReExportLintContext, finishReExportLint } from './re-export-lint.js';
|
|
16
17
|
import { applyReExportFilesPositionalFolding, validateReExportOptionCombinations, } from './re-export-options.js';
|
|
@@ -123,6 +124,8 @@ export async function reExportCommand(projectRoot, patches, options) {
|
|
|
123
124
|
binaryName: config.binaryName,
|
|
124
125
|
furnacePrefixes: await collectFurnaceManagedPrefixes(paths.root),
|
|
125
126
|
refuseAdjacentUnmanaged: options.refuseAdjacentUnmanaged === true,
|
|
127
|
+
approvedUnmanaged: new Set((options.expectUnmanaged ?? []).map((file) => normalizeEngineRelativeInput(file, '--expect-unmanaged'))),
|
|
128
|
+
approvedSeen: new Set(),
|
|
126
129
|
refusals: [],
|
|
127
130
|
};
|
|
128
131
|
const driftCtx = buildForeignDriftContext(options);
|
|
@@ -140,7 +143,17 @@ export async function reExportCommand(projectRoot, patches, options) {
|
|
|
140
143
|
const patchOptions = assignedScanFiles !== undefined ? { ...options, scanFiles: assignedScanFiles } : options;
|
|
141
144
|
progress.message(`Re-exporting ${index + 1}/${selectedPatches.length}: ${patch.filename} (${patch.filesAffected.length} file(s), ${elapsedSince(startedAt)} elapsed)...`);
|
|
142
145
|
try {
|
|
143
|
-
const exported = await reExportSinglePatchWithIndexLockRetry(
|
|
146
|
+
const exported = await reExportSinglePatchWithIndexLockRetry({
|
|
147
|
+
patch,
|
|
148
|
+
paths,
|
|
149
|
+
manifest,
|
|
150
|
+
options: patchOptions,
|
|
151
|
+
isDryRun,
|
|
152
|
+
config,
|
|
153
|
+
adjacentCtx,
|
|
154
|
+
driftCtx,
|
|
155
|
+
lintCtx,
|
|
156
|
+
});
|
|
144
157
|
if (exported) {
|
|
145
158
|
reExported++;
|
|
146
159
|
reExportedFilenames.push(patch.filename);
|
|
@@ -155,6 +168,7 @@ export async function reExportCommand(projectRoot, patches, options) {
|
|
|
155
168
|
}));
|
|
156
169
|
await finishReExportLint(lintCtx);
|
|
157
170
|
warnUnseenExpectedDrift(driftCtx);
|
|
171
|
+
warnUnseenApprovedUnmanaged(adjacentCtx);
|
|
158
172
|
throwRunLevelRefusals(adjacentCtx, driftCtx, progress);
|
|
159
173
|
if (reExported === 0 && selectedPatches.length > 0) {
|
|
160
174
|
progress.error('Re-export failed');
|
|
@@ -198,26 +212,22 @@ function buildForeignDriftContext(options) {
|
|
|
198
212
|
};
|
|
199
213
|
}
|
|
200
214
|
/**
|
|
201
|
-
* Names `--expect` paths that never
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
215
|
+
* Names `--expect-unmanaged` paths that were never met this run. A typo'd
|
|
216
|
+
* carve-out silently leaves the refusal armed for the path it was meant to
|
|
217
|
+
* admit — and, worse, reads as an approval that is in force when it is not.
|
|
218
|
+
* A warning rather than a refusal: an approved path legitimately goes unmet
|
|
219
|
+
* when the reviewed file has since been adopted into a patch, which is the
|
|
220
|
+
* outcome the carve-out exists to be retired by.
|
|
206
221
|
*/
|
|
207
|
-
function
|
|
208
|
-
const unseen = [...
|
|
222
|
+
function warnUnseenApprovedUnmanaged(adjacentCtx) {
|
|
223
|
+
const unseen = [...adjacentCtx.approvedUnmanaged].filter((file) => !adjacentCtx.approvedSeen.has(file));
|
|
209
224
|
if (unseen.length === 0)
|
|
210
225
|
return;
|
|
211
|
-
|
|
212
|
-
warn(`--expect path(s) were not evaluated because no selected patch reached the drift check (for example, an earlier refusal): ${unseen.join(', ')}`);
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
warn('--expect path(s) showed no drift this run: ' +
|
|
226
|
+
warn('--expect-unmanaged path(s) were not met as adjacent unmanaged files this run: ' +
|
|
216
227
|
`${unseen.join(', ')}. ` +
|
|
217
|
-
'Causes, in rough order of likelihood: a typo in the path; the
|
|
218
|
-
'
|
|
219
|
-
'
|
|
220
|
-
'converged before this run looked.');
|
|
228
|
+
'Causes, in rough order of likelihood: a typo in the path; the file is now owned by a ' +
|
|
229
|
+
'patch (the carve-out can be dropped); or no selected patch is adjacent to it. The ' +
|
|
230
|
+
'refusal stayed armed for everything else either way.');
|
|
221
231
|
}
|
|
222
232
|
/**
|
|
223
233
|
* Turns the run's collected `--refuse-adjacent-unmanaged` and
|
|
@@ -227,19 +237,26 @@ function warnUnseenExpectedDrift(driftCtx) {
|
|
|
227
237
|
function throwRunLevelRefusals(adjacentCtx, driftCtx, progress) {
|
|
228
238
|
if (adjacentCtx.refusals.length > 0) {
|
|
229
239
|
progress.error('Re-export refused');
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
240
|
+
// Each offender is named with the owned directory that made it
|
|
241
|
+
// adjacent: an unattended run reads only this line, and without the
|
|
242
|
+
// anchor it cannot tell which of a multi-directory patch's locations
|
|
243
|
+
// the file sits beside.
|
|
244
|
+
const offenders = adjacentCtx.refusals
|
|
245
|
+
.map((r) => `${r.patchFilename} (${r.anchored.join(', ')})`)
|
|
246
|
+
.join('; ');
|
|
247
|
+
throw new GeneralError(`Refused ${adjacentCtx.refusals.length} patch(es) with adjacent unmanaged files ` +
|
|
248
|
+
`(--refuse-adjacent-unmanaged): ${offenders}. Adjacent means the file sits in a ` +
|
|
249
|
+
`directory the patch already owns a file in. Adopt reviewed files with ` +
|
|
250
|
+
`--scan --scan-file, or export them as a new patch.`);
|
|
234
251
|
}
|
|
235
252
|
if (driftCtx.refusals.length > 0) {
|
|
236
253
|
progress.error('Re-export refused');
|
|
237
254
|
const names = driftCtx.refusals.map((r) => r.patchFilename).join(', ');
|
|
238
255
|
const unreadable = driftCtx.refusals.filter((r) => r.reason === 'baseline-unreadable');
|
|
239
256
|
const unreadablePart = unreadable.length > 0
|
|
240
|
-
? ` ${
|
|
257
|
+
? ` ${unreadable.length} of these refused because the old patch body was missing or unreadable, so the drift comparison could not run (fail-closed).`
|
|
241
258
|
: '';
|
|
242
|
-
throw new GeneralError(`Refused ${
|
|
259
|
+
throw new GeneralError(`Refused ${driftCtx.refusals.length} patch(es) whose refreshed body would absorb ` +
|
|
243
260
|
`engine edits not present in the old patch body (--refuse-foreign-drift): ${names}.${unreadablePart}\n\n` +
|
|
244
261
|
'The flag cannot tell WHO wrote those lines, and the two populations have opposite ' +
|
|
245
262
|
'remedies:\n' +
|
|
@@ -283,9 +300,9 @@ function reportReExportOutcome(args) {
|
|
|
283
300
|
if (reExported < selectedCount) {
|
|
284
301
|
const skippedCount = selectedCount - reExported - args.failedFilenames.length;
|
|
285
302
|
const failedPart = args.failedFilenames.length > 0 ? ` Failed: ${args.failedFilenames.join(', ')}.` : '';
|
|
286
|
-
const skippedPart = skippedCount > 0 ? ` Skipped: ${
|
|
287
|
-
throw new GeneralError(`${isDryRun ? '[dry-run] Would re-export' : 'Re-exported'} only ${
|
|
288
|
-
`${
|
|
303
|
+
const skippedPart = skippedCount > 0 ? ` Skipped: ${skippedCount} patch(es) (see warnings above).` : '';
|
|
304
|
+
throw new GeneralError(`${isDryRun ? '[dry-run] Would re-export' : 'Re-exported'} only ${reExported} of ` +
|
|
305
|
+
`${selectedCount} selected patch(es).${failedPart}${skippedPart} ` +
|
|
289
306
|
'Partial re-export exits non-zero so it cannot pass as success in scripts; ' +
|
|
290
307
|
'the per-patch errors are listed above.');
|
|
291
308
|
}
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Rebase abort flow.
|
|
4
4
|
*/
|
|
5
|
-
import { getProjectPaths
|
|
5
|
+
import { getProjectPaths } from '../../core/config.js';
|
|
6
6
|
import { clearAppliedFurnaceState } from '../../core/furnace-config.js';
|
|
7
7
|
import { resetChanges } from '../../core/git.js';
|
|
8
8
|
import { clearRebaseSession, getRebaseSessionPath, readRebaseSession, } from '../../core/rebase-session.js';
|
|
9
9
|
import { NoRebaseSessionError } from '../../errors/rebase.js';
|
|
10
10
|
import { intro, outro, spinner, success, warn } from '../../utils/logger.js';
|
|
11
|
+
import { clearPendingResolution } from '../pending-resolution.js';
|
|
11
12
|
import { confirmDirtyEngineReset } from './confirm.js';
|
|
12
13
|
/**
|
|
13
14
|
* Handles `fireforge rebase --abort`.
|
|
@@ -57,13 +58,7 @@ export async function handleAbort(projectRoot, yes) {
|
|
|
57
58
|
// idempotently re-clear furnace state.
|
|
58
59
|
await clearAppliedFurnaceState(projectRoot);
|
|
59
60
|
// Step 3: clear pending resolution transactionally.
|
|
60
|
-
await
|
|
61
|
-
if (!current.pendingResolution)
|
|
62
|
-
return current;
|
|
63
|
-
const next = { ...current };
|
|
64
|
-
delete next.pendingResolution;
|
|
65
|
-
return next;
|
|
66
|
-
});
|
|
61
|
+
await clearPendingResolution(projectRoot);
|
|
67
62
|
// Step 4: clear the rebase session LAST so a failure in any prior step
|
|
68
63
|
// preserves the session on disk and a retry of --abort can succeed.
|
|
69
64
|
await clearRebaseSession(projectRoot);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Rebase --continue flow.
|
|
4
4
|
*/
|
|
5
5
|
import { join } from 'node:path';
|
|
6
|
-
import { getProjectPaths
|
|
6
|
+
import { getProjectPaths } from '../../core/config.js';
|
|
7
7
|
import { getStagedDiffForFiles } from '../../core/git-diff.js';
|
|
8
8
|
import { stageFiles, unstageFiles } from '../../core/git-file-ops.js';
|
|
9
9
|
import { updatePatchAndMetadata } from '../../core/patch-export.js';
|
|
@@ -14,6 +14,7 @@ import { GeneralError } from '../../errors/base.js';
|
|
|
14
14
|
import { CorruptRebaseSessionError, NoRebaseSessionError, RebaseError, } from '../../errors/rebase.js';
|
|
15
15
|
import { pathExists } from '../../utils/fs.js';
|
|
16
16
|
import { info, intro, success, warn } from '../../utils/logger.js';
|
|
17
|
+
import { clearPendingResolution } from '../pending-resolution.js';
|
|
17
18
|
import { runPatchLoop } from './patch-loop.js';
|
|
18
19
|
/**
|
|
19
20
|
* Handles `fireforge rebase --continue`.
|
|
@@ -77,10 +78,15 @@ export async function handleContinue(projectRoot, maxFuzz, waitLockSeconds) {
|
|
|
77
78
|
// updatePatchMetadata sequence can interleave with a concurrent export /
|
|
78
79
|
// re-export / patch reorder / patch compact and leave the manifest
|
|
79
80
|
// disagreeing with the freshly-written patch body. Mirrors resolve.ts.
|
|
80
|
-
await updatePatchAndMetadata(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
await updatePatchAndMetadata({
|
|
82
|
+
patchesDir: paths.patches,
|
|
83
|
+
filename: currentPatch.filename,
|
|
84
|
+
newContent: diffContent,
|
|
85
|
+
updates: {
|
|
86
|
+
sourceEsrVersion: session.toVersion,
|
|
87
|
+
sourceVersion: session.toVersion,
|
|
88
|
+
...(session.toProduct !== undefined ? { sourceProduct: session.toProduct } : {}),
|
|
89
|
+
},
|
|
84
90
|
});
|
|
85
91
|
}
|
|
86
92
|
finally {
|
|
@@ -104,13 +110,7 @@ export async function handleContinue(projectRoot, maxFuzz, waitLockSeconds) {
|
|
|
104
110
|
await saveRebaseSession(projectRoot, session);
|
|
105
111
|
// Clear pending resolution transactionally so concurrent state-file
|
|
106
112
|
// writes to unrelated keys are not clobbered by a stale reload.
|
|
107
|
-
await
|
|
108
|
-
if (!current.pendingResolution)
|
|
109
|
-
return current;
|
|
110
|
-
const next = { ...current };
|
|
111
|
-
delete next.pendingResolution;
|
|
112
|
-
return next;
|
|
113
|
-
});
|
|
113
|
+
await clearPendingResolution(projectRoot);
|
|
114
114
|
});
|
|
115
115
|
success(`Resolved ${currentPatch.filename}`);
|
|
116
116
|
// Continue applying remaining patches
|
|
@@ -17,7 +17,7 @@ import { clearAppliedFurnaceState } from '../../core/furnace-config.js';
|
|
|
17
17
|
import { getHead, resetChanges } from '../../core/git.js';
|
|
18
18
|
import { discoverPatches } from '../../core/patch-files.js';
|
|
19
19
|
import { loadPatchesManifest } from '../../core/patch-manifest.js';
|
|
20
|
-
import {
|
|
20
|
+
import { getPatchSourceVersion } from '../../core/patch-source-metadata.js';
|
|
21
21
|
import { getRebaseSessionPath, readRebaseSession, saveRebaseSession, } from '../../core/rebase-session.js';
|
|
22
22
|
import { GeneralError } from '../../errors/base.js';
|
|
23
23
|
import { CorruptRebaseSessionError, RebaseSessionExistsError } from '../../errors/rebase.js';
|
|
@@ -59,7 +59,7 @@ async function handleFreshStart(projectRoot, options) {
|
|
|
59
59
|
}
|
|
60
60
|
// Determine the "from" version from the patches
|
|
61
61
|
const patchVersions = new Set(manifest.patches.map((p) => getPatchSourceVersion(p)));
|
|
62
|
-
const patchProducts = new Set(manifest.patches.map((p) =>
|
|
62
|
+
const patchProducts = new Set(manifest.patches.map((p) => p.sourceProduct).filter(Boolean));
|
|
63
63
|
const sortedVersions = [...patchVersions].sort();
|
|
64
64
|
const fromVersion = sortedVersions[0] ?? currentVersion;
|
|
65
65
|
const fromProduct = [...patchProducts].sort()[0] ?? config.firefox.product;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Patch application loop and re-export flow.
|
|
3
3
|
*/
|
|
4
|
-
import type { getProjectPaths } from '../../core/config.js';
|
|
5
4
|
import type { RebaseSession } from '../../core/rebase-session.js';
|
|
5
|
+
import type { ProjectPaths } from '../../types/config.js';
|
|
6
6
|
/**
|
|
7
7
|
* Runs the patch application loop, re-exports applied patches, and stamps versions.
|
|
8
8
|
*
|
|
@@ -12,4 +12,4 @@ import type { RebaseSession } from '../../core/rebase-session.js';
|
|
|
12
12
|
* @param maxFuzz - Maximum context-reduction steps for `git apply -C<n>`
|
|
13
13
|
* @param waitLockSeconds - `--wait-lock` budget for the patch-directory lock
|
|
14
14
|
*/
|
|
15
|
-
export declare function runPatchLoop(projectRoot: string, session: RebaseSession, paths:
|
|
15
|
+
export declare function runPatchLoop(projectRoot: string, session: RebaseSession, paths: ProjectPaths, maxFuzz: number, waitLockSeconds?: number): Promise<void>;
|
|
@@ -20,6 +20,7 @@ import { toError } from '../../utils/errors.js';
|
|
|
20
20
|
import { pathExists } from '../../utils/fs.js';
|
|
21
21
|
import { error, info, outro, spinner, success, warn } from '../../utils/logger.js';
|
|
22
22
|
import { isValidFirefoxVersion } from '../../utils/validation.js';
|
|
23
|
+
import { clearPendingResolution } from '../pending-resolution.js';
|
|
23
24
|
import { buildRebaseConflictSummary } from './conflict-summary.js';
|
|
24
25
|
import { printSummary } from './summary.js';
|
|
25
26
|
/**
|
|
@@ -208,13 +209,7 @@ export async function runPatchLoop(projectRoot, session, paths, maxFuzz, waitLoc
|
|
|
208
209
|
await clearRebaseSession(projectRoot);
|
|
209
210
|
// Clear pending resolution if any (transactionally, so a concurrent
|
|
210
211
|
// state write to an unrelated key is not clobbered by a stale reload).
|
|
211
|
-
await
|
|
212
|
-
if (!current.pendingResolution)
|
|
213
|
-
return current;
|
|
214
|
-
const next = { ...current };
|
|
215
|
-
delete next.pendingResolution;
|
|
216
|
-
return next;
|
|
217
|
-
});
|
|
212
|
+
await clearPendingResolution(projectRoot);
|
|
218
213
|
info('');
|
|
219
214
|
success(`All patches re-exported with sourceVersion=${session.toVersion}`);
|
|
220
215
|
outro('Rebase complete!');
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { confirm } from '@clack/prompts';
|
|
4
4
|
import { Command } from 'commander';
|
|
5
|
-
import { getProjectPaths, loadConfig, loadState
|
|
5
|
+
import { getProjectPaths, loadConfig, loadState } from '../core/config.js';
|
|
6
6
|
import { stdioIsInteractive } from '../core/destructive.js';
|
|
7
7
|
import { assertEngineGitReady } from '../core/engine-precondition.js';
|
|
8
8
|
import { getStagedDiffForFiles } from '../core/git-diff.js';
|
|
@@ -16,6 +16,7 @@ import { toError } from '../utils/errors.js';
|
|
|
16
16
|
import { pathExists } from '../utils/fs.js';
|
|
17
17
|
import { error as logError, info, intro, isCancel, outro, spinner, success, } from '../utils/logger.js';
|
|
18
18
|
import { addWaitLockOption, resolveWaitLockSeconds } from '../utils/options.js';
|
|
19
|
+
import { clearPendingResolution } from './pending-resolution.js';
|
|
19
20
|
/**
|
|
20
21
|
* Runs the resolve command to fix broken patches.
|
|
21
22
|
* @param projectRoot - Root directory of the project
|
|
@@ -123,20 +124,27 @@ export async function resolveCommand(projectRoot, options = {}) {
|
|
|
123
124
|
// here keeps resolve in agreement with every other writer.
|
|
124
125
|
const diffFilesAffected = extractAffectedFiles(diffContent);
|
|
125
126
|
const config = await loadConfig(projectRoot);
|
|
126
|
-
await updatePatchAndMetadata(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
await updatePatchAndMetadata({
|
|
128
|
+
patchesDir: paths.patches,
|
|
129
|
+
filename: patchFilename,
|
|
130
|
+
newContent: diffContent,
|
|
131
|
+
updates: {
|
|
132
|
+
filesAffected: diffFilesAffected,
|
|
133
|
+
...buildPatchSourceMetadata(config.firefox),
|
|
134
|
+
},
|
|
135
|
+
onCommitted: undefined,
|
|
136
|
+
policyGate: undefined,
|
|
137
|
+
lockOptions: {
|
|
138
|
+
waitLockSeconds: resolveWaitLockSeconds(options.waitLock),
|
|
139
|
+
command: 'resolve',
|
|
140
|
+
},
|
|
141
|
+
});
|
|
130
142
|
// Clear pendingResolution from state.json transactionally so a
|
|
131
143
|
// concurrent update to unrelated keys (another command writing buildMode
|
|
132
144
|
// or baseCommit between our load and save) is not clobbered. The updater
|
|
133
145
|
// runs inside the state-file lock with the freshest on-disk state, so
|
|
134
146
|
// only pendingResolution is affected.
|
|
135
|
-
await
|
|
136
|
-
const next = { ...current };
|
|
137
|
-
delete next.pendingResolution;
|
|
138
|
-
return next;
|
|
139
|
-
});
|
|
147
|
+
await clearPendingResolution(projectRoot);
|
|
140
148
|
s.stop(`Updated ${patchFilename}`);
|
|
141
149
|
success('Patch updated successfully and resolution state cleared.');
|
|
142
150
|
info('Patch updated. Run "fireforge import" next to resume the queue from this point — resolve only refreshes the one broken patch, it does not continue applying the remaining patches itself.');
|
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import type { CommandContext } from '../types/cli.js';
|
|
3
3
|
import type { RunOptions } from '../types/commands/index.js';
|
|
4
|
-
/**
|
|
5
|
-
* Exit code returned by smoke-run mode when the captured console stream
|
|
6
|
-
* produced one or more error lines that did NOT match the operator's
|
|
7
|
-
* allowlist.
|
|
8
|
-
*/
|
|
9
|
-
export declare const SMOKE_EXIT_FAILURE: 12;
|
|
10
|
-
/**
|
|
11
|
-
* Exit code returned by smoke-run mode when the browser itself exited
|
|
12
|
-
* with a non-clean status before the smoke window elapsed — i.e. a
|
|
13
|
-
* launch-side failure we could NOT observe as a console error line
|
|
14
|
-
* (crash before console wiring, missing profile, etc.).
|
|
15
|
-
*/
|
|
16
|
-
export declare const SMOKE_LAUNCH_FAILURE: 13;
|
|
17
4
|
/**
|
|
18
5
|
* Runs the run command to launch the built browser.
|
|
19
6
|
* @param projectRoot - Root directory of the project
|
package/dist/src/commands/run.js
CHANGED
|
@@ -17,19 +17,6 @@ import { toError } from '../utils/errors.js';
|
|
|
17
17
|
import { pathExists, removeDir, removeFile } from '../utils/fs.js';
|
|
18
18
|
import { info, intro, verbose, warn } from '../utils/logger.js';
|
|
19
19
|
import { commanderArgParser, pickDefined, stringListOption } from '../utils/options.js';
|
|
20
|
-
/**
|
|
21
|
-
* Exit code returned by smoke-run mode when the captured console stream
|
|
22
|
-
* produced one or more error lines that did NOT match the operator's
|
|
23
|
-
* allowlist.
|
|
24
|
-
*/
|
|
25
|
-
export const SMOKE_EXIT_FAILURE = ExitCode.SMOKE_EXIT_FAILURE;
|
|
26
|
-
/**
|
|
27
|
-
* Exit code returned by smoke-run mode when the browser itself exited
|
|
28
|
-
* with a non-clean status before the smoke window elapsed — i.e. a
|
|
29
|
-
* launch-side failure we could NOT observe as a console error line
|
|
30
|
-
* (crash before console wiring, missing profile, etc.).
|
|
31
|
-
*/
|
|
32
|
-
export const SMOKE_LAUNCH_FAILURE = ExitCode.SMOKE_LAUNCH_FAILURE;
|
|
33
20
|
/** Recommendation surfaced when the smoke window is shorter than a typical cold start. */
|
|
34
21
|
const SMOKE_COLD_START_THRESHOLD_MS = 30_000;
|
|
35
22
|
/** Maximum number of unallowed error lines to surface in the terminal summary. */
|
|
@@ -157,7 +144,7 @@ async function runSmokeExit(engineDir, options) {
|
|
|
157
144
|
// Not an error — cold starts just tend to exceed the window. Surfacing
|
|
158
145
|
// the hint here instead of failing lets agents run shorter windows
|
|
159
146
|
// intentionally (e.g. warm-cache smoke checks).
|
|
160
|
-
verbose(`Smoke window is ${
|
|
147
|
+
verbose(`Smoke window is ${smokeExit}s; cold starts on slow machines often exceed 30s.`);
|
|
161
148
|
}
|
|
162
149
|
const allowlist = await buildAllowlist(options);
|
|
163
150
|
const captureStream = options.captureConsole
|
|
@@ -216,7 +203,7 @@ async function runSmokeExit(engineDir, options) {
|
|
|
216
203
|
'console capture and can fail the run. Pass --headless (or run on an unattended host) ' +
|
|
217
204
|
'for reliable smoke checks.');
|
|
218
205
|
}
|
|
219
|
-
info(`Launching browser (smoke-exit after ${
|
|
206
|
+
info(`Launching browser (smoke-exit after ${smokeExit}s)...\n`);
|
|
220
207
|
const startedAt = Date.now();
|
|
221
208
|
let result;
|
|
222
209
|
try {
|
|
@@ -248,7 +235,7 @@ async function runSmokeExit(engineDir, options) {
|
|
|
248
235
|
});
|
|
249
236
|
// Exit contract (precedence: unallowed errors dominate timed-out).
|
|
250
237
|
if (findings.length > 0) {
|
|
251
|
-
throw new SmokeRunError(`Smoke run observed ${
|
|
238
|
+
throw new SmokeRunError(`Smoke run observed ${findings.length} unallowed console error(s).`, ExitCode.SMOKE_EXIT_FAILURE);
|
|
252
239
|
}
|
|
253
240
|
if (result.timedOut) {
|
|
254
241
|
// Clean window — SIGTERM from us. Treat as success.
|
|
@@ -257,7 +244,7 @@ async function runSmokeExit(engineDir, options) {
|
|
|
257
244
|
if (result.exitCode === 0 || result.exitCode === 130 || result.exitCode === 143) {
|
|
258
245
|
return;
|
|
259
246
|
}
|
|
260
|
-
throw new SmokeRunError(`Browser exited with code ${
|
|
247
|
+
throw new SmokeRunError(`Browser exited with code ${result.exitCode} before smoke-exit window elapsed.`, ExitCode.SMOKE_LAUNCH_FAILURE);
|
|
261
248
|
}
|
|
262
249
|
/**
|
|
263
250
|
* Compiles the active allowlist from `--console-allow` CLI values and
|
|
@@ -298,15 +285,15 @@ function reportSmokeSummary(args) {
|
|
|
298
285
|
const suffix = args.timedOut ? ' (deadline fired — SIGTERM sent to process group)' : '';
|
|
299
286
|
info('');
|
|
300
287
|
info(`Smoke run complete: ${seconds}s elapsed of ${windowSeconds}s window${suffix}`);
|
|
301
|
-
info(` Unallowed errors: ${
|
|
288
|
+
info(` Unallowed errors: ${args.findings.length}`);
|
|
302
289
|
// The "suppressed errors" count is what the exit contract cares about — the
|
|
303
290
|
// subset of allowlisted hits that would otherwise have been tallied as
|
|
304
291
|
// findings. The "all allowlisted lines" count answers the operator's mental
|
|
305
292
|
// model ("my --console-allow pattern matched N console lines"), without
|
|
306
293
|
// which a visibly matching regex reports 0 hits.
|
|
307
|
-
info(` Allowlisted error hits (suppressed): ${
|
|
308
|
-
info(` Allowlisted lines total: ${
|
|
309
|
-
info(` Child exit code: ${
|
|
294
|
+
info(` Allowlisted error hits (suppressed): ${args.allowlistedErrorHits}`);
|
|
295
|
+
info(` Allowlisted lines total: ${args.allowlistedTotalHits}`);
|
|
296
|
+
info(` Child exit code: ${args.exitCode}`);
|
|
310
297
|
// Per-entry attribution: first-match credit per line, with
|
|
311
298
|
// zero-hit entries always visible — an allowlist entry whose suppressed
|
|
312
299
|
// shape changed upstream is only detectable as a 0× row.
|
|
@@ -315,19 +302,19 @@ function reportSmokeSummary(args) {
|
|
|
315
302
|
args.allowlist.forEach((entry, index) => {
|
|
316
303
|
const hits = args.allowlistHits[index] ?? 0;
|
|
317
304
|
const zeroSuffix = hits === 0 ? ' (never matched — candidate for removal)' : '';
|
|
318
|
-
info(` ${
|
|
305
|
+
info(` ${hits}× ${entry.origin} ${entry.source}${zeroSuffix}`);
|
|
319
306
|
});
|
|
320
307
|
}
|
|
321
308
|
if (args.findings.length === 0)
|
|
322
309
|
return;
|
|
323
310
|
warn('');
|
|
324
|
-
warn(`Unallowed console errors (first ${
|
|
311
|
+
warn(`Unallowed console errors (first ${SMOKE_UNALLOWED_PREVIEW_MAX}):`);
|
|
325
312
|
args.findings.slice(0, SMOKE_UNALLOWED_PREVIEW_MAX).forEach((finding, index) => {
|
|
326
|
-
warn(` ${
|
|
313
|
+
warn(` ${index + 1}. [${finding.stream}] ${finding.line}`);
|
|
327
314
|
});
|
|
328
315
|
if (args.findings.length > SMOKE_UNALLOWED_PREVIEW_MAX) {
|
|
329
316
|
const remaining = args.findings.length - SMOKE_UNALLOWED_PREVIEW_MAX;
|
|
330
|
-
warn(` …and ${
|
|
317
|
+
warn(` …and ${remaining} more.`);
|
|
331
318
|
}
|
|
332
319
|
}
|
|
333
320
|
/** Registers the run command on the CLI program. */
|
|
@@ -9,10 +9,22 @@ export interface ResolvedSetupInputs {
|
|
|
9
9
|
finalProduct: FirefoxProduct;
|
|
10
10
|
finalLicense: ProjectLicense;
|
|
11
11
|
}
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Narrows a raw CLI or prompt value to a supported Firefox product.
|
|
14
|
+
* @param value - Raw value from `--product` or the setup prompt.
|
|
15
|
+
* @param field - Field name quoted in the refusal (e.g. `--product`).
|
|
16
|
+
* @returns The validated product.
|
|
17
|
+
* @throws {@link InvalidArgumentError} when the value names no supported product.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveFirefoxProduct(value: unknown, field: string): FirefoxProduct;
|
|
20
|
+
/**
|
|
21
|
+
* Narrows a raw CLI or prompt value to a supported SPDX licence identifier.
|
|
22
|
+
* @param value - Raw value from `--license` or the setup prompt.
|
|
23
|
+
* @param field - Field name quoted in the refusal (e.g. `--license`).
|
|
24
|
+
* @returns The validated licence identifier.
|
|
25
|
+
* @throws {@link InvalidArgumentError} when the value names no supported licence.
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveProjectLicense(value: unknown, field: string): ProjectLicense;
|
|
16
28
|
/** Validates non-interactive setup options before project scaffolding begins. */
|
|
17
29
|
export declare function validateSetupOptions(options: SetupOptions): void;
|
|
18
30
|
/** Resolves setup inputs from CLI flags and optional interactive prompts. */
|
|
@@ -18,7 +18,14 @@ function renderLicenseTemplate(license, template, vendor, now = new Date()) {
|
|
|
18
18
|
}
|
|
19
19
|
return template.replace(/\[year\]/g, String(now.getFullYear())).replace(/\[fullname\]/g, vendor);
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Narrows a raw CLI or prompt value to a supported Firefox product.
|
|
23
|
+
* @param value - Raw value from `--product` or the setup prompt.
|
|
24
|
+
* @param field - Field name quoted in the refusal (e.g. `--product`).
|
|
25
|
+
* @returns The validated product.
|
|
26
|
+
* @throws {@link InvalidArgumentError} when the value names no supported product.
|
|
27
|
+
*/
|
|
28
|
+
export function resolveFirefoxProduct(value, field) {
|
|
22
29
|
// Matches the sibling `resolveProjectLicense`, which already delegates to
|
|
23
30
|
// its guard. The `||` chain this replaced spelled the member list twice —
|
|
24
31
|
// once as the predicate and once in the message.
|
|
@@ -27,26 +34,19 @@ function resolveFirefoxProduct(value, field) {
|
|
|
27
34
|
}
|
|
28
35
|
throw new InvalidArgumentError(`Invalid product (use: ${FIREFOX_PRODUCTS.join(', ')})`, field);
|
|
29
36
|
}
|
|
30
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Narrows a raw CLI or prompt value to a supported SPDX licence identifier.
|
|
39
|
+
* @param value - Raw value from `--license` or the setup prompt.
|
|
40
|
+
* @param field - Field name quoted in the refusal (e.g. `--license`).
|
|
41
|
+
* @returns The validated licence identifier.
|
|
42
|
+
* @throws {@link InvalidArgumentError} when the value names no supported licence.
|
|
43
|
+
*/
|
|
44
|
+
export function resolveProjectLicense(value, field) {
|
|
31
45
|
if (typeof value === 'string' && isValidProjectLicense(value)) {
|
|
32
46
|
return value;
|
|
33
47
|
}
|
|
34
48
|
throw new InvalidArgumentError('Invalid license (use: EUPL-1.2, MPL-2.0, 0BSD, GPL-2.0-or-later)', field);
|
|
35
49
|
}
|
|
36
|
-
/** Parses an optional Firefox product flag into a typed product value. */
|
|
37
|
-
export function parseFirefoxProductOption(product) {
|
|
38
|
-
if (product === undefined) {
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
return resolveFirefoxProduct(product, '--product');
|
|
42
|
-
}
|
|
43
|
-
/** Parses an optional license flag into a validated SPDX identifier. */
|
|
44
|
-
export function parseProjectLicenseOption(license) {
|
|
45
|
-
if (license === undefined) {
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
48
|
-
return resolveProjectLicense(license, '--license');
|
|
49
|
-
}
|
|
50
50
|
/** Validates non-interactive setup options before project scaffolding begins. */
|
|
51
51
|
export function validateSetupOptions(options) {
|
|
52
52
|
if (options.name !== undefined) {
|
|
@@ -8,7 +8,7 @@ import { ConfigError } from '../errors/config.js';
|
|
|
8
8
|
import { cancel, intro, isCancel, note, outro, spinner } from '../utils/logger.js';
|
|
9
9
|
import { pickDefined } from '../utils/options.js';
|
|
10
10
|
import { FIREFOX_PRODUCTS, PROJECT_LICENSES } from '../utils/validation.js';
|
|
11
|
-
import { buildSetupConfig,
|
|
11
|
+
import { buildSetupConfig, resolveFirefoxProduct, resolveProjectLicense, resolveSetupInputs, validateSetupOptions, writeSetupProjectFiles, } from './setup-support.js';
|
|
12
12
|
/**
|
|
13
13
|
* Runs the setup command.
|
|
14
14
|
* @param projectRoot - Root directory for the project
|
|
@@ -83,16 +83,10 @@ export function registerSetup(program, { withErrorHandling }) {
|
|
|
83
83
|
if (yes === true)
|
|
84
84
|
setupOptions.force = true;
|
|
85
85
|
if (product !== undefined) {
|
|
86
|
-
|
|
87
|
-
if (parsedProduct !== undefined) {
|
|
88
|
-
setupOptions.product = parsedProduct;
|
|
89
|
-
}
|
|
86
|
+
setupOptions.product = resolveFirefoxProduct(product, '--product');
|
|
90
87
|
}
|
|
91
88
|
if (license !== undefined) {
|
|
92
|
-
|
|
93
|
-
if (parsedLicense !== undefined) {
|
|
94
|
-
setupOptions.license = parsedLicense;
|
|
95
|
-
}
|
|
89
|
+
setupOptions.license = resolveProjectLicense(license, '--license');
|
|
96
90
|
}
|
|
97
91
|
await setupCommand(resolve(process.cwd()), setupOptions);
|
|
98
92
|
}));
|
|
@@ -87,7 +87,14 @@ export async function sourceSetCommand(projectRoot, options) {
|
|
|
87
87
|
}
|
|
88
88
|
/** Registers the source command on the CLI program. */
|
|
89
89
|
export function registerSource(program, { getProjectRoot, withErrorHandling }) {
|
|
90
|
-
const source = program
|
|
90
|
+
const source = program
|
|
91
|
+
.command('source')
|
|
92
|
+
.description('Manage Firefox source configuration')
|
|
93
|
+
// No-subcommand contract shared with `patch`/`token`/`furnace`: help on
|
|
94
|
+
// stdout, exit 0 (see `handleParseError` for the groups without an action).
|
|
95
|
+
.action(() => {
|
|
96
|
+
source.outputHelp();
|
|
97
|
+
});
|
|
91
98
|
source
|
|
92
99
|
.command('set')
|
|
93
100
|
.description('Atomically set Firefox source version, product, and optional checksum')
|
|
@@ -95,9 +95,9 @@ export function runStatusCheck(classified, policy) {
|
|
|
95
95
|
const offending = collectStatusCheckOffenders(classified, policy).map((offender) => {
|
|
96
96
|
const preview = offender.files.slice(0, CHECK_FILE_PREVIEW_MAX).join(', ');
|
|
97
97
|
const more = offender.count > CHECK_FILE_PREVIEW_MAX
|
|
98
|
-
? `, +${
|
|
98
|
+
? `, +${offender.count - CHECK_FILE_PREVIEW_MAX} more`
|
|
99
99
|
: '';
|
|
100
|
-
return `${
|
|
100
|
+
return `${offender.count} ${offender.classification} (${preview}${more})`;
|
|
101
101
|
});
|
|
102
102
|
if (offending.length === 0)
|
|
103
103
|
return;
|
|
@@ -219,7 +219,7 @@ export function renderTestCoverageStatus(baseline) {
|
|
|
219
219
|
return;
|
|
220
220
|
}
|
|
221
221
|
const list = coverage.map((path) => ` - ${path}`).join('\n');
|
|
222
|
-
info(`Test packaging coverage: scoped to ${
|
|
222
|
+
info(`Test packaging coverage: scoped to ${coverage.length} path(s):\n${list}\n` +
|
|
223
223
|
'A "fireforge test" over paths outside this list will be refused as uncovered.');
|
|
224
|
-
outro(`Coverage: scoped (${
|
|
224
|
+
outro(`Coverage: scoped (${coverage.length} paths)`);
|
|
225
225
|
}
|
|
@@ -15,7 +15,7 @@ import { GeneralError, InvalidArgumentError } from '../errors/base.js';
|
|
|
15
15
|
import { FIREFORGE_TMP_PATH_PATTERN, pathExists } from '../utils/fs.js';
|
|
16
16
|
import { info, intro, isMachineOutputMode, outro, setMachineOutputMode, warn, } from '../utils/logger.js';
|
|
17
17
|
import { emitMachineError } from '../utils/machine-output.js';
|
|
18
|
-
import { resolveStatusCheckPolicy, runStatusCheck } from './status-check.js';
|
|
18
|
+
import { resolveStatusCheckPolicy, runStatusCheck, } from './status-check.js';
|
|
19
19
|
import { renderJsonStatus, renderJsonSummaryStatus } from './status-json.js';
|
|
20
20
|
import { renderDefaultStatus, renderTestCoverageStatus, renderUnmanagedOnly, } from './status-output.js';
|
|
21
21
|
import { buildOwnershipJsonBlock, collectOwnershipRows, summarizeOwnership, } from './status-ownership.js';
|
|
@@ -253,7 +253,6 @@ async function runStatusCommandBody(projectRoot, options) {
|
|
|
253
253
|
const config = await loadConfig(projectRoot);
|
|
254
254
|
// Shared envelope: see src/utils/machine-output.ts and
|
|
255
255
|
// docs/machine-output.md. `status` was the only command that had one.
|
|
256
|
-
const emitJsonError = emitMachineError;
|
|
257
256
|
// Ownership mode is a flat file→patch table; sources are the manifest's
|
|
258
257
|
// filesAffected, any worktree drift, and the cross-patch
|
|
259
258
|
// duplicate-new-file-creation map produced by walking each patch body. The
|
|
@@ -292,14 +291,14 @@ async function runStatusCommandBody(projectRoot, options) {
|
|
|
292
291
|
// message is kept identical to the helper's on purpose.
|
|
293
292
|
if (!(await pathExists(paths.engine))) {
|
|
294
293
|
if (options.json) {
|
|
295
|
-
|
|
294
|
+
emitMachineError('engine-missing', 'Firefox source not found. Run "fireforge download" first.');
|
|
296
295
|
}
|
|
297
296
|
throw new GeneralError('Firefox source not found. Run "fireforge download" first.');
|
|
298
297
|
}
|
|
299
298
|
// Check if it's a git repository
|
|
300
299
|
if (!(await isGitRepository(paths.engine))) {
|
|
301
300
|
if (options.json) {
|
|
302
|
-
|
|
301
|
+
emitMachineError('engine-not-git', 'Engine directory is not a git repository. Run "fireforge download" to initialize.');
|
|
303
302
|
}
|
|
304
303
|
throw new GeneralError('Engine directory is not a git repository. Run "fireforge download" to initialize.');
|
|
305
304
|
}
|