Skip to content
Draft
Show file tree
Hide file tree
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
45 changes: 36 additions & 9 deletions packages/cli/lib/cli/commands/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,24 @@ async function handleCache(argv) {
process.stderr.write(`Checking cache at ${chalk.bold(formatPath(ui5DataDir))} …\n`);
}

const [frameworkInfo, buildInfo] = await Promise.all([
const [frameworkInfo, buildInfo, olderVersionsInfo, pendingRemovalsInfo] = await Promise.all([
FrameworkCache.getCacheInfo(ui5DataDir),
CacheManager.getCacheInfo(ui5DataDir),
CacheManager.getOlderVersionsInfo(ui5DataDir),
CacheManager.getPendingRemovalsInfo(ui5DataDir),
]);

// Warn unconditionally about newer-version dirs — always a safety signal, never gated by verbose.
if (olderVersionsInfo.newer.length > 0) {
const newerVersions = olderVersionsInfo.newer.map((e) => e.version).join(", ");
process.stderr.write(
`${chalk.bold.yellow("Warning:")} Found newer build cache version(s): ` +
`${chalk.bold(newerVersions)} — skipping (created by a newer UI5 CLI).\n`
);
}

const hasOlderVersions = olderVersionsInfo.removable.length > 0;
const hasPendingRemovals = pendingRemovalsInfo.length > 0;
const hasActiveCache = Boolean(frameworkInfo || buildInfo);
let staleInfo = [];
let buildStaleInfo = [];
Expand All @@ -104,9 +117,9 @@ async function handleCache(argv) {
]);
}

const hasStaleCache = staleInfo.length > 0 || buildStaleInfo.length > 0;
const hasStaleCache = staleInfo.length > 0 || buildStaleInfo.length > 0 || hasPendingRemovals;

if (!hasActiveCache && !hasStaleCache) {
if (!hasActiveCache && !hasStaleCache && !hasOlderVersions) {
if (isVerbose) {
process.stderr.write(`${chalk.italic("Nothing to clean")}\n`);
}
Expand All @@ -121,7 +134,8 @@ async function handleCache(argv) {
buildAbsPath: getAbsPath(ui5DataDir, buildInfo),
buildPreSize: buildInfo?.size ?? 0,
staleInfo: withAbsPath(staleInfo, ui5DataDir),
buildAdditionalInfo: withAbsPath(buildStaleInfo, ui5DataDir),
buildAdditionalInfo: withAbsPath([...buildStaleInfo, ...pendingRemovalsInfo], ui5DataDir),
olderVersions: withAbsPath(olderVersionsInfo.removable, ui5DataDir),
});
}

Expand All @@ -139,21 +153,33 @@ async function handleCache(argv) {
buildStaleInfo = await CacheManager.getAdditionalCacheInfo(ui5DataDir);
}

// Mark all older build-cache version dirs for removal FIRST — a fast, atomic burst of
// renames before the current-version db is opened/dropped or any slow recursive delete
// runs. This guarantees that an interrupt mid-cleanup leaves every older dir resumable
// rather than stranded under its live version name.
const markedOlderVersions = await CacheManager.markOlderVersionsForRemoval(ui5DataDir);
// Keep this run's just-marked dirs out of the "stale remnant" removal report — they are
// already reported as active "outdated" caches below.
const markedPendingPaths = markedOlderVersions.map((entry) => entry.pendingPath);

const [frameworkCleanupResult, buildCleanupResult] = await Promise.all([
FrameworkCache.cleanCache(ui5DataDir),
CacheManager.cleanCache(ui5DataDir),
]);

const [additionalFrameworkCleanupResult, buildStaleCleanupResult] = await Promise.all([
FrameworkCache.cleanAdditional(ui5DataDir),
CacheManager.cleanAdditional(ui5DataDir),
]);
const [additionalFrameworkCleanupResult, buildStaleCleanupResult, removedPendingVersions] =
await Promise.all([
FrameworkCache.cleanAdditional(ui5DataDir),
CacheManager.cleanAdditional(ui5DataDir),
CacheManager.removePendingVersions(ui5DataDir, markedPendingPaths),
]);

