@hominis/fireforge 0.52.0 → 0.54.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 +45 -0
- package/dist/build-info.json +4 -4
- package/dist/src/commands/bootstrap.js +2 -3
- package/dist/src/commands/build-prelock.d.ts +3 -3
- package/dist/src/commands/build-prelock.js +3 -3
- package/dist/src/commands/build.js +4 -0
- package/dist/src/commands/declarations-report.d.ts +9 -3
- package/dist/src/commands/declarations-report.js +18 -11
- package/dist/src/commands/discard.js +3 -3
- package/dist/src/commands/doctor-furnace-config-sync.js +4 -4
- package/dist/src/commands/doctor-furnace.js +5 -5
- package/dist/src/commands/export-adopt-check.d.ts +5 -6
- package/dist/src/commands/export-adopt-check.js +5 -6
- package/dist/src/commands/export-flow.js +6 -9
- package/dist/src/commands/export-staged-registration.d.ts +4 -4
- package/dist/src/commands/export-staged-registration.js +4 -4
- package/dist/src/commands/forward-import-remedy.d.ts +2 -3
- package/dist/src/commands/forward-import-remedy.js +2 -3
- package/dist/src/commands/furnace/create-dry-run.d.ts +2 -2
- package/dist/src/commands/furnace/deploy-fragment-includers.d.ts +2 -2
- package/dist/src/commands/furnace/deploy-fragment-includers.js +2 -2
- package/dist/src/commands/furnace/index.js +2 -2
- package/dist/src/commands/lint.js +4 -4
- package/dist/src/commands/patch/compact.js +1 -1
- package/dist/src/commands/patch/lint-ignore-from.d.ts +3 -4
- package/dist/src/commands/patch/lint-ignore-from.js +3 -4
- package/dist/src/commands/patch/move-files-adopt.d.ts +33 -0
- package/dist/src/commands/patch/move-files-adopt.js +65 -0
- package/dist/src/commands/patch/move-files-create.js +41 -20
- package/dist/src/commands/patch/move-files-into.d.ts +18 -0
- package/dist/src/commands/patch/move-files-into.js +125 -60
- package/dist/src/commands/patch/move-files-retire.d.ts +39 -0
- package/dist/src/commands/patch/move-files-retire.js +91 -0
- package/dist/src/commands/patch/move-files.d.ts +3 -0
- package/dist/src/commands/patch/move-files.js +42 -12
- package/dist/src/commands/patch/move-size-tier.d.ts +27 -0
- package/dist/src/commands/patch/move-size-tier.js +66 -0
- package/dist/src/commands/patch/rename.js +1 -1
- package/dist/src/commands/patch/reorder-projection.d.ts +17 -0
- package/dist/src/commands/patch/reorder-projection.js +49 -0
- package/dist/src/commands/patch/reorder-shift.d.ts +30 -0
- package/dist/src/commands/patch/reorder-shift.js +307 -0
- package/dist/src/commands/patch/reorder.d.ts +9 -15
- package/dist/src/commands/patch/reorder.js +52 -50
- package/dist/src/commands/patch/split-plan.d.ts +31 -3
- package/dist/src/commands/patch/split-plan.js +36 -12
- package/dist/src/commands/patch/split-staged.d.ts +2 -2
- package/dist/src/commands/patch/split-staged.js +2 -2
- package/dist/src/commands/patch/split.js +18 -5
- package/dist/src/commands/patch/staged-dependency-entries.d.ts +9 -1
- package/dist/src/commands/patch/staged-dependency-entries.js +32 -4
- package/dist/src/commands/patch/staged-dependency-from.js +15 -9
- package/dist/src/commands/patch/staged-dependency-remove.d.ts +26 -4
- package/dist/src/commands/patch/staged-dependency-remove.js +60 -23
- package/dist/src/commands/patch/staged-dependency.js +35 -24
- package/dist/src/commands/patch/tier.js +2 -2
- package/dist/src/commands/patch-rename-map.d.ts +14 -3
- package/dist/src/commands/patch-rename-map.js +21 -3
- package/dist/src/commands/re-export-adjacent.d.ts +4 -5
- package/dist/src/commands/re-export-lint.d.ts +4 -3
- package/dist/src/commands/re-export-lint.js +4 -3
- package/dist/src/commands/re-export-options.js +8 -2
- package/dist/src/commands/re-export-preflight.js +1 -2
- package/dist/src/commands/re-export-purity-scope.d.ts +1 -1
- package/dist/src/commands/re-export-purity-scope.js +13 -7
- package/dist/src/commands/re-export-retired-declarations.d.ts +22 -0
- package/dist/src/commands/re-export-retired-declarations.js +113 -0
- package/dist/src/commands/re-export-single.js +2 -2
- package/dist/src/commands/re-export-stale-registrations.d.ts +27 -6
- package/dist/src/commands/re-export-stale-registrations.js +65 -21
- package/dist/src/commands/re-export.js +4 -0
- package/dist/src/commands/rebase/continue.js +3 -3
- package/dist/src/commands/register.js +5 -4
- package/dist/src/commands/run.js +11 -4
- package/dist/src/commands/status.js +1 -1
- package/dist/src/commands/test-build-furnace.d.ts +1 -2
- package/dist/src/commands/test-build-furnace.js +1 -2
- package/dist/src/commands/test-build-phase.d.ts +8 -0
- package/dist/src/commands/test-build-phase.js +42 -7
- package/dist/src/commands/test-control-restore.d.ts +40 -0
- package/dist/src/commands/test-control-restore.js +86 -0
- package/dist/src/commands/test-dispatch.js +68 -7
- package/dist/src/commands/test-multi-verdict.js +1 -0
- package/dist/src/commands/test-orchestrate.d.ts +6 -0
- package/dist/src/commands/test-orchestrate.js +32 -1
- package/dist/src/commands/test-perf-scope.d.ts +1 -1
- package/dist/src/commands/test-perf-scope.js +1 -1
- package/dist/src/commands/test-prelock.d.ts +18 -8
- package/dist/src/commands/test-prelock.js +42 -14
- package/dist/src/commands/test-register.js +4 -3
- package/dist/src/commands/test-results.d.ts +10 -1
- package/dist/src/commands/test-results.js +20 -3
- package/dist/src/commands/test-run.d.ts +6 -0
- package/dist/src/commands/test-run.js +23 -4
- package/dist/src/commands/test-shards.d.ts +7 -3
- package/dist/src/commands/test-shards.js +24 -7
- package/dist/src/commands/test-stale-gate.d.ts +6 -1
- package/dist/src/commands/test-stale-gate.js +40 -2
- package/dist/src/commands/test-verdict.d.ts +5 -3
- package/dist/src/commands/test.js +14 -2
- package/dist/src/commands/token.js +2 -2
- package/dist/src/commands/verify.js +1 -1
- package/dist/src/commands/watch.js +1 -1
- package/dist/src/commands/wire.js +1 -1
- package/dist/src/core/build-audit-resolve.d.ts +4 -5
- package/dist/src/core/build-audit-resolve.js +4 -5
- package/dist/src/core/build-audit-skip.d.ts +2 -2
- package/dist/src/core/build-audit.d.ts +3 -4
- package/dist/src/core/build-baseline-types.d.ts +24 -3
- package/dist/src/core/build-baseline-validate.d.ts +30 -1
- package/dist/src/core/build-baseline-validate.js +127 -35
- package/dist/src/core/build-baseline.d.ts +8 -2
- package/dist/src/core/build-baseline.js +25 -16
- package/dist/src/core/build-jar-escalation.d.ts +4 -4
- package/dist/src/core/build-jar-escalation.js +4 -4
- package/dist/src/core/build-prepare.js +4 -4
- package/dist/src/core/config-validate.js +4 -4
- package/dist/src/core/coverage-extend.d.ts +5 -3
- package/dist/src/core/coverage-extend.js +8 -3
- package/dist/src/core/coverage-records.d.ts +4 -5
- package/dist/src/core/coverage-records.js +4 -5
- package/dist/src/core/destructive.d.ts +2 -2
- package/dist/src/core/destructive.js +2 -2
- package/dist/src/core/discard-baseline.d.ts +2 -2
- package/dist/src/core/discard-baseline.js +3 -3
- package/dist/src/core/engine-session-lock.d.ts +1 -1
- package/dist/src/core/engine-session-lock.js +1 -1
- package/dist/src/core/file-lock-owner.d.ts +5 -5
- package/dist/src/core/file-lock-owner.js +5 -5
- package/dist/src/core/file-lock-queue.js +6 -8
- package/dist/src/core/file-lock-shared.d.ts +4 -4
- package/dist/src/core/file-lock-shared.js +4 -4
- package/dist/src/core/file-lock.js +7 -10
- package/dist/src/core/firefox.js +2 -2
- package/dist/src/core/furnace-apply-overwrite-warn.d.ts +1 -1
- package/dist/src/core/furnace-apply-overwrite-warn.js +1 -1
- package/dist/src/core/furnace-apply.js +3 -3
- package/dist/src/core/furnace-operation.js +5 -5
- package/dist/src/core/furnace-registration-jar-inplace.js +5 -6
- package/dist/src/core/furnace-registration-rename.d.ts +5 -5
- package/dist/src/core/furnace-registration-rename.js +5 -5
- package/dist/src/core/furnace-registration-validate.d.ts +2 -1
- package/dist/src/core/furnace-registration-validate.js +2 -1
- package/dist/src/core/furnace-registration.js +2 -2
- package/dist/src/core/git-base.js +4 -3
- package/dist/src/core/git-diff.js +1 -1
- package/dist/src/core/git.js +1 -1
- package/dist/src/core/harness-orphans.d.ts +6 -6
- package/dist/src/core/harness-orphans.js +6 -6
- package/dist/src/core/harness-port-reservation.d.ts +60 -0
- package/dist/src/core/harness-port-reservation.js +122 -0
- package/dist/src/core/history-log-file.d.ts +9 -9
- package/dist/src/core/history-log-file.js +15 -15
- package/dist/src/core/history-log.d.ts +3 -3
- package/dist/src/core/history-log.js +3 -3
- package/dist/src/core/launchable-build.d.ts +39 -0
- package/dist/src/core/launchable-build.js +85 -0
- package/dist/src/core/marionette-port.js +1 -3
- package/dist/src/core/moz-manifest-rules.js +3 -2
- package/dist/src/core/negative-control-compiled.d.ts +13 -3
- package/dist/src/core/negative-control-compiled.js +27 -5
- package/dist/src/core/negative-control-diff.js +31 -6
- package/dist/src/core/parser-fallback.d.ts +5 -5
- package/dist/src/core/parser-fallback.js +5 -5
- package/dist/src/core/patch-identifier-suggest.d.ts +6 -0
- package/dist/src/core/patch-identifier-suggest.js +11 -4
- package/dist/src/core/patch-lint-checkjs.d.ts +1 -1
- package/dist/src/core/patch-lint-checkjs.js +1 -1
- package/dist/src/core/patch-lint-css.js +2 -2
- package/dist/src/core/patch-lint-file-size.d.ts +5 -7
- package/dist/src/core/patch-lint-file-size.js +5 -7
- package/dist/src/core/patch-lint-ignore.d.ts +3 -3
- package/dist/src/core/patch-lint-ignore.js +3 -3
- package/dist/src/core/patch-lint-prettier.d.ts +4 -6
- package/dist/src/core/patch-lint-prettier.js +4 -6
- package/dist/src/core/patch-lint-projection.d.ts +1 -1
- package/dist/src/core/patch-lint-projection.js +1 -1
- package/dist/src/core/patch-lint-queue-types.d.ts +2 -2
- package/dist/src/core/patch-lint-queue-types.js +2 -2
- package/dist/src/core/patch-manifest-consistency.d.ts +2 -2
- package/dist/src/core/patch-manifest-consistency.js +2 -2
- package/dist/src/core/patch-manifest-io.d.ts +5 -1
- package/dist/src/core/patch-manifest-io.js +12 -10
- package/dist/src/core/patch-manifest-key-order.d.ts +3 -4
- package/dist/src/core/patch-manifest-key-order.js +3 -4
- package/dist/src/core/patch-parse.d.ts +3 -3
- package/dist/src/core/patch-parse.js +3 -3
- package/dist/src/core/patch-policy.d.ts +1 -1
- package/dist/src/core/patch-policy.js +1 -1
- package/dist/src/core/patch-source-metadata.d.ts +1 -1
- package/dist/src/core/patch-source-metadata.js +1 -1
- package/dist/src/core/patch-subset.d.ts +5 -3
- package/dist/src/core/patch-subset.js +8 -7
- package/dist/src/core/patch-transform.d.ts +2 -2
- package/dist/src/core/patch-transform.js +2 -2
- package/dist/src/core/rebase-session.js +4 -6
- package/dist/src/core/register-xpcshell-test.d.ts +28 -7
- package/dist/src/core/register-xpcshell-test.js +76 -27
- package/dist/src/core/run-log.d.ts +8 -8
- package/dist/src/core/run-log.js +8 -8
- package/dist/src/core/status-classify.d.ts +1 -2
- package/dist/src/core/status-classify.js +1 -2
- package/dist/src/core/test-harness-crash.d.ts +5 -3
- package/dist/src/core/test-harness-crash.js +13 -14
- package/dist/src/core/test-harness-infra.d.ts +32 -0
- package/dist/src/core/test-harness-infra.js +69 -0
- package/dist/src/core/test-harness-signature.d.ts +6 -0
- package/dist/src/core/test-stale-check.d.ts +38 -0
- package/dist/src/core/test-stale-check.js +82 -1
- package/dist/src/core/test-stall-triage.js +5 -7
- package/dist/src/core/token-category.js +2 -2
- package/dist/src/core/token-inventory.js +3 -3
- package/dist/src/core/token-manager.d.ts +2 -2
- package/dist/src/core/token-manager.js +5 -7
- package/dist/src/core/token-variant.d.ts +6 -6
- package/dist/src/core/token-variant.js +7 -7
- package/dist/src/core/typecheck-shim.js +1 -1
- package/dist/src/core/wire-destroy.d.ts +1 -1
- package/dist/src/core/wire-destroy.js +1 -1
- package/dist/src/core/wire-dom-fragment.js +1 -1
- package/dist/src/core/wire-init.d.ts +1 -1
- package/dist/src/core/wire-init.js +1 -1
- package/dist/src/core/wire-subscript.d.ts +1 -1
- package/dist/src/core/wire-subscript.js +1 -1
- package/dist/src/core/wire-utils.d.ts +2 -2
- package/dist/src/core/wire-utils.js +3 -3
- package/dist/src/errors/base.d.ts +1 -1
- package/dist/src/errors/base.js +1 -1
- package/dist/src/errors/rebase.d.ts +1 -1
- package/dist/src/errors/rebase.js +1 -1
- package/dist/src/types/commands/options.d.ts +38 -3
- package/dist/src/types/config.d.ts +5 -7
- package/dist/src/utils/options.js +2 -2
- package/dist/src/utils/process.js +2 -2
- package/dist/src/utils/regex.d.ts +2 -2
- package/dist/src/utils/regex.js +3 -3
- package/dist/src/utils/sleep.d.ts +5 -6
- package/dist/src/utils/sleep.js +5 -6
- package/dist/src/utils/validation.js +2 -2
- package/docs/configuration.md +28 -23
- package/docs/exit-codes.md +8 -11
- package/docs/furnace.md +3 -3
- package/docs/lifecycle-invariants.md +36 -45
- package/docs/machine-output.md +41 -24
- package/docs/patch-workflow.md +140 -29
- package/docs/run-logs.md +8 -9
- package/docs/testing.md +114 -48
- package/package.json +1 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* `patch move-files --retire-empty`: fold every file of a patch into
|
|
4
|
+
* another and retire the emptied patch's slot in the same transaction.
|
|
5
|
+
*
|
|
6
|
+
* Without the flag `move-files` refuses to empty the source, and
|
|
7
|
+
* `patch retire` on an emptied patch refuses while another patch's
|
|
8
|
+
* declaration still names it as the owner. With the flag the move re-points
|
|
9
|
+
* those declarations to the destination first (after the fold the
|
|
10
|
+
* destination creates every file the source did), carries the source's own declarations over with its files, and then
|
|
11
|
+
* retires the source the way `patch retire` does: the row and `.patch` file
|
|
12
|
+
* leave, and the order stays spent as a `patches.json#retired` slot so
|
|
13
|
+
* `patchPolicy.allowGaps: false` sees no gap.
|
|
14
|
+
*/
|
|
15
|
+
import { rewriteStagedDependencyOwners } from '../../core/patch-manifest.js';
|
|
16
|
+
/**
|
|
17
|
+
* Concatenates declaration lists without duplicates, dropping every entry
|
|
18
|
+
* owned by `destination` itself: after the fold the file it waits for
|
|
19
|
+
* arrives in the same patch, so it is no longer a forward edge.
|
|
20
|
+
*/
|
|
21
|
+
function foldDeclarations(lists, destination) {
|
|
22
|
+
const keys = new Set();
|
|
23
|
+
const merged = [];
|
|
24
|
+
for (const entry of lists.flatMap((list) => list ?? [])) {
|
|
25
|
+
if (entry.owner === destination)
|
|
26
|
+
continue;
|
|
27
|
+
const key = JSON.stringify(entry);
|
|
28
|
+
if (keys.has(key))
|
|
29
|
+
continue;
|
|
30
|
+
keys.add(key);
|
|
31
|
+
merged.push(entry);
|
|
32
|
+
}
|
|
33
|
+
return merged;
|
|
34
|
+
}
|
|
35
|
+
/** The destination row carrying its own and the source's declarations. */
|
|
36
|
+
function foldStaged(destination, source) {
|
|
37
|
+
const own = destination.stagedDependencies;
|
|
38
|
+
const forwardImports = foldDeclarations([own?.forwardImports, source?.forwardImports], destination.filename);
|
|
39
|
+
const registrations = foldDeclarations([own?.registrations, source?.registrations], destination.filename);
|
|
40
|
+
const folded = { ...destination };
|
|
41
|
+
delete folded.stagedDependencies;
|
|
42
|
+
if (forwardImports.length === 0 && registrations.length === 0)
|
|
43
|
+
return folded;
|
|
44
|
+
folded.stagedDependencies = {
|
|
45
|
+
...(forwardImports.length > 0 ? { forwardImports } : {}),
|
|
46
|
+
...(registrations.length > 0 ? { registrations } : {}),
|
|
47
|
+
};
|
|
48
|
+
return folded;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Re-points every declaration whose owner is `source` to `destination` and
|
|
52
|
+
* carries the source's own declarations onto the destination. The source
|
|
53
|
+
* row is returned unchanged; the caller retires it.
|
|
54
|
+
*
|
|
55
|
+
* @param patches - Manifest rows
|
|
56
|
+
* @param source - Filename of the patch the fold empties
|
|
57
|
+
* @param destination - Filename of the patch that receives its files
|
|
58
|
+
*/
|
|
59
|
+
export function foldSourceRows(patches, source, destination) {
|
|
60
|
+
const lookup = (owner) => owner === source ? destination : undefined;
|
|
61
|
+
const sourceRow = patches.find((patch) => patch.filename === source);
|
|
62
|
+
const sourceStaged = sourceRow
|
|
63
|
+
? rewriteStagedDependencyOwners(sourceRow, lookup).stagedDependencies
|
|
64
|
+
: undefined;
|
|
65
|
+
const repointed = [];
|
|
66
|
+
const rows = patches.map((patch) => {
|
|
67
|
+
if (patch.filename === source)
|
|
68
|
+
return patch;
|
|
69
|
+
const rewritten = rewriteStagedDependencyOwners(patch, lookup);
|
|
70
|
+
if (rewritten !== patch)
|
|
71
|
+
repointed.push(patch.filename);
|
|
72
|
+
return patch.filename === destination ? foldStaged(rewritten, sourceStaged) : rewritten;
|
|
73
|
+
});
|
|
74
|
+
return { patches: rows, repointed };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The retired slot the emptied source leaves, shaped as `patch retire`
|
|
78
|
+
* writes it.
|
|
79
|
+
*
|
|
80
|
+
* @param source - The emptied source row
|
|
81
|
+
* @param destination - Filename of the patch that took its files
|
|
82
|
+
*/
|
|
83
|
+
export function retiredSlotForFold(source, destination) {
|
|
84
|
+
return {
|
|
85
|
+
order: source.order,
|
|
86
|
+
category: source.category,
|
|
87
|
+
filename: source.filename,
|
|
88
|
+
retiredAt: new Date().toISOString(),
|
|
89
|
+
reason: `folded into ${destination} by patch move-files --retire-empty`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
* re-export choreography needed to move file ownership between two
|
|
4
4
|
* patches. With `--create --order <n>` the target patch is created and
|
|
5
5
|
* the files move into it as one transaction (see move-files-create.ts).
|
|
6
|
+
* `--scan-file` adopts new, unowned engine files into the destination in
|
|
7
|
+
* the same transaction, and `--retire-empty` lets the move empty the
|
|
8
|
+
* source and retire its slot.
|
|
6
9
|
*/
|
|
7
10
|
import { Command } from 'commander';
|
|
8
11
|
import type { CommandContext } from '../../types/cli.js';
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
* re-export choreography needed to move file ownership between two
|
|
5
5
|
* patches. With `--create --order <n>` the target patch is created and
|
|
6
6
|
* the files move into it as one transaction (see move-files-create.ts).
|
|
7
|
+
* `--scan-file` adopts new, unowned engine files into the destination in
|
|
8
|
+
* the same transaction, and `--retire-empty` lets the move empty the
|
|
9
|
+
* source and retire its slot.
|
|
7
10
|
*/
|
|
8
11
|
import { relative } from 'node:path';
|
|
9
12
|
import { Command } from 'commander';
|
|
@@ -15,6 +18,7 @@ import { pathExists } from '../../utils/fs.js';
|
|
|
15
18
|
import { info, intro, note, warn } from '../../utils/logger.js';
|
|
16
19
|
import { addWaitLockOption, commanderArgParser, pickDefined, stringListOption, } from '../../utils/options.js';
|
|
17
20
|
import { parsePositiveIntegerFlag } from '../../utils/validation.js';
|
|
21
|
+
import { resolveAdoptedFiles } from './move-files-adopt.js';
|
|
18
22
|
import { patchMoveFilesCreateCommand } from './move-files-create.js';
|
|
19
23
|
import { runMoveFilesInto } from './move-files-into.js';
|
|
20
24
|
function normalizeFileList(files) {
|
|
@@ -53,13 +57,25 @@ function assertTargetDoesNotAlreadyOwnFiles(target, files) {
|
|
|
53
57
|
return;
|
|
54
58
|
throw new InvalidArgumentError(`${target.filename} already owns ${duplicates.length} requested file(s):\n${formatFiles(duplicates)}`, 'patch move-files');
|
|
55
59
|
}
|
|
56
|
-
function computeFileMovePlan(source, target, files) {
|
|
60
|
+
function computeFileMovePlan(source, target, files, adoptedFiles) {
|
|
57
61
|
const moved = new Set(files);
|
|
58
62
|
return {
|
|
59
63
|
sourceAfter: source.filesAffected.filter((file) => !moved.has(file)).sort(),
|
|
60
|
-
targetAfter: [...new Set([...target.filesAffected, ...files])].sort(),
|
|
64
|
+
targetAfter: [...new Set([...target.filesAffected, ...files, ...adoptedFiles])].sort(),
|
|
61
65
|
};
|
|
62
66
|
}
|
|
67
|
+
/** Refuses a move that empties the source unless `--retire-empty` asks for it, and the flag on a move that does not. */
|
|
68
|
+
function assertEmptySourceIntent(source, sourceAfter, retireEmpty) {
|
|
69
|
+
if (sourceAfter.length === 0 && !retireEmpty) {
|
|
70
|
+
throw new InvalidArgumentError(`${source.filename} would have no filesAffected after this move. Pass --retire-empty to ` +
|
|
71
|
+
'fold it into the destination in one transaction: the declarations it owns re-point to ' +
|
|
72
|
+
'the destination and its order stays spent as a retired slot.', 'patch move-files');
|
|
73
|
+
}
|
|
74
|
+
if (sourceAfter.length > 0 && retireEmpty) {
|
|
75
|
+
throw new InvalidArgumentError(`--retire-empty applies only to a move that empties the source; ${source.filename} would ` +
|
|
76
|
+
`keep ${sourceAfter.length} file(s).`, '--retire-empty');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
63
79
|
/**
|
|
64
80
|
* Builds and prints a safe, no-write file ownership move plan.
|
|
65
81
|
*
|
|
@@ -75,6 +91,10 @@ export async function patchMoveFilesCommand(projectRoot, fromIdentifier, toIdent
|
|
|
75
91
|
if (options.create !== true && options.order !== undefined) {
|
|
76
92
|
throw new InvalidArgumentError('--order is only valid together with --create.', '--order');
|
|
77
93
|
}
|
|
94
|
+
if (options.create === true && options.retireEmpty === true) {
|
|
95
|
+
throw new InvalidArgumentError('--retire-empty folds the source into an existing patch. To move a whole patch to a new ' +
|
|
96
|
+
'name or order, use "fireforge patch rename" or "fireforge patch reorder".', '--retire-empty');
|
|
97
|
+
}
|
|
78
98
|
if (options.create === true && options.order !== undefined) {
|
|
79
99
|
await patchMoveFilesCreateCommand(projectRoot, fromIdentifier, toIdentifier, {
|
|
80
100
|
...options,
|
|
@@ -111,10 +131,10 @@ export async function patchMoveFilesCommand(projectRoot, fromIdentifier, toIdent
|
|
|
111
131
|
}
|
|
112
132
|
assertPatchHasFiles(source, files);
|
|
113
133
|
assertTargetDoesNotAlreadyOwnFiles(target, files);
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
134
|
+
const adoptedFiles = await resolveAdoptedFiles(options.scanFile, manifest.patches, paths.engine, files);
|
|
135
|
+
const { sourceAfter, targetAfter } = computeFileMovePlan(source, target, files, adoptedFiles);
|
|
136
|
+
const retireSource = options.retireEmpty === true;
|
|
137
|
+
assertEmptySourceIntent(source, sourceAfter, retireSource);
|
|
118
138
|
for (const file of files) {
|
|
119
139
|
if (!(await pathExists(`${paths.engine}/${file}`))) {
|
|
120
140
|
warn(`engine/${file} does not exist in the current worktree; re-export may drop or fail it.`);
|
|
@@ -124,19 +144,23 @@ export async function patchMoveFilesCommand(projectRoot, fromIdentifier, toIdent
|
|
|
124
144
|
info(`Moving ownership in ${relativePatchesDir}/patches.json.`);
|
|
125
145
|
info(`Move ${files.length} file(s) from ${source.filename} to ${target.filename}:`);
|
|
126
146
|
note(formatFiles(files), 'Files');
|
|
127
|
-
|
|
147
|
+
if (adoptedFiles.length > 0)
|
|
148
|
+
note(formatFiles(adoptedFiles), 'Adopted new files');
|
|
149
|
+
note(retireSource ? '(none: retired)' : formatFiles(sourceAfter), `${source.filename} files after move`);
|
|
128
150
|
note(formatFiles(targetAfter), `${target.filename} files after move`);
|
|
129
151
|
// Manual fallback (e.g. when the worktree cannot carry both patches'
|
|
130
152
|
// content right now): the equivalent re-export choreography, in
|
|
131
153
|
// executable order. The shrink must land before the grow, or the
|
|
132
154
|
// projected cross-patch lint refuses the pair with
|
|
133
155
|
// duplicate-new-file-creation.
|
|
134
|
-
const applySource = formatReExportCommand(source.filename, sourceAfter, [
|
|
135
|
-
'--allow-shrink',
|
|
136
|
-
'--yes',
|
|
137
|
-
]);
|
|
138
156
|
const applyTarget = formatReExportCommand(target.filename, targetAfter, ['--yes']);
|
|
139
|
-
|
|
157
|
+
if (!retireSource) {
|
|
158
|
+
const applySource = formatReExportCommand(source.filename, sourceAfter, [
|
|
159
|
+
'--allow-shrink',
|
|
160
|
+
'--yes',
|
|
161
|
+
]);
|
|
162
|
+
note(`1. ${applySource}\n2. ${applyTarget}\n(the shrink must land first)`, 'Equivalent manual commands');
|
|
163
|
+
}
|
|
140
164
|
const config = await loadConfig(projectRoot);
|
|
141
165
|
await runMoveFilesInto({
|
|
142
166
|
enginePath: paths.engine,
|
|
@@ -146,6 +170,8 @@ export async function patchMoveFilesCommand(projectRoot, fromIdentifier, toIdent
|
|
|
146
170
|
files,
|
|
147
171
|
sourceAfter,
|
|
148
172
|
targetAfter,
|
|
173
|
+
adoptedFiles,
|
|
174
|
+
retireSource,
|
|
149
175
|
options,
|
|
150
176
|
config,
|
|
151
177
|
});
|
|
@@ -171,6 +197,8 @@ export function registerPatchMoveFiles(parent, context) {
|
|
|
171
197
|
.option('-d, --description <desc>', 'Description for the created patch')
|
|
172
198
|
.option('--dry-run', 'Show what would happen without writing')
|
|
173
199
|
.option('-y, --yes', 'Skip confirmation prompt (required for non-TTY)')
|
|
200
|
+
.option('--scan-file <path>', 'New engine file no patch owns, adopted into <to> in the same transaction (repeatable)', ...stringListOption())
|
|
201
|
+
.option('--retire-empty', 'Allow the move to empty <from>: re-point the declarations it owns to <to> and retire its slot')
|
|
174
202
|
.option('--force-unsafe', 'Bypass projected-lint refusals')
|
|
175
203
|
.option('--skip-lint', 'Skip per-patch lint of the projected bodies')
|
|
176
204
|
.option('--declarations-out <path>', 'Where a refusal writes its one --from-ready staged-declaration report (default: .fireforge/declarations/<timestamp>.json)');
|
|
@@ -187,6 +215,8 @@ export function registerPatchMoveFiles(parent, context) {
|
|
|
187
215
|
forceUnsafe: options.forceUnsafe,
|
|
188
216
|
skipLint: options.skipLint,
|
|
189
217
|
declarationsOut: options.declarationsOut,
|
|
218
|
+
scanFile: options.scanFile,
|
|
219
|
+
retireEmpty: options.retireEmpty,
|
|
190
220
|
waitLock: options.waitLock,
|
|
191
221
|
}),
|
|
192
222
|
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Names a patch-size tier change in a move's lint refusal.
|
|
3
|
+
*
|
|
4
|
+
* `large-patch-lines` judges a patch by its tier: a patch of test files
|
|
5
|
+
* only is held to the test tier, everything else to the general tier
|
|
6
|
+
* (with test lines split out under `addedCode`). A move that takes a
|
|
7
|
+
* source's last product file out turns it into a test-only patch, and the
|
|
8
|
+
* test tier then counts every line against its own ceiling, where a
|
|
9
|
+
* `share: "test"` waiver no longer applies. A plain size error would hide
|
|
10
|
+
* that reordering the moves (tests first) fixes it, so the wrapper below appends which tier the patch moves into, its count and the
|
|
11
|
+
* ceiling that count is held to.
|
|
12
|
+
*/
|
|
13
|
+
import { type RunPatchLintOptions } from '../export-shared.js';
|
|
14
|
+
/** One side of a move, linted on its post-transaction body. */
|
|
15
|
+
export interface MoveSideLintOptions extends RunPatchLintOptions {
|
|
16
|
+
/** The patch's files before the move; absent for a patch the move creates. */
|
|
17
|
+
filesBefore?: readonly string[];
|
|
18
|
+
/** How the refusal names the patch (`source`, `destination`). */
|
|
19
|
+
role: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Runs {@link runPatchLint} on one side of a move and, when the lint
|
|
23
|
+
* refuses, appends {@link describeMoveTierChange}'s line to the refusal.
|
|
24
|
+
*
|
|
25
|
+
* @param options - The side's lint options, its files before the move and its role
|
|
26
|
+
*/
|
|
27
|
+
export declare function runMoveSideLint(options: MoveSideLintOptions): Promise<void>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Names a patch-size tier change in a move's lint refusal.
|
|
4
|
+
*
|
|
5
|
+
* `large-patch-lines` judges a patch by its tier: a patch of test files
|
|
6
|
+
* only is held to the test tier, everything else to the general tier
|
|
7
|
+
* (with test lines split out under `addedCode`). A move that takes a
|
|
8
|
+
* source's last product file out turns it into a test-only patch, and the
|
|
9
|
+
* test tier then counts every line against its own ceiling, where a
|
|
10
|
+
* `share: "test"` waiver no longer applies. A plain size error would hide
|
|
11
|
+
* that reordering the moves (tests first) fixes it, so the wrapper below appends which tier the patch moves into, its count and the
|
|
12
|
+
* ceiling that count is held to.
|
|
13
|
+
*/
|
|
14
|
+
import { getPatchSizeThresholds, measurePatchSize, resolvePatchSizeTier, } from '../../core/patch-lint-size.js';
|
|
15
|
+
import { GeneralError } from '../../errors/base.js';
|
|
16
|
+
import { runPatchLint } from '../export-shared.js';
|
|
17
|
+
/**
|
|
18
|
+
* Describes a tier change that leaves the patch above the new tier's
|
|
19
|
+
* `large-patch-lines` error ceiling, or returns undefined when the tier
|
|
20
|
+
* does not change, the rule is waived, or the count fits.
|
|
21
|
+
*
|
|
22
|
+
* @param options - The side's lint options and its files before the move
|
|
23
|
+
*/
|
|
24
|
+
async function describeMoveTierChange(options) {
|
|
25
|
+
const { filesBefore, filesAffected, patchTier, ignoreChecks, config } = options;
|
|
26
|
+
if (filesBefore === undefined || ignoreChecks?.has('large-patch-lines') === true) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
const before = resolvePatchSizeTier(filesBefore, patchTier).tier;
|
|
30
|
+
const after = resolvePatchSizeTier(filesAffected, patchTier).tier;
|
|
31
|
+
if (before === after)
|
|
32
|
+
return undefined;
|
|
33
|
+
const measurement = await measurePatchSize(options.engineDir, options.diffContent, filesAffected, config);
|
|
34
|
+
// Under `addedCode` a general-tier patch holds only its non-test lines to
|
|
35
|
+
// the general ceiling (the test lines meet the test tier's).
|
|
36
|
+
const count = measurement.metric === 'addedCode' && after === 'general'
|
|
37
|
+
? measurement.lines - measurement.testLines
|
|
38
|
+
: measurement.lines;
|
|
39
|
+
const limit = getPatchSizeThresholds(after, config).lines.error;
|
|
40
|
+
if (count <= limit)
|
|
41
|
+
return undefined;
|
|
42
|
+
const advice = after === 'test'
|
|
43
|
+
? 'Move its test files out before its last product file (or in the same move), ' +
|
|
44
|
+
'so it never stands as a test-only patch above that ceiling.'
|
|
45
|
+
: 'Order the moves so it does not change tier while above that ceiling.';
|
|
46
|
+
return `Tier change: the ${options.role} becomes ${after} tier (it was ${before} tier): ${count} lines against ${limit}. ${advice}`;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Runs {@link runPatchLint} on one side of a move and, when the lint
|
|
50
|
+
* refuses, appends {@link describeMoveTierChange}'s line to the refusal.
|
|
51
|
+
*
|
|
52
|
+
* @param options - The side's lint options, its files before the move and its role
|
|
53
|
+
*/
|
|
54
|
+
export async function runMoveSideLint(options) {
|
|
55
|
+
try {
|
|
56
|
+
await runPatchLint(options);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (!(error instanceof GeneralError))
|
|
60
|
+
throw error;
|
|
61
|
+
const note = await describeMoveTierChange(options);
|
|
62
|
+
if (note === undefined)
|
|
63
|
+
throw error;
|
|
64
|
+
throw new GeneralError(`${error.message}\n${note}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -35,7 +35,7 @@ import { addWaitLockOption, commanderArgParser, pickDefined, resolveWaitLockSeco
|
|
|
35
35
|
import { parsePositiveIntegerFlag } from '../../utils/validation.js';
|
|
36
36
|
import { proceedAfterDecision } from '../destructive-decision.js';
|
|
37
37
|
import { requirePatchQueue, requirePatchTarget } from './patch-context.js';
|
|
38
|
-
import { projectReorder } from './reorder.js';
|
|
38
|
+
import { projectReorder } from './reorder-projection.js';
|
|
39
39
|
/**
|
|
40
40
|
* Pulls the ordinal-string + category prefix out of a patch filename so
|
|
41
41
|
* the rename keeps the existing ordinal padding verbatim. Returning the
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rename-map helpers shared by `patch reorder` (single-patch moves and
|
|
3
|
+
* `--shift`), `patch compact` and `patch rename --order`: the filename a
|
|
4
|
+
* patch takes at a new order, and the queue context projected through a
|
|
5
|
+
* rename map for cross-patch lint.
|
|
6
|
+
*/
|
|
7
|
+
import type { PatchQueueContext } from '../../core/patch-lint.js';
|
|
8
|
+
import { type PatchRenameEntry } from '../../core/patch-manifest.js';
|
|
9
|
+
import type { PatchMetadata } from '../../types/commands/index.js';
|
|
10
|
+
/** Builds a new patch filename by replacing the numeric prefix with `newOrder`. */
|
|
11
|
+
export declare function rebuildFilenameForOrder(existing: PatchMetadata, newOrder: number): string;
|
|
12
|
+
/**
|
|
13
|
+
* Applies a rename map to a {@link PatchQueueContext} so cross-patch lint
|
|
14
|
+
* can run against the projected state without touching disk. Exported for
|
|
15
|
+
* `patch rename --order`, which projects the same single-row move shape.
|
|
16
|
+
*/
|
|
17
|
+
export declare function projectReorder(base: PatchQueueContext, renameMap: Map<string, PatchRenameEntry>): PatchQueueContext;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Rename-map helpers shared by `patch reorder` (single-patch moves and
|
|
4
|
+
* `--shift`), `patch compact` and `patch rename --order`: the filename a
|
|
5
|
+
* patch takes at a new order, and the queue context projected through a
|
|
6
|
+
* rename map for cross-patch lint.
|
|
7
|
+
*/
|
|
8
|
+
import { rewriteStagedDependencyOwners } from '../../core/patch-manifest.js';
|
|
9
|
+
/** Zero-pads an ordinal number to the given width. */
|
|
10
|
+
function padOrder(value, width) {
|
|
11
|
+
return String(value).padStart(width, '0');
|
|
12
|
+
}
|
|
13
|
+
/** Builds a new patch filename by replacing the numeric prefix with `newOrder`. */
|
|
14
|
+
export function rebuildFilenameForOrder(existing, newOrder) {
|
|
15
|
+
const currentPrefixMatch = /^(\d+)-/.exec(existing.filename);
|
|
16
|
+
const currentPrefix = currentPrefixMatch?.[1] ?? '001';
|
|
17
|
+
const width = Math.max(3, currentPrefix.length, String(newOrder).length);
|
|
18
|
+
const rest = existing.filename.replace(/^\d+-/, '');
|
|
19
|
+
return `${padOrder(newOrder, width)}-${rest}`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Applies a rename map to a {@link PatchQueueContext} so cross-patch lint
|
|
23
|
+
* can run against the projected state without touching disk. Exported for
|
|
24
|
+
* `patch rename --order`, which projects the same single-row move shape.
|
|
25
|
+
*/
|
|
26
|
+
export function projectReorder(base, renameMap) {
|
|
27
|
+
const ownerLookup = (oldFilename) => renameMap.get(oldFilename)?.newFilename;
|
|
28
|
+
const projectedEntries = base.entries.map((entry) => {
|
|
29
|
+
// Project staged-dependency owner references through the rename map on
|
|
30
|
+
// every entry. Owners point at other patches' filenames, so a
|
|
31
|
+
// projection that skips non-renamed entries would lint against stale
|
|
32
|
+
// owners and report false forward-import regressions.
|
|
33
|
+
const metadata = entry.metadata
|
|
34
|
+
? rewriteStagedDependencyOwners(entry.metadata, ownerLookup)
|
|
35
|
+
: entry.metadata;
|
|
36
|
+
const rename = renameMap.get(entry.filename);
|
|
37
|
+
if (!rename) {
|
|
38
|
+
return metadata === entry.metadata ? entry : { ...entry, metadata };
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
...entry,
|
|
42
|
+
metadata,
|
|
43
|
+
filename: rename.newFilename,
|
|
44
|
+
order: rename.newOrder,
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
projectedEntries.sort((a, b) => a.order - b.order || a.filename.localeCompare(b.filename));
|
|
48
|
+
return { entries: projectedEntries };
|
|
49
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fireforge patch reorder --shift <n> --from <order> [--through <order>]`
|
|
3
|
+
* renumbers a run of patches up by `n` in one gap-checked transaction, so
|
|
4
|
+
* an insert has room.
|
|
5
|
+
*
|
|
6
|
+
* Opening room for four inserts after 103 meant moving 104 to 128 up by
|
|
7
|
+
* four. `patch rename --order` moves one patch, and under
|
|
8
|
+
* `patchPolicy.allowGaps: false` every single step refused on the gap it
|
|
9
|
+
* left, so the only way through was one `patch split --after` per insert.
|
|
10
|
+
* The shift moves the whole run at once (the retired and reserved slots
|
|
11
|
+
* inside it move with it), and under `allowGaps: false` records the orders
|
|
12
|
+
* it opens as `patches.json#reserved` slots, so the queue stays valid and
|
|
13
|
+
* the inserts that follow fill them.
|
|
14
|
+
*
|
|
15
|
+
* Same contract as `patch compact`: a projected policy check, a projected
|
|
16
|
+
* cross-patch lint (reporting only what the shift itself adds), a
|
|
17
|
+
* confirmation, and a commit under the patch-directory lock that refuses
|
|
18
|
+
* if the plan changed while it waited.
|
|
19
|
+
*/
|
|
20
|
+
import type { PatchReorderOptions } from '../../types/commands/index.js';
|
|
21
|
+
/**
|
|
22
|
+
* Runs `patch reorder --shift <n> --from <order> [--through <order>]`.
|
|
23
|
+
*
|
|
24
|
+
* @param projectRoot - Project root directory
|
|
25
|
+
* @param options - Reorder options with `shift` and `from` set
|
|
26
|
+
*/
|
|
27
|
+
export declare function patchReorderShiftCommand(projectRoot: string, options: PatchReorderOptions & {
|
|
28
|
+
shift: number;
|
|
29
|
+
from: number;
|
|
30
|
+
}): Promise<void>;
|