@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
|
@@ -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
|
|
@@ -18,13 +18,35 @@ export interface AdjacentUnmanagedContext {
|
|
|
18
18
|
approvedUnmanaged: ReadonlySet<string>;
|
|
19
19
|
/** Approved paths actually met this run, so unused ones can be surfaced. */
|
|
20
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
|
+
*/
|
|
21
27
|
refusals: {
|
|
22
28
|
patchFilename: string;
|
|
23
29
|
files: string[];
|
|
30
|
+
anchored: string[];
|
|
24
31
|
}[];
|
|
25
32
|
}
|
|
26
33
|
/** Returns the subset of `files` that no longer exist under `engineDir`. */
|
|
27
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
|
+
}
|
|
28
50
|
/**
|
|
29
51
|
* Emits the scan-less advisory for one patch: missing manifest files and
|
|
30
52
|
* unmanaged files adjacent to the patch's ownership. When
|
|
@@ -35,7 +57,7 @@ export declare function findMissingFiles(engineDir: string, files: readonly stri
|
|
|
35
57
|
*/
|
|
36
58
|
export declare function reportAdjacentUnmanagedFiles(args: {
|
|
37
59
|
patch: PatchMetadata;
|
|
38
|
-
paths:
|
|
60
|
+
paths: ProjectPaths;
|
|
39
61
|
manifest: PatchesManifest;
|
|
40
62
|
currentFilesAffected: readonly string[];
|
|
41
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,13 @@ 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));
|
|
66
71
|
}
|
|
67
72
|
/**
|
|
68
73
|
* Splits the candidates into the ones that refuse and the ones a
|
|
@@ -73,17 +78,21 @@ async function findAdjacentUnmanagedFiles(args) {
|
|
|
73
78
|
function partitionApproved(files, ctx) {
|
|
74
79
|
const refusing = [];
|
|
75
80
|
const approved = [];
|
|
76
|
-
for (const
|
|
77
|
-
if (ctx.approvedUnmanaged.has(file)) {
|
|
78
|
-
ctx.approvedSeen.add(file);
|
|
79
|
-
approved.push(
|
|
81
|
+
for (const entry of files) {
|
|
82
|
+
if (ctx.approvedUnmanaged.has(entry.file)) {
|
|
83
|
+
ctx.approvedSeen.add(entry.file);
|
|
84
|
+
approved.push(entry);
|
|
80
85
|
}
|
|
81
86
|
else {
|
|
82
|
-
refusing.push(
|
|
87
|
+
refusing.push(entry);
|
|
83
88
|
}
|
|
84
89
|
}
|
|
85
90
|
return { refusing, approved };
|
|
86
91
|
}
|
|
92
|
+
/** Renders `<file> (beside engine/<dir>)` for a notice line. */
|
|
93
|
+
function describeAnchored(entry) {
|
|
94
|
+
return `${entry.file} (beside engine/${entry.anchorDir})`;
|
|
95
|
+
}
|
|
87
96
|
/**
|
|
88
97
|
* Emits the scan-less advisory for one patch: missing manifest files and
|
|
89
98
|
* unmanaged files adjacent to the patch's ownership. When
|
|
@@ -112,19 +121,26 @@ export async function reportAdjacentUnmanagedFiles(args) {
|
|
|
112
121
|
});
|
|
113
122
|
const { refusing: unmanagedFiles, approved } = partitionApproved(allUnmanaged, ctx);
|
|
114
123
|
if (approved.length > 0) {
|
|
115
|
-
info(`${patch.filename}: ${
|
|
116
|
-
`--expect-unmanaged (${approved.join(', ')}) — reported, not refused.`);
|
|
124
|
+
info(`${patch.filename}: ${approved.length} adjacent unmanaged file(s) admitted by ` +
|
|
125
|
+
`--expect-unmanaged (${approved.map(describeAnchored).join(', ')}) — reported, not refused.`);
|
|
117
126
|
}
|
|
118
127
|
if (unmanagedFiles.length === 0)
|
|
119
128
|
return false;
|
|
120
|
-
const firstFew = unmanagedFiles.slice(0, 3).join(', ');
|
|
121
|
-
const more = unmanagedFiles.length > 3 ? `, +${
|
|
122
|
-
warn(`${patch.filename}: found ${
|
|
123
|
-
|
|
124
|
-
|
|
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}`);
|
|
125
137
|
}
|
|
126
138
|
if (ctx.refuseAdjacentUnmanaged) {
|
|
127
|
-
ctx.refusals.push({
|
|
139
|
+
ctx.refusals.push({
|
|
140
|
+
patchFilename: patch.filename,
|
|
141
|
+
files: unmanagedFiles.map((entry) => entry.file),
|
|
142
|
+
anchored: unmanagedFiles.map(describeAnchored),
|
|
143
|
+
});
|
|
128
144
|
return true;
|
|
129
145
|
}
|
|
130
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) {
|
|
@@ -101,7 +101,7 @@ function summarizeForeignHunks(payload, foreignAdded, foreignRemoved) {
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
if (added > 0 || removed > 0) {
|
|
104
|
-
summaries.push(`@@ -${
|
|
104
|
+
summaries.push(`@@ -${hunk.oldStart},${hunk.oldCount} +${hunk.newStart},${hunk.newCount} @@ (+${added}/-${removed})`);
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
}
|
|
@@ -184,7 +184,7 @@ export async function reportForeignDrift(args) {
|
|
|
184
184
|
const recentlyEdited = await findFilesEditedSinceLastExport(engineDir, oldPath, drift.map((d) => d.file));
|
|
185
185
|
const totalLines = drift.reduce((sum, d) => sum + d.addedLines + d.removedLines, 0);
|
|
186
186
|
const lineNoun = totalLines === 1 ? 'line' : 'lines';
|
|
187
|
-
warn(`${patch.filename}: refreshed body absorbs ${
|
|
187
|
+
warn(`${patch.filename}: refreshed body absorbs ${totalLines} ${lineNoun} not present in the old patch body (${drift.length} file(s)):`);
|
|
188
188
|
for (const entry of drift.slice(0, MAX_FILES_REPORTED)) {
|
|
189
189
|
const expectedTag = ctx.expectedDriftFiles.has(entry.file) ? ' (expected via --expect)' : '';
|
|
190
190
|
const authorshipTag = recentlyEdited.has(entry.file)
|
|
@@ -194,13 +194,13 @@ export async function reportForeignDrift(args) {
|
|
|
194
194
|
info(` ${entry.file}: binary content changed${expectedTag}${authorshipTag}`);
|
|
195
195
|
continue;
|
|
196
196
|
}
|
|
197
|
-
info(` ${entry.file}: +${
|
|
197
|
+
info(` ${entry.file}: +${entry.addedLines}/-${entry.removedLines} newly captured line(s)${expectedTag}${authorshipTag}`);
|
|
198
198
|
for (const summary of entry.hunkSummaries) {
|
|
199
199
|
info(` ${summary}`);
|
|
200
200
|
}
|
|
201
201
|
}
|
|
202
202
|
if (drift.length > MAX_FILES_REPORTED) {
|
|
203
|
-
info(` +${
|
|
203
|
+
info(` +${drift.length - MAX_FILES_REPORTED} more file(s)`);
|
|
204
204
|
}
|
|
205
205
|
if (ctx.refuseForeignDrift) {
|
|
206
206
|
if (unexpected.length === 0) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { getProjectPaths } from '../core/config.js';
|
|
2
1
|
import type { PatchMetadata, ReExportOptions } from '../types/commands/index.js';
|
|
3
|
-
import type { FireForgeConfig } from '../types/config.js';
|
|
2
|
+
import type { FireForgeConfig, ProjectPaths } from '../types/config.js';
|
|
4
3
|
/**
|
|
5
4
|
* Handles `re-export --files` end-to-end: computes the projected diff, runs
|
|
6
5
|
* the per-patch and cross-patch lint against a context in which the target
|
|
@@ -13,4 +12,4 @@ import type { FireForgeConfig } from '../types/config.js';
|
|
|
13
12
|
* generic single-patch helper is what makes the projection lint run against
|
|
14
13
|
* the current (unchanged) queue instead of the projected state.
|
|
15
14
|
*/
|
|
16
|
-
export declare function reExportFilesInPlace(paths:
|
|
15
|
+
export declare function reExportFilesInPlace(paths: ProjectPaths, selectedPatches: PatchMetadata[], options: ReExportOptions, config: FireForgeConfig): Promise<void>;
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { getProjectPaths } from '../core/config.js';
|
|
4
3
|
import { appendHistory, confirmDestructive, } from '../core/destructive.js';
|
|
5
4
|
import { enforceFreshFurnaceSources } from '../core/furnace-stale-export.js';
|
|
6
5
|
import { getDiffForFilesAgainstHead } from '../core/git-diff.js';
|
|
7
6
|
import { listTrackedInHead } from '../core/git-file-ops.js';
|
|
8
7
|
import { extractAffectedFiles } from '../core/patch-apply.js';
|
|
9
8
|
import { updatePatchAndMetadata } from '../core/patch-export.js';
|
|
10
|
-
import {
|
|
9
|
+
import { buildPatchQueueContext, formatPatchLintIssue, lintPatchQueue, } from '../core/patch-lint.js';
|
|
11
10
|
import { computeProjectedLintRegressions } from '../core/patch-lint-projection.js';
|
|
12
11
|
import { loadPatchesManifest } from '../core/patch-manifest.js';
|
|
13
12
|
import { buildProjectedManifest, enforcePatchPolicy } from '../core/patch-policy.js';
|
|
14
|
-
import { buildNewFileTextProjection } from '../core/patch-transform.js';
|
|
15
13
|
import { InvalidArgumentError } from '../errors/base.js';
|
|
16
14
|
import { pathExists } from '../utils/fs.js';
|
|
17
15
|
import { info, outro, success, warn } from '../utils/logger.js';
|
|
18
16
|
import { runPatchLint } from './export-shared.js';
|
|
17
|
+
import { projectEntryBody } from './patch/entry-projection.js';
|
|
19
18
|
/**
|
|
20
19
|
* Computes the effective `tier` and `lintIgnore` carrying both the patch's
|
|
21
20
|
* existing values and the CLI flag overrides.
|
|
@@ -42,17 +41,11 @@ function resolveEffectiveTierAndLintIgnore(target, options) {
|
|
|
42
41
|
*/
|
|
43
42
|
async function runProjectedCrossPatchLint(patchesDir, targetFilename, projectedDiff) {
|
|
44
43
|
const baseCtx = await buildPatchQueueContext(patchesDir);
|
|
45
|
-
const
|
|
46
|
-
const projectedModifiedFileAdditions = buildModifiedFileAdditionsFromDiff(projectedDiff);
|
|
44
|
+
const projection = projectEntryBody(projectedDiff);
|
|
47
45
|
const projectedEntries = baseCtx.entries.map((entry) => {
|
|
48
46
|
if (entry.filename !== targetFilename)
|
|
49
47
|
return entry;
|
|
50
|
-
return {
|
|
51
|
-
...entry,
|
|
52
|
-
diff: projectedDiff,
|
|
53
|
-
newFiles: projectedNewFiles,
|
|
54
|
-
modifiedFileAdditions: projectedModifiedFileAdditions,
|
|
55
|
-
};
|
|
48
|
+
return { ...entry, ...projection };
|
|
56
49
|
});
|
|
57
50
|
const baselineIssues = lintPatchQueue(baseCtx).filter((i) => i.severity === 'error');
|
|
58
51
|
const projectedIssues = lintPatchQueue({ entries: projectedEntries }).filter((i) => i.severity === 'error');
|
|
@@ -235,7 +228,16 @@ export async function reExportFilesInPlace(paths, selectedPatches, options, conf
|
|
|
235
228
|
const patchQueueCtx = (await pathExists(paths.patches))
|
|
236
229
|
? await buildPatchQueueContext(paths.patches)
|
|
237
230
|
: undefined;
|
|
238
|
-
await runPatchLint(
|
|
231
|
+
await runPatchLint({
|
|
232
|
+
engineDir: paths.engine,
|
|
233
|
+
filesAffected: actualProjectedFiles,
|
|
234
|
+
diffContent: projectedDiff,
|
|
235
|
+
config,
|
|
236
|
+
skipLint: options.skipLint,
|
|
237
|
+
patchQueueCtx,
|
|
238
|
+
ignoreChecks,
|
|
239
|
+
patchTier: effectiveTier,
|
|
240
|
+
});
|
|
239
241
|
const conflicts = await runProjectedCrossPatchLint(paths.patches, target.filename, projectedDiff);
|
|
240
242
|
const filesUpdates = buildFilesModeMetadataUpdates(actualProjectedFiles, options, effectiveLintIgnore, flagIgnoreSet);
|
|
241
243
|
const manifest = await loadPatchesManifest(paths.patches);
|
|
@@ -273,23 +275,30 @@ export async function reExportFilesInPlace(paths, selectedPatches, options, conf
|
|
|
273
275
|
// as the mutation (via the onCommitted hook) so two concurrent re-exports
|
|
274
276
|
// cannot interleave records and a crash between mutation and append cannot
|
|
275
277
|
// orphan the audit trail.
|
|
276
|
-
await updatePatchAndMetadata(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
278
|
+
await updatePatchAndMetadata({
|
|
279
|
+
patchesDir: paths.patches,
|
|
280
|
+
filename: target.filename,
|
|
281
|
+
newContent: projectedDiff,
|
|
282
|
+
updates: filesUpdates,
|
|
283
|
+
onCommitted: async () => {
|
|
284
|
+
await appendHistory(paths.patches, {
|
|
285
|
+
operation: 're-export-files',
|
|
286
|
+
args: {
|
|
287
|
+
filename: target.filename,
|
|
288
|
+
files: actualProjectedFiles,
|
|
289
|
+
previousFiles: target.filesAffected,
|
|
290
|
+
deletionsCaptured: deletedFiles,
|
|
291
|
+
},
|
|
292
|
+
...(options.yes === true ? { yes: true } : {}),
|
|
293
|
+
...(options.forceUnsafe === true ? { unsafeOverride: true } : {}),
|
|
294
|
+
result: 'ok',
|
|
295
|
+
});
|
|
296
|
+
},
|
|
297
|
+
policyGate: {
|
|
298
|
+
config,
|
|
299
|
+
command: 're-export --files',
|
|
300
|
+
forceUnsafe: options.forceUnsafe === true,
|
|
301
|
+
},
|
|
293
302
|
});
|
|
294
303
|
success(`Re-exported ${target.filename}`);
|
|
295
304
|
outro('Re-export complete');
|
|
@@ -19,16 +19,15 @@
|
|
|
19
19
|
* (`refreshQueueCtxEntry`), so later iterations of an `--all` run lint
|
|
20
20
|
* against the just-refreshed body instead of the stale one.
|
|
21
21
|
*/
|
|
22
|
-
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
23
22
|
import { type PatchQueueContext } from '../core/patch-lint.js';
|
|
24
23
|
import { type PerPatchLintCacheFile } from '../core/patch-lint-cache.js';
|
|
25
24
|
import type { PatchLintIssue, PatchMetadata } from '../types/commands/index.js';
|
|
26
|
-
import type { FireForgeConfig } from '../types/config.js';
|
|
25
|
+
import type { FireForgeConfig, ProjectPaths } from '../types/config.js';
|
|
27
26
|
import { type PerRunCheckJs } from './lint-per-run-checkjs.js';
|
|
28
27
|
/** Per-invocation lint context, built once and threaded through the loop. */
|
|
29
28
|
export interface ReExportLintContext {
|
|
30
29
|
projectRoot: string;
|
|
31
|
-
paths:
|
|
30
|
+
paths: ProjectPaths;
|
|
32
31
|
config: FireForgeConfig;
|
|
33
32
|
/** Whole-queue context, built WITH config so the forward-import hints
|
|
34
33
|
* match `lint --per-patch`; undefined when no patches dir. */
|
|
@@ -44,7 +43,7 @@ export interface ReExportLintContext {
|
|
|
44
43
|
globalsEmitted: boolean;
|
|
45
44
|
}
|
|
46
45
|
/** Builds the once-per-invocation lint context. */
|
|
47
|
-
export declare function buildReExportLintContext(projectRoot: string, paths:
|
|
46
|
+
export declare function buildReExportLintContext(projectRoot: string, paths: ProjectPaths, config: FireForgeConfig, noCache: boolean): Promise<ReExportLintContext>;
|
|
48
47
|
/** Fresh lint payload carried to the post-write cache store. */
|
|
49
48
|
export interface ReExportLintResult {
|
|
50
49
|
issues: PatchLintIssue[];
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
* (`refreshQueueCtxEntry`), so later iterations of an `--all` run lint
|
|
21
21
|
* against the just-refreshed body instead of the stale one.
|
|
22
22
|
*/
|
|
23
|
-
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
24
23
|
import { buildPatchQueueContext, countNonBinaryDiffLines, detectNewFilesInDiff, lintExportedPatch, resolvePatchSizeTier, } from '../core/patch-lint.js';
|
|
25
24
|
import { buildPerPatchLintCacheKey, getCachedPerPatchLintIssues, getPerPatchLintCacheHeadSha, loadPerPatchLintCache, savePerPatchLintCache, setCachedPerPatchLintIssues, } from '../core/patch-lint-cache.js';
|
|
26
25
|
import { invalidateNewFileCreatorsCache } from '../core/patch-lint-cross.js';
|
|
@@ -169,7 +168,15 @@ export async function storeReExportLintResult(lintCtx, patchFilename, projectedM
|
|
|
169
168
|
queueContext: patchQueueCtx,
|
|
170
169
|
...(lintCtx.engineHeadSha === undefined ? {} : { engineHeadSha: lintCtx.engineHeadSha }),
|
|
171
170
|
});
|
|
172
|
-
setCachedPerPatchLintIssues(
|
|
171
|
+
setCachedPerPatchLintIssues({
|
|
172
|
+
cache,
|
|
173
|
+
patchFilename,
|
|
174
|
+
key: cacheKey,
|
|
175
|
+
issues: result.issues,
|
|
176
|
+
suppressed: result.suppressed,
|
|
177
|
+
lineCount: result.lineCount,
|
|
178
|
+
lintIgnore: result.lintIgnore,
|
|
179
|
+
});
|
|
173
180
|
lintCtx.cacheDirty = true;
|
|
174
181
|
}
|
|
175
182
|
/**
|