feat(cli): Add 'cache clean --project' to delete a project's build cache - #1564
feat(cli): Add 'cache clean --project' to delete a project's build cache#1564RandomByte wants to merge 4 commits into
Conversation
Add BuildCacheStorage#hasProjectRecords and #dropProjectRecords to delete a single project's entries across the four project-keyed tables (index_cache, stage_metadata, task_metadata, result_metadata) in one transaction. The content-addressed store is shared across projects and left untouched; orphaned blobs are reused on the next build and reclaimed by a full cache clean. Expose these via CacheManager.getProjectCacheInfo and cleanProject, mirroring getCacheInfo/cleanCache and reusing the #withStorage helper.
Add a --project/-p flag to 'ui5 cache clean' that removes only the build cache of the project in the current directory, leaving other projects' build cache and the downloaded framework packages intact. The root project id (the build cache key) is resolved from the project graph the same way 'ui5 build' does, honoring --config, --workspace and --dependency-definition. Deletion goes through the new CacheManager.cleanProject; the framework cache is not touched in this mode. Reuses the existing confirmation prompt, --force flag and verbose output gating.
Add BuildCacheStorage tests for hasProjectRecords and dropProjectRecords (only the target project's project-keyed rows are removed; other projects and shared content stay intact; unknown project is a no-op) and CacheManager tests for getProjectCacheInfo and cleanProject.
d3xter666
left a comment
There was a problem hiding this comment.
I tried to break the solution in the following ways and it behaved correctly in all cases.
1. SQL injection via --project
Not possible — all queries that accept projectId use prepared statements with ? placeholders. The value is bound, never interpolated into SQL.
2. Parallel cleanup scenarios
| Scenario | Result |
|---|---|
Two project cleanups in parallel (e.g. @openui5/sap.m and sap.ui.core) |
✅ Works correctly by design — processes operate on disjoint rows |
| Full cleanup running in parallel with a project cleanup | ✅ Both end states are equivalent; full cleanup removes everything regardless of ordering |
3. Leaving stale records after an interrupted run
Not possible, for two reasons:
- All deletes are wrapped in a
BEGIN/COMMIT/ROLLBACKtransaction — a partial run is automatically rolled back. - SQLite WAL mode provides crash recovery: if the process is terminated before
COMMIT(e.g. viaSIGTERM), the incomplete transaction has no commit record in the WAL and is discarded on the next open.
Reference: https://www.sqlite.org/wal.html
The per-project record set is also small enough that the delete completes in milliseconds, which further narrows the interruption window.
Observations
Silent no-op on unknown project name
Users need to know the exact project name (the package.json name field) to use --project effectively. For example, sap.m must be passed as @openui5/sap.m. If an incorrect or incomplete name is supplied:
ui5 cache clean --project my-project-nmae --forceThere is no error and no output — indistinguishable from a successful cleanup. This is consistent with the existing design (no output without --verbose), but it creates a silent no-op on a typo.
Co-authored-by: Yavor Ivanov <d3xter666@users.noreply.github.com>
81aaa55 to
ae466eb
Compare
This can be helpful when debugging the incremental build. It allows to delete individual cache entries related to a project instead of the entire cache.
JIRA: CPOUI5FOUNDATION-1244