mirror of
https://github.com/astral-sh/setup-uv.git
synced 2026-07-22 20:20:23 +00:00
47a7f4fb2e
## 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.
342 lines
13 KiB
Markdown
342 lines
13 KiB
Markdown
# setup-uv
|
|
|
|
Set up your GitHub Actions workflow with a specific version of [uv](https://docs.astral.sh/uv/).
|
|
|
|
- Install a version of uv and add it to PATH
|
|
- Cache the installed version of uv to speed up consecutive runs on self-hosted runners
|
|
- Register problem matchers for error output
|
|
- (Optional) Persist the uv's cache in the GitHub Actions Cache
|
|
- (Optional) Verify the checksum of the downloaded uv executable
|
|
|
|
## Contents
|
|
|
|
- [Usage](#usage)
|
|
- [Install a required-version or latest (default)](#install-a-required-version-or-latest-default)
|
|
- [Inputs](#inputs)
|
|
- [Outputs](#outputs)
|
|
- [Python version](#python-version)
|
|
- [Working directory](#working-directory)
|
|
- [Advanced Configuration](#advanced-configuration)
|
|
- [How it works](#how-it-works)
|
|
- [FAQ](#faq)
|
|
|
|
## Usage
|
|
|
|
### Install a required-version or latest (default)
|
|
|
|
```yaml
|
|
- name: Install the latest version of uv
|
|
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
```
|
|
|
|
If you do not specify a version, this action will look for a [required-version](https://docs.astral.sh/uv/reference/settings/#required-version)
|
|
in a `uv.toml` or `pyproject.toml` file in the repository root. If none is found, the latest version will be installed.
|
|
|
|
For an example workflow, see
|
|
[here](https://github.com/charliermarsh/autobot/blob/e42c66659bf97b90ca9ff305a19cc99952d0d43f/.github/workflows/ci.yaml).
|
|
|
|
### Inputs
|
|
|
|
All inputs and their defaults.
|
|
Have a look under [Advanced Configuration](#advanced-configuration) for detailed documentation on most of them.
|
|
|
|
```yaml
|
|
- name: Install uv with all available options
|
|
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
with:
|
|
# The version of uv to install (default: searches for version in config files, then latest)
|
|
version: ""
|
|
|
|
# Path to a file containing the version of uv to install, e.g., uv.toml, pyproject.toml, .tool-versions, requirements.txt or uv.lock (default: searches uv.toml then pyproject.toml)
|
|
version-file: ""
|
|
|
|
# Resolution strategy when resolving version ranges: 'highest' or 'lowest'
|
|
resolution-strategy: "highest"
|
|
|
|
# The version of Python to set UV_PYTHON to
|
|
python-version: ""
|
|
|
|
# Use uv venv to activate a venv ready to be used by later steps
|
|
activate-environment: "false"
|
|
|
|
# Custom path for the virtual environment when using activate-environment (default: .venv in the working directory)
|
|
venv-path: ""
|
|
|
|
# Pass --no-project when creating the venv with activate-environment.
|
|
no-project: "false"
|
|
|
|
# The directory to execute all commands in and look for files such as pyproject.toml
|
|
working-directory: ""
|
|
|
|
# The checksum of the uv version to install
|
|
checksum: ""
|
|
|
|
# Used when downloading uv from GitHub releases
|
|
github-token: ${{ github.token }}
|
|
|
|
# Enable uploading of the uv cache: true, false, or auto (enabled on GitHub-hosted runners, disabled on self-hosted runners)
|
|
enable-cache: "auto"
|
|
|
|
# Glob pattern to match files relative to the repository root to control the cache
|
|
cache-dependency-glob: |
|
|
**/*requirements*.txt
|
|
**/*requirements*.in
|
|
**/*constraints*.txt
|
|
**/*constraints*.in
|
|
**/pyproject.toml
|
|
**/uv.lock
|
|
**/*.py.lock
|
|
|
|
# Whether to restore the cache if found
|
|
restore-cache: "true"
|
|
|
|
# Whether to save the cache after the run
|
|
save-cache: "true"
|
|
|
|
# Suffix for the cache key
|
|
cache-suffix: ""
|
|
|
|
# Local path to store the cache (default: "" - uses system temp directory)
|
|
cache-local-path: ""
|
|
|
|
# Prune cache before saving
|
|
prune-cache: "false"
|
|
|
|
# Upload managed Python installations to the GitHub Actions cache
|
|
cache-python: "false"
|
|
|
|
# Ignore when nothing is found to cache
|
|
ignore-nothing-to-cache: "false"
|
|
|
|
# Ignore when the working directory is empty
|
|
ignore-empty-workdir: "false"
|
|
|
|
# Custom path to set UV_TOOL_DIR to
|
|
tool-dir: ""
|
|
|
|
# Custom path to set UV_TOOL_BIN_DIR to
|
|
tool-bin-dir: ""
|
|
|
|
# URL to a custom manifest file in the astral-sh/versions format
|
|
manifest-file: ""
|
|
|
|
# Download uv from the Astral mirror instead of directly from GitHub Releases
|
|
download-from-astral-mirror: "true"
|
|
|
|
# Add problem matchers
|
|
add-problem-matchers: "true"
|
|
|
|
# Suppress info-level log output. Only warnings and errors are shown
|
|
quiet: "false"
|
|
```
|
|
|
|
### Outputs
|
|
|
|
- `uv-version`: The installed uv version. Useful when using latest.
|
|
- `uv-path`: The path to the installed uv binary.
|
|
- `uvx-path`: The path to the installed uvx binary.
|
|
- `cache-hit`: A boolean value to indicate a cache entry was found.
|
|
- `venv`: Path to the activated venv if activate-environment is true.
|
|
- `python-version`: The Python version that was set.
|
|
- `python-cache-hit`: A boolean value to indicate the Python cache entry was found.
|
|
|
|
### Python version
|
|
|
|
You can use the input `python-version` to set the environment variable `UV_PYTHON` for the rest of your workflow
|
|
|
|
This will override any python version specifications in `pyproject.toml` and `.python-version`
|
|
|
|
```yaml
|
|
- name: Install the latest version of uv and set the python version to 3.13t
|
|
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
with:
|
|
python-version: 3.13t
|
|
- run: uv pip install --python=3.13t pip
|
|
```
|
|
|
|
You can combine this with a matrix to test multiple Python versions:
|
|
|
|
```yaml
|
|
jobs:
|
|
test:
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
matrix:
|
|
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- name: Install the latest version of uv and set the python version
|
|
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
with:
|
|
python-version: ${{ matrix.python-version }}
|
|
- name: Test with python ${{ matrix.python-version }}
|
|
run: uv run --frozen pytest
|
|
```
|
|
|
|
### Working directory
|
|
|
|
You can set the working directory with the `working-directory` input.
|
|
This controls where we look for `pyproject.toml`, `uv.toml` and `.python-version` files
|
|
which are used to determine the version of uv and python to install.
|
|
|
|
It also controls where [the venv gets created](#activate-environment), unless `venv-path` is set.
|
|
|
|
```yaml
|
|
- name: Install uv based on the config files in the working-directory
|
|
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
with:
|
|
working-directory: my/subproject/dir
|
|
```
|
|
|
|
## Advanced Configuration
|
|
|
|
For more advanced configuration options, see our detailed documentation:
|
|
|
|
- **[Advanced Version Configuration](docs/advanced-version-configuration.md)** - Resolution strategies and version files
|
|
- **[Caching](docs/caching.md)** - Complete guide to caching configuration
|
|
- **[Environment and Tools](docs/environment-and-tools.md)** - Environment activation, tool directories, authentication, and environment variables
|
|
- **[Customization](docs/customization.md)** - Checksum validation, custom manifests, and problem matchers
|
|
|
|
## How it works
|
|
|
|
By default, this action resolves uv versions from the
|
|
[`astral-sh/versions`](https://github.com/astral-sh/versions) manifest and downloads uv from the
|
|
official [GitHub Releases](https://github.com/astral-sh/uv).
|
|
|
|
It then uses the [GitHub Actions Toolkit](https://github.com/actions/toolkit) to cache uv as a
|
|
tool to speed up consecutive runs on self-hosted runners.
|
|
|
|
The installed version of uv is then added to the runner PATH, enabling later steps to invoke it
|
|
by name (`uv`).
|
|
|
|
## FAQ
|
|
|
|
### Do I still need `actions/setup-python` alongside `setup-uv`?
|
|
|
|
With `setup-uv`, you can install a specific version of Python using `uv python install` rather than
|
|
relying on `actions/setup-python`.
|
|
|
|
Using `actions/setup-python` can be faster (~1s), because GitHub includes several Python versions in the runner image
|
|
which are available to get activated by `actions/setup-python` without having to download them.
|
|
|
|
For example:
|
|
|
|
```yaml
|
|
- name: Checkout the repository
|
|
uses: actions/checkout@main
|
|
- name: Install the latest version of uv
|
|
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
with:
|
|
enable-cache: true
|
|
- name: Test
|
|
run: uv run --frozen pytest # Uses the Python version automatically installed by uv
|
|
```
|
|
|
|
To install a specific version of Python, use
|
|
[`uv python install`](https://docs.astral.sh/uv/guides/install-python/):
|
|
|
|
```yaml
|
|
- name: Install the latest version of uv
|
|
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
with:
|
|
enable-cache: true
|
|
- name: Install Python 3.12
|
|
run: uv python install 3.12
|
|
```
|
|
|
|
### What is the default version?
|
|
|
|
By default, this action installs the latest version of uv.
|
|
|
|
If you require the installed version in subsequent steps of your workflow, use the `uv-version`
|
|
output:
|
|
|
|
```yaml
|
|
- name: Checkout the repository
|
|
uses: actions/checkout@main
|
|
- name: Install the default version of uv
|
|
id: setup-uv
|
|
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
- name: Print the installed version
|
|
run: echo "Installed uv version is ${{ steps.setup-uv.outputs.uv-version }}"
|
|
```
|
|
|
|
### Should I include the resolution strategy in the cache key?
|
|
|
|
**Yes!**
|
|
|
|
The cache key gets computed by using the cache-dependency-glob (see [Caching documentation](docs/caching.md)).
|
|
|
|
If you have jobs which use the same dependency definitions from `requirements.txt` or
|
|
`pyproject.toml` but different
|
|
[resolution strategies](https://docs.astral.sh/uv/concepts/resolution/#resolution-strategy),
|
|
each job will have different dependencies or dependency versions.
|
|
But if you do not add the resolution strategy as a cache-suffix (see [Caching documentation](docs/caching.md)),
|
|
they will have the same cache key.
|
|
|
|
This means the first job which starts uploading its cache will win and all other job will fail
|
|
uploading the cache,
|
|
because they try to upload with the same cache key.
|
|
|
|
You might see errors like
|
|
`Failed to save: Failed to CreateCacheEntry: Received non-retryable error: Failed request: (409) Conflict: cache entry with the same key, version, and scope already exists`
|
|
|
|
### Why do I see warnings like `No GitHub Actions cache found for key`
|
|
|
|
When a workflow runs for the first time on a branch and has a new cache key, because the
|
|
cache-dependency-glob (see [Caching documentation](docs/caching.md)) found changed files (changed dependencies),
|
|
the cache will not be found and the warning `No GitHub Actions cache found for key` will be printed.
|
|
|
|
While this might be irritating at first, it is expected behaviour and the cache will be created
|
|
and reused in later workflows.
|
|
|
|
The reason for the warning is that we have to way to know if this is the first run of a new
|
|
cache key or the user accidentally misconfigured the cache-dependency-glob
|
|
or cache-suffix (see [Caching documentation](docs/caching.md)) and the cache never gets used.
|
|
|
|
### Do I have to run `actions/checkout` before or after `setup-uv`?
|
|
|
|
Some workflows need uv but do not need to access the repository content.
|
|
|
|
But **if** you need to access the repository content, you have run `actions/checkout` before running `setup-uv`.
|
|
Running `actions/checkout` after `setup-uv` **is not supported**.
|
|
|
|
### Does `setup-uv` also install my project or its dependencies automatically?
|
|
|
|
No, `setup-uv` alone won't install any libraries from your `pyproject.toml` or `requirements.txt`, it only sets up `uv`.
|
|
You should run `uv sync` or `uv pip install .` separately, or use `uv run ...` to ensure necessary dependencies are installed.
|
|
|
|
### Why is a changed cache not detected and not the full cache uploaded?
|
|
|
|
When `setup-uv` starts it has to know whether it is better to download an existing cache
|
|
or start fresh and download every dependency again.
|
|
It does this by using a combination of hashes calculated on the contents of e.g. `uv.lock`.
|
|
|
|
By calculating these hashes and combining them in a key `setup-uv` can check
|
|
if an uploaded cache exists for this key.
|
|
If yes (e.g. contents of `uv.lock` did not change since last run) the dependencies in the cache
|
|
are up to date and the cache will be downloaded and used.
|
|
|
|
Details on determining which files will lead to different caches can be read in the
|
|
[Caching documentation](docs/caching.md).
|
|
|
|
Some dependencies will never be uploaded to the cache and will be downloaded again on each run
|
|
as described in the [Caching documentation](docs/caching.md).
|
|
|
|
## Acknowledgements
|
|
|
|
`setup-uv` was initially written and published by [Kevin Stillhammer](https://github.com/eifinger)
|
|
before moving under the official [Astral](https://github.com/astral-sh) GitHub organization. You can
|
|
support Kevin's work in open source on [Buy me a coffee](https://www.buymeacoffee.com/eifinger) or
|
|
[PayPal](https://paypal.me/kevinstillhammer).
|
|
|
|
## License
|
|
|
|
MIT
|
|
|
|
<div align="center">
|
|
<a target="_blank" href="https://astral.sh" style="background:none">
|
|
<img src="https://raw.githubusercontent.com/astral-sh/uv/main/assets/svg/Astral.svg" alt="Made by Astral">
|
|
</a>
|
|
</div>
|