Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 66 additions & 55 deletions netlify-plugins/cache-docusaurus-dirs-file/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ function makeKey({ dir, repoRoot }) {

let key = dir;

// Auto-invalidate node_modules with yarn.lock changes
if (dir === "node_modules") {
const yarnLockPath = path.join(repoRoot, "yarn.lock");
const hash = fileSha256(yarnLockPath);
Expand All @@ -42,98 +41,104 @@ function makeKey({ dir, repoRoot }) {
}
}

// On-demand busting
if (cacheVersion) {
key = `${key}@cv-${cacheVersion}`;
}

// Per-branch isolation
if (perBranch) {
key = `${key}@${branch}`;
}
return key;
}

async function restoreDir(utils, absPath, key) {
async function restoreDir(utils, absPath, key, results) {
try {
// File-based caching: check if cache exists, then restore
const cacheExists = await utils.cache.has(key);
if (!cacheExists) {
utils.status.show({
title: `No cache: ${key}`,
summary: `No prior cache for ${absPath}`,
});
return false;
}
const restored = await utils.cache.restore(absPath, { key });

const restored = await utils.cache.restore(key);
if (restored) {
// File-based caching often requires manual copy from cache location
// The exact implementation may vary based on Netlify's file-based API
utils.status.show({
title: `Cache restored: ${key}`,
summary: `Restored into ${absPath}`,
});
const msg = `✓ Restored cache: ${key}`;
console.log(`[CACHE] ${msg}`);
results.push(msg);
} else {
utils.status.show({
title: `Cache restore failed: ${key}`,
summary: `Failed to restore cache for ${absPath}`,
});
const msg = `No cache found for ${key}`;
console.log(`[CACHE] ${msg}`);
results.push(msg);
}
return restored;
} catch (err) {
utils.status.show({
title: `Cache restore failed: ${key}`,
summary: err.message,
});
const msg = `✗ Error restoring ${key}: ${err.message}`;
console.log(`[CACHE] ${msg}`);
results.push(msg);
return false;
}
}

async function saveDir(utils, absPath, key) {
async function saveDir(utils, absPath, key, results, dir) {
try {
// File-based caching: save directory to cache
const saved = await utils.cache.save(key, absPath);
utils.status.show({
title: saved ? `Cache saved: ${key}` : `Cache skipped: ${key}`,
summary: `${saved ? "Saved" : "Skipped"} from ${absPath}`,
});
const saved = await utils.cache.save(absPath, { key });
const msg = saved ? `✓ Saved cache: ${key}` : `⊖ Skipped cache: ${key}`;
console.log(`[CACHE] ${msg}`);
results.push(msg);
return saved;
} catch (err) {
utils.status.show({
title: `Cache save failed: ${key}`,
summary: err.message,
});
// Known issue: node_modules/.bin symlinks can cause EISDIR errors
// This is non-critical as the rest of node_modules still gets cached
if (dir === "node_modules" && err.message.includes("EISDIR")) {
const msg = `⚠ Partial cache saved for ${key} (symlink issue in .bin - non-critical)`;
console.log(`[CACHE] ${msg}`);
results.push(msg);
return true; // Consider it a success since most of node_modules is cached
}
const msg = `✗ Error saving ${key}: ${err.message}`;
console.log(`[CACHE] ${msg}`);
results.push(msg);
return false;
}
}

module.exports = {
async onPreBuild({ inputs, utils }) {
const results = [];
console.log('[CACHE] ========== Cache Restore Starting ==========');
console.log('[CACHE] Environment:', {
CACHE_VERSION: process.env.CACHE_VERSION,
CACHE_PER_BRANCH: process.env.CACHE_PER_BRANCH,
NETLIFY_BRANCH: process.env.NETLIFY_BRANCH
});
Comment on lines +103 to +107
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The environment object is being logged directly to console.log, which will output "[object Object]" instead of the actual values. To properly display the environment variables, either use JSON.stringify() or log each variable separately.

Suggested change
console.log('[CACHE] Environment:', {
CACHE_VERSION: process.env.CACHE_VERSION,
CACHE_PER_BRANCH: process.env.CACHE_PER_BRANCH,
NETLIFY_BRANCH: process.env.NETLIFY_BRANCH
});
console.log('[CACHE] Environment:', JSON.stringify({
CACHE_VERSION: process.env.CACHE_VERSION,
CACHE_PER_BRANCH: process.env.CACHE_PER_BRANCH,
NETLIFY_BRANCH: process.env.NETLIFY_BRANCH
}, null, 2));

Copilot uses AI. Check for mistakes.

const repoRoot = process.cwd();
const dirs = inputs.dirs;

for (const dir of dirs) {
const key = makeKey({ dir, repoRoot });
console.log(`[CACHE] Processing ${dir} with key: ${key}`);
const abs = path.resolve(repoRoot, dir);

await restoreDir(utils, abs, key);
await restoreDir(utils, abs, key, results);

// Guidance for node_modules: ensure yarn.lock exists to keep cache fresh
if (dir === "node_modules") {
const yarnLockPath = path.join(repoRoot, "yarn.lock");
if (!fs.existsSync(yarnLockPath)) {
utils.status.show({
title: "node_modules cache warning",
summary:
"yarn.lock not found. Caching node_modules without yarn.lock can lead to stale dependencies. Ensure yarn.lock is committed.",
});
const msg = "⚠ Warning: yarn.lock not found - node_modules cache may be stale";
console.log(`[CACHE] ${msg}`);
results.push(msg);
}
}
}

console.log('[CACHE] ========== Cache Restore Complete ==========');

// Show combined status in deploy summary
utils.status.show({
title: "Cache Restore Summary",
summary: results.length > 0 ? results.join('\n') : 'No cache operations performed',
});
},

async onPostBuild({ inputs, utils }) {
const results = [];
console.log('[CACHE] ========== Cache Save Starting ==========');
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The onPostBuild function is missing the same environment variable logging that was added to onPreBuild. For consistency and debugging purposes, consider adding similar logging here to help track cache key generation during the save phase. This would help debug issues where save and restore operations might use different keys.

Suggested change
console.log('[CACHE] ========== Cache Save Starting ==========');
console.log('[CACHE] ========== Cache Save Starting ==========');
console.log('[CACHE] Environment:', {
CACHE_VERSION: process.env.CACHE_VERSION,
CACHE_PER_BRANCH: process.env.CACHE_PER_BRANCH,
NETLIFY_BRANCH: process.env.NETLIFY_BRANCH
});

Copilot uses AI. Check for mistakes.

const repoRoot = process.cwd();
const dirs = inputs.dirs;

Expand All @@ -142,23 +147,29 @@ module.exports = {
const abs = path.resolve(repoRoot, dir);

if (!dirExists(abs)) {
utils.status.show({
title: `Cache not saved (missing directory)`,
summary: `${abs} does not exist.`,
});
const msg = `⊖ Skipped ${dir}: directory does not exist`;
console.log(`[CACHE] ${msg}`);
results.push(msg);
continue;
}

const hasFiles = fs.readdirSync(abs).length > 0;
if (!hasFiles) {
utils.status.show({
title: `Cache not saved (empty directory)`,
summary: `${abs} is empty.`,
});
const msg = `⊖ Skipped ${dir}: directory is empty`;
console.log(`[CACHE] ${msg}`);
results.push(msg);
continue;
}

await saveDir(utils, abs, key);
await saveDir(utils, abs, key, results, dir);
}

console.log('[CACHE] ========== Cache Save Complete ==========');

// Show combined status in deploy summary (this overrides the onPreBuild status)
utils.status.show({
title: "Cache Operations Complete",
summary: results.length > 0 ? results.join('\n') : 'No cache operations performed',
});
},
};