fix: include runner image in cache key to prevent cross-provider collisions#456
fix: include runner image in cache key to prevent cross-provider collisions#456
Conversation
There was a problem hiding this comment.
Code Review
This pull request updates the {{platform}} template variable to include the runner's operating system image (e.g., ubuntu24), aiming to prevent cache collisions across different runner environments. However, the current implementation in getTarget() is problematic as it breaks the construction of download URLs for the mise binary, which do not include these suffixes. It is recommended to separate the platform string used for downloads from the one used for cache keys. Additionally, the documentation for the {{platform}} variable should be simplified to be more user-friendly.
| // images export ImageOS (e.g. "macos15", "ubuntu24"); other runners | ||
| // typically don't, so they pool under "self-hosted". | ||
| const imageOS = process.env.ImageOS || 'self-hosted' | ||
| return `${base}-${imageOS}` |
There was a problem hiding this comment.
This change will break the installation of the mise binary. The getTarget() function is used in setupMise() (lines 315 and 317) to construct the download URL for the mise executable. Since mise releases do not include the ImageOS suffix (e.g., -ubuntu24 or -self-hosted) in their asset names, the resulting URLs will be invalid (e.g., https://mise.jdx.dev/mise-latest-linux-x64-ubuntu24.tar.gz), causing the action to fail during the setup phase.
You should separate the platform string used for binary downloads from the one used for the cache key. A better approach would be to keep getTarget() returning the base platform and append the ImageOS discriminator directly in processCacheKeyTemplate() (where platform is assigned at line 521).
| - `{{version}}` - The mise version (from the `version` input) | ||
| - `{{cache_key_prefix}}` - The cache key prefix (from `cache_key_prefix` input or default) | ||
| - `{{platform}}` - The target platform (e.g., "linux-x64", "macos-arm64") | ||
| - `{{platform}}` - The target platform, including the runner image (e.g., "linux-x64-ubuntu24", "macos-arm64-macos15", "linux-x64-self-hosted"). The trailing segment is `process.env.ImageOS` on github-hosted runners and falls back to `"self-hosted"` elsewhere — preventing cache collisions when the same repo runs on different runner providers (github-hosted, namespace.so, self-hosted). |
There was a problem hiding this comment.
The description for {{platform}} is quite technical. Instead of mentioning process.env.ImageOS, it might be clearer to describe it as the runner's operating system image version. This makes the documentation more accessible to users who aren't familiar with GitHub Actions internal environment variables.
| - `{{platform}}` - The target platform, including the runner image (e.g., "linux-x64-ubuntu24", "macos-arm64-macos15", "linux-x64-self-hosted"). The trailing segment is `process.env.ImageOS` on github-hosted runners and falls back to `"self-hosted"` elsewhere — preventing cache collisions when the same repo runs on different runner providers (github-hosted, namespace.so, self-hosted). | |
| - `{{platform}}` - The target platform, including the runner image version (e.g., "linux-x64-ubuntu24", "macos-arm64-macos15", "linux-x64-self-hosted"). This prevents cache collisions when the same repository runs on different runner providers (GitHub-hosted, namespace.so, self-hosted). |
Greptile SummaryThis PR fixes cross-provider cache collisions in Confidence Score: 5/5Safe to merge — the image discriminator is correctly scoped to cache keys only, leaving download URLs intact. No P0 or P1 issues present. The previously flagged P0 (image suffix leaking into download URLs) does not apply to this implementation — No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Action runs] --> B{cache_key input set?}
B -- yes --> C[Custom template]
B -- no --> D[Default template]
C & D --> E[processCacheKeyTemplate]
E --> F[getTarget returns os-arch]
E --> G[getRunnerImageId returns ImageOS or self-hosted]
F & G --> H[platform = os-arch-imageId]
H --> I[Cache key scoped per runner image]
J[Download binary] --> K[getTarget only - no image suffix]
K --> L[Download URL unchanged by this PR]
Reviews (2): Last reviewed commit: "fix: include runner image in cache key t..." | Re-trigger Greptile |
…isions The cache key included only os and arch (`mise-v1-macos-arm64-<hash>`), so any repo running CI on multiple runner providers with the same os/arch — github-hosted, namespace.so, BuildJet, self-hosted M-series macs — would clobber each other's caches. The first run on a new provider would restore tool installs built for the previous provider's image, leading to "does not have an executable named X" errors or SIGILL crashes on cached binaries built against a different glibc. Append the GitHub Actions hosted-runner ImageOS env var (e.g. "macos15", "ubuntu24") to the cache key's platform segment. Other runners pool under "self-hosted"; users running multiple self-hosted profiles can scope further with cache_key_prefix. Implementation note: `getTarget()` is also used to construct the mise binary download URL (must match the release-asset filename, e.g. `mise-v2026.4.0-macos-arm64.tar.gz`). Adding the image suffix there would 404 the download. So `getTarget()` keeps its current shape and a new `getRunnerImageId()` helper composes the image discriminator into the cache-key platform segment only. This is a one-time cache miss for existing users; the cache rebuilds on the next run and stays scoped per-image after that.
315e733 to
ef1bd0e
Compare
|
Refactored after spotting that Split into two responsibilities:
Download URLs are unchanged; only the cache key gets the new suffix. This comment was generated by Claude Code. |
Workaround for jdx/mise-action#456: the action's default cache key was os+arch only, so github-hosted and namespace caches collide and restoring one onto the other corrupts tool installs (e.g. swiftlint SIGILL on Linux, "no executable named X" across backends on macOS). Set cache_key_prefix to include runner.environment ("github-hosted" or "self-hosted") so fork-PR (github-hosted) and main/non-fork (namespace) runs use separate cache pools. Drop once mise-action#456 lands and we re-pin to the new version. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Re-pin jdx/mise-action to b287efd (post jdx/mise-action#456 merge), which builds runner.environment-style discrimination into the cache key automatically. Drop the cache_key_prefix workaround everywhere except release-plz.yml, where the with: block keeps experimental: true. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Problem
The default cache key was
mise-v1-{os}-{arch}-{file_hash}— no runner-image discriminator. Any repo whose CI runs on multiple runner providers with the same os/arch shares one cache slot:macos-latestnscloud-macos-sequoia-arm64-*/namespace-profile-*-macos-arm64When a repo migrates from one provider to another, the new run restores the previous provider's tool installs (~200 MB of
~/.local/share/mise/installs/*), and tools that loaded fine in the original image break in the new one.Concrete failures observed
Discovered while migrating jdx/hk from github-hosted to namespace.so. Same
mise-v1-macos-arm64-<hash>cache hit on namespace; tool resolution fails everywhere:— installs are present (cache restored 185 MB) but the executable layout from the github-hosted macOS-15 image doesn't match what mise expects on namespace's macOS arm64 image.
On Linux, cached binaries built against the github-hosted ubuntu glibc/CPU featureset SIGILL on namespace's image (e.g.
swiftlintexit code 132).Fix
Append the GitHub Actions hosted-runner
ImageOSenv var (e.g.macos15,ubuntu24) to the platform segment of the default cache key. Other runners pool underself-hosted.After this change:
mise-v1-macos-arm64-macos15-<hash>(github-hosted)mise-v1-macos-arm64-self-hosted-<hash>(namespace, self-hosted, etc.)Users with multiple self-hosted profiles that need finer scoping can set
cache_key_prefixper workflow. The README's docs for{{platform}}are updated to reflect the new format.Trade-offs
macos15→macos16) will invalidate cache, which is desirable — that's exactly when stale binaries cause problems.self-hostedslot. They'd needcache_key_prefixper pool, same as before. This PR doesn't make that worse.Test plan
dist/index.jsrebuilt cleanly (yes,npm run packagesucceeded with the change visible atgetTarget()callsite).ImageOSis read from env (e.g.macos15) and shows up in themise cache restored from key:log line.-self-hosted.Note
Medium Risk
Changes cache-key generation and will cause a one-time cache miss plus different cache partitioning, which can affect build times and cache reuse across runners.
Overview
Updates the default cache-key
{{platform}}value to append a runner image discriminator (process.env.ImageOSon GitHub-hosted runners, otherwiseself-hosted), reducing cross-provider/image cache collisions that can restore incompatible tool installs.Implements this via a new
getRunnerImageId()helper used during cache-key template processing, and documents the new{{platform}}format in the README;dist/index.jsis rebuilt accordingly.Reviewed by Cursor Bugbot for commit ef1bd0e. Bugbot is set up for automated code reviews on this repo. Configure here.