@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
|
@@ -81,7 +81,7 @@ export async function readBuildBaseline(projectRoot, invalid = 'ignore') {
|
|
|
81
81
|
* @param options - See {@link WriteBuildBaselineOptions}
|
|
82
82
|
*/
|
|
83
83
|
export async function writeBuildBaseline(options) {
|
|
84
|
-
const { projectRoot, engineDir, binaryName, testPackagingCoverage, previousBaseline, recordedBy, staticComponentsHandling = 'auto', buildKind = 'full', } = options;
|
|
84
|
+
const { projectRoot, engineDir, binaryName, testPackagingCoverage, testCoverageRecords, fullCoverageAnchor, previousBaseline, recordedBy, staticComponentsHandling = 'auto', buildKind = 'full', } = options;
|
|
85
85
|
let engineHeadSha = '';
|
|
86
86
|
try {
|
|
87
87
|
engineHeadSha = await getHead(engineDir);
|
|
@@ -116,6 +116,8 @@ export async function writeBuildBaseline(options) {
|
|
|
116
116
|
...(testInputFingerprints !== undefined ? { testInputFingerprints } : {}),
|
|
117
117
|
...(buildInputFingerprints !== undefined ? { buildInputFingerprints } : {}),
|
|
118
118
|
...(testPackagingCoverage !== undefined ? { testPackagingCoverage } : {}),
|
|
119
|
+
...(testCoverageRecords !== undefined ? { testCoverageRecords } : {}),
|
|
120
|
+
...(fullCoverageAnchor !== undefined ? { fullCoverageAnchor } : {}),
|
|
119
121
|
...(mozconfigHash !== undefined ? { mozconfigHash } : {}),
|
|
120
122
|
...(staticComponentsBaseline !== undefined ? { staticComponentsBaseline } : {}),
|
|
121
123
|
...(recordedBy !== undefined ? { recordedBy } : {}),
|
|
@@ -14,8 +14,17 @@
|
|
|
14
14
|
* whose content is explained by neither a patch body nor the pristine
|
|
15
15
|
* baseline. Advisory by default (a warning naming every file), refusing
|
|
16
16
|
* under an explicit flag.
|
|
17
|
+
*
|
|
18
|
+
* A deployed Furnace file is checked against its component source as well
|
|
19
|
+
* (`deployed-unexported`): when the engine copy equals the Furnace-composed
|
|
20
|
+
* current source, differs from it only inside generated fragment
|
|
21
|
+
* expansions, or is exactly Furnace's last recorded deployment, the
|
|
22
|
+
* overwrite destroys nothing that is not a component source, so it is safe
|
|
23
|
+
* even though no patch records it yet. Without this, peers' deployed but
|
|
24
|
+
* unexported widgets refused every belted build on a shared checkout.
|
|
17
25
|
*/
|
|
18
26
|
import type { FireForgeConfig } from '../types/config.js';
|
|
27
|
+
import { type DeployedSourceMatch } from './furnace-composed-source.js';
|
|
19
28
|
import { type ClassifiedFile } from './status-classify.js';
|
|
20
29
|
/** One file whose unexported content a build-prepare overwrite would destroy. */
|
|
21
30
|
export interface UnexportedDriftAtRisk {
|
|
@@ -23,6 +32,24 @@ export interface UnexportedDriftAtRisk {
|
|
|
23
32
|
classification: ClassifiedFile['classification'];
|
|
24
33
|
owner?: string | undefined;
|
|
25
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* A deployed Furnace file no patch records yet whose content is its
|
|
37
|
+
* component source's (see {@link DeployedSourceMatch}), so the overwrite is
|
|
38
|
+
* safe. Its patch still owes a re-export.
|
|
39
|
+
*/
|
|
40
|
+
export interface DeployedUnexportedFile {
|
|
41
|
+
file: string;
|
|
42
|
+
classification: 'deployed-unexported';
|
|
43
|
+
match: DeployedSourceMatch;
|
|
44
|
+
owner?: string | undefined;
|
|
45
|
+
}
|
|
46
|
+
/** Outcome of {@link assessUnexportedDrift}. */
|
|
47
|
+
export interface UnexportedDriftAssessment {
|
|
48
|
+
/** Files whose content the overwrite would destroy unrecorded. */
|
|
49
|
+
atRisk: UnexportedDriftAtRisk[];
|
|
50
|
+
/** Files the overwrite rewrites from their own component source. */
|
|
51
|
+
deployedUnexported: DeployedUnexportedFile[];
|
|
52
|
+
}
|
|
26
53
|
/**
|
|
27
54
|
* Finds engine files that build-prepare is about to overwrite and whose
|
|
28
55
|
* current content is recorded nowhere.
|
|
@@ -30,11 +57,34 @@ export interface UnexportedDriftAtRisk {
|
|
|
30
57
|
* Fail-open: any probe failure is reported verbosely and yields an empty
|
|
31
58
|
* list. A build must not fail because an advisory guard could not run.
|
|
32
59
|
*
|
|
60
|
+
* @internal The at-risk half of {@link assessUnexportedDrift}, kept for the
|
|
61
|
+
* guard's tests; build-prepare consumes the full assessment.
|
|
62
|
+
*
|
|
33
63
|
* @param projectRoot - Project root
|
|
34
64
|
* @param config - Loaded FireForge config (reads `binaryName`)
|
|
35
65
|
* @returns At-risk files, sorted by path
|
|
36
66
|
*/
|
|
37
67
|
export declare function findUnexportedDriftAtRisk(projectRoot: string, config: FireForgeConfig): Promise<UnexportedDriftAtRisk[]>;
|
|
68
|
+
/**
|
|
69
|
+
* Classifies the engine files build-prepare is about to overwrite into the
|
|
70
|
+
* ones whose content is recorded nowhere (`atRisk`) and the deployed
|
|
71
|
+
* Furnace files no patch records yet whose content is their component
|
|
72
|
+
* source's (`deployedUnexported`, safe to overwrite).
|
|
73
|
+
*
|
|
74
|
+
* Fail-open like {@link findUnexportedDriftAtRisk}: a probe failure yields
|
|
75
|
+
* an empty assessment.
|
|
76
|
+
*
|
|
77
|
+
* @param projectRoot - Project root
|
|
78
|
+
* @param config - Loaded FireForge config (reads `binaryName`)
|
|
79
|
+
* @returns Both lists, sorted by path
|
|
80
|
+
*/
|
|
81
|
+
export declare function assessUnexportedDrift(projectRoot: string, config: FireForgeConfig): Promise<UnexportedDriftAssessment>;
|
|
82
|
+
/**
|
|
83
|
+
* Renders the informational note for deployed Furnace files the overwrite
|
|
84
|
+
* rewrites from their own component source: safe for this build, but their
|
|
85
|
+
* patches still owe a re-export.
|
|
86
|
+
*/
|
|
87
|
+
export declare function formatDeployedUnexportedNote(files: readonly DeployedUnexportedFile[]): string;
|
|
38
88
|
/**
|
|
39
89
|
* Renders the operator-facing warning (or refusal body) naming every file
|
|
40
90
|
* whose unexported content the build is about to overwrite.
|
|
@@ -15,10 +15,19 @@
|
|
|
15
15
|
* whose content is explained by neither a patch body nor the pristine
|
|
16
16
|
* baseline. Advisory by default (a warning naming every file), refusing
|
|
17
17
|
* under an explicit flag.
|
|
18
|
+
*
|
|
19
|
+
* A deployed Furnace file is checked against its component source as well
|
|
20
|
+
* (`deployed-unexported`): when the engine copy equals the Furnace-composed
|
|
21
|
+
* current source, differs from it only inside generated fragment
|
|
22
|
+
* expansions, or is exactly Furnace's last recorded deployment, the
|
|
23
|
+
* overwrite destroys nothing that is not a component source, so it is safe
|
|
24
|
+
* even though no patch records it yet. Without this, peers' deployed but
|
|
25
|
+
* unexported widgets refused every belted build on a shared checkout.
|
|
18
26
|
*/
|
|
19
27
|
import { toError } from '../utils/errors.js';
|
|
20
28
|
import { verbose } from '../utils/logger.js';
|
|
21
29
|
import { getProjectPaths } from './config.js';
|
|
30
|
+
import { matchDeployedComponentFiles, } from './furnace-composed-source.js';
|
|
22
31
|
import { collectFurnaceManagedPrefixes } from './furnace-config.js';
|
|
23
32
|
import { expandUntrackedDirectoryEntries, getWorkingTreeStatus } from './git-status.js';
|
|
24
33
|
import { classifyFiles } from './status-classify.js';
|
|
@@ -98,6 +107,40 @@ async function collectOverwrittenEntries(engineDir, status, binaryName, furnaceP
|
|
|
98
107
|
return expanded.filter((entry) => !entry.file.endsWith('/') &&
|
|
99
108
|
isOverwrittenByBuildPrepare(entry.file, binaryName, furnacePrefixes));
|
|
100
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Splits classified at-risk files into the ones whose engine content is a
|
|
112
|
+
* Furnace deployment of their component source and the rest.
|
|
113
|
+
*
|
|
114
|
+
* Fail-closed: when the source comparison cannot run, every file stays at
|
|
115
|
+
* risk, exactly as before the comparison existed.
|
|
116
|
+
*/
|
|
117
|
+
async function separateDeployedUnexported(projectRoot, engineDir, atRisk) {
|
|
118
|
+
let matches;
|
|
119
|
+
try {
|
|
120
|
+
matches = await matchDeployedComponentFiles(projectRoot, engineDir, atRisk.map((entry) => entry.file));
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
verbose(`Unexported-drift guard could not compare component sources: ${toError(error).message}`);
|
|
124
|
+
return { atRisk, deployedUnexported: [] };
|
|
125
|
+
}
|
|
126
|
+
const deployedUnexported = [];
|
|
127
|
+
const remaining = [];
|
|
128
|
+
for (const entry of atRisk) {
|
|
129
|
+
const match = matches.get(entry.file);
|
|
130
|
+
if (match === undefined) {
|
|
131
|
+
remaining.push(entry);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
deployedUnexported.push({
|
|
135
|
+
file: entry.file,
|
|
136
|
+
classification: 'deployed-unexported',
|
|
137
|
+
match,
|
|
138
|
+
owner: entry.owner,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return { atRisk: remaining, deployedUnexported };
|
|
143
|
+
}
|
|
101
144
|
/**
|
|
102
145
|
* Finds engine files that build-prepare is about to overwrite and whose
|
|
103
146
|
* current content is recorded nowhere.
|
|
@@ -105,22 +148,42 @@ async function collectOverwrittenEntries(engineDir, status, binaryName, furnaceP
|
|
|
105
148
|
* Fail-open: any probe failure is reported verbosely and yields an empty
|
|
106
149
|
* list. A build must not fail because an advisory guard could not run.
|
|
107
150
|
*
|
|
151
|
+
* @internal The at-risk half of {@link assessUnexportedDrift}, kept for the
|
|
152
|
+
* guard's tests; build-prepare consumes the full assessment.
|
|
153
|
+
*
|
|
108
154
|
* @param projectRoot - Project root
|
|
109
155
|
* @param config - Loaded FireForge config (reads `binaryName`)
|
|
110
156
|
* @returns At-risk files, sorted by path
|
|
111
157
|
*/
|
|
112
158
|
export async function findUnexportedDriftAtRisk(projectRoot, config) {
|
|
159
|
+
return (await assessUnexportedDrift(projectRoot, config)).atRisk;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Classifies the engine files build-prepare is about to overwrite into the
|
|
163
|
+
* ones whose content is recorded nowhere (`atRisk`) and the deployed
|
|
164
|
+
* Furnace files no patch records yet whose content is their component
|
|
165
|
+
* source's (`deployedUnexported`, safe to overwrite).
|
|
166
|
+
*
|
|
167
|
+
* Fail-open like {@link findUnexportedDriftAtRisk}: a probe failure yields
|
|
168
|
+
* an empty assessment.
|
|
169
|
+
*
|
|
170
|
+
* @param projectRoot - Project root
|
|
171
|
+
* @param config - Loaded FireForge config (reads `binaryName`)
|
|
172
|
+
* @returns Both lists, sorted by path
|
|
173
|
+
*/
|
|
174
|
+
export async function assessUnexportedDrift(projectRoot, config) {
|
|
175
|
+
const empty = { atRisk: [], deployedUnexported: [] };
|
|
113
176
|
const paths = getProjectPaths(projectRoot);
|
|
114
177
|
try {
|
|
115
178
|
const status = await getWorkingTreeStatus(paths.engine);
|
|
116
179
|
if (status.length === 0)
|
|
117
|
-
return
|
|
180
|
+
return empty;
|
|
118
181
|
const furnacePrefixes = await collectFurnaceManagedPrefixes(projectRoot);
|
|
119
182
|
const candidates = await collectOverwrittenEntries(paths.engine, status, config.binaryName, furnacePrefixes);
|
|
120
183
|
if (candidates.length === 0)
|
|
121
|
-
return
|
|
184
|
+
return empty;
|
|
122
185
|
const classified = await classifyFiles(candidates, paths.engine, paths.patches, config.binaryName, furnacePrefixes);
|
|
123
|
-
|
|
186
|
+
const atRisk = classified
|
|
124
187
|
.filter((entry) => AT_RISK_CLASSIFICATIONS.has(entry.classification))
|
|
125
188
|
.map((entry) => ({
|
|
126
189
|
file: entry.file,
|
|
@@ -128,12 +191,29 @@ export async function findUnexportedDriftAtRisk(projectRoot, config) {
|
|
|
128
191
|
owner: entry.owner,
|
|
129
192
|
}))
|
|
130
193
|
.sort((a, b) => a.file.localeCompare(b.file));
|
|
194
|
+
if (atRisk.length === 0 || furnacePrefixes.size === 0)
|
|
195
|
+
return { ...empty, atRisk };
|
|
196
|
+
return await separateDeployedUnexported(projectRoot, paths.engine, atRisk);
|
|
131
197
|
}
|
|
132
198
|
catch (error) {
|
|
133
199
|
verbose(`Unexported-drift guard could not run: ${toError(error).message}`);
|
|
134
|
-
return
|
|
200
|
+
return empty;
|
|
135
201
|
}
|
|
136
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Renders the informational note for deployed Furnace files the overwrite
|
|
205
|
+
* rewrites from their own component source: safe for this build, but their
|
|
206
|
+
* patches still owe a re-export.
|
|
207
|
+
*/
|
|
208
|
+
export function formatDeployedUnexportedNote(files) {
|
|
209
|
+
const rows = files.map((entry) => {
|
|
210
|
+
const owner = entry.owner !== undefined ? ` (owned by ${entry.owner})` : '';
|
|
211
|
+
return ` ${entry.file} [deployed-unexported: ${entry.match}]${owner}`;
|
|
212
|
+
});
|
|
213
|
+
return (`${files.length} deployed Furnace file(s) differ from their patch body but are a Furnace ` +
|
|
214
|
+
'deployment of their component source, so this overwrite loses nothing; re-export their ' +
|
|
215
|
+
`patches when the component work is done:\n${rows.join('\n')}`);
|
|
216
|
+
}
|
|
137
217
|
/**
|
|
138
218
|
* Renders the operator-facing warning (or refusal body) naming every file
|
|
139
219
|
* whose unexported content the build is about to overwrite.
|
|
@@ -10,6 +10,8 @@ import type { BuildBaseline } from './build-baseline-types.js';
|
|
|
10
10
|
export interface BuildPreparation {
|
|
11
11
|
/** Number of Furnace components applied (0 if none or no furnace.json) */
|
|
12
12
|
furnaceApplied: number;
|
|
13
|
+
/** Names of the Furnace components applied, in apply order. */
|
|
14
|
+
furnaceAppliedNames?: string[];
|
|
13
15
|
/** True when `mach configure` was auto-run to refresh a stale backend. */
|
|
14
16
|
reconfigured: boolean;
|
|
15
17
|
/** A changed packaging manifest cannot be safely handled by `mach build faster`. */
|
|
@@ -35,6 +37,17 @@ export interface PrepareBuildOptions {
|
|
|
35
37
|
* Scripted gates want the stop. An interactive build wants the warning.
|
|
36
38
|
*/
|
|
37
39
|
refuseUnexportedDrift?: boolean | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Skip the Furnace apply, so the build packages engine/ as it stands
|
|
42
|
+
* (`fireforge test --build --no-furnace-apply`).
|
|
43
|
+
*/
|
|
44
|
+
skipFurnaceApply?: boolean | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Apply only these components (`fireforge test --build --furnace-apply
|
|
47
|
+
* reached`). Undefined applies every component with undeployed edits; an
|
|
48
|
+
* empty list applies none.
|
|
49
|
+
*/
|
|
50
|
+
furnaceComponents?: readonly string[] | undefined;
|
|
38
51
|
}
|
|
39
52
|
/**
|
|
40
53
|
* Describes an exit code in the shell's 128+N signal convention. Truncated
|
|
@@ -13,7 +13,7 @@ import { info, notice, spinner, verbose, warn } from '../utils/logger.js';
|
|
|
13
13
|
import { isBrandingSetup, setupBranding } from './branding.js';
|
|
14
14
|
import { isBuildInputPath, isJarManifestPath } from './build-audit.js';
|
|
15
15
|
import { evaluateJarManifestEscalation, formatJarEscalationNotice, } from './build-jar-escalation.js';
|
|
16
|
-
import {
|
|
16
|
+
import { assessUnexportedDrift, formatDeployedUnexportedNote, formatUnexportedDriftWarning, } from './build-overwrite-guard.js';
|
|
17
17
|
import { hashEngineFile } from './coverage-extend.js';
|
|
18
18
|
import { collectChangedEnginePaths, dropPathsMatchingFingerprints } from './engine-changes.js';
|
|
19
19
|
import { applyAllComponents } from './furnace-apply.js';
|
|
@@ -156,7 +156,12 @@ export function isBackendInvalidatingFile(path) {
|
|
|
156
156
|
* @throws GeneralError under `--refuse-unexported-drift`
|
|
157
157
|
*/
|
|
158
158
|
async function reportUnexportedDriftBeforeOverwrite(projectRoot, config, options) {
|
|
159
|
-
const atRisk = await
|
|
159
|
+
const { atRisk, deployedUnexported } = await assessUnexportedDrift(projectRoot, config);
|
|
160
|
+
// Deployed Furnace files that are their component source's own
|
|
161
|
+
// deployment never block: the overwrite loses nothing. Say so, since
|
|
162
|
+
// their patches still owe a re-export.
|
|
163
|
+
if (deployedUnexported.length > 0)
|
|
164
|
+
info(formatDeployedUnexportedNote(deployedUnexported));
|
|
160
165
|
if (atRisk.length === 0)
|
|
161
166
|
return;
|
|
162
167
|
const message = formatUnexportedDriftWarning(atRisk);
|
|
@@ -165,24 +170,48 @@ async function reportUnexportedDriftBeforeOverwrite(projectRoot, config, options
|
|
|
165
170
|
}
|
|
166
171
|
notice(message);
|
|
167
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Applies the named components one at a time, each under its own Furnace
|
|
175
|
+
* mutation (one rollback journal per apply), and merges the results.
|
|
176
|
+
*/
|
|
177
|
+
async function applySelectedComponents(projectRoot, names) {
|
|
178
|
+
const merged = {
|
|
179
|
+
applied: [],
|
|
180
|
+
skipped: [],
|
|
181
|
+
errors: [],
|
|
182
|
+
warnings: [],
|
|
183
|
+
};
|
|
184
|
+
for (const componentName of names) {
|
|
185
|
+
const result = await runFurnaceMutation(projectRoot, 'apply-rollback', (ctx) => applyAllComponents(projectRoot, false, { operationContext: ctx, componentName }));
|
|
186
|
+
merged.applied.push(...result.applied);
|
|
187
|
+
merged.skipped.push(...result.skipped);
|
|
188
|
+
merged.errors.push(...result.errors);
|
|
189
|
+
merged.warnings.push(...(result.warnings ?? []));
|
|
190
|
+
}
|
|
191
|
+
return merged;
|
|
192
|
+
}
|
|
168
193
|
/**
|
|
169
194
|
* Applies Furnace components before the build when `furnace.json` exists.
|
|
170
195
|
* Prints every warning the apply computed, then the loud sync banner.
|
|
171
196
|
*
|
|
172
197
|
* @param projectRoot - Project root
|
|
173
|
-
* @
|
|
198
|
+
* @param only - Apply just these components; every component when undefined
|
|
199
|
+
* @returns Names of the components written to engine/
|
|
174
200
|
*/
|
|
175
|
-
async function applyFurnaceBeforeBuild(projectRoot) {
|
|
201
|
+
async function applyFurnaceBeforeBuild(projectRoot, only) {
|
|
176
202
|
if (!(await furnaceConfigExists(projectRoot)))
|
|
177
|
-
return
|
|
203
|
+
return [];
|
|
178
204
|
const furnaceConfig = await loadFurnaceConfig(projectRoot);
|
|
179
205
|
const hasComponents = Object.keys(furnaceConfig.overrides).length > 0 || Object.keys(furnaceConfig.custom).length > 0;
|
|
180
206
|
if (!hasComponents)
|
|
181
|
-
return
|
|
207
|
+
return [];
|
|
182
208
|
const furnaceSpinner = spinner('Applying Furnace components...');
|
|
183
209
|
let result;
|
|
184
210
|
try {
|
|
185
|
-
result =
|
|
211
|
+
result =
|
|
212
|
+
only === undefined
|
|
213
|
+
? await runFurnaceMutation(projectRoot, 'apply-rollback', (ctx) => applyAllComponents(projectRoot, false, { operationContext: ctx }))
|
|
214
|
+
: await applySelectedComponents(projectRoot, only);
|
|
186
215
|
}
|
|
187
216
|
catch (error) {
|
|
188
217
|
furnaceSpinner.error('Failed to apply Furnace components');
|
|
@@ -230,7 +259,7 @@ async function applyFurnaceBeforeBuild(projectRoot) {
|
|
|
230
259
|
else {
|
|
231
260
|
furnaceSpinner.stop('Components up to date');
|
|
232
261
|
}
|
|
233
|
-
return
|
|
262
|
+
return result.applied.map((entry) => entry.name);
|
|
234
263
|
}
|
|
235
264
|
/**
|
|
236
265
|
* Runs the auto-configure step (`mach configure`) against the freshly
|
|
@@ -344,7 +373,9 @@ export async function prepareBuildEnvironment(projectRoot, paths, config, option
|
|
|
344
373
|
throw error;
|
|
345
374
|
}
|
|
346
375
|
}
|
|
347
|
-
const
|
|
376
|
+
const furnaceAppliedNames = options.skipFurnaceApply === true || options.furnaceComponents?.length === 0
|
|
377
|
+
? []
|
|
378
|
+
: await applyFurnaceBeforeBuild(projectRoot, options.furnaceComponents);
|
|
348
379
|
// Generate mozconfig
|
|
349
380
|
const mozconfigSpinner = spinner('Generating mozconfig...');
|
|
350
381
|
let mozconfigWritten;
|
|
@@ -374,7 +405,8 @@ export async function prepareBuildEnvironment(projectRoot, paths, config, option
|
|
|
374
405
|
reconfigured = true;
|
|
375
406
|
}
|
|
376
407
|
return {
|
|
377
|
-
furnaceApplied,
|
|
408
|
+
furnaceApplied: furnaceAppliedNames.length,
|
|
409
|
+
furnaceAppliedNames,
|
|
378
410
|
reconfigured,
|
|
379
411
|
fullBuildRequired,
|
|
380
412
|
...(fullBuildReason !== undefined ? { fullBuildReason } : {}),
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A record of the mach build in flight, kept in `.fireforge/build-run.json`
|
|
3
|
+
* so any session on the checkout can see why the bundle is missing.
|
|
4
|
+
*
|
|
5
|
+
* An interrupted `fireforge build` (a killed peer session, a closed
|
|
6
|
+
* terminal) can leave `dist/<App>.app/Contents/MacOS/` without its
|
|
7
|
+
* executable. Every later `fireforge test`, in every session, then failed
|
|
8
|
+
* at launch with nothing saying a build had died there, and the sessions
|
|
9
|
+
* that had not run the build had no way to find out.
|
|
10
|
+
*
|
|
11
|
+
* The record is written when a protected mach build starts and removed when
|
|
12
|
+
* it succeeds. A failed build leaves it with `status: "failed"` and the exit
|
|
13
|
+
* code. A build that never reached either end, because its process died,
|
|
14
|
+
* leaves `status: "running"` with a PID that no longer exists, which is how
|
|
15
|
+
* an interrupted run is recognized. The next build that runs to the end
|
|
16
|
+
* replaces whatever was there.
|
|
17
|
+
*
|
|
18
|
+
* Best-effort throughout: a build never fails because its record could not
|
|
19
|
+
* be written, and an unreadable record reads as "no record".
|
|
20
|
+
*/
|
|
21
|
+
/** The persisted record. */
|
|
22
|
+
interface BuildRunRecord {
|
|
23
|
+
/** Whether the build is (or was last seen) running, or ended in failure. */
|
|
24
|
+
status: 'running' | 'failed';
|
|
25
|
+
/** PID of the FireForge process that ran the build. */
|
|
26
|
+
pid: number;
|
|
27
|
+
/** That process's boot-relative start tick, where procfs exposes one. */
|
|
28
|
+
startTick?: number;
|
|
29
|
+
/** The FireForge invocation, e.g. `fireforge build --ui`. */
|
|
30
|
+
command: string;
|
|
31
|
+
/** The mach command, e.g. `mach build faster`. */
|
|
32
|
+
machCommand: string;
|
|
33
|
+
/** ISO time the mach build started. */
|
|
34
|
+
startedAt: string;
|
|
35
|
+
/** ISO time a failed build ended. */
|
|
36
|
+
endedAt?: string;
|
|
37
|
+
/** Exit code of a failed build, when mach returned one. */
|
|
38
|
+
exitCode?: number;
|
|
39
|
+
}
|
|
40
|
+
/** What the record says about the last build. */
|
|
41
|
+
export type BuildRunState = {
|
|
42
|
+
kind: 'none';
|
|
43
|
+
} | {
|
|
44
|
+
kind: 'running' | 'interrupted' | 'failed';
|
|
45
|
+
record: BuildRunRecord;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Runs a mach build with its run record maintained around it. The record
|
|
49
|
+
* is removed on a zero exit and kept as `failed` on a non-zero exit or a
|
|
50
|
+
* throw, so only a build whose process died mid-run is left `running`.
|
|
51
|
+
*
|
|
52
|
+
* @param projectRoot - Project root holding `.fireforge/`
|
|
53
|
+
* @param machCommand - The mach command being run
|
|
54
|
+
* @param build - The build itself
|
|
55
|
+
* @returns Whatever the build returned
|
|
56
|
+
*/
|
|
57
|
+
export declare function withBuildRunRecord<T extends {
|
|
58
|
+
exitCode: number;
|
|
59
|
+
}>(projectRoot: string, machCommand: string, build: () => Promise<T>): Promise<T>;
|
|
60
|
+
/**
|
|
61
|
+
* Reads the last build's record and decides whether its process is still
|
|
62
|
+
* running.
|
|
63
|
+
*
|
|
64
|
+
* @param projectRoot - Project root holding `.fireforge/`
|
|
65
|
+
*/
|
|
66
|
+
export declare function readBuildRunState(projectRoot: string): Promise<BuildRunState>;
|
|
67
|
+
/**
|
|
68
|
+
* One sentence naming why the bundle may be missing, or undefined when
|
|
69
|
+
* there is no record to go on.
|
|
70
|
+
*
|
|
71
|
+
* @param state - From {@link readBuildRunState}
|
|
72
|
+
*/
|
|
73
|
+
export declare function describeBuildRunState(state: BuildRunState): string | undefined;
|
|
74
|
+
export {};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* A record of the mach build in flight, kept in `.fireforge/build-run.json`
|
|
4
|
+
* so any session on the checkout can see why the bundle is missing.
|
|
5
|
+
*
|
|
6
|
+
* An interrupted `fireforge build` (a killed peer session, a closed
|
|
7
|
+
* terminal) can leave `dist/<App>.app/Contents/MacOS/` without its
|
|
8
|
+
* executable. Every later `fireforge test`, in every session, then failed
|
|
9
|
+
* at launch with nothing saying a build had died there, and the sessions
|
|
10
|
+
* that had not run the build had no way to find out.
|
|
11
|
+
*
|
|
12
|
+
* The record is written when a protected mach build starts and removed when
|
|
13
|
+
* it succeeds. A failed build leaves it with `status: "failed"` and the exit
|
|
14
|
+
* code. A build that never reached either end, because its process died,
|
|
15
|
+
* leaves `status: "running"` with a PID that no longer exists, which is how
|
|
16
|
+
* an interrupted run is recognized. The next build that runs to the end
|
|
17
|
+
* replaces whatever was there.
|
|
18
|
+
*
|
|
19
|
+
* Best-effort throughout: a build never fails because its record could not
|
|
20
|
+
* be written, and an unreadable record reads as "no record".
|
|
21
|
+
*/
|
|
22
|
+
import { readFile, rm } from 'node:fs/promises';
|
|
23
|
+
import { join } from 'node:path';
|
|
24
|
+
import { toError } from '../utils/errors.js';
|
|
25
|
+
import { writeFileAtomic } from '../utils/fs.js';
|
|
26
|
+
import { verbose } from '../utils/logger.js';
|
|
27
|
+
import { FIREFORGE_DIR } from './config-paths.js';
|
|
28
|
+
import { isLockOwnerAlive, readProcessStartTick } from './file-lock-owner.js';
|
|
29
|
+
/** File name of the record under `.fireforge/`. */
|
|
30
|
+
const BUILD_RUN_RECORD = 'build-run.json';
|
|
31
|
+
function recordPath(projectRoot) {
|
|
32
|
+
return join(projectRoot, FIREFORGE_DIR, BUILD_RUN_RECORD);
|
|
33
|
+
}
|
|
34
|
+
async function writeRecord(projectRoot, record) {
|
|
35
|
+
try {
|
|
36
|
+
await writeFileAtomic(recordPath(projectRoot), `${JSON.stringify(record, null, 2)}\n`);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
verbose(`Build run record not written: ${toError(error).message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Runs a mach build with its run record maintained around it. The record
|
|
44
|
+
* is removed on a zero exit and kept as `failed` on a non-zero exit or a
|
|
45
|
+
* throw, so only a build whose process died mid-run is left `running`.
|
|
46
|
+
*
|
|
47
|
+
* @param projectRoot - Project root holding `.fireforge/`
|
|
48
|
+
* @param machCommand - The mach command being run
|
|
49
|
+
* @param build - The build itself
|
|
50
|
+
* @returns Whatever the build returned
|
|
51
|
+
*/
|
|
52
|
+
export async function withBuildRunRecord(projectRoot, machCommand, build) {
|
|
53
|
+
const startTick = readProcessStartTick(process.pid);
|
|
54
|
+
const started = {
|
|
55
|
+
status: 'running',
|
|
56
|
+
pid: process.pid,
|
|
57
|
+
...(startTick !== undefined ? { startTick } : {}),
|
|
58
|
+
command: ['fireforge', ...process.argv.slice(2)].join(' '),
|
|
59
|
+
machCommand,
|
|
60
|
+
startedAt: new Date().toISOString(),
|
|
61
|
+
};
|
|
62
|
+
await writeRecord(projectRoot, started);
|
|
63
|
+
let result;
|
|
64
|
+
try {
|
|
65
|
+
result = await build();
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
await writeRecord(projectRoot, {
|
|
69
|
+
...started,
|
|
70
|
+
status: 'failed',
|
|
71
|
+
endedAt: new Date().toISOString(),
|
|
72
|
+
});
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
if (result.exitCode === 0) {
|
|
76
|
+
await rm(recordPath(projectRoot), { force: true }).catch(() => undefined);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
await writeRecord(projectRoot, {
|
|
80
|
+
...started,
|
|
81
|
+
status: 'failed',
|
|
82
|
+
endedAt: new Date().toISOString(),
|
|
83
|
+
exitCode: result.exitCode,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Reads the last build's record and decides whether its process is still
|
|
90
|
+
* running.
|
|
91
|
+
*
|
|
92
|
+
* @param projectRoot - Project root holding `.fireforge/`
|
|
93
|
+
*/
|
|
94
|
+
export async function readBuildRunState(projectRoot) {
|
|
95
|
+
let record;
|
|
96
|
+
try {
|
|
97
|
+
record = JSON.parse(await readFile(recordPath(projectRoot), 'utf8'));
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return { kind: 'none' };
|
|
101
|
+
}
|
|
102
|
+
if (typeof record.pid !== 'number' || typeof record.startedAt !== 'string') {
|
|
103
|
+
return { kind: 'none' };
|
|
104
|
+
}
|
|
105
|
+
if (record.status === 'failed')
|
|
106
|
+
return { kind: 'failed', record };
|
|
107
|
+
return {
|
|
108
|
+
kind: isLockOwnerAlive({ pid: record.pid, startTick: record.startTick })
|
|
109
|
+
? 'running'
|
|
110
|
+
: 'interrupted',
|
|
111
|
+
record,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* One sentence naming why the bundle may be missing, or undefined when
|
|
116
|
+
* there is no record to go on.
|
|
117
|
+
*
|
|
118
|
+
* @param state - From {@link readBuildRunState}
|
|
119
|
+
*/
|
|
120
|
+
export function describeBuildRunState(state) {
|
|
121
|
+
if (state.kind === 'none')
|
|
122
|
+
return undefined;
|
|
123
|
+
const { record } = state;
|
|
124
|
+
const who = `"${record.command}" (${record.machCommand}, PID ${record.pid}, started ${record.startedAt})`;
|
|
125
|
+
switch (state.kind) {
|
|
126
|
+
case 'running':
|
|
127
|
+
return `A build is running right now: ${who}. The bundle is incomplete until it finishes.`;
|
|
128
|
+
case 'interrupted':
|
|
129
|
+
return (`The last build never finished: ${who} is gone without recording an end, so it was ` +
|
|
130
|
+
'interrupted (killed, or its terminal closed) part-way, which leaves the bundle without ' +
|
|
131
|
+
'its executable.');
|
|
132
|
+
case 'failed':
|
|
133
|
+
return (`The last build failed: ${who} ended ${record.endedAt ?? 'at an unrecorded time'}` +
|
|
134
|
+
`${record.exitCode !== undefined ? ` with exit code ${record.exitCode}` : ''}.`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unowned support heads for the named-file checkJs pass of `test --build`.
|
|
3
|
+
*
|
|
4
|
+
* Each named test is checked as its own program: the test plus the
|
|
5
|
+
* patch-owned `head*.js` beside it. A suite moved onto a NEW support head
|
|
6
|
+
* (`head_world_page.js`, loaded with `loadSubScript`) cannot be proven
|
|
7
|
+
* before that head is exported, because the head is in no patch: every kit
|
|
8
|
+
* name reads as "Cannot find name" and the run is refused at preflight,
|
|
9
|
+
* with the generic hint, which offers the shim as a remedy.
|
|
10
|
+
*
|
|
11
|
+
* So an unowned same-directory `head*.js` joins the pass when a named test
|
|
12
|
+
* loads it with `loadSubScript` or a manifest beside it lists it in
|
|
13
|
+
* `support-files`, which are the two ways the harness itself would bring
|
|
14
|
+
* it into the test's scope. The operator is told it is unowned and how to
|
|
15
|
+
* adopt it, as for the `head.js` companions in
|
|
16
|
+
* `patch-lint-unmanaged-companion.ts`.
|
|
17
|
+
*/
|
|
18
|
+
/** A same-directory head no patch owns, and why the pass includes it. */
|
|
19
|
+
export interface UnownedSupportHead {
|
|
20
|
+
/** Engine-relative path of the head. */
|
|
21
|
+
file: string;
|
|
22
|
+
/** What brings it into the test's scope. */
|
|
23
|
+
reason: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Finds the unowned same-directory `head*.js` files the named tests bring
|
|
27
|
+
* into scope.
|
|
28
|
+
*
|
|
29
|
+
* @param engineDir - Absolute engine directory
|
|
30
|
+
* @param selected - Engine-relative named test files
|
|
31
|
+
* @param owned - Every file the pass already includes
|
|
32
|
+
*/
|
|
33
|
+
export declare function findUnownedSupportHeads(engineDir: string, selected: Iterable<string>, owned: ReadonlySet<string>): Promise<UnownedSupportHead[]>;
|
|
34
|
+
/**
|
|
35
|
+
* The notice for an included unowned head: the ownership fact, the
|
|
36
|
+
* adoption command, and an explicit "not the shim", for the same reason
|
|
37
|
+
* `formatUnmanagedCompanionHint` gives.
|
|
38
|
+
*
|
|
39
|
+
* @param head - From {@link findUnownedSupportHeads}
|
|
40
|
+
*/
|
|
41
|
+
export declare function formatUnownedSupportHeadNotice(head: UnownedSupportHead): string;
|