@hominis/fireforge 0.44.4 → 0.46.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 +120 -0
- package/dist/bin/fireforge.js +6 -0
- package/dist/build-info.json +4 -4
- package/dist/src/cli-usage-error.d.ts +46 -0
- package/dist/src/cli-usage-error.js +81 -0
- package/dist/src/cli.d.ts +5 -1
- package/dist/src/cli.js +20 -21
- package/dist/src/commands/bootstrap.js +1 -2
- package/dist/src/commands/build.js +15 -6
- package/dist/src/commands/destructive-decision.d.ts +19 -0
- package/dist/src/commands/destructive-decision.js +31 -0
- package/dist/src/commands/doctor-check-core.d.ts +0 -6
- package/dist/src/commands/doctor-check-core.js +0 -8
- package/dist/src/commands/doctor-orphaned-harness.d.ts +0 -6
- package/dist/src/commands/doctor-orphaned-harness.js +4 -12
- package/dist/src/commands/doctor-post-rebase-audit.js +1 -1
- package/dist/src/commands/doctor-source-pin.d.ts +29 -0
- package/dist/src/commands/doctor-source-pin.js +81 -0
- package/dist/src/commands/doctor.d.ts +2 -0
- package/dist/src/commands/doctor.js +9 -8
- package/dist/src/commands/download.js +35 -24
- package/dist/src/commands/export-all.js +21 -4
- package/dist/src/commands/export-flow.js +15 -60
- package/dist/src/commands/export-placement-conflicts.js +3 -3
- package/dist/src/commands/export-placement-gate.js +2 -8
- package/dist/src/commands/export-placement-policy.js +3 -5
- package/dist/src/commands/export-shared.d.ts +42 -23
- package/dist/src/commands/export-shared.js +4 -19
- package/dist/src/commands/export.js +10 -1
- package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
- package/dist/src/commands/furnace/authoring-config.js +19 -0
- package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
- package/dist/src/commands/furnace/create-browser-test.js +3 -8
- package/dist/src/commands/furnace/create-dry-run.js +2 -5
- package/dist/src/commands/furnace/create-templates.d.ts +17 -1
- package/dist/src/commands/furnace/create-templates.js +2 -1
- package/dist/src/commands/furnace/create.js +25 -19
- package/dist/src/commands/furnace/deploy.js +11 -2
- package/dist/src/commands/furnace/init.js +2 -1
- package/dist/src/commands/furnace/override.js +28 -19
- package/dist/src/commands/furnace/refresh.js +9 -1
- package/dist/src/commands/furnace/remove-mutations.d.ts +6 -7
- package/dist/src/commands/furnace/remove-mutations.js +0 -1
- package/dist/src/commands/furnace/remove.js +2 -6
- package/dist/src/commands/furnace/rename-browser-test.js +1 -7
- package/dist/src/commands/furnace/rename.js +29 -29
- package/dist/src/commands/furnace/status.js +12 -9
- package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
- package/dist/src/commands/furnace/test-file-name.js +39 -0
- package/dist/src/commands/furnace/validation-output.d.ts +21 -9
- package/dist/src/commands/furnace/validation-output.js +3 -8
- package/dist/src/commands/import.js +2 -9
- package/dist/src/commands/lint-per-patch.d.ts +2 -2
- package/dist/src/commands/lint-per-patch.js +19 -4
- package/dist/src/commands/lint-per-run-checkjs.d.ts +2 -2
- package/dist/src/commands/lint.d.ts +2 -1
- package/dist/src/commands/lint.js +1 -1
- package/dist/src/commands/manifest.js +1 -1
- package/dist/src/commands/package.js +2 -4
- package/dist/src/commands/patch/compact.js +14 -15
- package/dist/src/commands/patch/delete.js +15 -25
- package/dist/src/commands/patch/entry-projection.d.ts +13 -0
- package/dist/src/commands/patch/entry-projection.js +24 -0
- package/dist/src/commands/patch/lint-ignore.d.ts +0 -8
- package/dist/src/commands/patch/lint-ignore.js +19 -29
- package/dist/src/commands/patch/move-files-create.js +24 -10
- package/dist/src/commands/patch/move-files-into.js +38 -37
- package/dist/src/commands/patch/move-files.js +4 -13
- package/dist/src/commands/patch/rename.js +27 -41
- package/dist/src/commands/patch/reorder.js +25 -38
- package/dist/src/commands/patch/split-plan.d.ts +4 -5
- package/dist/src/commands/patch/split-plan.js +4 -11
- package/dist/src/commands/patch/split.js +45 -43
- package/dist/src/commands/patch/staged-dependency.js +16 -22
- package/dist/src/commands/patch/tier.js +15 -25
- package/dist/src/commands/patch-rename-map.d.ts +19 -0
- package/dist/src/commands/patch-rename-map.js +36 -0
- package/dist/src/commands/pending-resolution.d.ts +15 -0
- package/dist/src/commands/pending-resolution.js +25 -0
- package/dist/src/commands/re-export-adjacent.d.ts +32 -2
- package/dist/src/commands/re-export-adjacent.js +51 -10
- package/dist/src/commands/re-export-bulk-scan.js +6 -10
- package/dist/src/commands/re-export-drift.d.ts +8 -0
- package/dist/src/commands/re-export-drift.js +65 -4
- package/dist/src/commands/re-export-files.d.ts +2 -3
- package/dist/src/commands/re-export-files.js +38 -32
- package/dist/src/commands/re-export-lint.d.ts +3 -4
- package/dist/src/commands/re-export-lint.js +9 -2
- package/dist/src/commands/re-export-options.js +19 -5
- package/dist/src/commands/re-export-register.js +5 -1
- package/dist/src/commands/re-export-scan.js +8 -8
- package/dist/src/commands/re-export-single.d.ts +22 -3
- package/dist/src/commands/re-export-single.js +19 -11
- package/dist/src/commands/re-export.js +44 -27
- package/dist/src/commands/rebase/abort.js +3 -8
- package/dist/src/commands/rebase/continue.js +12 -12
- package/dist/src/commands/rebase/index.js +2 -2
- package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
- package/dist/src/commands/rebase/patch-loop.js +2 -7
- package/dist/src/commands/resolve.js +18 -10
- package/dist/src/commands/run.d.ts +0 -13
- package/dist/src/commands/run.js +12 -25
- package/dist/src/commands/setup-support.d.ts +16 -4
- package/dist/src/commands/setup-support.js +16 -16
- package/dist/src/commands/setup.js +3 -9
- package/dist/src/commands/source.js +8 -1
- package/dist/src/commands/status-check.js +2 -2
- package/dist/src/commands/status-output.js +2 -2
- package/dist/src/commands/status.js +3 -4
- package/dist/src/commands/test-build-phase.d.ts +2 -2
- package/dist/src/commands/test-build-phase.js +33 -8
- package/dist/src/commands/test-diagnose.js +14 -5
- package/dist/src/commands/test-modes.d.ts +2 -2
- package/dist/src/commands/test-modes.js +0 -1
- package/dist/src/commands/test-register.js +6 -3
- package/dist/src/commands/test-run.js +29 -21
- package/dist/src/commands/test-stale-gate.js +8 -5
- package/dist/src/commands/test-verdict.d.ts +16 -2
- package/dist/src/commands/test-verdict.js +18 -3
- package/dist/src/commands/test.js +51 -7
- package/dist/src/commands/token-list.d.ts +26 -0
- package/dist/src/commands/token-list.js +153 -0
- package/dist/src/commands/token.js +17 -1
- package/dist/src/commands/tree.js +7 -2
- package/dist/src/commands/typecheck.js +9 -9
- package/dist/src/commands/wire.js +4 -3
- package/dist/src/core/branding.d.ts +0 -7
- package/dist/src/core/branding.js +3 -2
- package/dist/src/core/browser-wire.js +6 -3
- package/dist/src/core/build-audit-platform.d.ts +29 -0
- package/dist/src/core/build-audit-platform.js +140 -0
- package/dist/src/core/build-audit-registration.js +3 -2
- package/dist/src/core/build-audit-skip.d.ts +57 -0
- package/dist/src/core/build-audit-skip.js +112 -0
- package/dist/src/core/build-audit.d.ts +15 -2
- package/dist/src/core/build-audit.js +154 -12
- package/dist/src/core/build-baseline.d.ts +45 -28
- package/dist/src/core/build-baseline.js +6 -29
- package/dist/src/core/build-jar-escalation.d.ts +64 -0
- package/dist/src/core/build-jar-escalation.js +128 -0
- package/dist/src/core/build-overwrite-guard.js +1 -1
- package/dist/src/core/build-prepare.d.ts +5 -2
- package/dist/src/core/build-prepare.js +34 -6
- package/dist/src/core/config-paths.d.ts +2 -6
- package/dist/src/core/config-paths.js +4 -2
- package/dist/src/core/config-state.d.ts +0 -6
- package/dist/src/core/config-state.js +1 -19
- package/dist/src/core/config-validate-build-audit.d.ts +8 -0
- package/dist/src/core/config-validate-build-audit.js +80 -0
- package/dist/src/core/config-validate-file-size.d.ts +12 -0
- package/dist/src/core/config-validate-file-size.js +65 -0
- package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
- package/dist/src/core/config-validate-patch-policy.js +3 -3
- package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
- package/dist/src/core/config-validate-test-toolchains.js +4 -5
- package/dist/src/core/config-validate.js +20 -3
- package/dist/src/core/config.d.ts +2 -2
- package/dist/src/core/config.js +11 -15
- package/dist/src/core/coverage-extend.js +4 -4
- package/dist/src/core/discard-baseline.js +7 -7
- package/dist/src/core/engine-precondition.d.ts +1 -9
- package/dist/src/core/engine-precondition.js +3 -8
- package/dist/src/core/engine-session-lock.js +13 -17
- package/dist/src/core/export-moved-code-shape.js +2 -2
- package/dist/src/core/file-lock-owner.d.ts +103 -0
- package/dist/src/core/file-lock-owner.js +259 -0
- package/dist/src/core/file-lock.d.ts +2 -7
- package/dist/src/core/file-lock.js +40 -91
- package/dist/src/core/firefox-cache.d.ts +13 -1
- package/dist/src/core/firefox-cache.js +59 -34
- package/dist/src/core/firefox-download.d.ts +15 -0
- package/dist/src/core/firefox-download.js +37 -21
- package/dist/src/core/firefox-extract.d.ts +6 -5
- package/dist/src/core/firefox-extract.js +36 -18
- package/dist/src/core/firefox.d.ts +30 -32
- package/dist/src/core/firefox.js +5 -36
- package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
- package/dist/src/core/furnace-apply-dry-run.js +1 -7
- package/dist/src/core/furnace-apply-helpers.d.ts +0 -11
- package/dist/src/core/furnace-apply-helpers.js +8 -19
- package/dist/src/core/furnace-apply-overwrite-warn.js +4 -2
- package/dist/src/core/furnace-apply.d.ts +1 -1
- package/dist/src/core/furnace-apply.js +30 -10
- package/dist/src/core/furnace-config.d.ts +1 -1
- package/dist/src/core/furnace-config.js +23 -36
- package/dist/src/core/furnace-constants.js +3 -2
- package/dist/src/core/furnace-dir-entry.d.ts +20 -0
- package/dist/src/core/furnace-dir-entry.js +21 -0
- package/dist/src/core/furnace-operation.d.ts +2 -0
- package/dist/src/core/furnace-operation.js +3 -1
- package/dist/src/core/furnace-refresh.d.ts +18 -6
- package/dist/src/core/furnace-refresh.js +3 -6
- package/dist/src/core/furnace-registration-ast.d.ts +0 -1
- package/dist/src/core/furnace-registration-ast.js +0 -3
- package/dist/src/core/furnace-registration.d.ts +2 -1
- package/dist/src/core/furnace-registration.js +22 -29
- package/dist/src/core/furnace-scanner.js +3 -6
- package/dist/src/core/furnace-state-persist.d.ts +2 -2
- package/dist/src/core/furnace-validate-registration.js +5 -7
- package/dist/src/core/git-base.d.ts +3 -1
- package/dist/src/core/git-base.js +25 -3
- package/dist/src/core/git-diff-new-file.d.ts +40 -0
- package/dist/src/core/git-diff-new-file.js +119 -0
- package/dist/src/core/git-diff.d.ts +9 -9
- package/dist/src/core/git-diff.js +88 -157
- package/dist/src/core/git-file-ops.d.ts +0 -7
- package/dist/src/core/git-file-ops.js +4 -28
- package/dist/src/core/git-readonly-index.d.ts +0 -2
- package/dist/src/core/git-readonly-index.js +0 -4
- package/dist/src/core/git-status.d.ts +7 -0
- package/dist/src/core/git-status.js +21 -9
- package/dist/src/core/git.d.ts +0 -6
- package/dist/src/core/git.js +14 -43
- package/dist/src/core/harness-orphans.d.ts +73 -0
- package/dist/src/core/harness-orphans.js +187 -0
- package/dist/src/core/history-log.d.ts +19 -0
- package/dist/src/core/history-log.js +29 -0
- package/dist/src/core/mach-error-hints.d.ts +0 -5
- package/dist/src/core/mach-error-hints.js +1 -1
- package/dist/src/core/mach-known-noise-filter.d.ts +25 -3
- package/dist/src/core/mach-known-noise-filter.js +31 -10
- package/dist/src/core/mach-mozconfig.js +2 -1
- package/dist/src/core/mach-python.d.ts +2 -0
- package/dist/src/core/mach-python.js +12 -7
- package/dist/src/core/mach-resource-shim.d.ts +13 -1
- package/dist/src/core/mach-resource-shim.js +112 -9
- package/dist/src/core/mach.d.ts +46 -53
- package/dist/src/core/mach.js +31 -83
- package/dist/src/core/marionette-port.d.ts +0 -17
- package/dist/src/core/marionette-port.js +12 -58
- package/dist/src/core/marionette-preflight.js +9 -27
- package/dist/src/core/mochitest-server-port.js +3 -3
- package/dist/src/core/moz-manifest-rules.js +37 -33
- package/dist/src/core/patch-apply-fuzz.js +4 -4
- package/dist/src/core/patch-apply.d.ts +4 -5
- package/dist/src/core/patch-apply.js +55 -32
- package/dist/src/core/patch-export-coverage.d.ts +0 -9
- package/dist/src/core/patch-export-coverage.js +1 -30
- package/dist/src/core/patch-export-update.d.ts +18 -1
- package/dist/src/core/patch-export-update.js +4 -3
- package/dist/src/core/patch-export.d.ts +11 -21
- package/dist/src/core/patch-export.js +44 -66
- package/dist/src/core/patch-files.d.ts +0 -4
- package/dist/src/core/patch-files.js +0 -11
- package/dist/src/core/patch-lint-binary.d.ts +6 -12
- package/dist/src/core/patch-lint-cache.d.ts +17 -1
- package/dist/src/core/patch-lint-cache.js +3 -6
- package/dist/src/core/patch-lint-checkjs.d.ts +33 -16
- package/dist/src/core/patch-lint-checkjs.js +43 -25
- package/dist/src/core/patch-lint-creators.d.ts +2 -6
- package/dist/src/core/patch-lint-cross.d.ts +16 -8
- package/dist/src/core/patch-lint-cross.js +49 -92
- package/dist/src/core/patch-lint-css.js +10 -6
- package/dist/src/core/patch-lint-file-size.d.ts +52 -0
- package/dist/src/core/patch-lint-file-size.js +53 -0
- package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
- package/dist/src/core/patch-lint-forward-registration.js +210 -0
- package/dist/src/core/patch-lint-jsdoc.js +36 -36
- package/dist/src/core/patch-lint-module-registration.d.ts +2 -8
- package/dist/src/core/patch-lint-prettier.d.ts +36 -0
- package/dist/src/core/patch-lint-prettier.js +126 -0
- package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
- package/dist/src/core/patch-lint-queue-types.js +12 -0
- package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
- package/dist/src/core/patch-lint-staged-registration.js +90 -0
- package/dist/src/core/patch-lint-unmanaged-companion.d.ts +106 -0
- package/dist/src/core/patch-lint-unmanaged-companion.js +214 -0
- package/dist/src/core/patch-lint.d.ts +14 -9
- package/dist/src/core/patch-lint.js +27 -27
- package/dist/src/core/patch-lock.js +3 -3
- package/dist/src/core/patch-manifest-io.js +8 -7
- package/dist/src/core/patch-parse.d.ts +19 -8
- package/dist/src/core/patch-parse.js +25 -0
- package/dist/src/core/patch-policy.js +15 -27
- package/dist/src/core/patch-source-metadata.d.ts +1 -3
- package/dist/src/core/patch-source-metadata.js +0 -4
- package/dist/src/core/patch-transform.d.ts +21 -8
- package/dist/src/core/patch-transform.js +38 -10
- package/dist/src/core/process-cpu-time.js +1 -1
- package/dist/src/core/rebase-session.d.ts +2 -8
- package/dist/src/core/rebase-session.js +5 -17
- package/dist/src/core/register-browser-content.js +3 -2
- package/dist/src/core/register-browser-test.js +2 -1
- package/dist/src/core/register-module.js +2 -1
- package/dist/src/core/register-scaffold.js +5 -4
- package/dist/src/core/register-shared-css.js +2 -1
- package/dist/src/core/run-log-redact.d.ts +38 -0
- package/dist/src/core/run-log-redact.js +69 -0
- package/dist/src/core/run-log.d.ts +13 -2
- package/dist/src/core/run-log.js +67 -18
- package/dist/src/core/signal-critical.js +2 -4
- package/dist/src/core/smoke-patterns.d.ts +0 -1
- package/dist/src/core/smoke-patterns.js +18 -11
- package/dist/src/core/state-file.d.ts +12 -1
- package/dist/src/core/state-file.js +14 -1
- package/dist/src/core/status-classify.js +2 -1
- package/dist/src/core/test-changed-prefs.js +26 -11
- package/dist/src/core/test-harness-crash.js +61 -48
- package/dist/src/core/test-harness-verdict-notes.d.ts +55 -0
- package/dist/src/core/test-harness-verdict-notes.js +48 -0
- package/dist/src/core/test-stale-check.d.ts +23 -8
- package/dist/src/core/test-stale-check.js +77 -4
- package/dist/src/core/test-stall-triage.js +37 -19
- package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
- package/dist/src/core/test-xpcshell-retry.js +14 -11
- package/dist/src/core/token-category.d.ts +33 -0
- package/dist/src/core/token-category.js +56 -48
- package/dist/src/core/token-dark-mode.js +2 -14
- package/dist/src/core/token-inventory.d.ts +56 -0
- package/dist/src/core/token-inventory.js +109 -0
- package/dist/src/core/token-manager.d.ts +1 -1
- package/dist/src/core/token-manager.js +1 -1
- package/dist/src/core/token-variant.js +21 -22
- package/dist/src/core/toolchain-preflight.js +1 -1
- package/dist/src/core/tree-cow.d.ts +0 -2
- package/dist/src/core/tree-cow.js +2 -2
- package/dist/src/core/tree-store.js +4 -5
- package/dist/src/core/typecheck.js +2 -2
- package/dist/src/core/wire-dom-fragment.js +6 -17
- package/dist/src/core/wire-utils.js +5 -17
- package/dist/src/core/xpcshell-appdir.js +2 -4
- package/dist/src/errors/base.d.ts +53 -1
- package/dist/src/errors/base.js +79 -11
- package/dist/src/errors/build.d.ts +28 -0
- package/dist/src/errors/build.js +51 -10
- package/dist/src/errors/config.d.ts +0 -14
- package/dist/src/errors/config.js +13 -34
- package/dist/src/errors/download.d.ts +13 -0
- package/dist/src/errors/download.js +53 -24
- package/dist/src/errors/furnace.js +8 -9
- package/dist/src/errors/git.d.ts +8 -8
- package/dist/src/errors/git.js +20 -30
- package/dist/src/errors/patch.js +8 -10
- package/dist/src/errors/rebase.js +7 -7
- package/dist/src/types/commands/options.d.ts +96 -123
- package/dist/src/types/commands/patches.d.ts +17 -6
- package/dist/src/types/config.d.ts +75 -2
- package/dist/src/types/index.d.ts +0 -1
- package/dist/src/utils/assert.d.ts +3 -15
- package/dist/src/utils/assert.js +1 -18
- package/dist/src/utils/build-info.d.ts +1 -1
- package/dist/src/utils/child-env.d.ts +2 -1
- package/dist/src/utils/elapsed.js +10 -1
- package/dist/src/utils/fs.d.ts +4 -2
- package/dist/src/utils/fs.js +11 -14
- package/dist/src/utils/hash.d.ts +12 -0
- package/dist/src/utils/hash.js +16 -0
- package/dist/src/utils/line-dispatch.d.ts +15 -0
- package/dist/src/utils/line-dispatch.js +48 -0
- package/dist/src/utils/options.d.ts +1 -7
- package/dist/src/utils/options.js +6 -7
- package/dist/src/utils/package-root.js +3 -12
- package/dist/src/utils/platform.d.ts +0 -4
- package/dist/src/utils/process-group.d.ts +2 -1
- package/dist/src/utils/process-group.js +20 -19
- package/dist/src/utils/process.d.ts +40 -26
- package/dist/src/utils/process.js +115 -166
- package/dist/src/utils/ps-duration.js +3 -3
- package/dist/src/utils/sleep.d.ts +26 -0
- package/dist/src/utils/sleep.js +31 -0
- package/dist/src/utils/validation.d.ts +2 -38
- package/dist/src/utils/validation.js +7 -16
- package/docs/configuration.md +136 -1
- package/docs/exit-codes.md +34 -20
- package/docs/lifecycle-invariants.md +18 -12
- package/docs/machine-output.md +31 -8
- package/docs/patch-workflow.md +2 -1
- package/docs/run-logs.md +44 -4
- package/docs/testing.md +70 -11
- package/package.json +8 -7
- package/dist/src/commands/rebase.d.ts +0 -4
- package/dist/src/commands/rebase.js +0 -5
- package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
- package/dist/src/core/patch-artifact-normalize.js +0 -24
- package/dist/src/core/patch-lint-reexports.d.ts +0 -7
- package/dist/src/core/patch-lint-reexports.js +0 -8
- package/dist/src/core/wire-targets.d.ts +0 -7
- package/dist/src/core/wire-targets.js +0 -8
|
@@ -18,6 +18,27 @@
|
|
|
18
18
|
* binary new-file conflicts as unresolvable rather than auto-resolving.
|
|
19
19
|
*/
|
|
20
20
|
export declare function extractNewFileContentFromDiff(diff: string, targetFile?: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Projects every TEXT new file a diff creates into a path → content map, for
|
|
23
|
+
* the cross-patch lint projections that build a synthetic patch-queue entry.
|
|
24
|
+
*
|
|
25
|
+
* Binary new files are SKIPPED rather than refused. The map feeds only the
|
|
26
|
+
* forward-import rule, which scans content for import statements — a binary
|
|
27
|
+
* blob authors none, so omitting it contributes exactly what including it
|
|
28
|
+
* would. Refusing instead (which is what
|
|
29
|
+
* {@link extractNewFileContentFromDiff} correctly does for a caller that
|
|
30
|
+
* named ONE file) made vendoring a new binary impossible: every projection
|
|
31
|
+
* site fed it every detected new file, so `export --order`, `re-export
|
|
32
|
+
* --scan --scan-file`, `patch move-files-into` and `patch split` all died on
|
|
33
|
+
* a file the export half had just written a valid `GIT binary patch` for.
|
|
34
|
+
* Nothing is lost by skipping: binary bodies are covered independently by the
|
|
35
|
+
* `binary-body-not-reconstructable` queue lint, and
|
|
36
|
+
* `buildPatchQueueContext` already tolerates them for real queue entries.
|
|
37
|
+
*
|
|
38
|
+
* One parse for the whole diff, rather than one per new file as the five
|
|
39
|
+
* call sites previously did.
|
|
40
|
+
*/
|
|
41
|
+
export declare function buildNewFileTextProjection(diff: string): Map<string, string>;
|
|
21
42
|
/**
|
|
22
43
|
* Extracts the complete file content from a "new file" patch.
|
|
23
44
|
* When targetFile is provided, only extracts content for that file
|
|
@@ -27,14 +48,6 @@ export declare function extractNewFileContentFromDiff(diff: string, targetFile?:
|
|
|
27
48
|
* @returns The file content that the patch would create
|
|
28
49
|
*/
|
|
29
50
|
export declare function extractNewFileContent(patchPath: string, targetFile?: string): Promise<string>;
|
|
30
|
-
/**
|
|
31
|
-
* Applies a patch's changes to content.
|
|
32
|
-
* @param content - Original content (null for new files)
|
|
33
|
-
* @param patchPath - Path to the patch file
|
|
34
|
-
* @param targetFile - The file path within the patch
|
|
35
|
-
* @returns Modified content
|
|
36
|
-
*/
|
|
37
|
-
export declare function applyPatchToContent(content: string | null, patchPath: string, targetFile: string): Promise<string>;
|
|
38
51
|
/**
|
|
39
52
|
* Applies an already-read patch body to content. Batched callers that hold
|
|
40
53
|
* many (patch, file) pairs use this to read each patch body once instead of
|
|
@@ -27,6 +27,15 @@ export function extractNewFileContentFromDiff(diff, targetFile) {
|
|
|
27
27
|
if (binary) {
|
|
28
28
|
throw new PatchError(`Cannot extract text content from binary patch section for ${binary.targetPath}`, binary.targetPath);
|
|
29
29
|
}
|
|
30
|
+
return contentFromSections(sections);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Walks the added (`+`) lines of already-parsed sections into file content.
|
|
34
|
+
* Shared by {@link extractNewFileContentFromDiff} and
|
|
35
|
+
* {@link buildNewFileTextProjection} so the two cannot drift on the empty-file
|
|
36
|
+
* and no-trailing-newline cases below.
|
|
37
|
+
*/
|
|
38
|
+
function contentFromSections(sections) {
|
|
30
39
|
// An empty new file is a legitimate git diff: `new file mode` with no
|
|
31
40
|
// hunks at all. It must extract as '' — the historical line-walker
|
|
32
41
|
// returned '\n' for it, creating a one-byte file that failed checksum
|
|
@@ -51,6 +60,35 @@ export function extractNewFileContentFromDiff(diff, targetFile) {
|
|
|
51
60
|
const result = contentLines.join('\n');
|
|
52
61
|
return hasNoNewlineMarker ? result : result + '\n';
|
|
53
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Projects every TEXT new file a diff creates into a path → content map, for
|
|
65
|
+
* the cross-patch lint projections that build a synthetic patch-queue entry.
|
|
66
|
+
*
|
|
67
|
+
* Binary new files are SKIPPED rather than refused. The map feeds only the
|
|
68
|
+
* forward-import rule, which scans content for import statements — a binary
|
|
69
|
+
* blob authors none, so omitting it contributes exactly what including it
|
|
70
|
+
* would. Refusing instead (which is what
|
|
71
|
+
* {@link extractNewFileContentFromDiff} correctly does for a caller that
|
|
72
|
+
* named ONE file) made vendoring a new binary impossible: every projection
|
|
73
|
+
* site fed it every detected new file, so `export --order`, `re-export
|
|
74
|
+
* --scan --scan-file`, `patch move-files-into` and `patch split` all died on
|
|
75
|
+
* a file the export half had just written a valid `GIT binary patch` for.
|
|
76
|
+
* Nothing is lost by skipping: binary bodies are covered independently by the
|
|
77
|
+
* `binary-body-not-reconstructable` queue lint, and
|
|
78
|
+
* `buildPatchQueueContext` already tolerates them for real queue entries.
|
|
79
|
+
*
|
|
80
|
+
* One parse for the whole diff, rather than one per new file as the five
|
|
81
|
+
* call sites previously did.
|
|
82
|
+
*/
|
|
83
|
+
export function buildNewFileTextProjection(diff) {
|
|
84
|
+
const newFiles = new Map();
|
|
85
|
+
for (const section of parseDiffSections(diff)) {
|
|
86
|
+
if (!section.isNewFile || section.isBinary)
|
|
87
|
+
continue;
|
|
88
|
+
newFiles.set(section.targetPath, contentFromSections([section]));
|
|
89
|
+
}
|
|
90
|
+
return newFiles;
|
|
91
|
+
}
|
|
54
92
|
/**
|
|
55
93
|
* Extracts the complete file content from a "new file" patch.
|
|
56
94
|
* When targetFile is provided, only extracts content for that file
|
|
@@ -63,16 +101,6 @@ export async function extractNewFileContent(patchPath, targetFile) {
|
|
|
63
101
|
const content = await readText(patchPath);
|
|
64
102
|
return extractNewFileContentFromDiff(content, targetFile);
|
|
65
103
|
}
|
|
66
|
-
/**
|
|
67
|
-
* Applies a patch's changes to content.
|
|
68
|
-
* @param content - Original content (null for new files)
|
|
69
|
-
* @param patchPath - Path to the patch file
|
|
70
|
-
* @param targetFile - The file path within the patch
|
|
71
|
-
* @returns Modified content
|
|
72
|
-
*/
|
|
73
|
-
export async function applyPatchToContent(content, patchPath, targetFile) {
|
|
74
|
-
return applyPatchTextToContent(content, await readText(patchPath), targetFile);
|
|
75
|
-
}
|
|
76
104
|
/**
|
|
77
105
|
* Applies an already-read patch body to content. Batched callers that hold
|
|
78
106
|
* many (patch, file) pairs use this to read each patch body once instead of
|
|
@@ -34,7 +34,7 @@ export async function readProcessCpuSeconds(pid) {
|
|
|
34
34
|
'-NoProfile',
|
|
35
35
|
'-NonInteractive',
|
|
36
36
|
'-Command',
|
|
37
|
-
`(Get-Process -Id ${
|
|
37
|
+
`(Get-Process -Id ${pid} -ErrorAction SilentlyContinue).CPU`,
|
|
38
38
|
]);
|
|
39
39
|
const seconds = Number.parseFloat(result.stdout.trim());
|
|
40
40
|
return Number.isFinite(seconds) ? seconds : undefined;
|
|
@@ -52,6 +52,8 @@ export interface RebaseSession {
|
|
|
52
52
|
/** Index of the next patch to process (resume point). */
|
|
53
53
|
currentIndex: number;
|
|
54
54
|
}
|
|
55
|
+
/** Absolute path of the session file, for messages that must name it. */
|
|
56
|
+
export declare function getRebaseSessionPath(projectRoot: string): string;
|
|
55
57
|
/**
|
|
56
58
|
* Outcome of reading the session file, following the {@link LockOwner}
|
|
57
59
|
* discriminated-union form rather than collapsing three states into `null`.
|
|
@@ -74,8 +76,6 @@ export type RebaseSessionRead = {
|
|
|
74
76
|
valid: false;
|
|
75
77
|
reason: string;
|
|
76
78
|
};
|
|
77
|
-
/** Absolute path of the session file, for messages that must name it. */
|
|
78
|
-
export declare function getRebaseSessionPath(projectRoot: string): string;
|
|
79
79
|
/**
|
|
80
80
|
* Reads the session file, reporting absent, valid, and corrupt as three
|
|
81
81
|
* distinct outcomes. Never throws for a malformed file: `readJson` calls
|
|
@@ -90,12 +90,6 @@ export declare function getRebaseSessionPath(projectRoot: string): string;
|
|
|
90
90
|
* markers.
|
|
91
91
|
*/
|
|
92
92
|
export declare function readRebaseSession(projectRoot: string): Promise<RebaseSessionRead>;
|
|
93
|
-
/**
|
|
94
|
-
* Loads an existing rebase session, or returns `null` when none exists or the
|
|
95
|
-
* file on disk is unusable. Callers that must tell those two apart — every
|
|
96
|
-
* command that reports to an operator — should use {@link readRebaseSession}.
|
|
97
|
-
*/
|
|
98
|
-
export declare function tryReadRebaseSession(projectRoot: string): Promise<RebaseSession | null>;
|
|
99
93
|
/**
|
|
100
94
|
* Persists a rebase session atomically.
|
|
101
95
|
*/
|
|
@@ -13,7 +13,8 @@ import { getProjectPaths } from './config-paths.js';
|
|
|
13
13
|
import { createSiblingLockPath, withFileLock } from './file-lock.js';
|
|
14
14
|
// ── Helpers ──
|
|
15
15
|
const SESSION_FILENAME = 'rebase-session.json';
|
|
16
|
-
|
|
16
|
+
/** Absolute path of the session file, for messages that must name it. */
|
|
17
|
+
export function getRebaseSessionPath(projectRoot) {
|
|
17
18
|
return join(getProjectPaths(projectRoot).fireforgeDir, SESSION_FILENAME);
|
|
18
19
|
}
|
|
19
20
|
const REBASE_PATCH_STATUSES = [
|
|
@@ -116,10 +117,6 @@ function isValidSession(data) {
|
|
|
116
117
|
return false;
|
|
117
118
|
return currentIndex >= 0 && currentIndex <= patches.length;
|
|
118
119
|
}
|
|
119
|
-
/** Absolute path of the session file, for messages that must name it. */
|
|
120
|
-
export function getRebaseSessionPath(projectRoot) {
|
|
121
|
-
return sessionPath(projectRoot);
|
|
122
|
-
}
|
|
123
120
|
/**
|
|
124
121
|
* Reads the session file, reporting absent, valid, and corrupt as three
|
|
125
122
|
* distinct outcomes. Never throws for a malformed file: `readJson` calls
|
|
@@ -134,7 +131,7 @@ export function getRebaseSessionPath(projectRoot) {
|
|
|
134
131
|
* markers.
|
|
135
132
|
*/
|
|
136
133
|
export async function readRebaseSession(projectRoot) {
|
|
137
|
-
const path =
|
|
134
|
+
const path = getRebaseSessionPath(projectRoot);
|
|
138
135
|
let data;
|
|
139
136
|
try {
|
|
140
137
|
data = await readJson(path);
|
|
@@ -159,20 +156,11 @@ export async function readRebaseSession(projectRoot) {
|
|
|
159
156
|
session: { ...data, patches: data.patches.map(normalizeEntry) },
|
|
160
157
|
};
|
|
161
158
|
}
|
|
162
|
-
/**
|
|
163
|
-
* Loads an existing rebase session, or returns `null` when none exists or the
|
|
164
|
-
* file on disk is unusable. Callers that must tell those two apart — every
|
|
165
|
-
* command that reports to an operator — should use {@link readRebaseSession}.
|
|
166
|
-
*/
|
|
167
|
-
export async function tryReadRebaseSession(projectRoot) {
|
|
168
|
-
const result = await readRebaseSession(projectRoot);
|
|
169
|
-
return result.present && result.valid ? result.session : null;
|
|
170
|
-
}
|
|
171
159
|
/**
|
|
172
160
|
* Persists a rebase session atomically.
|
|
173
161
|
*/
|
|
174
162
|
export async function saveRebaseSession(projectRoot, session) {
|
|
175
|
-
const path =
|
|
163
|
+
const path = getRebaseSessionPath(projectRoot);
|
|
176
164
|
await withFileLock(createSiblingLockPath(path, '.rebase-session.lock'), async () => {
|
|
177
165
|
await writeJson(path, session);
|
|
178
166
|
});
|
|
@@ -181,7 +169,7 @@ export async function saveRebaseSession(projectRoot, session) {
|
|
|
181
169
|
* Removes the rebase session file.
|
|
182
170
|
*/
|
|
183
171
|
export async function clearRebaseSession(projectRoot) {
|
|
184
|
-
const path =
|
|
172
|
+
const path = getRebaseSessionPath(projectRoot);
|
|
185
173
|
if (await pathExists(path)) {
|
|
186
174
|
await removeFile(path);
|
|
187
175
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { GeneralError } from '../errors/base.js';
|
|
7
7
|
import { pathExists, readText, writeText } from '../utils/fs.js';
|
|
8
|
+
import { normalizePathSlashes } from '../utils/paths.js';
|
|
8
9
|
import { insertJarMnEntry } from './moz-manifest-helpers.js';
|
|
9
10
|
/**
|
|
10
11
|
* Tokenizer-based implementation for browser content registration.
|
|
@@ -30,8 +31,8 @@ export async function registerBrowserContent(engineDir, fileName, after, sourceP
|
|
|
30
31
|
if (!(await pathExists(manifestPath))) {
|
|
31
32
|
throw new GeneralError(`Manifest not found: ${manifest}`);
|
|
32
33
|
}
|
|
33
|
-
const source = (sourcePath ?? `content/${fileName}`)
|
|
34
|
-
const entry = ` content/browser/${fileName} (${source})
|
|
34
|
+
const source = normalizePathSlashes(sourcePath ?? `content/${fileName}`);
|
|
35
|
+
const entry = normalizePathSlashes(` content/browser/${fileName} (${source})`);
|
|
35
36
|
const content = await readText(manifestPath);
|
|
36
37
|
// Idempotency check
|
|
37
38
|
if (content.includes(`content/browser/${fileName}`)) {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { GeneralError } from '../errors/base.js';
|
|
7
7
|
import { pathExists, readText, writeText } from '../utils/fs.js';
|
|
8
|
+
import { normalizePathSlashes } from '../utils/paths.js';
|
|
8
9
|
import { insertMozBuildListEntry } from './moz-manifest-helpers.js';
|
|
9
10
|
/**
|
|
10
11
|
* Tokenizer-based implementation for test manifest registration.
|
|
@@ -28,7 +29,7 @@ export async function registerTestManifest(engineDir, testDir, dryRun = false) {
|
|
|
28
29
|
if (!(await pathExists(manifestPath))) {
|
|
29
30
|
throw new GeneralError(`Manifest not found: ${manifest}`);
|
|
30
31
|
}
|
|
31
|
-
const entry = ` "content/test/${testDir}/browser.toml"
|
|
32
|
+
const entry = normalizePathSlashes(` "content/test/${testDir}/browser.toml",`);
|
|
32
33
|
const content = await readText(manifestPath);
|
|
33
34
|
// Idempotency check
|
|
34
35
|
if (content.includes(`content/test/${testDir}/browser.toml`)) {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { GeneralError } from '../errors/base.js';
|
|
7
7
|
import { pathExists, readText, writeText } from '../utils/fs.js';
|
|
8
|
+
import { normalizePathSlashes } from '../utils/paths.js';
|
|
8
9
|
import { insertMozBuildListEntry } from './moz-manifest-helpers.js';
|
|
9
10
|
import { scaffoldModuleMozBuild } from './register-scaffold.js';
|
|
10
11
|
/**
|
|
@@ -41,7 +42,7 @@ export async function registerFireForgeModule(engineDir, fileName, moduleDir, dr
|
|
|
41
42
|
}
|
|
42
43
|
throw new GeneralError(`Manifest not found: ${manifest}. Pass --create-manifest to scaffold it and wire the parent DIRS entry.`);
|
|
43
44
|
}
|
|
44
|
-
const entry = ` "${fileName}"
|
|
45
|
+
const entry = normalizePathSlashes(` "${fileName}",`);
|
|
45
46
|
const content = await readText(manifestPath);
|
|
46
47
|
// Idempotency check
|
|
47
48
|
if (content.includes(`"${fileName}"`)) {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import { dirname, join } from 'node:path';
|
|
20
20
|
import { GeneralError } from '../errors/base.js';
|
|
21
21
|
import { pathExists, readText, writeText } from '../utils/fs.js';
|
|
22
|
+
import { normalizePathSlashes } from '../utils/paths.js';
|
|
22
23
|
import { getLicenseHeader } from './license-headers.js';
|
|
23
24
|
import { findAlphabeticalMozBuildPosition } from './moz-manifest-helpers.js';
|
|
24
25
|
import { tokenizeMozBuildList } from './moz-manifest-tokenizers.js';
|
|
@@ -64,7 +65,7 @@ export function upsertMozBuildListEntry(content, directive, value) {
|
|
|
64
65
|
export async function ensureParentDirsWiring(engineDir, childRelDir, dryRun) {
|
|
65
66
|
const actions = [];
|
|
66
67
|
let childName = childRelDir.split('/').at(-1) ?? childRelDir;
|
|
67
|
-
let parentRel = dirname(childRelDir)
|
|
68
|
+
let parentRel = normalizePathSlashes(dirname(childRelDir));
|
|
68
69
|
for (;;) {
|
|
69
70
|
if (parentRel === '.' || parentRel === '' || parentRel === '/') {
|
|
70
71
|
throw new GeneralError(`No moz.build found in any parent of ${childRelDir} up to the engine root; ` +
|
|
@@ -90,7 +91,7 @@ export async function ensureParentDirsWiring(engineDir, childRelDir, dryRun) {
|
|
|
90
91
|
await writeText(parentManifestPath, scaffold);
|
|
91
92
|
actions.push({ manifest: parentManifestRel, change: `created with DIRS += ["${childName}"]` });
|
|
92
93
|
childName = parentRel.split('/').at(-1) ?? parentRel;
|
|
93
|
-
parentRel = dirname(parentRel)
|
|
94
|
+
parentRel = normalizePathSlashes(dirname(parentRel));
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
/**
|
|
@@ -122,7 +123,7 @@ export async function scaffoldModuleMozBuild(engineDir, moduleDir, fileName, dry
|
|
|
122
123
|
* entry already exists).
|
|
123
124
|
*/
|
|
124
125
|
export async function ensureXpcshellManifestWiring(engineDir, manifestRelPath, dryRun) {
|
|
125
|
-
let dirRel = dirname(manifestRelPath)
|
|
126
|
+
let dirRel = normalizePathSlashes(dirname(manifestRelPath));
|
|
126
127
|
for (;;) {
|
|
127
128
|
if (dirRel === '.' || dirRel === '' || dirRel === '/') {
|
|
128
129
|
throw new GeneralError(`No moz.build found in any parent of ${manifestRelPath} up to the engine root; ` +
|
|
@@ -140,6 +141,6 @@ export async function ensureXpcshellManifestWiring(engineDir, manifestRelPath, d
|
|
|
140
141
|
await writeText(mozBuildPath, updated);
|
|
141
142
|
return [{ manifest: mozBuildRel, change: `XPCSHELL_TESTS_MANIFESTS += ["${relEntry}"]` }];
|
|
142
143
|
}
|
|
143
|
-
dirRel = dirname(dirRel)
|
|
144
|
+
dirRel = normalizePathSlashes(dirname(dirRel));
|
|
144
145
|
}
|
|
145
146
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { basename, join } from 'node:path';
|
|
6
6
|
import { GeneralError } from '../errors/base.js';
|
|
7
7
|
import { pathExists, readText, writeText } from '../utils/fs.js';
|
|
8
|
+
import { normalizePathSlashes } from '../utils/paths.js';
|
|
8
9
|
import { insertJarMnEntry } from './moz-manifest-helpers.js';
|
|
9
10
|
/**
|
|
10
11
|
* Tokenizer-based implementation for shared CSS registration.
|
|
@@ -65,7 +66,7 @@ export function buildEntry(name, sourceColumn) {
|
|
|
65
66
|
const minColumn = target.length + MIN_SOURCE_GAP;
|
|
66
67
|
const column = sourceColumn !== undefined && sourceColumn >= minColumn ? sourceColumn : minColumn;
|
|
67
68
|
const padding = ' '.repeat(column - target.length);
|
|
68
|
-
return `${target}${padding}(../shared/${name}.css)
|
|
69
|
+
return normalizePathSlashes(`${target}${padding}(../shared/${name}.css)`);
|
|
69
70
|
}
|
|
70
71
|
/**
|
|
71
72
|
* Registers a CSS file in browser/themes/shared/jar.inc.mn.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conservative secret redaction for the run-log FILE tee.
|
|
3
|
+
*
|
|
4
|
+
* A run log is a complete copy of mach's output, and mach echoes its
|
|
5
|
+
* environment in places (configure summaries, `mach env`, harness
|
|
6
|
+
* diagnostics that print the command line). The terminal already showed
|
|
7
|
+
* every byte to the operator who ran it; the file, by contrast, is retained
|
|
8
|
+
* twenty deep and gets attached to bug reports. So the file gets a narrow
|
|
9
|
+
* masking pass and the terminal does not.
|
|
10
|
+
*
|
|
11
|
+
* Deliberately minimal and pattern-based — see {@link redactRunLogText} for
|
|
12
|
+
* the exact contract. It is a seatbelt against the common accidental leak,
|
|
13
|
+
* not a guarantee that a log is free of secrets.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Masks the obvious secret shapes in run-log text, line by line.
|
|
17
|
+
*
|
|
18
|
+
* Redacted:
|
|
19
|
+
* - the value of an env-style assignment `KEY=value` whose KEY matches
|
|
20
|
+
* `/(TOKEN|SECRET|PASSWORD|PASSWD|API_KEY|AUTH)/i` — the value up to the
|
|
21
|
+
* next whitespace, or a whole quoted string — becomes `KEY=<redacted>`;
|
|
22
|
+
* the `--flag=value` and `-DNAME=value` spellings of the same shape are
|
|
23
|
+
* covered (`--password=x` → `--password=<redacted>`);
|
|
24
|
+
* - the credential of an `Authorization:` header (`Bearer`, `Basic` and
|
|
25
|
+
* `Token` schemes keep the scheme word; anything else masks the whole
|
|
26
|
+
* value).
|
|
27
|
+
*
|
|
28
|
+
* NOT redacted: secrets that appear without one of those key names or
|
|
29
|
+
* headers (a bare token in a URL query string, a space-separated
|
|
30
|
+
* `--password foo` CLI argument, JSON `"token": "..."` fields, cookies).
|
|
31
|
+
* Line structure and every other byte pass through unchanged; `\n`, `\r\n`
|
|
32
|
+
* and lone `\r` all delimit lines, so carriage-return-repainted progress
|
|
33
|
+
* output is masked per repaint.
|
|
34
|
+
*
|
|
35
|
+
* @param text - One or more complete lines
|
|
36
|
+
* @returns The same text with masked values
|
|
37
|
+
*/
|
|
38
|
+
export declare function redactRunLogText(text: string): string;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Conservative secret redaction for the run-log FILE tee.
|
|
4
|
+
*
|
|
5
|
+
* A run log is a complete copy of mach's output, and mach echoes its
|
|
6
|
+
* environment in places (configure summaries, `mach env`, harness
|
|
7
|
+
* diagnostics that print the command line). The terminal already showed
|
|
8
|
+
* every byte to the operator who ran it; the file, by contrast, is retained
|
|
9
|
+
* twenty deep and gets attached to bug reports. So the file gets a narrow
|
|
10
|
+
* masking pass and the terminal does not.
|
|
11
|
+
*
|
|
12
|
+
* Deliberately minimal and pattern-based — see {@link redactRunLogText} for
|
|
13
|
+
* the exact contract. It is a seatbelt against the common accidental leak,
|
|
14
|
+
* not a guarantee that a log is free of secrets.
|
|
15
|
+
*/
|
|
16
|
+
/** Env-style keys whose assigned value is masked. */
|
|
17
|
+
const SECRET_KEY_PATTERN = /(TOKEN|SECRET|PASSWORD|PASSWD|API_KEY|AUTH)/i;
|
|
18
|
+
/**
|
|
19
|
+
* `KEY=value` where KEY is an identifier and the value runs to the next
|
|
20
|
+
* whitespace, or is a single/double-quoted string. The leading group keeps
|
|
21
|
+
* the assignment anchored at a token boundary so `x.y=z` is not split
|
|
22
|
+
* mid-token. A hyphen IS a boundary: mach and configure echo their argv, so
|
|
23
|
+
* `--token=…`, `--password=…` and `-DAUTH_TOKEN=…` are exactly the forms a
|
|
24
|
+
* retained log would otherwise carry verbatim (the lead keeps the dash; the
|
|
25
|
+
* key is what follows it). Empty values are left alone (nothing to hide).
|
|
26
|
+
*/
|
|
27
|
+
const ENV_ASSIGNMENT_PATTERN = /(^|[\s"'([{,;-])([A-Za-z_][A-Za-z0-9_]*)=("(?:[^"\\]|\\.)*"|'[^']*'|[^\s"',;)\]}]+)/g;
|
|
28
|
+
/** `Authorization: <scheme> <credential>` (scheme optional) in HTTP dumps. */
|
|
29
|
+
const AUTHORIZATION_HEADER_PATTERN = /(authorization\s*:\s*)(?:(bearer|basic|token)\s+)?(\S+)/gi;
|
|
30
|
+
/** Marker written in place of a masked value. */
|
|
31
|
+
const REDACTED = '<redacted>';
|
|
32
|
+
function redactLine(line) {
|
|
33
|
+
return line
|
|
34
|
+
.replace(ENV_ASSIGNMENT_PATTERN, (whole, lead, key) => SECRET_KEY_PATTERN.test(key) ? `${lead}${key}=${REDACTED}` : whole)
|
|
35
|
+
.replace(AUTHORIZATION_HEADER_PATTERN, (_whole, lead, scheme) => scheme === undefined ? `${lead}${REDACTED}` : `${lead}${scheme} ${REDACTED}`);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Masks the obvious secret shapes in run-log text, line by line.
|
|
39
|
+
*
|
|
40
|
+
* Redacted:
|
|
41
|
+
* - the value of an env-style assignment `KEY=value` whose KEY matches
|
|
42
|
+
* `/(TOKEN|SECRET|PASSWORD|PASSWD|API_KEY|AUTH)/i` — the value up to the
|
|
43
|
+
* next whitespace, or a whole quoted string — becomes `KEY=<redacted>`;
|
|
44
|
+
* the `--flag=value` and `-DNAME=value` spellings of the same shape are
|
|
45
|
+
* covered (`--password=x` → `--password=<redacted>`);
|
|
46
|
+
* - the credential of an `Authorization:` header (`Bearer`, `Basic` and
|
|
47
|
+
* `Token` schemes keep the scheme word; anything else masks the whole
|
|
48
|
+
* value).
|
|
49
|
+
*
|
|
50
|
+
* NOT redacted: secrets that appear without one of those key names or
|
|
51
|
+
* headers (a bare token in a URL query string, a space-separated
|
|
52
|
+
* `--password foo` CLI argument, JSON `"token": "..."` fields, cookies).
|
|
53
|
+
* Line structure and every other byte pass through unchanged; `\n`, `\r\n`
|
|
54
|
+
* and lone `\r` all delimit lines, so carriage-return-repainted progress
|
|
55
|
+
* output is masked per repaint.
|
|
56
|
+
*
|
|
57
|
+
* @param text - One or more complete lines
|
|
58
|
+
* @returns The same text with masked values
|
|
59
|
+
*/
|
|
60
|
+
export function redactRunLogText(text) {
|
|
61
|
+
if (text.length === 0)
|
|
62
|
+
return text;
|
|
63
|
+
// Split with a capturing group so the terminators survive the round trip;
|
|
64
|
+
// odd indices are the terminators themselves.
|
|
65
|
+
return text
|
|
66
|
+
.split(/(\r\n|\n|\r)/)
|
|
67
|
+
.map((part, index) => (index % 2 === 0 ? redactLine(part) : part))
|
|
68
|
+
.join('');
|
|
69
|
+
}
|
|
@@ -22,7 +22,11 @@
|
|
|
22
22
|
export interface RunLog {
|
|
23
23
|
/** Absolute path to the log file. */
|
|
24
24
|
path: string;
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Appends output. Complete lines pass through {@link redactRunLogText}
|
|
27
|
+
* before hitting disk; a trailing partial line is held until its newline
|
|
28
|
+
* arrives (or until `close`). Silently drops on any stream error.
|
|
29
|
+
*/
|
|
26
30
|
write: (chunk: string) => void;
|
|
27
31
|
/** Flushes and closes. Resolves even when the stream already failed. */
|
|
28
32
|
close: () => Promise<void>;
|
|
@@ -45,7 +49,14 @@ export declare function setActiveRunLog(log: RunLog | undefined): void;
|
|
|
45
49
|
export declare function getActiveRunLogPath(): string | undefined;
|
|
46
50
|
/** Appends to the active run log. A no-op when none is open. */
|
|
47
51
|
export declare function writeToActiveRunLog(chunk: string): void;
|
|
48
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Closes and clears the active run log. Resolves even when none is open.
|
|
54
|
+
*
|
|
55
|
+
* Also called from the entry point's SIGINT/SIGTERM path: the held partial
|
|
56
|
+
* line and anything queued in the stream would otherwise be lost across
|
|
57
|
+
* `process.exit`, and a killed run's last line is usually the one that
|
|
58
|
+
* says what it was doing.
|
|
59
|
+
*/
|
|
49
60
|
export declare function closeActiveRunLog(): Promise<void>;
|
|
50
61
|
/**
|
|
51
62
|
* A writable that forwards to `base` AND to the active run log.
|
package/dist/src/core/run-log.js
CHANGED
|
@@ -23,8 +23,11 @@ import { createWriteStream } from 'node:fs';
|
|
|
23
23
|
import { mkdir, readdir, unlink } from 'node:fs/promises';
|
|
24
24
|
import { join } from 'node:path';
|
|
25
25
|
import { Writable } from 'node:stream';
|
|
26
|
+
import { toError } from '../utils/errors.js';
|
|
26
27
|
import { verbose } from '../utils/logger.js';
|
|
27
28
|
import { FIREFORGE_DIR } from './config-paths.js';
|
|
29
|
+
import { redactRunLogText } from './run-log-redact.js';
|
|
30
|
+
import { fileSafeTimestamp } from './state-file.js';
|
|
28
31
|
/** Directory under `.fireforge/` holding run logs. */
|
|
29
32
|
const LOGS_DIRNAME = 'logs';
|
|
30
33
|
/**
|
|
@@ -33,20 +36,34 @@ const LOGS_DIRNAME = 'logs';
|
|
|
33
36
|
* most often needs to read together.
|
|
34
37
|
*/
|
|
35
38
|
const RETAINED_LOGS_PER_COMMAND = 20;
|
|
39
|
+
/**
|
|
40
|
+
* Cap on the partial line held back between writes. A child emitting one
|
|
41
|
+
* enormous unterminated line (a minified dump on stderr) would otherwise
|
|
42
|
+
* grow the buffer without bound — and, because each write rebuilds
|
|
43
|
+
* `pending + chunk`, quadratically — with nothing reaching disk until
|
|
44
|
+
* close. Past the cap the tail is redacted and written as-is; a `KEY=value`
|
|
45
|
+
* split exactly at the cap boundary is the accepted cost. Same figure as
|
|
46
|
+
* `line-dispatch.ts` uses for the stream matchers.
|
|
47
|
+
*/
|
|
48
|
+
const MAX_PENDING_LINE_SIZE = 1024 * 1024;
|
|
49
|
+
/**
|
|
50
|
+
* Index just past the last line terminator in `text`, or -1 when there is
|
|
51
|
+
* none. `\n` and lone `\r` both terminate — progress-bar output (mach,
|
|
52
|
+
* cargo) repaints a line with carriage returns and may never send a
|
|
53
|
+
* newline, which would otherwise pin the whole run in `pending`. A single
|
|
54
|
+
* trailing `\r` is NOT a terminator yet: it may be the first half of a
|
|
55
|
+
* `\r\n` split across chunks.
|
|
56
|
+
*/
|
|
57
|
+
function lastLineEnd(text) {
|
|
58
|
+
const lastNewline = text.lastIndexOf('\n');
|
|
59
|
+
const lastReturn = text.lastIndexOf('\r');
|
|
60
|
+
const cut = lastReturn > lastNewline && lastReturn < text.length - 1 ? lastReturn : lastNewline;
|
|
61
|
+
return cut === -1 ? -1 : cut + 1;
|
|
62
|
+
}
|
|
36
63
|
/** `.fireforge/logs` under `projectRoot`. */
|
|
37
64
|
export function getRunLogDir(projectRoot) {
|
|
38
65
|
return join(projectRoot, FIREFORGE_DIR, LOGS_DIRNAME);
|
|
39
66
|
}
|
|
40
|
-
/**
|
|
41
|
-
* A filesystem-safe, sortable timestamp: `2026-08-28T14-32-05-123Z`.
|
|
42
|
-
*
|
|
43
|
-
* Colons are not portable in filenames (Windows rejects them outright), so
|
|
44
|
-
* the ISO form is punctuated with dashes only. Lexical order still equals
|
|
45
|
-
* chronological order, which is what the pruning below relies on.
|
|
46
|
-
*/
|
|
47
|
-
function logTimestamp(now) {
|
|
48
|
-
return now.toISOString().replace(/[:.]/g, '-');
|
|
49
|
-
}
|
|
50
67
|
/**
|
|
51
68
|
* Makes room for one new log by deleting the oldest, so that at most
|
|
52
69
|
* {@link RETAINED_LOGS_PER_COMMAND} logs for `command` exist once the new
|
|
@@ -76,7 +93,7 @@ async function pruneRunLogs(dir, command) {
|
|
|
76
93
|
}));
|
|
77
94
|
}
|
|
78
95
|
catch (error) {
|
|
79
|
-
verbose(`Run-log pruning skipped: ${
|
|
96
|
+
verbose(`Run-log pruning skipped: ${toError(error).message}`);
|
|
80
97
|
}
|
|
81
98
|
}
|
|
82
99
|
/**
|
|
@@ -91,14 +108,14 @@ async function pruneRunLogs(dir, command) {
|
|
|
91
108
|
export async function openRunLog(projectRoot, command, now = new Date()) {
|
|
92
109
|
const dir = getRunLogDir(projectRoot);
|
|
93
110
|
let stream;
|
|
94
|
-
const path = join(dir, `${command}-${
|
|
111
|
+
const path = join(dir, `${command}-${fileSafeTimestamp(now)}.log`);
|
|
95
112
|
try {
|
|
96
113
|
await mkdir(dir, { recursive: true });
|
|
97
114
|
await pruneRunLogs(dir, command);
|
|
98
115
|
stream = createWriteStream(path, { flags: 'a' });
|
|
99
116
|
}
|
|
100
117
|
catch (error) {
|
|
101
|
-
verbose(`Run log unavailable (${path}): ${
|
|
118
|
+
verbose(`Run log unavailable (${path}): ${toError(error).message}`);
|
|
102
119
|
return undefined;
|
|
103
120
|
}
|
|
104
121
|
// A stream error after open (disk full, a removed directory) must not
|
|
@@ -109,19 +126,44 @@ export async function openRunLog(projectRoot, command, now = new Date()) {
|
|
|
109
126
|
broken = true;
|
|
110
127
|
verbose(`Run log write failed (${path}): ${error.message}`);
|
|
111
128
|
});
|
|
129
|
+
// Redaction is per LINE, and chunks arrive at arbitrary byte boundaries,
|
|
130
|
+
// so a `KEY=value` split across two chunks would slip past a per-chunk
|
|
131
|
+
// pass. Hold the trailing partial line until its terminator arrives (or
|
|
132
|
+
// it outgrows MAX_PENDING_LINE_SIZE, or the log is closed).
|
|
133
|
+
let pending = '';
|
|
134
|
+
const emit = (text) => {
|
|
135
|
+
if (broken || text.length === 0)
|
|
136
|
+
return;
|
|
137
|
+
try {
|
|
138
|
+
stream.write(redactRunLogText(text));
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
broken = true;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
112
144
|
return {
|
|
113
145
|
path,
|
|
114
146
|
write: (chunk) => {
|
|
115
147
|
if (broken)
|
|
116
148
|
return;
|
|
117
|
-
|
|
118
|
-
|
|
149
|
+
const buffered = pending + chunk;
|
|
150
|
+
const lineEnd = lastLineEnd(buffered);
|
|
151
|
+
if (lineEnd === -1) {
|
|
152
|
+
pending = buffered;
|
|
119
153
|
}
|
|
120
|
-
|
|
121
|
-
|
|
154
|
+
else {
|
|
155
|
+
pending = buffered.slice(lineEnd);
|
|
156
|
+
emit(buffered.slice(0, lineEnd));
|
|
157
|
+
}
|
|
158
|
+
if (pending.length > MAX_PENDING_LINE_SIZE) {
|
|
159
|
+
emit(pending);
|
|
160
|
+
pending = '';
|
|
122
161
|
}
|
|
123
162
|
},
|
|
124
163
|
close: async () => {
|
|
164
|
+
const tail = pending;
|
|
165
|
+
pending = '';
|
|
166
|
+
emit(tail);
|
|
125
167
|
await new Promise((resolve) => {
|
|
126
168
|
stream.end(() => {
|
|
127
169
|
resolve();
|
|
@@ -155,7 +197,14 @@ export function getActiveRunLogPath() {
|
|
|
155
197
|
export function writeToActiveRunLog(chunk) {
|
|
156
198
|
activeRunLog?.write(chunk);
|
|
157
199
|
}
|
|
158
|
-
/**
|
|
200
|
+
/**
|
|
201
|
+
* Closes and clears the active run log. Resolves even when none is open.
|
|
202
|
+
*
|
|
203
|
+
* Also called from the entry point's SIGINT/SIGTERM path: the held partial
|
|
204
|
+
* line and anything queued in the stream would otherwise be lost across
|
|
205
|
+
* `process.exit`, and a killed run's last line is usually the one that
|
|
206
|
+
* says what it was doing.
|
|
207
|
+
*/
|
|
159
208
|
export async function closeActiveRunLog() {
|
|
160
209
|
const log = activeRunLog;
|
|
161
210
|
activeRunLog = undefined;
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* itself. The bin entry point is responsible for awaiting
|
|
19
19
|
* `waitForActiveCriticalSections` before terminating.
|
|
20
20
|
*/
|
|
21
|
+
import { sleep } from '../utils/sleep.js';
|
|
21
22
|
const activeSections = new Set();
|
|
22
23
|
/**
|
|
23
24
|
* Runs `fn` inside a signal-deferred critical section. The CLI entry point's
|
|
@@ -57,8 +58,5 @@ export async function waitForActiveCriticalSections(timeoutMs) {
|
|
|
57
58
|
if (activeSections.size === 0)
|
|
58
59
|
return;
|
|
59
60
|
const snapshot = [...activeSections].map((s) => s.promise);
|
|
60
|
-
await Promise.race([
|
|
61
|
-
Promise.allSettled(snapshot).then(() => undefined),
|
|
62
|
-
new Promise((resolve) => setTimeout(resolve, timeoutMs)),
|
|
63
|
-
]);
|
|
61
|
+
await Promise.race([Promise.allSettled(snapshot).then(() => undefined), sleep(timeoutMs)]);
|
|
64
62
|
}
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
* warning (e.g. `"pending JavaScript error cleanup"`) do not start with
|
|
11
11
|
* the prefix and should not trip the scanner.
|
|
12
12
|
*/
|
|
13
|
-
export declare const SMOKE_ERROR_PATTERNS: readonly RegExp[];
|
|
14
13
|
/**
|
|
15
14
|
* Returns `true` when `line` matches any pattern in
|
|
16
15
|
* {@link SMOKE_ERROR_PATTERNS}. Does not consult the allowlist — that step
|