@hominis/fireforge 0.45.0 → 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 +80 -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.d.ts +2 -0
- package/dist/src/commands/doctor.js +7 -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 -45
- 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 -34
- 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 -8
- 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 +24 -2
- package/dist/src/commands/re-export-adjacent.js +32 -16
- package/dist/src/commands/re-export-bulk-scan.js +6 -10
- package/dist/src/commands/re-export-drift.js +4 -4
- package/dist/src/commands/re-export-files.d.ts +2 -3
- package/dist/src/commands/re-export-files.js +38 -29
- 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-scan.js +5 -2
- 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 +28 -11
- 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 +31 -7
- 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 +5 -3
- package/dist/src/commands/test-run.js +25 -18
- package/dist/src/commands/test-stale-gate.js +4 -4
- 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 +42 -5
- 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 +110 -10
- 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.js +3 -2
- package/dist/src/core/build-overwrite-guard.js +1 -1
- package/dist/src/core/build-prepare.d.ts +0 -8
- package/dist/src/core/build-prepare.js +7 -12
- 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.js +1 -1
- 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 +14 -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.js +8 -7
- 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 +4 -3
- package/dist/src/core/mach-known-noise-filter.js +7 -9
- 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 +39 -53
- package/dist/src/core/mach.js +13 -81
- 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 +6 -12
- 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 +37 -24
- 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.js +3 -3
- 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.js +1 -1
- 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.d.ts +14 -9
- package/dist/src/core/patch-lint.js +4 -3
- 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 +0 -8
- package/dist/src/core/patch-transform.js +0 -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 +6 -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 +2 -2
- package/dist/src/core/test-stale-check.d.ts +0 -2
- package/dist/src/core/test-stale-check.js +2 -2
- 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.js +20 -28
- 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 +77 -123
- package/dist/src/types/config.d.ts +34 -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.js +8 -13
- 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 +89 -1
- package/docs/exit-codes.md +31 -19
- 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 +8 -1
- 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
|
@@ -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
|
}
|
|
@@ -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,7 +14,7 @@ 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';
|
|
@@ -22,6 +22,7 @@ import { PATCH_MANIFEST_CONSISTENCY_CHECK } from './doctor-patch-manifest.js';
|
|
|
22
22
|
import { POST_REBASE_AUDIT_CHECK } from './doctor-post-rebase-audit.js';
|
|
23
23
|
import { SOURCE_PIN_DOCTOR_CHECK } from './doctor-source-pin.js';
|
|
24
24
|
import { inspectEngineWorkingTree } from './doctor-working-tree.js';
|
|
25
|
+
import { clearPendingResolution } from './pending-resolution.js';
|
|
25
26
|
import { collectPatchQueueHealth } from './verify.js';
|
|
26
27
|
/**
|
|
27
28
|
* Runs a single check definition, converting thrown errors into
|
|
@@ -202,11 +203,7 @@ const DOCTOR_CHECKS = [
|
|
|
202
203
|
if (health.errorCount > 0) {
|
|
203
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".');
|
|
204
205
|
}
|
|
205
|
-
await
|
|
206
|
-
const next = { ...current };
|
|
207
|
-
delete next.pendingResolution;
|
|
208
|
-
return next;
|
|
209
|
-
});
|
|
206
|
+
await clearPendingResolution(ctx.projectRoot);
|
|
210
207
|
return ok('Pending Resolution');
|
|
211
208
|
}
|
|
212
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.');
|
|
@@ -322,6 +319,8 @@ validateCheckDependencies(DOCTOR_CHECKS);
|
|
|
322
319
|
* context-population dependency chain (see {@link DOCTOR_CHECKS}) must
|
|
323
320
|
* also update this list, which gives us a single place to notice and
|
|
324
321
|
* think through the consequences.
|
|
322
|
+
*
|
|
323
|
+
* @internal Exported only so tests can reach it; not part of the public surface.
|
|
325
324
|
*/
|
|
326
325
|
export const DOCTOR_CHECK_ORDER = DOCTOR_CHECKS.map((check) => check.name);
|
|
327
326
|
/**
|
|
@@ -336,7 +335,7 @@ export function reportDoctorResults(checks, mutations = []) {
|
|
|
336
335
|
let warningCount = 0;
|
|
337
336
|
let failedCount = 0;
|
|
338
337
|
for (const check of checks) {
|
|
339
|
-
const severity =
|
|
338
|
+
const severity = check.severity;
|
|
340
339
|
if (severity === 'warning') {
|
|
341
340
|
warn(`! ${check.name}: ${check.message}`);
|
|
342
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, 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 { buildNewFileTextProjection } 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
|
}
|
|
@@ -190,9 +174,7 @@ export async function projectPlacementForLint(patchesDir, plan, diff, config) {
|
|
|
190
174
|
filename: plan.newFilename,
|
|
191
175
|
order: plan.insertionOrder,
|
|
192
176
|
metadata: null,
|
|
193
|
-
diff,
|
|
194
|
-
newFiles: buildNewFileTextProjection(diff),
|
|
195
|
-
modifiedFileAdditions: buildModifiedFileAdditionsFromDiff(diff),
|
|
177
|
+
...projectEntryBody(diff),
|
|
196
178
|
});
|
|
197
179
|
projectedEntries.sort((a, b) => a.order - b.order || a.filename.localeCompare(b.filename));
|
|
198
180
|
const policyRest = baseCtx.patchPolicy ? { patchPolicy: baseCtx.patchPolicy } : {};
|
|
@@ -294,7 +276,7 @@ export async function commitPlacementExport(input) {
|
|
|
294
276
|
}
|
|
295
277
|
// Normalize identically to commitExportedPatch — the two export
|
|
296
278
|
// paths must produce one artifact contract for the same diff.
|
|
297
|
-
await writeText(patchPath,
|
|
279
|
+
await writeText(patchPath, input.diff);
|
|
298
280
|
await addPatchToManifest(input.patchesDir, {
|
|
299
281
|
...input.metadata,
|
|
300
282
|
filename: currentPlan.newFilename,
|
|
@@ -380,19 +362,7 @@ export async function renderDryRunPreview(input) {
|
|
|
380
362
|
const overlap = manifest !== null
|
|
381
363
|
? findPartialOwnershipOverlap(manifest, input.filesAffected, supersedingFilenames)
|
|
382
364
|
: new Map();
|
|
383
|
-
const plan = await planExport(
|
|
384
|
-
patchesDir: input.patchesDir,
|
|
385
|
-
category: input.category,
|
|
386
|
-
name: input.name,
|
|
387
|
-
description: input.description,
|
|
388
|
-
filesAffected: input.filesAffected,
|
|
389
|
-
sourceEsrVersion: input.sourceEsrVersion,
|
|
390
|
-
...(input.sourceProduct !== undefined ? { sourceProduct: input.sourceProduct } : {}),
|
|
391
|
-
...(input.sourceVersion !== undefined ? { sourceVersion: input.sourceVersion } : {}),
|
|
392
|
-
...(input.tier !== undefined ? { tier: input.tier } : {}),
|
|
393
|
-
...(input.lintIgnore !== undefined ? { lintIgnore: input.lintIgnore } : {}),
|
|
394
|
-
...(input.config !== undefined ? { config: input.config } : {}),
|
|
395
|
-
});
|
|
365
|
+
const plan = await planExport(buildPlanExportInput(input));
|
|
396
366
|
if (input.config !== undefined) {
|
|
397
367
|
enforcePatchPolicy({
|
|
398
368
|
config: input.config,
|
|
@@ -426,7 +396,7 @@ export async function renderDryRunPreview(input) {
|
|
|
426
396
|
}
|
|
427
397
|
if (overlap.size > 0) {
|
|
428
398
|
const entries = [...overlap.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
429
|
-
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'}:`);
|
|
430
400
|
for (const [file, owners] of entries) {
|
|
431
401
|
warn(` - ${file} already claimed by: ${owners.join(', ')}`);
|
|
432
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();
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Policy-aware checks for export placement plans.
|
|
4
4
|
*/
|
|
5
|
+
import { formatOrderRange, formatPatchOrder } from '../core/patch-parse.js';
|
|
5
6
|
import { InvalidArgumentError } from '../errors/base.js';
|
|
6
|
-
function reservedRangeLabel(range) {
|
|
7
|
-
return `${String(range.from).padStart(3, '0')}-${String(range.to).padStart(3, '0')}`;
|
|
8
|
-
}
|
|
9
7
|
function findReservedRange(config, order) {
|
|
10
8
|
return (config.patchPolicy?.reservedRanges?.find((range) => order >= range.from && order <= range.to) ??
|
|
11
9
|
null);
|
|
@@ -42,11 +40,11 @@ export function assertPlacementAvoidsReservedRanges(plan, manifestPatches, confi
|
|
|
42
40
|
(oldOrder !== undefined ? findReservedRange(config, oldOrder) : null);
|
|
43
41
|
if (reserved === null)
|
|
44
42
|
continue;
|
|
45
|
-
const label =
|
|
43
|
+
const label = formatOrderRange(reserved);
|
|
46
44
|
const freeOrder = firstFreeOrderBelowReservedRange(manifestPatches, reserved);
|
|
47
45
|
if (freeOrder !== null) {
|
|
48
46
|
throw new InvalidArgumentError(`Positional insert would renumber the reserved range ${label}; ` +
|
|
49
|
-
`pass --order ${
|
|
47
|
+
`pass --order ${formatPatchOrder(freeOrder)} (first free order below the ` +
|
|
50
48
|
'reserved block) to place the new patch without renumbering reserved patches.', 'export placement');
|
|
51
49
|
}
|
|
52
50
|
throw new InvalidArgumentError(`Positional insert would renumber the reserved range ${label}; ` +
|
|
@@ -2,29 +2,43 @@ import type { PatchesManifest, PatchLintIssue } from '../types/commands/index.js
|
|
|
2
2
|
import type { ExportOptions, PatchCategory } from '../types/commands/index.js';
|
|
3
3
|
import type { FireForgeConfig } from '../types/config.js';
|
|
4
4
|
import type { SpinnerHandle } from '../utils/logger.js';
|
|
5
|
+
export interface RunPatchLintOptions {
|
|
6
|
+
/** Engine root directory. */
|
|
7
|
+
engineDir: string;
|
|
8
|
+
/** Files touched by the patch. */
|
|
9
|
+
filesAffected: string[];
|
|
10
|
+
/** Raw unified diff string. */
|
|
11
|
+
diffContent: string;
|
|
12
|
+
/** Project configuration. */
|
|
13
|
+
config: FireForgeConfig;
|
|
14
|
+
/** If true, downgrade errors to warnings. */
|
|
15
|
+
skipLint?: boolean | undefined;
|
|
16
|
+
/** Optional cross-patch context for ownership resolution. */
|
|
17
|
+
patchQueueCtx?: import('../core/patch-lint-cross.js').PatchQueueContext | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Optional per-patch set of `check` IDs to suppress (threaded from
|
|
20
|
+
* `PatchMetadata.lintIgnore`). Surgical alternative to `--skip-lint` when
|
|
21
|
+
* exactly one advisory rule does not apply to a specific patch — e.g.
|
|
22
|
+
* `large-patch-lines` on a cohesive branding bundle that genuinely cannot
|
|
23
|
+
* be split.
|
|
24
|
+
*/
|
|
25
|
+
ignoreChecks?: ReadonlySet<string> | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Optional explicit tier override (threaded from `PatchMetadata.tier`).
|
|
28
|
+
* Forces the branding-tier thresholds when set, independent of the
|
|
29
|
+
* auto-detect allowlist. When the branding tier is applied, a single
|
|
30
|
+
* `info()` line surfaces the choice so the tier decision is visible rather
|
|
31
|
+
* than silent.
|
|
32
|
+
*/
|
|
33
|
+
patchTier?: 'branding' | undefined;
|
|
34
|
+
}
|
|
5
35
|
/**
|
|
6
36
|
* Runs the full patch lint pipeline and reports results. Warnings are always
|
|
7
37
|
* displayed. Errors block the export unless skipLint is true.
|
|
8
38
|
*
|
|
9
|
-
* @param
|
|
10
|
-
* @param filesAffected - Files touched by the patch
|
|
11
|
-
* @param diffContent - Raw unified diff string
|
|
12
|
-
* @param config - Project configuration
|
|
13
|
-
* @param skipLint - If true, downgrade errors to warnings
|
|
14
|
-
* @param patchQueueCtx - Optional cross-patch context for ownership
|
|
15
|
-
* resolution
|
|
16
|
-
* @param ignoreChecks - Optional per-patch set of `check` IDs to suppress
|
|
17
|
-
* (threaded from `PatchMetadata.lintIgnore`). Surgical alternative to
|
|
18
|
-
* `--skip-lint` when exactly one advisory rule does not apply to a
|
|
19
|
-
* specific patch — e.g. `large-patch-lines` on a cohesive branding bundle
|
|
20
|
-
* that genuinely cannot be split.
|
|
21
|
-
* @param patchTier - Optional explicit tier override (threaded from
|
|
22
|
-
* `PatchMetadata.tier`). Forces the branding-tier thresholds when set,
|
|
23
|
-
* independent of the auto-detect allowlist. When the branding tier is
|
|
24
|
-
* applied, a single `info()` line surfaces the choice so the tier decision
|
|
25
|
-
* is visible rather than silent.
|
|
39
|
+
* @param options - See {@link RunPatchLintOptions}
|
|
26
40
|
*/
|
|
27
|
-
export declare function runPatchLint(
|
|
41
|
+
export declare function runPatchLint(options: RunPatchLintOptions): Promise<void>;
|
|
28
42
|
/**
|
|
29
43
|
* Prints a patch-lint issue list with `runPatchLint`'s exact severity
|
|
30
44
|
* buckets and skip-lint semantics, throwing on errors unless `skipLint`.
|
|
@@ -32,17 +46,22 @@ export declare function runPatchLint(engineDir: string, filesAffected: string[],
|
|
|
32
46
|
* stored issues through the identical reporter without re-linting.
|
|
33
47
|
*/
|
|
34
48
|
export declare function reportPatchLintOutcome(issues: PatchLintIssue[], skipLint?: boolean): void;
|
|
49
|
+
/** Patch identity resolved for an export, from flags or interactive prompts. */
|
|
50
|
+
export interface ExportPatchMetadata {
|
|
51
|
+
/** Normalized patch name (without order prefix or extension). */
|
|
52
|
+
patchName: string;
|
|
53
|
+
/** Category the patch is filed under. */
|
|
54
|
+
selectedCategory: PatchCategory;
|
|
55
|
+
/** Human-readable patch description. */
|
|
56
|
+
description: string;
|
|
57
|
+
}
|
|
35
58
|
/**
|
|
36
59
|
* Resolves patch metadata interactively or from flags, with shared validation.
|
|
37
60
|
* @param options - Export command options
|
|
38
61
|
* @param isInteractive - Whether interactive prompts are allowed
|
|
39
62
|
* @param commandName - Command name for error/help text
|
|
40
63
|
*/
|
|
41
|
-
export declare function promptExportPatchMetadata(options: ExportOptions, isInteractive: boolean, commandName: 'export' | 'export-all', config?: FireForgeConfig): Promise<
|
|
42
|
-
patchName: string;
|
|
43
|
-
selectedCategory: PatchCategory;
|
|
44
|
-
description: string;
|
|
45
|
-
} | null>;
|
|
64
|
+
export declare function promptExportPatchMetadata(options: ExportOptions, isInteractive: boolean, commandName: 'export' | 'export-all', config?: FireForgeConfig): Promise<ExportPatchMetadata | null>;
|
|
46
65
|
/**
|
|
47
66
|
* Confirms whether an export may supersede existing patches.
|
|
48
67
|
* @param patchesDir - Patches directory
|
|
@@ -15,25 +15,10 @@ import { describePatchNameProblem, normalizePatchDisplayName, PATCH_CATEGORIES,
|
|
|
15
15
|
* Runs the full patch lint pipeline and reports results. Warnings are always
|
|
16
16
|
* displayed. Errors block the export unless skipLint is true.
|
|
17
17
|
*
|
|
18
|
-
* @param
|
|
19
|
-
* @param filesAffected - Files touched by the patch
|
|
20
|
-
* @param diffContent - Raw unified diff string
|
|
21
|
-
* @param config - Project configuration
|
|
22
|
-
* @param skipLint - If true, downgrade errors to warnings
|
|
23
|
-
* @param patchQueueCtx - Optional cross-patch context for ownership
|
|
24
|
-
* resolution
|
|
25
|
-
* @param ignoreChecks - Optional per-patch set of `check` IDs to suppress
|
|
26
|
-
* (threaded from `PatchMetadata.lintIgnore`). Surgical alternative to
|
|
27
|
-
* `--skip-lint` when exactly one advisory rule does not apply to a
|
|
28
|
-
* specific patch — e.g. `large-patch-lines` on a cohesive branding bundle
|
|
29
|
-
* that genuinely cannot be split.
|
|
30
|
-
* @param patchTier - Optional explicit tier override (threaded from
|
|
31
|
-
* `PatchMetadata.tier`). Forces the branding-tier thresholds when set,
|
|
32
|
-
* independent of the auto-detect allowlist. When the branding tier is
|
|
33
|
-
* applied, a single `info()` line surfaces the choice so the tier decision
|
|
34
|
-
* is visible rather than silent.
|
|
18
|
+
* @param options - See {@link RunPatchLintOptions}
|
|
35
19
|
*/
|
|
36
|
-
export async function runPatchLint(
|
|
20
|
+
export async function runPatchLint(options) {
|
|
21
|
+
const { engineDir, filesAffected, diffContent, config, skipLint, patchQueueCtx, ignoreChecks, patchTier, } = options;
|
|
37
22
|
// Compute the tier decision independently of the lint pipeline so it can
|
|
38
23
|
// be surfaced even when the rule body emitted no issues — a branding patch
|
|
39
24
|
// under the soft threshold still benefits from operators knowing which
|
|
@@ -353,7 +338,7 @@ export async function guardOwnershipOverlap(args) {
|
|
|
353
338
|
return true;
|
|
354
339
|
s.stop();
|
|
355
340
|
const entries = [...overlap.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
356
|
-
warn(`This export would create cross-patch ownership overlap on ${
|
|
341
|
+
warn(`This export would create cross-patch ownership overlap on ${entries.length} file${entries.length === 1 ? '' : 's'}:`);
|
|
357
342
|
for (const [file, owners] of entries) {
|
|
358
343
|
warn(` - ${file} already claimed by: ${owners.join(', ')}`);
|
|
359
344
|
}
|
|
@@ -258,7 +258,16 @@ export async function exportCommand(projectRoot, files, options) {
|
|
|
258
258
|
const patchQueueCtx = (await pathExists(paths.patches))
|
|
259
259
|
? await buildPatchQueueContext(paths.patches)
|
|
260
260
|
: undefined;
|
|
261
|
-
await runPatchLint(
|
|
261
|
+
await runPatchLint({
|
|
262
|
+
engineDir: paths.engine,
|
|
263
|
+
filesAffected,
|
|
264
|
+
diffContent: diff,
|
|
265
|
+
config,
|
|
266
|
+
skipLint: options.skipLint,
|
|
267
|
+
patchQueueCtx,
|
|
268
|
+
ignoreChecks: exportIgnoreChecks,
|
|
269
|
+
patchTier: options.tier,
|
|
270
|
+
});
|
|
262
271
|
// Resolve placement (if any flag was given). Placement is mutually
|
|
263
272
|
// exclusive with supersede — the semantics overlap confusingly.
|
|
264
273
|
let placementPlan = null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FurnaceConfig } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Loads `furnace.json` for an authoring command, defaulting to an empty
|
|
4
|
+
* configuration when the project has none yet.
|
|
5
|
+
*
|
|
6
|
+
* `furnace create` and `furnace override` both need this: they are the two
|
|
7
|
+
* commands that may run before any furnace configuration exists, and both must
|
|
8
|
+
* treat "no file" as "nothing registered yet" rather than an error. They had a
|
|
9
|
+
* byte-identical private copy each.
|
|
10
|
+
* @param projectRoot - Project root to load from.
|
|
11
|
+
* @returns The parsed configuration, or a fresh default.
|
|
12
|
+
*/
|
|
13
|
+
export declare function loadAuthoringFurnaceConfig(projectRoot: string): Promise<FurnaceConfig>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
import { createDefaultFurnaceConfig, furnaceConfigExists, loadFurnaceConfig, } from '../../core/furnace-config.js';
|
|
3
|
+
/**
|
|
4
|
+
* Loads `furnace.json` for an authoring command, defaulting to an empty
|
|
5
|
+
* configuration when the project has none yet.
|
|
6
|
+
*
|
|
7
|
+
* `furnace create` and `furnace override` both need this: they are the two
|
|
8
|
+
* commands that may run before any furnace configuration exists, and both must
|
|
9
|
+
* treat "no file" as "nothing registered yet" rather than an error. They had a
|
|
10
|
+
* byte-identical private copy each.
|
|
11
|
+
* @param projectRoot - Project root to load from.
|
|
12
|
+
* @returns The parsed configuration, or a fresh default.
|
|
13
|
+
*/
|
|
14
|
+
export async function loadAuthoringFurnaceConfig(projectRoot) {
|
|
15
|
+
if (await furnaceConfigExists(projectRoot)) {
|
|
16
|
+
return loadFurnaceConfig(projectRoot);
|
|
17
|
+
}
|
|
18
|
+
return createDefaultFurnaceConfig();
|
|
19
|
+
}
|
|
@@ -10,7 +10,7 @@ import { loadConfig } from '../../core/config.js';
|
|
|
10
10
|
import { stdioIsInteractive } from '../../core/destructive.js';
|
|
11
11
|
import { completeJournalRollback, runFurnaceMutation, } from '../../core/furnace-operation.js';
|
|
12
12
|
import { assertFurnaceEngineReady } from '../../core/furnace-precondition.js';
|
|
13
|
-
import { createRollbackJournal, snapshotDir, snapshotFile } from '../../core/furnace-rollback.js';
|
|
13
|
+
import { createRollbackJournal, snapshotDir, snapshotFile, } from '../../core/furnace-rollback.js';
|
|
14
14
|
import { FurnaceError } from '../../errors/furnace.js';
|
|
15
15
|
import { pathExists, readText, removeDir, removeFile, writeText } from '../../utils/fs.js';
|
|
16
16
|
import { cancel, info, intro, isCancel, note, outro } from '../../utils/logger.js';
|