@hominis/fireforge 0.44.4 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +120 -0
- package/dist/bin/fireforge.js +6 -0
- package/dist/build-info.json +4 -4
- package/dist/src/cli-usage-error.d.ts +46 -0
- package/dist/src/cli-usage-error.js +81 -0
- package/dist/src/cli.d.ts +5 -1
- package/dist/src/cli.js +20 -21
- package/dist/src/commands/bootstrap.js +1 -2
- package/dist/src/commands/build.js +15 -6
- package/dist/src/commands/destructive-decision.d.ts +19 -0
- package/dist/src/commands/destructive-decision.js +31 -0
- package/dist/src/commands/doctor-check-core.d.ts +0 -6
- package/dist/src/commands/doctor-check-core.js +0 -8
- package/dist/src/commands/doctor-orphaned-harness.d.ts +0 -6
- package/dist/src/commands/doctor-orphaned-harness.js +4 -12
- package/dist/src/commands/doctor-post-rebase-audit.js +1 -1
- package/dist/src/commands/doctor-source-pin.d.ts +29 -0
- package/dist/src/commands/doctor-source-pin.js +81 -0
- package/dist/src/commands/doctor.d.ts +2 -0
- package/dist/src/commands/doctor.js +9 -8
- package/dist/src/commands/download.js +35 -24
- package/dist/src/commands/export-all.js +21 -4
- package/dist/src/commands/export-flow.js +15 -60
- package/dist/src/commands/export-placement-conflicts.js +3 -3
- package/dist/src/commands/export-placement-gate.js +2 -8
- package/dist/src/commands/export-placement-policy.js +3 -5
- package/dist/src/commands/export-shared.d.ts +42 -23
- package/dist/src/commands/export-shared.js +4 -19
- package/dist/src/commands/export.js +10 -1
- package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
- package/dist/src/commands/furnace/authoring-config.js +19 -0
- package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
- package/dist/src/commands/furnace/create-browser-test.js +3 -8
- package/dist/src/commands/furnace/create-dry-run.js +2 -5
- package/dist/src/commands/furnace/create-templates.d.ts +17 -1
- package/dist/src/commands/furnace/create-templates.js +2 -1
- package/dist/src/commands/furnace/create.js +25 -19
- package/dist/src/commands/furnace/deploy.js +11 -2
- package/dist/src/commands/furnace/init.js +2 -1
- package/dist/src/commands/furnace/override.js +28 -19
- package/dist/src/commands/furnace/refresh.js +9 -1
- package/dist/src/commands/furnace/remove-mutations.d.ts +6 -7
- package/dist/src/commands/furnace/remove-mutations.js +0 -1
- package/dist/src/commands/furnace/remove.js +2 -6
- package/dist/src/commands/furnace/rename-browser-test.js +1 -7
- package/dist/src/commands/furnace/rename.js +29 -29
- package/dist/src/commands/furnace/status.js +12 -9
- package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
- package/dist/src/commands/furnace/test-file-name.js +39 -0
- package/dist/src/commands/furnace/validation-output.d.ts +21 -9
- package/dist/src/commands/furnace/validation-output.js +3 -8
- package/dist/src/commands/import.js +2 -9
- package/dist/src/commands/lint-per-patch.d.ts +2 -2
- package/dist/src/commands/lint-per-patch.js +19 -4
- package/dist/src/commands/lint-per-run-checkjs.d.ts +2 -2
- package/dist/src/commands/lint.d.ts +2 -1
- package/dist/src/commands/lint.js +1 -1
- package/dist/src/commands/manifest.js +1 -1
- package/dist/src/commands/package.js +2 -4
- package/dist/src/commands/patch/compact.js +14 -15
- package/dist/src/commands/patch/delete.js +15 -25
- package/dist/src/commands/patch/entry-projection.d.ts +13 -0
- package/dist/src/commands/patch/entry-projection.js +24 -0
- package/dist/src/commands/patch/lint-ignore.d.ts +0 -8
- package/dist/src/commands/patch/lint-ignore.js +19 -29
- package/dist/src/commands/patch/move-files-create.js +24 -10
- package/dist/src/commands/patch/move-files-into.js +38 -37
- package/dist/src/commands/patch/move-files.js +4 -13
- package/dist/src/commands/patch/rename.js +27 -41
- package/dist/src/commands/patch/reorder.js +25 -38
- package/dist/src/commands/patch/split-plan.d.ts +4 -5
- package/dist/src/commands/patch/split-plan.js +4 -11
- package/dist/src/commands/patch/split.js +45 -43
- package/dist/src/commands/patch/staged-dependency.js +16 -22
- package/dist/src/commands/patch/tier.js +15 -25
- package/dist/src/commands/patch-rename-map.d.ts +19 -0
- package/dist/src/commands/patch-rename-map.js +36 -0
- package/dist/src/commands/pending-resolution.d.ts +15 -0
- package/dist/src/commands/pending-resolution.js +25 -0
- package/dist/src/commands/re-export-adjacent.d.ts +32 -2
- package/dist/src/commands/re-export-adjacent.js +51 -10
- package/dist/src/commands/re-export-bulk-scan.js +6 -10
- package/dist/src/commands/re-export-drift.d.ts +8 -0
- package/dist/src/commands/re-export-drift.js +65 -4
- package/dist/src/commands/re-export-files.d.ts +2 -3
- package/dist/src/commands/re-export-files.js +38 -32
- package/dist/src/commands/re-export-lint.d.ts +3 -4
- package/dist/src/commands/re-export-lint.js +9 -2
- package/dist/src/commands/re-export-options.js +19 -5
- package/dist/src/commands/re-export-register.js +5 -1
- package/dist/src/commands/re-export-scan.js +8 -8
- package/dist/src/commands/re-export-single.d.ts +22 -3
- package/dist/src/commands/re-export-single.js +19 -11
- package/dist/src/commands/re-export.js +44 -27
- package/dist/src/commands/rebase/abort.js +3 -8
- package/dist/src/commands/rebase/continue.js +12 -12
- package/dist/src/commands/rebase/index.js +2 -2
- package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
- package/dist/src/commands/rebase/patch-loop.js +2 -7
- package/dist/src/commands/resolve.js +18 -10
- package/dist/src/commands/run.d.ts +0 -13
- package/dist/src/commands/run.js +12 -25
- package/dist/src/commands/setup-support.d.ts +16 -4
- package/dist/src/commands/setup-support.js +16 -16
- package/dist/src/commands/setup.js +3 -9
- package/dist/src/commands/source.js +8 -1
- package/dist/src/commands/status-check.js +2 -2
- package/dist/src/commands/status-output.js +2 -2
- package/dist/src/commands/status.js +3 -4
- package/dist/src/commands/test-build-phase.d.ts +2 -2
- package/dist/src/commands/test-build-phase.js +33 -8
- package/dist/src/commands/test-diagnose.js +14 -5
- package/dist/src/commands/test-modes.d.ts +2 -2
- package/dist/src/commands/test-modes.js +0 -1
- package/dist/src/commands/test-register.js +6 -3
- package/dist/src/commands/test-run.js +29 -21
- package/dist/src/commands/test-stale-gate.js +8 -5
- package/dist/src/commands/test-verdict.d.ts +16 -2
- package/dist/src/commands/test-verdict.js +18 -3
- package/dist/src/commands/test.js +51 -7
- package/dist/src/commands/token-list.d.ts +26 -0
- package/dist/src/commands/token-list.js +153 -0
- package/dist/src/commands/token.js +17 -1
- package/dist/src/commands/tree.js +7 -2
- package/dist/src/commands/typecheck.js +9 -9
- package/dist/src/commands/wire.js +4 -3
- package/dist/src/core/branding.d.ts +0 -7
- package/dist/src/core/branding.js +3 -2
- package/dist/src/core/browser-wire.js +6 -3
- package/dist/src/core/build-audit-platform.d.ts +29 -0
- package/dist/src/core/build-audit-platform.js +140 -0
- package/dist/src/core/build-audit-registration.js +3 -2
- package/dist/src/core/build-audit-skip.d.ts +57 -0
- package/dist/src/core/build-audit-skip.js +112 -0
- package/dist/src/core/build-audit.d.ts +15 -2
- package/dist/src/core/build-audit.js +154 -12
- package/dist/src/core/build-baseline.d.ts +45 -28
- package/dist/src/core/build-baseline.js +6 -29
- package/dist/src/core/build-jar-escalation.d.ts +64 -0
- package/dist/src/core/build-jar-escalation.js +128 -0
- package/dist/src/core/build-overwrite-guard.js +1 -1
- package/dist/src/core/build-prepare.d.ts +5 -2
- package/dist/src/core/build-prepare.js +34 -6
- package/dist/src/core/config-paths.d.ts +2 -6
- package/dist/src/core/config-paths.js +4 -2
- package/dist/src/core/config-state.d.ts +0 -6
- package/dist/src/core/config-state.js +1 -19
- package/dist/src/core/config-validate-build-audit.d.ts +8 -0
- package/dist/src/core/config-validate-build-audit.js +80 -0
- package/dist/src/core/config-validate-file-size.d.ts +12 -0
- package/dist/src/core/config-validate-file-size.js +65 -0
- package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
- package/dist/src/core/config-validate-patch-policy.js +3 -3
- package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
- package/dist/src/core/config-validate-test-toolchains.js +4 -5
- package/dist/src/core/config-validate.js +20 -3
- package/dist/src/core/config.d.ts +2 -2
- package/dist/src/core/config.js +11 -15
- package/dist/src/core/coverage-extend.js +4 -4
- package/dist/src/core/discard-baseline.js +7 -7
- package/dist/src/core/engine-precondition.d.ts +1 -9
- package/dist/src/core/engine-precondition.js +3 -8
- package/dist/src/core/engine-session-lock.js +13 -17
- package/dist/src/core/export-moved-code-shape.js +2 -2
- package/dist/src/core/file-lock-owner.d.ts +103 -0
- package/dist/src/core/file-lock-owner.js +259 -0
- package/dist/src/core/file-lock.d.ts +2 -7
- package/dist/src/core/file-lock.js +40 -91
- package/dist/src/core/firefox-cache.d.ts +13 -1
- package/dist/src/core/firefox-cache.js +59 -34
- package/dist/src/core/firefox-download.d.ts +15 -0
- package/dist/src/core/firefox-download.js +37 -21
- package/dist/src/core/firefox-extract.d.ts +6 -5
- package/dist/src/core/firefox-extract.js +36 -18
- package/dist/src/core/firefox.d.ts +30 -32
- package/dist/src/core/firefox.js +5 -36
- package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
- package/dist/src/core/furnace-apply-dry-run.js +1 -7
- package/dist/src/core/furnace-apply-helpers.d.ts +0 -11
- package/dist/src/core/furnace-apply-helpers.js +8 -19
- package/dist/src/core/furnace-apply-overwrite-warn.js +4 -2
- package/dist/src/core/furnace-apply.d.ts +1 -1
- package/dist/src/core/furnace-apply.js +30 -10
- package/dist/src/core/furnace-config.d.ts +1 -1
- package/dist/src/core/furnace-config.js +23 -36
- package/dist/src/core/furnace-constants.js +3 -2
- package/dist/src/core/furnace-dir-entry.d.ts +20 -0
- package/dist/src/core/furnace-dir-entry.js +21 -0
- package/dist/src/core/furnace-operation.d.ts +2 -0
- package/dist/src/core/furnace-operation.js +3 -1
- package/dist/src/core/furnace-refresh.d.ts +18 -6
- package/dist/src/core/furnace-refresh.js +3 -6
- package/dist/src/core/furnace-registration-ast.d.ts +0 -1
- package/dist/src/core/furnace-registration-ast.js +0 -3
- package/dist/src/core/furnace-registration.d.ts +2 -1
- package/dist/src/core/furnace-registration.js +22 -29
- package/dist/src/core/furnace-scanner.js +3 -6
- package/dist/src/core/furnace-state-persist.d.ts +2 -2
- package/dist/src/core/furnace-validate-registration.js +5 -7
- package/dist/src/core/git-base.d.ts +3 -1
- package/dist/src/core/git-base.js +25 -3
- package/dist/src/core/git-diff-new-file.d.ts +40 -0
- package/dist/src/core/git-diff-new-file.js +119 -0
- package/dist/src/core/git-diff.d.ts +9 -9
- package/dist/src/core/git-diff.js +88 -157
- package/dist/src/core/git-file-ops.d.ts +0 -7
- package/dist/src/core/git-file-ops.js +4 -28
- package/dist/src/core/git-readonly-index.d.ts +0 -2
- package/dist/src/core/git-readonly-index.js +0 -4
- package/dist/src/core/git-status.d.ts +7 -0
- package/dist/src/core/git-status.js +21 -9
- package/dist/src/core/git.d.ts +0 -6
- package/dist/src/core/git.js +14 -43
- package/dist/src/core/harness-orphans.d.ts +73 -0
- package/dist/src/core/harness-orphans.js +187 -0
- package/dist/src/core/history-log.d.ts +19 -0
- package/dist/src/core/history-log.js +29 -0
- package/dist/src/core/mach-error-hints.d.ts +0 -5
- package/dist/src/core/mach-error-hints.js +1 -1
- package/dist/src/core/mach-known-noise-filter.d.ts +25 -3
- package/dist/src/core/mach-known-noise-filter.js +31 -10
- package/dist/src/core/mach-mozconfig.js +2 -1
- package/dist/src/core/mach-python.d.ts +2 -0
- package/dist/src/core/mach-python.js +12 -7
- package/dist/src/core/mach-resource-shim.d.ts +13 -1
- package/dist/src/core/mach-resource-shim.js +112 -9
- package/dist/src/core/mach.d.ts +46 -53
- package/dist/src/core/mach.js +31 -83
- package/dist/src/core/marionette-port.d.ts +0 -17
- package/dist/src/core/marionette-port.js +12 -58
- package/dist/src/core/marionette-preflight.js +9 -27
- package/dist/src/core/mochitest-server-port.js +3 -3
- package/dist/src/core/moz-manifest-rules.js +37 -33
- package/dist/src/core/patch-apply-fuzz.js +4 -4
- package/dist/src/core/patch-apply.d.ts +4 -5
- package/dist/src/core/patch-apply.js +55 -32
- package/dist/src/core/patch-export-coverage.d.ts +0 -9
- package/dist/src/core/patch-export-coverage.js +1 -30
- package/dist/src/core/patch-export-update.d.ts +18 -1
- package/dist/src/core/patch-export-update.js +4 -3
- package/dist/src/core/patch-export.d.ts +11 -21
- package/dist/src/core/patch-export.js +44 -66
- package/dist/src/core/patch-files.d.ts +0 -4
- package/dist/src/core/patch-files.js +0 -11
- package/dist/src/core/patch-lint-binary.d.ts +6 -12
- package/dist/src/core/patch-lint-cache.d.ts +17 -1
- package/dist/src/core/patch-lint-cache.js +3 -6
- package/dist/src/core/patch-lint-checkjs.d.ts +33 -16
- package/dist/src/core/patch-lint-checkjs.js +43 -25
- package/dist/src/core/patch-lint-creators.d.ts +2 -6
- package/dist/src/core/patch-lint-cross.d.ts +16 -8
- package/dist/src/core/patch-lint-cross.js +49 -92
- package/dist/src/core/patch-lint-css.js +10 -6
- package/dist/src/core/patch-lint-file-size.d.ts +52 -0
- package/dist/src/core/patch-lint-file-size.js +53 -0
- package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
- package/dist/src/core/patch-lint-forward-registration.js +210 -0
- package/dist/src/core/patch-lint-jsdoc.js +36 -36
- package/dist/src/core/patch-lint-module-registration.d.ts +2 -8
- package/dist/src/core/patch-lint-prettier.d.ts +36 -0
- package/dist/src/core/patch-lint-prettier.js +126 -0
- package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
- package/dist/src/core/patch-lint-queue-types.js +12 -0
- package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
- package/dist/src/core/patch-lint-staged-registration.js +90 -0
- package/dist/src/core/patch-lint-unmanaged-companion.d.ts +106 -0
- package/dist/src/core/patch-lint-unmanaged-companion.js +214 -0
- package/dist/src/core/patch-lint.d.ts +14 -9
- package/dist/src/core/patch-lint.js +27 -27
- package/dist/src/core/patch-lock.js +3 -3
- package/dist/src/core/patch-manifest-io.js +8 -7
- package/dist/src/core/patch-parse.d.ts +19 -8
- package/dist/src/core/patch-parse.js +25 -0
- package/dist/src/core/patch-policy.js +15 -27
- package/dist/src/core/patch-source-metadata.d.ts +1 -3
- package/dist/src/core/patch-source-metadata.js +0 -4
- package/dist/src/core/patch-transform.d.ts +21 -8
- package/dist/src/core/patch-transform.js +38 -10
- package/dist/src/core/process-cpu-time.js +1 -1
- package/dist/src/core/rebase-session.d.ts +2 -8
- package/dist/src/core/rebase-session.js +5 -17
- package/dist/src/core/register-browser-content.js +3 -2
- package/dist/src/core/register-browser-test.js +2 -1
- package/dist/src/core/register-module.js +2 -1
- package/dist/src/core/register-scaffold.js +5 -4
- package/dist/src/core/register-shared-css.js +2 -1
- package/dist/src/core/run-log-redact.d.ts +38 -0
- package/dist/src/core/run-log-redact.js +69 -0
- package/dist/src/core/run-log.d.ts +13 -2
- package/dist/src/core/run-log.js +67 -18
- package/dist/src/core/signal-critical.js +2 -4
- package/dist/src/core/smoke-patterns.d.ts +0 -1
- package/dist/src/core/smoke-patterns.js +18 -11
- package/dist/src/core/state-file.d.ts +12 -1
- package/dist/src/core/state-file.js +14 -1
- package/dist/src/core/status-classify.js +2 -1
- package/dist/src/core/test-changed-prefs.js +26 -11
- package/dist/src/core/test-harness-crash.js +61 -48
- package/dist/src/core/test-harness-verdict-notes.d.ts +55 -0
- package/dist/src/core/test-harness-verdict-notes.js +48 -0
- package/dist/src/core/test-stale-check.d.ts +23 -8
- package/dist/src/core/test-stale-check.js +77 -4
- package/dist/src/core/test-stall-triage.js +37 -19
- package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
- package/dist/src/core/test-xpcshell-retry.js +14 -11
- package/dist/src/core/token-category.d.ts +33 -0
- package/dist/src/core/token-category.js +56 -48
- package/dist/src/core/token-dark-mode.js +2 -14
- package/dist/src/core/token-inventory.d.ts +56 -0
- package/dist/src/core/token-inventory.js +109 -0
- package/dist/src/core/token-manager.d.ts +1 -1
- package/dist/src/core/token-manager.js +1 -1
- package/dist/src/core/token-variant.js +21 -22
- package/dist/src/core/toolchain-preflight.js +1 -1
- package/dist/src/core/tree-cow.d.ts +0 -2
- package/dist/src/core/tree-cow.js +2 -2
- package/dist/src/core/tree-store.js +4 -5
- package/dist/src/core/typecheck.js +2 -2
- package/dist/src/core/wire-dom-fragment.js +6 -17
- package/dist/src/core/wire-utils.js +5 -17
- package/dist/src/core/xpcshell-appdir.js +2 -4
- package/dist/src/errors/base.d.ts +53 -1
- package/dist/src/errors/base.js +79 -11
- package/dist/src/errors/build.d.ts +28 -0
- package/dist/src/errors/build.js +51 -10
- package/dist/src/errors/config.d.ts +0 -14
- package/dist/src/errors/config.js +13 -34
- package/dist/src/errors/download.d.ts +13 -0
- package/dist/src/errors/download.js +53 -24
- package/dist/src/errors/furnace.js +8 -9
- package/dist/src/errors/git.d.ts +8 -8
- package/dist/src/errors/git.js +20 -30
- package/dist/src/errors/patch.js +8 -10
- package/dist/src/errors/rebase.js +7 -7
- package/dist/src/types/commands/options.d.ts +96 -123
- package/dist/src/types/commands/patches.d.ts +17 -6
- package/dist/src/types/config.d.ts +75 -2
- package/dist/src/types/index.d.ts +0 -1
- package/dist/src/utils/assert.d.ts +3 -15
- package/dist/src/utils/assert.js +1 -18
- package/dist/src/utils/build-info.d.ts +1 -1
- package/dist/src/utils/child-env.d.ts +2 -1
- package/dist/src/utils/elapsed.js +10 -1
- package/dist/src/utils/fs.d.ts +4 -2
- package/dist/src/utils/fs.js +11 -14
- package/dist/src/utils/hash.d.ts +12 -0
- package/dist/src/utils/hash.js +16 -0
- package/dist/src/utils/line-dispatch.d.ts +15 -0
- package/dist/src/utils/line-dispatch.js +48 -0
- package/dist/src/utils/options.d.ts +1 -7
- package/dist/src/utils/options.js +6 -7
- package/dist/src/utils/package-root.js +3 -12
- package/dist/src/utils/platform.d.ts +0 -4
- package/dist/src/utils/process-group.d.ts +2 -1
- package/dist/src/utils/process-group.js +20 -19
- package/dist/src/utils/process.d.ts +40 -26
- package/dist/src/utils/process.js +115 -166
- package/dist/src/utils/ps-duration.js +3 -3
- package/dist/src/utils/sleep.d.ts +26 -0
- package/dist/src/utils/sleep.js +31 -0
- package/dist/src/utils/validation.d.ts +2 -38
- package/dist/src/utils/validation.js +7 -16
- package/docs/configuration.md +136 -1
- package/docs/exit-codes.md +34 -20
- package/docs/lifecycle-invariants.md +18 -12
- package/docs/machine-output.md +31 -8
- package/docs/patch-workflow.md +2 -1
- package/docs/run-logs.md +44 -4
- package/docs/testing.md +70 -11
- package/package.json +8 -7
- package/dist/src/commands/rebase.d.ts +0 -4
- package/dist/src/commands/rebase.js +0 -5
- package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
- package/dist/src/core/patch-artifact-normalize.js +0 -24
- package/dist/src/core/patch-lint-reexports.d.ts +0 -7
- package/dist/src/core/patch-lint-reexports.js +0 -8
- package/dist/src/core/wire-targets.d.ts +0 -7
- package/dist/src/core/wire-targets.js +0 -8
|
@@ -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')
|
|
@@ -4,11 +4,10 @@
|
|
|
4
4
|
* PatchMetadata.stagedDependencies without rewriting the .patch body.
|
|
5
5
|
*/
|
|
6
6
|
import { Command } from 'commander';
|
|
7
|
-
import {
|
|
7
|
+
import { appendHistoryBestEffort } from '../../core/history-log.js';
|
|
8
8
|
import { mutatePatchMetadata } from '../../core/patch-export.js';
|
|
9
9
|
import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
|
|
10
|
-
import {
|
|
11
|
-
import { info, intro, outro, warn } from '../../utils/logger.js';
|
|
10
|
+
import { info, intro, outro } from '../../utils/logger.js';
|
|
12
11
|
import { addWaitLockOption, resolveWaitLockSeconds } from '../../utils/options.js';
|
|
13
12
|
import { requirePatchQueue, requirePatchTarget } from './patch-context.js';
|
|
14
13
|
import { validateStagedDependencyAdd } from './staged-dependency-validate.js';
|
|
@@ -113,7 +112,7 @@ function resolveImportRemovalTarget(options, staged) {
|
|
|
113
112
|
const list = candidates
|
|
114
113
|
.map((entry) => ` - ${dependencyLabel('import', importView(entry))}`)
|
|
115
114
|
.join('\n');
|
|
116
|
-
throw new GeneralError(`--remove matches ${
|
|
115
|
+
throw new GeneralError(`--remove matches ${candidates.length} staged forward-imports on this patch; ` +
|
|
117
116
|
`pass --creates to pick one:\n${list}`);
|
|
118
117
|
}
|
|
119
118
|
return { file, specifier, creates: '(no match)' };
|
|
@@ -142,7 +141,7 @@ function resolveRegistrationRemovalTarget(options, staged) {
|
|
|
142
141
|
const list = candidates
|
|
143
142
|
.map((entry) => ` - ${dependencyLabel('registration', registrationView(entry))}`)
|
|
144
143
|
.join('\n');
|
|
145
|
-
throw new GeneralError(`--remove matches ${
|
|
144
|
+
throw new GeneralError(`--remove matches ${candidates.length} staged registrations on this patch; ` +
|
|
146
145
|
`pass --creates to pick one:\n${list}`);
|
|
147
146
|
}
|
|
148
147
|
return { file, line, creates: '(no match)' };
|
|
@@ -280,23 +279,18 @@ export async function patchStagedDependencyCommand(projectRoot, identifier, opti
|
|
|
280
279
|
const before = result.before.stagedDependencies;
|
|
281
280
|
const after = result.after.stagedDependencies;
|
|
282
281
|
info(`${targetPatch.filename}: ${describeStagedDependencyChange(countStagedEntries(before), countStagedEntries(after), mode, kind, target)}.`);
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
catch (historyError) {
|
|
298
|
-
warn(`History log append failed after patch staged-dependency committed (${targetPatch.filename}): ${toError(historyError).message}`);
|
|
299
|
-
}
|
|
282
|
+
await appendHistoryBestEffort(paths.patches, {
|
|
283
|
+
operation: 'patch-staged-dependency',
|
|
284
|
+
args: {
|
|
285
|
+
filename: targetPatch.filename,
|
|
286
|
+
mode,
|
|
287
|
+
kind,
|
|
288
|
+
before: before ?? {},
|
|
289
|
+
after: after ?? {},
|
|
290
|
+
},
|
|
291
|
+
...(options.yes === true ? { yes: true } : {}),
|
|
292
|
+
result: 'ok',
|
|
293
|
+
}, `patch staged-dependency committed (${targetPatch.filename})`);
|
|
300
294
|
outro('Patch staged-dependency complete');
|
|
301
295
|
}
|
|
302
296
|
/**
|
|
@@ -15,12 +15,13 @@
|
|
|
15
15
|
* `--clear` must be supplied per invocation.
|
|
16
16
|
*/
|
|
17
17
|
import { Command, Option } from 'commander';
|
|
18
|
-
import {
|
|
18
|
+
import { confirmDestructive } from '../../core/destructive.js';
|
|
19
|
+
import { appendHistoryBestEffort } from '../../core/history-log.js';
|
|
19
20
|
import { updatePatchMetadata } from '../../core/patch-export.js';
|
|
20
21
|
import { InvalidArgumentError } from '../../errors/base.js';
|
|
21
|
-
import {
|
|
22
|
-
import { info, intro, outro, warn } from '../../utils/logger.js';
|
|
22
|
+
import { info, intro, outro } from '../../utils/logger.js';
|
|
23
23
|
import { addWaitLockOption, pickDefined, resolveWaitLockSeconds } from '../../utils/options.js';
|
|
24
|
+
import { proceedAfterDecision } from '../destructive-decision.js';
|
|
24
25
|
import { requirePatchQueue, requirePatchTarget } from './patch-context.js';
|
|
25
26
|
/**
|
|
26
27
|
* Runs the `patch tier` command: updates `PatchMetadata.tier` on the
|
|
@@ -71,14 +72,8 @@ export async function patchTierCommand(projectRoot, identifier, options = {}) {
|
|
|
71
72
|
yes: options.yes === true,
|
|
72
73
|
dryRun: isDryRun,
|
|
73
74
|
});
|
|
74
|
-
if (decision
|
|
75
|
-
outro('Dry run complete — no changes made');
|
|
75
|
+
if (!proceedAfterDecision(decision, 'Cancelled — no changes made'))
|
|
76
76
|
return;
|
|
77
|
-
}
|
|
78
|
-
if (decision === 'declined') {
|
|
79
|
-
outro('Cancelled — no changes made');
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
77
|
// Single write under the patch directory lock (delegated inside
|
|
83
78
|
// updatePatchMetadata). Setting routes through `updates`; clearing
|
|
84
79
|
// routes through `unsetFields` so TypeScript's exact optional types
|
|
@@ -92,21 +87,16 @@ export async function patchTierCommand(projectRoot, identifier, options = {}) {
|
|
|
92
87
|
else {
|
|
93
88
|
await updatePatchMetadata(paths.patches, target.filename, {}, ['tier']);
|
|
94
89
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
catch (historyError) {
|
|
108
|
-
warn(`History log append failed after patch tier committed (${target.filename}): ${toError(historyError).message}`);
|
|
109
|
-
}
|
|
90
|
+
await appendHistoryBestEffort(paths.patches, {
|
|
91
|
+
operation: 'patch-tier',
|
|
92
|
+
args: {
|
|
93
|
+
filename: target.filename,
|
|
94
|
+
...(before !== undefined ? { before } : {}),
|
|
95
|
+
...(after !== undefined ? { after } : {}),
|
|
96
|
+
},
|
|
97
|
+
...(options.yes === true ? { yes: true } : {}),
|
|
98
|
+
result: 'ok',
|
|
99
|
+
}, `patch tier committed (${target.filename})`);
|
|
110
100
|
info(`${target.filename}: ${action}.`);
|
|
111
101
|
outro('Patch tier complete');
|
|
112
102
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural equality for patch rename maps, shared by the placement-mode
|
|
3
|
+
* export preview/commit check (`export-flow.ts`) and `patch reorder`'s
|
|
4
|
+
* under-lock drift check. Both previously carried a byte-identical copy of
|
|
5
|
+
* the sorted-entries comparison; one helper keeps the two commit gates from
|
|
6
|
+
* drifting.
|
|
7
|
+
*/
|
|
8
|
+
import type { PatchRenameEntry } from '../core/patch-manifest.js';
|
|
9
|
+
/**
|
|
10
|
+
* Rename-map entries ordered by target order, so previews, history entries
|
|
11
|
+
* and equality checks all walk the queue in its final on-disk order.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getSortedRenameEntries(renameMap: Map<string, PatchRenameEntry>): Array<[string, PatchRenameEntry]>;
|
|
14
|
+
/**
|
|
15
|
+
* Compares two rename maps entry by entry after sorting both by target
|
|
16
|
+
* order. Equal when every (current filename, new filename, new order)
|
|
17
|
+
* triple matches positionally.
|
|
18
|
+
*/
|
|
19
|
+
export declare function renameMapsEqual(left: Map<string, PatchRenameEntry>, right: Map<string, PatchRenameEntry>): boolean;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Structural equality for patch rename maps, shared by the placement-mode
|
|
4
|
+
* export preview/commit check (`export-flow.ts`) and `patch reorder`'s
|
|
5
|
+
* under-lock drift check. Both previously carried a byte-identical copy of
|
|
6
|
+
* the sorted-entries comparison; one helper keeps the two commit gates from
|
|
7
|
+
* drifting.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Rename-map entries ordered by target order, so previews, history entries
|
|
11
|
+
* and equality checks all walk the queue in its final on-disk order.
|
|
12
|
+
*/
|
|
13
|
+
export function getSortedRenameEntries(renameMap) {
|
|
14
|
+
return Array.from(renameMap.entries()).sort((a, b) => a[1].newOrder - b[1].newOrder);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Compares two rename maps entry by entry after sorting both by target
|
|
18
|
+
* order. Equal when every (current filename, new filename, new order)
|
|
19
|
+
* triple matches positionally.
|
|
20
|
+
*/
|
|
21
|
+
export function renameMapsEqual(left, right) {
|
|
22
|
+
const leftEntries = getSortedRenameEntries(left);
|
|
23
|
+
const rightEntries = getSortedRenameEntries(right);
|
|
24
|
+
if (leftEntries.length !== rightEntries.length) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return leftEntries.every(([leftFilename, leftEntry], index) => {
|
|
28
|
+
const rightTuple = rightEntries[index];
|
|
29
|
+
if (!rightTuple)
|
|
30
|
+
return false;
|
|
31
|
+
const [rightFilename, rightEntry] = rightTuple;
|
|
32
|
+
return (leftFilename === rightFilename &&
|
|
33
|
+
leftEntry.newFilename === rightEntry.newFilename &&
|
|
34
|
+
leftEntry.newOrder === rightEntry.newOrder);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clears `pendingResolution` from `state.json` transactionally.
|
|
3
|
+
*
|
|
4
|
+
* Five commands (`doctor --clear-resolution`, `resolve`, `rebase --continue`,
|
|
5
|
+
* `rebase --abort`, and the rebase patch loop) had each copied the same
|
|
6
|
+
* spread-then-`delete` updater. Routing the clear through `updateState` rather
|
|
7
|
+
* than a load/modify/save pair is the load-bearing part: the updater runs
|
|
8
|
+
* inside the state-file lock against the freshest on-disk state, so a
|
|
9
|
+
* concurrent write to an unrelated key (`buildMode`, `baseCommit`) is not
|
|
10
|
+
* clobbered by a stale reload. Copies of that reasoning drift; one does not.
|
|
11
|
+
*
|
|
12
|
+
* A no-op when nothing is pending, so callers need no guard of their own.
|
|
13
|
+
* @param projectRoot - Project root holding `.fireforge/state.json`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function clearPendingResolution(projectRoot: string): Promise<void>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
import { updateState } from '../core/config.js';
|
|
3
|
+
/**
|
|
4
|
+
* Clears `pendingResolution` from `state.json` transactionally.
|
|
5
|
+
*
|
|
6
|
+
* Five commands (`doctor --clear-resolution`, `resolve`, `rebase --continue`,
|
|
7
|
+
* `rebase --abort`, and the rebase patch loop) had each copied the same
|
|
8
|
+
* spread-then-`delete` updater. Routing the clear through `updateState` rather
|
|
9
|
+
* than a load/modify/save pair is the load-bearing part: the updater runs
|
|
10
|
+
* inside the state-file lock against the freshest on-disk state, so a
|
|
11
|
+
* concurrent write to an unrelated key (`buildMode`, `baseCommit`) is not
|
|
12
|
+
* clobbered by a stale reload. Copies of that reasoning drift; one does not.
|
|
13
|
+
*
|
|
14
|
+
* A no-op when nothing is pending, so callers need no guard of their own.
|
|
15
|
+
* @param projectRoot - Project root holding `.fireforge/state.json`.
|
|
16
|
+
*/
|
|
17
|
+
export async function clearPendingResolution(projectRoot) {
|
|
18
|
+
await updateState(projectRoot, (current) => {
|
|
19
|
+
if (!current.pendingResolution)
|
|
20
|
+
return current;
|
|
21
|
+
const next = { ...current };
|
|
22
|
+
delete next.pendingResolution;
|
|
23
|
+
return next;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { getProjectPaths } from '../core/config.js';
|
|
2
1
|
import type { PatchesManifest, PatchMetadata } from '../types/commands/index.js';
|
|
2
|
+
import type { ProjectPaths } from '../types/config.js';
|
|
3
3
|
/**
|
|
4
4
|
* Shared per-run context for the adjacency advisory: the classification
|
|
5
5
|
* inputs computed once by `reExportCommand`, plus the collection of
|
|
@@ -10,13 +10,43 @@ export interface AdjacentUnmanagedContext {
|
|
|
10
10
|
binaryName: string;
|
|
11
11
|
furnacePrefixes: ReadonlySet<string>;
|
|
12
12
|
refuseAdjacentUnmanaged: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Engine-relative paths named by `--expect-unmanaged`: reviewed, recorded
|
|
15
|
+
* exceptions that are reported but never refuse. Normalized once by the
|
|
16
|
+
* caller so the comparison matches git's engine-relative output.
|
|
17
|
+
*/
|
|
18
|
+
approvedUnmanaged: ReadonlySet<string>;
|
|
19
|
+
/** Approved paths actually met this run, so unused ones can be surfaced. */
|
|
20
|
+
approvedSeen: Set<string>;
|
|
21
|
+
/**
|
|
22
|
+
* Offenders collected across the loop. `files` stays the bare paths that
|
|
23
|
+
* downstream consumers already read; `anchored` carries the same entries
|
|
24
|
+
* with the owned directory that made each one adjacent, so the run-level
|
|
25
|
+
* refusal can name the anchor without re-deriving it.
|
|
26
|
+
*/
|
|
13
27
|
refusals: {
|
|
14
28
|
patchFilename: string;
|
|
15
29
|
files: string[];
|
|
30
|
+
anchored: string[];
|
|
16
31
|
}[];
|
|
17
32
|
}
|
|
18
33
|
/** Returns the subset of `files` that no longer exist under `engineDir`. */
|
|
19
34
|
export declare function findMissingFiles(engineDir: string, files: readonly string[]): Promise<string[]>;
|
|
35
|
+
/**
|
|
36
|
+
* One unmanaged file and the owned directory that made it adjacent.
|
|
37
|
+
*
|
|
38
|
+
* The anchor is what an unattended run could not previously recover: the
|
|
39
|
+
* notice named the offending file and the patch, but not WHICH of the
|
|
40
|
+
* patch's owned locations the file sits beside, so triaging a refusal on a
|
|
41
|
+
* patch that owns files in several directories meant re-deriving the
|
|
42
|
+
* adjacency by hand from the manifest.
|
|
43
|
+
*/
|
|
44
|
+
export interface AdjacentUnmanagedFile {
|
|
45
|
+
/** Engine-relative path of the unmanaged file. */
|
|
46
|
+
file: string;
|
|
47
|
+
/** Directory holding at least one file this patch owns. */
|
|
48
|
+
anchorDir: string;
|
|
49
|
+
}
|
|
20
50
|
/**
|
|
21
51
|
* Emits the scan-less advisory for one patch: missing manifest files and
|
|
22
52
|
* unmanaged files adjacent to the patch's ownership. When
|
|
@@ -27,7 +57,7 @@ export declare function findMissingFiles(engineDir: string, files: readonly stri
|
|
|
27
57
|
*/
|
|
28
58
|
export declare function reportAdjacentUnmanagedFiles(args: {
|
|
29
59
|
patch: PatchMetadata;
|
|
30
|
-
paths:
|
|
60
|
+
paths: ProjectPaths;
|
|
31
61
|
manifest: PatchesManifest;
|
|
32
62
|
currentFilesAffected: readonly string[];
|
|
33
63
|
ctx: AdjacentUnmanagedContext;
|
|
@@ -47,7 +47,9 @@ async function findAdjacentUnmanagedFiles(args) {
|
|
|
47
47
|
const parentDirs = [...new Set(currentFilesAffected.map((file) => dirname(file)))];
|
|
48
48
|
const currentSet = new Set(currentFilesAffected);
|
|
49
49
|
const claimedByOthers = getClaimedFiles(manifest, patchFilename);
|
|
50
|
-
|
|
50
|
+
// First anchor wins: the dirs are scanned in ownership order and a file
|
|
51
|
+
// lives in exactly one of them, so the map is a dedupe, not a choice.
|
|
52
|
+
const candidates = new Map();
|
|
51
53
|
for (const dir of parentDirs) {
|
|
52
54
|
const [modifiedFiles, untrackedFiles] = await Promise.all([
|
|
53
55
|
getModifiedFilesInDir(engineDir, dir),
|
|
@@ -59,10 +61,37 @@ async function findAdjacentUnmanagedFiles(args) {
|
|
|
59
61
|
continue;
|
|
60
62
|
if (isToolManagedPath(file, untrackedSet.has(file), ctx))
|
|
61
63
|
continue;
|
|
62
|
-
candidates.
|
|
64
|
+
if (!candidates.has(file))
|
|
65
|
+
candidates.set(file, dir);
|
|
63
66
|
}
|
|
64
67
|
}
|
|
65
|
-
return [...candidates
|
|
68
|
+
return [...candidates.entries()]
|
|
69
|
+
.map(([file, anchorDir]) => ({ file, anchorDir }))
|
|
70
|
+
.sort((a, b) => a.file.localeCompare(b.file));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Splits the candidates into the ones that refuse and the ones a
|
|
74
|
+
* `--expect-unmanaged` carve-out admits. Approved paths are recorded as seen
|
|
75
|
+
* so an unused carve-out can be reported, and are RETURNED rather than
|
|
76
|
+
* dropped: an exception nobody can see is how a carve-out quietly widens.
|
|
77
|
+
*/
|
|
78
|
+
function partitionApproved(files, ctx) {
|
|
79
|
+
const refusing = [];
|
|
80
|
+
const approved = [];
|
|
81
|
+
for (const entry of files) {
|
|
82
|
+
if (ctx.approvedUnmanaged.has(entry.file)) {
|
|
83
|
+
ctx.approvedSeen.add(entry.file);
|
|
84
|
+
approved.push(entry);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
refusing.push(entry);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { refusing, approved };
|
|
91
|
+
}
|
|
92
|
+
/** Renders `<file> (beside engine/<dir>)` for a notice line. */
|
|
93
|
+
function describeAnchored(entry) {
|
|
94
|
+
return `${entry.file} (beside engine/${entry.anchorDir})`;
|
|
66
95
|
}
|
|
67
96
|
/**
|
|
68
97
|
* Emits the scan-less advisory for one patch: missing manifest files and
|
|
@@ -83,23 +112,35 @@ export async function reportAdjacentUnmanagedFiles(args) {
|
|
|
83
112
|
` Re-run with --scan to reconcile filesAffected with the current worktree, ` +
|
|
84
113
|
`or pass --files <paths> to set the list explicitly.`);
|
|
85
114
|
}
|
|
86
|
-
const
|
|
115
|
+
const allUnmanaged = await findAdjacentUnmanagedFiles({
|
|
87
116
|
currentFilesAffected,
|
|
88
117
|
engineDir: paths.engine,
|
|
89
118
|
manifest,
|
|
90
119
|
patchFilename: patch.filename,
|
|
91
120
|
ctx,
|
|
92
121
|
});
|
|
122
|
+
const { refusing: unmanagedFiles, approved } = partitionApproved(allUnmanaged, ctx);
|
|
123
|
+
if (approved.length > 0) {
|
|
124
|
+
info(`${patch.filename}: ${approved.length} adjacent unmanaged file(s) admitted by ` +
|
|
125
|
+
`--expect-unmanaged (${approved.map(describeAnchored).join(', ')}) — reported, not refused.`);
|
|
126
|
+
}
|
|
93
127
|
if (unmanagedFiles.length === 0)
|
|
94
128
|
return false;
|
|
95
|
-
const firstFew = unmanagedFiles.slice(0, 3).join(', ');
|
|
96
|
-
const more = unmanagedFiles.length > 3 ? `, +${
|
|
97
|
-
warn(`${patch.filename}: found ${
|
|
98
|
-
|
|
99
|
-
|
|
129
|
+
const firstFew = unmanagedFiles.slice(0, 3).map(describeAnchored).join(', ');
|
|
130
|
+
const more = unmanagedFiles.length > 3 ? `, +${unmanagedFiles.length - 3} more` : '';
|
|
131
|
+
warn(`${patch.filename}: found ${unmanagedFiles.length} unmanaged file(s) adjacent to this patch's ownership (${firstFew}${more}); ` +
|
|
132
|
+
`adjacent means the file sits in a directory this patch already owns a file in. ` +
|
|
133
|
+
`Plain re-export keeps filesAffected unchanged — use --scan-file to include reviewed files.`);
|
|
134
|
+
for (const entry of unmanagedFiles) {
|
|
135
|
+
info(` ${entry.file} — beside engine/${entry.anchorDir} — ` +
|
|
136
|
+
`fireforge re-export ${patch.filename} --scan --scan-file ${entry.file}`);
|
|
100
137
|
}
|
|
101
138
|
if (ctx.refuseAdjacentUnmanaged) {
|
|
102
|
-
ctx.refusals.push({
|
|
139
|
+
ctx.refusals.push({
|
|
140
|
+
patchFilename: patch.filename,
|
|
141
|
+
files: unmanagedFiles.map((entry) => entry.file),
|
|
142
|
+
anchored: unmanagedFiles.map(describeAnchored),
|
|
143
|
+
});
|
|
103
144
|
return true;
|
|
104
145
|
}
|
|
105
146
|
return false;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
2
|
import { readdir, readFile } from 'node:fs/promises';
|
|
4
3
|
import { join } from 'node:path';
|
|
5
4
|
import { isUnavailableGenerationToken, snapshotEngineGeneration, unavailableGenerationReason, } from '../core/engine-session-lock.js';
|
|
@@ -10,12 +9,11 @@ import { GeneralError, InvalidArgumentError } from '../errors/base.js';
|
|
|
10
9
|
import { mapWithConcurrency } from '../utils/concurrency.js';
|
|
11
10
|
import { getNodeErrorCode, toError } from '../utils/errors.js';
|
|
12
11
|
import { readText } from '../utils/fs.js';
|
|
12
|
+
import { sha256Hex } from '../utils/hash.js';
|
|
13
|
+
import { isObject } from '../utils/validation.js';
|
|
13
14
|
import { normalizeEngineRelativeInput } from './re-export-scan.js';
|
|
14
15
|
/** Concurrency bound for patch-body hashing (matches the classify/lint pools). */
|
|
15
16
|
const PATCH_HASH_CONCURRENCY = 8;
|
|
16
|
-
function isRecord(value) {
|
|
17
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
18
|
-
}
|
|
19
17
|
function parseScanFilesManifest(raw, manifestPath) {
|
|
20
18
|
let parsed;
|
|
21
19
|
try {
|
|
@@ -24,12 +22,12 @@ function parseScanFilesManifest(raw, manifestPath) {
|
|
|
24
22
|
catch (error) {
|
|
25
23
|
throw new InvalidArgumentError(`--scan-files manifest is not valid JSON (${manifestPath}): ${toError(error).message}`, '--scan-files');
|
|
26
24
|
}
|
|
27
|
-
if (!
|
|
25
|
+
if (!isObject(parsed) || !Array.isArray(parsed['assignments'])) {
|
|
28
26
|
throw new InvalidArgumentError('--scan-files manifest must contain an assignments array.', '--scan-files');
|
|
29
27
|
}
|
|
30
28
|
const assignments = [];
|
|
31
29
|
for (const [index, assignment] of parsed['assignments'].entries()) {
|
|
32
|
-
if (!
|
|
30
|
+
if (!isObject(assignment)) {
|
|
33
31
|
throw new InvalidArgumentError(`--scan-files assignments[${index}] must be an object.`, '--scan-files');
|
|
34
32
|
}
|
|
35
33
|
const { patch, files } = assignment;
|
|
@@ -99,7 +97,7 @@ async function fingerprintDryRunState(engineDir, patchesDir) {
|
|
|
99
97
|
throw new GeneralError(`[dry-run] cannot fingerprint the engine working tree for the purity guard: ` +
|
|
100
98
|
unavailableGenerationReason(generation));
|
|
101
99
|
}
|
|
102
|
-
fingerprint.set('the engine working tree',
|
|
100
|
+
fingerprint.set('the engine working tree', sha256Hex(generation));
|
|
103
101
|
let names = [];
|
|
104
102
|
try {
|
|
105
103
|
const entries = await readdir(patchesDir, { withFileTypes: true });
|
|
@@ -122,9 +120,7 @@ async function fingerprintDryRunState(engineDir, patchesDir) {
|
|
|
122
120
|
const results = await mapWithConcurrency(names, PATCH_HASH_CONCURRENCY, async (name) => {
|
|
123
121
|
try {
|
|
124
122
|
return {
|
|
125
|
-
hash:
|
|
126
|
-
.update(await readFile(join(patchesDir, name)))
|
|
127
|
-
.digest('hex'),
|
|
123
|
+
hash: sha256Hex(await readFile(join(patchesDir, name))),
|
|
128
124
|
};
|
|
129
125
|
}
|
|
130
126
|
catch (error) {
|
|
@@ -72,3 +72,11 @@ export declare function reportForeignDrift(args: {
|
|
|
72
72
|
newDiffContent: string;
|
|
73
73
|
ctx: ForeignDriftContext;
|
|
74
74
|
}): Promise<boolean>;
|
|
75
|
+
/**
|
|
76
|
+
* Names `--expect` paths that never drifted this run. A typo'd
|
|
77
|
+
* `--expect` path silently degrades the flag back to refusing the slice it
|
|
78
|
+
* was meant to admit, so surface the mismatch — but only as a warning: an
|
|
79
|
+
* expected file legitimately shows no drift in several shapes, all named
|
|
80
|
+
* below.
|
|
81
|
+
*/
|
|
82
|
+
export declare function warnUnseenExpectedDrift(driftCtx: ForeignDriftContext): void;
|