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

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

276 lines
8.0 KiB
TypeScript
Raw Normal View History

2025-08-12 22:12:10 +02:00
import fs from "node:fs";
import * as path from "node:path";
2025-08-12 22:12:10 +02:00
import * as core from "@actions/core";
import * as exec from "@actions/exec";
import { restoreCache } from "./cache/restore-cache";
2024-09-05 08:06:45 -04:00
import {
2025-06-18 22:33:20 +02:00
downloadVersionFromManifest,
2025-08-12 22:12:10 +02:00
resolveVersion,
tryGetFromToolCache,
2024-09-05 08:06:45 -04:00
} from "./download/download-version";
2025-10-07 16:08:30 +02:00
import { getConfigValueFromTomlFile } from "./utils/config-file";
2025-10-07 09:42:14 +02:00
import { STATE_UV_PATH, STATE_UV_VERSION } from "./utils/constants";
2025-08-12 22:12:10 +02:00
import {
activateEnvironment as activateEnvironmentInput,
2025-08-12 22:33:00 +02:00
addProblemMatchers,
2025-08-12 22:12:10 +02:00
cacheLocalPath,
checkSum,
enableCache,
githubToken,
ignoreEmptyWorkdir,
manifestFile,
pythonDir,
2025-08-12 22:12:10 +02:00
pythonVersion,
resolutionStrategy,
2025-08-12 22:12:10 +02:00
toolBinDir,
toolDir,
versionFile as versionFileInput,
version as versionInput,
workingDirectory,
} from "./utils/inputs";
2024-09-05 08:06:45 -04:00
import {
type Architecture,
2024-09-05 08:06:45 -04:00
getArch,
getPlatform,
type Platform,
2024-09-05 08:06:45 -04:00
} from "./utils/platforms";
2025-07-17 20:22:18 +02:00
import { getUvVersionFromFile } from "./version/resolve";
2024-08-23 23:58:26 +02:00
async function run(): Promise<void> {
2025-03-16 22:15:17 +01:00
detectEmptyWorkdir();
2025-02-17 10:32:34 +01:00
const platform = await getPlatform();
2024-09-05 08:06:45 -04:00
const arch = getArch();
2024-08-23 23:58:26 +02:00
try {
if (platform === undefined) {
2024-09-05 08:06:45 -04:00
throw new Error(`Unsupported platform: ${process.platform}`);
2024-08-23 23:58:26 +02:00
}
if (arch === undefined) {
2024-09-05 08:06:45 -04:00
throw new Error(`Unsupported architecture: ${process.arch}`);
2024-08-23 23:58:26 +02:00
}
const setupResult = await setupUv(platform, arch, checkSum, githubToken);
2024-08-23 23:58:26 +02:00
2024-09-21 10:14:36 +02:00
addToolBinToPath();
addUvToPathAndOutput(setupResult.uvDir);
2024-09-21 10:14:36 +02:00
setToolDir();
addPythonDirToPath();
setupPython();
await activateEnvironment();
2024-09-05 08:06:45 -04:00
addMatchers();
2025-10-07 16:08:30 +02:00
setCacheDir();
2024-08-23 23:58:26 +02:00
2024-11-28 21:41:37 +01:00
core.setOutput("uv-version", setupResult.version);
2025-10-07 09:42:14 +02:00
core.saveState(STATE_UV_VERSION, setupResult.version);
2024-11-28 21:41:37 +01:00
core.info(`Successfully installed uv version ${setupResult.version}`);
2024-08-23 23:58:26 +02:00
if (enableCache) {
2024-12-22 12:12:29 +01:00
await restoreCache();
2024-08-23 23:58:26 +02:00
}
// https://github.com/nodejs/node/issues/56645#issuecomment-3077594952
await new Promise((resolve) => setTimeout(resolve, 50));
2024-09-18 11:29:09 +02:00
process.exit(0);
2024-08-23 23:58:26 +02:00
} catch (err) {
2024-09-05 08:06:45 -04:00
core.setFailed((err as Error).message);
2024-08-23 23:58:26 +02:00
}
}
2025-03-16 22:15:17 +01:00
function detectEmptyWorkdir(): void {
if (fs.readdirSync(workingDirectory).length === 0) {
2025-03-16 22:15:17 +01:00
if (ignoreEmptyWorkdir) {
core.info(
"Empty workdir detected. Ignoring because ignore-empty-workdir is enabled",
);
} else {
core.warning(
"Empty workdir detected. This may cause unexpected behavior. You can enable ignore-empty-workdir to mute this warning.",
);
}
}
}
2024-08-23 23:58:26 +02:00
async function setupUv(
platform: Platform,
arch: Architecture,
checkSum: string | undefined,
2024-09-18 11:29:09 +02:00
githubToken: string,
2024-09-05 08:06:45 -04:00
): Promise<{ uvDir: string; version: string }> {
const resolvedVersion = await determineVersion(manifestFile);
const toolCacheResult = tryGetFromToolCache(arch, resolvedVersion);
if (toolCacheResult.installedPath) {
core.info(`Found uv in tool-cache for ${toolCacheResult.version}`);
return {
uvDir: toolCacheResult.installedPath,
version: toolCacheResult.version,
};
2024-08-23 23:58:26 +02:00
}
2025-10-03 19:48:56 +02:00
const downloadVersionResult = await downloadVersionFromManifest(
manifestFile,
platform,
arch,
resolvedVersion,
checkSum,
githubToken,
);
return {
uvDir: downloadVersionResult.cachedToolDir,
version: downloadVersionResult.version,
};
2024-08-23 23:58:26 +02:00
}
async function determineVersion(
manifestFile: string | undefined,
): Promise<string> {
if (versionInput !== "") {
return await resolveVersion(
versionInput,
manifestFile,
githubToken,
resolutionStrategy,
);
}
2025-07-17 20:22:18 +02:00
if (versionFileInput !== "") {
const versionFromFile = getUvVersionFromFile(versionFileInput);
if (versionFromFile === undefined) {
throw new Error(
`Could not determine uv version from file: ${versionFileInput}`,
);
}
return await resolveVersion(
versionFromFile,
manifestFile,
githubToken,
resolutionStrategy,
);
2025-07-17 20:22:18 +02:00
}
const versionFromUvToml = getUvVersionFromFile(
`${workingDirectory}${path.sep}uv.toml`,
);
2025-07-17 20:22:18 +02:00
const versionFromPyproject = getUvVersionFromFile(
`${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 await resolveVersion(
versionFromUvToml || versionFromPyproject || "latest",
manifestFile,
githubToken,
resolutionStrategy,
);
}
2025-03-21 13:44:23 +01:00
function addUvToPathAndOutput(cachedPath: string): void {
core.setOutput("uv-path", `${cachedPath}${path.sep}uv`);
2025-10-02 17:54:15 +02:00
core.saveState(STATE_UV_PATH, `${cachedPath}${path.sep}uv`);
2025-03-21 13:44:23 +01:00
core.setOutput("uvx-path", `${cachedPath}${path.sep}uvx`);
2025-10-02 17:54:15 +02:00
if (process.env.UV_NO_MODIFY_PATH !== undefined) {
core.info("UV_NO_MODIFY_PATH is set, not modifying PATH");
} else {
core.addPath(cachedPath);
core.info(`Added ${cachedPath} to the path`);
}
2024-08-23 23:58:26 +02:00
}
2024-09-21 10:14:36 +02:00
function addToolBinToPath(): void {
if (toolBinDir !== undefined) {
core.exportVariable("UV_TOOL_BIN_DIR", toolBinDir);
core.info(`Set UV_TOOL_BIN_DIR to ${toolBinDir}`);
2025-10-02 17:54:15 +02:00
if (process.env.UV_NO_MODIFY_PATH !== undefined) {
core.info(`UV_NO_MODIFY_PATH is set, not adding ${toolBinDir} to path`);
} else {
core.addPath(toolBinDir);
core.info(`Added ${toolBinDir} to the path`);
}
2024-09-21 10:14:36 +02:00
} else {
2025-10-02 17:54:15 +02:00
if (process.env.UV_NO_MODIFY_PATH !== undefined) {
core.info("UV_NO_MODIFY_PATH is set, not adding user local bin to path");
return;
}
2024-09-21 10:14:36 +02:00
if (process.env.XDG_BIN_HOME !== undefined) {
core.addPath(process.env.XDG_BIN_HOME);
core.info(`Added ${process.env.XDG_BIN_HOME} to the path`);
} else if (process.env.XDG_DATA_HOME !== undefined) {
core.addPath(`${process.env.XDG_DATA_HOME}/../bin`);
core.info(`Added ${process.env.XDG_DATA_HOME}/../bin to the path`);
} else {
core.addPath(`${process.env.HOME}/.local/bin`);
core.info(`Added ${process.env.HOME}/.local/bin to the path`);
}
}
}
function setToolDir(): void {
if (toolDir !== undefined) {
core.exportVariable("UV_TOOL_DIR", toolDir);
core.info(`Set UV_TOOL_DIR to ${toolDir}`);
}
}
function addPythonDirToPath(): void {
core.exportVariable("UV_PYTHON_INSTALL_DIR", pythonDir);
core.info(`Set UV_PYTHON_INSTALL_DIR to ${pythonDir}`);
if (process.env.UV_NO_MODIFY_PATH !== undefined) {
core.info("UV_NO_MODIFY_PATH is set, not adding python dir to path");
} else {
core.addPath(pythonDir);
core.info(`Added ${pythonDir} to the path`);
}
}
function setupPython(): void {
2024-11-28 21:41:37 +01:00
if (pythonVersion !== "") {
core.exportVariable("UV_PYTHON", pythonVersion);
core.info(`Set UV_PYTHON to ${pythonVersion}`);
}
}
async function activateEnvironment(): Promise<void> {
if (activateEnvironmentInput) {
2025-10-02 17:54:15 +02:00
if (process.env.UV_NO_MODIFY_PATH !== undefined) {
throw new Error(
"UV_NO_MODIFY_PATH and activate-environment cannot be used together.",
);
}
const execArgs = ["venv", ".venv", "--directory", workingDirectory];
core.info("Activating python venv...");
2025-04-17 21:02:21 +02:00
await exec.exec("uv", execArgs);
const venvPath = path.resolve(`${workingDirectory}${path.sep}.venv`);
let venvBinPath = `${venvPath}${path.sep}bin`;
if (process.platform === "win32") {
venvBinPath = `${venvPath}${path.sep}Scripts`;
}
core.addPath(path.resolve(venvBinPath));
core.exportVariable("VIRTUAL_ENV", venvPath);
core.setOutput("venv", venvPath);
2024-11-28 21:41:37 +01:00
}
}
2025-10-07 16:08:30 +02:00
function setCacheDir(): void {
if (enableCache) {
const cacheDirFromConfig = getConfigValueFromTomlFile("", "cache-dir");
if (cacheDirFromConfig !== undefined) {
core.info(
"Using cache-dir from uv config file, not modifying UV_CACHE_DIR",
);
return;
}
core.exportVariable("UV_CACHE_DIR", cacheLocalPath);
core.info(`Set UV_CACHE_DIR to ${cacheLocalPath}`);
}
2024-08-23 23:58:26 +02:00
}
function addMatchers(): void {
2025-08-12 22:33:00 +02:00
if (addProblemMatchers) {
const matchersPath = path.join(__dirname, `..${path.sep}..`, ".github");
core.info(`##[add-matcher]${path.join(matchersPath, "python.json")}`);
}
2024-08-23 23:58:26 +02:00
}
2024-09-05 08:06:45 -04:00
run();