@hominis/fireforge 0.49.1 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/README.md +1 -1
- package/dist/bin/fireforge.js +6 -0
- package/dist/build-info.json +4 -4
- package/dist/src/commands/build-prelock.d.ts +20 -0
- package/dist/src/commands/build-prelock.js +50 -0
- package/dist/src/commands/build.d.ts +3 -1
- package/dist/src/commands/build.js +13 -6
- package/dist/src/commands/check-report.d.ts +28 -0
- package/dist/src/commands/check-report.js +78 -0
- package/dist/src/commands/check-run.d.ts +28 -0
- package/dist/src/commands/check-run.js +104 -0
- package/dist/src/commands/check-steps.d.ts +67 -0
- package/dist/src/commands/check-steps.js +175 -0
- package/dist/src/commands/check.d.ts +32 -0
- package/dist/src/commands/check.js +160 -0
- package/dist/src/commands/config.js +5 -6
- package/dist/src/commands/doctor-engine-eslint.js +6 -1
- package/dist/src/commands/doctor-source-pin.js +4 -1
- package/dist/src/commands/export-adopt-check.d.ts +38 -0
- package/dist/src/commands/export-adopt-check.js +98 -0
- package/dist/src/commands/export-flow.js +29 -14
- package/dist/src/commands/export-placement-gate.d.ts +6 -2
- package/dist/src/commands/export-shared.d.ts +2 -1
- package/dist/src/commands/export-staged-registration.d.ts +46 -0
- package/dist/src/commands/export-staged-registration.js +91 -0
- package/dist/src/commands/forward-import-remedy.d.ts +32 -0
- package/dist/src/commands/forward-import-remedy.js +63 -0
- package/dist/src/commands/furnace/apply.js +3 -3
- package/dist/src/commands/furnace/chrome-doc-remove.d.ts +2 -1
- package/dist/src/commands/furnace/chrome-doc-remove.js +2 -2
- package/dist/src/commands/furnace/chrome-doc.d.ts +2 -1
- package/dist/src/commands/furnace/chrome-doc.js +2 -2
- package/dist/src/commands/furnace/create-templates.d.ts +8 -3
- package/dist/src/commands/furnace/create-templates.js +8 -23
- package/dist/src/commands/furnace/create.js +2 -2
- package/dist/src/commands/furnace/deploy-named.d.ts +48 -0
- package/dist/src/commands/furnace/deploy-named.js +107 -0
- package/dist/src/commands/furnace/deploy.d.ts +3 -2
- package/dist/src/commands/furnace/deploy.js +50 -86
- package/dist/src/commands/furnace/diff.js +26 -3
- package/dist/src/commands/furnace/index.js +66 -41
- package/dist/src/commands/furnace/init.js +13 -6
- package/dist/src/commands/furnace/override.js +4 -2
- package/dist/src/commands/furnace/preview.js +4 -3
- package/dist/src/commands/furnace/refresh.js +2 -2
- package/dist/src/commands/furnace/remove.js +4 -3
- package/dist/src/commands/furnace/rename-dry-run.d.ts +29 -0
- package/dist/src/commands/furnace/rename-dry-run.js +127 -0
- package/dist/src/commands/furnace/rename-helpers.d.ts +7 -0
- package/dist/src/commands/furnace/rename-helpers.js +15 -1
- package/dist/src/commands/furnace/rename-registrations.d.ts +41 -0
- package/dist/src/commands/furnace/rename-registrations.js +93 -0
- package/dist/src/commands/furnace/rename.d.ts +2 -1
- package/dist/src/commands/furnace/rename.js +28 -66
- package/dist/src/commands/furnace/scan.d.ts +9 -4
- package/dist/src/commands/furnace/scan.js +11 -9
- package/dist/src/commands/furnace/sync.js +3 -2
- package/dist/src/commands/furnace/validate-report.d.ts +45 -0
- package/dist/src/commands/furnace/validate-report.js +91 -0
- package/dist/src/commands/furnace/validate.d.ts +1 -1
- package/dist/src/commands/furnace/validate.js +127 -114
- package/dist/src/commands/furnace/validation-output.js +14 -3
- package/dist/src/commands/lint-per-patch.js +31 -60
- package/dist/src/commands/lint-report.d.ts +8 -4
- package/dist/src/commands/lint-report.js +15 -5
- package/dist/src/commands/lint.js +15 -34
- package/dist/src/commands/manifest.js +2 -0
- package/dist/src/commands/patch/delete-gap-notice.js +3 -1
- package/dist/src/commands/patch/delete.d.ts +1 -1
- package/dist/src/commands/patch/delete.js +13 -98
- package/dist/src/commands/patch/index.d.ts +1 -1
- package/dist/src/commands/patch/index.js +4 -2
- package/dist/src/commands/patch/lint-ignore.d.ts +5 -1
- package/dist/src/commands/patch/lint-ignore.js +65 -28
- package/dist/src/commands/patch/move-files-create.js +26 -17
- package/dist/src/commands/patch/move-files-into.js +5 -3
- package/dist/src/commands/patch/move-files.js +3 -1
- package/dist/src/commands/patch/removal-dependents.d.ts +63 -0
- package/dist/src/commands/patch/removal-dependents.js +133 -0
- package/dist/src/commands/patch/rename.js +7 -0
- package/dist/src/commands/patch/retire.d.ts +34 -0
- package/dist/src/commands/patch/retire.js +128 -0
- package/dist/src/commands/patch/split-plan.d.ts +7 -7
- package/dist/src/commands/patch/split-plan.js +45 -28
- package/dist/src/commands/patch/split-staged.d.ts +108 -0
- package/dist/src/commands/patch/split-staged.js +202 -0
- package/dist/src/commands/patch/split.d.ts +5 -0
- package/dist/src/commands/patch/split.js +51 -15
- package/dist/src/commands/patch/staged-dependency-batch.d.ts +48 -0
- package/dist/src/commands/patch/staged-dependency-batch.js +176 -0
- package/dist/src/commands/patch/staged-dependency-entries.d.ts +36 -0
- package/dist/src/commands/patch/staged-dependency-entries.js +52 -0
- package/dist/src/commands/patch/staged-dependency-from.d.ts +14 -0
- package/dist/src/commands/patch/staged-dependency-from.js +108 -0
- package/dist/src/commands/patch/staged-dependency.js +14 -41
- package/dist/src/commands/re-export-adjacent-registrations.d.ts +20 -0
- package/dist/src/commands/re-export-adjacent-registrations.js +75 -0
- package/dist/src/commands/re-export-adjacent.d.ts +45 -2
- package/dist/src/commands/re-export-adjacent.js +156 -37
- package/dist/src/commands/re-export-drift.d.ts +36 -8
- package/dist/src/commands/re-export-drift.js +73 -49
- package/dist/src/commands/re-export-files.js +11 -8
- package/dist/src/commands/re-export-lint.d.ts +2 -1
- package/dist/src/commands/re-export-lint.js +23 -10
- package/dist/src/commands/re-export-options.js +45 -4
- package/dist/src/commands/re-export-preflight.d.ts +23 -0
- package/dist/src/commands/re-export-preflight.js +160 -0
- package/dist/src/commands/re-export-register.js +42 -17
- package/dist/src/commands/re-export-removed.d.ts +12 -1
- package/dist/src/commands/re-export-removed.js +41 -11
- package/dist/src/commands/re-export-scan-owner.d.ts +44 -0
- package/dist/src/commands/re-export-scan-owner.js +116 -0
- package/dist/src/commands/re-export-scan.d.ts +17 -9
- package/dist/src/commands/re-export-scan.js +56 -23
- package/dist/src/commands/re-export-single.js +22 -6
- package/dist/src/commands/re-export-summary.d.ts +28 -0
- package/dist/src/commands/re-export-summary.js +36 -0
- package/dist/src/commands/re-export.js +37 -24
- package/dist/src/commands/rebase/index.js +17 -17
- package/dist/src/commands/run.js +16 -2
- package/dist/src/commands/status-check.d.ts +55 -16
- package/dist/src/commands/status-check.js +92 -37
- package/dist/src/commands/status-json.d.ts +22 -9
- package/dist/src/commands/status-json.js +47 -32
- package/dist/src/commands/status-output.js +3 -1
- package/dist/src/commands/status-scope.d.ts +37 -0
- package/dist/src/commands/status-scope.js +54 -0
- package/dist/src/commands/status.js +66 -23
- package/dist/src/commands/test-browser-preflight.js +19 -3
- package/dist/src/commands/test-build-furnace.d.ts +21 -0
- package/dist/src/commands/test-build-furnace.js +48 -0
- package/dist/src/commands/test-build-phase.js +47 -41
- package/dist/src/commands/test-checkjs.js +7 -0
- package/dist/src/commands/test-diagnose.js +7 -0
- package/dist/src/commands/test-modes.d.ts +16 -1
- package/dist/src/commands/test-modes.js +24 -1
- package/dist/src/commands/test-multi-verdict.d.ts +61 -0
- package/dist/src/commands/test-multi-verdict.js +111 -0
- package/dist/src/commands/test-orchestrate.d.ts +43 -0
- package/dist/src/commands/test-orchestrate.js +222 -0
- package/dist/src/commands/test-perf-scope.d.ts +29 -0
- package/dist/src/commands/test-perf-scope.js +83 -0
- package/dist/src/commands/test-prelock.d.ts +51 -0
- package/dist/src/commands/test-prelock.js +177 -0
- package/dist/src/commands/test-register.js +54 -10
- package/dist/src/commands/test-run.js +4 -1
- package/dist/src/commands/test-stale-gate.js +57 -27
- package/dist/src/commands/test-verdict.d.ts +10 -1
- package/dist/src/commands/test-verdict.js +28 -1
- package/dist/src/commands/test.d.ts +6 -1
- package/dist/src/commands/test.js +66 -60
- package/dist/src/commands/token-lint.d.ts +14 -0
- package/dist/src/commands/token-lint.js +111 -0
- package/dist/src/commands/token.js +10 -0
- package/dist/src/commands/verify-scope.d.ts +28 -0
- package/dist/src/commands/verify-scope.js +67 -0
- package/dist/src/commands/verify.d.ts +10 -2
- package/dist/src/commands/verify.js +59 -35
- package/dist/src/core/build-baseline-types.d.ts +46 -0
- package/dist/src/core/build-baseline-validate.js +19 -0
- package/dist/src/core/build-baseline.d.ts +5 -1
- package/dist/src/core/build-baseline.js +3 -1
- package/dist/src/core/build-overwrite-guard.d.ts +50 -0
- package/dist/src/core/build-overwrite-guard.js +84 -4
- package/dist/src/core/build-prepare.d.ts +7 -0
- package/dist/src/core/build-prepare.js +14 -8
- package/dist/src/core/build-run-record.d.ts +74 -0
- package/dist/src/core/build-run-record.js +136 -0
- package/dist/src/core/checkjs-support-heads.d.ts +41 -0
- package/dist/src/core/checkjs-support-heads.js +104 -0
- package/dist/src/core/config-paths.d.ts +2 -2
- package/dist/src/core/config-paths.js +8 -0
- package/dist/src/core/config-validate-check.d.ts +8 -0
- package/dist/src/core/config-validate-check.js +143 -0
- package/dist/src/core/config-validate-file-size.d.ts +9 -6
- package/dist/src/core/config-validate-file-size.js +91 -2
- package/dist/src/core/config-validate-history.d.ts +11 -0
- package/dist/src/core/config-validate-history.js +38 -0
- package/dist/src/core/config-validate-patch-policy.js +100 -0
- package/dist/src/core/config-validate-test-toolchains.js +7 -0
- package/dist/src/core/config-validate-token-scales.d.ts +6 -0
- package/dist/src/core/config-validate-token-scales.js +50 -0
- package/dist/src/core/config-validate.js +14 -5
- package/dist/src/core/config.d.ts +5 -0
- package/dist/src/core/config.js +16 -0
- package/dist/src/core/coverage-dispatch.d.ts +24 -0
- package/dist/src/core/coverage-dispatch.js +44 -0
- package/dist/src/core/coverage-extend.d.ts +70 -45
- package/dist/src/core/coverage-extend.js +199 -144
- package/dist/src/core/coverage-records.d.ts +97 -0
- package/dist/src/core/coverage-records.js +350 -0
- package/dist/src/core/destructive.d.ts +7 -4
- package/dist/src/core/destructive.js +10 -10
- package/dist/src/core/engine-session-lock.d.ts +10 -1
- package/dist/src/core/engine-session-lock.js +28 -8
- package/dist/src/core/export-moved-code-shape.d.ts +49 -11
- package/dist/src/core/export-moved-code-shape.js +135 -44
- package/dist/src/core/file-lock-queue.d.ts +67 -0
- package/dist/src/core/file-lock-queue.js +221 -0
- package/dist/src/core/file-lock.d.ts +4 -25
- package/dist/src/core/file-lock.js +105 -138
- package/dist/src/core/furnace-apply-output.d.ts +10 -0
- package/dist/src/core/furnace-apply-output.js +31 -1
- package/dist/src/core/furnace-apply.js +12 -2
- package/dist/src/core/furnace-composed-source.d.ts +76 -0
- package/dist/src/core/furnace-composed-source.js +167 -0
- package/dist/src/core/furnace-config-validate-exceptions.d.ts +9 -0
- package/dist/src/core/furnace-config-validate-exceptions.js +71 -0
- package/dist/src/core/furnace-config-validate.js +8 -0
- package/dist/src/core/furnace-css-fragments.d.ts +10 -0
- package/dist/src/core/furnace-css-fragments.js +26 -0
- package/dist/src/core/furnace-operation.d.ts +3 -3
- package/dist/src/core/furnace-registration-remove.d.ts +22 -1
- package/dist/src/core/furnace-registration-remove.js +53 -17
- package/dist/src/core/furnace-registration-rename.d.ts +26 -0
- package/dist/src/core/furnace-registration-rename.js +77 -0
- package/dist/src/core/furnace-registration.d.ts +14 -4
- package/dist/src/core/furnace-registration.js +44 -10
- package/dist/src/core/furnace-staleness.d.ts +7 -0
- package/dist/src/core/furnace-staleness.js +34 -24
- package/dist/src/core/furnace-validate-accessibility.d.ts +4 -2
- package/dist/src/core/furnace-validate-accessibility.js +10 -1
- package/dist/src/core/furnace-validate-exceptions.d.ts +40 -0
- package/dist/src/core/furnace-validate-exceptions.js +43 -0
- package/dist/src/core/furnace-validate-icon-button.d.ts +7 -0
- package/dist/src/core/furnace-validate-icon-button.js +49 -0
- package/dist/src/core/furnace-validate.js +1 -1
- package/dist/src/core/harness-port-fallback.d.ts +60 -0
- package/dist/src/core/harness-port-fallback.js +96 -0
- package/dist/src/core/history-log-file.d.ts +61 -0
- package/dist/src/core/history-log-file.js +157 -0
- package/dist/src/core/history-log.d.ts +2 -1
- package/dist/src/core/history-log.js +2 -1
- package/dist/src/core/json-preserving-edit.d.ts +22 -0
- package/dist/src/core/json-preserving-edit.js +199 -0
- package/dist/src/core/mach.js +8 -1
- package/dist/src/core/negative-control-diff.d.ts +39 -0
- package/dist/src/core/negative-control-diff.js +136 -0
- package/dist/src/core/negative-control.d.ts +138 -0
- package/dist/src/core/negative-control.js +356 -0
- package/dist/src/core/objdir-install-manifests.d.ts +73 -0
- package/dist/src/core/objdir-install-manifests.js +226 -0
- package/dist/src/core/patch-export-update.d.ts +2 -0
- package/dist/src/core/patch-export-update.js +1 -0
- package/dist/src/core/patch-export.js +3 -4
- package/dist/src/core/patch-lint-checkjs-any-import.d.ts +37 -0
- package/dist/src/core/patch-lint-checkjs-any-import.js +61 -0
- package/dist/src/core/patch-lint-checkjs.js +8 -11
- package/dist/src/core/patch-lint-code-lines.d.ts +59 -0
- package/dist/src/core/patch-lint-code-lines.js +105 -0
- package/dist/src/core/patch-lint-cross.js +12 -4
- package/dist/src/core/patch-lint-css.d.ts +8 -2
- package/dist/src/core/patch-lint-css.js +10 -4
- package/dist/src/core/patch-lint-file-size.d.ts +12 -5
- package/dist/src/core/patch-lint-file-size.js +22 -11
- package/dist/src/core/patch-lint-forward-registration-extended.d.ts +18 -0
- package/dist/src/core/patch-lint-forward-registration-extended.js +35 -10
- package/dist/src/core/patch-lint-forward-registration.js +10 -0
- package/dist/src/core/patch-lint-ignore.d.ts +152 -0
- package/dist/src/core/patch-lint-ignore.js +299 -0
- package/dist/src/core/patch-lint-size-thresholds.d.ts +52 -0
- package/dist/src/core/patch-lint-size-thresholds.js +110 -0
- package/dist/src/core/patch-lint-size.d.ts +117 -0
- package/dist/src/core/patch-lint-size.js +323 -0
- package/dist/src/core/patch-lint-staged-registration.d.ts +18 -1
- package/dist/src/core/patch-lint-staged-registration.js +20 -0
- package/dist/src/core/patch-lint-token-scale.d.ts +39 -0
- package/dist/src/core/patch-lint-token-scale.js +109 -0
- package/dist/src/core/patch-lint.d.ts +11 -83
- package/dist/src/core/patch-lint.js +64 -286
- package/dist/src/core/patch-lock.js +7 -6
- package/dist/src/core/patch-manifest-consistency.js +7 -6
- package/dist/src/core/patch-manifest-io.d.ts +6 -3
- package/dist/src/core/patch-manifest-io.js +19 -8
- package/dist/src/core/patch-manifest-validate.js +34 -2
- package/dist/src/core/patch-order-legal.d.ts +25 -0
- package/dist/src/core/patch-order-legal.js +83 -0
- package/dist/src/core/patch-policy-content.d.ts +40 -0
- package/dist/src/core/patch-policy-content.js +216 -0
- package/dist/src/core/patch-policy.d.ts +16 -3
- package/dist/src/core/patch-policy.js +16 -9
- package/dist/src/core/patch-retired.d.ts +35 -0
- package/dist/src/core/patch-retired.js +54 -0
- package/dist/src/core/patch-subset.d.ts +16 -0
- package/dist/src/core/patch-subset.js +66 -0
- package/dist/src/core/preflight-wait.d.ts +20 -0
- package/dist/src/core/preflight-wait.js +38 -0
- package/dist/src/core/rebase-from-version.d.ts +46 -0
- package/dist/src/core/rebase-from-version.js +71 -0
- package/dist/src/core/run-log.d.ts +20 -0
- package/dist/src/core/run-log.js +89 -3
- package/dist/src/core/run-profile.d.ts +7 -0
- package/dist/src/core/run-profile.js +7 -1
- package/dist/src/core/smoke-patterns.d.ts +5 -0
- package/dist/src/core/smoke-patterns.js +13 -0
- package/dist/src/core/test-harness-crash.d.ts +18 -1
- package/dist/src/core/test-harness-crash.js +6 -4
- package/dist/src/core/test-harness-signature.d.ts +17 -0
- package/dist/src/core/test-harness-signature.js +4 -0
- package/dist/src/core/test-run-completeness.d.ts +57 -0
- package/dist/src/core/test-run-completeness.js +144 -0
- package/dist/src/core/test-stale-check.d.ts +27 -1
- package/dist/src/core/test-stale-check.js +39 -49
- package/dist/src/core/test-stale-exempt.d.ts +40 -0
- package/dist/src/core/test-stale-exempt.js +70 -0
- package/dist/src/core/token-lint.d.ts +50 -0
- package/dist/src/core/token-lint.js +126 -0
- package/dist/src/core/tree-guard.js +3 -0
- package/dist/src/core/typecheck-shim.d.ts +1 -1
- package/dist/src/core/typecheck-shim.js +90 -2
- package/dist/src/index.d.ts +8 -3
- package/dist/src/index.js +3 -1
- package/dist/src/types/commands/index.d.ts +2 -2
- package/dist/src/types/commands/options.d.ts +148 -21
- package/dist/src/types/commands/patches.d.ts +75 -1
- package/dist/src/types/config.d.ts +192 -0
- package/dist/src/types/config.js +9 -0
- package/dist/src/types/furnace.d.ts +43 -1
- package/dist/src/types/furnace.js +7 -0
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/utils/options.d.ts +34 -6
- package/dist/src/utils/options.js +86 -5
- package/dist/src/utils/path-glob.d.ts +15 -0
- package/dist/src/utils/path-glob.js +47 -0
- package/dist/src/utils/regex.d.ts +15 -3
- package/dist/src/utils/regex.js +37 -16
- package/docs/configuration.md +282 -9
- package/docs/exit-codes.md +18 -1
- package/docs/furnace.md +97 -3
- package/docs/lifecycle-invariants.md +52 -19
- package/docs/machine-output.md +74 -12
- package/docs/patch-workflow.md +297 -22
- package/docs/run-logs.md +20 -1
- package/docs/testing.md +276 -20
- package/package.json +2 -2
|
@@ -8,17 +8,17 @@ import { collectFurnaceManagedPrefixes } from '../core/furnace-config.js';
|
|
|
8
8
|
import { getHead, getStatusWithCodes, isGitRepository, isMissingHeadError } from '../core/git.js';
|
|
9
9
|
import { listUntrackedFilesInDirs, resolveMaxUntrackedFilesPerDir } from '../core/git-status.js';
|
|
10
10
|
import { renderOwnershipTable } from '../core/ownership-table.js';
|
|
11
|
-
import { loadPatchesManifest } from '../core/patch-manifest.js';
|
|
12
11
|
import { readProcessCpuSeconds } from '../core/process-cpu-time.js';
|
|
13
12
|
import { classifyFiles } from '../core/status-classify.js';
|
|
14
13
|
import { GeneralError, InvalidArgumentError } from '../errors/base.js';
|
|
15
14
|
import { FIREFORGE_TMP_PATH_PATTERN, pathExists } from '../utils/fs.js';
|
|
16
15
|
import { info, intro, isMachineOutputMode, outro, setMachineOutputMode, warn, } from '../utils/logger.js';
|
|
17
16
|
import { emitMachineError } from '../utils/machine-output.js';
|
|
18
|
-
import { resolveStatusCheckPolicy, runStatusCheck, } from './status-check.js';
|
|
17
|
+
import { policyNeedsOwnership, resolveStatusCheckPolicy, runStatusCheck, } from './status-check.js';
|
|
19
18
|
import { renderJsonStatus, renderJsonSummaryStatus } from './status-json.js';
|
|
20
19
|
import { renderDefaultStatus, renderTestCoverageStatus, renderUnmanagedOnly, } from './status-output.js';
|
|
21
|
-
import { buildOwnershipJsonBlock,
|
|
20
|
+
import { buildOwnershipJsonBlock, summarizeOwnership } from './status-ownership.js';
|
|
21
|
+
import { collectScopedOwnershipRows, ownershipConflictPaths, resolveStatusScope, scopeClassified, } from './status-scope.js';
|
|
22
22
|
/**
|
|
23
23
|
* Renders raw worktree status as machine-parseable porcelain-style output.
|
|
24
24
|
* Each line is: STATUS<tab>FILE
|
|
@@ -121,26 +121,47 @@ async function scanEngineStatusFiles(engineDir, requireGitRepository) {
|
|
|
121
121
|
* policy, which stays the only exit driver on this path.
|
|
122
122
|
*
|
|
123
123
|
* `--include-ownership` is a modifier rather than a mode: it appends an
|
|
124
|
-
* ownership block without touching exit semantics
|
|
125
|
-
*
|
|
124
|
+
* ownership block without touching exit semantics. Ownership conflicts
|
|
125
|
+
* fail this path only when the policy names `ownership-conflict` (or
|
|
126
|
+
* leaves it unnamed under `--fail-on-unknown`).
|
|
126
127
|
*
|
|
127
|
-
* @param classified - Classified worktree entries
|
|
128
|
+
* @param classified - Classified worktree entries, unscoped
|
|
128
129
|
* @param files - The same entries pre-classification, for ownership rows
|
|
129
130
|
* @param paths - Resolved project paths
|
|
130
131
|
* @param options - Status display options
|
|
131
132
|
* @param checkPolicy - Resolved `--check`/`--fail-on` policy
|
|
133
|
+
* @param scope - The `--patches` scope, when given
|
|
132
134
|
*/
|
|
133
|
-
async function renderJsonMode(classified, files, paths, options, checkPolicy) {
|
|
134
|
-
const
|
|
135
|
-
?
|
|
136
|
-
:
|
|
135
|
+
async function renderJsonMode(classified, files, paths, options, checkPolicy, scope) {
|
|
136
|
+
const rows = options.includeOwnership === true || policyNeedsOwnership(checkPolicy)
|
|
137
|
+
? await collectScopedOwnershipRows(paths.patches, files, classified, scope)
|
|
138
|
+
: [];
|
|
139
|
+
const context = {
|
|
140
|
+
policy: checkPolicy,
|
|
141
|
+
ownershipConflicts: ownershipConflictPaths(rows),
|
|
142
|
+
ownership: options.includeOwnership === true ? buildOwnershipJsonBlock(rows) : undefined,
|
|
143
|
+
scope,
|
|
144
|
+
};
|
|
145
|
+
const scoped = scopeClassified(classified, scope);
|
|
137
146
|
if (options.summary === true) {
|
|
138
|
-
renderJsonSummaryStatus(
|
|
147
|
+
renderJsonSummaryStatus(scoped, context);
|
|
139
148
|
}
|
|
140
149
|
else {
|
|
141
|
-
renderJsonStatus(
|
|
150
|
+
renderJsonStatus(scoped, context);
|
|
142
151
|
}
|
|
143
|
-
runStatusCheck(
|
|
152
|
+
runStatusCheck(scoped, checkPolicy, context.ownershipConflicts);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Applies the `--check` policy to the human default view. Builds the
|
|
156
|
+
* ownership rows only when the policy can fail on `ownership-conflict`,
|
|
157
|
+
* which it can on a clean tree too (two patches claiming one path need no
|
|
158
|
+
* worktree change).
|
|
159
|
+
*/
|
|
160
|
+
async function runHumanStatusCheck(classified, files, paths, checkPolicy, scope) {
|
|
161
|
+
const conflicts = policyNeedsOwnership(checkPolicy)
|
|
162
|
+
? ownershipConflictPaths(await collectScopedOwnershipRows(paths.patches, files, classified, scope))
|
|
163
|
+
: [];
|
|
164
|
+
runStatusCheck(scopeClassified(classified, scope), checkPolicy, conflicts);
|
|
144
165
|
}
|
|
145
166
|
/**
|
|
146
167
|
* Detects the "unborn HEAD" aftermath of an interrupted `fireforge download`:
|
|
@@ -200,8 +221,12 @@ export async function statusCommand(projectRoot, options = {}) {
|
|
|
200
221
|
setMachineOutputMode(previousMachineOutputMode);
|
|
201
222
|
}
|
|
202
223
|
}
|
|
203
|
-
/**
|
|
204
|
-
|
|
224
|
+
/**
|
|
225
|
+
* Refuses flag combinations no view can honour, before any output: one
|
|
226
|
+
* mode at most, and the JSON-only and scope modifiers only where they mean
|
|
227
|
+
* something.
|
|
228
|
+
*/
|
|
229
|
+
function assertStatusFlagCombination(options) {
|
|
205
230
|
const modeCount = [
|
|
206
231
|
options.raw,
|
|
207
232
|
options.unmanaged,
|
|
@@ -224,6 +249,18 @@ async function runStatusCommandBody(projectRoot, options) {
|
|
|
224
249
|
if (options.includeOwnership === true && options.json !== true) {
|
|
225
250
|
throw new InvalidArgumentError('--include-ownership requires --json. Use --ownership for the human table.', '--include-ownership');
|
|
226
251
|
}
|
|
252
|
+
// --patches scopes the classifying views and the ownership table only.
|
|
253
|
+
if (options.patches !== undefined &&
|
|
254
|
+
(options.raw === true ||
|
|
255
|
+
options.unmanaged === true ||
|
|
256
|
+
options.testCoverage === true ||
|
|
257
|
+
options.lock === true)) {
|
|
258
|
+
throw new InvalidArgumentError('--patches cannot be combined with --raw, --unmanaged, --test-coverage, or --lock.', '--patches');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/** The status body proper. The machine-mode lifecycle lives in {@link statusCommand}. */
|
|
262
|
+
async function runStatusCommandBody(projectRoot, options) {
|
|
263
|
+
assertStatusFlagCombination(options);
|
|
227
264
|
// --check / --fail-on enforcement policy. Applies only
|
|
228
265
|
// where classification runs (the default view and --json).
|
|
229
266
|
const checkPolicy = resolveStatusCheckPolicy(options);
|
|
@@ -253,6 +290,7 @@ async function runStatusCommandBody(projectRoot, options) {
|
|
|
253
290
|
}
|
|
254
291
|
const paths = getProjectPaths(projectRoot);
|
|
255
292
|
const config = await loadConfig(projectRoot);
|
|
293
|
+
const scope = await resolveStatusScope(paths.patches, options.patches);
|
|
256
294
|
// Shared envelope: see src/utils/machine-output.ts and
|
|
257
295
|
// docs/machine-output.md. `status` was the only command that had one.
|
|
258
296
|
// Ownership mode is a flat file→patch table. Sources are the manifest's
|
|
@@ -265,13 +303,12 @@ async function runStatusCommandBody(projectRoot, options) {
|
|
|
265
303
|
// Rung 1 only. See the note below on why this branch does not take the
|
|
266
304
|
// full assertEngineGitReady ladder.
|
|
267
305
|
await assertEngineExists(paths.engine);
|
|
268
|
-
const manifest = await loadPatchesManifest(paths.patches);
|
|
269
306
|
// The same scan and the same classification pass as every other mode.
|
|
270
307
|
// This branch keeps its historical guard shape on purpose:
|
|
271
308
|
// no baseline-commit assertion, and a non-git engine degrades to an
|
|
272
309
|
// empty table rather than the recovery banner.
|
|
273
310
|
const rawFilesOwnership = await scanEngineStatusFiles(paths.engine, false);
|
|
274
|
-
const rows = await
|
|
311
|
+
const rows = await collectScopedOwnershipRows(paths.patches, rawFilesOwnership, await classifyStatusFiles(rawFilesOwnership, paths, projectRoot, config.binaryName), scope);
|
|
275
312
|
renderOwnershipTable(rows);
|
|
276
313
|
const { managed, unmanaged, conflicts } = summarizeOwnership(rows);
|
|
277
314
|
const parts = [`${managed} managed`];
|
|
@@ -313,7 +350,7 @@ async function runStatusCommandBody(projectRoot, options) {
|
|
|
313
350
|
// `[]` here and return before the human fallback.
|
|
314
351
|
if (options.json) {
|
|
315
352
|
const classified = await classifyStatusFiles(files, paths, projectRoot, config.binaryName);
|
|
316
|
-
await renderJsonMode(classified, files, paths, options, checkPolicy);
|
|
353
|
+
await renderJsonMode(classified, files, paths, options, checkPolicy, scope);
|
|
317
354
|
return;
|
|
318
355
|
}
|
|
319
356
|
// `--raw` consumers parse the native `git status --porcelain` output
|
|
@@ -326,6 +363,7 @@ async function runStatusCommandBody(projectRoot, options) {
|
|
|
326
363
|
if (files.length === 0) {
|
|
327
364
|
info('No modified files');
|
|
328
365
|
outro('Working tree clean');
|
|
366
|
+
await runHumanStatusCheck([], files, paths, checkPolicy, scope);
|
|
329
367
|
return;
|
|
330
368
|
}
|
|
331
369
|
// Raw mode: existing behavior
|
|
@@ -333,8 +371,10 @@ async function runStatusCommandBody(projectRoot, options) {
|
|
|
333
371
|
renderRawStatus(files);
|
|
334
372
|
return;
|
|
335
373
|
}
|
|
336
|
-
// Patch-aware classification
|
|
337
|
-
|
|
374
|
+
// Patch-aware classification. The full scan is classified (claims need
|
|
375
|
+
// every file) and the buckets show only what the scope covers.
|
|
376
|
+
const allClassified = await classifyStatusFiles(files, paths, projectRoot, config.binaryName);
|
|
377
|
+
const classified = scopeClassified(allClassified, scope);
|
|
338
378
|
const buckets = {
|
|
339
379
|
conflict: classified.filter((f) => f.classification === 'conflict'),
|
|
340
380
|
unmanaged: classified.filter((f) => f.classification === 'unmanaged'),
|
|
@@ -349,8 +389,8 @@ async function runStatusCommandBody(projectRoot, options) {
|
|
|
349
389
|
await renderUnmanagedOnly(buckets.unmanaged, files.length, projectRoot, config.binaryName);
|
|
350
390
|
return;
|
|
351
391
|
}
|
|
352
|
-
await renderDefaultStatus(files.length, buckets, projectRoot, config.binaryName);
|
|
353
|
-
|
|
392
|
+
await renderDefaultStatus(scope ? classified.length : files.length, buckets, projectRoot, config.binaryName);
|
|
393
|
+
await runHumanStatusCheck(allClassified, files, paths, checkPolicy, scope);
|
|
354
394
|
}
|
|
355
395
|
/** Registers the status command on the CLI program. */
|
|
356
396
|
export function registerStatus(program, { getProjectRoot, withErrorHandling }) {
|
|
@@ -366,7 +406,10 @@ export function registerStatus(program, { getProjectRoot, withErrorHandling }) {
|
|
|
366
406
|
.option('--summary', 'With --json: emit only summary counts (plus offending files when --check/--fail-on is active), omitting the per-file files[] payload')
|
|
367
407
|
.option('--include-ownership', 'With --json (composes with --summary): append an ownership block (path->owning-patch rows plus managed/unmanaged/conflict counts) to the payload, so one scan serves the classification, ownership, and check views. Does not change exit semantics.')
|
|
368
408
|
.option('--check', 'Exit non-zero when any unmanaged, patch-owned-drift, or conflict file exists (composes with --json; combine with --fail-on for finer policy)')
|
|
369
|
-
.option('--fail-on <classifications>', 'Comma-separated classification list that fails --check, replacing the default set (implies --check). Valid: patch-backed, patch-owned-drift, unmanaged, branding, furnace, conflict, binary-unsupported')
|
|
409
|
+
.option('--fail-on <classifications>', 'Comma-separated classification list that fails --check, replacing the default set (implies --check). Valid: patch-backed, patch-owned-drift, unmanaged, branding, furnace, conflict, binary-unsupported, ownership-conflict (a path claimed by more than one patch)')
|
|
410
|
+
.option('--allow <classifications>', 'Comma-separated classification list that never fails --check, removed from the fail set (implies --check; same values as --fail-on)')
|
|
411
|
+
.option('--fail-on-unknown', 'With --allow: also fail on every present classification that neither --fail-on nor --allow names, so a classification added in a later FireForge fails closed (implies --check)')
|
|
412
|
+
.option('--patches <names...>', 'Judge only the named patches: keeps the files each owns or claims and the ownership rows naming it (filename, stem, order number, manifest name, or slug; repeat or comma-separate). Unmanaged files belong to no patch and drop out. Default view, --json and --ownership only')
|
|
370
413
|
.action(withErrorHandling(async (options) => {
|
|
371
414
|
await statusCommand(getProjectRoot(), options);
|
|
372
415
|
}));
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { reportOrphanedHarnessProcesses } from '../core/harness-orphans.js';
|
|
4
|
-
import {
|
|
4
|
+
import { deferBusyPortToHarness, resolveAutoPorts } from '../core/harness-port-fallback.js';
|
|
5
|
+
import { assertMarionettePortAvailable, DEFAULT_MARIONETTE_PORT, ensureLaunchableBrowserNotRunning, ensureMarionettePortAvailable, extractForwardedMarionettePort, forwardedMachArgsIncludeMarionetteClient, shouldAutoForwardMarionettePortToMach, } from '../core/marionette-port.js';
|
|
5
6
|
import { formatMarionettePreflightLine, reportMarionettePreflight, runMarionettePreflight, } from '../core/marionette-preflight.js';
|
|
6
|
-
import { ensureMochitestServerPortAvailable } from '../core/mochitest-server-port.js';
|
|
7
|
+
import { DEFAULT_MOCHITEST_SERVER_PORT, ensureMochitestServerPortAvailable, } from '../core/mochitest-server-port.js';
|
|
7
8
|
import { waitForPreflight } from '../core/preflight-wait.js';
|
|
8
9
|
import { GeneralError } from '../errors/base.js';
|
|
9
10
|
import { info, outro, verbose } from '../utils/logger.js';
|
|
@@ -153,7 +154,15 @@ export async function ensureTestBrowserEnvironment(engineDir, launchablePath, xp
|
|
|
153
154
|
// browser connect to a server that cannot serve this run's manifest,
|
|
154
155
|
// which surfaces as a 370s "Ran 0 checks" stall naming nothing. xpcshell
|
|
155
156
|
// does not use the httpd, so an xpcshell-only run is never blocked by it.
|
|
156
|
-
|
|
157
|
+
// Auto ports (opt-in) step aside for a peer's listener and let the
|
|
158
|
+
// harness's own busy-port fallback move this run; see
|
|
159
|
+
// src/core/harness-port-fallback.ts.
|
|
160
|
+
const autoPorts = xpcshellOnly
|
|
161
|
+
? undefined
|
|
162
|
+
: await resolveAutoPorts(engineDir, options, projectConfig);
|
|
163
|
+
if (!xpcshellOnly &&
|
|
164
|
+
!(autoPorts?.serverPorts === true &&
|
|
165
|
+
(await deferBusyPortToHarness(DEFAULT_MOCHITEST_SERVER_PORT, 'Mochitest server', engineDir)))) {
|
|
157
166
|
await waitForPreflight(() => ensureMochitestServerPortAvailable(undefined, {
|
|
158
167
|
engineDir,
|
|
159
168
|
killStaleServer: options.killStaleMarionette === true,
|
|
@@ -173,6 +182,13 @@ export async function ensureTestBrowserEnvironment(engineDir, launchablePath, xp
|
|
|
173
182
|
? extractForwardedMarionettePort(options.machArg)
|
|
174
183
|
: undefined;
|
|
175
184
|
const effectivePort = options.marionettePort ?? forwardedPort;
|
|
185
|
+
// Never under --doctor: its handshake launches a browser on this port
|
|
186
|
+
// itself and would talk to the peer's browser instead.
|
|
187
|
+
if (autoPorts?.marionettePort === true &&
|
|
188
|
+
options.doctor !== true &&
|
|
189
|
+
(await deferBusyPortToHarness(effectivePort ?? DEFAULT_MARIONETTE_PORT, 'Marionette', engineDir))) {
|
|
190
|
+
return { forwardedPort, effectivePort };
|
|
191
|
+
}
|
|
176
192
|
await waitForPreflight(() => ensureTestMarionettePortAvailable(effectivePort, projectConfig.binaryName, options, {
|
|
177
193
|
xpcshellOnly,
|
|
178
194
|
doctor: options.doctor === true,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a pre-test build did with Furnace components. `prepareBuildEnvironment`
|
|
3
|
+
* runs `furnace apply` over the whole `components/` tree, so a
|
|
4
|
+
* `fireforge test --build` meant to capture the committed behaviour could
|
|
5
|
+
* silently ship component edits nobody had deployed yet (another session's
|
|
6
|
+
* included), and nothing in the test output said so. The applied names now
|
|
7
|
+
* ride the verdict line, and `--no-furnace-apply` packages `engine/` as it
|
|
8
|
+
* stands, naming the undeployed edits it left out.
|
|
9
|
+
*/
|
|
10
|
+
import type { BuildPreparation } from '../core/build-prepare.js';
|
|
11
|
+
/**
|
|
12
|
+
* Reports the pre-test build's Furnace step: the components it deployed
|
|
13
|
+
* (notice plus `furnace-applied=<names>` on the verdict line), or, when the
|
|
14
|
+
* apply was skipped, the components whose undeployed edits this build
|
|
15
|
+
* leaves out (`furnace-apply=skipped`).
|
|
16
|
+
*
|
|
17
|
+
* @param projectRoot - Root directory of the project
|
|
18
|
+
* @param preparation - Result of `prepareBuildEnvironment`
|
|
19
|
+
* @param skipped - True under `--no-furnace-apply`
|
|
20
|
+
*/
|
|
21
|
+
export declare function reportPreTestFurnace(projectRoot: string, preparation: BuildPreparation, skipped: boolean): Promise<void>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* What a pre-test build did with Furnace components. `prepareBuildEnvironment`
|
|
4
|
+
* runs `furnace apply` over the whole `components/` tree, so a
|
|
5
|
+
* `fireforge test --build` meant to capture the committed behaviour could
|
|
6
|
+
* silently ship component edits nobody had deployed yet (another session's
|
|
7
|
+
* included), and nothing in the test output said so. The applied names now
|
|
8
|
+
* ride the verdict line, and `--no-furnace-apply` packages `engine/` as it
|
|
9
|
+
* stands, naming the undeployed edits it left out.
|
|
10
|
+
*/
|
|
11
|
+
import { listStaleFurnaceComponents } from '../core/furnace-staleness.js';
|
|
12
|
+
import { toError } from '../utils/errors.js';
|
|
13
|
+
import { notice, verbose } from '../utils/logger.js';
|
|
14
|
+
import { setVerdictRunAttribute } from './test-verdict.js';
|
|
15
|
+
/**
|
|
16
|
+
* Reports the pre-test build's Furnace step: the components it deployed
|
|
17
|
+
* (notice plus `furnace-applied=<names>` on the verdict line), or, when the
|
|
18
|
+
* apply was skipped, the components whose undeployed edits this build
|
|
19
|
+
* leaves out (`furnace-apply=skipped`).
|
|
20
|
+
*
|
|
21
|
+
* @param projectRoot - Root directory of the project
|
|
22
|
+
* @param preparation - Result of `prepareBuildEnvironment`
|
|
23
|
+
* @param skipped - True under `--no-furnace-apply`
|
|
24
|
+
*/
|
|
25
|
+
export async function reportPreTestFurnace(projectRoot, preparation, skipped) {
|
|
26
|
+
if (skipped) {
|
|
27
|
+
setVerdictRunAttribute('furnace-apply', 'skipped');
|
|
28
|
+
let pending = [];
|
|
29
|
+
try {
|
|
30
|
+
pending = await listStaleFurnaceComponents(projectRoot);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
verbose(`Furnace pending-edit probe failed: ${toError(error).message}`);
|
|
34
|
+
}
|
|
35
|
+
notice(pending.length > 0
|
|
36
|
+
? `Furnace apply skipped (--no-furnace-apply): engine/ is packaged as it stands. ${pending.length} component(s) with undeployed components/ edits are NOT in this build: ${pending.join(', ')}.`
|
|
37
|
+
: 'Furnace apply skipped (--no-furnace-apply): engine/ is packaged as it stands.');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const applied = preparation.furnaceAppliedNames ?? [];
|
|
41
|
+
if (applied.length === 0)
|
|
42
|
+
return;
|
|
43
|
+
setVerdictRunAttribute('furnace-applied', applied.join(','));
|
|
44
|
+
notice(`This pre-test build deployed ${applied.length} Furnace component(s) whose components/ source ` +
|
|
45
|
+
`changed since their last deploy: ${applied.join(', ')}. Those edits (another session's ` +
|
|
46
|
+
'undeployed edits included) are in the runtime under test. Pass --no-furnace-apply to ' +
|
|
47
|
+
'package engine/ as it stands, for example for a before-capture.');
|
|
48
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { readBuildBaseline, writeBuildBaseline } from '../core/build-baseline.js';
|
|
8
8
|
import { prepareBuildEnvironment } from '../core/build-prepare.js';
|
|
9
|
-
import {
|
|
9
|
+
import { applyMozconfigAnchor, formatCoverageRetentionNotice, formatExtendCoverageRefusal, planCoverageRetention, recordScopedCoverage, } from '../core/coverage-extend.js';
|
|
10
10
|
import { hasBuildArtifacts } from '../core/mach.js';
|
|
11
11
|
import { runProtectedMachBuild, withBuildLock } from '../core/mach.js';
|
|
12
12
|
import { assertHealthyPartialConfig, pruneDanglingTestLinks } from '../core/objdir-maintenance.js';
|
|
@@ -15,7 +15,7 @@ import { GeneralError, InvalidArgumentError } from '../errors/base.js';
|
|
|
15
15
|
import { BuildError } from '../errors/build.js';
|
|
16
16
|
import { toError } from '../utils/errors.js';
|
|
17
17
|
import { info, notice, spinner, verbose } from '../utils/logger.js';
|
|
18
|
-
import {
|
|
18
|
+
import { reportPreTestFurnace } from './test-build-furnace.js';
|
|
19
19
|
import { reportBuildOnlyCompletion } from './test-modes.js';
|
|
20
20
|
import { enforceStaleBuildGate, enforceStaticComponentsGate } from './test-stale-gate.js';
|
|
21
21
|
import { emitPassVerdict } from './test-verdict.js';
|
|
@@ -26,7 +26,7 @@ import { emitPassVerdict } from './test-verdict.js';
|
|
|
26
26
|
* @param options - See {@link RunPreTestBuildOptions}
|
|
27
27
|
*/
|
|
28
28
|
async function runPreTestBuild(options) {
|
|
29
|
-
const { projectRoot, paths, projectConfig, harnessRetries, testPackagingCoverage, refuseUnexportedDrift, extend, } = options;
|
|
29
|
+
const { projectRoot, paths, projectConfig, harnessRetries, testPackagingCoverage, refuseUnexportedDrift, extend, skipFurnaceApply, } = options;
|
|
30
30
|
await withBuildLock(projectRoot, async () => {
|
|
31
31
|
// Pass the previous baseline exactly like `fireforge build` does, so
|
|
32
32
|
// auto-configure runs under the same conditions on both paths. The
|
|
@@ -41,32 +41,28 @@ async function runPreTestBuild(options) {
|
|
|
41
41
|
if (pruned > 0)
|
|
42
42
|
notice(`Pruned ${pruned} dangling _tests symlink(s).`);
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
// The named-file checkJs pass runs before the engine lock is even
|
|
45
|
+
// requested (test-prelock.ts), so a type error refuses without a wait.
|
|
45
46
|
const preparation = await prepareBuildEnvironment(projectRoot, paths, projectConfig, {
|
|
46
47
|
previousBaseline,
|
|
47
48
|
refuseUnexportedDrift,
|
|
49
|
+
skipFurnaceApply,
|
|
48
50
|
});
|
|
49
|
-
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
throw new GeneralError(formatExtendCoverageRefusal(!anchor.ok ? anchor : mozconfigAnchor));
|
|
61
|
-
}
|
|
62
|
-
if (!retainCoverage) {
|
|
63
|
-
const old = previousBaseline.testPackagingCoverage;
|
|
64
|
-
const dropped = old === undefined || old === 'full'
|
|
65
|
-
? ['full coverage outside this scope']
|
|
66
|
-
: old.filter((file) => !testPackagingCoverage.includes(file));
|
|
67
|
-
if (dropped.length)
|
|
68
|
-
notice(`Scoped build drops previous packaging coverage: ${dropped.join(', ')}. Earlier staging inputs changed or have no complete fingerprint anchor; rebuild those paths together to retain them.`);
|
|
51
|
+
await reportPreTestFurnace(projectRoot, preparation, skipFurnaceApply);
|
|
52
|
+
// Which earlier claims this scoped build keeps, per staged path. The
|
|
53
|
+
// mozconfig half can only be checked once prepareBuildEnvironment has
|
|
54
|
+
// regenerated engine/mozconfig (the file this build will configure
|
|
55
|
+
// with), and must still be checked before mach, so a refusal costs no
|
|
56
|
+
// build.
|
|
57
|
+
let plan;
|
|
58
|
+
if (testPackagingCoverage !== 'full') {
|
|
59
|
+
plan = await applyMozconfigAnchor(paths.engine, await planCoverageRetention(paths.engine, previousBaseline, testPackagingCoverage));
|
|
60
|
+
if (extend !== undefined && plan.dropped.length > 0) {
|
|
61
|
+
throw new GeneralError(formatExtendCoverageRefusal(plan.dropped));
|
|
69
62
|
}
|
|
63
|
+
const retentionNotice = formatCoverageRetentionNotice(plan);
|
|
64
|
+
if (retentionNotice !== undefined)
|
|
65
|
+
notice(retentionNotice);
|
|
70
66
|
}
|
|
71
67
|
const buildKind = preparation.fullBuildRequired ? 'full' : 'faster';
|
|
72
68
|
if (preparation.fullBuildRequired) {
|
|
@@ -103,24 +99,24 @@ async function runPreTestBuild(options) {
|
|
|
103
99
|
// carries the static-components anchor forward, since `mach build
|
|
104
100
|
// faster` does not rebake components.conf into the compiled table.
|
|
105
101
|
try {
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
102
|
+
// A scoped claim records this build's paths plus every earlier path
|
|
103
|
+
// the plan kept, each with its own staging-input anchor. When it
|
|
104
|
+
// kept anything the static-components anchor is carried forward
|
|
105
|
+
// explicitly: a kept full claim is still a `mach build faster` that
|
|
110
106
|
// did not rebake the compiled table.
|
|
111
|
-
const
|
|
112
|
-
?
|
|
113
|
-
: testPackagingCoverage;
|
|
107
|
+
const recorded = plan === undefined || testPackagingCoverage === 'full'
|
|
108
|
+
? { testPackagingCoverage }
|
|
109
|
+
: await recordScopedCoverage(paths.engine, plan, testPackagingCoverage);
|
|
114
110
|
await writeBuildBaseline({
|
|
115
111
|
projectRoot,
|
|
116
112
|
engineDir: paths.engine,
|
|
117
113
|
binaryName: projectConfig.binaryName,
|
|
118
|
-
|
|
114
|
+
...recorded,
|
|
119
115
|
previousBaseline,
|
|
120
116
|
recordedBy: describeBuildInvocation(extend !== undefined, testPackagingCoverage),
|
|
121
117
|
staticComponentsHandling: preparation.fullBuildRequired
|
|
122
118
|
? 'refresh'
|
|
123
|
-
:
|
|
119
|
+
: plan !== undefined && (plan.keepFull || Object.keys(plan.retained).length > 0)
|
|
124
120
|
? 'carry-forward'
|
|
125
121
|
: 'auto',
|
|
126
122
|
buildKind,
|
|
@@ -138,15 +134,15 @@ async function runPreTestBuild(options) {
|
|
|
138
134
|
});
|
|
139
135
|
}
|
|
140
136
|
/**
|
|
141
|
-
* `--extend-coverage` only means something for a scoped build: it
|
|
142
|
-
*
|
|
137
|
+
* `--extend-coverage` only means something for a scoped build: it refuses
|
|
138
|
+
* a build that would drop an earlier claim. Without `--build`/
|
|
143
139
|
* `--build-only` there is no build to extend from, and a path-less build
|
|
144
140
|
* records `'full'`, which already covers everything. Refusing both is more
|
|
145
141
|
* honest than silently ignoring the flag.
|
|
146
142
|
*/
|
|
147
143
|
function assertExtendCoverageUsage(options, normalizedPaths) {
|
|
148
144
|
if (options.build !== true && options.buildOnly !== true) {
|
|
149
|
-
throw new InvalidArgumentError('--extend-coverage requires --build or --build-only: it
|
|
145
|
+
throw new InvalidArgumentError('--extend-coverage requires --build or --build-only: it guards the coverage claim a build records.', '--extend-coverage');
|
|
150
146
|
}
|
|
151
147
|
if (normalizedPaths.length === 0) {
|
|
152
148
|
throw new InvalidArgumentError('--extend-coverage requires explicit test paths: a path-less build records full coverage, which already covers every path.', '--extend-coverage');
|
|
@@ -165,6 +161,14 @@ function assertRefuseUnexportedDriftUsage(options) {
|
|
|
165
161
|
return;
|
|
166
162
|
throw new InvalidArgumentError('--refuse-unexported-drift requires --build or --build-only: it guards the pre-test build, and this run dispatches none.', '--refuse-unexported-drift');
|
|
167
163
|
}
|
|
164
|
+
/** Refuses `--no-furnace-apply` on a run that dispatches no build, for the same reason. */
|
|
165
|
+
function assertNoFurnaceApplyUsage(options) {
|
|
166
|
+
if (options.furnaceApply !== false)
|
|
167
|
+
return;
|
|
168
|
+
if (options.build === true || options.buildOnly === true)
|
|
169
|
+
return;
|
|
170
|
+
throw new InvalidArgumentError("--no-furnace-apply requires --build or --build-only: it skips the pre-test build's Furnace apply, and this run dispatches no build.", '--no-furnace-apply');
|
|
171
|
+
}
|
|
168
172
|
/** Renders the `recordedBy` invocation string surfaced by `status --test-coverage`. */
|
|
169
173
|
function describeBuildInvocation(extending, coverage) {
|
|
170
174
|
const extendFlag = extending ? ' --extend-coverage' : '';
|
|
@@ -186,6 +190,7 @@ export async function runTestBuildPhase(projectRoot, paths, projectConfig, harne
|
|
|
186
190
|
assertExtendCoverageUsage(options, normalizedPaths);
|
|
187
191
|
}
|
|
188
192
|
assertRefuseUnexportedDriftUsage(options);
|
|
193
|
+
assertNoFurnaceApplyUsage(options);
|
|
189
194
|
if (options.build || options.buildOnly) {
|
|
190
195
|
// A path-less `test --build` runs (and packages for) the full suite.
|
|
191
196
|
// A scoped invocation only vouches for the requested paths. A scoped
|
|
@@ -197,11 +202,11 @@ export async function runTestBuildPhase(projectRoot, paths, projectConfig, harne
|
|
|
197
202
|
const previousBaseline = await readBuildBaseline(projectRoot);
|
|
198
203
|
await enforceStaticComponentsGate(paths.engine, previousBaseline, options);
|
|
199
204
|
if (extending) {
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
const
|
|
203
|
-
if (
|
|
204
|
-
throw new GeneralError(formatExtendCoverageRefusal(
|
|
205
|
+
// Staging-input half of the plan, before the build lock: a refusal
|
|
206
|
+
// here costs neither build time nor the lock.
|
|
207
|
+
const plan = await planCoverageRetention(paths.engine, previousBaseline, normalizedPaths);
|
|
208
|
+
if (plan.dropped.length > 0) {
|
|
209
|
+
throw new GeneralError(formatExtendCoverageRefusal(plan.dropped));
|
|
205
210
|
}
|
|
206
211
|
}
|
|
207
212
|
}
|
|
@@ -213,6 +218,7 @@ export async function runTestBuildPhase(projectRoot, paths, projectConfig, harne
|
|
|
213
218
|
testPackagingCoverage: coverage,
|
|
214
219
|
refuseUnexportedDrift: options.refuseUnexportedDrift === true,
|
|
215
220
|
extend: extending ? { requestedPaths: normalizedPaths } : undefined,
|
|
221
|
+
skipFurnaceApply: options.furnaceApply === false,
|
|
216
222
|
});
|
|
217
223
|
info('');
|
|
218
224
|
if (options.buildOnly) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
import { findUnownedSupportHeads, formatUnownedSupportHeadNotice, } from '../core/checkjs-support-heads.js';
|
|
2
3
|
import { runCheckJsTestFilesGrouped } from '../core/patch-lint-checkjs.js';
|
|
3
4
|
import { isTestScriptFile } from '../core/patch-lint-ownership.js';
|
|
4
5
|
import { loadPatchesManifest } from '../core/patch-manifest.js';
|
|
@@ -17,6 +18,12 @@ export async function checkRequestedTestTypes(paths, config, requested) {
|
|
|
17
18
|
const selected = new Set(scopes.flatMap((scope) => scope.dispatchPaths.filter(isTestScriptFile)));
|
|
18
19
|
for (const file of selected)
|
|
19
20
|
owned.add(file);
|
|
21
|
+
// A support head a named test brings into scope joins the pass even
|
|
22
|
+
// before any patch owns it, or every name it declares reads as undefined.
|
|
23
|
+
for (const head of await findUnownedSupportHeads(paths.engine, selected, owned)) {
|
|
24
|
+
owned.add(head.file);
|
|
25
|
+
notice(`Pre-build checkJs: ${formatUnownedSupportHeadNotice(head)}`);
|
|
26
|
+
}
|
|
20
27
|
const result = await runCheckJsTestFilesGrouped(paths.engine, owned, config.patchLint, paths.root, selected);
|
|
21
28
|
const issues = [...result.global, ...[...result.byFile.values()].flat()];
|
|
22
29
|
for (const issue of issues)
|
|
@@ -11,6 +11,7 @@ import { getActiveRunLogPath } from '../core/run-log.js';
|
|
|
11
11
|
import { describeChangedPrefNoise } from '../core/test-changed-prefs.js';
|
|
12
12
|
import { buildGreenSummaryRejectedMessage, buildHarnessCrashMessage, buildNoTestsRanMessage, buildSilentSegfaultMessage, headedNoOutputTimeoutHint, isSilentSegfault, } from '../core/test-harness-crash.js';
|
|
13
13
|
import { buildHarnessEarlyExitMessage, classifyHarnessEarlyExit, completePostRebuildFailureContext, prependPostRebuildFailureContext, } from '../core/test-harness-output.js';
|
|
14
|
+
import { buildIncompleteRunMessage } from '../core/test-run-completeness.js';
|
|
14
15
|
import { GeneralError } from '../errors/base.js';
|
|
15
16
|
import { TestFailureError } from '../errors/build.js';
|
|
16
17
|
import { toError } from '../utils/errors.js';
|
|
@@ -249,6 +250,9 @@ function applySingleRunOutcome(outcome, normalizedPaths, binaryName, postRebuild
|
|
|
249
250
|
}
|
|
250
251
|
return;
|
|
251
252
|
}
|
|
253
|
+
if (outcome.verdict.incomplete !== undefined) {
|
|
254
|
+
throw new TestFailureError(buildIncompleteRunMessage(outcome.verdict.incomplete), 'mach test', formatFailureBlocks(outcome.verdict.realFailureBlocks), getActiveRunLogPath());
|
|
255
|
+
}
|
|
252
256
|
if (outcome.verdict.kind === 'no-tests' && outcome.result.exitCode === 0) {
|
|
253
257
|
// The silent false green: exit 0 plus a "Passed: 0"-style summary with
|
|
254
258
|
// zero TEST-START lines must fail rather than pass.
|
|
@@ -282,6 +286,9 @@ export function diagnoseShardOutcome(outcome, path, binaryName, postRebuildConte
|
|
|
282
286
|
if (outcome.verdict.kind === 'no-tests' && outcome.result.exitCode === 0) {
|
|
283
287
|
return buildNoTestsRanMessage(0, [path]);
|
|
284
288
|
}
|
|
289
|
+
if (outcome.verdict.incomplete !== undefined) {
|
|
290
|
+
return buildIncompleteRunMessage(outcome.verdict.incomplete);
|
|
291
|
+
}
|
|
285
292
|
if (outcome.verdict.greenSummaryRejected) {
|
|
286
293
|
return buildGreenSummaryRejectedMessage(outcome.verdict.greenSummaryRejected, outcome.result.exitCode);
|
|
287
294
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TestOptions } from '../types/commands/index.js';
|
|
2
2
|
import type { FireForgeConfig } from '../types/config.js';
|
|
3
|
-
import type { TestRunOutcome } from './test-run.js';
|
|
3
|
+
import type { TestRunOutcome, TestSuite } from './test-run.js';
|
|
4
4
|
/** Rejects pathless `fireforge test` unless the operator selected a pathless mode. */
|
|
5
5
|
export declare function assertPathlessTestMode(testPaths: readonly string[], options: TestOptions): void;
|
|
6
6
|
/** Rejects invalid combinations for mode-like `fireforge test` options. */
|
|
@@ -16,6 +16,8 @@ export interface HarnessClassification {
|
|
|
16
16
|
xpcshell: string[];
|
|
17
17
|
nonXpcshell: string[];
|
|
18
18
|
}
|
|
19
|
+
/** Buckets normalized request paths by the harness their nearest manifest selects. */
|
|
20
|
+
export declare function classifyTestHarnesses(engineDir: string, normalizedPaths: readonly string[]): Promise<HarnessClassification>;
|
|
19
21
|
/**
|
|
20
22
|
* Harness classification is a pure path→manifest lookup and must precede
|
|
21
23
|
* the build dispatch: a mixed xpcshell+mochitest request can never
|
|
@@ -40,3 +42,16 @@ export declare function classifyBeforeDispatch(engineDir: string, normalizedPath
|
|
|
40
42
|
* run build-less without tripping the stale/coverage gate.
|
|
41
43
|
*/
|
|
42
44
|
export declare function reportBuildOnlyCompletion(classification: HarnessClassification, normalizedPaths: readonly string[]): void;
|
|
45
|
+
/**
|
|
46
|
+
* The mach args `--restart-between-tests` forwards: mochitest's own flag,
|
|
47
|
+
* which restarts the browser between test files (the run shape a
|
|
48
|
+
* `standalone` manifest key asks for). Mochitest-only, refused otherwise
|
|
49
|
+
* for the reason `--shuffle` is: xpcshell has no such flag and generic
|
|
50
|
+
* `mach test` would hand it to every suite. Not duplicated when
|
|
51
|
+
* `--mach-arg` already forwards it.
|
|
52
|
+
*
|
|
53
|
+
* @param options - Parsed test options
|
|
54
|
+
* @param suite - The resolved dispatch target
|
|
55
|
+
* @param forwardedMachArgs - `--mach-arg` values after filtering
|
|
56
|
+
*/
|
|
57
|
+
export declare function restartBetweenTestsArgs(options: TestOptions, suite: TestSuite, forwardedMachArgs: readonly string[]): string[];
|
|
@@ -72,7 +72,8 @@ function buildPathlessTestMessage() {
|
|
|
72
72
|
' - `fireforge test --doctor` to run the Marionette preflight only\n' +
|
|
73
73
|
' - `fireforge test --canary` to run the configured short harness canary');
|
|
74
74
|
}
|
|
75
|
-
|
|
75
|
+
/** Buckets normalized request paths by the harness their nearest manifest selects. */
|
|
76
|
+
export async function classifyTestHarnesses(engineDir, normalizedPaths) {
|
|
76
77
|
const result = { xpcshell: [], nonXpcshell: [] };
|
|
77
78
|
for (const testPath of normalizedPaths) {
|
|
78
79
|
const manifest = await findNearestXpcshellManifest(engineDir, testPath);
|
|
@@ -133,3 +134,25 @@ export function reportBuildOnlyCompletion(classification, normalizedPaths) {
|
|
|
133
134
|
info(`Run: fireforge test ${normalizedPaths.join(' ')}`);
|
|
134
135
|
}
|
|
135
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* The mach args `--restart-between-tests` forwards: mochitest's own flag,
|
|
139
|
+
* which restarts the browser between test files (the run shape a
|
|
140
|
+
* `standalone` manifest key asks for). Mochitest-only, refused otherwise
|
|
141
|
+
* for the reason `--shuffle` is: xpcshell has no such flag and generic
|
|
142
|
+
* `mach test` would hand it to every suite. Not duplicated when
|
|
143
|
+
* `--mach-arg` already forwards it.
|
|
144
|
+
*
|
|
145
|
+
* @param options - Parsed test options
|
|
146
|
+
* @param suite - The resolved dispatch target
|
|
147
|
+
* @param forwardedMachArgs - `--mach-arg` values after filtering
|
|
148
|
+
*/
|
|
149
|
+
export function restartBetweenTestsArgs(options, suite, forwardedMachArgs) {
|
|
150
|
+
if (options.restartBetweenTests !== true)
|
|
151
|
+
return [];
|
|
152
|
+
if (suite !== 'mochitest') {
|
|
153
|
+
throw new GeneralError("--restart-between-tests forwards the mochitest harness's per-file browser restart; the " +
|
|
154
|
+
'xpcshell harness has none, and generic `mach test` dispatch would hand it to every ' +
|
|
155
|
+
'suite. Narrow the paths to browser-chrome/mochitest files (and drop --generic-mach-test).');
|
|
156
|
+
}
|
|
157
|
+
return forwardedMachArgs.includes('--restart-between-tests') ? [] : ['--restart-between-tests'];
|
|
158
|
+
}
|