mirror of
https://github.com/astral-sh/setup-uv.git
synced 2026-11-05 01:35:32 +00:00
## 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>
76 lines
2.2 KiB
TypeScript
76 lines
2.2 KiB
TypeScript
import fs from "node:fs";
|
|
import * as toml from "smol-toml";
|
|
|
|
export function getUvVersionFromRequirementsFile(
|
|
filePath: string,
|
|
): string | undefined {
|
|
const fileContent = fs.readFileSync(filePath, "utf-8");
|
|
|
|
if (filePath.endsWith(".txt")) {
|
|
return getUvVersionFromRequirementsText(fileContent);
|
|
}
|
|
|
|
return getUvVersionFromPyprojectContent(fileContent);
|
|
}
|
|
|
|
export function getUvVersionFromRequirementsText(
|
|
fileContent: string,
|
|
): string | undefined {
|
|
return getUvVersionFromAllDependencies(fileContent.split("\n"));
|
|
}
|
|
|
|
export function getUvVersionFromParsedPyproject(
|
|
pyproject: Pyproject,
|
|
): string | undefined {
|
|
const dependencies: string[] = pyproject?.project?.dependencies || [];
|
|
const optionalDependencies: string[] = Object.values(
|
|
pyproject?.project?.["optional-dependencies"] || {},
|
|
).flat();
|
|
const devDependencies: string[] = Object.values(
|
|
pyproject?.["dependency-groups"] || {},
|
|
)
|
|
.flat()
|
|
.filter((item: string | object) => typeof item === "string");
|
|
|
|
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
|
|
.map(getUvVersionFromDependency)
|
|
.find((version): version is string => version !== undefined);
|
|
}
|
|
|
|
function getUvVersionFromDependency(dependency: string): string | undefined {
|
|
const dependencyWithoutMarker = dependency.split(";", 1)[0]?.trim();
|
|
return dependencyWithoutMarker
|
|
?.match(/^uv\s*((?:[=<>~!]+\s*[^\s,]+\s*,?\s*)+)/)?.[1]
|
|
.replace(/\s/g, "");
|
|
}
|