Compare commits

...

8 commits
v8.0.0 ... main

Author SHA1 Message Date
Kevin Stillhammer
cdfb2ee6dd
Refactor version resolving (#852)
Some checks failed
test / test-setup-cache-save-cache-false (push) Has been cancelled
test / test-setup-cache-restore-cache-false (push) Has been cancelled
test / test-cache-local (map[expected-cache-dir:/home/runner/work/_temp/setup-uv-cache os:ubuntu-latest]) (push) Has been cancelled
test / test-cache-local (map[expected-cache-dir:D:\a\_temp\setup-uv-cache os:windows-latest]) (push) Has been cancelled
test / test-cache-local-cache-disabled (push) Has been cancelled
test / test-cache-local-cache-disabled-but-explicit-path (push) Has been cancelled
test / test-no-python-version (push) Has been cancelled
test / test-custom-manifest-file (push) Has been cancelled
test / test-absolute-path (push) Has been cancelled
test / test-relative-path (push) Has been cancelled
test / test-cache-prune-force (push) Has been cancelled
test / test-cache-dir-from-file (push) Has been cancelled
test / test-cache-python-missing-managed-install-dir (push) Has been cancelled
test / test-cache-python-installs (push) Has been cancelled
test / test-python-install-dir (map[expected-python-dir:/home/runner/work/_temp/uv-python-dir os:ubuntu-latest]) (push) Has been cancelled
test / test-python-install-dir (map[expected-python-dir:D:\a\_temp\uv-python-dir os:windows-latest]) (push) Has been cancelled
test / test-act (push) Has been cancelled
test / validate-typings (push) Has been cancelled
test / test-restore-cache (auto, ubuntu-latest) (push) Has been cancelled
test / test-restore-cache (auto, windows-latest) (push) Has been cancelled
test / test-restore-cache (false, ubuntu-latest) (push) Has been cancelled
test / test-restore-cache (false, windows-latest) (push) Has been cancelled
test / test-restore-cache (true, ubuntu-latest) (push) Has been cancelled
test / test-restore-cache (true, windows-latest) (push) Has been cancelled
test / test-restore-cache-requirements-txt (push) Has been cancelled
test / test-restore-cache-dependency-glob (push) Has been cancelled
test / test-restore-cache-save-cache-false (push) Has been cancelled
test / test-restore-cache-restore-cache-false (push) Has been cancelled
test / test-restore-python-installs (push) Has been cancelled
test / all-tests-passed (push) Has been cancelled
2026-04-11 11:38:41 +02:00
github-actions[bot]
cb84d12dc6
chore: update known checksums for 0.11.6 (#850)
Some checks are pending
test / test-restore-cache (false, ubuntu-latest) (push) Blocked by required conditions
test / test-restore-cache (false, windows-latest) (push) Blocked by required conditions
test / test-restore-cache-save-cache-false (push) Blocked by required conditions
test / test-restore-cache (true, ubuntu-latest) (push) Blocked by required conditions
test / test-restore-cache (true, windows-latest) (push) Blocked by required conditions
test / test-setup-cache-requirements-txt (push) Waiting to run
test / test-setup-cache-dependency-glob (push) Waiting to run
test / test-restore-cache-dependency-glob (push) Blocked by required conditions
test / test-setup-cache-save-cache-false (push) Waiting to run
test / test-setup-cache-restore-cache-false (push) Waiting to run
test / test-restore-cache-restore-cache-false (push) Blocked by required conditions
test / test-cache-local (map[expected-cache-dir:D:\a\_temp\setup-uv-cache os:windows-latest]) (push) Waiting to run
test / test-cache-local-cache-disabled (push) Waiting to run
test / test-no-python-version (push) Waiting to run
test / test-custom-manifest-file (push) Waiting to run
test / test-absolute-path (push) Waiting to run
test / test-act (push) Waiting to run
test / test-restore-cache-requirements-txt (push) Blocked by required conditions
test / test-cache-local (map[expected-cache-dir:/home/runner/work/_temp/setup-uv-cache os:ubuntu-latest]) (push) Waiting to run
test / test-cache-local-cache-disabled-but-explicit-path (push) Waiting to run
test / test-relative-path (push) Waiting to run
test / test-cache-prune-force (push) Waiting to run
test / test-cache-dir-from-file (push) Waiting to run
test / test-cache-python-missing-managed-install-dir (push) Waiting to run
test / test-cache-python-installs (push) Waiting to run
test / test-restore-python-installs (push) Blocked by required conditions
test / test-python-install-dir (map[expected-python-dir:/home/runner/work/_temp/uv-python-dir os:ubuntu-latest]) (push) Waiting to run
test / test-python-install-dir (map[expected-python-dir:D:\a\_temp\uv-python-dir os:windows-latest]) (push) Waiting to run
test / validate-typings (push) Waiting to run
test / all-tests-passed (push) Blocked by required conditions
chore: update known checksums for 0.11.6

Co-authored-by: eifinger <eifinger@users.noreply.github.com>
2026-04-10 13:32:57 +02:00
github-actions[bot]
1912cc65f2
chore: update known checksums for 0.11.5 (#845)
Some checks failed
test / test-setup-cache-save-cache-false (push) Has been cancelled
test / test-setup-cache-restore-cache-false (push) Has been cancelled
test / test-cache-local (map[expected-cache-dir:/home/runner/work/_temp/setup-uv-cache os:ubuntu-latest]) (push) Has been cancelled
test / test-cache-local (map[expected-cache-dir:D:\a\_temp\setup-uv-cache os:windows-latest]) (push) Has been cancelled
test / test-cache-local-cache-disabled (push) Has been cancelled
test / test-cache-local-cache-disabled-but-explicit-path (push) Has been cancelled
test / test-no-python-version (push) Has been cancelled
test / test-custom-manifest-file (push) Has been cancelled
test / test-absolute-path (push) Has been cancelled
test / test-relative-path (push) Has been cancelled
test / test-cache-prune-force (push) Has been cancelled
test / test-cache-dir-from-file (push) Has been cancelled
test / test-cache-python-missing-managed-install-dir (push) Has been cancelled
test / test-cache-python-installs (push) Has been cancelled
test / test-python-install-dir (map[expected-python-dir:/home/runner/work/_temp/uv-python-dir os:ubuntu-latest]) (push) Has been cancelled
test / test-python-install-dir (map[expected-python-dir:D:\a\_temp\uv-python-dir os:windows-latest]) (push) Has been cancelled
test / test-act (push) Has been cancelled
test / validate-typings (push) Has been cancelled
test / test-restore-cache (auto, ubuntu-latest) (push) Has been cancelled
test / test-restore-cache (auto, windows-latest) (push) Has been cancelled
test / test-restore-cache (false, ubuntu-latest) (push) Has been cancelled
test / test-restore-cache (false, windows-latest) (push) Has been cancelled
test / test-restore-cache (true, ubuntu-latest) (push) Has been cancelled
test / test-restore-cache (true, windows-latest) (push) Has been cancelled
test / test-restore-cache-requirements-txt (push) Has been cancelled
test / test-restore-cache-dependency-glob (push) Has been cancelled
test / test-restore-cache-save-cache-false (push) Has been cancelled
test / test-restore-cache-restore-cache-false (push) Has been cancelled
test / test-restore-python-installs (push) Has been cancelled
test / all-tests-passed (push) Has been cancelled
chore: update known checksums for 0.11.5

Co-authored-by: eifinger <eifinger@users.noreply.github.com>
2026-04-09 08:26:38 +02:00
github-actions[bot]
a0b52019f1
chore: update known checksums for 0.11.4 (#843)
Some checks are pending
test / test-custom-manifest-file (push) Waiting to run
test / test-restore-python-installs (push) Blocked by required conditions
test / test-python-install-dir (map[expected-python-dir:/home/runner/work/_temp/uv-python-dir os:ubuntu-latest]) (push) Waiting to run
test / validate-typings (push) Waiting to run
test / all-tests-passed (push) Blocked by required conditions
test / test-restore-cache (false, ubuntu-latest) (push) Blocked by required conditions
test / test-restore-cache (true, ubuntu-latest) (push) Blocked by required conditions
test / test-setup-cache-restore-cache-false (push) Waiting to run
test / test-restore-cache-restore-cache-false (push) Blocked by required conditions
test / test-cache-local-cache-disabled-but-explicit-path (push) Waiting to run
test / test-absolute-path (push) Waiting to run
test / test-relative-path (push) Waiting to run
test / test-cache-dir-from-file (push) Waiting to run
test / test-cache-python-installs (push) Waiting to run
test / test-python-install-dir (map[expected-python-dir:D:\a\_temp\uv-python-dir os:windows-latest]) (push) Waiting to run
test / test-restore-cache (auto, ubuntu-latest) (push) Blocked by required conditions
test / test-restore-cache (auto, windows-latest) (push) Blocked by required conditions
test / test-restore-cache-requirements-txt (push) Blocked by required conditions
test / test-restore-cache (false, windows-latest) (push) Blocked by required conditions
test / test-restore-cache (true, windows-latest) (push) Blocked by required conditions
test / test-restore-cache-dependency-glob (push) Blocked by required conditions
test / test-setup-cache-save-cache-false (push) Waiting to run
test / test-restore-cache-save-cache-false (push) Blocked by required conditions
test / test-cache-local (map[expected-cache-dir:/home/runner/work/_temp/setup-uv-cache os:ubuntu-latest]) (push) Waiting to run
test / test-cache-local (map[expected-cache-dir:D:\a\_temp\setup-uv-cache os:windows-latest]) (push) Waiting to run
test / test-cache-local-cache-disabled (push) Waiting to run
test / test-no-python-version (push) Waiting to run
test / test-cache-prune-force (push) Waiting to run
test / test-cache-python-missing-managed-install-dir (push) Waiting to run
test / test-act (push) Waiting to run
chore: update known checksums for 0.11.4

Co-authored-by: eifinger <eifinger@users.noreply.github.com>
2026-04-08 07:42:52 +02:00
Zanie Blue
7b222e12b6
Add a release workflow (#839)
Some checks are pending
test / test-custom-manifest-file (push) Waiting to run
test / test-absolute-path (push) Waiting to run
test / test-relative-path (push) Waiting to run
test / test-cache-prune-force (push) Waiting to run
test / test-cache-dir-from-file (push) Waiting to run
test / test-cache-python-missing-managed-install-dir (push) Waiting to run
test / test-cache-python-installs (push) Waiting to run
test / test-restore-python-installs (push) Blocked by required conditions
test / test-python-install-dir (map[expected-python-dir:/home/runner/work/_temp/uv-python-dir os:ubuntu-latest]) (push) Waiting to run
test / test-act (push) Waiting to run
test / validate-typings (push) Waiting to run
test / all-tests-passed (push) Blocked by required conditions
test / lint (push) Waiting to run
test / test-default-version (macos-14) (push) Waiting to run
test / test-default-version (macos-latest) (push) Waiting to run
test / test-default-version (ubuntu-latest) (push) Waiting to run
test / test-default-version (windows-latest) (push) Waiting to run
test / test-uv-no-modify-path (push) Waiting to run
test / test-specific-version (map[expected-version:0.1.0 resolution-strategy:lowest version-input:>=0.1.0,<0.2]) (push) Waiting to run
test / test-specific-version (map[expected-version:0.1.45 resolution-strategy:highest version-input:>=0.1,<0.2]) (push) Waiting to run
test / test-specific-version (map[expected-version:0.3.0 version-input:0.3.0]) (push) Waiting to run
test / test-specific-version (map[expected-version:0.3.2 version-input:0.3.2]) (push) Waiting to run
test / test-cache-local (map[expected-cache-dir:/home/runner/work/_temp/setup-uv-cache os:ubuntu-latest]) (push) Waiting to run
test / test-cache-local-cache-disabled-but-explicit-path (push) Waiting to run
test / test-specific-version (map[expected-version:0.3.5 version-input:0.3.x]) (push) Waiting to run
test / test-python-install-dir (map[expected-python-dir:D:\a\_temp\uv-python-dir os:windows-latest]) (push) Waiting to run
test / test-restore-cache-restore-cache-false (push) Blocked by required conditions
test / test-cache-local (map[expected-cache-dir:D:\a\_temp\setup-uv-cache os:windows-latest]) (push) Waiting to run
test / test-cache-local-cache-disabled (push) Waiting to run
test / test-no-python-version (push) Waiting to run
Uses a release workflow with environment protection for publishing
releases instead of relying on user invocation.

The `release` environment can then be protected, e.g., requiring
approval from another team member. We can add a tag ruleset to prevent
tags from being created outside of the `release` environment.

I've never used Release drafter, but the workflow here differs from our
other projects in that the release process just marks the draft release
as final and adds the tag. The draft release is required, for
simplicity.
2026-04-07 08:39:52 -05:00
github-actions[bot]
1c15d185f0
chore: update known checksums for 0.11.3 (#836)
Some checks failed
test / test-cache-key-os-version (ubuntu-24.04, ubuntu-24.04) (push) Has been cancelled
test / test-cache-key-os-version (windows-2022, windows-2022) (push) Has been cancelled
test / test-cache-key-os-version (windows-2025, windows-2025) (push) Has been cancelled
test / test-setup-cache (auto, ubuntu-latest) (push) Has been cancelled
test / test-setup-cache-requirements-txt (push) Has been cancelled
test / test-setup-cache-dependency-glob (push) Has been cancelled
test / test-setup-cache-save-cache-false (push) Has been cancelled
test / test-setup-cache-restore-cache-false (push) Has been cancelled
test / test-cache-local (map[expected-cache-dir:/home/runner/work/_temp/setup-uv-cache os:ubuntu-latest]) (push) Has been cancelled
test / test-relative-path (push) Has been cancelled
test / test-cache-prune-force (push) Has been cancelled
test / test-cache-dir-from-file (push) Has been cancelled
test / test-cache-python-missing-managed-install-dir (push) Has been cancelled
test / test-cache-python-installs (push) Has been cancelled
test / test-python-install-dir (map[expected-python-dir:/home/runner/work/_temp/uv-python-dir os:ubuntu-latest]) (push) Has been cancelled
test / test-python-install-dir (map[expected-python-dir:D:\a\_temp\uv-python-dir os:windows-latest]) (push) Has been cancelled
test / test-act (push) Has been cancelled
test / validate-typings (push) Has been cancelled
test / test-restore-cache (auto, ubuntu-latest) (push) Has been cancelled
test / test-restore-cache (auto, windows-latest) (push) Has been cancelled
test / test-restore-cache (false, ubuntu-latest) (push) Has been cancelled
test / test-restore-cache (false, windows-latest) (push) Has been cancelled
test / test-restore-cache (true, ubuntu-latest) (push) Has been cancelled
test / test-restore-cache (true, windows-latest) (push) Has been cancelled
test / test-restore-cache-requirements-txt (push) Has been cancelled
test / test-restore-cache-dependency-glob (push) Has been cancelled
test / test-restore-cache-save-cache-false (push) Has been cancelled
test / test-restore-cache-restore-cache-false (push) Has been cancelled
test / test-restore-python-installs (push) Has been cancelled
test / all-tests-passed (push) Has been cancelled
chore: update known checksums for 0.11.3

Co-authored-by: eifinger <eifinger@users.noreply.github.com>
2026-04-02 10:42:50 +02:00
Kevin Stillhammer
d7fe1a5a18
Update ignore-nothing-to-cache documentation (#833)
Some checks failed
test / test-setup-cache-save-cache-false (push) Has been cancelled
test / test-setup-cache-restore-cache-false (push) Has been cancelled
test / test-cache-local (map[expected-cache-dir:/home/runner/work/_temp/setup-uv-cache os:ubuntu-latest]) (push) Has been cancelled
test / test-cache-local (map[expected-cache-dir:D:\a\_temp\setup-uv-cache os:windows-latest]) (push) Has been cancelled
test / test-cache-local-cache-disabled (push) Has been cancelled
test / test-cache-local-cache-disabled-but-explicit-path (push) Has been cancelled
test / test-no-python-version (push) Has been cancelled
test / test-custom-manifest-file (push) Has been cancelled
test / test-absolute-path (push) Has been cancelled
test / test-relative-path (push) Has been cancelled
test / test-cache-prune-force (push) Has been cancelled
test / test-cache-dir-from-file (push) Has been cancelled
test / test-cache-python-missing-managed-install-dir (push) Has been cancelled
test / test-cache-python-installs (push) Has been cancelled
test / test-python-install-dir (map[expected-python-dir:/home/runner/work/_temp/uv-python-dir os:ubuntu-latest]) (push) Has been cancelled
test / test-python-install-dir (map[expected-python-dir:D:\a\_temp\uv-python-dir os:windows-latest]) (push) Has been cancelled
test / test-act (push) Has been cancelled
test / validate-typings (push) Has been cancelled
test / test-restore-cache (auto, ubuntu-latest) (push) Has been cancelled
test / test-restore-cache (auto, windows-latest) (push) Has been cancelled
test / test-restore-cache (false, ubuntu-latest) (push) Has been cancelled
test / test-restore-cache (false, windows-latest) (push) Has been cancelled
test / test-restore-cache (true, ubuntu-latest) (push) Has been cancelled
test / test-restore-cache (true, windows-latest) (push) Has been cancelled
test / test-restore-cache-requirements-txt (push) Has been cancelled
test / test-restore-cache-dependency-glob (push) Has been cancelled
test / test-restore-cache-save-cache-false (push) Has been cancelled
test / test-restore-cache-restore-cache-false (push) Has been cancelled
test / test-restore-python-installs (push) Has been cancelled
test / all-tests-passed (push) Has been cancelled
Add the error message so it can be found when searching for it

Helps issues like #831
2026-03-31 09:27:10 +02:00
Kevin Stillhammer
16592cddee
Pin setup-uv docs to v8 (#829)
Some checks failed
test / test-specific-version (map[expected-version:0.4.30 version-input:>=0.4.25,<0.5]) (push) Has been cancelled
test / test-latest-version (>=0.8) (push) Has been cancelled
test / test-latest-version (latest) (push) Has been cancelled
test / test-from-working-directory-version (map[expected-version:0.5.14 working-directory:__tests__/fixtures/pyproject-toml-project]) (push) Has been cancelled
test / test-from-working-directory-version (map[expected-version:0.5.15 working-directory:__tests__/fixtures/uv-toml-project]) (push) Has been cancelled
test / test-version-file-version (map[expected-version:0.5.15 version-file:__tests__/fixtures/.tool-versions]) (push) Has been cancelled
test / test-version-file-version (map[expected-version:0.6.17 version-file:__tests__/fixtures/uv-in-requirements-txt-project/requirements.txt]) (push) Has been cancelled
test / test-activate-environment (macos-latest) (push) Has been cancelled
test / test-activate-environment (ubuntu-latest) (push) Has been cancelled
test / test-activate-environment (windows-latest) (push) Has been cancelled
test / test-activate-environment-custom-path (macos-latest) (push) Has been cancelled
test / test-activate-environment-custom-path (ubuntu-latest) (push) Has been cancelled
test / test-setup-cache-requirements-txt (push) Has been cancelled
test / test-setup-cache-dependency-glob (push) Has been cancelled
test / test-python-install-dir (map[expected-python-dir:/home/runner/work/_temp/uv-python-dir os:ubuntu-latest]) (push) Has been cancelled
test / test-python-install-dir (map[expected-python-dir:D:\a\_temp\uv-python-dir os:windows-latest]) (push) Has been cancelled
test / test-act (push) Has been cancelled
test / validate-typings (push) Has been cancelled
test / test-restore-cache-dependency-glob (push) Has been cancelled
test / test-restore-cache-save-cache-false (push) Has been cancelled
test / test-restore-cache-restore-cache-false (push) Has been cancelled
test / test-restore-cache (auto, ubuntu-latest) (push) Has been cancelled
test / test-restore-cache (auto, windows-latest) (push) Has been cancelled
test / test-restore-cache (false, ubuntu-latest) (push) Has been cancelled
test / test-restore-cache (false, windows-latest) (push) Has been cancelled
test / test-restore-cache (true, ubuntu-latest) (push) Has been cancelled
test / test-restore-cache (true, windows-latest) (push) Has been cancelled
test / test-restore-cache-requirements-txt (push) Has been cancelled
test / test-restore-python-installs (push) Has been cancelled
test / all-tests-passed (push) Has been cancelled
Update all README and docs examples to use the pinned v8 release SHA for
astral-sh/setup-uv, with a comment showing the release version for
clarity and best practices.
2026-03-29 19:36:34 +02:00
24 changed files with 3623 additions and 2474 deletions

58
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,58 @@
name: Release
on:
workflow_dispatch:
inputs:
version:
description: "Release version (e.g., 8.1.0)"
required: true
type: string
permissions: {}
jobs:
release:
name: Release
runs-on: ubuntu-latest
environment: release
permissions:
contents: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Validate version
env:
VERSION: ${{ inputs.version }}
run: |
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
echo "::error::Version must match MAJOR.MINOR.PATCH (e.g., 8.1.0)"
exit 1
fi
- name: Publish release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: v${{ inputs.version }}
run: |
RELEASE_JSON=$(gh release view "$TAG" --json isDraft,targetCommitish 2>&1) || {
echo "::error::No release found for $TAG"
exit 1
}
IS_DRAFT=$(echo "$RELEASE_JSON" | jq -r '.isDraft')
TARGET=$(echo "$RELEASE_JSON" | jq -r '.targetCommitish')
if [[ "$IS_DRAFT" != "true" ]]; then
echo "::error::Release $TAG already exists and is not a draft"
exit 1
fi
if [[ "$TARGET" != "$GITHUB_SHA" ]]; then
echo "::error::Draft release target ($TARGET) does not match current commit ($GITHUB_SHA)"
exit 1
fi
echo "Publishing draft release $TAG"
gh release edit "$TAG" --draft=false

View file

@ -26,7 +26,7 @@ Set up your GitHub Actions workflow with a specific version of [uv](https://docs
```yaml
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
```
If you do not specify a version, this action will look for a [required-version](https://docs.astral.sh/uv/reference/settings/#required-version)
@ -42,7 +42,7 @@ Have a look under [Advanced Configuration](#advanced-configuration) for detailed
```yaml
- name: Install uv with all available options
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
# The version of uv to install (default: searches for version in config files, then latest)
version: ""
@ -139,7 +139,7 @@ This will override any python version specifications in `pyproject.toml` and `.p
```yaml
- name: Install the latest version of uv and set the python version to 3.13t
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
python-version: 3.13t
- run: uv pip install --python=3.13t pip
@ -157,7 +157,7 @@ jobs:
steps:
- uses: actions/checkout@v5
- name: Install the latest version of uv and set the python version
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
python-version: ${{ matrix.python-version }}
- name: Test with python ${{ matrix.python-version }}
@ -174,7 +174,7 @@ It also controls where [the venv gets created](#activate-environment), unless `v
```yaml
- name: Install uv based on the config files in the working-directory
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
working-directory: my/subproject/dir
```
@ -216,7 +216,7 @@ For example:
- name: Checkout the repository
uses: actions/checkout@main
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: true
- name: Test
@ -228,7 +228,7 @@ To install a specific version of Python, use
```yaml
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: true
- name: Install Python 3.12
@ -247,7 +247,7 @@ output:
uses: actions/checkout@main
- name: Install the default version of uv
id: setup-uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
- name: Print the installed version
run: echo "Installed uv version is ${{ steps.setup-uv.outputs.uv-version }}"
```

View file

@ -95,6 +95,35 @@ describe("download-version", () => {
expect(mockGetAllVersions).toHaveBeenCalledWith(undefined);
});
it("treats == exact pins as explicit versions", async () => {
const version = await resolveVersion("==0.9.26", undefined);
expect(version).toBe("0.9.26");
expect(mockGetAllVersions).not.toHaveBeenCalled();
expect(mockGetLatestVersion).not.toHaveBeenCalled();
});
it("uses latest for minimum-only ranges when using the highest strategy", async () => {
mockGetLatestVersion.mockResolvedValue("0.9.26");
const version = await resolveVersion(">=0.9.0", undefined, "highest");
expect(version).toBe("0.9.26");
expect(mockGetLatestVersion).toHaveBeenCalledTimes(1);
expect(mockGetLatestVersion).toHaveBeenCalledWith(undefined);
expect(mockGetAllVersions).not.toHaveBeenCalled();
});
it("uses the lowest compatible version when requested", async () => {
mockGetAllVersions.mockResolvedValue(["0.9.26", "0.9.25"]);
const version = await resolveVersion("^0.9.0", undefined, "lowest");
expect(version).toBe("0.9.25");
expect(mockGetAllVersions).toHaveBeenCalledTimes(1);
expect(mockGetAllVersions).toHaveBeenCalledWith(undefined);
});
it("uses manifest-file when provided", async () => {
mockGetAllVersions.mockResolvedValue(["0.9.26", "0.9.25"]);

View file

@ -1,5 +1,5 @@
import { expect, test } from "@jest/globals";
import { getUvVersionFromFile } from "../../src/version/resolve";
import { getUvVersionFromFile } from "../../src/version/file-parser";
test("ignores dependencies starting with uv", async () => {
const parsedVersion = getUvVersionFromFile(

View file

@ -1,5 +1,5 @@
import { expect, test } from "@jest/globals";
import { getUvVersionFromFile } from "../../src/version/resolve";
import { getUvVersionFromFile } from "../../src/version/file-parser";
test("ignores dependencies starting with uv", async () => {
const parsedVersion = getUvVersionFromFile(

View file

@ -0,0 +1,125 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "@jest/globals";
import { resolveVersionRequest } from "../../src/version/version-request-resolver";
const tempDirs: string[] = [];
function createTempProject(files: Record<string, string> = {}): string {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "setup-uv-version-test-"));
tempDirs.push(dir);
for (const [relativePath, content] of Object.entries(files)) {
const filePath = path.join(dir, relativePath);
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, content);
}
return dir;
}
afterEach(() => {
for (const dir of tempDirs.splice(0)) {
fs.rmSync(dir, { force: true, recursive: true });
}
});
describe("resolveVersionRequest", () => {
it("prefers explicit input over version-file and workspace config", () => {
const workingDirectory = createTempProject({
".tool-versions": "uv 0.4.0\n",
"pyproject.toml": `[tool.uv]\nrequired-version = "==0.5.14"\n`,
"uv.toml": `required-version = "==0.5.15"\n`,
});
const request = resolveVersionRequest({
version: "==0.6.0",
versionFile: path.join(workingDirectory, ".tool-versions"),
workingDirectory,
});
expect(request).toEqual({
source: "input",
specifier: "0.6.0",
});
});
it("uses .tool-versions when it is passed via version-file", () => {
const workingDirectory = createTempProject({
".tool-versions": "uv 0.5.15\n",
});
const request = resolveVersionRequest({
versionFile: path.join(workingDirectory, ".tool-versions"),
workingDirectory,
});
expect(request).toEqual({
format: ".tool-versions",
source: "version-file",
sourcePath: path.join(workingDirectory, ".tool-versions"),
specifier: "0.5.15",
});
});
it("uses requirements.txt when it is passed via version-file", () => {
const workingDirectory = createTempProject({
"requirements.txt": "uv==0.6.17\nuvicorn==0.35.0\n",
});
const request = resolveVersionRequest({
versionFile: path.join(workingDirectory, "requirements.txt"),
workingDirectory,
});
expect(request).toEqual({
format: "requirements",
source: "version-file",
sourcePath: path.join(workingDirectory, "requirements.txt"),
specifier: "0.6.17",
});
});
it("prefers uv.toml over pyproject.toml during workspace discovery", () => {
const workingDirectory = createTempProject({
"pyproject.toml": `[tool.uv]\nrequired-version = "==0.5.14"\n`,
"uv.toml": `required-version = "==0.5.15"\n`,
});
const request = resolveVersionRequest({ workingDirectory });
expect(request).toEqual({
format: "uv.toml",
source: "uv.toml",
sourcePath: path.join(workingDirectory, "uv.toml"),
specifier: "0.5.15",
});
});
it("falls back to latest when no version source is found", () => {
const workingDirectory = createTempProject({});
const request = resolveVersionRequest({ workingDirectory });
expect(request).toEqual({
source: "default",
specifier: "latest",
});
});
it("throws when version-file does not resolve a version", () => {
const workingDirectory = createTempProject({
"requirements.txt": "uvicorn==0.35.0\n",
});
expect(() =>
resolveVersionRequest({
versionFile: path.join(workingDirectory, "requirements.txt"),
workingDirectory,
}),
).toThrow(
`Could not determine uv version from file: ${path.join(workingDirectory, "requirements.txt")}`,
);
});
});

6
dist/save-cache/index.cjs generated vendored
View file

@ -62947,6 +62947,12 @@ function getConfigValueFromTomlFile(filePath, key) {
return void 0;
}
const fileContent = import_node_fs2.default.readFileSync(filePath, "utf-8");
return getConfigValueFromTomlContent(filePath, fileContent, key);
}
function getConfigValueFromTomlContent(filePath, fileContent, key) {
if (!filePath.endsWith(".toml")) {
return void 0;
}
if (filePath.endsWith("pyproject.toml")) {
const tomlContent2 = parse2(fileContent);
return tomlContent2?.tool?.uv?.[key];

4766
dist/setup/index.cjs generated vendored

File diff suppressed because it is too large Load diff

View file

@ -44949,6 +44949,78 @@ var semver = __toESM(require_semver(), 1);
// src/download/checksum/known-checksums.ts
var KNOWN_CHECKSUMS = {
"aarch64-apple-darwin-0.11.6": "4b69a4e366ec38cd5f305707de95e12951181c448679a00dce2a78868dfc9f5b",
"aarch64-pc-windows-msvc-0.11.6": "bee7b25a7a999f17291810242b47565c3ef2b9205651a0fd02a086f261a7e167",
"aarch64-unknown-linux-gnu-0.11.6": "d5be4bf7015ea000378cb3c3aba53ba81a8673458ace9c7fa25a0be005b74802",
"aarch64-unknown-linux-musl-0.11.6": "d14ebd6f200047264152daaf97b8bd36c7885a5033e9e8bba8366cb0049c0d00",
"arm-unknown-linux-musleabihf-0.11.6": "4410a9489e0a29ce8f86fc8604b75a3dd821e9e52734282cbb413b4e19c5c70a",
"armv7-unknown-linux-gnueabihf-0.11.6": "9758d49c200c211ccb2c9cbf43877102031c3457e80b6c3cb9da1e4c00119d2a",
"armv7-unknown-linux-musleabihf-0.11.6": "0677423d98cea5011d346d7d4a33a53360b99a51a04df4b45f67d43a8308c831",
"i686-pc-windows-msvc-0.11.6": "c5569da150166363389a719553d87f99e0c29e542b2c31bc8bd4aeeb8eb83d99",
"i686-unknown-linux-gnu-0.11.6": "b4bf8d78478b573c1816b17ec86da7ade14242cd68ac092c1701c5b4a75dc228",
"i686-unknown-linux-musl-0.11.6": "ca31705d93f48313d5ffdc23da165e680c6c5389d9a2cc62b85a1ed495e0331f",
"powerpc64le-unknown-linux-gnu-0.11.6": "153397d3d82e45e68fb1f4a40ee9898245ec8ed86fd03fcaacaf6e793316acf7",
"riscv64gc-unknown-linux-gnu-0.11.6": "0e3ead8667b51b07b5fb9d114bcd1914a5fe3159e6959a584dc2f89c6724e123",
"riscv64gc-unknown-linux-musl-0.11.6": "87d5932bffef3b7b9cba4a2a042f95edf75cd34555fc80cfa98cc5a4426635f9",
"s390x-unknown-linux-gnu-0.11.6": "6e3d4338da2db2c63326721f1eb3b4f32d9bde24aeff11208d397e1aeba8678e",
"x86_64-apple-darwin-0.11.6": "8e0ed5035eaa28c7c8cd2a46b5b9a05bfff1ef01dbdc090a010eb8fdf193a457",
"x86_64-pc-windows-msvc-0.11.6": "99aa60edd017a256dbf378f372d1cff3292dbc6696e0ea01716d9158d773ab77",
"x86_64-unknown-linux-gnu-0.11.6": "0c6bab77a67a445dc849ed5e8ee8d3cb333b6e2eba863643ce1e228075f27943",
"x86_64-unknown-linux-musl-0.11.6": "aa342a53abe42364093506d7704214d2cdca30b916843e520bc67759a5d20132",
"aarch64-apple-darwin-0.11.5": "470993e87503874c7c48861daa308b48a7c367e117235bbecf19368b9fdd35b2",
"aarch64-pc-windows-msvc-0.11.5": "9b9b99a985cccf249225aaad76412823e9d9736d605dc2252151172a7f6ab3db",
"aarch64-unknown-linux-gnu-0.11.5": "3e9b525d686ae4f3682412bce21536366a5c79616a41055530319c501c883169",
"aarch64-unknown-linux-musl-0.11.5": "d73860013061c62d6a89f3370527d4c407214038af331147773ae2fd8f6394c1",
"arm-unknown-linux-musleabihf-0.11.5": "dcfb4dc15f46eae90ac6d64e7dfc91d8bc0b16816f53b9f8d58ccc8a1220dbb8",
"armv7-unknown-linux-gnueabihf-0.11.5": "818d86386fb57ca4182f39df25dd6160e97300d5ba362bc44e25d8adc904776c",
"armv7-unknown-linux-musleabihf-0.11.5": "2cae8baae2c1b42249e656e16f5fe733189b0760ee93995be024f9cc5e72eb19",
"i686-pc-windows-msvc-0.11.5": "2057ccf3dba9ed23755df92318a08ab221e9e088385c667292acc09d9cc477c6",
"i686-unknown-linux-gnu-0.11.5": "2d340e2e5b3354ee7208bb8f2bbf4d2347d7ffdf2af733c21bee98746e34076d",
"i686-unknown-linux-musl-0.11.5": "ffe2bc9e0c4fdc18f69b7c5bc016a03fa17028d42620ab2b024ad5bb22cd3f3d",
"powerpc64le-unknown-linux-gnu-0.11.5": "c4dabaaa36a13989ab04389263064ca5c27093eb2e7c851ab62d50b6312d9800",
"riscv64gc-unknown-linux-gnu-0.11.5": "6ae3ec3cf1aab72604bc6aa8486faf4b473066422c49d9c42ea8366ff3039de4",
"riscv64gc-unknown-linux-musl-0.11.5": "d4686fb144563a40e791fc3f010a91e57fdce9cac7a03b8a14a972c25be4464c",
"s390x-unknown-linux-gnu-0.11.5": "1309f1e462462dab2da6a55c37012a228d1c06a55c5b43f8ef901ba1599d9e12",
"x86_64-apple-darwin-0.11.5": "b8964bed538143f9016d807e421e28f0237a29589851fc79e8159751ac64779a",
"x86_64-pc-windows-msvc-0.11.5": "3fa5b6ea9de9256a035e0471f5ef0bb5d95344659723d6eb063e27c76431515d",
"x86_64-unknown-linux-gnu-0.11.5": "0d87793f733f327849ebf9cf51b576cfb08328e22af73061405e4bec96ae84d1",
"x86_64-unknown-linux-musl-0.11.5": "ee8a52743ce3979e52872b49c5e58ffa541048cb95132142bff23fe5608d73ea",
"aarch64-apple-darwin-0.11.4": "9b9cb6c6f58c3246dbf3351ed4e97c500bc3266f5f237d2fd620b66e1c31dc56",
"aarch64-pc-windows-msvc-0.11.4": "708b1c210109e50ff520bcd9b6d29cbd8cee584bb55e84d3d1941bf75ab0893d",
"aarch64-unknown-linux-gnu-0.11.4": "f5aa91bba0b98d85a4e5262e2847f9ab2273c754f6374dff62b37ef18c65a2e7",
"aarch64-unknown-linux-musl-0.11.4": "a02ec7667d7bb1d33cdb7e1de22f7e4242967e3df7e350bac6212515e3bce8ac",
"arm-unknown-linux-musleabihf-0.11.4": "5bbc59d8c3d5fdade88fca47e4c18298e44a367e178e97e11466b22e992edae2",
"armv7-unknown-linux-gnueabihf-0.11.4": "9d2299155b65988643a55777c638408a0df8e65f606933d1e44691ada72ff106",
"armv7-unknown-linux-musleabihf-0.11.4": "43b1e02f8f4b27fd1d085fb14a246638bb607af32408cb13c5c3b3fb47db027f",
"i686-pc-windows-msvc-0.11.4": "661588b3607e6d5bb78551f596772a0d04a930ce128189c90800d07f6fca1998",
"i686-unknown-linux-gnu-0.11.4": "4248773a2574c3b697588655d7bf14f97baa744c3e156585230e5c711befa6ff",
"i686-unknown-linux-musl-0.11.4": "0323c08c1e7455cdf65c89296eda28bad9051cb09d16ea3ce1d0bf718143449e",
"powerpc64le-unknown-linux-gnu-0.11.4": "3ddb764538a5dcb4967d7375fde193ce5391e37ddd4d1242012d04cf3848479f",
"riscv64gc-unknown-linux-gnu-0.11.4": "93db93607a824d677c47003ee828936913cfdeb2c871bb34cd79c3ec4481e2b1",
"riscv64gc-unknown-linux-musl-0.11.4": "78f0d7f92244ce3d7a7a0df5fab2495450bcb18600b59acf1755e77cafed2300",
"s390x-unknown-linux-gnu-0.11.4": "07361e1fb32e870841a27d3d7b0b20c4a81e0cc25eeb8b9115425bfd227d2d05",
"x86_64-apple-darwin-0.11.4": "c326edaf3fd492f53d1c58777f3459c0d87bf9dae8d89e80aec4b0da6622dcf3",
"x86_64-pc-windows-msvc-0.11.4": "26d84455a40b0272b2ab4785cad298ff2c89cd0765b482e9f85b5a1bd880a863",
"x86_64-unknown-linux-gnu-0.11.4": "12f9a192bb32d70470aa22cbd2a193d1323a3f58f6ac5f9e3866aaca760c98c6",
"x86_64-unknown-linux-musl-0.11.4": "36ce1c5d8997db9b6a24d0f41646d5509b6d1d8b9448c7325f8248a6ea5d4b00",
"aarch64-apple-darwin-0.11.3": "2bc3d0c7bf2bd08325b1e170abac6f7e5b3346e1d4eab3370d17cefec934996f",
"aarch64-pc-windows-msvc-0.11.3": "e99c56f9ab5e1e1ddcaea3e2389990c94baf38e0d7cb2148de08baf2d3261d49",
"aarch64-unknown-linux-gnu-0.11.3": "711382e3158433f06b11d99afb440f4416359fc3c84558886d8ed8826a921bff",
"aarch64-unknown-linux-musl-0.11.3": "8ecec82cb9a744d5fabff6d16d7777218a7730f699d2aa0d2f751c17858e2efa",
"arm-unknown-linux-musleabihf-0.11.3": "3d021046a94ad11f12b9d83f36442a1a28e92e7149c3f79ba2951c96653dafac",
"armv7-unknown-linux-gnueabihf-0.11.3": "13c9a0f5f624275ccd36db2896607f4fee3585f420734b16f6c66d70e32aa458",
"armv7-unknown-linux-musleabihf-0.11.3": "260a88e2f00daab0363a745fde036a7881002d7a81094388f31925acb284110b",
"i686-pc-windows-msvc-0.11.3": "036fa39fa5ea3cb86c127324924b913b5858e8d91c4cb413edacfc3123001696",
"i686-unknown-linux-gnu-0.11.3": "b9410c8dae2fa0d4939af5b0ee7272d5591bd55890e8274dcf7f1aea84bfe043",
"i686-unknown-linux-musl-0.11.3": "afe533fd409105e753d844490c65a4375e75bfb3812e49122684f996bed9e90a",
"powerpc64le-unknown-linux-gnu-0.11.3": "5cdcadf4d50a5354312bc8ef37c2a6cfab4e2f13ccdf8380d3012b927b4ded95",
"riscv64gc-unknown-linux-gnu-0.11.3": "8271e07ed9695870f4b0ae5ec722e3ae08fff280068f08bc6a8ca76c67d7fefa",
"riscv64gc-unknown-linux-musl-0.11.3": "b750fc8393ced9939448849b05e94de6bf1e998bb7030c4ebe744b47b372bce9",
"s390x-unknown-linux-gnu-0.11.3": "6dc4f555a5f6515f7fddb281422d2a8a3943853dae5de837bbb5d996d7576c71",
"x86_64-apple-darwin-0.11.3": "b0e05e0b43a000fdc2132ee3f3400ba5dee427bc2337d3ec4eb8cf4f3d5722af",
"x86_64-pc-windows-msvc-0.11.3": "ae681c0aaec7cc96af184648cb88d73f8393ed60fa5880abdd6bdb910f9b227c",
"x86_64-unknown-linux-gnu-0.11.3": "c0f3236f146e55472663cfbcc9be3042a9f1092275bbe3fe2a56a6cbfd3da5ce",
"x86_64-unknown-linux-musl-0.11.3": "8b40cf16b849634b81a530a3d0a0bcae5f24996ef9ae782976fd69b6266d3b8e",
"aarch64-apple-darwin-0.11.2": "4beaa9550f93ef7f0fc02f7c28c9c48cd61fe30db00f5ac8947e0a425c3fb282",
"aarch64-pc-windows-msvc-0.11.2": "ffdded8338205f53727b51d404563a5ac8eaa9aea53279a7b7c42177e11d478c",
"aarch64-unknown-linux-gnu-0.11.2": "04792cac761c4a6ba78267f36f2af541b7f92196d42ac55d21d3ff6b0f5ab6a5",

View file

@ -6,7 +6,7 @@ This document covers advanced options for configuring which version of uv to ins
```yaml
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
version: "latest"
```
@ -15,7 +15,7 @@ This document covers advanced options for configuring which version of uv to ins
```yaml
- name: Install a specific version of uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
version: "0.4.4"
```
@ -28,21 +28,21 @@ to install the latest version that satisfies the range.
```yaml
- name: Install a semver range of uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
version: ">=0.4.0"
```
```yaml
- name: Pinning a minor version of uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
version: "0.4.x"
```
```yaml
- name: Install a pep440-specifier-satisfying version of uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
version: ">=0.4.25,<0.5"
```
@ -54,7 +54,7 @@ You can change this behavior using the `resolution-strategy` input:
```yaml
- name: Install the lowest compatible version of uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
version: ">=0.4.0"
resolution-strategy: "lowest"
@ -76,7 +76,7 @@ uv defined as a dependency in `pyproject.toml` or `requirements.txt`.
```yaml
- name: Install uv based on the version defined in pyproject.toml
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
version-file: "pyproject.toml"
```

View file

@ -23,7 +23,7 @@ The computed cache key is available as the `cache-key` output:
```yaml
- name: Setup uv
id: setup-uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: true
- name: Print cache key
@ -50,7 +50,7 @@ You can optionally define a custom cache key suffix.
```yaml
- name: Enable caching and define a custom cache key suffix
id: setup-uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: true
cache-suffix: "optional-suffix"
@ -89,7 +89,7 @@ changes. If you use relative paths, they are relative to the working directory.
```yaml
- name: Define a cache dependency glob
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: true
cache-dependency-glob: "**/pyproject.toml"
@ -97,7 +97,7 @@ changes. If you use relative paths, they are relative to the working directory.
```yaml
- name: Define a list of cache dependency globs
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: true
cache-dependency-glob: |
@ -107,7 +107,7 @@ changes. If you use relative paths, they are relative to the working directory.
```yaml
- name: Define an absolute cache dependency glob
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: true
cache-dependency-glob: "/tmp/my-folder/requirements*.txt"
@ -115,7 +115,7 @@ changes. If you use relative paths, they are relative to the working directory.
```yaml
- name: Never invalidate the cache
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: true
cache-dependency-glob: ""
@ -128,7 +128,7 @@ By default, the cache will be restored.
```yaml
- name: Don't restore an existing cache
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: true
restore-cache: false
@ -142,7 +142,7 @@ By default, the cache will be saved.
```yaml
- name: Don't save the cache after the run
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: true
save-cache: false
@ -168,7 +168,7 @@ It defaults to `setup-uv-cache` in the `TMP` dir, `D:\a\_temp\setup-uv-cache` on
```yaml
- name: Define a custom uv cache path
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
cache-local-path: "/path/to/cache"
```
@ -187,7 +187,7 @@ input.
```yaml
- name: Don't prune the cache before saving it
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: true
prune-cache: false
@ -205,7 +205,7 @@ To force managed Python installs, set `UV_PYTHON_PREFERENCE=only-managed`.
```yaml
- name: Cache Python installs
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: true
cache-python: true
@ -213,12 +213,17 @@ To force managed Python installs, set `UV_PYTHON_PREFERENCE=only-managed`.
## Ignore nothing to cache
By default, the action will fail if caching is enabled but there is nothing to upload (the uv cache directory does not exist).
By default, the action will fail if caching is enabled but there is nothing to upload (the uv cache directory does not exist) with an error like
```console
Error: Cache path /home/runner/.cache/uv does not exist on disk. This likely indicates that there are no dependencies to cache. Consider disabling the cache input if it is not needed.
```
If you want to ignore this, set the `ignore-nothing-to-cache` input to `true`.
```yaml
- name: Ignore nothing to cache
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: true
ignore-nothing-to-cache: true

View file

@ -10,7 +10,7 @@ are automatically verified by this action. The sha256 hashes can be found on the
```yaml
- name: Install a specific version and validate the checksum
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
version: "0.3.1"
checksum: "e11b01402ab645392c7ad6044db63d37e4fd1e745e015306993b07695ea5f9f8"
@ -39,7 +39,7 @@ The `archive_format` field is currently ignored.
```yaml
- name: Use a custom manifest file
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
manifest-file: "https://example.com/my-custom-manifest.ndjson"
```
@ -58,7 +58,7 @@ You can disable this by setting the `add-problem-matchers` input to `false`.
```yaml
- name: Install the latest version of uv without problem matchers
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
add-problem-matchers: false
```

View file

@ -9,7 +9,7 @@ This allows directly using it in later steps:
```yaml
- name: Install the latest version of uv and activate the environment
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
activate-environment: true
- run: uv pip install pip
@ -20,7 +20,7 @@ By default, the venv is created at `.venv` inside the `working-directory`.
You can customize the venv location with `venv-path`, for example to place it in the runner temp directory:
```yaml
- uses: astral-sh/setup-uv@v7
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
activate-environment: true
venv-path: ${{ runner.temp }}/custom-venv
@ -51,7 +51,7 @@ are not sufficient, you can provide a custom GitHub token with the necessary per
```yaml
- name: Install the latest version of uv with a custom GitHub token
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
github-token: ${{ secrets.CUSTOM_GITHUB_TOKEN }}
```
@ -69,7 +69,7 @@ input:
```yaml
- name: Install the latest version of uv with a custom tool dir
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
tool-dir: "/path/to/tool/dir"
```
@ -88,7 +88,7 @@ If you want to change this behaviour (especially on self-hosted runners) you can
```yaml
- name: Install the latest version of uv with a custom tool bin dir
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
tool-bin-dir: "/path/to/tool-bin/dir"
```
@ -105,7 +105,7 @@ This action supports expanding the `~` character to the user's home directory fo
```yaml
- name: Expand the tilde character
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
cache-local-path: "~/path/to/cache"
tool-dir: "~/path/to/tool/dir"
@ -122,7 +122,7 @@ If you want to ignore this, set the `ignore-empty-workdir` input to `true`.
```yaml
- name: Ignore empty workdir
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
ignore-empty-workdir: true
```
@ -145,7 +145,7 @@ This action sets several environment variables that influence uv's behavior and
```yaml
- name: Example using environment variables
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
python-version: "3.12"
tool-dir: "/custom/tool/dir"

View file

@ -1,5 +1,149 @@
// AUTOGENERATED_DO_NOT_EDIT
export const KNOWN_CHECKSUMS: { [key: string]: string } = {
"aarch64-apple-darwin-0.11.6":
"4b69a4e366ec38cd5f305707de95e12951181c448679a00dce2a78868dfc9f5b",
"aarch64-pc-windows-msvc-0.11.6":
"bee7b25a7a999f17291810242b47565c3ef2b9205651a0fd02a086f261a7e167",
"aarch64-unknown-linux-gnu-0.11.6":
"d5be4bf7015ea000378cb3c3aba53ba81a8673458ace9c7fa25a0be005b74802",
"aarch64-unknown-linux-musl-0.11.6":
"d14ebd6f200047264152daaf97b8bd36c7885a5033e9e8bba8366cb0049c0d00",
"arm-unknown-linux-musleabihf-0.11.6":
"4410a9489e0a29ce8f86fc8604b75a3dd821e9e52734282cbb413b4e19c5c70a",
"armv7-unknown-linux-gnueabihf-0.11.6":
"9758d49c200c211ccb2c9cbf43877102031c3457e80b6c3cb9da1e4c00119d2a",
"armv7-unknown-linux-musleabihf-0.11.6":
"0677423d98cea5011d346d7d4a33a53360b99a51a04df4b45f67d43a8308c831",
"i686-pc-windows-msvc-0.11.6":
"c5569da150166363389a719553d87f99e0c29e542b2c31bc8bd4aeeb8eb83d99",
"i686-unknown-linux-gnu-0.11.6":
"b4bf8d78478b573c1816b17ec86da7ade14242cd68ac092c1701c5b4a75dc228",
"i686-unknown-linux-musl-0.11.6":
"ca31705d93f48313d5ffdc23da165e680c6c5389d9a2cc62b85a1ed495e0331f",
"powerpc64le-unknown-linux-gnu-0.11.6":
"153397d3d82e45e68fb1f4a40ee9898245ec8ed86fd03fcaacaf6e793316acf7",
"riscv64gc-unknown-linux-gnu-0.11.6":
"0e3ead8667b51b07b5fb9d114bcd1914a5fe3159e6959a584dc2f89c6724e123",
"riscv64gc-unknown-linux-musl-0.11.6":
"87d5932bffef3b7b9cba4a2a042f95edf75cd34555fc80cfa98cc5a4426635f9",
"s390x-unknown-linux-gnu-0.11.6":
"6e3d4338da2db2c63326721f1eb3b4f32d9bde24aeff11208d397e1aeba8678e",
"x86_64-apple-darwin-0.11.6":
"8e0ed5035eaa28c7c8cd2a46b5b9a05bfff1ef01dbdc090a010eb8fdf193a457",
"x86_64-pc-windows-msvc-0.11.6":
"99aa60edd017a256dbf378f372d1cff3292dbc6696e0ea01716d9158d773ab77",
"x86_64-unknown-linux-gnu-0.11.6":
"0c6bab77a67a445dc849ed5e8ee8d3cb333b6e2eba863643ce1e228075f27943",
"x86_64-unknown-linux-musl-0.11.6":
"aa342a53abe42364093506d7704214d2cdca30b916843e520bc67759a5d20132",
"aarch64-apple-darwin-0.11.5":
"470993e87503874c7c48861daa308b48a7c367e117235bbecf19368b9fdd35b2",
"aarch64-pc-windows-msvc-0.11.5":
"9b9b99a985cccf249225aaad76412823e9d9736d605dc2252151172a7f6ab3db",
"aarch64-unknown-linux-gnu-0.11.5":
"3e9b525d686ae4f3682412bce21536366a5c79616a41055530319c501c883169",
"aarch64-unknown-linux-musl-0.11.5":
"d73860013061c62d6a89f3370527d4c407214038af331147773ae2fd8f6394c1",
"arm-unknown-linux-musleabihf-0.11.5":
"dcfb4dc15f46eae90ac6d64e7dfc91d8bc0b16816f53b9f8d58ccc8a1220dbb8",
"armv7-unknown-linux-gnueabihf-0.11.5":
"818d86386fb57ca4182f39df25dd6160e97300d5ba362bc44e25d8adc904776c",
"armv7-unknown-linux-musleabihf-0.11.5":
"2cae8baae2c1b42249e656e16f5fe733189b0760ee93995be024f9cc5e72eb19",
"i686-pc-windows-msvc-0.11.5":
"2057ccf3dba9ed23755df92318a08ab221e9e088385c667292acc09d9cc477c6",
"i686-unknown-linux-gnu-0.11.5":
"2d340e2e5b3354ee7208bb8f2bbf4d2347d7ffdf2af733c21bee98746e34076d",
"i686-unknown-linux-musl-0.11.5":
"ffe2bc9e0c4fdc18f69b7c5bc016a03fa17028d42620ab2b024ad5bb22cd3f3d",
"powerpc64le-unknown-linux-gnu-0.11.5":
"c4dabaaa36a13989ab04389263064ca5c27093eb2e7c851ab62d50b6312d9800",
"riscv64gc-unknown-linux-gnu-0.11.5":
"6ae3ec3cf1aab72604bc6aa8486faf4b473066422c49d9c42ea8366ff3039de4",
"riscv64gc-unknown-linux-musl-0.11.5":
"d4686fb144563a40e791fc3f010a91e57fdce9cac7a03b8a14a972c25be4464c",
"s390x-unknown-linux-gnu-0.11.5":
"1309f1e462462dab2da6a55c37012a228d1c06a55c5b43f8ef901ba1599d9e12",
"x86_64-apple-darwin-0.11.5":
"b8964bed538143f9016d807e421e28f0237a29589851fc79e8159751ac64779a",
"x86_64-pc-windows-msvc-0.11.5":
"3fa5b6ea9de9256a035e0471f5ef0bb5d95344659723d6eb063e27c76431515d",
"x86_64-unknown-linux-gnu-0.11.5":
"0d87793f733f327849ebf9cf51b576cfb08328e22af73061405e4bec96ae84d1",
"x86_64-unknown-linux-musl-0.11.5":
"ee8a52743ce3979e52872b49c5e58ffa541048cb95132142bff23fe5608d73ea",
"aarch64-apple-darwin-0.11.4":
"9b9cb6c6f58c3246dbf3351ed4e97c500bc3266f5f237d2fd620b66e1c31dc56",
"aarch64-pc-windows-msvc-0.11.4":
"708b1c210109e50ff520bcd9b6d29cbd8cee584bb55e84d3d1941bf75ab0893d",
"aarch64-unknown-linux-gnu-0.11.4":
"f5aa91bba0b98d85a4e5262e2847f9ab2273c754f6374dff62b37ef18c65a2e7",
"aarch64-unknown-linux-musl-0.11.4":
"a02ec7667d7bb1d33cdb7e1de22f7e4242967e3df7e350bac6212515e3bce8ac",
"arm-unknown-linux-musleabihf-0.11.4":
"5bbc59d8c3d5fdade88fca47e4c18298e44a367e178e97e11466b22e992edae2",
"armv7-unknown-linux-gnueabihf-0.11.4":
"9d2299155b65988643a55777c638408a0df8e65f606933d1e44691ada72ff106",
"armv7-unknown-linux-musleabihf-0.11.4":
"43b1e02f8f4b27fd1d085fb14a246638bb607af32408cb13c5c3b3fb47db027f",
"i686-pc-windows-msvc-0.11.4":
"661588b3607e6d5bb78551f596772a0d04a930ce128189c90800d07f6fca1998",
"i686-unknown-linux-gnu-0.11.4":
"4248773a2574c3b697588655d7bf14f97baa744c3e156585230e5c711befa6ff",
"i686-unknown-linux-musl-0.11.4":
"0323c08c1e7455cdf65c89296eda28bad9051cb09d16ea3ce1d0bf718143449e",
"powerpc64le-unknown-linux-gnu-0.11.4":
"3ddb764538a5dcb4967d7375fde193ce5391e37ddd4d1242012d04cf3848479f",
"riscv64gc-unknown-linux-gnu-0.11.4":
"93db93607a824d677c47003ee828936913cfdeb2c871bb34cd79c3ec4481e2b1",
"riscv64gc-unknown-linux-musl-0.11.4":
"78f0d7f92244ce3d7a7a0df5fab2495450bcb18600b59acf1755e77cafed2300",
"s390x-unknown-linux-gnu-0.11.4":
"07361e1fb32e870841a27d3d7b0b20c4a81e0cc25eeb8b9115425bfd227d2d05",
"x86_64-apple-darwin-0.11.4":
"c326edaf3fd492f53d1c58777f3459c0d87bf9dae8d89e80aec4b0da6622dcf3",
"x86_64-pc-windows-msvc-0.11.4":
"26d84455a40b0272b2ab4785cad298ff2c89cd0765b482e9f85b5a1bd880a863",
"x86_64-unknown-linux-gnu-0.11.4":
"12f9a192bb32d70470aa22cbd2a193d1323a3f58f6ac5f9e3866aaca760c98c6",
"x86_64-unknown-linux-musl-0.11.4":
"36ce1c5d8997db9b6a24d0f41646d5509b6d1d8b9448c7325f8248a6ea5d4b00",
"aarch64-apple-darwin-0.11.3":
"2bc3d0c7bf2bd08325b1e170abac6f7e5b3346e1d4eab3370d17cefec934996f",
"aarch64-pc-windows-msvc-0.11.3":
"e99c56f9ab5e1e1ddcaea3e2389990c94baf38e0d7cb2148de08baf2d3261d49",
"aarch64-unknown-linux-gnu-0.11.3":
"711382e3158433f06b11d99afb440f4416359fc3c84558886d8ed8826a921bff",
"aarch64-unknown-linux-musl-0.11.3":
"8ecec82cb9a744d5fabff6d16d7777218a7730f699d2aa0d2f751c17858e2efa",
"arm-unknown-linux-musleabihf-0.11.3":
"3d021046a94ad11f12b9d83f36442a1a28e92e7149c3f79ba2951c96653dafac",
"armv7-unknown-linux-gnueabihf-0.11.3":
"13c9a0f5f624275ccd36db2896607f4fee3585f420734b16f6c66d70e32aa458",
"armv7-unknown-linux-musleabihf-0.11.3":
"260a88e2f00daab0363a745fde036a7881002d7a81094388f31925acb284110b",
"i686-pc-windows-msvc-0.11.3":
"036fa39fa5ea3cb86c127324924b913b5858e8d91c4cb413edacfc3123001696",
"i686-unknown-linux-gnu-0.11.3":
"b9410c8dae2fa0d4939af5b0ee7272d5591bd55890e8274dcf7f1aea84bfe043",
"i686-unknown-linux-musl-0.11.3":
"afe533fd409105e753d844490c65a4375e75bfb3812e49122684f996bed9e90a",
"powerpc64le-unknown-linux-gnu-0.11.3":
"5cdcadf4d50a5354312bc8ef37c2a6cfab4e2f13ccdf8380d3012b927b4ded95",
"riscv64gc-unknown-linux-gnu-0.11.3":
"8271e07ed9695870f4b0ae5ec722e3ae08fff280068f08bc6a8ca76c67d7fefa",
"riscv64gc-unknown-linux-musl-0.11.3":
"b750fc8393ced9939448849b05e94de6bf1e998bb7030c4ebe744b47b372bce9",
"s390x-unknown-linux-gnu-0.11.3":
"6dc4f555a5f6515f7fddb281422d2a8a3943853dae5de837bbb5d996d7576c71",
"x86_64-apple-darwin-0.11.3":
"b0e05e0b43a000fdc2132ee3f3400ba5dee427bc2337d3ec4eb8cf4f3d5722af",
"x86_64-pc-windows-msvc-0.11.3":
"ae681c0aaec7cc96af184648cb88d73f8393ed60fa5880abdd6bdb910f9b227c",
"x86_64-unknown-linux-gnu-0.11.3":
"c0f3236f146e55472663cfbcc9be3042a9f1092275bbe3fe2a56a6cbfd3da5ce",
"x86_64-unknown-linux-musl-0.11.3":
"8b40cf16b849634b81a530a3d0a0bcae5f24996ef9ae782976fd69b6266d3b8e",
"aarch64-apple-darwin-0.11.2":
"4beaa9550f93ef7f0fc02f7c28c9c48cd61fe30db00f5ac8947e0a425c3fb282",
"aarch64-pc-windows-msvc-0.11.2":

View file

@ -2,8 +2,6 @@ import { promises as fs } from "node:fs";
import * as path from "node:path";
import * as core from "@actions/core";
import * as tc from "@actions/tool-cache";
import * as pep440 from "@renovatebot/pep440";
import * as semver from "semver";
import {
ASTRAL_MIRROR_PREFIX,
GITHUB_RELEASES_PREFIX,
@ -12,7 +10,9 @@ import {
} from "../utils/constants";
import type { Architecture, Platform } from "../utils/platforms";
import { validateChecksum } from "./checksum/checksum";
import { getAllVersions, getArtifact, getLatestVersion } from "./manifest";
import { getArtifact } from "./manifest";
export { resolveVersion } from "../version/resolve";
export function tryGetFromToolCache(
arch: Architecture,
@ -172,102 +172,3 @@ function resolveChecksum(
function getExtension(platform: Platform): string {
return platform === "pc-windows-msvc" ? ".zip" : ".tar.gz";
}
export async function resolveVersion(
versionInput: string,
manifestUrl: string | undefined,
resolutionStrategy: "highest" | "lowest" = "highest",
): Promise<string> {
core.debug(`Resolving version: ${versionInput}`);
const isSimpleMinimumVersionSpecifier =
versionInput.includes(">") && !versionInput.includes(",");
const resolveVersionSpecifierToLatest =
isSimpleMinimumVersionSpecifier && resolutionStrategy === "highest";
if (resolveVersionSpecifierToLatest) {
core.info("Found minimum version specifier, using latest version");
}
const version =
versionInput === "latest" || resolveVersionSpecifierToLatest
? await getLatestVersion(manifestUrl)
: versionInput;
if (tc.isExplicitVersion(version)) {
core.debug(`Version ${version} is an explicit version.`);
if (
resolveVersionSpecifierToLatest &&
!pep440.satisfies(version, versionInput)
) {
throw new Error(`No version found for ${versionInput}`);
}
return version;
}
const availableVersions = await getAvailableVersions(manifestUrl);
core.debug(`Available versions: ${availableVersions}`);
const resolvedVersion =
resolutionStrategy === "lowest"
? minSatisfying(availableVersions, version)
: maxSatisfying(availableVersions, version);
if (resolvedVersion === undefined) {
throw new Error(`No version found for ${version}`);
}
return resolvedVersion;
}
async function getAvailableVersions(
manifestUrl: string | undefined,
): Promise<string[]> {
if (manifestUrl !== undefined) {
core.info(
`Getting available versions from manifest-file ${manifestUrl} ...`,
);
} else {
core.info(`Getting available versions from ${VERSIONS_MANIFEST_URL} ...`);
}
return await getAllVersions(manifestUrl);
}
function maxSatisfying(
versions: string[],
version: string,
): string | undefined {
const maxSemver = tc.evaluateVersions(versions, version);
if (maxSemver !== "") {
core.debug(`Found a version that satisfies the semver range: ${maxSemver}`);
return maxSemver;
}
const maxPep440 = pep440.maxSatisfying(versions, version);
if (maxPep440 !== null) {
core.debug(
`Found a version that satisfies the pep440 specifier: ${maxPep440}`,
);
return maxPep440;
}
return undefined;
}
function minSatisfying(
versions: string[],
version: string,
): string | undefined {
// For semver, we need to use a different approach since tc.evaluateVersions only returns max
// Let's use semver directly for min satisfying
const minSemver = semver.minSatisfying(versions, version);
if (minSemver !== null) {
core.debug(`Found a version that satisfies the semver range: ${minSemver}`);
return minSemver;
}
const minPep440 = pep440.minSatisfying(versions, version);
if (minPep440 !== null) {
core.debug(
`Found a version that satisfies the pep440 specifier: ${minPep440}`,
);
return minPep440;
}
return undefined;
}

View file

@ -111,6 +111,9 @@ export async function getLatestVersion(
export async function getAllVersions(
manifestUrl: string = VERSIONS_MANIFEST_URL,
): Promise<string[]> {
core.info(
`Getting available versions from ${manifestSource(manifestUrl)} ...`,
);
const versions = await fetchManifest(manifestUrl);
return versions.map((versionData) => versionData.version);
}
@ -165,6 +168,14 @@ export function clearManifestCache(manifestUrl?: string): void {
cachedManifestData.delete(manifestUrl);
}
function manifestSource(manifestUrl: string): string {
if (manifestUrl === VERSIONS_MANIFEST_URL) {
return VERSIONS_MANIFEST_URL;
}
return `manifest-file ${manifestUrl}`;
}
function isManifestVersion(value: unknown): value is ManifestVersion {
if (!isRecord(value)) {
return false;

View file

@ -5,7 +5,6 @@ import * as exec from "@actions/exec";
import { restoreCache } from "./cache/restore-cache";
import {
downloadVersion,
resolveVersion,
tryGetFromToolCache,
} from "./download/download-version";
import { STATE_UV_PATH, STATE_UV_VERSION } from "./utils/constants";
@ -16,7 +15,7 @@ import {
getPlatform,
type Platform,
} from "./utils/platforms";
import { getUvVersionFromFile } from "./version/resolve";
import { resolveUvVersion } from "./version/resolve";
const sourceDir = __dirname;
@ -112,7 +111,13 @@ async function setupUv(
platform: Platform,
arch: Architecture,
): Promise<{ uvDir: string; version: string }> {
const resolvedVersion = await determineVersion(inputs);
const resolvedVersion = await resolveUvVersion({
manifestFile: inputs.manifestFile,
resolutionStrategy: inputs.resolutionStrategy,
version: inputs.version,
versionFile: inputs.versionFile,
workingDirectory: inputs.workingDirectory,
});
const toolCacheResult = tryGetFromToolCache(arch, resolvedVersion);
if (toolCacheResult.installedPath) {
core.info(`Found uv in tool-cache for ${toolCacheResult.version}`);
@ -137,45 +142,6 @@ async function setupUv(
};
}
async function determineVersion(inputs: SetupInputs): Promise<string> {
return await resolveVersion(
getRequestedVersion(inputs),
inputs.manifestFile,
inputs.resolutionStrategy,
);
}
function getRequestedVersion(inputs: SetupInputs): string {
if (inputs.version !== "") {
return inputs.version;
}
if (inputs.versionFile !== "") {
const versionFromFile = getUvVersionFromFile(inputs.versionFile);
if (versionFromFile === undefined) {
throw new Error(
`Could not determine uv version from file: ${inputs.versionFile}`,
);
}
return versionFromFile;
}
const versionFromUvToml = getUvVersionFromFile(
`${inputs.workingDirectory}${path.sep}uv.toml`,
);
const versionFromPyproject = getUvVersionFromFile(
`${inputs.workingDirectory}${path.sep}pyproject.toml`,
);
if (versionFromUvToml === undefined && versionFromPyproject === undefined) {
core.info(
"Could not determine uv version from uv.toml or pyproject.toml. Falling back to latest.",
);
}
return versionFromUvToml || versionFromPyproject || "latest";
}
function addUvToPathAndOutput(cachedPath: string): void {
core.setOutput("uv-path", `${cachedPath}${path.sep}uv`);
core.saveState(STATE_UV_PATH, `${cachedPath}${path.sep}uv`);

View file

@ -8,7 +8,19 @@ export function getConfigValueFromTomlFile(
if (!fs.existsSync(filePath) || !filePath.endsWith(".toml")) {
return undefined;
}
const fileContent = fs.readFileSync(filePath, "utf-8");
return getConfigValueFromTomlContent(filePath, fileContent, key);
}
export function getConfigValueFromTomlContent(
filePath: string,
fileContent: string,
key: string,
): string | undefined {
if (!filePath.endsWith(".toml")) {
return undefined;
}
if (filePath.endsWith("pyproject.toml")) {
const tomlContent = toml.parse(fileContent) as {
@ -16,6 +28,7 @@ export function getConfigValueFromTomlFile(
};
return tomlContent?.tool?.uv?.[key];
}
const tomlContent = toml.parse(fileContent) as Record<
string,
string | undefined

103
src/version/file-parser.ts Normal file
View file

@ -0,0 +1,103 @@
import fs from "node:fs";
import * as core from "@actions/core";
import { getConfigValueFromTomlContent } from "../utils/config-file";
import {
getUvVersionFromParsedPyproject,
getUvVersionFromRequirementsText,
parsePyprojectContent,
} from "./requirements-file";
import { normalizeVersionSpecifier } from "./specifier";
import { getUvVersionFromToolVersions } from "./tool-versions-file";
import type { ParsedVersionFile, VersionFileFormat } from "./types";
interface VersionFileParser {
format: VersionFileFormat;
parse(filePath: string): string | undefined;
supports(filePath: string): boolean;
}
const VERSION_FILE_PARSERS: VersionFileParser[] = [
{
format: ".tool-versions",
parse: (filePath) => getUvVersionFromToolVersions(filePath),
supports: (filePath) => filePath.endsWith(".tool-versions"),
},
{
format: "uv.toml",
parse: (filePath) => {
const fileContent = fs.readFileSync(filePath, "utf-8");
return getConfigValueFromTomlContent(
filePath,
fileContent,
"required-version",
);
},
supports: (filePath) => filePath.endsWith("uv.toml"),
},
{
format: "pyproject.toml",
parse: (filePath) => {
const fileContent = fs.readFileSync(filePath, "utf-8");
const pyproject = parsePyprojectContent(fileContent);
const requiredVersion = pyproject.tool?.uv?.["required-version"];
if (requiredVersion !== undefined) {
return requiredVersion;
}
return getUvVersionFromParsedPyproject(pyproject);
},
supports: (filePath) => filePath.endsWith("pyproject.toml"),
},
{
format: "requirements",
parse: (filePath) => {
const fileContent = fs.readFileSync(filePath, "utf-8");
return getUvVersionFromRequirementsText(fileContent);
},
supports: (filePath) => filePath.endsWith(".txt"),
},
];
export function getParsedVersionFile(
filePath: string,
): ParsedVersionFile | undefined {
core.info(`Trying to find version for uv in: ${filePath}`);
if (!fs.existsSync(filePath)) {
core.info(`Could not find file: ${filePath}`);
return undefined;
}
const parser = getVersionFileParser(filePath);
if (parser === undefined) {
return undefined;
}
try {
const specifier = parser.parse(filePath);
if (specifier === undefined) {
return undefined;
}
const normalizedSpecifier = normalizeVersionSpecifier(specifier);
core.info(`Found version for uv in ${filePath}: ${normalizedSpecifier}`);
return {
format: parser.format,
specifier: normalizedSpecifier,
};
} catch (error) {
core.warning(
`Error while parsing ${filePath}: ${(error as Error).message}`,
);
return undefined;
}
}
export function getUvVersionFromFile(filePath: string): string | undefined {
return getParsedVersionFile(filePath)?.specifier;
}
function getVersionFileParser(filePath: string): VersionFileParser | undefined {
return VERSION_FILE_PARSERS.find((parser) => parser.supports(filePath));
}

View file

@ -5,31 +5,23 @@ export function getUvVersionFromRequirementsFile(
filePath: string,
): string | undefined {
const fileContent = fs.readFileSync(filePath, "utf-8");
if (filePath.endsWith(".txt")) {
return getUvVersionFromAllDependencies(fileContent.split("\n"));
return getUvVersionFromRequirementsText(fileContent);
}
const dependencies = parsePyprojectDependencies(fileContent);
return getUvVersionFromAllDependencies(dependencies);
return getUvVersionFromPyprojectContent(fileContent);
}
function getUvVersionFromAllDependencies(
allDependencies: string[],
export function getUvVersionFromRequirementsText(
fileContent: string,
): string | undefined {
return allDependencies
.find((dep: string) => dep.match(/^uv[=<>~!]/))
?.match(/^uv([=<>~!]+\S*)/)?.[1]
.trim();
return getUvVersionFromAllDependencies(fileContent.split("\n"));
}
interface Pyproject {
project?: {
dependencies?: string[];
"optional-dependencies"?: Record<string, string[]>;
};
"dependency-groups"?: Record<string, Array<string | object>>;
}
function parsePyprojectDependencies(pyprojectContent: string): string[] {
const pyproject: Pyproject = toml.parse(pyprojectContent);
export function getUvVersionFromParsedPyproject(
pyproject: Pyproject,
): string | undefined {
const dependencies: string[] = pyproject?.project?.dependencies || [];
const optionalDependencies: string[] = Object.values(
pyproject?.project?.["optional-dependencies"] || {},
@ -39,5 +31,39 @@ function parsePyprojectDependencies(pyprojectContent: string): string[] {
)
.flat()
.filter((item: string | object) => typeof item === "string");
return dependencies.concat(optionalDependencies, devDependencies);
return getUvVersionFromAllDependencies(
dependencies.concat(optionalDependencies, devDependencies),
);
}
export function getUvVersionFromPyprojectContent(
pyprojectContent: string,
): string | undefined {
const pyproject = parsePyprojectContent(pyprojectContent);
return getUvVersionFromParsedPyproject(pyproject);
}
export interface Pyproject {
project?: {
dependencies?: string[];
"optional-dependencies"?: Record<string, string[]>;
};
"dependency-groups"?: Record<string, Array<string | object>>;
tool?: {
uv?: Record<string, string | undefined>;
};
}
export function parsePyprojectContent(pyprojectContent: string): Pyproject {
return toml.parse(pyprojectContent) as Pyproject;
}
function getUvVersionFromAllDependencies(
allDependencies: string[],
): string | undefined {
return allDependencies
.find((dep: string) => dep.match(/^uv[=<>~!]/))
?.match(/^uv([=<>~!]+\S*)/)?.[1]
.trim();
}

View file

@ -1,34 +1,183 @@
import fs from "node:fs";
import * as core from "@actions/core";
import { getConfigValueFromTomlFile } from "../utils/config-file";
import { getUvVersionFromRequirementsFile } from "./requirements-file";
import { getUvVersionFromToolVersions } from "./tool-versions-file";
import * as tc from "@actions/tool-cache";
import * as pep440 from "@renovatebot/pep440";
import * as semver from "semver";
import { getAllVersions, getLatestVersion } from "../download/manifest";
import type { ResolutionStrategy } from "../utils/inputs";
import {
type ParsedVersionSpecifier,
parseVersionSpecifier,
} from "./specifier";
import type { ResolveUvVersionOptions } from "./types";
import { resolveVersionRequest } from "./version-request-resolver";
export function getUvVersionFromFile(filePath: string): string | undefined {
core.info(`Trying to find version for uv in: ${filePath}`);
if (!fs.existsSync(filePath)) {
core.info(`Could not find file: ${filePath}`);
return undefined;
}
let uvVersion: string | undefined;
try {
uvVersion = getUvVersionFromToolVersions(filePath);
if (uvVersion === undefined) {
uvVersion = getConfigValueFromTomlFile(filePath, "required-version");
}
if (uvVersion === undefined) {
uvVersion = getUvVersionFromRequirementsFile(filePath);
}
} catch (err) {
const message = (err as Error).message;
core.warning(`Error while parsing ${filePath}: ${message}`);
return undefined;
}
if (uvVersion?.startsWith("==")) {
uvVersion = uvVersion.slice(2);
}
if (uvVersion !== undefined) {
core.info(`Found version for uv in ${filePath}: ${uvVersion}`);
}
return uvVersion;
interface ConcreteVersionResolutionContext {
manifestUrl?: string;
parsedSpecifier: ParsedVersionSpecifier;
resolutionStrategy: ResolutionStrategy;
}
interface ConcreteVersionResolver {
resolve(
context: ConcreteVersionResolutionContext,
): Promise<string | undefined>;
}
class ExactVersionResolver implements ConcreteVersionResolver {
async resolve(
context: ConcreteVersionResolutionContext,
): Promise<string | undefined> {
if (context.parsedSpecifier.kind !== "exact") {
return undefined;
}
core.debug(
`Version ${context.parsedSpecifier.normalized} is an explicit version.`,
);
return context.parsedSpecifier.normalized;
}
}
class LatestVersionResolver implements ConcreteVersionResolver {
async resolve(
context: ConcreteVersionResolutionContext,
): Promise<string | undefined> {
const shouldUseLatestVersion =
context.parsedSpecifier.kind === "latest" ||
(context.parsedSpecifier.kind === "range" &&
context.parsedSpecifier.isSimpleMinimumVersionSpecifier &&
context.resolutionStrategy === "highest");
if (!shouldUseLatestVersion) {
return undefined;
}
if (
context.parsedSpecifier.kind === "range" &&
context.parsedSpecifier.isSimpleMinimumVersionSpecifier
) {
core.info("Found minimum version specifier, using latest version");
}
const latestVersion = await getLatestVersion(context.manifestUrl);
if (
context.parsedSpecifier.kind === "range" &&
context.parsedSpecifier.isSimpleMinimumVersionSpecifier &&
!pep440.satisfies(latestVersion, context.parsedSpecifier.raw)
) {
throw new Error(`No version found for ${context.parsedSpecifier.raw}`);
}
return latestVersion;
}
}
class RangeVersionResolver implements ConcreteVersionResolver {
async resolve(
context: ConcreteVersionResolutionContext,
): Promise<string | undefined> {
if (context.parsedSpecifier.kind !== "range") {
return undefined;
}
const availableVersions = await getAllVersions(context.manifestUrl);
core.debug(`Available versions: ${availableVersions}`);
const resolvedVersion =
context.resolutionStrategy === "lowest"
? minSatisfying(availableVersions, context.parsedSpecifier.normalized)
: maxSatisfying(availableVersions, context.parsedSpecifier.normalized);
if (resolvedVersion === undefined) {
throw new Error(`No version found for ${context.parsedSpecifier.raw}`);
}
return resolvedVersion;
}
}
const CONCRETE_VERSION_RESOLVERS: ConcreteVersionResolver[] = [
new ExactVersionResolver(),
new LatestVersionResolver(),
new RangeVersionResolver(),
];
export async function resolveUvVersion(
options: ResolveUvVersionOptions,
): Promise<string> {
const request = resolveVersionRequest(options);
const resolutionStrategy = options.resolutionStrategy ?? "highest";
const version = await resolveVersion(
request.specifier,
options.manifestFile,
resolutionStrategy,
);
return version;
}
export async function resolveVersion(
versionInput: string,
manifestUrl: string | undefined,
resolutionStrategy: ResolutionStrategy = "highest",
): Promise<string> {
core.debug(`Resolving version: ${versionInput}`);
const context: ConcreteVersionResolutionContext = {
manifestUrl,
parsedSpecifier: parseVersionSpecifier(versionInput),
resolutionStrategy,
};
for (const resolver of CONCRETE_VERSION_RESOLVERS) {
const version = await resolver.resolve(context);
if (version !== undefined) {
return version;
}
}
throw new Error(`No version found for ${versionInput}`);
}
function maxSatisfying(
versions: string[],
version: string,
): string | undefined {
const maxSemver = tc.evaluateVersions(versions, version);
if (maxSemver !== "") {
core.debug(`Found a version that satisfies the semver range: ${maxSemver}`);
return maxSemver;
}
const maxPep440 = pep440.maxSatisfying(versions, version);
if (maxPep440 !== null) {
core.debug(
`Found a version that satisfies the pep440 specifier: ${maxPep440}`,
);
return maxPep440;
}
return undefined;
}
function minSatisfying(
versions: string[],
version: string,
): string | undefined {
const minSemver = semver.minSatisfying(versions, version);
if (minSemver !== null) {
core.debug(`Found a version that satisfies the semver range: ${minSemver}`);
return minSemver;
}
const minPep440 = pep440.minSatisfying(versions, version);
if (minPep440 !== null) {
core.debug(
`Found a version that satisfies the pep440 specifier: ${minPep440}`,
);
return minPep440;
}
return undefined;
}

59
src/version/specifier.ts Normal file
View file

@ -0,0 +1,59 @@
import * as tc from "@actions/tool-cache";
export type ParsedVersionSpecifier =
| {
kind: "exact";
normalized: string;
raw: string;
}
| {
kind: "latest";
normalized: "latest";
raw: string;
}
| {
isSimpleMinimumVersionSpecifier: boolean;
kind: "range";
normalized: string;
raw: string;
};
export function normalizeVersionSpecifier(specifier: string): string {
const trimmedSpecifier = specifier.trim();
if (trimmedSpecifier.startsWith("==")) {
return trimmedSpecifier.slice(2);
}
return trimmedSpecifier;
}
export function parseVersionSpecifier(
specifier: string,
): ParsedVersionSpecifier {
const raw = specifier.trim();
const normalized = normalizeVersionSpecifier(raw);
if (normalized === "latest") {
return {
kind: "latest",
normalized: "latest",
raw,
};
}
if (tc.isExplicitVersion(normalized)) {
return {
kind: "exact",
normalized,
raw,
};
}
return {
isSimpleMinimumVersionSpecifier: raw.includes(">") && !raw.includes(","),
kind: "range",
normalized,
raw,
};
}

34
src/version/types.ts Normal file
View file

@ -0,0 +1,34 @@
import type { ResolutionStrategy } from "../utils/inputs";
export type VersionSource =
| "input"
| "version-file"
| "uv.toml"
| "pyproject.toml"
| "default";
export type VersionFileFormat =
| ".tool-versions"
| "pyproject.toml"
| "requirements"
| "uv.toml";
export interface ParsedVersionFile {
format: VersionFileFormat;
specifier: string;
}
export interface ResolveUvVersionOptions {
manifestFile?: string;
resolutionStrategy?: ResolutionStrategy;
version?: string;
versionFile?: string;
workingDirectory: string;
}
export interface VersionRequest {
format?: VersionFileFormat;
source: VersionSource;
sourcePath?: string;
specifier: string;
}

View file

@ -0,0 +1,158 @@
import * as path from "node:path";
import * as core from "@actions/core";
import { getParsedVersionFile } from "./file-parser";
import { normalizeVersionSpecifier } from "./specifier";
import type {
ParsedVersionFile,
ResolveUvVersionOptions,
VersionRequest,
} from "./types";
export interface VersionRequestResolver {
resolve(context: VersionRequestContext): VersionRequest | undefined;
}
export class VersionRequestContext {
readonly version: string | undefined;
readonly versionFile: string | undefined;
readonly workingDirectory: string;
private readonly parsedFiles = new Map<
string,
ParsedVersionFile | undefined
>();
constructor(
version: string | undefined,
versionFile: string | undefined,
workingDirectory: string,
) {
this.version = version;
this.versionFile = versionFile;
this.workingDirectory = workingDirectory;
}
getVersionFile(filePath: string): ParsedVersionFile | undefined {
const cachedResult = this.parsedFiles.get(filePath);
if (cachedResult !== undefined || this.parsedFiles.has(filePath)) {
return cachedResult;
}
const result = getParsedVersionFile(filePath);
this.parsedFiles.set(filePath, result);
return result;
}
getWorkspaceCandidates(): Array<{
source: "pyproject.toml" | "uv.toml";
sourcePath: string;
}> {
return [
{
source: "uv.toml",
sourcePath: path.join(this.workingDirectory, "uv.toml"),
},
{
source: "pyproject.toml",
sourcePath: path.join(this.workingDirectory, "pyproject.toml"),
},
];
}
}
export class ExplicitInputVersionResolver implements VersionRequestResolver {
resolve(context: VersionRequestContext): VersionRequest | undefined {
if (context.version === undefined) {
return undefined;
}
return {
source: "input",
specifier: normalizeVersionSpecifier(context.version),
};
}
}
export class VersionFileVersionResolver implements VersionRequestResolver {
resolve(context: VersionRequestContext): VersionRequest | undefined {
if (context.versionFile === undefined) {
return undefined;
}
const versionFile = context.getVersionFile(context.versionFile);
if (versionFile === undefined) {
throw new Error(
`Could not determine uv version from file: ${context.versionFile}`,
);
}
return {
format: versionFile.format,
source: "version-file",
sourcePath: context.versionFile,
specifier: versionFile.specifier,
};
}
}
export class WorkspaceVersionResolver implements VersionRequestResolver {
resolve(context: VersionRequestContext): VersionRequest | undefined {
for (const candidate of context.getWorkspaceCandidates()) {
const versionFile = context.getVersionFile(candidate.sourcePath);
if (versionFile === undefined) {
continue;
}
return {
format: versionFile.format,
source: candidate.source,
sourcePath: candidate.sourcePath,
specifier: versionFile.specifier,
};
}
core.info(
"Could not determine uv version from uv.toml or pyproject.toml. Falling back to latest.",
);
return undefined;
}
}
export class LatestVersionResolver implements VersionRequestResolver {
resolve(): VersionRequest {
return {
source: "default",
specifier: "latest",
};
}
}
const VERSION_REQUEST_RESOLVERS: VersionRequestResolver[] = [
new ExplicitInputVersionResolver(),
new VersionFileVersionResolver(),
new WorkspaceVersionResolver(),
new LatestVersionResolver(),
];
export function resolveVersionRequest(
options: ResolveUvVersionOptions,
): VersionRequest {
const context = new VersionRequestContext(
emptyToUndefined(options.version),
emptyToUndefined(options.versionFile),
options.workingDirectory,
);
for (const resolver of VERSION_REQUEST_RESOLVERS) {
const request = resolver.resolve(context);
if (request !== undefined) {
return request;
}
}
throw new Error("Could not resolve a requested uv version.");
}
function emptyToUndefined(value: string | undefined): string | undefined {
return value === undefined || value === "" ? undefined : value;
}