This is an automated email from the ASF dual-hosted git repository.

sjanc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit 49e46f0541aa9857fd6ffd8db68a62daf9be5616
Author: Szymon Janc <[email protected]>
AuthorDate: Tue Oct 13 02:40:25 2026 +0200

    ci: Add workflow removing superseded caches
    
    ccache directories are saved from master branch with unique keys on
    every run, while only newest entry of each job is used by later runs.
    Same applies to repositories and newt caches when those change. Add
    workflow that removes all but newest cache entry of each kind created
    by master branch, so that total cache size stays below repository
    limit and useful entries are not evicted. It runs after each master
    Build run and daily.
    
    This is done in separate workflow as removing caches requires write
    access to actions, which should not be granted to workflows running
    for pull requests.
    
    Assisted-by: Claude:claude-opus-5-5
---
 .github/workflows/cache_cleanup.yml | 100 ++++++++++++++++++++++++++++++++++++
 1 file changed, 100 insertions(+)

diff --git a/.github/workflows/cache_cleanup.yml 
b/.github/workflows/cache_cleanup.yml
new file mode 100644
index 000000000..5e076eeb3
--- /dev/null
+++ b/.github/workflows/cache_cleanup.yml
@@ -0,0 +1,100 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+name: Cache cleanup
+
+on:
+  # Remove caches superseded by master build right after it completes,
+  # daily run handles caches saved by other workflows
+  workflow_run:
+    workflows: [Build]
+    types: [completed]
+  schedule:
+    - cron: '30 3 * * *'
+  workflow_dispatch:
+
+permissions:
+  actions: write
+
+concurrency:
+  group: ${{ github.workflow }}
+
+jobs:
+  cleanup:
+    name: Remove superseded caches
+    # Build runs on push only for master, pull request runs are ignored
+    if: >-
+      github.event_name != 'workflow_run' ||
+      github.event.workflow_run.event == 'push'
+    runs-on: ubuntu-slim
+    steps:
+      - uses: actions/github-script@v9
+        with:
+          script: |
+            // Caches created by master branch builds are saved with
+            // unique keys (e.g. ccache-<job>-<run_id>-<attempt>), only
+            // newest entry of each kind is used by later runs. Remove
+            // older entries to keep total cache size below limit.
+            const families = [
+              { re: /^(ccache-.+)-\d+-\d+$/ },
+              { re: /^(newt-repos-[^-]+)-[0-9a-f]+$/ },
+              { re: /^(newt-[^-]+-[^-]+)-[0-9a-f]{40}$/ },
+            ];
+
+            const caches = await github.paginate(
+              github.rest.actions.getActionsCacheList,
+              { ...context.repo, ref: 'refs/heads/master', per_page: 100 },
+              (response) => response.data.actions_caches);
+
+            const newest = new Map();
+            const stale = [];
+            for (const cache of caches) {
+              const family = families
+                .map((f) => cache.key.match(f.re))
+                .find((m) => m);
+              if (!family) {
+                continue;
+              }
+              const name = family[1];
+              const current = newest.get(name);
+              if (!current) {
+                newest.set(name, cache);
+              } else if (new Date(cache.created_at) > new 
Date(current.created_at)) {
+                stale.push(current);
+                newest.set(name, cache);
+              } else {
+                stale.push(cache);
+              }
+            }
+
+            for (const cache of stale) {
+              core.info(`Removing ${cache.key} (${cache.size_in_bytes} 
bytes)`);
+              try {
+                await github.rest.actions.deleteActionsCacheById({
+                  ...context.repo,
+                  cache_id: cache.id,
+                });
+              } catch (e) {
+                // Cache may be already evicted by GitHub
+                if (e.status !== 404) {
+                  throw e;
+                }
+              }
+            }
+            core.info(`Kept ${newest.size} caches, removed ${stale.length}`);

Reply via email to