@hominis/fireforge 0.44.4 → 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 +120 -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-source-pin.d.ts +29 -0
- package/dist/src/commands/doctor-source-pin.js +81 -0
- package/dist/src/commands/doctor.d.ts +2 -0
- package/dist/src/commands/doctor.js +9 -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 -60
- 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 -37
- 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 -11
- 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 +32 -2
- package/dist/src/commands/re-export-adjacent.js +51 -10
- package/dist/src/commands/re-export-bulk-scan.js +6 -10
- package/dist/src/commands/re-export-drift.d.ts +8 -0
- package/dist/src/commands/re-export-drift.js +65 -4
- package/dist/src/commands/re-export-files.d.ts +2 -3
- package/dist/src/commands/re-export-files.js +38 -32
- 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-options.js +19 -5
- package/dist/src/commands/re-export-register.js +5 -1
- package/dist/src/commands/re-export-scan.js +8 -8
- 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 +44 -27
- 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 +33 -8
- package/dist/src/commands/test-diagnose.js +14 -5
- 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 +6 -3
- package/dist/src/commands/test-run.js +29 -21
- package/dist/src/commands/test-stale-gate.js +8 -5
- 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 +51 -7
- 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 +154 -12
- 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.d.ts +64 -0
- package/dist/src/core/build-jar-escalation.js +128 -0
- package/dist/src/core/build-overwrite-guard.js +1 -1
- package/dist/src/core/build-prepare.d.ts +5 -2
- package/dist/src/core/build-prepare.js +34 -6
- 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.d.ts +12 -0
- package/dist/src/core/config-validate-file-size.js +65 -0
- 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 +20 -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.d.ts +73 -0
- package/dist/src/core/harness-orphans.js +187 -0
- 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 +25 -3
- package/dist/src/core/mach-known-noise-filter.js +31 -10
- 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 +46 -53
- package/dist/src/core/mach.js +31 -83
- 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 +55 -32
- 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 +43 -25
- 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.d.ts +52 -0
- package/dist/src/core/patch-lint-file-size.js +53 -0
- 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.d.ts +36 -0
- package/dist/src/core/patch-lint-prettier.js +126 -0
- 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-unmanaged-companion.d.ts +106 -0
- package/dist/src/core/patch-lint-unmanaged-companion.js +214 -0
- package/dist/src/core/patch-lint.d.ts +14 -9
- package/dist/src/core/patch-lint.js +27 -27
- 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 +21 -8
- package/dist/src/core/patch-transform.js +38 -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 +18 -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 +61 -48
- package/dist/src/core/test-harness-verdict-notes.d.ts +55 -0
- package/dist/src/core/test-harness-verdict-notes.js +48 -0
- package/dist/src/core/test-stale-check.d.ts +23 -8
- package/dist/src/core/test-stale-check.js +77 -4
- package/dist/src/core/test-stall-triage.js +37 -19
- 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.d.ts +28 -0
- package/dist/src/errors/build.js +51 -10
- 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 +96 -123
- package/dist/src/types/commands/patches.d.ts +17 -6
- package/dist/src/types/config.d.ts +75 -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.d.ts +4 -2
- package/dist/src/utils/fs.js +11 -14
- 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 +136 -1
- package/docs/exit-codes.md +34 -20
- 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 +70 -11
- 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
|
@@ -8,6 +8,8 @@ import { parseObject } from '../utils/parse.js';
|
|
|
8
8
|
import { isContainedRelativePath, isExplicitAbsolutePath } from '../utils/paths.js';
|
|
9
9
|
import { describeProductVersionIncompatibility, FIREFOX_PRODUCTS, isObject, isValidAppId, isValidFirefoxCandidate, isValidFirefoxProduct, isValidFirefoxVersion, isValidProjectLicense, PROJECT_LICENSES, } from '../utils/validation.js';
|
|
10
10
|
import { SUPPORTED_CONFIG_ROOT_KEYS } from './config-paths.js';
|
|
11
|
+
import { parseBuildAuditConfig } from './config-validate-build-audit.js';
|
|
12
|
+
import { parsePatchLintFileSizeThresholds } from './config-validate-file-size.js';
|
|
11
13
|
import { parsePatchPolicyBlock } from './config-validate-patch-policy.js';
|
|
12
14
|
import { parseExternalToolchainsBlock, parseTestBlock } from './config-validate-test-toolchains.js';
|
|
13
15
|
/**
|
|
@@ -53,7 +55,8 @@ function parseIdentityFields(rec) {
|
|
|
53
55
|
/**
|
|
54
56
|
* Parses and validates the required `firefox` block: version shape,
|
|
55
57
|
* product allowlist, product/version cross-compatibility, and the
|
|
56
|
-
* optional sha256 digest (normalized to lowercase)
|
|
58
|
+
* optional sha256 digest (normalized to lowercase), optional
|
|
59
|
+
* `allowUnverifiedDownload` opt-out, optional candidate directory.
|
|
57
60
|
*/
|
|
58
61
|
function parseFirefoxBlock(rec) {
|
|
59
62
|
let firefoxRec;
|
|
@@ -84,6 +87,10 @@ function parseFirefoxBlock(rec) {
|
|
|
84
87
|
if (firefoxSha256 !== undefined && !/^[a-f0-9]{64}$/i.test(firefoxSha256)) {
|
|
85
88
|
throw new ConfigError('Config field "firefox.sha256" must be a 64-character SHA-256 hex digest');
|
|
86
89
|
}
|
|
90
|
+
const allowUnverifiedDownload = firefoxRec.raw('allowUnverifiedDownload');
|
|
91
|
+
if (allowUnverifiedDownload !== undefined && typeof allowUnverifiedDownload !== 'boolean') {
|
|
92
|
+
throw new ConfigError('Config field "firefox.allowUnverifiedDownload" must be a boolean');
|
|
93
|
+
}
|
|
87
94
|
const firefoxCandidate = optionalConfigString(firefoxRec, 'candidate', 'firefox.candidate');
|
|
88
95
|
if (firefoxCandidate !== undefined && !isValidFirefoxCandidate(firefoxCandidate)) {
|
|
89
96
|
throw new ConfigError('Config field "firefox.candidate" must look like "buildN" (e.g. "build2")');
|
|
@@ -92,6 +99,7 @@ function parseFirefoxBlock(rec) {
|
|
|
92
99
|
version: firefoxVersion,
|
|
93
100
|
product: firefoxProduct,
|
|
94
101
|
...(firefoxSha256 !== undefined ? { sha256: firefoxSha256.toLowerCase() } : {}),
|
|
102
|
+
...(allowUnverifiedDownload !== undefined ? { allowUnverifiedDownload } : {}),
|
|
95
103
|
...(firefoxCandidate !== undefined ? { candidate: firefoxCandidate } : {}),
|
|
96
104
|
};
|
|
97
105
|
}
|
|
@@ -154,6 +162,10 @@ export function validateConfig(data) {
|
|
|
154
162
|
const firefox = parseFirefoxBlock(rec);
|
|
155
163
|
const config = { ...identity, firefox };
|
|
156
164
|
parseBuildBlock(rec, config);
|
|
165
|
+
const buildAuditRaw = rec.raw('buildAudit');
|
|
166
|
+
if (buildAuditRaw !== undefined) {
|
|
167
|
+
config.buildAudit = parseBuildAuditConfig(buildAuditRaw);
|
|
168
|
+
}
|
|
157
169
|
parseTestBlock(rec, config);
|
|
158
170
|
parseExternalToolchainsBlock(rec, config);
|
|
159
171
|
parseWireBlock(rec, config);
|
|
@@ -323,6 +335,7 @@ function optionalPatchLintBoolean(rec, key) {
|
|
|
323
335
|
}
|
|
324
336
|
/** Severity-gate fields, each parsed and assigned identically. */
|
|
325
337
|
const PATCH_LINT_SEVERITY_GATE_KEYS = [
|
|
338
|
+
'prettier',
|
|
326
339
|
'jsdocClassMethods',
|
|
327
340
|
'testAssertionFloor',
|
|
328
341
|
'chromeScriptJsDoc',
|
|
@@ -359,6 +372,10 @@ function parsePatchLintBlock(rec) {
|
|
|
359
372
|
}
|
|
360
373
|
out.rawColorAllowlist = rawColorAllowlist;
|
|
361
374
|
}
|
|
375
|
+
const fileSizeThresholds = rec.raw('fileSizeThresholds');
|
|
376
|
+
if (fileSizeThresholds !== undefined) {
|
|
377
|
+
out.fileSizeThresholds = parsePatchLintFileSizeThresholds(fileSizeThresholds);
|
|
378
|
+
}
|
|
362
379
|
for (const key of PATCH_LINT_SEVERITY_GATE_KEYS) {
|
|
363
380
|
const gate = parseSeverityGate(rec.raw(key), `patchLint.${key}`);
|
|
364
381
|
if (gate !== undefined)
|
|
@@ -419,10 +436,10 @@ function parseTypecheckBlock(rec) {
|
|
|
419
436
|
for (let i = 0; i < projectsRaw.length; i++) {
|
|
420
437
|
const entry = projectsRaw[i];
|
|
421
438
|
if (typeof entry !== 'string' || entry.trim() === '') {
|
|
422
|
-
throw new ConfigError(`Config field "typecheck.projects[${
|
|
439
|
+
throw new ConfigError(`Config field "typecheck.projects[${i}]" must be a non-empty string`);
|
|
423
440
|
}
|
|
424
441
|
if (!isContainedRelativePath(entry)) {
|
|
425
|
-
throw new ConfigError(`Config field "typecheck.projects[${
|
|
442
|
+
throw new ConfigError(`Config field "typecheck.projects[${i}]" must be a project-relative path`);
|
|
426
443
|
}
|
|
427
444
|
projects.push(entry);
|
|
428
445
|
}
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
import type { FireForgeConfig } from '../types/config.js';
|
|
11
11
|
import type { JsonObject } from '../types/json.js';
|
|
12
12
|
export { mutateConfig } from './config-mutate.js';
|
|
13
|
-
export {
|
|
14
|
-
export { loadState,
|
|
13
|
+
export { FIREFORGE_DIR, getProjectPaths, SUPPORTED_CONFIG_PATHS, SUPPORTED_CONFIG_ROOT_KEYS, } from './config-paths.js';
|
|
14
|
+
export { loadState, updateState } from './config-state.js';
|
|
15
15
|
export { validateConfig } from './config-validate.js';
|
|
16
16
|
/**
|
|
17
17
|
* Checks if a fireforge.json exists in the given directory.
|
package/dist/src/core/config.js
CHANGED
|
@@ -18,20 +18,10 @@ import { validateConfig } from './config-validate.js';
|
|
|
18
18
|
import { createSiblingLockPath, withFileLock } from './file-lock.js';
|
|
19
19
|
// ---- re-exports ----
|
|
20
20
|
export { mutateConfig } from './config-mutate.js';
|
|
21
|
-
export {
|
|
22
|
-
export { loadState,
|
|
21
|
+
export { FIREFORGE_DIR, getProjectPaths, SUPPORTED_CONFIG_PATHS, SUPPORTED_CONFIG_ROOT_KEYS, } from './config-paths.js';
|
|
22
|
+
export { loadState, updateState } from './config-state.js';
|
|
23
23
|
export { validateConfig } from './config-validate.js';
|
|
24
24
|
// ---- config I/O (stays here because it bridges paths + validation) ----
|
|
25
|
-
/**
|
|
26
|
-
* Config-file existence probe.
|
|
27
|
-
*
|
|
28
|
-
* Uses {@link pathExistsStrict} deliberately: a permission error probing
|
|
29
|
-
* `fireforge.json` must propagate rather than read as "no config here",
|
|
30
|
-
* which is what plain `pathExists` would report.
|
|
31
|
-
*/
|
|
32
|
-
async function configPathExists(path) {
|
|
33
|
-
return fsUtils.pathExistsStrict(path);
|
|
34
|
-
}
|
|
35
25
|
/**
|
|
36
26
|
* Checks if a fireforge.json exists in the given directory.
|
|
37
27
|
* @param root - Root directory to check
|
|
@@ -39,7 +29,9 @@ async function configPathExists(path) {
|
|
|
39
29
|
*/
|
|
40
30
|
export async function configExists(root) {
|
|
41
31
|
const paths = getProjectPaths(root);
|
|
42
|
-
|
|
32
|
+
// Strict probe: a permission error on `fireforge.json` must propagate
|
|
33
|
+
// rather than read as "no config here", which plain `pathExists` reports.
|
|
34
|
+
return fsUtils.pathExistsStrict(paths.config);
|
|
43
35
|
}
|
|
44
36
|
/**
|
|
45
37
|
* Loads and validates the fireforge.json configuration.
|
|
@@ -49,7 +41,9 @@ export async function configExists(root) {
|
|
|
49
41
|
*/
|
|
50
42
|
export async function loadConfig(root) {
|
|
51
43
|
const paths = getProjectPaths(root);
|
|
52
|
-
|
|
44
|
+
// Strict probe: a permission error on `fireforge.json` must propagate
|
|
45
|
+
// rather than read as "no config here", which plain `pathExists` reports.
|
|
46
|
+
if (!(await fsUtils.pathExistsStrict(paths.config))) {
|
|
53
47
|
throw new ConfigNotFoundError(paths.config);
|
|
54
48
|
}
|
|
55
49
|
try {
|
|
@@ -81,7 +75,9 @@ export async function loadConfig(root) {
|
|
|
81
75
|
*/
|
|
82
76
|
export async function loadRawConfigDocument(root) {
|
|
83
77
|
const paths = getProjectPaths(root);
|
|
84
|
-
|
|
78
|
+
// Strict probe: a permission error on `fireforge.json` must propagate
|
|
79
|
+
// rather than read as "no config here", which plain `pathExists` reports.
|
|
80
|
+
if (!(await fsUtils.pathExistsStrict(paths.config))) {
|
|
85
81
|
throw new ConfigNotFoundError(paths.config);
|
|
86
82
|
}
|
|
87
83
|
try {
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
* staging stale while extend still vouches for it. Plain REPLACE has no such
|
|
35
35
|
* window, because the earlier scope simply stops being covered.
|
|
36
36
|
*/
|
|
37
|
-
import { createHash } from 'node:crypto';
|
|
38
37
|
import { readFile } from 'node:fs/promises';
|
|
39
38
|
import { join } from 'node:path';
|
|
40
39
|
import { getNodeErrorCode, toError } from '../utils/errors.js';
|
|
40
|
+
import { sha256Hex } from '../utils/hash.js';
|
|
41
41
|
import { verbose } from '../utils/logger.js';
|
|
42
42
|
import { DELETED_FILE_FINGERPRINT, } from './build-baseline-types.js';
|
|
43
43
|
import { getHead, isMissingHeadError } from './git.js';
|
|
@@ -144,7 +144,7 @@ export function formatExtendCoverageRefusal(result) {
|
|
|
144
144
|
`longer anchored. ${remedy}`);
|
|
145
145
|
case 'fingerprint-diverged':
|
|
146
146
|
return ('--extend-coverage refused: ' +
|
|
147
|
-
`${
|
|
147
|
+
`${result.detail.length} packageable file(s) the recorded build staged have changed ` +
|
|
148
148
|
`since (${formatDivergedPaths(result.detail)}). Extending would vouch for the earlier scope ` +
|
|
149
149
|
`while obj-*/_tests/ still holds its stale staging. ${remedy}`);
|
|
150
150
|
case 'mozconfig-changed':
|
|
@@ -156,7 +156,7 @@ export function formatExtendCoverageRefusal(result) {
|
|
|
156
156
|
function formatDivergedPaths(paths) {
|
|
157
157
|
const head = paths.slice(0, MAX_DIVERGED_REPORTED).join(', ');
|
|
158
158
|
const extra = paths.length - Math.min(paths.length, MAX_DIVERGED_REPORTED);
|
|
159
|
-
return extra > 0 ? `${head}, … (+${
|
|
159
|
+
return extra > 0 ? `${head}, … (+${extra} more)` : head;
|
|
160
160
|
}
|
|
161
161
|
/**
|
|
162
162
|
* Hex-encoded SHA-256 of an engine-relative file, matching the digest the
|
|
@@ -168,7 +168,7 @@ function formatDivergedPaths(paths) {
|
|
|
168
168
|
export async function hashEngineFile(engineDir, relPath) {
|
|
169
169
|
try {
|
|
170
170
|
const buffer = await readFile(join(engineDir, relPath));
|
|
171
|
-
return
|
|
171
|
+
return sha256Hex(buffer);
|
|
172
172
|
}
|
|
173
173
|
catch (error) {
|
|
174
174
|
if (getNodeErrorCode(error) === 'ENOENT') {
|
|
@@ -226,22 +226,22 @@ export function summarizeDiscardBaselines(plans, succeeded) {
|
|
|
226
226
|
const deleted = plans.filter((p) => p.kind === 'patch-deleted').length;
|
|
227
227
|
const unmanaged = plans.filter((p) => p.kind === 'unmanaged').length;
|
|
228
228
|
if (patchBacked + created + deleted === 0) {
|
|
229
|
-
return `${
|
|
229
|
+
return `${succeeded} file(s) restored to upstream state`;
|
|
230
230
|
}
|
|
231
231
|
const parts = [];
|
|
232
232
|
if (patchBacked > 0)
|
|
233
|
-
parts.push(`${
|
|
233
|
+
parts.push(`${patchBacked} to patch baseline`);
|
|
234
234
|
if (created > 0)
|
|
235
|
-
parts.push(`${
|
|
235
|
+
parts.push(`${created} re-materialized`);
|
|
236
236
|
if (deleted > 0)
|
|
237
|
-
parts.push(`${
|
|
237
|
+
parts.push(`${deleted} removed per patch baseline`);
|
|
238
238
|
if (unmanaged > 0)
|
|
239
|
-
parts.push(`${
|
|
240
|
-
return `${
|
|
239
|
+
parts.push(`${unmanaged} to upstream state`);
|
|
240
|
+
return `${succeeded} file(s) restored: ${parts.join(', ')}`;
|
|
241
241
|
}
|
|
242
242
|
/** Warn text for a multi-owner (conflicted) restore. */
|
|
243
243
|
export function describeConflictWarning(plan) {
|
|
244
|
-
return (`${plan.entry.file} is claimed by ${
|
|
244
|
+
return (`${plan.entry.file} is claimed by ${plan.owners.length} patches ` +
|
|
245
245
|
`(${plan.owners.join(', ')}). Restored the cumulative baseline; run ` +
|
|
246
246
|
'"fireforge status --ownership" to resolve ownership.');
|
|
247
247
|
}
|
|
@@ -19,13 +19,6 @@ export interface EngineGitReadyOptions {
|
|
|
19
19
|
*/
|
|
20
20
|
unbornHeadSuffix?: string;
|
|
21
21
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Error factory for callers whose domain uses a class other than
|
|
24
|
-
* `GeneralError`. The furnace family raises `FurnaceError`, which carries
|
|
25
|
-
* its own `userMessage` and exit code 9, so a helper hardcoding
|
|
26
|
-
* `GeneralError` would silently change both.
|
|
27
|
-
*/
|
|
28
|
-
export type PreconditionErrorFactory = (message: string) => Error;
|
|
29
22
|
/**
|
|
30
23
|
* Asserts only that the engine checkout EXISTS.
|
|
31
24
|
*
|
|
@@ -36,9 +29,8 @@ export type PreconditionErrorFactory = (message: string) => Error;
|
|
|
36
29
|
* would refuse runs that work today.
|
|
37
30
|
*
|
|
38
31
|
* @param engineDir - Absolute path to the engine checkout
|
|
39
|
-
* @param errorFactory - Optional domain-specific error constructor
|
|
40
32
|
*/
|
|
41
|
-
export declare function assertEngineExists(engineDir: string
|
|
33
|
+
export declare function assertEngineExists(engineDir: string): Promise<void>;
|
|
42
34
|
/**
|
|
43
35
|
* Asserts that `engineDir` exists, is a git repository, and has a baseline
|
|
44
36
|
* commit — in that order, so the operator gets the most specific applicable
|
|
@@ -20,8 +20,6 @@ const UNBORN_HEAD_REMEDIATION = 'Engine repository has no baseline commit yet
|
|
|
20
20
|
// No terminating period: the suffix below supplies its own punctuation, and
|
|
21
21
|
// hardcoding one here rendered as "…cleanly., then retry the rebase."
|
|
22
22
|
'Re-run "fireforge download --force" to recreate the baseline repository cleanly';
|
|
23
|
-
/** Default factory: the message every non-furnace command already used. */
|
|
24
|
-
const defaultErrorFactory = (message) => new GeneralError(message);
|
|
25
23
|
/**
|
|
26
24
|
* Asserts only that the engine checkout EXISTS.
|
|
27
25
|
*
|
|
@@ -32,11 +30,10 @@ const defaultErrorFactory = (message) => new GeneralError(message);
|
|
|
32
30
|
* would refuse runs that work today.
|
|
33
31
|
*
|
|
34
32
|
* @param engineDir - Absolute path to the engine checkout
|
|
35
|
-
* @param errorFactory - Optional domain-specific error constructor
|
|
36
33
|
*/
|
|
37
|
-
export async function assertEngineExists(engineDir
|
|
34
|
+
export async function assertEngineExists(engineDir) {
|
|
38
35
|
if (!(await pathExists(engineDir))) {
|
|
39
|
-
throw
|
|
36
|
+
throw new GeneralError('Firefox source not found. Run "fireforge download" first.');
|
|
40
37
|
}
|
|
41
38
|
}
|
|
42
39
|
/**
|
|
@@ -48,9 +45,7 @@ export async function assertEngineExists(engineDir, errorFactory = defaultErrorF
|
|
|
48
45
|
* @param options - Optional per-command message tailoring
|
|
49
46
|
*/
|
|
50
47
|
export async function assertEngineGitReady(engineDir, options = {}) {
|
|
51
|
-
|
|
52
|
-
throw new GeneralError('Firefox source not found. Run "fireforge download" first.');
|
|
53
|
-
}
|
|
48
|
+
await assertEngineExists(engineDir);
|
|
54
49
|
if (!(await isGitRepository(engineDir))) {
|
|
55
50
|
throw new GeneralError('Engine directory is not a git repository. Run "fireforge download" to initialize.');
|
|
56
51
|
}
|
|
@@ -3,6 +3,7 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { InconclusiveVerdictError } from '../errors/base.js';
|
|
4
4
|
import { toError } from '../utils/errors.js';
|
|
5
5
|
import { info, warn } from '../utils/logger.js';
|
|
6
|
+
import { sleep } from '../utils/sleep.js';
|
|
6
7
|
import { readLockStatus, withFileLock } from './file-lock.js';
|
|
7
8
|
import { git } from './git-base.js';
|
|
8
9
|
const ENGINE_SESSION_LOCK_PATH = join('.fireforge', 'engine-session.lock');
|
|
@@ -21,19 +22,19 @@ const MAX_WAIT_LOCK_MS = 3600 * 1000;
|
|
|
21
22
|
* owner file degrades to the anonymous form.
|
|
22
23
|
*/
|
|
23
24
|
function formatWaitProgressLine(waitedMs, timeoutMs, holder, queue) {
|
|
24
|
-
const progress = `${
|
|
25
|
+
const progress = `${Math.round(waitedMs / 1000)}s of up to ${Math.round(timeoutMs / 1000)}s`;
|
|
25
26
|
// Queue position: under several concurrent sessions the wait
|
|
26
27
|
// line alone left operators inferring their place from `ps`.
|
|
27
28
|
const position = queue === undefined || queue.depth === 0
|
|
28
29
|
? ''
|
|
29
30
|
: queue.ahead === 0
|
|
30
|
-
? ` You are next in a queue of ${
|
|
31
|
-
: ` ${
|
|
31
|
+
? ` You are next in a queue of ${queue.depth}.`
|
|
32
|
+
: ` ${queue.ahead} ahead of you (queue of ${queue.depth}).`;
|
|
32
33
|
if (holder === undefined) {
|
|
33
34
|
return `Waiting for the FireForge engine lock — ${progress}.${position}`;
|
|
34
35
|
}
|
|
35
36
|
const details = holder.metadata.length > 0 ? ` (${holder.metadata.join(', ')})` : '';
|
|
36
|
-
return `Waiting for the FireForge engine lock held by PID ${
|
|
37
|
+
return `Waiting for the FireForge engine lock held by PID ${holder.pid}${details} — ${progress}.${position}`;
|
|
37
38
|
}
|
|
38
39
|
/**
|
|
39
40
|
* Formats the notice printed when the wait budget is EXTENDED because the
|
|
@@ -45,10 +46,10 @@ function formatWaitProgressLine(waitedMs, timeoutMs, holder, queue) {
|
|
|
45
46
|
* number they typed would otherwise conclude the timeout is broken.
|
|
46
47
|
*/
|
|
47
48
|
export function formatWaitExtendedLine(ahead, budgetMs, requestedMs) {
|
|
48
|
-
const position = ahead === 0 ? 'you are now next' : `${
|
|
49
|
+
const position = ahead === 0 ? 'you are now next' : `${ahead} still ahead of you`;
|
|
49
50
|
return (`The queue advanced (${position}), so the engine-lock wait was extended to ` +
|
|
50
|
-
`${
|
|
51
|
-
`${
|
|
51
|
+
`${Math.round(budgetMs / 1000)}s total (you asked for ` +
|
|
52
|
+
`${Math.round(requestedMs / 1000)}s). A queue that stops moving still ` +
|
|
52
53
|
`gives up on the budget you asked for.`);
|
|
53
54
|
}
|
|
54
55
|
/**
|
|
@@ -128,15 +129,15 @@ export function formatEngineSessionLockStatus(snapshot, holderCpuSeconds) {
|
|
|
128
129
|
}
|
|
129
130
|
const elapsed = snapshot.heldForMs === undefined
|
|
130
131
|
? 'unknown duration'
|
|
131
|
-
: `${
|
|
132
|
+
: `${Math.round(snapshot.heldForMs / 1000)}s`;
|
|
132
133
|
const holder = snapshot.holder;
|
|
133
134
|
const identity = holder === undefined
|
|
134
135
|
? 'held by an unidentified process (no readable owner record)'
|
|
135
|
-
: `held by PID ${
|
|
136
|
+
: `held by PID ${holder.pid}${holder.alive ? '' : ' (NOT RUNNING — stale lock)'}` +
|
|
136
137
|
(holder.metadata.length > 0 ? ` (${holder.metadata.join(', ')})` : '');
|
|
137
138
|
const lines = [
|
|
138
139
|
`Engine session lock: ${identity}, for ${elapsed}.`,
|
|
139
|
-
`Queue depth: ${
|
|
140
|
+
`Queue depth: ${snapshot.queueDepth} waiter(s).`,
|
|
140
141
|
];
|
|
141
142
|
// Liveness answers "does the process exist"; it does not answer "is it
|
|
142
143
|
// getting anywhere". A holder alive for minutes having used a fraction of
|
|
@@ -182,7 +183,7 @@ export async function snapshotEngineGeneration(engineDir) {
|
|
|
182
183
|
// and is reported immediately.
|
|
183
184
|
if (!isIndexLockError(error) || attempt === GENERATION_PROBE_ATTEMPTS - 1)
|
|
184
185
|
break;
|
|
185
|
-
await
|
|
186
|
+
await sleep(GENERATION_PROBE_RETRY_MS);
|
|
186
187
|
}
|
|
187
188
|
}
|
|
188
189
|
return `${UNAVAILABLE_PREFIX}${toError(lastError).message}`;
|
|
@@ -191,11 +192,6 @@ export async function snapshotEngineGeneration(engineDir) {
|
|
|
191
192
|
const GENERATION_PROBE_ATTEMPTS = 3;
|
|
192
193
|
/** Pause between generation-probe attempts. */
|
|
193
194
|
const GENERATION_PROBE_RETRY_MS = 250;
|
|
194
|
-
function delay(ms) {
|
|
195
|
-
return new Promise((resolveDelay) => {
|
|
196
|
-
setTimeout(resolveDelay, ms);
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
195
|
/**
|
|
200
196
|
* True when a git failure is the transient index-lock contention shape
|
|
201
197
|
* (`Unable to create '…/.git/index.lock': File exists`) rather than a
|
|
@@ -290,7 +286,7 @@ export function describeEngineGenerationDelta(before, after) {
|
|
|
290
286
|
return;
|
|
291
287
|
const head = records.slice(0, DELTA_ENTRY_LIMIT);
|
|
292
288
|
const truncated = records.length - head.length;
|
|
293
|
-
lines.push(`${label}: ${head.join(', ')}${truncated > 0 ? `, … (+${
|
|
289
|
+
lines.push(`${label}: ${head.join(', ')}${truncated > 0 ? `, … (+${truncated} more)` : ''}`);
|
|
294
290
|
};
|
|
295
291
|
render('Working-tree entries that appeared', appeared);
|
|
296
292
|
render('Working-tree entries that went away', disappeared);
|
|
@@ -114,14 +114,14 @@ export function formatAdoptThenSplitRemedy(overlaps, newPatchName, insertionOrde
|
|
|
114
114
|
const suggestedName = newPatchName.replace(/^\d+-/, '').replace(/\.patch$/, '');
|
|
115
115
|
return [
|
|
116
116
|
'',
|
|
117
|
-
`This looks like a new-file + moved-code slice: ${
|
|
117
|
+
`This looks like a new-file + moved-code slice: ${strongest.sharedLines} substantive ` +
|
|
118
118
|
`line(s) the new file(s) would add are still carried by ${strongest.sourcePatch}. ` +
|
|
119
119
|
'Exporting them as their own patch cannot pass cross-patch lint while the source patch ' +
|
|
120
120
|
'still owns the same code, and re-exporting cannot adopt them while they are unmanaged — ' +
|
|
121
121
|
'each guard is right, and together they dead-end.',
|
|
122
122
|
'Working sequence (adopt, then split):',
|
|
123
123
|
` 1. fireforge re-export ${strongest.sourcePatch} --scan ${fileArgs}`,
|
|
124
|
-
` 2. fireforge patch move-files ${strongest.sourcePatch} ${suggestedName} --create --order ${
|
|
124
|
+
` 2. fireforge patch move-files ${strongest.sourcePatch} ${suggestedName} --create --order ${insertionOrder} ${moveArgs}`,
|
|
125
125
|
'Step 1 adopts the new files into the patch that already owns the moved lines; step 2 ' +
|
|
126
126
|
'splits them back out as one transaction, so the queue is never in a state where two ' +
|
|
127
127
|
'patches own the same code.',
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filename of a lock directory's owner record, relative to the lock dir.
|
|
3
|
+
*
|
|
4
|
+
* Re-exported by `file-lock.ts` so external readers (`tree-store.ts`) share
|
|
5
|
+
* the constant instead of re-spelling `'pid'`; the file *format* is
|
|
6
|
+
* documented on {@link readLockOwner}.
|
|
7
|
+
*/
|
|
8
|
+
export declare const LOCK_PID_FILE = "pid";
|
|
9
|
+
/**
|
|
10
|
+
* Owner record read back from a lock directory's PID file.
|
|
11
|
+
*
|
|
12
|
+
* `token` is the per-acquisition UUID written on line 2 of the PID file,
|
|
13
|
+
* `acquiredAtMs` the wall-clock time the record was written (diagnostic),
|
|
14
|
+
* and `startTick` the writer's boot-relative start tick (liveness). Records
|
|
15
|
+
* from releases before a field was introduced parse with it `undefined`;
|
|
16
|
+
* readers must treat that as "unknown", never as a mismatch.
|
|
17
|
+
*/
|
|
18
|
+
export type LockOwner = {
|
|
19
|
+
present: false;
|
|
20
|
+
} | {
|
|
21
|
+
present: true;
|
|
22
|
+
pid: number;
|
|
23
|
+
token?: string;
|
|
24
|
+
acquiredAtMs?: number;
|
|
25
|
+
startTick?: number;
|
|
26
|
+
metadata: string[];
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Reads the owner PID (line 1), acquisition token (line 2), the mechanical
|
|
30
|
+
* `acquired-at-ms=` / `start-tick=` lines (when present), and diagnostic
|
|
31
|
+
* metadata (the remaining lines 3+) from a lock directory's PID file.
|
|
32
|
+
* Returns `{ present: false }` when the PID file is missing, unreadable, or
|
|
33
|
+
* the PID does not parse as a finite integer (caller falls back to the
|
|
34
|
+
* age-only staleness heuristic).
|
|
35
|
+
*
|
|
36
|
+
* File format note: external readers do `parseInt(content.trim(), 10)`,
|
|
37
|
+
* which parses the leading digits of a multi-line file — so the token and
|
|
38
|
+
* mechanical lines stay compatible.
|
|
39
|
+
*
|
|
40
|
+
* @param lockPath - Lock directory whose owner record to read
|
|
41
|
+
* @returns The parsed owner record, or `{ present: false }`
|
|
42
|
+
*/
|
|
43
|
+
export declare function readLockOwner(lockPath: string): Promise<LockOwner>;
|
|
44
|
+
/**
|
|
45
|
+
* Stamps this process's ownership (PID, per-acquisition token, acquisition
|
|
46
|
+
* time, own start tick, diagnostic metadata) into a lock directory it has
|
|
47
|
+
* just created, and reads the record back to prove it landed.
|
|
48
|
+
*
|
|
49
|
+
* The write is FATAL. It used to be best-effort, on the theory that stale
|
|
50
|
+
* recovery would fall back to the age heuristic — but that heuristic reaps
|
|
51
|
+
* any owner-less lock after five minutes, and the build lock legitimately
|
|
52
|
+
* holds for hours. A holder whose record failed to write was therefore
|
|
53
|
+
* reaped mid-build and a second process entered the critical section. On
|
|
54
|
+
* failure the just-created lock directory is removed (nothing else can own
|
|
55
|
+
* it yet) and a {@link GeneralError} names the cause.
|
|
56
|
+
*
|
|
57
|
+
* @param lockPath - Lock directory this process has just `mkdir`'d
|
|
58
|
+
* @param token - Per-acquisition UUID, later checked by release
|
|
59
|
+
* @param ownerMetadata - Extra human-diagnostic lines (see `FileLockOptions.ownerMetadata`)
|
|
60
|
+
* @throws {GeneralError} When the record cannot be written or does not read back as written
|
|
61
|
+
*/
|
|
62
|
+
export declare function writeLockOwner(lockPath: string, token: string, ownerMetadata: readonly string[] | undefined): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Boot-relative start tick of a process, where the platform exposes it
|
|
65
|
+
* cheaply.
|
|
66
|
+
*
|
|
67
|
+
* Linux only: field 22 of `/proc/<pid>/stat`, in USER_HZ since boot.
|
|
68
|
+
* Elsewhere (macOS, Windows) obtaining a start time means spawning
|
|
69
|
+
* `ps`/`wmic` per probe, which is not worth paying on every poll of a
|
|
70
|
+
* contended lock — those platforms return `undefined` and liveness stays
|
|
71
|
+
* PID-only.
|
|
72
|
+
*
|
|
73
|
+
* Deliberately NOT converted to wall-clock time via `/proc/stat` `btime`:
|
|
74
|
+
* on current kernels `btime` is derived from the realtime clock and moves
|
|
75
|
+
* with every clock step, so a wall-clock comparison against a recorded
|
|
76
|
+
* acquisition time misjudges a live holder as a reused PID after NTP steps
|
|
77
|
+
* the clock forward — and reaps a lock that is in use.
|
|
78
|
+
*
|
|
79
|
+
* @param pid - Process to look up
|
|
80
|
+
* @param procRoot - procfs mount, overridable for tests
|
|
81
|
+
* @returns Start tick, or `undefined` when unobtainable
|
|
82
|
+
*/
|
|
83
|
+
export declare function readProcessStartTick(pid: number, procRoot?: string): number | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* Whether the process named by a lock's owner record is the SAME process
|
|
86
|
+
* that acquired the lock, not merely a live process wearing its PID.
|
|
87
|
+
*
|
|
88
|
+
* `isProcessAlive` answers "does this PID exist"; after a holder crashes,
|
|
89
|
+
* the kernel may hand its PID to a long-lived daemon, and a PID-only check
|
|
90
|
+
* then honours the dead lock until the waiter's timeout. Where the record
|
|
91
|
+
* carries the writer's start tick and the candidate's own tick is cheaply
|
|
92
|
+
* obtainable (see {@link readProcessStartTick}), the two must be equal: a
|
|
93
|
+
* different tick is a different process. Records without a start tick, and
|
|
94
|
+
* platforms without one, keep the PID-only answer.
|
|
95
|
+
*
|
|
96
|
+
* @param owner - Owner record fields that identify the holder
|
|
97
|
+
* @param procRoot - procfs mount, overridable for tests
|
|
98
|
+
* @returns True when the original holder is still running (or cannot be proven gone)
|
|
99
|
+
*/
|
|
100
|
+
export declare function isLockOwnerAlive(owner: {
|
|
101
|
+
pid: number;
|
|
102
|
+
startTick?: number | undefined;
|
|
103
|
+
}, procRoot?: string): boolean;
|