@hominis/fireforge 0.18.0 → 0.18.2
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 +18 -2
- package/README.md +55 -34
- package/dist/src/commands/doctor.js +13 -1
- package/dist/src/commands/export-all.js +63 -1
- package/dist/src/commands/export-flow.d.ts +4 -0
- package/dist/src/commands/export-flow.js +8 -0
- package/dist/src/commands/export.js +26 -2
- package/dist/src/commands/furnace/create-xpcshell.js +4 -2
- package/dist/src/commands/furnace/preview.js +38 -0
- package/dist/src/commands/furnace/remove.js +67 -1
- package/dist/src/commands/furnace/rename-xpcshell.d.ts +35 -0
- package/dist/src/commands/furnace/rename-xpcshell.js +97 -0
- package/dist/src/commands/furnace/rename.js +9 -0
- package/dist/src/commands/patch/index.d.ts +5 -3
- package/dist/src/commands/patch/index.js +10 -4
- package/dist/src/commands/patch/lint-ignore.d.ts +39 -0
- package/dist/src/commands/patch/lint-ignore.js +200 -0
- package/dist/src/commands/patch/tier.d.ts +34 -0
- package/dist/src/commands/patch/tier.js +134 -0
- package/dist/src/commands/re-export-files.js +88 -45
- package/dist/src/commands/re-export.js +49 -6
- package/dist/src/commands/rebase/index.js +19 -1
- package/dist/src/commands/status.js +44 -5
- package/dist/src/commands/test.js +27 -16
- package/dist/src/commands/verify.js +81 -6
- package/dist/src/commands/watch.js +43 -7
- package/dist/src/core/furnace-constants.d.ts +14 -0
- package/dist/src/core/furnace-constants.js +16 -0
- package/dist/src/core/furnace-validate.js +67 -1
- package/dist/src/core/git-base.d.ts +27 -2
- package/dist/src/core/git-base.js +41 -3
- package/dist/src/core/git-diff.js +34 -2
- package/dist/src/core/git.js +53 -14
- package/dist/src/core/mach.d.ts +14 -2
- package/dist/src/core/mach.js +12 -2
- package/dist/src/core/marionette-preflight.d.ts +16 -0
- package/dist/src/core/marionette-preflight.js +19 -0
- package/dist/src/core/patch-export.d.ts +77 -2
- package/dist/src/core/patch-export.js +82 -3
- package/dist/src/core/patch-lint-diff-tag.d.ts +20 -0
- package/dist/src/core/patch-lint-diff-tag.js +25 -0
- package/dist/src/core/patch-lint.js +82 -32
- package/dist/src/core/patch-registration-refs.d.ts +42 -0
- package/dist/src/core/patch-registration-refs.js +117 -0
- package/dist/src/core/xpcshell-appdir.d.ts +19 -5
- package/dist/src/core/xpcshell-appdir.js +46 -20
- package/dist/src/errors/git.d.ts +20 -0
- package/dist/src/errors/git.js +39 -0
- package/dist/src/types/commands/index.d.ts +1 -1
- package/dist/src/types/commands/options.d.ts +67 -0
- package/dist/src/types/commands/patches.d.ts +6 -5
- package/package.json +1 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Extracts furnace-shaped registration references from a patch body.
|
|
4
|
+
*
|
|
5
|
+
* 2026-04-24 eval Finding 1: `export-all --exclude-furnace` can land a
|
|
6
|
+
* patch that registers a furnace component (via edits to
|
|
7
|
+
* `toolkit/content/customElements.js`, `toolkit/content/jar.mn`, or
|
|
8
|
+
* `toolkit/locales/jar.mn`) without including the component's source
|
|
9
|
+
* files in the patch. `fireforge verify` then reports "Verify clean" for
|
|
10
|
+
* the broken queue. This module provides a pattern-scoped scan so
|
|
11
|
+
* `verify` can cross-check registrations against available file bodies.
|
|
12
|
+
*
|
|
13
|
+
* The scan is deliberately narrow: it only matches component-shaped
|
|
14
|
+
* references (widget tag names, locale fluent names). Unrelated jar.mn
|
|
15
|
+
* or customElements.js edits pass through without spurious warnings.
|
|
16
|
+
*/
|
|
17
|
+
/** Canonical file paths that registration-shaped diffs touch. */
|
|
18
|
+
const REGISTRATION_FILE_PATHS = new Set([
|
|
19
|
+
'toolkit/content/customElements.js',
|
|
20
|
+
'toolkit/content/jar.mn',
|
|
21
|
+
'toolkit/locales/jar.mn',
|
|
22
|
+
]);
|
|
23
|
+
/**
|
|
24
|
+
* Walks a unified-diff patch body and returns the set of
|
|
25
|
+
* component-shaped engine paths that the patch ADDS a registration for.
|
|
26
|
+
*
|
|
27
|
+
* Returns the empty array when no registration hunks are present OR
|
|
28
|
+
* when the registration hunks do not mention any component-shaped
|
|
29
|
+
* paths — that leaves the scan silent on the vast majority of patches
|
|
30
|
+
* (branding tweaks, behavioural fixes, module additions) so it only
|
|
31
|
+
* fires when a furnace-managed component is being newly registered.
|
|
32
|
+
*
|
|
33
|
+
* @param patchBody - Full unified-diff body of the patch file.
|
|
34
|
+
*/
|
|
35
|
+
export function collectPatchRegistrationReferences(patchBody) {
|
|
36
|
+
if (!patchBody)
|
|
37
|
+
return [];
|
|
38
|
+
const refs = [];
|
|
39
|
+
let currentFile;
|
|
40
|
+
// Walk line-by-line. The canonical unified-diff header line is
|
|
41
|
+
// `diff --git a/<path> b/<path>` — we key the file state off the `b/`
|
|
42
|
+
// path because that names the target side and is stable against
|
|
43
|
+
// renames. Additional diff metadata lines (index/---/+++/@@) are
|
|
44
|
+
// ignored for the purposes of tracking the current file.
|
|
45
|
+
const lines = patchBody.split(/\r?\n/);
|
|
46
|
+
for (const line of lines) {
|
|
47
|
+
const diffHeader = /^diff --git a\/(.+?) b\/(.+)$/.exec(line);
|
|
48
|
+
if (diffHeader?.[2]) {
|
|
49
|
+
currentFile = diffHeader[2];
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (!currentFile)
|
|
53
|
+
continue;
|
|
54
|
+
if (!REGISTRATION_FILE_PATHS.has(currentFile))
|
|
55
|
+
continue;
|
|
56
|
+
if (!line.startsWith('+'))
|
|
57
|
+
continue;
|
|
58
|
+
// Skip the `+++ b/<path>` header line — only real hunk adds count.
|
|
59
|
+
if (line.startsWith('+++'))
|
|
60
|
+
continue;
|
|
61
|
+
const added = line.slice(1);
|
|
62
|
+
const extracted = extractTargetPathsFromRegistrationLine(currentFile, added);
|
|
63
|
+
for (const target of extracted) {
|
|
64
|
+
refs.push({ targetPath: target, source: currentFile, lineText: added });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return refs;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Per-source extractor. Each registration file has a distinct syntactic
|
|
71
|
+
* shape; we scope the match to that file so a jar.mn regex does not
|
|
72
|
+
* accidentally match a customElements.js line.
|
|
73
|
+
*/
|
|
74
|
+
function extractTargetPathsFromRegistrationLine(sourceFile, added) {
|
|
75
|
+
if (sourceFile === 'toolkit/content/jar.mn') {
|
|
76
|
+
// Example (added line, leading `+` already stripped):
|
|
77
|
+
// ` content/global/elements/moz-qa-panel.mjs (widgets/moz-qa-panel/moz-qa-panel.mjs)`
|
|
78
|
+
// The parenthesised second half is the repo-relative path Firefox's
|
|
79
|
+
// packaging system reads. Widget registrations always live under
|
|
80
|
+
// `widgets/<tag>/<file>` — the enclosing tree is
|
|
81
|
+
// `toolkit/content/widgets/`. Reconstruct the engine-relative
|
|
82
|
+
// target path so callers can check it against patch bodies.
|
|
83
|
+
const widgetMatch = /\(\s*(widgets\/[^\s)]+)\s*\)/.exec(added);
|
|
84
|
+
if (widgetMatch?.[1]) {
|
|
85
|
+
return [`toolkit/content/${widgetMatch[1]}`];
|
|
86
|
+
}
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
if (sourceFile === 'toolkit/locales/jar.mn') {
|
|
90
|
+
// Example:
|
|
91
|
+
// ` locale/@AB_CD@/toolkit/global/moz-qa-panel.ftl (%toolkit/global/moz-qa-panel.ftl)`
|
|
92
|
+
// The `%`-prefixed repo-relative reference points at
|
|
93
|
+
// `toolkit/locales/en-US/<rel>`, which is the canonical FTL path.
|
|
94
|
+
const localeMatch = /\(%\s*([^\s)]+\.ftl)\s*\)/.exec(added);
|
|
95
|
+
if (localeMatch?.[1]) {
|
|
96
|
+
return [`toolkit/locales/en-US/${localeMatch[1]}`];
|
|
97
|
+
}
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
if (sourceFile === 'toolkit/content/customElements.js') {
|
|
101
|
+
// Example:
|
|
102
|
+
// ` ["moz-qa-panel", "chrome://global/content/elements/moz-qa-panel.mjs"],`
|
|
103
|
+
// The chrome URL maps back to
|
|
104
|
+
// `toolkit/content/widgets/<tag>/<tag>.mjs` by convention: the
|
|
105
|
+
// packager rewrites `chrome://global/content/elements/<file>` to the
|
|
106
|
+
// widget tree root. The tag name is the identifier we key off.
|
|
107
|
+
const elementMatch = /\[\s*"([a-z][a-z0-9-]*)"\s*,\s*"chrome:\/\/global\/content\/elements\/([a-zA-Z0-9_-]+)\.mjs"\s*\]/.exec(added);
|
|
108
|
+
if (elementMatch?.[1] && elementMatch[2]) {
|
|
109
|
+
const tag = elementMatch[1];
|
|
110
|
+
const fileStem = elementMatch[2];
|
|
111
|
+
return [`toolkit/content/widgets/${tag}/${fileStem}.mjs`];
|
|
112
|
+
}
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=patch-registration-refs.js.map
|
|
@@ -96,11 +96,25 @@ export declare function readMozinfoAppname(objDirPath: string): Promise<string>;
|
|
|
96
96
|
* (which fails with a different error than the original `firefox-appdir`
|
|
97
97
|
* symptom and confuses triage).
|
|
98
98
|
*
|
|
99
|
-
* Probe order
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
99
|
+
* Probe order differs by host platform:
|
|
100
|
+
*
|
|
101
|
+
* - **macOS (`darwin`)**: prefer `<objDir>/dist/<App>.app/Contents/Resources/
|
|
102
|
+
* <value>` FIRST, then fall back to `<objDir>/dist/bin/<value>`.
|
|
103
|
+
* 2026-04-24 eval Finding 8: on macOS `dist/bin` is symlinked to
|
|
104
|
+
* `dist/<App>.app/Contents/MacOS/` (the *binaries* directory), so
|
|
105
|
+
* `dist/bin/browser` actually resolves to `<App>.app/Contents/MacOS/
|
|
106
|
+
* browser/`. That is NOT where `resource:///modules/` is rooted — on
|
|
107
|
+
* macOS, `-a` for xpcshell must point at the `.app/Contents/Resources/
|
|
108
|
+
* <value>` subtree where modules / chrome.manifest live. Returning
|
|
109
|
+
* `dist/bin/browser` caused the injected `--app-path` to look
|
|
110
|
+
* successful (the info log showed it) but pointed at a directory
|
|
111
|
+
* without the modules tree, so every `resource:///modules/…` import
|
|
112
|
+
* still threw.
|
|
113
|
+
* - **non-macOS**: keep the historical order — `dist/bin/<value>` first,
|
|
114
|
+
* `.app/Contents/Resources/<value>` as fallback.
|
|
115
|
+
*
|
|
116
|
+
* On both platforms the final `.app` fallback iterates every `*.app`
|
|
117
|
+
* entry because a rebranded fork may pick an arbitrary app name.
|
|
104
118
|
*/
|
|
105
119
|
export declare function resolveAbsoluteAppPath(objDirAbs: string, relativeAppdir: string): Promise<string | null>;
|
|
106
120
|
/**
|
|
@@ -164,34 +164,60 @@ export async function readMozinfoAppname(objDirPath) {
|
|
|
164
164
|
* (which fails with a different error than the original `firefox-appdir`
|
|
165
165
|
* symptom and confuses triage).
|
|
166
166
|
*
|
|
167
|
-
* Probe order
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
167
|
+
* Probe order differs by host platform:
|
|
168
|
+
*
|
|
169
|
+
* - **macOS (`darwin`)**: prefer `<objDir>/dist/<App>.app/Contents/Resources/
|
|
170
|
+
* <value>` FIRST, then fall back to `<objDir>/dist/bin/<value>`.
|
|
171
|
+
* 2026-04-24 eval Finding 8: on macOS `dist/bin` is symlinked to
|
|
172
|
+
* `dist/<App>.app/Contents/MacOS/` (the *binaries* directory), so
|
|
173
|
+
* `dist/bin/browser` actually resolves to `<App>.app/Contents/MacOS/
|
|
174
|
+
* browser/`. That is NOT where `resource:///modules/` is rooted — on
|
|
175
|
+
* macOS, `-a` for xpcshell must point at the `.app/Contents/Resources/
|
|
176
|
+
* <value>` subtree where modules / chrome.manifest live. Returning
|
|
177
|
+
* `dist/bin/browser` caused the injected `--app-path` to look
|
|
178
|
+
* successful (the info log showed it) but pointed at a directory
|
|
179
|
+
* without the modules tree, so every `resource:///modules/…` import
|
|
180
|
+
* still threw.
|
|
181
|
+
* - **non-macOS**: keep the historical order — `dist/bin/<value>` first,
|
|
182
|
+
* `.app/Contents/Resources/<value>` as fallback.
|
|
183
|
+
*
|
|
184
|
+
* On both platforms the final `.app` fallback iterates every `*.app`
|
|
185
|
+
* entry because a rebranded fork may pick an arbitrary app name.
|
|
172
186
|
*/
|
|
173
187
|
export async function resolveAbsoluteAppPath(objDirAbs, relativeAppdir) {
|
|
174
188
|
const distBinCandidate = join(objDirAbs, 'dist', 'bin', relativeAppdir);
|
|
175
|
-
if (await pathExists(distBinCandidate))
|
|
176
|
-
return distBinCandidate;
|
|
177
189
|
const distDir = join(objDirAbs, 'dist');
|
|
178
|
-
|
|
190
|
+
const isMacos = process.platform === 'darwin';
|
|
191
|
+
async function probeMacAppBundle() {
|
|
192
|
+
if (!(await pathExists(distDir)))
|
|
193
|
+
return null;
|
|
194
|
+
let entries;
|
|
195
|
+
try {
|
|
196
|
+
entries = await readdir(distDir);
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
for (const entry of entries) {
|
|
202
|
+
if (!entry.endsWith('.app'))
|
|
203
|
+
continue;
|
|
204
|
+
const candidate = join(distDir, entry, 'Contents', 'Resources', relativeAppdir);
|
|
205
|
+
if (await pathExists(candidate))
|
|
206
|
+
return candidate;
|
|
207
|
+
}
|
|
179
208
|
return null;
|
|
180
|
-
let entries;
|
|
181
|
-
try {
|
|
182
|
-
entries = await readdir(distDir);
|
|
183
209
|
}
|
|
184
|
-
|
|
210
|
+
if (isMacos) {
|
|
211
|
+
const appBundle = await probeMacAppBundle();
|
|
212
|
+
if (appBundle)
|
|
213
|
+
return appBundle;
|
|
214
|
+
if (await pathExists(distBinCandidate))
|
|
215
|
+
return distBinCandidate;
|
|
185
216
|
return null;
|
|
186
217
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const candidate = join(distDir, entry, 'Contents', 'Resources', relativeAppdir);
|
|
191
|
-
if (await pathExists(candidate))
|
|
192
|
-
return candidate;
|
|
193
|
-
}
|
|
194
|
-
return null;
|
|
218
|
+
if (await pathExists(distBinCandidate))
|
|
219
|
+
return distBinCandidate;
|
|
220
|
+
return probeMacAppBundle();
|
|
195
221
|
}
|
|
196
222
|
/**
|
|
197
223
|
* Top-level resolver. Walks every test path, reads the nearest
|
package/dist/src/errors/git.d.ts
CHANGED
|
@@ -39,3 +39,23 @@ export declare class GitIndexLockError extends GitError {
|
|
|
39
39
|
constructor(lockPath: string, ageMs?: number | undefined);
|
|
40
40
|
get userMessage(): string;
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Error thrown when `git add` (monolithic or chunked) exceeds the
|
|
44
|
+
* configured timeout while indexing the Firefox source tree.
|
|
45
|
+
*
|
|
46
|
+
* 2026-04-24 eval Finding 10: a 140.10.0esr bump on a previously-working
|
|
47
|
+
* 140.9.0esr workspace aborted after ~854s with a generic
|
|
48
|
+
* `AbortError: The operation was aborted`. The root cause was the
|
|
49
|
+
* `git add` timeout firing, but the surfaced error was indistinguishable
|
|
50
|
+
* from any other AbortError and gave the operator no actionable
|
|
51
|
+
* direction. This typed error carries the elapsed budget and the
|
|
52
|
+
* environment-variable override so the recovery path is
|
|
53
|
+
* self-documenting.
|
|
54
|
+
*/
|
|
55
|
+
export declare class GitIndexingTimeoutError extends GitError {
|
|
56
|
+
readonly phase: 'monolithic' | 'chunked';
|
|
57
|
+
readonly timeoutMs: number;
|
|
58
|
+
readonly envVar: string;
|
|
59
|
+
constructor(phase: 'monolithic' | 'chunked', timeoutMs: number, envVar: string, cause?: Error);
|
|
60
|
+
get userMessage(): string;
|
|
61
|
+
}
|
package/dist/src/errors/git.js
CHANGED
|
@@ -96,4 +96,43 @@ export class GitIndexLockError extends GitError {
|
|
|
96
96
|
' 3. Re-run "fireforge download --force"');
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Error thrown when `git add` (monolithic or chunked) exceeds the
|
|
101
|
+
* configured timeout while indexing the Firefox source tree.
|
|
102
|
+
*
|
|
103
|
+
* 2026-04-24 eval Finding 10: a 140.10.0esr bump on a previously-working
|
|
104
|
+
* 140.9.0esr workspace aborted after ~854s with a generic
|
|
105
|
+
* `AbortError: The operation was aborted`. The root cause was the
|
|
106
|
+
* `git add` timeout firing, but the surfaced error was indistinguishable
|
|
107
|
+
* from any other AbortError and gave the operator no actionable
|
|
108
|
+
* direction. This typed error carries the elapsed budget and the
|
|
109
|
+
* environment-variable override so the recovery path is
|
|
110
|
+
* self-documenting.
|
|
111
|
+
*/
|
|
112
|
+
export class GitIndexingTimeoutError extends GitError {
|
|
113
|
+
phase;
|
|
114
|
+
timeoutMs;
|
|
115
|
+
envVar;
|
|
116
|
+
constructor(phase, timeoutMs, envVar, cause) {
|
|
117
|
+
super(`Git ${phase} indexing exceeded the ${Math.round(timeoutMs / 1000)}s timeout`, 'add -A', cause);
|
|
118
|
+
this.phase = phase;
|
|
119
|
+
this.timeoutMs = timeoutMs;
|
|
120
|
+
this.envVar = envVar;
|
|
121
|
+
}
|
|
122
|
+
get userMessage() {
|
|
123
|
+
const minutes = Math.max(1, Math.round(this.timeoutMs / 60_000));
|
|
124
|
+
const phaseDescription = this.phase === 'monolithic'
|
|
125
|
+
? 'the monolithic `git add -A` pass'
|
|
126
|
+
: 'one of the chunked `git add -- <dir>` passes';
|
|
127
|
+
return (`Git Error: ${phaseDescription} exceeded the ${minutes}-minute timeout while indexing the Firefox source tree.\n\n` +
|
|
128
|
+
'Common triggers:\n' +
|
|
129
|
+
' - Slow or loaded disk (an external volume, encrypted filesystem, or heavily-used SSD under load).\n' +
|
|
130
|
+
' - A Firefox source tree that has grown beyond what the default timeout accommodates.\n' +
|
|
131
|
+
' - A background process (antivirus, backup, indexing) holding the working directory.\n\n' +
|
|
132
|
+
'To recover:\n' +
|
|
133
|
+
` 1. Extend the timeout via the ${this.envVar} environment variable (milliseconds; e.g. "export ${this.envVar}=1800000" for 30 minutes).\n` +
|
|
134
|
+
' 2. Re-run "fireforge download --force" — the resume path resumes from the partial initialisation, so the repeat pass is not wasted work.\n' +
|
|
135
|
+
' 3. If the problem persists, check disk throughput and free space; Firefox source indexing on a cold SSD typically completes in 1–3 minutes.');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
99
138
|
//# sourceMappingURL=git.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Re-exports all command-related types from focused sub-modules.
|
|
3
3
|
*/
|
|
4
|
-
export type { BuildOptions, DiscardOptions, DoctorOptions, DownloadOptions, ExportOptions, FurnaceApplyOptions, FurnaceCreateOptions, FurnaceDeployOptions, FurnaceOverrideOptions, FurnacePreviewOptions, FurnaceRefreshOptions, FurnaceRemoveOptions, FurnaceSyncOptions, FurnaceValidateOptions, GlobalOptions, ImportOptions, PackageOptions, PatchCompactOptions, PatchDeleteOptions, PatchReorderOptions, RebaseOptions, ReExportOptions, RegisterOptions, ResetOptions, RunOptions, SetupOptions, StatusOptions, TestOptions, TokenAddOptions, WireOptions, } from './options.js';
|
|
4
|
+
export type { BuildOptions, DiscardOptions, DoctorOptions, DownloadOptions, ExportOptions, FurnaceApplyOptions, FurnaceCreateOptions, FurnaceDeployOptions, FurnaceOverrideOptions, FurnacePreviewOptions, FurnaceRefreshOptions, FurnaceRemoveOptions, FurnaceSyncOptions, FurnaceValidateOptions, GlobalOptions, ImportOptions, PackageOptions, PatchCompactOptions, PatchDeleteOptions, PatchLintIgnoreOptions, PatchReorderOptions, PatchTierOptions, RebaseOptions, ReExportOptions, RegisterOptions, ResetOptions, RunOptions, SetupOptions, StatusOptions, TestOptions, TokenAddOptions, WireOptions, } from './options.js';
|
|
5
5
|
export type { ImportSummary, PatchCategory, PatchesManifest, PatchInfo, PatchLintIssue, PatchMetadata, PatchResult, } from './patches.js';
|
|
6
6
|
export type { DoctorCheck, ProjectStatus, TokenCoverageFileEntry, TokenCoverageReport, } from './project.js';
|
|
@@ -93,6 +93,24 @@ export interface ExportOptions {
|
|
|
93
93
|
* another patch, because the resulting queue fails `verify` immediately.
|
|
94
94
|
*/
|
|
95
95
|
allowOverlap?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Force a tier override on the new patch's `PatchMetadata.tier`. Only
|
|
98
|
+
* `"branding"` is currently recognised — Commander rejects other values
|
|
99
|
+
* before the handler runs. Use when a branding patch legitimately
|
|
100
|
+
* touches a non-allowlisted sibling that `isBrandingOnlyPatch` cannot
|
|
101
|
+
* reach (a fork-specific theme override under `browser/themes/<name>/`,
|
|
102
|
+
* a vendor-specific icon resource, etc.).
|
|
103
|
+
*/
|
|
104
|
+
tier?: 'branding';
|
|
105
|
+
/**
|
|
106
|
+
* Lint check IDs to suppress on this patch. Writes to
|
|
107
|
+
* `PatchMetadata.lintIgnore`. Repeatable on the CLI; each occurrence
|
|
108
|
+
* appends to the list. Useful when a patch is advisory-noisy by nature
|
|
109
|
+
* (a cohesive branding bundle, an auto-generated manifest) and a
|
|
110
|
+
* specific check does not apply, but `--skip-lint` is too coarse a
|
|
111
|
+
* hammer.
|
|
112
|
+
*/
|
|
113
|
+
lintIgnore?: string[];
|
|
96
114
|
}
|
|
97
115
|
/**
|
|
98
116
|
* Options for the reset command.
|
|
@@ -172,6 +190,55 @@ export interface ReExportOptions {
|
|
|
172
190
|
* `rebase`.
|
|
173
191
|
*/
|
|
174
192
|
stamp?: boolean;
|
|
193
|
+
/**
|
|
194
|
+
* Force a tier override on the selected patch(es). Only `"branding"` is
|
|
195
|
+
* currently recognised. Mutually exclusive with `--all` — mass tier
|
|
196
|
+
* changes are virtually always footguns, since different patches in
|
|
197
|
+
* the queue have different shapes.
|
|
198
|
+
*/
|
|
199
|
+
tier?: 'branding';
|
|
200
|
+
/**
|
|
201
|
+
* Lint check IDs to suppress, **appended** (union) to the patch's
|
|
202
|
+
* existing `lintIgnore` list. De-duplicated. Mutually exclusive with
|
|
203
|
+
* `--all`. To remove an entry or clear the list entirely, use the
|
|
204
|
+
* `fireforge patch lint-ignore` subcommand (which has explicit
|
|
205
|
+
* `--add` / `--remove` / `--clear` modes); re-export's append-only
|
|
206
|
+
* semantics match the operator's most common intent ("I want this
|
|
207
|
+
* patch to also suppress X").
|
|
208
|
+
*/
|
|
209
|
+
lintIgnore?: string[];
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Options for the `fireforge patch tier` subcommand. Sets or clears the
|
|
213
|
+
* `PatchMetadata.tier` field on a single patch without rewriting the
|
|
214
|
+
* `.patch` file body — the manifest is the only thing that changes.
|
|
215
|
+
*/
|
|
216
|
+
export interface PatchTierOptions {
|
|
217
|
+
/** Force the named tier on the patch. Only `"branding"` is recognised. */
|
|
218
|
+
tier?: 'branding';
|
|
219
|
+
/** Remove the `tier` override entirely, restoring auto-detection. */
|
|
220
|
+
clear?: boolean;
|
|
221
|
+
/** Print the planned change without writing. */
|
|
222
|
+
dryRun?: boolean;
|
|
223
|
+
/** Skip the confirmation prompt (required for non-TTY). */
|
|
224
|
+
yes?: boolean;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Options for the `fireforge patch lint-ignore` subcommand. Modes are
|
|
228
|
+
* mutually exclusive — exactly one of `add`, `remove`, or `clear` must
|
|
229
|
+
* be set per invocation.
|
|
230
|
+
*/
|
|
231
|
+
export interface PatchLintIgnoreOptions {
|
|
232
|
+
/** Lint check IDs to add to the patch's `lintIgnore` list (union, de-duped). */
|
|
233
|
+
add?: string[];
|
|
234
|
+
/** Lint check IDs to remove from the patch's `lintIgnore` list. */
|
|
235
|
+
remove?: string[];
|
|
236
|
+
/** Drop the `lintIgnore` field entirely. */
|
|
237
|
+
clear?: boolean;
|
|
238
|
+
/** Print the planned change without writing. */
|
|
239
|
+
dryRun?: boolean;
|
|
240
|
+
/** Skip the confirmation prompt (required for non-TTY). */
|
|
241
|
+
yes?: boolean;
|
|
175
242
|
}
|
|
176
243
|
/**
|
|
177
244
|
* Options for the rebase command.
|
|
@@ -86,11 +86,12 @@ export interface PatchMetadata {
|
|
|
86
86
|
* limit on what is legitimately one branding diff.
|
|
87
87
|
*
|
|
88
88
|
* Declaring `tier: "branding"` here forces the branding thresholds
|
|
89
|
-
* (notice
|
|
90
|
-
* `filesAffected`. The tier is the weaker claim than
|
|
91
|
-
* of all-tests still lands in the test tier even if
|
|
92
|
-
* set, because the test-tier thresholds are already
|
|
93
|
-
* and a test that is also branding-shaped is
|
|
89
|
+
* (notice 8000 / warning 18000 / error 30000 lines, ≤60 files)
|
|
90
|
+
* regardless of `filesAffected`. The tier is the weaker claim than
|
|
91
|
+
* test — a patch of all-tests still lands in the test tier even if
|
|
92
|
+
* this field is set, because the test-tier thresholds are already
|
|
93
|
+
* more permissive and a test that is also branding-shaped is
|
|
94
|
+
* vanishingly rare.
|
|
94
95
|
*
|
|
95
96
|
* Only `"branding"` is currently recognised. Unknown values are
|
|
96
97
|
* rejected by the manifest validator, not silently stripped.
|