@hominis/fireforge 0.49.2 → 0.50.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 +40 -1
- package/README.md +1 -1
- package/dist/bin/fireforge.js +6 -0
- package/dist/build-info.json +4 -4
- package/dist/src/commands/build-prelock.d.ts +20 -0
- package/dist/src/commands/build-prelock.js +50 -0
- package/dist/src/commands/build.d.ts +3 -1
- package/dist/src/commands/build.js +13 -6
- package/dist/src/commands/check-report.d.ts +28 -0
- package/dist/src/commands/check-report.js +78 -0
- package/dist/src/commands/check-run.d.ts +28 -0
- package/dist/src/commands/check-run.js +104 -0
- package/dist/src/commands/check-steps.d.ts +67 -0
- package/dist/src/commands/check-steps.js +175 -0
- package/dist/src/commands/check.d.ts +32 -0
- package/dist/src/commands/check.js +160 -0
- package/dist/src/commands/config.js +5 -6
- package/dist/src/commands/doctor-engine-eslint.js +6 -1
- package/dist/src/commands/doctor-source-pin.js +4 -1
- package/dist/src/commands/export-adopt-check.d.ts +38 -0
- package/dist/src/commands/export-adopt-check.js +98 -0
- package/dist/src/commands/export-flow.js +29 -14
- package/dist/src/commands/export-placement-gate.d.ts +6 -2
- package/dist/src/commands/export-shared.d.ts +2 -1
- package/dist/src/commands/export-staged-registration.d.ts +46 -0
- package/dist/src/commands/export-staged-registration.js +91 -0
- package/dist/src/commands/forward-import-remedy.d.ts +32 -0
- package/dist/src/commands/forward-import-remedy.js +63 -0
- package/dist/src/commands/furnace/apply.js +3 -3
- package/dist/src/commands/furnace/chrome-doc-remove.d.ts +2 -1
- package/dist/src/commands/furnace/chrome-doc-remove.js +2 -2
- package/dist/src/commands/furnace/chrome-doc.d.ts +2 -1
- package/dist/src/commands/furnace/chrome-doc.js +2 -2
- package/dist/src/commands/furnace/create.js +2 -2
- package/dist/src/commands/furnace/deploy-named.d.ts +48 -0
- package/dist/src/commands/furnace/deploy-named.js +107 -0
- package/dist/src/commands/furnace/deploy.d.ts +3 -2
- package/dist/src/commands/furnace/deploy.js +50 -86
- package/dist/src/commands/furnace/diff.js +26 -3
- package/dist/src/commands/furnace/index.js +66 -41
- package/dist/src/commands/furnace/init.js +13 -6
- package/dist/src/commands/furnace/override.js +4 -2
- package/dist/src/commands/furnace/preview.js +4 -3
- package/dist/src/commands/furnace/refresh.js +2 -2
- package/dist/src/commands/furnace/remove.js +4 -3
- package/dist/src/commands/furnace/rename-dry-run.d.ts +29 -0
- package/dist/src/commands/furnace/rename-dry-run.js +127 -0
- package/dist/src/commands/furnace/rename-helpers.d.ts +7 -0
- package/dist/src/commands/furnace/rename-helpers.js +15 -1
- package/dist/src/commands/furnace/rename-registrations.d.ts +41 -0
- package/dist/src/commands/furnace/rename-registrations.js +93 -0
- package/dist/src/commands/furnace/rename.d.ts +2 -1
- package/dist/src/commands/furnace/rename.js +28 -66
- package/dist/src/commands/furnace/scan.d.ts +9 -4
- package/dist/src/commands/furnace/scan.js +11 -9
- package/dist/src/commands/furnace/sync.js +3 -2
- package/dist/src/commands/furnace/validate-report.d.ts +45 -0
- package/dist/src/commands/furnace/validate-report.js +91 -0
- package/dist/src/commands/furnace/validate.d.ts +1 -1
- package/dist/src/commands/furnace/validate.js +127 -114
- package/dist/src/commands/furnace/validation-output.js +14 -3
- package/dist/src/commands/lint-per-patch.js +31 -60
- package/dist/src/commands/lint-report.d.ts +8 -4
- package/dist/src/commands/lint-report.js +15 -5
- package/dist/src/commands/lint.js +15 -34
- package/dist/src/commands/manifest.js +2 -0
- package/dist/src/commands/patch/delete-gap-notice.js +3 -1
- package/dist/src/commands/patch/delete.d.ts +1 -1
- package/dist/src/commands/patch/delete.js +13 -98
- package/dist/src/commands/patch/index.d.ts +1 -1
- package/dist/src/commands/patch/index.js +4 -2
- package/dist/src/commands/patch/lint-ignore.d.ts +5 -1
- package/dist/src/commands/patch/lint-ignore.js +65 -28
- package/dist/src/commands/patch/move-files-create.js +26 -17
- package/dist/src/commands/patch/move-files-into.js +5 -3
- package/dist/src/commands/patch/move-files.js +3 -1
- package/dist/src/commands/patch/removal-dependents.d.ts +63 -0
- package/dist/src/commands/patch/removal-dependents.js +133 -0
- package/dist/src/commands/patch/rename.js +7 -0
- package/dist/src/commands/patch/retire.d.ts +34 -0
- package/dist/src/commands/patch/retire.js +128 -0
- package/dist/src/commands/patch/split-plan.d.ts +7 -7
- package/dist/src/commands/patch/split-plan.js +45 -28
- package/dist/src/commands/patch/split-staged.d.ts +108 -0
- package/dist/src/commands/patch/split-staged.js +202 -0
- package/dist/src/commands/patch/split.d.ts +5 -0
- package/dist/src/commands/patch/split.js +51 -15
- package/dist/src/commands/patch/staged-dependency-batch.d.ts +48 -0
- package/dist/src/commands/patch/staged-dependency-batch.js +176 -0
- package/dist/src/commands/patch/staged-dependency-entries.d.ts +36 -0
- package/dist/src/commands/patch/staged-dependency-entries.js +52 -0
- package/dist/src/commands/patch/staged-dependency-from.d.ts +14 -0
- package/dist/src/commands/patch/staged-dependency-from.js +108 -0
- package/dist/src/commands/patch/staged-dependency.js +14 -41
- package/dist/src/commands/re-export-adjacent-registrations.d.ts +20 -0
- package/dist/src/commands/re-export-adjacent-registrations.js +75 -0
- package/dist/src/commands/re-export-adjacent.d.ts +45 -2
- package/dist/src/commands/re-export-adjacent.js +156 -37
- package/dist/src/commands/re-export-drift.d.ts +36 -8
- package/dist/src/commands/re-export-drift.js +73 -49
- package/dist/src/commands/re-export-files.js +11 -8
- package/dist/src/commands/re-export-lint.d.ts +2 -1
- package/dist/src/commands/re-export-lint.js +23 -10
- package/dist/src/commands/re-export-options.js +45 -4
- package/dist/src/commands/re-export-preflight.d.ts +23 -0
- package/dist/src/commands/re-export-preflight.js +160 -0
- package/dist/src/commands/re-export-register.js +42 -17
- package/dist/src/commands/re-export-removed.d.ts +12 -1
- package/dist/src/commands/re-export-removed.js +41 -11
- package/dist/src/commands/re-export-scan-owner.d.ts +44 -0
- package/dist/src/commands/re-export-scan-owner.js +116 -0
- package/dist/src/commands/re-export-scan.d.ts +17 -9
- package/dist/src/commands/re-export-scan.js +56 -23
- package/dist/src/commands/re-export-single.js +22 -6
- package/dist/src/commands/re-export-summary.d.ts +28 -0
- package/dist/src/commands/re-export-summary.js +36 -0
- package/dist/src/commands/re-export.js +37 -24
- package/dist/src/commands/rebase/index.js +17 -17
- package/dist/src/commands/run.js +16 -2
- package/dist/src/commands/status-check.d.ts +55 -16
- package/dist/src/commands/status-check.js +92 -37
- package/dist/src/commands/status-json.d.ts +22 -9
- package/dist/src/commands/status-json.js +47 -32
- package/dist/src/commands/status-output.js +3 -1
- package/dist/src/commands/status-scope.d.ts +37 -0
- package/dist/src/commands/status-scope.js +54 -0
- package/dist/src/commands/status.js +66 -23
- package/dist/src/commands/test-browser-preflight.js +19 -3
- package/dist/src/commands/test-build-furnace.d.ts +21 -0
- package/dist/src/commands/test-build-furnace.js +48 -0
- package/dist/src/commands/test-build-phase.js +47 -41
- package/dist/src/commands/test-checkjs.js +7 -0
- package/dist/src/commands/test-diagnose.js +7 -0
- package/dist/src/commands/test-modes.d.ts +16 -1
- package/dist/src/commands/test-modes.js +24 -1
- package/dist/src/commands/test-multi-verdict.d.ts +61 -0
- package/dist/src/commands/test-multi-verdict.js +111 -0
- package/dist/src/commands/test-orchestrate.d.ts +43 -0
- package/dist/src/commands/test-orchestrate.js +222 -0
- package/dist/src/commands/test-perf-scope.d.ts +29 -0
- package/dist/src/commands/test-perf-scope.js +83 -0
- package/dist/src/commands/test-prelock.d.ts +51 -0
- package/dist/src/commands/test-prelock.js +177 -0
- package/dist/src/commands/test-register.js +54 -10
- package/dist/src/commands/test-run.js +4 -1
- package/dist/src/commands/test-stale-gate.js +57 -27
- package/dist/src/commands/test-verdict.d.ts +10 -1
- package/dist/src/commands/test-verdict.js +28 -1
- package/dist/src/commands/test.d.ts +6 -1
- package/dist/src/commands/test.js +66 -60
- package/dist/src/commands/token-lint.d.ts +14 -0
- package/dist/src/commands/token-lint.js +111 -0
- package/dist/src/commands/token.js +10 -0
- package/dist/src/commands/verify-scope.d.ts +28 -0
- package/dist/src/commands/verify-scope.js +67 -0
- package/dist/src/commands/verify.d.ts +10 -2
- package/dist/src/commands/verify.js +59 -35
- package/dist/src/core/build-baseline-types.d.ts +46 -0
- package/dist/src/core/build-baseline-validate.js +19 -0
- package/dist/src/core/build-baseline.d.ts +5 -1
- package/dist/src/core/build-baseline.js +3 -1
- package/dist/src/core/build-overwrite-guard.d.ts +50 -0
- package/dist/src/core/build-overwrite-guard.js +84 -4
- package/dist/src/core/build-prepare.d.ts +7 -0
- package/dist/src/core/build-prepare.js +14 -8
- package/dist/src/core/build-run-record.d.ts +74 -0
- package/dist/src/core/build-run-record.js +136 -0
- package/dist/src/core/checkjs-support-heads.d.ts +41 -0
- package/dist/src/core/checkjs-support-heads.js +104 -0
- package/dist/src/core/config-paths.d.ts +2 -2
- package/dist/src/core/config-paths.js +8 -0
- package/dist/src/core/config-validate-check.d.ts +8 -0
- package/dist/src/core/config-validate-check.js +143 -0
- package/dist/src/core/config-validate-file-size.d.ts +9 -6
- package/dist/src/core/config-validate-file-size.js +91 -2
- package/dist/src/core/config-validate-history.d.ts +11 -0
- package/dist/src/core/config-validate-history.js +38 -0
- package/dist/src/core/config-validate-patch-policy.js +100 -0
- package/dist/src/core/config-validate-test-toolchains.js +7 -0
- package/dist/src/core/config-validate-token-scales.d.ts +6 -0
- package/dist/src/core/config-validate-token-scales.js +50 -0
- package/dist/src/core/config-validate.js +14 -5
- package/dist/src/core/config.d.ts +5 -0
- package/dist/src/core/config.js +16 -0
- package/dist/src/core/coverage-dispatch.d.ts +24 -0
- package/dist/src/core/coverage-dispatch.js +44 -0
- package/dist/src/core/coverage-extend.d.ts +70 -45
- package/dist/src/core/coverage-extend.js +199 -144
- package/dist/src/core/coverage-records.d.ts +97 -0
- package/dist/src/core/coverage-records.js +350 -0
- package/dist/src/core/destructive.d.ts +7 -4
- package/dist/src/core/destructive.js +10 -10
- package/dist/src/core/engine-session-lock.d.ts +10 -1
- package/dist/src/core/engine-session-lock.js +28 -8
- package/dist/src/core/export-moved-code-shape.d.ts +49 -11
- package/dist/src/core/export-moved-code-shape.js +135 -44
- package/dist/src/core/file-lock-queue.d.ts +67 -0
- package/dist/src/core/file-lock-queue.js +221 -0
- package/dist/src/core/file-lock.d.ts +4 -25
- package/dist/src/core/file-lock.js +105 -138
- package/dist/src/core/furnace-apply-output.d.ts +10 -0
- package/dist/src/core/furnace-apply-output.js +31 -1
- package/dist/src/core/furnace-apply.js +12 -2
- package/dist/src/core/furnace-composed-source.d.ts +76 -0
- package/dist/src/core/furnace-composed-source.js +167 -0
- package/dist/src/core/furnace-config-validate-exceptions.d.ts +9 -0
- package/dist/src/core/furnace-config-validate-exceptions.js +71 -0
- package/dist/src/core/furnace-config-validate.js +8 -0
- package/dist/src/core/furnace-css-fragments.d.ts +10 -0
- package/dist/src/core/furnace-css-fragments.js +26 -0
- package/dist/src/core/furnace-operation.d.ts +3 -3
- package/dist/src/core/furnace-registration-remove.d.ts +22 -1
- package/dist/src/core/furnace-registration-remove.js +53 -17
- package/dist/src/core/furnace-registration-rename.d.ts +26 -0
- package/dist/src/core/furnace-registration-rename.js +77 -0
- package/dist/src/core/furnace-registration.d.ts +14 -4
- package/dist/src/core/furnace-registration.js +44 -10
- package/dist/src/core/furnace-staleness.d.ts +7 -0
- package/dist/src/core/furnace-staleness.js +34 -24
- package/dist/src/core/furnace-validate-accessibility.d.ts +4 -2
- package/dist/src/core/furnace-validate-accessibility.js +10 -1
- package/dist/src/core/furnace-validate-exceptions.d.ts +40 -0
- package/dist/src/core/furnace-validate-exceptions.js +43 -0
- package/dist/src/core/furnace-validate-icon-button.d.ts +7 -0
- package/dist/src/core/furnace-validate-icon-button.js +49 -0
- package/dist/src/core/furnace-validate.js +1 -1
- package/dist/src/core/harness-port-fallback.d.ts +60 -0
- package/dist/src/core/harness-port-fallback.js +96 -0
- package/dist/src/core/history-log-file.d.ts +61 -0
- package/dist/src/core/history-log-file.js +157 -0
- package/dist/src/core/history-log.d.ts +2 -1
- package/dist/src/core/history-log.js +2 -1
- package/dist/src/core/json-preserving-edit.d.ts +22 -0
- package/dist/src/core/json-preserving-edit.js +199 -0
- package/dist/src/core/mach.js +8 -1
- package/dist/src/core/negative-control-diff.d.ts +39 -0
- package/dist/src/core/negative-control-diff.js +136 -0
- package/dist/src/core/negative-control.d.ts +138 -0
- package/dist/src/core/negative-control.js +356 -0
- package/dist/src/core/objdir-install-manifests.d.ts +73 -0
- package/dist/src/core/objdir-install-manifests.js +226 -0
- package/dist/src/core/patch-export-update.d.ts +2 -0
- package/dist/src/core/patch-export-update.js +1 -0
- package/dist/src/core/patch-export.js +3 -4
- package/dist/src/core/patch-lint-checkjs-any-import.d.ts +37 -0
- package/dist/src/core/patch-lint-checkjs-any-import.js +61 -0
- package/dist/src/core/patch-lint-checkjs.js +8 -11
- package/dist/src/core/patch-lint-code-lines.d.ts +59 -0
- package/dist/src/core/patch-lint-code-lines.js +105 -0
- package/dist/src/core/patch-lint-cross.js +12 -4
- package/dist/src/core/patch-lint-css.d.ts +8 -2
- package/dist/src/core/patch-lint-css.js +10 -4
- package/dist/src/core/patch-lint-file-size.d.ts +12 -5
- package/dist/src/core/patch-lint-file-size.js +22 -11
- package/dist/src/core/patch-lint-forward-registration-extended.d.ts +18 -0
- package/dist/src/core/patch-lint-forward-registration-extended.js +35 -10
- package/dist/src/core/patch-lint-forward-registration.js +10 -0
- package/dist/src/core/patch-lint-ignore.d.ts +152 -0
- package/dist/src/core/patch-lint-ignore.js +299 -0
- package/dist/src/core/patch-lint-size-thresholds.d.ts +52 -0
- package/dist/src/core/patch-lint-size-thresholds.js +110 -0
- package/dist/src/core/patch-lint-size.d.ts +117 -0
- package/dist/src/core/patch-lint-size.js +323 -0
- package/dist/src/core/patch-lint-staged-registration.d.ts +18 -1
- package/dist/src/core/patch-lint-staged-registration.js +20 -0
- package/dist/src/core/patch-lint-token-scale.d.ts +39 -0
- package/dist/src/core/patch-lint-token-scale.js +109 -0
- package/dist/src/core/patch-lint.d.ts +11 -83
- package/dist/src/core/patch-lint.js +64 -286
- package/dist/src/core/patch-lock.js +7 -6
- package/dist/src/core/patch-manifest-consistency.js +7 -6
- package/dist/src/core/patch-manifest-io.d.ts +6 -3
- package/dist/src/core/patch-manifest-io.js +19 -8
- package/dist/src/core/patch-manifest-validate.js +34 -2
- package/dist/src/core/patch-order-legal.d.ts +25 -0
- package/dist/src/core/patch-order-legal.js +83 -0
- package/dist/src/core/patch-policy-content.d.ts +40 -0
- package/dist/src/core/patch-policy-content.js +216 -0
- package/dist/src/core/patch-policy.d.ts +16 -3
- package/dist/src/core/patch-policy.js +16 -9
- package/dist/src/core/patch-retired.d.ts +35 -0
- package/dist/src/core/patch-retired.js +54 -0
- package/dist/src/core/patch-subset.d.ts +16 -0
- package/dist/src/core/patch-subset.js +66 -0
- package/dist/src/core/preflight-wait.d.ts +20 -0
- package/dist/src/core/preflight-wait.js +38 -0
- package/dist/src/core/rebase-from-version.d.ts +46 -0
- package/dist/src/core/rebase-from-version.js +71 -0
- package/dist/src/core/run-log.d.ts +20 -0
- package/dist/src/core/run-log.js +89 -3
- package/dist/src/core/run-profile.d.ts +7 -0
- package/dist/src/core/run-profile.js +7 -1
- package/dist/src/core/smoke-patterns.d.ts +5 -0
- package/dist/src/core/smoke-patterns.js +13 -0
- package/dist/src/core/test-harness-crash.d.ts +18 -1
- package/dist/src/core/test-harness-crash.js +6 -4
- package/dist/src/core/test-harness-signature.d.ts +17 -0
- package/dist/src/core/test-harness-signature.js +4 -0
- package/dist/src/core/test-run-completeness.d.ts +57 -0
- package/dist/src/core/test-run-completeness.js +144 -0
- package/dist/src/core/test-stale-check.d.ts +27 -1
- package/dist/src/core/test-stale-check.js +39 -49
- package/dist/src/core/test-stale-exempt.d.ts +40 -0
- package/dist/src/core/test-stale-exempt.js +70 -0
- package/dist/src/core/token-lint.d.ts +50 -0
- package/dist/src/core/token-lint.js +126 -0
- package/dist/src/core/tree-guard.js +3 -0
- package/dist/src/core/typecheck-shim.d.ts +1 -1
- package/dist/src/core/typecheck-shim.js +90 -2
- package/dist/src/index.d.ts +8 -3
- package/dist/src/index.js +3 -1
- package/dist/src/types/commands/index.d.ts +2 -2
- package/dist/src/types/commands/options.d.ts +148 -21
- package/dist/src/types/commands/patches.d.ts +75 -1
- package/dist/src/types/config.d.ts +192 -0
- package/dist/src/types/config.js +9 -0
- package/dist/src/types/furnace.d.ts +43 -1
- package/dist/src/types/furnace.js +7 -0
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/utils/options.d.ts +34 -6
- package/dist/src/utils/options.js +86 -5
- package/dist/src/utils/path-glob.d.ts +15 -0
- package/dist/src/utils/path-glob.js +47 -0
- package/dist/src/utils/regex.d.ts +15 -3
- package/dist/src/utils/regex.js +37 -16
- package/docs/configuration.md +282 -9
- package/docs/exit-codes.md +18 -1
- package/docs/furnace.md +97 -3
- package/docs/lifecycle-invariants.md +52 -19
- package/docs/machine-output.md +74 -12
- package/docs/patch-workflow.md +297 -22
- package/docs/run-logs.md +20 -1
- package/docs/testing.md +276 -20
- package/package.json +1 -1
|
@@ -10,6 +10,9 @@ import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
|
|
|
10
10
|
import { info, intro, outro } from '../../utils/logger.js';
|
|
11
11
|
import { addWaitLockOption, resolveWaitLockSeconds } from '../../utils/options.js';
|
|
12
12
|
import { requirePatchQueue, requirePatchTarget } from './patch-context.js';
|
|
13
|
+
import { stagedDependencyDelta } from './staged-dependency-batch.js';
|
|
14
|
+
import { applyMode, countStagedEntries, dependencyLabel, importView, registrationView, } from './staged-dependency-entries.js';
|
|
15
|
+
import { runBatchAdd } from './staged-dependency-from.js';
|
|
13
16
|
import { validateStagedDependencyAdd } from './staged-dependency-validate.js';
|
|
14
17
|
/** Joins the names of the missing flags for error attribution. */
|
|
15
18
|
function missingFlagList(pairs) {
|
|
@@ -146,41 +149,6 @@ function resolveRegistrationRemovalTarget(options, staged) {
|
|
|
146
149
|
}
|
|
147
150
|
return { file, line, creates: '(no match)' };
|
|
148
151
|
}
|
|
149
|
-
function importView(dependency) {
|
|
150
|
-
return { ...dependency, matcher: dependency.specifier };
|
|
151
|
-
}
|
|
152
|
-
function registrationView(dependency) {
|
|
153
|
-
return { ...dependency, matcher: dependency.line };
|
|
154
|
-
}
|
|
155
|
-
function dependencyKey(view) {
|
|
156
|
-
return [view.file, view.matcher, view.creates, view.owner ?? ''].join('\0');
|
|
157
|
-
}
|
|
158
|
-
function dependencyLabel(kind, view) {
|
|
159
|
-
const owner = view.owner ? ` owner=${view.owner}` : '';
|
|
160
|
-
const reason = view.reason ? ` reason="${view.reason}"` : '';
|
|
161
|
-
if (kind === 'import') {
|
|
162
|
-
return `${view.file} imports "${view.matcher}" from ${view.creates}${owner}${reason}`;
|
|
163
|
-
}
|
|
164
|
-
return `${view.file} registers "${view.matcher}" for ${view.creates}${owner}${reason}`;
|
|
165
|
-
}
|
|
166
|
-
function matchesTarget(view, target) {
|
|
167
|
-
return (view.file === target.file &&
|
|
168
|
-
view.matcher === target.matcher &&
|
|
169
|
-
view.creates === target.creates &&
|
|
170
|
-
(target.owner === undefined || view.owner === target.owner));
|
|
171
|
-
}
|
|
172
|
-
function applyMode(existing, mode, target, toView, addition) {
|
|
173
|
-
if (mode === 'clear')
|
|
174
|
-
return [];
|
|
175
|
-
if (!target)
|
|
176
|
-
return [...existing];
|
|
177
|
-
if (mode === 'remove')
|
|
178
|
-
return existing.filter((entry) => !matchesTarget(toView(entry), target));
|
|
179
|
-
const seen = new Set(existing.map((entry) => dependencyKey(toView(entry))));
|
|
180
|
-
if (seen.has(dependencyKey(target)) || addition === undefined)
|
|
181
|
-
return [...existing];
|
|
182
|
-
return [...existing, addition];
|
|
183
|
-
}
|
|
184
152
|
/**
|
|
185
153
|
* Renders a one-line summary of a staged-dependency metadata change.
|
|
186
154
|
*/
|
|
@@ -202,9 +170,6 @@ function describeStagedDependencyChange(beforeCount, afterCount, mode, kind, tar
|
|
|
202
170
|
? `no ${noun} matched: ${dependencyLabel(kind, target)}`
|
|
203
171
|
: `removed ${beforeCount - afterCount} ${noun} declaration(s): ${dependencyLabel(kind, target)}`;
|
|
204
172
|
}
|
|
205
|
-
function countStagedEntries(staged) {
|
|
206
|
-
return (staged?.forwardImports?.length ?? 0) + (staged?.registrations?.length ?? 0);
|
|
207
|
-
}
|
|
208
173
|
/**
|
|
209
174
|
* Runs the metadata-only staged-dependency mutation command.
|
|
210
175
|
*
|
|
@@ -216,6 +181,10 @@ export async function patchStagedDependencyCommand(projectRoot, identifier, opti
|
|
|
216
181
|
const isDryRun = options.dryRun === true;
|
|
217
182
|
intro(isDryRun ? 'FireForge patch staged-dependency (dry run)' : 'FireForge patch staged-dependency');
|
|
218
183
|
const mode = modeFromOptions(options);
|
|
184
|
+
if (options.from !== undefined) {
|
|
185
|
+
await runBatchAdd(projectRoot, identifier, mode, options);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
219
188
|
const kind = kindFromOptions(options);
|
|
220
189
|
// The patch queue loads before the declaration fields resolve so
|
|
221
190
|
// `--remove` can infer `--creates` from the target patch's staged
|
|
@@ -281,12 +250,13 @@ export async function patchStagedDependencyCommand(projectRoot, identifier, opti
|
|
|
281
250
|
info(`${targetPatch.filename}: ${describeStagedDependencyChange(countStagedEntries(before), countStagedEntries(after), mode, kind, target)}.`);
|
|
282
251
|
await appendHistoryBestEffort(paths.patches, {
|
|
283
252
|
operation: 'patch-staged-dependency',
|
|
253
|
+
// Only the delta: the whole list on both sides grew the log
|
|
254
|
+
// quadratically with the list (see staged-dependency-batch.ts).
|
|
284
255
|
args: {
|
|
285
256
|
filename: targetPatch.filename,
|
|
286
257
|
mode,
|
|
287
258
|
kind,
|
|
288
|
-
before
|
|
289
|
-
after: after ?? {},
|
|
259
|
+
...stagedDependencyDelta(before, after),
|
|
290
260
|
},
|
|
291
261
|
...(options.yes === true ? { yes: true } : {}),
|
|
292
262
|
result: 'ok',
|
|
@@ -313,7 +283,10 @@ export function registerPatchStagedDependency(parent, context) {
|
|
|
313
283
|
.option('--line <text>', 'Registration/packaging line as the patch adds it, compared whitespace-trimmed (--kind registration)')
|
|
314
284
|
.option('--creates <path>', 'Later-created file path relative to engine/')
|
|
315
285
|
.option('--owner <patch>', 'Exact later patch filename expected to create --creates')
|
|
316
|
-
.option('--reason <text>', 'Human-readable rationale stored with the declaration')
|
|
286
|
+
.option('--reason <text>', 'Human-readable rationale stored with the declaration (with --from: the default for entries without one)')
|
|
287
|
+
.option('--from <json-file>', 'Add every declaration in a JSON file under one lock hold (--add only): a "lint --per-patch --report" ' +
|
|
288
|
+
"document (its findings' stagedDeclarations for this patch) or an array of " +
|
|
289
|
+
'{ file, specifier | line, creates, owner?, reason?, patch? } entries')
|
|
317
290
|
.option('--dry-run', 'Show what would change without writing')
|
|
318
291
|
.option('-y, --yes', 'Record scripted consent in the destructive-operation history log. This command never prompts; the flag exists for workflow uniformity with commands that do.');
|
|
319
292
|
addWaitLockOption(command).action(withErrorHandling(async (name, options) => {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** One changed engine file a registration line the patch adds names. */
|
|
2
|
+
export interface RegisteredChangedFile {
|
|
3
|
+
/** Engine-relative path of the named file. */
|
|
4
|
+
file: string;
|
|
5
|
+
/** Engine-relative path of the carrier whose added line names it. */
|
|
6
|
+
carrier: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Lists the changed (modified, deleted or untracked) engine files named by
|
|
10
|
+
* the registration lines the patch's owned carriers add relative to HEAD.
|
|
11
|
+
* Only added lines count: an upstream line naming an upstream file is not
|
|
12
|
+
* the patch's registration. A wildcard token is matched against the changed
|
|
13
|
+
* files under its carrier's directory, the only place such a token reaches
|
|
14
|
+
* without the build system.
|
|
15
|
+
*
|
|
16
|
+
* @param engineDir - Engine checkout
|
|
17
|
+
* @param ownedFiles - The patch's current filesAffected
|
|
18
|
+
* @returns Named changed files, first carrier wins, sorted by path
|
|
19
|
+
*/
|
|
20
|
+
export declare function findRegisteredChangedFiles(engineDir: string, ownedFiles: readonly string[]): Promise<RegisteredChangedFile[]>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* The registration arm of the scan-less re-export adjacency belt.
|
|
4
|
+
*
|
|
5
|
+
* A file the patch registers (a jar.mn, moz.build or test-manifest line the
|
|
6
|
+
* patch adds names it) belongs with the patch even when it lives in another
|
|
7
|
+
* directory, so an unmanaged one is adjacent in the sense that matters: the
|
|
8
|
+
* refreshed body would ship a registration for a file it leaves out. This
|
|
9
|
+
* module answers which changed engine files those lines name. The caller
|
|
10
|
+
* filters out owned, claimed and tool-managed paths.
|
|
11
|
+
*
|
|
12
|
+
* Split out of re-export-adjacent.ts to keep that module about the
|
|
13
|
+
* classification, not the carrier parsing.
|
|
14
|
+
*/
|
|
15
|
+
import { dirname } from 'node:path';
|
|
16
|
+
import { getDiffForFilesAgainstHead } from '../core/git-diff.js';
|
|
17
|
+
import { getDirtyFiles, getModifiedFilesInDir, getUntrackedFilesInDir, } from '../core/git-status.js';
|
|
18
|
+
import { extractAddedLinesPerFile } from '../core/patch-lint-diff.js';
|
|
19
|
+
import { isRegistrationCarrier, resolveCarrierReferences, } from '../core/patch-lint-forward-registration-extended.js';
|
|
20
|
+
import { normalizePathSlashes } from '../utils/paths.js';
|
|
21
|
+
/**
|
|
22
|
+
* Lists the changed (modified, deleted or untracked) engine files named by
|
|
23
|
+
* the registration lines the patch's owned carriers add relative to HEAD.
|
|
24
|
+
* Only added lines count: an upstream line naming an upstream file is not
|
|
25
|
+
* the patch's registration. A wildcard token is matched against the changed
|
|
26
|
+
* files under its carrier's directory, the only place such a token reaches
|
|
27
|
+
* without the build system.
|
|
28
|
+
*
|
|
29
|
+
* @param engineDir - Engine checkout
|
|
30
|
+
* @param ownedFiles - The patch's current filesAffected
|
|
31
|
+
* @returns Named changed files, first carrier wins, sorted by path
|
|
32
|
+
*/
|
|
33
|
+
export async function findRegisteredChangedFiles(engineDir, ownedFiles) {
|
|
34
|
+
const carriers = ownedFiles.filter(isRegistrationCarrier);
|
|
35
|
+
if (carriers.length === 0)
|
|
36
|
+
return [];
|
|
37
|
+
const diff = await getDiffForFilesAgainstHead(engineDir, carriers);
|
|
38
|
+
const exactToCarrier = new Map();
|
|
39
|
+
const wildcardsByCarrier = new Map();
|
|
40
|
+
for (const [carrier, lines] of extractAddedLinesPerFile(diff)) {
|
|
41
|
+
for (const ref of resolveCarrierReferences(carrier, lines.join('\n'))) {
|
|
42
|
+
if (ref.exact !== undefined) {
|
|
43
|
+
if (!exactToCarrier.has(ref.exact))
|
|
44
|
+
exactToCarrier.set(ref.exact, carrier);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const list = wildcardsByCarrier.get(carrier) ?? [];
|
|
48
|
+
list.push(ref.matches);
|
|
49
|
+
wildcardsByCarrier.set(carrier, list);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const found = new Map();
|
|
54
|
+
if (exactToCarrier.size > 0) {
|
|
55
|
+
for (const file of await getDirtyFiles(engineDir, [...exactToCarrier.keys()])) {
|
|
56
|
+
const carrier = exactToCarrier.get(file);
|
|
57
|
+
if (carrier !== undefined)
|
|
58
|
+
found.set(file, carrier);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (const [carrier, matchers] of wildcardsByCarrier) {
|
|
62
|
+
const scope = normalizePathSlashes(dirname(carrier));
|
|
63
|
+
const [modified, untracked] = await Promise.all([
|
|
64
|
+
getModifiedFilesInDir(engineDir, scope),
|
|
65
|
+
getUntrackedFilesInDir(engineDir, scope),
|
|
66
|
+
]);
|
|
67
|
+
for (const file of [...modified, ...untracked]) {
|
|
68
|
+
if (!found.has(file) && matchers.some((matches) => matches(file)))
|
|
69
|
+
found.set(file, carrier);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return [...found.entries()]
|
|
73
|
+
.map(([file, carrier]) => ({ file, carrier }))
|
|
74
|
+
.sort((a, b) => a.file.localeCompare(b.file));
|
|
75
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PatchesManifest, PatchMetadata } from '../types/commands/index.js';
|
|
1
|
+
import type { PatchesManifest, PatchMetadata, ReExportOptions } from '../types/commands/index.js';
|
|
2
2
|
import type { ProjectPaths } from '../types/config.js';
|
|
3
3
|
/**
|
|
4
4
|
* Shared per-run context for the adjacency advisory: the classification
|
|
@@ -18,6 +18,23 @@ 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
|
+
* Engine-relative paths named by `--expect-new`: files this session
|
|
23
|
+
* created and will adopt with its next export (or `--scan-file`). They
|
|
24
|
+
* pass the belt and are reported, while every other adjacent file still
|
|
25
|
+
* refuses, so a peer's neighbour is not admitted with them.
|
|
26
|
+
*/
|
|
27
|
+
expectedNew: ReadonlySet<string>;
|
|
28
|
+
/** `--expect-new` paths actually met this run. */
|
|
29
|
+
expectedNewSeen: Set<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Reviewed `--adjacent-ignore` entries: an engine-relative path, or a
|
|
32
|
+
* glob whose `*` stays within one path segment. Matches are reported in
|
|
33
|
+
* one line and never refuse.
|
|
34
|
+
*/
|
|
35
|
+
adjacentIgnore: readonly string[];
|
|
36
|
+
/** `--adjacent-ignore` entries that matched a file this run. */
|
|
37
|
+
adjacentIgnoreSeen: Set<string>;
|
|
21
38
|
/**
|
|
22
39
|
* Offenders collected across the loop. `files` stays the bare paths that
|
|
23
40
|
* downstream consumers already read. `anchored` carries the same entries
|
|
@@ -30,6 +47,16 @@ export interface AdjacentUnmanagedContext {
|
|
|
30
47
|
anchored: string[];
|
|
31
48
|
}[];
|
|
32
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Builds the run's adjacency context. The path flags are normalized once
|
|
52
|
+
* here, so the per-file comparison matches git's engine-relative output.
|
|
53
|
+
*/
|
|
54
|
+
export declare function buildAdjacentUnmanagedContext(options: ReExportOptions, binaryName: string, furnacePrefixes: ReadonlySet<string>): AdjacentUnmanagedContext;
|
|
55
|
+
/**
|
|
56
|
+
* The one definition of "adjacent", shared by the per-patch notice and the
|
|
57
|
+
* run-level refusal so the two cannot drift apart.
|
|
58
|
+
*/
|
|
59
|
+
export declare const ADJACENT_MEANS: string;
|
|
33
60
|
/** Returns the subset of `files` that no longer exist under `engineDir`. */
|
|
34
61
|
export declare function findMissingFiles(engineDir: string, files: readonly string[]): Promise<string[]>;
|
|
35
62
|
/**
|
|
@@ -44,8 +71,13 @@ export declare function findMissingFiles(engineDir: string, files: readonly stri
|
|
|
44
71
|
export interface AdjacentUnmanagedFile {
|
|
45
72
|
/** Engine-relative path of the unmanaged file. */
|
|
46
73
|
file: string;
|
|
47
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Directory holding at least one file this patch owns, or for a
|
|
76
|
+
* registration-named file, the directory of the carrier naming it.
|
|
77
|
+
*/
|
|
48
78
|
anchorDir: string;
|
|
79
|
+
/** The owned carrier whose added registration line names the file. */
|
|
80
|
+
registeredBy?: string;
|
|
49
81
|
}
|
|
50
82
|
/**
|
|
51
83
|
* Emits the scan-less advisory for one patch: missing manifest files and
|
|
@@ -62,3 +94,14 @@ export declare function reportAdjacentUnmanagedFiles(args: {
|
|
|
62
94
|
currentFilesAffected: readonly string[];
|
|
63
95
|
ctx: AdjacentUnmanagedContext;
|
|
64
96
|
}): Promise<boolean>;
|
|
97
|
+
/**
|
|
98
|
+
* End-of-run accounting for `--expect-new` and `--adjacent-ignore`.
|
|
99
|
+
*
|
|
100
|
+
* An `--expect-new` path is a promise that the next export adopts it, so a
|
|
101
|
+
* met one is repeated as the export still owed, and an unmet one is a
|
|
102
|
+
* warning: a typo there leaves the refusal armed for the file it meant to
|
|
103
|
+
* admit. An unmatched `--adjacent-ignore` entry is only noted, because a
|
|
104
|
+
* reviewed ignore list is reused across runs and patches, and "not met"
|
|
105
|
+
* is its normal case.
|
|
106
|
+
*/
|
|
107
|
+
export declare function reportAdjacentCarveOutUsage(ctx: AdjacentUnmanagedContext): void;
|
|
@@ -21,6 +21,34 @@ import { isGeneratedBrandingPath } from '../core/status-classify.js';
|
|
|
21
21
|
import { mapWithConcurrency } from '../utils/concurrency.js';
|
|
22
22
|
import { pathExists } from '../utils/fs.js';
|
|
23
23
|
import { info, warn } from '../utils/logger.js';
|
|
24
|
+
import { escapeRegex } from '../utils/regex.js';
|
|
25
|
+
import { findRegisteredChangedFiles } from './re-export-adjacent-registrations.js';
|
|
26
|
+
import { normalizeEngineRelativeInput } from './re-export-scan.js';
|
|
27
|
+
/**
|
|
28
|
+
* Builds the run's adjacency context. The path flags are normalized once
|
|
29
|
+
* here, so the per-file comparison matches git's engine-relative output.
|
|
30
|
+
*/
|
|
31
|
+
export function buildAdjacentUnmanagedContext(options, binaryName, furnacePrefixes) {
|
|
32
|
+
const normalized = (flag, values) => (values ?? []).map((value) => normalizeEngineRelativeInput(value, flag));
|
|
33
|
+
return {
|
|
34
|
+
binaryName,
|
|
35
|
+
furnacePrefixes,
|
|
36
|
+
refuseAdjacentUnmanaged: options.refuseAdjacentUnmanaged === true,
|
|
37
|
+
approvedUnmanaged: new Set(normalized('--expect-unmanaged', options.expectUnmanaged)),
|
|
38
|
+
approvedSeen: new Set(),
|
|
39
|
+
expectedNew: new Set(normalized('--expect-new', options.expectNew)),
|
|
40
|
+
expectedNewSeen: new Set(),
|
|
41
|
+
adjacentIgnore: normalized('--adjacent-ignore', options.adjacentIgnore),
|
|
42
|
+
adjacentIgnoreSeen: new Set(),
|
|
43
|
+
refusals: [],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The one definition of "adjacent", shared by the per-patch notice and the
|
|
48
|
+
* run-level refusal so the two cannot drift apart.
|
|
49
|
+
*/
|
|
50
|
+
export const ADJACENT_MEANS = 'Adjacent means the file sits in the same directory as a file this patch owns, ' +
|
|
51
|
+
'or a registration line this patch adds (jar.mn, moz.build, test manifest) names it.';
|
|
24
52
|
/** Concurrency bound for existence probes (matches the classify/lint pools). */
|
|
25
53
|
const PATH_PROBE_CONCURRENCY = 8;
|
|
26
54
|
/** Returns the subset of `files` that no longer exist under `engineDir`. */
|
|
@@ -42,14 +70,41 @@ function isToolManagedPath(file, isNewFile, ctx) {
|
|
|
42
70
|
}
|
|
43
71
|
return false;
|
|
44
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Matches an `--adjacent-ignore` entry. A `*` stays inside one path
|
|
75
|
+
* segment, the same narrowness as `buildAudit.unpackaged`: a carve-out that
|
|
76
|
+
* silently spans subtrees is how a reviewed exception becomes a blanket one.
|
|
77
|
+
*/
|
|
78
|
+
function matchesIgnoreEntry(entry, file) {
|
|
79
|
+
if (!entry.includes('*'))
|
|
80
|
+
return entry === file;
|
|
81
|
+
const pattern = entry
|
|
82
|
+
.split('*')
|
|
83
|
+
.map((part) => escapeRegex(part))
|
|
84
|
+
.join('[^/]*');
|
|
85
|
+
return new RegExp(`^${pattern}$`).test(file);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Lists the unmanaged files a re-export of this patch would plausibly have
|
|
89
|
+
* to capture: changed files directly in a directory the patch owns a file
|
|
90
|
+
* in, and changed files a registration line the patch adds names. A file in
|
|
91
|
+
* a sibling or nested directory is not adjacent. The directory listing
|
|
92
|
+
* recurses (pathspecs do), so it is filtered back to the exact directories,
|
|
93
|
+
* the same footprint the broad `--scan` adopts from.
|
|
94
|
+
*/
|
|
45
95
|
async function findAdjacentUnmanagedFiles(args) {
|
|
46
96
|
const { currentFilesAffected, engineDir, manifest, patchFilename, ctx } = args;
|
|
47
97
|
const parentDirs = [...new Set(currentFilesAffected.map((file) => dirname(file)))];
|
|
98
|
+
const parentDirSet = new Set(parentDirs);
|
|
48
99
|
const currentSet = new Set(currentFilesAffected);
|
|
49
100
|
const claimedByOthers = getClaimedFiles(manifest, patchFilename);
|
|
50
|
-
// First anchor wins:
|
|
51
|
-
//
|
|
101
|
+
// First anchor wins: a file lives in exactly one directory, and a
|
|
102
|
+
// directory anchor is the stronger statement than a registration one.
|
|
52
103
|
const candidates = new Map();
|
|
104
|
+
const isCandidate = (file, isNewFile) => !currentSet.has(file) &&
|
|
105
|
+
!claimedByOthers.has(file) &&
|
|
106
|
+
!candidates.has(file) &&
|
|
107
|
+
!isToolManagedPath(file, isNewFile, ctx);
|
|
53
108
|
for (const dir of parentDirs) {
|
|
54
109
|
const [modifiedFiles, untrackedFiles] = await Promise.all([
|
|
55
110
|
getModifiedFilesInDir(engineDir, dir),
|
|
@@ -57,41 +112,80 @@ async function findAdjacentUnmanagedFiles(args) {
|
|
|
57
112
|
]);
|
|
58
113
|
const untrackedSet = new Set(untrackedFiles);
|
|
59
114
|
for (const file of [...modifiedFiles, ...untrackedFiles]) {
|
|
60
|
-
if (
|
|
115
|
+
if (!parentDirSet.has(dirname(file)))
|
|
61
116
|
continue;
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
117
|
+
if (isCandidate(file, untrackedSet.has(file))) {
|
|
118
|
+
candidates.set(file, { file, anchorDir: dirname(file) });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const registered = await findRegisteredChangedFiles(engineDir, currentFilesAffected);
|
|
123
|
+
for (const { file, carrier } of registered) {
|
|
124
|
+
// A registered path is a patch candidate whatever its tracking state,
|
|
125
|
+
// so it is classified as new: only a generated path is tool-managed.
|
|
126
|
+
if (isCandidate(file, true)) {
|
|
127
|
+
candidates.set(file, { file, anchorDir: dirname(carrier), registeredBy: carrier });
|
|
66
128
|
}
|
|
67
129
|
}
|
|
68
|
-
return [...candidates.
|
|
69
|
-
|
|
70
|
-
|
|
130
|
+
return [...candidates.values()].sort((a, b) => a.file.localeCompare(b.file));
|
|
131
|
+
}
|
|
132
|
+
function dispositionOf(file, ctx) {
|
|
133
|
+
if (ctx.approvedUnmanaged.has(file)) {
|
|
134
|
+
ctx.approvedSeen.add(file);
|
|
135
|
+
return 'approved';
|
|
136
|
+
}
|
|
137
|
+
if (ctx.expectedNew.has(file)) {
|
|
138
|
+
ctx.expectedNewSeen.add(file);
|
|
139
|
+
return 'expected-new';
|
|
140
|
+
}
|
|
141
|
+
const ignore = ctx.adjacentIgnore.find((entry) => matchesIgnoreEntry(entry, file));
|
|
142
|
+
if (ignore !== undefined) {
|
|
143
|
+
ctx.adjacentIgnoreSeen.add(ignore);
|
|
144
|
+
return 'ignored';
|
|
145
|
+
}
|
|
146
|
+
return 'refusing';
|
|
71
147
|
}
|
|
72
148
|
/**
|
|
73
|
-
* Splits the candidates into the ones that refuse and the ones a
|
|
74
|
-
* `--expect-unmanaged
|
|
75
|
-
* so an unused
|
|
76
|
-
* dropped, so the exception stays visible
|
|
149
|
+
* Splits the candidates into the ones that refuse and the ones a carve-out
|
|
150
|
+
* (`--expect-unmanaged`, `--expect-new`, `--adjacent-ignore`) admits. Met
|
|
151
|
+
* carve-outs are recorded so an unused one can be reported, and admitted
|
|
152
|
+
* files are returned rather than dropped, so the exception stays visible
|
|
153
|
+
* instead of quietly widening.
|
|
77
154
|
*/
|
|
78
155
|
function partitionApproved(files, ctx) {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return { refusing, approved };
|
|
156
|
+
const groups = {
|
|
157
|
+
refusing: [],
|
|
158
|
+
approved: [],
|
|
159
|
+
'expected-new': [],
|
|
160
|
+
ignored: [],
|
|
161
|
+
};
|
|
162
|
+
for (const entry of files)
|
|
163
|
+
groups[dispositionOf(entry.file, ctx)].push(entry);
|
|
164
|
+
return groups;
|
|
91
165
|
}
|
|
92
|
-
/**
|
|
166
|
+
/** How a notice names one file's anchor. */
|
|
167
|
+
function describeAnchor(entry) {
|
|
168
|
+
return entry.registeredBy !== undefined
|
|
169
|
+
? `registered by engine/${entry.registeredBy}`
|
|
170
|
+
: `beside engine/${entry.anchorDir}`;
|
|
171
|
+
}
|
|
172
|
+
/** Renders `<file> (beside engine/<dir>)` or `<file> (registered by engine/<carrier>)`. */
|
|
93
173
|
function describeAnchored(entry) {
|
|
94
|
-
return `${entry.file} (
|
|
174
|
+
return `${entry.file} (${describeAnchor(entry)})`;
|
|
175
|
+
}
|
|
176
|
+
/** The admitted-file notice lines, one per carve-out that admitted anything. */
|
|
177
|
+
function reportAdmitted(patchFilename, groups) {
|
|
178
|
+
const notices = [
|
|
179
|
+
[groups.approved, 'admitted by --expect-unmanaged'],
|
|
180
|
+
[groups['expected-new'], 'expected by --expect-new'],
|
|
181
|
+
[groups.ignored, 'ignored by --adjacent-ignore'],
|
|
182
|
+
];
|
|
183
|
+
for (const [entries, how] of notices) {
|
|
184
|
+
if (entries.length === 0)
|
|
185
|
+
continue;
|
|
186
|
+
info(`${patchFilename}: ${entries.length} adjacent unmanaged file(s) ${how} ` +
|
|
187
|
+
`(${entries.map(describeAnchored).join(', ')}) — reported, not refused.`);
|
|
188
|
+
}
|
|
95
189
|
}
|
|
96
190
|
/**
|
|
97
191
|
* Emits the scan-less advisory for one patch: missing manifest files and
|
|
@@ -119,20 +213,19 @@ export async function reportAdjacentUnmanagedFiles(args) {
|
|
|
119
213
|
patchFilename: patch.filename,
|
|
120
214
|
ctx,
|
|
121
215
|
});
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
`--expect-unmanaged (${approved.map(describeAnchored).join(', ')}) — reported, not refused.`);
|
|
126
|
-
}
|
|
216
|
+
const groups = partitionApproved(allUnmanaged, ctx);
|
|
217
|
+
reportAdmitted(patch.filename, groups);
|
|
218
|
+
const unmanagedFiles = groups.refusing;
|
|
127
219
|
if (unmanagedFiles.length === 0)
|
|
128
220
|
return false;
|
|
129
221
|
const firstFew = unmanagedFiles.slice(0, 3).map(describeAnchored).join(', ');
|
|
130
222
|
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
|
-
|
|
133
|
-
`Plain re-export keeps filesAffected unchanged — use --scan-file to include reviewed files
|
|
223
|
+
warn(`${patch.filename}: found ${unmanagedFiles.length} unmanaged file(s) adjacent to this patch's ownership (${firstFew}${more}). ` +
|
|
224
|
+
`${ADJACENT_MEANS} ` +
|
|
225
|
+
`Plain re-export keeps filesAffected unchanged — use --scan-file to include reviewed files, ` +
|
|
226
|
+
`or --expect-new for a file your next export adopts.`);
|
|
134
227
|
for (const entry of unmanagedFiles) {
|
|
135
|
-
info(` ${entry.file} —
|
|
228
|
+
info(` ${entry.file} — ${describeAnchor(entry)} — ` +
|
|
136
229
|
`fireforge re-export ${patch.filename} --scan --scan-file ${entry.file}`);
|
|
137
230
|
}
|
|
138
231
|
if (ctx.refuseAdjacentUnmanaged) {
|
|
@@ -145,3 +238,29 @@ export async function reportAdjacentUnmanagedFiles(args) {
|
|
|
145
238
|
}
|
|
146
239
|
return false;
|
|
147
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* End-of-run accounting for `--expect-new` and `--adjacent-ignore`.
|
|
243
|
+
*
|
|
244
|
+
* An `--expect-new` path is a promise that the next export adopts it, so a
|
|
245
|
+
* met one is repeated as the export still owed, and an unmet one is a
|
|
246
|
+
* warning: a typo there leaves the refusal armed for the file it meant to
|
|
247
|
+
* admit. An unmatched `--adjacent-ignore` entry is only noted, because a
|
|
248
|
+
* reviewed ignore list is reused across runs and patches, and "not met"
|
|
249
|
+
* is its normal case.
|
|
250
|
+
*/
|
|
251
|
+
export function reportAdjacentCarveOutUsage(ctx) {
|
|
252
|
+
const unseenNew = [...ctx.expectedNew].filter((file) => !ctx.expectedNewSeen.has(file));
|
|
253
|
+
if (unseenNew.length > 0) {
|
|
254
|
+
warn(`--expect-new path(s) were not met as adjacent unmanaged files this run: ` +
|
|
255
|
+
`${unseenNew.join(', ')}. Check the path for a typo, and that a selected patch owns a ` +
|
|
256
|
+
'file in its directory or registers it. The refusal stayed armed for everything else.');
|
|
257
|
+
}
|
|
258
|
+
if (ctx.expectedNewSeen.size > 0) {
|
|
259
|
+
info(`--expect-new admitted ${[...ctx.expectedNewSeen].sort().join(', ')}, which no patch owns ` +
|
|
260
|
+
'yet. Export them next so the queue does not register files it does not ship.');
|
|
261
|
+
}
|
|
262
|
+
const unseenIgnore = ctx.adjacentIgnore.filter((entry) => !ctx.adjacentIgnoreSeen.has(entry));
|
|
263
|
+
if (unseenIgnore.length > 0) {
|
|
264
|
+
info(`--adjacent-ignore entries matched no adjacent file this run: ${unseenIgnore.join(', ')}.`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* (`re-export-adjacent.ts`) warns about unmanaged neighbour files but is
|
|
9
9
|
* structurally blind to drift inside owned files. This module compares
|
|
10
10
|
* the old body against the refreshed body per file and reports the
|
|
11
|
-
* payload lines that are about to enter the body. Under
|
|
11
|
+
* payload lines that are about to enter or leave the body. Under
|
|
12
12
|
* `--refuse-foreign-drift` the patch is skipped and the run exits
|
|
13
13
|
* non-zero, mirroring `--refuse-adjacent-unmanaged`.
|
|
14
14
|
*
|
|
@@ -41,23 +41,39 @@ export interface ForeignDriftContext {
|
|
|
41
41
|
reason: ForeignDriftRefusalReason;
|
|
42
42
|
}[];
|
|
43
43
|
}
|
|
44
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* One owned file's foreign drift between the old and refreshed body,
|
|
46
|
+
* counted as edits to the engine file rather than as body payload, so a
|
|
47
|
+
* deletion-only change is drift like any other.
|
|
48
|
+
*/
|
|
45
49
|
export interface FileForeignDrift {
|
|
46
50
|
file: string;
|
|
47
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Lines the engine file gained since the old body: `+` payload the new
|
|
53
|
+
* body carries that the old did not, plus `-` payload the old body carried
|
|
54
|
+
* that the new one dropped (an upstream line restored).
|
|
55
|
+
*/
|
|
48
56
|
addedLines: number;
|
|
49
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* Lines the engine file lost since the old body: `-` payload the new body
|
|
59
|
+
* carries that the old did not, plus `+` payload the old body carried that
|
|
60
|
+
* the new one dropped (a line the patch added, now deleted).
|
|
61
|
+
*/
|
|
50
62
|
removedLines: number;
|
|
51
|
-
/** `@@` headers of
|
|
63
|
+
/** `@@` headers of the hunks carrying those lines (capped). */
|
|
52
64
|
hunkSummaries: string[];
|
|
53
65
|
/** True when a binary section's recorded new-side blob hash changed. */
|
|
54
66
|
binaryChanged: boolean;
|
|
55
67
|
}
|
|
56
68
|
/**
|
|
57
69
|
* Pure comparison: per owned file, which `+`/`-` payload lines the
|
|
58
|
-
* refreshed body carries that the old body did not
|
|
59
|
-
*
|
|
60
|
-
*
|
|
70
|
+
* refreshed body carries that the old body did not, and which the old body
|
|
71
|
+
* carried that the refreshed one dropped. Both directions are drift: a
|
|
72
|
+
* concurrent session deleting a line the patch added changes the patch as
|
|
73
|
+
* surely as one adding a line, and a belt that only saw absorbed lines let
|
|
74
|
+
* such a deletion through silently. Files not in `previousFilesAffected`
|
|
75
|
+
* are skipped, since those are intentional adoptions (`--scan`) or
|
|
76
|
+
* retirements (`--expect-removed`) rather than drift.
|
|
61
77
|
*/
|
|
62
78
|
export declare function computeForeignDrift(oldBody: string, newBody: string, previousFilesAffected: readonly string[]): FileForeignDrift[];
|
|
63
79
|
/**
|
|
@@ -71,6 +87,18 @@ export declare function reportForeignDrift(args: {
|
|
|
71
87
|
engineDir: string;
|
|
72
88
|
newDiffContent: string;
|
|
73
89
|
ctx: ForeignDriftContext;
|
|
90
|
+
/**
|
|
91
|
+
* The patch's files that the comparison covers. Defaults to its
|
|
92
|
+
* `filesAffected`; the caller narrows it by the `--expect-removed`
|
|
93
|
+
* retirements, which leave the body on purpose.
|
|
94
|
+
*/
|
|
95
|
+
ownedFiles?: readonly string[];
|
|
96
|
+
/**
|
|
97
|
+
* Files a `--scan`/`--scan-file` adoption brings in this run. They sit
|
|
98
|
+
* outside the comparison (nothing to compare against) and count as
|
|
99
|
+
* expected, so naming one in `--expect` is not reported as unseen.
|
|
100
|
+
*/
|
|
101
|
+
adoptedFiles?: readonly string[];
|
|
74
102
|
}): Promise<boolean>;
|
|
75
103
|
/**
|
|
76
104
|
* Names `--expect` paths that never drifted this run. A typo'd
|