@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
package/dist/src/core/git.js
CHANGED
|
@@ -225,15 +225,8 @@ export async function stageAllFiles(dir, options = {}) {
|
|
|
225
225
|
// monolithic budget forward.
|
|
226
226
|
phase = 'chunked';
|
|
227
227
|
phaseStartedAt = Date.now();
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
reportProgress?.('Git phase complete: chunked source indexing finished.');
|
|
231
|
-
}
|
|
232
|
-
catch (error) {
|
|
233
|
-
if (error instanceof GitIndexingTimeoutError)
|
|
234
|
-
throw error;
|
|
235
|
-
throw error;
|
|
236
|
-
}
|
|
228
|
+
await stageAllFilesChunked(dir, scan, options);
|
|
229
|
+
reportProgress?.('Git phase complete: chunked source indexing finished.');
|
|
237
230
|
}
|
|
238
231
|
finally {
|
|
239
232
|
if (heartbeatTimer)
|
|
@@ -285,10 +278,20 @@ export async function initRepository(dir, branchName = 'main', options = {}) {
|
|
|
285
278
|
reportProgress('Configuring origin remote for build compatibility...');
|
|
286
279
|
await git(['remote', 'add', 'origin', 'https://github.com/mozilla-firefox/firefox'], dir);
|
|
287
280
|
reportProgress('Git phase complete: source git repository metadata initialized.');
|
|
281
|
+
await stageAndCommitSource(dir, reportProgress, 'Indexing Firefox source with git add -A (this can take several minutes on large trees)...');
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* The tail shared by {@link initRepository} and {@link resumeRepository}:
|
|
285
|
+
* normalizes the Firefox ignore files, stages the whole tree, and creates
|
|
286
|
+
* the initial source commit. Index-lock failures on either git step are
|
|
287
|
+
* wrapped into the typed lock error so both entry points report them the
|
|
288
|
+
* same way.
|
|
289
|
+
*/
|
|
290
|
+
async function stageAndCommitSource(dir, reportProgress, stagingMessage) {
|
|
288
291
|
reportProgress('Normalizing Firefox ignore files for Git-backed mach lint compatibility...');
|
|
289
292
|
await ensureFirefoxIgnorefileCompatibility(dir);
|
|
290
293
|
// Add all files
|
|
291
|
-
reportProgress(
|
|
294
|
+
reportProgress(stagingMessage);
|
|
292
295
|
await assertNoGitIndexLock(dir);
|
|
293
296
|
try {
|
|
294
297
|
await stageAllFiles(dir, { onProgress: reportProgress });
|
|
@@ -310,7 +313,6 @@ export async function initRepository(dir, branchName = 'main', options = {}) {
|
|
|
310
313
|
* cleans up stale locks, stages all files, and creates the initial commit.
|
|
311
314
|
*/
|
|
312
315
|
export async function resumeRepository(dir, options = {}) {
|
|
313
|
-
await ensureGit();
|
|
314
316
|
const reportProgress = options.onProgress ?? (() => { });
|
|
315
317
|
if (!(await isGitRepository(dir))) {
|
|
316
318
|
throw new GitError('Not a git repository', 'resume');
|
|
@@ -324,24 +326,7 @@ export async function resumeRepository(dir, options = {}) {
|
|
|
324
326
|
await cleanupIndexLock(dir);
|
|
325
327
|
// Ensure origin remote exists (may have been added before the interrupt)
|
|
326
328
|
await ensureOriginRemote(dir);
|
|
327
|
-
reportProgress
|
|
328
|
-
await ensureFirefoxIgnorefileCompatibility(dir);
|
|
329
|
-
// Stage all files
|
|
330
|
-
reportProgress('Indexing Firefox source (resuming)...');
|
|
331
|
-
await assertNoGitIndexLock(dir);
|
|
332
|
-
try {
|
|
333
|
-
await stageAllFiles(dir, { onProgress: reportProgress });
|
|
334
|
-
}
|
|
335
|
-
catch (error) {
|
|
336
|
-
throw await maybeWrapIndexLockError(dir, error);
|
|
337
|
-
}
|
|
338
|
-
// Create initial commit
|
|
339
|
-
try {
|
|
340
|
-
await createInitialSourceCommit(dir, reportProgress);
|
|
341
|
-
}
|
|
342
|
-
catch (error) {
|
|
343
|
-
throw await maybeWrapIndexLockError(dir, error);
|
|
344
|
-
}
|
|
329
|
+
await stageAndCommitSource(dir, reportProgress, 'Indexing Firefox source (resuming)...');
|
|
345
330
|
}
|
|
346
331
|
async function assertNoGitIndexLock(dir) {
|
|
347
332
|
const lockPath = join(dir, '.git', 'index.lock');
|
|
@@ -499,7 +484,6 @@ export async function reversePatch(patchPath, repoDir) {
|
|
|
499
484
|
* @returns True if there are uncommitted changes
|
|
500
485
|
*/
|
|
501
486
|
export async function hasChanges(repoDir) {
|
|
502
|
-
await ensureGit();
|
|
503
487
|
const entries = await getWorkingTreeStatus(repoDir);
|
|
504
488
|
return entries.length > 0;
|
|
505
489
|
}
|
|
@@ -518,7 +502,6 @@ export function isMissingHeadError(error) {
|
|
|
518
502
|
* @returns Commit hash
|
|
519
503
|
*/
|
|
520
504
|
export async function getHead(repoDir) {
|
|
521
|
-
await ensureGit();
|
|
522
505
|
const output = await git(['rev-parse', 'HEAD'], repoDir);
|
|
523
506
|
return output.trim();
|
|
524
507
|
}
|
|
@@ -528,7 +511,6 @@ export async function getHead(repoDir) {
|
|
|
528
511
|
* @returns Branch name
|
|
529
512
|
*/
|
|
530
513
|
export async function getCurrentBranch(repoDir) {
|
|
531
|
-
await ensureGit();
|
|
532
514
|
const output = await git(['rev-parse', '--abbrev-ref', 'HEAD'], repoDir);
|
|
533
515
|
return output.trim();
|
|
534
516
|
}
|
|
@@ -537,7 +519,6 @@ export async function getCurrentBranch(repoDir) {
|
|
|
537
519
|
* @param repoDir - Repository directory
|
|
538
520
|
*/
|
|
539
521
|
export async function resetChanges(repoDir) {
|
|
540
|
-
await ensureGit();
|
|
541
522
|
try {
|
|
542
523
|
await git(['reset', '--hard', 'HEAD'], repoDir);
|
|
543
524
|
}
|
|
@@ -546,16 +527,6 @@ export async function resetChanges(repoDir) {
|
|
|
546
527
|
}
|
|
547
528
|
await git(['clean', '-fd'], repoDir);
|
|
548
529
|
}
|
|
549
|
-
/**
|
|
550
|
-
* Creates a commit with all current changes.
|
|
551
|
-
* @param repoDir - Repository directory
|
|
552
|
-
* @param message - Commit message
|
|
553
|
-
*/
|
|
554
|
-
export async function commit(repoDir, message) {
|
|
555
|
-
await ensureGit();
|
|
556
|
-
await stageAllFiles(repoDir);
|
|
557
|
-
await git(['commit', '-m', message], repoDir);
|
|
558
|
-
}
|
|
559
530
|
/**
|
|
560
531
|
* Gets the status of files with their status codes.
|
|
561
532
|
* @param repoDir - Repository directory
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preflight census of ORPHANED test-harness helper processes.
|
|
3
|
+
*
|
|
4
|
+
* Stopping a long mochitest run mid-flight does not necessarily take its
|
|
5
|
+
* helpers with it. A downstream incident left `xpcshell` (the harness
|
|
6
|
+
* httpd, pegged at 100% CPU), `pywebsocket`, `ssltunnel` and `moz-http2`
|
|
7
|
+
* alive for an hour. Every subsequent run crawled — a three-second suite
|
|
8
|
+
* took six minutes of wall clock and audio-start waits timed out — and
|
|
9
|
+
* nothing in FireForge's output connected the slowness to the survivors.
|
|
10
|
+
* The hour was spent mis-attributing it to the change under test.
|
|
11
|
+
*
|
|
12
|
+
* Visibility alone would have collapsed that hour, which is why the census
|
|
13
|
+
* runs by default and the kill does not. Two rules keep it honest:
|
|
14
|
+
*
|
|
15
|
+
* - The census runs at PREFLIGHT, before this run spawns anything, so
|
|
16
|
+
* every matching process is by construction a survivor of an earlier
|
|
17
|
+
* run. No age heuristic is needed, and none is used.
|
|
18
|
+
* - A match must be anchored to a Firefox OBJDIR. `xpcshell` and
|
|
19
|
+
* `server.js` are far too generic on their own; the same rule
|
|
20
|
+
* `mochitest-server-port.ts` applies to the httpd holder applies here,
|
|
21
|
+
* for the same reason — FireForge has no business reporting, let alone
|
|
22
|
+
* terminating, a process it cannot attribute to the harness.
|
|
23
|
+
*
|
|
24
|
+
* `--reap-orphans` opts into termination. Without it the census is
|
|
25
|
+
* report-only, exactly like the `Orphaned harness workers` doctor check
|
|
26
|
+
* (which covers a DIFFERENT shape: reparented Python multiprocessing
|
|
27
|
+
* workers, matched on PPID 1 and accumulated CPU time).
|
|
28
|
+
*/
|
|
29
|
+
/** One surviving harness helper process. */
|
|
30
|
+
export interface OrphanedHarnessProcess {
|
|
31
|
+
pid: number;
|
|
32
|
+
ppid: number;
|
|
33
|
+
/** Raw `ps` ELAPSED column. */
|
|
34
|
+
elapsed: string;
|
|
35
|
+
/** Parsed elapsed seconds since the process started. */
|
|
36
|
+
elapsedSeconds: number;
|
|
37
|
+
command: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Scans `ps -axo pid=,ppid=,etime=,command=` output for surviving harness
|
|
41
|
+
* helpers. Pure — fixture-testable without spawning anything.
|
|
42
|
+
*
|
|
43
|
+
* @param psOutput - Raw `ps` output
|
|
44
|
+
* @param objDir - Absolute objdir of this project, when known; widens the
|
|
45
|
+
* provenance test beyond the generic `obj-…` path segment
|
|
46
|
+
* @param selfPid - This process's pid, excluded so FireForge cannot report
|
|
47
|
+
* itself
|
|
48
|
+
*/
|
|
49
|
+
export declare function findOrphanedHarnessProcesses(psOutput: string, objDir?: string, selfPid?: number): OrphanedHarnessProcess[];
|
|
50
|
+
/**
|
|
51
|
+
* Renders the census. Names every process with its elapsed time, because
|
|
52
|
+
* "a leftover harness process exists" and "a leftover harness process has
|
|
53
|
+
* been burning a core for 58 minutes" are different findings.
|
|
54
|
+
*/
|
|
55
|
+
export declare function formatOrphanReport(orphans: readonly OrphanedHarnessProcess[]): string;
|
|
56
|
+
/**
|
|
57
|
+
* Preflight census of surviving harness helpers, run before a test
|
|
58
|
+
* dispatch.
|
|
59
|
+
*
|
|
60
|
+
* Best-effort by construction: a host without a usable `ps` (Windows, a
|
|
61
|
+
* locked-down container) logs at verbose and runs exactly as before. It
|
|
62
|
+
* never refuses a run — unlike the server-port preflight, a survivor here
|
|
63
|
+
* degrades performance rather than making the run dispatch against the
|
|
64
|
+
* wrong server, so the correct response is to say so loudly, not to stop.
|
|
65
|
+
*
|
|
66
|
+
* @param objDir - Absolute objdir of this project, when known
|
|
67
|
+
* @param options - `reap` terminates each recognized survivor (SIGTERM,
|
|
68
|
+
* then SIGKILL for anything that stays)
|
|
69
|
+
* @returns The census, empty when nothing was found or nothing could be probed
|
|
70
|
+
*/
|
|
71
|
+
export declare function reportOrphanedHarnessProcesses(objDir: string | undefined, options?: {
|
|
72
|
+
reap?: boolean;
|
|
73
|
+
}): Promise<OrphanedHarnessProcess[]>;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Preflight census of ORPHANED test-harness helper processes.
|
|
4
|
+
*
|
|
5
|
+
* Stopping a long mochitest run mid-flight does not necessarily take its
|
|
6
|
+
* helpers with it. A downstream incident left `xpcshell` (the harness
|
|
7
|
+
* httpd, pegged at 100% CPU), `pywebsocket`, `ssltunnel` and `moz-http2`
|
|
8
|
+
* alive for an hour. Every subsequent run crawled — a three-second suite
|
|
9
|
+
* took six minutes of wall clock and audio-start waits timed out — and
|
|
10
|
+
* nothing in FireForge's output connected the slowness to the survivors.
|
|
11
|
+
* The hour was spent mis-attributing it to the change under test.
|
|
12
|
+
*
|
|
13
|
+
* Visibility alone would have collapsed that hour, which is why the census
|
|
14
|
+
* runs by default and the kill does not. Two rules keep it honest:
|
|
15
|
+
*
|
|
16
|
+
* - The census runs at PREFLIGHT, before this run spawns anything, so
|
|
17
|
+
* every matching process is by construction a survivor of an earlier
|
|
18
|
+
* run. No age heuristic is needed, and none is used.
|
|
19
|
+
* - A match must be anchored to a Firefox OBJDIR. `xpcshell` and
|
|
20
|
+
* `server.js` are far too generic on their own; the same rule
|
|
21
|
+
* `mochitest-server-port.ts` applies to the httpd holder applies here,
|
|
22
|
+
* for the same reason — FireForge has no business reporting, let alone
|
|
23
|
+
* terminating, a process it cannot attribute to the harness.
|
|
24
|
+
*
|
|
25
|
+
* `--reap-orphans` opts into termination. Without it the census is
|
|
26
|
+
* report-only, exactly like the `Orphaned harness workers` doctor check
|
|
27
|
+
* (which covers a DIFFERENT shape: reparented Python multiprocessing
|
|
28
|
+
* workers, matched on PPID 1 and accumulated CPU time).
|
|
29
|
+
*/
|
|
30
|
+
import { toError } from '../utils/errors.js';
|
|
31
|
+
import { info, verbose, warn } from '../utils/logger.js';
|
|
32
|
+
import { exec } from '../utils/process.js';
|
|
33
|
+
import { parsePsDuration } from '../utils/ps-duration.js';
|
|
34
|
+
import { sleep } from '../utils/sleep.js';
|
|
35
|
+
/**
|
|
36
|
+
* Harness helper executables/scripts. Every one of these is started by the
|
|
37
|
+
* mochitest/xpcshell harness and is expected to die with it; none of them
|
|
38
|
+
* is a thing a developer runs by hand.
|
|
39
|
+
*/
|
|
40
|
+
const HARNESS_HELPER_PATTERN = /\b(?:xpcshell|pywebsocket\w*|websocket_server\.py|ssltunnel|moz-http2|http2_server|httpd\.js|server\.js|runtests\.py|runxpcshelltests\.py)\b/;
|
|
41
|
+
/** Objdir provenance: an explicit objdir path, a `obj-…` path segment, or `_tests/`. */
|
|
42
|
+
function isObjdirAnchored(command, objDir) {
|
|
43
|
+
if (objDir !== undefined && objDir.length > 0 && command.includes(objDir))
|
|
44
|
+
return true;
|
|
45
|
+
if (/[/\\]obj-[^/\\\s]*[/\\]/.test(command))
|
|
46
|
+
return true;
|
|
47
|
+
return /[/\\]_tests[/\\]/.test(command);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Scans `ps -axo pid=,ppid=,etime=,command=` output for surviving harness
|
|
51
|
+
* helpers. Pure — fixture-testable without spawning anything.
|
|
52
|
+
*
|
|
53
|
+
* @param psOutput - Raw `ps` output
|
|
54
|
+
* @param objDir - Absolute objdir of this project, when known; widens the
|
|
55
|
+
* provenance test beyond the generic `obj-…` path segment
|
|
56
|
+
* @param selfPid - This process's pid, excluded so FireForge cannot report
|
|
57
|
+
* itself
|
|
58
|
+
*/
|
|
59
|
+
export function findOrphanedHarnessProcesses(psOutput, objDir, selfPid = process.pid) {
|
|
60
|
+
const found = [];
|
|
61
|
+
for (const line of psOutput.split('\n')) {
|
|
62
|
+
const match = /^\s*(\d+)\s+(\d+)\s+(\S+)\s+(.+)$/.exec(line);
|
|
63
|
+
if (!match)
|
|
64
|
+
continue;
|
|
65
|
+
const pid = Number(match[1]);
|
|
66
|
+
const ppid = Number(match[2]);
|
|
67
|
+
const elapsed = match[3] ?? '';
|
|
68
|
+
const command = (match[4] ?? '').trim();
|
|
69
|
+
if (pid === selfPid || ppid === selfPid)
|
|
70
|
+
continue;
|
|
71
|
+
if (!HARNESS_HELPER_PATTERN.test(command))
|
|
72
|
+
continue;
|
|
73
|
+
if (!isObjdirAnchored(command, objDir))
|
|
74
|
+
continue;
|
|
75
|
+
const elapsedSeconds = parsePsDuration(elapsed);
|
|
76
|
+
found.push({
|
|
77
|
+
pid,
|
|
78
|
+
ppid,
|
|
79
|
+
elapsed,
|
|
80
|
+
elapsedSeconds: Number.isNaN(elapsedSeconds) ? 0 : elapsedSeconds,
|
|
81
|
+
command,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return found;
|
|
85
|
+
}
|
|
86
|
+
/** Grace period between SIGTERM and SIGKILL when reaping. */
|
|
87
|
+
const REAP_GRACE_MS = 500;
|
|
88
|
+
/** Longest command excerpt carried into the report line. */
|
|
89
|
+
const COMMAND_EXCERPT_LIMIT = 160;
|
|
90
|
+
/**
|
|
91
|
+
* Renders the census. Names every process with its elapsed time, because
|
|
92
|
+
* "a leftover harness process exists" and "a leftover harness process has
|
|
93
|
+
* been burning a core for 58 minutes" are different findings.
|
|
94
|
+
*/
|
|
95
|
+
export function formatOrphanReport(orphans) {
|
|
96
|
+
const rows = orphans
|
|
97
|
+
.map((p) => ` PID ${p.pid} (up ${p.elapsed}): ${p.command.slice(0, COMMAND_EXCERPT_LIMIT)}`)
|
|
98
|
+
.join('\n');
|
|
99
|
+
const pids = orphans.map((p) => String(p.pid)).join(' ');
|
|
100
|
+
return (`${orphans.length} harness helper process(es) from an EARLIER run are still alive ` +
|
|
101
|
+
`(this preflight runs before the current run spawns anything, so none of these belong to ` +
|
|
102
|
+
`it):\n${rows}\n` +
|
|
103
|
+
`Survivors like these slow every later run without appearing anywhere in its output — a ` +
|
|
104
|
+
`three-second suite taking minutes of wall clock is the usual symptom. Terminate them with ` +
|
|
105
|
+
`"kill ${pids}", or re-run with --reap-orphans to have FireForge do it.`);
|
|
106
|
+
}
|
|
107
|
+
/** Thin exec wrapper for the process listing (tests mock `exec` instead). */
|
|
108
|
+
async function listSystemProcesses() {
|
|
109
|
+
const result = await exec('ps', ['-axo', 'pid=,ppid=,etime=,command='], { timeout: 10000 });
|
|
110
|
+
if (result.exitCode !== 0) {
|
|
111
|
+
throw new Error(`ps exited ${result.exitCode}`);
|
|
112
|
+
}
|
|
113
|
+
return result.stdout;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Preflight census of surviving harness helpers, run before a test
|
|
117
|
+
* dispatch.
|
|
118
|
+
*
|
|
119
|
+
* Best-effort by construction: a host without a usable `ps` (Windows, a
|
|
120
|
+
* locked-down container) logs at verbose and runs exactly as before. It
|
|
121
|
+
* never refuses a run — unlike the server-port preflight, a survivor here
|
|
122
|
+
* degrades performance rather than making the run dispatch against the
|
|
123
|
+
* wrong server, so the correct response is to say so loudly, not to stop.
|
|
124
|
+
*
|
|
125
|
+
* @param objDir - Absolute objdir of this project, when known
|
|
126
|
+
* @param options - `reap` terminates each recognized survivor (SIGTERM,
|
|
127
|
+
* then SIGKILL for anything that stays)
|
|
128
|
+
* @returns The census, empty when nothing was found or nothing could be probed
|
|
129
|
+
*/
|
|
130
|
+
export async function reportOrphanedHarnessProcesses(objDir, options = {}) {
|
|
131
|
+
if (process.platform === 'win32')
|
|
132
|
+
return [];
|
|
133
|
+
let psOutput;
|
|
134
|
+
try {
|
|
135
|
+
psOutput = await listSystemProcesses();
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
verbose(`Orphan preflight: could not scan processes (${toError(error).message}); skipping.`);
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
const orphans = findOrphanedHarnessProcesses(psOutput, objDir);
|
|
142
|
+
if (orphans.length === 0) {
|
|
143
|
+
verbose('Orphan preflight: no surviving harness helper processes.');
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
warn(formatOrphanReport(orphans));
|
|
147
|
+
if (options.reap === true) {
|
|
148
|
+
await reapOrphanedHarnessProcesses(orphans);
|
|
149
|
+
}
|
|
150
|
+
return orphans;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Terminates each census entry: SIGTERM first, then SIGKILL for anything
|
|
154
|
+
* still alive. Failures are reported, never thrown — a process that exited
|
|
155
|
+
* between the census and the signal is the common case, not an error.
|
|
156
|
+
*/
|
|
157
|
+
async function reapOrphanedHarnessProcesses(orphans) {
|
|
158
|
+
for (const orphan of orphans) {
|
|
159
|
+
try {
|
|
160
|
+
process.kill(orphan.pid, 'SIGTERM');
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
verbose(`--reap-orphans: SIGTERM to ${orphan.pid} failed (${toError(error).message}).`);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
// Grace period before escalating: the httpd shape in the field
|
|
167
|
+
// incident ignored SIGTERM while spinning, but an ordinary helper exits
|
|
168
|
+
// promptly and must not be SIGKILLed for being slow by a millisecond.
|
|
169
|
+
await sleep(REAP_GRACE_MS);
|
|
170
|
+
let alive = true;
|
|
171
|
+
try {
|
|
172
|
+
process.kill(orphan.pid, 0);
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
alive = false;
|
|
176
|
+
}
|
|
177
|
+
if (alive) {
|
|
178
|
+
try {
|
|
179
|
+
process.kill(orphan.pid, 'SIGKILL');
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
verbose(`--reap-orphans: SIGKILL to ${orphan.pid} failed (${toError(error).message}).`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
info(`--reap-orphans: terminated PID ${orphan.pid}.`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { HistoryEntry } from './destructive.js';
|
|
2
|
+
/**
|
|
3
|
+
* Appends a history entry for a mutation that has ALREADY committed, degrading
|
|
4
|
+
* to a warning when the log cannot be written.
|
|
5
|
+
*
|
|
6
|
+
* Eleven call sites across `export-flow` and the `patch` subcommands had each
|
|
7
|
+
* grown the same `try { await appendHistory(...) } catch { warn(...) }` block.
|
|
8
|
+
* The swallow is deliberate and load-bearing: the patch files and the manifest
|
|
9
|
+
* are already on disk by the time this runs, so letting a failed log write
|
|
10
|
+
* reject would report a completed, irreversible mutation as a failure and send
|
|
11
|
+
* the operator looking for damage that is not there. Because that reasoning is
|
|
12
|
+
* easy to lose in a copy, it now lives in one place.
|
|
13
|
+
* @param patchesDir - The `patches/` directory holding the history log.
|
|
14
|
+
* @param entry - The entry to append.
|
|
15
|
+
* @param context - What just committed, rendered into
|
|
16
|
+
* `History log append failed after <context>: <reason>` — e.g.
|
|
17
|
+
* `patch tier committed (007-foo.patch)`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function appendHistoryBestEffort(patchesDir: string, entry: HistoryEntry, context: string): Promise<void>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
import { toError } from '../utils/errors.js';
|
|
3
|
+
import { warn } from '../utils/logger.js';
|
|
4
|
+
import { appendHistory } from './destructive.js';
|
|
5
|
+
/**
|
|
6
|
+
* Appends a history entry for a mutation that has ALREADY committed, degrading
|
|
7
|
+
* to a warning when the log cannot be written.
|
|
8
|
+
*
|
|
9
|
+
* Eleven call sites across `export-flow` and the `patch` subcommands had each
|
|
10
|
+
* grown the same `try { await appendHistory(...) } catch { warn(...) }` block.
|
|
11
|
+
* The swallow is deliberate and load-bearing: the patch files and the manifest
|
|
12
|
+
* are already on disk by the time this runs, so letting a failed log write
|
|
13
|
+
* reject would report a completed, irreversible mutation as a failure and send
|
|
14
|
+
* the operator looking for damage that is not there. Because that reasoning is
|
|
15
|
+
* easy to lose in a copy, it now lives in one place.
|
|
16
|
+
* @param patchesDir - The `patches/` directory holding the history log.
|
|
17
|
+
* @param entry - The entry to append.
|
|
18
|
+
* @param context - What just committed, rendered into
|
|
19
|
+
* `History log append failed after <context>: <reason>` — e.g.
|
|
20
|
+
* `patch tier committed (007-foo.patch)`.
|
|
21
|
+
*/
|
|
22
|
+
export async function appendHistoryBestEffort(patchesDir, entry, context) {
|
|
23
|
+
try {
|
|
24
|
+
await appendHistory(patchesDir, entry);
|
|
25
|
+
}
|
|
26
|
+
catch (historyError) {
|
|
27
|
+
warn(`History log append failed after ${context}: ${toError(historyError).message}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -15,11 +15,6 @@ export interface MachErrorHint {
|
|
|
15
15
|
/** Actionable, one-line hint to surface alongside the raw mach output. */
|
|
16
16
|
hint: string;
|
|
17
17
|
}
|
|
18
|
-
/**
|
|
19
|
-
* Registered hint patterns. Order-sensitive: the first match wins per
|
|
20
|
-
* pattern, but multiple distinct patterns may fire for the same stderr.
|
|
21
|
-
*/
|
|
22
|
-
export declare const MACH_ERROR_HINTS: MachErrorHint[];
|
|
23
18
|
/**
|
|
24
19
|
* Scans captured stderr for known mach errors and returns matching hints.
|
|
25
20
|
* Pure function — safe to call on any string; never throws.
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* Registered hint patterns. Order-sensitive: the first match wins per
|
|
14
14
|
* pattern, but multiple distinct patterns may fire for the same stderr.
|
|
15
15
|
*/
|
|
16
|
-
|
|
16
|
+
const MACH_ERROR_HINTS = [
|
|
17
17
|
{
|
|
18
18
|
pattern: /mozbuild\.preprocessor\.Preprocessor\.Error[\s\S]*?no preprocessor directives found/,
|
|
19
19
|
hint: 'A file registered under JS_PREFERENCE_PP_FILES contains no preprocessor directives. ' +
|
|
@@ -25,12 +25,27 @@
|
|
|
25
25
|
* echoed verbatim, always.
|
|
26
26
|
* - The hold buffer is bounded; on overflow the block is flushed verbatim
|
|
27
27
|
* and the filter returns to pass-through, so output is never lost.
|
|
28
|
+
*
|
|
29
|
+
* The COLLAPSE is test-only, deliberately, and not an oversight — the two
|
|
30
|
+
* reasons are worth stating because from outside it reads like one:
|
|
31
|
+
* 1. Recognition requires a previously-seen SUITE_END shutdown marker, and
|
|
32
|
+
* `mach build` never prints one. Wiring the option into the build
|
|
33
|
+
* dispatch would therefore be a silent no-op.
|
|
34
|
+
* 2. Relaxing that gate for builds would be wrong on its own terms. The
|
|
35
|
+
* marker is what separates teardown noise from a traceback raised while
|
|
36
|
+
* work was still happening; a build has no equivalent boundary, so
|
|
37
|
+
* FireForge cannot tell the known-cosmetic case from a real build-time
|
|
38
|
+
* failure and must not withhold the block.
|
|
39
|
+
* The build path instead RECOGNIZES the signature and says so beside the
|
|
40
|
+
* verbatim traceback — see {@link KNOWN_TEARDOWN_NOISE_BUILD_NOTE} and
|
|
41
|
+
* `runProtectedMachBuild`.
|
|
28
42
|
*/
|
|
29
43
|
/**
|
|
30
|
-
*
|
|
31
|
-
* teardown
|
|
44
|
+
* Whole-block recognition — every signal must hold (see module doc). True
|
|
45
|
+
* when CAPTURED output carries the documented mozsystemmonitor teardown
|
|
46
|
+
* traceback.
|
|
32
47
|
*
|
|
33
|
-
* The echo filter
|
|
48
|
+
* The echo filter below only affects what a human sees. The CLASSIFIER
|
|
34
49
|
* reads the raw capture, and it needs the same recognition to tell "the
|
|
35
50
|
* suite finished clean and then upstream fell over at shutdown" from "the
|
|
36
51
|
* suite did not finish". Deliberately the same two-signal test the echo
|
|
@@ -76,6 +91,13 @@ export interface TeardownNoiseContext {
|
|
|
76
91
|
}
|
|
77
92
|
/** Fresh per-run context — hand the same instance to both stream filters. */
|
|
78
93
|
export declare function createTeardownNoiseContext(): TeardownNoiseContext;
|
|
94
|
+
/**
|
|
95
|
+
* Build-phase note for the SAME recognized signature. The build path does
|
|
96
|
+
* not COLLAPSE the traceback (see the module doc's "test-only" note) — it
|
|
97
|
+
* prints verbatim and this line is added beside it, so one signature reads
|
|
98
|
+
* the same in both phases without a build losing output it may need.
|
|
99
|
+
*/
|
|
100
|
+
export declare const KNOWN_TEARDOWN_NOISE_BUILD_NOTE: string;
|
|
79
101
|
/** One line replaces the whole recognized traceback block in the echo. */
|
|
80
102
|
export declare const KNOWN_TEARDOWN_NOISE_ANNOTATION: string;
|
|
81
103
|
/** Chunk-safe, line-buffered echo filter. See module doc. */
|
|
@@ -26,6 +26,20 @@
|
|
|
26
26
|
* echoed verbatim, always.
|
|
27
27
|
* - The hold buffer is bounded; on overflow the block is flushed verbatim
|
|
28
28
|
* and the filter returns to pass-through, so output is never lost.
|
|
29
|
+
*
|
|
30
|
+
* The COLLAPSE is test-only, deliberately, and not an oversight — the two
|
|
31
|
+
* reasons are worth stating because from outside it reads like one:
|
|
32
|
+
* 1. Recognition requires a previously-seen SUITE_END shutdown marker, and
|
|
33
|
+
* `mach build` never prints one. Wiring the option into the build
|
|
34
|
+
* dispatch would therefore be a silent no-op.
|
|
35
|
+
* 2. Relaxing that gate for builds would be wrong on its own terms. The
|
|
36
|
+
* marker is what separates teardown noise from a traceback raised while
|
|
37
|
+
* work was still happening; a build has no equivalent boundary, so
|
|
38
|
+
* FireForge cannot tell the known-cosmetic case from a real build-time
|
|
39
|
+
* failure and must not withhold the block.
|
|
40
|
+
* The build path instead RECOGNIZES the signature and says so beside the
|
|
41
|
+
* verbatim traceback — see {@link KNOWN_TEARDOWN_NOISE_BUILD_NOTE} and
|
|
42
|
+
* `runProtectedMachBuild`.
|
|
29
43
|
*/
|
|
30
44
|
const TRACEBACK_HEADER_PATTERN = /^Traceback \(most recent call last\)/;
|
|
31
45
|
const CHAINED_EXCEPTION_CONNECTOR_PATTERN = /^(?:During handling of the above exception, another exception occurred:|The above exception was the direct cause of the following exception:)\s*$/;
|
|
@@ -43,15 +57,12 @@ const RESOURCEMONITOR_FRAME_PATTERN = /mozsystemmonitor[/\\]resourcemonitor\.py/
|
|
|
43
57
|
* there; duplicated here with this cross-reference rather than exported).
|
|
44
58
|
*/
|
|
45
59
|
const SHUTDOWN_MARKER_PATTERN = /\bSUITE_END\b/;
|
|
46
|
-
/** Whole-block recognition — every signal must hold (see module doc). */
|
|
47
|
-
function isRecognizedTeardownNoise(block) {
|
|
48
|
-
return (KNOWN_TEARDOWN_ATTRIBUTE_ERROR_PATTERN.test(block) && RESOURCEMONITOR_FRAME_PATTERN.test(block));
|
|
49
|
-
}
|
|
50
60
|
/**
|
|
51
|
-
*
|
|
52
|
-
* teardown
|
|
61
|
+
* Whole-block recognition — every signal must hold (see module doc). True
|
|
62
|
+
* when CAPTURED output carries the documented mozsystemmonitor teardown
|
|
63
|
+
* traceback.
|
|
53
64
|
*
|
|
54
|
-
* The echo filter
|
|
65
|
+
* The echo filter below only affects what a human sees. The CLASSIFIER
|
|
55
66
|
* reads the raw capture, and it needs the same recognition to tell "the
|
|
56
67
|
* suite finished clean and then upstream fell over at shutdown" from "the
|
|
57
68
|
* suite did not finish". Deliberately the same two-signal test the echo
|
|
@@ -68,7 +79,8 @@ function isRecognizedTeardownNoise(block) {
|
|
|
68
79
|
* @returns True when the recognized teardown traceback is present
|
|
69
80
|
*/
|
|
70
81
|
export function hasKnownTeardownNoise(output) {
|
|
71
|
-
return
|
|
82
|
+
return (KNOWN_TEARDOWN_ATTRIBUTE_ERROR_PATTERN.test(output) &&
|
|
83
|
+
RESOURCEMONITOR_FRAME_PATTERN.test(output));
|
|
72
84
|
}
|
|
73
85
|
/**
|
|
74
86
|
* True when a SINGLE line is the documented teardown family's
|
|
@@ -93,9 +105,18 @@ export function isKnownTeardownNoiseLine(line) {
|
|
|
93
105
|
export function createTeardownNoiseContext() {
|
|
94
106
|
return { shutdownSeen: false };
|
|
95
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Build-phase note for the SAME recognized signature. The build path does
|
|
110
|
+
* not COLLAPSE the traceback (see the module doc's "test-only" note) — it
|
|
111
|
+
* prints verbatim and this line is added beside it, so one signature reads
|
|
112
|
+
* the same in both phases without a build losing output it may need.
|
|
113
|
+
*/
|
|
114
|
+
export const KNOWN_TEARDOWN_NOISE_BUILD_NOTE = '[FireForge] The mozsystemmonitor traceback above is the known upstream ' +
|
|
115
|
+
'SystemResourceMonitor AttributeError — cosmetic, and not a build failure. It is left ' +
|
|
116
|
+
'verbatim here (unlike a test run, which collapses it); see docs/testing.md.';
|
|
96
117
|
/** One line replaces the whole recognized traceback block in the echo. */
|
|
97
118
|
export const KNOWN_TEARDOWN_NOISE_ANNOTATION = '[FireForge] Known upstream mozsystemmonitor teardown noise (SystemResourceMonitor ' +
|
|
98
|
-
'AttributeError at harness shutdown) — not a test failure. See
|
|
119
|
+
'AttributeError at harness shutdown) — not a test failure. See docs/testing.md.\n';
|
|
99
120
|
/** Bounds on the held traceback block before flushing verbatim. */
|
|
100
121
|
const HOLD_LINE_LIMIT = 100;
|
|
101
122
|
const HOLD_BYTE_LIMIT = 16 * 1024;
|
|
@@ -136,7 +157,7 @@ export function createKnownTeardownNoiseFilter(context = createTeardownNoiseCont
|
|
|
136
157
|
const block = resetHold();
|
|
137
158
|
if (block.length === 0)
|
|
138
159
|
return '';
|
|
139
|
-
return context.shutdownSeen &&
|
|
160
|
+
return context.shutdownSeen && hasKnownTeardownNoise(block)
|
|
140
161
|
? KNOWN_TEARDOWN_NOISE_ANNOTATION
|
|
141
162
|
: block;
|
|
142
163
|
};
|
|
@@ -3,6 +3,7 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { MozconfigError } from '../errors/build.js';
|
|
4
4
|
import { pathExists, readText, writeTextIfChanged } from '../utils/fs.js';
|
|
5
5
|
import { verbose } from '../utils/logger.js';
|
|
6
|
+
import { normalizePathSlashes } from '../utils/paths.js';
|
|
6
7
|
import { getPlatform } from '../utils/platform.js';
|
|
7
8
|
import { BrandingMozconfigMismatchError, splitAppId } from './branding.js';
|
|
8
9
|
/**
|
|
@@ -96,7 +97,7 @@ export async function assertBrandingMozconfigAgreement(engineDir, mozconfigPath,
|
|
|
96
97
|
// Normalise both sides to forward slashes before compare — Windows-edited
|
|
97
98
|
// configs can carry backslash path separators that the build would treat
|
|
98
99
|
// as literal characters in a repo-relative path.
|
|
99
|
-
const normalizedFound = found
|
|
100
|
+
const normalizedFound = normalizePathSlashes(found);
|
|
100
101
|
if (normalizedFound !== expected) {
|
|
101
102
|
throw new BrandingMozconfigMismatchError(expected, found, 'name-mismatch');
|
|
102
103
|
}
|
|
@@ -6,7 +6,12 @@ import { pathExists, readText } from '../utils/fs.js';
|
|
|
6
6
|
import { verbose } from '../utils/logger.js';
|
|
7
7
|
import { exec, executableExists } from '../utils/process.js';
|
|
8
8
|
/** Cached Python resolution state */
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* The resolved interpreter and the requirement range it was resolved for.
|
|
11
|
+
* `undefined` means "not resolved yet"; the two always move together, so one
|
|
12
|
+
* nullable record beats two independently-deletable fields.
|
|
13
|
+
*/
|
|
14
|
+
let pythonCache;
|
|
10
15
|
const DEFAULT_MACH_PYTHON_REQUIREMENTS = {
|
|
11
16
|
min: { major: 3, minor: 8 },
|
|
12
17
|
max: { major: 3, minor: 12 },
|
|
@@ -73,10 +78,11 @@ function buildPythonCandidates(requirements) {
|
|
|
73
78
|
}
|
|
74
79
|
/**
|
|
75
80
|
* Resets the resolved Python executable. Primarily useful for testing.
|
|
81
|
+
*
|
|
82
|
+
* @internal Exported only so tests can reach it; not part of the public surface.
|
|
76
83
|
*/
|
|
77
84
|
export function resetResolvedPython() {
|
|
78
|
-
|
|
79
|
-
delete pythonCache.requirementsKey;
|
|
85
|
+
pythonCache = undefined;
|
|
80
86
|
}
|
|
81
87
|
/**
|
|
82
88
|
* Dynamically resolves the python executable and ensures it is available.
|
|
@@ -86,7 +92,7 @@ export function resetResolvedPython() {
|
|
|
86
92
|
export async function ensurePython(engineDir) {
|
|
87
93
|
const requirements = await readMachPythonRequirements(engineDir);
|
|
88
94
|
const requirementsKey = formatRequirementsKey(requirements);
|
|
89
|
-
if (pythonCache
|
|
95
|
+
if (pythonCache?.requirementsKey === requirementsKey) {
|
|
90
96
|
return;
|
|
91
97
|
}
|
|
92
98
|
const candidates = buildPythonCandidates(requirements);
|
|
@@ -99,8 +105,7 @@ export async function ensurePython(engineDir) {
|
|
|
99
105
|
]);
|
|
100
106
|
const version = parsePythonVersion(stdout);
|
|
101
107
|
if (version && isVersionWithinRequirements(version, requirements)) {
|
|
102
|
-
pythonCache
|
|
103
|
-
pythonCache.requirementsKey = requirementsKey;
|
|
108
|
+
pythonCache = { python: candidate, requirementsKey };
|
|
104
109
|
return;
|
|
105
110
|
}
|
|
106
111
|
}
|
|
@@ -118,7 +123,7 @@ export async function ensurePython(engineDir) {
|
|
|
118
123
|
*/
|
|
119
124
|
export async function getPython(engineDir) {
|
|
120
125
|
await ensurePython(engineDir);
|
|
121
|
-
if (
|
|
126
|
+
if (pythonCache === undefined) {
|
|
122
127
|
throw new PythonNotFoundError();
|
|
123
128
|
}
|
|
124
129
|
return pythonCache.python;
|