@hominis/fireforge 0.18.1 → 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/README.md +35 -21
- 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/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/core/git-diff.js +34 -2
- 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.js +78 -29
- 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
|
@@ -63,6 +63,8 @@ export async function commitExportedPatch(input) {
|
|
|
63
63
|
description: input.description,
|
|
64
64
|
filesAffected: input.filesAffected,
|
|
65
65
|
sourceEsrVersion: input.sourceEsrVersion,
|
|
66
|
+
...(input.tier !== undefined ? { tier: input.tier } : {}),
|
|
67
|
+
...(input.lintIgnore !== undefined ? { lintIgnore: input.lintIgnore } : {}),
|
|
66
68
|
});
|
|
67
69
|
const patchPath = plan.patchPath;
|
|
68
70
|
const originalPatchContent = (await pathExists(patchPath)) ? await readText(patchPath) : null;
|
|
@@ -236,13 +238,50 @@ export async function updatePatchAndMetadata(patchesDir, filename, newContent, u
|
|
|
236
238
|
}
|
|
237
239
|
});
|
|
238
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* Merges `updates` onto `existing` and removes the listed `unset`
|
|
243
|
+
* fields. The unset path is an explicit switch over the
|
|
244
|
+
* {@link ClearablePatchMetadataField} union rather than a dynamic
|
|
245
|
+
* `delete obj[k]` so the typecheck-time guarantee that only optional
|
|
246
|
+
* fields can be cleared survives the runtime erasure — and so the lint
|
|
247
|
+
* rule against dynamic deletes does not have to be silenced. Adding a
|
|
248
|
+
* new clearable field requires extending both the union and this
|
|
249
|
+
* switch in lockstep, which is exactly the constraint we want.
|
|
250
|
+
*/
|
|
251
|
+
function applyMetadataUpdate(existing, updates, unset) {
|
|
252
|
+
const next = { ...existing, ...updates };
|
|
253
|
+
for (const field of unset) {
|
|
254
|
+
switch (field) {
|
|
255
|
+
case 'tier':
|
|
256
|
+
delete next.tier;
|
|
257
|
+
break;
|
|
258
|
+
case 'lintIgnore':
|
|
259
|
+
delete next.lintIgnore;
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return next;
|
|
264
|
+
}
|
|
239
265
|
/**
|
|
240
266
|
* Updates metadata for a patch in the manifest.
|
|
267
|
+
*
|
|
268
|
+
* Required-field updates go through the `updates` partial. Clearing an
|
|
269
|
+
* optional field (e.g. removing the `tier` override) goes through
|
|
270
|
+
* `unsetFields` because TypeScript's `exactOptionalPropertyTypes` does
|
|
271
|
+
* not let `Partial<PatchMetadata>` carry an explicit `undefined` value
|
|
272
|
+
* for fields whose declared type does not include `undefined`. The
|
|
273
|
+
* implementation deletes the listed keys from the merged record before
|
|
274
|
+
* writing, so the on-disk JSON omits them and the validator's
|
|
275
|
+
* "preserve only when present" contract is preserved.
|
|
276
|
+
*
|
|
241
277
|
* @param patchesDir - Path to the patches directory
|
|
242
278
|
* @param filename - Patch filename
|
|
243
|
-
* @param updates -
|
|
279
|
+
* @param updates - Field values to set. Pass an empty object when only
|
|
280
|
+
* clearing fields.
|
|
281
|
+
* @param unsetFields - Optional fields to remove from the entry (so
|
|
282
|
+
* serialization drops them).
|
|
244
283
|
*/
|
|
245
|
-
export async function updatePatchMetadata(patchesDir, filename, updates) {
|
|
284
|
+
export async function updatePatchMetadata(patchesDir, filename, updates, unsetFields = []) {
|
|
246
285
|
await withPatchDirectoryLock(patchesDir, async () => {
|
|
247
286
|
const manifest = await loadPatchesManifest(patchesDir);
|
|
248
287
|
if (!manifest)
|
|
@@ -252,11 +291,47 @@ export async function updatePatchMetadata(patchesDir, filename, updates) {
|
|
|
252
291
|
return;
|
|
253
292
|
const existingPatch = manifest.patches[patchIndex];
|
|
254
293
|
if (existingPatch) {
|
|
255
|
-
manifest.patches[patchIndex] =
|
|
294
|
+
manifest.patches[patchIndex] = applyMetadataUpdate(existingPatch, updates, unsetFields);
|
|
256
295
|
await savePatchesManifest(patchesDir, manifest);
|
|
257
296
|
}
|
|
258
297
|
});
|
|
259
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* Reads a patch's metadata under the directory lock, applies a mutator
|
|
301
|
+
* function to compute the update, and writes the result back — all
|
|
302
|
+
* under a single lock so a concurrent writer cannot interleave a
|
|
303
|
+
* read-modify-write cycle. Useful for operations that need to compute
|
|
304
|
+
* the new value from the old (e.g. unioning a `lintIgnore` list,
|
|
305
|
+
* removing a specific entry), which {@link updatePatchMetadata}'s flat
|
|
306
|
+
* merge cannot express on its own.
|
|
307
|
+
*
|
|
308
|
+
* The mutator returns `{ set, unset }` so it can both write fields
|
|
309
|
+
* and drop optional ones. `set` and `unset` are merged before write:
|
|
310
|
+
* `set` runs first via spread, then `unset` deletes the listed keys.
|
|
311
|
+
*
|
|
312
|
+
* @returns The pre/post metadata pair when the patch is found and the
|
|
313
|
+
* write succeeds; `null` when the manifest is missing or the named
|
|
314
|
+
* patch is not in it. Callers should treat `null` as "no-op, nothing
|
|
315
|
+
* to log".
|
|
316
|
+
*/
|
|
317
|
+
export async function mutatePatchMetadata(patchesDir, filename, mutator) {
|
|
318
|
+
return await withPatchDirectoryLock(patchesDir, async () => {
|
|
319
|
+
const manifest = await loadPatchesManifest(patchesDir);
|
|
320
|
+
if (!manifest)
|
|
321
|
+
return null;
|
|
322
|
+
const patchIndex = manifest.patches.findIndex((p) => p.filename === filename);
|
|
323
|
+
if (patchIndex === -1)
|
|
324
|
+
return null;
|
|
325
|
+
const existingPatch = manifest.patches[patchIndex];
|
|
326
|
+
if (!existingPatch)
|
|
327
|
+
return null;
|
|
328
|
+
const { set = {}, unset = [] } = mutator(existingPatch);
|
|
329
|
+
const updatedPatch = applyMetadataUpdate(existingPatch, set, unset);
|
|
330
|
+
manifest.patches[patchIndex] = updatedPatch;
|
|
331
|
+
await savePatchesManifest(patchesDir, manifest);
|
|
332
|
+
return { before: existingPatch, after: updatedPatch };
|
|
333
|
+
});
|
|
334
|
+
}
|
|
260
335
|
/**
|
|
261
336
|
* Finds patches that are completely superseded by newer patches.
|
|
262
337
|
* A patch is superseded if all its affected files are covered by newer patches.
|
|
@@ -419,6 +494,10 @@ async function computeExportPlanUnderLock(input) {
|
|
|
419
494
|
createdAt: new Date().toISOString(),
|
|
420
495
|
sourceEsrVersion: input.sourceEsrVersion,
|
|
421
496
|
filesAffected: input.filesAffected,
|
|
497
|
+
...(input.tier !== undefined ? { tier: input.tier } : {}),
|
|
498
|
+
...(input.lintIgnore !== undefined && input.lintIgnore.length > 0
|
|
499
|
+
? { lintIgnore: input.lintIgnore }
|
|
500
|
+
: {}),
|
|
422
501
|
};
|
|
423
502
|
const supersedeMatches = await findAllPatchesForFilesWithDetails(input.patchesDir, input.filesAffected, patchFilename);
|
|
424
503
|
const supersededDetails = supersedeMatches.map((m) => ({
|
|
@@ -65,14 +65,55 @@ const PATCH_LINE_THRESHOLDS = {
|
|
|
65
65
|
* Branding patches have a legitimate reason to be large: they include
|
|
66
66
|
* every locale's `brand.ftl`, copied upstream CSS/PNG assets, and the
|
|
67
67
|
* fork-specific `configure.sh` / `brand.properties` under a single
|
|
68
|
-
* `browser/branding/<name>/` subtree.
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
68
|
+
* `browser/branding/<name>/` subtree. Calibrated against:
|
|
69
|
+
*
|
|
70
|
+
* - The 2026-04-21 eval baseline: a fresh-fork branding export landed
|
|
71
|
+
* at 15904 lines (localized brand.ftl across many locales + SVG path
|
|
72
|
+
* data + copied upstream CSS).
|
|
73
|
+
* - The 2026-04-25 operator data point: a freshly setup branding patch
|
|
74
|
+
* (post-binary-exclusion, after Phase 1+2 patch splits) landed at
|
|
75
|
+
* 15650 lines — within 2% of the eval baseline.
|
|
76
|
+
*
|
|
77
|
+
* Both data points need to surface as a soft `notice` rather than a
|
|
78
|
+
* `warning`, since they represent the *minimum* branding diff. The
|
|
79
|
+
* pre-2026-04-25 calibration {3000/8000/20000} put 15904 firmly in the
|
|
80
|
+
* `warning` band, contradicting the docstring's "loud but not
|
|
81
|
+
* actionable" intent. The current calibration moves the warning band
|
|
82
|
+
* above the eval baseline (with ~13% headroom) and the error band to
|
|
83
|
+
* roughly 2× the baseline — reaching `error` strongly suggests
|
|
84
|
+
* non-branding work is bundled in.
|
|
85
|
+
*
|
|
86
|
+
* Permissive thresholds are safe because the *gate* into this tier is
|
|
87
|
+
* narrow (auto-detect requires every file under `browser/branding/`
|
|
88
|
+
* plus a tight registration allowlist, or an explicit
|
|
89
|
+
* `PatchMetadata.tier: "branding"` opt-in). A non-branding patch
|
|
90
|
+
* cannot accidentally land here.
|
|
74
91
|
*/
|
|
75
|
-
branding: { notice:
|
|
92
|
+
branding: { notice: 8000, warning: 18000, error: 30000 },
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* File-count thresholds for the `large-patch-files` rule, mirroring the
|
|
96
|
+
* tier shape of {@link PATCH_LINE_THRESHOLDS}. A single warning-only
|
|
97
|
+
* threshold per tier is intentional — file count expresses scope, not
|
|
98
|
+
* blast radius, and there is no error band that would block export.
|
|
99
|
+
*
|
|
100
|
+
* The branding tier sits well above the typical floor because branding
|
|
101
|
+
* patches inherently span many files: PNG/ICO icon assets in 7+ sizes,
|
|
102
|
+
* MSIX manifests, channel-specific configs, locale `.ftl` files,
|
|
103
|
+
* Windows/macOS launcher resources. The 2026-04-25 operator data point
|
|
104
|
+
* reported a 56-file fresh-fork branding bundle as the minimum shape;
|
|
105
|
+
* 60 leaves headroom for additional channels/locales while still firing
|
|
106
|
+
* on a genuinely bloated patch.
|
|
107
|
+
*
|
|
108
|
+
* Test tier matches general because a test-only patch rarely touches
|
|
109
|
+
* many files (a single regression test usually adds 1–3 fixtures); the
|
|
110
|
+
* elevation in {@link PATCH_LINE_THRESHOLDS.test} addresses big
|
|
111
|
+
* table-driven test bodies, not file fan-out.
|
|
112
|
+
*/
|
|
113
|
+
const PATCH_FILES_THRESHOLDS = {
|
|
114
|
+
general: 5,
|
|
115
|
+
test: 5,
|
|
116
|
+
branding: 60,
|
|
76
117
|
};
|
|
77
118
|
/**
|
|
78
119
|
* Fixed allowlist of non-branding sibling paths that real-world Firefox
|
|
@@ -502,50 +543,58 @@ export function resolvePatchSizeTier(filesAffected, patchTier) {
|
|
|
502
543
|
*/
|
|
503
544
|
export function lintPatchSize(filesAffected, lineCount, patchTier) {
|
|
504
545
|
const issues = [];
|
|
505
|
-
if (filesAffected.length > 5) {
|
|
506
|
-
issues.push({
|
|
507
|
-
file: AGGREGATE_PATCH_FILE,
|
|
508
|
-
check: 'large-patch-files',
|
|
509
|
-
message: `Patch affects ${filesAffected.length} files (recommended: ≤5). Consider splitting into smaller, focused patches.`,
|
|
510
|
-
severity: 'warning',
|
|
511
|
-
});
|
|
512
|
-
}
|
|
513
546
|
// Tier selection: test > branding > general. Tests keep their elevated
|
|
514
547
|
// thresholds because a big regression test is legitimate (table-driven
|
|
515
|
-
// harnesses run into the thousands of lines). Branding patches get
|
|
516
|
-
// own tier so a first-export of setup-generated branding doesn't
|
|
517
|
-
// the general hard limit — see `PATCH_LINE_THRESHOLDS.branding`
|
|
518
|
-
// for the eval data
|
|
519
|
-
//
|
|
520
|
-
//
|
|
521
|
-
//
|
|
548
|
+
// harnesses run into the thousands of lines). Branding patches get
|
|
549
|
+
// their own tier so a first-export of setup-generated branding doesn't
|
|
550
|
+
// fire the general hard limit — see `PATCH_LINE_THRESHOLDS.branding`
|
|
551
|
+
// and `PATCH_FILES_THRESHOLDS.branding` above for the eval data
|
|
552
|
+
// motivating this tier. An explicit `patchTier` opt-in forces branding
|
|
553
|
+
// even when `isBrandingOnlyPatch` cannot reach the patch's actual
|
|
554
|
+
// shape (a branding patch that also touches a non-allowlisted sibling
|
|
555
|
+
// like a vendor-specific icon resource). Both checks read off the
|
|
556
|
+
// same decision so the file-count and line-count rules cannot
|
|
557
|
+
// disagree about which tier applies.
|
|
522
558
|
const decision = resolvePatchSizeTier(filesAffected, patchTier);
|
|
523
|
-
const
|
|
559
|
+
const fileThreshold = decision.tier === 'test'
|
|
560
|
+
? PATCH_FILES_THRESHOLDS.test
|
|
561
|
+
: decision.tier === 'branding'
|
|
562
|
+
? PATCH_FILES_THRESHOLDS.branding
|
|
563
|
+
: PATCH_FILES_THRESHOLDS.general;
|
|
564
|
+
const lineThresholds = decision.tier === 'test'
|
|
524
565
|
? PATCH_LINE_THRESHOLDS.test
|
|
525
566
|
: decision.tier === 'branding'
|
|
526
567
|
? PATCH_LINE_THRESHOLDS.branding
|
|
527
568
|
: PATCH_LINE_THRESHOLDS.general;
|
|
528
|
-
if (
|
|
569
|
+
if (filesAffected.length > fileThreshold) {
|
|
570
|
+
issues.push({
|
|
571
|
+
file: AGGREGATE_PATCH_FILE,
|
|
572
|
+
check: 'large-patch-files',
|
|
573
|
+
message: `Patch affects ${filesAffected.length} files (recommended: ≤${fileThreshold}). Consider splitting into smaller, focused patches.`,
|
|
574
|
+
severity: 'warning',
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
if (lineCount >= lineThresholds.error) {
|
|
529
578
|
issues.push({
|
|
530
579
|
file: AGGREGATE_PATCH_FILE,
|
|
531
580
|
check: 'large-patch-lines',
|
|
532
|
-
message: `Patch is ${lineCount} lines (hard limit: ${
|
|
581
|
+
message: `Patch is ${lineCount} lines (hard limit: ${lineThresholds.error}). Consider splitting into smaller, focused patches.`,
|
|
533
582
|
severity: 'error',
|
|
534
583
|
});
|
|
535
584
|
}
|
|
536
|
-
else if (lineCount >=
|
|
585
|
+
else if (lineCount >= lineThresholds.warning) {
|
|
537
586
|
issues.push({
|
|
538
587
|
file: AGGREGATE_PATCH_FILE,
|
|
539
588
|
check: 'large-patch-lines',
|
|
540
|
-
message: `Patch is ${lineCount} lines (soft limit: ${
|
|
589
|
+
message: `Patch is ${lineCount} lines (soft limit: ${lineThresholds.warning}, hard limit: ${lineThresholds.error}). Consider splitting into smaller, focused patches.`,
|
|
541
590
|
severity: 'warning',
|
|
542
591
|
});
|
|
543
592
|
}
|
|
544
|
-
else if (lineCount >=
|
|
593
|
+
else if (lineCount >= lineThresholds.notice) {
|
|
545
594
|
issues.push({
|
|
546
595
|
file: AGGREGATE_PATCH_FILE,
|
|
547
596
|
check: 'large-patch-lines',
|
|
548
|
-
message: `Patch is ${lineCount} lines (soft limit: ${
|
|
597
|
+
message: `Patch is ${lineCount} lines (soft limit: ${lineThresholds.warning}, hard limit: ${lineThresholds.error}). Consider splitting into smaller, focused patches.`,
|
|
549
598
|
severity: 'notice',
|
|
550
599
|
});
|
|
551
600
|
}
|
|
@@ -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.
|