How the Quarto development Deno cache is cached in GitHub Actions, how invalidation works, and how to force a fresh download.
Two Deno caches are restored by the quarto-dev composite action
(.github/workflows/actions/quarto-dev/action.yml):
| Cache | Path | Populated by |
|---|---|---|
| Deno standard library | ./src/resources/deno_std/cache |
package/scripts/deno_std/deno_std.ts |
| Deno development cache | ./package/dist/bin/deno_cache |
package/scripts/vendoring/vendor.sh / vendor.cmd |
This document covers the second one. The first predates it and is keyed on
deno_std.lock + deno_std.ts.
${{ runner.os }}-${{ runner.arch }}-deno-cache-v1-<hash>
<hash> is hashFiles() over five files:
| File | Why it's in the key |
|---|---|
configuration |
Pins the Deno binary version. |
src/import_map.json |
Top-level dep version map — the primary driver of what gets downloaded. |
src/vendor_deps.ts |
Explicit "things to vendor" entrypoint. |
tests/test-deps.ts |
Test-only deps entrypoint (iterated by vendor.sh / vendor.cmd). |
package/scripts/deno_std/deno_std.ts |
deno_std entrypoint iterated by the vendor scripts. |
restore-keys falls back to the same prefix without the hash suffix, so a
partial-match cache from a sibling branch is reused if the exact key misses.
quarto.tsand othersrc/**/*.tsfiles: these are consumer code, not dep-resolution inputs.deno installis additive on top of an existing cache, so new imports in consumer code download without needing a key change. Hashingsrc/**would invalidate on every commit.vendor.sh/vendor.cmd: they control howdeno installruns, not what it resolves. Including them would cross-invalidate (a Unix-onlyvendor.shedit would bust the Windows cache too, sincehashFiles()is OS-independent).deno.lock/tests/deno.lock: both are gitignored (.gitignore:26) and are generated bydeno installat run time, so they do not exist on a fresh checkout and cannot serve as key inputs.
vendor.sh and vendor.cmd delete ./package/dist/bin/deno_cache before
running deno install. That default is preserved for local development. In
CI we need the opposite — the cache was just restored by actions/cache and
must survive until deno install uses it.
The coordination uses a single environment variable:
- Name:
QUARTO_SKIP_DENO_CACHE_WIPE - Truthy value:
"1"(anything else, including unset, means "wipe"). - Set by: both configure steps in
action.yml(env: QUARTO_SKIP_DENO_CACHE_WIPE: "1"). - Read by:
vendor.shandvendor.cmd. Nothing else. - Do not set locally. The variable only makes sense when paired with a restore step. Without one, skipping the wipe just leaves a stale cache on disk.
Editing any of the five keyed files changes the hash, which produces a new cache key. Next CI run misses, re-downloads, then saves under the new key. Old keys age out via GitHub's 7-day LRU.
Only if the cache contents go bad in a way the key can't detect — e.g. a
partial download that wasn't corrected, or a silent shape change from a new
Deno version that still resolves to the same configuration string. In that
case, bump the -v1- version prefix in both the key and restore-keys of
the cache step in action.yml:
- key: ${{ runner.os }}-${{ runner.arch }}-deno-cache-v1-...
+ key: ${{ runner.os }}-${{ runner.arch }}-deno-cache-v2-...
restore-keys: |
- ${{ runner.os }}-${{ runner.arch }}-deno-cache-v1-
+ ${{ runner.os }}-${{ runner.arch }}-deno-cache-v2-This avoids editing any of the five hashed files (which may not even be involved in the regression) and leaves a clear audit trail in git history.
- Cache misbehaves: delete the "Cache Deno development cache" step in
action.yml.configure.sh/configure.cmdrunvendor.sh/vendor.cmdas before and redownload from scratch. - Wipe gate misbehaves: remove the
QUARTO_SKIP_DENO_CACHE_WIPEenv from the configure steps. Vendor scripts fall back to the default wipe.
~150–200 MB per OS, three OSes → ~600 MB total repo cache usage, well under GitHub's 10 GB per-repo quota.