@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
|
@@ -3,6 +3,7 @@ import { basename, join } from 'node:path';
|
|
|
3
3
|
import { GeneralError, InvalidArgumentError } from '../errors/base.js';
|
|
4
4
|
import { pathExists, readText } from '../utils/fs.js';
|
|
5
5
|
import { warn } from '../utils/logger.js';
|
|
6
|
+
import { normalizePathSlashes } from '../utils/paths.js';
|
|
6
7
|
import { escapeRegex } from '../utils/regex.js';
|
|
7
8
|
import { getProjectPaths, loadConfig } from './config.js';
|
|
8
9
|
import { registerBrowserContent, registerFireForgeModule, registerSharedCSS, registerTestManifest, registerToolkitWidget, } from './moz-manifest-register.js';
|
|
@@ -84,13 +85,30 @@ export function getRules(binaryName, createManifest = false) {
|
|
|
84
85
|
},
|
|
85
86
|
];
|
|
86
87
|
}
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Reads an engine manifest and reports whether it contains any of `needles`.
|
|
90
|
+
*
|
|
91
|
+
* The five registration probes below share this shape: resolve the manifest
|
|
92
|
+
* relative to the engine, refuse with the manifest's own path when it is
|
|
93
|
+
* missing, then substring-match the entry each registrar writes.
|
|
94
|
+
*
|
|
95
|
+
* @param engineDir - Path to the engine directory
|
|
96
|
+
* @param manifestRel - Engine-relative manifest path (forward slashes; also
|
|
97
|
+
* the path named in the refusal message)
|
|
98
|
+
* @param needles - Entry forms that count as registered
|
|
99
|
+
* @returns True when the manifest contains at least one needle
|
|
100
|
+
* @throws GeneralError when the manifest does not exist
|
|
101
|
+
*/
|
|
102
|
+
async function manifestIncludes(engineDir, manifestRel, needles) {
|
|
103
|
+
const manifestPath = join(engineDir, manifestRel);
|
|
89
104
|
if (!(await pathExists(manifestPath))) {
|
|
90
|
-
throw new GeneralError(
|
|
105
|
+
throw new GeneralError(`Manifest not found: ${manifestRel}`);
|
|
91
106
|
}
|
|
92
|
-
const name = basename(fileName, '.css');
|
|
93
107
|
const content = await readText(manifestPath);
|
|
108
|
+
return needles.some((needle) => content.includes(needle));
|
|
109
|
+
}
|
|
110
|
+
async function isSharedCSSRegistered(engineDir, fileName) {
|
|
111
|
+
const name = basename(fileName, '.css');
|
|
94
112
|
// `register` writes the canonical `skin/classic/browser/<name>.css` form;
|
|
95
113
|
// `furnace chrome-doc create` writes a `content/browser/<name>.css` entry
|
|
96
114
|
// because the CSS is loaded by a chrome document via a `chrome://browser/
|
|
@@ -98,40 +116,26 @@ async function isSharedCSSRegistered(engineDir, fileName) {
|
|
|
98
116
|
// chrome-doc scaffolder are not flagged as "potentially unregistered" by
|
|
99
117
|
// `status` and so a re-run of `register` against the same file recognises
|
|
100
118
|
// the existing entry instead of proposing a duplicate.
|
|
101
|
-
return (
|
|
102
|
-
|
|
119
|
+
return manifestIncludes(engineDir, 'browser/themes/shared/jar.inc.mn', [
|
|
120
|
+
`skin/classic/browser/${name}.css`,
|
|
121
|
+
`content/browser/${name}.css`,
|
|
122
|
+
]);
|
|
103
123
|
}
|
|
104
124
|
async function isBrowserContentRegistered(engineDir, fileName) {
|
|
105
|
-
|
|
106
|
-
if (!(await pathExists(manifestPath))) {
|
|
107
|
-
throw new GeneralError('Manifest not found: browser/base/jar.mn');
|
|
108
|
-
}
|
|
109
|
-
const content = await readText(manifestPath);
|
|
110
|
-
return content.includes(`content/browser/${fileName}`);
|
|
125
|
+
return manifestIncludes(engineDir, 'browser/base/jar.mn', [`content/browser/${fileName}`]);
|
|
111
126
|
}
|
|
112
127
|
async function isTestManifestRegistered(engineDir, testDir) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
const content = await readText(manifestPath);
|
|
118
|
-
return content.includes(`content/test/${testDir}/browser.toml`);
|
|
128
|
+
return manifestIncludes(engineDir, 'browser/base/moz.build', [
|
|
129
|
+
`content/test/${testDir}/browser.toml`,
|
|
130
|
+
]);
|
|
119
131
|
}
|
|
120
132
|
async function isFireForgeModuleRegistered(engineDir, fileName, moduleDir) {
|
|
121
|
-
|
|
122
|
-
if (!(await pathExists(manifestPath))) {
|
|
123
|
-
throw new GeneralError(`Manifest not found: ${moduleDir}/moz.build`);
|
|
124
|
-
}
|
|
125
|
-
const content = await readText(manifestPath);
|
|
126
|
-
return content.includes(`"${fileName}"`);
|
|
133
|
+
return manifestIncludes(engineDir, `${moduleDir}/moz.build`, [`"${fileName}"`]);
|
|
127
134
|
}
|
|
128
135
|
async function isToolkitWidgetRegistered(engineDir, _tagName, fileName) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
const content = await readText(manifestPath);
|
|
134
|
-
return content.includes(`content/global/elements/${fileName}`);
|
|
136
|
+
return manifestIncludes(engineDir, 'toolkit/content/jar.mn', [
|
|
137
|
+
`content/global/elements/${fileName}`,
|
|
138
|
+
]);
|
|
135
139
|
}
|
|
136
140
|
/**
|
|
137
141
|
* Checks if a file path matches any known registrable pattern.
|
|
@@ -140,7 +144,7 @@ async function isToolkitWidgetRegistered(engineDir, _tagName, fileName) {
|
|
|
140
144
|
* @returns True if the file matches a known registration pattern
|
|
141
145
|
*/
|
|
142
146
|
export function matchesRegistrablePattern(filePath, binaryName) {
|
|
143
|
-
const normalized = filePath
|
|
147
|
+
const normalized = normalizePathSlashes(filePath);
|
|
144
148
|
const rules = getRules(binaryName);
|
|
145
149
|
return rules.some((rule) => rule.pattern.test(normalized));
|
|
146
150
|
}
|
|
@@ -197,7 +201,7 @@ export async function isFileRegistered(root, filePath) {
|
|
|
197
201
|
const { engine: engineDir } = getProjectPaths(root);
|
|
198
202
|
const config = await loadConfig(root);
|
|
199
203
|
const rules = getRules(config.binaryName);
|
|
200
|
-
const normalizedPath = filePath
|
|
204
|
+
const normalizedPath = normalizePathSlashes(filePath);
|
|
201
205
|
for (const rule of rules) {
|
|
202
206
|
const match = normalizedPath.match(rule.pattern);
|
|
203
207
|
if (match) {
|
|
@@ -237,7 +241,7 @@ export async function registerFile(root, filePath, dryRun = false, after, option
|
|
|
237
241
|
const config = await loadConfig(root);
|
|
238
242
|
const rules = getRules(config.binaryName, options.createManifest === true);
|
|
239
243
|
// Normalize path separators
|
|
240
|
-
const normalizedPath = filePath
|
|
244
|
+
const normalizedPath = normalizePathSlashes(filePath);
|
|
241
245
|
for (const rule of rules) {
|
|
242
246
|
const match = normalizedPath.match(rule.pattern);
|
|
243
247
|
if (match) {
|
|
@@ -42,13 +42,13 @@ const GIT_DEFAULT_CONTEXT = 3;
|
|
|
42
42
|
*/
|
|
43
43
|
export async function applyPatchWithFuzz(patchPath, engineDir, maxFuzz = 3) {
|
|
44
44
|
if (!Number.isInteger(maxFuzz) || maxFuzz < 0) {
|
|
45
|
-
throw new InvalidArgumentError(`maxFuzz must be a non-negative integer, got ${
|
|
45
|
+
throw new InvalidArgumentError(`maxFuzz must be a non-negative integer, got ${maxFuzz}.`, 'maxFuzz');
|
|
46
46
|
}
|
|
47
47
|
await ensureGit();
|
|
48
48
|
// Try exact match first, then escalate context reduction.
|
|
49
49
|
const maxSteps = Math.min(maxFuzz, GIT_DEFAULT_CONTEXT);
|
|
50
50
|
for (let step = 0; step <= maxSteps; step++) {
|
|
51
|
-
const contextArgs = step > 0 ? [`-C${
|
|
51
|
+
const contextArgs = step > 0 ? [`-C${GIT_DEFAULT_CONTEXT - step}`] : [];
|
|
52
52
|
const check = await exec('git', ['apply', '--check', ...contextArgs, '--', patchPath], {
|
|
53
53
|
cwd: engineDir,
|
|
54
54
|
});
|
|
@@ -59,12 +59,12 @@ export async function applyPatchWithFuzz(patchPath, engineDir, maxFuzz = 3) {
|
|
|
59
59
|
});
|
|
60
60
|
if (apply.exitCode === 0) {
|
|
61
61
|
if (step > 0) {
|
|
62
|
-
verbose(`Patch applied with reduced context (-C${
|
|
62
|
+
verbose(`Patch applied with reduced context (-C${GIT_DEFAULT_CONTEXT - step}): ${patchPath}`);
|
|
63
63
|
}
|
|
64
64
|
return { success: true, fuzzFactor: step };
|
|
65
65
|
}
|
|
66
66
|
// Unlikely: --check passed but apply failed; fall through to next step
|
|
67
|
-
verbose(`git apply -C${
|
|
67
|
+
verbose(`git apply -C${GIT_DEFAULT_CONTEXT - step} --check passed but apply failed: ${apply.stderr.trim()}`);
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
// All context-reduction levels failed → generate .rej files for manual resolution
|
|
@@ -4,10 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { ImportSummary, PatchInfo, PatchMetadata } from '../types/commands/index.js';
|
|
6
6
|
export { PatchError } from '../errors/patch.js';
|
|
7
|
-
export { countPatches, discoverPatches
|
|
7
|
+
export { countPatches, discoverPatches } from './patch-files.js';
|
|
8
8
|
export { withPatchDirectoryLock } from './patch-lock.js';
|
|
9
|
-
export { extractAffectedFiles
|
|
10
|
-
export { applyPatchToContent, extractNewFileContent } from './patch-transform.js';
|
|
9
|
+
export { extractAffectedFiles } from './patch-parse.js';
|
|
11
10
|
/**
|
|
12
11
|
* Options for {@link applyPatchesWithContinue}.
|
|
13
12
|
*/
|
|
@@ -47,10 +46,10 @@ export declare function matchesUntilFilename(patchFilename: string, needle: stri
|
|
|
47
46
|
* on the first failure to keep the engine directory in a clean state.
|
|
48
47
|
* @param patchesDir - Path to the patches directory
|
|
49
48
|
* @param engineDir - Path to the engine directory
|
|
50
|
-
* @param
|
|
49
|
+
* @param options - Application options
|
|
51
50
|
* @returns Import summary with all results
|
|
52
51
|
*/
|
|
53
|
-
export declare function applyPatchesWithContinue(patchesDir: string, engineDir: string,
|
|
52
|
+
export declare function applyPatchesWithContinue(patchesDir: string, engineDir: string, options?: ApplyPatchesOptions): Promise<ImportSummary>;
|
|
54
53
|
/**
|
|
55
54
|
* Batched patched-content computation shared by status classification,
|
|
56
55
|
* import's unmanaged-dirty guard, and discard baseline planning.
|
|
@@ -11,17 +11,16 @@ import { pathExists, readText, writeFileAtomic, writeText } from '../utils/fs.js
|
|
|
11
11
|
import { verbose } from '../utils/logger.js';
|
|
12
12
|
import { isContainedRelativePath } from '../utils/paths.js';
|
|
13
13
|
import { applyPatchIdempotent, reversePatch } from './git.js';
|
|
14
|
-
import {
|
|
14
|
+
import { getFileContentAtRef } from './git-file-ops.js';
|
|
15
15
|
import { discoverPatches } from './patch-files.js';
|
|
16
16
|
import { loadPatchesManifest } from './patch-manifest.js';
|
|
17
17
|
import { extractAffectedFiles, extractConflictingFiles, isNewFileInPatch, parseDiffSections, } from './patch-parse.js';
|
|
18
18
|
import { applyPatchTextToContent, extractNewFileContentFromDiff } from './patch-transform.js';
|
|
19
19
|
// Re-export from split modules so existing import sites continue working
|
|
20
20
|
export { PatchError } from '../errors/patch.js';
|
|
21
|
-
export { countPatches, discoverPatches
|
|
21
|
+
export { countPatches, discoverPatches } from './patch-files.js';
|
|
22
22
|
export { withPatchDirectoryLock } from './patch-lock.js';
|
|
23
|
-
export { extractAffectedFiles
|
|
24
|
-
export { applyPatchToContent, extractNewFileContent } from './patch-transform.js';
|
|
23
|
+
export { extractAffectedFiles } from './patch-parse.js';
|
|
25
24
|
/**
|
|
26
25
|
* Applies a single patch.
|
|
27
26
|
*
|
|
@@ -272,15 +271,10 @@ export function matchesUntilFilename(patchFilename, needle) {
|
|
|
272
271
|
* on the first failure to keep the engine directory in a clean state.
|
|
273
272
|
* @param patchesDir - Path to the patches directory
|
|
274
273
|
* @param engineDir - Path to the engine directory
|
|
275
|
-
* @param
|
|
274
|
+
* @param options - Application options
|
|
276
275
|
* @returns Import summary with all results
|
|
277
276
|
*/
|
|
278
|
-
export async function applyPatchesWithContinue(patchesDir, engineDir,
|
|
279
|
-
// Accept both the legacy boolean positional and the new options object so
|
|
280
|
-
// existing call sites (tests and rebase) keep working without a rewrite.
|
|
281
|
-
const options = typeof optionsOrContinue === 'boolean'
|
|
282
|
-
? { continueOnFailure: optionsOrContinue }
|
|
283
|
-
: optionsOrContinue;
|
|
277
|
+
export async function applyPatchesWithContinue(patchesDir, engineDir, options = {}) {
|
|
284
278
|
const continueOnFailure = options.continueOnFailure ?? false;
|
|
285
279
|
const untilFilename = options.untilFilename;
|
|
286
280
|
const patches = await discoverPatches(patchesDir);
|
|
@@ -406,7 +400,7 @@ export async function createPatchedContentContext(patchesDir, engineDir) {
|
|
|
406
400
|
getAffectingPatches,
|
|
407
401
|
readPatchBody,
|
|
408
402
|
computePatched: async (filePath) => {
|
|
409
|
-
let content = await
|
|
403
|
+
let content = await getFileContentAtRef(engineDir, filePath);
|
|
410
404
|
for (const patch of getAffectingPatches(filePath)) {
|
|
411
405
|
content = applyPatchTextToContent(content, await readPatchBody(patch), filePath);
|
|
412
406
|
}
|
|
@@ -2,15 +2,6 @@
|
|
|
2
2
|
* Patch coverage and supersession helpers used by export planning.
|
|
3
3
|
*/
|
|
4
4
|
import type { PatchInfo, PatchMetadata } from '../types/commands/index.js';
|
|
5
|
-
/**
|
|
6
|
-
* Finds patches that are completely superseded by newer patches.
|
|
7
|
-
* A patch is superseded if all its affected files are covered by newer patches.
|
|
8
|
-
* @param patchesDir - Path to the patches directory
|
|
9
|
-
* @param newPatchFiles - Files affected by the new patch
|
|
10
|
-
* @param excludeFilename - Filename to exclude from results (the new patch itself)
|
|
11
|
-
* @returns Superseded patches
|
|
12
|
-
*/
|
|
13
|
-
export declare function findSupersededPatches(patchesDir: string, newPatchFiles: string[], excludeFilename?: string): Promise<PatchInfo[]>;
|
|
14
5
|
/**
|
|
15
6
|
* Report whether a patch is fully covered by a new export, and which of its
|
|
16
7
|
* files caused the coverage.
|
|
@@ -2,37 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Patch coverage and supersession helpers used by export planning.
|
|
4
4
|
*/
|
|
5
|
-
import { discoverPatches
|
|
5
|
+
import { discoverPatches } from './patch-apply.js';
|
|
6
6
|
import { loadPatchesManifest } from './patch-manifest.js';
|
|
7
|
-
/**
|
|
8
|
-
* Finds patches that are completely superseded by newer patches.
|
|
9
|
-
* A patch is superseded if all its affected files are covered by newer patches.
|
|
10
|
-
* @param patchesDir - Path to the patches directory
|
|
11
|
-
* @param newPatchFiles - Files affected by the new patch
|
|
12
|
-
* @param excludeFilename - Filename to exclude from results (the new patch itself)
|
|
13
|
-
* @returns Superseded patches
|
|
14
|
-
*/
|
|
15
|
-
export async function findSupersededPatches(patchesDir, newPatchFiles, excludeFilename) {
|
|
16
|
-
const manifest = await loadPatchesManifest(patchesDir);
|
|
17
|
-
if (!manifest)
|
|
18
|
-
return [];
|
|
19
|
-
const patches = await discoverPatches(patchesDir);
|
|
20
|
-
const superseded = [];
|
|
21
|
-
for (const metadata of manifest.patches) {
|
|
22
|
-
if (excludeFilename && metadata.filename === excludeFilename)
|
|
23
|
-
continue;
|
|
24
|
-
if (metadata.filesAffected.length === 1) {
|
|
25
|
-
const affectedFile = metadata.filesAffected[0];
|
|
26
|
-
if (affectedFile && newPatchFiles.includes(affectedFile)) {
|
|
27
|
-
const patch = patches.find((p) => p.filename === metadata.filename);
|
|
28
|
-
if (patch && (await isNewFilePatch(patch.path))) {
|
|
29
|
-
superseded.push(patch);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return superseded;
|
|
35
|
-
}
|
|
36
7
|
/**
|
|
37
8
|
* Checks whether a patch is fully covered by a new export.
|
|
38
9
|
* A patch is fully covered when every file it affects is present in the new export.
|
|
@@ -13,6 +13,22 @@ export interface UpdatePatchPolicyGate {
|
|
|
13
13
|
command: string;
|
|
14
14
|
forceUnsafe?: boolean;
|
|
15
15
|
}
|
|
16
|
+
export interface UpdatePatchAndMetadataOptions {
|
|
17
|
+
/** Patch directory holding the body and `patches.json`. */
|
|
18
|
+
patchesDir: string;
|
|
19
|
+
/** Patch file to rewrite. */
|
|
20
|
+
filename: string;
|
|
21
|
+
/** New patch body; an identical body skips the write. */
|
|
22
|
+
newContent: string;
|
|
23
|
+
/** Manifest-row fields to merge into the patch's metadata. */
|
|
24
|
+
updates: Partial<PatchMetadata>;
|
|
25
|
+
/** Hook run under the lock after the mutation succeeds. */
|
|
26
|
+
onCommitted?: UpdatePatchCommittedHook | undefined;
|
|
27
|
+
/** Policy gate run against the under-lock projected manifest. */
|
|
28
|
+
policyGate?: UpdatePatchPolicyGate | undefined;
|
|
29
|
+
/** Wait budget and command name for the patch directory lock. */
|
|
30
|
+
lockOptions?: PatchDirectoryLockOptions | undefined;
|
|
31
|
+
}
|
|
16
32
|
/**
|
|
17
33
|
* Updates a patch file body and its manifest row under the same patch
|
|
18
34
|
* directory lock. Intended for commands like `re-export --files` where the
|
|
@@ -26,7 +42,8 @@ export interface UpdatePatchPolicyGate {
|
|
|
26
42
|
* unexpected rewrite in `git status`. Metadata still moves either way — only
|
|
27
43
|
* the redundant byte-for-byte write is elided.
|
|
28
44
|
*
|
|
45
|
+
* @param options - See {@link UpdatePatchAndMetadataOptions}
|
|
29
46
|
* @returns True when the patch body on disk changed, false when it was
|
|
30
47
|
* already byte-identical to the new content.
|
|
31
48
|
*/
|
|
32
|
-
export declare function updatePatchAndMetadata(
|
|
49
|
+
export declare function updatePatchAndMetadata(options: UpdatePatchAndMetadataOptions): Promise<boolean>;
|
|
@@ -5,7 +5,6 @@ import { toError } from '../utils/errors.js';
|
|
|
5
5
|
import { pathExists, readText, writeText } from '../utils/fs.js';
|
|
6
6
|
import { warn } from '../utils/logger.js';
|
|
7
7
|
import { withPatchDirectoryLock } from './patch-apply.js';
|
|
8
|
-
import { normalizePatchArtifact } from './patch-artifact-normalize.js';
|
|
9
8
|
import { loadPatchesManifestForWrite, mutatePatchRowsInManifest } from './patch-manifest.js';
|
|
10
9
|
import { buildProjectedManifest, enforcePatchPolicy } from './patch-policy.js';
|
|
11
10
|
/**
|
|
@@ -21,10 +20,12 @@ import { buildProjectedManifest, enforcePatchPolicy } from './patch-policy.js';
|
|
|
21
20
|
* unexpected rewrite in `git status`. Metadata still moves either way — only
|
|
22
21
|
* the redundant byte-for-byte write is elided.
|
|
23
22
|
*
|
|
23
|
+
* @param options - See {@link UpdatePatchAndMetadataOptions}
|
|
24
24
|
* @returns True when the patch body on disk changed, false when it was
|
|
25
25
|
* already byte-identical to the new content.
|
|
26
26
|
*/
|
|
27
|
-
export async function updatePatchAndMetadata(
|
|
27
|
+
export async function updatePatchAndMetadata(options) {
|
|
28
|
+
const { patchesDir, filename, newContent, updates, onCommitted, policyGate, lockOptions = {}, } = options;
|
|
28
29
|
return withPatchDirectoryLock(patchesDir, async () => {
|
|
29
30
|
const manifest = await loadPatchesManifestForWrite(patchesDir);
|
|
30
31
|
if (!manifest) {
|
|
@@ -49,7 +50,7 @@ export async function updatePatchAndMetadata(patchesDir, filename, newContent, u
|
|
|
49
50
|
forceUnsafe: policyGate.forceUnsafe === true,
|
|
50
51
|
});
|
|
51
52
|
}
|
|
52
|
-
const normalized =
|
|
53
|
+
const normalized = newContent;
|
|
53
54
|
const bodyChanged = normalized !== originalContent;
|
|
54
55
|
let patchWritten = false;
|
|
55
56
|
try {
|
|
@@ -2,9 +2,19 @@ import type { PatchCategory, PatchesManifest, PatchInfo, PatchMetadata } from '.
|
|
|
2
2
|
import type { FireForgeConfig } from '../types/config.js';
|
|
3
3
|
import { type SupersedeCoverageDetail } from './patch-export-coverage.js';
|
|
4
4
|
import type { PatchDirectoryLockOptions } from './patch-lock.js';
|
|
5
|
-
export { findAllPatchesForFiles, findAllPatchesForFilesWithDetails,
|
|
5
|
+
export { findAllPatchesForFiles, findAllPatchesForFilesWithDetails, } from './patch-export-coverage.js';
|
|
6
6
|
export { mutatePatchMetadata, updatePatchMetadata } from './patch-export-metadata.js';
|
|
7
7
|
export { updatePatchAndMetadata } from './patch-export-update.js';
|
|
8
|
+
/**
|
|
9
|
+
* Projects the planning subset out of a wider export input (commit input,
|
|
10
|
+
* dry-run preview input) — the fields `planExport` /
|
|
11
|
+
* `computeExportPlanUnderLock` read. Optional fields are copied only when
|
|
12
|
+
* present so the plan input never carries explicit `undefined` keys, which
|
|
13
|
+
* would otherwise leak into the written metadata via spread. The commit
|
|
14
|
+
* path and the dry-run preview both go through this one projection so
|
|
15
|
+
* they cannot drift field by field.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildPlanExportInput(input: PlanExportInput): PlanExportInput;
|
|
8
18
|
/**
|
|
9
19
|
* Gets the next patch number for a new patch.
|
|
10
20
|
* @param patchesDir - Path to the patches directory
|
|
@@ -91,26 +101,6 @@ export interface CommitExportedPatchResult {
|
|
|
91
101
|
* to filename allocation or supersede detection lands in both automatically.
|
|
92
102
|
*/
|
|
93
103
|
export declare function commitExportedPatch(input: CommitExportedPatchInput): Promise<CommitExportedPatchResult>;
|
|
94
|
-
/**
|
|
95
|
-
* Parses a patch filename to extract order, category, and name.
|
|
96
|
-
* Supports both new format (001-category-name.patch) and legacy (001-name.patch).
|
|
97
|
-
*/
|
|
98
|
-
export declare function parseFilename(filename: string): {
|
|
99
|
-
order: number;
|
|
100
|
-
category: PatchCategory | null;
|
|
101
|
-
name: string;
|
|
102
|
-
};
|
|
103
|
-
/**
|
|
104
|
-
* Finds an existing patch that contains the specified file.
|
|
105
|
-
* Returns the most recent (highest order) patch if multiple exist.
|
|
106
|
-
* @param patchesDir - Path to the patches directory
|
|
107
|
-
* @param filePath - File path to search for
|
|
108
|
-
* @returns The patch info and metadata, or null if not found
|
|
109
|
-
*/
|
|
110
|
-
export declare function findExistingPatchForFile(patchesDir: string, filePath: string): Promise<{
|
|
111
|
-
patch: PatchInfo;
|
|
112
|
-
metadata: PatchMetadata;
|
|
113
|
-
} | null>;
|
|
114
104
|
/**
|
|
115
105
|
* Updates the content of a patch file.
|
|
116
106
|
* @param patchPath - Path to the patch file
|
|
@@ -3,17 +3,39 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { toError } from '../utils/errors.js';
|
|
4
4
|
import { pathExists, readText, removeFile, writeText } from '../utils/fs.js';
|
|
5
5
|
import { warn } from '../utils/logger.js';
|
|
6
|
-
import { PATCH_CATEGORIES } from '../utils/validation.js';
|
|
7
6
|
import { discoverPatches, withPatchDirectoryLock } from './patch-apply.js';
|
|
8
|
-
import { normalizePatchArtifact } from './patch-artifact-normalize.js';
|
|
9
7
|
import { findAllPatchesForFilesWithDetails, } from './patch-export-coverage.js';
|
|
10
|
-
import { addPatchToManifest,
|
|
11
|
-
import { requirePatchOrder } from './patch-parse.js';
|
|
8
|
+
import { addPatchToManifest, loadPatchesManifestForWrite, PATCHES_MANIFEST, savePatchesManifest, } from './patch-manifest.js';
|
|
9
|
+
import { formatPatchOrder, requirePatchOrder } from './patch-parse.js';
|
|
12
10
|
import { allocatePolicyOrder, enforcePatchPolicy } from './patch-policy.js';
|
|
13
|
-
export { findAllPatchesForFiles, findAllPatchesForFilesWithDetails,
|
|
11
|
+
export { findAllPatchesForFiles, findAllPatchesForFilesWithDetails, } from './patch-export-coverage.js';
|
|
14
12
|
import { escapeRegex } from '../utils/regex.js';
|
|
15
13
|
export { mutatePatchMetadata, updatePatchMetadata } from './patch-export-metadata.js';
|
|
16
14
|
export { updatePatchAndMetadata } from './patch-export-update.js';
|
|
15
|
+
/**
|
|
16
|
+
* Projects the planning subset out of a wider export input (commit input,
|
|
17
|
+
* dry-run preview input) — the fields `planExport` /
|
|
18
|
+
* `computeExportPlanUnderLock` read. Optional fields are copied only when
|
|
19
|
+
* present so the plan input never carries explicit `undefined` keys, which
|
|
20
|
+
* would otherwise leak into the written metadata via spread. The commit
|
|
21
|
+
* path and the dry-run preview both go through this one projection so
|
|
22
|
+
* they cannot drift field by field.
|
|
23
|
+
*/
|
|
24
|
+
export function buildPlanExportInput(input) {
|
|
25
|
+
return {
|
|
26
|
+
patchesDir: input.patchesDir,
|
|
27
|
+
category: input.category,
|
|
28
|
+
name: input.name,
|
|
29
|
+
description: input.description,
|
|
30
|
+
filesAffected: input.filesAffected,
|
|
31
|
+
sourceEsrVersion: input.sourceEsrVersion,
|
|
32
|
+
...(input.sourceProduct !== undefined ? { sourceProduct: input.sourceProduct } : {}),
|
|
33
|
+
...(input.sourceVersion !== undefined ? { sourceVersion: input.sourceVersion } : {}),
|
|
34
|
+
...(input.tier !== undefined ? { tier: input.tier } : {}),
|
|
35
|
+
...(input.lintIgnore !== undefined ? { lintIgnore: input.lintIgnore } : {}),
|
|
36
|
+
...(input.config !== undefined ? { config: input.config } : {}),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
17
39
|
/**
|
|
18
40
|
* Gets the next patch number for a new patch.
|
|
19
41
|
* @param patchesDir - Path to the patches directory
|
|
@@ -110,19 +132,7 @@ export async function getNextPatchFilename(patchesDir, category, name) {
|
|
|
110
132
|
*/
|
|
111
133
|
export async function commitExportedPatch(input) {
|
|
112
134
|
return withPatchDirectoryLock(input.patchesDir, async () => {
|
|
113
|
-
const plan = await computeExportPlanUnderLock(
|
|
114
|
-
patchesDir: input.patchesDir,
|
|
115
|
-
category: input.category,
|
|
116
|
-
name: input.name,
|
|
117
|
-
description: input.description,
|
|
118
|
-
filesAffected: input.filesAffected,
|
|
119
|
-
sourceEsrVersion: input.sourceEsrVersion,
|
|
120
|
-
...(input.sourceProduct !== undefined ? { sourceProduct: input.sourceProduct } : {}),
|
|
121
|
-
...(input.sourceVersion !== undefined ? { sourceVersion: input.sourceVersion } : {}),
|
|
122
|
-
...(input.tier !== undefined ? { tier: input.tier } : {}),
|
|
123
|
-
...(input.lintIgnore !== undefined ? { lintIgnore: input.lintIgnore } : {}),
|
|
124
|
-
...(input.config !== undefined ? { config: input.config } : {}),
|
|
125
|
-
});
|
|
135
|
+
const plan = await computeExportPlanUnderLock(buildPlanExportInput(input));
|
|
126
136
|
if (input.config !== undefined) {
|
|
127
137
|
enforcePatchPolicy({
|
|
128
138
|
config: input.config,
|
|
@@ -140,7 +150,20 @@ export async function commitExportedPatch(input) {
|
|
|
140
150
|
}
|
|
141
151
|
}
|
|
142
152
|
try {
|
|
143
|
-
|
|
153
|
+
// Patch bodies are written byte-for-byte as git produced them, including the
|
|
154
|
+
// single-space rendering of a blank context line.
|
|
155
|
+
//
|
|
156
|
+
// Do NOT add a whitespace-trimming pass here or at any other write site.
|
|
157
|
+
// Stripping marker lines whose payload is pure whitespace (`/^[ +-]\s+$/` →
|
|
158
|
+
// bare marker) corrupts real content: Firefox sources contain whitespace-only
|
|
159
|
+
// lines, so a ` `/`-` line whose payload was two spaces no longer matches the
|
|
160
|
+
// pristine tree and the freshly exported patch fails `git apply --check`,
|
|
161
|
+
// while a `+` line silently changes what the patch produces. The repository
|
|
162
|
+
// whitespace check already exempts `patches/*.patch`
|
|
163
|
+
// (scripts/check-worktree-whitespace.mjs), and the byte-fidelity contract is
|
|
164
|
+
// pinned end-to-end by `git-diff-latin1-roundtrip.test.ts` and
|
|
165
|
+
// `re-export.integration.test.ts`.
|
|
166
|
+
await writeText(patchPath, input.diff);
|
|
144
167
|
await addPatchToManifest(input.patchesDir, plan.metadata, plan.supersededPatches.map((p) => p.filename));
|
|
145
168
|
for (const oldPatch of plan.supersededPatches) {
|
|
146
169
|
await removeFile(oldPatch.path);
|
|
@@ -188,58 +211,13 @@ export async function commitExportedPatch(input) {
|
|
|
188
211
|
};
|
|
189
212
|
}, input.lockOptions ?? {});
|
|
190
213
|
}
|
|
191
|
-
/**
|
|
192
|
-
* Parses a patch filename to extract order, category, and name.
|
|
193
|
-
* Supports both new format (001-category-name.patch) and legacy (001-name.patch).
|
|
194
|
-
*/
|
|
195
|
-
export function parseFilename(filename) {
|
|
196
|
-
// New format: 001-ui-sidebar.patch
|
|
197
|
-
const newMatch = /^(\d+)-([a-z]+)-(.+)\.patch$/.exec(filename);
|
|
198
|
-
if (newMatch?.[1] && newMatch[2] && newMatch[3]) {
|
|
199
|
-
const orderStr = newMatch[1];
|
|
200
|
-
const category = newMatch[2];
|
|
201
|
-
const name = newMatch[3];
|
|
202
|
-
if (PATCH_CATEGORIES.includes(category)) {
|
|
203
|
-
return {
|
|
204
|
-
order: parseInt(orderStr, 10),
|
|
205
|
-
category,
|
|
206
|
-
name,
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
// Legacy format: 001-name.patch
|
|
211
|
-
const legacyMatch = /^(\d+)-(.+)\.patch$/.exec(filename);
|
|
212
|
-
if (legacyMatch?.[1] && legacyMatch[2]) {
|
|
213
|
-
return {
|
|
214
|
-
order: parseInt(legacyMatch[1], 10),
|
|
215
|
-
category: null,
|
|
216
|
-
name: legacyMatch[2],
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
return { order: Infinity, category: null, name: filename };
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Finds an existing patch that contains the specified file.
|
|
223
|
-
* Returns the most recent (highest order) patch if multiple exist.
|
|
224
|
-
* @param patchesDir - Path to the patches directory
|
|
225
|
-
* @param filePath - File path to search for
|
|
226
|
-
* @returns The patch info and metadata, or null if not found
|
|
227
|
-
*/
|
|
228
|
-
export async function findExistingPatchForFile(patchesDir, filePath) {
|
|
229
|
-
const affectingPatches = await findPatchesAffectingFile(patchesDir, filePath);
|
|
230
|
-
if (affectingPatches.length === 0) {
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
// Return the most recent (highest order) patch
|
|
234
|
-
return affectingPatches[affectingPatches.length - 1] ?? null;
|
|
235
|
-
}
|
|
236
214
|
/**
|
|
237
215
|
* Updates the content of a patch file.
|
|
238
216
|
* @param patchPath - Path to the patch file
|
|
239
217
|
* @param newContent - New patch content
|
|
240
218
|
*/
|
|
241
219
|
export async function updatePatch(patchPath, newContent) {
|
|
242
|
-
await writeText(patchPath,
|
|
220
|
+
await writeText(patchPath, newContent);
|
|
243
221
|
}
|
|
244
222
|
/**
|
|
245
223
|
* Internal planning helper. Does NOT take the patch directory lock — the
|
|
@@ -259,7 +237,7 @@ async function computeExportPlanUnderLock(input) {
|
|
|
259
237
|
? allocatePolicyOrder(input.config, manifestBefore?.patches ?? [], input.category)
|
|
260
238
|
: null;
|
|
261
239
|
const patchFilename = policyOrder !== null
|
|
262
|
-
? `${
|
|
240
|
+
? `${formatPatchOrder(policyOrder)}-${input.category}-${patchNameSlug(input.name, input.category)}.patch`
|
|
263
241
|
: await getNextPatchFilename(input.patchesDir, input.category, input.name);
|
|
264
242
|
const patchPath = join(input.patchesDir, patchFilename);
|
|
265
243
|
const metadata = {
|
|
@@ -3,10 +3,6 @@ import type { PatchInfo } from '../types/commands/index.js';
|
|
|
3
3
|
export declare function discoverPatches(patchesDir: string): Promise<PatchInfo[]>;
|
|
4
4
|
/** Counts the patch files currently present in a patch directory. */
|
|
5
5
|
export declare function countPatches(patchesDir: string): Promise<number>;
|
|
6
|
-
/** Checks whether a patch creates a new file rather than modifying an existing one. */
|
|
7
|
-
export declare function isNewFilePatch(patchPath: string): Promise<boolean>;
|
|
8
|
-
/** Returns the first target file path referenced by a patch, if any. */
|
|
9
|
-
export declare function getTargetFileFromPatch(patchPath: string): Promise<string | null>;
|
|
10
6
|
/**
|
|
11
7
|
* Returns all target file paths referenced by a multi-file patch.
|
|
12
8
|
*
|
|
@@ -24,17 +24,6 @@ export async function countPatches(patchesDir) {
|
|
|
24
24
|
const patches = await discoverPatches(patchesDir);
|
|
25
25
|
return patches.length;
|
|
26
26
|
}
|
|
27
|
-
/** Checks whether a patch creates a new file rather than modifying an existing one. */
|
|
28
|
-
export async function isNewFilePatch(patchPath) {
|
|
29
|
-
const content = await readText(patchPath);
|
|
30
|
-
return content.includes('new file mode') && content.includes('--- /dev/null');
|
|
31
|
-
}
|
|
32
|
-
/** Returns the first target file path referenced by a patch, if any. */
|
|
33
|
-
export async function getTargetFileFromPatch(patchPath) {
|
|
34
|
-
const content = await readText(patchPath);
|
|
35
|
-
const match = /^\+\+\+ b\/(.+)$/m.exec(content);
|
|
36
|
-
return match?.[1] ?? null;
|
|
37
|
-
}
|
|
38
27
|
/**
|
|
39
28
|
* Returns all target file paths referenced by a multi-file patch.
|
|
40
29
|
*
|
|
@@ -21,20 +21,14 @@
|
|
|
21
21
|
* cross-patch lint graph.
|
|
22
22
|
*/
|
|
23
23
|
import type { PatchLintIssue } from '../types/commands/index.js';
|
|
24
|
+
import type { PatchQueueBodyEntry, PatchQueueView } from './patch-lint-queue-types.js';
|
|
24
25
|
/**
|
|
25
|
-
*
|
|
26
|
-
* than importing `
|
|
27
|
-
* this module back — the
|
|
28
|
-
* `dpdm` clean
|
|
26
|
+
* Queue view accepted by {@link lintPatchQueueBinaryBodies}. Described
|
|
27
|
+
* structurally rather than by importing `PatchQueueContext` from
|
|
28
|
+
* `patch-lint-cross.ts`, which imports this module back — the shared leaf
|
|
29
|
+
* type keeps the dependency edge one-way and `dpdm` clean.
|
|
29
30
|
*/
|
|
30
|
-
|
|
31
|
-
filename: string;
|
|
32
|
-
diff: string;
|
|
33
|
-
}
|
|
34
|
-
/** Queue view accepted by {@link lintPatchQueueBinaryBodies}. */
|
|
35
|
-
interface BinaryBodyQueueContext {
|
|
36
|
-
entries: readonly BinaryBodyQueueEntry[];
|
|
37
|
-
}
|
|
31
|
+
type BinaryBodyQueueContext = PatchQueueView<PatchQueueBodyEntry>;
|
|
38
32
|
/** Lint check identifier emitted by {@link lintPatchQueueBinaryBodies}. */
|
|
39
33
|
export declare const BINARY_BODY_CHECK = "binary-body-not-reconstructable";
|
|
40
34
|
/**
|
|
@@ -58,5 +58,21 @@ export interface CachedPerPatchLint {
|
|
|
58
58
|
* is compared explicitly rather than trusted to the key.
|
|
59
59
|
*/
|
|
60
60
|
export declare function getCachedPerPatchLintIssues(cache: PerPatchLintCacheFile, patchFilename: string, key: string, lintIgnore?: Iterable<string>): CachedPerPatchLint | undefined;
|
|
61
|
+
export interface SetCachedPerPatchLintIssuesInput {
|
|
62
|
+
/** Cache file to mutate in place. */
|
|
63
|
+
cache: PerPatchLintCacheFile;
|
|
64
|
+
/** Patch the payload belongs to; also the cache entry key. */
|
|
65
|
+
patchFilename: string;
|
|
66
|
+
/** Fingerprint the payload is valid for. */
|
|
67
|
+
key: string;
|
|
68
|
+
/** Reported issues, stored as copies. */
|
|
69
|
+
issues: PatchLintIssue[];
|
|
70
|
+
/** Issues suppressed by `lintIgnore`, stored as copies. */
|
|
71
|
+
suppressed: PatchLintIssue[];
|
|
72
|
+
/** Line count the run measured. */
|
|
73
|
+
lineCount: number;
|
|
74
|
+
/** Suppression ids in force for the run, normalized on store. */
|
|
75
|
+
lintIgnore?: Iterable<string> | undefined;
|
|
76
|
+
}
|
|
61
77
|
/** Stores the per-patch lint payload after a successful lint calculation. */
|
|
62
|
-
export declare function setCachedPerPatchLintIssues(
|
|
78
|
+
export declare function setCachedPerPatchLintIssues(input: SetCachedPerPatchLintIssuesInput): void;
|