@hominis/fireforge 0.45.0 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -0
- package/dist/bin/fireforge.js +6 -0
- package/dist/build-info.json +4 -4
- package/dist/src/cli-usage-error.d.ts +46 -0
- package/dist/src/cli-usage-error.js +81 -0
- package/dist/src/cli.d.ts +5 -1
- package/dist/src/cli.js +20 -21
- package/dist/src/commands/bootstrap.js +1 -2
- package/dist/src/commands/build.js +15 -6
- package/dist/src/commands/destructive-decision.d.ts +19 -0
- package/dist/src/commands/destructive-decision.js +31 -0
- package/dist/src/commands/doctor-check-core.d.ts +0 -6
- package/dist/src/commands/doctor-check-core.js +0 -8
- package/dist/src/commands/doctor-orphaned-harness.d.ts +0 -6
- package/dist/src/commands/doctor-orphaned-harness.js +4 -12
- package/dist/src/commands/doctor-post-rebase-audit.js +1 -1
- package/dist/src/commands/doctor.d.ts +2 -0
- package/dist/src/commands/doctor.js +7 -8
- package/dist/src/commands/download.js +35 -24
- package/dist/src/commands/export-all.js +21 -4
- package/dist/src/commands/export-flow.js +15 -45
- package/dist/src/commands/export-placement-conflicts.js +3 -3
- package/dist/src/commands/export-placement-gate.js +2 -8
- package/dist/src/commands/export-placement-policy.js +3 -5
- package/dist/src/commands/export-shared.d.ts +42 -23
- package/dist/src/commands/export-shared.js +4 -19
- package/dist/src/commands/export.js +10 -1
- package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
- package/dist/src/commands/furnace/authoring-config.js +19 -0
- package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
- package/dist/src/commands/furnace/create-browser-test.js +3 -8
- package/dist/src/commands/furnace/create-dry-run.js +2 -5
- package/dist/src/commands/furnace/create-templates.d.ts +17 -1
- package/dist/src/commands/furnace/create-templates.js +2 -1
- package/dist/src/commands/furnace/create.js +25 -19
- package/dist/src/commands/furnace/deploy.js +11 -2
- package/dist/src/commands/furnace/init.js +2 -1
- package/dist/src/commands/furnace/override.js +28 -19
- package/dist/src/commands/furnace/refresh.js +9 -1
- package/dist/src/commands/furnace/remove-mutations.d.ts +6 -7
- package/dist/src/commands/furnace/remove-mutations.js +0 -1
- package/dist/src/commands/furnace/remove.js +2 -6
- package/dist/src/commands/furnace/rename-browser-test.js +1 -7
- package/dist/src/commands/furnace/rename.js +29 -29
- package/dist/src/commands/furnace/status.js +12 -9
- package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
- package/dist/src/commands/furnace/test-file-name.js +39 -0
- package/dist/src/commands/furnace/validation-output.d.ts +21 -9
- package/dist/src/commands/furnace/validation-output.js +3 -8
- package/dist/src/commands/import.js +2 -9
- package/dist/src/commands/lint-per-patch.d.ts +2 -2
- package/dist/src/commands/lint-per-patch.js +19 -4
- package/dist/src/commands/lint-per-run-checkjs.d.ts +2 -2
- package/dist/src/commands/lint.d.ts +2 -1
- package/dist/src/commands/lint.js +1 -1
- package/dist/src/commands/manifest.js +1 -1
- package/dist/src/commands/package.js +2 -4
- package/dist/src/commands/patch/compact.js +14 -15
- package/dist/src/commands/patch/delete.js +15 -25
- package/dist/src/commands/patch/entry-projection.d.ts +13 -0
- package/dist/src/commands/patch/entry-projection.js +24 -0
- package/dist/src/commands/patch/lint-ignore.d.ts +0 -8
- package/dist/src/commands/patch/lint-ignore.js +19 -29
- package/dist/src/commands/patch/move-files-create.js +24 -10
- package/dist/src/commands/patch/move-files-into.js +38 -34
- package/dist/src/commands/patch/move-files.js +4 -13
- package/dist/src/commands/patch/rename.js +27 -41
- package/dist/src/commands/patch/reorder.js +25 -38
- package/dist/src/commands/patch/split-plan.d.ts +4 -5
- package/dist/src/commands/patch/split-plan.js +4 -8
- package/dist/src/commands/patch/split.js +45 -43
- package/dist/src/commands/patch/staged-dependency.js +16 -22
- package/dist/src/commands/patch/tier.js +15 -25
- package/dist/src/commands/patch-rename-map.d.ts +19 -0
- package/dist/src/commands/patch-rename-map.js +36 -0
- package/dist/src/commands/pending-resolution.d.ts +15 -0
- package/dist/src/commands/pending-resolution.js +25 -0
- package/dist/src/commands/re-export-adjacent.d.ts +24 -2
- package/dist/src/commands/re-export-adjacent.js +32 -16
- package/dist/src/commands/re-export-bulk-scan.js +6 -10
- package/dist/src/commands/re-export-drift.js +4 -4
- package/dist/src/commands/re-export-files.d.ts +2 -3
- package/dist/src/commands/re-export-files.js +38 -29
- package/dist/src/commands/re-export-lint.d.ts +3 -4
- package/dist/src/commands/re-export-lint.js +9 -2
- package/dist/src/commands/re-export-scan.js +5 -2
- package/dist/src/commands/re-export-single.d.ts +22 -3
- package/dist/src/commands/re-export-single.js +19 -11
- package/dist/src/commands/re-export.js +28 -11
- package/dist/src/commands/rebase/abort.js +3 -8
- package/dist/src/commands/rebase/continue.js +12 -12
- package/dist/src/commands/rebase/index.js +2 -2
- package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
- package/dist/src/commands/rebase/patch-loop.js +2 -7
- package/dist/src/commands/resolve.js +18 -10
- package/dist/src/commands/run.d.ts +0 -13
- package/dist/src/commands/run.js +12 -25
- package/dist/src/commands/setup-support.d.ts +16 -4
- package/dist/src/commands/setup-support.js +16 -16
- package/dist/src/commands/setup.js +3 -9
- package/dist/src/commands/source.js +8 -1
- package/dist/src/commands/status-check.js +2 -2
- package/dist/src/commands/status-output.js +2 -2
- package/dist/src/commands/status.js +3 -4
- package/dist/src/commands/test-build-phase.d.ts +2 -2
- package/dist/src/commands/test-build-phase.js +31 -7
- package/dist/src/commands/test-modes.d.ts +2 -2
- package/dist/src/commands/test-modes.js +0 -1
- package/dist/src/commands/test-register.js +5 -3
- package/dist/src/commands/test-run.js +25 -18
- package/dist/src/commands/test-stale-gate.js +4 -4
- package/dist/src/commands/test-verdict.d.ts +16 -2
- package/dist/src/commands/test-verdict.js +18 -3
- package/dist/src/commands/test.js +42 -5
- package/dist/src/commands/token-list.d.ts +26 -0
- package/dist/src/commands/token-list.js +153 -0
- package/dist/src/commands/token.js +17 -1
- package/dist/src/commands/tree.js +7 -2
- package/dist/src/commands/typecheck.js +9 -9
- package/dist/src/commands/wire.js +4 -3
- package/dist/src/core/branding.d.ts +0 -7
- package/dist/src/core/branding.js +3 -2
- package/dist/src/core/browser-wire.js +6 -3
- package/dist/src/core/build-audit-platform.d.ts +29 -0
- package/dist/src/core/build-audit-platform.js +140 -0
- package/dist/src/core/build-audit-registration.js +3 -2
- package/dist/src/core/build-audit-skip.d.ts +57 -0
- package/dist/src/core/build-audit-skip.js +112 -0
- package/dist/src/core/build-audit.d.ts +15 -2
- package/dist/src/core/build-audit.js +110 -10
- package/dist/src/core/build-baseline.d.ts +45 -28
- package/dist/src/core/build-baseline.js +6 -29
- package/dist/src/core/build-jar-escalation.js +3 -2
- package/dist/src/core/build-overwrite-guard.js +1 -1
- package/dist/src/core/build-prepare.d.ts +0 -8
- package/dist/src/core/build-prepare.js +7 -12
- package/dist/src/core/config-paths.d.ts +2 -6
- package/dist/src/core/config-paths.js +4 -2
- package/dist/src/core/config-state.d.ts +0 -6
- package/dist/src/core/config-state.js +1 -19
- package/dist/src/core/config-validate-build-audit.d.ts +8 -0
- package/dist/src/core/config-validate-build-audit.js +80 -0
- package/dist/src/core/config-validate-file-size.js +1 -1
- package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
- package/dist/src/core/config-validate-patch-policy.js +3 -3
- package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
- package/dist/src/core/config-validate-test-toolchains.js +4 -5
- package/dist/src/core/config-validate.js +14 -3
- package/dist/src/core/config.d.ts +2 -2
- package/dist/src/core/config.js +11 -15
- package/dist/src/core/coverage-extend.js +4 -4
- package/dist/src/core/discard-baseline.js +7 -7
- package/dist/src/core/engine-precondition.d.ts +1 -9
- package/dist/src/core/engine-precondition.js +3 -8
- package/dist/src/core/engine-session-lock.js +13 -17
- package/dist/src/core/export-moved-code-shape.js +2 -2
- package/dist/src/core/file-lock-owner.d.ts +103 -0
- package/dist/src/core/file-lock-owner.js +259 -0
- package/dist/src/core/file-lock.d.ts +2 -7
- package/dist/src/core/file-lock.js +40 -91
- package/dist/src/core/firefox-cache.d.ts +13 -1
- package/dist/src/core/firefox-cache.js +59 -34
- package/dist/src/core/firefox-download.d.ts +15 -0
- package/dist/src/core/firefox-download.js +37 -21
- package/dist/src/core/firefox-extract.d.ts +6 -5
- package/dist/src/core/firefox-extract.js +36 -18
- package/dist/src/core/firefox.d.ts +30 -32
- package/dist/src/core/firefox.js +5 -36
- package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
- package/dist/src/core/furnace-apply-dry-run.js +1 -7
- package/dist/src/core/furnace-apply-helpers.d.ts +0 -11
- package/dist/src/core/furnace-apply-helpers.js +8 -19
- package/dist/src/core/furnace-apply-overwrite-warn.js +4 -2
- package/dist/src/core/furnace-apply.d.ts +1 -1
- package/dist/src/core/furnace-apply.js +30 -10
- package/dist/src/core/furnace-config.d.ts +1 -1
- package/dist/src/core/furnace-config.js +23 -36
- package/dist/src/core/furnace-constants.js +3 -2
- package/dist/src/core/furnace-dir-entry.d.ts +20 -0
- package/dist/src/core/furnace-dir-entry.js +21 -0
- package/dist/src/core/furnace-operation.d.ts +2 -0
- package/dist/src/core/furnace-operation.js +3 -1
- package/dist/src/core/furnace-refresh.d.ts +18 -6
- package/dist/src/core/furnace-refresh.js +3 -6
- package/dist/src/core/furnace-registration-ast.d.ts +0 -1
- package/dist/src/core/furnace-registration-ast.js +0 -3
- package/dist/src/core/furnace-registration.d.ts +2 -1
- package/dist/src/core/furnace-registration.js +22 -29
- package/dist/src/core/furnace-scanner.js +3 -6
- package/dist/src/core/furnace-state-persist.d.ts +2 -2
- package/dist/src/core/furnace-validate-registration.js +5 -7
- package/dist/src/core/git-base.d.ts +3 -1
- package/dist/src/core/git-base.js +25 -3
- package/dist/src/core/git-diff-new-file.d.ts +40 -0
- package/dist/src/core/git-diff-new-file.js +119 -0
- package/dist/src/core/git-diff.d.ts +9 -9
- package/dist/src/core/git-diff.js +88 -157
- package/dist/src/core/git-file-ops.d.ts +0 -7
- package/dist/src/core/git-file-ops.js +4 -28
- package/dist/src/core/git-readonly-index.d.ts +0 -2
- package/dist/src/core/git-readonly-index.js +0 -4
- package/dist/src/core/git-status.d.ts +7 -0
- package/dist/src/core/git-status.js +21 -9
- package/dist/src/core/git.d.ts +0 -6
- package/dist/src/core/git.js +14 -43
- package/dist/src/core/harness-orphans.js +8 -7
- package/dist/src/core/history-log.d.ts +19 -0
- package/dist/src/core/history-log.js +29 -0
- package/dist/src/core/mach-error-hints.d.ts +0 -5
- package/dist/src/core/mach-error-hints.js +1 -1
- package/dist/src/core/mach-known-noise-filter.d.ts +4 -3
- package/dist/src/core/mach-known-noise-filter.js +7 -9
- package/dist/src/core/mach-mozconfig.js +2 -1
- package/dist/src/core/mach-python.d.ts +2 -0
- package/dist/src/core/mach-python.js +12 -7
- package/dist/src/core/mach-resource-shim.d.ts +13 -1
- package/dist/src/core/mach-resource-shim.js +112 -9
- package/dist/src/core/mach.d.ts +39 -53
- package/dist/src/core/mach.js +13 -81
- package/dist/src/core/marionette-port.d.ts +0 -17
- package/dist/src/core/marionette-port.js +12 -58
- package/dist/src/core/marionette-preflight.js +9 -27
- package/dist/src/core/mochitest-server-port.js +3 -3
- package/dist/src/core/moz-manifest-rules.js +37 -33
- package/dist/src/core/patch-apply-fuzz.js +4 -4
- package/dist/src/core/patch-apply.d.ts +4 -5
- package/dist/src/core/patch-apply.js +6 -12
- package/dist/src/core/patch-export-coverage.d.ts +0 -9
- package/dist/src/core/patch-export-coverage.js +1 -30
- package/dist/src/core/patch-export-update.d.ts +18 -1
- package/dist/src/core/patch-export-update.js +4 -3
- package/dist/src/core/patch-export.d.ts +11 -21
- package/dist/src/core/patch-export.js +44 -66
- package/dist/src/core/patch-files.d.ts +0 -4
- package/dist/src/core/patch-files.js +0 -11
- package/dist/src/core/patch-lint-binary.d.ts +6 -12
- package/dist/src/core/patch-lint-cache.d.ts +17 -1
- package/dist/src/core/patch-lint-cache.js +3 -6
- package/dist/src/core/patch-lint-checkjs.d.ts +33 -16
- package/dist/src/core/patch-lint-checkjs.js +37 -24
- package/dist/src/core/patch-lint-creators.d.ts +2 -6
- package/dist/src/core/patch-lint-cross.d.ts +16 -8
- package/dist/src/core/patch-lint-cross.js +49 -92
- package/dist/src/core/patch-lint-css.js +10 -6
- package/dist/src/core/patch-lint-file-size.js +3 -3
- package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
- package/dist/src/core/patch-lint-forward-registration.js +210 -0
- package/dist/src/core/patch-lint-jsdoc.js +36 -36
- package/dist/src/core/patch-lint-module-registration.d.ts +2 -8
- package/dist/src/core/patch-lint-prettier.js +1 -1
- package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
- package/dist/src/core/patch-lint-queue-types.js +12 -0
- package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
- package/dist/src/core/patch-lint-staged-registration.js +90 -0
- package/dist/src/core/patch-lint.d.ts +14 -9
- package/dist/src/core/patch-lint.js +4 -3
- package/dist/src/core/patch-lock.js +3 -3
- package/dist/src/core/patch-manifest-io.js +8 -7
- package/dist/src/core/patch-parse.d.ts +19 -8
- package/dist/src/core/patch-parse.js +25 -0
- package/dist/src/core/patch-policy.js +15 -27
- package/dist/src/core/patch-source-metadata.d.ts +1 -3
- package/dist/src/core/patch-source-metadata.js +0 -4
- package/dist/src/core/patch-transform.d.ts +0 -8
- package/dist/src/core/patch-transform.js +0 -10
- package/dist/src/core/process-cpu-time.js +1 -1
- package/dist/src/core/rebase-session.d.ts +2 -8
- package/dist/src/core/rebase-session.js +5 -17
- package/dist/src/core/register-browser-content.js +3 -2
- package/dist/src/core/register-browser-test.js +2 -1
- package/dist/src/core/register-module.js +2 -1
- package/dist/src/core/register-scaffold.js +5 -4
- package/dist/src/core/register-shared-css.js +2 -1
- package/dist/src/core/run-log-redact.d.ts +38 -0
- package/dist/src/core/run-log-redact.js +69 -0
- package/dist/src/core/run-log.d.ts +13 -2
- package/dist/src/core/run-log.js +67 -18
- package/dist/src/core/signal-critical.js +2 -4
- package/dist/src/core/smoke-patterns.d.ts +0 -1
- package/dist/src/core/smoke-patterns.js +6 -11
- package/dist/src/core/state-file.d.ts +12 -1
- package/dist/src/core/state-file.js +14 -1
- package/dist/src/core/status-classify.js +2 -1
- package/dist/src/core/test-changed-prefs.js +26 -11
- package/dist/src/core/test-harness-crash.js +2 -2
- package/dist/src/core/test-stale-check.d.ts +0 -2
- package/dist/src/core/test-stale-check.js +2 -2
- package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
- package/dist/src/core/test-xpcshell-retry.js +14 -11
- package/dist/src/core/token-category.d.ts +33 -0
- package/dist/src/core/token-category.js +56 -48
- package/dist/src/core/token-dark-mode.js +2 -14
- package/dist/src/core/token-inventory.d.ts +56 -0
- package/dist/src/core/token-inventory.js +109 -0
- package/dist/src/core/token-manager.d.ts +1 -1
- package/dist/src/core/token-manager.js +1 -1
- package/dist/src/core/token-variant.js +21 -22
- package/dist/src/core/toolchain-preflight.js +1 -1
- package/dist/src/core/tree-cow.d.ts +0 -2
- package/dist/src/core/tree-cow.js +2 -2
- package/dist/src/core/tree-store.js +4 -5
- package/dist/src/core/typecheck.js +2 -2
- package/dist/src/core/wire-dom-fragment.js +6 -17
- package/dist/src/core/wire-utils.js +5 -17
- package/dist/src/core/xpcshell-appdir.js +2 -4
- package/dist/src/errors/base.d.ts +53 -1
- package/dist/src/errors/base.js +79 -11
- package/dist/src/errors/build.js +20 -28
- package/dist/src/errors/config.d.ts +0 -14
- package/dist/src/errors/config.js +13 -34
- package/dist/src/errors/download.d.ts +13 -0
- package/dist/src/errors/download.js +53 -24
- package/dist/src/errors/furnace.js +8 -9
- package/dist/src/errors/git.d.ts +8 -8
- package/dist/src/errors/git.js +20 -30
- package/dist/src/errors/patch.js +8 -10
- package/dist/src/errors/rebase.js +7 -7
- package/dist/src/types/commands/options.d.ts +77 -123
- package/dist/src/types/config.d.ts +34 -2
- package/dist/src/types/index.d.ts +0 -1
- package/dist/src/utils/assert.d.ts +3 -15
- package/dist/src/utils/assert.js +1 -18
- package/dist/src/utils/build-info.d.ts +1 -1
- package/dist/src/utils/child-env.d.ts +2 -1
- package/dist/src/utils/elapsed.js +10 -1
- package/dist/src/utils/fs.js +8 -13
- package/dist/src/utils/hash.d.ts +12 -0
- package/dist/src/utils/hash.js +16 -0
- package/dist/src/utils/line-dispatch.d.ts +15 -0
- package/dist/src/utils/line-dispatch.js +48 -0
- package/dist/src/utils/options.d.ts +1 -7
- package/dist/src/utils/options.js +6 -7
- package/dist/src/utils/package-root.js +3 -12
- package/dist/src/utils/platform.d.ts +0 -4
- package/dist/src/utils/process-group.d.ts +2 -1
- package/dist/src/utils/process-group.js +20 -19
- package/dist/src/utils/process.d.ts +40 -26
- package/dist/src/utils/process.js +115 -166
- package/dist/src/utils/ps-duration.js +3 -3
- package/dist/src/utils/sleep.d.ts +26 -0
- package/dist/src/utils/sleep.js +31 -0
- package/dist/src/utils/validation.d.ts +2 -38
- package/dist/src/utils/validation.js +7 -16
- package/docs/configuration.md +89 -1
- package/docs/exit-codes.md +31 -19
- package/docs/lifecycle-invariants.md +18 -12
- package/docs/machine-output.md +31 -8
- package/docs/patch-workflow.md +2 -1
- package/docs/run-logs.md +44 -4
- package/docs/testing.md +8 -1
- package/package.json +8 -7
- package/dist/src/commands/rebase.d.ts +0 -4
- package/dist/src/commands/rebase.js +0 -5
- package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
- package/dist/src/core/patch-artifact-normalize.js +0 -24
- package/dist/src/core/patch-lint-reexports.d.ts +0 -7
- package/dist/src/core/patch-lint-reexports.js +0 -8
- package/dist/src/core/wire-targets.d.ts +0 -7
- package/dist/src/core/wire-targets.js +0 -8
|
@@ -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>;
|
|
@@ -39,9 +39,10 @@ import { toError } from '../utils/errors.js';
|
|
|
39
39
|
import { pathExists } from '../utils/fs.js';
|
|
40
40
|
import { info, verbose, warn } from '../utils/logger.js';
|
|
41
41
|
import { normalizePathSlashes } from '../utils/paths.js';
|
|
42
|
-
import { detectPlatformGate } from './build-audit-platform.js';
|
|
42
|
+
import { detectAncestorDirsGate, detectPlatformGate } from './build-audit-platform.js';
|
|
43
43
|
import { collectSameBasenameCandidates, findRegisteredTarget, resolveArtifactByRegistration, } from './build-audit-registration.js';
|
|
44
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';
|
|
45
46
|
import { resolveArtifactByKnownTransform } from './build-audit-transforms.js';
|
|
46
47
|
import { collectChangedEnginePaths } from './engine-changes.js';
|
|
47
48
|
import { loadPatchesManifest } from './patch-manifest-io.js';
|
|
@@ -101,6 +102,10 @@ export function isBuildInputPath(sourcePath) {
|
|
|
101
102
|
export function isJarManifestPath(sourcePath) {
|
|
102
103
|
return sourcePath === 'jar.mn' || sourcePath.endsWith('/jar.mn');
|
|
103
104
|
}
|
|
105
|
+
/** Zeroed per-class skip counters. */
|
|
106
|
+
function emptySkipCounts() {
|
|
107
|
+
return Object.fromEntries(AUDIT_SKIP_REASONS.map((reason) => [reason, 0]));
|
|
108
|
+
}
|
|
104
109
|
/**
|
|
105
110
|
* Decides whether a source path should be packaged. Returns true for paths
|
|
106
111
|
* whose extension or directory fragment matches a known-packaged pattern,
|
|
@@ -147,7 +152,7 @@ export function isPackageablePath(sourcePath) {
|
|
|
147
152
|
* @returns True when the path is a `components.conf` manifest.
|
|
148
153
|
*/
|
|
149
154
|
export function isXpcomManifestPath(sourcePath) {
|
|
150
|
-
return basename(sourcePath
|
|
155
|
+
return basename(normalizePathSlashes(sourcePath)) === 'components.conf';
|
|
151
156
|
}
|
|
152
157
|
/*
|
|
153
158
|
* Finds the unique obj-star directory with a dist subtree, or undefined
|
|
@@ -273,6 +278,55 @@ function isConfidentMatch(source, candidate) {
|
|
|
273
278
|
}
|
|
274
279
|
return false;
|
|
275
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
|
+
}
|
|
276
330
|
/**
|
|
277
331
|
* Audits one engine source path and returns its entry. Pure orchestration
|
|
278
332
|
* helper kept separate so `auditBuildArtifacts` stays under the per-function
|
|
@@ -280,12 +334,30 @@ function isConfidentMatch(source, candidate) {
|
|
|
280
334
|
*/
|
|
281
335
|
async function auditSinglePath(source, ctx) {
|
|
282
336
|
if (!isPackageablePath(source)) {
|
|
283
|
-
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' };
|
|
284
346
|
}
|
|
285
347
|
const gate = await detectPlatformGate(ctx.engineDir, source);
|
|
286
348
|
if (gate.gatedOff) {
|
|
287
349
|
verbose(`Audit: skipping engine/${source} — gated off by "${gate.gateExpression ?? '?'}".`);
|
|
288
|
-
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
|
+
};
|
|
289
361
|
}
|
|
290
362
|
// Tests only end up under `_tests/` after `mach package-tests` (or a
|
|
291
363
|
// test-run that invokes the target) has executed. A plain `mach build`
|
|
@@ -294,7 +366,7 @@ async function auditSinglePath(source, ctx) {
|
|
|
294
366
|
// Skip audit for test sources when no packaged-tests marker is present.
|
|
295
367
|
if (isTestPath(source) && !ctx.testsPackaged) {
|
|
296
368
|
verbose(`Audit: skipping engine/${source} — _tests/${PACKAGED_TESTS_MARKER} not present; full test packaging has not run for this build.`);
|
|
297
|
-
return { source, artifact: undefined, status: 'skipped' };
|
|
369
|
+
return { source, artifact: undefined, status: 'skipped', skipReason: 'tests-not-packaged' };
|
|
298
370
|
}
|
|
299
371
|
const sourcePath = join(ctx.engineDir, source);
|
|
300
372
|
let sourceMtime;
|
|
@@ -304,7 +376,7 @@ async function auditSinglePath(source, ctx) {
|
|
|
304
376
|
}
|
|
305
377
|
catch {
|
|
306
378
|
// Deletion that didn't propagate — distinct class of bug, not audited yet.
|
|
307
|
-
return { source, artifact: undefined, status: 'skipped' };
|
|
379
|
+
return { source, artifact: undefined, status: 'skipped', skipReason: 'source-unreadable' };
|
|
308
380
|
}
|
|
309
381
|
const roots = searchRootsFor(source, ctx.distRoot, ctx.testsRoot);
|
|
310
382
|
// Registration-aware resolution first: a `jar.mn` entry whose `(source)`
|
|
@@ -488,12 +560,13 @@ function describeOwnership(ownersByPath, source) {
|
|
|
488
560
|
* @param baseline Optional previous-build baseline marker.
|
|
489
561
|
* @returns Summary of artifact status counts.
|
|
490
562
|
*/
|
|
491
|
-
export async function auditBuildArtifacts(projectRoot, engineDir, baseline) {
|
|
563
|
+
export async function auditBuildArtifacts(projectRoot, engineDir, baseline, options = {}) {
|
|
492
564
|
const summary = {
|
|
493
565
|
updated: 0,
|
|
494
566
|
stale: 0,
|
|
495
567
|
missing: 0,
|
|
496
568
|
skipped: 0,
|
|
569
|
+
skippedByReason: emptySkipCounts(),
|
|
497
570
|
entries: [],
|
|
498
571
|
};
|
|
499
572
|
const distRoot = await resolveDistRoot(engineDir);
|
|
@@ -514,18 +587,45 @@ export async function auditBuildArtifacts(projectRoot, engineDir, baseline) {
|
|
|
514
587
|
// it. FireForge already resolves this for `status --ownership`; carrying
|
|
515
588
|
// it here turns a three-line mystery into three dismissible lines.
|
|
516
589
|
const ownersByPath = await resolveAuditOwnership(projectRoot);
|
|
517
|
-
const ctx = {
|
|
590
|
+
const ctx = {
|
|
591
|
+
engineDir,
|
|
592
|
+
distRoot,
|
|
593
|
+
testsRoot,
|
|
594
|
+
testsPackaged,
|
|
595
|
+
selectedBranding: await resolveSelectedBranding(engineDir),
|
|
596
|
+
unpackaged: options.unpackaged ?? [],
|
|
597
|
+
};
|
|
518
598
|
for (const source of changed) {
|
|
519
|
-
const result = await auditSinglePath(source, ctx);
|
|
599
|
+
const result = applyUnpackagedCarveOut(await auditSinglePath(source, ctx), ctx);
|
|
520
600
|
summary[result.status] += 1;
|
|
601
|
+
if (result.status === 'skipped' && result.skipReason !== undefined) {
|
|
602
|
+
summary.skippedByReason[result.skipReason] += 1;
|
|
603
|
+
}
|
|
521
604
|
summary.entries.push({
|
|
522
605
|
source: result.source,
|
|
523
606
|
artifact: result.artifact,
|
|
524
607
|
status: result.status,
|
|
608
|
+
...(result.skipReason === undefined ? {} : { skipReason: result.skipReason }),
|
|
525
609
|
});
|
|
526
610
|
if (result.warning)
|
|
527
611
|
warn(`${result.warning}${describeOwnership(ownersByPath, result.source)}`);
|
|
612
|
+
if (result.notice)
|
|
613
|
+
info(result.notice);
|
|
528
614
|
}
|
|
529
|
-
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)}`);
|
|
530
617
|
return summary;
|
|
531
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') {
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
* error direction stays the slow-but-correct one.
|
|
31
31
|
*/
|
|
32
32
|
import { join } from 'node:path';
|
|
33
|
+
import { toError } from '../utils/errors.js';
|
|
33
34
|
import { readText } from '../utils/fs.js';
|
|
34
35
|
import { verbose } from '../utils/logger.js';
|
|
35
36
|
import { isJarManifestPath } from './build-audit.js';
|
|
@@ -82,7 +83,7 @@ export async function evaluateJarManifestEscalation(engineDir, changedPaths, bas
|
|
|
82
83
|
}
|
|
83
84
|
catch (error) {
|
|
84
85
|
// Fail closed: an unanswerable "is this new?" keeps the old rule.
|
|
85
|
-
verbose(`jar escalation: could not classify engine/${path} (${
|
|
86
|
+
verbose(`jar escalation: could not classify engine/${path} (${toError(error).message}); escalating.`);
|
|
86
87
|
causes.push({ path, reason: 'could not determine whether the manifest is new' });
|
|
87
88
|
continue;
|
|
88
89
|
}
|
|
@@ -95,7 +96,7 @@ export async function evaluateJarManifestEscalation(engineDir, changedPaths, bas
|
|
|
95
96
|
body = await readText(join(engineDir, path));
|
|
96
97
|
}
|
|
97
98
|
catch (error) {
|
|
98
|
-
verbose(`jar escalation: could not read engine/${path} (${
|
|
99
|
+
verbose(`jar escalation: could not read engine/${path} (${toError(error).message}); escalating.`);
|
|
99
100
|
causes.push({ path, reason: 'manifest could not be read' });
|
|
100
101
|
continue;
|
|
101
102
|
}
|
|
@@ -143,7 +143,7 @@ export function formatUnexportedDriftWarning(files) {
|
|
|
143
143
|
const owner = entry.owner !== undefined ? ` (owned by ${entry.owner})` : '';
|
|
144
144
|
return ` ${entry.file} [${entry.classification}]${owner}`;
|
|
145
145
|
});
|
|
146
|
-
return (`This build is about to rewrite ${
|
|
146
|
+
return (`This build is about to rewrite ${files.length} engine file(s) from FireForge-owned ` +
|
|
147
147
|
'sources, and their current content matches NEITHER a patch body NOR the pristine ' +
|
|
148
148
|
'baseline — so the edits below are recorded nowhere and the build will destroy them:\n' +
|
|
149
149
|
`${rows.join('\n')}\n\n` +
|
|
@@ -36,14 +36,6 @@ export interface PrepareBuildOptions {
|
|
|
36
36
|
*/
|
|
37
37
|
refuseUnexportedDrift?: boolean | undefined;
|
|
38
38
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Packaging manifests whose graph/destination directories MAY need a full
|
|
41
|
-
* build. Path-shape only: {@link evaluateJarManifestEscalation} decides
|
|
42
|
-
* whether a given changed manifest actually forces one (a new manifest, or
|
|
43
|
-
* one that redirects its install base directory), so an entry added to an
|
|
44
|
-
* existing `dist/bin` manifest no longer costs a ~10-minute build.
|
|
45
|
-
*/
|
|
46
|
-
export declare function requiresFullBuildForIncrementalTest(path: string): boolean;
|
|
47
39
|
/**
|
|
48
40
|
* Describes an exit code in the shell's 128+N signal convention. Truncated
|
|
49
41
|
* configure/build logs with a signal-shaped exit (e.g. 144 = 128+16, SIGURG
|
|
@@ -24,16 +24,6 @@ import { generateMozconfig, runMachCapture } from './mach.js';
|
|
|
24
24
|
import { explainMachError } from './mach-error-hints.js';
|
|
25
25
|
/** Path fragments of files whose edits invalidate the recursive-make backend. */
|
|
26
26
|
const BACKEND_INVALIDATING_SUFFIXES = ['moz.build', 'moz.configure', 'Makefile.in'];
|
|
27
|
-
/**
|
|
28
|
-
* Packaging manifests whose graph/destination directories MAY need a full
|
|
29
|
-
* build. Path-shape only: {@link evaluateJarManifestEscalation} decides
|
|
30
|
-
* whether a given changed manifest actually forces one (a new manifest, or
|
|
31
|
-
* one that redirects its install base directory), so an entry added to an
|
|
32
|
-
* existing `dist/bin` manifest no longer costs a ~10-minute build.
|
|
33
|
-
*/
|
|
34
|
-
export function requiresFullBuildForIncrementalTest(path) {
|
|
35
|
-
return isJarManifestPath(path);
|
|
36
|
-
}
|
|
37
27
|
/**
|
|
38
28
|
* Build-input manifests changed since the last SUCCESSFUL build — not
|
|
39
29
|
* merely dirty against HEAD. `collectChangedEnginePaths` unions the
|
|
@@ -126,9 +116,14 @@ function buildConfigureFailureError(captured) {
|
|
|
126
116
|
* @param baseline - Last successful build's baseline
|
|
127
117
|
*/
|
|
128
118
|
async function decideJarEscalation(engineDir, changed, baseline) {
|
|
129
|
-
|
|
119
|
+
// Packaging manifests whose graph/destination directories MAY need a full
|
|
120
|
+
// build. Path-shape only: `evaluateJarManifestEscalation` decides whether a
|
|
121
|
+
// given changed manifest actually forces one (a new manifest, or one that
|
|
122
|
+
// redirects its install base directory), so an entry added to an existing
|
|
123
|
+
// `dist/bin` manifest no longer costs a ~10-minute build.
|
|
124
|
+
const decision = await evaluateJarManifestEscalation(engineDir, changed.filter(isJarManifestPath), baseline);
|
|
130
125
|
if (decision.cleared.length > 0) {
|
|
131
|
-
verbose(`jar escalation: ${
|
|
126
|
+
verbose(`jar escalation: ${decision.cleared.length} changed jar.mn add entries to an existing dist/bin manifest; not escalating for ${decision.cleared.join(', ')}.`);
|
|
132
127
|
}
|
|
133
128
|
return decision.escalate ? formatJarEscalationNotice(decision) : undefined;
|
|
134
129
|
}
|
|
@@ -2,16 +2,12 @@
|
|
|
2
2
|
* Project path derivation from a root directory.
|
|
3
3
|
*/
|
|
4
4
|
import type { ProjectPaths } from '../types/config.js';
|
|
5
|
-
/** Name of the configuration file */
|
|
6
|
-
export declare const CONFIG_FILENAME = "fireforge.json";
|
|
7
5
|
/** Name of the fireforge data directory */
|
|
8
6
|
export declare const FIREFORGE_DIR = ".fireforge";
|
|
9
|
-
/** Name of the state file */
|
|
10
|
-
export declare const STATE_FILENAME = "state.json";
|
|
11
7
|
/** Supported top-level fireforge.json keys backed by the current schema. */
|
|
12
|
-
export declare const SUPPORTED_CONFIG_ROOT_KEYS: readonly ["name", "vendor", "appId", "binaryName", "firefox", "build", "test", "externalToolchains", "license", "wire", "patchLint", "patchPolicy", "typecheck", "markerComment"];
|
|
8
|
+
export declare const SUPPORTED_CONFIG_ROOT_KEYS: readonly ["name", "vendor", "appId", "binaryName", "firefox", "build", "buildAudit", "test", "externalToolchains", "license", "wire", "patchLint", "patchPolicy", "typecheck", "markerComment"];
|
|
13
9
|
/** Supported config paths that can be read or set without --force. */
|
|
14
|
-
export declare const SUPPORTED_CONFIG_PATHS: readonly ["name", "vendor", "appId", "binaryName", "license", "firefox", "firefox.version", "firefox.product", "firefox.sha256", "firefox.candidate", "build", "build.jobs", "test", "test.canaryPath", "test.canaryTimeoutSeconds", "externalToolchains", "wire", "wire.subscriptDir", "patchLint", "patchLint.checkJs", "patchLint.checkJsStrict", "patchLint.checkJsCompilerOptions", "patchLint.checkJsExtraShim", "patchLint.checkJsTestFiles", "patchLint.checkJsTestShim", "patchLint.rawColorAllowlist", "patchLint.jsdocClassMethods", "patchLint.testAssertionFloor", "patchLint.chromeScriptJsDoc", "patchPolicy", "typecheck", "typecheck.projects", "typecheck.extraShim", "markerComment"];
|
|
10
|
+
export declare const SUPPORTED_CONFIG_PATHS: readonly ["name", "vendor", "appId", "binaryName", "license", "firefox", "firefox.version", "firefox.product", "firefox.sha256", "firefox.candidate", "firefox.allowUnverifiedDownload", "build", "build.jobs", "test", "test.canaryPath", "test.canaryTimeoutSeconds", "externalToolchains", "wire", "wire.subscriptDir", "patchLint", "patchLint.checkJs", "patchLint.checkJsStrict", "patchLint.checkJsCompilerOptions", "patchLint.checkJsExtraShim", "patchLint.checkJsTestFiles", "patchLint.checkJsTestShim", "patchLint.rawColorAllowlist", "patchLint.jsdocClassMethods", "patchLint.testAssertionFloor", "patchLint.chromeScriptJsDoc", "patchPolicy", "typecheck", "typecheck.projects", "typecheck.extraShim", "markerComment"];
|
|
15
11
|
/**
|
|
16
12
|
* Gets all project paths based on a root directory.
|
|
17
13
|
* @param root - Root directory of the project
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
/** Name of the configuration file */
|
|
7
|
-
|
|
7
|
+
const CONFIG_FILENAME = 'fireforge.json';
|
|
8
8
|
/** Name of the fireforge data directory */
|
|
9
9
|
export const FIREFORGE_DIR = '.fireforge';
|
|
10
10
|
/** Name of the state file */
|
|
11
|
-
|
|
11
|
+
const STATE_FILENAME = 'state.json';
|
|
12
12
|
/** Name of the engine directory */
|
|
13
13
|
const ENGINE_DIR = 'engine';
|
|
14
14
|
/** Name of the patches directory */
|
|
@@ -25,6 +25,7 @@ export const SUPPORTED_CONFIG_ROOT_KEYS = [
|
|
|
25
25
|
'binaryName',
|
|
26
26
|
'firefox',
|
|
27
27
|
'build',
|
|
28
|
+
'buildAudit',
|
|
28
29
|
'test',
|
|
29
30
|
'externalToolchains',
|
|
30
31
|
'license',
|
|
@@ -46,6 +47,7 @@ export const SUPPORTED_CONFIG_PATHS = [
|
|
|
46
47
|
'firefox.product',
|
|
47
48
|
'firefox.sha256',
|
|
48
49
|
'firefox.candidate',
|
|
50
|
+
'firefox.allowUnverifiedDownload',
|
|
49
51
|
'build',
|
|
50
52
|
'build.jobs',
|
|
51
53
|
'test',
|