Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,30 @@ You can also extend the default cache key:

This gives you full control over cache invalidation based on the specific aspects that matter to your workflow.

### Rust and Cargo Caches

This action caches mise's own data, but it does not save Rust toolchains.
According to [mise's Rust documentation](https://mise.jdx.dev/lang/rust.html),
Rust is managed by rustup, uses `RUSTUP_HOME` and `CARGO_HOME`, and does not live
under mise's `installs` directory. For Rust projects, cache Cargo dependencies,
build artifacts, and Cargo-installed tools explicitly.

The usual setup is to run `Swatinem/rust-cache` after `mise-action`, so the cache
key can include the Rust/Cargo environment and Cargo lockfiles:

```yaml
- uses: jdx/mise-action@v4
with:
install: true

- uses: Swatinem/rust-cache@v2
```

`Swatinem/rust-cache` caches `~/.cargo` and `target` by default, including
`~/.cargo/bin` for tools installed during the workflow. If your workflow uses a
custom `CARGO_HOME`, or installs Cargo tools somewhere else, add that directory
to your cache configuration manually.
Comment on lines +116 to +119
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The ~/.cargo description is a slight over-simplification. Swatinem/rust-cache does not cache the entire ~/.cargo tree — it caches specific subdirectories (registry/index, registry/cache, git/db) plus ~/.cargo/bin (when cache-bin: true, the default) and the target directory. Notably, ~/.cargo/registry/src is explicitly not cached because cargo recreates it from the compressed archives. Saying "caches ~/.cargo and target" may lead users to assume all cargo data is persisted when it isn't.

Suggested change
`Swatinem/rust-cache` caches `~/.cargo` and `target` by default, including
`~/.cargo/bin` for tools installed during the workflow. If your workflow uses a
custom `CARGO_HOME`, or installs Cargo tools somewhere else, add that directory
to your cache configuration manually.
`Swatinem/rust-cache` caches the Cargo registry, git dependencies, `~/.cargo/bin`,
and `target` by default. If your workflow uses a custom `CARGO_HOME`, or installs
Cargo tools somewhere else, add that directory to your cache configuration manually.

Comment on lines +116 to +119
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It is more accurate to state that Swatinem/rust-cache caches the Cargo registry, git database, and binaries (~/.cargo/{registry,git,bin}) rather than the entire ~/.cargo directory. Additionally, since mise-action exports environment variables like CARGO_HOME to GITHUB_ENV, rust-cache will typically pick them up automatically. Manual configuration is usually only necessary for non-standard tool installation paths outside of the Cargo home.


## GitHub API Rate Limits

When installing tools hosted on GitHub (like `gh`, `node`, `bun`, etc.), mise needs to make API calls to GitHub's releases API. Without authentication, these calls are subject to GitHub's rate limit of 60 requests per hour, which can cause installation failures.
Expand Down
36 changes: 32 additions & 4 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

51 changes: 48 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ const MISE_CONFIG_FILE_PATTERNS = [
const DEFAULT_CACHE_KEY_TEMPLATE =
'{{cache_key_prefix}}-{{platform}}{{#if version}}-{{version}}{{/if}}{{#if mise_env}}-{{mise_env}}{{/if}}{{#if install_args_hash}}-{{install_args_hash}}{{/if}}-{{#if file_hash}}{{file_hash}}{{else}}no-config{{/if}}'

const RUST_CACHE_PATHS = [path.join('installs', 'rust')]
Comment thread
greptile-apps[bot] marked this conversation as resolved.

async function run(): Promise<void> {
try {
await setToolVersions()
Expand Down Expand Up @@ -475,13 +477,56 @@ async function saveCache(cacheKey: string): Promise<void> {
throw new Error(`Cache folder path does not exist on disk: ${cachePath}`)
}

const cacheId = await cache.saveCache([cachePath], cacheKey)
if (cacheId === -1) return
const excludedPaths = await stageRustCachePaths(cachePath)
try {
const cacheId = await cache.saveCache([cachePath], cacheKey)
if (cacheId === -1) return

core.info(`Cache saved from ${cachePath} with key: ${cacheKey}`)
core.info(`Cache saved from ${cachePath} with key: ${cacheKey}`)
} finally {
await restoreRustCachePaths(excludedPaths)
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.
})
}

type StagedCachePath = {
originalPath: string
stagedPath: string
}

async function stageRustCachePaths(
cachePath: string
): Promise<StagedCachePath[]> {
const stagedPaths: StagedCachePath[] = []

for (const relativePath of RUST_CACHE_PATHS) {
const originalPath = path.join(cachePath, relativePath)
if (!fs.existsSync(originalPath)) continue

const stagedPath = path.join(
path.dirname(cachePath),
`${path.basename(cachePath)}-excluded-${relativePath.replaceAll(path.sep, '-')}-${process.pid}-${Date.now()}`
)

core.info(`Excluding ${originalPath} from mise cache`)
await fs.promises.rename(originalPath, stagedPath)
stagedPaths.push({ originalPath, stagedPath })
}

return stagedPaths
}

async function restoreRustCachePaths(
stagedPaths: StagedCachePath[]
): Promise<void> {
for (const { originalPath, stagedPath } of stagedPaths.reverse()) {
if (!fs.existsSync(stagedPath)) continue

await fs.promises.mkdir(path.dirname(originalPath), { recursive: true })
await fs.promises.rename(stagedPath, originalPath)
}
}

async function getTarget(): Promise<string> {
const arch = process.arch === 'arm' ? 'armv7' : process.arch
switch (process.platform) {
Expand Down
Loading