if (isVerbose) {
const staleBuildCleanupResult = buildStaleInfo?.length > 0 ?
buildStaleCleanupResult : [];
const cleanedStaleFramework = withAbsPath(additionalFrameworkCleanupResult, ui5DataDir);
const cleanedStaleBuild = withAbsPath(staleBuildCleanupResult, ui5DataDir);
const cleanedStaleBuild = withAbsPath(
[...staleBuildCleanupResult, ...removedPendingVersions], ui5DataDir);
const frameworkResultAbsPath = getAbsPath(ui5DataDir, frameworkCleanupResult);
const buildResultAbsPath = getAbsPath(ui5DataDir, buildCleanupResult);
await displayCleanupResult({
Expand All @@ -164,6 +190,7 @@ async function handleCache(argv) {
buildSize: buildCleanupResult?.size ?? 0,
staleInfoWithAbsPaths: cleanedStaleFramework,
buildAdditionalResult: cleanedStaleBuild,
olderVersions: withAbsPath(markedOlderVersions, ui5DataDir),
});
}
}
Expand Down
75 changes: 50 additions & 25 deletions packages/cli/lib/cli/commands/helpers/cacheOutput.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ const GROUP_BUILD = "Build";
const SECTION_ACTIVE_CACHE = "Active Cache";
const SECTION_STALE_CACHE = "Stale Cache";

// Version qualifiers describe a build-cache dir's relationship to the current CACHE_VERSION.
// They only appear on live, named version dirs under Active Cache → Build. Stale entries are
// identity-less rename remnants and never carry a qualifier.
const QUALIFIER_CURRENT = "current version";
const QUALIFIER_OUTDATED = "outdated";

const CACHE_CLEAN_WARNING =
"Only run ui5 cache clean when no UI5 CLI process and no @ui5/* API consumer is actively running.";
const CACHE_CLEAN_WARNING_IMPACT =
Expand Down Expand Up @@ -46,17 +52,19 @@ function writeCategoryHeader(title) {
process.stderr.write(` ${chalk.bold(title)}\n`);
}

