fix(ci): key web Metro cache on .env to stop stale EXPO_PUBLIC_* poisoning#560
fix(ci): key web Metro cache on .env to stop stale EXPO_PUBLIC_* poisoning#560ignaciosantise wants to merge 1 commit into
Conversation
…oning Expo inlines EXPO_PUBLIC_* into the web bundle at Babel transform time, but Metro's transform cache (node_modules/.cache) is not keyed on env values. Callers that inject per-run ephemeral creds (e.g. a rotating wallet private key/address) therefore restored a cache built with a different .env and shipped the previous run's address baked into the bundle. Hash the written .env into the cache key and drop restore-keys: the prefix fallback matched any older cache regardless of .env, so an exact-key miss re-poisoned from a stale entry. Exact-match-only makes it safe. Stable-creds callers keep a full cache hit (identical .env => identical key); per-run-creds callers cold-build each run, as intended. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Claude finished @ignaciosantise's task in 1m 38s —— View job Review Tasks
The fix is mechanically correct: Found 1 issue(s)Issue 1: Per-run-creds callers accumulate unreusable cache entries on every runID: action-web-metro-cache-churn-b3f1 Context:
Recommendation: Split into restore + conditional save using the v4 separate actions, or add a - uses: actions/cache/restore@v4 # restore only
with:
path: ...
key: ${{ runner.os }}-web-metro-${{ hashFiles(...) }}
# only save when not using ephemeral creds (caller sets input)
- if: inputs.save-web-metro-cache != 'false'
uses: actions/cache/save@v4
with:
path: ...
key: ${{ runner.os }}-web-metro-${{ hashFiles(...) }}This is a follow-up improvement, not a blocker for merging — the current behavior is safe and correct. |
There was a problem hiding this comment.
Pull request overview
Updates the reusable WalletKit CI composite action to prevent stale EXPO_PUBLIC_* values from being inlined into Expo web bundles due to Metro’s persisted transform cache being restored across runs.
Changes:
- Add the generated wallet
.envfile to theactions/cachekey for the web Metro cache. - Remove
restore-keysso cache restores are exact-match-only and cannot fall back to older.envvariants.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Folding the Metro web-cache env-poisoning fix into #559 so there's a single SHA to test both changes against from the downstream repo. |
| key: ${{ runner.os }}-web-metro-${{ hashFiles(format('{0}/yarn.lock', steps.paths.outputs.wallet_root)) }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-web-metro- | ||
| key: ${{ runner.os }}-web-metro-${{ hashFiles(format('{0}/yarn.lock', steps.paths.outputs.wallet_root), format('{0}/.env', steps.paths.outputs.wallet_root)) }} |
There was a problem hiding this comment.
🤖 Auto Review Issue: Per-run-creds callers accumulate unreusable cache entries on every run
Severity: LOW
Category: performance
Tool: Claude Auto Review
Context:
- Pattern:
actions/cache@v4always saves the cache in its post-step when the restore key missed. For callers with rotatingEXPO_PUBLIC_*, every run produces a unique.envhash → perpetual miss → perpetual save of a cache entry that will never be reused. - Risk: Callers' GitHub Actions cache fills with one-shot entries at ~the transform cache size per run (typically tens to hundreds of MB). GitHub evicts entries not accessed in 7 days, so the churn is bounded, but active CI pipelines will permanently occupy significant cache quota.
- Impact: Wasted cache quota in external caller repos; no correctness impact.
- Trigger: Any caller injecting per-run ephemeral credentials (the scenario this PR targets).
Recommendation: Split into restore + conditional save using the v4 separate actions, or add a save-cache input (default true) to let callers opt out of saving when they know their key will never repeat:
- uses: actions/cache/restore@v4 # restore only
with:
path: ...
key: ${{ runner.os }}-web-metro-${{ hashFiles(...) }}
# only save when not using ephemeral creds (caller sets input)
- if: inputs.save-web-metro-cache != 'false'
uses: actions/cache/save@v4
with:
path: ...
key: ${{ runner.os }}-web-metro-${{ hashFiles(...) }}This is a follow-up improvement, not a blocker for merging — the current behavior is safe and correct.
Problem
The web leg's
Cache Metro web build cachestep warmsnode_modules/.cache(Expo/Metro's transformFileStore) across runs. Expo inlinesEXPO_PUBLIC_*into the bundle at Babel transform time, and Metro's transform cache is not keyed on env values.For callers that inject per-run ephemeral credentials — e.g. an external repo rotating
EXPO_PUBLIC_TEST_PRIVATE_KEY(and therefore the wallet address) on every run — the restored cache still holds the previous run's address inlined into the bundle. The exported web app ships a stale wallet address, and the Maestro pay flows run against the wrong account.Fix
.github/actions/walletkit-build-and-maestro/action.yml:.envinto the cache key (alongsideyarn.lock), so a differentEXPO_PUBLIC_*set produces a different key.restore-keys. This is the actual bug.restore-keys: <os>-web-metro-is a prefix fallback that matches any older cache regardless of.env— so even with.envin the exact key, an exact-key miss falls back to a stale cache and re-poisons. Exact-match-only is what makes it safe.Ordering is fine:
.envis written by theWrite wallet .envstep, which runs before this cache step, sohashFiles(…/.env)resolves.Effect
.envdiffers every run → key always misses → cold build → correct address inlined. No more poisoning.secrets.*):.envidentical across runs → exact key hit → full transform-cache speedup preserved.Note: this repo's in-repo CI uses stable secrets, so it was not actively poisoned; the fix is correct and defensive for the reusable composite action and harmless to in-repo cache performance.
Follow-up to #555 (which added the Expo web leg).
🤖 Generated with Claude Code