@hominis/fireforge 0.45.0 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -0
- package/dist/bin/fireforge.js +6 -0
- package/dist/build-info.json +4 -4
- package/dist/src/cli-usage-error.d.ts +46 -0
- package/dist/src/cli-usage-error.js +81 -0
- package/dist/src/cli.d.ts +5 -1
- package/dist/src/cli.js +20 -21
- package/dist/src/commands/bootstrap.js +1 -2
- package/dist/src/commands/build.js +15 -6
- package/dist/src/commands/destructive-decision.d.ts +19 -0
- package/dist/src/commands/destructive-decision.js +31 -0
- package/dist/src/commands/doctor-check-core.d.ts +0 -6
- package/dist/src/commands/doctor-check-core.js +0 -8
- package/dist/src/commands/doctor-orphaned-harness.d.ts +0 -6
- package/dist/src/commands/doctor-orphaned-harness.js +4 -12
- package/dist/src/commands/doctor-post-rebase-audit.js +1 -1
- package/dist/src/commands/doctor.d.ts +2 -0
- package/dist/src/commands/doctor.js +7 -8
- package/dist/src/commands/download.js +35 -24
- package/dist/src/commands/export-all.js +21 -4
- package/dist/src/commands/export-flow.js +15 -45
- package/dist/src/commands/export-placement-conflicts.js +3 -3
- package/dist/src/commands/export-placement-gate.js +2 -8
- package/dist/src/commands/export-placement-policy.js +3 -5
- package/dist/src/commands/export-shared.d.ts +42 -23
- package/dist/src/commands/export-shared.js +4 -19
- package/dist/src/commands/export.js +10 -1
- package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
- package/dist/src/commands/furnace/authoring-config.js +19 -0
- package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
- package/dist/src/commands/furnace/create-browser-test.js +3 -8
- package/dist/src/commands/furnace/create-dry-run.js +2 -5
- package/dist/src/commands/furnace/create-templates.d.ts +17 -1
- package/dist/src/commands/furnace/create-templates.js +2 -1
- package/dist/src/commands/furnace/create.js +25 -19
- package/dist/src/commands/furnace/deploy.js +11 -2
- package/dist/src/commands/furnace/init.js +2 -1
- package/dist/src/commands/furnace/override.js +28 -19
- package/dist/src/commands/furnace/refresh.js +9 -1
- package/dist/src/commands/furnace/remove-mutations.d.ts +6 -7
- package/dist/src/commands/furnace/remove-mutations.js +0 -1
- package/dist/src/commands/furnace/remove.js +2 -6
- package/dist/src/commands/furnace/rename-browser-test.js +1 -7
- package/dist/src/commands/furnace/rename.js +29 -29
- package/dist/src/commands/furnace/status.js +12 -9
- package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
- package/dist/src/commands/furnace/test-file-name.js +39 -0
- package/dist/src/commands/furnace/validation-output.d.ts +21 -9
- package/dist/src/commands/furnace/validation-output.js +3 -8
- package/dist/src/commands/import.js +2 -9
- package/dist/src/commands/lint-per-patch.d.ts +2 -2
- package/dist/src/commands/lint-per-patch.js +19 -4
- package/dist/src/commands/lint-per-run-checkjs.d.ts +2 -2
- package/dist/src/commands/lint.d.ts +2 -1
- package/dist/src/commands/lint.js +1 -1
- package/dist/src/commands/manifest.js +1 -1
- package/dist/src/commands/package.js +2 -4
- package/dist/src/commands/patch/compact.js +14 -15
- package/dist/src/commands/patch/delete.js +15 -25
- package/dist/src/commands/patch/entry-projection.d.ts +13 -0
- package/dist/src/commands/patch/entry-projection.js +24 -0
- package/dist/src/commands/patch/lint-ignore.d.ts +0 -8
- package/dist/src/commands/patch/lint-ignore.js +19 -29
- package/dist/src/commands/patch/move-files-create.js +24 -10
- package/dist/src/commands/patch/move-files-into.js +38 -34
- package/dist/src/commands/patch/move-files.js +4 -13
- package/dist/src/commands/patch/rename.js +27 -41
- package/dist/src/commands/patch/reorder.js +25 -38
- package/dist/src/commands/patch/split-plan.d.ts +4 -5
- package/dist/src/commands/patch/split-plan.js +4 -8
- package/dist/src/commands/patch/split.js +45 -43
- package/dist/src/commands/patch/staged-dependency.js +16 -22
- package/dist/src/commands/patch/tier.js +15 -25
- package/dist/src/commands/patch-rename-map.d.ts +19 -0
- package/dist/src/commands/patch-rename-map.js +36 -0
- package/dist/src/commands/pending-resolution.d.ts +15 -0
- package/dist/src/commands/pending-resolution.js +25 -0
- package/dist/src/commands/re-export-adjacent.d.ts +24 -2
- package/dist/src/commands/re-export-adjacent.js +32 -16
- package/dist/src/commands/re-export-bulk-scan.js +6 -10
- package/dist/src/commands/re-export-drift.js +4 -4
- package/dist/src/commands/re-export-files.d.ts +2 -3
- package/dist/src/commands/re-export-files.js +38 -29
- package/dist/src/commands/re-export-lint.d.ts +3 -4
- package/dist/src/commands/re-export-lint.js +9 -2
- package/dist/src/commands/re-export-scan.js +5 -2
- package/dist/src/commands/re-export-single.d.ts +22 -3
- package/dist/src/commands/re-export-single.js +19 -11
- package/dist/src/commands/re-export.js +28 -11
- package/dist/src/commands/rebase/abort.js +3 -8
- package/dist/src/commands/rebase/continue.js +12 -12
- package/dist/src/commands/rebase/index.js +2 -2
- package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
- package/dist/src/commands/rebase/patch-loop.js +2 -7
- package/dist/src/commands/resolve.js +18 -10
- package/dist/src/commands/run.d.ts +0 -13
- package/dist/src/commands/run.js +12 -25
- package/dist/src/commands/setup-support.d.ts +16 -4
- package/dist/src/commands/setup-support.js +16 -16
- package/dist/src/commands/setup.js +3 -9
- package/dist/src/commands/source.js +8 -1
- package/dist/src/commands/status-check.js +2 -2
- package/dist/src/commands/status-output.js +2 -2
- package/dist/src/commands/status.js +3 -4
- package/dist/src/commands/test-build-phase.d.ts +2 -2
- package/dist/src/commands/test-build-phase.js +31 -7
- package/dist/src/commands/test-modes.d.ts +2 -2
- package/dist/src/commands/test-modes.js +0 -1
- package/dist/src/commands/test-register.js +5 -3
- package/dist/src/commands/test-run.js +25 -18
- package/dist/src/commands/test-stale-gate.js +4 -4
- package/dist/src/commands/test-verdict.d.ts +16 -2
- package/dist/src/commands/test-verdict.js +18 -3
- package/dist/src/commands/test.js +42 -5
- package/dist/src/commands/token-list.d.ts +26 -0
- package/dist/src/commands/token-list.js +153 -0
- package/dist/src/commands/token.js +17 -1
- package/dist/src/commands/tree.js +7 -2
- package/dist/src/commands/typecheck.js +9 -9
- package/dist/src/commands/wire.js +4 -3
- package/dist/src/core/branding.d.ts +0 -7
- package/dist/src/core/branding.js +3 -2
- package/dist/src/core/browser-wire.js +6 -3
- package/dist/src/core/build-audit-platform.d.ts +29 -0
- package/dist/src/core/build-audit-platform.js +140 -0
- package/dist/src/core/build-audit-registration.js +3 -2
- package/dist/src/core/build-audit-skip.d.ts +57 -0
- package/dist/src/core/build-audit-skip.js +112 -0
- package/dist/src/core/build-audit.d.ts +15 -2
- package/dist/src/core/build-audit.js +110 -10
- package/dist/src/core/build-baseline.d.ts +45 -28
- package/dist/src/core/build-baseline.js +6 -29
- package/dist/src/core/build-jar-escalation.js +3 -2
- package/dist/src/core/build-overwrite-guard.js +1 -1
- package/dist/src/core/build-prepare.d.ts +0 -8
- package/dist/src/core/build-prepare.js +7 -12
- package/dist/src/core/config-paths.d.ts +2 -6
- package/dist/src/core/config-paths.js +4 -2
- package/dist/src/core/config-state.d.ts +0 -6
- package/dist/src/core/config-state.js +1 -19
- package/dist/src/core/config-validate-build-audit.d.ts +8 -0
- package/dist/src/core/config-validate-build-audit.js +80 -0
- package/dist/src/core/config-validate-file-size.js +1 -1
- package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
- package/dist/src/core/config-validate-patch-policy.js +3 -3
- package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
- package/dist/src/core/config-validate-test-toolchains.js +4 -5
- package/dist/src/core/config-validate.js +14 -3
- package/dist/src/core/config.d.ts +2 -2
- package/dist/src/core/config.js +11 -15
- package/dist/src/core/coverage-extend.js +4 -4
- package/dist/src/core/discard-baseline.js +7 -7
- package/dist/src/core/engine-precondition.d.ts +1 -9
- package/dist/src/core/engine-precondition.js +3 -8
- package/dist/src/core/engine-session-lock.js +13 -17
- package/dist/src/core/export-moved-code-shape.js +2 -2
- package/dist/src/core/file-lock-owner.d.ts +103 -0
- package/dist/src/core/file-lock-owner.js +259 -0
- package/dist/src/core/file-lock.d.ts +2 -7
- package/dist/src/core/file-lock.js +40 -91
- package/dist/src/core/firefox-cache.d.ts +13 -1
- package/dist/src/core/firefox-cache.js +59 -34
- package/dist/src/core/firefox-download.d.ts +15 -0
- package/dist/src/core/firefox-download.js +37 -21
- package/dist/src/core/firefox-extract.d.ts +6 -5
- package/dist/src/core/firefox-extract.js +36 -18
- package/dist/src/core/firefox.d.ts +30 -32
- package/dist/src/core/firefox.js +5 -36
- package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
- package/dist/src/core/furnace-apply-dry-run.js +1 -7
- package/dist/src/core/furnace-apply-helpers.d.ts +0 -11
- package/dist/src/core/furnace-apply-helpers.js +8 -19
- package/dist/src/core/furnace-apply-overwrite-warn.js +4 -2
- package/dist/src/core/furnace-apply.d.ts +1 -1
- package/dist/src/core/furnace-apply.js +30 -10
- package/dist/src/core/furnace-config.d.ts +1 -1
- package/dist/src/core/furnace-config.js +23 -36
- package/dist/src/core/furnace-constants.js +3 -2
- package/dist/src/core/furnace-dir-entry.d.ts +20 -0
- package/dist/src/core/furnace-dir-entry.js +21 -0
- package/dist/src/core/furnace-operation.d.ts +2 -0
- package/dist/src/core/furnace-operation.js +3 -1
- package/dist/src/core/furnace-refresh.d.ts +18 -6
- package/dist/src/core/furnace-refresh.js +3 -6
- package/dist/src/core/furnace-registration-ast.d.ts +0 -1
- package/dist/src/core/furnace-registration-ast.js +0 -3
- package/dist/src/core/furnace-registration.d.ts +2 -1
- package/dist/src/core/furnace-registration.js +22 -29
- package/dist/src/core/furnace-scanner.js +3 -6
- package/dist/src/core/furnace-state-persist.d.ts +2 -2
- package/dist/src/core/furnace-validate-registration.js +5 -7
- package/dist/src/core/git-base.d.ts +3 -1
- package/dist/src/core/git-base.js +25 -3
- package/dist/src/core/git-diff-new-file.d.ts +40 -0
- package/dist/src/core/git-diff-new-file.js +119 -0
- package/dist/src/core/git-diff.d.ts +9 -9
- package/dist/src/core/git-diff.js +88 -157
- package/dist/src/core/git-file-ops.d.ts +0 -7
- package/dist/src/core/git-file-ops.js +4 -28
- package/dist/src/core/git-readonly-index.d.ts +0 -2
- package/dist/src/core/git-readonly-index.js +0 -4
- package/dist/src/core/git-status.d.ts +7 -0
- package/dist/src/core/git-status.js +21 -9
- package/dist/src/core/git.d.ts +0 -6
- package/dist/src/core/git.js +14 -43
- package/dist/src/core/harness-orphans.js +8 -7
- package/dist/src/core/history-log.d.ts +19 -0
- package/dist/src/core/history-log.js +29 -0
- package/dist/src/core/mach-error-hints.d.ts +0 -5
- package/dist/src/core/mach-error-hints.js +1 -1
- package/dist/src/core/mach-known-noise-filter.d.ts +4 -3
- package/dist/src/core/mach-known-noise-filter.js +7 -9
- package/dist/src/core/mach-mozconfig.js +2 -1
- package/dist/src/core/mach-python.d.ts +2 -0
- package/dist/src/core/mach-python.js +12 -7
- package/dist/src/core/mach-resource-shim.d.ts +13 -1
- package/dist/src/core/mach-resource-shim.js +112 -9
- package/dist/src/core/mach.d.ts +39 -53
- package/dist/src/core/mach.js +13 -81
- package/dist/src/core/marionette-port.d.ts +0 -17
- package/dist/src/core/marionette-port.js +12 -58
- package/dist/src/core/marionette-preflight.js +9 -27
- package/dist/src/core/mochitest-server-port.js +3 -3
- package/dist/src/core/moz-manifest-rules.js +37 -33
- package/dist/src/core/patch-apply-fuzz.js +4 -4
- package/dist/src/core/patch-apply.d.ts +4 -5
- package/dist/src/core/patch-apply.js +6 -12
- package/dist/src/core/patch-export-coverage.d.ts +0 -9
- package/dist/src/core/patch-export-coverage.js +1 -30
- package/dist/src/core/patch-export-update.d.ts +18 -1
- package/dist/src/core/patch-export-update.js +4 -3
- package/dist/src/core/patch-export.d.ts +11 -21
- package/dist/src/core/patch-export.js +44 -66
- package/dist/src/core/patch-files.d.ts +0 -4
- package/dist/src/core/patch-files.js +0 -11
- package/dist/src/core/patch-lint-binary.d.ts +6 -12
- package/dist/src/core/patch-lint-cache.d.ts +17 -1
- package/dist/src/core/patch-lint-cache.js +3 -6
- package/dist/src/core/patch-lint-checkjs.d.ts +33 -16
- package/dist/src/core/patch-lint-checkjs.js +37 -24
- package/dist/src/core/patch-lint-creators.d.ts +2 -6
- package/dist/src/core/patch-lint-cross.d.ts +16 -8
- package/dist/src/core/patch-lint-cross.js +49 -92
- package/dist/src/core/patch-lint-css.js +10 -6
- package/dist/src/core/patch-lint-file-size.js +3 -3
- package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
- package/dist/src/core/patch-lint-forward-registration.js +210 -0
- package/dist/src/core/patch-lint-jsdoc.js +36 -36
- package/dist/src/core/patch-lint-module-registration.d.ts +2 -8
- package/dist/src/core/patch-lint-prettier.js +1 -1
- package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
- package/dist/src/core/patch-lint-queue-types.js +12 -0
- package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
- package/dist/src/core/patch-lint-staged-registration.js +90 -0
- package/dist/src/core/patch-lint.d.ts +14 -9
- package/dist/src/core/patch-lint.js +4 -3
- package/dist/src/core/patch-lock.js +3 -3
- package/dist/src/core/patch-manifest-io.js +8 -7
- package/dist/src/core/patch-parse.d.ts +19 -8
- package/dist/src/core/patch-parse.js +25 -0
- package/dist/src/core/patch-policy.js +15 -27
- package/dist/src/core/patch-source-metadata.d.ts +1 -3
- package/dist/src/core/patch-source-metadata.js +0 -4
- package/dist/src/core/patch-transform.d.ts +0 -8
- package/dist/src/core/patch-transform.js +0 -10
- package/dist/src/core/process-cpu-time.js +1 -1
- package/dist/src/core/rebase-session.d.ts +2 -8
- package/dist/src/core/rebase-session.js +5 -17
- package/dist/src/core/register-browser-content.js +3 -2
- package/dist/src/core/register-browser-test.js +2 -1
- package/dist/src/core/register-module.js +2 -1
- package/dist/src/core/register-scaffold.js +5 -4
- package/dist/src/core/register-shared-css.js +2 -1
- package/dist/src/core/run-log-redact.d.ts +38 -0
- package/dist/src/core/run-log-redact.js +69 -0
- package/dist/src/core/run-log.d.ts +13 -2
- package/dist/src/core/run-log.js +67 -18
- package/dist/src/core/signal-critical.js +2 -4
- package/dist/src/core/smoke-patterns.d.ts +0 -1
- package/dist/src/core/smoke-patterns.js +6 -11
- package/dist/src/core/state-file.d.ts +12 -1
- package/dist/src/core/state-file.js +14 -1
- package/dist/src/core/status-classify.js +2 -1
- package/dist/src/core/test-changed-prefs.js +26 -11
- package/dist/src/core/test-harness-crash.js +2 -2
- package/dist/src/core/test-stale-check.d.ts +0 -2
- package/dist/src/core/test-stale-check.js +2 -2
- package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
- package/dist/src/core/test-xpcshell-retry.js +14 -11
- package/dist/src/core/token-category.d.ts +33 -0
- package/dist/src/core/token-category.js +56 -48
- package/dist/src/core/token-dark-mode.js +2 -14
- package/dist/src/core/token-inventory.d.ts +56 -0
- package/dist/src/core/token-inventory.js +109 -0
- package/dist/src/core/token-manager.d.ts +1 -1
- package/dist/src/core/token-manager.js +1 -1
- package/dist/src/core/token-variant.js +21 -22
- package/dist/src/core/toolchain-preflight.js +1 -1
- package/dist/src/core/tree-cow.d.ts +0 -2
- package/dist/src/core/tree-cow.js +2 -2
- package/dist/src/core/tree-store.js +4 -5
- package/dist/src/core/typecheck.js +2 -2
- package/dist/src/core/wire-dom-fragment.js +6 -17
- package/dist/src/core/wire-utils.js +5 -17
- package/dist/src/core/xpcshell-appdir.js +2 -4
- package/dist/src/errors/base.d.ts +53 -1
- package/dist/src/errors/base.js +79 -11
- package/dist/src/errors/build.js +20 -28
- package/dist/src/errors/config.d.ts +0 -14
- package/dist/src/errors/config.js +13 -34
- package/dist/src/errors/download.d.ts +13 -0
- package/dist/src/errors/download.js +53 -24
- package/dist/src/errors/furnace.js +8 -9
- package/dist/src/errors/git.d.ts +8 -8
- package/dist/src/errors/git.js +20 -30
- package/dist/src/errors/patch.js +8 -10
- package/dist/src/errors/rebase.js +7 -7
- package/dist/src/types/commands/options.d.ts +77 -123
- package/dist/src/types/config.d.ts +34 -2
- package/dist/src/types/index.d.ts +0 -1
- package/dist/src/utils/assert.d.ts +3 -15
- package/dist/src/utils/assert.js +1 -18
- package/dist/src/utils/build-info.d.ts +1 -1
- package/dist/src/utils/child-env.d.ts +2 -1
- package/dist/src/utils/elapsed.js +10 -1
- package/dist/src/utils/fs.js +8 -13
- package/dist/src/utils/hash.d.ts +12 -0
- package/dist/src/utils/hash.js +16 -0
- package/dist/src/utils/line-dispatch.d.ts +15 -0
- package/dist/src/utils/line-dispatch.js +48 -0
- package/dist/src/utils/options.d.ts +1 -7
- package/dist/src/utils/options.js +6 -7
- package/dist/src/utils/package-root.js +3 -12
- package/dist/src/utils/platform.d.ts +0 -4
- package/dist/src/utils/process-group.d.ts +2 -1
- package/dist/src/utils/process-group.js +20 -19
- package/dist/src/utils/process.d.ts +40 -26
- package/dist/src/utils/process.js +115 -166
- package/dist/src/utils/ps-duration.js +3 -3
- package/dist/src/utils/sleep.d.ts +26 -0
- package/dist/src/utils/sleep.js +31 -0
- package/dist/src/utils/validation.d.ts +2 -38
- package/dist/src/utils/validation.js +7 -16
- package/docs/configuration.md +89 -1
- package/docs/exit-codes.md +31 -19
- package/docs/lifecycle-invariants.md +18 -12
- package/docs/machine-output.md +31 -8
- package/docs/patch-workflow.md +2 -1
- package/docs/run-logs.md +44 -4
- package/docs/testing.md +8 -1
- package/package.json +8 -7
- package/dist/src/commands/rebase.d.ts +0 -4
- package/dist/src/commands/rebase.js +0 -5
- package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
- package/dist/src/core/patch-artifact-normalize.js +0 -24
- package/dist/src/core/patch-lint-reexports.d.ts +0 -7
- package/dist/src/core/patch-lint-reexports.js +0 -8
- package/dist/src/core/wire-targets.d.ts +0 -7
- package/dist/src/core/wire-targets.js +0 -8
|
@@ -6,6 +6,7 @@ import { listTrackedInHead } from '../core/git-file-ops.js';
|
|
|
6
6
|
import { getModifiedFilesInDir, getUntrackedFilesInDir } from '../core/git-status.js';
|
|
7
7
|
import { extractAffectedFiles } from '../core/patch-apply.js';
|
|
8
8
|
import { buildModifiedFileAdditionsFromDiff, buildPatchQueueContext, lintPatchQueue, } from '../core/patch-lint.js';
|
|
9
|
+
import { detectNewFilesInDiff } from '../core/patch-lint-diff.js';
|
|
9
10
|
import { computeProjectedLintRegressions } from '../core/patch-lint-projection.js';
|
|
10
11
|
import { getClaimedFiles } from '../core/patch-manifest.js';
|
|
11
12
|
import { buildNewFileTextProjection } from '../core/patch-transform.js';
|
|
@@ -135,14 +136,14 @@ export async function confirmBroadScanAdditions(args) {
|
|
|
135
136
|
if (isDryRun || yes || !scanAdditionsNeedConfirmation(added))
|
|
136
137
|
return true;
|
|
137
138
|
const dirCount = new Set(added.map((f) => dirname(f))).size;
|
|
138
|
-
warn(`${patchFilename}: --scan would add ${
|
|
139
|
+
warn(`${patchFilename}: --scan would add ${added.length} file(s) that span ${dirCount} director${dirCount === 1 ? 'y' : 'ies'}. ` +
|
|
139
140
|
'Broad scans can silently pull adjacent features into a patch — review the diff before continuing.');
|
|
140
141
|
if (!isInteractive) {
|
|
141
142
|
throw new GeneralError(`Refusing to broaden "${patchFilename}" via --scan in non-interactive mode. ` +
|
|
142
143
|
'Pass --yes to acknowledge the expansion, or run with --dry-run first to review.');
|
|
143
144
|
}
|
|
144
145
|
const confirmed = await confirm({
|
|
145
|
-
message: `Proceed and broaden ${patchFilename} with ${
|
|
146
|
+
message: `Proceed and broaden ${patchFilename} with ${added.length} newly discovered file(s)?`,
|
|
146
147
|
initialValue: false,
|
|
147
148
|
});
|
|
148
149
|
if (isCancel(confirmed) || !confirmed) {
|
|
@@ -177,6 +178,7 @@ export async function assertScanAdoptionsHaveNoForwardImports(args) {
|
|
|
177
178
|
if (!candidateDiff.trim())
|
|
178
179
|
return;
|
|
179
180
|
const candidateNewFiles = buildNewFileTextProjection(candidateDiff);
|
|
181
|
+
const candidateCreations = detectNewFilesInDiff(candidateDiff);
|
|
180
182
|
const candidateAdditions = buildModifiedFileAdditionsFromDiff(candidateDiff);
|
|
181
183
|
const baseCtx = await buildPatchQueueContext(patchesDir);
|
|
182
184
|
const projectedEntries = baseCtx.entries.map((entry) => {
|
|
@@ -186,6 +188,7 @@ export async function assertScanAdoptionsHaveNoForwardImports(args) {
|
|
|
186
188
|
...entry,
|
|
187
189
|
diff: `${entry.diff}\n${candidateDiff}`,
|
|
188
190
|
newFiles: new Map([...entry.newFiles, ...candidateNewFiles]),
|
|
191
|
+
createdFiles: new Set([...entry.createdFiles, ...candidateCreations]),
|
|
189
192
|
modifiedFileAdditions: new Map([...entry.modifiedFileAdditions, ...candidateAdditions]),
|
|
190
193
|
};
|
|
191
194
|
});
|
|
@@ -4,12 +4,31 @@
|
|
|
4
4
|
* loop calls {@link reExportSinglePatchWithIndexLockRetry} per selected
|
|
5
5
|
* patch.
|
|
6
6
|
*/
|
|
7
|
-
import { getProjectPaths } from '../core/config.js';
|
|
8
7
|
import type { PatchesManifest, PatchMetadata, ReExportOptions } from '../types/commands/index.js';
|
|
9
|
-
import type { FireForgeConfig } from '../types/config.js';
|
|
8
|
+
import type { FireForgeConfig, ProjectPaths } from '../types/config.js';
|
|
10
9
|
import type { AdjacentUnmanagedContext } from './re-export-adjacent.js';
|
|
11
10
|
import type { ForeignDriftContext } from './re-export-drift.js';
|
|
12
11
|
import { type ReExportLintContext } from './re-export-lint.js';
|
|
12
|
+
export interface ReExportSinglePatchArgs {
|
|
13
|
+
/** Manifest row for the patch being refreshed. */
|
|
14
|
+
patch: PatchMetadata;
|
|
15
|
+
/** Resolved project paths. */
|
|
16
|
+
paths: ProjectPaths;
|
|
17
|
+
/** Patch queue manifest as loaded by the orchestrator. */
|
|
18
|
+
manifest: PatchesManifest;
|
|
19
|
+
/** Effective re-export options for this patch. */
|
|
20
|
+
options: ReExportOptions;
|
|
21
|
+
/** Whether the run is a dry run. */
|
|
22
|
+
isDryRun: boolean;
|
|
23
|
+
/** Project configuration. */
|
|
24
|
+
config: FireForgeConfig;
|
|
25
|
+
/** Adjacent-unmanaged-file reporting context. */
|
|
26
|
+
adjacentCtx: AdjacentUnmanagedContext;
|
|
27
|
+
/** Foreign-drift reporting context. */
|
|
28
|
+
driftCtx: ForeignDriftContext;
|
|
29
|
+
/** Per-patch lint context, including the shared lint cache. */
|
|
30
|
+
lintCtx: ReExportLintContext;
|
|
31
|
+
}
|
|
13
32
|
/**
|
|
14
33
|
* Retries a single patch re-export exactly once when the failure is
|
|
15
34
|
* transient git `index.lock` contention (an external git process holding
|
|
@@ -17,4 +36,4 @@ import { type ReExportLintContext } from './re-export-lint.js';
|
|
|
17
36
|
* from the top, so it re-reads clean state; a second lock failure
|
|
18
37
|
* propagates to the loop's honest "N of M" accounting.
|
|
19
38
|
*/
|
|
20
|
-
export declare function reExportSinglePatchWithIndexLockRetry(
|
|
39
|
+
export declare function reExportSinglePatchWithIndexLockRetry(args: ReExportSinglePatchArgs): Promise<boolean>;
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* loop calls {@link reExportSinglePatchWithIndexLockRetry} per selected
|
|
6
6
|
* patch.
|
|
7
7
|
*/
|
|
8
|
-
import { getProjectPaths } from '../core/config.js';
|
|
9
8
|
import { stdioIsInteractive } from '../core/destructive.js';
|
|
10
9
|
import { enforceFreshFurnaceSources } from '../core/furnace-stale-export.js';
|
|
11
10
|
import { getDiffForFilesAgainstHead } from '../core/git-diff.js';
|
|
@@ -14,6 +13,7 @@ import { updatePatchAndMetadata } from '../core/patch-export.js';
|
|
|
14
13
|
import { buildProjectedManifest, enforcePatchPolicy } from '../core/patch-policy.js';
|
|
15
14
|
import { isGitIndexLockConflict } from '../errors/git.js';
|
|
16
15
|
import { info, success, warn } from '../utils/logger.js';
|
|
16
|
+
import { sleep } from '../utils/sleep.js';
|
|
17
17
|
import { findMissingFiles, reportAdjacentUnmanagedFiles } from './re-export-adjacent.js';
|
|
18
18
|
import { reportForeignDrift } from './re-export-drift.js';
|
|
19
19
|
import { lintReExportedPatch, refreshQueueCtxEntry, storeReExportLintResult, } from './re-export-lint.js';
|
|
@@ -26,20 +26,21 @@ const GIT_INDEX_LOCK_RETRY_MS = 300;
|
|
|
26
26
|
* from the top, so it re-reads clean state; a second lock failure
|
|
27
27
|
* propagates to the loop's honest "N of M" accounting.
|
|
28
28
|
*/
|
|
29
|
-
export async function reExportSinglePatchWithIndexLockRetry(
|
|
29
|
+
export async function reExportSinglePatchWithIndexLockRetry(args) {
|
|
30
30
|
try {
|
|
31
|
-
return await reExportSinglePatch(
|
|
31
|
+
return await reExportSinglePatch(args);
|
|
32
32
|
}
|
|
33
33
|
catch (error) {
|
|
34
34
|
if (!isGitIndexLockConflict(error)) {
|
|
35
35
|
throw error;
|
|
36
36
|
}
|
|
37
|
-
warn(`${patch.filename}: git index.lock contention detected — retrying once after ${
|
|
38
|
-
await
|
|
39
|
-
return await reExportSinglePatch(
|
|
37
|
+
warn(`${args.patch.filename}: git index.lock contention detected — retrying once after ${GIT_INDEX_LOCK_RETRY_MS} ms...`);
|
|
38
|
+
await sleep(GIT_INDEX_LOCK_RETRY_MS);
|
|
39
|
+
return await reExportSinglePatch(args);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
async function reExportSinglePatch(
|
|
42
|
+
async function reExportSinglePatch(args) {
|
|
43
|
+
const { patch, paths, manifest, options, isDryRun, config, adjacentCtx, driftCtx, lintCtx } = args;
|
|
43
44
|
let currentFilesAffected = [...patch.filesAffected];
|
|
44
45
|
// --- Scan for new/removed files ---
|
|
45
46
|
if (options.scan) {
|
|
@@ -219,10 +220,17 @@ async function reExportSinglePatch(patch, paths, manifest, options, isDryRun, co
|
|
|
219
220
|
*/
|
|
220
221
|
async function commitRefreshedPatch(args) {
|
|
221
222
|
const { patch, paths, manifest, options, config, lintCtx, diffContent, updates } = args;
|
|
222
|
-
const bodyChanged = await updatePatchAndMetadata(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
223
|
+
const bodyChanged = await updatePatchAndMetadata({
|
|
224
|
+
patchesDir: paths.patches,
|
|
225
|
+
filename: patch.filename,
|
|
226
|
+
newContent: diffContent,
|
|
227
|
+
updates,
|
|
228
|
+
onCommitted: undefined,
|
|
229
|
+
policyGate: {
|
|
230
|
+
config,
|
|
231
|
+
command: 're-export',
|
|
232
|
+
forceUnsafe: options.forceUnsafe === true,
|
|
233
|
+
},
|
|
226
234
|
});
|
|
227
235
|
refreshQueueCtxEntry(lintCtx, patch.filename, diffContent);
|
|
228
236
|
if (args.lintResult !== null) {
|
|
@@ -9,7 +9,7 @@ import { loadPatchesManifest, resolvePatchIdentifier, stampPatchVersions, } from
|
|
|
9
9
|
import { GeneralError, InvalidArgumentError } from '../errors/base.js';
|
|
10
10
|
import { elapsedSince } from '../utils/elapsed.js';
|
|
11
11
|
import { toError } from '../utils/errors.js';
|
|
12
|
-
import { cancel, info, intro, isCancel, outro, spinner, success, warn } from '../utils/logger.js';
|
|
12
|
+
import { cancel, info, intro, isCancel, outro, spinner, success, warn, } from '../utils/logger.js';
|
|
13
13
|
import { loadScanFilesAssignments, withDryRunPurityGuard, withDryRunReExportLock, } from './re-export-bulk-scan.js';
|
|
14
14
|
import { warnUnseenExpectedDrift } from './re-export-drift.js';
|
|
15
15
|
import { reExportFilesInPlace } from './re-export-files.js';
|
|
@@ -143,7 +143,17 @@ export async function reExportCommand(projectRoot, patches, options) {
|
|
|
143
143
|
const patchOptions = assignedScanFiles !== undefined ? { ...options, scanFiles: assignedScanFiles } : options;
|
|
144
144
|
progress.message(`Re-exporting ${index + 1}/${selectedPatches.length}: ${patch.filename} (${patch.filesAffected.length} file(s), ${elapsedSince(startedAt)} elapsed)...`);
|
|
145
145
|
try {
|
|
146
|
-
const exported = await reExportSinglePatchWithIndexLockRetry(
|
|
146
|
+
const exported = await reExportSinglePatchWithIndexLockRetry({
|
|
147
|
+
patch,
|
|
148
|
+
paths,
|
|
149
|
+
manifest,
|
|
150
|
+
options: patchOptions,
|
|
151
|
+
isDryRun,
|
|
152
|
+
config,
|
|
153
|
+
adjacentCtx,
|
|
154
|
+
driftCtx,
|
|
155
|
+
lintCtx,
|
|
156
|
+
});
|
|
147
157
|
if (exported) {
|
|
148
158
|
reExported++;
|
|
149
159
|
reExportedFilenames.push(patch.filename);
|
|
@@ -227,19 +237,26 @@ function warnUnseenApprovedUnmanaged(adjacentCtx) {
|
|
|
227
237
|
function throwRunLevelRefusals(adjacentCtx, driftCtx, progress) {
|
|
228
238
|
if (adjacentCtx.refusals.length > 0) {
|
|
229
239
|
progress.error('Re-export refused');
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
240
|
+
// Each offender is named with the owned directory that made it
|
|
241
|
+
// adjacent: an unattended run reads only this line, and without the
|
|
242
|
+
// anchor it cannot tell which of a multi-directory patch's locations
|
|
243
|
+
// the file sits beside.
|
|
244
|
+
const offenders = adjacentCtx.refusals
|
|
245
|
+
.map((r) => `${r.patchFilename} (${r.anchored.join(', ')})`)
|
|
246
|
+
.join('; ');
|
|
247
|
+
throw new GeneralError(`Refused ${adjacentCtx.refusals.length} patch(es) with adjacent unmanaged files ` +
|
|
248
|
+
`(--refuse-adjacent-unmanaged): ${offenders}. Adjacent means the file sits in a ` +
|
|
249
|
+
`directory the patch already owns a file in. Adopt reviewed files with ` +
|
|
250
|
+
`--scan --scan-file, or export them as a new patch.`);
|
|
234
251
|
}
|
|
235
252
|
if (driftCtx.refusals.length > 0) {
|
|
236
253
|
progress.error('Re-export refused');
|
|
237
254
|
const names = driftCtx.refusals.map((r) => r.patchFilename).join(', ');
|
|
238
255
|
const unreadable = driftCtx.refusals.filter((r) => r.reason === 'baseline-unreadable');
|
|
239
256
|
const unreadablePart = unreadable.length > 0
|
|
240
|
-
? ` ${
|
|
257
|
+
? ` ${unreadable.length} of these refused because the old patch body was missing or unreadable, so the drift comparison could not run (fail-closed).`
|
|
241
258
|
: '';
|
|
242
|
-
throw new GeneralError(`Refused ${
|
|
259
|
+
throw new GeneralError(`Refused ${driftCtx.refusals.length} patch(es) whose refreshed body would absorb ` +
|
|
243
260
|
`engine edits not present in the old patch body (--refuse-foreign-drift): ${names}.${unreadablePart}\n\n` +
|
|
244
261
|
'The flag cannot tell WHO wrote those lines, and the two populations have opposite ' +
|
|
245
262
|
'remedies:\n' +
|
|
@@ -283,9 +300,9 @@ function reportReExportOutcome(args) {
|
|
|
283
300
|
if (reExported < selectedCount) {
|
|
284
301
|
const skippedCount = selectedCount - reExported - args.failedFilenames.length;
|
|
285
302
|
const failedPart = args.failedFilenames.length > 0 ? ` Failed: ${args.failedFilenames.join(', ')}.` : '';
|
|
286
|
-
const skippedPart = skippedCount > 0 ? ` Skipped: ${
|
|
287
|
-
throw new GeneralError(`${isDryRun ? '[dry-run] Would re-export' : 'Re-exported'} only ${
|
|
288
|
-
`${
|
|
303
|
+
const skippedPart = skippedCount > 0 ? ` Skipped: ${skippedCount} patch(es) (see warnings above).` : '';
|
|
304
|
+
throw new GeneralError(`${isDryRun ? '[dry-run] Would re-export' : 'Re-exported'} only ${reExported} of ` +
|
|
305
|
+
`${selectedCount} selected patch(es).${failedPart}${skippedPart} ` +
|
|
289
306
|
'Partial re-export exits non-zero so it cannot pass as success in scripts; ' +
|
|
290
307
|
'the per-patch errors are listed above.');
|
|
291
308
|
}
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Rebase abort flow.
|
|
4
4
|
*/
|
|
5
|
-
import { getProjectPaths
|
|
5
|
+
import { getProjectPaths } from '../../core/config.js';
|
|
6
6
|
import { clearAppliedFurnaceState } from '../../core/furnace-config.js';
|
|
7
7
|
import { resetChanges } from '../../core/git.js';
|
|
8
8
|
import { clearRebaseSession, getRebaseSessionPath, readRebaseSession, } from '../../core/rebase-session.js';
|
|
9
9
|
import { NoRebaseSessionError } from '../../errors/rebase.js';
|
|
10
10
|
import { intro, outro, spinner, success, warn } from '../../utils/logger.js';
|
|
11
|
+
import { clearPendingResolution } from '../pending-resolution.js';
|
|
11
12
|
import { confirmDirtyEngineReset } from './confirm.js';
|
|
12
13
|
/**
|
|
13
14
|
* Handles `fireforge rebase --abort`.
|
|
@@ -57,13 +58,7 @@ export async function handleAbort(projectRoot, yes) {
|
|
|
57
58
|
// idempotently re-clear furnace state.
|
|
58
59
|
await clearAppliedFurnaceState(projectRoot);
|
|
59
60
|
// Step 3: clear pending resolution transactionally.
|
|
60
|
-
await
|
|
61
|
-
if (!current.pendingResolution)
|
|
62
|
-
return current;
|
|
63
|
-
const next = { ...current };
|
|
64
|
-
delete next.pendingResolution;
|
|
65
|
-
return next;
|
|
66
|
-
});
|
|
61
|
+
await clearPendingResolution(projectRoot);
|
|
67
62
|
// Step 4: clear the rebase session LAST so a failure in any prior step
|
|
68
63
|
// preserves the session on disk and a retry of --abort can succeed.
|
|
69
64
|
await clearRebaseSession(projectRoot);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Rebase --continue flow.
|
|
4
4
|
*/
|
|
5
5
|
import { join } from 'node:path';
|
|
6
|
-
import { getProjectPaths
|
|
6
|
+
import { getProjectPaths } from '../../core/config.js';
|
|
7
7
|
import { getStagedDiffForFiles } from '../../core/git-diff.js';
|
|
8
8
|
import { stageFiles, unstageFiles } from '../../core/git-file-ops.js';
|
|
9
9
|
import { updatePatchAndMetadata } from '../../core/patch-export.js';
|
|
@@ -14,6 +14,7 @@ import { GeneralError } from '../../errors/base.js';
|
|
|
14
14
|
import { CorruptRebaseSessionError, NoRebaseSessionError, RebaseError, } from '../../errors/rebase.js';
|
|
15
15
|
import { pathExists } from '../../utils/fs.js';
|
|
16
16
|
import { info, intro, success, warn } from '../../utils/logger.js';
|
|
17
|
+
import { clearPendingResolution } from '../pending-resolution.js';
|
|
17
18
|
import { runPatchLoop } from './patch-loop.js';
|
|
18
19
|
/**
|
|
19
20
|
* Handles `fireforge rebase --continue`.
|
|
@@ -77,10 +78,15 @@ export async function handleContinue(projectRoot, maxFuzz, waitLockSeconds) {
|
|
|
77
78
|
// updatePatchMetadata sequence can interleave with a concurrent export /
|
|
78
79
|
// re-export / patch reorder / patch compact and leave the manifest
|
|
79
80
|
// disagreeing with the freshly-written patch body. Mirrors resolve.ts.
|
|
80
|
-
await updatePatchAndMetadata(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
await updatePatchAndMetadata({
|
|
82
|
+
patchesDir: paths.patches,
|
|
83
|
+
filename: currentPatch.filename,
|
|
84
|
+
newContent: diffContent,
|
|
85
|
+
updates: {
|
|
86
|
+
sourceEsrVersion: session.toVersion,
|
|
87
|
+
sourceVersion: session.toVersion,
|
|
88
|
+
...(session.toProduct !== undefined ? { sourceProduct: session.toProduct } : {}),
|
|
89
|
+
},
|
|
84
90
|
});
|
|
85
91
|
}
|
|
86
92
|
finally {
|
|
@@ -104,13 +110,7 @@ export async function handleContinue(projectRoot, maxFuzz, waitLockSeconds) {
|
|
|
104
110
|
await saveRebaseSession(projectRoot, session);
|
|
105
111
|
// Clear pending resolution transactionally so concurrent state-file
|
|
106
112
|
// writes to unrelated keys are not clobbered by a stale reload.
|
|
107
|
-
await
|
|
108
|
-
if (!current.pendingResolution)
|
|
109
|
-
return current;
|
|
110
|
-
const next = { ...current };
|
|
111
|
-
delete next.pendingResolution;
|
|
112
|
-
return next;
|
|
113
|
-
});
|
|
113
|
+
await clearPendingResolution(projectRoot);
|
|
114
114
|
});
|
|
115
115
|
success(`Resolved ${currentPatch.filename}`);
|
|
116
116
|
// Continue applying remaining patches
|
|
@@ -17,7 +17,7 @@ import { clearAppliedFurnaceState } from '../../core/furnace-config.js';
|
|
|
17
17
|
import { getHead, resetChanges } from '../../core/git.js';
|
|
18
18
|
import { discoverPatches } from '../../core/patch-files.js';
|
|
19
19
|
import { loadPatchesManifest } from '../../core/patch-manifest.js';
|
|
20
|
-
import {
|
|
20
|
+
import { getPatchSourceVersion } from '../../core/patch-source-metadata.js';
|
|
21
21
|
import { getRebaseSessionPath, readRebaseSession, saveRebaseSession, } from '../../core/rebase-session.js';
|
|
22
22
|
import { GeneralError } from '../../errors/base.js';
|
|
23
23
|
import { CorruptRebaseSessionError, RebaseSessionExistsError } from '../../errors/rebase.js';
|
|
@@ -59,7 +59,7 @@ async function handleFreshStart(projectRoot, options) {
|
|
|
59
59
|
}
|
|
60
60
|
// Determine the "from" version from the patches
|
|
61
61
|
const patchVersions = new Set(manifest.patches.map((p) => getPatchSourceVersion(p)));
|
|
62
|
-
const patchProducts = new Set(manifest.patches.map((p) =>
|
|
62
|
+
const patchProducts = new Set(manifest.patches.map((p) => p.sourceProduct).filter(Boolean));
|
|
63
63
|
const sortedVersions = [...patchVersions].sort();
|
|
64
64
|
const fromVersion = sortedVersions[0] ?? currentVersion;
|
|
65
65
|
const fromProduct = [...patchProducts].sort()[0] ?? config.firefox.product;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Patch application loop and re-export flow.
|
|
3
3
|
*/
|
|
4
|
-
import type { getProjectPaths } from '../../core/config.js';
|
|
5
4
|
import type { RebaseSession } from '../../core/rebase-session.js';
|
|
5
|
+
import type { ProjectPaths } from '../../types/config.js';
|
|
6
6
|
/**
|
|
7
7
|
* Runs the patch application loop, re-exports applied patches, and stamps versions.
|
|
8
8
|
*
|
|
@@ -12,4 +12,4 @@ import type { RebaseSession } from '../../core/rebase-session.js';
|
|
|
12
12
|
* @param maxFuzz - Maximum context-reduction steps for `git apply -C<n>`
|
|
13
13
|
* @param waitLockSeconds - `--wait-lock` budget for the patch-directory lock
|
|
14
14
|
*/
|
|
15
|
-
export declare function runPatchLoop(projectRoot: string, session: RebaseSession, paths:
|
|
15
|
+
export declare function runPatchLoop(projectRoot: string, session: RebaseSession, paths: ProjectPaths, maxFuzz: number, waitLockSeconds?: number): Promise<void>;
|
|
@@ -20,6 +20,7 @@ import { toError } from '../../utils/errors.js';
|
|
|
20
20
|
import { pathExists } from '../../utils/fs.js';
|
|
21
21
|
import { error, info, outro, spinner, success, warn } from '../../utils/logger.js';
|
|
22
22
|
import { isValidFirefoxVersion } from '../../utils/validation.js';
|
|
23
|
+
import { clearPendingResolution } from '../pending-resolution.js';
|
|
23
24
|
import { buildRebaseConflictSummary } from './conflict-summary.js';
|
|
24
25
|
import { printSummary } from './summary.js';
|
|
25
26
|
/**
|
|
@@ -208,13 +209,7 @@ export async function runPatchLoop(projectRoot, session, paths, maxFuzz, waitLoc
|
|
|
208
209
|
await clearRebaseSession(projectRoot);
|
|
209
210
|
// Clear pending resolution if any (transactionally, so a concurrent
|
|
210
211
|
// state write to an unrelated key is not clobbered by a stale reload).
|
|
211
|
-
await
|
|
212
|
-
if (!current.pendingResolution)
|
|
213
|
-
return current;
|
|
214
|
-
const next = { ...current };
|
|
215
|
-
delete next.pendingResolution;
|
|
216
|
-
return next;
|
|
217
|
-
});
|
|
212
|
+
await clearPendingResolution(projectRoot);
|
|
218
213
|
info('');
|
|
219
214
|
success(`All patches re-exported with sourceVersion=${session.toVersion}`);
|
|
220
215
|
outro('Rebase complete!');
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { confirm } from '@clack/prompts';
|
|
4
4
|
import { Command } from 'commander';
|
|
5
|
-
import { getProjectPaths, loadConfig, loadState
|
|
5
|
+
import { getProjectPaths, loadConfig, loadState } from '../core/config.js';
|
|
6
6
|
import { stdioIsInteractive } from '../core/destructive.js';
|
|
7
7
|
import { assertEngineGitReady } from '../core/engine-precondition.js';
|
|
8
8
|
import { getStagedDiffForFiles } from '../core/git-diff.js';
|
|
@@ -16,6 +16,7 @@ import { toError } from '../utils/errors.js';
|
|
|
16
16
|
import { pathExists } from '../utils/fs.js';
|
|
17
17
|
import { error as logError, info, intro, isCancel, outro, spinner, success, } from '../utils/logger.js';
|
|
18
18
|
import { addWaitLockOption, resolveWaitLockSeconds } from '../utils/options.js';
|
|
19
|
+
import { clearPendingResolution } from './pending-resolution.js';
|
|
19
20
|
/**
|
|
20
21
|
* Runs the resolve command to fix broken patches.
|
|
21
22
|
* @param projectRoot - Root directory of the project
|
|
@@ -123,20 +124,27 @@ export async function resolveCommand(projectRoot, options = {}) {
|
|
|
123
124
|
// here keeps resolve in agreement with every other writer.
|
|
124
125
|
const diffFilesAffected = extractAffectedFiles(diffContent);
|
|
125
126
|
const config = await loadConfig(projectRoot);
|
|
126
|
-
await updatePatchAndMetadata(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
await updatePatchAndMetadata({
|
|
128
|
+
patchesDir: paths.patches,
|
|
129
|
+
filename: patchFilename,
|
|
130
|
+
newContent: diffContent,
|
|
131
|
+
updates: {
|
|
132
|
+
filesAffected: diffFilesAffected,
|
|
133
|
+
...buildPatchSourceMetadata(config.firefox),
|
|
134
|
+
},
|
|
135
|
+
onCommitted: undefined,
|
|
136
|
+
policyGate: undefined,
|
|
137
|
+
lockOptions: {
|
|
138
|
+
waitLockSeconds: resolveWaitLockSeconds(options.waitLock),
|
|
139
|
+
command: 'resolve',
|
|
140
|
+
},
|
|
141
|
+
});
|
|
130
142
|
// Clear pendingResolution from state.json transactionally so a
|
|
131
143
|
// concurrent update to unrelated keys (another command writing buildMode
|
|
132
144
|
// or baseCommit between our load and save) is not clobbered. The updater
|
|
133
145
|
// runs inside the state-file lock with the freshest on-disk state, so
|
|
134
146
|
// only pendingResolution is affected.
|
|
135
|
-
await
|
|
136
|
-
const next = { ...current };
|
|
137
|
-
delete next.pendingResolution;
|
|
138
|
-
return next;
|
|
139
|
-
});
|
|
147
|
+
await clearPendingResolution(projectRoot);
|
|
140
148
|
s.stop(`Updated ${patchFilename}`);
|
|
141
149
|
success('Patch updated successfully and resolution state cleared.');
|
|
142
150
|
info('Patch updated. Run "fireforge import" next to resume the queue from this point — resolve only refreshes the one broken patch, it does not continue applying the remaining patches itself.');
|
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import type { CommandContext } from '../types/cli.js';
|
|
3
3
|
import type { RunOptions } from '../types/commands/index.js';
|
|
4
|
-
/**
|
|
5
|
-
* Exit code returned by smoke-run mode when the captured console stream
|
|
6
|
-
* produced one or more error lines that did NOT match the operator's
|
|
7
|
-
* allowlist.
|
|
8
|
-
*/
|
|
9
|
-
export declare const SMOKE_EXIT_FAILURE: 12;
|
|
10
|
-
/**
|
|
11
|
-
* Exit code returned by smoke-run mode when the browser itself exited
|
|
12
|
-
* with a non-clean status before the smoke window elapsed — i.e. a
|
|
13
|
-
* launch-side failure we could NOT observe as a console error line
|
|
14
|
-
* (crash before console wiring, missing profile, etc.).
|
|
15
|
-
*/
|
|
16
|
-
export declare const SMOKE_LAUNCH_FAILURE: 13;
|
|
17
4
|
/**
|
|
18
5
|
* Runs the run command to launch the built browser.
|
|
19
6
|
* @param projectRoot - Root directory of the project
|
package/dist/src/commands/run.js
CHANGED
|
@@ -17,19 +17,6 @@ import { toError } from '../utils/errors.js';
|
|
|
17
17
|
import { pathExists, removeDir, removeFile } from '../utils/fs.js';
|
|
18
18
|
import { info, intro, verbose, warn } from '../utils/logger.js';
|
|
19
19
|
import { commanderArgParser, pickDefined, stringListOption } from '../utils/options.js';
|
|
20
|
-
/**
|
|
21
|
-
* Exit code returned by smoke-run mode when the captured console stream
|
|
22
|
-
* produced one or more error lines that did NOT match the operator's
|
|
23
|
-
* allowlist.
|
|
24
|
-
*/
|
|
25
|
-
export const SMOKE_EXIT_FAILURE = ExitCode.SMOKE_EXIT_FAILURE;
|
|
26
|
-
/**
|
|
27
|
-
* Exit code returned by smoke-run mode when the browser itself exited
|
|
28
|
-
* with a non-clean status before the smoke window elapsed — i.e. a
|
|
29
|
-
* launch-side failure we could NOT observe as a console error line
|
|
30
|
-
* (crash before console wiring, missing profile, etc.).
|
|
31
|
-
*/
|
|
32
|
-
export const SMOKE_LAUNCH_FAILURE = ExitCode.SMOKE_LAUNCH_FAILURE;
|
|
33
20
|
/** Recommendation surfaced when the smoke window is shorter than a typical cold start. */
|
|
34
21
|
const SMOKE_COLD_START_THRESHOLD_MS = 30_000;
|
|
35
22
|
/** Maximum number of unallowed error lines to surface in the terminal summary. */
|
|
@@ -157,7 +144,7 @@ async function runSmokeExit(engineDir, options) {
|
|
|
157
144
|
// Not an error — cold starts just tend to exceed the window. Surfacing
|
|
158
145
|
// the hint here instead of failing lets agents run shorter windows
|
|
159
146
|
// intentionally (e.g. warm-cache smoke checks).
|
|
160
|
-
verbose(`Smoke window is ${
|
|
147
|
+
verbose(`Smoke window is ${smokeExit}s; cold starts on slow machines often exceed 30s.`);
|
|
161
148
|
}
|
|
162
149
|
const allowlist = await buildAllowlist(options);
|
|
163
150
|
const captureStream = options.captureConsole
|
|
@@ -216,7 +203,7 @@ async function runSmokeExit(engineDir, options) {
|
|
|
216
203
|
'console capture and can fail the run. Pass --headless (or run on an unattended host) ' +
|
|
217
204
|
'for reliable smoke checks.');
|
|
218
205
|
}
|
|
219
|
-
info(`Launching browser (smoke-exit after ${
|
|
206
|
+
info(`Launching browser (smoke-exit after ${smokeExit}s)...\n`);
|
|
220
207
|
const startedAt = Date.now();
|
|
221
208
|
let result;
|
|
222
209
|
try {
|
|
@@ -248,7 +235,7 @@ async function runSmokeExit(engineDir, options) {
|
|
|
248
235
|
});
|
|
249
236
|
// Exit contract (precedence: unallowed errors dominate timed-out).
|
|
250
237
|
if (findings.length > 0) {
|
|
251
|
-
throw new SmokeRunError(`Smoke run observed ${
|
|
238
|
+
throw new SmokeRunError(`Smoke run observed ${findings.length} unallowed console error(s).`, ExitCode.SMOKE_EXIT_FAILURE);
|
|
252
239
|
}
|
|
253
240
|
if (result.timedOut) {
|
|
254
241
|
// Clean window — SIGTERM from us. Treat as success.
|
|
@@ -257,7 +244,7 @@ async function runSmokeExit(engineDir, options) {
|
|
|
257
244
|
if (result.exitCode === 0 || result.exitCode === 130 || result.exitCode === 143) {
|
|
258
245
|
return;
|
|
259
246
|
}
|
|
260
|
-
throw new SmokeRunError(`Browser exited with code ${
|
|
247
|
+
throw new SmokeRunError(`Browser exited with code ${result.exitCode} before smoke-exit window elapsed.`, ExitCode.SMOKE_LAUNCH_FAILURE);
|
|
261
248
|
}
|
|
262
249
|
/**
|
|
263
250
|
* Compiles the active allowlist from `--console-allow` CLI values and
|
|
@@ -298,15 +285,15 @@ function reportSmokeSummary(args) {
|
|
|
298
285
|
const suffix = args.timedOut ? ' (deadline fired — SIGTERM sent to process group)' : '';
|
|
299
286
|
info('');
|
|
300
287
|
info(`Smoke run complete: ${seconds}s elapsed of ${windowSeconds}s window${suffix}`);
|
|
301
|
-
info(` Unallowed errors: ${
|
|
288
|
+
info(` Unallowed errors: ${args.findings.length}`);
|
|
302
289
|
// The "suppressed errors" count is what the exit contract cares about — the
|
|
303
290
|
// subset of allowlisted hits that would otherwise have been tallied as
|
|
304
291
|
// findings. The "all allowlisted lines" count answers the operator's mental
|
|
305
292
|
// model ("my --console-allow pattern matched N console lines"), without
|
|
306
293
|
// which a visibly matching regex reports 0 hits.
|
|
307
|
-
info(` Allowlisted error hits (suppressed): ${
|
|
308
|
-
info(` Allowlisted lines total: ${
|
|
309
|
-
info(` Child exit code: ${
|
|
294
|
+
info(` Allowlisted error hits (suppressed): ${args.allowlistedErrorHits}`);
|
|
295
|
+
info(` Allowlisted lines total: ${args.allowlistedTotalHits}`);
|
|
296
|
+
info(` Child exit code: ${args.exitCode}`);
|
|
310
297
|
// Per-entry attribution: first-match credit per line, with
|
|
311
298
|
// zero-hit entries always visible — an allowlist entry whose suppressed
|
|
312
299
|
// shape changed upstream is only detectable as a 0× row.
|
|
@@ -315,19 +302,19 @@ function reportSmokeSummary(args) {
|
|
|
315
302
|
args.allowlist.forEach((entry, index) => {
|
|
316
303
|
const hits = args.allowlistHits[index] ?? 0;
|
|
317
304
|
const zeroSuffix = hits === 0 ? ' (never matched — candidate for removal)' : '';
|
|
318
|
-
info(` ${
|
|
305
|
+
info(` ${hits}× ${entry.origin} ${entry.source}${zeroSuffix}`);
|
|
319
306
|
});
|
|
320
307
|
}
|
|
321
308
|
if (args.findings.length === 0)
|
|
322
309
|
return;
|
|
323
310
|
warn('');
|
|
324
|
-
warn(`Unallowed console errors (first ${
|
|
311
|
+
warn(`Unallowed console errors (first ${SMOKE_UNALLOWED_PREVIEW_MAX}):`);
|
|
325
312
|
args.findings.slice(0, SMOKE_UNALLOWED_PREVIEW_MAX).forEach((finding, index) => {
|
|
326
|
-
warn(` ${
|
|
313
|
+
warn(` ${index + 1}. [${finding.stream}] ${finding.line}`);
|
|
327
314
|
});
|
|
328
315
|
if (args.findings.length > SMOKE_UNALLOWED_PREVIEW_MAX) {
|
|
329
316
|
const remaining = args.findings.length - SMOKE_UNALLOWED_PREVIEW_MAX;
|
|
330
|
-
warn(` …and ${
|
|
317
|
+
warn(` …and ${remaining} more.`);
|
|
331
318
|
}
|
|
332
319
|
}
|
|
333
320
|
/** Registers the run command on the CLI program. */
|
|
@@ -9,10 +9,22 @@ export interface ResolvedSetupInputs {
|
|
|
9
9
|
finalProduct: FirefoxProduct;
|
|
10
10
|
finalLicense: ProjectLicense;
|
|
11
11
|
}
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Narrows a raw CLI or prompt value to a supported Firefox product.
|
|
14
|
+
* @param value - Raw value from `--product` or the setup prompt.
|
|
15
|
+
* @param field - Field name quoted in the refusal (e.g. `--product`).
|
|
16
|
+
* @returns The validated product.
|
|
17
|
+
* @throws {@link InvalidArgumentError} when the value names no supported product.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveFirefoxProduct(value: unknown, field: string): FirefoxProduct;
|
|
20
|
+
/**
|
|
21
|
+
* Narrows a raw CLI or prompt value to a supported SPDX licence identifier.
|
|
22
|
+
* @param value - Raw value from `--license` or the setup prompt.
|
|
23
|
+
* @param field - Field name quoted in the refusal (e.g. `--license`).
|
|
24
|
+
* @returns The validated licence identifier.
|
|
25
|
+
* @throws {@link InvalidArgumentError} when the value names no supported licence.
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveProjectLicense(value: unknown, field: string): ProjectLicense;
|
|
16
28
|
/** Validates non-interactive setup options before project scaffolding begins. */
|
|
17
29
|
export declare function validateSetupOptions(options: SetupOptions): void;
|
|
18
30
|
/** Resolves setup inputs from CLI flags and optional interactive prompts. */
|
|
@@ -18,7 +18,14 @@ function renderLicenseTemplate(license, template, vendor, now = new Date()) {
|
|
|
18
18
|
}
|
|
19
19
|
return template.replace(/\[year\]/g, String(now.getFullYear())).replace(/\[fullname\]/g, vendor);
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Narrows a raw CLI or prompt value to a supported Firefox product.
|
|
23
|
+
* @param value - Raw value from `--product` or the setup prompt.
|
|
24
|
+
* @param field - Field name quoted in the refusal (e.g. `--product`).
|
|
25
|
+
* @returns The validated product.
|
|
26
|
+
* @throws {@link InvalidArgumentError} when the value names no supported product.
|
|
27
|
+
*/
|
|
28
|
+
export function resolveFirefoxProduct(value, field) {
|
|
22
29
|
// Matches the sibling `resolveProjectLicense`, which already delegates to
|
|
23
30
|
// its guard. The `||` chain this replaced spelled the member list twice —
|
|
24
31
|
// once as the predicate and once in the message.
|
|
@@ -27,26 +34,19 @@ function resolveFirefoxProduct(value, field) {
|
|
|
27
34
|
}
|
|
28
35
|
throw new InvalidArgumentError(`Invalid product (use: ${FIREFOX_PRODUCTS.join(', ')})`, field);
|
|
29
36
|
}
|
|
30
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Narrows a raw CLI or prompt value to a supported SPDX licence identifier.
|
|
39
|
+
* @param value - Raw value from `--license` or the setup prompt.
|
|
40
|
+
* @param field - Field name quoted in the refusal (e.g. `--license`).
|
|
41
|
+
* @returns The validated licence identifier.
|
|
42
|
+
* @throws {@link InvalidArgumentError} when the value names no supported licence.
|
|
43
|
+
*/
|
|
44
|
+
export function resolveProjectLicense(value, field) {
|
|
31
45
|
if (typeof value === 'string' && isValidProjectLicense(value)) {
|
|
32
46
|
return value;
|
|
33
47
|
}
|
|
34
48
|
throw new InvalidArgumentError('Invalid license (use: EUPL-1.2, MPL-2.0, 0BSD, GPL-2.0-or-later)', field);
|
|
35
49
|
}
|
|
36
|
-
/** Parses an optional Firefox product flag into a typed product value. */
|
|
37
|
-
export function parseFirefoxProductOption(product) {
|
|
38
|
-
if (product === undefined) {
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
return resolveFirefoxProduct(product, '--product');
|
|
42
|
-
}
|
|
43
|
-
/** Parses an optional license flag into a validated SPDX identifier. */
|
|
44
|
-
export function parseProjectLicenseOption(license) {
|
|
45
|
-
if (license === undefined) {
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
48
|
-
return resolveProjectLicense(license, '--license');
|
|
49
|
-
}
|
|
50
50
|
/** Validates non-interactive setup options before project scaffolding begins. */
|
|
51
51
|
export function validateSetupOptions(options) {
|
|
52
52
|
if (options.name !== undefined) {
|