function writePreviewItem(absPath, detail) {
function writePreviewItem({absPath, detail, qualifier}) {
process.stderr.write(
` ${PREVIEW_MARKER} ${chalk.dim(formatPath(absPath))}` +
`${detail ? ` ${ITEM_DIVIDER} ${detail}` : ""}\n`
`${detail ? ` ${ITEM_DIVIDER} ${detail}` : ""}` +
`${qualifier ? ` ${chalk.dim(`(${qualifier})`)}` : ""}\n`
);
}

function writeCleanupItem(absPath, detail) {
function writeCleanupItem({absPath, detail, qualifier}) {
process.stderr.write(
` ${SUCCESS_MARKER} Removed ${chalk.dim(formatPath(absPath))}` +
`${detail ? ` ${ITEM_DIVIDER} ${detail}` : ""}\n`
`${detail ? ` ${ITEM_DIVIDER} ${detail}` : ""}` +
`${qualifier ? ` ${chalk.dim(`(${qualifier})`)}` : ""}\n`
);
}

Expand Down Expand Up @@ -102,7 +110,7 @@ function createBuildItems(entries, detailFormatter) {

function createSections({
activeFramework,
activeBuild,
activeBuildEntries = [],
staleFrameworkEntries,
staleBuildEntries,
staleBuildDetailFormatter,
Expand All @@ -117,12 +125,13 @@ function createSections({
items: [{absPath: activeFramework.absPath, detail}],
});
}
if (activeBuild) {
const detail = activeBuild.size > 0 ? formatSize(activeBuild.size) : "";
activeCategories.push({
title: GROUP_BUILD,
items: [{absPath: activeBuild.absPath, detail}],
});
const activeBuildItems = activeBuildEntries.map((entry) => ({
absPath: entry.absPath,
detail: entry.size > 0 ? formatSize(entry.size) : "",
qualifier: entry.qualifier,
}));
if (activeBuildItems.length > 0) {
activeCategories.push({title: GROUP_BUILD, items: activeBuildItems});
}
if (activeCategories.length > 0) {
sections.push({title: SECTION_ACTIVE_CACHE, categories: activeCategories});
Expand All @@ -148,6 +157,10 @@ function createSections({
* Display information about the cached data that will be removed.
* Entries are grouped by active and stale cache data.
*
* Older build-cache version dirs (and the current version) are listed under
* Active Cache → Build with a version qualifier. Stale entries are rename remnants
* and carry no qualifier.
*
* @param {object} data
* @param {object|null} data.frameworkInfo
* @param {object|null} data.buildInfo
Expand All @@ -156,6 +169,8 @@ function createSections({
* @param {number} data.buildPreSize
* @param {Array<{absPath: string, libraryCount: number, versionCount: number}>} data.staleInfo
* @param {Array<{absPath: string, size: number}>} data.buildAdditionalInfo
* @param {Array<{absPath: string, size: number}>} [data.olderVersions] Older build-cache version dirs
* that will be removed this run — shown under Active Cache → Build as "outdated".
*/
export function displayCacheInfo({
frameworkInfo,
Expand All @@ -165,27 +180,31 @@ export function displayCacheInfo({
buildPreSize,
staleInfo,
buildAdditionalInfo,
olderVersions = [],
}) {
const activeBuildEntries = [];
if (buildInfo) {
activeBuildEntries.push({absPath: buildAbsPath, size: buildPreSize, qualifier: QUALIFIER_CURRENT});
}
for (const entry of olderVersions) {
activeBuildEntries.push({absPath: entry.absPath, size: entry.size, qualifier: QUALIFIER_OUTDATED});
}

const sections = createSections({
activeFramework: frameworkInfo ? {
absPath: frameworkAbsPath,
libraryCount: frameworkInfo.libraryCount,
versionCount: frameworkInfo.versionCount,
} : null,
activeBuild: buildInfo ? {
absPath: buildAbsPath,
size: buildPreSize,
} : null,
activeBuildEntries,
staleFrameworkEntries: staleInfo,
staleBuildEntries: buildAdditionalInfo,
staleBuildDetailFormatter: (size) => size > 0 ? formatSize(size) : "",
});

process.stderr.write(`\n${chalk.bold("The following cached data will be removed:")}\n`);
process.stderr.write("\n");
writeGroupedSections(sections, ({absPath, detail}) => {
writePreviewItem(absPath, detail);
});
writeGroupedSections(sections, writePreviewItem);
process.stderr.write("\n");
}

Expand All @@ -200,6 +219,8 @@ export function displayCacheInfo({
* @param {number} data.buildSize
* @param {Array<{absPath: string, libraryCount: number, versionCount: number}>} data.staleInfoWithAbsPaths
* @param {Array<{absPath: string, size: number}>} data.buildAdditionalResult
* @param {Array<{absPath: string, size: number}>} [data.olderVersions] Older build-cache version dirs
* removed this run — shown under Active Cache → Build as "outdated".
*/
export function displayCleanupResult({
frameworkResult,
Expand All @@ -209,17 +230,23 @@ export function displayCleanupResult({
buildSize,
staleInfoWithAbsPaths,
buildAdditionalResult,
olderVersions = [],
}) {
const activeBuildEntries = [];
if (buildResult && buildAbsPath) {
activeBuildEntries.push({absPath: buildAbsPath, size: buildSize, qualifier: QUALIFIER_CURRENT});
}
for (const entry of olderVersions) {
activeBuildEntries.push({absPath: entry.absPath, size: entry.size, qualifier: QUALIFIER_OUTDATED});
}

const sections = createSections({
activeFramework: frameworkResult && frameworkAbsPath ? {
absPath: frameworkAbsPath,
libraryCount: frameworkResult.libraryCount,
versionCount: frameworkResult.versionCount,
} : null,
activeBuild: buildResult && buildAbsPath ? {
absPath: buildAbsPath,
size: buildSize,
} : null,
activeBuildEntries,
staleFrameworkEntries: staleInfoWithAbsPaths,
staleBuildEntries: buildAdditionalResult,
staleBuildDetailFormatter: (size) => size > 0 ? `freed ${formatSize(size)}` : "",
Expand All @@ -233,9 +260,7 @@ export function displayCleanupResult({
process.stderr.write(`\n${chalk.bold("Cleanup result:")}\n`);

process.stderr.write("\n");
writeGroupedSections(sections, ({absPath, detail}) => {
writeCleanupItem(absPath, detail);
});
writeGroupedSections(sections, writeCleanupItem);

const cleanedSections = sections.map((section) => {
const categories = section.categories.map((category) => category.title).join(" and ");
Expand Down
Loading
Loading