@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
|
@@ -7,9 +7,12 @@
|
|
|
7
7
|
* stays wiring. The probes and refusal copy live in
|
|
8
8
|
* `src/core/test-stale-check.ts`.
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
10
|
+
import { filterByCarriedFullCoverage } from '../core/coverage-dispatch.js';
|
|
11
|
+
import { checkStaleBuildForTest, checkStaticComponentsStale, findChangedTestManifestsForPaths, findUncoveredRequestPaths, formatStaleBuildWarning, formatStaticComponentsRefusal, formatTestCoverageRefusal, FULL_SUITE_REQUEST, } from '../core/test-stale-check.js';
|
|
12
|
+
import { formatStaleExemptions } from '../core/test-stale-exempt.js';
|
|
11
13
|
import { PreflightRefusalError } from '../errors/base.js';
|
|
12
|
-
import { warn } from '../utils/logger.js';
|
|
14
|
+
import { info, warn } from '../utils/logger.js';
|
|
15
|
+
import { describePerfStaleImpact } from './test-perf-scope.js';
|
|
13
16
|
/**
|
|
14
17
|
* Stale-build preflight: when `--build` was not requested, detect packageable
|
|
15
18
|
* engine edits since the last successful build and fail up front unless the
|
|
@@ -30,38 +33,67 @@ import { warn } from '../utils/logger.js';
|
|
|
30
33
|
* a full-suite request would refuse a probe that touches no fixtures. The
|
|
31
34
|
* stale-content refusal still applies to it unchanged.
|
|
32
35
|
*/
|
|
33
|
-
export async function enforceStaleBuildGate(projectRoot, engineDir, options, normalizedPaths
|
|
36
|
+
export async function enforceStaleBuildGate(projectRoot, engineDir, options, normalizedPaths,
|
|
37
|
+
/** `quiet`: refuse only, without the exemption note (the pre-lock pass). */
|
|
38
|
+
gate = {}) {
|
|
34
39
|
const stale = await checkStaleBuildForTest(projectRoot, engineDir);
|
|
35
40
|
const dispatchesNoTests = options.doctor === true && normalizedPaths.length === 0;
|
|
36
41
|
if (!dispatchesNoTests) {
|
|
37
|
-
|
|
38
|
-
const uncovered = findUncoveredRequestPaths(recordedCoverage, normalizedPaths);
|
|
39
|
-
if (uncovered.length > 0) {
|
|
40
|
-
// The refusal is correct on its own. Naming the manifest that gained
|
|
41
|
-
// an entry is the triage step it otherwise leaves to the reader.
|
|
42
|
-
const changedManifests = await findChangedTestManifestsForPaths(engineDir, stale.baseline, uncovered);
|
|
43
|
-
throw new PreflightRefusalError(formatTestCoverageRefusal(uncovered, Array.isArray(recordedCoverage) ? recordedCoverage : [], changedManifests), 'coverage-replaced');
|
|
44
|
-
}
|
|
42
|
+
await enforceCoverageGate(engineDir, stale.baseline, normalizedPaths);
|
|
45
43
|
}
|
|
46
44
|
await enforceStaticComponentsGate(engineDir, stale.baseline, options);
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
'always the right move, because the usual cause is your own edit landing between the ' +
|
|
51
|
-
'last build and this run.\n\n' +
|
|
52
|
-
'`--allow-stale-build` is NOT the general escape hatch: it accepts a packaging that ' +
|
|
53
|
-
'predates the edit under test, so the run tests the OLD code. Pass it only when you ' +
|
|
54
|
-
'rebuilt out-of-band (outside FireForge) and know the packaged runtime already contains ' +
|
|
55
|
-
'the change.'
|
|
56
|
-
: undefined;
|
|
57
|
-
if (staleMessage !== undefined) {
|
|
58
|
-
if (options.allowStaleBuild === true) {
|
|
59
|
-
warn(staleMessage);
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
throw new PreflightRefusalError(staleMessage, 'stale-build');
|
|
63
|
-
}
|
|
45
|
+
const exempt = stale.exempt ?? [];
|
|
46
|
+
if (!stale.stale && exempt.length > 0 && gate.quiet !== true) {
|
|
47
|
+
info(`Stale-build preflight: ${exempt.length} edit(s) since the last build need no rebuild: ${formatStaleExemptions(exempt)}.`);
|
|
64
48
|
}
|
|
49
|
+
if (!stale.stale)
|
|
50
|
+
return;
|
|
51
|
+
// The one-line repackaging command, so a build-less sitting (a perf
|
|
52
|
+
// series) can repackage and continue without dispatching anything.
|
|
53
|
+
const repackage = normalizedPaths.length > 0
|
|
54
|
+
? `fireforge test --build-only ${normalizedPaths.join(' ')}`
|
|
55
|
+
: 'fireforge build --ui';
|
|
56
|
+
const perfImpact = options.perfSamples === undefined
|
|
57
|
+
? ''
|
|
58
|
+
: `\n\n${await describePerfStaleImpact(engineDir, stale.changedPaths, normalizedPaths)}`;
|
|
59
|
+
const staleMessage = `${formatStaleBuildWarning(stale)}\n\n` +
|
|
60
|
+
'Run `fireforge test --build` to refresh the packaged runtime first — that is almost ' +
|
|
61
|
+
'always the right move, because the usual cause is your own edit landing between the ' +
|
|
62
|
+
'last build and this run. To repackage without running anything: ' +
|
|
63
|
+
`\`${repackage}\`.${perfImpact}\n\n` +
|
|
64
|
+
'`--allow-stale-build` is NOT the general escape hatch: it accepts a packaging that ' +
|
|
65
|
+
'predates the edit under test, so the run tests the OLD code. Pass it only when you ' +
|
|
66
|
+
'rebuilt out-of-band (outside FireForge) and know the packaged runtime already contains ' +
|
|
67
|
+
'the change.';
|
|
68
|
+
if (options.allowStaleBuild === true) {
|
|
69
|
+
warn(staleMessage);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
throw new PreflightRefusalError(staleMessage, 'stale-build');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Packaging-coverage refusal. A path outside the recorded scoped list is
|
|
77
|
+
* still covered while its own staging inputs are unchanged against a
|
|
78
|
+
* carried full-build anchor. The refusal names, per uncovered path, the
|
|
79
|
+
* inputs that changed.
|
|
80
|
+
*/
|
|
81
|
+
async function enforceCoverageGate(engineDir, baseline, normalizedPaths) {
|
|
82
|
+
const recordedCoverage = baseline?.testPackagingCoverage;
|
|
83
|
+
const outsideList = findUncoveredRequestPaths(recordedCoverage, normalizedPaths);
|
|
84
|
+
if (outsideList.length === 0)
|
|
85
|
+
return;
|
|
86
|
+
const remaining = await filterByCarriedFullCoverage(engineDir, baseline, outsideList, FULL_SUITE_REQUEST);
|
|
87
|
+
if (remaining.length === 0)
|
|
88
|
+
return;
|
|
89
|
+
const uncovered = remaining.map((request) => request.path);
|
|
90
|
+
// The refusal is correct on its own. Naming the manifest that gained
|
|
91
|
+
// an entry is the triage step it otherwise leaves to the reader.
|
|
92
|
+
const changedManifests = await findChangedTestManifestsForPaths(engineDir, baseline, uncovered);
|
|
93
|
+
throw new PreflightRefusalError(formatTestCoverageRefusal(uncovered, Array.isArray(recordedCoverage) ? recordedCoverage : [], changedManifests, {
|
|
94
|
+
changedInputs: Object.fromEntries(remaining.map((request) => [request.path, request.changed])),
|
|
95
|
+
carriesFullAnchor: baseline?.fullCoverageAnchor !== undefined,
|
|
96
|
+
}), 'coverage-replaced');
|
|
65
97
|
}
|
|
66
98
|
/**
|
|
67
99
|
* Compiled-StaticComponents gate: refuses runs whose child process would
|
|
@@ -13,7 +13,7 @@ import type { HarnessRunVerdict } from '../core/test-harness-crash.js';
|
|
|
13
13
|
*/
|
|
14
14
|
export type FireforgeVerdictReason = 'crash' | 'harness-arguments' | 'no-tests' | 'test-failures' | 'preflight' | 'inconclusive' | 'lock-timeout' | 'killed';
|
|
15
15
|
/** Additive keys a run can stamp on its verdict line. */
|
|
16
|
-
export type VerdictRunAttributeKey = 'shuffle' | 'orphans-reaped' | 'host-load' | 'host-cpu-warning' | 'power-source' | 'power-changed';
|
|
16
|
+
export type VerdictRunAttributeKey = 'shuffle' | 'orphans-reaped' | 'host-load' | 'host-cpu-warning' | 'power-source' | 'power-changed' | 'furnace-applied' | 'furnace-apply' | 'furnace-skipped' | 'control-build' | 'drift-tolerated' | 'lock' | 'jobs' | 'results';
|
|
17
17
|
/** Records an additive attribute for this run's verdict line. */
|
|
18
18
|
export declare function setVerdictRunAttribute(key: VerdictRunAttributeKey, value: string): void;
|
|
19
19
|
/**
|
|
@@ -30,6 +30,15 @@ export declare function addVerdictRunCount(key: 'orphans-reaped', count: number)
|
|
|
30
30
|
export declare function resetVerdictEmission(): void;
|
|
31
31
|
/** True once this run has written its `FIREFORGE-VERDICT:` line. */
|
|
32
32
|
export declare function verdictEmitted(): boolean;
|
|
33
|
+
/** Installs (or, with `undefined`, removes) the sub-run capture slot. */
|
|
34
|
+
export declare function setVerdictCapture(slot: {
|
|
35
|
+
line?: string;
|
|
36
|
+
} | undefined): void;
|
|
37
|
+
/**
|
|
38
|
+
* Emits a multi-run aggregate verdict. `body` is everything after the
|
|
39
|
+
* `FIREFORGE-VERDICT: ` prefix.
|
|
40
|
+
*/
|
|
41
|
+
export declare function emitAggregateVerdict(body: string): void;
|
|
33
42
|
/**
|
|
34
43
|
* Emits the classifier-derived verdict for a completed harness run, with
|
|
35
44
|
* the aggregate `shards=<passed>/<total>` suffix for sharded runs.
|
|
@@ -37,6 +46,7 @@ export declare function verdictEmitted(): boolean;
|
|
|
37
46
|
export declare function emitHarnessVerdict(verdict: HarnessRunVerdict, shards?: {
|
|
38
47
|
passed: number;
|
|
39
48
|
total: number;
|
|
49
|
+
failed?: readonly string[];
|
|
40
50
|
}): void;
|
|
41
51
|
/** Emits the pathless `--doctor` PASS verdict. */
|
|
42
52
|
export declare function emitPassVerdict(): void;
|
|
@@ -51,11 +51,31 @@ export function resetVerdictEmission() {
|
|
|
51
51
|
emitted = false;
|
|
52
52
|
armed = true;
|
|
53
53
|
runAttributes.clear();
|
|
54
|
+
capture = undefined;
|
|
54
55
|
setStdoutSealed(false);
|
|
55
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* The sub-run capture slot of a multi-run invocation (`--repeat`, mixed
|
|
59
|
+
* harnesses, `--negative-control`; see `test-orchestrate.ts`). While one is
|
|
60
|
+
* installed, a sub-run's verdict line is recorded here, first write wins as
|
|
61
|
+
* on stdout, and nothing is printed or sealed, so the invocation can still
|
|
62
|
+
* end with its own single aggregate line.
|
|
63
|
+
*/
|
|
64
|
+
let capture;
|
|
56
65
|
/** True once this run has written its `FIREFORGE-VERDICT:` line. */
|
|
57
66
|
export function verdictEmitted() {
|
|
58
|
-
return emitted;
|
|
67
|
+
return capture === undefined ? emitted : capture.line !== undefined;
|
|
68
|
+
}
|
|
69
|
+
/** Installs (or, with `undefined`, removes) the sub-run capture slot. */
|
|
70
|
+
export function setVerdictCapture(slot) {
|
|
71
|
+
capture = slot;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Emits a multi-run aggregate verdict. `body` is everything after the
|
|
75
|
+
* `FIREFORGE-VERDICT: ` prefix.
|
|
76
|
+
*/
|
|
77
|
+
export function emitAggregateVerdict(body) {
|
|
78
|
+
writeVerdictLine(`FIREFORGE-VERDICT: ${body}`);
|
|
59
79
|
}
|
|
60
80
|
/**
|
|
61
81
|
* Writes the run's single verdict line to raw stdout (clack's renderer can
|
|
@@ -68,6 +88,10 @@ export function verdictEmitted() {
|
|
|
68
88
|
* rendering after an emit-then-throw path, routes to stderr.
|
|
69
89
|
*/
|
|
70
90
|
function writeVerdictLine(line) {
|
|
91
|
+
if (capture !== undefined) {
|
|
92
|
+
capture.line ??= line;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
71
95
|
if (emitted)
|
|
72
96
|
return;
|
|
73
97
|
emitted = true;
|
|
@@ -130,6 +154,9 @@ export function emitFailVerdict(reason, note) {
|
|
|
130
154
|
export function emitKilledVerdict(signal) {
|
|
131
155
|
if (!armed || emitted)
|
|
132
156
|
return false;
|
|
157
|
+
// A kill ends the whole invocation, so a sub-run capture must not
|
|
158
|
+
// swallow the one line that says so.
|
|
159
|
+
capture = undefined;
|
|
133
160
|
writeVerdictLine(`FIREFORGE-VERDICT: FAIL reason=killed signal=${signal}`);
|
|
134
161
|
return true;
|
|
135
162
|
}
|
|
@@ -10,8 +10,13 @@ import type { TestOptions } from '../types/commands/index.js';
|
|
|
10
10
|
* since no harness classification exists for such a run. Writers closer to
|
|
11
11
|
* the harness (doctor, canary, single, sharded, the engine-generation
|
|
12
12
|
* guard) emit first and win.
|
|
13
|
+
* The lock-free checks (launchable binary, named-file checkJs, the
|
|
14
|
+
* `--wait-browser`/`--wait-port` budgets) run first, outside `underLock`,
|
|
15
|
+
* so a run that would be refused or would only wait never holds a place in
|
|
16
|
+
* the engine lock queue. See `test-prelock.ts`.
|
|
13
17
|
* @param projectRoot - Root directory of the project
|
|
14
18
|
* @param testPaths - Test file or directory paths
|
|
15
19
|
* @param options - Test options
|
|
20
|
+
* @param underLock - Runs the rest of the command under the engine session lock
|
|
16
21
|
*/
|
|
17
|
-
export declare function testCommand(projectRoot: string, testPaths: string[], options?: TestOptions): Promise<void>;
|
|
22
|
+
export declare function testCommand(projectRoot: string, testPaths: string[], options?: TestOptions, underLock?: (body: () => Promise<void>, mode?: 'exclusive' | 'shared') => Promise<void>): Promise<void>;
|
|
@@ -1,32 +1,33 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
-
import { join } from 'node:path';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
3
|
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
4
4
|
import { assertEngineExists } from '../core/engine-precondition.js';
|
|
5
|
-
import {
|
|
6
|
-
import { hasBuildArtifacts, hasRunnableBundle } from '../core/mach.js';
|
|
5
|
+
import { hasBuildArtifacts } from '../core/mach.js';
|
|
7
6
|
import { assertBuildArtifacts } from '../core/mach-build-artifacts.js';
|
|
8
7
|
import { ensureLaunchableBrowserNotRunning } from '../core/marionette-port.js';
|
|
9
8
|
import { waitForPreflight } from '../core/preflight-wait.js';
|
|
10
|
-
import { closeActiveRunLog, openRunLog, setActiveRunLog, writeToActiveRunLog, } from '../core/run-log.js';
|
|
9
|
+
import { closeActiveRunLog, keepRunLogCopy, openRunLog, setActiveRunLog, writeToActiveRunLog, } from '../core/run-log.js';
|
|
11
10
|
import { createPostRebuildFailureContext } from '../core/test-harness-output.js';
|
|
12
11
|
import { analyzeTestPathScopes, formatScopeNotice, } from '../core/test-path-scope.js';
|
|
13
12
|
import { cleanupProfileFiles, stageProfileFiles, } from '../core/test-profile-files.js';
|
|
14
13
|
import { assertObjdirMatchesTreeMarker } from '../core/tree-store.js';
|
|
15
|
-
import { FireForgeError, GeneralError, PreflightRefusalError } from '../errors/base.js';
|
|
16
|
-
import { BuildError } from '../errors/build.js';
|
|
14
|
+
import { FireForgeError, GeneralError, LockContentionError, PreflightRefusalError, } from '../errors/base.js';
|
|
17
15
|
import { toError } from '../utils/errors.js';
|
|
18
16
|
import { pathExists } from '../utils/fs.js';
|
|
19
|
-
import { info, intro,
|
|
17
|
+
import { info, intro, warn } from '../utils/logger.js';
|
|
20
18
|
import { resolveWaitLockSeconds } from '../utils/options.js';
|
|
21
19
|
import { stripEnginePrefix } from '../utils/paths.js';
|
|
22
20
|
import { appendMarionetteForwardingArgs, ensureTestBrowserEnvironment, runDoctorPreflight, } from './test-browser-preflight.js';
|
|
23
21
|
import { runTestBuildPhase } from './test-build-phase.js';
|
|
24
|
-
import {
|
|
25
|
-
import { buildPerfSampleEnv, mergeHarnessEnv, resolveShuffleSeed
|
|
22
|
+
import { assertParallelTestUsage, dispatchTests } from './test-dispatch.js';
|
|
23
|
+
import { buildPerfSampleEnv, mergeHarnessEnv, resolveShuffleSeed } from './test-harness-env.js';
|
|
26
24
|
import { removePgidFile, setActivePgidFile } from './test-harness-teardown.js';
|
|
27
|
-
import { assertPathlessTestMode, assertTestModeCombinations, canaryTimeoutSeconds, classifyBeforeDispatch,
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
25
|
+
import { assertPathlessTestMode, assertTestModeCombinations, canaryTimeoutSeconds, classifyBeforeDispatch, resolveCanaryPath, restartBetweenTestsArgs, } from './test-modes.js';
|
|
26
|
+
import { orchestrateTestRuns } from './test-orchestrate.js';
|
|
27
|
+
import { resolveLaunchablePathForTests, runTestPreLockPreflight, TEST_BUILD_PREFLIGHT, } from './test-prelock.js';
|
|
28
|
+
import { resetTestResults, withRawLogDir, writeTestResults } from './test-results.js';
|
|
29
|
+
import { DEFAULT_HARNESS_RETRIES } from './test-run.js';
|
|
30
|
+
import { emitFailVerdict, resetVerdictEmission, setVerdictRunAttribute, verdictEmitted, } from './test-verdict.js';
|
|
30
31
|
async function assertTestPathsExist(engineDir, testPaths) {
|
|
31
32
|
const missingPaths = [];
|
|
32
33
|
for (const testPath of testPaths) {
|
|
@@ -77,20 +78,6 @@ function filterRedundantXpcshellFlavorArgs(machArgs, classification) {
|
|
|
77
78
|
}
|
|
78
79
|
return filtered;
|
|
79
80
|
}
|
|
80
|
-
async function resolveLaunchablePathForTests(engineDir, binaryName, objDir) {
|
|
81
|
-
if (!objDir)
|
|
82
|
-
return undefined;
|
|
83
|
-
const bundleCheck = await hasRunnableBundle(engineDir, binaryName, objDir);
|
|
84
|
-
if (!bundleCheck.runnable) {
|
|
85
|
-
const expectedSuffix = bundleCheck.expectedPath
|
|
86
|
-
? ` (expected at engine/${bundleCheck.expectedPath})`
|
|
87
|
-
: '';
|
|
88
|
-
throw new GeneralError(`Tests require a complete launchable build${expectedSuffix}. ` +
|
|
89
|
-
'The obj-*/dist/ tree exists but the launchable binary is missing — typically the result of an interrupted or partially failed `fireforge build`.\n\n' +
|
|
90
|
-
'Run "fireforge build" again and let it finish before retrying "fireforge test".');
|
|
91
|
-
}
|
|
92
|
-
return bundleCheck.expectedPath;
|
|
93
|
-
}
|
|
94
81
|
function logTestSelection(scopes) {
|
|
95
82
|
if (scopes.length > 0) {
|
|
96
83
|
const labels = scopes.map((scope) => scope.isDirectory && scope.testFileCount > 0
|
|
@@ -103,13 +90,6 @@ function logTestSelection(scopes) {
|
|
|
103
90
|
}
|
|
104
91
|
info('');
|
|
105
92
|
}
|
|
106
|
-
/** Build-artifact preflight wording for `fireforge test`. */
|
|
107
|
-
const TEST_BUILD_PREFLIGHT = {
|
|
108
|
-
label: 'Tests',
|
|
109
|
-
requirement: 'Tests require a completed build.',
|
|
110
|
-
remediation: "Run 'fireforge build' first, then run 'fireforge test'.",
|
|
111
|
-
requireExisting: true,
|
|
112
|
-
};
|
|
113
93
|
/**
|
|
114
94
|
* Runs the test command to execute mach tests.
|
|
115
95
|
*
|
|
@@ -121,17 +101,22 @@ const TEST_BUILD_PREFLIGHT = {
|
|
|
121
101
|
* since no harness classification exists for such a run. Writers closer to
|
|
122
102
|
* the harness (doctor, canary, single, sharded, the engine-generation
|
|
123
103
|
* guard) emit first and win.
|
|
104
|
+
* The lock-free checks (launchable binary, named-file checkJs, the
|
|
105
|
+
* `--wait-browser`/`--wait-port` budgets) run first, outside `underLock`,
|
|
106
|
+
* so a run that would be refused or would only wait never holds a place in
|
|
107
|
+
* the engine lock queue. See `test-prelock.ts`.
|
|
124
108
|
* @param projectRoot - Root directory of the project
|
|
125
109
|
* @param testPaths - Test file or directory paths
|
|
126
110
|
* @param options - Test options
|
|
111
|
+
* @param underLock - Runs the rest of the command under the engine session lock
|
|
127
112
|
*/
|
|
128
|
-
export async function testCommand(projectRoot, testPaths, options = {}) {
|
|
113
|
+
export async function testCommand(projectRoot, testPaths, options = {}, underLock = (body) => body()) {
|
|
129
114
|
intro('FireForge Test');
|
|
130
115
|
resetVerdictEmission();
|
|
131
116
|
// Opened before any work so a preflight refusal is logged too. Those are
|
|
132
117
|
// exactly the runs whose only output a `tail` throws away. Closed in
|
|
133
118
|
// `finally`, after the verdict line has already read the path.
|
|
134
|
-
setActiveRunLog(await openRunLog(projectRoot, 'test'));
|
|
119
|
+
setActiveRunLog(await keepRunLogCopy(await openRunLog(projectRoot, 'test'), options.keepLog, 'test'));
|
|
135
120
|
setActivePgidFile(options.pgidFile);
|
|
136
121
|
let staged;
|
|
137
122
|
try {
|
|
@@ -139,12 +124,43 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
|
|
|
139
124
|
staged = await stageProfileFiles(projectRoot, options.profileFile);
|
|
140
125
|
options = { ...options, machArg: [...(options.machArg ?? []), ...staged.args] };
|
|
141
126
|
}
|
|
142
|
-
|
|
127
|
+
assertParallelTestUsage(testPaths, options);
|
|
128
|
+
const lockedOptions = await runTestPreLockPreflight(projectRoot, testPaths, options);
|
|
129
|
+
const profileEnv = staged?.env;
|
|
130
|
+
// The pre-lock pass resolved `sharedLock` against config and the run's shape.
|
|
131
|
+
const lockMode = lockedOptions.sharedLock === true ? 'shared' : 'exclusive';
|
|
132
|
+
if (lockMode === 'shared')
|
|
133
|
+
setVerdictRunAttribute('lock', 'shared');
|
|
134
|
+
resetTestResults();
|
|
135
|
+
if (options.results !== undefined) {
|
|
136
|
+
setVerdictRunAttribute('results', resolve(projectRoot, options.results));
|
|
137
|
+
}
|
|
138
|
+
const orchestrate = (rawLogDir) => orchestrateTestRuns(projectRoot, testPaths, lockedOptions, (paths, runOptions, hooks) => runTestCommandBody(projectRoot, paths, runOptions, profileEnv, {
|
|
139
|
+
...hooks,
|
|
140
|
+
...(rawLogDir === undefined ? {} : { rawLogDir }),
|
|
141
|
+
}), (error) => {
|
|
142
|
+
renderPreflightRefusal(error);
|
|
143
|
+
emitFailVerdict('preflight', refusalNote(error));
|
|
144
|
+
});
|
|
145
|
+
const collectsResults = options.results !== undefined || options.shardSize !== undefined;
|
|
146
|
+
try {
|
|
147
|
+
await underLock(() => (collectsResults ? withRawLogDir((dir) => orchestrate(dir)) : orchestrate()), lockMode);
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
if (options.results !== undefined) {
|
|
151
|
+
await writeResultsFile(resolve(projectRoot, options.results));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
143
154
|
}
|
|
144
155
|
catch (error) {
|
|
145
156
|
if (!verdictEmitted()) {
|
|
146
|
-
|
|
147
|
-
|
|
157
|
+
if (error instanceof LockContentionError) {
|
|
158
|
+
emitFailVerdict('lock-timeout');
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
renderPreflightRefusal(error);
|
|
162
|
+
emitFailVerdict('preflight', refusalNote(error));
|
|
163
|
+
}
|
|
148
164
|
}
|
|
149
165
|
throw error;
|
|
150
166
|
}
|
|
@@ -167,6 +183,15 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
|
|
|
167
183
|
}
|
|
168
184
|
}
|
|
169
185
|
}
|
|
186
|
+
/** Writes `--results`; a failure warns, since the run's verdict is already out. */
|
|
187
|
+
async function writeResultsFile(path) {
|
|
188
|
+
try {
|
|
189
|
+
await writeTestResults(path);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
warn(`Could not write --results ${path}: ${toError(error).message}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
170
195
|
/** The refusal class a preflight gate named, when it named one. */
|
|
171
196
|
function refusalNote(error) {
|
|
172
197
|
return error instanceof PreflightRefusalError ? error.note : undefined;
|
|
@@ -201,24 +226,6 @@ function renderPreflightRefusal(error) {
|
|
|
201
226
|
process.stdout.write(block);
|
|
202
227
|
writeToActiveRunLog(block);
|
|
203
228
|
}
|
|
204
|
-
/**
|
|
205
|
-
* Verifies `engine/` did not change under the run before any PASS verdict
|
|
206
|
-
* may print. On failure the run's verdict is `FAIL reason=inconclusive`,
|
|
207
|
-
* because the harness result exists but cannot be trusted (`preflight` would
|
|
208
|
-
* misdescribe a run whose harness already executed). It is emitted here so the
|
|
209
|
-
* guard's throw can never leave a stale or missing verdict line behind.
|
|
210
|
-
*/
|
|
211
|
-
async function verifyEngineGenerationOrEmitInconclusive(engineDir, before, powerChanged = false) {
|
|
212
|
-
try {
|
|
213
|
-
await assertEngineGenerationUnchanged(engineDir, before);
|
|
214
|
-
if (powerChanged)
|
|
215
|
-
throw new GeneralError('Perf power source changed during the run; repeat this sitting on a stable power source.');
|
|
216
|
-
}
|
|
217
|
-
catch (error) {
|
|
218
|
-
emitFailVerdict('inconclusive');
|
|
219
|
-
throw error;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
229
|
async function guardBuildBrowser(engineDir, launchablePath, options) {
|
|
223
230
|
if ((options.build || options.buildOnly) && launchablePath) {
|
|
224
231
|
await waitForPreflight(() => ensureLaunchableBrowserNotRunning(join(engineDir, launchablePath), {
|
|
@@ -255,9 +262,40 @@ function prepareDispatchArguments(classification, options, canaryPath, projectCo
|
|
|
255
262
|
? filterRedundantXpcshellFlavorArgs(options.machArg, classification)
|
|
256
263
|
: [];
|
|
257
264
|
const extraArgs = prepareExtraMachArgs(options, shuffleSeed, canaryPath, projectConfig, forwardedMachArgs);
|
|
265
|
+
// A `--shard-size` chunk runs several files in one browser; mochitest
|
|
266
|
+
// restarts it between them so a chunk judges each file as a shard would.
|
|
267
|
+
const restart = options.shardSize !== undefined && suite === 'mochitest'
|
|
268
|
+
? { ...options, restartBetweenTests: true }
|
|
269
|
+
: options;
|
|
270
|
+
extraArgs.push(...restartBetweenTestsArgs(restart, suite, forwardedMachArgs));
|
|
258
271
|
return { suite, shuffleSeed, extraArgs };
|
|
259
272
|
}
|
|
260
|
-
|
|
273
|
+
/**
|
|
274
|
+
* Directory arguments mean exactly that directory: mozbuild's test resolver
|
|
275
|
+
* matches paths by string prefix, so a bare directory arg silently sweeps in
|
|
276
|
+
* prefix-named siblings, with `…/test/hominis` also running
|
|
277
|
+
* `…/test/hominis-tiles`, with no indication the scope widened. Each
|
|
278
|
+
* directory argument therefore dispatches as its enumerated explicit
|
|
279
|
+
* test-file list, which cannot prefix-match a sibling. A trailing-`/`
|
|
280
|
+
* normalization does not work, as mach still sweeps the sibling in. Any
|
|
281
|
+
* prefix-siblings are echoed so the narrowed scope is visible.
|
|
282
|
+
*
|
|
283
|
+
* xpcshell appdir auto-injection happens per harness invocation inside
|
|
284
|
+
* `runTestsWithRetries` (src/commands/test-run.ts) so sharded runs probe the
|
|
285
|
+
* manifest for each file individually. See src/core/xpcshell-appdir.ts for
|
|
286
|
+
* the full motivation.
|
|
287
|
+
*/
|
|
288
|
+
async function resolveDispatchGroups(engineDir, normalizedPaths) {
|
|
289
|
+
const scopes = await analyzeTestPathScopes(engineDir, normalizedPaths);
|
|
290
|
+
for (const scope of scopes) {
|
|
291
|
+
const notice = formatScopeNotice(scope);
|
|
292
|
+
if (notice)
|
|
293
|
+
info(notice);
|
|
294
|
+
}
|
|
295
|
+
logTestSelection(scopes);
|
|
296
|
+
return scopes.map((scope) => ({ label: scope.requestedPath, paths: scope.dispatchPaths }));
|
|
297
|
+
}
|
|
298
|
+
async function runTestCommandBody(projectRoot, testPaths, options = {}, profileEnv, hooks = {}) {
|
|
261
299
|
const paths = getProjectPaths(projectRoot);
|
|
262
300
|
// Check if engine exists
|
|
263
301
|
await assertEngineExists(paths.engine);
|
|
@@ -281,7 +319,7 @@ async function runTestCommandBody(projectRoot, testPaths, options = {}, profileE
|
|
|
281
319
|
// against an incomplete build surface the missing-bundle path instead of a
|
|
282
320
|
// cryptic `Browser process exited during spawn (exit code 1, signal none).
|
|
283
321
|
// stderr tail: (empty)`.
|
|
284
|
-
const launchablePath = await resolveLaunchablePathForTests(paths.engine, projectConfig.binaryName, buildCheck.objDir);
|
|
322
|
+
const launchablePath = await resolveLaunchablePathForTests(projectRoot, paths.engine, projectConfig.binaryName, buildCheck.objDir);
|
|
285
323
|
const harnessRetries = options.harnessRetries ?? DEFAULT_HARNESS_RETRIES;
|
|
286
324
|
// Normalized engine-relative request paths, hoisted above the build/stale
|
|
287
325
|
// gate: the pre-test build records them as the packaging-coverage claim,
|
|
@@ -302,9 +340,12 @@ async function runTestCommandBody(projectRoot, testPaths, options = {}, profileE
|
|
|
302
340
|
// documented `--mach-arg --marionette-port=NNNN` route still hits the
|
|
303
341
|
// wrapper preflight refusing on 2828 before the forwarded arg reaches
|
|
304
342
|
// mach.
|
|
343
|
+
// A negative control on compiled source goes in here, so the build
|
|
344
|
+
// compiles it (see negative-control-compiled.ts).
|
|
345
|
+
await hooks.beforeBuild?.();
|
|
305
346
|
if (await runTestBuildPhase(projectRoot, paths, projectConfig, harnessRetries, options, {
|
|
306
347
|
classification,
|
|
307
|
-
normalizedPaths,
|
|
348
|
+
normalizedPaths: hooks.coveragePaths ?? normalizedPaths,
|
|
308
349
|
})) {
|
|
309
350
|
return;
|
|
310
351
|
}
|
|
@@ -324,31 +365,9 @@ async function runTestCommandBody(projectRoot, testPaths, options = {}, profileE
|
|
|
324
365
|
await assertTestPathsExist(paths.engine, normalizedPaths);
|
|
325
366
|
const { suite, shuffleSeed, extraArgs } = prepareDispatchArguments(classification, options, canaryPath, projectConfig);
|
|
326
367
|
appendMarionetteForwardingArgs(extraArgs, options, forwardedPort, xpcshellOnly);
|
|
327
|
-
// Directory arguments mean exactly that directory: mozbuild's test
|
|
328
|
-
// resolver matches paths by string prefix, so a bare directory arg
|
|
329
|
-
// silently sweeps in prefix-named siblings, with `…/test/hominis` also
|
|
330
|
-
// running `…/test/hominis-tiles`, with no indication the scope widened. Each
|
|
331
|
-
// directory argument therefore dispatches as its enumerated explicit
|
|
332
|
-
// test-file list, which cannot prefix-match a sibling. A trailing-`/`
|
|
333
|
-
// normalization does not work, as mach still sweeps the sibling in. Any
|
|
334
|
-
// prefix-siblings are echoed so the narrowed scope is visible.
|
|
335
368
|
// Classification above intentionally used the raw argument forms. Only the
|
|
336
369
|
// mach dispatch needs the exact-match shape.
|
|
337
|
-
const
|
|
338
|
-
const dispatchGroups = scopes.map((scope) => ({
|
|
339
|
-
label: scope.requestedPath,
|
|
340
|
-
paths: scope.dispatchPaths,
|
|
341
|
-
}));
|
|
342
|
-
for (const scope of scopes) {
|
|
343
|
-
const notice = formatScopeNotice(scope);
|
|
344
|
-
if (notice)
|
|
345
|
-
info(notice);
|
|
346
|
-
}
|
|
347
|
-
// xpcshell appdir auto-injection happens per harness invocation inside
|
|
348
|
-
// `runTestsWithRetries` (src/commands/test-run.ts) so sharded runs probe
|
|
349
|
-
// the manifest for each file individually. See src/core/xpcshell-appdir.ts
|
|
350
|
-
// for the full motivation.
|
|
351
|
-
logTestSelection(scopes);
|
|
370
|
+
const dispatchGroups = await resolveDispatchGroups(paths.engine, normalizedPaths);
|
|
352
371
|
const harnessEnv = mergeHarnessEnv(buildPerfSampleEnv(projectRoot, projectConfig.binaryName, options.perfSamples), profileEnv, shuffleSeed === undefined ? undefined : { FIREFORGE_SHUFFLE_SEED: String(shuffleSeed) });
|
|
353
372
|
const runCtx = {
|
|
354
373
|
engineDir: paths.engine,
|
|
@@ -361,55 +380,32 @@ async function runTestCommandBody(projectRoot, testPaths, options = {}, profileE
|
|
|
361
380
|
...(options.fullOutput === true ? { fullOutput: true } : {}),
|
|
362
381
|
...(harnessEnv ? { env: harnessEnv } : {}),
|
|
363
382
|
pgidFile: options.pgidFile,
|
|
383
|
+
...(hooks.rawLogDir === undefined ? {} : { rawLogDir: hooks.rawLogDir }),
|
|
384
|
+
...(hooks.repetition === undefined ? {} : { repetition: hooks.repetition }),
|
|
364
385
|
};
|
|
365
386
|
const postRebuildContext = options.build
|
|
366
387
|
? createPostRebuildFailureContext('fireforge test --build', normalizedPaths)
|
|
367
388
|
: undefined;
|
|
389
|
+
// A negative control goes in here: after every gate and the build, so
|
|
390
|
+
// packaging cannot overwrite it and stale-build cannot refuse it.
|
|
391
|
+
await hooks.beforeDispatch?.();
|
|
368
392
|
// Multi-argument requests shard into sequential harness runs by default:
|
|
369
393
|
// one shared mochitest profile across files bleeds pref/media-query state
|
|
370
394
|
// into later files. Sharding is per path argument: a directory argument
|
|
371
395
|
// keeps its enumerated files together in one invocation, preserving the
|
|
372
396
|
// one-browser-instance semantics of a directory run. `--no-shard` restores
|
|
373
|
-
// the combined invocation
|
|
374
|
-
//
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
}
|
|
388
|
-
finally {
|
|
389
|
-
// Runs before the aggregate verdict below: a mutated engine/ emits
|
|
390
|
-
// `FAIL reason=inconclusive` and throws, so an invalidated run can
|
|
391
|
-
// never print `PASS shards=N/N` first. (A throw here masks an
|
|
392
|
-
// in-flight shard error, as the plain assert always did.)
|
|
393
|
-
await verifyEngineGenerationOrEmitInconclusive(paths.engine, generationBefore, runCtx.powerChanged);
|
|
394
|
-
}
|
|
395
|
-
finalizeShardedOutcome(summary);
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
const combinedDispatchPaths = dispatchGroups.flatMap((group) => group.paths);
|
|
399
|
-
let outcome;
|
|
400
|
-
const generationBefore = await snapshotEngineGeneration(paths.engine);
|
|
401
|
-
try {
|
|
402
|
-
outcome = await runTestsWithRetries(runCtx, combinedDispatchPaths);
|
|
403
|
-
}
|
|
404
|
-
catch (error) {
|
|
405
|
-
throw new BuildError('Test process failed to start', 'mach test', error instanceof Error ? error : undefined);
|
|
406
|
-
}
|
|
407
|
-
finally {
|
|
408
|
-
await verifyEngineGenerationOrEmitInconclusive(paths.engine, generationBefore, runCtx.powerChanged);
|
|
409
|
-
}
|
|
410
|
-
if (canaryPath !== undefined) {
|
|
411
|
-
reportCanaryOutcome(outcome);
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
finalizeSingleRunOutcome(outcome, normalizedPaths, projectConfig.binaryName, postRebuildContext, options.headless === true);
|
|
397
|
+
// the combined invocation; `--shard-size` and `--jobs` reshape it (see
|
|
398
|
+
// test-dispatch.ts).
|
|
399
|
+
await dispatchTests({
|
|
400
|
+
engineDir: paths.engine,
|
|
401
|
+
runCtx,
|
|
402
|
+
dispatchGroups,
|
|
403
|
+
options,
|
|
404
|
+
projectConfig,
|
|
405
|
+
canaryPath,
|
|
406
|
+
shuffleSeed,
|
|
407
|
+
normalizedPaths,
|
|
408
|
+
xpcshellOnly,
|
|
409
|
+
postRebuildContext,
|
|
410
|
+
});
|
|
415
411
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Options accepted by {@link tokenLintCommand}. */
|
|
2
|
+
export interface TokenLintOptions {
|
|
3
|
+
/** Emit the machine-readable envelope instead of the human report. */
|
|
4
|
+
json?: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Runs `token lint`. Exits non-zero (GeneralError) when any finding is
|
|
8
|
+
* reported; with `--json` the report is written first, as `status --check
|
|
9
|
+
* --json` does.
|
|
10
|
+
*
|
|
11
|
+
* @param projectRoot - Root directory of the project
|
|
12
|
+
* @param options - Output-mode options
|
|
13
|
+
*/
|
|
14
|
+
export declare function tokenLintCommand(projectRoot: string, options?: TokenLintOptions): Promise<void>;
|