@hominis/fireforge 0.49.2 → 0.51.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 +87 -1
- package/README.md +1 -1
- package/dist/bin/fireforge.js +6 -0
- package/dist/build-info.json +4 -4
- package/dist/src/commands/build-prelock.d.ts +20 -0
- package/dist/src/commands/build-prelock.js +50 -0
- package/dist/src/commands/build.d.ts +3 -1
- package/dist/src/commands/build.js +13 -6
- package/dist/src/commands/check-report.d.ts +41 -0
- package/dist/src/commands/check-report.js +91 -0
- package/dist/src/commands/check-run.d.ts +33 -0
- package/dist/src/commands/check-run.js +112 -0
- package/dist/src/commands/check-steps.d.ts +75 -0
- package/dist/src/commands/check-steps.js +200 -0
- package/dist/src/commands/check.d.ts +32 -0
- package/dist/src/commands/check.js +160 -0
- package/dist/src/commands/config.js +5 -6
- package/dist/src/commands/declarations-report.d.ts +43 -0
- package/dist/src/commands/declarations-report.js +76 -0
- package/dist/src/commands/doctor-engine-eslint.js +6 -1
- package/dist/src/commands/doctor-source-pin.js +4 -1
- package/dist/src/commands/export-adopt-check.d.ts +38 -0
- package/dist/src/commands/export-adopt-check.js +98 -0
- package/dist/src/commands/export-flow.js +29 -14
- package/dist/src/commands/export-placement-gate.d.ts +6 -2
- package/dist/src/commands/export-placement-gate.js +8 -0
- package/dist/src/commands/export-shared.d.ts +2 -1
- package/dist/src/commands/export-staged-registration.d.ts +46 -0
- package/dist/src/commands/export-staged-registration.js +91 -0
- package/dist/src/commands/forward-import-remedy.d.ts +32 -0
- package/dist/src/commands/forward-import-remedy.js +63 -0
- package/dist/src/commands/furnace/apply.js +3 -3
- package/dist/src/commands/furnace/chrome-doc-remove.d.ts +2 -1
- package/dist/src/commands/furnace/chrome-doc-remove.js +2 -2
- package/dist/src/commands/furnace/chrome-doc.d.ts +2 -1
- package/dist/src/commands/furnace/chrome-doc.js +2 -2
- package/dist/src/commands/furnace/create.js +2 -2
- package/dist/src/commands/furnace/deploy-named.d.ts +48 -0
- package/dist/src/commands/furnace/deploy-named.js +107 -0
- package/dist/src/commands/furnace/deploy.d.ts +3 -2
- package/dist/src/commands/furnace/deploy.js +50 -86
- package/dist/src/commands/furnace/diff.js +26 -3
- package/dist/src/commands/furnace/index.js +66 -41
- package/dist/src/commands/furnace/init.js +13 -6
- package/dist/src/commands/furnace/override.js +4 -2
- package/dist/src/commands/furnace/preview.js +4 -3
- package/dist/src/commands/furnace/refresh.js +2 -2
- package/dist/src/commands/furnace/remove.js +4 -3
- package/dist/src/commands/furnace/rename-dry-run.d.ts +29 -0
- package/dist/src/commands/furnace/rename-dry-run.js +127 -0
- package/dist/src/commands/furnace/rename-helpers.d.ts +7 -0
- package/dist/src/commands/furnace/rename-helpers.js +15 -1
- package/dist/src/commands/furnace/rename-registrations.d.ts +41 -0
- package/dist/src/commands/furnace/rename-registrations.js +98 -0
- package/dist/src/commands/furnace/rename.d.ts +2 -1
- package/dist/src/commands/furnace/rename.js +28 -66
- package/dist/src/commands/furnace/scan.d.ts +9 -4
- package/dist/src/commands/furnace/scan.js +11 -9
- package/dist/src/commands/furnace/sync.js +3 -2
- package/dist/src/commands/furnace/validate-report.d.ts +45 -0
- package/dist/src/commands/furnace/validate-report.js +91 -0
- package/dist/src/commands/furnace/validate.d.ts +1 -1
- package/dist/src/commands/furnace/validate.js +151 -113
- package/dist/src/commands/furnace/validation-output.js +14 -3
- package/dist/src/commands/lint-per-patch.js +36 -61
- package/dist/src/commands/lint-per-run-checkjs.js +2 -2
- package/dist/src/commands/lint-report.d.ts +8 -4
- package/dist/src/commands/lint-report.js +20 -5
- package/dist/src/commands/lint.js +15 -34
- package/dist/src/commands/manifest.js +4 -0
- package/dist/src/commands/park.d.ts +22 -0
- package/dist/src/commands/park.js +232 -0
- package/dist/src/commands/patch/delete-gap-notice.js +3 -1
- package/dist/src/commands/patch/delete.d.ts +1 -1
- package/dist/src/commands/patch/delete.js +24 -104
- package/dist/src/commands/patch/index.d.ts +3 -3
- package/dist/src/commands/patch/index.js +10 -4
- package/dist/src/commands/patch/lint-ignore-from.d.ts +44 -0
- package/dist/src/commands/patch/lint-ignore-from.js +142 -0
- package/dist/src/commands/patch/lint-ignore.d.ts +5 -1
- package/dist/src/commands/patch/lint-ignore.js +78 -31
- package/dist/src/commands/patch/move-files-create.js +34 -18
- package/dist/src/commands/patch/move-files-into.js +15 -7
- package/dist/src/commands/patch/move-files.js +6 -2
- package/dist/src/commands/patch/removal-created-files.d.ts +48 -0
- package/dist/src/commands/patch/removal-created-files.js +83 -0
- package/dist/src/commands/patch/removal-dependents.d.ts +63 -0
- package/dist/src/commands/patch/removal-dependents.js +133 -0
- package/dist/src/commands/patch/rename.js +20 -1
- package/dist/src/commands/patch/reserve.d.ts +33 -0
- package/dist/src/commands/patch/reserve.js +152 -0
- package/dist/src/commands/patch/retire.d.ts +34 -0
- package/dist/src/commands/patch/retire.js +133 -0
- package/dist/src/commands/patch/split-plan.d.ts +17 -8
- package/dist/src/commands/patch/split-plan.js +60 -28
- package/dist/src/commands/patch/split-staged.d.ts +108 -0
- package/dist/src/commands/patch/split-staged.js +202 -0
- package/dist/src/commands/patch/split.d.ts +5 -0
- package/dist/src/commands/patch/split.js +62 -17
- package/dist/src/commands/patch/stage.d.ts +34 -0
- package/dist/src/commands/patch/stage.js +153 -0
- package/dist/src/commands/patch/staged-dependency-batch.d.ts +48 -0
- package/dist/src/commands/patch/staged-dependency-batch.js +176 -0
- package/dist/src/commands/patch/staged-dependency-entries.d.ts +36 -0
- package/dist/src/commands/patch/staged-dependency-entries.js +52 -0
- package/dist/src/commands/patch/staged-dependency-from.d.ts +14 -0
- package/dist/src/commands/patch/staged-dependency-from.js +108 -0
- package/dist/src/commands/patch/staged-dependency-remove.d.ts +31 -0
- package/dist/src/commands/patch/staged-dependency-remove.js +80 -0
- package/dist/src/commands/patch/staged-dependency.js +54 -139
- package/dist/src/commands/re-export-adjacent-registrations.d.ts +20 -0
- package/dist/src/commands/re-export-adjacent-registrations.js +75 -0
- package/dist/src/commands/re-export-adjacent.d.ts +50 -2
- package/dist/src/commands/re-export-adjacent.js +160 -40
- package/dist/src/commands/re-export-bulk-scan.d.ts +2 -1
- package/dist/src/commands/re-export-bulk-scan.js +22 -7
- package/dist/src/commands/re-export-drift.d.ts +36 -8
- package/dist/src/commands/re-export-drift.js +73 -49
- package/dist/src/commands/re-export-files.js +11 -8
- package/dist/src/commands/re-export-hunks.d.ts +35 -0
- package/dist/src/commands/re-export-hunks.js +220 -0
- package/dist/src/commands/re-export-lint.d.ts +2 -1
- package/dist/src/commands/re-export-lint.js +23 -10
- package/dist/src/commands/re-export-options.js +74 -6
- package/dist/src/commands/re-export-preflight.d.ts +23 -0
- package/dist/src/commands/re-export-preflight.js +160 -0
- package/dist/src/commands/re-export-purity-scope.d.ts +18 -0
- package/dist/src/commands/re-export-purity-scope.js +131 -0
- package/dist/src/commands/re-export-register.js +56 -17
- package/dist/src/commands/re-export-removed.d.ts +13 -2
- package/dist/src/commands/re-export-removed.js +57 -12
- package/dist/src/commands/re-export-scan-owner.d.ts +44 -0
- package/dist/src/commands/re-export-scan-owner.js +116 -0
- package/dist/src/commands/re-export-scan.d.ts +19 -9
- package/dist/src/commands/re-export-scan.js +60 -24
- package/dist/src/commands/re-export-single.js +122 -46
- package/dist/src/commands/re-export-stale-registrations.d.ts +18 -0
- package/dist/src/commands/re-export-stale-registrations.js +62 -0
- package/dist/src/commands/re-export-summary.d.ts +28 -0
- package/dist/src/commands/re-export-summary.js +36 -0
- package/dist/src/commands/re-export.js +71 -57
- package/dist/src/commands/rebase/index.js +17 -17
- package/dist/src/commands/run.js +16 -2
- package/dist/src/commands/status-check.d.ts +55 -16
- package/dist/src/commands/status-check.js +92 -37
- package/dist/src/commands/status-json.d.ts +22 -9
- package/dist/src/commands/status-json.js +47 -32
- package/dist/src/commands/status-output.js +3 -1
- package/dist/src/commands/status-scope.d.ts +37 -0
- package/dist/src/commands/status-scope.js +54 -0
- package/dist/src/commands/status.js +66 -23
- package/dist/src/commands/test-browser-preflight.js +19 -3
- package/dist/src/commands/test-build-furnace.d.ts +22 -0
- package/dist/src/commands/test-build-furnace.js +54 -0
- package/dist/src/commands/test-build-phase.js +64 -41
- package/dist/src/commands/test-checkjs.js +10 -2
- package/dist/src/commands/test-diagnose.js +7 -0
- package/dist/src/commands/test-dispatch.d.ts +39 -0
- package/dist/src/commands/test-dispatch.js +211 -0
- package/dist/src/commands/test-furnace-reach.d.ts +37 -0
- package/dist/src/commands/test-furnace-reach.js +132 -0
- package/dist/src/commands/test-modes.d.ts +16 -1
- package/dist/src/commands/test-modes.js +24 -1
- package/dist/src/commands/test-multi-verdict.d.ts +61 -0
- package/dist/src/commands/test-multi-verdict.js +111 -0
- package/dist/src/commands/test-orchestrate.d.ts +52 -0
- package/dist/src/commands/test-orchestrate.js +240 -0
- package/dist/src/commands/test-perf-scope.d.ts +50 -0
- package/dist/src/commands/test-perf-scope.js +138 -0
- package/dist/src/commands/test-prelock.d.ts +51 -0
- package/dist/src/commands/test-prelock.js +273 -0
- package/dist/src/commands/test-register.js +69 -10
- package/dist/src/commands/test-results.d.ts +62 -0
- package/dist/src/commands/test-results.js +62 -0
- package/dist/src/commands/test-run.d.ts +13 -55
- package/dist/src/commands/test-run.js +74 -99
- package/dist/src/commands/test-shards.d.ts +110 -0
- package/dist/src/commands/test-shards.js +255 -0
- package/dist/src/commands/test-stale-gate.d.ts +5 -1
- package/dist/src/commands/test-stale-gate.js +60 -28
- package/dist/src/commands/test-verdict.d.ts +11 -1
- package/dist/src/commands/test-verdict.js +28 -1
- package/dist/src/commands/test.d.ts +6 -1
- package/dist/src/commands/test.js +120 -124
- package/dist/src/commands/token-lint.d.ts +14 -0
- package/dist/src/commands/token-lint.js +151 -0
- package/dist/src/commands/token.js +10 -0
- package/dist/src/commands/verify-scope.d.ts +28 -0
- package/dist/src/commands/verify-scope.js +67 -0
- package/dist/src/commands/verify.d.ts +10 -2
- package/dist/src/commands/verify.js +67 -36
- package/dist/src/core/build-baseline-types.d.ts +46 -0
- package/dist/src/core/build-baseline-validate.js +19 -0
- package/dist/src/core/build-baseline.d.ts +5 -1
- package/dist/src/core/build-baseline.js +3 -1
- package/dist/src/core/build-overwrite-guard.d.ts +50 -0
- package/dist/src/core/build-overwrite-guard.js +84 -4
- package/dist/src/core/build-prepare.d.ts +13 -0
- package/dist/src/core/build-prepare.js +42 -10
- package/dist/src/core/build-run-record.d.ts +74 -0
- package/dist/src/core/build-run-record.js +136 -0
- package/dist/src/core/checkjs-support-heads.d.ts +41 -0
- package/dist/src/core/checkjs-support-heads.js +104 -0
- package/dist/src/core/config-paths.d.ts +2 -2
- package/dist/src/core/config-paths.js +10 -0
- package/dist/src/core/config-validate-check.d.ts +8 -0
- package/dist/src/core/config-validate-check.js +219 -0
- package/dist/src/core/config-validate-file-size.d.ts +9 -6
- package/dist/src/core/config-validate-file-size.js +91 -2
- package/dist/src/core/config-validate-history.d.ts +11 -0
- package/dist/src/core/config-validate-history.js +38 -0
- package/dist/src/core/config-validate-patch-policy.js +100 -0
- package/dist/src/core/config-validate-test-toolchains.js +21 -0
- package/dist/src/core/config-validate-token-scales.d.ts +9 -0
- package/dist/src/core/config-validate-token-scales.js +92 -0
- package/dist/src/core/config-validate.js +20 -5
- package/dist/src/core/config.d.ts +5 -0
- package/dist/src/core/config.js +16 -0
- package/dist/src/core/coverage-dispatch.d.ts +24 -0
- package/dist/src/core/coverage-dispatch.js +44 -0
- package/dist/src/core/coverage-extend.d.ts +70 -45
- package/dist/src/core/coverage-extend.js +199 -144
- package/dist/src/core/coverage-records.d.ts +108 -0
- package/dist/src/core/coverage-records.js +384 -0
- package/dist/src/core/destructive.d.ts +7 -4
- package/dist/src/core/destructive.js +10 -10
- package/dist/src/core/engine-session-lock.d.ts +16 -1
- package/dist/src/core/engine-session-lock.js +38 -10
- package/dist/src/core/export-moved-code-shape.d.ts +49 -11
- package/dist/src/core/export-moved-code-shape.js +135 -44
- package/dist/src/core/file-lock-queue.d.ts +67 -0
- package/dist/src/core/file-lock-queue.js +221 -0
- package/dist/src/core/file-lock-shared.d.ts +57 -0
- package/dist/src/core/file-lock-shared.js +117 -0
- package/dist/src/core/file-lock.d.ts +13 -25
- package/dist/src/core/file-lock.js +163 -140
- package/dist/src/core/furnace-apply-output.d.ts +10 -0
- package/dist/src/core/furnace-apply-output.js +31 -1
- package/dist/src/core/furnace-apply.js +22 -12
- package/dist/src/core/furnace-composed-source.d.ts +76 -0
- package/dist/src/core/furnace-composed-source.js +167 -0
- package/dist/src/core/furnace-config-validate-exceptions.d.ts +16 -0
- package/dist/src/core/furnace-config-validate-exceptions.js +102 -0
- package/dist/src/core/furnace-config-validate.js +14 -0
- package/dist/src/core/furnace-css-fragments.d.ts +10 -0
- package/dist/src/core/furnace-css-fragments.js +26 -0
- package/dist/src/core/furnace-operation.d.ts +3 -3
- package/dist/src/core/furnace-registration-jar-inplace.d.ts +12 -0
- package/dist/src/core/furnace-registration-jar-inplace.js +80 -0
- package/dist/src/core/furnace-registration-remove.d.ts +22 -1
- package/dist/src/core/furnace-registration-remove.js +53 -17
- package/dist/src/core/furnace-registration-rename.d.ts +26 -0
- package/dist/src/core/furnace-registration-rename.js +77 -0
- package/dist/src/core/furnace-registration.d.ts +14 -4
- package/dist/src/core/furnace-registration.js +44 -10
- package/dist/src/core/furnace-staleness.d.ts +7 -0
- package/dist/src/core/furnace-staleness.js +34 -24
- package/dist/src/core/furnace-validate-accessibility.d.ts +8 -4
- package/dist/src/core/furnace-validate-accessibility.js +71 -28
- package/dist/src/core/furnace-validate-exceptions.d.ts +48 -0
- package/dist/src/core/furnace-validate-exceptions.js +80 -0
- package/dist/src/core/furnace-validate-icon-button.d.ts +7 -0
- package/dist/src/core/furnace-validate-icon-button.js +78 -0
- package/dist/src/core/furnace-validate.js +1 -1
- package/dist/src/core/harness-port-fallback.d.ts +60 -0
- package/dist/src/core/harness-port-fallback.js +96 -0
- package/dist/src/core/history-log-file.d.ts +61 -0
- package/dist/src/core/history-log-file.js +164 -0
- package/dist/src/core/history-log.d.ts +2 -1
- package/dist/src/core/history-log.js +2 -1
- package/dist/src/core/json-preserving-edit.d.ts +22 -0
- package/dist/src/core/json-preserving-edit.js +199 -0
- package/dist/src/core/line-prefixer.d.ts +18 -0
- package/dist/src/core/line-prefixer.js +34 -0
- package/dist/src/core/mach.d.ts +7 -0
- package/dist/src/core/mach.js +32 -10
- package/dist/src/core/module-resolution-preflight.d.ts +19 -3
- package/dist/src/core/module-resolution-preflight.js +24 -6
- package/dist/src/core/negative-control-compiled.d.ts +27 -0
- package/dist/src/core/negative-control-compiled.js +73 -0
- package/dist/src/core/negative-control-diff.d.ts +51 -0
- package/dist/src/core/negative-control-diff.js +179 -0
- package/dist/src/core/negative-control.d.ts +150 -0
- package/dist/src/core/negative-control.js +399 -0
- package/dist/src/core/objdir-install-manifests.d.ts +73 -0
- package/dist/src/core/objdir-install-manifests.js +226 -0
- package/dist/src/core/park-store.d.ts +63 -0
- package/dist/src/core/park-store.js +216 -0
- package/dist/src/core/patch-export-update.d.ts +6 -0
- package/dist/src/core/patch-export-update.js +13 -3
- package/dist/src/core/patch-export.d.ts +3 -2
- package/dist/src/core/patch-export.js +22 -16
- package/dist/src/core/patch-files.js +26 -8
- package/dist/src/core/patch-lint-checkjs-any-import.d.ts +39 -0
- package/dist/src/core/patch-lint-checkjs-any-import.js +63 -0
- package/dist/src/core/patch-lint-checkjs-diagnostics.d.ts +17 -0
- package/dist/src/core/patch-lint-checkjs-diagnostics.js +32 -0
- package/dist/src/core/patch-lint-checkjs.d.ts +10 -1
- package/dist/src/core/patch-lint-checkjs.js +20 -13
- package/dist/src/core/patch-lint-code-lines.d.ts +59 -0
- package/dist/src/core/patch-lint-code-lines.js +105 -0
- package/dist/src/core/patch-lint-cross.d.ts +3 -1
- package/dist/src/core/patch-lint-cross.js +17 -4
- package/dist/src/core/patch-lint-css.d.ts +8 -2
- package/dist/src/core/patch-lint-css.js +24 -14
- package/dist/src/core/patch-lint-file-size.d.ts +12 -5
- package/dist/src/core/patch-lint-file-size.js +22 -11
- package/dist/src/core/patch-lint-forward-registration-extended.d.ts +23 -0
- package/dist/src/core/patch-lint-forward-registration-extended.js +36 -11
- package/dist/src/core/patch-lint-forward-registration.js +10 -0
- package/dist/src/core/patch-lint-ignore.d.ts +196 -0
- package/dist/src/core/patch-lint-ignore.js +429 -0
- package/dist/src/core/patch-lint-ownership.d.ts +10 -0
- package/dist/src/core/patch-lint-ownership.js +26 -0
- package/dist/src/core/patch-lint-size-thresholds.d.ts +52 -0
- package/dist/src/core/patch-lint-size-thresholds.js +110 -0
- package/dist/src/core/patch-lint-size.d.ts +117 -0
- package/dist/src/core/patch-lint-size.js +331 -0
- package/dist/src/core/patch-lint-staged-registration.d.ts +18 -1
- package/dist/src/core/patch-lint-staged-registration.js +20 -0
- package/dist/src/core/patch-lint-token-scale.d.ts +57 -0
- package/dist/src/core/patch-lint-token-scale.js +286 -0
- package/dist/src/core/patch-lint.d.ts +11 -83
- package/dist/src/core/patch-lint.js +66 -288
- package/dist/src/core/patch-lock.js +7 -6
- package/dist/src/core/patch-manifest-consistency.js +7 -6
- package/dist/src/core/patch-manifest-io.d.ts +6 -3
- package/dist/src/core/patch-manifest-io.js +27 -9
- package/dist/src/core/patch-manifest-key-order.d.ts +24 -0
- package/dist/src/core/patch-manifest-key-order.js +61 -0
- package/dist/src/core/patch-manifest-stage.d.ts +46 -0
- package/dist/src/core/patch-manifest-stage.js +96 -0
- package/dist/src/core/patch-manifest-validate.js +61 -3
- package/dist/src/core/patch-order-legal.d.ts +26 -0
- package/dist/src/core/patch-order-legal.js +88 -0
- package/dist/src/core/patch-policy-content.d.ts +40 -0
- package/dist/src/core/patch-policy-content.js +216 -0
- package/dist/src/core/patch-policy.d.ts +22 -5
- package/dist/src/core/patch-policy.js +26 -12
- package/dist/src/core/patch-retired.d.ts +57 -0
- package/dist/src/core/patch-retired.js +92 -0
- package/dist/src/core/patch-subset.d.ts +16 -0
- package/dist/src/core/patch-subset.js +66 -0
- package/dist/src/core/preflight-wait.d.ts +20 -0
- package/dist/src/core/preflight-wait.js +38 -0
- package/dist/src/core/rebase-from-version.d.ts +46 -0
- package/dist/src/core/rebase-from-version.js +71 -0
- package/dist/src/core/run-log.d.ts +26 -0
- package/dist/src/core/run-log.js +106 -4
- package/dist/src/core/run-profile.d.ts +7 -0
- package/dist/src/core/run-profile.js +7 -1
- package/dist/src/core/smoke-patterns.d.ts +5 -0
- package/dist/src/core/smoke-patterns.js +13 -0
- package/dist/src/core/test-harness-crash.d.ts +19 -1
- package/dist/src/core/test-harness-crash.js +8 -9
- package/dist/src/core/test-harness-signature.d.ts +33 -0
- package/dist/src/core/test-harness-signature.js +23 -1
- package/dist/src/core/test-raw-log.d.ts +66 -0
- package/dist/src/core/test-raw-log.js +122 -0
- package/dist/src/core/test-run-completeness.d.ts +90 -0
- package/dist/src/core/test-run-completeness.js +184 -0
- package/dist/src/core/test-stale-check.d.ts +27 -1
- package/dist/src/core/test-stale-check.js +39 -49
- package/dist/src/core/test-stale-exempt.d.ts +40 -0
- package/dist/src/core/test-stale-exempt.js +70 -0
- package/dist/src/core/test-xpcshell-retry.d.ts +2 -0
- package/dist/src/core/test-xpcshell-retry.js +2 -1
- package/dist/src/core/token-lint.d.ts +53 -0
- package/dist/src/core/token-lint.js +131 -0
- package/dist/src/core/token-sibling-sheets.d.ts +46 -0
- package/dist/src/core/token-sibling-sheets.js +119 -0
- package/dist/src/core/tree-guard.js +5 -0
- package/dist/src/core/typecheck-shim.d.ts +6 -2
- package/dist/src/core/typecheck-shim.js +205 -4
- package/dist/src/core/typecheck.js +3 -1
- package/dist/src/index.d.ts +14 -3
- package/dist/src/index.js +6 -1
- package/dist/src/types/commands/index.d.ts +2 -2
- package/dist/src/types/commands/options.d.ts +212 -24
- package/dist/src/types/commands/patches.d.ts +118 -1
- package/dist/src/types/config.d.ts +264 -0
- package/dist/src/types/config.js +9 -0
- package/dist/src/types/furnace.d.ts +63 -1
- package/dist/src/types/furnace.js +7 -0
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/utils/options.d.ts +36 -7
- package/dist/src/utils/options.js +94 -8
- package/dist/src/utils/path-glob.d.ts +15 -0
- package/dist/src/utils/path-glob.js +47 -0
- package/dist/src/utils/regex.d.ts +15 -3
- package/dist/src/utils/regex.js +37 -16
- package/docs/configuration.md +369 -11
- package/docs/exit-codes.md +18 -1
- package/docs/furnace.md +139 -3
- package/docs/lifecycle-invariants.md +65 -30
- package/docs/machine-output.md +120 -12
- package/docs/patch-workflow.md +458 -22
- package/docs/run-logs.md +20 -1
- package/docs/testing.md +346 -21
- package/package.json +3 -3
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { reportOrphanedHarnessProcesses } from '../core/harness-orphans.js';
|
|
4
|
-
import {
|
|
4
|
+
import { deferBusyPortToHarness, resolveAutoPorts } from '../core/harness-port-fallback.js';
|
|
5
|
+
import { assertMarionettePortAvailable, DEFAULT_MARIONETTE_PORT, ensureLaunchableBrowserNotRunning, ensureMarionettePortAvailable, extractForwardedMarionettePort, forwardedMachArgsIncludeMarionetteClient, shouldAutoForwardMarionettePortToMach, } from '../core/marionette-port.js';
|
|
5
6
|
import { formatMarionettePreflightLine, reportMarionettePreflight, runMarionettePreflight, } from '../core/marionette-preflight.js';
|
|
6
|
-
import { ensureMochitestServerPortAvailable } from '../core/mochitest-server-port.js';
|
|
7
|
+
import { DEFAULT_MOCHITEST_SERVER_PORT, ensureMochitestServerPortAvailable, } from '../core/mochitest-server-port.js';
|
|
7
8
|
import { waitForPreflight } from '../core/preflight-wait.js';
|
|
8
9
|
import { GeneralError } from '../errors/base.js';
|
|
9
10
|
import { info, outro, verbose } from '../utils/logger.js';
|
|
@@ -153,7 +154,15 @@ export async function ensureTestBrowserEnvironment(engineDir, launchablePath, xp
|
|
|
153
154
|
// browser connect to a server that cannot serve this run's manifest,
|
|
154
155
|
// which surfaces as a 370s "Ran 0 checks" stall naming nothing. xpcshell
|
|
155
156
|
// does not use the httpd, so an xpcshell-only run is never blocked by it.
|
|
156
|
-
|
|
157
|
+
// Auto ports (opt-in) step aside for a peer's listener and let the
|
|
158
|
+
// harness's own busy-port fallback move this run; see
|
|
159
|
+
// src/core/harness-port-fallback.ts.
|
|
160
|
+
const autoPorts = xpcshellOnly
|
|
161
|
+
? undefined
|
|
162
|
+
: await resolveAutoPorts(engineDir, options, projectConfig);
|
|
163
|
+
if (!xpcshellOnly &&
|
|
164
|
+
!(autoPorts?.serverPorts === true &&
|
|
165
|
+
(await deferBusyPortToHarness(DEFAULT_MOCHITEST_SERVER_PORT, 'Mochitest server', engineDir)))) {
|
|
157
166
|
await waitForPreflight(() => ensureMochitestServerPortAvailable(undefined, {
|
|
158
167
|
engineDir,
|
|
159
168
|
killStaleServer: options.killStaleMarionette === true,
|
|
@@ -173,6 +182,13 @@ export async function ensureTestBrowserEnvironment(engineDir, launchablePath, xp
|
|
|
173
182
|
? extractForwardedMarionettePort(options.machArg)
|
|
174
183
|
: undefined;
|
|
175
184
|
const effectivePort = options.marionettePort ?? forwardedPort;
|
|
185
|
+
// Never under --doctor: its handshake launches a browser on this port
|
|
186
|
+
// itself and would talk to the peer's browser instead.
|
|
187
|
+
if (autoPorts?.marionettePort === true &&
|
|
188
|
+
options.doctor !== true &&
|
|
189
|
+
(await deferBusyPortToHarness(effectivePort ?? DEFAULT_MARIONETTE_PORT, 'Marionette', engineDir))) {
|
|
190
|
+
return { forwardedPort, effectivePort };
|
|
191
|
+
}
|
|
176
192
|
await waitForPreflight(() => ensureTestMarionettePortAvailable(effectivePort, projectConfig.binaryName, options, {
|
|
177
193
|
xpcshellOnly,
|
|
178
194
|
doctor: options.doctor === true,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a pre-test build did with Furnace components. `prepareBuildEnvironment`
|
|
3
|
+
* runs `furnace apply` over the whole `components/` tree, so a
|
|
4
|
+
* `fireforge test --build` meant to capture the committed behaviour could
|
|
5
|
+
* silently ship component edits nobody had deployed yet (another session's
|
|
6
|
+
* included), and nothing in the test output said so. The applied names now
|
|
7
|
+
* ride the verdict line, and `--no-furnace-apply` packages `engine/` as it
|
|
8
|
+
* stands, naming the undeployed edits it left out.
|
|
9
|
+
*/
|
|
10
|
+
import type { BuildPreparation } from '../core/build-prepare.js';
|
|
11
|
+
/**
|
|
12
|
+
* Reports the pre-test build's Furnace step: the components it deployed
|
|
13
|
+
* (notice plus `furnace-applied=<names>` on the verdict line), or, when the
|
|
14
|
+
* apply was skipped, the components whose undeployed edits this build
|
|
15
|
+
* leaves out (`furnace-apply=skipped`).
|
|
16
|
+
*
|
|
17
|
+
* @param projectRoot - Root directory of the project
|
|
18
|
+
* @param preparation - Result of `prepareBuildEnvironment`
|
|
19
|
+
* @param skipped - True under `--no-furnace-apply` / `--furnace-apply none`
|
|
20
|
+
* @param unreached - Components `--furnace-apply reached` left out
|
|
21
|
+
*/
|
|
22
|
+
export declare function reportPreTestFurnace(projectRoot: string, preparation: BuildPreparation, skipped: boolean, unreached?: readonly string[]): Promise<void>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* What a pre-test build did with Furnace components. `prepareBuildEnvironment`
|
|
4
|
+
* runs `furnace apply` over the whole `components/` tree, so a
|
|
5
|
+
* `fireforge test --build` meant to capture the committed behaviour could
|
|
6
|
+
* silently ship component edits nobody had deployed yet (another session's
|
|
7
|
+
* included), and nothing in the test output said so. The applied names now
|
|
8
|
+
* ride the verdict line, and `--no-furnace-apply` packages `engine/` as it
|
|
9
|
+
* stands, naming the undeployed edits it left out.
|
|
10
|
+
*/
|
|
11
|
+
import { listStaleFurnaceComponents } from '../core/furnace-staleness.js';
|
|
12
|
+
import { toError } from '../utils/errors.js';
|
|
13
|
+
import { notice, verbose } from '../utils/logger.js';
|
|
14
|
+
import { setVerdictRunAttribute } from './test-verdict.js';
|
|
15
|
+
/**
|
|
16
|
+
* Reports the pre-test build's Furnace step: the components it deployed
|
|
17
|
+
* (notice plus `furnace-applied=<names>` on the verdict line), or, when the
|
|
18
|
+
* apply was skipped, the components whose undeployed edits this build
|
|
19
|
+
* leaves out (`furnace-apply=skipped`).
|
|
20
|
+
*
|
|
21
|
+
* @param projectRoot - Root directory of the project
|
|
22
|
+
* @param preparation - Result of `prepareBuildEnvironment`
|
|
23
|
+
* @param skipped - True under `--no-furnace-apply` / `--furnace-apply none`
|
|
24
|
+
* @param unreached - Components `--furnace-apply reached` left out
|
|
25
|
+
*/
|
|
26
|
+
export async function reportPreTestFurnace(projectRoot, preparation, skipped, unreached) {
|
|
27
|
+
if (skipped) {
|
|
28
|
+
setVerdictRunAttribute('furnace-apply', 'skipped');
|
|
29
|
+
let pending = [];
|
|
30
|
+
try {
|
|
31
|
+
pending = await listStaleFurnaceComponents(projectRoot);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
verbose(`Furnace pending-edit probe failed: ${toError(error).message}`);
|
|
35
|
+
}
|
|
36
|
+
notice(pending.length > 0
|
|
37
|
+
? `Furnace apply skipped (--no-furnace-apply): engine/ is packaged as it stands. ${pending.length} component(s) with undeployed components/ edits are NOT in this build: ${pending.join(', ')}.`
|
|
38
|
+
: 'Furnace apply skipped (--no-furnace-apply): engine/ is packaged as it stands.');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (unreached !== undefined && unreached.length > 0) {
|
|
42
|
+
setVerdictRunAttribute('furnace-skipped', unreached.join(','));
|
|
43
|
+
notice(`--furnace-apply reached: ${unreached.length} component(s) with undeployed components/ ` +
|
|
44
|
+
`edits that no requested test reaches are NOT in this build: ${unreached.join(', ')}.`);
|
|
45
|
+
}
|
|
46
|
+
const applied = preparation.furnaceAppliedNames ?? [];
|
|
47
|
+
if (applied.length === 0)
|
|
48
|
+
return;
|
|
49
|
+
setVerdictRunAttribute('furnace-applied', applied.join(','));
|
|
50
|
+
notice(`This pre-test build deployed ${applied.length} Furnace component(s) whose components/ source ` +
|
|
51
|
+
`changed since their last deploy: ${applied.join(', ')}. Those edits (another session's ` +
|
|
52
|
+
'undeployed edits included) are in the runtime under test. Pass --no-furnace-apply to ' +
|
|
53
|
+
'package engine/ as it stands, for example for a before-capture.');
|
|
54
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { readBuildBaseline, writeBuildBaseline } from '../core/build-baseline.js';
|
|
8
8
|
import { prepareBuildEnvironment } from '../core/build-prepare.js';
|
|
9
|
-
import {
|
|
9
|
+
import { applyMozconfigAnchor, formatCoverageRetentionNotice, formatExtendCoverageRefusal, planCoverageRetention, recordScopedCoverage, } from '../core/coverage-extend.js';
|
|
10
10
|
import { hasBuildArtifacts } from '../core/mach.js';
|
|
11
11
|
import { runProtectedMachBuild, withBuildLock } from '../core/mach.js';
|
|
12
12
|
import { assertHealthyPartialConfig, pruneDanglingTestLinks } from '../core/objdir-maintenance.js';
|
|
@@ -15,7 +15,8 @@ import { GeneralError, InvalidArgumentError } from '../errors/base.js';
|
|
|
15
15
|
import { BuildError } from '../errors/build.js';
|
|
16
16
|
import { toError } from '../utils/errors.js';
|
|
17
17
|
import { info, notice, spinner, verbose } from '../utils/logger.js';
|
|
18
|
-
import {
|
|
18
|
+
import { reportPreTestFurnace } from './test-build-furnace.js';
|
|
19
|
+
import { partitionReachedComponents, resolveFurnaceApplyMode } from './test-furnace-reach.js';
|
|
19
20
|
import { reportBuildOnlyCompletion } from './test-modes.js';
|
|
20
21
|
import { enforceStaleBuildGate, enforceStaticComponentsGate } from './test-stale-gate.js';
|
|
21
22
|
import { emitPassVerdict } from './test-verdict.js';
|
|
@@ -26,7 +27,7 @@ import { emitPassVerdict } from './test-verdict.js';
|
|
|
26
27
|
* @param options - See {@link RunPreTestBuildOptions}
|
|
27
28
|
*/
|
|
28
29
|
async function runPreTestBuild(options) {
|
|
29
|
-
const { projectRoot, paths, projectConfig, harnessRetries, testPackagingCoverage, refuseUnexportedDrift, extend, } = options;
|
|
30
|
+
const { projectRoot, paths, projectConfig, harnessRetries, testPackagingCoverage, refuseUnexportedDrift, extend, furnace, } = options;
|
|
30
31
|
await withBuildLock(projectRoot, async () => {
|
|
31
32
|
// Pass the previous baseline exactly like `fireforge build` does, so
|
|
32
33
|
// auto-configure runs under the same conditions on both paths. The
|
|
@@ -41,32 +42,29 @@ async function runPreTestBuild(options) {
|
|
|
41
42
|
if (pruned > 0)
|
|
42
43
|
notice(`Pruned ${pruned} dangling _tests symlink(s).`);
|
|
43
44
|
}
|
|
44
|
-
|
|
45
|
+
// The named-file checkJs pass runs before the engine lock is even
|
|
46
|
+
// requested (test-prelock.ts), so a type error refuses without a wait.
|
|
45
47
|
const preparation = await prepareBuildEnvironment(projectRoot, paths, projectConfig, {
|
|
46
48
|
previousBaseline,
|
|
47
49
|
refuseUnexportedDrift,
|
|
50
|
+
skipFurnaceApply: furnace.skip,
|
|
51
|
+
furnaceComponents: furnace.only,
|
|
48
52
|
});
|
|
49
|
-
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
throw new GeneralError(formatExtendCoverageRefusal(!anchor.ok ? anchor : mozconfigAnchor));
|
|
61
|
-
}
|
|
62
|
-
if (!retainCoverage) {
|
|
63
|
-
const old = previousBaseline.testPackagingCoverage;
|
|
64
|
-
const dropped = old === undefined || old === 'full'
|
|
65
|
-
? ['full coverage outside this scope']
|
|
66
|
-
: old.filter((file) => !testPackagingCoverage.includes(file));
|
|
67
|
-
if (dropped.length)
|
|
68
|
-
notice(`Scoped build drops previous packaging coverage: ${dropped.join(', ')}. Earlier staging inputs changed or have no complete fingerprint anchor; rebuild those paths together to retain them.`);
|
|
53
|
+
await reportPreTestFurnace(projectRoot, preparation, furnace.skip, furnace.unreached);
|
|
54
|
+
// Which earlier claims this scoped build keeps, per staged path. The
|
|
55
|
+
// mozconfig half can only be checked once prepareBuildEnvironment has
|
|
56
|
+
// regenerated engine/mozconfig (the file this build will configure
|
|
57
|
+
// with), and must still be checked before mach, so a refusal costs no
|
|
58
|
+
// build.
|
|
59
|
+
let plan;
|
|
60
|
+
if (testPackagingCoverage !== 'full') {
|
|
61
|
+
plan = await applyMozconfigAnchor(paths.engine, await planCoverageRetention(paths.engine, previousBaseline, testPackagingCoverage));
|
|
62
|
+
if (extend !== undefined && plan.dropped.length > 0) {
|
|
63
|
+
throw new GeneralError(formatExtendCoverageRefusal(plan.dropped));
|
|
69
64
|
}
|
|
65
|
+
const retentionNotice = formatCoverageRetentionNotice(plan);
|
|
66
|
+
if (retentionNotice !== undefined)
|
|
67
|
+
notice(retentionNotice);
|
|
70
68
|
}
|
|
71
69
|
const buildKind = preparation.fullBuildRequired ? 'full' : 'faster';
|
|
72
70
|
if (preparation.fullBuildRequired) {
|
|
@@ -103,24 +101,24 @@ async function runPreTestBuild(options) {
|
|
|
103
101
|
// carries the static-components anchor forward, since `mach build
|
|
104
102
|
// faster` does not rebake components.conf into the compiled table.
|
|
105
103
|
try {
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
104
|
+
// A scoped claim records this build's paths plus every earlier path
|
|
105
|
+
// the plan kept, each with its own staging-input anchor. When it
|
|
106
|
+
// kept anything the static-components anchor is carried forward
|
|
107
|
+
// explicitly: a kept full claim is still a `mach build faster` that
|
|
110
108
|
// did not rebake the compiled table.
|
|
111
|
-
const
|
|
112
|
-
?
|
|
113
|
-
: testPackagingCoverage;
|
|
109
|
+
const recorded = plan === undefined || testPackagingCoverage === 'full'
|
|
110
|
+
? { testPackagingCoverage }
|
|
111
|
+
: await recordScopedCoverage(paths.engine, plan, testPackagingCoverage);
|
|
114
112
|
await writeBuildBaseline({
|
|
115
113
|
projectRoot,
|
|
116
114
|
engineDir: paths.engine,
|
|
117
115
|
binaryName: projectConfig.binaryName,
|
|
118
|
-
|
|
116
|
+
...recorded,
|
|
119
117
|
previousBaseline,
|
|
120
118
|
recordedBy: describeBuildInvocation(extend !== undefined, testPackagingCoverage),
|
|
121
119
|
staticComponentsHandling: preparation.fullBuildRequired
|
|
122
120
|
? 'refresh'
|
|
123
|
-
:
|
|
121
|
+
: plan !== undefined && (plan.keepFull || Object.keys(plan.retained).length > 0)
|
|
124
122
|
? 'carry-forward'
|
|
125
123
|
: 'auto',
|
|
126
124
|
buildKind,
|
|
@@ -138,15 +136,15 @@ async function runPreTestBuild(options) {
|
|
|
138
136
|
});
|
|
139
137
|
}
|
|
140
138
|
/**
|
|
141
|
-
* `--extend-coverage` only means something for a scoped build: it
|
|
142
|
-
*
|
|
139
|
+
* `--extend-coverage` only means something for a scoped build: it refuses
|
|
140
|
+
* a build that would drop an earlier claim. Without `--build`/
|
|
143
141
|
* `--build-only` there is no build to extend from, and a path-less build
|
|
144
142
|
* records `'full'`, which already covers everything. Refusing both is more
|
|
145
143
|
* honest than silently ignoring the flag.
|
|
146
144
|
*/
|
|
147
145
|
function assertExtendCoverageUsage(options, normalizedPaths) {
|
|
148
146
|
if (options.build !== true && options.buildOnly !== true) {
|
|
149
|
-
throw new InvalidArgumentError('--extend-coverage requires --build or --build-only: it
|
|
147
|
+
throw new InvalidArgumentError('--extend-coverage requires --build or --build-only: it guards the coverage claim a build records.', '--extend-coverage');
|
|
150
148
|
}
|
|
151
149
|
if (normalizedPaths.length === 0) {
|
|
152
150
|
throw new InvalidArgumentError('--extend-coverage requires explicit test paths: a path-less build records full coverage, which already covers every path.', '--extend-coverage');
|
|
@@ -165,6 +163,29 @@ function assertRefuseUnexportedDriftUsage(options) {
|
|
|
165
163
|
return;
|
|
166
164
|
throw new InvalidArgumentError('--refuse-unexported-drift requires --build or --build-only: it guards the pre-test build, and this run dispatches none.', '--refuse-unexported-drift');
|
|
167
165
|
}
|
|
166
|
+
/** Refuses `--no-furnace-apply`/`--furnace-apply` on a run that dispatches no build, for the same reason. */
|
|
167
|
+
function assertNoFurnaceApplyUsage(options) {
|
|
168
|
+
if (options.furnaceApply === undefined || options.furnaceApply === true)
|
|
169
|
+
return;
|
|
170
|
+
if (options.build === true || options.buildOnly === true)
|
|
171
|
+
return;
|
|
172
|
+
const flag = options.furnaceApply === false ? '--no-furnace-apply' : '--furnace-apply';
|
|
173
|
+
throw new InvalidArgumentError(`${flag} requires --build or --build-only: it shapes the pre-test build's Furnace apply, and this run dispatches no build.`, flag);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* The Furnace part of a pre-test build: skip the apply, apply everything,
|
|
177
|
+
* or apply only the components the request reaches (and name the rest).
|
|
178
|
+
*/
|
|
179
|
+
async function planPreTestFurnace(projectRoot, paths, projectConfig, options, normalizedPaths) {
|
|
180
|
+
const mode = resolveFurnaceApplyMode(options, projectConfig);
|
|
181
|
+
if (mode === 'none')
|
|
182
|
+
return { skip: true };
|
|
183
|
+
// A path-less build packages the whole suite, which reaches everything.
|
|
184
|
+
if (mode === 'all' || normalizedPaths.length === 0)
|
|
185
|
+
return { skip: false };
|
|
186
|
+
const { reached, skipped } = await partitionReachedComponents(projectRoot, paths.engine, normalizedPaths);
|
|
187
|
+
return { skip: false, only: reached, unreached: skipped };
|
|
188
|
+
}
|
|
168
189
|
/** Renders the `recordedBy` invocation string surfaced by `status --test-coverage`. */
|
|
169
190
|
function describeBuildInvocation(extending, coverage) {
|
|
170
191
|
const extendFlag = extending ? ' --extend-coverage' : '';
|
|
@@ -186,6 +207,7 @@ export async function runTestBuildPhase(projectRoot, paths, projectConfig, harne
|
|
|
186
207
|
assertExtendCoverageUsage(options, normalizedPaths);
|
|
187
208
|
}
|
|
188
209
|
assertRefuseUnexportedDriftUsage(options);
|
|
210
|
+
assertNoFurnaceApplyUsage(options);
|
|
189
211
|
if (options.build || options.buildOnly) {
|
|
190
212
|
// A path-less `test --build` runs (and packages for) the full suite.
|
|
191
213
|
// A scoped invocation only vouches for the requested paths. A scoped
|
|
@@ -197,11 +219,11 @@ export async function runTestBuildPhase(projectRoot, paths, projectConfig, harne
|
|
|
197
219
|
const previousBaseline = await readBuildBaseline(projectRoot);
|
|
198
220
|
await enforceStaticComponentsGate(paths.engine, previousBaseline, options);
|
|
199
221
|
if (extending) {
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
const
|
|
203
|
-
if (
|
|
204
|
-
throw new GeneralError(formatExtendCoverageRefusal(
|
|
222
|
+
// Staging-input half of the plan, before the build lock: a refusal
|
|
223
|
+
// here costs neither build time nor the lock.
|
|
224
|
+
const plan = await planCoverageRetention(paths.engine, previousBaseline, normalizedPaths);
|
|
225
|
+
if (plan.dropped.length > 0) {
|
|
226
|
+
throw new GeneralError(formatExtendCoverageRefusal(plan.dropped));
|
|
205
227
|
}
|
|
206
228
|
}
|
|
207
229
|
}
|
|
@@ -213,6 +235,7 @@ export async function runTestBuildPhase(projectRoot, paths, projectConfig, harne
|
|
|
213
235
|
testPackagingCoverage: coverage,
|
|
214
236
|
refuseUnexportedDrift: options.refuseUnexportedDrift === true,
|
|
215
237
|
extend: extending ? { requestedPaths: normalizedPaths } : undefined,
|
|
238
|
+
furnace: await planPreTestFurnace(projectRoot, paths, projectConfig, options, normalizedPaths),
|
|
216
239
|
});
|
|
217
240
|
info('');
|
|
218
241
|
if (options.buildOnly) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
import { findUnownedSupportHeads, formatUnownedSupportHeadNotice, } from '../core/checkjs-support-heads.js';
|
|
3
|
+
import { buildPatchQueueContext } from '../core/patch-lint.js';
|
|
2
4
|
import { runCheckJsTestFilesGrouped } from '../core/patch-lint-checkjs.js';
|
|
3
|
-
import { isTestScriptFile } from '../core/patch-lint-ownership.js';
|
|
5
|
+
import { isTestScriptFile, resolvableModulesForTests } from '../core/patch-lint-ownership.js';
|
|
4
6
|
import { loadPatchesManifest } from '../core/patch-manifest.js';
|
|
5
7
|
import { analyzeTestPathScopes } from '../core/test-path-scope.js';
|
|
6
8
|
import { GeneralError } from '../errors/base.js';
|
|
@@ -17,7 +19,13 @@ export async function checkRequestedTestTypes(paths, config, requested) {
|
|
|
17
19
|
const selected = new Set(scopes.flatMap((scope) => scope.dispatchPaths.filter(isTestScriptFile)));
|
|
18
20
|
for (const file of selected)
|
|
19
21
|
owned.add(file);
|
|
20
|
-
|
|
22
|
+
// A support head a named test brings into scope joins the pass even
|
|
23
|
+
// before any patch owns it, or every name it declares reads as undefined.
|
|
24
|
+
for (const head of await findUnownedSupportHeads(paths.engine, selected, owned)) {
|
|
25
|
+
owned.add(head.file);
|
|
26
|
+
notice(`Pre-build checkJs: ${formatUnownedSupportHeadNotice(head)}`);
|
|
27
|
+
}
|
|
28
|
+
const result = await runCheckJsTestFilesGrouped(paths.engine, owned, config.patchLint, paths.root, selected, resolvableModulesForTests(await buildPatchQueueContext(paths.patches)));
|
|
21
29
|
const issues = [...result.global, ...[...result.byFile.values()].flat()];
|
|
22
30
|
for (const issue of issues)
|
|
23
31
|
notice(`Pre-build checkJs: ${issue.file}: ${issue.message}`);
|
|
@@ -11,6 +11,7 @@ import { getActiveRunLogPath } from '../core/run-log.js';
|
|
|
11
11
|
import { describeChangedPrefNoise } from '../core/test-changed-prefs.js';
|
|
12
12
|
import { buildGreenSummaryRejectedMessage, buildHarnessCrashMessage, buildNoTestsRanMessage, buildSilentSegfaultMessage, headedNoOutputTimeoutHint, isSilentSegfault, } from '../core/test-harness-crash.js';
|
|
13
13
|
import { buildHarnessEarlyExitMessage, classifyHarnessEarlyExit, completePostRebuildFailureContext, prependPostRebuildFailureContext, } from '../core/test-harness-output.js';
|
|
14
|
+
import { buildIncompleteRunMessage } from '../core/test-run-completeness.js';
|
|
14
15
|
import { GeneralError } from '../errors/base.js';
|
|
15
16
|
import { TestFailureError } from '../errors/build.js';
|
|
16
17
|
import { toError } from '../utils/errors.js';
|
|
@@ -249,6 +250,9 @@ function applySingleRunOutcome(outcome, normalizedPaths, binaryName, postRebuild
|
|
|
249
250
|
}
|
|
250
251
|
return;
|
|
251
252
|
}
|
|
253
|
+
if (outcome.verdict.incomplete !== undefined) {
|
|
254
|
+
throw new TestFailureError(buildIncompleteRunMessage(outcome.verdict.incomplete), 'mach test', formatFailureBlocks(outcome.verdict.realFailureBlocks), getActiveRunLogPath());
|
|
255
|
+
}
|
|
252
256
|
if (outcome.verdict.kind === 'no-tests' && outcome.result.exitCode === 0) {
|
|
253
257
|
// The silent false green: exit 0 plus a "Passed: 0"-style summary with
|
|
254
258
|
// zero TEST-START lines must fail rather than pass.
|
|
@@ -282,6 +286,9 @@ export function diagnoseShardOutcome(outcome, path, binaryName, postRebuildConte
|
|
|
282
286
|
if (outcome.verdict.kind === 'no-tests' && outcome.result.exitCode === 0) {
|
|
283
287
|
return buildNoTestsRanMessage(0, [path]);
|
|
284
288
|
}
|
|
289
|
+
if (outcome.verdict.incomplete !== undefined) {
|
|
290
|
+
return buildIncompleteRunMessage(outcome.verdict.incomplete);
|
|
291
|
+
}
|
|
285
292
|
if (outcome.verdict.greenSummaryRejected) {
|
|
286
293
|
return buildGreenSummaryRejectedMessage(outcome.verdict.greenSummaryRejected, outcome.result.exitCode);
|
|
287
294
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dispatch half of one `fireforge test` run, split out of `test.ts`:
|
|
3
|
+
* the engine-generation guard around the harness, and the choice between
|
|
4
|
+
* one combined invocation, per-argument shards, `--shard-size` chunks and
|
|
5
|
+
* `--jobs` parallel harness instances.
|
|
6
|
+
*/
|
|
7
|
+
import type { PostRebuildFailureContext } from '../core/test-harness-output.js';
|
|
8
|
+
import type { TestOptions } from '../types/commands/index.js';
|
|
9
|
+
import type { FireForgeConfig } from '../types/config.js';
|
|
10
|
+
import { type TestRunContext } from './test-run.js';
|
|
11
|
+
import { type ShardGroup } from './test-shards.js';
|
|
12
|
+
/** Everything the dispatch half needs from the run body. */
|
|
13
|
+
export interface TestDispatchInputs {
|
|
14
|
+
engineDir: string;
|
|
15
|
+
runCtx: TestRunContext;
|
|
16
|
+
dispatchGroups: ShardGroup[];
|
|
17
|
+
options: TestOptions;
|
|
18
|
+
projectConfig: FireForgeConfig;
|
|
19
|
+
canaryPath: string | undefined;
|
|
20
|
+
shuffleSeed: number | undefined;
|
|
21
|
+
normalizedPaths: string[];
|
|
22
|
+
xpcshellOnly: boolean;
|
|
23
|
+
postRebuildContext: PostRebuildFailureContext | undefined;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Refuses `--jobs`/`--shard-size` shapes that cannot work, before any lock
|
|
27
|
+
* or build: both need explicit paths and a real dispatch, and parallel
|
|
28
|
+
* instances cannot share one operator-pinned Marionette port.
|
|
29
|
+
*
|
|
30
|
+
* @param testPaths - Requested paths
|
|
31
|
+
* @param options - Parsed options
|
|
32
|
+
*/
|
|
33
|
+
export declare function assertParallelTestUsage(testPaths: readonly string[], options: TestOptions): void;
|
|
34
|
+
/**
|
|
35
|
+
* Runs the planned dispatch and finalizes its verdict.
|
|
36
|
+
*
|
|
37
|
+
* @param inputs - See {@link TestDispatchInputs}
|
|
38
|
+
*/
|
|
39
|
+
export declare function dispatchTests(inputs: TestDispatchInputs): Promise<void>;
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* The dispatch half of one `fireforge test` run, split out of `test.ts`:
|
|
4
|
+
* the engine-generation guard around the harness, and the choice between
|
|
5
|
+
* one combined invocation, per-argument shards, `--shard-size` chunks and
|
|
6
|
+
* `--jobs` parallel harness instances.
|
|
7
|
+
*/
|
|
8
|
+
import { createServer } from 'node:net';
|
|
9
|
+
import { assertEngineGenerationUnchanged, snapshotEngineGeneration, } from '../core/engine-session-lock.js';
|
|
10
|
+
import { autoPortsRequested } from '../core/harness-port-fallback.js';
|
|
11
|
+
import { extractForwardedMarionettePort } from '../core/marionette-port.js';
|
|
12
|
+
import { GeneralError, InvalidArgumentError } from '../errors/base.js';
|
|
13
|
+
import { BuildError } from '../errors/build.js';
|
|
14
|
+
import { notice } from '../utils/logger.js';
|
|
15
|
+
import { diagnoseShardOutcome, finalizeSingleRunOutcome } from './test-diagnose.js';
|
|
16
|
+
import { shuffleTestGroups } from './test-harness-env.js';
|
|
17
|
+
import { reportCanaryOutcome } from './test-modes.js';
|
|
18
|
+
import { perfGenerationTolerance, runGenerationTolerance } from './test-perf-scope.js';
|
|
19
|
+
import { runTestsWithRetries } from './test-run.js';
|
|
20
|
+
import { chunkShardGroups, finalizeShardedOutcome, runShardedTests, splitContiguous, } from './test-shards.js';
|
|
21
|
+
import { emitFailVerdict, setVerdictRunAttribute } from './test-verdict.js';
|
|
22
|
+
/**
|
|
23
|
+
* Refuses `--jobs`/`--shard-size` shapes that cannot work, before any lock
|
|
24
|
+
* or build: both need explicit paths and a real dispatch, and parallel
|
|
25
|
+
* instances cannot share one operator-pinned Marionette port.
|
|
26
|
+
*
|
|
27
|
+
* @param testPaths - Requested paths
|
|
28
|
+
* @param options - Parsed options
|
|
29
|
+
*/
|
|
30
|
+
export function assertParallelTestUsage(testPaths, options) {
|
|
31
|
+
const parallel = (options.jobs ?? 1) > 1;
|
|
32
|
+
const flags = [
|
|
33
|
+
...(parallel ? ['--jobs'] : []),
|
|
34
|
+
...(options.shardSize !== undefined ? ['--shard-size'] : []),
|
|
35
|
+
];
|
|
36
|
+
if (flags.length === 0)
|
|
37
|
+
return;
|
|
38
|
+
const flag = flags.join('/');
|
|
39
|
+
if (testPaths.length === 0 || options.canary !== undefined || options.auto === true) {
|
|
40
|
+
throw new InvalidArgumentError(`${flag} splits explicit test paths, so it needs paths and cannot be combined with --canary or --auto.`, flags[0]);
|
|
41
|
+
}
|
|
42
|
+
if (options.buildOnly === true) {
|
|
43
|
+
throw new InvalidArgumentError(`${flag} shapes a dispatch; --build-only runs none.`, flags[0]);
|
|
44
|
+
}
|
|
45
|
+
if (parallel &&
|
|
46
|
+
(options.marionettePort !== undefined ||
|
|
47
|
+
extractForwardedMarionettePort(options.machArg ?? []) !== undefined)) {
|
|
48
|
+
throw new InvalidArgumentError('--jobs gives every harness instance its own Marionette port, so it cannot be combined with a pinned --marionette-port (or a forwarded one).', '--jobs');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Verifies `engine/` did not change under the run in a way the run could
|
|
53
|
+
* see before any PASS verdict may print. On failure the run's verdict is
|
|
54
|
+
* `FAIL reason=inconclusive`, because the harness result exists but cannot
|
|
55
|
+
* be trusted (`preflight` would misdescribe a run whose harness already
|
|
56
|
+
* executed). It is emitted here so the guard's throw can never leave a
|
|
57
|
+
* stale or missing verdict line behind.
|
|
58
|
+
*/
|
|
59
|
+
async function verifyEngineGenerationOrEmitInconclusive(engineDir, before, inputs) {
|
|
60
|
+
try {
|
|
61
|
+
await assertEngineGenerationUnchanged(engineDir, before, inputs.perfSamples === undefined
|
|
62
|
+
? runGenerationTolerance(engineDir, inputs.requested, (count) => {
|
|
63
|
+
setVerdictRunAttribute('drift-tolerated', String(count));
|
|
64
|
+
})
|
|
65
|
+
: perfGenerationTolerance(engineDir));
|
|
66
|
+
if (inputs.powerChanged === true)
|
|
67
|
+
throw new GeneralError('Perf power source changed during the run; repeat this sitting on a stable power source.');
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
emitFailVerdict('inconclusive');
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** A free TCP port on the loopback interface, for one harness instance. */
|
|
75
|
+
async function allocateLoopbackPort() {
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
const server = createServer();
|
|
78
|
+
server.once('error', reject);
|
|
79
|
+
server.listen(0, '127.0.0.1', () => {
|
|
80
|
+
const address = server.address();
|
|
81
|
+
const port = typeof address === 'object' && address !== null ? address.port : 0;
|
|
82
|
+
server.close(() => {
|
|
83
|
+
resolve(port);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The context for `--jobs` instance `index` (0-based): its own output
|
|
90
|
+
* prefix, and from the second instance on its own Marionette port, so
|
|
91
|
+
* browsers started at the same moment never race for 2828. The mochitest
|
|
92
|
+
* server ports are left to the engine harness's busy-port fallback, which
|
|
93
|
+
* `--jobs` requires (auto ports).
|
|
94
|
+
*/
|
|
95
|
+
async function instanceContext(base, index, xpcshellOnly) {
|
|
96
|
+
const ctx = { ...base, outputPrefix: `[job ${index + 1}] ` };
|
|
97
|
+
if (index === 0 || xpcshellOnly)
|
|
98
|
+
return ctx;
|
|
99
|
+
const port = await allocateLoopbackPort();
|
|
100
|
+
return {
|
|
101
|
+
...ctx,
|
|
102
|
+
baseExtraArgs: [
|
|
103
|
+
...base.baseExtraArgs,
|
|
104
|
+
`--setpref=marionette.port=${port}`,
|
|
105
|
+
`--marionette=127.0.0.1:${port}`,
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/** The shards to run, or undefined for one combined invocation. */
|
|
110
|
+
function planShards(inputs) {
|
|
111
|
+
const { dispatchGroups, options, canaryPath, shuffleSeed } = inputs;
|
|
112
|
+
if (canaryPath !== undefined)
|
|
113
|
+
return undefined;
|
|
114
|
+
const ordered = shuffleSeed === undefined ? dispatchGroups : shuffleTestGroups(dispatchGroups, shuffleSeed);
|
|
115
|
+
if (options.shardSize !== undefined)
|
|
116
|
+
return chunkShardGroups(ordered, options.shardSize);
|
|
117
|
+
const jobs = options.jobs ?? 1;
|
|
118
|
+
if (jobs > 1 && (options.shard === false || ordered.length === 1)) {
|
|
119
|
+
return splitContiguous(ordered.flatMap((group) => group.paths), jobs);
|
|
120
|
+
}
|
|
121
|
+
if (ordered.length > 1 && options.shard !== false)
|
|
122
|
+
return ordered;
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Runs the planned dispatch and finalizes its verdict.
|
|
127
|
+
*
|
|
128
|
+
* @param inputs - See {@link TestDispatchInputs}
|
|
129
|
+
*/
|
|
130
|
+
export async function dispatchTests(inputs) {
|
|
131
|
+
const { engineDir, runCtx, options, projectConfig, normalizedPaths } = inputs;
|
|
132
|
+
const jobs = options.jobs ?? 1;
|
|
133
|
+
if (jobs > 1 && !inputs.xpcshellOnly && !autoPortsRequested(options, projectConfig)) {
|
|
134
|
+
throw new InvalidArgumentError('--jobs runs several browsers at once, which needs auto ports (--auto-ports or ' +
|
|
135
|
+
'test.mochitestPorts: "auto") so the harness can move each instance to free server ports.', '--jobs');
|
|
136
|
+
}
|
|
137
|
+
const guard = {
|
|
138
|
+
perfSamples: options.perfSamples,
|
|
139
|
+
requested: normalizedPaths,
|
|
140
|
+
};
|
|
141
|
+
const shards = planShards(inputs);
|
|
142
|
+
if (shards !== undefined) {
|
|
143
|
+
notice(describeSharding(shards.length, options, jobs));
|
|
144
|
+
const base = jobs > 1 ? await instanceContext(runCtx, 0, inputs.xpcshellOnly) : runCtx;
|
|
145
|
+
if (jobs > 1)
|
|
146
|
+
setVerdictRunAttribute('jobs', String(jobs));
|
|
147
|
+
const generationBefore = await snapshotEngineGeneration(engineDir);
|
|
148
|
+
let summary;
|
|
149
|
+
try {
|
|
150
|
+
summary = await runShardedTests(base, shards, (outcome, label) => diagnoseShardOutcome(outcome, label, projectConfig.binaryName, inputs.postRebuildContext), {
|
|
151
|
+
jobs,
|
|
152
|
+
contextFor: (index) => instanceContext(runCtx, index, inputs.xpcshellOnly),
|
|
153
|
+
perFile: options.shardSize !== undefined,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
// Runs before the aggregate verdict below: a mutated engine/ emits
|
|
158
|
+
// `FAIL reason=inconclusive` and throws, so an invalidated run can
|
|
159
|
+
// never print `PASS shards=N/N` first. (A throw here masks an
|
|
160
|
+
// in-flight shard error, as the plain assert always did.)
|
|
161
|
+
await verifyEngineGenerationOrEmitInconclusive(engineDir, generationBefore, {
|
|
162
|
+
...guard,
|
|
163
|
+
...(base.powerChanged === true || runCtx.powerChanged === true
|
|
164
|
+
? { powerChanged: true }
|
|
165
|
+
: {}),
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
finalizeShardedOutcome(summary);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const combinedDispatchPaths = inputs.dispatchGroups.flatMap((group) => group.paths);
|
|
172
|
+
let outcome;
|
|
173
|
+
const generationBefore = await snapshotEngineGeneration(engineDir);
|
|
174
|
+
try {
|
|
175
|
+
outcome = await runTestsWithRetries(runCtx, combinedDispatchPaths);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
throw new BuildError('Test process failed to start', 'mach test', error instanceof Error ? error : undefined);
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
await verifyEngineGenerationOrEmitInconclusive(engineDir, generationBefore, {
|
|
182
|
+
...guard,
|
|
183
|
+
...(runCtx.powerChanged === true ? { powerChanged: true } : {}),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
if (inputs.canaryPath !== undefined) {
|
|
187
|
+
reportCanaryOutcome(outcome);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
finalizeSingleRunOutcome(outcome, normalizedPaths, projectConfig.binaryName, inputs.postRebuildContext, options.headless === true);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* The sharding notice. The default must not be silent: a cross-file
|
|
194
|
+
* pollution repro otherwise "passes" because the comparison run was
|
|
195
|
+
* sharded without saying so, misattributing a suite-context bug upstream.
|
|
196
|
+
*/
|
|
197
|
+
function describeSharding(count, options, jobs) {
|
|
198
|
+
const instances = jobs > 1 ? ` on ${jobs} harness instances at once (each with its own ports and profile)` : '';
|
|
199
|
+
if (options.shardSize !== undefined) {
|
|
200
|
+
return (`Sharding: running ${count} chunk(s) of up to ${options.shardSize} file(s)${instances}, ` +
|
|
201
|
+
'each chunk one mach invocation with a browser restart between files; results are judged ' +
|
|
202
|
+
'per file from the structured log.');
|
|
203
|
+
}
|
|
204
|
+
if (jobs > 1 && options.shard === false) {
|
|
205
|
+
return (`Sharding: --jobs splits the combined request into ${count} contiguous chunk(s) in order` +
|
|
206
|
+
`${instances}. Cross-chunk state is NOT exercised.`);
|
|
207
|
+
}
|
|
208
|
+
return (`Sharding: running ${count} test path arguments in isolated browser instances${instances} ` +
|
|
209
|
+
'(one mach invocation per argument; a directory argument keeps its files in one instance). ' +
|
|
210
|
+
'Cross-argument state is NOT exercised — pass --no-shard for a combined single-instance run.');
|
|
211
|
+
}
|