# CI Deno cache

How the Quarto development Deno cache is cached in GitHub Actions, how
invalidation works, and how to force a fresh download.

## What gets cached

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`.

## Cache key

```
${{ 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.

### Not in the key

- `quarto.ts` and other `src/**/*.ts` files: these are consumer code, not
  dep-resolution inputs. `deno install` is additive on top of an existing
  cache, so new imports in consumer code download without needing a key change.
  Hashing `src/**` would invalidate on every commit.
- `vendor.sh` / `vendor.cmd`: they control *how* `deno install` runs, not
  *what* it resolves. Including them would cross-invalidate (a Unix-only
  `vendor.sh` edit would bust the Windows cache too, since `hashFiles()` is
  OS-independent).
- `deno.lock` / `tests/deno.lock`: both are gitignored (`.gitignore:26`) and
  are generated by `deno install` at run time, so they do not exist on a
  fresh checkout and cannot serve as key inputs.

## Wipe gate

`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.sh` and `vendor.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.

## Forcing invalidation

### When it happens automatically

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.

### When you have to force it manually

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`:

```diff
-        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.

## Rollback

- **Cache misbehaves:** delete the "Cache Deno development cache" step in
  `action.yml`. `configure.sh` / `configure.cmd` run `vendor.sh` /
  `vendor.cmd` as before and redownload from scratch.
- **Wipe gate misbehaves:** remove the `QUARTO_SKIP_DENO_CACHE_WIPE` env
  from the configure steps. Vendor scripts fall back to the default wipe.

## Expected footprint

~150–200 MB per OS, three OSes → ~600 MB total repo cache usage, well under
GitHub's 10 GB per-repo quota.
