From 47a7f4fb2e900d6c33a5b5f231fa21dbfaeba52f Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Mon, 20 Jul 2026 14:25:19 -0400 Subject: [PATCH] Change `prune-cache` default to `false` (#967) ## Summary This changes the default for `prune-cache` from `true` to `false`, motivated by [#745](https://github.com/astral-sh/setup-uv/issues/745). Users that want the existing behavior can continue to set `prune-cache: true` explicitly. Some history: I originally added [`uv cache prune --ci`](https://github.com/astral-sh/uv/pull/5391) after looking at a workload where the uv cache was ~2.2 GB, almost entirely due to the enormous pre-built `torch` and `nvidia_cudnn_cu12` wheels ([original analysis](https://github.com/actions/setup-python/issues/822#issuecomment-2248728264)). Persisting and restoring thousands of extracted files through the GitHub Actions cache could be slower than downloading the wheels again. In contrast, wheels built from source can be very expensive to recreate. The intent was to remove pre-built wheels while retaining locally-built wheels. `setup-uv` subsequently made pruning configurable, but defaulted `prune-cache` to `true`; it also later enabled caching by default on GitHub-hosted runners. As a result, the default configuration repeatedly downloads pre-built wheels from PyPI even on a cache hit. That tradeoff has become more important as uv adoption has grown: [the PyPI analysis in #745](https://github.com/astral-sh/setup-uv/issues/745#issuecomment-3867334064) estimates that uv accounts for roughly half of reported CI downloads from PyPI, and roughly 65-75% for `boto3`. I ran the comparison across a few different workloads: | Workload | PR | Packages | Cache: keep / prune / prune-ci | Warm restore+sync: keep / prune / prune-ci | Downloads: prune / prune-ci | |---|---:|---:|---:|---:|---:| | Tiny | [#1](https://github.com/astral-sh/setup-uv-benchmarks/pull/1) | 19 | 6 / 6 / 2 MB | 0.3-0.4 / 0.3 / 0.4-0.5 s | 0 / 2 | | Web | [#2](https://github.com/astral-sh/setup-uv-benchmarks/pull/2) | 65 | 43 / 43 / 7 MB | 0.6-1.0 / 0.5-0.6 / 1.5-1.7 s | 0 / 6 | | Scientific | [#3](https://github.com/astral-sh/setup-uv-benchmarks/pull/3) | 118 | 586 / 586 / 8 MB | 8.2-16.0 / 7.0-8.2 / 8.9-12.1 s | 0 / 19 | | PySpark | [#4](https://github.com/astral-sh/setup-uv-benchmarks/pull/4) | 19 | 1820 / 1820 / 436 MB | 9.9-21.1 / 10.5-11.0 / 5.0-7.0 s | 0 / 4 | | CPU PyTorch | [#5](https://github.com/astral-sh/setup-uv-benchmarks/pull/5) | 14 | 182 / 182 / 1 MB | 3.0-6.0 / 3.6-4.0 / 5.7-6.4 s | 0 / 6 | | CPU-PyTorch ML | [#6](https://github.com/astral-sh/setup-uv-benchmarks/pull/6) | 137 | 346 / 346 / 10 MB | 7.4-18.0 / 8.8-8.9 / 9.7-11.9 s | 0 / 20 | | CUDA PyTorch | [#7](https://github.com/astral-sh/setup-uv-benchmarks/pull/7) | 201 | 2316 / 2315 / 16 MB | 30.2-67.9 / 31.0-63.6 / 33.3-36.7 s | 0 / 40 | The CUDA workload intentionally reproduces the original `torch==2.1.1` example. Keeping wheels again produces a ~2.3 GB Actions cache. Across nine warm runs, restoring that cache ranged from slightly faster than re-downloading to roughly twice as slow; pruning consistently re-downloaded 40 distributions in ~33-37 seconds ([original runs](https://github.com/astral-sh/setup-uv-benchmarks/actions/runs/29750292738), [additional runs](https://github.com/astral-sh/setup-uv-benchmarks/actions/runs/29761705492)). I also tried running `uv cache prune --force` without `--ci` across every workload, to see if it provided a useful middle ground. It did not meaningfully reduce any of the caches: plain prune took 11-21 ms and left the extracted cache and file count unchanged, including PySpark. On these fresh caches, there are no dangling entries to remove; without `--ci`, the pre-built wheels and unpacked source/build artifacts are retained. The per-workload runs are linked in the table above. So the original motivation still holds for very large CUDA or source-heavy workloads, but it is not representative of the common case. For smaller workloads, keeping pre-built wheels is generally faster and avoids repeated PyPI traffic. This changes the default accordingly, while retaining `prune-cache: true` as an opt-in for workloads where the smaller cache is worthwhile. Closes https://github.com/astral-sh/setup-uv/issues/745. --- README.md | 2 +- __tests__/cache/restore-cache.test.ts | 4 ++-- __tests__/helpers/setup-inputs.ts | 2 +- action.yml | 2 +- docs/caching.md | 27 +++++++++++++-------------- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index c1a3170..1e58e93 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ Have a look under [Advanced Configuration](#advanced-configuration) for detailed cache-local-path: "" # Prune cache before saving - prune-cache: "true" + prune-cache: "false" # Upload managed Python installations to the GitHub Actions cache cache-python: "false" diff --git a/__tests__/cache/restore-cache.test.ts b/__tests__/cache/restore-cache.test.ts index ec342d3..bb97348 100644 --- a/__tests__/cache/restore-cache.test.ts +++ b/__tests__/cache/restore-cache.test.ts @@ -62,13 +62,13 @@ describe("restoreCache", () => { expect(cacheKey).toContain("-tests-3.10%2C3.11"); }); - it("keeps cache keys unchanged for exact Python versions and simple suffixes", async () => { + it("uses an unpruned cache key by default", async () => { const inputs = createSetupInputs({ cacheSuffix: "tests-3.11" }); await restoreCache(inputs, "3.11"); expect(cacheKeyOutput()).toBe( - "setup-uv-2-x86_64-unknown-linux-gnu-ubuntu-24.04-3.11-pruned-dependencyhash-tests-3.11", + "setup-uv-2-x86_64-unknown-linux-gnu-ubuntu-24.04-3.11-dependencyhash-tests-3.11", ); }); }); diff --git a/__tests__/helpers/setup-inputs.ts b/__tests__/helpers/setup-inputs.ts index 1ca0d0d..0a6a625 100644 --- a/__tests__/helpers/setup-inputs.ts +++ b/__tests__/helpers/setup-inputs.ts @@ -20,7 +20,7 @@ export function createSetupInputs( ignoreEmptyWorkdir: false, ignoreNothingToCache: false, noProject: false, - pruneCache: true, + pruneCache: false, pythonDir: "/tmp/uv-python-dir", pythonVersion: "", quiet: false, diff --git a/action.yml b/action.yml index 4bc3eaf..ecacacb 100644 --- a/action.yml +++ b/action.yml @@ -61,7 +61,7 @@ inputs: default: "" prune-cache: description: "Prune cache before saving." - default: "true" + default: "false" cache-python: description: "Upload managed Python installations to the Github Actions cache." default: "false" diff --git a/docs/caching.md b/docs/caching.md index 37501a3..367d443 100644 --- a/docs/caching.md +++ b/docs/caching.md @@ -33,8 +33,8 @@ The computed cache key is available as the `cache-key` output: ## Enable caching > [!NOTE] -> The cache is pruned before it is uploaded to the GitHub Actions cache. This can lead to -> a small or empty cache. See [Disable cache pruning](#disable-cache-pruning) for more details. +> The entire uv cache is uploaded to the GitHub Actions cache by default. To reduce the cache size, +> see [Enable cache pruning](#enable-cache-pruning). If you enable caching, the [uv cache](https://docs.astral.sh/uv/concepts/cache/) will be uploaded to the GitHub Actions cache. This can speed up runs that reuse the cache by several minutes. @@ -173,30 +173,29 @@ It defaults to `setup-uv-cache` in the `TMP` dir, `D:\a\_temp\setup-uv-cache` on cache-local-path: "/path/to/cache" ``` -## Disable cache pruning +## Enable cache pruning -By default, the uv cache is pruned after every run, removing pre-built wheels, but retaining any -wheels that were built from source. On GitHub-hosted runners, it's typically faster to omit those -pre-built wheels from the cache (and instead re-download them from the registry on each run). -However, on self-hosted or local runners, preserving the cache may be more efficient. See -the [documentation](https://docs.astral.sh/uv/concepts/cache/#caching-in-continuous-integration) for -more information. +By default, the entire uv cache is persisted across runs. On GitHub-hosted runners, it's typically +faster to prune the cache before saving it, removing pre-built wheels, but retaining any wheels that +were built from source. The pre-built wheels are then re-downloaded from the registry on each run. +See the [documentation](https://docs.astral.sh/uv/concepts/cache/#caching-in-continuous-integration) +for more information. -If you want to persist the entire cache across runs, disable cache pruning with the `prune-cache` -input. +If you want to prune the cache before saving it, enable cache pruning with the `prune-cache` input. ```yaml -- name: Don't prune the cache before saving it +- name: Prune the cache before saving it uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 with: enable-cache: true - prune-cache: false + prune-cache: true ``` ## Cache Python installs By default, the Python install dir (`uv python dir` / `UV_PYTHON_INSTALL_DIR`) is not cached, -for the same reason that the dependency cache is pruned. +for the same reason that pruning the dependency cache can improve performance on GitHub-hosted +runners. If you want to cache Python installs along with your dependencies, set the `cache-python` input to `true`. Note that this only caches Python versions that uv actually installs into `UV_PYTHON_INSTALL_DIR`