@hominis/fireforge 0.27.1 → 0.27.3
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 +12 -0
- package/README.md +5 -5
- package/dist/src/cli.js +5 -1
- package/dist/src/commands/build.js +61 -1
- package/dist/src/commands/doctor-working-tree.js +5 -1
- package/dist/src/commands/download.js +178 -112
- package/dist/src/commands/export-all.js +3 -2
- package/dist/src/commands/export-flow.d.ts +2 -0
- package/dist/src/commands/export-flow.js +2 -0
- package/dist/src/commands/export.js +5 -4
- package/dist/src/commands/import.js +2 -1
- package/dist/src/commands/re-export.js +6 -6
- package/dist/src/commands/rebase/continue.js +2 -0
- package/dist/src/commands/rebase/index.d.ts +2 -2
- package/dist/src/commands/rebase/index.js +9 -4
- package/dist/src/commands/rebase/patch-loop.js +5 -5
- package/dist/src/commands/rebase/summary.js +7 -2
- package/dist/src/commands/resolve.js +2 -1
- package/dist/src/commands/status-output.d.ts +13 -0
- package/dist/src/commands/status-output.js +186 -0
- package/dist/src/commands/status.js +4 -247
- package/dist/src/commands/verify.js +32 -16
- package/dist/src/core/build-prepare.js +12 -4
- package/dist/src/core/firefox-archive.js +7 -3
- package/dist/src/core/firefox-cache.d.ts +1 -1
- package/dist/src/core/firefox-cache.js +12 -5
- package/dist/src/core/firefox.js +1 -1
- package/dist/src/core/git.js +7 -2
- package/dist/src/core/ownership-table.d.ts +3 -1
- package/dist/src/core/ownership-table.js +31 -7
- package/dist/src/core/patch-export.d.ts +4 -0
- package/dist/src/core/patch-export.js +4 -0
- package/dist/src/core/patch-manifest-consistency.d.ts +1 -1
- package/dist/src/core/patch-manifest-consistency.js +4 -2
- package/dist/src/core/patch-manifest-query.d.ts +4 -3
- package/dist/src/core/patch-manifest-query.js +12 -4
- package/dist/src/core/patch-manifest-validate.js +22 -4
- package/dist/src/core/patch-source-metadata.d.ts +8 -0
- package/dist/src/core/patch-source-metadata.js +17 -0
- package/dist/src/core/rebase-session.d.ts +8 -3
- package/dist/src/core/rebase-session.js +1 -1
- package/dist/src/core/status-classify.d.ts +4 -1
- package/dist/src/core/status-classify.js +4 -5
- package/dist/src/errors/download.d.ts +11 -0
- package/dist/src/errors/download.js +33 -1
- package/dist/src/types/commands/patches.d.ts +9 -1
- package/package.json +1 -1
|
@@ -28,7 +28,7 @@ import { info } from '../utils/logger.js';
|
|
|
28
28
|
* {@link import('../core/patch-lint.js').collectNewFileCreatorsByPath};
|
|
29
29
|
* paths with a `.length > 1` owner list become duplicate-create conflicts
|
|
30
30
|
*/
|
|
31
|
-
export function buildOwnershipTable(manifestPatches, worktreeFiles, newFileCreatorsByPath) {
|
|
31
|
+
export function buildOwnershipTable(manifestPatches, worktreeFiles, newFileCreatorsByPath, classifications = new Map()) {
|
|
32
32
|
const ownersByPath = new Map();
|
|
33
33
|
for (const patch of manifestPatches) {
|
|
34
34
|
for (const file of patch.filesAffected) {
|
|
@@ -86,6 +86,13 @@ export function buildOwnershipTable(manifestPatches, worktreeFiles, newFileCreat
|
|
|
86
86
|
conflict: isFilesAffectedConflict || isDuplicateCreateConflict,
|
|
87
87
|
conflictReason,
|
|
88
88
|
unmanaged: false,
|
|
89
|
+
state: isFilesAffectedConflict || isDuplicateCreateConflict
|
|
90
|
+
? 'conflict'
|
|
91
|
+
: classifications.get(path) === 'patch-backed'
|
|
92
|
+
? 'patch-backed'
|
|
93
|
+
: classifications.get(path) === 'patch-owned-drift'
|
|
94
|
+
? 'patch-owned-drift'
|
|
95
|
+
: 'owned',
|
|
89
96
|
});
|
|
90
97
|
}
|
|
91
98
|
for (const path of unmanagedOnly) {
|
|
@@ -95,6 +102,7 @@ export function buildOwnershipTable(manifestPatches, worktreeFiles, newFileCreat
|
|
|
95
102
|
conflict: false,
|
|
96
103
|
conflictReason: null,
|
|
97
104
|
unmanaged: true,
|
|
105
|
+
state: 'unmanaged',
|
|
98
106
|
});
|
|
99
107
|
}
|
|
100
108
|
rows.sort((a, b) => a.path.localeCompare(b.path));
|
|
@@ -115,6 +123,22 @@ function renderConflictCell(row) {
|
|
|
115
123
|
return 'CONFLICT (dup-create)';
|
|
116
124
|
return 'CONFLICT';
|
|
117
125
|
}
|
|
126
|
+
function renderStateCell(row) {
|
|
127
|
+
if (row.conflict)
|
|
128
|
+
return renderConflictCell(row);
|
|
129
|
+
switch (row.state) {
|
|
130
|
+
case 'owned':
|
|
131
|
+
return 'owned';
|
|
132
|
+
case 'patch-backed':
|
|
133
|
+
return 'patch-backed';
|
|
134
|
+
case 'patch-owned-drift':
|
|
135
|
+
return 'patch-owned drift';
|
|
136
|
+
case 'unmanaged':
|
|
137
|
+
return 'unmanaged';
|
|
138
|
+
case 'conflict':
|
|
139
|
+
return renderConflictCell(row);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
118
142
|
/**
|
|
119
143
|
* Renders the ownership table as a GitHub-flavored Markdown pipe table.
|
|
120
144
|
* Using markdown-table's own serializer would require a seed document to
|
|
@@ -128,17 +152,17 @@ export function renderOwnershipTable(rows) {
|
|
|
128
152
|
}
|
|
129
153
|
const pathHeader = 'path';
|
|
130
154
|
const ownerHeader = 'owning patch';
|
|
131
|
-
const
|
|
155
|
+
const stateHeader = 'state';
|
|
132
156
|
const pathWidth = Math.max(pathHeader.length, ...rows.map((r) => r.path.length));
|
|
133
157
|
const ownerWidth = Math.max(ownerHeader.length, ...rows.map((r) => (r.unmanaged ? 1 : r.owners.join(', ').length)));
|
|
134
|
-
const
|
|
158
|
+
const stateWidth = Math.max(stateHeader.length, ...rows.map((r) => renderStateCell(r).length), 8);
|
|
135
159
|
const pad = (text, width) => text + ' '.repeat(width - text.length);
|
|
136
|
-
info(`| ${pad(pathHeader, pathWidth)} | ${pad(ownerHeader, ownerWidth)} | ${pad(
|
|
137
|
-
info(`| ${'-'.repeat(pathWidth)} | ${'-'.repeat(ownerWidth)} | ${'-'.repeat(
|
|
160
|
+
info(`| ${pad(pathHeader, pathWidth)} | ${pad(ownerHeader, ownerWidth)} | ${pad(stateHeader, stateWidth)} |`);
|
|
161
|
+
info(`| ${'-'.repeat(pathWidth)} | ${'-'.repeat(ownerWidth)} | ${'-'.repeat(stateWidth)} |`);
|
|
138
162
|
for (const row of rows) {
|
|
139
163
|
const ownerCell = row.unmanaged ? '-' : row.owners.join(', ');
|
|
140
|
-
const
|
|
141
|
-
info(`| ${pad(row.path, pathWidth)} | ${pad(ownerCell, ownerWidth)} | ${pad(
|
|
164
|
+
const stateCell = renderStateCell(row);
|
|
165
|
+
info(`| ${pad(row.path, pathWidth)} | ${pad(ownerCell, ownerWidth)} | ${pad(stateCell, stateWidth)} |`);
|
|
142
166
|
}
|
|
143
167
|
}
|
|
144
168
|
//# sourceMappingURL=ownership-table.js.map
|
|
@@ -36,6 +36,8 @@ export interface CommitExportedPatchInput {
|
|
|
36
36
|
diff: string;
|
|
37
37
|
filesAffected: string[];
|
|
38
38
|
sourceEsrVersion: string;
|
|
39
|
+
sourceProduct?: FireForgeConfig['firefox']['product'];
|
|
40
|
+
sourceVersion?: string;
|
|
39
41
|
/** Optional `PatchMetadata.tier` opt-in (only `"branding"` recognised). */
|
|
40
42
|
tier?: 'branding';
|
|
41
43
|
/** Optional `PatchMetadata.lintIgnore` (empty array treated as absent). */
|
|
@@ -126,6 +128,8 @@ export interface PlanExportInput {
|
|
|
126
128
|
description: string;
|
|
127
129
|
filesAffected: string[];
|
|
128
130
|
sourceEsrVersion: string;
|
|
131
|
+
sourceProduct?: FireForgeConfig['firefox']['product'];
|
|
132
|
+
sourceVersion?: string;
|
|
129
133
|
/**
|
|
130
134
|
* Optional `PatchMetadata.tier` opt-in carried from the CLI flag.
|
|
131
135
|
* Only `"branding"` is currently recognised. When provided the field
|
|
@@ -75,6 +75,8 @@ export async function commitExportedPatch(input) {
|
|
|
75
75
|
description: input.description,
|
|
76
76
|
filesAffected: input.filesAffected,
|
|
77
77
|
sourceEsrVersion: input.sourceEsrVersion,
|
|
78
|
+
...(input.sourceProduct !== undefined ? { sourceProduct: input.sourceProduct } : {}),
|
|
79
|
+
...(input.sourceVersion !== undefined ? { sourceVersion: input.sourceVersion } : {}),
|
|
78
80
|
...(input.tier !== undefined ? { tier: input.tier } : {}),
|
|
79
81
|
...(input.lintIgnore !== undefined ? { lintIgnore: input.lintIgnore } : {}),
|
|
80
82
|
...(input.config !== undefined ? { config: input.config } : {}),
|
|
@@ -262,6 +264,8 @@ async function computeExportPlanUnderLock(input) {
|
|
|
262
264
|
description: input.description,
|
|
263
265
|
createdAt: new Date().toISOString(),
|
|
264
266
|
sourceEsrVersion: input.sourceEsrVersion,
|
|
267
|
+
...(input.sourceProduct !== undefined ? { sourceProduct: input.sourceProduct } : {}),
|
|
268
|
+
sourceVersion: input.sourceVersion ?? input.sourceEsrVersion,
|
|
265
269
|
filesAffected: input.filesAffected,
|
|
266
270
|
...(input.tier !== undefined ? { tier: input.tier } : {}),
|
|
267
271
|
...(input.lintIgnore !== undefined && input.lintIgnore.length > 0
|
|
@@ -37,7 +37,7 @@ export interface RebuildPatchesManifestResult {
|
|
|
37
37
|
* Existing metadata is preserved when possible; missing entries are recovered
|
|
38
38
|
* from filename structure, patch contents, and file mtimes.
|
|
39
39
|
* @param patchesDir - Path to the patches directory
|
|
40
|
-
* @param fallbackSourceEsrVersion -
|
|
40
|
+
* @param fallbackSourceEsrVersion - source version to use for recovered legacy entries
|
|
41
41
|
* @returns {@link RebuildPatchesManifestResult} — the persisted manifest
|
|
42
42
|
* plus the filenames that were reconstructed from generic defaults.
|
|
43
43
|
*/
|
|
@@ -84,7 +84,7 @@ export async function validatePatchesManifestConsistency(patchesDir) {
|
|
|
84
84
|
* Existing metadata is preserved when possible; missing entries are recovered
|
|
85
85
|
* from filename structure, patch contents, and file mtimes.
|
|
86
86
|
* @param patchesDir - Path to the patches directory
|
|
87
|
-
* @param fallbackSourceEsrVersion -
|
|
87
|
+
* @param fallbackSourceEsrVersion - source version to use for recovered legacy entries
|
|
88
88
|
* @returns {@link RebuildPatchesManifestResult} — the persisted manifest
|
|
89
89
|
* plus the filenames that were reconstructed from generic defaults.
|
|
90
90
|
*/
|
|
@@ -135,9 +135,11 @@ export async function rebuildPatchesManifest(patchesDir, fallbackSourceEsrVersio
|
|
|
135
135
|
category: existing?.category ?? inferred.category,
|
|
136
136
|
name: existing?.name ?? inferred.name,
|
|
137
137
|
description: existing?.description ??
|
|
138
|
-
`Recovered manifest entry for ${patch.filename}. Review description and
|
|
138
|
+
`Recovered manifest entry for ${patch.filename}. Review description and source version.`,
|
|
139
139
|
createdAt: existing?.createdAt ?? new Date(patchStats.mtimeMs).toISOString(),
|
|
140
140
|
sourceEsrVersion: existing?.sourceEsrVersion ?? fallbackSourceEsrVersion,
|
|
141
|
+
sourceVersion: existing?.sourceVersion ?? existing?.sourceEsrVersion ?? fallbackSourceEsrVersion,
|
|
142
|
+
...(existing?.sourceProduct !== undefined ? { sourceProduct: existing.sourceProduct } : {}),
|
|
141
143
|
filesAffected,
|
|
142
144
|
};
|
|
143
145
|
if (existing?.lintIgnore !== undefined)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Query helpers: finding patches by file, integrity checks, version compat, stamping.
|
|
3
3
|
*/
|
|
4
4
|
import type { PatchesManifest, PatchInfo, PatchMetadata } from '../types/commands/index.js';
|
|
5
|
+
import type { FirefoxProduct } from '../types/config.js';
|
|
5
6
|
/**
|
|
6
7
|
* Gets all file paths claimed by patches other than the excluded one.
|
|
7
8
|
* @param manifest - The patches manifest
|
|
@@ -10,7 +11,7 @@ import type { PatchesManifest, PatchInfo, PatchMetadata } from '../types/command
|
|
|
10
11
|
*/
|
|
11
12
|
export declare function getClaimedFiles(manifest: PatchesManifest, excludeFilename: string): Set<string>;
|
|
12
13
|
/**
|
|
13
|
-
* Checks
|
|
14
|
+
* Checks Firefox source version compatibility.
|
|
14
15
|
* @param patchVersion - Version the patch was created for
|
|
15
16
|
* @param currentVersion - Current project version
|
|
16
17
|
* @returns Warning message if versions differ, null if compatible
|
|
@@ -39,10 +40,10 @@ export declare function validatePatchIntegrity(patchesDir: string, engineDir: st
|
|
|
39
40
|
targetFile: string | null;
|
|
40
41
|
}>>;
|
|
41
42
|
/**
|
|
42
|
-
* Stamps multiple patches with a new
|
|
43
|
+
* Stamps multiple patches with a new source version in a single
|
|
43
44
|
* manifest read-modify-write cycle.
|
|
44
45
|
* @param patchesDir - Path to the patches directory
|
|
45
46
|
* @param filenames - Patch filenames to update
|
|
46
47
|
* @param newVersion - Version string to set (e.g. "140.9.0esr")
|
|
47
48
|
*/
|
|
48
|
-
export declare function stampPatchVersions(patchesDir: string, filenames: string[], newVersion: string): Promise<void>;
|
|
49
|
+
export declare function stampPatchVersions(patchesDir: string, filenames: string[], newVersion: string, newProduct?: FirefoxProduct): Promise<void>;
|
|
@@ -25,7 +25,7 @@ export function getClaimedFiles(manifest, excludeFilename) {
|
|
|
25
25
|
return claimed;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
-
* Checks
|
|
28
|
+
* Checks Firefox source version compatibility.
|
|
29
29
|
* @param patchVersion - Version the patch was created for
|
|
30
30
|
* @param currentVersion - Current project version
|
|
31
31
|
* @returns Warning message if versions differ, null if compatible
|
|
@@ -99,21 +99,29 @@ export async function validatePatchIntegrity(patchesDir, engineDir) {
|
|
|
99
99
|
return issues;
|
|
100
100
|
}
|
|
101
101
|
/**
|
|
102
|
-
* Stamps multiple patches with a new
|
|
102
|
+
* Stamps multiple patches with a new source version in a single
|
|
103
103
|
* manifest read-modify-write cycle.
|
|
104
104
|
* @param patchesDir - Path to the patches directory
|
|
105
105
|
* @param filenames - Patch filenames to update
|
|
106
106
|
* @param newVersion - Version string to set (e.g. "140.9.0esr")
|
|
107
107
|
*/
|
|
108
|
-
export async function stampPatchVersions(patchesDir, filenames, newVersion) {
|
|
108
|
+
export async function stampPatchVersions(patchesDir, filenames, newVersion, newProduct) {
|
|
109
109
|
const manifest = await loadPatchesManifest(patchesDir);
|
|
110
110
|
if (!manifest)
|
|
111
111
|
return;
|
|
112
112
|
const filenameSet = new Set(filenames);
|
|
113
113
|
let modified = false;
|
|
114
114
|
for (const patch of manifest.patches) {
|
|
115
|
-
if (filenameSet.has(patch.filename)
|
|
115
|
+
if (!filenameSet.has(patch.filename))
|
|
116
|
+
continue;
|
|
117
|
+
if (patch.sourceEsrVersion !== newVersion ||
|
|
118
|
+
patch.sourceVersion !== newVersion ||
|
|
119
|
+
(newProduct !== undefined && patch.sourceProduct !== newProduct)) {
|
|
116
120
|
patch.sourceEsrVersion = newVersion;
|
|
121
|
+
patch.sourceVersion = newVersion;
|
|
122
|
+
if (newProduct !== undefined) {
|
|
123
|
+
patch.sourceProduct = newProduct;
|
|
124
|
+
}
|
|
117
125
|
modified = true;
|
|
118
126
|
}
|
|
119
127
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Schema validation for patches.json manifest data.
|
|
4
4
|
*/
|
|
5
5
|
import { parseObject } from '../utils/parse.js';
|
|
6
|
-
import { isArray, isObject, isValidFirefoxVersion, PATCH_CATEGORIES } from '../utils/validation.js';
|
|
6
|
+
import { isArray, isObject, isValidFirefoxProduct, isValidFirefoxVersion, PATCH_CATEGORIES, } from '../utils/validation.js';
|
|
7
7
|
function parseForwardImports(data, label) {
|
|
8
8
|
if (!isArray(data)) {
|
|
9
9
|
throw new Error(`${label} must be an array`);
|
|
@@ -45,10 +45,25 @@ export function validatePatchMetadata(data, index) {
|
|
|
45
45
|
const name = rec.string('name');
|
|
46
46
|
const description = rec.string('description');
|
|
47
47
|
const createdAt = rec.string('createdAt');
|
|
48
|
-
const sourceEsrVersion = rec.
|
|
48
|
+
const sourceEsrVersion = rec.optionalString('sourceEsrVersion');
|
|
49
|
+
const sourceVersion = rec.optionalString('sourceVersion') ?? sourceEsrVersion;
|
|
50
|
+
if (sourceVersion === undefined) {
|
|
51
|
+
throw new Error(`patches[${index}] must include sourceVersion or legacy sourceEsrVersion`);
|
|
52
|
+
}
|
|
53
|
+
const sourceProductRaw = rec.optionalString('sourceProduct');
|
|
54
|
+
let sourceProduct;
|
|
55
|
+
if (sourceProductRaw !== undefined) {
|
|
56
|
+
if (!isValidFirefoxProduct(sourceProductRaw)) {
|
|
57
|
+
throw new Error(`patches[${index}].sourceProduct must be one of: firefox, firefox-esr, firefox-beta, firefox-devedition`);
|
|
58
|
+
}
|
|
59
|
+
sourceProduct = sourceProductRaw;
|
|
60
|
+
}
|
|
49
61
|
const order = rec.nonNegativeInteger('order');
|
|
50
62
|
const category = rec.validatedString('category', (value) => /^[a-z][a-z0-9-]*$/.test(value), 'a lowercase category identifier (letters, numbers, hyphens)');
|
|
51
|
-
if (!isValidFirefoxVersion(
|
|
63
|
+
if (!isValidFirefoxVersion(sourceVersion)) {
|
|
64
|
+
throw new Error(`patches[${index}].sourceVersion must be a valid Firefox version string`);
|
|
65
|
+
}
|
|
66
|
+
if (sourceEsrVersion !== undefined && !isValidFirefoxVersion(sourceEsrVersion)) {
|
|
52
67
|
throw new Error(`patches[${index}].sourceEsrVersion must be a valid Firefox version string`);
|
|
53
68
|
}
|
|
54
69
|
const filesAffected = rec.stringArray('filesAffected');
|
|
@@ -78,9 +93,12 @@ export function validatePatchMetadata(data, index) {
|
|
|
78
93
|
name,
|
|
79
94
|
description,
|
|
80
95
|
createdAt,
|
|
81
|
-
sourceEsrVersion,
|
|
96
|
+
sourceEsrVersion: sourceEsrVersion ?? sourceVersion,
|
|
97
|
+
sourceVersion,
|
|
82
98
|
filesAffected,
|
|
83
99
|
};
|
|
100
|
+
if (sourceProduct !== undefined)
|
|
101
|
+
result.sourceProduct = sourceProduct;
|
|
84
102
|
if (lintIgnore !== undefined)
|
|
85
103
|
result.lintIgnore = lintIgnore;
|
|
86
104
|
if (tier !== undefined)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PatchMetadata } from '../types/commands/index.js';
|
|
2
|
+
import type { FirefoxConfig, FirefoxProduct } from '../types/config.js';
|
|
3
|
+
/** Metadata fields stamped on new or refreshed patch entries. */
|
|
4
|
+
export declare function buildPatchSourceMetadata(firefox: Pick<FirefoxConfig, 'product' | 'version'>): Pick<PatchMetadata, 'sourceEsrVersion' | 'sourceProduct' | 'sourceVersion'>;
|
|
5
|
+
/** Backward-compatible source version reader for legacy manifests. */
|
|
6
|
+
export declare function getPatchSourceVersion(patch: Pick<PatchMetadata, 'sourceEsrVersion' | 'sourceVersion'>): string;
|
|
7
|
+
/** Backward-compatible source product reader for legacy manifests. */
|
|
8
|
+
export declare function getPatchSourceProduct(patch: Pick<PatchMetadata, 'sourceProduct'>): FirefoxProduct | undefined;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Metadata fields stamped on new or refreshed patch entries. */
|
|
2
|
+
export function buildPatchSourceMetadata(firefox) {
|
|
3
|
+
return {
|
|
4
|
+
sourceEsrVersion: firefox.version,
|
|
5
|
+
sourceProduct: firefox.product,
|
|
6
|
+
sourceVersion: firefox.version,
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
/** Backward-compatible source version reader for legacy manifests. */
|
|
10
|
+
export function getPatchSourceVersion(patch) {
|
|
11
|
+
return patch.sourceVersion ?? patch.sourceEsrVersion;
|
|
12
|
+
}
|
|
13
|
+
/** Backward-compatible source product reader for legacy manifests. */
|
|
14
|
+
export function getPatchSourceProduct(patch) {
|
|
15
|
+
return patch.sourceProduct;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=patch-source-metadata.js.map
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Rebase session persistence for multi-patch
|
|
2
|
+
* Rebase session persistence for multi-patch Firefox source upgrades.
|
|
3
3
|
* Session state is stored at `.fireforge/rebase-session.json` and
|
|
4
4
|
* survives across CLI invocations so the user can fix conflicts and
|
|
5
5
|
* resume with `fireforge rebase --continue`.
|
|
6
6
|
*/
|
|
7
|
+
import type { FirefoxProduct } from '../types/config.js';
|
|
7
8
|
export type RebasePatchStatus = 'pending' | 'applied-clean' | 'applied-fuzz' | 'failed' | 'resolved' | 'skipped';
|
|
8
9
|
export interface RebasePatchEntry {
|
|
9
10
|
filename: string;
|
|
@@ -18,9 +19,13 @@ export interface RebasePatchEntry {
|
|
|
18
19
|
export interface RebaseSession {
|
|
19
20
|
/** ISO timestamp when the rebase started. */
|
|
20
21
|
startedAt: string;
|
|
21
|
-
/**
|
|
22
|
+
/** Source product being rebased FROM. */
|
|
23
|
+
fromProduct?: FirefoxProduct;
|
|
24
|
+
/** Source product being rebased TO. */
|
|
25
|
+
toProduct?: FirefoxProduct;
|
|
26
|
+
/** Source version being rebased FROM. */
|
|
22
27
|
fromVersion: string;
|
|
23
|
-
/**
|
|
28
|
+
/** Source version being rebased TO. */
|
|
24
29
|
toVersion: string;
|
|
25
30
|
/** Commit hash recorded before the rebase started (for --abort). */
|
|
26
31
|
preRebaseCommit: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
/**
|
|
3
|
-
* Rebase session persistence for multi-patch
|
|
3
|
+
* Rebase session persistence for multi-patch Firefox source upgrades.
|
|
4
4
|
* Session state is stored at `.fireforge/rebase-session.json` and
|
|
5
5
|
* survives across CLI invocations so the user can fix conflicts and
|
|
6
6
|
* resume with `fireforge rebase --continue`.
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
* Classification buckets for engine file changes:
|
|
10
10
|
* - `patch-backed`: content matches the expected post-patch state —
|
|
11
11
|
* normal after `fireforge import`.
|
|
12
|
+
* - `patch-owned-drift`: the file is claimed by exactly one patch, but
|
|
13
|
+
* the live engine content no longer matches that patch's expected
|
|
14
|
+
* post-apply content.
|
|
12
15
|
* - `unmanaged`: edits not explained by any patch or tool — local
|
|
13
16
|
* drift to export or discard.
|
|
14
17
|
* - `branding`: files under tool-managed branding paths, written by
|
|
@@ -23,7 +26,7 @@
|
|
|
23
26
|
* in `--json`, which misled scripts built on top of the JSON view
|
|
24
27
|
* into treating the file as routine local drift.
|
|
25
28
|
*/
|
|
26
|
-
export type FileClassification = 'patch-backed' | 'unmanaged' | 'branding' | 'furnace' | 'conflict';
|
|
29
|
+
export type FileClassification = 'patch-backed' | 'patch-owned-drift' | 'unmanaged' | 'branding' | 'furnace' | 'conflict';
|
|
27
30
|
export interface StatusFile {
|
|
28
31
|
status: string;
|
|
29
32
|
file: string;
|
|
@@ -123,7 +123,7 @@ export async function classifyFiles(files, engineDir, patchesDir, binaryName, fu
|
|
|
123
123
|
const expected = await computePatchedContent(patchesDir, engineDir, entry.file);
|
|
124
124
|
results.push({
|
|
125
125
|
...entry,
|
|
126
|
-
classification: expected === null ? 'patch-backed' : '
|
|
126
|
+
classification: expected === null ? 'patch-backed' : 'patch-owned-drift',
|
|
127
127
|
});
|
|
128
128
|
continue;
|
|
129
129
|
}
|
|
@@ -135,13 +135,12 @@ export async function classifyFiles(files, engineDir, patchesDir, binaryName, fu
|
|
|
135
135
|
]);
|
|
136
136
|
results.push({
|
|
137
137
|
...entry,
|
|
138
|
-
classification: actual === expected ? 'patch-backed' : '
|
|
138
|
+
classification: actual === expected ? 'patch-backed' : 'patch-owned-drift',
|
|
139
139
|
});
|
|
140
140
|
}
|
|
141
141
|
catch (error) {
|
|
142
|
-
verbose(`Treating ${entry.file} as
|
|
143
|
-
|
|
144
|
-
results.push({ ...entry, classification: 'unmanaged' });
|
|
142
|
+
verbose(`Treating ${entry.file} as patch-owned drift because patch-backed classification failed: ${toError(error).message}`);
|
|
143
|
+
results.push({ ...entry, classification: 'patch-owned-drift' });
|
|
145
144
|
}
|
|
146
145
|
}
|
|
147
146
|
return results;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { FirefoxProduct } from '../types/config.js';
|
|
1
2
|
import { FireForgeError } from './base.js';
|
|
2
3
|
/**
|
|
3
4
|
* Error thrown when Firefox source download fails.
|
|
@@ -8,6 +9,16 @@ export declare class DownloadError extends FireForgeError {
|
|
|
8
9
|
constructor(message: string, url?: string | undefined, cause?: Error);
|
|
9
10
|
get userMessage(): string;
|
|
10
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Error thrown when a pinned Firefox source archive checksum does not match.
|
|
14
|
+
*/
|
|
15
|
+
export declare class ChecksumMismatchError extends DownloadError {
|
|
16
|
+
readonly product: FirefoxProduct;
|
|
17
|
+
readonly expectedSha256: string;
|
|
18
|
+
readonly actualSha256: string;
|
|
19
|
+
constructor(product: FirefoxProduct, expectedSha256: string, actualSha256: string, url: string);
|
|
20
|
+
get userMessage(): string;
|
|
21
|
+
}
|
|
11
22
|
/**
|
|
12
23
|
* Error thrown when extraction of the downloaded archive fails.
|
|
13
24
|
*/
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// SPDX-License-Identifier: EUPL-1.2
|
|
2
1
|
import { FireForgeError } from './base.js';
|
|
3
2
|
import { ExitCode } from './codes.js';
|
|
4
3
|
/**
|
|
@@ -23,6 +22,39 @@ export class DownloadError extends FireForgeError {
|
|
|
23
22
|
return msg;
|
|
24
23
|
}
|
|
25
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Error thrown when a pinned Firefox source archive checksum does not match.
|
|
27
|
+
*/
|
|
28
|
+
export class ChecksumMismatchError extends DownloadError {
|
|
29
|
+
product;
|
|
30
|
+
expectedSha256;
|
|
31
|
+
actualSha256;
|
|
32
|
+
constructor(product, expectedSha256, actualSha256, url) {
|
|
33
|
+
super(`Downloaded archive SHA-256 mismatch: expected ${expectedSha256}, got ${actualSha256}`, url);
|
|
34
|
+
this.product = product;
|
|
35
|
+
this.expectedSha256 = expectedSha256;
|
|
36
|
+
this.actualSha256 = actualSha256;
|
|
37
|
+
}
|
|
38
|
+
get userMessage() {
|
|
39
|
+
let msg = `Download Error: Firefox source archive checksum mismatch.\n\n` +
|
|
40
|
+
`Product: ${this.product}\n` +
|
|
41
|
+
`URL: ${this.url}\n` +
|
|
42
|
+
`Expected SHA-256: ${this.expectedSha256}\n` +
|
|
43
|
+
`Actual SHA-256: ${this.actualSha256}`;
|
|
44
|
+
msg += '\n\nTo fix this:\n';
|
|
45
|
+
msg += ' 1. Verify firefox.product, firefox.version, and firefox.sha256 in fireforge.json\n';
|
|
46
|
+
msg += ' 2. Compare the pinned hash with Mozilla SHA256SUMMARY for the resolved archive\n';
|
|
47
|
+
if (this.product === 'firefox-devedition') {
|
|
48
|
+
msg +=
|
|
49
|
+
' 3. Developer Edition archives should resolve under https://archive.mozilla.org/pub/devedition/releases/\n';
|
|
50
|
+
msg += ' 4. Re-run "fireforge download --force" after correcting the source settings';
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
msg += ' 3. Re-run "fireforge download --force" after correcting the source settings';
|
|
54
|
+
}
|
|
55
|
+
return msg;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
26
58
|
/**
|
|
27
59
|
* Error thrown when extraction of the downloaded archive fails.
|
|
28
60
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Types for the patch system: patch metadata, manifests, lint, and import results.
|
|
3
3
|
*/
|
|
4
|
+
import type { FirefoxProduct } from '../config.js';
|
|
4
5
|
/**
|
|
5
6
|
* Patch categories for organizational classification.
|
|
6
7
|
*/
|
|
@@ -47,8 +48,15 @@ export interface PatchMetadata {
|
|
|
47
48
|
description: string;
|
|
48
49
|
/** ISO timestamp of when the patch was created */
|
|
49
50
|
createdAt: string;
|
|
50
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Deprecated compatibility alias for the source version the patch was
|
|
53
|
+
* created against. New writes also include `sourceVersion`.
|
|
54
|
+
*/
|
|
51
55
|
sourceEsrVersion: string;
|
|
56
|
+
/** Firefox source product the patch was created against. */
|
|
57
|
+
sourceProduct?: FirefoxProduct;
|
|
58
|
+
/** Firefox source version the patch was created against. */
|
|
59
|
+
sourceVersion?: string;
|
|
52
60
|
/** Array of file paths affected by this patch */
|
|
53
61
|
filesAffected: string[];
|
|
54
62
|
/**
|