Skip to content

Releases: xSAVIKx/cloud-cache-action

v1.5.0 – RustFS Support

Choose a tag to compare

@xSAVIKx xSAVIKx released this 22 Sep 04:10

No breaking changes, and nothing changes for an existing workflow: this release only adds a
provider.

Added

  • RustFS provider preset. provider: rustfs selects path-style addressing and the us-east-1
    default region for RustFS, the Apache-2.0 Rust object store that reached
    1.0 in September 2026. The full integration suite runs against RustFS 1.0.0 in CI, next to
    Garage, SeaweedFS and MinIO. Ranged downloads, multipart uploads, object tagging, user metadata
    and conditional writes all work, so no feature is disabled for it.
    • RustFS serves S3 on port 9000, the port MinIO uses, so endpoint sniffing cannot tell the two
      apart and reports minio. Both presets resolve to the same settings; set provider: rustfs
      to name it in the log.

v1.4.0 – Parallel Downloads, Upload Concurrency & Transfer Benchmarks

Choose a tag to compare

@xSAVIKx xSAVIKx released this 21 Sep 21:35

No breaking changes. Caches saved by v1.1, v1.2 and v1.3 stay valid.

Added

  • Parallel downloads. A restore now fetches any archive larger than download-chunk-size
    (default 8388608, 8 MiB) as concurrent Range requests, download-concurrency (default 8,
    1–32) at a time, in both file and streaming mode, the same fan-out actions/cache uses with a
    larger block (it uses 4 MiB; 8 MiB measured faster on every provider). Each part is retried on
    its own. Archives no larger than one chunk, and every restore with download-concurrency: 1,
    use a single request as before. A provider that answers a ranged
    request with the whole object logs
    s3://<bucket>/<key> does not support ranged GET requests; downloading it in one request. and
    gets the single request. The restore's metrics line reports the part count as downloadParts.
  • upload-concurrency input (default 8, 1–32) on the main and save actions: how many
    multipart parts a save sends at once, in both file and streaming mode. It was fixed at 4.
  • Transfer benchmark workflow (benchmark.yml, manual) that measures save and restore speed
    for several transfer settings against the live providers, and a "Transfer Performance" guide
    with the measured tables and the settings that measured best.

Changed

  • Upload defaults follow actions/cache. The default upload-chunk-size is 64 MiB instead of
    10 MiB and a save sends 8 parts at once instead of 4, so a save may hold up to 512 MiB of parts
    in memory. Set upload-concurrency: 4 and upload-chunk-size: 10485760 to keep the v1.3
    footprint. An upload-chunk-size below 5 MiB or above 128 MiB now warns and uses the default;
    before, a small value was silently replaced by 10 MiB.

v1.3.0 – Object Metadata & Tags, Explain & Inspect, Metrics

Choose a tag to compare

@xSAVIKx xSAVIKx released this 21 Sep 19:23

No breaking changes. Caches saved by v1.1 and v1.2 stay valid: the key layout, ${version}
hashing and archive format are unchanged, and a save that sets none of the new inputs writes the
same object as v1.2 did.

