@hominis/fireforge 0.30.0 → 0.30.1
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 +1 -1
- package/README.md +0 -5
- package/dist/src/commands/lint-per-patch.js +30 -18
- package/dist/src/core/lint-cache.d.ts +3 -0
- package/dist/src/core/lint-cache.js +6 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## 0.30.0
|
|
4
4
|
|
|
5
|
-
- Added safe repo-local per-patch lint result caching for `lint --per-patch`, plus `--no-cache` and `lint cache clear` escape hatches while preserving release-gate severity accounting and queue-wide checks.
|
|
5
|
+
- Added safe repo-local per-patch lint result caching for `lint --per-patch`, plus `--no-cache` and `lint cache clear` escape hatches while preserving release-gate severity accounting and queue-wide checks. Warm cache hits now skip per-patch diff generation as well as lint rule execution, guarded by patch, config, engine content, queue ownership, and engine HEAD inputs.
|
|
6
6
|
|
|
7
7
|
## 0.29.0
|
|
8
8
|
|
package/README.md
CHANGED
|
@@ -66,11 +66,6 @@ npx fireforge test browser/base/content/test/browser/
|
|
|
66
66
|
|
|
67
67
|
Use `fireforge --help` for the full set of commands.
|
|
68
68
|
|
|
69
|
-
`lint --per-patch` reuses safe repo-local results for unchanged patches from
|
|
70
|
-
`.fireforge/lint-cache/`, while still running queue-wide checks every time. Use
|
|
71
|
-
`npx fireforge lint --per-patch --no-cache` to force a fresh run, or
|
|
72
|
-
`npx fireforge lint cache clear` to drop cached per-patch lint results.
|
|
73
|
-
|
|
74
69
|
For large generated asset batches, `re-export --scan --scan-files <manifest>` assigns files to
|
|
75
70
|
their owner patches without broad directory scanning. The manifest is JSON:
|
|
76
71
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { loadConfig } from '../core/config.js';
|
|
4
4
|
import { getDiffForFilesAgainstHead } from '../core/git-diff.js';
|
|
5
|
-
import { buildPerPatchLintCacheKey, getCachedPerPatchLintIssues, loadPerPatchLintCache, savePerPatchLintCache, setCachedPerPatchLintIssues, } from '../core/lint-cache.js';
|
|
5
|
+
import { buildPerPatchLintCacheKey, getCachedPerPatchLintIssues, getPerPatchLintCacheHeadSha, loadPerPatchLintCache, savePerPatchLintCache, setCachedPerPatchLintIssues, } from '../core/lint-cache.js';
|
|
6
6
|
import { buildPatchQueueContext, lintExportedPatch, lintPatchQueue, resolvePatchSizeTier, } from '../core/patch-lint.js';
|
|
7
7
|
import { loadPatchesManifest } from '../core/patch-manifest.js';
|
|
8
8
|
import { evaluatePatchPolicy } from '../core/patch-policy.js';
|
|
@@ -13,6 +13,14 @@ function buildPerPatchMaxWarningsMessage(count, maxWarnings, linted) {
|
|
|
13
13
|
return (`Patch lint found ${count} warning(s) across ${linted} patch(es), exceeding --max-warnings ${maxWarnings}.` +
|
|
14
14
|
' If this is a release gate, run with --per-patch to identify the owning patch. For intentional staged imports, use patch staged-dependency; for ownership repairs, preview patch move-files, patch reorder --dry-run, or re-export --files --dry-run; add scoped lintIgnore only after review.');
|
|
15
15
|
}
|
|
16
|
+
function emitTierNotice(filename, files, tier) {
|
|
17
|
+
const decision = resolvePatchSizeTier(files, tier);
|
|
18
|
+
if (decision.tier !== 'branding')
|
|
19
|
+
return;
|
|
20
|
+
info(decision.source === 'explicit'
|
|
21
|
+
? `${filename}: branding threshold tier applied via patches.json \`tier: "branding"\` opt-in.`
|
|
22
|
+
: `${filename}: branding threshold tier applied (all files under browser/branding/ plus registration siblings).`);
|
|
23
|
+
}
|
|
16
24
|
/**
|
|
17
25
|
* Lints each patch in the queue as its own isolated diff, honouring
|
|
18
26
|
* per-patch `lintIgnore` entries. Cross-patch rules still run once over
|
|
@@ -28,6 +36,7 @@ export async function lintPerPatch(projectRoot, paths, options = {}) {
|
|
|
28
36
|
const config = await loadConfig(projectRoot);
|
|
29
37
|
const ctx = await buildPatchQueueContext(paths.patches);
|
|
30
38
|
const cache = options.noCache === true ? undefined : await loadPerPatchLintCache(projectRoot);
|
|
39
|
+
const engineHeadSha = cache ? await getPerPatchLintCacheHeadSha(paths.engine) : undefined;
|
|
31
40
|
let cacheDirty = false;
|
|
32
41
|
let reusedCacheEntries = 0;
|
|
33
42
|
const issues = [];
|
|
@@ -51,21 +60,11 @@ export async function lintPerPatch(projectRoot, paths, options = {}) {
|
|
|
51
60
|
skipped++;
|
|
52
61
|
continue;
|
|
53
62
|
}
|
|
54
|
-
const diff = await getDiffForFilesAgainstHead(paths.engine, existing);
|
|
55
|
-
if (!diff.trim()) {
|
|
56
|
-
skipped++;
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
63
|
const ignore = patch.lintIgnore?.length ? new Set(patch.lintIgnore) : undefined;
|
|
60
|
-
const decision = resolvePatchSizeTier(existing, patch.tier);
|
|
61
|
-
if (decision.tier === 'branding') {
|
|
62
|
-
info(decision.source === 'explicit'
|
|
63
|
-
? `${patch.filename}: branding threshold tier applied via patches.json \`tier: "branding"\` opt-in.`
|
|
64
|
-
: `${patch.filename}: branding threshold tier applied (all files under browser/branding/ plus registration siblings).`);
|
|
65
|
-
}
|
|
66
64
|
let patchIssues;
|
|
65
|
+
let cacheKey;
|
|
67
66
|
if (cache) {
|
|
68
|
-
|
|
67
|
+
cacheKey = await buildPerPatchLintCacheKey({
|
|
69
68
|
projectRoot,
|
|
70
69
|
engineDir: paths.engine,
|
|
71
70
|
patchesDir: paths.patches,
|
|
@@ -73,16 +72,29 @@ export async function lintPerPatch(projectRoot, paths, options = {}) {
|
|
|
73
72
|
existingFiles: existing,
|
|
74
73
|
config,
|
|
75
74
|
queueContext: ctx,
|
|
75
|
+
...(engineHeadSha === undefined ? {} : { engineHeadSha }),
|
|
76
76
|
});
|
|
77
77
|
patchIssues = getCachedPerPatchLintIssues(cache, patch.filename, cacheKey);
|
|
78
78
|
if (patchIssues) {
|
|
79
79
|
reusedCacheEntries++;
|
|
80
|
+
emitTierNotice(patch.filename, existing, patch.tier);
|
|
81
|
+
for (const issue of patchIssues) {
|
|
82
|
+
issues.push({ ...issue, file: `${patch.filename} :: ${issue.file}` });
|
|
83
|
+
}
|
|
84
|
+
linted++;
|
|
85
|
+
continue;
|
|
80
86
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
}
|
|
88
|
+
const diff = await getDiffForFilesAgainstHead(paths.engine, existing);
|
|
89
|
+
if (!diff.trim()) {
|
|
90
|
+
skipped++;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
emitTierNotice(patch.filename, existing, patch.tier);
|
|
94
|
+
if (cache && cacheKey) {
|
|
95
|
+
patchIssues = await lintExportedPatch(paths.engine, existing, diff, config, ctx, ignore, patch.tier);
|
|
96
|
+
setCachedPerPatchLintIssues(cache, patch.filename, cacheKey, patchIssues);
|
|
97
|
+
cacheDirty = true;
|
|
86
98
|
}
|
|
87
99
|
else {
|
|
88
100
|
patchIssues = await lintExportedPatch(paths.engine, existing, diff, config, ctx, ignore, patch.tier);
|
|
@@ -21,6 +21,7 @@ export interface PerPatchLintCacheKeyInput {
|
|
|
21
21
|
existingFiles: string[];
|
|
22
22
|
config: FireForgeConfig;
|
|
23
23
|
queueContext: PatchQueueContext;
|
|
24
|
+
engineHeadSha?: string;
|
|
24
25
|
packageVersion?: string;
|
|
25
26
|
}
|
|
26
27
|
type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
@@ -32,6 +33,8 @@ export declare function sha256Hex(content: string | Buffer): string;
|
|
|
32
33
|
export declare function stableHash(value: JsonValue): string;
|
|
33
34
|
/** Returns the repo-local per-patch lint cache file path. */
|
|
34
35
|
export declare function getPerPatchLintCachePath(projectRoot: string): string;
|
|
36
|
+
/** Returns the engine git HEAD identity used to guard diff-derived cache hits. */
|
|
37
|
+
export declare function getPerPatchLintCacheHeadSha(engineDir: string): Promise<string>;
|
|
35
38
|
/**
|
|
36
39
|
* Builds the complete per-patch lint cache key for one lintable patch.
|
|
37
40
|
* The key includes source, metadata, config, engine state, and ownership inputs.
|
|
@@ -5,6 +5,7 @@ import { join, resolve } from 'node:path';
|
|
|
5
5
|
import { pathExists, readJson, writeJson } from '../utils/fs.js';
|
|
6
6
|
import { getPackageVersion } from '../utils/package-root.js';
|
|
7
7
|
import { getFurnacePaths } from './furnace-config.js';
|
|
8
|
+
import { git } from './git-base.js';
|
|
8
9
|
import { collectNewFileCreatorsByPath } from './patch-lint.js';
|
|
9
10
|
export const LINT_CACHE_SCHEMA_VERSION = 1;
|
|
10
11
|
export const LINT_IMPLEMENTATION_VERSION = 1;
|
|
@@ -36,6 +37,10 @@ export function stableHash(value) {
|
|
|
36
37
|
export function getPerPatchLintCachePath(projectRoot) {
|
|
37
38
|
return join(projectRoot, '.fireforge', LINT_CACHE_DIRNAME, PER_PATCH_CACHE_FILENAME);
|
|
38
39
|
}
|
|
40
|
+
/** Returns the engine git HEAD identity used to guard diff-derived cache hits. */
|
|
41
|
+
export async function getPerPatchLintCacheHeadSha(engineDir) {
|
|
42
|
+
return (await git(['rev-parse', 'HEAD'], engineDir)).trim();
|
|
43
|
+
}
|
|
39
44
|
async function fileHash(path) {
|
|
40
45
|
if (!(await pathExists(path))) {
|
|
41
46
|
return { exists: false };
|
|
@@ -90,6 +95,7 @@ export async function buildPerPatchLintCacheKey(input) {
|
|
|
90
95
|
};
|
|
91
96
|
return stableHash({
|
|
92
97
|
cacheSchemaVersion: LINT_CACHE_SCHEMA_VERSION,
|
|
98
|
+
engineHeadSha: input.engineHeadSha ?? null,
|
|
93
99
|
lintImplementationVersion: LINT_IMPLEMENTATION_VERSION,
|
|
94
100
|
packageVersion: input.packageVersion ?? getPackageVersion(),
|
|
95
101
|
patchFile: await fileHash(join(input.patchesDir, input.patch.filename)),
|