Skip to content

Commit 4f0bb23

Browse files
adrians5jclaude
andauthored
perf(build): gate cache→dist copy-skip behind experimental flag (#5418)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 6b93bf1 commit 4f0bb23

3 files changed

Lines changed: 46 additions & 18 deletions

File tree

scripts/buildPackages/src/buildPackages.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import path from "path";
1515
import { hideBin } from "yargs/helpers";
1616
import { PackageBuildError } from "./PackageBuildError";
1717
import { queueMetaWrite } from "./writeMetaQueue";
18-
import { writeDistBuildHash } from "./distBuildHash";
18+
import { writeDistBuildHash, isExperimentalBuildCacheEnabled } from "./distBuildHash";
1919

2020
const argv = yargs(hideBin(process.argv)).parse();
2121

@@ -90,13 +90,16 @@ export const buildPackages = async () => {
9090
try {
9191
await buildPackage(pkg, options.buildOverrides, "inherit", options.safeReplace);
9292

93-
// Record the source hash (in dist as a marker and in build meta) so
94-
// a later build treats this package as a cache hit and skips the copy.
95-
const sourceHash = await getPackageSourceHash(pkg);
96-
writeDistBuildHash(pkg, sourceHash);
97-
const meta = getBuildMeta();
98-
meta.packages[pkg.packageJson.name] = { sourceHash };
99-
writeJsonFileSync(META_FILE_PATH, meta);
93+
// EXPERIMENTAL (opt-in): record the source hash (in dist as a marker
94+
// and in build meta) so a later build treats this package as a cache
95+
// hit and skips the copy. Off by default — see distBuildHash.ts.
96+
if (isExperimentalBuildCacheEnabled()) {
97+
const sourceHash = await getPackageSourceHash(pkg);
98+
writeDistBuildHash(pkg, sourceHash);
99+
const meta = getBuildMeta();
100+
meta.packages[pkg.packageJson.name] = { sourceHash };
101+
writeJsonFileSync(META_FILE_PATH, meta);
102+
}
100103

101104
sendNotification(`Webiny Build (${projectFolder})`, "Build completed successfully");
102105
} catch (err) {
@@ -140,9 +143,12 @@ export const buildPackages = async () => {
140143

141144
// Store package hash
142145
const sourceHash = await getPackageSourceHash(pkg);
143-
// Stamp dist so a later no-op build can
144-
// skip the cache→dist copy for this package.
145-
writeDistBuildHash(pkg, sourceHash);
146+
// EXPERIMENTAL (opt-in): stamp dist so a
147+
// later no-op build can skip the
148+
// cache→dist copy for this package.
149+
if (isExperimentalBuildCacheEnabled()) {
150+
writeDistBuildHash(pkg, sourceHash);
151+
}
146152
await queueMetaWrite(async () => {
147153
const currentMeta = getBuildMeta();
148154
currentMeta.packages[pkg.packageJson.name] = {

scripts/buildPackages/src/distBuildHash.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,19 @@ import type { Package } from "./types";
88
// dist into place when the existing dist is already up to date.
99
const MARKER = ".webiny-build-hash";
1010

11+
/**
12+
* Whether the experimental "skip cache→dist copy when dist is already fresh"
13+
* optimization is enabled. OFF by default: the optimization trusts a dist
14+
* marker, which can go stale if something writes dist out of band (e.g.
15+
* `webiny watch`), so it is opt-in until that class of issues is fully solved.
16+
*
17+
* Enable with `WEBINY_EXPERIMENTAL_BUILD_CACHE=true` (or `1`).
18+
*/
19+
export function isExperimentalBuildCacheEnabled(): boolean {
20+
const value = process.env.WEBINY_EXPERIMENTAL_BUILD_CACHE;
21+
return value === "true" || value === "1";
22+
}
23+
1124
const markerPath = (pkg: Package) => path.join(getBuildOutputFolder(pkg), MARKER);
1225

1326
export function readDistBuildHash(pkg: Package): string | null {

scripts/buildPackages/src/getBatches.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import { getBuildOutputFolder } from "./getBuildOutputFolder";
99
import { getPackageSourceHash } from "./getPackageSourceHash";
1010
import { getBuildMeta } from "./getBuildMeta";
1111
import { getPackageCacheFolderPath } from "./getPackageCacheFolderPath";
12-
import { distBuildHashMatches, writeDistBuildHash } from "./distBuildHash";
12+
import {
13+
distBuildHashMatches,
14+
writeDistBuildHash,
15+
isExperimentalBuildCacheEnabled
16+
} from "./distBuildHash";
1317

1418
const { green } = chalk;
1519

@@ -119,25 +123,30 @@ export async function getBatches(options: GetBatchesOptions = {}) {
119123
}
120124
}
121125

126+
const experimentalCache = isExperimentalBuildCacheEnabled();
127+
122128
let copied = 0;
123129
for (let i = 0; i < packagesUseCache.length; i++) {
124130
const workspacePackage = packagesUseCache[i];
125131
const sourceHash = cacheHitHashes.get(workspacePackage.name)!;
126132

127-
// Skip the copy when dist was already built/restored from this exact
128-
// source hash — the bytes on disk are already identical. This is the
129-
// common local-dev case (dist persists between builds).
130-
if (distBuildHashMatches(workspacePackage, sourceHash)) {
133+
// EXPERIMENTAL (opt-in): skip the copy when dist was already
134+
// built/restored from this exact source hash — the bytes on disk are
135+
// already identical. Off by default; the marker can go stale if
136+
// something writes dist out of band (e.g. `webiny watch`).
137+
if (experimentalCache && distBuildHashMatches(workspacePackage, sourceHash)) {
131138
continue;
132139
}
133140

134141
const cacheFolderPath = path.join(CACHE_FOLDER_PATH, workspacePackage.packageJson.name);
135142
fs.copySync(cacheFolderPath, getBuildOutputFolder(workspacePackage));
136-
writeDistBuildHash(workspacePackage, sourceHash);
143+
if (experimentalCache) {
144+
writeDistBuildHash(workspacePackage, sourceHash);
145+
}
137146
copied++;
138147
}
139148

140-
if (copied > 0) {
149+
if (experimentalCache && copied > 0) {
141150
console.log(`Restored ${green(copied)} package(s) from cache into dist.`);
142151
}
143152
} else {

0 commit comments

Comments
 (0)