2.7.4 在 GitHub Actions 中使用
原文链接: https://docs.astral.sh/uv/guides/integration/github/
2.7.4 在 GitHub Actions 中使用
安装
在 GitHub Actions 中使用时,我们推荐官方 astral-sh/setup-uv action:它会安装 uv、把它加入 PATH、(可选地)持久化缓存等,并支持 uv 支持的所有平台。
安装最新版本的 uv:
1
2
3
4
5
6
7
8
9
10
11
12
13
| # example.yml
name: Example
jobs:
uv-example:
name: python
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
最佳实践是固定到特定的 uv 版本,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # example.yml
name: Example
jobs:
uv-example:
name: python
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
# 安装指定版本的 uv。
version: "0.12.19"
|
配置 Python
可以用 python install 命令安装 Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # example.yml
name: Example
jobs:
uv-example:
name: python
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
- name: Set up Python
run: uv python install
|
这会遵循项目中固定的 Python 版本。
另一种方式是使用官方 GitHub setup-python action。它可能更快,因为 GitHub 会在 runner 旁边缓存 Python 版本。
设置 python-version-file 选项即可使用项目固定的版本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # example.yml
name: Example
jobs:
uv-example:
name: python
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: "Set up Python"
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version-file: ".python-version"
- name: Install uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
或者,指定 pyproject.toml 文件,以忽略固定版本并使用与项目 requires-python 约束兼容的最新版本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # example.yml
name: Example
jobs:
uv-example:
name: python
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: "Set up Python"
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version-file: "pyproject.toml"
- name: Install uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
多个 Python 版本
使用矩阵测试多个 Python 版本时,请用 astral-sh/setup-uv 设置 Python 版本,它会覆盖 pyproject.toml 或 .python-version 文件中的 Python 版本声明:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # example.yml
jobs:
build:
name: continuous-integration
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.10"
- "3.11"
- "3.12"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install uv and set the Python version
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
python-version: ${{ matrix.python-version }}
|
如果不使用 setup-uv action,可以设置 UV_PYTHON 环境变量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # example.yml
jobs:
build:
name: continuous-integration
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.10"
- "3.11"
- "3.12"
env:
UV_PYTHON: ${{ matrix.python-version }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
同步与运行
uv 和 Python 安装完成后,可以用 uv sync 安装项目,并用 uv run 在环境中运行命令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # example.yml
name: Example
jobs:
uv-example:
name: python
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
- name: Install the project
run: uv sync --locked --all-extras --dev
- name: Run tests
# 例如,使用 `pytest`
run: uv run pytest tests
|
提示
可以使用 UV_PROJECT_ENVIRONMENT 设置把包安装到系统 Python 环境,而不是创建虚拟环境。
缓存
在多次工作流运行之间保留 uv 缓存可以改善 CI 时间。
astral-sh/setup-uv 内置了持久化缓存的支持:
1
2
3
4
5
| # example.yml
- name: Enable caching
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
enable-cache: true
|
或者,你也可以用 actions/cache action 手动管理缓存:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| # example.yml
jobs:
install_job:
env:
# 为 uv 缓存配置固定位置
UV_CACHE_DIR: /tmp/.uv-cache
steps:
# ... 配置 Python 和 uv ...
- name: Restore uv cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: /tmp/.uv-cache
key: uv-${{ runner.os }}-${{ hashFiles('uv.lock') }}
restore-keys: |
uv-${{ runner.os }}-${{ hashFiles('uv.lock') }}
uv-${{ runner.os }}
# ... 安装包、运行测试等 ...
- name: Minimize uv cache
run: uv cache prune --ci
|
uv cache prune --ci 命令用于减小缓存体积,并针对 CI 做了优化。它对性能的影响取决于所安装的包。
提示
如果使用 uv pip,请在缓存键中使用 requirements.txt 而不是 uv.lock。
注意
使用非临时性的自托管 runner 时,默认缓存目录可能无限增长。这种情况下,在任务之间共享缓存可能并非最优。更好的做法是把缓存移到 GitHub 工作区内部,并在任务结束后用 Post Job Hook 删除它。
1
2
3
4
| install_job:
env:
# 为 uv 缓存配置相对位置
UV_CACHE_DIR: ${{ github.workspace }}/.cache/uv
|
使用 post job hook 需要把自托管 runner 上的 ACTIONS_RUNNER_HOOK_JOB_STARTED 环境变量设置为清理脚本的路径,例如下面这个:
1
2
3
| #!/usr/bin/env sh
# clean-uv-cache.sh
uv cache clean
|
使用 uv pip
如果使用 uv pip 接口而不是 uv 项目接口,uv 默认要求存在虚拟环境。要允许把包安装到系统环境,请在所有 uv 调用中加上 --system 标志,或设置 UV_SYSTEM_PYTHON 变量。
UV_SYSTEM_PYTHON 变量可以在不同作用域中定义。
在顶层定义即可对整个工作流启用:
1
2
3
4
5
| # example.yml
env:
UV_SYSTEM_PYTHON: 1
jobs: ...
|
或者对工作流中的特定任务启用:
1
2
3
4
5
6
| # example.yml
jobs:
install_job:
env:
UV_SYSTEM_PYTHON: 1
...
|
或者对任务中的特定步骤启用:
1
2
3
4
5
6
| # example.yml
steps:
- name: Install requirements
run: uv pip install -r requirements.txt
env:
UV_SYSTEM_PYTHON: 1
|
要重新关闭该行为,可以在任意 uv 调用中使用 --no-system 标志。
私有仓库
如果你的项目依赖私有 GitHub 仓库(参见依赖),你需要配置个人访问令牌(PAT),以便 uv 能够拉取它们。
创建一个对私有仓库有读取权限的 PAT 之后,把它添加为仓库 secret。
然后,你可以用 gh CLI(GitHub Actions runner 中默认已安装)配置 Git 凭据助手,以便访问 github.com 上托管的仓库时使用该 PAT。
例如,如果你的仓库 secret 名为 MY_PAT:
1
2
3
4
5
6
| # example.yml
steps:
- name: Register the personal access token
run: echo "${{ secrets.MY_PAT }}" | gh auth login --with-token
- name: Configure the Git credential helper
run: gh auth setup-git
|
发布到 PyPI
uv 可以用 GitHub Actions 把包构建并发布到 PyPI。我们在 astral-sh/trusted-publishing-examples 中提供了一个与本指南配套的独立示例。该工作流使用受信发布,因此无需配置任何凭据。
在该示例工作流中,我们用一段脚本测试源码分发和 wheel 是否都可用、有没有遗漏文件。这一步是推荐但可选的。
重要
该示例工作流使用两个独立的任务(build 和 publish),这样发布步骤(通过 id-token: write 拥有发布凭据的访问权限)就不会与构建步骤共享权限。这减少了供应链攻击面。
首先,为项目添加发布工作流:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
| # .github/workflows/release.yml
name: "Publish release to PyPI"
on:
push:
tags:
# 在版本标签上发布,例如 v0.1.0
- "v[0-9]+.[0-9]+.[0-9]+"
- "v[0-9]+.[0-9]+.[0-9]+rc[0-9]+"
- "v[0-9]+.[0-9]+.[0-9]+[ab][0-9]+"
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
enable-cache: false
- name: Build
run: uv build
# 可选但推荐:对分发包运行冒烟测试
- name: Smoke test (wheel)
run: uv run --isolated --no-project --with dist/*.whl tests/smoke_test.py
- name: Smoke test (source distribution)
run: uv run --isolated --no-project --with dist/*.tar.gz tests/smoke_test.py
- name: Upload distributions as artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: dist
path: dist/
publish:
needs:
- build
runs-on: ubuntu-latest
environment:
name: pypi
permissions:
id-token: write
steps:
- name: Install uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
enable-cache: false
- name: Download distributions artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: dist
path: dist/
- name: Generate PEP 740 attestations
uses: astral-sh/attest-action@f589a42a7efb6fe400b4f400de60b4bc90390027 # v0.0.6
- name: Publish
run: uv publish
|
然后,在 GitHub 仓库的 “Settings” -> “Environments” 下创建该工作流中定义的环境。

在 PyPI 项目设置的 “Publishing” 下为项目添加受信发布者。确保所有字段与你的 GitHub 配置一致。

保存之后:

最后,打一个发布标签并推送。确保标签以 v 开头,以匹配工作流中的模式。
1
2
| $ git tag -a v0.1.0 -m v0.1.0
$ git push --tags
|