@hominis/fireforge 0.19.2 → 0.19.4
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 +5 -0
- package/README.md +6 -0
- package/dist/src/commands/bootstrap-checks.js +1 -1
- package/dist/src/commands/doctor-check-core.d.ts +101 -0
- package/dist/src/commands/doctor-check-core.js +32 -0
- package/dist/src/commands/doctor-furnace-manifest-sync.d.ts +1 -1
- package/dist/src/commands/doctor-furnace-manifest-sync.js +1 -1
- package/dist/src/commands/doctor-furnace.d.ts +1 -1
- package/dist/src/commands/doctor-furnace.js +1 -1
- package/dist/src/commands/doctor-working-tree.d.ts +1 -1
- package/dist/src/commands/doctor-working-tree.js +1 -1
- package/dist/src/commands/doctor.d.ts +1 -107
- package/dist/src/commands/doctor.js +1 -29
- package/dist/src/commands/furnace/index.d.ts +0 -17
- package/dist/src/commands/furnace/index.js +0 -1
- package/dist/src/commands/furnace/validate.d.ts +2 -3
- package/dist/src/commands/patch/index.d.ts +0 -6
- package/dist/src/commands/patch/index.js +0 -6
- package/dist/src/commands/test.js +10 -9
- package/dist/src/commands/wire.js +34 -32
- package/dist/src/core/config.d.ts +2 -2
- package/dist/src/core/config.js +2 -2
- package/dist/src/core/furnace-apply-helpers.js +2 -2
- package/dist/src/core/furnace-config-array-utils.d.ts +11 -0
- package/dist/src/core/furnace-config-array-utils.js +27 -0
- package/dist/src/core/furnace-config-custom.js +1 -1
- package/dist/src/core/furnace-config-tokens.js +1 -1
- package/dist/src/core/furnace-config.d.ts +0 -6
- package/dist/src/core/furnace-config.js +2 -14
- package/dist/src/core/furnace-refresh.d.ts +0 -4
- package/dist/src/core/furnace-registration-ast.d.ts +0 -1
- package/dist/src/core/furnace-registration-ast.js +0 -1
- package/dist/src/core/furnace-registration.d.ts +2 -3
- package/dist/src/core/furnace-registration.js +2 -3
- package/dist/src/core/git-file-ops.d.ts +0 -6
- package/dist/src/core/git-file-ops.js +0 -8
- package/dist/src/core/git-status.d.ts +0 -14
- package/dist/src/core/git-status.js +0 -24
- package/dist/src/core/git.d.ts +0 -1
- package/dist/src/core/mach.d.ts +2 -2
- package/dist/src/core/marionette-port.d.ts +22 -4
- package/dist/src/core/marionette-port.js +35 -8
- package/dist/src/core/patch-apply.d.ts +2 -2
- package/dist/src/core/patch-apply.js +2 -2
- package/dist/src/core/patch-lint-reexports.d.ts +1 -4
- package/dist/src/core/patch-lint-reexports.js +0 -3
- package/dist/src/core/patch-manifest.d.ts +1 -2
- package/dist/src/core/patch-manifest.js +1 -1
- package/dist/src/core/signal-critical.d.ts +0 -11
- package/dist/src/core/signal-critical.js +0 -15
- package/dist/src/core/wire-targets.d.ts +4 -4
- package/dist/src/core/wire-targets.js +4 -4
- package/dist/src/types/commands/options.d.ts +4 -3
- package/dist/src/utils/logger.d.ts +0 -5
- package/dist/src/utils/logger.js +2 -2
- package/dist/src/utils/package-root.d.ts +0 -2
- package/dist/src/utils/package-root.js +0 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
### Hardening
|
|
11
11
|
|
|
12
12
|
- **`modified-file-missing-header` — standard Mozilla MPL-2.0 block headers with wrapped line breaks.** The upstream fallback scan required a contiguous `Mozilla Public License` substring in the first few lines, so files that follow Mozilla’s usual `/* … Mozilla Public` / ` * License, v. 2.0 … */` wrap (including after Emacs/vim directive blocks) were warned despite a valid notice. `containsUpstreamLicenseText` in `src/core/license-headers.ts` now normalizes common block-comment continuation prefixes before matching, so forks need not add SPDX solely to satisfy patch lint.
|
|
13
|
+
- **`fireforge test` — `--marionette-port` auto-forward matches toolkit mochitests and mixed suites.** Auto-forward of `--setpref=marionette.port=<n>` previously keyed off a path heuristic that missed `toolkit/content/tests/**` widget HTML tests (no `/mochitest/` segment), so the preflight could use the operator’s port while mach still defaulted to **2828**. Forwarding now runs whenever `--marionette-port` is set unless `--mach-arg` explicitly includes `--flavor=xpcshell` / `xpcshell-tests` (the pref is unused there). `isMarionetteFlavor` also treats `toolkit/content/tests/` paths as Marionette-relevant unless they sit under `/tests/xpcshell/`, for consistency with other callers of that helper.
|
|
14
|
+
|
|
15
|
+
### Documentation
|
|
16
|
+
|
|
17
|
+
- **README — mochitest timeouts vs Marionette.** The Test harness section documents long idle timeouts (~370s, `TEST_END: TIMEOUT`) on fork custom chrome, `--marionette-port` behaviour with xpcshell flavor, and pointers to fork-side prefs and investigation (for example Hominis `AGENT_RULES.md`).
|
|
13
18
|
|
|
14
19
|
## 0.18.0
|
|
15
20
|
|
package/README.md
CHANGED
|
@@ -638,6 +638,12 @@ xpcshell has a chrome-URI boundary that is worth knowing before writing assertio
|
|
|
638
638
|
|
|
639
639
|
The two flags can be combined — `--with-tests --xpcshell` writes both harnesses.
|
|
640
640
|
|
|
641
|
+
### Mochitest stalls and `--marionette-port`
|
|
642
|
+
|
|
643
|
+
When you pass `--marionette-port <n>`, FireForge uses that port for the stale-listener probe and for `--doctor`, and it forwards `--setpref=marionette.port=<n>` to `mach test` so the harness binds the same port. The only exception is an explicit `--mach-arg --flavor=xpcshell` (or `--flavor=xpcshell-tests`): that harness ignores the pref, so FireForge skips auto-forward and logs a short notice instead. Toolkit widget mochitests under `toolkit/content/tests/` (for example `test_*.html` next to `browser_*.js` suites) therefore stay aligned with the probe without duplicating `--mach-arg=--setpref=marionette.port=…`.
|
|
644
|
+
|
|
645
|
+
Some forks see mochitests end with **`TEST_END: TIMEOUT`** after on the order of **370 seconds** with **no harness output** — including runs where mozinfo reports `headless: false`, so the failure is not explained by SWGL headless alone. When tests wait on custom chrome (for example a fork tile shell that never sets a `_readyForTesting` gate), the hang is **engine-side**; use your fork’s test docs and `browser.toml` defaults (for Hominis-style trees, see the fork’s `AGENT_RULES.md` for `hominis.testing.*` prefs). Operationally: ensure Marionette port **2828** is free, pass **`--marionette-port`** when you use a non-default port (FireForge keeps preflight and mach consistent as above), and narrow the failure with `--doctor` or by splitting a lighter smoke test that does not depend on full chrome init.
|
|
646
|
+
|
|
641
647
|
### Stale-build preflight on `fireforge test`
|
|
642
648
|
|
|
643
649
|
`fireforge test <path>` (without `--build`) now runs a preflight that diffs engine HEAD and the workdir against the last successful `fireforge build` (recorded at `.fireforge/last-build.json`). When packageable engine files have changed since that baseline, the command prints a single up-front warning naming the paths and pointing at `fireforge test --build`. This catches the class of failure where a newly scaffolded chrome resource or pref file is registered correctly but `obj-*/dist/` still holds the pre-edit bundle, so the test reads stale packaged artifacts and errors out with a cryptic `NS_ERROR_FILE_NOT_FOUND` inside xpcshell / mach test. The preflight is warn-only — a fork that rebuilt out-of-band (direct `./mach build`, IDE plugin, separate CI stage) is not blocked. Passing `--build` skips the preflight because the rebuild just refreshed the bundle.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
import { execFile } from 'node:child_process';
|
|
3
|
-
import { failure, warning } from './doctor.js';
|
|
3
|
+
import { failure, warning } from './doctor-check-core.js';
|
|
4
4
|
/**
|
|
5
5
|
* Scans bootstrap output for known failure patterns and returns structured
|
|
6
6
|
* issue tags. A Python traceback paired with an HTTP 403 is collapsed into
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor check types and lightweight result builders shared between
|
|
3
|
+
* `doctor.ts` and sibling check modules. Kept separate so registries can import
|
|
4
|
+
* these symbols without creating cycles through `doctor.ts`.
|
|
5
|
+
*/
|
|
6
|
+
import type { DoctorCheck, DoctorOptions } from '../types/commands/index.js';
|
|
7
|
+
import type { FireForgeConfig, FireForgeState, ProjectPaths } from '../types/config.js';
|
|
8
|
+
import type { FurnaceConfig } from '../types/furnace.js';
|
|
9
|
+
/**
|
|
10
|
+
* Shared state available to every doctor check during a single run.
|
|
11
|
+
*
|
|
12
|
+
* The context is populated lazily by the doctor runner. Individual checks
|
|
13
|
+
* can record side-observations (e.g. the parsed `fireforge.json`) into the
|
|
14
|
+
* context for later checks to consume without re-parsing.
|
|
15
|
+
*/
|
|
16
|
+
export interface DoctorCheckContext {
|
|
17
|
+
projectRoot: string;
|
|
18
|
+
paths: ProjectPaths;
|
|
19
|
+
state: FireForgeState;
|
|
20
|
+
options: DoctorOptions;
|
|
21
|
+
/**
|
|
22
|
+
* Whether the engine/ directory exists on disk. Populated before checks
|
|
23
|
+
* run so downstream checks can skip git/mach inspections cheaply.
|
|
24
|
+
*/
|
|
25
|
+
engineExists: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* The loaded project config, set by the "fireforge.json is valid" check
|
|
28
|
+
* when it succeeds. Undefined before that check runs and whenever loading
|
|
29
|
+
* failed.
|
|
30
|
+
*/
|
|
31
|
+
config: FireForgeConfig | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Whether `furnace.json` exists on disk. Populated before checks run so
|
|
34
|
+
* the furnace group can skipIf cheaply when the subsystem is not in use.
|
|
35
|
+
*/
|
|
36
|
+
furnaceConfigExists: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* The parsed furnace config, set by the "Furnace configuration" check
|
|
39
|
+
* when it succeeds. Later furnace checks read from this so they do not
|
|
40
|
+
* re-parse the file; undefined when the config could not be loaded.
|
|
41
|
+
*/
|
|
42
|
+
furnaceConfig: FurnaceConfig | undefined;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Result a check may return. A single object is the common case; an array
|
|
46
|
+
* lets a single check emit multiple related rows (e.g. the engine branch
|
|
47
|
+
* check which may report on branch + detached state together).
|
|
48
|
+
*/
|
|
49
|
+
export type CheckResult = DoctorCheck | DoctorCheck[];
|
|
50
|
+
/**
|
|
51
|
+
* Declarative definition of a single doctor check.
|
|
52
|
+
*
|
|
53
|
+
* Every check opts into the shared execution/reporting pipeline by
|
|
54
|
+
* implementing only its inspection logic in `run`. Cross-cutting concerns
|
|
55
|
+
* (result aggregation, summary, exit codes) live in the runner instead of
|
|
56
|
+
* being duplicated at each call site.
|
|
57
|
+
*/
|
|
58
|
+
export interface DoctorCheckDefinition {
|
|
59
|
+
/**
|
|
60
|
+
* Human-readable name surfaced in the check report (e.g. "Git installed").
|
|
61
|
+
* Not required to be unique, but tests assert on it.
|
|
62
|
+
*/
|
|
63
|
+
name: string;
|
|
64
|
+
/**
|
|
65
|
+
* When `true`, the check is silently skipped. Used for checks that only
|
|
66
|
+
* apply when the engine is present, or only when specific state flags
|
|
67
|
+
* are set. Skipped checks contribute nothing to the final report.
|
|
68
|
+
*/
|
|
69
|
+
skipIf?: (ctx: DoctorCheckContext) => boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Names of checks that must appear earlier in the registry and run before
|
|
72
|
+
* this check. Enforced at startup via validateCheckDependencies so
|
|
73
|
+
* accidental reorders surface immediately instead of producing subtle
|
|
74
|
+
* context-population bugs at runtime.
|
|
75
|
+
*/
|
|
76
|
+
dependsOn?: readonly string[];
|
|
77
|
+
/**
|
|
78
|
+
* Runs the inspection. Throwing is shorthand for "this check failed with
|
|
79
|
+
* severity 'error'" — the runner converts the exception message into a
|
|
80
|
+
* DoctorCheck. Returning a DoctorCheck (or array) lets the check control
|
|
81
|
+
* severity, warnings, and fix hints directly.
|
|
82
|
+
*/
|
|
83
|
+
run: (ctx: DoctorCheckContext) => CheckResult | Promise<CheckResult>;
|
|
84
|
+
/**
|
|
85
|
+
* Optional recovery hint attached to the auto-generated failure result
|
|
86
|
+
* when `run` throws. Ignored when `run` returns a DoctorCheck explicitly.
|
|
87
|
+
*/
|
|
88
|
+
fix?: string;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Builds a DoctorCheck object representing a successful "OK" check.
|
|
92
|
+
*/
|
|
93
|
+
export declare function ok(name: string): DoctorCheck;
|
|
94
|
+
/**
|
|
95
|
+
* Builds a DoctorCheck object representing a warning result.
|
|
96
|
+
*/
|
|
97
|
+
export declare function warning(name: string, message: string, fix?: string): DoctorCheck;
|
|
98
|
+
/**
|
|
99
|
+
* Builds a DoctorCheck object representing a failure result.
|
|
100
|
+
*/
|
|
101
|
+
export declare function failure(name: string, message: string, fix?: string): DoctorCheck;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Doctor check types and lightweight result builders shared between
|
|
4
|
+
* `doctor.ts` and sibling check modules. Kept separate so registries can import
|
|
5
|
+
* these symbols without creating cycles through `doctor.ts`.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Builds a DoctorCheck object representing a successful "OK" check.
|
|
9
|
+
*/
|
|
10
|
+
export function ok(name) {
|
|
11
|
+
return { name, passed: true, severity: 'ok', message: 'OK' };
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Builds a DoctorCheck object representing a warning result.
|
|
15
|
+
*/
|
|
16
|
+
export function warning(name, message, fix) {
|
|
17
|
+
return {
|
|
18
|
+
name,
|
|
19
|
+
passed: true,
|
|
20
|
+
severity: 'warning',
|
|
21
|
+
warning: true,
|
|
22
|
+
message,
|
|
23
|
+
...(fix ? { fix } : {}),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Builds a DoctorCheck object representing a failure result.
|
|
28
|
+
*/
|
|
29
|
+
export function failure(name, message, fix) {
|
|
30
|
+
return { name, passed: false, severity: 'error', message, ...(fix ? { fix } : {}) };
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=doctor-check-core.js.map
|
|
@@ -14,5 +14,5 @@
|
|
|
14
14
|
* Lives in a sibling module to keep `doctor-furnace.ts` under the
|
|
15
15
|
* per-file LOC budget.
|
|
16
16
|
*/
|
|
17
|
-
import type { DoctorCheckDefinition } from './doctor.js';
|
|
17
|
+
import type { DoctorCheckDefinition } from './doctor-check-core.js';
|
|
18
18
|
export declare const furnaceManifestSyncCheck: DoctorCheckDefinition;
|
|
@@ -20,7 +20,7 @@ import { join } from 'node:path';
|
|
|
20
20
|
import { getFurnacePaths, loadFurnaceConfig, writeFurnaceConfig } from '../core/furnace-config.js';
|
|
21
21
|
import { toError } from '../utils/errors.js';
|
|
22
22
|
import { pathExists, readJson } from '../utils/fs.js';
|
|
23
|
-
import { failure, ok, warning } from './doctor.js';
|
|
23
|
+
import { failure, ok, warning } from './doctor-check-core.js';
|
|
24
24
|
async function listComponentDirs(dir) {
|
|
25
25
|
if (!(await pathExists(dir)))
|
|
26
26
|
return [];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DoctorCheckDefinition } from './doctor.js';
|
|
1
|
+
import type { DoctorCheckDefinition } from './doctor-check-core.js';
|
|
2
2
|
/**
|
|
3
3
|
* The ordered furnace check group. Exported as an array so `doctor.ts`
|
|
4
4
|
* can splice it into the main registry at the right position. The order
|
|
@@ -9,7 +9,7 @@ import { getFurnaceLockPath, runFurnaceMutation } from '../core/furnace-operatio
|
|
|
9
9
|
import { validateAllComponents } from '../core/furnace-validate.js';
|
|
10
10
|
import { toError } from '../utils/errors.js';
|
|
11
11
|
import { pathExists } from '../utils/fs.js';
|
|
12
|
-
import { failure, ok, warning } from './doctor.js';
|
|
12
|
+
import { failure, ok, warning } from './doctor-check-core.js';
|
|
13
13
|
import { furnaceManifestSyncCheck } from './doctor-furnace-manifest-sync.js';
|
|
14
14
|
const ENGINE_REPAIRABLE_OPERATIONS = [
|
|
15
15
|
'preview-teardown',
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* budget; see the call site in `runEngineGitChecks`.
|
|
11
11
|
*/
|
|
12
12
|
import type { DoctorCheck } from '../types/commands/index.js';
|
|
13
|
-
import type { DoctorCheckContext } from './doctor.js';
|
|
13
|
+
import type { DoctorCheckContext } from './doctor-check-core.js';
|
|
14
14
|
/**
|
|
15
15
|
* Inspects the engine working tree and returns a single
|
|
16
16
|
* `DoctorCheck`. Ownership-aware: patch-backed / branding / furnace
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { collectFurnaceManagedPrefixes } from '../core/furnace-config.js';
|
|
14
14
|
import { expandUntrackedDirectoryEntries, getWorkingTreeStatus } from '../core/git-status.js';
|
|
15
15
|
import { classifyFiles } from '../core/status-classify.js';
|
|
16
|
-
import { ok, warning } from './doctor.js';
|
|
16
|
+
import { ok, warning } from './doctor-check-core.js';
|
|
17
17
|
function summarizeWorkingTreeChangeCount(changeCount) {
|
|
18
18
|
return `Engine working tree has ${changeCount} local change${changeCount === 1 ? '' : 's'}. Some FireForge commands assume a clean baseline and may behave differently until these are exported, discarded, or committed.`;
|
|
19
19
|
}
|
|
@@ -2,113 +2,7 @@ import { Command } from 'commander';
|
|
|
2
2
|
import { ExitCode } from '../errors/codes.js';
|
|
3
3
|
import type { CommandContext } from '../types/cli.js';
|
|
4
4
|
import type { DoctorCheck, DoctorOptions } from '../types/commands/index.js';
|
|
5
|
-
import type {
|
|
6
|
-
import type { FurnaceConfig } from '../types/furnace.js';
|
|
7
|
-
/**
|
|
8
|
-
* Shared state available to every doctor check during a single run.
|
|
9
|
-
*
|
|
10
|
-
* The context is populated lazily by the doctor runner. Individual checks
|
|
11
|
-
* can record side-observations (e.g. the parsed `fireforge.json`) into the
|
|
12
|
-
* context for later checks to consume without re-parsing.
|
|
13
|
-
*
|
|
14
|
-
* Exported so sibling modules (e.g. `doctor-furnace.ts`) can declare
|
|
15
|
-
* `DoctorCheckDefinition` entries against the same shared context.
|
|
16
|
-
*/
|
|
17
|
-
export interface DoctorCheckContext {
|
|
18
|
-
projectRoot: string;
|
|
19
|
-
paths: ProjectPaths;
|
|
20
|
-
state: FireForgeState;
|
|
21
|
-
options: DoctorOptions;
|
|
22
|
-
/**
|
|
23
|
-
* Whether the engine/ directory exists on disk. Populated before checks
|
|
24
|
-
* run so downstream checks can skip git/mach inspections cheaply.
|
|
25
|
-
*/
|
|
26
|
-
engineExists: boolean;
|
|
27
|
-
/**
|
|
28
|
-
* The loaded project config, set by the "fireforge.json is valid" check
|
|
29
|
-
* when it succeeds. Undefined before that check runs and whenever loading
|
|
30
|
-
* failed.
|
|
31
|
-
*/
|
|
32
|
-
config: FireForgeConfig | undefined;
|
|
33
|
-
/**
|
|
34
|
-
* Whether `furnace.json` exists on disk. Populated before checks run so
|
|
35
|
-
* the furnace group can skipIf cheaply when the subsystem is not in use.
|
|
36
|
-
* A missing furnace.json is not an error — plenty of projects never touch
|
|
37
|
-
* the subsystem — so the doctor stays silent rather than failing.
|
|
38
|
-
*/
|
|
39
|
-
furnaceConfigExists: boolean;
|
|
40
|
-
/**
|
|
41
|
-
* The parsed furnace config, set by the "Furnace configuration" check
|
|
42
|
-
* when it succeeds. Later furnace checks read from this so they do not
|
|
43
|
-
* re-parse the file; undefined when the config could not be loaded.
|
|
44
|
-
*/
|
|
45
|
-
furnaceConfig: FurnaceConfig | undefined;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Result a check may return. A single object is the common case; an array
|
|
49
|
-
* lets a single check emit multiple related rows (e.g. the engine branch
|
|
50
|
-
* check which may report on branch + detached state together).
|
|
51
|
-
*/
|
|
52
|
-
export type CheckResult = DoctorCheck | DoctorCheck[];
|
|
53
|
-
/**
|
|
54
|
-
* Declarative definition of a single doctor check.
|
|
55
|
-
*
|
|
56
|
-
* Every check opts into the shared execution/reporting pipeline by
|
|
57
|
-
* implementing only its inspection logic in `run`. Cross-cutting concerns
|
|
58
|
-
* (result aggregation, summary, exit codes) live in the runner instead of
|
|
59
|
-
* being duplicated at each call site.
|
|
60
|
-
*
|
|
61
|
-
* Exported so sibling modules (e.g. `doctor-furnace.ts`) can declare
|
|
62
|
-
* new checks without re-deriving the shape.
|
|
63
|
-
*/
|
|
64
|
-
export interface DoctorCheckDefinition {
|
|
65
|
-
/**
|
|
66
|
-
* Human-readable name surfaced in the check report (e.g. "Git installed").
|
|
67
|
-
* Not required to be unique, but tests assert on it.
|
|
68
|
-
*/
|
|
69
|
-
name: string;
|
|
70
|
-
/**
|
|
71
|
-
* When `true`, the check is silently skipped. Used for checks that only
|
|
72
|
-
* apply when the engine is present, or only when specific state flags
|
|
73
|
-
* are set. Skipped checks contribute nothing to the final report.
|
|
74
|
-
*/
|
|
75
|
-
skipIf?: (ctx: DoctorCheckContext) => boolean;
|
|
76
|
-
/**
|
|
77
|
-
* Names of checks that must appear earlier in the registry and run before
|
|
78
|
-
* this check. Enforced at startup via {@link validateCheckDependencies} so
|
|
79
|
-
* accidental reorders surface immediately instead of producing subtle
|
|
80
|
-
* context-population bugs at runtime.
|
|
81
|
-
*/
|
|
82
|
-
dependsOn?: readonly string[];
|
|
83
|
-
/**
|
|
84
|
-
* Runs the inspection. Throwing is shorthand for "this check failed with
|
|
85
|
-
* severity 'error'" — the runner converts the exception message into a
|
|
86
|
-
* DoctorCheck. Returning a DoctorCheck (or array) lets the check control
|
|
87
|
-
* severity, warnings, and fix hints directly.
|
|
88
|
-
*/
|
|
89
|
-
run: (ctx: DoctorCheckContext) => CheckResult | Promise<CheckResult>;
|
|
90
|
-
/**
|
|
91
|
-
* Optional recovery hint attached to the auto-generated failure result
|
|
92
|
-
* when `run` throws. Ignored when `run` returns a DoctorCheck explicitly.
|
|
93
|
-
*/
|
|
94
|
-
fix?: string;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Builds a DoctorCheck object representing a successful "OK" check.
|
|
98
|
-
* Exported for sibling check modules that declare `DoctorCheckDefinition`
|
|
99
|
-
* entries out-of-file (e.g. `doctor-furnace.ts`).
|
|
100
|
-
*/
|
|
101
|
-
export declare function ok(name: string): DoctorCheck;
|
|
102
|
-
/**
|
|
103
|
-
* Builds a DoctorCheck object representing a warning result.
|
|
104
|
-
* Exported for sibling check modules — see {@link ok}.
|
|
105
|
-
*/
|
|
106
|
-
export declare function warning(name: string, message: string, fix?: string): DoctorCheck;
|
|
107
|
-
/**
|
|
108
|
-
* Builds a DoctorCheck object representing a failure result.
|
|
109
|
-
* Exported for sibling check modules — see {@link ok}.
|
|
110
|
-
*/
|
|
111
|
-
export declare function failure(name: string, message: string, fix?: string): DoctorCheck;
|
|
5
|
+
import type { DoctorCheckDefinition } from './doctor-check-core.js';
|
|
112
6
|
/**
|
|
113
7
|
* Validates that every check's `dependsOn` entries appear earlier in the
|
|
114
8
|
* registry. Called once at module load time so a broken reorder surfaces
|
|
@@ -10,37 +10,9 @@ import { toError } from '../utils/errors.js';
|
|
|
10
10
|
import { pathExists } from '../utils/fs.js';
|
|
11
11
|
import { error, info, intro, outro, success, warn } from '../utils/logger.js';
|
|
12
12
|
import { findExecutable } from '../utils/process.js';
|
|
13
|
+
import { failure, ok, warning } from './doctor-check-core.js';
|
|
13
14
|
import { FURNACE_DOCTOR_CHECKS } from './doctor-furnace.js';
|
|
14
15
|
import { inspectEngineWorkingTree } from './doctor-working-tree.js';
|
|
15
|
-
/**
|
|
16
|
-
* Builds a DoctorCheck object representing a successful "OK" check.
|
|
17
|
-
* Exported for sibling check modules that declare `DoctorCheckDefinition`
|
|
18
|
-
* entries out-of-file (e.g. `doctor-furnace.ts`).
|
|
19
|
-
*/
|
|
20
|
-
export function ok(name) {
|
|
21
|
-
return { name, passed: true, severity: 'ok', message: 'OK' };
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Builds a DoctorCheck object representing a warning result.
|
|
25
|
-
* Exported for sibling check modules — see {@link ok}.
|
|
26
|
-
*/
|
|
27
|
-
export function warning(name, message, fix) {
|
|
28
|
-
return {
|
|
29
|
-
name,
|
|
30
|
-
passed: true,
|
|
31
|
-
severity: 'warning',
|
|
32
|
-
warning: true,
|
|
33
|
-
message,
|
|
34
|
-
...(fix ? { fix } : {}),
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Builds a DoctorCheck object representing a failure result.
|
|
39
|
-
* Exported for sibling check modules — see {@link ok}.
|
|
40
|
-
*/
|
|
41
|
-
export function failure(name, message, fix) {
|
|
42
|
-
return { name, passed: false, severity: 'error', message, ...(fix ? { fix } : {}) };
|
|
43
|
-
}
|
|
44
16
|
/**
|
|
45
17
|
* Runs a single check definition, converting thrown errors into
|
|
46
18
|
* DoctorCheck failure rows. Always returns an array so the caller can
|
|
@@ -1,21 +1,4 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import type { CommandContext } from '../../types/cli.js';
|
|
3
|
-
import { furnaceApplyCommand } from './apply.js';
|
|
4
|
-
import { furnaceChromeDocCreateCommand } from './chrome-doc.js';
|
|
5
|
-
import { furnaceCreateCommand } from './create.js';
|
|
6
|
-
import { furnaceDeployCommand } from './deploy.js';
|
|
7
|
-
import { furnaceDiffCommand } from './diff.js';
|
|
8
|
-
import { furnaceInitCommand } from './init.js';
|
|
9
|
-
import { furnaceListCommand } from './list.js';
|
|
10
|
-
import { furnaceBatchOverrideCommand, furnaceOverrideCommand } from './override.js';
|
|
11
|
-
import { furnacePreviewCommand } from './preview.js';
|
|
12
|
-
import { furnaceRefreshCommand } from './refresh.js';
|
|
13
|
-
import { furnaceRemoveCommand } from './remove.js';
|
|
14
|
-
import { furnaceRenameCommand } from './rename.js';
|
|
15
|
-
import { furnaceScanCommand } from './scan.js';
|
|
16
|
-
import { furnaceStatusCommand } from './status.js';
|
|
17
|
-
import { furnaceSyncCommand } from './sync.js';
|
|
18
|
-
import { furnaceValidateCommand } from './validate.js';
|
|
19
|
-
export { furnaceApplyCommand, furnaceBatchOverrideCommand, furnaceChromeDocCreateCommand, furnaceCreateCommand, furnaceDeployCommand, furnaceDiffCommand, furnaceInitCommand, furnaceListCommand, furnaceOverrideCommand, furnacePreviewCommand, furnaceRefreshCommand, furnaceRemoveCommand, furnaceRenameCommand, furnaceScanCommand, furnaceStatusCommand, furnaceSyncCommand, furnaceValidateCommand, };
|
|
20
3
|
/** Registers the furnace command on the CLI program. */
|
|
21
4
|
export declare function registerFurnace(program: Command, context: CommandContext): void;
|
|
@@ -17,7 +17,6 @@ import { furnaceScanCommand } from './scan.js';
|
|
|
17
17
|
import { furnaceStatusCommand } from './status.js';
|
|
18
18
|
import { furnaceSyncCommand } from './sync.js';
|
|
19
19
|
import { furnaceValidateCommand } from './validate.js';
|
|
20
|
-
export { furnaceApplyCommand, furnaceBatchOverrideCommand, furnaceChromeDocCreateCommand, furnaceCreateCommand, furnaceDeployCommand, furnaceDiffCommand, furnaceInitCommand, furnaceListCommand, furnaceOverrideCommand, furnacePreviewCommand, furnaceRefreshCommand, furnaceRemoveCommand, furnaceRenameCommand, furnaceScanCommand, furnaceStatusCommand, furnaceSyncCommand, furnaceValidateCommand, };
|
|
21
20
|
/**
|
|
22
21
|
* Registers Furnace commands for querying component state: status, scan,
|
|
23
22
|
* and action commands like apply, deploy, and create.
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
import type { FurnaceValidateOptions } from '../../types/commands/index.js';
|
|
1
2
|
/**
|
|
2
3
|
* Runs the furnace validate command to perform static analysis on components.
|
|
3
4
|
* @param projectRoot - Root directory of the project
|
|
4
5
|
* @param name - Optional component name to validate (validates all if omitted)
|
|
5
6
|
* @param options - Optional command options (e.g. --fix)
|
|
6
7
|
*/
|
|
7
|
-
export declare function furnaceValidateCommand(projectRoot: string, name?: string, options?:
|
|
8
|
-
fix?: boolean;
|
|
9
|
-
}): Promise<void>;
|
|
8
|
+
export declare function furnaceValidateCommand(projectRoot: string, name?: string, options?: FurnaceValidateOptions): Promise<void>;
|
|
@@ -7,12 +7,6 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { Command } from 'commander';
|
|
9
9
|
import type { CommandContext } from '../../types/cli.js';
|
|
10
|
-
export { patchCompactCommand } from './compact.js';
|
|
11
|
-
export { patchDeleteCommand } from './delete.js';
|
|
12
|
-
export { patchLintIgnoreCommand } from './lint-ignore.js';
|
|
13
|
-
export { patchRenameCommand } from './rename.js';
|
|
14
|
-
export { patchReorderCommand } from './reorder.js';
|
|
15
|
-
export { patchTierCommand } from './tier.js';
|
|
16
10
|
/**
|
|
17
11
|
* Registers the `patch` subcommand parent and its verbs on the CLI.
|
|
18
12
|
*
|
|
@@ -12,12 +12,6 @@ import { registerPatchLintIgnore } from './lint-ignore.js';
|
|
|
12
12
|
import { registerPatchRename } from './rename.js';
|
|
13
13
|
import { registerPatchReorder } from './reorder.js';
|
|
14
14
|
import { registerPatchTier } from './tier.js';
|
|
15
|
-
export { patchCompactCommand } from './compact.js';
|
|
16
|
-
export { patchDeleteCommand } from './delete.js';
|
|
17
|
-
export { patchLintIgnoreCommand } from './lint-ignore.js';
|
|
18
|
-
export { patchRenameCommand } from './rename.js';
|
|
19
|
-
export { patchReorderCommand } from './reorder.js';
|
|
20
|
-
export { patchTierCommand } from './tier.js';
|
|
21
15
|
/**
|
|
22
16
|
* Registers the `patch` subcommand parent and its verbs on the CLI.
|
|
23
17
|
*
|
|
@@ -3,7 +3,7 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { prepareBuildEnvironment } from '../core/build-prepare.js';
|
|
4
4
|
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
5
5
|
import { buildArtifactMismatchMessage, buildUI, hasBuildArtifacts, hasRunnableBundle, testWithOutput, } from '../core/mach.js';
|
|
6
|
-
import { assertMarionettePortAvailable, extractForwardedMarionettePort,
|
|
6
|
+
import { assertMarionettePortAvailable, extractForwardedMarionettePort, shouldAutoForwardMarionettePortToMach, } from '../core/marionette-port.js';
|
|
7
7
|
import { formatMarionettePreflightLine, reportMarionettePreflight, runMarionettePreflight, } from '../core/marionette-preflight.js';
|
|
8
8
|
import { checkStaleBuildForTest, formatStaleBuildWarning } from '../core/test-stale-check.js';
|
|
9
9
|
import { operatorAlreadySetAppPath, resolveXpcshellAppdirArg, } from '../core/xpcshell-appdir.js';
|
|
@@ -326,21 +326,22 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
|
|
|
326
326
|
//
|
|
327
327
|
// Skip forwarding when the operator already supplied an equivalent arg
|
|
328
328
|
// via `--mach-arg` — duplicates would be confusing without changing
|
|
329
|
-
// semantics. Skip
|
|
330
|
-
// (xpcshell
|
|
331
|
-
//
|
|
332
|
-
//
|
|
333
|
-
//
|
|
329
|
+
// semantics. Skip when mach args explicitly request `--flavor=xpcshell`
|
|
330
|
+
// (or `xpcshell-tests`): the preflight still honours `--marionette-port`,
|
|
331
|
+
// but mach does not use the marionette.port pref on that harness. Any
|
|
332
|
+
// other arg shape still forwards so toolkit widget paths and mixed suites
|
|
333
|
+
// stay aligned with the probe without duplicate `--mach-arg` flags.
|
|
334
334
|
if (options.marionettePort !== undefined) {
|
|
335
335
|
const operatorAlreadyForwarded = forwardedPort !== undefined;
|
|
336
|
+
const machArgs = options.machArg ?? [];
|
|
336
337
|
if (operatorAlreadyForwarded) {
|
|
337
338
|
info(`--marionette-port=${options.marionettePort} set, but the same port is already forwarded via --mach-arg; skipping auto-forward.`);
|
|
338
339
|
}
|
|
339
|
-
else if (
|
|
340
|
+
else if (shouldAutoForwardMarionettePortToMach(machArgs)) {
|
|
340
341
|
extraArgs.push(`--setpref=marionette.port=${options.marionettePort}`);
|
|
341
342
|
}
|
|
342
343
|
else {
|
|
343
|
-
info(`--marionette-port=${options.marionettePort} applied to the preflight probe, but
|
|
344
|
+
info(`--marionette-port=${options.marionettePort} applied to the preflight probe, but --flavor=xpcshell is set — mach is not auto-configured with --setpref=marionette.port (xpcshell ignores that pref). Pass --mach-arg --setpref=marionette.port=${options.marionettePort} explicitly if you still need mach to see the port.`);
|
|
344
345
|
}
|
|
345
346
|
}
|
|
346
347
|
// xpcshell appdir auto-injection — see src/core/xpcshell-appdir.ts for the
|
|
@@ -408,7 +409,7 @@ export function registerTest(program, { getProjectRoot, withErrorHandling }) {
|
|
|
408
409
|
acc.push(value);
|
|
409
410
|
return acc;
|
|
410
411
|
}, [])
|
|
411
|
-
.option('--marionette-port <port>', 'Override the Marionette control port (default 2828) for the stale-browser probe, the --doctor preflight, and the auto-forwarded --setpref=marionette.port=<n>
|
|
412
|
+
.option('--marionette-port <port>', 'Override the Marionette control port (default 2828) for the stale-browser probe, the --doctor preflight, and (unless --mach-arg includes --flavor=xpcshell) the auto-forwarded --setpref=marionette.port=<n> passed to mach. Use this when a stale process holds 2828 or a CI runner reserves a different port.', (raw) => {
|
|
412
413
|
const n = Number.parseInt(raw, 10);
|
|
413
414
|
if (!Number.isFinite(n) || n < 1 || n > 65535) {
|
|
414
415
|
throw new GeneralError(`--marionette-port must be an integer in 1..65535 (got "${raw}")`);
|
|
@@ -111,6 +111,38 @@ async function assertDomTargetIsWireable(projectRoot, domFilePath, domTargetPath
|
|
|
111
111
|
'`--target <path>`.', 'target');
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
+
async function resolveWireSubscriptDir(projectRoot, options) {
|
|
115
|
+
let subscriptDir = DEFAULT_BROWSER_SUBSCRIPT_DIR;
|
|
116
|
+
try {
|
|
117
|
+
const config = await loadConfig(projectRoot);
|
|
118
|
+
if (config.wire?.subscriptDir) {
|
|
119
|
+
subscriptDir = config.wire.subscriptDir;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
warn(`Using default wire.subscriptDir because fireforge.json could not be loaded: ${toError(error).message}`);
|
|
124
|
+
}
|
|
125
|
+
if (options.subscriptDir) {
|
|
126
|
+
if (!isContainedRelativePath(options.subscriptDir)) {
|
|
127
|
+
throw new InvalidArgumentError(`Subscript directory must stay within engine/: ${options.subscriptDir}`, 'subscriptDir');
|
|
128
|
+
}
|
|
129
|
+
subscriptDir = options.subscriptDir;
|
|
130
|
+
}
|
|
131
|
+
return subscriptDir;
|
|
132
|
+
}
|
|
133
|
+
async function ensureSubscriptSourceExists(projectRoot, subscriptDir, name, dryRun) {
|
|
134
|
+
const paths = getProjectPaths(projectRoot);
|
|
135
|
+
const subscriptPath = join(paths.engine, subscriptDir, `${name}.js`);
|
|
136
|
+
if (!(await pathExists(subscriptPath))) {
|
|
137
|
+
if (dryRun) {
|
|
138
|
+
info(`Note: ${subscriptDir}/${name}.js does not exist yet — the real wire command will require it before writing. Create the file before re-running without --dry-run.`);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
throw new InvalidArgumentError(`Subscript file not found: ${subscriptDir}/${name}.js\n` +
|
|
142
|
+
'Create the file in engine/ before wiring.', 'name');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
114
146
|
/**
|
|
115
147
|
* Wires a chrome subscript into the browser.
|
|
116
148
|
*
|
|
@@ -144,23 +176,7 @@ export async function wireCommand(projectRoot, name, options = {}) {
|
|
|
144
176
|
validateWireExpression(options.destroy, 'destroy expression');
|
|
145
177
|
}
|
|
146
178
|
consumeParserFallbackEvents();
|
|
147
|
-
|
|
148
|
-
let subscriptDir = DEFAULT_BROWSER_SUBSCRIPT_DIR;
|
|
149
|
-
try {
|
|
150
|
-
const config = await loadConfig(projectRoot);
|
|
151
|
-
if (config.wire?.subscriptDir) {
|
|
152
|
-
subscriptDir = config.wire.subscriptDir;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
catch (error) {
|
|
156
|
-
warn(`Using default wire.subscriptDir because fireforge.json could not be loaded: ${toError(error).message}`);
|
|
157
|
-
}
|
|
158
|
-
if (options.subscriptDir) {
|
|
159
|
-
if (!isContainedRelativePath(options.subscriptDir)) {
|
|
160
|
-
throw new InvalidArgumentError(`Subscript directory must stay within engine/: ${options.subscriptDir}`, 'subscriptDir');
|
|
161
|
-
}
|
|
162
|
-
subscriptDir = options.subscriptDir;
|
|
163
|
-
}
|
|
179
|
+
const subscriptDir = await resolveWireSubscriptDir(projectRoot, options);
|
|
164
180
|
// Validate DOM fragment file exists and compute path relative to engine root.
|
|
165
181
|
//
|
|
166
182
|
// Accepts three shapes:
|
|
@@ -235,21 +251,7 @@ export async function wireCommand(projectRoot, name, options = {}) {
|
|
|
235
251
|
// plausible plan and the non-dry-run invocation then errored. The
|
|
236
252
|
// info line surfaces the mismatch in preview mode so the operator
|
|
237
253
|
// can act on the warning before re-running without --dry-run.
|
|
238
|
-
|
|
239
|
-
const paths = getProjectPaths(projectRoot);
|
|
240
|
-
const subscriptPath = join(paths.engine, subscriptDir, `${name}.js`);
|
|
241
|
-
if (!(await pathExists(subscriptPath))) {
|
|
242
|
-
throw new InvalidArgumentError(`Subscript file not found: ${subscriptDir}/${name}.js\n` +
|
|
243
|
-
'Create the file in engine/ before wiring.', 'name');
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
else {
|
|
247
|
-
const paths = getProjectPaths(projectRoot);
|
|
248
|
-
const subscriptPath = join(paths.engine, subscriptDir, `${name}.js`);
|
|
249
|
-
if (!(await pathExists(subscriptPath))) {
|
|
250
|
-
info(`Note: ${subscriptDir}/${name}.js does not exist yet — the real wire command will require it before writing. Create the file before re-running without --dry-run.`);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
254
|
+
await ensureSubscriptSourceExists(projectRoot, subscriptDir, name, options.dryRun ?? false);
|
|
253
255
|
if (options.dryRun) {
|
|
254
256
|
printWireDryRun(getProjectPaths(projectRoot).engine, name, subscriptDir, domFilePath, domTargetPath, options);
|
|
255
257
|
return;
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { FireForgeConfig } from '../types/config.js';
|
|
11
11
|
export { mutateConfig } from './config-mutate.js';
|
|
12
|
-
export { CONFIG_FILENAME,
|
|
13
|
-
export { loadState, saveState, updateState
|
|
12
|
+
export { CONFIG_FILENAME, FIREFORGE_DIR, getProjectPaths, STATE_FILENAME, SUPPORTED_CONFIG_PATHS, SUPPORTED_CONFIG_ROOT_KEYS, } from './config-paths.js';
|
|
13
|
+
export { loadState, saveState, updateState } from './config-state.js';
|
|
14
14
|
export { validateConfig } from './config-validate.js';
|
|
15
15
|
/**
|
|
16
16
|
* Checks if a fireforge.json exists in the given directory.
|
package/dist/src/core/config.js
CHANGED
|
@@ -17,8 +17,8 @@ import { validateConfig } from './config-validate.js';
|
|
|
17
17
|
import { createSiblingLockPath, withFileLock } from './file-lock.js';
|
|
18
18
|
// ---- re-exports ----
|
|
19
19
|
export { mutateConfig } from './config-mutate.js';
|
|
20
|
-
export { CONFIG_FILENAME,
|
|
21
|
-
export { loadState, saveState, updateState
|
|
20
|
+
export { CONFIG_FILENAME, FIREFORGE_DIR, getProjectPaths, STATE_FILENAME, SUPPORTED_CONFIG_PATHS, SUPPORTED_CONFIG_ROOT_KEYS, } from './config-paths.js';
|
|
21
|
+
export { loadState, saveState, updateState } from './config-state.js';
|
|
22
22
|
export { validateConfig } from './config-validate.js';
|
|
23
23
|
// ---- config I/O (stays here because it bridges paths + validation) ----
|
|
24
24
|
/**
|
|
@@ -8,7 +8,7 @@ import { copyFile, ensureDir, pathExists, readText, removeFile } from '../utils/
|
|
|
8
8
|
import { verbose } from '../utils/logger.js';
|
|
9
9
|
import { applyCustomFtlFile, describeLocaleFtlJarMnRegistration, removeCustomFtlJarMnEntry, } from './furnace-apply-ftl.js';
|
|
10
10
|
import { CUSTOM_ELEMENTS_JS, JAR_MN } from './furnace-constants.js';
|
|
11
|
-
import { addCustomElementRegistration, addJarMnEntries, validateCustomElementRegistration,
|
|
11
|
+
import { addCustomElementRegistration, addJarMnEntries, validateCustomElementRegistration, validateJarMnInsertionForFiles, } from './furnace-registration.js';
|
|
12
12
|
import { recordCreatedDir, snapshotFile } from './furnace-rollback.js';
|
|
13
13
|
import { checkRegistrationConsistency } from './furnace-validate-registration.js';
|
|
14
14
|
import { isGitRepository } from './git.js';
|
|
@@ -352,7 +352,7 @@ async function buildCustomDryRunActions(name, componentDir, engineDir, config, t
|
|
|
352
352
|
.map((entry) => entry.name);
|
|
353
353
|
if (copiedFileNames.length > 0) {
|
|
354
354
|
try {
|
|
355
|
-
await
|
|
355
|
+
await validateJarMnInsertionForFiles(engineDir, name, copiedFileNames);
|
|
356
356
|
}
|
|
357
357
|
catch (error) {
|
|
358
358
|
stepErrors.push({
|