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

311 lines
8.6 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 {
2026-03-23 09:15:51 +01:00
downloadVersion,
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 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,
CacheLocalSource,
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,
venvPath,
2025-08-12 22:12:10 +02:00
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
const sourceDir = __dirname;
async function getPythonVersion(): Promise<string> {
if (pythonVersion !== "") {
return pythonVersion;
}
let output = "";
const options: exec.ExecOptions = {
listeners: {
stdout: (data: Buffer) => {
output += data.toString();
},
},
silent: !core.isDebug(),
};
try {
const execArgs = ["python", "find", "--directory", workingDirectory];
await exec.exec("uv", execArgs, options);
const pythonPath = output.trim();
output = "";
await exec.exec(pythonPath, ["--version"], options);
// output is like "Python 3.8.10"
return output.split(" ")[1].trim();
} catch (error) {
const err = error as Error;
core.debug(`Failed to get python version from uv. Error: ${err.message}`);
return "unknown";
}
}
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}`);
const pythonVersion = await getPythonVersion();
core.setOutput("python-version", pythonVersion);
2024-08-23 23:58:26 +02:00
if (enableCache) {
await restoreCache(pythonVersion);
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 }> {
2026-03-23 09:15:51 +01:00
const resolvedVersion = await determineVersion();
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
}
2026-03-23 09:15:51 +01:00
const downloadResult = await downloadVersion(
platform,
arch,
resolvedVersion,
checkSum,
githubToken,
manifestFile,
);
return {
2026-03-23 09:15:51 +01:00
uvDir: downloadResult.cachedToolDir,
version: downloadResult.version,
};
2024-08-23 23:58:26 +02:00
}
2026-03-23 09:15:51 +01:00
async function determineVersion(): Promise<string> {
return await resolveVersion(
getRequestedVersion(),
manifestFile,
resolutionStrategy,
);
}
function getRequestedVersion(): string {
if (versionInput !== "") {
2026-03-23 09:15:51 +01:00
return versionInput;
}
2026-03-23 09:15:51 +01:00
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}`,
);
}
2026-03-23 09:15:51 +01:00
return versionFromFile;
2025-07-17 20:22:18 +02:00
}
2026-03-23 09:15:51 +01:00
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`,
);
2026-03-23 09:15:51 +01:00
if (versionFromUvToml === undefined && versionFromPyproject === undefined) {
core.info(
"Could not determine uv version from uv.toml or pyproject.toml. Falling back to latest.",
);
}
2026-03-23 09:15:51 +01:00
return versionFromUvToml || versionFromPyproject || "latest";
}
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.",
);
}
core.info(`Creating and activating python venv at ${venvPath}...`);
2026-02-06 10:48:26 +01:00
await exec.exec("uv", [
"venv",
venvPath,
"--directory",
workingDirectory,
"--clear",
]);
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 (cacheLocalPath !== undefined) {
if (cacheLocalPath.source === CacheLocalSource.Config) {
2025-10-07 16:08:30 +02:00
core.info(
"Using cache-dir from uv config file, not modifying UV_CACHE_DIR",
);
return;
}
core.exportVariable("UV_CACHE_DIR", cacheLocalPath.path);
core.info(`Set UV_CACHE_DIR to ${cacheLocalPath.path}`);
2025-10-07 16:08:30 +02:00
}
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(sourceDir, "..", "..", ".github");
2025-08-12 22:33:00 +02:00
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();