@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
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
* regression test (mach-resource-shim.python.test.ts), which asserts the
|
|
72
72
|
* degraded readings' runtime behavior rather than string-matching the source.
|
|
73
73
|
*/
|
|
74
|
-
export declare const GUARD_PYTHON_SOURCE = "# Generated by FireForge - do not edit.\n# Degrades the broken host resource monitor family (psutil vs Darwin) from\n# fatal startup errors into non-fatal warnings, so mach builds and tests\n# proceed. Installed into the mach virtualenv site-packages via a .pth file\n# (survives mach's venv re-exec, which drops PYTHONPATH).\nimport builtins\nimport collections\nimport sys\nimport warnings\n\n# Any psutil degradation observed in this process. Once set, monitors are\n# kept inert (start never spawns the collector child) \u2014 on a flapping host a\n# healthy-looking moment must not be trusted to start a sample stream that a\n# later degraded reading could malform.\n_fireforge_host_degraded = [False]\n\n\ndef _fireforge_degraded_notice(exc):\n _fireforge_host_degraded[0] = True\n warnings.warn(\n \"FireForge: host resource monitor degraded (%s); continuing without resource monitoring.\" % exc\n )\n\n\nclass _DegradedReading(object):\n # Last-resort duck type of psutil's namedtuple results (svmem field\n # order): mozsystemmonitor subscripts, iterates, and unpacks readings,\n # so the degraded fallback must survive r[0], list(r), len(r), r._fields\n # and r._asdict(), not just attribute access. The parent also reconstructs\n # readings via type(reading)(*values), so the constructor must tolerate\n # the full positional field list. Normal\n # degraded paths use the per-function namedtuple fallbacks below; this\n # class only backstops a fallback that itself raised.\n _fields = (\"total\", \"available\", \"percent\", \"used\", \"free\", \"active\", \"inactive\", \"wired\")\n total = available = used = free = active = inactive = wired = 0\n percent = 0.0\n\n def __init__(self, *_args, **_kwargs):\n pass\n\n def __getattr__(self, _name):\n return 0\n\n def _values(self):\n return tuple(getattr(self, _field) for _field in self._fields)\n\n def __getitem__(self, index):\n return self._values()[index]\n\n def __iter__(self):\n return iter(self._values())\n\n def __len__(self):\n return len(self._fields)\n\n def _asdict(self):\n return dict(zip(self._fields, self._values()))\n\n\n# Per-function fallback result classes with the exact macOS field\n# orders/arities. Module-level on purpose: readings cross the\n# mozsystemmonitor collector pipe via pickle (by reference), and the parent\n# rebuilds each sample with type(reading)(*values) \u2014 so a swap fallback must\n# be 6-field sswap-shaped in every position. An svmem-shaped fallback there\n# makes the parent reject every sample, fill the pipe, block the collector\n# child in send(), and wedge mach's atexit join forever.\n_FallbackVmem = collections.namedtuple(\n \"_FallbackVmem\",\n (\"total\", \"available\", \"percent\", \"used\", \"free\", \"active\", \"inactive\", \"wired\"),\n)\n_FallbackSwap = collections.namedtuple(\n \"_FallbackSwap\", (\"total\", \"used\", \"free\", \"percent\", \"sin\", \"sout\")\n)\n_FallbackCpuTimes = collections.namedtuple(\"_FallbackCpuTimes\", (\"user\", \"nice\", \"system\", \"idle\"))\n_FallbackDiskIO = collections.namedtuple(\n \"_FallbackDiskIO\",\n (\"read_count\", \"write_count\", \"read_bytes\", \"write_bytes\", \"read_time\", \"write_time\"),\n)\n\n_FALLBACK_CLASSES = {\n \"virtual_memory\": _FallbackVmem,\n \"swap_memory\": _FallbackSwap,\n \"cpu_times\": _FallbackCpuTimes,\n \"disk_io_counters\": _FallbackDiskIO,\n}\n\n# psutil result namedtuple per wrapped function, resolvable via getattr from\n# the platform module / _common without invoking the failing syscall.\n_PSUTIL_RESULT_CLASSES = {\n \"virtual_memory\": \"svmem\",\n \"swap_memory\": \"sswap\",\n \"cpu_times\": \"scputimes\",\n \"disk_io_counters\": \"sdiskio\",\n}\n\n\ndef _zeroed(cls):\n return cls(*([0] * len(cls._fields)))\n\n\ndef _percpu_requested(args, kwargs, positional_index):\n if \"percpu\" in kwargs:\n return bool(kwargs[\"percpu\"])\n return len(args) > positional_index and bool(args[positional_index])\n\n\ndef _degraded_result_factory(psutil, name):\n # cpu_percent returns a plain float (or a per-CPU list) callers do\n # arithmetic on.\n if name == \"cpu_percent\":\n def _cpu_percent_fallback(*args, **kwargs):\n # cpu_percent(interval=None, percpu=False)\n if _percpu_requested(args, kwargs, 1):\n return []\n return 0.0\n\n return _cpu_percent_fallback\n # The guard-owned class is always shape-correct for this function;\n # psutil's own result namedtuple is still preferred when resolvable so\n # type() captures stay identical to real readings on a flapping host.\n _cls = _FALLBACK_CLASSES.get(name, _DegradedReading)\n _cls_name = _PSUTIL_RESULT_CLASSES.get(name)\n if _cls_name is not None:\n for _mod in (getattr(psutil, \"_psplatform\", None), getattr(psutil, \"_common\", None)):\n _real = getattr(_mod, _cls_name, None) if _mod is not None else None\n if _real is not None and getattr(_real, \"_fields\", None):\n _cls = _real\n break\n if name == \"cpu_times\":\n def _cpu_times_fallback(*args, **kwargs):\n # cpu_times(percpu=False); the collector child samples per-CPU.\n if _percpu_requested(args, kwargs, 0):\n return []\n return _zeroed(_cls)\n\n return _cpu_times_fallback\n\n def _reading_fallback(*_args, **_kwargs):\n return _zeroed(_cls)\n\n return _reading_fallback\n\n\ndef _guard_psutil():\n try:\n import psutil\n except Exception:\n return\n\n def _wrap(orig, fallback):\n def wrapper(*args, **kwargs):\n try:\n return orig(*args, **kwargs)\n except Exception as exc: # noqa: BLE001\n _fireforge_degraded_notice(exc)\n try:\n return fallback(*args, **kwargs)\n except Exception: # noqa: BLE001\n return _DegradedReading()\n\n return wrapper\n\n for _name in (\"virtual_memory\", \"swap_memory\", \"cpu_percent\", \"cpu_times\", \"disk_io_counters\"):\n _orig = getattr(psutil, _name, None)\n if _orig is not None and not getattr(_orig, \"_fireforge_guarded\", False):\n try:\n _fallback = _degraded_result_factory(psutil, _name)\n except Exception: # noqa: BLE001\n _fallback = _DegradedReading\n _wrapped = _wrap(_orig, _fallback)\n _wrapped._fireforge_guarded = True\n setattr(psutil, _name, _wrapped)\n\n\n_MONITOR_METHOD_NAMES = (\n \"start\",\n \"stop\",\n \"record_event\",\n \"record_marker\",\n \"begin_phase\",\n \"finish_phase\",\n \"aggregate_cpu_percent\",\n \"aggregate_cpu_times\",\n \"aggregate_io\",\n \"min_memory_available\",\n \"as_dict\",\n)\n\n\ndef _fireforge_stop_collector(monitor):\n # Best-effort: terminate a live collector child and drain the parent end\n # of the pipe, so a child blocked in send() on a full pipe can never\n # keep mach's atexit join (os.waitpid) waiting forever. Attribute names\n # follow upstream resourcemonitor.py (self._process / self._pipe); a\n # miss on a refactored upstream is harmless \u2014 the shape-correct\n # fallbacks above still keep the sample stream well-formed.\n try:\n proc = getattr(monitor, \"_process\", None)\n if proc is not None and getattr(proc, \"is_alive\", lambda: False)():\n proc.terminate()\n proc.join(1)\n except Exception: # noqa: BLE001\n pass\n try:\n pipe = getattr(monitor, \"_pipe\", None)\n if pipe is not None:\n while pipe.poll(0):\n pipe.recv()\n except Exception: # noqa: BLE001\n pass\n\n\ndef _monitor_per_cpu_requested(args, kwargs):\n # aggregate_cpu_percent/aggregate_cpu_times(start=None, end=None,\n # phase=None, per_cpu=True) \u2014 self already stripped from args.\n if \"per_cpu\" in kwargs:\n return bool(kwargs[\"per_cpu\"])\n if len(args) > 3:\n return bool(args[3])\n return True\n\n\ndef _monitor_degraded_result(name, args, kwargs):\n # Degraded aggregate methods return zeroed shapes, not None, where\n # callers subscript the result: mozbuild's log_resource_usage does\n # usage[\"io\"].read_bytes after a successful compile, so None there fails\n # the build with complete artifacts already on disk.\n if name == \"aggregate_io\":\n return _zeroed(_FallbackDiskIO)\n if name == \"aggregate_cpu_percent\":\n return [] if _monitor_per_cpu_requested(args, kwargs) else 0.0\n if name == \"aggregate_cpu_times\":\n return [] if _monitor_per_cpu_requested(args, kwargs) else _zeroed(_FallbackCpuTimes)\n return None\n\n\ndef _patch_monitor_class(cls):\n if cls is None or getattr(cls, \"_fireforge_guarded\", False):\n return\n orig_init = cls.__init__\n\n def guarded_init(self, *args, **kwargs):\n # Pre-populate the attributes a partially-constructed instance is\n # later polled for, so a failing __init__ can never surface\n # AttributeError: poll_interval.\n self.poll_interval = kwargs.get(\"poll_interval\") or 1.0\n self._fireforge_degraded = False\n try:\n orig_init(self, *args, **kwargs)\n except Exception as exc: # noqa: BLE001\n self._fireforge_degraded = True\n _fireforge_degraded_notice(exc)\n if _fireforge_host_degraded[0] and not self._fireforge_degraded:\n # Host already degraded once: keep the monitor inert so the\n # collector child never spawns on a flapping host.\n self._fireforge_degraded = True\n if self._fireforge_degraded:\n _fireforge_stop_collector(self)\n\n cls.__init__ = guarded_init\n\n def _wrap_method(name, orig):\n def wrapper(self, *args, **kwargs):\n if (\n name == \"start\"\n and _fireforge_host_degraded[0]\n and not getattr(self, \"_fireforge_degraded\", False)\n ):\n self._fireforge_degraded = True\n _fireforge_stop_collector(self)\n if getattr(self, \"_fireforge_degraded\", False):\n if name == \"stop\":\n _fireforge_stop_collector(self)\n return _monitor_degraded_result(name, args, kwargs)\n try:\n return orig(self, *args, **kwargs)\n except Exception as exc: # noqa: BLE001\n self._fireforge_degraded = True\n _fireforge_degraded_notice(exc)\n # A raising stop (or any degraded transition) must still\n # take the collector child down, or the parent's atexit\n # join hangs on a child blocked writing to a full pipe.\n _fireforge_stop_collector(self)\n return _monitor_degraded_result(name, args, kwargs)\n\n return wrapper\n\n for _name in _MONITOR_METHOD_NAMES:\n _orig = getattr(cls, _name, None)\n if _orig is not None:\n setattr(cls, _name, _wrap_method(_name, _orig))\n cls._fireforge_guarded = True\n\n\ndef _patch_build_monitor_class(cls):\n # mozbuild's BuildMonitor is not a SystemResourceMonitor; only\n # log_resource_usage needs guarding \u2014 end-of-build resource reporting\n # must never fail a build whose artifacts are complete. No __init__\n # replacement, no degraded-instance gating.\n if cls is None or getattr(cls, \"_fireforge_guarded\", False):\n return\n _orig = getattr(cls, \"log_resource_usage\", None)\n if _orig is not None:\n def _guarded_log_resource_usage(self, *args, **kwargs):\n try:\n return _orig(self, *args, **kwargs)\n except Exception as exc: # noqa: BLE001\n _fireforge_degraded_notice(exc)\n return None\n\n cls.log_resource_usage = _guarded_log_resource_usage\n cls._fireforge_guarded = True\n\n\n_MONITOR_CLASS_PATCHES = (\n (\"mozsystemmonitor.resourcemonitor\", \"SystemResourceMonitor\", _patch_monitor_class),\n (\"mozbuild.resources\", \"SystemResourceMonitor\", _patch_monitor_class),\n (\"mozbuild.controller.building\", \"BuildMonitor\", _patch_build_monitor_class),\n)\n\n\ndef _patch_loaded_monitor_modules():\n for _mod_name, _cls_name, _patcher in _MONITOR_CLASS_PATCHES:\n _mod = sys.modules.get(_mod_name)\n if _mod is not None:\n try:\n _patcher(getattr(_mod, _cls_name, None))\n except Exception: # noqa: BLE001\n pass\n\n\ndef _install_import_hook():\n _orig_import = builtins.__import__\n if getattr(_orig_import, \"_fireforge_guarded\", False):\n return\n\n def _guarding_import(name, *args, **kwargs):\n _module = _orig_import(name, *args, **kwargs)\n try:\n _patch_loaded_monitor_modules()\n except Exception: # noqa: BLE001\n pass\n return _module\n\n _guarding_import._fireforge_guarded = True\n builtins.__import__ = _guarding_import\n\n\ntry:\n _guard_psutil()\n _patch_loaded_monitor_modules()\n _install_import_hook()\nexcept Exception: # noqa: BLE001\n pass\n";
|
|
74
|
+
export declare const GUARD_PYTHON_SOURCE = "# Generated by FireForge - do not edit.\n# Degrades the broken host resource monitor family (psutil vs Darwin) from\n# fatal startup errors into non-fatal warnings, so mach builds and tests\n# proceed. Installed into the mach virtualenv site-packages via a .pth file\n# (survives mach's venv re-exec, which drops PYTHONPATH).\nimport builtins\nimport collections\nimport inspect\nimport sys\nimport warnings\n\n# Any psutil degradation observed in this process. Once set, monitors are\n# kept inert (start never spawns the collector child) \u2014 on a flapping host a\n# healthy-looking moment must not be trusted to start a sample stream that a\n# later degraded reading could malform.\n_fireforge_host_degraded = [False]\n\n\ndef _fireforge_degraded_notice(exc):\n _fireforge_host_degraded[0] = True\n warnings.warn(\n \"FireForge: host resource monitor degraded (%s); continuing without resource monitoring.\" % exc\n )\n\n\nclass _DegradedReading(object):\n # Last-resort duck type of psutil's namedtuple results (svmem field\n # order): mozsystemmonitor subscripts, iterates, and unpacks readings,\n # so the degraded fallback must survive r[0], list(r), len(r), r._fields\n # and r._asdict(), not just attribute access. The parent also reconstructs\n # readings via type(reading)(*values), so the constructor must tolerate\n # the full positional field list. Normal\n # degraded paths use the per-function namedtuple fallbacks below; this\n # class only backstops a fallback that itself raised.\n _fields = (\"total\", \"available\", \"percent\", \"used\", \"free\", \"active\", \"inactive\", \"wired\")\n total = available = used = free = active = inactive = wired = 0\n percent = 0.0\n\n def __init__(self, *_args, **_kwargs):\n pass\n\n def __getattr__(self, _name):\n return 0\n\n def _values(self):\n return tuple(getattr(self, _field) for _field in self._fields)\n\n def __getitem__(self, index):\n return self._values()[index]\n\n def __iter__(self):\n return iter(self._values())\n\n def __len__(self):\n return len(self._fields)\n\n def _asdict(self):\n return dict(zip(self._fields, self._values()))\n\n\n# Per-function fallback result classes with the exact macOS field\n# orders/arities. Module-level on purpose: readings cross the\n# mozsystemmonitor collector pipe via pickle (by reference), and the parent\n# rebuilds each sample with type(reading)(*values) \u2014 so a swap fallback must\n# be 6-field sswap-shaped in every position. An svmem-shaped fallback there\n# makes the parent reject every sample, fill the pipe, block the collector\n# child in send(), and wedge mach's atexit join forever.\n_FallbackVmem = collections.namedtuple(\n \"_FallbackVmem\",\n (\"total\", \"available\", \"percent\", \"used\", \"free\", \"active\", \"inactive\", \"wired\"),\n)\n_FallbackSwap = collections.namedtuple(\n \"_FallbackSwap\", (\"total\", \"used\", \"free\", \"percent\", \"sin\", \"sout\")\n)\n_FallbackCpuTimes = collections.namedtuple(\"_FallbackCpuTimes\", (\"user\", \"nice\", \"system\", \"idle\"))\n_FallbackDiskIO = collections.namedtuple(\n \"_FallbackDiskIO\",\n (\"read_count\", \"write_count\", \"read_bytes\", \"write_bytes\", \"read_time\", \"write_time\"),\n)\n\n_FALLBACK_CLASSES = {\n \"virtual_memory\": _FallbackVmem,\n \"swap_memory\": _FallbackSwap,\n \"cpu_times\": _FallbackCpuTimes,\n \"disk_io_counters\": _FallbackDiskIO,\n}\n\n# psutil result namedtuple per wrapped function, resolvable via getattr from\n# the platform module / _common without invoking the failing syscall.\n_PSUTIL_RESULT_CLASSES = {\n \"virtual_memory\": \"svmem\",\n \"swap_memory\": \"sswap\",\n \"cpu_times\": \"scputimes\",\n \"disk_io_counters\": \"sdiskio\",\n}\n\n\ndef _zeroed(cls):\n return cls(*([0] * len(cls._fields)))\n\n\ndef _percpu_requested(args, kwargs, positional_index):\n if \"percpu\" in kwargs:\n return bool(kwargs[\"percpu\"])\n return len(args) > positional_index and bool(args[positional_index])\n\n\ndef _degraded_result_factory(psutil, name):\n # cpu_percent returns a plain float (or a per-CPU list) callers do\n # arithmetic on.\n if name == \"cpu_percent\":\n def _cpu_percent_fallback(*args, **kwargs):\n # cpu_percent(interval=None, percpu=False)\n if _percpu_requested(args, kwargs, 1):\n return []\n return 0.0\n\n return _cpu_percent_fallback\n # The guard-owned class is always shape-correct for this function;\n # psutil's own result namedtuple is still preferred when resolvable so\n # type() captures stay identical to real readings on a flapping host.\n _cls = _FALLBACK_CLASSES.get(name, _DegradedReading)\n _cls_name = _PSUTIL_RESULT_CLASSES.get(name)\n if _cls_name is not None:\n for _mod in (getattr(psutil, \"_psplatform\", None), getattr(psutil, \"_common\", None)):\n _real = getattr(_mod, _cls_name, None) if _mod is not None else None\n if _real is not None and getattr(_real, \"_fields\", None):\n _cls = _real\n break\n if name == \"cpu_times\":\n def _cpu_times_fallback(*args, **kwargs):\n # cpu_times(percpu=False); the collector child samples per-CPU.\n if _percpu_requested(args, kwargs, 0):\n return []\n return _zeroed(_cls)\n\n return _cpu_times_fallback\n\n def _reading_fallback(*_args, **_kwargs):\n return _zeroed(_cls)\n\n return _reading_fallback\n\n\ndef _guard_psutil():\n try:\n import psutil\n except Exception:\n return\n\n def _wrap(orig, fallback):\n def wrapper(*args, **kwargs):\n try:\n return orig(*args, **kwargs)\n except Exception as exc: # noqa: BLE001\n _fireforge_degraded_notice(exc)\n try:\n return fallback(*args, **kwargs)\n except Exception: # noqa: BLE001\n return _DegradedReading()\n\n return wrapper\n\n for _name in (\"virtual_memory\", \"swap_memory\", \"cpu_percent\", \"cpu_times\", \"disk_io_counters\"):\n _orig = getattr(psutil, _name, None)\n if _orig is not None and not getattr(_orig, \"_fireforge_guarded\", False):\n try:\n _fallback = _degraded_result_factory(psutil, _name)\n except Exception: # noqa: BLE001\n _fallback = _DegradedReading\n _wrapped = _wrap(_orig, _fallback)\n _wrapped._fireforge_guarded = True\n setattr(psutil, _name, _wrapped)\n\n\n_MONITOR_METHOD_NAMES = (\n \"start\",\n \"stop\",\n \"record_event\",\n \"record_marker\",\n \"begin_phase\",\n \"finish_phase\",\n \"aggregate_cpu_percent\",\n \"aggregate_cpu_times\",\n \"aggregate_io\",\n \"min_memory_available\",\n \"as_dict\",\n)\n\n\ndef _fireforge_stop_collector(monitor):\n # Best-effort: terminate a live collector child and drain the parent end\n # of the pipe, so a child blocked in send() on a full pipe can never\n # keep mach's atexit join (os.waitpid) waiting forever. Attribute names\n # follow upstream resourcemonitor.py (self._process / self._pipe); a\n # miss on a refactored upstream is harmless \u2014 the shape-correct\n # fallbacks above still keep the sample stream well-formed.\n try:\n proc = getattr(monitor, \"_process\", None)\n if proc is not None and getattr(proc, \"is_alive\", lambda: False)():\n proc.terminate()\n proc.join(1)\n except Exception: # noqa: BLE001\n pass\n try:\n pipe = getattr(monitor, \"_pipe\", None)\n if pipe is not None:\n while pipe.poll(0):\n pipe.recv()\n except Exception: # noqa: BLE001\n pass\n\n\ndef _monitor_per_cpu_requested(args, kwargs):\n # aggregate_cpu_percent/aggregate_cpu_times(start=None, end=None,\n # phase=None, per_cpu=True) \u2014 self already stripped from args.\n if \"per_cpu\" in kwargs:\n return bool(kwargs[\"per_cpu\"])\n if len(args) > 3:\n return bool(args[3])\n return True\n\n\ndef _monitor_degraded_result(name, args, kwargs):\n # Degraded aggregate methods return zeroed shapes, not None, where\n # callers subscript the result: mozbuild's log_resource_usage does\n # usage[\"io\"].read_bytes after a successful compile, so None there fails\n # the build with complete artifacts already on disk.\n if name == \"aggregate_io\":\n return _zeroed(_FallbackDiskIO)\n if name == \"aggregate_cpu_percent\":\n return [] if _monitor_per_cpu_requested(args, kwargs) else 0.0\n if name == \"aggregate_cpu_times\":\n return [] if _monitor_per_cpu_requested(args, kwargs) else _zeroed(_FallbackCpuTimes)\n return None\n\n\ndef _patch_monitor_class(cls):\n if cls is None or getattr(cls, \"_fireforge_guarded\", False):\n return\n orig_init = cls.__init__\n\n def guarded_init(self, *args, **kwargs):\n # Pre-populate the attributes a partially-constructed instance is\n # later polled for, so a failing __init__ can never surface\n # AttributeError: poll_interval.\n self.poll_interval = kwargs.get(\"poll_interval\") or 1.0\n self._fireforge_degraded = False\n try:\n orig_init(self, *args, **kwargs)\n except Exception as exc: # noqa: BLE001\n self._fireforge_degraded = True\n _fireforge_degraded_notice(exc)\n if _fireforge_host_degraded[0] and not self._fireforge_degraded:\n # Host already degraded once: keep the monitor inert so the\n # collector child never spawns on a flapping host.\n self._fireforge_degraded = True\n if self._fireforge_degraded:\n _fireforge_stop_collector(self)\n\n cls.__init__ = guarded_init\n\n def _wrap_method(name, orig):\n def wrapper(self, *args, **kwargs):\n if (\n name == \"start\"\n and _fireforge_host_degraded[0]\n and not getattr(self, \"_fireforge_degraded\", False)\n ):\n self._fireforge_degraded = True\n _fireforge_stop_collector(self)\n if getattr(self, \"_fireforge_degraded\", False):\n if name == \"stop\":\n _fireforge_stop_collector(self)\n return _monitor_degraded_result(name, args, kwargs)\n try:\n return orig(self, *args, **kwargs)\n except Exception as exc: # noqa: BLE001\n self._fireforge_degraded = True\n _fireforge_degraded_notice(exc)\n # A raising stop (or any degraded transition) must still\n # take the collector child down, or the parent's atexit\n # join hangs on a child blocked writing to a full pipe.\n _fireforge_stop_collector(self)\n return _monitor_degraded_result(name, args, kwargs)\n\n return wrapper\n\n def _wrap_unbound(name, func):\n # Upstream declares record_event/record_marker as @staticmethods (and\n # may declare others as @classmethods). Those take no receiver, so\n # they cannot use the per-instance degraded flag and must not have one\n # threaded in \u2014 see the descriptor dispatch below.\n def wrapper(*args, **kwargs):\n if _fireforge_host_degraded[0]:\n return _monitor_degraded_result(name, args, kwargs)\n try:\n return func(*args, **kwargs)\n except Exception as exc: # noqa: BLE001\n _fireforge_degraded_notice(exc)\n return _monitor_degraded_result(name, args, kwargs)\n\n return wrapper\n\n for _name in _MONITOR_METHOD_NAMES:\n # getattr_static returns the raw class attribute, so a staticmethod\n # is still a staticmethod object here. Plain getattr would hand back\n # the underlying function, and re-setting THAT on the class rebinds\n # it as an instance method: the receiver is then passed through as an\n # extra leading positional and every call raises\n # \"takes N positional arguments but N+1 were given\". Re-apply the\n # descriptor the attribute actually had.\n try:\n _raw = inspect.getattr_static(cls, _name)\n except AttributeError:\n continue\n if isinstance(_raw, staticmethod):\n setattr(cls, _name, staticmethod(_wrap_unbound(_name, _raw.__func__)))\n continue\n if isinstance(_raw, classmethod):\n setattr(cls, _name, classmethod(_wrap_unbound(_name, _raw.__func__)))\n continue\n _orig = getattr(cls, _name, None)\n if _orig is not None:\n setattr(cls, _name, _wrap_method(_name, _orig))\n cls._fireforge_guarded = True\n\n\ndef _patch_build_monitor_class(cls):\n # mozbuild's BuildMonitor is not a SystemResourceMonitor; only\n # log_resource_usage needs guarding \u2014 end-of-build resource reporting\n # must never fail a build whose artifacts are complete. No __init__\n # replacement, no degraded-instance gating.\n if cls is None or getattr(cls, \"_fireforge_guarded\", False):\n return\n _orig = getattr(cls, \"log_resource_usage\", None)\n if _orig is not None:\n def _guarded_log_resource_usage(self, *args, **kwargs):\n try:\n return _orig(self, *args, **kwargs)\n except Exception as exc: # noqa: BLE001\n _fireforge_degraded_notice(exc)\n return None\n\n cls.log_resource_usage = _guarded_log_resource_usage\n cls._fireforge_guarded = True\n\n\n_MONITOR_CLASS_PATCHES = (\n (\"mozsystemmonitor.resourcemonitor\", \"SystemResourceMonitor\", _patch_monitor_class),\n (\"mozbuild.resources\", \"SystemResourceMonitor\", _patch_monitor_class),\n (\"mozbuild.controller.building\", \"BuildMonitor\", _patch_build_monitor_class),\n)\n\n\ndef _patch_loaded_monitor_modules():\n for _mod_name, _cls_name, _patcher in _MONITOR_CLASS_PATCHES:\n _mod = sys.modules.get(_mod_name)\n if _mod is not None:\n try:\n _patcher(getattr(_mod, _cls_name, None))\n except Exception: # noqa: BLE001\n pass\n\n\ndef _install_import_hook():\n _orig_import = builtins.__import__\n if getattr(_orig_import, \"_fireforge_guarded\", False):\n return\n\n def _guarding_import(name, *args, **kwargs):\n _module = _orig_import(name, *args, **kwargs)\n try:\n _patch_loaded_monitor_modules()\n except Exception: # noqa: BLE001\n pass\n return _module\n\n _guarding_import._fireforge_guarded = True\n builtins.__import__ = _guarding_import\n\n\ntry:\n _guard_psutil()\n _patch_loaded_monitor_modules()\n _install_import_hook()\nexcept Exception: # noqa: BLE001\n pass\n";
|
|
75
75
|
/** Result of installing the mach resource guard before a dispatch. */
|
|
76
76
|
export interface MachResourceGuardInstallation {
|
|
77
77
|
/**
|
|
@@ -83,6 +83,18 @@ export interface MachResourceGuardInstallation {
|
|
|
83
83
|
/** site-packages directories the in-venv guard was written into. */
|
|
84
84
|
sitePackagesDirs: string[];
|
|
85
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Refuses a shim directory that is not a private directory of the current
|
|
88
|
+
* user. `lstat`, not `stat`: a symlink planted at the path would otherwise
|
|
89
|
+
* be followed and the check would describe its target. On Windows only the
|
|
90
|
+
* "is a real directory" half applies — there is no POSIX owner or mode bit
|
|
91
|
+
* to read, and the per-user name is the isolation.
|
|
92
|
+
*
|
|
93
|
+
* @param dir - Shim directory that {@link ensureSitecustomizeFallback} created
|
|
94
|
+
* @throws BuildError when the directory is a symlink/file, owned by another
|
|
95
|
+
* uid, or group/world-writable
|
|
96
|
+
*/
|
|
97
|
+
export declare function assertPrivateShimDir(dir: string): Promise<void>;
|
|
86
98
|
/**
|
|
87
99
|
* Installs the resource-monitor degrade guard for a mach dispatch:
|
|
88
100
|
* `fireforge_mach_guard.pth` + `fireforge_mach_guard.py` into every
|
|
@@ -63,13 +63,20 @@
|
|
|
63
63
|
* warn-and-continue on any exception, so end-of-build resource reporting
|
|
64
64
|
* can never fail a build whose artifacts are complete.
|
|
65
65
|
*/
|
|
66
|
-
import { readdir } from 'node:fs/promises';
|
|
67
|
-
import { homedir, tmpdir } from 'node:os';
|
|
66
|
+
import { lstat, mkdir, readdir } from 'node:fs/promises';
|
|
67
|
+
import { homedir, tmpdir, userInfo } from 'node:os';
|
|
68
68
|
import { delimiter, join } from 'node:path';
|
|
69
|
+
import { BuildError } from '../errors/build.js';
|
|
69
70
|
import { toError } from '../utils/errors.js';
|
|
70
|
-
import {
|
|
71
|
+
import { pathExists, writeText } from '../utils/fs.js';
|
|
71
72
|
import { verbose } from '../utils/logger.js';
|
|
72
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* Prefix of the per-user directory (under the OS temp dir) holding the
|
|
75
|
+
* PYTHONPATH fallback. The uid (or, on Windows, the username) is appended:
|
|
76
|
+
* a FIXED name under a shared `/tmp` lets any other local account pre-create
|
|
77
|
+
* the directory and own the `sitecustomize.py` that every mach dispatch
|
|
78
|
+
* then imports.
|
|
79
|
+
*/
|
|
73
80
|
const RESOURCE_SHIM_DIRNAME = 'fireforge-mach-resource-shim';
|
|
74
81
|
/** Basename (sans extension) of the in-venv guard module. */
|
|
75
82
|
const GUARD_MODULE_NAME = 'fireforge_mach_guard';
|
|
@@ -89,6 +96,7 @@ export const GUARD_PYTHON_SOURCE = `# Generated by FireForge - do not edit.
|
|
|
89
96
|
# (survives mach's venv re-exec, which drops PYTHONPATH).
|
|
90
97
|
import builtins
|
|
91
98
|
import collections
|
|
99
|
+
import inspect
|
|
92
100
|
import sys
|
|
93
101
|
import warnings
|
|
94
102
|
|
|
@@ -369,7 +377,40 @@ def _patch_monitor_class(cls):
|
|
|
369
377
|
|
|
370
378
|
return wrapper
|
|
371
379
|
|
|
380
|
+
def _wrap_unbound(name, func):
|
|
381
|
+
# Upstream declares record_event/record_marker as @staticmethods (and
|
|
382
|
+
# may declare others as @classmethods). Those take no receiver, so
|
|
383
|
+
# they cannot use the per-instance degraded flag and must not have one
|
|
384
|
+
# threaded in — see the descriptor dispatch below.
|
|
385
|
+
def wrapper(*args, **kwargs):
|
|
386
|
+
if _fireforge_host_degraded[0]:
|
|
387
|
+
return _monitor_degraded_result(name, args, kwargs)
|
|
388
|
+
try:
|
|
389
|
+
return func(*args, **kwargs)
|
|
390
|
+
except Exception as exc: # noqa: BLE001
|
|
391
|
+
_fireforge_degraded_notice(exc)
|
|
392
|
+
return _monitor_degraded_result(name, args, kwargs)
|
|
393
|
+
|
|
394
|
+
return wrapper
|
|
395
|
+
|
|
372
396
|
for _name in _MONITOR_METHOD_NAMES:
|
|
397
|
+
# getattr_static returns the raw class attribute, so a staticmethod
|
|
398
|
+
# is still a staticmethod object here. Plain getattr would hand back
|
|
399
|
+
# the underlying function, and re-setting THAT on the class rebinds
|
|
400
|
+
# it as an instance method: the receiver is then passed through as an
|
|
401
|
+
# extra leading positional and every call raises
|
|
402
|
+
# "takes N positional arguments but N+1 were given". Re-apply the
|
|
403
|
+
# descriptor the attribute actually had.
|
|
404
|
+
try:
|
|
405
|
+
_raw = inspect.getattr_static(cls, _name)
|
|
406
|
+
except AttributeError:
|
|
407
|
+
continue
|
|
408
|
+
if isinstance(_raw, staticmethod):
|
|
409
|
+
setattr(cls, _name, staticmethod(_wrap_unbound(_name, _raw.__func__)))
|
|
410
|
+
continue
|
|
411
|
+
if isinstance(_raw, classmethod):
|
|
412
|
+
setattr(cls, _name, classmethod(_wrap_unbound(_name, _raw.__func__)))
|
|
413
|
+
continue
|
|
373
414
|
_orig = getattr(cls, _name, None)
|
|
374
415
|
if _orig is not None:
|
|
375
416
|
setattr(cls, _name, _wrap_method(_name, _orig))
|
|
@@ -488,13 +529,75 @@ async function discoverMachSitePackages(engineDir) {
|
|
|
488
529
|
return found;
|
|
489
530
|
}
|
|
490
531
|
/**
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
532
|
+
* Per-user discriminator for the shim directory name. The numeric uid on
|
|
533
|
+
* POSIX, read via `process.getuid` rather than `os.userInfo()`: the latter
|
|
534
|
+
* resolves the passwd entry and THROWS (`ERR_SYSTEM_ERROR`) for a uid that
|
|
535
|
+
* has none — `docker --user 1001:1001`, OpenShift's random uids, many CI
|
|
536
|
+
* agents — which would take every build and test dispatch down with a raw
|
|
537
|
+
* system error. On Windows there is no uid; the username is the
|
|
538
|
+
* discriminator, with `userInfo()` still guarded because the same lookup
|
|
539
|
+
* can fail there for a service account, falling back to the environment.
|
|
540
|
+
*/
|
|
541
|
+
function shimDirOwner() {
|
|
542
|
+
const uid = process.getuid?.();
|
|
543
|
+
if (uid !== undefined)
|
|
544
|
+
return String(uid);
|
|
545
|
+
let username;
|
|
546
|
+
try {
|
|
547
|
+
username = userInfo().username;
|
|
548
|
+
}
|
|
549
|
+
catch {
|
|
550
|
+
username = process.env['USERNAME'] ?? process.env['USER'];
|
|
551
|
+
}
|
|
552
|
+
return (username ?? 'unknown-user').replace(/[^A-Za-z0-9_.-]/g, '_');
|
|
553
|
+
}
|
|
554
|
+
/** Per-user shim directory: `<tmpdir>/fireforge-mach-resource-shim-<uid>`. */
|
|
555
|
+
function resourceShimDir() {
|
|
556
|
+
return join(tmpdir(), `${RESOURCE_SHIM_DIRNAME}-${shimDirOwner()}`);
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Refuses a shim directory that is not a private directory of the current
|
|
560
|
+
* user. `lstat`, not `stat`: a symlink planted at the path would otherwise
|
|
561
|
+
* be followed and the check would describe its target. On Windows only the
|
|
562
|
+
* "is a real directory" half applies — there is no POSIX owner or mode bit
|
|
563
|
+
* to read, and the per-user name is the isolation.
|
|
564
|
+
*
|
|
565
|
+
* @param dir - Shim directory that {@link ensureSitecustomizeFallback} created
|
|
566
|
+
* @throws BuildError when the directory is a symlink/file, owned by another
|
|
567
|
+
* uid, or group/world-writable
|
|
568
|
+
*/
|
|
569
|
+
export async function assertPrivateShimDir(dir) {
|
|
570
|
+
const stats = await lstat(dir);
|
|
571
|
+
const refuse = (reason) => {
|
|
572
|
+
throw new BuildError(`Refusing to use mach resource shim directory ${dir}: ${reason}. ` +
|
|
573
|
+
'Another local account may have planted it; remove or fix it and retry.');
|
|
574
|
+
};
|
|
575
|
+
if (!stats.isDirectory()) {
|
|
576
|
+
refuse('not a directory (symlink or file)');
|
|
577
|
+
}
|
|
578
|
+
if (process.platform === 'win32')
|
|
579
|
+
return;
|
|
580
|
+
const uid = process.getuid?.();
|
|
581
|
+
if (uid !== undefined && stats.uid !== uid) {
|
|
582
|
+
refuse(`owned by uid ${stats.uid}, not the current user (uid ${uid})`);
|
|
583
|
+
}
|
|
584
|
+
if ((stats.mode & 0o022) !== 0) {
|
|
585
|
+
refuse(`mode ${(stats.mode & 0o777).toString(8)} is group/world-writable`);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Writes the PYTHONPATH `sitecustomize.py` fallback into a per-user,
|
|
590
|
+
* mode-0700 FireForge-owned temp directory, verified private before the
|
|
591
|
+
* write, and returns that directory. Idempotent — overwriting each call
|
|
592
|
+
* keeps it in sync with this source across upgrades.
|
|
494
593
|
*/
|
|
495
594
|
async function ensureSitecustomizeFallback() {
|
|
496
|
-
const dir =
|
|
497
|
-
|
|
595
|
+
const dir = resourceShimDir();
|
|
596
|
+
// `mode` only applies when mkdir creates the directory (and is masked by
|
|
597
|
+
// the umask); a pre-existing one keeps whatever it had, which is exactly
|
|
598
|
+
// what the assertion below refuses if it is not private.
|
|
599
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
600
|
+
await assertPrivateShimDir(dir);
|
|
498
601
|
await writeText(join(dir, 'sitecustomize.py'), GUARD_PYTHON_SOURCE);
|
|
499
602
|
return dir;
|
|
500
603
|
}
|
package/dist/src/core/mach.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type SmokeLineCallback, type SmokeRunResult } from '../utils/process.js
|
|
|
2
2
|
import { type HarnessCrashSignature } from './test-harness-crash.js';
|
|
3
3
|
export { attemptMozinfoRewrite, buildArtifactMismatchMessage, hasBuildArtifacts, hasRunnableBundle, } from './mach-build-artifacts.js';
|
|
4
4
|
export { generateMozconfig } from './mach-mozconfig.js';
|
|
5
|
-
export { ensurePython
|
|
5
|
+
export { ensurePython } from './mach-python.js';
|
|
6
6
|
/**
|
|
7
7
|
* Ensures mach is available in the engine directory.
|
|
8
8
|
* @param engineDir - Path to the engine directory
|
|
@@ -23,6 +23,13 @@ export interface MachOptions {
|
|
|
23
23
|
* stdout/stderr stay raw — the harness classifier depends on the raw
|
|
24
24
|
* traceback. Unrecognized tracebacks always echo verbatim. Opted into by
|
|
25
25
|
* the test dispatchers only.
|
|
26
|
+
*
|
|
27
|
+
* Consumed by {@link runMachCapture} ALONE. `runMachInheritCapture` accepts
|
|
28
|
+
* it in the type and ignores it — the inherit path has no per-chunk hook,
|
|
29
|
+
* only a mirror stream that feeds the terminal and the run log the same
|
|
30
|
+
* string, so filtering there would strip the traceback from the raw log
|
|
31
|
+
* too. The build path uses the recognition NOTE instead; see
|
|
32
|
+
* `mach-known-noise-filter.ts`.
|
|
26
33
|
*/
|
|
27
34
|
annotateKnownTeardownNoise?: boolean;
|
|
28
35
|
/**
|
|
@@ -59,12 +66,6 @@ export declare function runMachCapture(args: string[], engineDir: string, option
|
|
|
59
66
|
* capturing stdout/stderr for post-run diagnostics.
|
|
60
67
|
*/
|
|
61
68
|
export declare function runMachInheritCapture(args: string[], engineDir: string, options?: Omit<MachOptions, 'inherit'>): Promise<MachCommandResult>;
|
|
62
|
-
/**
|
|
63
|
-
* Runs mach bootstrap to install build dependencies.
|
|
64
|
-
* @param engineDir - Path to the engine directory
|
|
65
|
-
* @returns Exit code
|
|
66
|
-
*/
|
|
67
|
-
export declare function bootstrap(engineDir: string): Promise<number>;
|
|
68
69
|
/**
|
|
69
70
|
* Runs mach bootstrap while preserving stdin and capturing the emitted output.
|
|
70
71
|
* @param engineDir - Path to the engine directory
|
|
@@ -190,29 +191,15 @@ export interface RunMachSmokeOptions {
|
|
|
190
191
|
* the runner to chrome-specific pattern logic.
|
|
191
192
|
*/
|
|
192
193
|
export declare function runMachSmoke(args: string[], engineDir: string, options: RunMachSmokeOptions): Promise<SmokeRunResult>;
|
|
193
|
-
/**
|
|
194
|
-
* Creates a distribution package.
|
|
195
|
-
* @param engineDir - Path to the engine directory
|
|
196
|
-
* @returns Exit code
|
|
197
|
-
*/
|
|
198
|
-
export declare function machPackage(engineDir: string): Promise<number>;
|
|
199
194
|
/**
|
|
200
195
|
* Creates a distribution package while streaming output to the terminal
|
|
201
|
-
* and capturing the stderr tail for post-run diagnostics
|
|
202
|
-
*
|
|
203
|
-
* variant; the inherit-only `machPackage` above remains for callers that
|
|
204
|
-
* just need an exit code.
|
|
196
|
+
* and capturing the stderr tail for post-run diagnostics, so callers can
|
|
197
|
+
* consult {@link explainMachError} on failure.
|
|
205
198
|
*
|
|
206
199
|
* @param engineDir - Path to the engine directory
|
|
207
200
|
* @returns Captured mach result (stdout tail, stderr tail, exit code)
|
|
208
201
|
*/
|
|
209
202
|
export declare function machPackageCapture(engineDir: string): Promise<MachCommandResult>;
|
|
210
|
-
/**
|
|
211
|
-
* Runs mach watch for auto-rebuilding.
|
|
212
|
-
* @param engineDir - Path to the engine directory
|
|
213
|
-
* @returns Exit code
|
|
214
|
-
*/
|
|
215
|
-
export declare function watch(engineDir: string): Promise<number>;
|
|
216
203
|
/**
|
|
217
204
|
* Runs mach watch while preserving stdin and capturing emitted output.
|
|
218
205
|
*
|
|
@@ -231,38 +218,44 @@ export declare function watchWithOutput(engineDir: string, options?: {
|
|
|
231
218
|
env?: Record<string, string>;
|
|
232
219
|
}): Promise<MachCommandResult>;
|
|
233
220
|
/**
|
|
234
|
-
*
|
|
235
|
-
* @param engineDir - Path to the engine directory
|
|
236
|
-
* @param testPaths - Test file or directory paths (relative to engine)
|
|
237
|
-
* @param args - Additional arguments to pass to mach test
|
|
238
|
-
* @returns Exit code
|
|
239
|
-
*/
|
|
240
|
-
export declare function test(engineDir: string, testPaths?: string[], args?: string[]): Promise<number>;
|
|
241
|
-
/**
|
|
242
|
-
* Runs mach test while capturing streamed output for better diagnostics.
|
|
221
|
+
* Which mach test command a capturing test dispatch runs.
|
|
243
222
|
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
223
|
+
* - `test`: the generic `mach test` (mixed/all-tests runs, or the
|
|
224
|
+
* `--generic-mach-test` opt-out).
|
|
225
|
+
* - `xpcshell-test` / `mochitest`: the suite-specific commands. Unlike the
|
|
226
|
+
* generic `mach test`, these degrade a broken mozlog resource monitor to a
|
|
227
|
+
* warning instead of crashing at startup, so `fireforge test` dispatches
|
|
228
|
+
* single-suite runs to them to stay resilient to the host psutil failure.
|
|
250
229
|
*/
|
|
251
|
-
export
|
|
230
|
+
export type MachTestSuiteKind = 'test' | 'xpcshell-test' | 'mochitest';
|
|
231
|
+
/** Options for {@link runMachTestSuite}. */
|
|
232
|
+
export interface MachTestSuiteOptions {
|
|
233
|
+
/** Absolute path to the engine checkout. */
|
|
234
|
+
engineDir: string;
|
|
235
|
+
/** Test paths to pass to the mach command; empty runs the default set. */
|
|
236
|
+
testPaths?: string[];
|
|
237
|
+
/** Extra mach arguments appended after the paths. */
|
|
238
|
+
args?: string[];
|
|
239
|
+
/**
|
|
240
|
+
* Extra environment variables for the mach process (merged over
|
|
241
|
+
* `process.env` by the exec layer). Used by `fireforge test
|
|
242
|
+
* --perf-samples` to publish the artifact-path contract.
|
|
243
|
+
*/
|
|
244
|
+
env?: Record<string, string> | undefined;
|
|
245
|
+
/**
|
|
246
|
+
* Pass full mozbuild verbosity through by unsetting the coding-agent
|
|
247
|
+
* markers that quiet it. See {@link CODING_AGENT_ENV_MARKERS}.
|
|
248
|
+
*/
|
|
249
|
+
fullOutput?: boolean | undefined;
|
|
250
|
+
}
|
|
252
251
|
/**
|
|
253
|
-
* Runs `mach
|
|
254
|
-
*
|
|
255
|
-
* commands
|
|
256
|
-
*
|
|
257
|
-
*
|
|
252
|
+
* Runs one mach test command (`mach test` / `mach xpcshell-test` / `mach
|
|
253
|
+
* mochitest`) while capturing streamed output for better diagnostics. The
|
|
254
|
+
* three commands share every other detail of the dispatch — resource guard,
|
|
255
|
+
* verbosity env, teardown-noise annotation — so they share one entry point
|
|
256
|
+
* keyed by `kind` rather than three copies that drift.
|
|
258
257
|
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*/
|
|
262
|
-
export declare function xpcshellTestWithOutput(engineDir: string, testPaths?: string[], args?: string[], env?: Record<string, string>, fullOutput?: boolean): Promise<MachCommandResult>;
|
|
263
|
-
/**
|
|
264
|
-
* Runs `mach mochitest` (covers browser-chrome / mochitest flavors) while
|
|
265
|
-
* capturing output. The suite-specific counterpart to {@link testWithOutput}
|
|
266
|
-
* for non-xpcshell single-suite runs — see {@link xpcshellTestWithOutput}.
|
|
258
|
+
* @param kind - Which mach command to run
|
|
259
|
+
* @param options - Engine checkout, test paths, extra args, env, verbosity
|
|
267
260
|
*/
|
|
268
|
-
export declare function
|
|
261
|
+
export declare function runMachTestSuite(kind: MachTestSuiteKind, options: MachTestSuiteOptions): Promise<MachCommandResult>;
|
package/dist/src/core/mach.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
import { basename, join } from 'node:path';
|
|
3
3
|
import { MachNotFoundError } from '../errors/build.js';
|
|
4
4
|
import { pathExists } from '../utils/fs.js';
|
|
5
|
-
import { warn } from '../utils/logger.js';
|
|
5
|
+
import { info, warn } from '../utils/logger.js';
|
|
6
6
|
import { exec, execInherit, execInheritCapture, execSmokeRun, execStream, } from '../utils/process.js';
|
|
7
7
|
import { createSiblingLockPath, withFileLock } from './file-lock.js';
|
|
8
8
|
import { ensureFirefoxIgnorefileCompatibility } from './firefox-ignorefile.js';
|
|
9
9
|
import { explainMachError } from './mach-error-hints.js';
|
|
10
|
-
import { createKnownTeardownNoiseFilter, createTeardownNoiseContext, } from './mach-known-noise-filter.js';
|
|
10
|
+
import { createKnownTeardownNoiseFilter, createTeardownNoiseContext, hasKnownTeardownNoise, KNOWN_TEARDOWN_NOISE_BUILD_NOTE, } from './mach-known-noise-filter.js';
|
|
11
11
|
import { getPython } from './mach-python.js';
|
|
12
12
|
import { installMachResourceGuard } from './mach-resource-shim.js';
|
|
13
13
|
import { teeToRunLog, writeToActiveRunLog } from './run-log.js';
|
|
@@ -20,7 +20,7 @@ import { detectHarnessCrashSignature } from './test-harness-crash.js';
|
|
|
20
20
|
// that mocks `mach.js`.
|
|
21
21
|
export { attemptMozinfoRewrite, buildArtifactMismatchMessage, hasBuildArtifacts, hasRunnableBundle, } from './mach-build-artifacts.js';
|
|
22
22
|
export { generateMozconfig } from './mach-mozconfig.js';
|
|
23
|
-
export { ensurePython
|
|
23
|
+
export { ensurePython } from './mach-python.js';
|
|
24
24
|
/**
|
|
25
25
|
* Ensures mach is available in the engine directory.
|
|
26
26
|
* @param engineDir - Path to the engine directory
|
|
@@ -149,14 +149,6 @@ export async function runMachInheritCapture(args, engineDir, options = {}) {
|
|
|
149
149
|
processGroup: true,
|
|
150
150
|
});
|
|
151
151
|
}
|
|
152
|
-
/**
|
|
153
|
-
* Runs mach bootstrap to install build dependencies.
|
|
154
|
-
* @param engineDir - Path to the engine directory
|
|
155
|
-
* @returns Exit code
|
|
156
|
-
*/
|
|
157
|
-
export async function bootstrap(engineDir) {
|
|
158
|
-
return runMach(['bootstrap', '--application-choice', 'browser'], engineDir, { inherit: true });
|
|
159
|
-
}
|
|
160
152
|
/**
|
|
161
153
|
* Runs mach bootstrap while preserving stdin and capturing the emitted output.
|
|
162
154
|
* @param engineDir - Path to the engine directory
|
|
@@ -193,6 +185,20 @@ function surfaceMachErrorHints(result) {
|
|
|
193
185
|
* the same crash family the same number of times.
|
|
194
186
|
*/
|
|
195
187
|
const DEFAULT_BUILD_CRASH_RETRIES = 2;
|
|
188
|
+
/**
|
|
189
|
+
* Names the known mozsystemmonitor teardown traceback when a BUILD carried
|
|
190
|
+
* it. The test path collapses this signature in the echo; a build cannot
|
|
191
|
+
* (see `mach-known-noise-filter.ts`), so it reached operators as an
|
|
192
|
+
* unexplained traceback in a build log — a recognized, documented upstream
|
|
193
|
+
* defect wearing the appearance of a new one. The output is left verbatim
|
|
194
|
+
* and this one line is added beside it, so the same signature reads the same
|
|
195
|
+
* in both phases.
|
|
196
|
+
*/
|
|
197
|
+
function noteKnownTeardownNoiseInBuild(result) {
|
|
198
|
+
if (!hasKnownTeardownNoise(`${result.stdout}\n${result.stderr}`))
|
|
199
|
+
return;
|
|
200
|
+
info(KNOWN_TEARDOWN_NOISE_BUILD_NOTE);
|
|
201
|
+
}
|
|
196
202
|
/**
|
|
197
203
|
* The single protected path every FireForge mach build dispatch routes
|
|
198
204
|
* through — `build`, `build --ui`, and the pre-test `--build` step all use
|
|
@@ -222,6 +228,7 @@ export async function runProtectedMachBuild(kind, engineDir, options = {}) {
|
|
|
222
228
|
const { env } = await installMachResourceGuard(engineDir);
|
|
223
229
|
const result = await runMachInheritCapture(args, engineDir, { env });
|
|
224
230
|
if (result.exitCode === 0) {
|
|
231
|
+
noteKnownTeardownNoiseInBuild(result);
|
|
225
232
|
return { ...result, attempts: attempt };
|
|
226
233
|
}
|
|
227
234
|
const signature = detectHarnessCrashSignature(`${result.stdout}\n${result.stderr}`);
|
|
@@ -233,6 +240,7 @@ export async function runProtectedMachBuild(kind, engineDir, options = {}) {
|
|
|
233
240
|
continue;
|
|
234
241
|
}
|
|
235
242
|
surfaceMachErrorHints(result);
|
|
243
|
+
noteKnownTeardownNoiseInBuild(result);
|
|
236
244
|
return { ...result, attempts: attempt, ...(signature ? { crashSignature: signature } : {}) };
|
|
237
245
|
}
|
|
238
246
|
}
|
|
@@ -335,20 +343,10 @@ export async function runMachSmoke(args, engineDir, options) {
|
|
|
335
343
|
...(options.mirror ? { mirror: options.mirror } : {}),
|
|
336
344
|
});
|
|
337
345
|
}
|
|
338
|
-
/**
|
|
339
|
-
* Creates a distribution package.
|
|
340
|
-
* @param engineDir - Path to the engine directory
|
|
341
|
-
* @returns Exit code
|
|
342
|
-
*/
|
|
343
|
-
export async function machPackage(engineDir) {
|
|
344
|
-
return runMach(['package'], engineDir, { inherit: true });
|
|
345
|
-
}
|
|
346
346
|
/**
|
|
347
347
|
* Creates a distribution package while streaming output to the terminal
|
|
348
|
-
* and capturing the stderr tail for post-run diagnostics
|
|
349
|
-
*
|
|
350
|
-
* variant; the inherit-only `machPackage` above remains for callers that
|
|
351
|
-
* just need an exit code.
|
|
348
|
+
* and capturing the stderr tail for post-run diagnostics, so callers can
|
|
349
|
+
* consult {@link explainMachError} on failure.
|
|
352
350
|
*
|
|
353
351
|
* @param engineDir - Path to the engine directory
|
|
354
352
|
* @returns Captured mach result (stdout tail, stderr tail, exit code)
|
|
@@ -356,14 +354,6 @@ export async function machPackage(engineDir) {
|
|
|
356
354
|
export async function machPackageCapture(engineDir) {
|
|
357
355
|
return runMachCapture(['package'], engineDir);
|
|
358
356
|
}
|
|
359
|
-
/**
|
|
360
|
-
* Runs mach watch for auto-rebuilding.
|
|
361
|
-
* @param engineDir - Path to the engine directory
|
|
362
|
-
* @returns Exit code
|
|
363
|
-
*/
|
|
364
|
-
export async function watch(engineDir) {
|
|
365
|
-
return runMach(['watch'], engineDir, { inherit: true });
|
|
366
|
-
}
|
|
367
357
|
/**
|
|
368
358
|
* Runs mach watch while preserving stdin and capturing emitted output.
|
|
369
359
|
*
|
|
@@ -381,16 +371,6 @@ export async function watch(engineDir) {
|
|
|
381
371
|
export async function watchWithOutput(engineDir, options = {}) {
|
|
382
372
|
return runMachInheritCapture(['watch'], engineDir, options.env ? { env: options.env } : {});
|
|
383
373
|
}
|
|
384
|
-
/**
|
|
385
|
-
* Runs mach test with the given test paths.
|
|
386
|
-
* @param engineDir - Path to the engine directory
|
|
387
|
-
* @param testPaths - Test file or directory paths (relative to engine)
|
|
388
|
-
* @param args - Additional arguments to pass to mach test
|
|
389
|
-
* @returns Exit code
|
|
390
|
-
*/
|
|
391
|
-
export async function test(engineDir, testPaths = [], args = []) {
|
|
392
|
-
return runMach(['test', ...testPaths, ...args], engineDir, { inherit: true });
|
|
393
|
-
}
|
|
394
374
|
/**
|
|
395
375
|
* Environment markers mozbuild reads to decide it is running under a coding
|
|
396
376
|
* agent (`mozbuild.util.is_running_under_coding_agent`), which quiets
|
|
@@ -417,52 +397,20 @@ function testVerbosityEnvUnset(fullOutput) {
|
|
|
417
397
|
return fullOutput === true ? CODING_AGENT_ENV_MARKERS : undefined;
|
|
418
398
|
}
|
|
419
399
|
/**
|
|
420
|
-
* Runs mach test
|
|
400
|
+
* Runs one mach test command (`mach test` / `mach xpcshell-test` / `mach
|
|
401
|
+
* mochitest`) while capturing streamed output for better diagnostics. The
|
|
402
|
+
* three commands share every other detail of the dispatch — resource guard,
|
|
403
|
+
* verbosity env, teardown-noise annotation — so they share one entry point
|
|
404
|
+
* keyed by `kind` rather than three copies that drift.
|
|
421
405
|
*
|
|
422
|
-
* @param
|
|
423
|
-
* @param
|
|
424
|
-
* @param args - Extra `mach test` arguments appended after the paths
|
|
425
|
-
* @param env - Optional extra environment variables for the mach process
|
|
426
|
-
* (merged over `process.env` by the exec layer). Used by
|
|
427
|
-
* `fireforge test --perf-samples` to publish the artifact-path contract.
|
|
428
|
-
*/
|
|
429
|
-
export async function testWithOutput(engineDir, testPaths = [], args = [], env, fullOutput) {
|
|
430
|
-
const guard = await installMachResourceGuard(engineDir);
|
|
431
|
-
const envUnset = testVerbosityEnvUnset(fullOutput);
|
|
432
|
-
return runMachCapture(['test', ...testPaths, ...args], engineDir, {
|
|
433
|
-
env: { ...guard.env, ...env },
|
|
434
|
-
...(envUnset ? { envUnset } : {}),
|
|
435
|
-
annotateKnownTeardownNoise: true,
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
/**
|
|
439
|
-
* Runs `mach xpcshell-test` (the suite-specific xpcshell command) while
|
|
440
|
-
* capturing output. Unlike the generic `mach test`, the suite-specific
|
|
441
|
-
* commands degrade a broken mozlog resource monitor to a warning instead of
|
|
442
|
-
* crashing at startup, so `fireforge test` dispatches single-suite runs here
|
|
443
|
-
* to stay resilient to the host psutil failure.
|
|
444
|
-
*
|
|
445
|
-
* Signature mirrors {@link testWithOutput} so the two are interchangeable in
|
|
446
|
-
* the dispatch path.
|
|
447
|
-
*/
|
|
448
|
-
export async function xpcshellTestWithOutput(engineDir, testPaths = [], args = [], env, fullOutput) {
|
|
449
|
-
const guard = await installMachResourceGuard(engineDir);
|
|
450
|
-
const envUnset = testVerbosityEnvUnset(fullOutput);
|
|
451
|
-
return runMachCapture(['xpcshell-test', ...testPaths, ...args], engineDir, {
|
|
452
|
-
env: { ...guard.env, ...env },
|
|
453
|
-
...(envUnset ? { envUnset } : {}),
|
|
454
|
-
annotateKnownTeardownNoise: true,
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
/**
|
|
458
|
-
* Runs `mach mochitest` (covers browser-chrome / mochitest flavors) while
|
|
459
|
-
* capturing output. The suite-specific counterpart to {@link testWithOutput}
|
|
460
|
-
* for non-xpcshell single-suite runs — see {@link xpcshellTestWithOutput}.
|
|
406
|
+
* @param kind - Which mach command to run
|
|
407
|
+
* @param options - Engine checkout, test paths, extra args, env, verbosity
|
|
461
408
|
*/
|
|
462
|
-
export async function
|
|
409
|
+
export async function runMachTestSuite(kind, options) {
|
|
410
|
+
const { engineDir, testPaths = [], args = [], env, fullOutput } = options;
|
|
463
411
|
const guard = await installMachResourceGuard(engineDir);
|
|
464
412
|
const envUnset = testVerbosityEnvUnset(fullOutput);
|
|
465
|
-
return runMachCapture([
|
|
413
|
+
return runMachCapture([kind, ...testPaths, ...args], engineDir, {
|
|
466
414
|
env: { ...guard.env, ...env },
|
|
467
415
|
...(envUnset ? { envUnset } : {}),
|
|
468
416
|
annotateKnownTeardownNoise: true,
|
|
@@ -147,20 +147,3 @@ export declare function hasExplicitXpcshellFlavor(machArgs: string[]): boolean;
|
|
|
147
147
|
* Marionette-driven browser).
|
|
148
148
|
*/
|
|
149
149
|
export declare function shouldAutoForwardMarionettePortToMach(machArgs: string[]): boolean;
|
|
150
|
-
/**
|
|
151
|
-
* Heuristic: do the test paths or forwarded mach args indicate a flavour
|
|
152
|
-
* that actually launches a Marionette-driven browser? Browser-chrome and
|
|
153
|
-
* mochitest do; xpcshell does not. A no-paths invocation (the default "run
|
|
154
|
-
* all tests" shape) is treated as marionette-relevant since it includes
|
|
155
|
-
* browser-chrome.
|
|
156
|
-
*
|
|
157
|
-
* Note: `fireforge test` auto-forward of `--marionette-port` to mach uses
|
|
158
|
-
* {@link shouldAutoForwardMarionettePortToMach} (mach-arg flavor gate) rather
|
|
159
|
-
* than this function alone, so toolkit paths without `/mochitest/` still get
|
|
160
|
-
* the listener pref and harness `--marionette=127.0.0.1:<n>` when appropriate.
|
|
161
|
-
*
|
|
162
|
-
* @param testPaths - Engine-relative paths after `stripEnginePrefix`.
|
|
163
|
-
* @param machArgs - Forwarded mach args (post-`--mach-arg`).
|
|
164
|
-
* @returns `true` when the run is likely to bind a Marionette listener.
|
|
165
|
-
*/
|
|
166
|
-
export declare function isMarionetteFlavor(testPaths: string[], machArgs: string[]): boolean;
|