6 Commits
Author SHA1 Message Date
saru d4b6457b06 fix: restore step died with exit 141 (SIGPIPE) on every cache hit — act runs step scripts with 'bash -e -o pipefail', so the 'FIRST_ENTRY=$(tar -tf ... | head -1)' detection line killed the step: head -1 closes the pipe after the first line, tar gets SIGPIPE (141), and pipefail propagates it as the script exit status (observed: 'Cache hit' then 'Process completed with exit code 141', step duration 0s, on deploy-leptos after this detection line was added on 2026-08-14)
The previous restores predate the detection line, which is why this only started failing on 2026-08-17. Suppress the pipeline status with '|| true' — FIRST_ENTRY still captures the first archive entry, and a genuinely unreadable archive degrades to the legacy fallback / cache-hit=false path as before.
2026-08-17 11:18:46 +08:00
saru 8cdec4603c Restore into the path input, not the archive's embedded absolute path
Restore only used the path input for a redundant mkdir and then extracted
to / using the absolute path baked into the archive, which pointed at the
stale per-run workspace of whichever run saved it. Extract relative
archives (<basename>/...) into the parent of the requested path; keep a
legacy fallback (extract to /) for old archives, which report cache-hit
only when the requested path actually gains content.
2026-08-14 23:28:31 +08:00
saru 43f1755c0e Touch archives on hit and prune the cache store after restore
Record last use by touching the archive so the retention policy can
expire by access time, and run prune.sh after every restore (even on
miss) to keep the shared store trimmed: keep the 3 newest archives per
key-prefix family, expire anything older than 7 days, hard-cap the store
at 2 GB (oldest first, trumping the keep set), never touch files younger
than 1h, and drop stale run workspaces under ~/.cache/act older than 2
days.
2026-08-14 22:54:47 +08:00
saru 2d128b5303 Add more debug output: key-file, path input, and expanded path on restore 2026-05-22 02:16:56 +08:00
saru 2d57ace5e4 Change path default to github.workspace 2026-05-22 02:02:48 +08:00
saru e744d2a004 Create restore-only action 2026-05-17 02:03:11 +08:00
2 changed files with 41 additions and 20 deletions
+2 -1
View File
@@ -22,7 +22,8 @@ A Gitea Action that caches build files as `tar.xz` archives on the runner filesy
1. Hashes `key-file` with SHA-256, combines with `key-prefix` as `<prefix>-<hash>` 1. Hashes `key-file` with SHA-256, combines with `key-prefix` as `<prefix>-<hash>`
2. Stores archives at `~/.cache/.cache-store/<key>.tar.xz` 2. Stores archives at `~/.cache/.cache-store/<key>.tar.xz`
3. Restores by extracting the archive to `/`, saves by creating one 3. Restores by extracting the archive to `/`, saves by creating one
4. Saves atomically (write to `<key>.tar.xz.tmp`, then rename) 4. Touches the archive on every hit, so the retention policy can expire by
last use
5. Archives store the cached directory **relative** (`<basename>/...`), not its 5. Archives store the cached directory **relative** (`<basename>/...`), not its
absolute path — a later run restores into its own `path` location, so the absolute path — a later run restores into its own `path` location, so the
cache works across runs even though each run gets a fresh workspace cache works across runs even though each run gets a fresh workspace
+39 -19
View File
@@ -1,5 +1,5 @@
name: 'Cache Save' name: 'Cache Restore'
description: 'Save cache as tar.xz archive on the runner filesystem' description: 'Restore cache from tar.xz archive on the runner filesystem'
inputs: inputs:
path: path:
description: 'Directory to cache' description: 'Directory to cache'
@@ -11,10 +11,15 @@ inputs:
key-file: key-file:
description: 'File to hash for the cache key' description: 'File to hash for the cache key'
required: true required: true
outputs:
cache-hit:
description: 'true if exact key match found, false otherwise'
value: ${{ steps.restore.outputs.cache-hit }}
runs: runs:
using: "composite" using: "composite"
steps: steps:
- name: Save cache - name: Restore cache
id: restore
shell: bash shell: bash
run: | run: |
CACHE_DIR="$HOME/.cache/.cache-store" CACHE_DIR="$HOME/.cache/.cache-store"
@@ -29,25 +34,40 @@ runs:
echo "Key file: ${{ inputs.key-file }}" echo "Key file: ${{ inputs.key-file }}"
echo "Path input: ${{ inputs.path }}" echo "Path input: ${{ inputs.path }}"
echo "Expanded path: ${EXPANDED_PATH}" echo "Expanded path: ${EXPANDED_PATH}"
echo "Cache key: ${CACHE_KEY}"
echo "Archive path: ${ARCHIVE}" echo "Archive path: ${ARCHIVE}"
if [ ! -d "$EXPANDED_PATH" ] || [ -z "$(ls -A "$EXPANDED_PATH" 2>/dev/null)" ]; then if [ -f "$ARCHIVE" ]; then
echo "Cache directory empty, skipping save" echo "Cache hit: ${CACHE_KEY}"
exit 0 mkdir -p "$EXPANDED_PATH"
fi # New-format archives hold entries relative to the cached directory
# (<basename>/...), so extract into the parent of the requested
echo "Saving cache: ${CACHE_KEY}" # path. Legacy archives embed the absolute path of the run that
# Archive only the directory itself (entries: <basename>/...), not its # saved them; fall back to extracting them to "/" (status quo,
# absolute path, so a later run can restore it into its own (per-run) # they are TTL-pruned within the retention window anyway).
# workspace location. Restores use the same `path` input to find it. # NOTE: `|| true` is mandatory — act runs step scripts with
PARENT="$(dirname "$EXPANDED_PATH")" # `bash -e -o pipefail`, and `head -1` closes the pipe after the
BASE="$(basename "$EXPANDED_PATH")" # first line, so tar dies with SIGPIPE (141) and pipefail turns
TMP_ARCHIVE="${ARCHIVE}.tmp" # that into a step failure (restore only ever "worked" before
if tar -cJf "$TMP_ARCHIVE" -C "$PARENT" "$BASE" 2>/dev/null && mv -f "$TMP_ARCHIVE" "$ARCHIVE"; then # this detection line existed).
echo "Cache saved" FIRST_ENTRY=$(tar -tf "$ARCHIVE" 2>/dev/null | head -1 || true)
if [ "${FIRST_ENTRY%%/*}" = "$(basename "$EXPANDED_PATH")" ]; then
echo "Restoring to: $(dirname "$EXPANDED_PATH")"
tar -xf "$ARCHIVE" -C "$(dirname "$EXPANDED_PATH")" 2>/dev/null
else
tar -xf "$ARCHIVE" -C "/" 2>/dev/null
fi
if [ -d "$EXPANDED_PATH" ] && [ -n "$(ls -A "$EXPANDED_PATH" 2>/dev/null)" ]; then
# record last use so the retention policy can expire by access time
touch "$ARCHIVE"
echo "cache-hit=true" >> $GITHUB_OUTPUT
else
echo "Cache restore failed"
echo "cache-hit=false" >> $GITHUB_OUTPUT
fi
else else
rm -f "$TMP_ARCHIVE" echo "Cache miss: ${CACHE_KEY}"
echo "Cache save failed" echo "cache-hit=false" >> $GITHUB_OUTPUT
fi fi
# Trim the shared cache store and stale workspaces (best-effort) # Trim the shared cache store and stale workspaces (best-effort)