Added

  • Object metadata and tags. The new metadata input stores user metadata (x-amz-meta-*) on
    every saved cache object and the new tags input sets object tags, one key=value per line.
    • Keys starting with cloud-cache- are reserved, metadata is capped at 2048 bytes in total
      (including the cloud-cache-sha256 entry the save adds) and tags at 10, with S3's tag
      character set enforced. An invalid value fails the step before any S3 call.
    • Providers without object tagging log one warning
      (s3://<bucket> could not store object tags (<reason>); saved without them.) and save without
      tags.
      Garage accepts the Tagging header but implements no tagging API, so its tags cannot be read
      back and should be assumed dropped.
    • With streaming: true the metadata is attached by a copy of the object onto itself after the
      upload; a provider that cannot do that copy costs the metadata, not the cache.
  • cache-metadata output. The restored object's user metadata as a JSON object, excluding
    cloud-cache-* keys; {} when there is none, on a GitHub-tier hit, or with
    lookup-only: true, which never downloads the object.
  • A sha256 on streaming saves. A streaming: true save now attaches cloud-cache-sha256
    once the stream has finished, so streamed archives get the same integrity check on restore as
    file-mode ones. The attach is best-effort: if it fails, the save still succeeds and the object
    simply carries no checksum.
  • explain input (default false) on the main and restore actions. It logs the whole cache
    lookup into a Cache lookup explained group, and into a job summary section of the same name,
    before the restore runs: the raw and resolved s3-key-pattern, the ${version} hash and the
    paths, compression method and cross-OS flag it is computed from, the refs and tier order, every
    listing performed, every candidate object with the version it carries, and a plain-language
    reason for the outcome. Building the report never fails the step.
  • cloud-cache-action/inspect sub-action that reports which object a restore would use, and
    why, without restoring. It only lists objects — it downloads nothing and writes nothing.
    • Outputs: would-hit, would-match-key, would-match-object, candidate-count, report
      (the full report as JSON, replaced by a {"truncated":true,…} summary beyond 64 KB) and
      cache-storage-provider.
    • max-candidates (default 20) caps how many objects the report shows per search; every
      object under the prefix is still listed, as a restore does, and the rest are counted as
      "… and N more not shown". fail-on-cache-miss: true fails the step when nothing would be
      restored.
    • The report lists objects and never HEADs the exact key, so on a provider with eventually
      consistent listings a report taken right after a save can say "would miss" where a restore
      would hit.
  • Metrics outputs. cache-restore-duration-ms, cache-save-duration-ms,
    cache-transfer-duration-ms and cache-bytes are set on every path, so they are always
    defined (0 when the step did not complete or transferred nothing).
  • metrics-file input on the main, restore, save, prune and inspect actions. Every
    step writes one cloud-cache-metrics <json> debug line, and appends the same JSON as one line to
    this file when it is set, resolved relative to GITHUB_WORKSPACE.
    • transferDurationMs measures the S3 transfer alone in file mode; with streaming: true it
      covers download-plus-extract, and the line's streaming field tells the two apart.
    • prune and inspect write their line once the step has finished its work, so a step that
      fails earlier writes none.
    • Writing the file is best-effort: a failure only warns and never fails the step.
  • Documentation: a new "Inspecting Lookups" guide on the documentation site, plus metrics
    sections in the README and the Getting Started and Pruning guides.

Changed

  • cache-etag on streaming saves. With streaming: true the output now reports the ETag of
    the object after its metadata copy, rather than the multipart upload's ETag.
  • Live cloud CI gating. The Amazon S3, Cloudflare R2 and Google Cloud Storage suites now run on
    main, nightly and on manual dispatch, and on a pull request only when it carries the full-ci
    label. Every self-hosted-S3 job (Garage, SeaweedFS, MinIO) still runs on every pull request.

v1.2.0 – Pruning, Integrity Checks, Safe Concurrent Saves & Opt-in Streaming

Choose a tag to compare

@xSAVIKx xSAVIKx released this 15 Sep 19:41

No breaking changes. Caches saved by v1.1 stay valid, and the default save and restore paths still
work the same way, apart from the additions below.

Added

  • cloud-cache-action/prune sub-action that deletes cache archives older than
    older-than-days. It supports ref, scoped-to-ref, scoped-to-repository, prefix and
    dry-run.
    • It only deletes objects whose whole key matches the resolved s3-key-pattern, so other
      repositories, other refs and non-archive objects under the same listing prefix are never
      touched.
    • It refuses to run when a pattern can't be scoped safely.
    • An invalid dry-run value fails the step before any S3 call.
  • Archive integrity. File-mode saves store a sha256 of the archive in the
    cloud-cache-sha256 object metadata, and restores verify it.
    • A mismatch counts as a cache miss with a warning.
    • It only fails the step when both dual-cache and dual-cache-strict are true.
    • Objects without the metadata (any v1.1 cache, or a streamed save) skip the check.
  • Safe concurrent saves. Saves send If-None-Match: *, so when two jobs save the same key,
    the first one wins and the second keeps the existing cache.
    • If a provider rejects the condition, the save retries once without it and doesn't send it
      again for the rest of the run.
    • A 409 ConditionalRequestConflict is retried once.
    • Providers that ignore the condition, such as Garage, keep last-writer-wins.
  • Job summary. Restore and save each write a step summary table with the key, hit and
    source, size and duration. The new job-summary input defaults to true.
  • Opt-in streaming (streaming: true, default false): pipes tar straight to a multipart
    upload and downloads straight into tar, with no temporary archive file.
    • The file-based path stays the default and is unchanged.
    • A streamed upload is only completed after tar exits successfully, so a failed tar never
      leaves a truncated cache.
    • It falls back to file mode for BSD tar with zstd on Windows and for rejected conditions.
  • Maintenance. Dependabot for npm and GitHub Actions, with minor and patch updates grouped
    and major updates proposed separately.
  • Release workflow (.github/workflows/release.yml), which runs when a GitHub release is
    published.
    • It verifies the tag against package.json and checks that dist is up to date.
    • It then moves the major tag, but only for the highest stable release of that major.

Changed

  • The main action, restore and save gain the job-summary and streaming inputs.
  • A bare $ref, $key, $prefix, $version, $archive_filename or $GITHUB_REPOSITORY in
    s3-key-pattern is no longer expanded from the environment. It stays literal and logs one
    warning per name.

Fixed

  • A strict dual-cache save where path matches nothing no longer fails the step. The GitHub
    tier's "Path Validation Error" now counts as a skipped save, as it already did for the S3 tier.
  • With scoped-to-repository: false or scoped-to-ref: false, removing the placeholder no longer
    leaves a leading, doubled or trailing /. A / is only removed when the placeholder fills a
    whole path segment, so custom patterns keep their v1.1 keys.
  • When CompleteMultipartUpload fails (for example, a lost save race), the multipart upload is
    aborted instead of leaving billed parts behind.
  • Errors rewrapped by the restore and save steps keep the original error as cause.

Full changelog: v1.1.0...v1.2.0

v1.1.0 – actions/cache-Compatible Paths, Versioned Ref-Scoped Keys & Cross-OS CI

Choose a tag to compare

@xSAVIKx xSAVIKx released this 13 Sep 17:30

🚀 Cloud Cache Action v1.1.0

v1.1 is a correctness release. path now behaves like actions/cache, S3 keys carry the Git ref and a cache version, dual-cache strict mode actually fails the step, and CI covers Linux, macOS, Windows, cross-OS restores and three self-hosted S3 servers.

Important

Caches saved by v1.0 are not reused. The S3 object layout changed, so every cache misses once on the first v1.1 run and is rebuilt. The action never reads v1.0 objects again; let a bucket lifecycle rule expire them.


⚠️ Breaking changes

  • New key layout: ${GITHUB_REPOSITORY}/${prefix}${ref}/${key}/${version}/${archive_filename}. ${version} hashes the path patterns, the compression method and, on Windows, enableCrossOsArchive, so a cache is never restored into a job that caches different paths.
  • Branch isolation like actions/cache: restores search the current ref, then the pull request base branch, then the default branch. main never restores a feature branch's cache. Set the new scoped-to-ref: false input to share caches across all refs.
  • save-always removed: it never worked, because post-if can't read inputs. To save after failed steps, use cloud-cache-action/restore and cloud-cache-action/save with if: always().
  • dual-cache-strategy: independent removed: it now logs a warning and behaves as backfill.
  • dual-cache-strict: true fails the step: it now fails on tier errors during restore and save. Previously save errors only produced warnings.

🐛 Fixes

  • path patterns go through @actions/glob, so ~/.npm, **/node_modules and ! exclusions work. Previously they reached tar unexpanded and failed with Cannot stat.
    • Archives store paths relative to GITHUB_WORKSPACE.
  • tar selection matches actions/cache:
    • GNU tar on Linux; gtar or BSD tar on macOS; Git's GNU tar or System32 tar on Windows.
    • Two-step zstd with BSD tar on Windows.
    • MSYS=winsymlinks:nativestrict for symlinks.
    • File names starting with - can no longer inject tar options.
  • Restore-key matching:
    • Listing now reads every page. It used to stop after 100 keys, which made the "newest" match arbitrary.
    • The primary key is tried as a prefix before restore-keys, as in actions/cache.
    • Keys containing / or $ survive the round trip.
  • Failed downloads or extractions count as a cache miss with a warning instead of failing the job.
  • backfill checks the other tier first: it only archives and uploads to a tier that doesn't already have the key.
  • Retries:
    • retry-count now sets the SDK's standard retries.
    • Stream retries only cover network failures the SDK doesn't retry itself.
    • 403s and other permanent errors are no longer retried.
    • retry-count: 0 is honoured.
  • Checksums: S3-compatible providers (R2, GCS, B2, Garage, MinIO, …) only receive request checksums when S3 requires them.
  • Warnings: unknown providers, invalid booleans and enum values, and a lone access or secret key now log a warning instead of being ignored.
  • Consistency: the post step reuses the restore step's settings and compression method, so both steps compute the same object key.
  • Branding: the restore and save sub-actions now use valid icons.

✅ Testing

  • Test suites: unit tests on real temporary directories, real tar round trips (symlinks, file modes, unicode, option-like file names), and a contract test that keeps all three action.yml manifests in sync with the code.
  • Integration tests run the real restore and save code against SeaweedFS, MinIO and Garage, and fail instead of skipping when no server is available.
  • CI on every pull request:
    • Jest on Linux, macOS and Windows
    • per-OS round trips through ./save and ./restore
    • post-step saves by the main action
    • Linux → macOS/Windows cross-OS restores
    • dual-cache against the live GitHub Actions Cache
    • a strict-mode failure check
    • actionlint
  • Nightly: live cross-OS restores on Cloudflare R2.

📦 Upgrading

- uses: xSAVIKx/cloud-cache-action@v1   # now v1.1.0
  with:
    bucket: my-ci-cache-bucket
    access-key: ${{ secrets.S3_ACCESS_KEY }}
    secret-key: ${{ secrets.S3_SECRET_KEY }}
    path: |
      ~/.npm
      packages/*/node_modules
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-
  • save-always: if you set it, remove it and use the restore/save split shown in the README.
  • Custom s3-key-pattern: add ${ref} and ${version} to it. The action warns when they're missing.
  • Storage: ref scoping stores a separate cache per branch and per pull request, so configure a bucket lifecycle rule (for example, expire after 30–60 days).
  • Security: read Branch isolation and trust model. Anyone holding the bucket's write credentials can write cache objects.

Full changelog: v1.0.0...v1.1.0

v1.0.0 – Production Release: Universal S3 Cache with 1:1 actions/cache Parity

Choose a tag to compare

@xSAVIKx xSAVIKx released this 11 Sep 18:28

🚀 Cloud Cache Action v1.0.0 (Production Release)

Cloud Cache Action is a production-ready, high-performance GitHub Action for saving and restoring cache artifacts to any S3-compatible cloud or self-hosted object storage with full 1:1 actions/cache parity (v4–v6) and native Node 24 execution.


✨ Highlights & Features

  • 1:1 actions/cache Parity: Complete drop-in replacement for all official inputs (path, key, restore-keys, fail-on-cache-miss, lookup-only, enableCrossOsArchive, read-only, save-always) and outputs (cache-hit, cache-primary-key, cache-matched-key).
  • Modern Node 24 Runtime: Native runs: using: 'node24' runtime with zero deprecation warnings on modern GitHub runners.
  • Universal Cloud Provider Compatibility: Live verified against:
    • AWS S3 (both static IAM credentials and GitHub Actions OIDC role assumption)
    • Cloudflare R2 (zero egress cost caching)
    • Google Cloud Storage (GCS) (via XML API and HMAC service account keys)
    • Backblaze B2
    • Fastly Object Storage
    • Garage S3 & SeaweedFS (self-hosted distributed object storage)
    • MinIO / LocalStack
  • ⚡ Dual-Caching Synchronization: Cache simultaneously across both your S3 bucket and GitHub Actions Cache tier with configurable priority (s3-first or github-first) and automatic backfill synchronization.
  • Custom S3 Key Templating: Flexible patterns such as ${GITHUB_REPOSITORY}/${prefix}${key}/${archive_filename} with support for custom environment variables (${RUNNER_OS}, ${GITHUB_JOB}).
  • Safe Cross-Platform Keys: Guarantees standard POSIX forward slashes (/) in object storage across Linux, macOS, and Windows runners.
  • Resilient Archiving & Transfer: Multi-threaded compression (zstd with automatic gzip fallback), streaming multipart uploads, and exponential backoff retries on transient network errors.
  • Modular Sub-Actions: Dedicated restore-only (restore/action.yml) and save-only (save/action.yml) workflows for custom build pipelines.

📦 Quick Start

- name: Cache dependencies to S3
  uses: xSAVIKx/cloud-cache-action@v1
  with:
    bucket: my-ci-cache-bucket
    endpoint: https://<account_id>.r2.cloudflarestorage.com # Or AWS, GCS, B2, MinIO
    access-key: ${{ secrets.S3_ACCESS_KEY }}
    secret-key: ${{ secrets.S3_SECRET_KEY }}
    path: |
      ~/.npm
      node_modules
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

Dual-Caching (S3 + GitHub Actions Cache)

- name: Dual Cache (S3 + GitHub Cache Fallback)
  uses: xSAVIKx/cloud-cache-action@v1
  with:
    bucket: my-ci-cache-bucket
    dual-cache: true
    restore-priority: github-first
    dual-cache-strategy: backfill
    access-key: ${{ secrets.S3_ACCESS_KEY }}
    secret-key: ${{ secrets.S3_SECRET_KEY }}
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}

🧪 Verification & Reliability

  • 138 Automated Tests: Unit tests, contract parity validation, and live container integration tests.
  • Dedicated Provider Verification: Automated CI test suites running live against Amazon S3, Cloudflare R2, Google Cloud Storage, and MinIO.
  • Dogfooded: Powers caching for repository CI and documentation deployment.

Documentation: https://xsavikx.github.io/cloud-cache-action/
Created by: Yurii Serhiichuk

v0.1.0 – Universal S3 Cache with 1:1 actions/cache Parity & Dual-Caching

Choose a tag to compare

@xSAVIKx xSAVIKx released this 11 Sep 17:45

🚀 Introducing Cloud Cache Action (v0.1.0)

Cloud Cache Action is a drop-in, high-performance GitHub Action for saving and restoring cache artifacts to any S3-compatible cloud or self-hosted object storage with full 1:1 actions/cache parity and native Node 24 execution.


✨ Key Features

  • 1:1 actions/cache Parity: Complete drop-in replacement for official inputs (path, key, restore-keys, fail-on-cache-miss, lookup-only, enableCrossOsArchive, read-only, save-always) and outputs (cache-hit, cache-primary-key, cache-matched-key).
  • Modern Node 24 Runtime: Native runs: using: 'node24' runtime with zero deprecation warnings.
  • Universal S3 Compatibility: Tested and verified across AWS S3, Cloudflare R2, Google Cloud Storage, Backblaze B2, Fastly Object Storage, Garage, SeaweedFS, MinIO, and custom endpoints.
  • ⚡ Dual-Caching Synchronization: Cache simultaneously to both your S3 bucket and GitHub Actions Cache tier for maximum resilience, warm local runners, and rapid remote builds.
  • Resilient Archiving & Transfer: Multi-threaded compression (zstd with automatic gzip fallback), chunked streaming uploads, and exponential backoff retries on transient network errors.
  • Modular Sub-actions: Dedicated restore-only (restore/action.yml) and save-only (save/action.yml) workflows for custom build step placement.

📦 Quick Start

- name: Cache Dependencies (S3)
  uses: xSAVIKx/cloud-cache-action@v0
  with:
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-npm-
    bucket: my-ci-cache-bucket
    aws-region: us-east-1
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

Dual-Caching Example (S3 + GitHub Actions Cache)

- name: Dual Cache (S3 + GitHub Cache Fallback)
  uses: xSAVIKx/cloud-cache-action@v0
  with:
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
    bucket: my-ci-cache-bucket
    dual-cache: true
    cache-source: dual
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

Documentation: https://xsavikx.github.io/cloud-cache-action/
Website: https://serhiichuk.dev