@hominis/fireforge 0.19.3 → 0.19.5
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/README.md +1 -3
- 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 +16 -8
- 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 +16 -6
- package/dist/src/core/marionette-port.js +32 -6
- 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 +6 -5
- 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/README.md
CHANGED
|
@@ -640,9 +640,7 @@ The two flags can be combined — `--with-tests --xpcshell` writes both harnesse
|
|
|
640
640
|
|
|
641
641
|
### Mochitest stalls and `--marionette-port`
|
|
642
642
|
|
|
643
|
-
When you pass `--marionette-port <n>`, FireForge uses that port for the stale-listener probe and for
|
|
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.
|
|
643
|
+
When you pass `--marionette-port <n>`, FireForge uses that port for the **stale-listener probe** (before `mach test` runs) and for **`--doctor`**, forwards **`--setpref=marionette.port=<n>`** to `mach test` so the **browser Marionette listener** binds that port, and forwards **`--marionette=127.0.0.1:<n>`** so the **mochitest harness client** connects to the same endpoint (the client otherwise defaults to `127.0.0.1:2828`). If you already pass **`--mach-arg=--marionette=...`**, FireForge does not add a second client flag. If **`--mach-arg`** already forwards the port via **`--marionette-port=...`** or **`--setpref=marionette.port=...`**, FireForge skips duplicating the setpref (existing behaviour). The exception is an explicit **`--mach-arg --flavor=xpcshell`** (or **`--flavor=xpcshell-tests`**): that harness does not use the browser Marionette path, so FireForge skips both auto-forwarded flags 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=…`**.
|
|
646
644
|
|
|
647
645
|
### Stale-build preflight on `fireforge test`
|
|
648
646
|
|
|
@@ -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, shouldAutoForwardMarionettePortToMach, } from '../core/marionette-port.js';
|
|
6
|
+
import { assertMarionettePortAvailable, extractForwardedMarionettePort, forwardedMachArgsIncludeMarionetteClient, 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';
|
|
@@ -320,17 +320,21 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
|
|
|
320
320
|
extraArgs.push(...options.machArg);
|
|
321
321
|
}
|
|
322
322
|
// Auto-forward the Marionette port to mach when `--marionette-port` is
|
|
323
|
-
// set.
|
|
324
|
-
// listener
|
|
325
|
-
//
|
|
323
|
+
// set. `--setpref=marionette.port=<n>` configures where the browser
|
|
324
|
+
// listener binds; `--marionette=127.0.0.1:<n>` tells the mochitest harness
|
|
325
|
+
// client to connect there (default client is 127.0.0.1:2828). xpcshell
|
|
326
|
+
// ignores both for browser Marionette.
|
|
326
327
|
//
|
|
327
|
-
// Skip forwarding when the operator already supplied an equivalent
|
|
328
|
-
// via `--mach-arg` — duplicates would be confusing without changing
|
|
328
|
+
// Skip setpref forwarding when the operator already supplied an equivalent
|
|
329
|
+
// arg via `--mach-arg` — duplicates would be confusing without changing
|
|
329
330
|
// semantics. Skip when mach args explicitly request `--flavor=xpcshell`
|
|
330
331
|
// (or `xpcshell-tests`): the preflight still honours `--marionette-port`,
|
|
331
332
|
// but mach does not use the marionette.port pref on that harness. Any
|
|
332
333
|
// other arg shape still forwards so toolkit widget paths and mixed suites
|
|
333
334
|
// stay aligned with the probe without duplicate `--mach-arg` flags.
|
|
335
|
+
//
|
|
336
|
+
// Skip auto `--marionette=...` when `--mach-arg` already includes a client
|
|
337
|
+
// `--marionette=...` (or two-token `--marionette host:port`).
|
|
334
338
|
if (options.marionettePort !== undefined) {
|
|
335
339
|
const operatorAlreadyForwarded = forwardedPort !== undefined;
|
|
336
340
|
const machArgs = options.machArg ?? [];
|
|
@@ -341,7 +345,11 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
|
|
|
341
345
|
extraArgs.push(`--setpref=marionette.port=${options.marionettePort}`);
|
|
342
346
|
}
|
|
343
347
|
else {
|
|
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
|
|
348
|
+
info(`--marionette-port=${options.marionettePort} applied to the preflight probe, but --flavor=xpcshell is set — mach is not auto-configured with --setpref=marionette.port or --marionette (xpcshell ignores the browser Marionette path). Pass --mach-arg --setpref=marionette.port=${options.marionettePort} explicitly if you still need mach to see the port.`);
|
|
349
|
+
}
|
|
350
|
+
if (shouldAutoForwardMarionettePortToMach(machArgs) &&
|
|
351
|
+
!forwardedMachArgsIncludeMarionetteClient(machArgs)) {
|
|
352
|
+
extraArgs.push(`--marionette=127.0.0.1:${options.marionettePort}`);
|
|
345
353
|
}
|
|
346
354
|
}
|
|
347
355
|
// xpcshell appdir auto-injection — see src/core/xpcshell-appdir.ts for the
|
|
@@ -409,7 +417,7 @@ export function registerTest(program, { getProjectRoot, withErrorHandling }) {
|
|
|
409
417
|
acc.push(value);
|
|
410
418
|
return acc;
|
|
411
419
|
}, [])
|
|
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)
|
|
420
|
+
.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) auto-forwarded mach args: --setpref=marionette.port=<n> (browser listener) and --marionette=127.0.0.1:<n> (mochitest client). Omits the client flag when --mach-arg already sets --marionette. Use when 2828 is busy or CI assigns another port.', (raw) => {
|
|
413
421
|
const n = Number.parseInt(raw, 10);
|
|
414
422
|
if (!Number.isFinite(n) || n < 1 || n > 65535) {
|
|
415
423
|
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({
|