@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
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Skip classification for the post-build packaging audit.
|
|
4
|
+
*
|
|
5
|
+
* The audit is warn-only, which makes a false positive cheap individually
|
|
6
|
+
* and expensive in aggregate: an operator who learns that `Audit:` lines are
|
|
7
|
+
* usually wrong stops reading the one that is right. Every classifier here
|
|
8
|
+
* exists to remove a warning whose "missing packaged artifact" claim is
|
|
9
|
+
* structurally impossible, and each carries its own reason so the
|
|
10
|
+
* `Packaged:` summary can say what it skipped and why — a run reporting
|
|
11
|
+
* `4 missing` with zero real misses is what this replaces.
|
|
12
|
+
*/
|
|
13
|
+
import { readText } from '../utils/fs.js';
|
|
14
|
+
import { normalizePathSlashes } from '../utils/paths.js';
|
|
15
|
+
import { escapeRegex } from '../utils/regex.js';
|
|
16
|
+
import { extractWithBrandingPath } from './mach-mozconfig.js';
|
|
17
|
+
/** Render order for the `Packaged:` summary's per-class breakdown. */
|
|
18
|
+
export const AUDIT_SKIP_REASONS = [
|
|
19
|
+
'not-packageable',
|
|
20
|
+
'platform-gated',
|
|
21
|
+
'platform-gated-ancestor',
|
|
22
|
+
'tests-not-packaged',
|
|
23
|
+
'source-unreadable',
|
|
24
|
+
'branding-not-selected',
|
|
25
|
+
'storybook-story',
|
|
26
|
+
'declared-unpackaged',
|
|
27
|
+
];
|
|
28
|
+
/**
|
|
29
|
+
* Suffixes of Storybook story modules.
|
|
30
|
+
*
|
|
31
|
+
* Firefox's in-tree Storybook renders these through its own Vite config;
|
|
32
|
+
* no `jar.mn` or `moz.build` entry packages one, so "no packaged artifact"
|
|
33
|
+
* is the expected state rather than a missing registration. Kept as a
|
|
34
|
+
* CLOSED list on purpose: generalising to "anything with a dotted infix"
|
|
35
|
+
* would swallow `*.worker.js` and `*.sys.mjs`, which are packaged.
|
|
36
|
+
*/
|
|
37
|
+
const STORYBOOK_SUFFIXES = ['.stories.mjs', '.stories.js'];
|
|
38
|
+
/** True for a Storybook story module. */
|
|
39
|
+
export function isStorybookStoryPath(sourcePath) {
|
|
40
|
+
const normalised = normalizePathSlashes(sourcePath);
|
|
41
|
+
return STORYBOOK_SUFFIXES.some((suffix) => normalised.endsWith(suffix));
|
|
42
|
+
}
|
|
43
|
+
/** Prefix under which every branding tree lives. */
|
|
44
|
+
const BRANDING_ROOT = 'browser/branding/';
|
|
45
|
+
/**
|
|
46
|
+
* Reads the branding directory this objdir was configured with.
|
|
47
|
+
*
|
|
48
|
+
* FireForge generates `engine/mozconfig` and writes the
|
|
49
|
+
* `ac_add_options --with-branding=…` line into it, so the selected branding
|
|
50
|
+
* is knowable from the tree. Returns undefined when the mozconfig cannot be
|
|
51
|
+
* read or carries no directive — the caller must then keep warning, because
|
|
52
|
+
* a skip that cannot name its evidence is a masked warning.
|
|
53
|
+
*
|
|
54
|
+
* @param engineDir - Absolute path to the engine root
|
|
55
|
+
* @returns Engine-relative branding path, or undefined
|
|
56
|
+
*/
|
|
57
|
+
export async function resolveSelectedBranding(engineDir) {
|
|
58
|
+
try {
|
|
59
|
+
const content = await readText(`${engineDir.replace(/[/\\]+$/, '')}/mozconfig`);
|
|
60
|
+
const selected = extractWithBrandingPath(content);
|
|
61
|
+
return selected === undefined ? undefined : normalizePathSlashes(selected).replace(/\/+$/, '');
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* True when `sourcePath` lives under a branding tree that is NOT the one
|
|
69
|
+
* this objdir was configured with.
|
|
70
|
+
*
|
|
71
|
+
* An unselected branding directory is not an input to this build at all, so
|
|
72
|
+
* its files can never appear in `dist/`. Worse, the basename walker matched
|
|
73
|
+
* them against the SELECTED branding's same-named artifact and reported the
|
|
74
|
+
* unselected file as "newer than its packaged artifact" — a stale warning
|
|
75
|
+
* about a comparison that has no meaning.
|
|
76
|
+
*
|
|
77
|
+
* @param sourcePath - Engine-relative POSIX path
|
|
78
|
+
* @param selectedBranding - Engine-relative selected branding path
|
|
79
|
+
*/
|
|
80
|
+
export function isUnselectedBrandingPath(sourcePath, selectedBranding) {
|
|
81
|
+
if (selectedBranding === undefined)
|
|
82
|
+
return false;
|
|
83
|
+
const normalised = normalizePathSlashes(sourcePath);
|
|
84
|
+
if (!normalised.startsWith(BRANDING_ROOT))
|
|
85
|
+
return false;
|
|
86
|
+
const selected = `${selectedBranding}/`;
|
|
87
|
+
return !normalised.startsWith(selected);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Finds the declaration admitting `sourcePath`, if any.
|
|
91
|
+
*
|
|
92
|
+
* Exact match or a `*`-within-one-segment glob, matching the
|
|
93
|
+
* `support-files` matcher's deliberate narrowness: `**` and other
|
|
94
|
+
* cross-directory shapes are not accepted, because a carve-out that
|
|
95
|
+
* silently widens is exactly the failure this feature is warned against.
|
|
96
|
+
*
|
|
97
|
+
* @param sourcePath - Engine-relative POSIX path
|
|
98
|
+
* @param declarations - The project's `buildAudit.unpackaged` entries
|
|
99
|
+
*/
|
|
100
|
+
export function matchUnpackagedDeclaration(sourcePath, declarations) {
|
|
101
|
+
const normalised = normalizePathSlashes(sourcePath);
|
|
102
|
+
return declarations.find((declaration) => {
|
|
103
|
+
const pattern = normalizePathSlashes(declaration.path);
|
|
104
|
+
if (!pattern.includes('*'))
|
|
105
|
+
return pattern === normalised;
|
|
106
|
+
const regex = new RegExp(`^${pattern
|
|
107
|
+
.split('*')
|
|
108
|
+
.map((part) => escapeRegex(part))
|
|
109
|
+
.join('[^/]*')}$`);
|
|
110
|
+
return regex.test(normalised);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AuditSkipReason, type UnpackagedDeclaration } from './build-audit-skip.js';
|
|
1
2
|
import type { BuildBaseline } from './build-baseline-types.js';
|
|
2
3
|
/**
|
|
3
4
|
* True for the build-input manifests the build consumes but never packages
|
|
@@ -34,9 +35,17 @@ export interface AuditEntry {
|
|
|
34
35
|
* updated: an artifact exists and is at least as new as the source.
|
|
35
36
|
* stale: artifact exists but is older than the source (probable packaging drop).
|
|
36
37
|
* missing: no artifact with a matching basename was found anywhere under dist/.
|
|
37
|
-
* skipped: the file
|
|
38
|
+
* skipped: the file was not audited; `skipReason` says why.
|
|
38
39
|
*/
|
|
39
40
|
status: 'updated' | 'stale' | 'missing' | 'skipped';
|
|
41
|
+
/**
|
|
42
|
+
* Why a `skipped` entry was skipped. Present only for `skipped`; the
|
|
43
|
+
* reasons used to exist only as verbose log strings, so the summary line
|
|
44
|
+
* could report `4 missing` on a run with zero real misses and nothing in
|
|
45
|
+
* the counts distinguished a structural non-input from an unregistered
|
|
46
|
+
* file an operator must act on.
|
|
47
|
+
*/
|
|
48
|
+
skipReason?: AuditSkipReason;
|
|
40
49
|
}
|
|
41
50
|
/** Summary counts for the "Packaged:" end-of-build line. */
|
|
42
51
|
export interface AuditSummary {
|
|
@@ -44,6 +53,8 @@ export interface AuditSummary {
|
|
|
44
53
|
stale: number;
|
|
45
54
|
missing: number;
|
|
46
55
|
skipped: number;
|
|
56
|
+
/** Per-class skip counts, so the summary can name what it dismissed. */
|
|
57
|
+
skippedByReason: Record<AuditSkipReason, number>;
|
|
47
58
|
entries: AuditEntry[];
|
|
48
59
|
}
|
|
49
60
|
/**
|
|
@@ -78,4 +89,6 @@ export declare function isXpcomManifestPath(sourcePath: string): boolean;
|
|
|
78
89
|
* @param baseline Optional previous-build baseline marker.
|
|
79
90
|
* @returns Summary of artifact status counts.
|
|
80
91
|
*/
|
|
81
|
-
export declare function auditBuildArtifacts(projectRoot: string, engineDir: string, baseline: BuildBaseline | undefined
|
|
92
|
+
export declare function auditBuildArtifacts(projectRoot: string, engineDir: string, baseline: BuildBaseline | undefined, options?: {
|
|
93
|
+
unpackaged?: readonly UnpackagedDeclaration[];
|
|
94
|
+
}): Promise<AuditSummary>;
|
|
@@ -35,14 +35,18 @@
|
|
|
35
35
|
*/
|
|
36
36
|
import { readdir, stat } from 'node:fs/promises';
|
|
37
37
|
import { basename, join } from 'node:path';
|
|
38
|
+
import { toError } from '../utils/errors.js';
|
|
38
39
|
import { pathExists } from '../utils/fs.js';
|
|
39
40
|
import { info, verbose, warn } from '../utils/logger.js';
|
|
40
41
|
import { normalizePathSlashes } from '../utils/paths.js';
|
|
41
|
-
import { detectPlatformGate } from './build-audit-platform.js';
|
|
42
|
+
import { detectAncestorDirsGate, detectPlatformGate } from './build-audit-platform.js';
|
|
42
43
|
import { collectSameBasenameCandidates, findRegisteredTarget, resolveArtifactByRegistration, } from './build-audit-registration.js';
|
|
43
44
|
import { countTrailingSegmentMatches, GENERIC_PATH_SEGMENTS, isTestPath, resolveBestArtifact, } from './build-audit-resolve.js';
|
|
45
|
+
import { AUDIT_SKIP_REASONS, isStorybookStoryPath, isUnselectedBrandingPath, matchUnpackagedDeclaration, resolveSelectedBranding, } from './build-audit-skip.js';
|
|
44
46
|
import { resolveArtifactByKnownTransform } from './build-audit-transforms.js';
|
|
45
47
|
import { collectChangedEnginePaths } from './engine-changes.js';
|
|
48
|
+
import { loadPatchesManifest } from './patch-manifest-io.js';
|
|
49
|
+
import { buildPatchClaims } from './status-classify.js';
|
|
46
50
|
/** Path extensions that are conventionally packaged into the Firefox bundle. */
|
|
47
51
|
const PACKAGEABLE_EXTENSIONS = [
|
|
48
52
|
'.js',
|
|
@@ -98,6 +102,10 @@ export function isBuildInputPath(sourcePath) {
|
|
|
98
102
|
export function isJarManifestPath(sourcePath) {
|
|
99
103
|
return sourcePath === 'jar.mn' || sourcePath.endsWith('/jar.mn');
|
|
100
104
|
}
|
|
105
|
+
/** Zeroed per-class skip counters. */
|
|
106
|
+
function emptySkipCounts() {
|
|
107
|
+
return Object.fromEntries(AUDIT_SKIP_REASONS.map((reason) => [reason, 0]));
|
|
108
|
+
}
|
|
101
109
|
/**
|
|
102
110
|
* Decides whether a source path should be packaged. Returns true for paths
|
|
103
111
|
* whose extension or directory fragment matches a known-packaged pattern,
|
|
@@ -144,7 +152,7 @@ export function isPackageablePath(sourcePath) {
|
|
|
144
152
|
* @returns True when the path is a `components.conf` manifest.
|
|
145
153
|
*/
|
|
146
154
|
export function isXpcomManifestPath(sourcePath) {
|
|
147
|
-
return basename(sourcePath
|
|
155
|
+
return basename(normalizePathSlashes(sourcePath)) === 'components.conf';
|
|
148
156
|
}
|
|
149
157
|
/*
|
|
150
158
|
* Finds the unique obj-star directory with a dist subtree, or undefined
|
|
@@ -270,6 +278,55 @@ function isConfidentMatch(source, candidate) {
|
|
|
270
278
|
}
|
|
271
279
|
return false;
|
|
272
280
|
}
|
|
281
|
+
/**
|
|
282
|
+
* Applies a `buildAudit.unpackaged` carve-out to a classified result.
|
|
283
|
+
*
|
|
284
|
+
* Two behaviours, both mirroring the `--expect-unmanaged` model:
|
|
285
|
+
*
|
|
286
|
+
* - An admitted path is LISTED, not silenced. A carve-out nobody can see
|
|
287
|
+
* is how one quietly widens, so the run says which paths it admitted and
|
|
288
|
+
* on what stated reason.
|
|
289
|
+
* - An admitted path that DOES resolve to a packaged artifact is a STALE
|
|
290
|
+
* carve-out: the declaration asserts a fact about the tree that is no
|
|
291
|
+
* longer true, and suppressing on it would hide a real packaging change.
|
|
292
|
+
* That is reported as a warning, not accepted.
|
|
293
|
+
*
|
|
294
|
+
* A declaration matching nothing that changed this run says nothing —
|
|
295
|
+
* unlike `--expect-unmanaged`, whose unseen entries are reported. The
|
|
296
|
+
* difference is deliberate: that is a per-invocation flag list, where an
|
|
297
|
+
* unmet entry is probably a typo in the command just typed. This is a
|
|
298
|
+
* standing config list checked against only the files that happened to
|
|
299
|
+
* change, so "not met" is the normal case and reporting it would put a
|
|
300
|
+
* warning on every build.
|
|
301
|
+
*/
|
|
302
|
+
function applyUnpackagedCarveOut(result, ctx) {
|
|
303
|
+
// Already skipped for a more specific structural reason — the carve-out
|
|
304
|
+
// is redundant there and reporting it would be noise.
|
|
305
|
+
if (result.status === 'skipped')
|
|
306
|
+
return result;
|
|
307
|
+
const declaration = matchUnpackagedDeclaration(result.source, ctx.unpackaged);
|
|
308
|
+
if (declaration === undefined)
|
|
309
|
+
return result;
|
|
310
|
+
if (result.artifact !== undefined) {
|
|
311
|
+
return {
|
|
312
|
+
source: result.source,
|
|
313
|
+
artifact: result.artifact,
|
|
314
|
+
status: 'skipped',
|
|
315
|
+
skipReason: 'declared-unpackaged',
|
|
316
|
+
warning: `Audit: engine/${result.source} is declared unpackaged in fireforge.json ` +
|
|
317
|
+
`("${declaration.reason}") but a packaged artifact exists at ${result.artifact}. ` +
|
|
318
|
+
'The declaration is stale — remove it, or correct its path.',
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
return {
|
|
322
|
+
source: result.source,
|
|
323
|
+
artifact: undefined,
|
|
324
|
+
status: 'skipped',
|
|
325
|
+
skipReason: 'declared-unpackaged',
|
|
326
|
+
notice: `Audit: engine/${result.source} admitted as unpackaged by ` +
|
|
327
|
+
`buildAudit.unpackaged "${declaration.path}" — ${declaration.reason}`,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
273
330
|
/**
|
|
274
331
|
* Audits one engine source path and returns its entry. Pure orchestration
|
|
275
332
|
* helper kept separate so `auditBuildArtifacts` stays under the per-function
|
|
@@ -277,12 +334,30 @@ function isConfidentMatch(source, candidate) {
|
|
|
277
334
|
*/
|
|
278
335
|
async function auditSinglePath(source, ctx) {
|
|
279
336
|
if (!isPackageablePath(source)) {
|
|
280
|
-
return { source, artifact: undefined, status: 'skipped' };
|
|
337
|
+
return { source, artifact: undefined, status: 'skipped', skipReason: 'not-packageable' };
|
|
338
|
+
}
|
|
339
|
+
if (isStorybookStoryPath(source)) {
|
|
340
|
+
verbose(`Audit: skipping engine/${source} — Storybook story, never packaged.`);
|
|
341
|
+
return { source, artifact: undefined, status: 'skipped', skipReason: 'storybook-story' };
|
|
342
|
+
}
|
|
343
|
+
if (isUnselectedBrandingPath(source, ctx.selectedBranding)) {
|
|
344
|
+
verbose(`Audit: skipping engine/${source} — branding tree is not the selected ${ctx.selectedBranding ?? '?'}, so it is not an input to this objdir.`);
|
|
345
|
+
return { source, artifact: undefined, status: 'skipped', skipReason: 'branding-not-selected' };
|
|
281
346
|
}
|
|
282
347
|
const gate = await detectPlatformGate(ctx.engineDir, source);
|
|
283
348
|
if (gate.gatedOff) {
|
|
284
349
|
verbose(`Audit: skipping engine/${source} — gated off by "${gate.gateExpression ?? '?'}".`);
|
|
285
|
-
return { source, artifact: undefined, status: 'skipped' };
|
|
350
|
+
return { source, artifact: undefined, status: 'skipped', skipReason: 'platform-gated' };
|
|
351
|
+
}
|
|
352
|
+
const ancestorGate = await detectAncestorDirsGate(ctx.engineDir, source);
|
|
353
|
+
if (ancestorGate.gatedOff) {
|
|
354
|
+
verbose(`Audit: skipping engine/${source} — its directory is reached through a DIRS entry in engine/${ancestorGate.gateFile ?? '?'} gated by "${ancestorGate.gateExpression ?? '?'}".`);
|
|
355
|
+
return {
|
|
356
|
+
source,
|
|
357
|
+
artifact: undefined,
|
|
358
|
+
status: 'skipped',
|
|
359
|
+
skipReason: 'platform-gated-ancestor',
|
|
360
|
+
};
|
|
286
361
|
}
|
|
287
362
|
// Tests only end up under `_tests/` after `mach package-tests` (or a
|
|
288
363
|
// test-run that invokes the target) has executed. A plain `mach build`
|
|
@@ -291,7 +366,7 @@ async function auditSinglePath(source, ctx) {
|
|
|
291
366
|
// Skip audit for test sources when no packaged-tests marker is present.
|
|
292
367
|
if (isTestPath(source) && !ctx.testsPackaged) {
|
|
293
368
|
verbose(`Audit: skipping engine/${source} — _tests/${PACKAGED_TESTS_MARKER} not present; full test packaging has not run for this build.`);
|
|
294
|
-
return { source, artifact: undefined, status: 'skipped' };
|
|
369
|
+
return { source, artifact: undefined, status: 'skipped', skipReason: 'tests-not-packaged' };
|
|
295
370
|
}
|
|
296
371
|
const sourcePath = join(ctx.engineDir, source);
|
|
297
372
|
let sourceMtime;
|
|
@@ -301,7 +376,7 @@ async function auditSinglePath(source, ctx) {
|
|
|
301
376
|
}
|
|
302
377
|
catch {
|
|
303
378
|
// Deletion that didn't propagate — distinct class of bug, not audited yet.
|
|
304
|
-
return { source, artifact: undefined, status: 'skipped' };
|
|
379
|
+
return { source, artifact: undefined, status: 'skipped', skipReason: 'source-unreadable' };
|
|
305
380
|
}
|
|
306
381
|
const roots = searchRootsFor(source, ctx.distRoot, ctx.testsRoot);
|
|
307
382
|
// Registration-aware resolution first: a `jar.mn` entry whose `(source)`
|
|
@@ -442,6 +517,39 @@ function relativeManifestPath(engineDir, manifest) {
|
|
|
442
517
|
}
|
|
443
518
|
return manifest;
|
|
444
519
|
}
|
|
520
|
+
/**
|
|
521
|
+
* Path → owning patch filenames, from the patches manifest's
|
|
522
|
+
* `filesAffected`. Best-effort: a project with no queue (or an unreadable
|
|
523
|
+
* manifest) yields an empty map and the notices render exactly as before.
|
|
524
|
+
*/
|
|
525
|
+
async function resolveAuditOwnership(projectRoot) {
|
|
526
|
+
try {
|
|
527
|
+
const patchesDir = join(projectRoot, 'patches');
|
|
528
|
+
const manifest = await loadPatchesManifest(patchesDir);
|
|
529
|
+
if (!manifest)
|
|
530
|
+
return new Map();
|
|
531
|
+
return buildPatchClaims(manifest.patches);
|
|
532
|
+
}
|
|
533
|
+
catch (error) {
|
|
534
|
+
verbose(`Audit: ownership lookup unavailable (${toError(error).message}).`);
|
|
535
|
+
return new Map();
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Renders the ownership suffix for one audit notice: the owning patch, or
|
|
540
|
+
* an explicit `unmanaged` mark. An empty map (no queue, unreadable
|
|
541
|
+
* manifest) renders nothing rather than claiming everything is unmanaged —
|
|
542
|
+
* "unmanaged" must mean "checked and unowned", never "could not check".
|
|
543
|
+
*/
|
|
544
|
+
function describeOwnership(ownersByPath, source) {
|
|
545
|
+
if (ownersByPath.size === 0)
|
|
546
|
+
return '';
|
|
547
|
+
const owners = ownersByPath.get(source);
|
|
548
|
+
if (owners === undefined || owners.length === 0) {
|
|
549
|
+
return ' Ownership: unmanaged (no patch claims this path).';
|
|
550
|
+
}
|
|
551
|
+
return ` Ownership: ${owners.join(', ')}.`;
|
|
552
|
+
}
|
|
445
553
|
/**
|
|
446
554
|
* Runs the post-build audit. Emits per-file warnings for missing or
|
|
447
555
|
* stale artifacts and a summary info line at the end. Always returns
|
|
@@ -452,13 +560,13 @@ function relativeManifestPath(engineDir, manifest) {
|
|
|
452
560
|
* @param baseline Optional previous-build baseline marker.
|
|
453
561
|
* @returns Summary of artifact status counts.
|
|
454
562
|
*/
|
|
455
|
-
export async function auditBuildArtifacts(projectRoot, engineDir, baseline) {
|
|
456
|
-
void projectRoot;
|
|
563
|
+
export async function auditBuildArtifacts(projectRoot, engineDir, baseline, options = {}) {
|
|
457
564
|
const summary = {
|
|
458
565
|
updated: 0,
|
|
459
566
|
stale: 0,
|
|
460
567
|
missing: 0,
|
|
461
568
|
skipped: 0,
|
|
569
|
+
skippedByReason: emptySkipCounts(),
|
|
462
570
|
entries: [],
|
|
463
571
|
};
|
|
464
572
|
const distRoot = await resolveDistRoot(engineDir);
|
|
@@ -472,18 +580,52 @@ export async function auditBuildArtifacts(projectRoot, engineDir, baseline) {
|
|
|
472
580
|
if (changed.length === 0) {
|
|
473
581
|
return summary;
|
|
474
582
|
}
|
|
475
|
-
|
|
583
|
+
// Ownership, resolved once for the whole audit. The notices key on "was
|
|
584
|
+
// touched", which on a shared checkout means any concurrent session's
|
|
585
|
+
// dirty files — so an operator gets a registration-shaped warning about a
|
|
586
|
+
// file they never edited and has to census ownership by hand to dismiss
|
|
587
|
+
// it. FireForge already resolves this for `status --ownership`; carrying
|
|
588
|
+
// it here turns a three-line mystery into three dismissible lines.
|
|
589
|
+
const ownersByPath = await resolveAuditOwnership(projectRoot);
|
|
590
|
+
const ctx = {
|
|
591
|
+
engineDir,
|
|
592
|
+
distRoot,
|
|
593
|
+
testsRoot,
|
|
594
|
+
testsPackaged,
|
|
595
|
+
selectedBranding: await resolveSelectedBranding(engineDir),
|
|
596
|
+
unpackaged: options.unpackaged ?? [],
|
|
597
|
+
};
|
|
476
598
|
for (const source of changed) {
|
|
477
|
-
const result = await auditSinglePath(source, ctx);
|
|
599
|
+
const result = applyUnpackagedCarveOut(await auditSinglePath(source, ctx), ctx);
|
|
478
600
|
summary[result.status] += 1;
|
|
601
|
+
if (result.status === 'skipped' && result.skipReason !== undefined) {
|
|
602
|
+
summary.skippedByReason[result.skipReason] += 1;
|
|
603
|
+
}
|
|
479
604
|
summary.entries.push({
|
|
480
605
|
source: result.source,
|
|
481
606
|
artifact: result.artifact,
|
|
482
607
|
status: result.status,
|
|
608
|
+
...(result.skipReason === undefined ? {} : { skipReason: result.skipReason }),
|
|
483
609
|
});
|
|
484
610
|
if (result.warning)
|
|
485
|
-
warn(result.warning);
|
|
611
|
+
warn(`${result.warning}${describeOwnership(ownersByPath, result.source)}`);
|
|
612
|
+
if (result.notice)
|
|
613
|
+
info(result.notice);
|
|
486
614
|
}
|
|
487
|
-
info(`Packaged: ${summary.updated} updated, ${summary.stale} stale, ${summary.missing} missing,
|
|
615
|
+
info(`Packaged: ${summary.updated} updated, ${summary.stale} stale, ${summary.missing} missing, ` +
|
|
616
|
+
`${summary.skipped} skipped${describeSkipBreakdown(summary)}`);
|
|
488
617
|
return summary;
|
|
489
618
|
}
|
|
619
|
+
/**
|
|
620
|
+
* Renders the per-class skip breakdown appended to the `Packaged:` line.
|
|
621
|
+
*
|
|
622
|
+
* Without it the summary reported one undifferentiated `skipped` count, so
|
|
623
|
+
* a run that dismissed four unselected-branding files and one
|
|
624
|
+
* ancestor-gated directory looked identical to one that dismissed five
|
|
625
|
+
* unregistered sources — and the four false `missing` entries downstream
|
|
626
|
+
* reported had no counterpart in the counts an operator could check.
|
|
627
|
+
*/
|
|
628
|
+
function describeSkipBreakdown(summary) {
|
|
629
|
+
const parts = AUDIT_SKIP_REASONS.filter((reason) => summary.skippedByReason[reason] > 0).map((reason) => `${reason} ${String(summary.skippedByReason[reason])}`);
|
|
630
|
+
return parts.length === 0 ? '' : ` (${parts.join(', ')})`;
|
|
631
|
+
}
|
|
@@ -25,8 +25,6 @@ import { type BuildBaseline, type TestPackagingCoverage } from './build-baseline
|
|
|
25
25
|
* `'full'`.
|
|
26
26
|
*/
|
|
27
27
|
export type BaselineBuildKind = 'full' | 'faster';
|
|
28
|
-
/** Name of the last-build marker file under `.fireforge/`. */
|
|
29
|
-
export declare const BUILD_BASELINE_FILENAME = "last-build.json";
|
|
30
28
|
/**
|
|
31
29
|
* Resolves the on-disk path of the build baseline marker.
|
|
32
30
|
* @param projectRoot - Root directory of the project
|
|
@@ -40,36 +38,55 @@ export declare function getBuildBaselinePath(projectRoot: string): string;
|
|
|
40
38
|
* @param projectRoot - Root directory of the project
|
|
41
39
|
*/
|
|
42
40
|
export declare function readBuildBaseline(projectRoot: string): Promise<BuildBaseline | undefined>;
|
|
41
|
+
export interface WriteBuildBaselineOptions {
|
|
42
|
+
/** Root directory of the project. */
|
|
43
|
+
projectRoot: string;
|
|
44
|
+
/** Path to the engine directory. */
|
|
45
|
+
engineDir: string;
|
|
46
|
+
/** Current `binaryName` from fireforge.json. */
|
|
47
|
+
binaryName: string;
|
|
48
|
+
/**
|
|
49
|
+
* Coverage claim of the packaged test runtime this build produced
|
|
50
|
+
* (`'full'`, or the scoped request paths of a `test --build` invocation).
|
|
51
|
+
* Omitted → field left off the marker.
|
|
52
|
+
*/
|
|
53
|
+
testPackagingCoverage?: TestPackagingCoverage | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* The baseline this write replaces, when the caller has it. A scoped
|
|
56
|
+
* (non-`'full'`) write carries its `staticComponentsBaseline` forward
|
|
57
|
+
* verbatim — `mach build faster` does not rebake `components.conf`
|
|
58
|
+
* registrations, so the last FULL build stays the honest anchor for the
|
|
59
|
+
* compiled StaticComponents table.
|
|
60
|
+
*/
|
|
61
|
+
previousBaseline?: BuildBaseline | undefined;
|
|
62
|
+
/** Invocation shape that produced this baseline. */
|
|
63
|
+
recordedBy?: string | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* `'auto'` (default) refreshes the static-components anchor whenever the
|
|
66
|
+
* coverage claim is `'full'` or absent. `'refresh'` records it even for a
|
|
67
|
+
* scoped coverage claim whose implementation escalated to a full build.
|
|
68
|
+
* `'carry-forward'` always keeps the previous anchor: needed by
|
|
69
|
+
* `--extend-coverage`, whose union can EVALUATE to `'full'` while the
|
|
70
|
+
* build that produced it was still a scoped `mach build faster` that did
|
|
71
|
+
* not rebake the compiled table.
|
|
72
|
+
*/
|
|
73
|
+
staticComponentsHandling?: 'auto' | 'refresh' | 'carry-forward' | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Which mach build produced this baseline (default `'full'`). A
|
|
76
|
+
* `'faster'` write carries the previous record's `jar.mn` fingerprints
|
|
77
|
+
* forward instead of refreshing them — see
|
|
78
|
+
* {@link BuildBaseline.buildInputFingerprints}. Every caller that ran
|
|
79
|
+
* `mach build faster` MUST say so, or the next pre-test build skips an
|
|
80
|
+
* escalation no full build has honoured.
|
|
81
|
+
*/
|
|
82
|
+
buildKind?: BaselineBuildKind | undefined;
|
|
83
|
+
}
|
|
43
84
|
/**
|
|
44
85
|
* Records a successful build by writing a fresh baseline marker. Captures
|
|
45
86
|
* engine HEAD SHA (or an empty string when the engine has no HEAD yet) and
|
|
46
87
|
* the current binaryName. Caller is responsible for only invoking this
|
|
47
88
|
* after the build exit code was zero.
|
|
48
89
|
*
|
|
49
|
-
* @param
|
|
50
|
-
* @param engineDir - Path to the engine directory
|
|
51
|
-
* @param binaryName - Current `binaryName` from fireforge.json
|
|
52
|
-
* @param testPackagingCoverage - Coverage claim of the packaged test
|
|
53
|
-
* runtime this build produced (`'full'`, or the scoped request paths of
|
|
54
|
-
* a `test --build` invocation). Omitted → field left off the marker.
|
|
55
|
-
* @param previousBaseline - The baseline this write replaces, when the
|
|
56
|
-
* caller has it. A scoped (non-`'full'`) write carries its
|
|
57
|
-
* `staticComponentsBaseline` forward verbatim — `mach build faster`
|
|
58
|
-
* does not rebake `components.conf` registrations, so the last FULL
|
|
59
|
-
* build stays the honest anchor for the compiled StaticComponents table.
|
|
60
|
-
* @param recordedBy - Invocation shape that produced this baseline.
|
|
61
|
-
* @param staticComponentsHandling - `'auto'` (default) refreshes the
|
|
62
|
-
* static-components anchor whenever the coverage claim is `'full'` or
|
|
63
|
-
* absent. `'refresh'` records it even for a scoped coverage claim whose
|
|
64
|
-
* implementation escalated to a full build. `'carry-forward'` always
|
|
65
|
-
* keeps the previous anchor: needed by `--extend-coverage`, whose union
|
|
66
|
-
* can EVALUATE to `'full'` while the build that produced it was still a
|
|
67
|
-
* scoped `mach build faster` that did not rebake the compiled table.
|
|
68
|
-
* @param buildKind - Which mach build produced this baseline (default
|
|
69
|
-
* `'full'`). A `'faster'` write carries the previous record's `jar.mn`
|
|
70
|
-
* fingerprints forward instead of refreshing them — see
|
|
71
|
-
* {@link BuildBaseline.buildInputFingerprints}. Every caller that ran
|
|
72
|
-
* `mach build faster` MUST say so, or the next pre-test build skips an
|
|
73
|
-
* escalation no full build has honoured.
|
|
90
|
+
* @param options - See {@link WriteBuildBaselineOptions}
|
|
74
91
|
*/
|
|
75
|
-
export declare function writeBuildBaseline(
|
|
92
|
+
export declare function writeBuildBaseline(options: WriteBuildBaselineOptions): Promise<void>;
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
* on successful build completion; a failed build does not update it, so a
|
|
17
17
|
* subsequent run still audits against the last known-good tree.
|
|
18
18
|
*/
|
|
19
|
-
import { createHash } from 'node:crypto';
|
|
20
19
|
import { readFile } from 'node:fs/promises';
|
|
21
20
|
import { join } from 'node:path';
|
|
22
21
|
import { mapWithConcurrency } from '../utils/concurrency.js';
|
|
23
22
|
import { getNodeErrorCode, toError } from '../utils/errors.js';
|
|
24
23
|
import { pathExists, readJson, writeJson } from '../utils/fs.js';
|
|
24
|
+
import { sha256Hex } from '../utils/hash.js';
|
|
25
25
|
import { verbose } from '../utils/logger.js';
|
|
26
26
|
import { isBuildInputPath, isJarManifestPath, isPackageablePath, isXpcomManifestPath, } from './build-audit.js';
|
|
27
27
|
import { DELETED_FILE_FINGERPRINT, } from './build-baseline-types.js';
|
|
@@ -36,7 +36,7 @@ import { getUntrackedFiles } from './git-status.js';
|
|
|
36
36
|
*/
|
|
37
37
|
const FINGERPRINT_IO_CONCURRENCY = 16;
|
|
38
38
|
/** Name of the last-build marker file under `.fireforge/`. */
|
|
39
|
-
|
|
39
|
+
const BUILD_BASELINE_FILENAME = 'last-build.json';
|
|
40
40
|
/**
|
|
41
41
|
* Resolves the on-disk path of the build baseline marker.
|
|
42
42
|
* @param projectRoot - Root directory of the project
|
|
@@ -71,33 +71,10 @@ export async function readBuildBaseline(projectRoot) {
|
|
|
71
71
|
* the current binaryName. Caller is responsible for only invoking this
|
|
72
72
|
* after the build exit code was zero.
|
|
73
73
|
*
|
|
74
|
-
* @param
|
|
75
|
-
* @param engineDir - Path to the engine directory
|
|
76
|
-
* @param binaryName - Current `binaryName` from fireforge.json
|
|
77
|
-
* @param testPackagingCoverage - Coverage claim of the packaged test
|
|
78
|
-
* runtime this build produced (`'full'`, or the scoped request paths of
|
|
79
|
-
* a `test --build` invocation). Omitted → field left off the marker.
|
|
80
|
-
* @param previousBaseline - The baseline this write replaces, when the
|
|
81
|
-
* caller has it. A scoped (non-`'full'`) write carries its
|
|
82
|
-
* `staticComponentsBaseline` forward verbatim — `mach build faster`
|
|
83
|
-
* does not rebake `components.conf` registrations, so the last FULL
|
|
84
|
-
* build stays the honest anchor for the compiled StaticComponents table.
|
|
85
|
-
* @param recordedBy - Invocation shape that produced this baseline.
|
|
86
|
-
* @param staticComponentsHandling - `'auto'` (default) refreshes the
|
|
87
|
-
* static-components anchor whenever the coverage claim is `'full'` or
|
|
88
|
-
* absent. `'refresh'` records it even for a scoped coverage claim whose
|
|
89
|
-
* implementation escalated to a full build. `'carry-forward'` always
|
|
90
|
-
* keeps the previous anchor: needed by `--extend-coverage`, whose union
|
|
91
|
-
* can EVALUATE to `'full'` while the build that produced it was still a
|
|
92
|
-
* scoped `mach build faster` that did not rebake the compiled table.
|
|
93
|
-
* @param buildKind - Which mach build produced this baseline (default
|
|
94
|
-
* `'full'`). A `'faster'` write carries the previous record's `jar.mn`
|
|
95
|
-
* fingerprints forward instead of refreshing them — see
|
|
96
|
-
* {@link BuildBaseline.buildInputFingerprints}. Every caller that ran
|
|
97
|
-
* `mach build faster` MUST say so, or the next pre-test build skips an
|
|
98
|
-
* escalation no full build has honoured.
|
|
74
|
+
* @param options - See {@link WriteBuildBaselineOptions}
|
|
99
75
|
*/
|
|
100
|
-
export async function writeBuildBaseline(
|
|
76
|
+
export async function writeBuildBaseline(options) {
|
|
77
|
+
const { projectRoot, engineDir, binaryName, testPackagingCoverage, previousBaseline, recordedBy, staticComponentsHandling = 'auto', buildKind = 'full', } = options;
|
|
101
78
|
let engineHeadSha = '';
|
|
102
79
|
try {
|
|
103
80
|
engineHeadSha = await getHead(engineDir);
|
|
@@ -224,7 +201,7 @@ async function collectDirtyFingerprints(engineDir, includePath, contextLabel) {
|
|
|
224
201
|
const entries = await mapWithConcurrency(included, FINGERPRINT_IO_CONCURRENCY, async (relPath) => {
|
|
225
202
|
try {
|
|
226
203
|
const buffer = await readFile(join(engineDir, relPath));
|
|
227
|
-
return [relPath,
|
|
204
|
+
return [relPath, sha256Hex(buffer)];
|
|
228
205
|
}
|
|
229
206
|
catch (fileError) {
|
|
230
207
|
if (getNodeErrorCode(fileError) === 'ENOENT') {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decides whether a changed `jar.mn` really forces the pre-test build to
|
|
3
|
+
* escalate from `mach build faster` to a full `mach build`.
|
|
4
|
+
*
|
|
5
|
+
* The 0.41.0 rule was `any changed jar.mn ⇒ full build`, which costs ~10
|
|
6
|
+
* minutes against ~20 s for a chrome-only slice and is paid TWICE per
|
|
7
|
+
* green/no-change pair on a Furnace-touching slice. A downstream experiment
|
|
8
|
+
* (two clean runs, 2026-08-29, against 0.44.4) showed the rule is over-broad
|
|
9
|
+
* for the common case: a jar-only registration of a NEW content file added
|
|
10
|
+
* to an EXISTING `dist/bin` manifest was installed by a plain
|
|
11
|
+
* `fireforge build --ui` both times — `faster/install_dist_bin_browser` named
|
|
12
|
+
* the destination, the file landed in `dist/bin/…/content/browser/` and in
|
|
13
|
+
* the `.app` bundle, and a plain `fireforge test` fetched it over
|
|
14
|
+
* `chrome://` at runtime. Neither run escalated, and neither needed to.
|
|
15
|
+
*
|
|
16
|
+
* What the experiment did NOT exercise, and what therefore still escalates:
|
|
17
|
+
*
|
|
18
|
+
* - a NEW `jar.mn` file. The install manifest for a manifest the backend
|
|
19
|
+
* has never seen does not exist yet, and `mach build faster` consumes
|
|
20
|
+
* install manifests rather than regenerating the backend that writes
|
|
21
|
+
* them.
|
|
22
|
+
* - a manifest whose jar declaration carries a bracketed base-directory
|
|
23
|
+
* prefix (`[some/dir] name.jar:`), which redirects the install
|
|
24
|
+
* destination away from the default chrome root. That redirect is the
|
|
25
|
+
* "non-dist/bin target" half nobody has run.
|
|
26
|
+
*
|
|
27
|
+
* Everything else about the rule is unchanged, and every failure to read or
|
|
28
|
+
* parse a manifest escalates: this narrows a conservative rule, so its
|
|
29
|
+
* error direction stays the slow-but-correct one.
|
|
30
|
+
*/
|
|
31
|
+
import type { BuildBaseline } from './build-baseline-types.js';
|
|
32
|
+
/** Why one changed `jar.mn` still forces a full build. */
|
|
33
|
+
export interface JarEscalationCause {
|
|
34
|
+
/** Engine-relative manifest path. */
|
|
35
|
+
path: string;
|
|
36
|
+
/** Operator-facing reason, printed with the escalation notice. */
|
|
37
|
+
reason: string;
|
|
38
|
+
}
|
|
39
|
+
/** Outcome of {@link evaluateJarManifestEscalation}. */
|
|
40
|
+
export interface JarEscalationDecision {
|
|
41
|
+
/** True when at least one changed manifest still needs a full build. */
|
|
42
|
+
escalate: boolean;
|
|
43
|
+
/** One entry per manifest that forces the escalation. */
|
|
44
|
+
causes: JarEscalationCause[];
|
|
45
|
+
/** Manifests the narrowing cleared; kept for the verbose explanation. */
|
|
46
|
+
cleared: string[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Evaluates every changed `jar.mn` against the narrowed rule.
|
|
50
|
+
*
|
|
51
|
+
* @param engineDir - Absolute engine directory
|
|
52
|
+
* @param changedPaths - Engine-relative paths changed since the last
|
|
53
|
+
* successful build (non-`jar.mn` entries are ignored)
|
|
54
|
+
* @param baseline - Last successful build's baseline (reserved: the
|
|
55
|
+
* fingerprint map already gated which paths reach here)
|
|
56
|
+
* @returns The escalation decision with per-manifest causes
|
|
57
|
+
*/
|
|
58
|
+
export declare function evaluateJarManifestEscalation(engineDir: string, changedPaths: readonly string[], baseline: BuildBaseline | undefined): Promise<JarEscalationDecision>;
|
|
59
|
+
/**
|
|
60
|
+
* Renders the operator-facing escalation notice. Names the manifest AND the
|
|
61
|
+
* reason: "a jar.mn changed" is the sentence that made the previous rule
|
|
62
|
+
* impossible to argue with from outside.
|
|
63
|
+
*/
|
|
64
|
+
export declare function formatJarEscalationNotice(decision: JarEscalationDecision): string;
|