Skip to content
shellhacks.dev
DevOps

Cache a Directory in GitHub Actions CI with restore-keys

Cache a directory with actions/cache. The restore-keys input is a block of text, one prefix per line, and a YAML list parses as a list instead of text.

2 min read
Summarize in AI

A workflow reinstalls the same dependencies on every run, and the cache step needs a key that changes only with the lock file.

YAML
- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: npm-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      npm-
Python
import yaml

step = yaml.safe_load("""
- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: npm-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      npm-
""")[0]
w = step["with"]
print(repr(w["key"]))
print(repr(w["restore-keys"]))

as_list = yaml.safe_load("""
with:
  restore-keys:
    - npm-
    - node-
""")
print(repr(as_list["with"]["restore-keys"]))
Text
"npm-${{ hashFiles('**/package-lock.json') }}"
'npm-\n'
['npm-', 'node-']

The step names a path, a key built with hashFiles on the lock file, and restore-keys as fallbacks. A pipe after restore-keys: makes a YAML block scalar, a single string with one prefix per line, which is the form the action reads: PyYAML shows a string that ends in a newline. Written as a YAML list, the same field becomes ['npm-', 'node-'], a list where the action expects text. The key itself is an ordinary string with the expression inside. See the actions/cache repository.

A cache entry is never updated once saved under a key, so a key that does not change with the dependencies keeps stale files; the hash in the key is what forces a new one. Prefix matches in restore-keys restore the newest entry that starts with the prefix. The parser here is PyYAML and not GitHub's, but the YAML types are the same.

Share

More snippets

Get new posts by email

One email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.