@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
|
@@ -96,12 +96,6 @@ export interface DoctorCheckDefinition {
|
|
|
96
96
|
*/
|
|
97
97
|
fix?: string;
|
|
98
98
|
}
|
|
99
|
-
/**
|
|
100
|
-
* Resolves a {@link DoctorCheck} to its effective severity. `severity` is the
|
|
101
|
-
* single source of truth; this exists so every consumer of a
|
|
102
|
-
* `DoctorCheck[]` reads it the same way.
|
|
103
|
-
*/
|
|
104
|
-
export declare function resolveDoctorSeverity(check: DoctorCheck): 'ok' | 'warning' | 'error';
|
|
105
99
|
/**
|
|
106
100
|
* Builds a DoctorCheck object representing a successful "OK" check.
|
|
107
101
|
*/
|
|
@@ -4,14 +4,6 @@
|
|
|
4
4
|
* `doctor.ts` and sibling check modules. Kept separate so registries can import
|
|
5
5
|
* these symbols without creating cycles through `doctor.ts`.
|
|
6
6
|
*/
|
|
7
|
-
/**
|
|
8
|
-
* Resolves a {@link DoctorCheck} to its effective severity. `severity` is the
|
|
9
|
-
* single source of truth; this exists so every consumer of a
|
|
10
|
-
* `DoctorCheck[]` reads it the same way.
|
|
11
|
-
*/
|
|
12
|
-
export function resolveDoctorSeverity(check) {
|
|
13
|
-
return check.severity;
|
|
14
|
-
}
|
|
15
7
|
/**
|
|
16
8
|
* Builds a DoctorCheck object representing a successful "OK" check.
|
|
17
9
|
*/
|
|
@@ -27,12 +27,6 @@ export interface OrphanedHarnessWorker {
|
|
|
27
27
|
cpuSeconds: number;
|
|
28
28
|
command: string;
|
|
29
29
|
}
|
|
30
|
-
/**
|
|
31
|
-
* Parses a `ps` TIME (accumulated CPU) value. Implemented in terms of
|
|
32
|
-
* {@link parsePsDuration} — `time=` and `etime=` share one grammar, and a
|
|
33
|
-
* second copy is how the two would drift.
|
|
34
|
-
*/
|
|
35
|
-
export declare function parseCpuTime(time: string): number;
|
|
36
30
|
/**
|
|
37
31
|
* Scans `ps -axo pid=,ppid=,time=,command=` output for orphaned harness
|
|
38
32
|
* workers: PPID 1 (reparented to init/launchd), a multiprocessing
|
|
@@ -29,14 +29,6 @@ import { ok, warning } from './doctor-check-core.js';
|
|
|
29
29
|
const HARNESS_WORKER_COMMAND_PATTERN = /multiprocessing\.(?:spawn|forkserver)\b|multiprocessing\.resource_tracker|from multiprocessing\.\w+ import/;
|
|
30
30
|
/** Minimum accumulated CPU time before a match is reported (10 minutes). */
|
|
31
31
|
const DEFAULT_MIN_CPU_SECONDS = 600;
|
|
32
|
-
/**
|
|
33
|
-
* Parses a `ps` TIME (accumulated CPU) value. Implemented in terms of
|
|
34
|
-
* {@link parsePsDuration} — `time=` and `etime=` share one grammar, and a
|
|
35
|
-
* second copy is how the two would drift.
|
|
36
|
-
*/
|
|
37
|
-
export function parseCpuTime(time) {
|
|
38
|
-
return parsePsDuration(time);
|
|
39
|
-
}
|
|
40
32
|
/**
|
|
41
33
|
* Scans `ps -axo pid=,ppid=,time=,command=` output for orphaned harness
|
|
42
34
|
* workers: PPID 1 (reparented to init/launchd), a multiprocessing
|
|
@@ -57,7 +49,7 @@ export function findOrphanedHarnessWorkers(psOutput, minCpuSeconds = DEFAULT_MIN
|
|
|
57
49
|
continue;
|
|
58
50
|
if (!HARNESS_WORKER_COMMAND_PATTERN.test(command))
|
|
59
51
|
continue;
|
|
60
|
-
const cpuSeconds =
|
|
52
|
+
const cpuSeconds = parsePsDuration(cpuTime);
|
|
61
53
|
if (Number.isNaN(cpuSeconds) || cpuSeconds < minCpuSeconds)
|
|
62
54
|
continue;
|
|
63
55
|
workers.push({ pid, ppid, cpuTime, cpuSeconds, command });
|
|
@@ -68,7 +60,7 @@ export function findOrphanedHarnessWorkers(psOutput, minCpuSeconds = DEFAULT_MIN
|
|
|
68
60
|
async function listSystemProcesses() {
|
|
69
61
|
const result = await exec('ps', ['-axo', 'pid=,ppid=,time=,command='], { timeout: 10000 });
|
|
70
62
|
if (result.exitCode !== 0) {
|
|
71
|
-
throw new Error(`ps exited ${
|
|
63
|
+
throw new Error(`ps exited ${result.exitCode}`);
|
|
72
64
|
}
|
|
73
65
|
return result.stdout;
|
|
74
66
|
}
|
|
@@ -86,10 +78,10 @@ async function runOrphanedHarnessCheck() {
|
|
|
86
78
|
return ok(CHECK_NAME);
|
|
87
79
|
}
|
|
88
80
|
const rows = orphans
|
|
89
|
-
.map((w) => `PID ${
|
|
81
|
+
.map((w) => `PID ${w.pid} (CPU time ${w.cpuTime}): ${w.command.slice(0, 160)}`)
|
|
90
82
|
.join('; ');
|
|
91
83
|
const pids = orphans.map((w) => String(w.pid)).join(' ');
|
|
92
|
-
return warning(CHECK_NAME, `Found ${
|
|
84
|
+
return warning(CHECK_NAME, `Found ${orphans.length} orphaned Python multiprocessing worker(s) — PPID 1 with ` +
|
|
93
85
|
`high accumulated CPU time, the shape a test harness that died at startup leaves behind ` +
|
|
94
86
|
`(field incident: one such worker busy-spun for ~26 days). ${rows}`, `These look like workers orphaned by a crashed test harness. Verify each command line, ` +
|
|
95
87
|
`then terminate with: kill ${pids} (or kill -9 if a process survives). ` +
|
|
@@ -88,7 +88,7 @@ async function runPostRebaseAudit(ctx) {
|
|
|
88
88
|
}
|
|
89
89
|
const browserTomls = await collectBrowserTomlFiles(engineDir);
|
|
90
90
|
if (browserTomls.unreadable.length > 0) {
|
|
91
|
-
issues.push(`could not read ${
|
|
91
|
+
issues.push(`could not read ${browserTomls.unreadable.length} ` +
|
|
92
92
|
`${browserTomls.unreadable.length === 1 ? 'directory' : 'directories'} under ` +
|
|
93
93
|
`browser/base/content/test: ${browserTomls.unreadable.join(', ')}`);
|
|
94
94
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor check comparing the CONFIGURED Firefox source pin against what the
|
|
3
|
+
* engine checkout actually is.
|
|
4
|
+
*
|
|
5
|
+
* `fireforge source set` lands `firefox.version` inside `fireforge.json`,
|
|
6
|
+
* beside hand-maintained policy sections. That is convenient and it is also
|
|
7
|
+
* the whole problem: a routine `git checkout -- fireforge.json` — undoing an
|
|
8
|
+
* accidental reformat, say — silently reverts an uncommitted pin along with
|
|
9
|
+
* it, and nothing afterwards reports that the pin and the checkout have
|
|
10
|
+
* diverged. The tell in the field was a gate flipping green with no change
|
|
11
|
+
* that should have made it green.
|
|
12
|
+
*
|
|
13
|
+
* This is deliberately not a lock and not a refusal: a tree may legitimately
|
|
14
|
+
* be mid-migration between two versions. It only makes a mismatched tree
|
|
15
|
+
* VISIBLE, which is all that was missing.
|
|
16
|
+
*
|
|
17
|
+
* Scope is limited by what is actually recorded. `firefox.product`,
|
|
18
|
+
* `firefox.sha256` and `firefox.candidate` have NO counterpart anywhere on
|
|
19
|
+
* disk — `.fireforge/state.json` records only `downloadedVersion` and
|
|
20
|
+
* `baseCommit` — so there is nothing to compare them against and the check
|
|
21
|
+
* says nothing about them rather than implying it verified them.
|
|
22
|
+
*/
|
|
23
|
+
import type { DoctorCheckDefinition } from './doctor-check-core.js';
|
|
24
|
+
/**
|
|
25
|
+
* Pin-vs-checkout reporting. Needs the loaded config, so it declares the
|
|
26
|
+
* dependency on the config check that populates `ctx.config`; skipped
|
|
27
|
+
* entirely without an engine, where there is nothing to compare.
|
|
28
|
+
*/
|
|
29
|
+
export declare const SOURCE_PIN_DOCTOR_CHECK: DoctorCheckDefinition;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Doctor check comparing the CONFIGURED Firefox source pin against what the
|
|
4
|
+
* engine checkout actually is.
|
|
5
|
+
*
|
|
6
|
+
* `fireforge source set` lands `firefox.version` inside `fireforge.json`,
|
|
7
|
+
* beside hand-maintained policy sections. That is convenient and it is also
|
|
8
|
+
* the whole problem: a routine `git checkout -- fireforge.json` — undoing an
|
|
9
|
+
* accidental reformat, say — silently reverts an uncommitted pin along with
|
|
10
|
+
* it, and nothing afterwards reports that the pin and the checkout have
|
|
11
|
+
* diverged. The tell in the field was a gate flipping green with no change
|
|
12
|
+
* that should have made it green.
|
|
13
|
+
*
|
|
14
|
+
* This is deliberately not a lock and not a refusal: a tree may legitimately
|
|
15
|
+
* be mid-migration between two versions. It only makes a mismatched tree
|
|
16
|
+
* VISIBLE, which is all that was missing.
|
|
17
|
+
*
|
|
18
|
+
* Scope is limited by what is actually recorded. `firefox.product`,
|
|
19
|
+
* `firefox.sha256` and `firefox.candidate` have NO counterpart anywhere on
|
|
20
|
+
* disk — `.fireforge/state.json` records only `downloadedVersion` and
|
|
21
|
+
* `baseCommit` — so there is nothing to compare them against and the check
|
|
22
|
+
* says nothing about them rather than implying it verified them.
|
|
23
|
+
*/
|
|
24
|
+
import { getFirefoxVersion } from '../core/firefox.js';
|
|
25
|
+
import { toError } from '../utils/errors.js';
|
|
26
|
+
import { ok, warning } from './doctor-check-core.js';
|
|
27
|
+
const CHECK_NAME = 'Source pin matches engine';
|
|
28
|
+
/**
|
|
29
|
+
* Reports the configured pin beside the two facts that can contradict it:
|
|
30
|
+
* the engine's own `browser/config/version.txt`, and the version the last
|
|
31
|
+
* download recorded into state.
|
|
32
|
+
*/
|
|
33
|
+
async function runSourcePinCheck(ctx) {
|
|
34
|
+
const pinned = ctx.config?.firefox.version;
|
|
35
|
+
if (pinned === undefined) {
|
|
36
|
+
// The config failed to load (its own check already reported that), so
|
|
37
|
+
// there is no pin to compare. Silence beats a second complaint.
|
|
38
|
+
return ok(CHECK_NAME, 'Skipped: no configured source version');
|
|
39
|
+
}
|
|
40
|
+
let actual;
|
|
41
|
+
try {
|
|
42
|
+
// A blank or whitespace-only version.txt (a truncated write, a partial
|
|
43
|
+
// extraction) carries no version to compare — treat it as absent rather
|
|
44
|
+
// than reporting the engine as being at version "".
|
|
45
|
+
actual = (await getFirefoxVersion(ctx.paths.engine))?.trim() || undefined;
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
// Best-effort: an unreadable version.txt is not a pin mismatch, and a
|
|
49
|
+
// diagnostic must never turn into a failure of its own.
|
|
50
|
+
return ok(CHECK_NAME, `Configured ${pinned}; engine version could not be read (${toError(error).message})`);
|
|
51
|
+
}
|
|
52
|
+
const downloaded = ctx.state.downloadedVersion;
|
|
53
|
+
const mismatches = [];
|
|
54
|
+
if (actual !== undefined && actual !== pinned) {
|
|
55
|
+
mismatches.push(`engine/browser/config/version.txt reads ${actual}`);
|
|
56
|
+
}
|
|
57
|
+
if (downloaded !== undefined && downloaded !== pinned) {
|
|
58
|
+
mismatches.push(`the last download recorded ${downloaded}`);
|
|
59
|
+
}
|
|
60
|
+
if (mismatches.length === 0) {
|
|
61
|
+
const observed = actual ?? downloaded;
|
|
62
|
+
return ok(CHECK_NAME, observed === undefined
|
|
63
|
+
? `Configured ${pinned}; no engine version to compare against yet`
|
|
64
|
+
: `Configured ${pinned}, engine ${observed}`);
|
|
65
|
+
}
|
|
66
|
+
return warning(CHECK_NAME, `fireforge.json pins Firefox ${pinned}, but ${mismatches.join(' and ')}.`, 'If the pin is right, run "fireforge download" to bring the checkout to it. If the ' +
|
|
67
|
+
'CHECKOUT is right, run "fireforge source set --version <version>" to record it — a ' +
|
|
68
|
+
'pin set but not committed is lost by any revert of fireforge.json, which is the most ' +
|
|
69
|
+
'common way these diverge.');
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Pin-vs-checkout reporting. Needs the loaded config, so it declares the
|
|
73
|
+
* dependency on the config check that populates `ctx.config`; skipped
|
|
74
|
+
* entirely without an engine, where there is nothing to compare.
|
|
75
|
+
*/
|
|
76
|
+
export const SOURCE_PIN_DOCTOR_CHECK = {
|
|
77
|
+
name: CHECK_NAME,
|
|
78
|
+
skipIf: (ctx) => !ctx.engineExists,
|
|
79
|
+
dependsOn: ['fireforge.json is valid'],
|
|
80
|
+
run: runSourcePinCheck,
|
|
81
|
+
};
|
|
@@ -21,6 +21,8 @@ export declare function validateCheckDependencies(checks: readonly DoctorCheckDe
|
|
|
21
21
|
* context-population dependency chain (see {@link DOCTOR_CHECKS}) must
|
|
22
22
|
* also update this list, which gives us a single place to notice and
|
|
23
23
|
* think through the consequences.
|
|
24
|
+
*
|
|
25
|
+
* @internal Exported only so tests can reach it; not part of the public surface.
|
|
24
26
|
*/
|
|
25
27
|
export declare const DOCTOR_CHECK_ORDER: readonly string[];
|
|
26
28
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
-
import { configExists, getProjectPaths, loadConfig, loadState
|
|
3
|
+
import { configExists, getProjectPaths, loadConfig, loadState } from '../core/config.js';
|
|
4
4
|
import { furnaceConfigExists as checkFurnaceConfigExists } from '../core/furnace-config.js';
|
|
5
5
|
import { getCurrentBranch, getHead, isGitRepository, isMissingHeadError } from '../core/git.js';
|
|
6
6
|
import { ensureGit } from '../core/git-base.js';
|
|
@@ -14,13 +14,15 @@ import { pathExists } from '../utils/fs.js';
|
|
|
14
14
|
import { error, info, intro, outro, success, warn } from '../utils/logger.js';
|
|
15
15
|
import { addWaitLockOption } from '../utils/options.js';
|
|
16
16
|
import { findExecutable } from '../utils/process.js';
|
|
17
|
-
import { failure, ok,
|
|
17
|
+
import { failure, ok, warning } from './doctor-check-core.js';
|
|
18
18
|
import { EXTERNAL_TOOLCHAIN_DOCTOR_CHECK } from './doctor-external-toolchains.js';
|
|
19
19
|
import { FURNACE_DOCTOR_CHECKS } from './doctor-furnace.js';
|
|
20
20
|
import { ORPHANED_HARNESS_DOCTOR_CHECK } from './doctor-orphaned-harness.js';
|
|
21
21
|
import { PATCH_MANIFEST_CONSISTENCY_CHECK } from './doctor-patch-manifest.js';
|
|
22
22
|
import { POST_REBASE_AUDIT_CHECK } from './doctor-post-rebase-audit.js';
|
|
23
|
+
import { SOURCE_PIN_DOCTOR_CHECK } from './doctor-source-pin.js';
|
|
23
24
|
import { inspectEngineWorkingTree } from './doctor-working-tree.js';
|
|
25
|
+
import { clearPendingResolution } from './pending-resolution.js';
|
|
24
26
|
import { collectPatchQueueHealth } from './verify.js';
|
|
25
27
|
/**
|
|
26
28
|
* Runs a single check definition, converting thrown errors into
|
|
@@ -201,16 +203,13 @@ const DOCTOR_CHECKS = [
|
|
|
201
203
|
if (health.errorCount > 0) {
|
|
202
204
|
return failure('Pending Resolution', `Refusing to clear pending resolution for ${patchFilename}: patch queue health check found ${health.errorCount} error(s).`, 'Run "fireforge verify" for details, fix the queue, then retry "fireforge doctor --clear-resolution".');
|
|
203
205
|
}
|
|
204
|
-
await
|
|
205
|
-
const next = { ...current };
|
|
206
|
-
delete next.pendingResolution;
|
|
207
|
-
return next;
|
|
208
|
-
});
|
|
206
|
+
await clearPendingResolution(ctx.projectRoot);
|
|
209
207
|
return ok('Pending Resolution');
|
|
210
208
|
}
|
|
211
209
|
return failure('Pending Resolution', `You are currently resolving a conflict for patch ${patchFilename}.`, 'Build and Export commands may behave unexpectedly until "fireforge resolve" is completed. If the queue now verifies cleanly, run "fireforge doctor --clear-resolution" to discard the stale marker.');
|
|
212
210
|
},
|
|
213
211
|
},
|
|
212
|
+
SOURCE_PIN_DOCTOR_CHECK,
|
|
214
213
|
{
|
|
215
214
|
name: 'Engine is git repository',
|
|
216
215
|
skipIf: (ctx) => !ctx.engineExists,
|
|
@@ -320,6 +319,8 @@ validateCheckDependencies(DOCTOR_CHECKS);
|
|
|
320
319
|
* context-population dependency chain (see {@link DOCTOR_CHECKS}) must
|
|
321
320
|
* also update this list, which gives us a single place to notice and
|
|
322
321
|
* think through the consequences.
|
|
322
|
+
*
|
|
323
|
+
* @internal Exported only so tests can reach it; not part of the public surface.
|
|
323
324
|
*/
|
|
324
325
|
export const DOCTOR_CHECK_ORDER = DOCTOR_CHECKS.map((check) => check.name);
|
|
325
326
|
/**
|
|
@@ -334,7 +335,7 @@ export function reportDoctorResults(checks, mutations = []) {
|
|
|
334
335
|
let warningCount = 0;
|
|
335
336
|
let failedCount = 0;
|
|
336
337
|
for (const check of checks) {
|
|
337
|
-
const severity =
|
|
338
|
+
const severity = check.severity;
|
|
338
339
|
if (severity === 'warning') {
|
|
339
340
|
warn(`! ${check.name}: ${check.message}`);
|
|
340
341
|
if (check.fix) {
|
|
@@ -110,11 +110,6 @@ function closeRestoreSpinner(restoreSpinner, result) {
|
|
|
110
110
|
}
|
|
111
111
|
restoreSpinner.stop('Patch-touched files restored');
|
|
112
112
|
}
|
|
113
|
-
async function clearStaleFurnaceApplyState(projectRoot) {
|
|
114
|
-
// --force installs a new baseCommit, which invalidates every applied
|
|
115
|
-
// checksum in furnace-state.json.
|
|
116
|
-
await clearAppliedFurnaceState(projectRoot);
|
|
117
|
-
}
|
|
118
113
|
async function activateReplacementEngine(args) {
|
|
119
114
|
const { engineDir, replacementDir, backupDir } = args;
|
|
120
115
|
await rename(engineDir, backupDir);
|
|
@@ -155,28 +150,39 @@ async function restorePreviousEngine(args) {
|
|
|
155
150
|
}
|
|
156
151
|
}
|
|
157
152
|
async function downloadAndExtractFirefox(args) {
|
|
158
|
-
const { version, product, engineDir, cacheDir, sha256, candidate } = args;
|
|
153
|
+
const { version, product, engineDir, cacheDir, sha256, candidate, allowUnverifiedDownload } = args;
|
|
159
154
|
let s = spinner(`Downloading Firefox ${version}...`);
|
|
160
155
|
let lastPercent = 0;
|
|
161
156
|
const phaseState = { value: 'download' };
|
|
162
157
|
try {
|
|
163
|
-
await downloadFirefoxSource(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
158
|
+
await downloadFirefoxSource({
|
|
159
|
+
version: version,
|
|
160
|
+
product: product,
|
|
161
|
+
destDir: engineDir,
|
|
162
|
+
cacheDir: cacheDir,
|
|
163
|
+
onProgress: (downloaded, total) => {
|
|
164
|
+
if (total <= 0)
|
|
165
|
+
return;
|
|
166
|
+
const percent = Math.floor((downloaded / total) * 100);
|
|
167
|
+
if (percent !== lastPercent && percent % 5 === 0) {
|
|
168
|
+
s.message(`Downloading Firefox ${version}... ${percent}% (${formatBytes(downloaded)} / ${formatBytes(total)})`);
|
|
169
|
+
lastPercent = percent;
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
onPhase: (phase) => {
|
|
173
|
+
if (phase === 'extract' && phaseState.value === 'download') {
|
|
174
|
+
s.stop(`Firefox ${version} downloaded`);
|
|
175
|
+
phaseState.value = 'extract';
|
|
176
|
+
s = spinner(`Extracting Firefox ${version}... (decompressing ~600 MB of source; typically 30–90s)`);
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
expectedSha256: sha256,
|
|
180
|
+
onPhaseProgress: (message) => {
|
|
181
|
+
s.message(message);
|
|
182
|
+
},
|
|
183
|
+
candidate: candidate,
|
|
184
|
+
integrity: allowUnverifiedDownload === undefined ? undefined : { allowUnverifiedDownload },
|
|
185
|
+
});
|
|
180
186
|
s.stop(phaseState.value === 'extract'
|
|
181
187
|
? `Firefox ${version} extracted`
|
|
182
188
|
: `Firefox ${version} downloaded`);
|
|
@@ -235,7 +241,9 @@ async function initializeDownloadedEngine(args) {
|
|
|
235
241
|
throw error;
|
|
236
242
|
}
|
|
237
243
|
if (replacementActivated) {
|
|
238
|
-
|
|
244
|
+
// --force installs a new baseCommit, which invalidates every applied
|
|
245
|
+
// checksum in furnace-state.json.
|
|
246
|
+
await clearAppliedFurnaceState(projectRoot);
|
|
239
247
|
}
|
|
240
248
|
await updateState(projectRoot, {
|
|
241
249
|
downloadedVersion: version,
|
|
@@ -389,6 +397,9 @@ export async function downloadCommand(projectRoot, options) {
|
|
|
389
397
|
...(config.firefox.candidate !== undefined
|
|
390
398
|
? { candidate: config.firefox.candidate }
|
|
391
399
|
: {}),
|
|
400
|
+
...(config.firefox.allowUnverifiedDownload !== undefined
|
|
401
|
+
? { allowUnverifiedDownload: config.firefox.allowUnverifiedDownload }
|
|
402
|
+
: {}),
|
|
392
403
|
});
|
|
393
404
|
if (replacementEngineDir && backupEngineDir) {
|
|
394
405
|
warn('Activating replacement engine directory...');
|
|
@@ -19,10 +19,20 @@ import { info, intro, outro, spinner } from '../utils/logger.js';
|
|
|
19
19
|
import { addWaitLockOption, pickDefined, resolveWaitLockSeconds } from '../utils/options.js';
|
|
20
20
|
import { renderDryRunPreview } from './export-flow.js';
|
|
21
21
|
import { autoFixLicenseHeaders, promptExportPatchMetadata, runPatchLint, runSupersedeAndOverlapGates, } from './export-shared.js';
|
|
22
|
+
/**
|
|
23
|
+
* The engine paths an export-all entry owns: its current file plus, for a
|
|
24
|
+
* rename, the path it came from. Spelled once because three call sites had
|
|
25
|
+
* copied the same `filter((value): value is string => !!value)` narrowing.
|
|
26
|
+
* @param entry - Export entry carrying `file` and an optional `originalPath`.
|
|
27
|
+
* @returns Both paths when the entry was renamed, otherwise just `file`.
|
|
28
|
+
*/
|
|
29
|
+
function ownedPaths(entry) {
|
|
30
|
+
return [entry.file, entry.originalPath].filter((value) => !!value);
|
|
31
|
+
}
|
|
22
32
|
async function checkBrandingManagedFiles(paths, config) {
|
|
23
33
|
const changedFiles = await getWorkingTreeStatus(paths.engine);
|
|
24
34
|
const brandingManagedFiles = changedFiles
|
|
25
|
-
.flatMap((entry) =>
|
|
35
|
+
.flatMap((entry) => ownedPaths(entry))
|
|
26
36
|
.filter((file) => isBrandingManagedPath(file, config.binaryName));
|
|
27
37
|
if (brandingManagedFiles.length > 0) {
|
|
28
38
|
throw new GeneralError('Export-all refuses to capture tool-managed branding changes by default.\n\n' +
|
|
@@ -52,7 +62,7 @@ async function resolveFurnaceExclusionPolicy(paths, projectRoot, excludeFurnace)
|
|
|
52
62
|
const rawStatus = await getWorkingTreeStatus(paths.engine);
|
|
53
63
|
const changedFiles = await expandUntrackedDirectoryEntries(paths.engine, rawStatus);
|
|
54
64
|
const furnaceManagedFiles = changedFiles
|
|
55
|
-
.flatMap((entry) =>
|
|
65
|
+
.flatMap((entry) => ownedPaths(entry))
|
|
56
66
|
.filter((file) => [...prefixes].some((prefix) => file.startsWith(prefix)));
|
|
57
67
|
if (furnaceManagedFiles.length === 0)
|
|
58
68
|
return new Set();
|
|
@@ -196,7 +206,7 @@ export async function exportAllCommand(projectRoot, options = {}) {
|
|
|
196
206
|
const allChanged = await expandUntrackedDirectoryEntries(paths.engine, rawChanged);
|
|
197
207
|
const nonFurnacePaths = [
|
|
198
208
|
...new Set(allChanged
|
|
199
|
-
.flatMap((entry) =>
|
|
209
|
+
.flatMap((entry) => ownedPaths(entry))
|
|
200
210
|
.filter((file) => !furnaceExcluded.has(file))),
|
|
201
211
|
].sort();
|
|
202
212
|
scopedExportPaths = nonFurnacePaths;
|
|
@@ -254,7 +264,14 @@ export async function exportAllCommand(projectRoot, options = {}) {
|
|
|
254
264
|
const patchQueueCtx = (await pathExists(paths.patches))
|
|
255
265
|
? await buildPatchQueueContext(paths.patches)
|
|
256
266
|
: undefined;
|
|
257
|
-
await runPatchLint(
|
|
267
|
+
await runPatchLint({
|
|
268
|
+
engineDir: paths.engine,
|
|
269
|
+
filesAffected,
|
|
270
|
+
diffContent: diff,
|
|
271
|
+
config,
|
|
272
|
+
skipLint: options.skipLint,
|
|
273
|
+
patchQueueCtx,
|
|
274
|
+
});
|
|
258
275
|
// Dry-run: enumerate filename, metadata, and supersede coverage without
|
|
259
276
|
// writing. Mirrors `fireforge export --dry-run` so the same preview
|
|
260
277
|
// surface is available for both targeted and aggregate exports. Runs
|
|
@@ -10,14 +10,12 @@
|
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import {} from '../core/destructive.js';
|
|
12
12
|
import { detectMovedCodeOverlaps, formatAdoptThenSplitRemedy, } from '../core/export-moved-code-shape.js';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { buildModifiedFileAdditionsFromDiff, buildPatchQueueContext, detectNewFilesInDiff, lintPatchQueue, } from '../core/patch-lint.js';
|
|
13
|
+
import { buildPlanExportInput, findAllPatchesForFilesWithDetails, patchNameSlug, planExport, } from '../core/patch-export.js';
|
|
14
|
+
import { buildPatchQueueContext, lintPatchQueue } from '../core/patch-lint.js';
|
|
16
15
|
import { withPatchDirectoryLock } from '../core/patch-lock.js';
|
|
17
16
|
import { addPatchToManifest, loadPatchesManifest, loadPatchesManifestForWrite, renumberPatchesInManifest, resolvePatchIdentifier, savePatchesManifest, } from '../core/patch-manifest.js';
|
|
18
|
-
import { requirePatchOrder } from '../core/patch-parse.js';
|
|
17
|
+
import { formatPatchOrder, requirePatchOrder } from '../core/patch-parse.js';
|
|
19
18
|
import { applyRenameMapToManifest, buildProjectedManifest, enforcePatchPolicy, } from '../core/patch-policy.js';
|
|
20
|
-
import { extractNewFileContentFromDiff } from '../core/patch-transform.js';
|
|
21
19
|
import { GeneralError, InvalidArgumentError } from '../errors/base.js';
|
|
22
20
|
import { toError } from '../utils/errors.js';
|
|
23
21
|
import { pathExists, readText, removeFile, writeText } from '../utils/fs.js';
|
|
@@ -25,6 +23,8 @@ import { info, warn } from '../utils/logger.js';
|
|
|
25
23
|
import { groupProjectedPlacementErrors } from './export-placement-conflicts.js';
|
|
26
24
|
import { assertPlacementAvoidsReservedRanges } from './export-placement-policy.js';
|
|
27
25
|
import { findPartialOwnershipOverlap } from './export-shared.js';
|
|
26
|
+
import { projectEntryBody } from './patch/entry-projection.js';
|
|
27
|
+
import { getSortedRenameEntries, renameMapsEqual } from './patch-rename-map.js';
|
|
28
28
|
function buildFilenameForPlacement(category, name, order, width) {
|
|
29
29
|
const padded = String(order).padStart(Math.max(3, width), '0');
|
|
30
30
|
return `${padded}-${category}-${patchNameSlug(name, category)}.patch`;
|
|
@@ -35,9 +35,6 @@ function prefixWidthForPatches(manifestPatches, requestedOrder) {
|
|
|
35
35
|
return Math.max(width, match?.[1]?.length ?? 3, String(requestedOrder).length);
|
|
36
36
|
}, 3);
|
|
37
37
|
}
|
|
38
|
-
function getSortedRenameEntries(renameMap) {
|
|
39
|
-
return Array.from(renameMap.entries()).sort((a, b) => a[1].newOrder - b[1].newOrder);
|
|
40
|
-
}
|
|
41
38
|
/**
|
|
42
39
|
* Structural equality for placement plans — used by placement-mode export
|
|
43
40
|
* and `patch split` to verify the queue did not change between the
|
|
@@ -47,20 +44,7 @@ export function placementPlansEqual(left, right) {
|
|
|
47
44
|
if (left.insertionOrder !== right.insertionOrder || left.newFilename !== right.newFilename) {
|
|
48
45
|
return false;
|
|
49
46
|
}
|
|
50
|
-
|
|
51
|
-
const rightEntries = getSortedRenameEntries(right.renameMap);
|
|
52
|
-
if (leftEntries.length !== rightEntries.length) {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
return leftEntries.every(([leftFilename, leftEntry], index) => {
|
|
56
|
-
const rightTuple = rightEntries[index];
|
|
57
|
-
if (!rightTuple)
|
|
58
|
-
return false;
|
|
59
|
-
const [rightFilename, rightEntry] = rightTuple;
|
|
60
|
-
return (leftFilename === rightFilename &&
|
|
61
|
-
leftEntry.newFilename === rightEntry.newFilename &&
|
|
62
|
-
leftEntry.newOrder === rightEntry.newOrder);
|
|
63
|
-
});
|
|
47
|
+
return renameMapsEqual(left.renameMap, right.renameMap);
|
|
64
48
|
}
|
|
65
49
|
/**
|
|
66
50
|
* Computes the shift map that moves existing patches out of the requested
|
|
@@ -72,7 +56,7 @@ export function computePlacementPlan(manifestPatches, newPatchCategory, newPatch
|
|
|
72
56
|
// Failing fast here prevents a NaN requestedOrder from producing a
|
|
73
57
|
// filename like "NaN-ui-foo.patch".
|
|
74
58
|
if (!Number.isInteger(requestedOrder) || requestedOrder <= 0) {
|
|
75
|
-
throw new InvalidArgumentError(`computePlacementPlan requires a positive integer order, got ${
|
|
59
|
+
throw new InvalidArgumentError(`computePlacementPlan requires a positive integer order, got ${requestedOrder}.`, 'requestedOrder');
|
|
76
60
|
}
|
|
77
61
|
const sorted = [...manifestPatches].sort((a, b) => a.order - b.order);
|
|
78
62
|
const renameMap = new Map();
|
|
@@ -90,7 +74,7 @@ export function computePlacementPlan(manifestPatches, newPatchCategory, newPatch
|
|
|
90
74
|
}
|
|
91
75
|
const newOrder = cursor + 1;
|
|
92
76
|
const currentRest = patch.filename.replace(/^\d+-/, '');
|
|
93
|
-
const newFilename = `${
|
|
77
|
+
const newFilename = `${formatPatchOrder(newOrder, prefixWidth)}-${currentRest}`;
|
|
94
78
|
renameMap.set(patch.filename, { newOrder, newFilename });
|
|
95
79
|
cursor = newOrder;
|
|
96
80
|
}
|
|
@@ -109,11 +93,11 @@ export function computePlacementPlan(manifestPatches, newPatchCategory, newPatch
|
|
|
109
93
|
*/
|
|
110
94
|
export function computeExactPlacementPlan(manifestPatches, newPatchCategory, newPatchName, requestedOrder) {
|
|
111
95
|
if (!Number.isInteger(requestedOrder) || requestedOrder <= 0) {
|
|
112
|
-
throw new InvalidArgumentError(`--order must be a positive integer, got ${
|
|
96
|
+
throw new InvalidArgumentError(`--order must be a positive integer, got ${requestedOrder}.`, '--order');
|
|
113
97
|
}
|
|
114
98
|
const occupied = manifestPatches.find((patch) => patch.order === requestedOrder);
|
|
115
99
|
if (occupied) {
|
|
116
|
-
throw new InvalidArgumentError(`--order ${
|
|
100
|
+
throw new InvalidArgumentError(`--order ${requestedOrder} is already occupied by ${occupied.filename}. ` +
|
|
117
101
|
'Choose an unused order or use --before/--after for positional insertion.', '--order');
|
|
118
102
|
}
|
|
119
103
|
return {
|
|
@@ -140,7 +124,7 @@ export async function resolvePlacementPlan(patchesDir, options, category, name,
|
|
|
140
124
|
// function is called directly from the command body which could
|
|
141
125
|
// reach here with a NaN/0/negative value passed in via test harness.
|
|
142
126
|
if (!Number.isInteger(options.order) || options.order <= 0) {
|
|
143
|
-
throw new InvalidArgumentError(`--order must be a positive integer, got ${
|
|
127
|
+
throw new InvalidArgumentError(`--order must be a positive integer, got ${options.order}.`, '--order');
|
|
144
128
|
}
|
|
145
129
|
return computeExactPlacementPlan(existingPatches, category, name, options.order);
|
|
146
130
|
}
|
|
@@ -166,21 +150,6 @@ export async function resolvePlacementPlan(patchesDir, options, category, name,
|
|
|
166
150
|
assertPlacementAvoidsReservedRanges(plan, existingPatches, config);
|
|
167
151
|
return plan;
|
|
168
152
|
}
|
|
169
|
-
/**
|
|
170
|
-
* Extracts the newly-created files a diff would produce and builds the
|
|
171
|
-
* `newFiles` map in the shape expected by {@link PatchQueueEntry}. Used
|
|
172
|
-
* to build a faithful synthetic entry for the pending patch when
|
|
173
|
-
* projecting through cross-patch lint — without this the forward-import
|
|
174
|
-
* rule cannot see imports authored by the new patch itself.
|
|
175
|
-
*/
|
|
176
|
-
function buildNewFilesFromDiff(diff) {
|
|
177
|
-
const newFiles = new Map();
|
|
178
|
-
const newFilePaths = detectNewFilesInDiff(diff);
|
|
179
|
-
for (const path of newFilePaths) {
|
|
180
|
-
newFiles.set(path, extractNewFileContentFromDiff(diff, path));
|
|
181
|
-
}
|
|
182
|
-
return newFiles;
|
|
183
|
-
}
|
|
184
153
|
/**
|
|
185
154
|
* Projects the placement through cross-patch lint to detect forward-imports
|
|
186
155
|
* the renumber would introduce *or* that the new patch itself would
|
|
@@ -205,9 +174,7 @@ export async function projectPlacementForLint(patchesDir, plan, diff, config) {
|
|
|
205
174
|
filename: plan.newFilename,
|
|
206
175
|
order: plan.insertionOrder,
|
|
207
176
|
metadata: null,
|
|
208
|
-
diff,
|
|
209
|
-
newFiles: buildNewFilesFromDiff(diff),
|
|
210
|
-
modifiedFileAdditions: buildModifiedFileAdditionsFromDiff(diff),
|
|
177
|
+
...projectEntryBody(diff),
|
|
211
178
|
});
|
|
212
179
|
projectedEntries.sort((a, b) => a.order - b.order || a.filename.localeCompare(b.filename));
|
|
213
180
|
const policyRest = baseCtx.patchPolicy ? { patchPolicy: baseCtx.patchPolicy } : {};
|
|
@@ -309,7 +276,7 @@ export async function commitPlacementExport(input) {
|
|
|
309
276
|
}
|
|
310
277
|
// Normalize identically to commitExportedPatch — the two export
|
|
311
278
|
// paths must produce one artifact contract for the same diff.
|
|
312
|
-
await writeText(patchPath,
|
|
279
|
+
await writeText(patchPath, input.diff);
|
|
313
280
|
await addPatchToManifest(input.patchesDir, {
|
|
314
281
|
...input.metadata,
|
|
315
282
|
filename: currentPlan.newFilename,
|
|
@@ -395,19 +362,7 @@ export async function renderDryRunPreview(input) {
|
|
|
395
362
|
const overlap = manifest !== null
|
|
396
363
|
? findPartialOwnershipOverlap(manifest, input.filesAffected, supersedingFilenames)
|
|
397
364
|
: new Map();
|
|
398
|
-
const plan = await planExport(
|
|
399
|
-
patchesDir: input.patchesDir,
|
|
400
|
-
category: input.category,
|
|
401
|
-
name: input.name,
|
|
402
|
-
description: input.description,
|
|
403
|
-
filesAffected: input.filesAffected,
|
|
404
|
-
sourceEsrVersion: input.sourceEsrVersion,
|
|
405
|
-
...(input.sourceProduct !== undefined ? { sourceProduct: input.sourceProduct } : {}),
|
|
406
|
-
...(input.sourceVersion !== undefined ? { sourceVersion: input.sourceVersion } : {}),
|
|
407
|
-
...(input.tier !== undefined ? { tier: input.tier } : {}),
|
|
408
|
-
...(input.lintIgnore !== undefined ? { lintIgnore: input.lintIgnore } : {}),
|
|
409
|
-
...(input.config !== undefined ? { config: input.config } : {}),
|
|
410
|
-
});
|
|
365
|
+
const plan = await planExport(buildPlanExportInput(input));
|
|
411
366
|
if (input.config !== undefined) {
|
|
412
367
|
enforcePatchPolicy({
|
|
413
368
|
config: input.config,
|
|
@@ -441,7 +396,7 @@ export async function renderDryRunPreview(input) {
|
|
|
441
396
|
}
|
|
442
397
|
if (overlap.size > 0) {
|
|
443
398
|
const entries = [...overlap.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
444
|
-
warn(`\n[dry-run] Would create cross-patch ownership overlap on ${
|
|
399
|
+
warn(`\n[dry-run] Would create cross-patch ownership overlap on ${entries.length} file${entries.length === 1 ? '' : 's'}:`);
|
|
445
400
|
for (const [file, owners] of entries) {
|
|
446
401
|
warn(` - ${file} already claimed by: ${owners.join(', ')}`);
|
|
447
402
|
}
|
|
@@ -66,10 +66,10 @@ export function groupProjectedPlacementErrors(projectedErrors, baselineErrors, p
|
|
|
66
66
|
details.push(` ${formatPatchLintIssue(issue)}`);
|
|
67
67
|
}
|
|
68
68
|
};
|
|
69
|
-
pushGroup(`errors in the exported patch content (${
|
|
69
|
+
pushGroup(`errors in the exported patch content (${exported.length}):`, exported);
|
|
70
70
|
pushGroup(`consequences of renumbering existing patches to make room at ordinal ` +
|
|
71
|
-
`${
|
|
71
|
+
`${plan.insertionOrder} (${renumbering.length}) — these come from ` +
|
|
72
72
|
'renumbering, NOT from the exported content:', renumbering);
|
|
73
|
-
pushGroup(`errors already present in the queue before this export (${
|
|
73
|
+
pushGroup(`errors already present in the queue before this export (${preExisting.length}):`, preExisting);
|
|
74
74
|
return details;
|
|
75
75
|
}
|
|
@@ -10,7 +10,7 @@ import { loadPatchesManifest } from '../core/patch-manifest.js';
|
|
|
10
10
|
import { applyRenameMapToManifest, buildProjectedManifest, enforcePatchPolicy, } from '../core/patch-policy.js';
|
|
11
11
|
import { buildPatchSourceMetadata } from '../core/patch-source-metadata.js';
|
|
12
12
|
import { InvalidArgumentError } from '../errors/base.js';
|
|
13
|
-
import {
|
|
13
|
+
import { proceedAfterDecision } from './destructive-decision.js';
|
|
14
14
|
import { placementSummary, projectPlacementForLint, resolvePlacementPlan, } from './export-flow.js';
|
|
15
15
|
/**
|
|
16
16
|
* Spreadable optional metadata (`tier`, `lintIgnore`) derived from the
|
|
@@ -85,14 +85,8 @@ export async function gatePlacementPlan(args) {
|
|
|
85
85
|
unsafeOverride: options.forceUnsafe === true,
|
|
86
86
|
conflicts,
|
|
87
87
|
});
|
|
88
|
-
if (decision
|
|
89
|
-
outro('Dry run complete — no changes made');
|
|
88
|
+
if (!proceedAfterDecision(decision, 'Export cancelled'))
|
|
90
89
|
return 'stop';
|
|
91
|
-
}
|
|
92
|
-
if (decision === 'declined') {
|
|
93
|
-
outro('Export cancelled');
|
|
94
|
-
return 'stop';
|
|
95
|
-
}
|
|
96
90
|
}
|
|
97
91
|
else if (conflicts && options.forceUnsafe !== true) {
|
|
98
92
|
s.stop();
|