From 9ac9efd667c75d134f312be97f2be93e2394cc95 Mon Sep 17 00:00:00 2001 From: mlugg Date: Mon, 10 Mar 2025 17:28:47 +0000 Subject: [PATCH] Clear Zig cache directory when it grows too large Resolves: #21 --- README.md | 5 +++++ action.yml | 4 ++++ post.js | 29 +++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/README.md b/README.md index a2eb8dd..ef26750 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,11 @@ with all of your matrix variables to ensure that every job is correctly cached. provide any means for the Action to automatically distinguish jobs in a matrix. However, variables which select the runner OS can be omitted from the `cache-key`, since the runner OS is included in the cache key by default. +Zig cache directories can get incredibly large over time. By default, this Action will clear the cache directory +once its size exceeds 2 GiB. This threshold can be changed by setting the `cache-size-limit` option to a different +value (in MiB); for instance, `cache-size-limit: 4096` for a 4 GiB limit. The limit can be disabled entirely by +setting `cache-size-limit: 0`. + [mach-nominated]: https://machengine.org/about/nominated-zig/ [matrix]: https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow diff --git a/action.yml b/action.yml index bf78457..733eb58 100644 --- a/action.yml +++ b/action.yml @@ -16,6 +16,10 @@ inputs: description: 'Additional cache key component to include when caching the global Zig cache directory. When using a matrix strategy, this should include the matrix variables to ensure all jobs are cached. Matrix variables which decide the OS can be omitted, since the OS is always included in the cache key.' required: false default: '' + cache-size-limit: + description: 'The maximum permitted size of the global Zig cache directory, in MiB. When the cache directory exceeds this size, it is cleared. Default is 2048 (2 GiB). 0 means no limit.' + required: true + default: 2048 runs: using: 'node20' main: 'main.js' diff --git a/post.js b/post.js index 6aecd4d..7904d0c 100644 --- a/post.js +++ b/post.js @@ -1,3 +1,4 @@ +const path = require('path'); const fs = require('fs').promises; const core = require('@actions/core'); const github = require('@actions/github'); @@ -17,6 +18,16 @@ async function main() { } if (accessible) { + const size = await totalSize(cache_path); + const size_limit = core.getInput('cache-size-limit') * 1024 * 1024; // MiB -> bytes + if (size_limit !== 0 && size > size_limit) { + core.info(`Cache directory reached ${size} bytes, exceeding limit of ${size_limit} bytes; clearing cache`); + // We want to clear the cache and start over. Unfortunately, we can't programmatically + // remove the old cache entries, so we instead want to save an empty cache directory. + // To do this, delete all the contents of the cache directory before saving the cache. + await rmDirContents(cache_path); + } + const prefix = await common.getCachePrefix(); const name = prefix + github.context.runId; await cache.saveCache([cache_path], name); @@ -27,4 +38,22 @@ async function main() { } } +async function totalSize(p) { + const stat = await fs.stat(p); + if (stat.isFile()) return stat.size; + if (stat.isDirectory()) { + let total = 0; + for (const entry of await fs.readdir(p)) { + total += await totalSize(path.join(p, entry)); + } + return total; + } + return 0; +} + +async function rmDirContents(dir) { + const entries = await fs.readdir(dir); + await Promise.all(entries.map(e => fs.rm(path.join(dir, e), { recursive: true, force: true }))); +} + main();