Compare commits

...
Author SHA1 Message Date
dependabot[bot]andGitHub 8274394096 chore(deps): bump github/codeql-action/init from 4.38.1 to 4.38.2
Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.38.1 to 4.38.2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/1c5b675653bb5c22dbe9b12b556ec555138e09fd...2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.38.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-26 10:56:39 +00:00
2f29784424 docs: use JSDoc for compatible release specifier comment (#1113)
Amp-Thread-ID:
https://ampcode.com/threads/T-01a17443-b5b9-703c-8877-780650eae403

Co-authored-by: Amp <amp@ampcode.com>
2026-10-26 08:49:11 +01:00
9c7332190c fix: allow whitespace in uv requirement specifiers (#1112)
## Summary

When the uv version is read from dependencies (`requirements.txt`, or
`project.dependencies` / `optional-dependencies` / `dependency-groups`
in `pyproject.toml` via `version-file`), the specifier was matched with
`/^uv([=<>~!]+\S*)/`. PEP 508 allows whitespace around operators and
after commas, and those valid forms were misread:

| dependency | before | after |
|---|---|---|
| `uv >= 0.5.0` | not found, latest uv installed | `>=0.5.0` |
| `uv == 0.11.20` | not found, latest uv installed | `==0.11.20` |
| `uv>=0.5.0, <0.6.0` | `>=0.5.0,`, fails with `No version found for
>=0.5.0,` | `>=0.5.0,<0.6.0` |

The regex now allows whitespace around operators and between
comma-separated clauses, and strips it from the result. Inputs that
worked before give the same result, including inline `--hash=` options,
trailing ` # comments`, the `\` continuation lines from `uv pip compile
--generate-hashes`, and names that only start with `uv` (`uvicorn`,
`uv-build`).

## Test plan

- Added cases for spaced operators, spaced clauses, inline `--hash` and
trailing comments. The four whitespace cases failed before the change.
- `npm run all` on Ubuntu (WSL) with Node 24.19.0: build, Biome (no
fixes), packaging, and 165 tests across 15 suites pass. The rebuilt
`dist/setup/index.cjs` only differs in the changed line.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 08:37:03 +01:00
80fbe0140c fix: resolve ~= specifiers with PEP 440 semantics (#1111)
## Summary

`~=` is the PEP 440 compatible release operator, but the resolver tries
`semver.validRange()` first, and semver accepts `~=` as a tilde range:

| specifier | semver | PEP 440 |
|---|---|---|
| `~=0.9` | `>=0.9.0 <0.10.0` | `>=0.9, ==0.*` |

So with the default `highest` strategy, `version: "~=0.9"` (or
`required-version = "~=0.9"` picked up through `version-file`) installs
the highest 0.9.x instead of the highest 0.x. The same happens with
`manifest-file`, where `tc.evaluateVersions` is semver-based.

This skips the semver evaluation for specifiers containing `~=`, so they
are only evaluated with `@renovatebot/pep440`. No semver range
legitimately uses `~=`.

Notes:

- Only a bare two-segment `~=X.Y` resolved incorrectly. `~=X.Y.Z` gives
the same result in both, and compound specifiers like `~=0.9, !=0.9.3`
were already rejected by semver and fell through to PEP 440.
- `minSatisfying` gets the same check for consistency. The lower bound
is the same under both interpretations, so `lowest` didn't produce wrong
results before.
- A single-segment `~=1` is invalid PEP 440. semver used to resolve it
to 1.x; it now fails with `No version found for ~=1`, same as other
invalid specifiers.

## Test plan

- Added tests for `~=0.9` with the default manifest and with
`manifest-file`. Both returned `0.9.26` instead of `0.10.2` before the
change.
- `npm run all` on Ubuntu (WSL) with Node 24.19.0: build, Biome (no
fixes), packaging, and 161 tests across 15 suites pass. The rebuilt
`dist/setup/index.cjs` only differs in the changed lines.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:39:29 +01:00
36b7b57007 fix: only report a saved cache when saving succeeded (#1110)
## Summary

The post step logs `uv cache saved with key: ...` (and `Python cache
saved with key: ...`) even when the save failed. The log in #769 shows
it right after `Warning: Failed to save: ...`:

```
Warning: Failed to save: "C:\Program failed with error: The process 'C:\Program Files\Git\usr\bin\tar.exe' failed with exit code 2
uv cache saved with key: setup-uv-2-x86_64-pc-windows-msvc-windows-11-3.12.12-pruned-...
```

`@actions/cache`'s `saveCache` doesn't throw for these failures. It logs
them and returns `-1`, and `saveCacheToKey` ignored the return value.
This now checks for `-1` before logging, which is the same check the
`actions/cache` save step uses (`if (cacheId != -1) core.info("Cache
saved with key: ...")`). `@actions/cache` already logs the reason, so
nothing else is logged on failure, and the exit code stays the same.

This only changes the log line. It doesn't address the underlying
tar/gzip failure in #769. One limitation: with the legacy (v1) cache
service, an upload that fails after the cache was reserved still returns
the reserved id, so that one case is still reported as saved. That's on
the `@actions/cache` side and affects `actions/cache` the same way.

Related to #769

## Test plan

- Added `__tests__/cache/save-cache.test.ts`. The `-1` case fails before
the change and passes after it.
- `npm run all` on Ubuntu (WSL) with Node 24.19.0 / npm 11.17.0: build,
Biome (no fixes), packaging, and all 161 tests across 16 suites pass.
The regenerated `dist/save-cache/index.cjs` only differs in the changed
lines.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:38:39 +01:00
56618cab7a docs: update version references to v10.3.2 (#1104)
Update `uses: astral-sh/setup-uv@...` references in documentation to
`07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2`.

Co-authored-by: eifinger <1481961+eifinger@users.noreply.github.com>
2026-10-22 11:25:32 +02:00
14 changed files with 198 additions and 54 deletions
+1 -1
View File
@@ -47,7 +47,7 @@ jobs:
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1 uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
source-root: src source-root: src
+9 -9
View File
@@ -27,7 +27,7 @@ Set up your GitHub Actions workflow with a specific version of [uv](https://docs
```yaml ```yaml
- name: Install the latest version of uv - name: Install the latest version of uv
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.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) If you do not specify a version, this action will look for a [required-version](https://docs.astral.sh/uv/reference/settings/#required-version)
@@ -43,7 +43,7 @@ Have a look under [Advanced Configuration](#advanced-configuration) for detailed
```yaml ```yaml
- name: Install uv with all available options - name: Install uv with all available options
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
# The version of uv to install, e.g., "0.5.0", "latest", or "latest-known" (default: searches for version in config files, then latest) # The version of uv to install, e.g., "0.5.0", "latest", or "latest-known" (default: searches for version in config files, then latest)
version: "" version: ""
@@ -161,7 +161,7 @@ python 3.13
``` ```
```yaml ```yaml
- uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
version-file: ".tool-versions" version-file: ".tool-versions"
``` ```
@@ -172,7 +172,7 @@ a warning.
```yaml ```yaml
- name: Install the latest version of uv and set the python version to 3.13t - name: Install the latest version of uv and set the python version to 3.13t
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
python-version: 3.13t python-version: 3.13t
- run: uv pip install --python=3.13t pip - run: uv pip install --python=3.13t pip
@@ -190,7 +190,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
- name: Install the latest version of uv and set the python version - name: Install the latest version of uv and set the python version
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Test with python ${{ matrix.python-version }} - name: Test with python ${{ matrix.python-version }}
@@ -226,7 +226,7 @@ It also controls where [the venv gets created](#activate-environment), unless `v
```yaml ```yaml
- name: Install uv based on the config files in the working-directory - name: Install uv based on the config files in the working-directory
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
working-directory: my/subproject/dir working-directory: my/subproject/dir
``` ```
@@ -268,7 +268,7 @@ For example:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@main uses: actions/checkout@main
- name: Install the latest version of uv - name: Install the latest version of uv
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
enable-cache: true enable-cache: true
- name: Test - name: Test
@@ -280,7 +280,7 @@ To install a specific version of Python, use
```yaml ```yaml
- name: Install the latest version of uv - name: Install the latest version of uv
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
enable-cache: true enable-cache: true
- name: Install Python 3.12 - name: Install Python 3.12
@@ -299,7 +299,7 @@ output:
uses: actions/checkout@main uses: actions/checkout@main
- name: Install the default version of uv - name: Install the default version of uv
id: setup-uv id: setup-uv
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
- name: Print the installed version - name: Print the installed version
run: echo "Installed uv version is ${{ steps.setup-uv.outputs.uv-version }}" run: echo "Installed uv version is ${{ steps.setup-uv.outputs.uv-version }}"
``` ```
+87
View File
@@ -0,0 +1,87 @@
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import {
afterAll,
beforeEach,
describe,
expect,
it,
jest,
} from "@jest/globals";
import { CacheLocalSource, type SetupInputs } from "../../src/utils/inputs";
import { createSetupInputs } from "../helpers/setup-inputs";
const mockSaveCache = jest.fn<() => Promise<number>>();
const mockInfo = jest.fn();
const mockLoadInputs = jest.fn<() => SetupInputs>(() =>
createSetupInputs({ enableCache: false }),
);
const state: Record<string, string> = {};
jest.unstable_mockModule("@actions/cache", () => ({
saveCache: mockSaveCache,
}));
jest.unstable_mockModule("@actions/core", () => ({
getState: jest.fn((name: string) => state[name] ?? ""),
setFailed: jest.fn(),
}));
jest.unstable_mockModule("../../src/utils/inputs", () => ({
loadInputs: mockLoadInputs,
}));
jest.unstable_mockModule("../../src/utils/logging", () => ({
info: mockInfo,
warning: jest.fn(),
}));
jest.spyOn(process, "exit").mockImplementation((() => undefined) as never);
const { run } = await import("../../src/save-cache");
const cacheDir = mkdtempSync(join(tmpdir(), "setup-uv-save-cache-"));
const cacheKey = "setup-uv-2-x86_64-unknown-linux-gnu-ubuntu-24.04-3.12-hash";
function infoMessages(): string[] {
return mockInfo.mock.calls.map(([msg]) => msg as string);
}
beforeEach(() => {
delete process.env.UV_CACHE_DIR;
state["cache-key"] = cacheKey;
state["cache-matched-key"] = "";
mockLoadInputs.mockReturnValue(
createSetupInputs({
cacheLocalPath: { path: cacheDir, source: CacheLocalSource.Input },
}),
);
});
afterAll(() => {
rmSync(cacheDir, { force: true, recursive: true });
});
describe("saveCache", () => {
it("reports the saved key when the cache is saved", async () => {
mockSaveCache.mockResolvedValue(42);
await run();
expect(mockSaveCache).toHaveBeenCalledWith([cacheDir], cacheKey);
expect(infoMessages()).toContain(`uv cache saved with key: ${cacheKey}`);
});
it("does not report the cache as saved when saving fails", async () => {
// @actions/cache logs the failure itself and returns -1
mockSaveCache.mockResolvedValue(-1);
await run();
expect(mockSaveCache).toHaveBeenCalledWith([cacheDir], cacheKey);
expect(infoMessages()).not.toContain(
`uv cache saved with key: ${cacheKey}`,
);
});
});
@@ -146,6 +146,28 @@ describe("download-version", () => {
expect(mockGetAllVersions).not.toHaveBeenCalled(); expect(mockGetAllVersions).not.toHaveBeenCalled();
}); });
it("resolves ~= with PEP 440 compatible release semantics", async () => {
mockGetFirstMatchingVersion.mockImplementation(
(predicate: (version: string) => boolean) =>
["1.0.0", "0.10.2", "0.9.26"].find(predicate),
);
const version = await resolveVersion("~=0.9", undefined);
expect(version).toBe("0.10.2");
});
it("resolves ~= with PEP 440 compatible release semantics from manifest-file", async () => {
mockGetAllVersions.mockResolvedValue(["1.0.0", "0.10.2", "0.9.26"]);
const version = await resolveVersion(
"~=0.9",
"https://example.com/custom.ndjson",
);
expect(version).toBe("0.10.2");
});
it("treats == exact pins as explicit versions", async () => { it("treats == exact pins as explicit versions", async () => {
const version = await resolveVersion("==0.9.26", undefined); const version = await resolveVersion("==0.9.26", undefined);
@@ -35,3 +35,14 @@ test("strips PEP 508 markers from requirements dependencies", () => {
expect(parsedVersion).toBe("==0.11.20"); expect(parsedVersion).toBe("==0.11.20");
}); });
test.each([
["uv >= 0.5.0", ">=0.5.0"],
["uv == 0.11.20", "==0.11.20"],
["uv>=0.5.0, <0.6.0", ">=0.5.0,<0.6.0"],
["uv >= 0.5.0 , < 0.6.0", ">=0.5.0,<0.6.0"],
["uv==0.11.20 --hash=sha256:abc", "==0.11.20"],
["uv==0.11.20 # pinned for CI", "==0.11.20"],
])("allows whitespace in the uv specifier %s", (dependency, expected) => {
expect(getUvVersionFromRequirementsText(dependency)).toBe(expected);
});
Generated Vendored
+4 -2
View File
@@ -66671,8 +66671,10 @@ async function saveCacheToKey(cacheKey, cachePath, stateKey, cacheName) {
return; return;
} }
info2(`Including ${cacheName} path: ${cachePath}`); info2(`Including ${cacheName} path: ${cachePath}`);
await saveCache2([cachePath], cacheKey); const cacheId = await saveCache2([cachePath], cacheKey);
info2(`${cacheName} saved with key: ${cacheKey}`); if (cacheId !== -1) {
info2(`${cacheName} saved with key: ${cacheKey}`);
}
} }
__name(saveCacheToKey, "saveCacheToKey"); __name(saveCacheToKey, "saveCacheToKey");
run(); run();
Generated Vendored
+7 -4
View File
@@ -101579,7 +101579,7 @@ function getUvVersionFromAllDependencies(allDependencies) {
} }
function getUvVersionFromDependency(dependency) { function getUvVersionFromDependency(dependency) {
const dependencyWithoutMarker = dependency.split(";", 1)[0]?.trim(); const dependencyWithoutMarker = dependency.split(";", 1)[0]?.trim();
return dependencyWithoutMarker?.match(/^uv([=<>~!]+\S*)/)?.[1].trim(); return dependencyWithoutMarker?.match(/^uv\s*((?:[=<>~!]+\s*[^\s,]+\s*,?\s*)+)/)?.[1].replace(/\s/g, "");
} }
// src/version/tool-versions-file.ts // src/version/tool-versions-file.ts
@@ -101937,7 +101937,7 @@ async function resolveVersion(versionInput, manifestUrl, resolutionStrategy = "h
throw new Error(`No version found for ${versionInput}`); throw new Error(`No version found for ${versionInput}`);
} }
async function findHighestSatisfyingVersion(versionSpecifier) { async function findHighestSatisfyingVersion(versionSpecifier) {
const semverRange = semver5.validRange(versionSpecifier); const semverRange = isCompatibleReleaseSpecifier(versionSpecifier) ? null : semver5.validRange(versionSpecifier);
if (semverRange !== null) { if (semverRange !== null) {
const semverMatch = await getFirstMatchingVersion( const semverMatch = await getFirstMatchingVersion(
(version3) => semver5.satisfies(version3, semverRange) (version3) => semver5.satisfies(version3, semverRange)
@@ -101959,8 +101959,11 @@ async function findHighestSatisfyingVersion(versionSpecifier) {
} }
return pep440Match; return pep440Match;
} }
function isCompatibleReleaseSpecifier(versionSpecifier) {
return versionSpecifier.includes("~=");
}
function maxSatisfying2(versions, version3) { function maxSatisfying2(versions, version3) {
const maxSemver = evaluateVersions(versions, version3); const maxSemver = isCompatibleReleaseSpecifier(version3) ? "" : evaluateVersions(versions, version3);
if (maxSemver !== "") { if (maxSemver !== "") {
debug(`Found a version that satisfies the semver range: ${maxSemver}`); debug(`Found a version that satisfies the semver range: ${maxSemver}`);
return maxSemver; return maxSemver;
@@ -101975,7 +101978,7 @@ function maxSatisfying2(versions, version3) {
return void 0; return void 0;
} }
function minSatisfying3(versions, version3) { function minSatisfying3(versions, version3) {
const minSemver = semver5.minSatisfying(versions, version3); const minSemver = isCompatibleReleaseSpecifier(version3) ? null : semver5.minSatisfying(versions, version3);
if (minSemver !== null) { if (minSemver !== null) {
debug(`Found a version that satisfies the semver range: ${minSemver}`); debug(`Found a version that satisfies the semver range: ${minSemver}`);
return minSemver; return minSemver;
+9 -9
View File
@@ -6,7 +6,7 @@ This document covers advanced options for configuring which version of uv to ins
```yaml ```yaml
- name: Install the latest version of uv - name: Install the latest version of uv
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
version: "latest" version: "latest"
``` ```
@@ -19,7 +19,7 @@ When `manifest-file` is set, `latest-known` still selects a version from setup-u
```yaml ```yaml
- name: Install the latest version of uv known to setup-uv - name: Install the latest version of uv known to setup-uv
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
version: "latest-known" version: "latest-known"
``` ```
@@ -28,7 +28,7 @@ When `manifest-file` is set, `latest-known` still selects a version from setup-u
```yaml ```yaml
- name: Install a specific version of uv - name: Install a specific version of uv
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
version: "0.4.4" version: "0.4.4"
``` ```
@@ -41,21 +41,21 @@ to install the latest version that satisfies the range.
```yaml ```yaml
- name: Install a semver range of uv - name: Install a semver range of uv
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
version: ">=0.4.0" version: ">=0.4.0"
``` ```
```yaml ```yaml
- name: Pinning a minor version of uv - name: Pinning a minor version of uv
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
version: "0.4.x" version: "0.4.x"
``` ```
```yaml ```yaml
- name: Install a pep440-specifier-satisfying version of uv - name: Install a pep440-specifier-satisfying version of uv
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
version: ">=0.4.25,<0.5" version: ">=0.4.25,<0.5"
``` ```
@@ -67,7 +67,7 @@ You can change this behavior using the `resolution-strategy` input:
```yaml ```yaml
- name: Install the lowest compatible version of uv - name: Install the lowest compatible version of uv
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
version: ">=0.4.0" version: ">=0.4.0"
resolution-strategy: "lowest" resolution-strategy: "lowest"
@@ -92,7 +92,7 @@ as `UV_PYTHON`. Only a single Python version is supported; multiple fallback ver
```yaml ```yaml
- name: Install uv based on the version defined in pyproject.toml - name: Install uv based on the version defined in pyproject.toml
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
version-file: "pyproject.toml" version-file: "pyproject.toml"
``` ```
@@ -103,7 +103,7 @@ silently picking up a newer uv until the lockfile is updated.
```yaml ```yaml
- name: Install uv based on the version locked in uv.lock - name: Install uv based on the version locked in uv.lock
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
version-file: "uv.lock" version-file: "uv.lock"
``` ```
+12 -12
View File
@@ -24,7 +24,7 @@ The computed cache key is available as the `cache-key` output:
```yaml ```yaml
- name: Setup uv - name: Setup uv
id: setup-uv id: setup-uv
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
enable-cache: true enable-cache: true
- name: Print cache key - name: Print cache key
@@ -54,7 +54,7 @@ You can optionally define a custom cache key suffix.
```yaml ```yaml
- name: Enable caching and define a custom cache key suffix - name: Enable caching and define a custom cache key suffix
id: setup-uv id: setup-uv
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
enable-cache: true enable-cache: true
cache-suffix: "optional-suffix" cache-suffix: "optional-suffix"
@@ -93,7 +93,7 @@ changes. If you use relative paths, they are relative to the working directory.
```yaml ```yaml
- name: Define a cache dependency glob - name: Define a cache dependency glob
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
enable-cache: true enable-cache: true
cache-dependency-glob: "**/pyproject.toml" cache-dependency-glob: "**/pyproject.toml"
@@ -101,7 +101,7 @@ changes. If you use relative paths, they are relative to the working directory.
```yaml ```yaml
- name: Define a list of cache dependency globs - name: Define a list of cache dependency globs
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
enable-cache: true enable-cache: true
cache-dependency-glob: | cache-dependency-glob: |
@@ -111,7 +111,7 @@ changes. If you use relative paths, they are relative to the working directory.
```yaml ```yaml
- name: Define an absolute cache dependency glob - name: Define an absolute cache dependency glob
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
enable-cache: true enable-cache: true
cache-dependency-glob: "/tmp/my-folder/requirements*.txt" cache-dependency-glob: "/tmp/my-folder/requirements*.txt"
@@ -119,7 +119,7 @@ changes. If you use relative paths, they are relative to the working directory.
```yaml ```yaml
- name: Never invalidate the cache - name: Never invalidate the cache
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
enable-cache: true enable-cache: true
cache-dependency-glob: "" cache-dependency-glob: ""
@@ -132,7 +132,7 @@ By default, the cache will be restored.
```yaml ```yaml
- name: Don't restore an existing cache - name: Don't restore an existing cache
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
enable-cache: true enable-cache: true
restore-cache: false restore-cache: false
@@ -148,7 +148,7 @@ these events. Set `save-cache: true` to save the cache for all events.
```yaml ```yaml
- name: Don't save the cache after the run - name: Don't save the cache after the run
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
enable-cache: true enable-cache: true
save-cache: false save-cache: false
@@ -174,7 +174,7 @@ It defaults to `setup-uv-cache` in the `TMP` dir, `D:\a\_temp\setup-uv-cache` on
```yaml ```yaml
- name: Define a custom uv cache path - name: Define a custom uv cache path
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
cache-local-path: "/path/to/cache" cache-local-path: "/path/to/cache"
``` ```
@@ -191,7 +191,7 @@ If you want to prune the cache before saving it, enable cache pruning with the `
```yaml ```yaml
- name: Prune the cache before saving it - name: Prune the cache before saving it
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
enable-cache: true enable-cache: true
prune-cache: true prune-cache: true
@@ -210,7 +210,7 @@ To force managed Python installs, set `UV_PYTHON_PREFERENCE=only-managed`.
```yaml ```yaml
- name: Cache Python installs - name: Cache Python installs
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
enable-cache: true enable-cache: true
cache-python: true cache-python: true
@@ -228,7 +228,7 @@ If you want to ignore this, set the `ignore-nothing-to-cache` input to `true`.
```yaml ```yaml
- name: Ignore nothing to cache - name: Ignore nothing to cache
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
enable-cache: true enable-cache: true
ignore-nothing-to-cache: true ignore-nothing-to-cache: true
+3 -3
View File
@@ -11,7 +11,7 @@ You can specify a checksum to override those values. The sha256 hashes can also
```yaml ```yaml
- name: Install a specific version and validate the checksum - name: Install a specific version and validate the checksum
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
version: "0.3.1" version: "0.3.1"
checksum: "e11b01402ab645392c7ad6044db63d37e4fd1e745e015306993b07695ea5f9f8" checksum: "e11b01402ab645392c7ad6044db63d37e4fd1e745e015306993b07695ea5f9f8"
@@ -40,7 +40,7 @@ The `archive_format` field is currently ignored.
```yaml ```yaml
- name: Use a custom manifest file - name: Use a custom manifest file
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
manifest-file: "https://example.com/my-custom-manifest.ndjson" manifest-file: "https://example.com/my-custom-manifest.ndjson"
``` ```
@@ -59,7 +59,7 @@ You can disable this by setting the `add-problem-matchers` input to `false`.
```yaml ```yaml
- name: Install the latest version of uv without problem matchers - name: Install the latest version of uv without problem matchers
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
add-problem-matchers: false add-problem-matchers: false
``` ```
+8 -8
View File
@@ -9,7 +9,7 @@ This allows directly using it in later steps:
```yaml ```yaml
- name: Install the latest version of uv and activate the environment - name: Install the latest version of uv and activate the environment
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
activate-environment: true activate-environment: true
- run: uv pip install pip - run: uv pip install pip
@@ -32,7 +32,7 @@ key: build-${{ runner.os }}-${{ runner.arch }}-${{ steps.setup-uv.outputs.python
You can customize the venv location with `venv-path`, for example to place it in the runner temp directory: You can customize the venv location with `venv-path`, for example to place it in the runner temp directory:
```yaml ```yaml
- uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
activate-environment: true activate-environment: true
venv-path: ${{ runner.temp }}/custom-venv venv-path: ${{ runner.temp }}/custom-venv
@@ -63,7 +63,7 @@ are not sufficient, you can provide a custom GitHub token with the necessary per
```yaml ```yaml
- name: Install the latest version of uv with a custom GitHub token - name: Install the latest version of uv with a custom GitHub token
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
github-token: ${{ secrets.CUSTOM_GITHUB_TOKEN }} github-token: ${{ secrets.CUSTOM_GITHUB_TOKEN }}
``` ```
@@ -81,7 +81,7 @@ input:
```yaml ```yaml
- name: Install the latest version of uv with a custom tool dir - name: Install the latest version of uv with a custom tool dir
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
tool-dir: "/path/to/tool/dir" tool-dir: "/path/to/tool/dir"
``` ```
@@ -100,7 +100,7 @@ If you want to change this behaviour (especially on self-hosted runners) you can
```yaml ```yaml
- name: Install the latest version of uv with a custom tool bin dir - name: Install the latest version of uv with a custom tool bin dir
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
tool-bin-dir: "/path/to/tool-bin/dir" tool-bin-dir: "/path/to/tool-bin/dir"
``` ```
@@ -117,7 +117,7 @@ This action supports expanding the `~` character to the user's home directory fo
```yaml ```yaml
- name: Expand the tilde character - name: Expand the tilde character
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
cache-local-path: "~/path/to/cache" cache-local-path: "~/path/to/cache"
tool-dir: "~/path/to/tool/dir" tool-dir: "~/path/to/tool/dir"
@@ -134,7 +134,7 @@ If you want to ignore this, set the `ignore-empty-workdir` input to `true`.
```yaml ```yaml
- name: Ignore empty workdir - name: Ignore empty workdir
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
ignore-empty-workdir: true ignore-empty-workdir: true
``` ```
@@ -160,7 +160,7 @@ This action sets several environment variables that influence uv's behavior and
```yaml ```yaml
- name: Example using environment variables - name: Example using environment variables
uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with: with:
python-version: "3.12" python-version: "3.12"
tool-dir: "/custom/tool/dir" tool-dir: "/custom/tool/dir"
+4 -2
View File
@@ -159,8 +159,10 @@ async function saveCacheToKey(
} }
log.info(`Including ${cacheName} path: ${cachePath}`); log.info(`Including ${cacheName} path: ${cachePath}`);
await cache.saveCache([cachePath], cacheKey); const cacheId = await cache.saveCache([cachePath], cacheKey);
log.info(`${cacheName} saved with key: ${cacheKey}`); if (cacheId !== -1) {
log.info(`${cacheName} saved with key: ${cacheKey}`);
}
} }
run(); run();
+3 -1
View File
@@ -69,5 +69,7 @@ function getUvVersionFromAllDependencies(
function getUvVersionFromDependency(dependency: string): string | undefined { function getUvVersionFromDependency(dependency: string): string | undefined {
const dependencyWithoutMarker = dependency.split(";", 1)[0]?.trim(); const dependencyWithoutMarker = dependency.split(";", 1)[0]?.trim();
return dependencyWithoutMarker?.match(/^uv([=<>~!]+\S*)/)?.[1].trim(); return dependencyWithoutMarker
?.match(/^uv\s*((?:[=<>~!]+\s*[^\s,]+\s*,?\s*)+)/)?.[1]
.replace(/\s/g, "");
} }
+18 -3
View File
@@ -169,7 +169,9 @@ async function findHighestSatisfyingVersion(
): Promise<string | undefined> { ): Promise<string | undefined> {
// This fast path assumes uv releases are semver-monotonic: newer manifest // This fast path assumes uv releases are semver-monotonic: newer manifest
// entries always have higher versions, with no lower-version backports. // entries always have higher versions, with no lower-version backports.
const semverRange = semver.validRange(versionSpecifier); const semverRange = isCompatibleReleaseSpecifier(versionSpecifier)
? null
: semver.validRange(versionSpecifier);
if (semverRange !== null) { if (semverRange !== null) {
const semverMatch = await getFirstMatchingVersion((version) => const semverMatch = await getFirstMatchingVersion((version) =>
semver.satisfies(version, semverRange), semver.satisfies(version, semverRange),
@@ -193,11 +195,22 @@ async function findHighestSatisfyingVersion(
return pep440Match; return pep440Match;
} }
/**
* `~=` is the PEP 440 compatible release operator. semver accepts it as a
* tilde range with a narrower upper bound (`~=0.5` as `<0.6.0` instead of
* `<1.0`), so it must only be evaluated as PEP 440.
*/
function isCompatibleReleaseSpecifier(versionSpecifier: string): boolean {
return versionSpecifier.includes("~=");
}
function maxSatisfying( function maxSatisfying(
versions: string[], versions: string[],
version: string, version: string,
): string | undefined { ): string | undefined {
const maxSemver = tc.evaluateVersions(versions, version); const maxSemver = isCompatibleReleaseSpecifier(version)
? ""
: tc.evaluateVersions(versions, version);
if (maxSemver !== "") { if (maxSemver !== "") {
core.debug(`Found a version that satisfies the semver range: ${maxSemver}`); core.debug(`Found a version that satisfies the semver range: ${maxSemver}`);
return maxSemver; return maxSemver;
@@ -218,7 +231,9 @@ function minSatisfying(
versions: string[], versions: string[],
version: string, version: string,
): string | undefined { ): string | undefined {
const minSemver = semver.minSatisfying(versions, version); const minSemver = isCompatibleReleaseSpecifier(version)
? null
: semver.minSatisfying(versions, version);
if (minSemver !== null) { if (minSemver !== null) {
core.debug(`Found a version that satisfies the semver range: ${minSemver}`); core.debug(`Found a version that satisfies the semver range: ${minSemver}`);
return minSemver; return minSemver;