@hominis/fireforge 0.49.1 → 0.50.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 +41 -0
- 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 +28 -0
- package/dist/src/commands/check-report.js +78 -0
- package/dist/src/commands/check-run.d.ts +28 -0
- package/dist/src/commands/check-run.js +104 -0
- package/dist/src/commands/check-steps.d.ts +67 -0
- package/dist/src/commands/check-steps.js +175 -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/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-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-templates.d.ts +8 -3
- package/dist/src/commands/furnace/create-templates.js +8 -23
- 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 +93 -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 +127 -114
- package/dist/src/commands/furnace/validation-output.js +14 -3
- package/dist/src/commands/lint-per-patch.js +31 -60
- package/dist/src/commands/lint-report.d.ts +8 -4
- package/dist/src/commands/lint-report.js +15 -5
- package/dist/src/commands/lint.js +15 -34
- package/dist/src/commands/manifest.js +2 -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 +13 -98
- package/dist/src/commands/patch/index.d.ts +1 -1
- package/dist/src/commands/patch/index.js +4 -2
- package/dist/src/commands/patch/lint-ignore.d.ts +5 -1
- package/dist/src/commands/patch/lint-ignore.js +65 -28
- package/dist/src/commands/patch/move-files-create.js +26 -17
- package/dist/src/commands/patch/move-files-into.js +5 -3
- package/dist/src/commands/patch/move-files.js +3 -1
- 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 +7 -0
- package/dist/src/commands/patch/retire.d.ts +34 -0
- package/dist/src/commands/patch/retire.js +128 -0
- package/dist/src/commands/patch/split-plan.d.ts +7 -7
- package/dist/src/commands/patch/split-plan.js +45 -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 +51 -15
- 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.js +14 -41
- 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 +45 -2
- package/dist/src/commands/re-export-adjacent.js +156 -37
- 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-lint.d.ts +2 -1
- package/dist/src/commands/re-export-lint.js +23 -10
- package/dist/src/commands/re-export-options.js +45 -4
- 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-register.js +42 -17
- package/dist/src/commands/re-export-removed.d.ts +12 -1
- package/dist/src/commands/re-export-removed.js +41 -11
- 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 +17 -9
- package/dist/src/commands/re-export-scan.js +56 -23
- package/dist/src/commands/re-export-single.js +22 -6
- 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 +37 -24
- 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 +21 -0
- package/dist/src/commands/test-build-furnace.js +48 -0
- package/dist/src/commands/test-build-phase.js +47 -41
- package/dist/src/commands/test-checkjs.js +7 -0
- package/dist/src/commands/test-diagnose.js +7 -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 +43 -0
- package/dist/src/commands/test-orchestrate.js +222 -0
- package/dist/src/commands/test-perf-scope.d.ts +29 -0
- package/dist/src/commands/test-perf-scope.js +83 -0
- package/dist/src/commands/test-prelock.d.ts +51 -0
- package/dist/src/commands/test-prelock.js +177 -0
- package/dist/src/commands/test-register.js +54 -10
- package/dist/src/commands/test-run.js +4 -1
- package/dist/src/commands/test-stale-gate.js +57 -27
- package/dist/src/commands/test-verdict.d.ts +10 -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 +66 -60
- package/dist/src/commands/token-lint.d.ts +14 -0
- package/dist/src/commands/token-lint.js +111 -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 +59 -35
- 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 +7 -0
- package/dist/src/core/build-prepare.js +14 -8
- 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 +8 -0
- package/dist/src/core/config-validate-check.d.ts +8 -0
- package/dist/src/core/config-validate-check.js +143 -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 +7 -0
- package/dist/src/core/config-validate-token-scales.d.ts +6 -0
- package/dist/src/core/config-validate-token-scales.js +50 -0
- package/dist/src/core/config-validate.js +14 -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 +97 -0
- package/dist/src/core/coverage-records.js +350 -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 +10 -1
- package/dist/src/core/engine-session-lock.js +28 -8
- 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.d.ts +4 -25
- package/dist/src/core/file-lock.js +105 -138
- 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 +12 -2
- 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 +9 -0
- package/dist/src/core/furnace-config-validate-exceptions.js +71 -0
- package/dist/src/core/furnace-config-validate.js +8 -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-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 +4 -2
- package/dist/src/core/furnace-validate-accessibility.js +10 -1
- package/dist/src/core/furnace-validate-exceptions.d.ts +40 -0
- package/dist/src/core/furnace-validate-exceptions.js +43 -0
- package/dist/src/core/furnace-validate-icon-button.d.ts +7 -0
- package/dist/src/core/furnace-validate-icon-button.js +49 -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 +157 -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/mach.js +8 -1
- package/dist/src/core/negative-control-diff.d.ts +39 -0
- package/dist/src/core/negative-control-diff.js +136 -0
- package/dist/src/core/negative-control.d.ts +138 -0
- package/dist/src/core/negative-control.js +356 -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/patch-export-update.d.ts +2 -0
- package/dist/src/core/patch-export-update.js +1 -0
- package/dist/src/core/patch-export.js +3 -4
- package/dist/src/core/patch-lint-checkjs-any-import.d.ts +37 -0
- package/dist/src/core/patch-lint-checkjs-any-import.js +61 -0
- package/dist/src/core/patch-lint-checkjs.js +8 -11
- 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.js +12 -4
- package/dist/src/core/patch-lint-css.d.ts +8 -2
- package/dist/src/core/patch-lint-css.js +10 -4
- 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 +18 -0
- package/dist/src/core/patch-lint-forward-registration-extended.js +35 -10
- package/dist/src/core/patch-lint-forward-registration.js +10 -0
- package/dist/src/core/patch-lint-ignore.d.ts +152 -0
- package/dist/src/core/patch-lint-ignore.js +299 -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 +323 -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 +39 -0
- package/dist/src/core/patch-lint-token-scale.js +109 -0
- package/dist/src/core/patch-lint.d.ts +11 -83
- package/dist/src/core/patch-lint.js +64 -286
- 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 +19 -8
- package/dist/src/core/patch-manifest-validate.js +34 -2
- package/dist/src/core/patch-order-legal.d.ts +25 -0
- package/dist/src/core/patch-order-legal.js +83 -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 +16 -3
- package/dist/src/core/patch-policy.js +16 -9
- package/dist/src/core/patch-retired.d.ts +35 -0
- package/dist/src/core/patch-retired.js +54 -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 +20 -0
- package/dist/src/core/run-log.js +89 -3
- 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 +18 -1
- package/dist/src/core/test-harness-crash.js +6 -4
- package/dist/src/core/test-harness-signature.d.ts +17 -0
- package/dist/src/core/test-harness-signature.js +4 -0
- package/dist/src/core/test-run-completeness.d.ts +57 -0
- package/dist/src/core/test-run-completeness.js +144 -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/token-lint.d.ts +50 -0
- package/dist/src/core/token-lint.js +126 -0
- package/dist/src/core/tree-guard.js +3 -0
- package/dist/src/core/typecheck-shim.d.ts +1 -1
- package/dist/src/core/typecheck-shim.js +90 -2
- package/dist/src/index.d.ts +8 -3
- package/dist/src/index.js +3 -1
- package/dist/src/types/commands/index.d.ts +2 -2
- package/dist/src/types/commands/options.d.ts +148 -21
- package/dist/src/types/commands/patches.d.ts +75 -1
- package/dist/src/types/config.d.ts +192 -0
- package/dist/src/types/config.js +9 -0
- package/dist/src/types/furnace.d.ts +43 -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 +34 -6
- package/dist/src/utils/options.js +86 -5
- 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 +282 -9
- package/docs/exit-codes.md +18 -1
- package/docs/furnace.md +97 -3
- package/docs/lifecycle-invariants.md +52 -19
- package/docs/machine-output.md +74 -12
- package/docs/patch-workflow.md +297 -22
- package/docs/run-logs.md +20 -1
- package/docs/testing.md +276 -20
- package/package.json +2 -2
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The exit-0 completeness veto for `fireforge test`.
|
|
3
|
+
*
|
|
4
|
+
* `classifyHarnessRun` trusts a mach exit code of 0 once any test started.
|
|
5
|
+
* That is wrong for one shape a downstream fork hit: the browser shut down
|
|
6
|
+
* cleanly inside the 105th of 314 files ("Application shut down (without
|
|
7
|
+
* crashing) in the middle of a test!"), nothing after it ran, mach still
|
|
8
|
+
* exited 0, and the verdict read `PASS checks=7919 unexpected=1`. The
|
|
9
|
+
* truncation checks the classifier already applies to a non-zero exit with
|
|
10
|
+
* a green-looking summary never ran, because the exit code short-circuited
|
|
11
|
+
* them.
|
|
12
|
+
*
|
|
13
|
+
* So a would-be pass is re-examined here against three facts the captured
|
|
14
|
+
* output carries: a non-zero `Unexpected results:` count, the harness's
|
|
15
|
+
* mid-test shutdown line, and requested test files that never produced a
|
|
16
|
+
* start marker. Any one of them turns the run into
|
|
17
|
+
* `FAIL reason=test-failures`, and a short start count is stamped on the
|
|
18
|
+
* verdict line as `started=<n>/<m> first-unstarted=<path>`.
|
|
19
|
+
*
|
|
20
|
+
* The start-count check only runs when at least one start marker names a
|
|
21
|
+
* requested file. The suite-specific xpcshell dispatch can prove execution
|
|
22
|
+
* with a result-summary block alone and print no `TEST_START` line, and a
|
|
23
|
+
* run that never names its files cannot be counted against them.
|
|
24
|
+
*/
|
|
25
|
+
import { type HarnessRunVerdict } from './test-harness-crash.js';
|
|
26
|
+
import type { IncompleteRunEvidence } from './test-harness-signature.js';
|
|
27
|
+
/**
|
|
28
|
+
* Counts the requested test files that produced a start or skip marker.
|
|
29
|
+
* Returns undefined when nothing can be counted: no test-file-shaped
|
|
30
|
+
* request, or no start line that names any requested file.
|
|
31
|
+
*
|
|
32
|
+
* @param output - Captured mach stdout and stderr
|
|
33
|
+
* @param requestedPaths - Dispatched paths, in dispatch order
|
|
34
|
+
*/
|
|
35
|
+
export declare function countStartedFiles(output: string, requestedPaths: readonly string[]): {
|
|
36
|
+
started: number;
|
|
37
|
+
requested: number;
|
|
38
|
+
firstUnstarted?: string;
|
|
39
|
+
} | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Re-examines a passing verdict for a mach exit-0 run and fails it when
|
|
42
|
+
* the run is incomplete or reported unexpected results. Any other verdict
|
|
43
|
+
* is returned unchanged.
|
|
44
|
+
*
|
|
45
|
+
* @param verdict - The classifier's verdict
|
|
46
|
+
* @param output - Captured mach stdout and stderr
|
|
47
|
+
* @param requestedPaths - Dispatched paths, in dispatch order
|
|
48
|
+
*/
|
|
49
|
+
export declare function vetoIncompletePass(verdict: HarnessRunVerdict, output: string, requestedPaths: readonly string[]): HarnessRunVerdict;
|
|
50
|
+
/**
|
|
51
|
+
* Operator-facing explanation for a pass {@link vetoIncompletePass}
|
|
52
|
+
* refused. Names the first file that never started, because that is
|
|
53
|
+
* where a re-run has to pick up.
|
|
54
|
+
*
|
|
55
|
+
* @param evidence - The veto's evidence
|
|
56
|
+
*/
|
|
57
|
+
export declare function buildIncompleteRunMessage(evidence: IncompleteRunEvidence): string;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* The exit-0 completeness veto for `fireforge test`.
|
|
4
|
+
*
|
|
5
|
+
* `classifyHarnessRun` trusts a mach exit code of 0 once any test started.
|
|
6
|
+
* That is wrong for one shape a downstream fork hit: the browser shut down
|
|
7
|
+
* cleanly inside the 105th of 314 files ("Application shut down (without
|
|
8
|
+
* crashing) in the middle of a test!"), nothing after it ran, mach still
|
|
9
|
+
* exited 0, and the verdict read `PASS checks=7919 unexpected=1`. The
|
|
10
|
+
* truncation checks the classifier already applies to a non-zero exit with
|
|
11
|
+
* a green-looking summary never ran, because the exit code short-circuited
|
|
12
|
+
* them.
|
|
13
|
+
*
|
|
14
|
+
* So a would-be pass is re-examined here against three facts the captured
|
|
15
|
+
* output carries: a non-zero `Unexpected results:` count, the harness's
|
|
16
|
+
* mid-test shutdown line, and requested test files that never produced a
|
|
17
|
+
* start marker. Any one of them turns the run into
|
|
18
|
+
* `FAIL reason=test-failures`, and a short start count is stamped on the
|
|
19
|
+
* verdict line as `started=<n>/<m> first-unstarted=<path>`.
|
|
20
|
+
*
|
|
21
|
+
* The start-count check only runs when at least one start marker names a
|
|
22
|
+
* requested file. The suite-specific xpcshell dispatch can prove execution
|
|
23
|
+
* with a result-summary block alone and print no `TEST_START` line, and a
|
|
24
|
+
* run that never names its files cannot be counted against them.
|
|
25
|
+
*/
|
|
26
|
+
import { SHUTDOWN_REENTRY_PATTERN, START_MARKER_PATTERN, TEST_FILE_BASENAME_PATTERN, } from './test-harness-crash.js';
|
|
27
|
+
/**
|
|
28
|
+
* A skip marker naming a test (`TEST-SKIP | path`, `TEST_SKIP: path`). A
|
|
29
|
+
* manifest-skipped file is accounted for, not missing.
|
|
30
|
+
*/
|
|
31
|
+
const SKIP_MARKER_PATTERN = /\bTEST[-_]SKIP(?:PED)?\b[:| ]*\s*(\S+)?/;
|
|
32
|
+
/** Basename of a `/`-separated path. */
|
|
33
|
+
function basenameOf(path) {
|
|
34
|
+
return path.slice(path.lastIndexOf('/') + 1);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Counts the requested test files that produced a start or skip marker.
|
|
38
|
+
* Returns undefined when nothing can be counted: no test-file-shaped
|
|
39
|
+
* request, or no start line that names any requested file.
|
|
40
|
+
*
|
|
41
|
+
* @param output - Captured mach stdout and stderr
|
|
42
|
+
* @param requestedPaths - Dispatched paths, in dispatch order
|
|
43
|
+
*/
|
|
44
|
+
export function countStartedFiles(output, requestedPaths) {
|
|
45
|
+
const requested = requestedPaths.filter((path) => TEST_FILE_BASENAME_PATTERN.test(basenameOf(path)));
|
|
46
|
+
if (requested.length === 0)
|
|
47
|
+
return undefined;
|
|
48
|
+
const started = [];
|
|
49
|
+
const skipped = [];
|
|
50
|
+
for (const line of output.split(/\r?\n/)) {
|
|
51
|
+
const start = START_MARKER_PATTERN.exec(line)?.[1];
|
|
52
|
+
if (start !== undefined) {
|
|
53
|
+
started.push(start);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const skip = SKIP_MARKER_PATTERN.exec(line)?.[1];
|
|
57
|
+
if (skip !== undefined)
|
|
58
|
+
skipped.push(skip);
|
|
59
|
+
}
|
|
60
|
+
const names = (path, list) => {
|
|
61
|
+
const base = basenameOf(path);
|
|
62
|
+
return list.some((name) => name.includes(base));
|
|
63
|
+
};
|
|
64
|
+
// Counting needs a dialect that names the requested files on its start
|
|
65
|
+
// lines. When no start line names any of them, the output cannot be
|
|
66
|
+
// counted against the request at all.
|
|
67
|
+
if (!requested.some((path) => names(path, started)))
|
|
68
|
+
return undefined;
|
|
69
|
+
const unstarted = requested.filter((path) => !names(path, started) && !names(path, skipped));
|
|
70
|
+
const firstUnstarted = unstarted[0];
|
|
71
|
+
return {
|
|
72
|
+
started: requested.length - unstarted.length,
|
|
73
|
+
requested: requested.length,
|
|
74
|
+
...(firstUnstarted !== undefined ? { firstUnstarted } : {}),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Re-examines a passing verdict for a mach exit-0 run and fails it when
|
|
79
|
+
* the run is incomplete or reported unexpected results. Any other verdict
|
|
80
|
+
* is returned unchanged.
|
|
81
|
+
*
|
|
82
|
+
* @param verdict - The classifier's verdict
|
|
83
|
+
* @param output - Captured mach stdout and stderr
|
|
84
|
+
* @param requestedPaths - Dispatched paths, in dispatch order
|
|
85
|
+
*/
|
|
86
|
+
export function vetoIncompletePass(verdict, output, requestedPaths) {
|
|
87
|
+
if (verdict.kind !== 'tests-ran-ok')
|
|
88
|
+
return verdict;
|
|
89
|
+
const shutdownLine = output
|
|
90
|
+
.split(/\r?\n/)
|
|
91
|
+
.find((line) => SHUTDOWN_REENTRY_PATTERN.test(line))
|
|
92
|
+
?.trim();
|
|
93
|
+
const counted = countStartedFiles(output, requestedPaths);
|
|
94
|
+
const unexpected = verdict.unexpected ?? 0;
|
|
95
|
+
const truncated = counted?.firstUnstarted !== undefined;
|
|
96
|
+
if (unexpected === 0 && shutdownLine === undefined && !truncated)
|
|
97
|
+
return verdict;
|
|
98
|
+
const evidence = {
|
|
99
|
+
started: counted?.started ?? 0,
|
|
100
|
+
requested: counted?.requested ?? 0,
|
|
101
|
+
...(counted?.firstUnstarted !== undefined ? { firstUnstarted: counted.firstUnstarted } : {}),
|
|
102
|
+
...(shutdownLine !== undefined ? { shutdownLine } : {}),
|
|
103
|
+
...(unexpected > 0 ? { unexpected } : {}),
|
|
104
|
+
};
|
|
105
|
+
// The pass-only annotations would misdescribe a failed run: "harness
|
|
106
|
+
// teardown noise ignored" and the green-summary override both say the
|
|
107
|
+
// run was trusted.
|
|
108
|
+
const rest = { ...verdict };
|
|
109
|
+
delete rest.greenSummaryOverride;
|
|
110
|
+
delete rest.note;
|
|
111
|
+
return {
|
|
112
|
+
...rest,
|
|
113
|
+
kind: 'test-failures',
|
|
114
|
+
incomplete: evidence,
|
|
115
|
+
...(shutdownLine !== undefined ? { realFailureLine: shutdownLine } : {}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Operator-facing explanation for a pass {@link vetoIncompletePass}
|
|
120
|
+
* refused. Names the first file that never started, because that is
|
|
121
|
+
* where a re-run has to pick up.
|
|
122
|
+
*
|
|
123
|
+
* @param evidence - The veto's evidence
|
|
124
|
+
*/
|
|
125
|
+
export function buildIncompleteRunMessage(evidence) {
|
|
126
|
+
const lines = [];
|
|
127
|
+
if (evidence.firstUnstarted !== undefined) {
|
|
128
|
+
lines.push(` - only ${evidence.started} of ${evidence.requested} requested test files started; ` +
|
|
129
|
+
`the first that never started is ${evidence.firstUnstarted}`);
|
|
130
|
+
}
|
|
131
|
+
if (evidence.shutdownLine !== undefined) {
|
|
132
|
+
lines.push(` - the browser shut down in the middle of a test: ${evidence.shutdownLine}`);
|
|
133
|
+
}
|
|
134
|
+
if (evidence.unexpected !== undefined) {
|
|
135
|
+
lines.push(` - the suite summary reported ${evidence.unexpected} unexpected result(s)`);
|
|
136
|
+
}
|
|
137
|
+
const rerun = evidence.firstUnstarted !== undefined
|
|
138
|
+
? `Re-run from ${evidence.firstUnstarted} onward, or the whole selection once the engine is quiet.`
|
|
139
|
+
: 'Read the unexpected results in the output above.';
|
|
140
|
+
return ('mach exited 0, but FireForge did NOT treat the run as passed:\n\n' +
|
|
141
|
+
`${lines.join('\n')}\n\n` +
|
|
142
|
+
'A browser that quits cleanly mid-suite exits 0, and its summary then covers only the ' +
|
|
143
|
+
`files that ran. ${rerun}`);
|
|
144
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BuildBaseline, TestPackagingCoverage } from './build-baseline-types.js';
|
|
2
|
+
import { type StaleExemption } from './test-stale-exempt.js';
|
|
2
3
|
/** Result of the stale-build preflight probe. */
|
|
3
4
|
export interface StaleBuildResult {
|
|
4
5
|
/** True when at least one packageable engine file changed since the baseline. */
|
|
@@ -22,6 +23,13 @@ export interface StaleBuildResult {
|
|
|
22
23
|
* we have nothing to compare against and a warning would mislead.
|
|
23
24
|
*/
|
|
24
25
|
baseline: BuildBaseline | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Changed packageable paths the packaged runtime already serves (objdir
|
|
28
|
+
* symlinks to the edited source, test files restaged at dispatch), which
|
|
29
|
+
* therefore do not make the build stale. Named in the refusal and in a
|
|
30
|
+
* notice so the exemption is visible.
|
|
31
|
+
*/
|
|
32
|
+
exempt?: StaleExemption[];
|
|
25
33
|
}
|
|
26
34
|
/**
|
|
27
35
|
* Probes the engine tree for packageable changes since the last successful
|
|
@@ -79,15 +87,33 @@ export declare function findUncoveredRequestPaths(coverage: TestPackagingCoverag
|
|
|
79
87
|
* {@link STALE_PATHS_LIMIT}
|
|
80
88
|
*/
|
|
81
89
|
export declare function findChangedTestManifestsForPaths(engineDir: string, baseline: BuildBaseline | undefined, uncovered: readonly string[]): Promise<string[]>;
|
|
90
|
+
/** Extra evidence for {@link formatTestCoverageRefusal}. */
|
|
91
|
+
export interface CoverageRefusalDetail {
|
|
92
|
+
/**
|
|
93
|
+
* Per uncovered path, the staging inputs that changed since the full
|
|
94
|
+
* build the recorded claim carries forward (what the replaced coverage
|
|
95
|
+
* affects).
|
|
96
|
+
*/
|
|
97
|
+
changedInputs?: Readonly<Record<string, readonly string[]>>;
|
|
98
|
+
/** The record carries a full build's anchor forward for unchanged paths. */
|
|
99
|
+
carriesFullAnchor?: boolean;
|
|
100
|
+
}
|
|
82
101
|
/**
|
|
102
|
+
* Formats the refusal shown when a non-`--build` run requests paths the
|
|
103
|
+
* packaged runtime's scoped `test --build` coverage does not include. It is
|
|
104
|
+
* enforced on every such run, with or without `--allow-stale-build`. Kept
|
|
105
|
+
* separate from the matcher so tests can pin structure and copy
|
|
106
|
+
* independently (same split as {@link formatStaleBuildWarning}).
|
|
107
|
+
*
|
|
83
108
|
* @param uncovered - Request paths the recorded packaging does not cover
|
|
84
109
|
* @param coverage - The recorded scoped coverage claim
|
|
85
110
|
* @param changedManifests - Test manifests changed since the recorded
|
|
86
111
|
* build that explain an uncovered path (from
|
|
87
112
|
* {@link findChangedTestManifestsForPaths}). Omitted or empty renders
|
|
88
113
|
* nothing
|
|
114
|
+
* @param detail - See {@link CoverageRefusalDetail}
|
|
89
115
|
*/
|
|
90
|
-
export declare function formatTestCoverageRefusal(uncovered: string[], coverage: string[], changedManifests?: readonly string[]): string;
|
|
116
|
+
export declare function formatTestCoverageRefusal(uncovered: string[], coverage: string[], changedManifests?: readonly string[], detail?: CoverageRefusalDetail): string;
|
|
91
117
|
/** Result of the compiled-StaticComponents staleness probe. */
|
|
92
118
|
export interface StaticComponentsStaleResult {
|
|
93
119
|
/** True when at least one `components.conf` genuinely diverged from the anchor. */
|
|
@@ -28,7 +28,9 @@ import { normalizePathSlashes } from '../utils/paths.js';
|
|
|
28
28
|
import { isPackageablePath, isXpcomManifestPath } from './build-audit.js';
|
|
29
29
|
import { readBuildBaseline } from './build-baseline.js';
|
|
30
30
|
import { hashEngineFile } from './coverage-extend.js';
|
|
31
|
+
import { isTestManifestPath } from './coverage-records.js';
|
|
31
32
|
import { collectChangedEnginePaths, dropPathsMatchingFingerprints } from './engine-changes.js';
|
|
33
|
+
import { formatStaleExemptions, partitionStaleCandidates, } from './test-stale-exempt.js';
|
|
32
34
|
/** Cap on the number of changed paths rendered inline. */
|
|
33
35
|
const STALE_PATHS_LIMIT = 10;
|
|
34
36
|
/**
|
|
@@ -61,12 +63,15 @@ export async function checkStaleBuildForTest(projectRoot, engineDir) {
|
|
|
61
63
|
// fingerprints capture "these files had this content when the build ran".
|
|
62
64
|
// A path stays stale only when its live hash diverges.
|
|
63
65
|
packageable = await dropPathsMatchingFingerprints(engineDir, packageable, baseline.packageableFingerprints);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
// Edits the runtime already serves (symlinked into the objdir, or test
|
|
67
|
+
// files `mach test` restages at dispatch) cannot make the packaging stale.
|
|
68
|
+
const { stale, exempt } = await partitionStaleCandidates(engineDir, packageable);
|
|
69
|
+
if (stale.length === 0) {
|
|
70
|
+
return { stale: false, changedPaths: [], truncated: 0, baseline, exempt };
|
|
66
71
|
}
|
|
67
|
-
const head =
|
|
68
|
-
const truncated = Math.max(0,
|
|
69
|
-
return { stale: true, changedPaths: head, truncated, baseline };
|
|
72
|
+
const head = stale.slice(0, STALE_PATHS_LIMIT);
|
|
73
|
+
const truncated = Math.max(0, stale.length - head.length);
|
|
74
|
+
return { stale: true, changedPaths: head, truncated, baseline, exempt };
|
|
70
75
|
}
|
|
71
76
|
/**
|
|
72
77
|
* Formats a human-readable warning body from a {@link StaleBuildResult}.
|
|
@@ -76,10 +81,15 @@ export async function checkStaleBuildForTest(projectRoot, engineDir) {
|
|
|
76
81
|
export function formatStaleBuildWarning(result) {
|
|
77
82
|
const tail = result.truncated > 0 ? `, … (+${result.truncated} more)` : '';
|
|
78
83
|
const list = result.changedPaths.join(', ') + tail;
|
|
84
|
+
const exempt = result.exempt ?? [];
|
|
85
|
+
const exemptLine = exempt.length > 0
|
|
86
|
+
? `\nNot counted as stale, because the runtime already serves them: ${formatStaleExemptions(exempt)}.`
|
|
87
|
+
: '';
|
|
79
88
|
return (`Engine tree has changed since the last successful fireforge build (${list}).\n` +
|
|
80
89
|
'The current obj-*/dist/ bundle may not reflect those edits. If your test reads ' +
|
|
81
90
|
'packaged chrome / jar.mn resources, rerun with "fireforge test --build" (or ' +
|
|
82
|
-
'"fireforge build --ui") first. Passing --build skips this check.'
|
|
91
|
+
'"fireforge build --ui") first. Passing --build skips this check.' +
|
|
92
|
+
exemptLine);
|
|
83
93
|
}
|
|
84
94
|
/**
|
|
85
95
|
* Sentinel returned by {@link findUncoveredRequestPaths} when a full-suite
|
|
@@ -120,37 +130,6 @@ export function findUncoveredRequestPaths(coverage, requestedPaths) {
|
|
|
120
130
|
function normalizeCoveragePath(path) {
|
|
121
131
|
return normalizePathSlashes(path.trim()).replace(/\/+$/, '');
|
|
122
132
|
}
|
|
123
|
-
/**
|
|
124
|
-
* Formats the refusal shown when a non-`--build` run requests paths the
|
|
125
|
-
* packaged runtime's scoped `test --build` coverage does not include. It is
|
|
126
|
-
* enforced on every such run, with or without `--allow-stale-build`. Kept
|
|
127
|
-
* separate from the matcher so tests can pin structure and copy
|
|
128
|
-
* independently (same split as {@link formatStaleBuildWarning}).
|
|
129
|
-
*/
|
|
130
|
-
/**
|
|
131
|
-
* Basenames of the test manifests whose entries decide what a packaged test
|
|
132
|
-
* runtime stages. A manifest that gained an entry is the single most common
|
|
133
|
-
* reason a run needs coverage the last build never claimed, and it is a
|
|
134
|
-
* fact FireForge already holds but never said, leaving the operator to
|
|
135
|
-
* rediscover it after a correct refusal.
|
|
136
|
-
*/
|
|
137
|
-
const TEST_MANIFEST_BASENAMES = new Set([
|
|
138
|
-
'xpcshell.toml',
|
|
139
|
-
'xpcshell.ini',
|
|
140
|
-
'browser.toml',
|
|
141
|
-
'browser.ini',
|
|
142
|
-
'mochitest.toml',
|
|
143
|
-
'mochitest.ini',
|
|
144
|
-
'chrome.toml',
|
|
145
|
-
'chrome.ini',
|
|
146
|
-
'a11y.toml',
|
|
147
|
-
'a11y.ini',
|
|
148
|
-
]);
|
|
149
|
-
/** True for a test manifest path (by basename). */
|
|
150
|
-
function isTestManifestPath(path) {
|
|
151
|
-
const base = path.slice(path.lastIndexOf('/') + 1);
|
|
152
|
-
return TEST_MANIFEST_BASENAMES.has(base);
|
|
153
|
-
}
|
|
154
133
|
/**
|
|
155
134
|
* Test manifests changed since the recorded build that sit in (or above)
|
|
156
135
|
* the directory of an uncovered request path, i.e. the manifests that
|
|
@@ -185,31 +164,35 @@ export async function findChangedTestManifestsForPaths(engineDir, baseline, unco
|
|
|
185
164
|
return hits.slice(0, STALE_PATHS_LIMIT);
|
|
186
165
|
}
|
|
187
166
|
/**
|
|
167
|
+
* Formats the refusal shown when a non-`--build` run requests paths the
|
|
168
|
+
* packaged runtime's scoped `test --build` coverage does not include. It is
|
|
169
|
+
* enforced on every such run, with or without `--allow-stale-build`. Kept
|
|
170
|
+
* separate from the matcher so tests can pin structure and copy
|
|
171
|
+
* independently (same split as {@link formatStaleBuildWarning}).
|
|
172
|
+
*
|
|
188
173
|
* @param uncovered - Request paths the recorded packaging does not cover
|
|
189
174
|
* @param coverage - The recorded scoped coverage claim
|
|
190
175
|
* @param changedManifests - Test manifests changed since the recorded
|
|
191
176
|
* build that explain an uncovered path (from
|
|
192
177
|
* {@link findChangedTestManifestsForPaths}). Omitted or empty renders
|
|
193
178
|
* nothing
|
|
179
|
+
* @param detail - See {@link CoverageRefusalDetail}
|
|
194
180
|
*/
|
|
195
|
-
export function formatTestCoverageRefusal(uncovered, coverage, changedManifests = []) {
|
|
181
|
+
export function formatTestCoverageRefusal(uncovered, coverage, changedManifests = [], detail = {}) {
|
|
196
182
|
const cap = (paths) => {
|
|
197
183
|
const head = paths.slice(0, STALE_PATHS_LIMIT);
|
|
198
184
|
const truncated = paths.length - head.length;
|
|
199
185
|
return head.join(', ') + (truncated > 0 ? `, … (+${truncated} more)` : '');
|
|
200
186
|
};
|
|
201
187
|
const rebuildTargets = uncovered.filter((p) => p !== FULL_SUITE_REQUEST);
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
// because they are what decides whether it will work.
|
|
208
|
-
const extendHint = rebuildTargets.length > 0
|
|
209
|
-
? ` Add --extend-coverage to that rebuild to UNION the new paths onto the recorded claim instead of replacing it — it holds while engine HEAD and engine/mozconfig are unchanged since the recorded build.`
|
|
188
|
+
// Per-path retention is the default, so rebuilding only the uncovered
|
|
189
|
+
// paths no longer costs anyone else's claim. Saying so is what keeps a
|
|
190
|
+
// peer from rebuilding everything "to be safe".
|
|
191
|
+
const keepHint = rebuildTargets.length > 0
|
|
192
|
+
? ' That scoped build keeps every other recorded path whose own staging inputs (manifest directory and the fixtures its manifest names) and engine/mozconfig are unchanged.'
|
|
210
193
|
: '';
|
|
211
194
|
const rebuildHint = rebuildTargets.length > 0
|
|
212
|
-
? `Rerun "fireforge test --build ${rebuildTargets.slice(0, STALE_PATHS_LIMIT).join(' ')}" to package them, or run "fireforge build" for full coverage.${
|
|
195
|
+
? `Rerun "fireforge test --build ${rebuildTargets.slice(0, STALE_PATHS_LIMIT).join(' ')}" to package them (or "fireforge test --build-only ..." to package without running), or run "fireforge build" for full coverage.${keepHint}`
|
|
213
196
|
: 'Run "fireforge build" (or a path-less "fireforge test --build") for full coverage first.';
|
|
214
197
|
// Why this run needs new coverage, when a changed manifest explains it: a
|
|
215
198
|
// manifest that gained an entry is not covered by a build claim recorded
|
|
@@ -217,10 +200,17 @@ export function formatTestCoverageRefusal(uncovered, coverage, changedManifests
|
|
|
217
200
|
const manifestLine = changedManifests.length > 0
|
|
218
201
|
? ` This run needs coverage the recorded build never claimed because a test manifest changed since it: ${changedManifests.map((m) => `engine/${m}`).join(', ')} — a manifest that gained an entry stages new support fixtures, and a claim recorded for other paths (or another harness) does not cover them.`
|
|
219
202
|
: '';
|
|
220
|
-
|
|
203
|
+
const affected = Object.entries(detail.changedInputs ?? {}).filter(([, files]) => files.length > 0);
|
|
204
|
+
const affectedLine = affected.length > 0
|
|
205
|
+
? ` Staging inputs changed since the full build this record carries forward: ${affected.map(([path, files]) => `${path} (${cap(files)})`).join('; ')}.`
|
|
206
|
+
: '';
|
|
207
|
+
const scope = detail.carriesFullAnchor
|
|
208
|
+
? `${cap(coverage)}, plus earlier full coverage for paths whose own staging inputs are unchanged`
|
|
209
|
+
: cap(coverage);
|
|
210
|
+
return (`The packaged test runtime was produced by a scoped "fireforge test --build" covering only: ${scope}.\n` +
|
|
221
211
|
`The requested run needs ${cap(uncovered)}, which that packaging does not cover — support ` +
|
|
222
212
|
'fixtures for those manifests may be missing from obj-*/_tests/, and the run can hang ' +
|
|
223
|
-
`rather than fail.${manifestLine} ${rebuildHint} ` +
|
|
213
|
+
`rather than fail.${affectedLine}${manifestLine} ${rebuildHint} ` +
|
|
224
214
|
'(--allow-stale-build does not bypass this check — it accepts stale content, not missing coverage.)');
|
|
225
215
|
}
|
|
226
216
|
/**
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edits the stale-build preflight must not count. A test file the build
|
|
3
|
+
* never packages is restaged by `mach test` at dispatch, and a file whose
|
|
4
|
+
* every objdir copy is a symlink back to the edited source is already what
|
|
5
|
+
* the runtime reads. Refusing on either made every iteration on a test (or
|
|
6
|
+
* on symlinked chrome) pay a packaging pass or reach for
|
|
7
|
+
* `--allow-stale-build`, the flag whose refusal text says not to.
|
|
8
|
+
*/
|
|
9
|
+
import { type InstallManifestIndex } from './objdir-install-manifests.js';
|
|
10
|
+
/** A changed path the preflight does not count as stale, and why. */
|
|
11
|
+
export interface StaleExemption {
|
|
12
|
+
path: string;
|
|
13
|
+
/**
|
|
14
|
+
* `symlinked`: every objdir copy is a symlink to the edited source.
|
|
15
|
+
* `test-only`: a test file the build does not package, restaged at dispatch.
|
|
16
|
+
*/
|
|
17
|
+
why: 'symlinked' | 'test-only';
|
|
18
|
+
}
|
|
19
|
+
/** The single `obj-*` directory under the engine, or undefined when zero or several exist. */
|
|
20
|
+
export declare function findSingleObjDir(engineDir: string): Promise<string | undefined>;
|
|
21
|
+
/**
|
|
22
|
+
* The objdir's install-manifest index, or undefined when there is no single
|
|
23
|
+
* objdir or nothing in it is readable.
|
|
24
|
+
* @param engineDir - Absolute engine directory
|
|
25
|
+
*/
|
|
26
|
+
export declare function loadEngineInstallIndex(engineDir: string): Promise<InstallManifestIndex | undefined>;
|
|
27
|
+
/**
|
|
28
|
+
* Splits changed packageable paths into the ones that make the packaged
|
|
29
|
+
* runtime stale and the ones it already serves. Without a readable objdir
|
|
30
|
+
* index every path stays stale, the behaviour before the exemption existed.
|
|
31
|
+
*
|
|
32
|
+
* @param engineDir - Absolute engine directory
|
|
33
|
+
* @param paths - Changed engine-relative paths, already fingerprint-filtered
|
|
34
|
+
*/
|
|
35
|
+
export declare function partitionStaleCandidates(engineDir: string, paths: readonly string[]): Promise<{
|
|
36
|
+
stale: string[];
|
|
37
|
+
exempt: StaleExemption[];
|
|
38
|
+
}>;
|
|
39
|
+
/** Renders exemptions for a notice or refusal: `a (symlinked), b (test file)`. */
|
|
40
|
+
export declare function formatStaleExemptions(exempt: readonly StaleExemption[], limit?: number): string;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Edits the stale-build preflight must not count. A test file the build
|
|
4
|
+
* never packages is restaged by `mach test` at dispatch, and a file whose
|
|
5
|
+
* every objdir copy is a symlink back to the edited source is already what
|
|
6
|
+
* the runtime reads. Refusing on either made every iteration on a test (or
|
|
7
|
+
* on symlinked chrome) pay a packaging pass or reach for
|
|
8
|
+
* `--allow-stale-build`, the flag whose refusal text says not to.
|
|
9
|
+
*/
|
|
10
|
+
import { readdir } from 'node:fs/promises';
|
|
11
|
+
import { isTestPath } from './build-audit-resolve.js';
|
|
12
|
+
import { classifyRuntimeDelivery, loadInstallManifestIndex, } from './objdir-install-manifests.js';
|
|
13
|
+
/** The single `obj-*` directory under the engine, or undefined when zero or several exist. */
|
|
14
|
+
export async function findSingleObjDir(engineDir) {
|
|
15
|
+
try {
|
|
16
|
+
const entries = await readdir(engineDir, { withFileTypes: true });
|
|
17
|
+
const objDirs = entries.filter((entry) => entry.isDirectory() && entry.name.startsWith('obj-'));
|
|
18
|
+
return objDirs.length === 1 ? objDirs[0]?.name : undefined;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The objdir's install-manifest index, or undefined when there is no single
|
|
26
|
+
* objdir or nothing in it is readable.
|
|
27
|
+
* @param engineDir - Absolute engine directory
|
|
28
|
+
*/
|
|
29
|
+
export async function loadEngineInstallIndex(engineDir) {
|
|
30
|
+
const objDir = await findSingleObjDir(engineDir);
|
|
31
|
+
return objDir === undefined ? undefined : loadInstallManifestIndex(engineDir, objDir);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Splits changed packageable paths into the ones that make the packaged
|
|
35
|
+
* runtime stale and the ones it already serves. Without a readable objdir
|
|
36
|
+
* index every path stays stale, the behaviour before the exemption existed.
|
|
37
|
+
*
|
|
38
|
+
* @param engineDir - Absolute engine directory
|
|
39
|
+
* @param paths - Changed engine-relative paths, already fingerprint-filtered
|
|
40
|
+
*/
|
|
41
|
+
export async function partitionStaleCandidates(engineDir, paths) {
|
|
42
|
+
if (paths.length === 0)
|
|
43
|
+
return { stale: [], exempt: [] };
|
|
44
|
+
const index = await loadEngineInstallIndex(engineDir);
|
|
45
|
+
if (index === undefined)
|
|
46
|
+
return { stale: [...paths], exempt: [] };
|
|
47
|
+
const stale = [];
|
|
48
|
+
const exempt = [];
|
|
49
|
+
for (const path of paths) {
|
|
50
|
+
const delivery = await classifyRuntimeDelivery(index, engineDir, path);
|
|
51
|
+
if (delivery === 'live')
|
|
52
|
+
exempt.push({ path, why: 'symlinked' });
|
|
53
|
+
else if (delivery === 'at-dispatch' || (delivery === 'unlisted' && isTestPath(path))) {
|
|
54
|
+
exempt.push({ path, why: 'test-only' });
|
|
55
|
+
}
|
|
56
|
+
else
|
|
57
|
+
stale.push(path);
|
|
58
|
+
}
|
|
59
|
+
return { stale, exempt };
|
|
60
|
+
}
|
|
61
|
+
/** Renders exemptions for a notice or refusal: `a (symlinked), b (test file)`. */
|
|
62
|
+
export function formatStaleExemptions(exempt, limit = 10) {
|
|
63
|
+
const head = exempt
|
|
64
|
+
.slice(0, limit)
|
|
65
|
+
.map(({ path, why }) => why === 'symlinked'
|
|
66
|
+
? `${path} (objdir symlink to the source)`
|
|
67
|
+
: `${path} (test file, restaged at dispatch)`);
|
|
68
|
+
const extra = exempt.length - head.length;
|
|
69
|
+
return head.join(', ') + (extra > 0 ? `, … (+${extra} more)` : '');
|
|
70
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fireforge token lint`: structural checks over the design-token system.
|
|
3
|
+
*
|
|
4
|
+
* - `token-lockstep`: a themed override block (the dark `@media` mirror, a
|
|
5
|
+
* `:root[variant]` block) declares only tokens the base `:root` declares.
|
|
6
|
+
* An override with no base is a token that exists only in one theme, so
|
|
7
|
+
* every other theme resolves it to nothing.
|
|
8
|
+
* - `token-undeclared`: a `var(--<prefix>…)` reference in the tokens CSS or
|
|
9
|
+
* in a CSS file the patch queue touches names a token no block of the
|
|
10
|
+
* tokens CSS declares. Same-file declarations, `furnace.json`
|
|
11
|
+
* `tokenAllowlist` and `runtimeVariables` are exempt, matching the
|
|
12
|
+
* `token-prefix-violation` rule's notion of a runtime channel.
|
|
13
|
+
*
|
|
14
|
+
* Built on `token-category.ts`'s base-block and comment-mask primitives, so
|
|
15
|
+
* this report and `token add` agree on which block is the base.
|
|
16
|
+
*/
|
|
17
|
+
/** One `token lint` finding. */
|
|
18
|
+
export interface TokenLintFinding {
|
|
19
|
+
check: 'token-lockstep' | 'token-undeclared';
|
|
20
|
+
/** Engine-relative file. */
|
|
21
|
+
file: string;
|
|
22
|
+
/** 1-based line. */
|
|
23
|
+
line: number;
|
|
24
|
+
/** The custom property at fault. */
|
|
25
|
+
token: string;
|
|
26
|
+
message: string;
|
|
27
|
+
}
|
|
28
|
+
/** Every custom property the tokens CSS declares, in any block. */
|
|
29
|
+
export declare function collectDeclaredTokenNames(lines: string[]): Set<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Themed override declarations of `tokenPrefix` tokens that the base
|
|
32
|
+
* `:root` block does not declare.
|
|
33
|
+
*
|
|
34
|
+
* @param lines - Tokens CSS split into lines
|
|
35
|
+
* @param tokensCssPath - Engine-relative path, for the findings
|
|
36
|
+
* @param tokenPrefix - Only tokens with this prefix are held to lockstep;
|
|
37
|
+
* a themed block may still override platform properties
|
|
38
|
+
*/
|
|
39
|
+
export declare function lintTokenLockstep(lines: string[], tokensCssPath: string, tokenPrefix: string): TokenLintFinding[];
|
|
40
|
+
/**
|
|
41
|
+
* `var(--<prefix>…)` references naming a token the tokens CSS never
|
|
42
|
+
* declares.
|
|
43
|
+
*
|
|
44
|
+
* @param file - Engine-relative path, for the findings
|
|
45
|
+
* @param css - The file's content
|
|
46
|
+
* @param tokenPrefix - Only references with this prefix are checked
|
|
47
|
+
* @param declared - {@link collectDeclaredTokenNames} of the tokens CSS
|
|
48
|
+
* @param exempt - `tokenAllowlist` and `runtimeVariables` names
|
|
49
|
+
*/
|
|
50
|
+
export declare function lintUndeclaredTokenReferences(file: string, css: string, tokenPrefix: string, declared: ReadonlySet<string>, exempt: ReadonlySet<string>): TokenLintFinding[];
|