Add cache retention and prune utility (#4) #8

Merged
stephen merged 1 commit from feat/4-retention-prune into main 2026-06-17 00:31:12 +00:00
Owner

What changed

The action never evicts on its own and there is no per-cache size cap, so operators with no storage lifecycle rule (or using the filesystem backend) have no first-party way to reclaim space (#4). Adds a prune utility that scans the configured store, deletes entries past a retention horizon, and removes each archive together with its .sha256 sidecar. It runs as the prune sub-action or as node src/prune.js, against the same RASTER_CACHE_* backend as restore and save.

  • Two policies, either or both: --max-age (delete entries older than 30d/168h/90m/seconds) and --max-bytes (keep the newest entries up to a total size cap, prune the rest).
  • Safe by default: --dry-run changes nothing, and a run with no policy is refused outright rather than treated as "delete everything".
  • Never deletes an entry it cannot reason about: an archive whose last-modified metadata is missing or unparseable, or an orphan sidecar, is skipped unless --force is given.
  • The archive and its .sha256 sidecar are always pruned together; the sidecar's bytes count toward the size cap.
  • Adds remove() and a recursive listTree() to both backends (S3 reuses the flat ListObjectsV2; local gets a tree walk), plus DELETE support in the S3 mock.

How it was tested

  • make lint clean (actionlint/yq absent locally and skipped; new YAML parsed separately)
  • make test green (78 tests)
  • new behavior has a test: expired pruned and fresh kept, dry-run is a no-op, missing-metadata skipped (and pruned under --force), max-bytes keeps the newest under the cap, age+size union, archive+sidecar deleted together on both the local backend and the S3 mock, backend listTree/remove, duration/size parsing, plus CLI coverage (--help, no-policy failure, dry-run vs real)
  • e2e workflow green on a runner

CHANGELOG updated? yes

**What changed** The action never evicts on its own and there is no per-cache size cap, so operators with no storage lifecycle rule (or using the filesystem backend) have no first-party way to reclaim space (#4). Adds a prune utility that scans the configured store, deletes entries past a retention horizon, and removes each archive together with its `.sha256` sidecar. It runs as the `prune` sub-action or as `node src/prune.js`, against the same `RASTER_CACHE_*` backend as restore and save. - Two policies, either or both: `--max-age` (delete entries older than `30d`/`168h`/`90m`/seconds) and `--max-bytes` (keep the newest entries up to a total size cap, prune the rest). - Safe by default: `--dry-run` changes nothing, and a run with no policy is refused outright rather than treated as "delete everything". - Never deletes an entry it cannot reason about: an archive whose last-modified metadata is missing or unparseable, or an orphan sidecar, is skipped unless `--force` is given. - The archive and its `.sha256` sidecar are always pruned together; the sidecar's bytes count toward the size cap. - Adds `remove()` and a recursive `listTree()` to both backends (S3 reuses the flat ListObjectsV2; local gets a tree walk), plus DELETE support in the S3 mock. **How it was tested** - [x] `make lint` clean (actionlint/yq absent locally and skipped; new YAML parsed separately) - [x] `make test` green (78 tests) - [x] new behavior has a test: expired pruned and fresh kept, dry-run is a no-op, missing-metadata skipped (and pruned under `--force`), `max-bytes` keeps the newest under the cap, age+size union, archive+sidecar deleted together on both the local backend and the S3 mock, backend `listTree`/`remove`, duration/size parsing, plus CLI coverage (`--help`, no-policy failure, dry-run vs real) - [ ] e2e workflow green on a runner **CHANGELOG updated?** yes
Add cache retention and prune utility (#4)
All checks were successful
test / e2e-post-save (pull_request) Successful in 5s
test / unit (pull_request) Successful in 7s
test / e2e (pull_request) Successful in 23s
9f6d37d7df
The action never evicts on its own and there is no per-cache size cap, so
operators with no storage lifecycle rule (or using the filesystem backend)
have no first-party way to reclaim space. Add a prune utility that scans the
configured store, deletes entries past a retention horizon, and removes each
archive together with its .sha256 sidecar.

Runs as the prune sub-action or as node src/prune.js, against the same
RASTER_CACHE_* backend as restore and save. Two policies, either or both:
max-age (older than a horizon) and max-bytes (keep newest up to a size cap,
prune the rest). Safe by default: dry-run changes nothing, a run with no
policy is refused, and an entry whose last-modified metadata is missing or
unparseable (or an orphan sidecar) is skipped unless forced.

Adds remove() and a recursive listTree() to both backends and DELETE support
to the S3 mock.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
rasterstate/cache-action!8
No description provided.