feat: support uv.lock as a version-file source (#918)

Adds `uv.lock` as a supported `version-file` source. When `uv` is locked
as a
dependency in `uv.lock`, the action now installs the exact pinned
version,
closing the gap reported in #682.

This is useful for deterministic CI: the same uv version is used until
the
lockfile is updated, which avoids "CI worked yesterday, fails today"
drift and
reduces supply-chain exposure from auto-installing the latest release.

The implementation mirrors the existing `version-file` parsers — a new
`uv.lock`
entry in the parser registry reads the `[[package]]` whose `name = "uv"`
and
returns its locked `version`. Scoped to explicit `version-file:
uv.lock`;
workspace auto-detection is left as a possible follow-up to avoid
precedence
ambiguity with `uv.toml` / `pyproject.toml`.

Validation (local, Node 23; dist build is esbuild-deterministic):
- `npm run all` → build clean, biome clean, package clean, jest 77/77
- New tests: 3 unit (`uv-lock-file.test.ts`) + 1 integration — exact pin
resolves
  through the full pipeline (`uv.lock` → `0.8.17`)
- dist rebuilt + committed (single bundle, no spurious churn)

related: #682
This commit is contained in:
somaz
2026-06-19 14:08:57 +09:00
committed by GitHub
parent ca5ddd015e
commit 3faa3174e6
9 changed files with 124 additions and 11 deletions
+6
View File
@@ -9,6 +9,7 @@ import {
import { normalizeVersionSpecifier } from "./specifier";
import { getUvVersionFromToolVersions } from "./tool-versions-file";
import type { ParsedVersionFile, VersionFileFormat } from "./types";
import { getUvVersionFromUvLock } from "./uv-lock-file";
interface VersionFileParser {
format: VersionFileFormat;
@@ -49,6 +50,11 @@ const VERSION_FILE_PARSERS: VersionFileParser[] = [
},
supports: (filePath) => filePath.endsWith("pyproject.toml"),
},
{
format: "uv.lock",
parse: (filePath) => getUvVersionFromUvLock(filePath),
supports: (filePath) => filePath.endsWith("uv.lock"),
},
{
format: "requirements",
parse: (filePath) => {
+1
View File
@@ -11,6 +11,7 @@ export type VersionFileFormat =
| ".tool-versions"
| "pyproject.toml"
| "requirements"
| "uv.lock"
| "uv.toml";
export interface ParsedVersionFile {
+24
View File
@@ -0,0 +1,24 @@
import fs from "node:fs";
import * as toml from "smol-toml";
interface UvLockPackage {
name?: string;
version?: string;
}
interface UvLock {
package?: UvLockPackage[];
}
export function getUvVersionFromUvLock(filePath: string): string | undefined {
const fileContent = fs.readFileSync(filePath, "utf-8");
return getUvVersionFromUvLockContent(fileContent);
}
export function getUvVersionFromUvLockContent(
fileContent: string,
): string | undefined {
const parsed = toml.parse(fileContent) as UvLock;
const uvPackage = parsed.package?.find((pkg) => pkg.name === "uv");
return uvPackage?.version;
}