@hominis/fireforge 0.52.0 → 0.53.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 +26 -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.js +13 -9
- 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-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/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 +28 -0
- 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 +7 -8
- package/dist/src/commands/test-prelock.js +15 -12
- package/dist/src/commands/test-register.js +2 -2
- 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 +10 -0
- 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/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 +26 -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 +21 -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 +129 -24
- package/docs/run-logs.md +8 -9
- package/docs/testing.md +105 -46
- package/package.json +1 -1
|
@@ -26,12 +26,11 @@ import { sha256Hex } from '../utils/hash.js';
|
|
|
26
26
|
import { verbose } from '../utils/logger.js';
|
|
27
27
|
import { isBuildInputPath, isJarManifestPath, isPackageablePath, isXpcomManifestPath, } from './build-audit.js';
|
|
28
28
|
import { DELETED_FILE_FINGERPRINT, } from './build-baseline-types.js';
|
|
29
|
-
import {
|
|
29
|
+
import { describeBuildBaselineProblem, findBuildBaselineProblem, isCoverageProblem, } from './build-baseline-validate.js';
|
|
30
30
|
import { FIREFORGE_DIR } from './config-paths.js';
|
|
31
31
|
import { hashEngineFile } from './coverage-extend.js';
|
|
32
32
|
import { getHead, hasChanges, isMissingHeadError } from './git.js';
|
|
33
33
|
import { git } from './git-base.js';
|
|
34
|
-
import { getUntrackedFiles } from './git-status.js';
|
|
35
34
|
import { historyRecordIdentity } from './history-log-file.js';
|
|
36
35
|
/*
|
|
37
36
|
* Keep the IO fan-out bounded: a patched Firefox checkout can carry hundreds
|
|
@@ -52,22 +51,31 @@ export function getBuildBaselinePath(projectRoot) {
|
|
|
52
51
|
* Reads the last-build baseline if present. Returns undefined when no
|
|
53
52
|
* previous successful build has been recorded. Builds can replace invalid
|
|
54
53
|
* markers; dispatch preflights pass `refuse` so invalid coverage never
|
|
55
|
-
* becomes the
|
|
54
|
+
* becomes the implicit full claim of a baseline without coverage.
|
|
56
55
|
* @param projectRoot - Root directory of the project
|
|
57
56
|
*/
|
|
58
57
|
export async function readBuildBaseline(projectRoot, invalid = 'ignore') {
|
|
59
58
|
const path = getBuildBaselinePath(projectRoot);
|
|
59
|
+
let problem;
|
|
60
60
|
try {
|
|
61
61
|
const value = await readJson(path);
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
problem = findBuildBaselineProblem(value);
|
|
63
|
+
if (problem !== undefined) {
|
|
64
|
+
throw new Error(`invalid baseline field ${describeBuildBaselineProblem(problem)}`);
|
|
65
|
+
}
|
|
64
66
|
return value;
|
|
65
67
|
}
|
|
66
68
|
catch (error) {
|
|
67
69
|
if (getNodeErrorCode(error) === 'ENOENT')
|
|
68
70
|
return undefined;
|
|
69
71
|
if (invalid === 'refuse') {
|
|
70
|
-
|
|
72
|
+
// A coverage field is what a scoped `test --build` writes, so a fresh
|
|
73
|
+
// scoped build over the requested paths replaces it as well as a full
|
|
74
|
+
// one does.
|
|
75
|
+
const remedy = problem !== undefined && isCoverageProblem(problem)
|
|
76
|
+
? 'The field is part of the packaging-coverage claim a scoped "fireforge test --build" records. Rerun this test with --build to record a fresh claim for its paths, or run "fireforge build" to record a full baseline.'
|
|
77
|
+
: 'Run "fireforge build" to record a fresh baseline before dispatching tests.';
|
|
78
|
+
throw new PreflightRefusalError(`Build baseline ${path} is invalid: ${toError(error).message}. ${remedy}`, 'invalid-build-baseline');
|
|
71
79
|
}
|
|
72
80
|
verbose(`Ignoring invalid build baseline ${path}: ${toError(error).message}`);
|
|
73
81
|
return undefined;
|
|
@@ -87,7 +95,7 @@ function baselineRecorder() {
|
|
|
87
95
|
* @param options - See {@link WriteBuildBaselineOptions}
|
|
88
96
|
*/
|
|
89
97
|
export async function writeBuildBaseline(options) {
|
|
90
|
-
const { projectRoot, engineDir, binaryName, testPackagingCoverage, testCoverageRecords, fullCoverageAnchor, previousBaseline, recordedBy, staticComponentsHandling = 'auto', buildKind = 'full', } = options;
|
|
98
|
+
const { projectRoot, engineDir, binaryName, testPackagingCoverage, testCoverageRecords, fullCoverageAnchor, previousBaseline, recordedBy, staticComponentsHandling = 'auto', buildKind = 'full', controlBuild, } = options;
|
|
91
99
|
let engineHeadSha = '';
|
|
92
100
|
try {
|
|
93
101
|
engineHeadSha = await getHead(engineDir);
|
|
@@ -104,7 +112,7 @@ export async function writeBuildBaseline(options) {
|
|
|
104
112
|
const packageableFingerprints = await collectPackageableFingerprints(engineDir);
|
|
105
113
|
const testInputFingerprints = await collectDirtyFingerprints(engineDir, () => true, 'test staging fingerprint');
|
|
106
114
|
const buildInputFingerprints = await collectBuildInputFingerprints(engineDir, previousBaseline, buildKind);
|
|
107
|
-
// A full-coverage write (and
|
|
115
|
+
// A full-coverage write (and a no-claim `fireforge build` record)
|
|
108
116
|
// just recompiled the StaticComponents table, so it anchors the table to
|
|
109
117
|
// the current engine state. A scoped write did not, so carry the previous
|
|
110
118
|
// anchor forward verbatim.
|
|
@@ -128,6 +136,7 @@ export async function writeBuildBaseline(options) {
|
|
|
128
136
|
...(staticComponentsBaseline !== undefined ? { staticComponentsBaseline } : {}),
|
|
129
137
|
...(recordedBy !== undefined ? { recordedBy } : {}),
|
|
130
138
|
recorder: baselineRecorder(),
|
|
139
|
+
...(controlBuild !== undefined ? { controlBuild } : {}),
|
|
131
140
|
};
|
|
132
141
|
await writeJson(getBuildBaselinePath(projectRoot), baseline);
|
|
133
142
|
}
|
|
@@ -202,14 +211,14 @@ async function collectDirtyFingerprints(engineDir, includePath, contextLabel) {
|
|
|
202
211
|
try {
|
|
203
212
|
const dirtyPaths = new Set();
|
|
204
213
|
if (await hasChanges(engineDir)) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
|
|
214
|
+
// NUL-separated and unquoted: git's default output backslash-quotes a
|
|
215
|
+
// non-ASCII path (`"a/\303\251.js"`), which is neither the file on
|
|
216
|
+
// disk nor a key the baseline validator accepts.
|
|
217
|
+
const worktreeDiff = await git(['-c', 'core.quotePath=false', 'diff', '--name-only', '-z', 'HEAD'], engineDir);
|
|
218
|
+
const untracked = await git(['-c', 'core.quotePath=false', 'ls-files', '--others', '--exclude-standard', '-z'], engineDir);
|
|
219
|
+
for (const path of `${worktreeDiff}\0${untracked}`.split('\0')) {
|
|
220
|
+
if (path.length > 0)
|
|
221
|
+
dirtyPaths.add(path);
|
|
213
222
|
}
|
|
214
223
|
}
|
|
215
224
|
const included = [...dirtyPaths].filter(includePath);
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* Decides whether a changed `jar.mn` really forces the pre-test build to
|
|
3
3
|
* escalate from `mach build faster` to a full `mach build`.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* minutes against ~20 s for a chrome-only slice and is paid twice
|
|
7
|
-
* green/no-change pair on a Furnace-touching slice. A downstream
|
|
8
|
-
* (two clean runs, 2026-08-29
|
|
5
|
+
* Escalating on every changed jar.mn (`any changed jar.mn ⇒ full build`)
|
|
6
|
+
* costs ~10 minutes against ~20 s for a chrome-only slice and is paid twice
|
|
7
|
+
* per green/no-change pair on a Furnace-touching slice. A downstream
|
|
8
|
+
* experiment (two clean runs, 2026-08-29) showed that rule is over-broad
|
|
9
9
|
* for the common case: a jar-only registration of a new content file added
|
|
10
10
|
* to an existing `dist/bin` manifest was installed by a plain
|
|
11
11
|
* `fireforge build --ui` both times: `faster/install_dist_bin_browser` named
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* Decides whether a changed `jar.mn` really forces the pre-test build to
|
|
4
4
|
* escalate from `mach build faster` to a full `mach build`.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* minutes against ~20 s for a chrome-only slice and is paid twice
|
|
8
|
-
* green/no-change pair on a Furnace-touching slice. A downstream
|
|
9
|
-
* (two clean runs, 2026-08-29
|
|
6
|
+
* Escalating on every changed jar.mn (`any changed jar.mn ⇒ full build`)
|
|
7
|
+
* costs ~10 minutes against ~20 s for a chrome-only slice and is paid twice
|
|
8
|
+
* per green/no-change pair on a Furnace-touching slice. A downstream
|
|
9
|
+
* experiment (two clean runs, 2026-08-29) showed that rule is over-broad
|
|
10
10
|
* for the common case: a jar-only registration of a new content file added
|
|
11
11
|
* to an existing `dist/bin` manifest was installed by a plain
|
|
12
12
|
* `fireforge build --ui` both times: `faster/install_dist_bin_browser` named
|
|
@@ -122,7 +122,7 @@ async function decideJarEscalation(engineDir, changed) {
|
|
|
122
122
|
// build. Path-shape only: `evaluateJarManifestEscalation` decides whether a
|
|
123
123
|
// given changed manifest actually forces one (a new manifest, or one that
|
|
124
124
|
// redirects its install base directory), so an entry added to an existing
|
|
125
|
-
// `dist/bin` manifest
|
|
125
|
+
// `dist/bin` manifest does not cost a ~10-minute build.
|
|
126
126
|
const decision = await evaluateJarManifestEscalation(engineDir, changed.filter(isJarManifestPath));
|
|
127
127
|
if (decision.cleared.length > 0) {
|
|
128
128
|
verbose(`jar escalation: ${decision.cleared.length} changed jar.mn add entries to an existing dist/bin manifest; not escalating for ${decision.cleared.join(', ')}.`);
|
|
@@ -241,9 +241,9 @@ async function applyFurnaceBeforeBuild(projectRoot, only) {
|
|
|
241
241
|
}
|
|
242
242
|
// The apply computed these (patch-owned engine files whose content
|
|
243
243
|
// differed from components/ and were replaced). `furnace apply`
|
|
244
|
-
// prints them
|
|
245
|
-
// that edited a deployed Furnace file
|
|
246
|
-
// with a green result and only a checksum could tell.
|
|
244
|
+
// prints them, and so does this path: dropped silently, a negative
|
|
245
|
+
// control that edited a deployed Furnace file would run against the
|
|
246
|
+
// shipped code with a green result and only a checksum could tell.
|
|
247
247
|
const overwriteCount = logApplyWarnings(result.warnings);
|
|
248
248
|
if (overwriteCount > 0) {
|
|
249
249
|
notice(`Furnace: ${overwriteCount} Furnace-managed engine file${overwriteCount === 1 ? '' : 's'} differed from components/ and ${overwriteCount === 1 ? 'was' : 'were'} REPLACED before this build. An engine-side edit to a Furnace-managed file (for example a negative control) did not run; edit the component source under components/ instead, or restore the edit after the build.`);
|
|
@@ -74,10 +74,10 @@ function parseFirefoxBlock(rec) {
|
|
|
74
74
|
throw new ConfigError('Config field "firefox.version" must be a valid Firefox version (e.g., "145.0")');
|
|
75
75
|
}
|
|
76
76
|
const firefoxProduct = requireConfigString(firefoxRec, 'product', 'firefox.product');
|
|
77
|
-
// Uses the shared guard rather than a
|
|
78
|
-
// is the fireforge.json read path: a product added to the union but
|
|
79
|
-
//
|
|
80
|
-
//
|
|
77
|
+
// Uses the shared guard rather than a separate copy of the member list.
|
|
78
|
+
// This is the fireforge.json read path: a product added to the union but
|
|
79
|
+
// not to a local copy would be silently rejected from every project
|
|
80
|
+
// config.
|
|
81
81
|
if (!isValidFirefoxProduct(firefoxProduct)) {
|
|
82
82
|
throw new ConfigError(`Config field "firefox.product" must be one of: ${FIREFOX_PRODUCTS.join(', ')}`);
|
|
83
83
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* collapsing to this build's paths. `--extend-coverage` turns any drop into
|
|
8
8
|
* a refusal before the build.
|
|
9
9
|
*
|
|
10
|
-
* Fingerprints must be complete: a
|
|
10
|
+
* Fingerprints must be complete: a record of packageable inputs only cannot
|
|
11
11
|
* prove that a test fixture or manifest still matches its staged copy.
|
|
12
12
|
*/
|
|
13
13
|
import type { BuildBaseline, CoverageAnchor, CoverageRecord, TestPackagingCoverage } from './build-baseline-types.js';
|
|
@@ -70,9 +70,11 @@ export interface RecordedCoverage {
|
|
|
70
70
|
*
|
|
71
71
|
* @param engineDir - Path to the engine directory
|
|
72
72
|
* @param plan - Plan after {@link applyMozconfigAnchor}
|
|
73
|
-
* @param
|
|
73
|
+
* @param requestedPaths - Request paths this build packaged. Normalized
|
|
74
|
+
* here (no trailing slash, no `./`): the baseline validator rejects the
|
|
75
|
+
* raw CLI shapes, and a baseline it rejects refuses every later run.
|
|
74
76
|
*/
|
|
75
|
-
export declare function recordScopedCoverage(engineDir: string, plan: CoverageRetentionPlan,
|
|
77
|
+
export declare function recordScopedCoverage(engineDir: string, plan: CoverageRetentionPlan, requestedPaths: string[]): Promise<RecordedCoverage>;
|
|
76
78
|
/** Renders one dropped claim with its cause. */
|
|
77
79
|
export declare function describeDroppedClaim(claim: DroppedClaim): string;
|
|
78
80
|
/**
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* collapsing to this build's paths. `--extend-coverage` turns any drop into
|
|
9
9
|
* a refusal before the build.
|
|
10
10
|
*
|
|
11
|
-
* Fingerprints must be complete: a
|
|
11
|
+
* Fingerprints must be complete: a record of packageable inputs only cannot
|
|
12
12
|
* prove that a test fixture or manifest still matches its staged copy.
|
|
13
13
|
*/
|
|
14
14
|
import { toError } from '../utils/errors.js';
|
|
@@ -163,11 +163,16 @@ export async function applyMozconfigAnchor(engineDir, plan) {
|
|
|
163
163
|
*
|
|
164
164
|
* @param engineDir - Path to the engine directory
|
|
165
165
|
* @param plan - Plan after {@link applyMozconfigAnchor}
|
|
166
|
-
* @param
|
|
166
|
+
* @param requestedPaths - Request paths this build packaged. Normalized
|
|
167
|
+
* here (no trailing slash, no `./`): the baseline validator rejects the
|
|
168
|
+
* raw CLI shapes, and a baseline it rejects refuses every later run.
|
|
167
169
|
*/
|
|
168
|
-
export async function recordScopedCoverage(engineDir, plan,
|
|
170
|
+
export async function recordScopedCoverage(engineDir, plan, requestedPaths) {
|
|
169
171
|
if (plan.keepFull)
|
|
170
172
|
return { testPackagingCoverage: 'full' };
|
|
173
|
+
const requested = [
|
|
174
|
+
...new Set(requestedPaths.map(normalizeRequestPath).filter((path) => path.length > 0)),
|
|
175
|
+
];
|
|
171
176
|
const records = { ...plan.retained };
|
|
172
177
|
try {
|
|
173
178
|
const state = await readEngineContentState(engineDir);
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-path packaging-coverage anchors for scoped `fireforge test --build`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* one of those inputs changed.
|
|
4
|
+
* Each staged request path carries its own anchor restricted to its own
|
|
5
|
+
* staging inputs, and a claim is dropped only when one of those inputs
|
|
6
|
+
* changed. One claim anchored to the whole engine would be replaced by any
|
|
7
|
+
* edit outside the new scope (a peer's, on a shared engine, nearly always).
|
|
9
8
|
*
|
|
10
9
|
* The staging inputs of a request path are its manifest directory (the
|
|
11
10
|
* nearest directory at or above it that holds a test manifest) plus every
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Per-path packaging-coverage anchors for scoped `fireforge test --build`.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* one of those inputs changed.
|
|
5
|
+
* Each staged request path carries its own anchor restricted to its own
|
|
6
|
+
* staging inputs, and a claim is dropped only when one of those inputs
|
|
7
|
+
* changed. One claim anchored to the whole engine would be replaced by any
|
|
8
|
+
* edit outside the new scope (a peer's, on a shared engine, nearly always).
|
|
10
9
|
*
|
|
11
10
|
* The staging inputs of a request path are its manifest directory (the
|
|
12
11
|
* nearest directory at or above it that holds a test manifest) plus every
|
|
@@ -128,8 +128,8 @@ export declare function confirmDestructive(input: DestructiveOpInput): Promise<D
|
|
|
128
128
|
/**
|
|
129
129
|
* Appends a single JSONL record to the project's patch history log
|
|
130
130
|
* (`.fireforge/history.jsonl` unless `fireforge.json#history.path` says
|
|
131
|
-
* otherwise; a log at
|
|
132
|
-
*
|
|
131
|
+
* otherwise; a log found at `patches/.fireforge-history.jsonl` is moved
|
|
132
|
+
* there first).
|
|
133
133
|
*
|
|
134
134
|
* Call order matters: append only after the mutation succeeds, never
|
|
135
135
|
* pre-emptively, so rolled-back failures do not leave ghost entries. The log
|
|
@@ -159,8 +159,8 @@ export async function confirmDestructive(input) {
|
|
|
159
159
|
/**
|
|
160
160
|
* Appends a single JSONL record to the project's patch history log
|
|
161
161
|
* (`.fireforge/history.jsonl` unless `fireforge.json#history.path` says
|
|
162
|
-
* otherwise; a log at
|
|
163
|
-
*
|
|
162
|
+
* otherwise; a log found at `patches/.fireforge-history.jsonl` is moved
|
|
163
|
+
* there first).
|
|
164
164
|
*
|
|
165
165
|
* Call order matters: append only after the mutation succeeds, never
|
|
166
166
|
* pre-emptively, so rolled-back failures do not leave ghost entries. The log
|
|
@@ -30,7 +30,7 @@ export interface DiscardBaselinePlan {
|
|
|
30
30
|
/** Rename/copy only: baseline content for `originalPath` when that side is claimed. */
|
|
31
31
|
expectedOriginalContent?: string | null;
|
|
32
32
|
}
|
|
33
|
-
/** Builds an all-unmanaged plan set, the `--to-upstream`
|
|
33
|
+
/** Builds an all-unmanaged plan set, the `--to-upstream` shape. */
|
|
34
34
|
export declare function planUpstreamDiscards(entries: readonly GitStatusEntry[]): DiscardBaselinePlan[];
|
|
35
35
|
/**
|
|
36
36
|
* Plans the restore target for each status entry. Entries claimed by the
|
|
@@ -47,7 +47,7 @@ export declare function planDiscardBaselines(patchesDir: string, engineDir: stri
|
|
|
47
47
|
* Applies one restore plan. Patch-claimed paths end up exactly as `import`
|
|
48
48
|
* leaves them: patched content as an unstaged worktree state (tracked files
|
|
49
49
|
* read ` M`, created files `??`), nothing staged. Unmanaged plans delegate
|
|
50
|
-
* to the
|
|
50
|
+
* to the plain git {@link discardStatusEntry} mechanics.
|
|
51
51
|
*/
|
|
52
52
|
export declare function applyDiscardBaseline(engineDir: string, plan: DiscardBaselinePlan): Promise<void>;
|
|
53
53
|
/** One-line annotation for dry-run listings and confirmation summaries. */
|
|
@@ -24,7 +24,7 @@ import { createPatchedContentContext } from './patch-apply.js';
|
|
|
24
24
|
import { loadPatchesManifestState } from './patch-manifest-io.js';
|
|
25
25
|
import { parseDiffSections } from './patch-parse.js';
|
|
26
26
|
import { buildPatchClaims } from './status-classify.js';
|
|
27
|
-
/** Builds an all-unmanaged plan set, the `--to-upstream`
|
|
27
|
+
/** Builds an all-unmanaged plan set, the `--to-upstream` shape. */
|
|
28
28
|
export function planUpstreamDiscards(entries) {
|
|
29
29
|
return entries.map((entry) => ({ entry, kind: 'unmanaged', owners: [], conflicted: false }));
|
|
30
30
|
}
|
|
@@ -151,7 +151,7 @@ export async function planDiscardBaselines(patchesDir, engineDir, entries) {
|
|
|
151
151
|
* Applies one restore plan. Patch-claimed paths end up exactly as `import`
|
|
152
152
|
* leaves them: patched content as an unstaged worktree state (tracked files
|
|
153
153
|
* read ` M`, created files `??`), nothing staged. Unmanaged plans delegate
|
|
154
|
-
* to the
|
|
154
|
+
* to the plain git {@link discardStatusEntry} mechanics.
|
|
155
155
|
*/
|
|
156
156
|
export async function applyDiscardBaseline(engineDir, plan) {
|
|
157
157
|
if (plan.kind === 'unmanaged') {
|
|
@@ -173,7 +173,7 @@ export async function applyDiscardBaseline(engineDir, plan) {
|
|
|
173
173
|
await writeText(join(engineDir, entry.file), plan.expectedContent);
|
|
174
174
|
}
|
|
175
175
|
// A rename/copy's original side restores to its baseline too: the patch
|
|
176
|
-
// baseline when claimed, else the
|
|
176
|
+
// baseline when claimed, else the HEAD restore (matching what
|
|
177
177
|
// discardStatusEntry does for that side).
|
|
178
178
|
if (entry.originalPath !== undefined) {
|
|
179
179
|
if (plan.expectedOriginalContent !== undefined) {
|
|
@@ -77,7 +77,7 @@ export declare function generationDeltaPaths(before: string, after: string): {
|
|
|
77
77
|
/**
|
|
78
78
|
* Describes what moved between two generation tokens, in operator terms.
|
|
79
79
|
*
|
|
80
|
-
*
|
|
80
|
+
* A refusal that said only that the engine changed would leave the
|
|
81
81
|
* operator to reconstruct the writer by hand: `find -newermt` against the
|
|
82
82
|
* run window, minus the objdir, minus git's own bookkeeping. Everything
|
|
83
83
|
* needed to answer it is already in the two tokens, so the refusal answers
|
|
@@ -281,7 +281,7 @@ export function generationDeltaPaths(before, after) {
|
|
|
281
281
|
/**
|
|
282
282
|
* Describes what moved between two generation tokens, in operator terms.
|
|
283
283
|
*
|
|
284
|
-
*
|
|
284
|
+
* A refusal that said only that the engine changed would leave the
|
|
285
285
|
* operator to reconstruct the writer by hand: `find -newermt` against the
|
|
286
286
|
* run window, minus the objdir, minus git's own bookkeeping. Everything
|
|
287
287
|
* needed to answer it is already in the two tokens, so the refusal answers
|
|
@@ -46,11 +46,11 @@ export declare function readLockOwner(lockPath: string): Promise<LockOwner>;
|
|
|
46
46
|
* time, own start tick, diagnostic metadata) into a lock directory it has
|
|
47
47
|
* just created, and reads the record back to prove it landed.
|
|
48
48
|
*
|
|
49
|
-
* The write is fatal
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
49
|
+
* The write is fatal rather than best-effort. Stale recovery could fall
|
|
50
|
+
* back to the age heuristic, but that heuristic reaps any owner-less lock
|
|
51
|
+
* after five minutes, and the build lock legitimately holds for hours. A
|
|
52
|
+
* holder whose record failed to write would be reaped mid-build and a
|
|
53
|
+
* second process would enter the critical section. On
|
|
54
54
|
* failure the just-created lock directory is removed (nothing else can own
|
|
55
55
|
* it yet) and a {@link GeneralError} names the cause.
|
|
56
56
|
*
|
|
@@ -141,11 +141,11 @@ async function readBackLockOwner(lockPath) {
|
|
|
141
141
|
* time, own start tick, diagnostic metadata) into a lock directory it has
|
|
142
142
|
* just created, and reads the record back to prove it landed.
|
|
143
143
|
*
|
|
144
|
-
* The write is fatal
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
144
|
+
* The write is fatal rather than best-effort. Stale recovery could fall
|
|
145
|
+
* back to the age heuristic, but that heuristic reaps any owner-less lock
|
|
146
|
+
* after five minutes, and the build lock legitimately holds for hours. A
|
|
147
|
+
* holder whose record failed to write would be reaped mid-build and a
|
|
148
|
+
* second process would enter the critical section. On
|
|
149
149
|
* failure the just-created lock directory is removed (nothing else can own
|
|
150
150
|
* it yet) and a {@link GeneralError} names the cause.
|
|
151
151
|
*
|
|
@@ -3,18 +3,16 @@
|
|
|
3
3
|
* Waiter tickets for `withFileLock`: who is queued for a contended lock, in
|
|
4
4
|
* what order, and whose turn it is.
|
|
5
5
|
*
|
|
6
|
-
* The
|
|
7
|
-
*
|
|
8
|
-
* `mkdir` polls
|
|
9
|
-
*
|
|
10
|
-
* was next
|
|
11
|
-
* turn. The tickets now decide the grant: only the head of the queue may
|
|
12
|
-
* attempt the lock, so the position printed is the position honoured.
|
|
6
|
+
* The tickets decide the grant: only the head of the queue may attempt the
|
|
7
|
+
* lock, so the position printed is the position honoured. A free-for-all of
|
|
8
|
+
* `mkdir` polls would let later arrivals polling at 100 ms take every free
|
|
9
|
+
* moment from a waiter whose poll interval had backed off to 2 s, so a run
|
|
10
|
+
* told it was next could watch later runs take the lock in turn.
|
|
13
11
|
*
|
|
14
12
|
* A ticket is a file in `<lock>.waiters/` named
|
|
15
13
|
* `<startedAtMs>-<pid>-<uuid>`. Arrival order is the wait start, with the
|
|
16
14
|
* whole name as a tie-break so every reader sorts the queue identically.
|
|
17
|
-
* Because a ticket
|
|
15
|
+
* Because a ticket blocks everyone behind it, a ticket whose owner is
|
|
18
16
|
* gone must not hold the queue: a dead PID (or a reused one, where the
|
|
19
17
|
* start-tick check can tell) drops out and its ticket is removed, and a
|
|
20
18
|
* ticket its owner has stopped refreshing drops out without being removed,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared holds on a FireForge directory lock.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* serially on a
|
|
4
|
+
* Two build-less `fireforge test` runs on one objdir, each with its own
|
|
5
|
+
* profile and (under auto ports) its own ports, have no reason to wait for
|
|
6
|
+
* each other; with only exclusive holds the gate's browser tiers would run
|
|
7
|
+
* strictly serially on a many-core host. A shared hold lets such runs overlap while
|
|
8
8
|
* anything that writes (a build, a re-export, a test run that builds)
|
|
9
9
|
* still holds the lock alone.
|
|
10
10
|
*
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Shared holds on a FireForge directory lock.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* serially on a
|
|
5
|
+
* Two build-less `fireforge test` runs on one objdir, each with its own
|
|
6
|
+
* profile and (under auto ports) its own ports, have no reason to wait for
|
|
7
|
+
* each other; with only exclusive holds the gate's browser tiers would run
|
|
8
|
+
* strictly serially on a many-core host. A shared hold lets such runs overlap while
|
|
9
9
|
* anything that writes (a build, a re-export, a test run that builds)
|
|
10
10
|
* still holds the lock alone.
|
|
11
11
|
*
|
|
@@ -269,10 +269,9 @@ async function buildLockTimeoutError(lockPath, onTimeoutMessage, cause, queue) {
|
|
|
269
269
|
*
|
|
270
270
|
* The queue is read at the moment the budget expires rather than taken from
|
|
271
271
|
* the last progress probe. A probe snapshot is up to one `waitProgressMs`
|
|
272
|
-
* stale, and
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
* probe at all never had one. Both used to lose the position silently.
|
|
272
|
+
* stale, and it may not exist at all: a short budget can expire before the
|
|
273
|
+
* first probe lands (a slow runner's `registerWaiter` alone can outlast it),
|
|
274
|
+
* and a caller that configured no probe at all never has one.
|
|
276
275
|
*/
|
|
277
276
|
function formatQueuePositionReached(queue) {
|
|
278
277
|
if (queue.depth === 0)
|
|
@@ -423,12 +422,10 @@ export async function withFileLock(lockPath, operation, options = {}) {
|
|
|
423
422
|
// buys a fresh budget, because the wait is working. Only a queue
|
|
424
423
|
// that stops moving starves, which is the case the timeout is for.
|
|
425
424
|
//
|
|
426
|
-
// The first observation is a seed, never an advance.
|
|
427
|
-
//
|
|
428
|
-
//
|
|
429
|
-
//
|
|
430
|
-
// once, which is the "it does not reliably give up after N
|
|
431
|
-
// seconds" behaviour a downstream fork reported.
|
|
425
|
+
// The first observation is a seed, never an advance. Counted as an
|
|
426
|
+
// advance, the opening probe would hand out a free full budget: a
|
|
427
|
+
// `--wait-lock 300` run would outlive its 300 s even against a queue
|
|
428
|
+
// that never moved once.
|
|
432
429
|
if (hardDeadline !== undefined) {
|
|
433
430
|
if (bestAhead === undefined) {
|
|
434
431
|
bestAhead = probe.queue.ahead;
|
package/dist/src/core/firefox.js
CHANGED
|
@@ -96,8 +96,8 @@ export async function downloadFirefoxSource(options) {
|
|
|
96
96
|
await removeDir(tempDir);
|
|
97
97
|
// Only throw away the ~600 MB cached archive when the failure indicts
|
|
98
98
|
// the archive, not the environment. ENOSPC, a permission problem in
|
|
99
|
-
// destDir, or tar disappearing mid-run
|
|
100
|
-
//
|
|
99
|
+
// destDir, or tar disappearing mid-run leaves a perfectly good download
|
|
100
|
+
// that a retry can reuse without a full re-fetch.
|
|
101
101
|
if (isLikelyArchiveCorruptionError(error)) {
|
|
102
102
|
await invalidateArchiveCache(archive, cacheDir);
|
|
103
103
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* The per-file byte comparison already exists:
|
|
14
14
|
* `checkRegistrationConsistency` for custom components (fragment- and
|
|
15
15
|
* ftl-aware) and the copy-candidate walk for overrides. This module only
|
|
16
|
-
* surfaces what
|
|
16
|
+
* surfaces what those comparisons compute.
|
|
17
17
|
*/
|
|
18
18
|
import type { CustomComponentConfig, OverrideComponentConfig } from '../types/furnace.js';
|
|
19
19
|
/** One deployed, patch-owned file about to be overwritten with different bytes. */
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* The per-file byte comparison already exists:
|
|
15
15
|
* `checkRegistrationConsistency` for custom components (fragment- and
|
|
16
16
|
* ftl-aware) and the copy-candidate walk for overrides. This module only
|
|
17
|
-
* surfaces what
|
|
17
|
+
* surfaces what those comparisons compute.
|
|
18
18
|
*/
|
|
19
19
|
import { readdir } from 'node:fs/promises';
|
|
20
20
|
import { join, relative } from 'node:path';
|
|
@@ -258,9 +258,9 @@ async function applyCustomBatch(ctx, root, markerComment) {
|
|
|
258
258
|
filesAffectedTotal.push(...removed);
|
|
259
259
|
}
|
|
260
260
|
if (!dryRun) {
|
|
261
|
-
// Runs unconditionally
|
|
262
|
-
//
|
|
263
|
-
//
|
|
261
|
+
// Runs unconditionally, including on the `changed === true` path:
|
|
262
|
+
// that is when a deployed engine-only fix gets silently replaced
|
|
263
|
+
// (J6).
|
|
264
264
|
recordOverwriteWarnings(result, await findPatchOwnedOverwrites({
|
|
265
265
|
type: 'custom',
|
|
266
266
|
root,
|
|
@@ -305,11 +305,11 @@ export async function completeJournalRollback(ctx, journal, error, options) {
|
|
|
305
305
|
export async function forceReleaseFurnaceLocksForActiveOperations() {
|
|
306
306
|
const paths = new Set([...activeOperations.values()].map((op) => getFurnaceLockPath(op.root)));
|
|
307
307
|
for (const lockPath of paths) {
|
|
308
|
-
// Ownership-checked, like every other release path.
|
|
309
|
-
//
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
//
|
|
308
|
+
// Ownership-checked, like every other release path. A bare `rm` would
|
|
309
|
+
// delete a lock a different process had acquired in the window between
|
|
310
|
+
// our operation dying and this sweep running. That is the exact race
|
|
311
|
+
// `releaseLock`'s PID verification closes, and the sweeper has no
|
|
312
|
+
// acquisition token to check.
|
|
313
313
|
const removed = await releaseLockIfOwned(lockPath);
|
|
314
314
|
verbose(removed
|
|
315
315
|
? `Removed furnace lock at ${lockPath} during signal teardown`
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* In-place edits to a widget's `toolkit/content/jar.mn` block.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* the lines that change: a deleted file's line goes, a renamed component's
|
|
5
|
+
* Dropping every line of the widget and re-adding the live ones would
|
|
6
|
+
* re-sort the whole block and leave the owning patch carrying reorder
|
|
7
|
+
* hunks. These helpers touch only the lines that change: a deleted file's line goes, a renamed component's
|
|
9
8
|
* lines are rewritten where they stand, and anything still missing is left
|
|
10
9
|
* for `addJarMnEntries` to insert.
|
|
11
10
|
*/
|
|
@@ -15,8 +14,8 @@ import { escapeRegex } from '../utils/regex.js';
|
|
|
15
14
|
import { JAR_MN } from './furnace-constants.js';
|
|
16
15
|
/**
|
|
17
16
|
* The file a widget jar.mn line registers: the `(widgets/<tag>/<file>)`
|
|
18
|
-
* source mapping, or the
|
|
19
|
-
*
|
|
17
|
+
* source mapping, or the `content/global/elements/<tag>.<ext>` target of a
|
|
18
|
+
* line that carries no source mapping. Undefined when the line is
|
|
20
19
|
* not one of this tag's.
|
|
21
20
|
*/
|
|
22
21
|
function registeredFile(line, tagName) {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* In-place rename of a custom element registration in customElements.js.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Removing the old entry and adding a new one would, on a registration
|
|
5
|
+
* list the remove path cannot see, leave the old entry beside the new one,
|
|
6
|
+
* and the new entry would lack the project marker every sibling line
|
|
7
|
+
* carries (`// <MARKER>:`), so the next lint would flag it as an unmarked
|
|
8
|
+
* engine edit. Rewriting the tag and module
|
|
9
9
|
* literals inside the existing entry keeps the entry's position, layout and
|
|
10
10
|
* trailing marker comments exactly as they were.
|
|
11
11
|
*/
|