Das Problem
GitHub speichert Cache-Einträge für actions/cache, setup-node,
setup-python und ähnliche Actions, aber es gibt keinen Button, keinen
API-Aufruf und keinen gh-Befehl, der das Archiv liefert. Der Cache-Dienst
gibt Download-URLs nur an Workflow-Läufe heraus. Wer einen Cache debuggen
will, der wiederhergestellt, aber nicht genutzt wird, oder der unerwartet
groß ist, braucht den Inhalt.
Der Bedarf ist belegt:
- cli/cli#9125, "Add a way to
download github actions caches", hat 113 Reaktionen und ist seit Mai 2024
offen. Ein gh-Maintainer hat es im Juli 2025 an das Actions-Team
weitergegeben. Die Umgehungen im Thread sind Workflow-Snippets, die den
Cache mit
actions/cachewiederherstellen und als Artefakt hochladen. Sie brauchen die ursprünglichepath-Liste, weil sie die Cache-Version bestimmt. - actions/cache#105, "Ability to download cache and inspect contents", hat 34 Reaktionen. Der Stale-Bot hat es 2023 geschlossen, nachdem ein Maintainer erklärt hatte, dass ein Download nicht geplant sei.
gh cache list,gh cache deleteund actions/gh-actions-cache listen und löschen Einträge, können sie aber nicht herunterladen.
Wie es funktioniert
gh-cache-download besteht aus
zwei Teilen. Der erste ist eine gh-Extension, eine einzelne Python-Datei ohne
Abhängigkeiten. gh cache-download get KEY sucht den Key mit
GET /repos/{owner}/{repo}/actions/caches (exakte Übereinstimmung), startet
einen kleinen Helfer-Workflow auf dem Branch, dem der Cache gehört, wartet
auf den Lauf und lädt das Ergebnis herunter. gh cache-download init
schreibt diesen Helfer-Workflow, der committet und gepusht werden muss. Er
läuft nur bei manuellem Start und hat nur Leserechte.
Der zweite Teil ist eine Composite Action, die der Helfer-Workflow nutzt.
Innerhalb des Laufs ruft sie den Endpunkt GetCacheEntryDownloadURL des
Cache-Dienstes mit Key und Version auf, lädt das Archiv und lädt es als
Artefakt hoch. Weil sie über Key und Version abruft, muss sie nicht wissen,
welche Pfade im Cache liegen.
actions/cache speichert Pfade relativ zum Workspace. Alles, was außerhalb
davon liegt, etwa ~/.npm, erscheint im Archiv als ../../../.npm/....
gh cache-download extract (oder get -x) schreibt nie außerhalb des
Zielverzeichnisses: Jedes .. wird zu einem Verzeichnis __parent__,
absolute Pfade verlieren den führenden /, und Einträge, die
hinausführen würden, etwa Symlinks auf absolute Pfade, werden übersprungen.
Das ist ein echter Lauf gegen einen Cache, den das CI des Repositorys aus
e2e-data im Workspace und ~/.e2e-home außerhalb davon gespeichert hat:
$ gh cache list -R Jwrede/gh-cache-download
9507487879 e2e-40288214454-1 459 B 2026-10-26T07:50:08Z 2026-10-26T07:50:10Z
$ gh cache-download get e2e-40288214454-1 -R Jwrede/gh-cache-download -x
Cache 9507487879: refs/heads/main, 459 bytes, version b107c4986568
Run 40288300859: https://github.com/Jwrede/gh-cache-download/actions/runs/40288300859
Archive: cache-9507487879/cache.tzst (459 bytes)
Extracted 6 entries to cache-9507487879/files
$ find cache-9507487879/files -type f -o -type l
cache-9507487879/files/e2e-data/link
cache-9507487879/files/e2e-data/sub/a.txt
cache-9507487879/files/__parent__/__parent__/__parent__/.e2e-home/b.txt
Das gesamte get dauerte 14 Sekunden.
Tests
Die Python-Unit-Tests decken die Suche nach dem Key ab (nur exakte
Übereinstimmung, Auflistung von Präfix-Treffern bei fehlendem Key, --ref
nötig, wenn ein Key auf mehreren Branches existiert) und die Ablehnung von
Pull-Request-Refs. Das Entpacken wird
an tar-, gzip- und zstd-Archiven getestet, die einen Symlink, einen Hardlink,
einen Pfad außerhalb des Workspaces und einen Symlink auf /etc/passwd
enthalten, der übersprungen werden muss. Der Befehl get läuft gegen ein
nachgebildetes gh, das die Aufrufe aufzeichnet, auch für einen
fehlgeschlagenen Workflow-Start und einen fehlgeschlagenen Lauf.
Node-Tests decken die Versionsauswahl der Action und die Erkennung des
Archivtyps ab.
Das CI führt die Unit-Tests auf Python 3.10 und 3.14 aus. Ein
End-to-End-Job legt Dateien innerhalb und außerhalb des Workspaces an,
speichert sie mit actions/cache/save, lädt den Eintrag mit der Action
herunter, entpackt ihn mit der CLI und vergleicht Inhalt und Archivgröße.
Grenzen
- Ein Lauf kann nur Caches seiner eigenen Ref und des Default-Branches lesen. Der Helfer-Workflow muss deshalb auf dem Branch liegen, dem der Cache gehört.
- Caches aus Pull-Request-Läufen (
refs/pull/N/merge) lassen sich nicht über einen manuellen Start abrufen. Dafür muss die Action in den Workflow des Pull Requests aufgenommen werden. GetCacheEntryDownloadURList die interne API, dieactions/cacheab v4 nutzt. GitHub dokumentiert sie nicht als öffentliche API, sie kann sich also ändern. Getestet ist das Werkzeug nur auf github.com, mit Archiven von Linux-Runnern.
Code und Installationsanleitung liegen unter github.com/Jwrede/gh-cache-download.