@hominis/fireforge 0.15.6 → 0.15.8
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 +78 -0
- package/README.md +158 -15
- package/dist/src/commands/build.js +60 -3
- package/dist/src/commands/furnace/chrome-doc-templates.d.ts +17 -0
- package/dist/src/commands/furnace/chrome-doc-templates.js +18 -0
- package/dist/src/commands/furnace/chrome-doc-tests.d.ts +23 -0
- package/dist/src/commands/furnace/chrome-doc-tests.js +120 -0
- package/dist/src/commands/furnace/chrome-doc.d.ts +11 -0
- package/dist/src/commands/furnace/chrome-doc.js +37 -4
- package/dist/src/commands/furnace/create-dry-run.d.ts +38 -0
- package/dist/src/commands/furnace/create-dry-run.js +100 -0
- package/dist/src/commands/furnace/create-features.d.ts +24 -0
- package/dist/src/commands/furnace/create-features.js +56 -0
- package/dist/src/commands/furnace/create-templates.d.ts +9 -5
- package/dist/src/commands/furnace/create-templates.js +28 -6
- package/dist/src/commands/furnace/create.js +62 -63
- package/dist/src/commands/furnace/index.js +4 -1
- package/dist/src/commands/lint.d.ts +17 -2
- package/dist/src/commands/lint.js +25 -2
- package/dist/src/commands/register.d.ts +1 -1
- package/dist/src/commands/register.js +30 -7
- package/dist/src/commands/run.d.ts +15 -1
- package/dist/src/commands/run.js +202 -7
- package/dist/src/commands/test.js +113 -3
- package/dist/src/core/build-audit-registration.d.ts +80 -0
- package/dist/src/core/build-audit-registration.js +187 -0
- package/dist/src/core/build-audit-transforms.d.ts +23 -0
- package/dist/src/core/build-audit-transforms.js +94 -0
- package/dist/src/core/build-audit.js +107 -7
- package/dist/src/core/furnace-apply-ftl.d.ts +5 -3
- package/dist/src/core/furnace-apply-ftl.js +6 -2
- package/dist/src/core/furnace-apply-helpers.js +14 -4
- package/dist/src/core/furnace-config-custom.d.ts +14 -0
- package/dist/src/core/furnace-config-custom.js +64 -0
- package/dist/src/core/furnace-config.js +2 -39
- package/dist/src/core/furnace-validate-accessibility.d.ts +9 -2
- package/dist/src/core/furnace-validate-accessibility.js +17 -3
- package/dist/src/core/furnace-validate-helpers.d.ts +13 -1
- package/dist/src/core/furnace-validate-helpers.js +19 -0
- package/dist/src/core/furnace-validate-registration.d.ts +6 -4
- package/dist/src/core/furnace-validate-registration.js +66 -6
- package/dist/src/core/furnace-validate-structure.js +6 -2
- package/dist/src/core/furnace-validate.js +6 -3
- package/dist/src/core/mach-build-artifacts.d.ts +44 -0
- package/dist/src/core/mach-build-artifacts.js +104 -3
- package/dist/src/core/mach.d.ts +27 -1
- package/dist/src/core/mach.js +26 -2
- package/dist/src/core/shared-ftl.d.ts +28 -0
- package/dist/src/core/shared-ftl.js +42 -0
- package/dist/src/core/smoke-patterns.d.ts +45 -0
- package/dist/src/core/smoke-patterns.js +100 -0
- package/dist/src/core/test-stale-check.d.ts +42 -0
- package/dist/src/core/test-stale-check.js +114 -0
- package/dist/src/core/xpcshell-appdir.d.ts +143 -0
- package/dist/src/core/xpcshell-appdir.js +273 -0
- package/dist/src/errors/codes.d.ts +13 -0
- package/dist/src/errors/codes.js +13 -0
- package/dist/src/errors/run.d.ts +16 -0
- package/dist/src/errors/run.js +22 -0
- package/dist/src/types/commands/options.d.ts +64 -0
- package/dist/src/types/furnace.d.ts +39 -0
- package/dist/src/utils/process.d.ts +63 -0
- package/dist/src/utils/process.js +122 -0
- package/package.json +1 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared regex library for `fireforge run --smoke-exit`. Used by the smoke
|
|
3
|
+
* runner to decide whether a console line from the real chrome counts as a
|
|
4
|
+
* runtime error. Kept separate from the runner so the patterns can be
|
|
5
|
+
* exercised in isolation and amended without touching process-group logic.
|
|
6
|
+
*
|
|
7
|
+
* Matching is anchored at the start of the line (`^`) on purpose: a runtime
|
|
8
|
+
* error leaves a canonical prefix that Firefox's logging layer prints at
|
|
9
|
+
* column zero. Embedded mentions of the same string inside an unrelated
|
|
10
|
+
* warning (e.g. `"pending JavaScript error cleanup"`) do not start with
|
|
11
|
+
* the prefix and should not trip the scanner.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Line prefixes that signal an actual runtime error in a Firefox chrome
|
|
15
|
+
* process. A hit on any of these patterns is a smoke failure unless the
|
|
16
|
+
* line also matches the caller-supplied allowlist.
|
|
17
|
+
*
|
|
18
|
+
* Additions here should be conservative — false positives turn every
|
|
19
|
+
* smoke run into noise for operators and every CI run into flake.
|
|
20
|
+
*/
|
|
21
|
+
export declare const SMOKE_ERROR_PATTERNS: readonly RegExp[];
|
|
22
|
+
/**
|
|
23
|
+
* Returns `true` when `line` matches any pattern in
|
|
24
|
+
* {@link SMOKE_ERROR_PATTERNS}. Does not consult the allowlist — that step
|
|
25
|
+
* lives in {@link matchesAllowlist}, so the smoke runner can count
|
|
26
|
+
* allowlisted hits separately from raw error matches for its summary.
|
|
27
|
+
*/
|
|
28
|
+
export declare function matchesSmokeError(line: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Returns `true` when `line` matches any regex in `allow`. Safe to call
|
|
31
|
+
* with an empty allowlist (always returns `false`).
|
|
32
|
+
*/
|
|
33
|
+
export declare function matchesAllowlist(line: string, allow: readonly RegExp[]): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Parses a newline-delimited allowlist file body. Lines are trimmed; blank
|
|
36
|
+
* lines and `#`-prefixed comments are skipped. Each remaining line is
|
|
37
|
+
* compiled as a RegExp. A bad pattern throws immediately — better to fail
|
|
38
|
+
* fast at CLI parse time than to silently let a typo match nothing.
|
|
39
|
+
*/
|
|
40
|
+
export declare function compileAllowlistFromFile(body: string, sourcePath: string): RegExp[];
|
|
41
|
+
/**
|
|
42
|
+
* Compiles an array of regex-string inputs (e.g. repeated `--console-allow`
|
|
43
|
+
* flag values). Same fail-fast semantics as {@link compileAllowlistFromFile}.
|
|
44
|
+
*/
|
|
45
|
+
export declare function compileAllowlistFromStrings(sources: readonly string[]): RegExp[];
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Shared regex library for `fireforge run --smoke-exit`. Used by the smoke
|
|
4
|
+
* runner to decide whether a console line from the real chrome counts as a
|
|
5
|
+
* runtime error. Kept separate from the runner so the patterns can be
|
|
6
|
+
* exercised in isolation and amended without touching process-group logic.
|
|
7
|
+
*
|
|
8
|
+
* Matching is anchored at the start of the line (`^`) on purpose: a runtime
|
|
9
|
+
* error leaves a canonical prefix that Firefox's logging layer prints at
|
|
10
|
+
* column zero. Embedded mentions of the same string inside an unrelated
|
|
11
|
+
* warning (e.g. `"pending JavaScript error cleanup"`) do not start with
|
|
12
|
+
* the prefix and should not trip the scanner.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Line prefixes that signal an actual runtime error in a Firefox chrome
|
|
16
|
+
* process. A hit on any of these patterns is a smoke failure unless the
|
|
17
|
+
* line also matches the caller-supplied allowlist.
|
|
18
|
+
*
|
|
19
|
+
* Additions here should be conservative — false positives turn every
|
|
20
|
+
* smoke run into noise for operators and every CI run into flake.
|
|
21
|
+
*/
|
|
22
|
+
export const SMOKE_ERROR_PATTERNS = [
|
|
23
|
+
// Firefox chrome error lines — `JavaScript error: chrome://…, line N: TypeError: …`.
|
|
24
|
+
/^\s*JavaScript error:/i,
|
|
25
|
+
// Some log paths prefix browser-console `console.error(...)` with the literal label below.
|
|
26
|
+
/^\s*console\.error:/i,
|
|
27
|
+
// Older bracketed-prefix variant still seen in some chrome logs / test runs.
|
|
28
|
+
/^\s*\[JavaScript (Error|Warning)\]/i,
|
|
29
|
+
// IPC-layer fatal assertions — Firefox prints `###!!! [Parent] Error: …` on content-process crashes.
|
|
30
|
+
/^\s*###!!! \[Parent\]/,
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Returns `true` when `line` matches any pattern in
|
|
34
|
+
* {@link SMOKE_ERROR_PATTERNS}. Does not consult the allowlist — that step
|
|
35
|
+
* lives in {@link matchesAllowlist}, so the smoke runner can count
|
|
36
|
+
* allowlisted hits separately from raw error matches for its summary.
|
|
37
|
+
*/
|
|
38
|
+
export function matchesSmokeError(line) {
|
|
39
|
+
for (const pattern of SMOKE_ERROR_PATTERNS) {
|
|
40
|
+
if (pattern.test(line)) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Returns `true` when `line` matches any regex in `allow`. Safe to call
|
|
48
|
+
* with an empty allowlist (always returns `false`).
|
|
49
|
+
*/
|
|
50
|
+
export function matchesAllowlist(line, allow) {
|
|
51
|
+
for (const pattern of allow) {
|
|
52
|
+
if (pattern.test(line)) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Parses a newline-delimited allowlist file body. Lines are trimmed; blank
|
|
60
|
+
* lines and `#`-prefixed comments are skipped. Each remaining line is
|
|
61
|
+
* compiled as a RegExp. A bad pattern throws immediately — better to fail
|
|
62
|
+
* fast at CLI parse time than to silently let a typo match nothing.
|
|
63
|
+
*/
|
|
64
|
+
export function compileAllowlistFromFile(body, sourcePath) {
|
|
65
|
+
const lines = body.split(/\r?\n/);
|
|
66
|
+
const compiled = [];
|
|
67
|
+
lines.forEach((raw, index) => {
|
|
68
|
+
const line = raw.trim();
|
|
69
|
+
if (!line || line.startsWith('#'))
|
|
70
|
+
return;
|
|
71
|
+
try {
|
|
72
|
+
compiled.push(new RegExp(line));
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
76
|
+
throw new Error(`Invalid allowlist regex at ${sourcePath}:${String(index + 1)}: ${message}`, {
|
|
77
|
+
cause: error,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return compiled;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Compiles an array of regex-string inputs (e.g. repeated `--console-allow`
|
|
85
|
+
* flag values). Same fail-fast semantics as {@link compileAllowlistFromFile}.
|
|
86
|
+
*/
|
|
87
|
+
export function compileAllowlistFromStrings(sources) {
|
|
88
|
+
const compiled = [];
|
|
89
|
+
sources.forEach((source, index) => {
|
|
90
|
+
try {
|
|
91
|
+
compiled.push(new RegExp(source));
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
95
|
+
throw new Error(`Invalid --console-allow regex at position ${String(index + 1)} ("${source}"): ${message}`, { cause: error });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return compiled;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=smoke-patterns.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { BuildBaseline } from './build-baseline.js';
|
|
2
|
+
/** Result of the stale-build preflight probe. */
|
|
3
|
+
export interface StaleBuildResult {
|
|
4
|
+
/** True when at least one packageable engine file changed since the baseline. */
|
|
5
|
+
stale: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Engine-relative paths that would have been packaged but appear to have
|
|
8
|
+
* changed since the baseline. Sorted and deduplicated. Truncated at
|
|
9
|
+
* {@link STALE_PATHS_LIMIT} entries for rendering; consult
|
|
10
|
+
* {@link StaleBuildResult.truncated} to know when to append a `(+N more)`
|
|
11
|
+
* tail to the warning.
|
|
12
|
+
*/
|
|
13
|
+
changedPaths: string[];
|
|
14
|
+
/**
|
|
15
|
+
* How many paths were dropped from `changedPaths` due to the render cap.
|
|
16
|
+
* Callers render this as `(+N more)` in the warning body.
|
|
17
|
+
*/
|
|
18
|
+
truncated: number;
|
|
19
|
+
/**
|
|
20
|
+
* The baseline that anchored the diff, or undefined when no previous
|
|
21
|
+
* successful build exists. A missing baseline is treated as "not stale"
|
|
22
|
+
* — we have nothing to compare against and a warning would mislead.
|
|
23
|
+
*/
|
|
24
|
+
baseline: BuildBaseline | undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Probes the engine tree for packageable changes since the last successful
|
|
28
|
+
* `fireforge build`. Returns a summary the `fireforge test` handler renders
|
|
29
|
+
* as an up-front warning when `--build` was NOT passed. The probe never
|
|
30
|
+
* throws; git failures and a missing baseline both degrade to `stale: false`
|
|
31
|
+
* so a broken probe cannot block a test run.
|
|
32
|
+
*
|
|
33
|
+
* @param projectRoot Root directory of the project.
|
|
34
|
+
* @param engineDir Path to the engine directory.
|
|
35
|
+
*/
|
|
36
|
+
export declare function checkStaleBuildForTest(projectRoot: string, engineDir: string): Promise<StaleBuildResult>;
|
|
37
|
+
/**
|
|
38
|
+
* Formats a human-readable warning body from a {@link StaleBuildResult}.
|
|
39
|
+
* Kept separate from the probe so test code can assert on the structured
|
|
40
|
+
* result without matching the rendered copy.
|
|
41
|
+
*/
|
|
42
|
+
export declare function formatStaleBuildWarning(result: StaleBuildResult): string;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/*
|
|
3
|
+
* Stale-build preflight for `fireforge test`.
|
|
4
|
+
*
|
|
5
|
+
* Without this preflight, an operator who edits engine chrome / packaged
|
|
6
|
+
* resources (`jar.mn` entries, `.xhtml`/`.mjs`/`.css` under chrome trees,
|
|
7
|
+
* pref files) and then runs `fireforge test <path>` only discovers the
|
|
8
|
+
* build is stale AFTER xpcshell / mach test starts and errors out with
|
|
9
|
+
* `NS_ERROR_FILE_NOT_FOUND` against a `chrome://browser/content/…` URI
|
|
10
|
+
* — which reads as a test bug, not a rebuild prompt. The motivating case
|
|
11
|
+
* was scaffolding a new top-level chrome document + BrowserGlue-style
|
|
12
|
+
* xpcshell test: the test file existed, the manifests were registered,
|
|
13
|
+
* but `dist/` still held the pre-edit bundle and chrome URIs resolved
|
|
14
|
+
* to nothing.
|
|
15
|
+
*
|
|
16
|
+
* This preflight diffs engine HEAD (or workdir) against the last-build
|
|
17
|
+
* baseline (`.fireforge/last-build.json`), filters to paths that imply
|
|
18
|
+
* packaging, and returns a compact summary. `fireforge test` prints a
|
|
19
|
+
* warning up-front so the operator sees "you edited X, Y, Z since the
|
|
20
|
+
* last build — rerun with `--build` to refresh" BEFORE mach test
|
|
21
|
+
* launches. Detection stays advisory (warn-only) because a fork that
|
|
22
|
+
* rebuilds out-of-band (a separate `./mach build` invocation, an IDE
|
|
23
|
+
* plugin, etc.) can legitimately have a fresh `dist/` with no
|
|
24
|
+
* FireForge-recorded baseline update.
|
|
25
|
+
*/
|
|
26
|
+
import { toError } from '../utils/errors.js';
|
|
27
|
+
import { verbose } from '../utils/logger.js';
|
|
28
|
+
import { isPackageablePath } from './build-audit.js';
|
|
29
|
+
import { readBuildBaseline } from './build-baseline.js';
|
|
30
|
+
import { hasChanges, isMissingHeadError } from './git.js';
|
|
31
|
+
import { git } from './git-base.js';
|
|
32
|
+
import { getUntrackedFiles } from './git-status.js';
|
|
33
|
+
/** Cap on the number of changed paths rendered inline. */
|
|
34
|
+
const STALE_PATHS_LIMIT = 10;
|
|
35
|
+
/**
|
|
36
|
+
* Collects engine paths that changed since the baseline SHA plus any
|
|
37
|
+
* workdir modifications. Mirrors the helper inside `build-prepare.ts` but
|
|
38
|
+
* is kept separate so the test-side preflight does not need to pull in
|
|
39
|
+
* the full build-prepare dependency graph (mozconfig generation, furnace
|
|
40
|
+
* apply hooks, …).
|
|
41
|
+
*/
|
|
42
|
+
async function collectChangedEnginePaths(engineDir, baseline) {
|
|
43
|
+
const collected = new Set();
|
|
44
|
+
if (baseline.engineHeadSha) {
|
|
45
|
+
try {
|
|
46
|
+
const diff = await git(['diff', '--name-only', `${baseline.engineHeadSha}..HEAD`], engineDir);
|
|
47
|
+
for (const line of diff.split('\n')) {
|
|
48
|
+
const trimmed = line.trim();
|
|
49
|
+
if (trimmed)
|
|
50
|
+
collected.add(trimmed);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (!isMissingHeadError(error)) {
|
|
55
|
+
verbose(`Stale-build preflight: could not diff engine against baseline — ${toError(error).message}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
if (await hasChanges(engineDir)) {
|
|
61
|
+
const worktreeDiff = await git(['diff', '--name-only', 'HEAD'], engineDir);
|
|
62
|
+
for (const line of worktreeDiff.split('\n')) {
|
|
63
|
+
const trimmed = line.trim();
|
|
64
|
+
if (trimmed)
|
|
65
|
+
collected.add(trimmed);
|
|
66
|
+
}
|
|
67
|
+
for (const untracked of await getUntrackedFiles(engineDir)) {
|
|
68
|
+
collected.add(untracked);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
verbose(`Stale-build preflight: could not enumerate workdir changes — ${toError(error).message}`);
|
|
74
|
+
}
|
|
75
|
+
return [...collected];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Probes the engine tree for packageable changes since the last successful
|
|
79
|
+
* `fireforge build`. Returns a summary the `fireforge test` handler renders
|
|
80
|
+
* as an up-front warning when `--build` was NOT passed. The probe never
|
|
81
|
+
* throws; git failures and a missing baseline both degrade to `stale: false`
|
|
82
|
+
* so a broken probe cannot block a test run.
|
|
83
|
+
*
|
|
84
|
+
* @param projectRoot Root directory of the project.
|
|
85
|
+
* @param engineDir Path to the engine directory.
|
|
86
|
+
*/
|
|
87
|
+
export async function checkStaleBuildForTest(projectRoot, engineDir) {
|
|
88
|
+
const baseline = await readBuildBaseline(projectRoot);
|
|
89
|
+
if (!baseline) {
|
|
90
|
+
return { stale: false, changedPaths: [], truncated: 0, baseline: undefined };
|
|
91
|
+
}
|
|
92
|
+
const changed = await collectChangedEnginePaths(engineDir, baseline);
|
|
93
|
+
const packageable = changed.filter((path) => isPackageablePath(path)).sort();
|
|
94
|
+
if (packageable.length === 0) {
|
|
95
|
+
return { stale: false, changedPaths: [], truncated: 0, baseline };
|
|
96
|
+
}
|
|
97
|
+
const head = packageable.slice(0, STALE_PATHS_LIMIT);
|
|
98
|
+
const truncated = Math.max(0, packageable.length - head.length);
|
|
99
|
+
return { stale: true, changedPaths: head, truncated, baseline };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Formats a human-readable warning body from a {@link StaleBuildResult}.
|
|
103
|
+
* Kept separate from the probe so test code can assert on the structured
|
|
104
|
+
* result without matching the rendered copy.
|
|
105
|
+
*/
|
|
106
|
+
export function formatStaleBuildWarning(result) {
|
|
107
|
+
const tail = result.truncated > 0 ? `, … (+${result.truncated} more)` : '';
|
|
108
|
+
const list = result.changedPaths.join(', ') + tail;
|
|
109
|
+
return (`Engine tree has changed since the last successful fireforge build (${list}).\n` +
|
|
110
|
+
'The current obj-*/dist/ bundle may not reflect those edits. If your test reads ' +
|
|
111
|
+
'packaged chrome / jar.mn resources, rerun with "fireforge test --build" (or ' +
|
|
112
|
+
'"fireforge build --ui") first. Passing --build skips this check.');
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=test-stale-check.js.map
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-injects `--app-path=<abs>` into `mach test` invocations whose nearest
|
|
3
|
+
* xpcshell.toml sets `firefox-appdir = "browser"` (or `<appname>-appdir = …`)
|
|
4
|
+
* but whose `appname` is not `firefox`.
|
|
5
|
+
*
|
|
6
|
+
* ## Why this exists
|
|
7
|
+
*
|
|
8
|
+
* The upstream xpcshell harness computes the manifest key for the appdir
|
|
9
|
+
* override as `mozInfo["appname"] + "-appdir"`. On a stock Firefox build the
|
|
10
|
+
* key is `firefox-appdir`, so the very common `firefox-appdir = "browser"`
|
|
11
|
+
* directive is honoured. On a rebranded fork (appname=`hominis`,
|
|
12
|
+
* `mybrowser`, …) the harness looks for `hominis-appdir` / `mybrowser-appdir`
|
|
13
|
+
* — the literal `firefox-appdir` line is silently ignored, `appPath` falls
|
|
14
|
+
* back to `xrePath`, and every `resource:///modules/…` import throws
|
|
15
|
+
* `Failed to load resource:///modules/<name>.sys.mjs` because xpcshell now
|
|
16
|
+
* resolves the `resource:///` prefix one level above the real app root.
|
|
17
|
+
*
|
|
18
|
+
* ## Strategy
|
|
19
|
+
*
|
|
20
|
+
* 1. For each test path the operator handed us, find the nearest
|
|
21
|
+
* `xpcshell.toml`. If none exists, the test is not an xpcshell test and
|
|
22
|
+
* nothing to inject.
|
|
23
|
+
* 2. Read the manifest's `[DEFAULT]` section. Look for `<appname>-appdir`
|
|
24
|
+
* first — if present, the harness already finds it and there's nothing to
|
|
25
|
+
* do. Fall back to `firefox-appdir`. This ordering matches upstream
|
|
26
|
+
* precedence and avoids overriding an operator who already migrated.
|
|
27
|
+
* 3. If only `firefox-appdir` is present and `appname != "firefox"`, compute
|
|
28
|
+
* the absolute app dir path against the active `obj-X/dist` tree
|
|
29
|
+
* (probing `dist/bin/<value>` first, then any `dist/<bundle>.app/Contents/
|
|
30
|
+
* Resources/<value>` for the macOS packaged layout) and return it as
|
|
31
|
+
* the value to pass to `--app-path`.
|
|
32
|
+
* 4. If multiple test paths disagree on the resolved value (e.g. one
|
|
33
|
+
* manifest sets `browser`, another sets `xulrunner`), refuse injection
|
|
34
|
+
* and return null — the operator can drop down to `--mach-arg`.
|
|
35
|
+
*
|
|
36
|
+
* Operator escape hatches: `--mach-arg=--app-path=…` always wins (handled in
|
|
37
|
+
* test.ts; we skip injection when `--app-path=` already appears in the
|
|
38
|
+
* forwarded args).
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* Result of attempting to resolve the auto-injected `--app-path` value.
|
|
42
|
+
* Carries enough context for the caller to log a useful info line and for
|
|
43
|
+
* the diagnostic hint to know whether an injection was attempted.
|
|
44
|
+
*/
|
|
45
|
+
export interface AppdirResolveResult {
|
|
46
|
+
/** Absolute path to the app dir. Pass as `--app-path=<value>`. */
|
|
47
|
+
appPath: string;
|
|
48
|
+
/** Manifest the value was sourced from. Used for the info log. */
|
|
49
|
+
manifestPath: string;
|
|
50
|
+
/** Manifest key (e.g. `firefox-appdir`) that triggered the injection. */
|
|
51
|
+
key: string;
|
|
52
|
+
/** Relative appdir from the manifest (e.g. `browser`). */
|
|
53
|
+
relativeAppdir: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* `[DEFAULT]` section parser shaped to the narrow case we need: pull a
|
|
57
|
+
* single key/value out without depending on a real TOML parser. Avoids
|
|
58
|
+
* pulling a TOML dep into the test path for a one-shot lookup.
|
|
59
|
+
*
|
|
60
|
+
* Accepts:
|
|
61
|
+
* - Single- or double-quoted values
|
|
62
|
+
* - Whitespace either side of `=`
|
|
63
|
+
* - Continuation comments (`#` or `;`) at the end of the line
|
|
64
|
+
* - Bare unquoted bareword values (e.g. `firefox-appdir = browser`) — some
|
|
65
|
+
* operators omit the quotes and the harness honours either form.
|
|
66
|
+
*
|
|
67
|
+
* Returns `undefined` when the key is absent or sits outside `[DEFAULT]`.
|
|
68
|
+
*/
|
|
69
|
+
export declare function parseAppdirFromToml(tomlText: string, key: string): {
|
|
70
|
+
value: string;
|
|
71
|
+
lineIndex: number;
|
|
72
|
+
} | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* Walks up from `startPath` (a file or directory under `engineDir`) and
|
|
75
|
+
* returns the absolute path of the first sibling `xpcshell.toml` found.
|
|
76
|
+
* Stops at `engineDir` (inclusive) and returns null on miss.
|
|
77
|
+
*
|
|
78
|
+
* Special-cases `startPath` itself when it already ends with
|
|
79
|
+
* `xpcshell.toml` — operators sometimes pass a manifest path directly.
|
|
80
|
+
*/
|
|
81
|
+
export declare function findNearestXpcshellManifest(engineDir: string, startPath: string): Promise<string | null>;
|
|
82
|
+
/**
|
|
83
|
+
* Reads `<objDir>/mozinfo.json` for the active app name. Returns
|
|
84
|
+
* `"firefox"` when mozinfo cannot be read or the field is missing — that
|
|
85
|
+
* is the safe default because it matches stock Firefox behaviour and
|
|
86
|
+
* means the resolver will not inject anything (the manifest's
|
|
87
|
+
* `firefox-appdir` value WILL be honoured by the upstream harness when
|
|
88
|
+
* appname is firefox).
|
|
89
|
+
*/
|
|
90
|
+
export declare function readMozinfoAppname(objDirPath: string): Promise<string>;
|
|
91
|
+
/**
|
|
92
|
+
* Probes the obj-dir's `dist/` subtree for the absolute path that the
|
|
93
|
+
* harness would have computed if the manifest key had been honoured.
|
|
94
|
+
* Returns null when no candidate exists — better to skip injection
|
|
95
|
+
* silently than to point the harness at a path that doesn't exist
|
|
96
|
+
* (which fails with a different error than the original `firefox-appdir`
|
|
97
|
+
* symptom and confuses triage).
|
|
98
|
+
*
|
|
99
|
+
* Probe order matches the on-disk layouts FireForge supports today:
|
|
100
|
+
* 1. `<objDir>/dist/bin/<value>` — Linux primary, also macOS via the
|
|
101
|
+
* `dist/bin -> dist/<App>.app/Contents/MacOS/` symlink.
|
|
102
|
+
* 2. `<objDir>/dist/<bundle>.app/Contents/Resources/<value>` — macOS
|
|
103
|
+
* packaged layout, where `dist/bin/` may not exist as a directory.
|
|
104
|
+
*/
|
|
105
|
+
export declare function resolveAbsoluteAppPath(objDirAbs: string, relativeAppdir: string): Promise<string | null>;
|
|
106
|
+
/**
|
|
107
|
+
* Outcome carrier for {@link resolveXpcshellAppdirArg}. Distinguishes the
|
|
108
|
+
* three "did nothing" cases so callers can shape diagnostics:
|
|
109
|
+
* - `none`: no manifest under any test path needs injection.
|
|
110
|
+
* - `mismatch`: at least two manifests resolved to different values; we
|
|
111
|
+
* refuse to guess which one the operator meant.
|
|
112
|
+
* - `unresolved`: the manifest asks for `firefox-appdir = "<value>"` but
|
|
113
|
+
* no `dist/` candidate exists for that value.
|
|
114
|
+
* - `injected`: the absolute path to pass via `--app-path=`.
|
|
115
|
+
*/
|
|
116
|
+
export type XpcshellAppdirOutcome = {
|
|
117
|
+
kind: 'none';
|
|
118
|
+
} | {
|
|
119
|
+
kind: 'mismatch';
|
|
120
|
+
values: string[];
|
|
121
|
+
} | {
|
|
122
|
+
kind: 'unresolved';
|
|
123
|
+
relativeAppdir: string;
|
|
124
|
+
manifestPath: string;
|
|
125
|
+
} | {
|
|
126
|
+
kind: 'injected';
|
|
127
|
+
result: AppdirResolveResult;
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Top-level resolver. Walks every test path, reads the nearest
|
|
131
|
+
* xpcshell.toml, and returns the single absolute path to inject (or a
|
|
132
|
+
* structured "no injection" outcome). Never throws — every fs / parse
|
|
133
|
+
* error is folded into a `none` outcome so the test command always falls
|
|
134
|
+
* through to the diagnostic hint instead of dying inside a helper.
|
|
135
|
+
*/
|
|
136
|
+
export declare function resolveXpcshellAppdirArg(engineDir: string, testPaths: readonly string[], objDirName: string): Promise<XpcshellAppdirOutcome>;
|
|
137
|
+
/**
|
|
138
|
+
* Returns true when the operator already passed `--app-path=` (or its
|
|
139
|
+
* `--app-path <value>` two-token form) through `--mach-arg`. Used by the
|
|
140
|
+
* test command to skip auto-injection so the operator override always
|
|
141
|
+
* wins.
|
|
142
|
+
*/
|
|
143
|
+
export declare function operatorAlreadySetAppPath(extraArgs: readonly string[]): boolean;
|