@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
|
@@ -15,24 +15,21 @@
|
|
|
15
15
|
* the worktree.
|
|
16
16
|
*/
|
|
17
17
|
import { join } from 'node:path';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
18
|
+
import { assertConfirmationAvailable, confirmDestructive, } from '../../core/destructive.js';
|
|
19
|
+
import { appendHistoryBestEffort } from '../../core/history-log.js';
|
|
20
|
+
import { buildPatchQueueContext, collectForwardImportEdges, formatPatchLintIssue, lintPatchQueue, } from '../../core/patch-lint.js';
|
|
21
21
|
import { computeProjectedLintRegressions } from '../../core/patch-lint-projection.js';
|
|
22
22
|
import { withPatchDirectoryLock } from '../../core/patch-lock.js';
|
|
23
23
|
import { loadPatchesManifest, savePatchesManifest, validatePatchesManifest, } from '../../core/patch-manifest.js';
|
|
24
|
-
import { buildNewFileTextProjection } from '../../core/patch-transform.js';
|
|
25
24
|
import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
|
|
26
25
|
import { toError } from '../../utils/errors.js';
|
|
27
26
|
import { readText, writeText } from '../../utils/fs.js';
|
|
28
27
|
import { info, outro, success, warn } from '../../utils/logger.js';
|
|
29
28
|
import { resolveWaitLockSeconds } from '../../utils/options.js';
|
|
29
|
+
import { proceedAfterDecision } from '../destructive-decision.js';
|
|
30
30
|
import { runPatchLint } from '../export-shared.js';
|
|
31
|
+
import { projectEntryBody } from './entry-projection.js';
|
|
31
32
|
import { buildSplitDiff, findOwnerRewriteHolders, mergeStagedForwardImports, rewriteSplitOwners, } from './split-plan.js';
|
|
32
|
-
function projectEntryBody(diff) {
|
|
33
|
-
const newFiles = buildNewFileTextProjection(diff);
|
|
34
|
-
return { diff, newFiles, modifiedFileAdditions: buildModifiedFileAdditionsFromDiff(diff) };
|
|
35
|
-
}
|
|
36
33
|
/**
|
|
37
34
|
* Projects the post-move queue through cross-patch lint, auto-declaring the
|
|
38
35
|
* forward edges the move introduces into the target (an importer that used
|
|
@@ -121,9 +118,9 @@ async function commitMoveInto(patchesDir, plan, waitLockSeconds) {
|
|
|
121
118
|
let sourceWritten = false;
|
|
122
119
|
let targetWritten = false;
|
|
123
120
|
try {
|
|
124
|
-
await writeText(sourcePath,
|
|
121
|
+
await writeText(sourcePath, plan.sourceDiff);
|
|
125
122
|
sourceWritten = true;
|
|
126
|
-
await writeText(targetPath,
|
|
123
|
+
await writeText(targetPath, plan.targetDiff);
|
|
127
124
|
targetWritten = true;
|
|
128
125
|
const updatedPatches = manifest.patches.map((patch) => {
|
|
129
126
|
let updated = rewriteSplitOwners(patch, plan.source.filename, movedSet, plan.target.filename);
|
|
@@ -140,21 +137,16 @@ async function commitMoveInto(patchesDir, plan, waitLockSeconds) {
|
|
|
140
137
|
});
|
|
141
138
|
const updated = validatePatchesManifest({ ...manifest, patches: updatedPatches });
|
|
142
139
|
await savePatchesManifest(patchesDir, updated);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
catch (historyError) {
|
|
156
|
-
warn(`History log append failed after patch move-files committed: ${toError(historyError).message}`);
|
|
157
|
-
}
|
|
140
|
+
await appendHistoryBestEffort(patchesDir, {
|
|
141
|
+
operation: 'patch-move-files',
|
|
142
|
+
args: {
|
|
143
|
+
source: plan.source.filename,
|
|
144
|
+
target: plan.target.filename,
|
|
145
|
+
files: plan.movedFiles,
|
|
146
|
+
ownerRewrites: plan.ownerRewrites,
|
|
147
|
+
},
|
|
148
|
+
result: 'ok',
|
|
149
|
+
}, `patch move-files committed`);
|
|
158
150
|
}
|
|
159
151
|
catch (error) {
|
|
160
152
|
if (targetWritten) {
|
|
@@ -199,8 +191,26 @@ export async function runMoveFilesInto(args) {
|
|
|
199
191
|
// parity) resolves cross-patch imports and sibling head.js harness roots
|
|
200
192
|
// instead of linting each projected body blind.
|
|
201
193
|
const patchQueueCtx = await buildPatchQueueContext(patchesDir, args.config);
|
|
202
|
-
await runPatchLint(
|
|
203
|
-
|
|
194
|
+
await runPatchLint({
|
|
195
|
+
engineDir: enginePath,
|
|
196
|
+
filesAffected: sourceAfter,
|
|
197
|
+
diffContent: sourceDiff,
|
|
198
|
+
config: args.config,
|
|
199
|
+
skipLint: options.skipLint,
|
|
200
|
+
patchQueueCtx,
|
|
201
|
+
ignoreChecks: source.lintIgnore ? new Set(source.lintIgnore) : undefined,
|
|
202
|
+
patchTier: source.tier,
|
|
203
|
+
});
|
|
204
|
+
await runPatchLint({
|
|
205
|
+
engineDir: enginePath,
|
|
206
|
+
filesAffected: targetAfter,
|
|
207
|
+
diffContent: targetDiff,
|
|
208
|
+
config: args.config,
|
|
209
|
+
skipLint: options.skipLint,
|
|
210
|
+
patchQueueCtx,
|
|
211
|
+
ignoreChecks: target.lintIgnore ? new Set(target.lintIgnore) : undefined,
|
|
212
|
+
patchTier: target.tier,
|
|
213
|
+
});
|
|
204
214
|
const manifest = await loadPatchesManifest(patchesDir);
|
|
205
215
|
const plan = {
|
|
206
216
|
source,
|
|
@@ -231,14 +241,8 @@ export async function runMoveFilesInto(args) {
|
|
|
231
241
|
unsafeOverride: options.forceUnsafe === true,
|
|
232
242
|
conflicts,
|
|
233
243
|
});
|
|
234
|
-
if (decision
|
|
235
|
-
outro('Dry run complete — no changes made');
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
if (decision === 'declined') {
|
|
239
|
-
outro('Move cancelled');
|
|
244
|
+
if (!proceedAfterDecision(decision, 'Move cancelled'))
|
|
240
245
|
return;
|
|
241
|
-
}
|
|
242
246
|
await commitMoveInto(patchesDir, plan, resolveWaitLockSeconds(options.waitLock));
|
|
243
247
|
success(`Moved ${files.length} file(s) from ${source.filename} to ${target.filename}`);
|
|
244
248
|
if (plan.ownerRewrites.length > 0) {
|
|
@@ -13,13 +13,10 @@ import { loadPatchesManifest, resolvePatchIdentifier } from '../../core/patch-ma
|
|
|
13
13
|
import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
|
|
14
14
|
import { pathExists } from '../../utils/fs.js';
|
|
15
15
|
import { info, intro, note, warn } from '../../utils/logger.js';
|
|
16
|
-
import { addWaitLockOption, commanderArgParser, pickDefined } from '../../utils/options.js';
|
|
16
|
+
import { addWaitLockOption, commanderArgParser, pickDefined, stringListOption, } from '../../utils/options.js';
|
|
17
|
+
import { parsePositiveIntegerFlag } from '../../utils/validation.js';
|
|
17
18
|
import { patchMoveFilesCreateCommand } from './move-files-create.js';
|
|
18
19
|
import { runMoveFilesInto } from './move-files-into.js';
|
|
19
|
-
function collectOption(value, previous) {
|
|
20
|
-
previous.push(value);
|
|
21
|
-
return previous;
|
|
22
|
-
}
|
|
23
20
|
function normalizeFileList(files) {
|
|
24
21
|
const cleaned = (files ?? []).map((file) => file.trim()).filter((file) => file.length > 0);
|
|
25
22
|
return [...new Set(cleaned)].sort((left, right) => left.localeCompare(right));
|
|
@@ -165,15 +162,9 @@ export function registerPatchMoveFiles(parent, context) {
|
|
|
165
162
|
.command('move-files <from> <to>')
|
|
166
163
|
.description('Preview the re-export commands needed to move file ownership between patches, ' +
|
|
167
164
|
'or create the target patch and move the files in one transaction with --create --order <n>.')
|
|
168
|
-
.option('--file <path>', 'File path relative to engine/ to move (repeatable)',
|
|
165
|
+
.option('--file <path>', 'File path relative to engine/ to move (repeatable)', ...stringListOption())
|
|
169
166
|
.option('--create', 'Create <to> as a new patch and move the files into it (requires --order)')
|
|
170
|
-
.option('--order <n>', 'Exact sparse order for the created patch (only valid with --create)', commanderArgParser((raw) =>
|
|
171
|
-
const n = Number.parseInt(raw, 10);
|
|
172
|
-
if (!Number.isInteger(n) || n <= 0) {
|
|
173
|
-
throw new InvalidArgumentError(`--order must be a positive integer, got "${raw}".`, '--order');
|
|
174
|
-
}
|
|
175
|
-
return n;
|
|
176
|
-
}))
|
|
167
|
+
.option('--order <n>', 'Exact sparse order for the created patch (only valid with --create)', commanderArgParser((raw) => parsePositiveIntegerFlag('--order', raw)))
|
|
177
168
|
.option('--category <category>', "Category for the created patch (default: the source patch's)")
|
|
178
169
|
.option('-d, --description <desc>', 'Description for the created patch')
|
|
179
170
|
.option('--dry-run', 'Show what would happen without writing')
|
|
@@ -19,7 +19,8 @@ import { rename as fsRename } from 'node:fs/promises';
|
|
|
19
19
|
import { join } from 'node:path';
|
|
20
20
|
import { Command } from 'commander';
|
|
21
21
|
import { loadConfig } from '../../core/config.js';
|
|
22
|
-
import {
|
|
22
|
+
import { confirmDestructive } from '../../core/destructive.js';
|
|
23
|
+
import { appendHistoryBestEffort } from '../../core/history-log.js';
|
|
23
24
|
import { patchNameSlug } from '../../core/patch-export.js';
|
|
24
25
|
import { buildPatchQueueContext, formatPatchLintIssue, lintPatchQueue, } from '../../core/patch-lint.js';
|
|
25
26
|
import { withPatchDirectoryLock } from '../../core/patch-lock.js';
|
|
@@ -30,6 +31,8 @@ import { toError } from '../../utils/errors.js';
|
|
|
30
31
|
import { pathExists } from '../../utils/fs.js';
|
|
31
32
|
import { info, intro, outro, warn } from '../../utils/logger.js';
|
|
32
33
|
import { addWaitLockOption, commanderArgParser, pickDefined, resolveWaitLockSeconds, } from '../../utils/options.js';
|
|
34
|
+
import { parsePositiveIntegerFlag } from '../../utils/validation.js';
|
|
35
|
+
import { proceedAfterDecision } from '../destructive-decision.js';
|
|
33
36
|
import { requirePatchQueue, requirePatchTarget } from './patch-context.js';
|
|
34
37
|
import { projectReorder } from './reorder.js';
|
|
35
38
|
/**
|
|
@@ -76,7 +79,7 @@ async function commitRenameUnderLock(input) {
|
|
|
76
79
|
if (orderChanging && input.newOrder !== undefined) {
|
|
77
80
|
const orderHolder = fresh.patches.find((p) => p.order === input.newOrder && p.filename !== target.filename);
|
|
78
81
|
if (orderHolder) {
|
|
79
|
-
throw new InvalidArgumentError(`Order ${
|
|
82
|
+
throw new InvalidArgumentError(`Order ${input.newOrder} was claimed by ${orderHolder.filename} concurrently. Pick an unused order, or use "fireforge patch reorder" to renumber siblings.`, '--order');
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
85
|
let oldPath;
|
|
@@ -136,28 +139,23 @@ async function commitRenameUnderLock(input) {
|
|
|
136
139
|
else {
|
|
137
140
|
await savePatchesManifest(patchesDir, fresh);
|
|
138
141
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
catch (historyError) {
|
|
159
|
-
warn(`History log append failed after patch rename committed (${newFilename}): ${toError(historyError).message}`);
|
|
160
|
-
}
|
|
142
|
+
await appendHistoryBestEffort(patchesDir, {
|
|
143
|
+
operation: 'patch-rename',
|
|
144
|
+
args: {
|
|
145
|
+
oldFilename: target.filename,
|
|
146
|
+
newFilename,
|
|
147
|
+
oldName: target.name,
|
|
148
|
+
newName,
|
|
149
|
+
...(descriptionChanging ? { oldDescription: target.description, newDescription } : {}),
|
|
150
|
+
...(orderChanging ? { oldOrder: target.order, newOrder: input.newOrder } : {}),
|
|
151
|
+
...(categoryChanging
|
|
152
|
+
? { oldCategory: target.category, newCategory: input.newCategory }
|
|
153
|
+
: {}),
|
|
154
|
+
},
|
|
155
|
+
...(input.yes === true ? { yes: true } : {}),
|
|
156
|
+
...(input.forceUnsafe === true ? { unsafeOverride: true } : {}),
|
|
157
|
+
result: 'ok',
|
|
158
|
+
}, `patch rename committed (${newFilename})`);
|
|
161
159
|
}, { waitLockSeconds: input.waitLockSeconds, command: 'patch rename' });
|
|
162
160
|
}
|
|
163
161
|
/** Validates the flags and resolves the new filename/identity fields. */
|
|
@@ -215,14 +213,14 @@ async function assertRenamePreconditions(patchesDir, manifest, target, plan, opt
|
|
|
215
213
|
if (plan.orderChanging && options.order !== undefined) {
|
|
216
214
|
const holder = manifest.patches.find((p) => p.order === options.order && p.filename !== target.filename);
|
|
217
215
|
if (holder) {
|
|
218
|
-
throw new InvalidArgumentError(`Order ${
|
|
216
|
+
throw new InvalidArgumentError(`Order ${options.order} is already used by ${holder.filename}. Pick an unused order, or use "fireforge patch reorder ${target.filename} --to ${options.order}" to renumber siblings.`, '--order');
|
|
219
217
|
}
|
|
220
218
|
if (options.forceUnsafe !== true) {
|
|
221
219
|
const baseCtx = await buildPatchQueueContext(patchesDir, config);
|
|
222
220
|
const projected = projectReorder(baseCtx, new Map([[target.filename, { newFilename: plan.newFilename, newOrder: options.order }]]));
|
|
223
221
|
const projectedErrors = lintPatchQueue(projected).filter((i) => i.severity === 'error');
|
|
224
222
|
if (projectedErrors.length > 0) {
|
|
225
|
-
throw new InvalidArgumentError(`Refusing to run patch rename: the order change would introduce ${
|
|
223
|
+
throw new InvalidArgumentError(`Refusing to run patch rename: the order change would introduce ${projectedErrors.length} cross-patch lint error(s):\n ${projectedErrors
|
|
226
224
|
.map(formatPatchLintIssue)
|
|
227
225
|
.join('\n ')}\nPass --force-unsafe to override.`, '--force-unsafe');
|
|
228
226
|
}
|
|
@@ -248,7 +246,7 @@ function buildRenameSummary(target, plan, options) {
|
|
|
248
246
|
summary.push(`description: "${target.description || '(none)'}" → "${options.description ?? '(none)'}"`);
|
|
249
247
|
}
|
|
250
248
|
if (plan.orderChanging && options.order !== undefined) {
|
|
251
|
-
summary.push(`order: ${
|
|
249
|
+
summary.push(`order: ${target.order} → ${options.order}`);
|
|
252
250
|
}
|
|
253
251
|
if (plan.categoryChanging) {
|
|
254
252
|
summary.push(`category: ${target.category} → ${plan.newCategory}`);
|
|
@@ -309,14 +307,8 @@ export async function patchRenameCommand(projectRoot, identifier, options = {})
|
|
|
309
307
|
dryRun: isDryRun,
|
|
310
308
|
conflicts: null,
|
|
311
309
|
});
|
|
312
|
-
if (decision
|
|
313
|
-
outro('Dry run complete — no changes made');
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
if (decision === 'declined') {
|
|
317
|
-
outro('Rename cancelled');
|
|
310
|
+
if (!proceedAfterDecision(decision, 'Rename cancelled'))
|
|
318
311
|
return;
|
|
319
|
-
}
|
|
320
312
|
await commitRenameUnderLock({
|
|
321
313
|
patchesDir: paths.patches,
|
|
322
314
|
target,
|
|
@@ -356,13 +348,7 @@ export function registerPatchRename(parent, context) {
|
|
|
356
348
|
.description('Rename a patch: filename, manifest name, category, order, and/or description update in one transaction without rewriting the .patch body.')
|
|
357
349
|
.option('--to <new-name>', 'New human-readable name, category-prefixed slug, or full filename stem (normalised into the filename slug)')
|
|
358
350
|
.option('--category <category>', 'New category for the patch (validated against configured categories); rewrites the filename prefix and manifest row in one transaction')
|
|
359
|
-
.option('--order <n>', 'Move the patch to this exact unused order; refuses on collision — use "patch reorder" to renumber siblings', commanderArgParser((raw) =>
|
|
360
|
-
const n = Number.parseInt(raw, 10);
|
|
361
|
-
if (!Number.isInteger(n) || n <= 0) {
|
|
362
|
-
throw new InvalidArgumentError(`--order must be a positive integer, got "${raw}".`, '--order');
|
|
363
|
-
}
|
|
364
|
-
return n;
|
|
365
|
-
}))
|
|
351
|
+
.option('--order <n>', 'Move the patch to this exact unused order; refuses on collision — use "patch reorder" to renumber siblings', commanderArgParser((raw) => parsePositiveIntegerFlag('--order', raw)))
|
|
366
352
|
.option('-d, --description <text>', 'Replacement description (omit to leave description unchanged)')
|
|
367
353
|
.option('--dry-run', 'Show what would change without writing')
|
|
368
354
|
.option('-y, --yes', 'Skip confirmation prompt (required for non-TTY)')
|
|
@@ -10,16 +10,18 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { Command, Option } from 'commander';
|
|
12
12
|
import { loadConfig } from '../../core/config.js';
|
|
13
|
-
import {
|
|
13
|
+
import { confirmDestructive, } from '../../core/destructive.js';
|
|
14
|
+
import { appendHistoryBestEffort } from '../../core/history-log.js';
|
|
14
15
|
import { buildPatchQueueContext, formatPatchLintIssue, lintPatchQueue, } from '../../core/patch-lint.js';
|
|
15
16
|
import { withPatchDirectoryLock } from '../../core/patch-lock.js';
|
|
16
17
|
import { loadPatchesManifest, renumberPatchesInManifest, resolvePatchIdentifier, rewriteStagedDependencyOwners, } from '../../core/patch-manifest.js';
|
|
17
18
|
import { applyRenameMapToManifest, enforcePatchPolicy } from '../../core/patch-policy.js';
|
|
18
19
|
import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
|
|
19
|
-
import {
|
|
20
|
-
import { info, intro, outro, warn } from '../../utils/logger.js';
|
|
20
|
+
import { info, intro, outro } from '../../utils/logger.js';
|
|
21
21
|
import { addWaitLockOption, commanderArgParser, pickDefined, resolveWaitLockSeconds, } from '../../utils/options.js';
|
|
22
22
|
import { parsePositiveIntegerFlag } from '../../utils/validation.js';
|
|
23
|
+
import { proceedAfterDecision } from '../destructive-decision.js';
|
|
24
|
+
import { getSortedRenameEntries, renameMapsEqual } from '../patch-rename-map.js';
|
|
23
25
|
import { requirePatchQueue, requirePatchTarget } from './patch-context.js';
|
|
24
26
|
/** Zero-pads an ordinal number to the given width. */
|
|
25
27
|
function padOrder(value, width) {
|
|
@@ -98,25 +100,6 @@ function computeRenameMap(manifestPatches, target, destinationOrder) {
|
|
|
98
100
|
});
|
|
99
101
|
return renames;
|
|
100
102
|
}
|
|
101
|
-
function getSortedRenameEntries(renameMap) {
|
|
102
|
-
return Array.from(renameMap.entries()).sort((a, b) => a[1].newOrder - b[1].newOrder);
|
|
103
|
-
}
|
|
104
|
-
function renameMapsEqual(left, right) {
|
|
105
|
-
const leftEntries = getSortedRenameEntries(left);
|
|
106
|
-
const rightEntries = getSortedRenameEntries(right);
|
|
107
|
-
if (leftEntries.length !== rightEntries.length) {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
return leftEntries.every(([leftFilename, leftEntry], index) => {
|
|
111
|
-
const rightTuple = rightEntries[index];
|
|
112
|
-
if (!rightTuple)
|
|
113
|
-
return false;
|
|
114
|
-
const [rightFilename, rightEntry] = rightTuple;
|
|
115
|
-
return (leftFilename === rightFilename &&
|
|
116
|
-
leftEntry.newFilename === rightEntry.newFilename &&
|
|
117
|
-
leftEntry.newOrder === rightEntry.newOrder);
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
103
|
/**
|
|
121
104
|
* Applies a rename map to a {@link PatchQueueContext} so cross-patch lint
|
|
122
105
|
* can run against the projected state without touching disk. Exported for
|
|
@@ -159,7 +142,7 @@ function resolveDestination(target, manifestPatches, options) {
|
|
|
159
142
|
// integers, but the function is reachable from tests that may pass
|
|
160
143
|
// `{ to: NaN }` directly.
|
|
161
144
|
if (!Number.isInteger(options.to) || options.to <= 0) {
|
|
162
|
-
throw new InvalidArgumentError(`--to must be a positive integer, got ${
|
|
145
|
+
throw new InvalidArgumentError(`--to must be a positive integer, got ${options.to}.`, '--to');
|
|
163
146
|
}
|
|
164
147
|
return { destinationOrder: options.to, anchorFilename: undefined };
|
|
165
148
|
}
|
|
@@ -195,7 +178,14 @@ function resolveDestination(target, manifestPatches, options) {
|
|
|
195
178
|
}
|
|
196
179
|
return { destinationOrder: anchor.order + 1, anchorFilename: anchor.filename };
|
|
197
180
|
}
|
|
198
|
-
|
|
181
|
+
/**
|
|
182
|
+
* Re-validates the reorder against the freshly-loaded manifest and commits
|
|
183
|
+
* it, all under the patch directory lock.
|
|
184
|
+
*
|
|
185
|
+
* @param commitOptions - See {@link CommitReorderPlanOptions}
|
|
186
|
+
*/
|
|
187
|
+
async function commitReorderPlan(commitOptions) {
|
|
188
|
+
const { patchesDir, target, renameMap, anchorFilename, options, config, buildHistoryEntry } = commitOptions;
|
|
199
189
|
await withPatchDirectoryLock(patchesDir, async () => {
|
|
200
190
|
const currentManifest = await loadPatchesManifest(patchesDir);
|
|
201
191
|
if (!currentManifest || currentManifest.patches.length === 0) {
|
|
@@ -248,12 +238,7 @@ async function commitReorderPlan(patchesDir, target, renameMap, anchorFilename,
|
|
|
248
238
|
// permissions) is warned but not re-thrown: the mutation has already
|
|
249
239
|
// succeeded and is not reversible, so surfacing it as a command
|
|
250
240
|
// failure would mislead.
|
|
251
|
-
|
|
252
|
-
await appendHistory(patchesDir, buildHistoryEntry(currentRenameMap));
|
|
253
|
-
}
|
|
254
|
-
catch (historyError) {
|
|
255
|
-
warn(`History log append failed after patch reorder committed: ${toError(historyError).message}`);
|
|
256
|
-
}
|
|
241
|
+
await appendHistoryBestEffort(patchesDir, buildHistoryEntry(currentRenameMap), `patch reorder committed`);
|
|
257
242
|
}, { waitLockSeconds: resolveWaitLockSeconds(options.waitLock), command: 'patch reorder' });
|
|
258
243
|
}
|
|
259
244
|
/**
|
|
@@ -329,14 +314,8 @@ export async function patchReorderCommand(projectRoot, identifier, options = {})
|
|
|
329
314
|
unsafeOverride: options.forceUnsafe === true,
|
|
330
315
|
conflicts,
|
|
331
316
|
});
|
|
332
|
-
if (decision
|
|
333
|
-
outro('Dry run complete — no changes made');
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
if (decision === 'declined') {
|
|
337
|
-
outro('Reorder cancelled');
|
|
317
|
+
if (!proceedAfterDecision(decision, 'Reorder cancelled'))
|
|
338
318
|
return;
|
|
339
|
-
}
|
|
340
319
|
// The history entry is built inside commitReorderPlan (still under the
|
|
341
320
|
// lock) from the *final* rename map, not the pre-confirmation one, so
|
|
342
321
|
// the destinationOrder and renames mirror what actually landed on disk.
|
|
@@ -359,7 +338,15 @@ export async function patchReorderCommand(projectRoot, identifier, options = {})
|
|
|
359
338
|
result: 'ok',
|
|
360
339
|
};
|
|
361
340
|
};
|
|
362
|
-
await commitReorderPlan(
|
|
341
|
+
await commitReorderPlan({
|
|
342
|
+
patchesDir: paths.patches,
|
|
343
|
+
target,
|
|
344
|
+
renameMap,
|
|
345
|
+
anchorFilename,
|
|
346
|
+
options,
|
|
347
|
+
config,
|
|
348
|
+
buildHistoryEntry,
|
|
349
|
+
});
|
|
363
350
|
info(`Reordered ${renameMap.size} patch(es).`);
|
|
364
351
|
outro('Reorder complete');
|
|
365
352
|
}
|
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
* budget; consumed only by the split command.
|
|
7
7
|
*/
|
|
8
8
|
import { type ConflictReport } from '../../core/destructive.js';
|
|
9
|
-
import {
|
|
10
|
-
import { buildProjectedManifest } from '../../core/patch-policy.js';
|
|
9
|
+
import type { PatchQueueContext } from '../../core/patch-lint-cross.js';
|
|
11
10
|
import type { PatchStagedForwardImport } from '../../types/commands/index.js';
|
|
12
|
-
import type { PatchCategory, PatchMetadata } from '../../types/commands/index.js';
|
|
11
|
+
import type { PatchCategory, PatchesManifest, PatchMetadata } from '../../types/commands/index.js';
|
|
13
12
|
import type { FireForgeConfig } from '../../types/config.js';
|
|
14
13
|
import { type PlacementPlan } from '../export-flow.js';
|
|
15
14
|
/** Everything the commit step needs, computed and confirmed up front. */
|
|
@@ -68,7 +67,7 @@ export declare function mergeStagedForwardImports(patch: PatchMetadata, decls: r
|
|
|
68
67
|
* patch are auto-declared (and the declarations returned) so the projection
|
|
69
68
|
* matches the real per-patch gate the split leaves behind.
|
|
70
69
|
*/
|
|
71
|
-
export declare function runProjectedSplitLint(plan: SplitPlan, baseCtx:
|
|
70
|
+
export declare function runProjectedSplitLint(plan: SplitPlan, baseCtx: PatchQueueContext): {
|
|
72
71
|
conflicts: ConflictReport | null;
|
|
73
72
|
stagedDependencyAdditions: Map<string, PatchStagedForwardImport[]>;
|
|
74
73
|
};
|
|
@@ -76,7 +75,7 @@ export declare function runProjectedSplitLint(plan: SplitPlan, baseCtx: Awaited<
|
|
|
76
75
|
export declare function projectSplitManifest(manifest: {
|
|
77
76
|
version: 1;
|
|
78
77
|
patches: PatchMetadata[];
|
|
79
|
-
}, plan: SplitPlan, newMetadata: PatchMetadata):
|
|
78
|
+
}, plan: SplitPlan, newMetadata: PatchMetadata): PatchesManifest;
|
|
80
79
|
/**
|
|
81
80
|
* Builds the manifest row for the patch created by a split, carrying the
|
|
82
81
|
* source's provenance (product, version, category) onto the new patch.
|
|
@@ -10,16 +10,16 @@ import { join } from 'node:path';
|
|
|
10
10
|
import {} from '../../core/destructive.js';
|
|
11
11
|
import { getDiffForFilesAgainstHead } from '../../core/git-diff.js';
|
|
12
12
|
import { extractAffectedFiles } from '../../core/patch-apply.js';
|
|
13
|
-
import {
|
|
13
|
+
import { collectForwardImportEdges, formatPatchLintIssue, lintPatchQueue, } from '../../core/patch-lint.js';
|
|
14
14
|
import { computeProjectedLintRegressions } from '../../core/patch-lint-projection.js';
|
|
15
15
|
import { rewriteStagedDependencyOwners } from '../../core/patch-manifest.js';
|
|
16
16
|
import { applyRenameMapToManifest, buildProjectedManifest } from '../../core/patch-policy.js';
|
|
17
17
|
import { buildPatchSourceMetadata } from '../../core/patch-source-metadata.js';
|
|
18
|
-
import { buildNewFileTextProjection } from '../../core/patch-transform.js';
|
|
19
18
|
import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
|
|
20
19
|
import { pathExists } from '../../utils/fs.js';
|
|
21
20
|
import { warn } from '../../utils/logger.js';
|
|
22
21
|
import {} from '../export-flow.js';
|
|
22
|
+
import { projectEntryBody } from './entry-projection.js';
|
|
23
23
|
/**
|
|
24
24
|
* Refuses the split unless the source patch currently owns every requested
|
|
25
25
|
* file. Splitting out a file the source does not own would produce a new patch
|
|
@@ -81,10 +81,6 @@ export function rewriteSplitOwners(patch, sourceFilename, movedSet, newFilename)
|
|
|
81
81
|
},
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
|
-
function buildEntryProjection(diff) {
|
|
85
|
-
const newFiles = buildNewFileTextProjection(diff);
|
|
86
|
-
return { diff, newFiles, modifiedFileAdditions: buildModifiedFileAdditionsFromDiff(diff) };
|
|
87
|
-
}
|
|
88
84
|
/** Builds the projected post-split queue entries (renumber + shrunken source + new patch). */
|
|
89
85
|
function buildProjectedSplitEntries(baseCtx, plan) {
|
|
90
86
|
const movedSet = new Set(plan.movedFiles);
|
|
@@ -101,13 +97,13 @@ function buildProjectedSplitEntries(baseCtx, plan) {
|
|
|
101
97
|
: { ...entry, metadata };
|
|
102
98
|
if (entry.filename !== plan.source.filename)
|
|
103
99
|
return base;
|
|
104
|
-
return { ...base, ...
|
|
100
|
+
return { ...base, ...projectEntryBody(plan.remainingDiff) };
|
|
105
101
|
});
|
|
106
102
|
entries.push({
|
|
107
103
|
filename: plan.placement.newFilename,
|
|
108
104
|
order: plan.placement.insertionOrder,
|
|
109
105
|
metadata: null,
|
|
110
|
-
...
|
|
106
|
+
...projectEntryBody(plan.movedDiff),
|
|
111
107
|
});
|
|
112
108
|
entries.sort((a, b) => a.order - b.order || a.filename.localeCompare(b.filename));
|
|
113
109
|
return entries;
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
import { join } from 'node:path';
|
|
19
19
|
import { Command } from 'commander';
|
|
20
20
|
import { getProjectPaths, loadConfig } from '../../core/config.js';
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
21
|
+
import { assertConfirmationAvailable, confirmDestructive } from '../../core/destructive.js';
|
|
22
|
+
import { appendHistoryBestEffort } from '../../core/history-log.js';
|
|
23
23
|
import { formatPatchNotFoundError } from '../../core/patch-identifier-suggest.js';
|
|
24
24
|
import { buildPatchQueueContext } from '../../core/patch-lint.js';
|
|
25
25
|
import { withPatchDirectoryLock } from '../../core/patch-lock.js';
|
|
@@ -30,7 +30,8 @@ import { toError } from '../../utils/errors.js';
|
|
|
30
30
|
import { readText, removeFile, writeText } from '../../utils/fs.js';
|
|
31
31
|
import { info, intro, outro, success, warn } from '../../utils/logger.js';
|
|
32
32
|
import { addWaitLockOption, commanderArgParser, pickDefined, resolveWaitLockSeconds, } from '../../utils/options.js';
|
|
33
|
-
import { normalizePatchDisplayName } from '../../utils/validation.js';
|
|
33
|
+
import { normalizePatchDisplayName, parsePositiveIntegerFlag } from '../../utils/validation.js';
|
|
34
|
+
import { proceedAfterDecision } from '../destructive-decision.js';
|
|
34
35
|
import { placementPlansEqual, resolvePlacementPlan } from '../export-flow.js';
|
|
35
36
|
import { runPatchLint } from '../export-shared.js';
|
|
36
37
|
import { assertSourceOwnsFiles, buildNewPatchMetadata, buildSplitDiff, buildSplitSummary, findOwnerRewriteHolders, mergeStagedForwardImports, projectSplitManifest, rewriteSplitOwners, runProjectedSplitLint, } from './split-plan.js';
|
|
@@ -68,9 +69,9 @@ export async function commitPatchSplit(patchesDir, plan, newMetadata, options, c
|
|
|
68
69
|
await renumberPatchesInManifest(patchesDir, plan.placement.renameMap);
|
|
69
70
|
renumberApplied = true;
|
|
70
71
|
}
|
|
71
|
-
await writeText(newPatchPath,
|
|
72
|
+
await writeText(newPatchPath, plan.movedDiff);
|
|
72
73
|
newPatchWritten = true;
|
|
73
|
-
await writeText(sourcePathAfter,
|
|
74
|
+
await writeText(sourcePathAfter, plan.remainingDiff);
|
|
74
75
|
sourceRewritten = true;
|
|
75
76
|
const fresh = await loadPatchesManifest(patchesDir);
|
|
76
77
|
if (!fresh)
|
|
@@ -91,28 +92,23 @@ export async function commitPatchSplit(patchesDir, plan, newMetadata, options, c
|
|
|
91
92
|
updatedPatches.sort((a, b) => a.order - b.order || a.filename.localeCompare(b.filename));
|
|
92
93
|
const updated = validatePatchesManifest({ ...fresh, patches: updatedPatches });
|
|
93
94
|
await savePatchesManifest(patchesDir, updated);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
catch (historyError) {
|
|
114
|
-
warn(`History log append failed after patch split committed: ${toError(historyError).message}`);
|
|
115
|
-
}
|
|
95
|
+
await appendHistoryBestEffort(patchesDir, {
|
|
96
|
+
operation: 'patch-split',
|
|
97
|
+
args: {
|
|
98
|
+
source: effectiveSourceFilename,
|
|
99
|
+
newFilename: plan.placement.newFilename,
|
|
100
|
+
order: plan.placement.insertionOrder,
|
|
101
|
+
files: plan.movedFiles,
|
|
102
|
+
ownerRewrites: plan.ownerRewrites,
|
|
103
|
+
renames: [...plan.placement.renameMap.entries()].map(([from, entry]) => ({
|
|
104
|
+
from,
|
|
105
|
+
to: entry.newFilename,
|
|
106
|
+
})),
|
|
107
|
+
},
|
|
108
|
+
...(options.yes === true ? { yes: true } : {}),
|
|
109
|
+
...(options.forceUnsafe === true ? { unsafeOverride: true } : {}),
|
|
110
|
+
result: 'ok',
|
|
111
|
+
}, `patch split committed`);
|
|
116
112
|
}
|
|
117
113
|
catch (error) {
|
|
118
114
|
// Reverse-order rollback; each step warns on its own failure so the
|
|
@@ -226,8 +222,26 @@ export async function patchSplitCommand(projectRoot, sourceId, options) {
|
|
|
226
222
|
// instead of linting each projected body blind.
|
|
227
223
|
const patchQueueCtx = await buildPatchQueueContext(paths.patches, config);
|
|
228
224
|
const ignoreChecks = source.lintIgnore ? new Set(source.lintIgnore) : undefined;
|
|
229
|
-
await runPatchLint(
|
|
230
|
-
|
|
225
|
+
await runPatchLint({
|
|
226
|
+
engineDir: paths.engine,
|
|
227
|
+
filesAffected: remainingFiles,
|
|
228
|
+
diffContent: remainingDiff,
|
|
229
|
+
config,
|
|
230
|
+
skipLint: options.skipLint,
|
|
231
|
+
patchQueueCtx,
|
|
232
|
+
ignoreChecks,
|
|
233
|
+
patchTier: source.tier,
|
|
234
|
+
});
|
|
235
|
+
await runPatchLint({
|
|
236
|
+
engineDir: paths.engine,
|
|
237
|
+
filesAffected: movedFiles,
|
|
238
|
+
diffContent: movedDiff,
|
|
239
|
+
config,
|
|
240
|
+
skipLint: options.skipLint,
|
|
241
|
+
patchQueueCtx,
|
|
242
|
+
ignoreChecks,
|
|
243
|
+
patchTier: source.tier,
|
|
244
|
+
});
|
|
231
245
|
const { conflicts, stagedDependencyAdditions } = runProjectedSplitLint(plan, patchQueueCtx);
|
|
232
246
|
plan.stagedDependencyAdditions = stagedDependencyAdditions;
|
|
233
247
|
const newMetadata = buildNewPatchMetadata(plan, config);
|
|
@@ -246,14 +260,8 @@ export async function patchSplitCommand(projectRoot, sourceId, options) {
|
|
|
246
260
|
unsafeOverride: options.forceUnsafe === true,
|
|
247
261
|
conflicts,
|
|
248
262
|
});
|
|
249
|
-
if (decision
|
|
250
|
-
outro('Dry run complete — no changes made');
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
if (decision === 'declined') {
|
|
254
|
-
outro('Split cancelled');
|
|
263
|
+
if (!proceedAfterDecision(decision, 'Split cancelled'))
|
|
255
264
|
return;
|
|
256
|
-
}
|
|
257
265
|
await commitPatchSplit(paths.patches, plan, newMetadata, options, config);
|
|
258
266
|
success(`Split ${source.filename}: ${placement.newFilename} now owns ${plan.movedFiles.length} file(s)`);
|
|
259
267
|
if (plan.ownerRewrites.length > 0) {
|
|
@@ -273,13 +281,7 @@ export function registerPatchSplit(parent, context) {
|
|
|
273
281
|
.requiredOption('--name <name>', 'Name for the new patch')
|
|
274
282
|
.option('--category <category>', "Category for the new patch (default: the source patch's)")
|
|
275
283
|
.option('--description <desc>', 'Description for the new patch')
|
|
276
|
-
.option('--order <n>', 'Exact sparse order for the new patch', commanderArgParser((raw) =>
|
|
277
|
-
const n = Number.parseInt(raw, 10);
|
|
278
|
-
if (!Number.isInteger(n) || n <= 0) {
|
|
279
|
-
throw new InvalidArgumentError(`--order must be a positive integer, got "${raw}".`, '--order');
|
|
280
|
-
}
|
|
281
|
-
return n;
|
|
282
|
-
}))
|
|
284
|
+
.option('--order <n>', 'Exact sparse order for the new patch', commanderArgParser((raw) => parsePositiveIntegerFlag('--order', raw)))
|
|
283
285
|
.option('--before <patch>', 'Place the new patch before this patch')
|
|
284
286
|
.option('--after <patch>', 'Place the new patch after this patch (default: the source patch)')
|
|
285
287
|
.option('--dry-run', 'Show what would happen without writing')
|