@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
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { CheckConfig, FireForgeConfig } from '../types/config.js';
|
|
2
|
+
/** One planned step. */
|
|
3
|
+
export interface CheckStepPlan {
|
|
4
|
+
name: string;
|
|
5
|
+
kind: 'builtin' | 'extra';
|
|
6
|
+
/** Executable to spawn. */
|
|
7
|
+
command: string;
|
|
8
|
+
/** Its arguments. */
|
|
9
|
+
args: string[];
|
|
10
|
+
/** The command line as the table prints it. */
|
|
11
|
+
display: string;
|
|
12
|
+
cwd: string;
|
|
13
|
+
timeoutMs?: number | undefined;
|
|
14
|
+
/** Steps that must finish before this one starts (ordering only). */
|
|
15
|
+
after: string[];
|
|
16
|
+
/** Set when the step is planned but not run, with the reason. */
|
|
17
|
+
skipReason?: string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Why it is skipped: `config` when `check.skip` names it (a decision),
|
|
20
|
+
* `unavailable` when the project has nothing for it to run (a gap,
|
|
21
|
+
* which `check.failOnSkipped` fails).
|
|
22
|
+
*/
|
|
23
|
+
skipKind?: 'config' | 'unavailable' | undefined;
|
|
24
|
+
/** Extra environment for the child. */
|
|
25
|
+
env?: Record<string, string> | undefined;
|
|
26
|
+
/** Condenses a finished run into the table's detail cell. */
|
|
27
|
+
summarize: (output: {
|
|
28
|
+
stdout: string;
|
|
29
|
+
combined: string;
|
|
30
|
+
exitCode: number;
|
|
31
|
+
}) => string;
|
|
32
|
+
}
|
|
33
|
+
/** The project facts the plan depends on. */
|
|
34
|
+
export interface CheckPlanInput {
|
|
35
|
+
projectRoot: string;
|
|
36
|
+
config: FireForgeConfig;
|
|
37
|
+
furnaceConfigured: boolean;
|
|
38
|
+
/** Resolved `--patches` filenames, forwarded to status, verify and lint. */
|
|
39
|
+
patches?: string[] | undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The generic detail: nothing on success, and on failure the last line
|
|
43
|
+
* the step printed (its refusal, usually), cut to fit a table cell.
|
|
44
|
+
*/
|
|
45
|
+
export declare function lastOutputLine(combined: string, exitCode: number): string;
|
|
46
|
+
/** Status detail from its `--json --summary` document: the failing classes. */
|
|
47
|
+
export declare function summarizeStatus(output: {
|
|
48
|
+
stdout: string;
|
|
49
|
+
combined: string;
|
|
50
|
+
exitCode: number;
|
|
51
|
+
}): string;
|
|
52
|
+
/** Furnace detail from its `--json` document: counts after exceptions. */
|
|
53
|
+
export declare function summarizeFurnace(output: {
|
|
54
|
+
stdout: string;
|
|
55
|
+
combined: string;
|
|
56
|
+
exitCode: number;
|
|
57
|
+
}): string;
|
|
58
|
+
/** Token-coverage detail: the coverage percentage it reports. */
|
|
59
|
+
export declare function summarizeTokenCoverage(output: {
|
|
60
|
+
stdout: string;
|
|
61
|
+
combined: string;
|
|
62
|
+
exitCode: number;
|
|
63
|
+
}): string;
|
|
64
|
+
/** The status step's `--check` flags from `check.status`. */
|
|
65
|
+
export declare function statusPolicyArgs(policy: CheckConfig['status']): string[];
|
|
66
|
+
/**
|
|
67
|
+
* Builds the full plan: the six built-in steps (with the ones this project
|
|
68
|
+
* cannot run marked skipped) followed by `check.extraSteps[]`.
|
|
69
|
+
*
|
|
70
|
+
* `furnace-validate` waits for `typecheck`, the one built-in step that
|
|
71
|
+
* writes: it regenerates a stale Furnace jsconfig, whose drift
|
|
72
|
+
* `furnace validate` reports. Run side by side, the two would race on that
|
|
73
|
+
* file and the verdict would depend on which won.
|
|
74
|
+
*/
|
|
75
|
+
export declare function buildCheckPlan(input: CheckPlanInput): CheckStepPlan[];
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* The step plan of `fireforge check`: which commands run, with which
|
|
4
|
+
* arguments, in which order constraints, and how each one's output is
|
|
5
|
+
* condensed into its table row.
|
|
6
|
+
*
|
|
7
|
+
* Every built-in step is the real command run as a child `fireforge`
|
|
8
|
+
* process, not an in-process call. That is what keeps the gate's output
|
|
9
|
+
* sane (each command owns its stdout, its machine mode and its error
|
|
10
|
+
* boundary) and what avoids double locking: `check` itself takes no lock,
|
|
11
|
+
* so each child takes exactly the locks its command takes standalone.
|
|
12
|
+
* None of the built-in steps takes the engine session lock, which is why
|
|
13
|
+
* they can run side by side.
|
|
14
|
+
*/
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { GeneralError } from '../errors/base.js';
|
|
17
|
+
/**
|
|
18
|
+
* How to re-run this CLI as a child: the same Node, the same loader flags
|
|
19
|
+
* (minus debugger flags, whose port the parent already holds), the same
|
|
20
|
+
* entry script.
|
|
21
|
+
*/
|
|
22
|
+
function fireforgeInvocation() {
|
|
23
|
+
const entry = process.argv[1];
|
|
24
|
+
if (entry === undefined) {
|
|
25
|
+
throw new GeneralError('Cannot resolve the fireforge CLI entry point for check steps.');
|
|
26
|
+
}
|
|
27
|
+
const loaderFlags = process.execArgv.filter((flag) => !flag.startsWith('--inspect'));
|
|
28
|
+
return { command: process.execPath, prefix: [...loaderFlags, entry] };
|
|
29
|
+
}
|
|
30
|
+
/** Strips clack's gutter glyphs so a captured line reads as plain text. */
|
|
31
|
+
function plainLine(line) {
|
|
32
|
+
return line.replace(/^[\s│■●◆◇▲└┌|]+/u, '').trim();
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The generic detail: nothing on success, and on failure the last line
|
|
36
|
+
* the step printed (its refusal, usually), cut to fit a table cell.
|
|
37
|
+
*/
|
|
38
|
+
export function lastOutputLine(combined, exitCode) {
|
|
39
|
+
if (exitCode === 0)
|
|
40
|
+
return '';
|
|
41
|
+
const lines = combined
|
|
42
|
+
.split(/\r?\n/)
|
|
43
|
+
.map(plainLine)
|
|
44
|
+
.filter((line) => line.length > 0);
|
|
45
|
+
const last = lines.at(-1) ?? '';
|
|
46
|
+
return last.length > 120 ? `${last.slice(0, 117)}...` : last;
|
|
47
|
+
}
|
|
48
|
+
function parseJsonDocument(stdout) {
|
|
49
|
+
try {
|
|
50
|
+
const parsed = JSON.parse(stdout);
|
|
51
|
+
return typeof parsed === 'object' && parsed !== null
|
|
52
|
+
? parsed
|
|
53
|
+
: undefined;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Status detail from its `--json --summary` document: the failing classes. */
|
|
60
|
+
export function summarizeStatus(output) {
|
|
61
|
+
const doc = parseJsonDocument(output.stdout);
|
|
62
|
+
if (doc?.summary === undefined)
|
|
63
|
+
return lastOutputLine(output.combined, output.exitCode);
|
|
64
|
+
const offenders = doc.check?.offenders ?? [];
|
|
65
|
+
if (offenders.length > 0) {
|
|
66
|
+
return offenders.map((entry) => `${entry.count} ${entry.classification}`).join(', ');
|
|
67
|
+
}
|
|
68
|
+
return `${doc.summary.total ?? 0} changed file(s), none failing`;
|
|
69
|
+
}
|
|
70
|
+
/** Furnace detail from its `--json` document: counts after exceptions. */
|
|
71
|
+
export function summarizeFurnace(output) {
|
|
72
|
+
const doc = parseJsonDocument(output.stdout);
|
|
73
|
+
if (doc?.summary === undefined)
|
|
74
|
+
return lastOutputLine(output.combined, output.exitCode);
|
|
75
|
+
const { errors, warnings, excepted, staleExceptions } = doc.summary;
|
|
76
|
+
const extra = [
|
|
77
|
+
excepted > 0 ? `${excepted} excepted` : '',
|
|
78
|
+
staleExceptions > 0 ? `${staleExceptions} stale exception(s)` : '',
|
|
79
|
+
].filter((part) => part.length > 0);
|
|
80
|
+
return [`${errors} error(s), ${warnings} warning(s)`, ...extra].join(', ');
|
|
81
|
+
}
|
|
82
|
+
/** Token-coverage detail: the coverage percentage it reports. */
|
|
83
|
+
export function summarizeTokenCoverage(output) {
|
|
84
|
+
const match = /Token coverage: (\d+)%/.exec(output.combined);
|
|
85
|
+
if (match?.[1] !== undefined)
|
|
86
|
+
return `coverage ${match[1]}%`;
|
|
87
|
+
if (output.combined.includes('No modified CSS files'))
|
|
88
|
+
return 'no modified CSS';
|
|
89
|
+
return lastOutputLine(output.combined, output.exitCode);
|
|
90
|
+
}
|
|
91
|
+
function generic(output) {
|
|
92
|
+
return lastOutputLine(output.combined, output.exitCode);
|
|
93
|
+
}
|
|
94
|
+
/** The status step's `--check` flags from `check.status`. */
|
|
95
|
+
export function statusPolicyArgs(policy) {
|
|
96
|
+
const args = [];
|
|
97
|
+
if (policy?.failOn !== undefined)
|
|
98
|
+
args.push('--fail-on', policy.failOn.join(','));
|
|
99
|
+
if (policy?.allow !== undefined)
|
|
100
|
+
args.push('--allow', policy.allow.join(','));
|
|
101
|
+
if (policy?.failOnUnknown === true)
|
|
102
|
+
args.push('--fail-on-unknown');
|
|
103
|
+
return args;
|
|
104
|
+
}
|
|
105
|
+
function builtinStep(invocation, projectRoot, name, args, summarize, extra = {}) {
|
|
106
|
+
return {
|
|
107
|
+
name,
|
|
108
|
+
kind: 'builtin',
|
|
109
|
+
command: invocation.command,
|
|
110
|
+
args: [...invocation.prefix, ...args],
|
|
111
|
+
display: `fireforge ${args.join(' ')}`,
|
|
112
|
+
cwd: projectRoot,
|
|
113
|
+
timeoutMs: extra.timeoutMs,
|
|
114
|
+
after: extra.after ?? [],
|
|
115
|
+
skipReason: extra.skipReason,
|
|
116
|
+
skipKind: extra.skipReason !== undefined ? 'unavailable' : undefined,
|
|
117
|
+
summarize,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/** A built-in step's budget: `check.timeouts.<step>`, else `check.timeout`. */
|
|
121
|
+
function builtinTimeoutMs(check, name) {
|
|
122
|
+
const seconds = check.timeouts?.[name] ?? check.timeout;
|
|
123
|
+
return seconds !== undefined ? seconds * 1000 : undefined;
|
|
124
|
+
}
|
|
125
|
+
/** Runs a project command line through the platform shell. */
|
|
126
|
+
function shellInvocation(commandLine) {
|
|
127
|
+
return process.platform === 'win32'
|
|
128
|
+
? { command: 'cmd.exe', args: ['/d', '/s', '/c', commandLine] }
|
|
129
|
+
: { command: '/bin/sh', args: ['-c', commandLine] };
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Builds the full plan: the six built-in steps (with the ones this project
|
|
133
|
+
* cannot run marked skipped) followed by `check.extraSteps[]`.
|
|
134
|
+
*
|
|
135
|
+
* `furnace-validate` waits for `typecheck`, the one built-in step that
|
|
136
|
+
* writes: it regenerates a stale Furnace jsconfig, whose drift
|
|
137
|
+
* `furnace validate` reports. Run side by side, the two would race on that
|
|
138
|
+
* file and the verdict would depend on which won.
|
|
139
|
+
*/
|
|
140
|
+
export function buildCheckPlan(input) {
|
|
141
|
+
const { projectRoot, config } = input;
|
|
142
|
+
const invocation = fireforgeInvocation();
|
|
143
|
+
const check = config.check ?? {};
|
|
144
|
+
const patchArgs = input.patches !== undefined ? ['--patches', input.patches.join(',')] : [];
|
|
145
|
+
const lintWarnings = check.maxWarnings?.lint !== undefined ? ['--max-warnings', String(check.maxWarnings.lint)] : [];
|
|
146
|
+
const furnaceWarnings = check.maxWarnings?.furnace !== undefined
|
|
147
|
+
? ['--max-warnings', String(check.maxWarnings.furnace)]
|
|
148
|
+
: [];
|
|
149
|
+
const lintReport = check.lintReport !== undefined ? join(projectRoot, check.lintReport) : undefined;
|
|
150
|
+
const lintReportArgs = lintReport !== undefined ? ['--report', lintReport] : [];
|
|
151
|
+
const timeoutMs = (name) => builtinTimeoutMs(check, name);
|
|
152
|
+
const plan = [
|
|
153
|
+
builtinStep(invocation, projectRoot, 'status', ['status', '--json', '--summary', '--check', ...statusPolicyArgs(check.status), ...patchArgs], summarizeStatus, { timeoutMs: timeoutMs('status') }),
|
|
154
|
+
builtinStep(invocation, projectRoot, 'verify', ['verify', ...patchArgs], generic, {
|
|
155
|
+
timeoutMs: timeoutMs('verify'),
|
|
156
|
+
}),
|
|
157
|
+
builtinStep(invocation, projectRoot, 'lint', [
|
|
158
|
+
'lint',
|
|
159
|
+
'--per-patch',
|
|
160
|
+
'--notices',
|
|
161
|
+
'summary',
|
|
162
|
+
...lintWarnings,
|
|
163
|
+
...lintReportArgs,
|
|
164
|
+
...patchArgs,
|
|
165
|
+
], generic, { timeoutMs: timeoutMs('lint') }),
|
|
166
|
+
builtinStep(invocation, projectRoot, 'typecheck', ['typecheck'], generic, {
|
|
167
|
+
skipReason: config.typecheck === undefined ? 'no "typecheck" block in fireforge.json' : undefined,
|
|
168
|
+
timeoutMs: timeoutMs('typecheck'),
|
|
169
|
+
}),
|
|
170
|
+
builtinStep(invocation, projectRoot, 'furnace-validate', ['furnace', 'validate', '--json', ...furnaceWarnings], summarizeFurnace, {
|
|
171
|
+
after: ['typecheck'],
|
|
172
|
+
skipReason: input.furnaceConfigured ? undefined : 'no furnace.json',
|
|
173
|
+
timeoutMs: timeoutMs('furnace-validate'),
|
|
174
|
+
}),
|
|
175
|
+
builtinStep(invocation, projectRoot, 'token-coverage', ['token', 'coverage'], summarizeTokenCoverage, { timeoutMs: timeoutMs('token-coverage') }),
|
|
176
|
+
];
|
|
177
|
+
for (const step of check.extraSteps ?? []) {
|
|
178
|
+
const shell = shellInvocation(step.command);
|
|
179
|
+
plan.push({
|
|
180
|
+
name: step.name,
|
|
181
|
+
kind: 'extra',
|
|
182
|
+
command: shell.command,
|
|
183
|
+
args: shell.args,
|
|
184
|
+
display: step.command,
|
|
185
|
+
cwd: step.cwd !== undefined ? join(projectRoot, step.cwd) : projectRoot,
|
|
186
|
+
timeoutMs: step.timeout !== undefined ? step.timeout * 1000 : undefined,
|
|
187
|
+
after: step.after ?? [],
|
|
188
|
+
env: lintReport !== undefined ? { FIREFORGE_CHECK_LINT_REPORT: lintReport } : undefined,
|
|
189
|
+
summarize: generic,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
for (const name of check.skip ?? []) {
|
|
193
|
+
const step = plan.find((entry) => entry.name === name);
|
|
194
|
+
if (step !== undefined) {
|
|
195
|
+
step.skipReason = 'skipped by check.skip in fireforge.json';
|
|
196
|
+
step.skipKind = 'config';
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return plan;
|
|
200
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import type { CommandContext } from '../types/cli.js';
|
|
3
|
+
import { type CheckStepPlan } from './check-steps.js';
|
|
4
|
+
/** Options Commander forwards from `fireforge check`. */
|
|
5
|
+
export interface CheckCommandOptions {
|
|
6
|
+
json?: boolean;
|
|
7
|
+
/** Judge only these patches in status, verify and lint. */
|
|
8
|
+
patches?: string[];
|
|
9
|
+
/** Comma-separated step names to leave out. */
|
|
10
|
+
skip?: string;
|
|
11
|
+
/** Comma-separated step names to run, leaving out the rest. */
|
|
12
|
+
only?: string;
|
|
13
|
+
/** Raw `--jobs` value. */
|
|
14
|
+
jobs?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Applies `--only` and `--skip` to the plan. A step left out this way is
|
|
18
|
+
* dropped from the table rather than shown as skipped: the caller asked
|
|
19
|
+
* for a narrower gate, not a report of what it chose not to run.
|
|
20
|
+
*/
|
|
21
|
+
export declare function selectCheckSteps(plan: CheckStepPlan[], options: Pick<CheckCommandOptions, 'only' | 'skip'>): CheckStepPlan[];
|
|
22
|
+
/** Parses `--jobs`: a positive integer. Defaults to one slot per CPU. */
|
|
23
|
+
export declare function parseCheckJobs(raw: string | undefined): number;
|
|
24
|
+
/**
|
|
25
|
+
* Runs the composed gate.
|
|
26
|
+
*
|
|
27
|
+
* @param projectRoot - Project root directory
|
|
28
|
+
* @param options - See {@link CheckCommandOptions}
|
|
29
|
+
*/
|
|
30
|
+
export declare function checkCommand(projectRoot: string, options?: CheckCommandOptions): Promise<void>;
|
|
31
|
+
/** Registers the `check` command on the CLI program. */
|
|
32
|
+
export declare function registerCheck(program: Command, { getProjectRoot, withErrorHandling }: CommandContext): void;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* `fireforge check`: the composed static gate. Runs status, verify,
|
|
4
|
+
* per-patch lint, typecheck, Furnace validation and token coverage, plus
|
|
5
|
+
* the project's `check.extraSteps[]`, in parallel, and ends with one
|
|
6
|
+
* `FIREFORGE-VERDICT:` line over a per-step table (or one JSON document
|
|
7
|
+
* under `--json`).
|
|
8
|
+
*
|
|
9
|
+
* Each step is the real command in a child process (see check-steps.ts for
|
|
10
|
+
* why), so a step behaves exactly as it does when run on its own.
|
|
11
|
+
*/
|
|
12
|
+
import { availableParallelism } from 'node:os';
|
|
13
|
+
import { Command } from 'commander';
|
|
14
|
+
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
15
|
+
import { furnaceConfigExists } from '../core/furnace-config.js';
|
|
16
|
+
import { loadPatchesManifest } from '../core/patch-manifest.js';
|
|
17
|
+
import { selectPatchSubset } from '../core/patch-subset.js';
|
|
18
|
+
import { GeneralError, InvalidArgumentError, LockContentionError } from '../errors/base.js';
|
|
19
|
+
import { ConfigError } from '../errors/config.js';
|
|
20
|
+
import { toError } from '../utils/errors.js';
|
|
21
|
+
import { info, intro, isMachineOutputMode, note, outro, setMachineOutputMode, } from '../utils/logger.js';
|
|
22
|
+
import { formatCheckTable, judgeCheck, writeCheckJson, writeCheckVerdictLine, } from './check-report.js';
|
|
23
|
+
import { runCheckSteps } from './check-run.js';
|
|
24
|
+
import { buildCheckPlan } from './check-steps.js';
|
|
25
|
+
import { resolveStatusCheckPolicy } from './status-check.js';
|
|
26
|
+
function parseNameList(raw) {
|
|
27
|
+
if (raw === undefined)
|
|
28
|
+
return undefined;
|
|
29
|
+
return raw
|
|
30
|
+
.split(',')
|
|
31
|
+
.map((name) => name.trim())
|
|
32
|
+
.filter((name) => name.length > 0);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Applies `--only` and `--skip` to the plan. A step left out this way is
|
|
36
|
+
* dropped from the table rather than shown as skipped: the caller asked
|
|
37
|
+
* for a narrower gate, not a report of what it chose not to run.
|
|
38
|
+
*/
|
|
39
|
+
export function selectCheckSteps(plan, options) {
|
|
40
|
+
const known = plan.map((step) => step.name);
|
|
41
|
+
const only = parseNameList(options.only);
|
|
42
|
+
const skip = parseNameList(options.skip);
|
|
43
|
+
for (const [flag, names] of [
|
|
44
|
+
['--only', only],
|
|
45
|
+
['--skip', skip],
|
|
46
|
+
]) {
|
|
47
|
+
const unknown = (names ?? []).filter((name) => !known.includes(name));
|
|
48
|
+
if (unknown.length > 0) {
|
|
49
|
+
throw new InvalidArgumentError(`Unknown ${flag} step "${unknown.join('", "')}". Steps: ${known.join(', ')}.`, flag);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return plan.filter((step) => (only === undefined || only.includes(step.name)) && skip?.includes(step.name) !== true);
|
|
53
|
+
}
|
|
54
|
+
/** Parses `--jobs`: a positive integer. Defaults to one slot per CPU. */
|
|
55
|
+
export function parseCheckJobs(raw) {
|
|
56
|
+
if (raw === undefined)
|
|
57
|
+
return availableParallelism();
|
|
58
|
+
const jobs = Number(raw);
|
|
59
|
+
if (!Number.isInteger(jobs) || jobs < 1) {
|
|
60
|
+
throw new InvalidArgumentError('--jobs must be a positive integer.', '--jobs');
|
|
61
|
+
}
|
|
62
|
+
return jobs;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Refuses a `check.status` policy the status step would refuse, before any
|
|
66
|
+
* step runs, as the config error it is.
|
|
67
|
+
*/
|
|
68
|
+
function assertStatusPolicy(config) {
|
|
69
|
+
const policy = config.check?.status;
|
|
70
|
+
if (policy === undefined)
|
|
71
|
+
return;
|
|
72
|
+
try {
|
|
73
|
+
resolveStatusCheckPolicy({
|
|
74
|
+
check: true,
|
|
75
|
+
...(policy.failOn !== undefined ? { failOn: policy.failOn.join(',') } : {}),
|
|
76
|
+
...(policy.allow !== undefined ? { allow: policy.allow.join(',') } : {}),
|
|
77
|
+
...(policy.failOnUnknown === true ? { failOnUnknown: true } : {}),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
throw new ConfigError(`Config field "check.status" is invalid: ${toError(error).message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Resolves `--patches` once, so a typo fails here rather than in three steps. */
|
|
85
|
+
async function resolvePatchScope(patchesDir, requested) {
|
|
86
|
+
if (requested === undefined || requested.length === 0)
|
|
87
|
+
return undefined;
|
|
88
|
+
const manifest = await loadPatchesManifest(patchesDir);
|
|
89
|
+
if (!manifest || manifest.patches.length === 0) {
|
|
90
|
+
throw new GeneralError('--patches needs a patch queue, but patches.json lists no patches.');
|
|
91
|
+
}
|
|
92
|
+
return selectPatchSubset(manifest, requested, '--patches selects patches (filename, stem, order number, manifest name, or slug).').map((patch) => patch.filename);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Runs the composed gate.
|
|
96
|
+
*
|
|
97
|
+
* @param projectRoot - Project root directory
|
|
98
|
+
* @param options - See {@link CheckCommandOptions}
|
|
99
|
+
*/
|
|
100
|
+
export async function checkCommand(projectRoot, options = {}) {
|
|
101
|
+
const previousMachineOutputMode = isMachineOutputMode();
|
|
102
|
+
// `--json` owns stdout from the first byte. On a throw the mode stays
|
|
103
|
+
// engaged and withErrorHandling resets it after routing the refusal.
|
|
104
|
+
if (options.json === true)
|
|
105
|
+
setMachineOutputMode(true);
|
|
106
|
+
await runCheck(projectRoot, options);
|
|
107
|
+
if (isMachineOutputMode() !== previousMachineOutputMode) {
|
|
108
|
+
setMachineOutputMode(previousMachineOutputMode);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function runCheck(projectRoot, options) {
|
|
112
|
+
intro('FireForge Check');
|
|
113
|
+
const jobs = parseCheckJobs(options.jobs);
|
|
114
|
+
const config = await loadConfig(projectRoot);
|
|
115
|
+
assertStatusPolicy(config);
|
|
116
|
+
const patches = await resolvePatchScope(getProjectPaths(projectRoot).patches, options.patches);
|
|
117
|
+
const plan = selectCheckSteps(buildCheckPlan({
|
|
118
|
+
projectRoot,
|
|
119
|
+
config,
|
|
120
|
+
furnaceConfigured: await furnaceConfigExists(projectRoot),
|
|
121
|
+
patches,
|
|
122
|
+
}), options);
|
|
123
|
+
const runnable = plan.filter((step) => step.skipReason === undefined).length;
|
|
124
|
+
info(`Running ${runnable} step(s), up to ${Math.min(jobs, Math.max(1, runnable))} at once${patches ? `, judging ${patches.join(', ')}` : ''}.`);
|
|
125
|
+
const results = await runCheckSteps(plan, jobs);
|
|
126
|
+
const verdict = judgeCheck(results, { failOnSkipped: config.check?.failOnSkipped });
|
|
127
|
+
if (options.json === true) {
|
|
128
|
+
writeCheckJson(results, verdict);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
note(formatCheckTable(results), 'Steps');
|
|
132
|
+
for (const result of results) {
|
|
133
|
+
if (result.status === 'fail' && result.outputTail) {
|
|
134
|
+
note(result.outputTail, `${result.name}: ${result.command}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
outro(verdict.passed ? 'Check passed' : `Check failed: ${verdict.failed.join(', ')}`);
|
|
138
|
+
writeCheckVerdictLine(verdict);
|
|
139
|
+
}
|
|
140
|
+
if (verdict.reason === 'lock-timeout') {
|
|
141
|
+
throw new LockContentionError(`fireforge check: ${verdict.failed.join(', ')} could not take their locks in time. Re-run when the holder is done.`);
|
|
142
|
+
}
|
|
143
|
+
if (!verdict.passed) {
|
|
144
|
+
throw new GeneralError(`fireforge check failed: ${verdict.failed.join(', ')}. Run a failing step on its own for its full output.`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** Registers the `check` command on the CLI program. */
|
|
148
|
+
export function registerCheck(program, { getProjectRoot, withErrorHandling }) {
|
|
149
|
+
program
|
|
150
|
+
.command('check')
|
|
151
|
+
.description('Composed static gate: status, verify, per-patch lint, typecheck, Furnace validation, token coverage and fireforge.json check.extraSteps in parallel, with one FIREFORGE-VERDICT line')
|
|
152
|
+
.option('--json', 'Write one JSON document (verdict plus a result per step) instead of the table')
|
|
153
|
+
.option('--patches <names...>', 'Judge only these patches in the status, verify and lint steps (filename, stem, order number, manifest name, or slug)')
|
|
154
|
+
.option('--only <steps>', 'Comma-separated steps to run, leaving out the rest')
|
|
155
|
+
.option('--skip <steps>', 'Comma-separated steps to leave out')
|
|
156
|
+
.option('--jobs <n>', 'Steps to run at once (default: one per CPU)')
|
|
157
|
+
.action(withErrorHandling(async (options) => {
|
|
158
|
+
await checkCommand(getProjectRoot(), options);
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
@@ -35,6 +35,7 @@ const STRING_TYPED_KEYS = new Set([
|
|
|
35
35
|
'firefox.candidate',
|
|
36
36
|
'license',
|
|
37
37
|
'wire.subscriptDir',
|
|
38
|
+
'history.path',
|
|
38
39
|
]);
|
|
39
40
|
/**
|
|
40
41
|
* Parses a string value into the appropriate type.
|
|
@@ -131,12 +132,10 @@ export async function configCommand(projectRoot, key, value, options = {}) {
|
|
|
131
132
|
// Readers stay lock-free (see `withConfigFileLock`).
|
|
132
133
|
unchanged = await withConfigFileLock(projectRoot, async () => {
|
|
133
134
|
// Short-circuit when the new value matches the current on-disk
|
|
134
|
-
// value
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
// check uses the raw on-disk document so forced keys round-trip the
|
|
139
|
-
// same as known keys.
|
|
135
|
+
// value, so a harmless re-set neither rewrites the file nor bumps
|
|
136
|
+
// its mtime. The check uses the raw on-disk document so forced keys
|
|
137
|
+
// round-trip the same as known keys. A real change is written by
|
|
138
|
+
// `writeConfigDocument`, which edits only the changed value in place.
|
|
140
139
|
const rawConfig = await loadRawConfigDocument(projectRoot);
|
|
141
140
|
const currentValue = getNestedValue(rawConfig, key);
|
|
142
141
|
if (deepEqual(currentValue, parsedValue)) {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ConflictReport } from '../core/destructive.js';
|
|
2
|
+
import type { PatchLintIssue, PatchLintStagedDeclaration } from '../types/commands/index.js';
|
|
3
|
+
/** A written declaration report and the commands that apply it. */
|
|
4
|
+
export interface DeclarationsReport {
|
|
5
|
+
/** Absolute path of the JSON report. */
|
|
6
|
+
path: string;
|
|
7
|
+
/** Declarations written. */
|
|
8
|
+
count: number;
|
|
9
|
+
/** One `patch staged-dependency <holder> --add --from` call per holder. */
|
|
10
|
+
commands: string[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A declaration as the report records it. `owner` is dropped when it names
|
|
14
|
+
* a patch the refused change would have created, which does not exist yet.
|
|
15
|
+
*/
|
|
16
|
+
export type ReportedDeclaration = Omit<PatchLintStagedDeclaration, 'owner'> & {
|
|
17
|
+
owner?: string;
|
|
18
|
+
};
|
|
19
|
+
/** Rewrites a projected declaration into the names the current queue uses. */
|
|
20
|
+
export type DeclarationRenamer = (declaration: PatchLintStagedDeclaration) => ReportedDeclaration;
|
|
21
|
+
/**
|
|
22
|
+
* Writes every declaration `issues` propose into one report, or returns
|
|
23
|
+
* undefined when they propose none.
|
|
24
|
+
*
|
|
25
|
+
* @param projectRoot - Project root (the default report directory lives under it)
|
|
26
|
+
* @param issues - The refusal's findings
|
|
27
|
+
* @param out - `--declarations-out`, relative to the working directory
|
|
28
|
+
* @param rename - Maps projected patch names back to current ones
|
|
29
|
+
*/
|
|
30
|
+
export declare function writeDeclarationsReport(projectRoot: string, issues: readonly PatchLintIssue[], out?: string, rename?: DeclarationRenamer): Promise<DeclarationsReport | undefined>;
|
|
31
|
+
/** The refusal lines that point at a written report. */
|
|
32
|
+
export declare function formatDeclarationsReport(report: DeclarationsReport): string[];
|
|
33
|
+
/**
|
|
34
|
+
* Appends the report lines to a refusal, writing the report from the
|
|
35
|
+
* refusal's findings. A refusal that proposes no declaration is returned
|
|
36
|
+
* unchanged, and so is no refusal at all.
|
|
37
|
+
*/
|
|
38
|
+
export declare function withDeclarationsReport(conflicts: ConflictReport | null, args: {
|
|
39
|
+
projectRoot: string;
|
|
40
|
+
regressions: readonly PatchLintIssue[];
|
|
41
|
+
out?: string | undefined;
|
|
42
|
+
rename?: DeclarationRenamer;
|
|
43
|
+
}): Promise<ConflictReport | null>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* One `--from`-ready declaration report for a refused queue change.
|
|
4
|
+
*
|
|
5
|
+
* Moving a test out of a patch that owns a shared manifest refused with one
|
|
6
|
+
* `forward-registration` error per manifest line, and each line needed its
|
|
7
|
+
* own `patch staged-dependency --add` call: a slice that added seven suites
|
|
8
|
+
* wrote eighteen declarations by hand. `patch move-files`, `patch split`
|
|
9
|
+
* and `re-export --scan` now write every declaration their refusal proposes
|
|
10
|
+
* into one lint-report-shaped JSON (`queueIssues[].stagedDeclarations`) and
|
|
11
|
+
* name the single `patch staged-dependency <holder> --add --from <file>`
|
|
12
|
+
* call per holding patch that discharges them.
|
|
13
|
+
*/
|
|
14
|
+
import { join, resolve } from 'node:path';
|
|
15
|
+
import { STAGED_DEPENDENCY_REASON_PLACEHOLDER } from '../core/patch-lint-staged-registration.js';
|
|
16
|
+
import { ensureDir, writeJson } from '../utils/fs.js';
|
|
17
|
+
/** Where an unnamed report goes, relative to the project root. */
|
|
18
|
+
const DEFAULT_REPORT_DIR = join('.fireforge', 'declarations');
|
|
19
|
+
/** The checks whose findings carry declarations that discharge them. */
|
|
20
|
+
const DECLARABLE_CHECKS = new Set(['forward-import', 'forward-registration']);
|
|
21
|
+
/**
|
|
22
|
+
* Writes every declaration `issues` propose into one report, or returns
|
|
23
|
+
* undefined when they propose none.
|
|
24
|
+
*
|
|
25
|
+
* @param projectRoot - Project root (the default report directory lives under it)
|
|
26
|
+
* @param issues - The refusal's findings
|
|
27
|
+
* @param out - `--declarations-out`, relative to the working directory
|
|
28
|
+
* @param rename - Maps projected patch names back to current ones
|
|
29
|
+
*/
|
|
30
|
+
export async function writeDeclarationsReport(projectRoot, issues, out, rename = (declaration) => declaration) {
|
|
31
|
+
const queueIssues = issues
|
|
32
|
+
.filter((issue) => DECLARABLE_CHECKS.has(issue.check))
|
|
33
|
+
.map((issue) => ({
|
|
34
|
+
check: issue.check,
|
|
35
|
+
file: issue.file,
|
|
36
|
+
message: issue.message,
|
|
37
|
+
stagedDeclarations: (issue.stagedDeclarations ?? []).map(rename),
|
|
38
|
+
}))
|
|
39
|
+
.filter((issue) => issue.stagedDeclarations.length > 0);
|
|
40
|
+
const declarations = queueIssues.flatMap((issue) => issue.stagedDeclarations);
|
|
41
|
+
if (declarations.length === 0)
|
|
42
|
+
return undefined;
|
|
43
|
+
const path = out !== undefined
|
|
44
|
+
? resolve(out)
|
|
45
|
+
: join(projectRoot, DEFAULT_REPORT_DIR, `${new Date().toISOString().replace(/[:.]/g, '-')}-${process.pid}.json`);
|
|
46
|
+
await ensureDir(resolve(path, '..'));
|
|
47
|
+
await writeJson(path, { queueIssues });
|
|
48
|
+
const holders = [...new Set(declarations.map((declaration) => declaration.patch))].sort();
|
|
49
|
+
return {
|
|
50
|
+
path,
|
|
51
|
+
count: declarations.length,
|
|
52
|
+
commands: holders.map((holder) => `fireforge patch staged-dependency ${holder} --add --from ${path} ` +
|
|
53
|
+
STAGED_DEPENDENCY_REASON_PLACEHOLDER),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** The refusal lines that point at a written report. */
|
|
57
|
+
export function formatDeclarationsReport(report) {
|
|
58
|
+
return [
|
|
59
|
+
`All ${report.count} declaration(s) this refusal proposes are in ${report.path}; ` +
|
|
60
|
+
'declare them in one call per holding patch:',
|
|
61
|
+
...report.commands.map((command) => ` ${command}`),
|
|
62
|
+
];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Appends the report lines to a refusal, writing the report from the
|
|
66
|
+
* refusal's findings. A refusal that proposes no declaration is returned
|
|
67
|
+
* unchanged, and so is no refusal at all.
|
|
68
|
+
*/
|
|
69
|
+
export async function withDeclarationsReport(conflicts, args) {
|
|
70
|
+
if (conflicts === null)
|
|
71
|
+
return null;
|
|
72
|
+
const report = await writeDeclarationsReport(args.projectRoot, args.regressions, args.out, args.rename);
|
|
73
|
+
if (report === undefined)
|
|
74
|
+
return conflicts;
|
|
75
|
+
return { ...conflicts, details: [...conflicts.details, ...formatDeclarationsReport(report)] };
|
|
76
|
+
}
|
|
@@ -8,5 +8,10 @@ export const ENGINE_ESLINT_DOCTOR_CHECK = {
|
|
|
8
8
|
skipIf: (ctx) => !ctx.engineExists,
|
|
9
9
|
run: async (ctx) => (await pathExists(join(ctx.paths.engine, 'node_modules/eslint/bin/eslint.js')))
|
|
10
10
|
? ok('Engine ESLint installed')
|
|
11
|
-
: warning('Engine ESLint installed', 'Engine-local ESLint is missing after source extraction; an engine-eslint ratchet cannot run.',
|
|
11
|
+
: warning('Engine ESLint installed', 'Engine-local ESLint is missing after source extraction; an engine-eslint ratchet cannot run.',
|
|
12
|
+
// `./mach npm ci` installs only the node dependencies the ratchet
|
|
13
|
+
// runs. `./mach eslint --setup` also builds mach's Python `lint`
|
|
14
|
+
// virtualenv, whose pinned requirements can stop resolving on PyPI
|
|
15
|
+
// and fail the whole step with node_modules still missing.
|
|
16
|
+
'Run "cd engine && ./mach npm ci --no-audit --no-fund" after each source refresh, before building or running the gate. It installs engine/node_modules without the Python lint virtualenv "./mach eslint --setup" also builds.'),
|
|
12
17
|
};
|
|
@@ -58,7 +58,10 @@ async function runSourcePinCheck(ctx) {
|
|
|
58
58
|
}
|
|
59
59
|
const downloaded = ctx.state.downloadedVersion;
|
|
60
60
|
const mismatches = [];
|
|
61
|
-
|
|
61
|
+
// Firefox's version.txt never carries the `esr` suffix (an ESR 153.4.0esr
|
|
62
|
+
// tree reads 153.4.0), so the engine comparison drops it from the pin. The
|
|
63
|
+
// last download recorded the full pin, so that comparison keeps it.
|
|
64
|
+
if (actual !== undefined && actual !== pinned && actual !== pinned.replace(/esr$/, '')) {
|
|
62
65
|
mismatches.push(`engine baseline browser/config/version.txt reads ${actual}`);
|
|
63
66
|
}
|
|
64
67
|
if (downloaded !== undefined && downloaded !== pinned) {
|