Files
setup-uv/src/download/download-version.ts
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

189 lines
5.4 KiB
TypeScript
Raw Normal View History

2025-08-12 22:12:10 +02:00
import { promises as fs } from "node:fs";
import * as path from "node:path";
2024-09-05 08:06:45 -04:00
import * as core from "@actions/core";
import * as tc from "@actions/tool-cache";
import {
ASTRAL_MIRROR_PREFIX,
GITHUB_RELEASES_PREFIX,
TOOL_CACHE_NAME,
2026-03-23 09:15:51 +01:00
VERSIONS_MANIFEST_URL,
} from "../utils/constants";
import type { Architecture, Platform } from "../utils/platforms";
2024-09-05 08:06:45 -04:00
import { validateChecksum } from "./checksum/checksum";
2026-04-11 11:38:41 +02:00
import { getArtifact } from "./manifest";
export { resolveVersion } from "../version/resolve";
2025-10-07 08:44:57 +02:00
2024-08-23 23:58:26 +02:00
export function tryGetFromToolCache(
arch: Architecture,
2024-09-05 08:06:45 -04:00
version: string,
2024-09-18 11:29:09 +02:00
): { version: string; installedPath: string | undefined } {
2024-09-05 08:06:45 -04:00
core.debug(`Trying to get uv from tool cache for ${version}...`);
const cachedVersions = tc.findAllVersions(TOOL_CACHE_NAME, arch);
core.debug(`Cached versions: ${cachedVersions}`);
2024-09-18 11:29:09 +02:00
let resolvedVersion = tc.evaluateVersions(cachedVersions, version);
if (resolvedVersion === "") {
resolvedVersion = version;
}
const installedPath = tc.find(TOOL_CACHE_NAME, resolvedVersion, arch);
2025-08-12 22:12:10 +02:00
return { installedPath, version: resolvedVersion };
2024-08-23 23:58:26 +02:00
}
2026-03-23 09:15:51 +01:00
export async function downloadVersion(
2024-08-23 23:58:26 +02:00
platform: Platform,
arch: Architecture,
version: string,
2026-03-28 16:23:26 +01:00
checksum: string | undefined,
2024-09-18 11:29:09 +02:00
githubToken: string,
2026-03-23 09:15:51 +01:00
manifestUrl?: string,
downloadFromAstralMirror = true,
2024-09-18 11:29:09 +02:00
): Promise<{ version: string; cachedToolDir: string }> {
2026-03-23 09:15:51 +01:00
const artifact = await getArtifact(version, arch, platform, manifestUrl);
if (!artifact) {
throw new Error(
2026-03-23 09:15:51 +01:00
getMissingArtifactMessage(version, arch, platform, manifestUrl),
);
}
2026-03-23 09:15:51 +01:00
// For the default astral-sh/versions source, checksum validation relies on
// user input or the built-in KNOWN_CHECKSUMS table, not manifest sha256 values.
2026-03-28 16:23:26 +01:00
const resolvedChecksum =
2026-03-23 09:15:51 +01:00
manifestUrl === undefined
2026-03-28 16:23:26 +01:00
? checksum
: resolveChecksum(checksum, artifact.checksum);
2026-03-23 09:15:51 +01:00
const mirrorUrl = downloadFromAstralMirror
? rewriteToMirror(artifact.downloadUrl)
: undefined;
2026-03-23 09:15:51 +01:00
const downloadUrl = mirrorUrl ?? artifact.downloadUrl;
try {
2026-03-23 09:15:51 +01:00
return await downloadArtifact(
downloadUrl,
`uv-${arch}-${platform}`,
platform,
arch,
version,
2026-03-28 16:23:26 +01:00
resolvedChecksum,
githubTokenForUrl(downloadUrl, githubToken),
);
} catch (err) {
if (mirrorUrl === undefined) {
throw err;
}
core.warning(
`Failed to download from mirror, falling back to GitHub Releases: ${(err as Error).message}`,
);
2026-03-23 09:15:51 +01:00
return await downloadArtifact(
artifact.downloadUrl,
`uv-${arch}-${platform}`,
platform,
arch,
version,
2026-03-28 16:23:26 +01:00
resolvedChecksum,
githubTokenForUrl(artifact.downloadUrl, githubToken),
);
}
}
/**
* Rewrite a GitHub Releases URL to the Astral mirror.
* Returns `undefined` if the URL does not match the expected GitHub prefix.
*/
export function rewriteToMirror(url: string): string | undefined {
if (!url.startsWith(GITHUB_RELEASES_PREFIX)) {
return undefined;
}
2026-03-23 09:15:51 +01:00
return ASTRAL_MIRROR_PREFIX + url.slice(GITHUB_RELEASES_PREFIX.length);
2025-06-18 22:33:20 +02:00
}
2024-08-23 23:58:26 +02:00
function githubTokenForUrl(
downloadUrl: string,
githubToken: string,
): string | undefined {
try {
return new URL(downloadUrl).origin === "https://github.com"
? githubToken
: undefined;
} catch {
return undefined;
}
}
2026-03-23 09:15:51 +01:00
async function downloadArtifact(
2025-06-18 22:33:20 +02:00
downloadUrl: string,
artifactName: string,
platform: Platform,
arch: Architecture,
version: string,
checksum: string | undefined,
githubToken: string | undefined,
2025-06-18 22:33:20 +02:00
): Promise<{ version: string; cachedToolDir: string }> {
core.info(`Downloading uv from "${downloadUrl}" ...`);
2024-08-23 23:58:26 +02:00
const downloadPath = await tc.downloadTool(
downloadUrl,
undefined,
2024-09-05 08:06:45 -04:00
githubToken,
);
await validateChecksum(checksum, downloadPath, arch, platform, version);
2024-08-23 23:58:26 +02:00
2024-09-05 08:06:45 -04:00
let uvDir: string;
if (platform === "pc-windows-msvc") {
// On windows extracting the zip does not create an intermediate directory.
try {
// Try tar first as it's much faster, but only bsdtar supports zip files,
// so this may fail if another tar, like gnu tar, ends up being used.
uvDir = await tc.extractTar(downloadPath, undefined, "x");
} catch (err) {
core.info(
`Extracting with tar failed, falling back to zip extraction: ${(err as Error).message}`,
);
const extension = getExtension(platform);
const fullPathWithExtension = `${downloadPath}${extension}`;
await fs.copyFile(downloadPath, fullPathWithExtension);
uvDir = await tc.extractZip(fullPathWithExtension);
}
2024-08-23 23:58:26 +02:00
} else {
2024-09-05 08:06:45 -04:00
const extractedDir = await tc.extractTar(downloadPath);
2025-06-18 22:33:20 +02:00
uvDir = path.join(extractedDir, artifactName);
2024-08-23 23:58:26 +02:00
}
2024-09-18 11:29:09 +02:00
const cachedToolDir = await tc.cacheDir(
uvDir,
TOOL_CACHE_NAME,
2025-06-18 22:33:20 +02:00
version,
2024-09-18 11:29:09 +02:00
arch,
);
2026-03-23 09:15:51 +01:00
return { cachedToolDir, version };
}
function getMissingArtifactMessage(
version: string,
arch: Architecture,
platform: Platform,
manifestUrl?: string,
): string {
if (manifestUrl === undefined) {
return `Could not find artifact for version ${version}, arch ${arch}, platform ${platform} in ${VERSIONS_MANIFEST_URL} .`;
}
return `manifest-file does not contain version ${version}, arch ${arch}, platform ${platform}.`;
2025-06-18 22:33:20 +02:00
}
function resolveChecksum(
2026-03-28 16:23:26 +01:00
checksum: string | undefined,
2026-03-23 09:15:51 +01:00
manifestChecksum: string,
): string {
2026-03-28 16:23:26 +01:00
return checksum !== undefined && checksum !== ""
? checksum
: manifestChecksum;
}
2025-06-18 22:33:20 +02:00
function getExtension(platform: Platform): string {
return platform === "pc-windows-msvc" ? ".zip" : ".tar.gz";
2024-09-18 11:29:09 +02:00
}