mirror of
https://github.com/astral-sh/setup-uv.git
synced 2026-03-29 01:49:53 +00:00
Refactor inputs (#823)
Don't load at import time and make it easier to test
This commit is contained in:
committed by
GitHub
parent
868d1f74d9
commit
f82eb19c06
@@ -17,7 +17,6 @@ const {
|
||||
fetchManifest,
|
||||
getAllVersions,
|
||||
getArtifact,
|
||||
getLatestVersion,
|
||||
parseManifest,
|
||||
} = await import("../../src/download/manifest");
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import {
|
||||
afterEach,
|
||||
beforeEach,
|
||||
@@ -7,9 +10,13 @@ import {
|
||||
jest,
|
||||
} from "@jest/globals";
|
||||
|
||||
// Will be mutated per test before (re-)importing the module under test
|
||||
let mockInputs: Record<string, string> = {};
|
||||
const tempDirs: string[] = [];
|
||||
const ORIGINAL_HOME = process.env.HOME;
|
||||
const ORIGINAL_RUNNER_ENVIRONMENT = process.env.RUNNER_ENVIRONMENT;
|
||||
const ORIGINAL_RUNNER_TEMP = process.env.RUNNER_TEMP;
|
||||
const ORIGINAL_UV_CACHE_DIR = process.env.UV_CACHE_DIR;
|
||||
const ORIGINAL_UV_PYTHON_INSTALL_DIR = process.env.UV_PYTHON_INSTALL_DIR;
|
||||
|
||||
const mockDebug = jest.fn();
|
||||
const mockGetBooleanInput = jest.fn(
|
||||
@@ -27,118 +34,228 @@ jest.unstable_mockModule("@actions/core", () => ({
|
||||
warning: mockWarning,
|
||||
}));
|
||||
|
||||
async function importInputsModule() {
|
||||
return await import("../../src/utils/inputs");
|
||||
const { CacheLocalSource, loadInputs } = await import("../../src/utils/inputs");
|
||||
|
||||
function createTempProject(files: Record<string, string>): string {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "setup-uv-inputs-test-"));
|
||||
tempDirs.push(dir);
|
||||
|
||||
for (const [relativePath, content] of Object.entries(files)) {
|
||||
const filePath = path.join(dir, relativePath);
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
fs.writeFileSync(filePath, content);
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
describe("cacheDependencyGlob", () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
function resetEnvironment(): void {
|
||||
jest.clearAllMocks();
|
||||
mockInputs = {};
|
||||
process.env.HOME = "/home/testuser";
|
||||
});
|
||||
delete process.env.RUNNER_ENVIRONMENT;
|
||||
delete process.env.RUNNER_TEMP;
|
||||
delete process.env.UV_CACHE_DIR;
|
||||
delete process.env.UV_PYTHON_INSTALL_DIR;
|
||||
}
|
||||
|
||||
function restoreEnvironment(): void {
|
||||
while (tempDirs.length > 0) {
|
||||
const dir = tempDirs.pop();
|
||||
if (dir !== undefined) {
|
||||
fs.rmSync(dir, { force: true, recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
process.env.HOME = ORIGINAL_HOME;
|
||||
});
|
||||
process.env.RUNNER_ENVIRONMENT = ORIGINAL_RUNNER_ENVIRONMENT;
|
||||
process.env.RUNNER_TEMP = ORIGINAL_RUNNER_TEMP;
|
||||
process.env.UV_CACHE_DIR = ORIGINAL_UV_CACHE_DIR;
|
||||
process.env.UV_PYTHON_INSTALL_DIR = ORIGINAL_UV_PYTHON_INSTALL_DIR;
|
||||
}
|
||||
|
||||
it("returns empty string when input not provided", async () => {
|
||||
beforeEach(() => {
|
||||
resetEnvironment();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
restoreEnvironment();
|
||||
});
|
||||
|
||||
describe("loadInputs", () => {
|
||||
it("loads defaults for a github-hosted runner", () => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
const { cacheDependencyGlob } = await importInputsModule();
|
||||
expect(cacheDependencyGlob).toBe("");
|
||||
mockInputs["enable-cache"] = "auto";
|
||||
process.env.RUNNER_ENVIRONMENT = "github-hosted";
|
||||
process.env.RUNNER_TEMP = "/runner-temp";
|
||||
|
||||
const inputs = loadInputs();
|
||||
|
||||
expect(inputs.enableCache).toBe(true);
|
||||
expect(inputs.cacheLocalPath).toEqual({
|
||||
path: "/runner-temp/setup-uv-cache",
|
||||
source: CacheLocalSource.Default,
|
||||
});
|
||||
expect(inputs.pythonDir).toBe("/runner-temp/uv-python-dir");
|
||||
expect(inputs.venvPath).toBe("/workspace/.venv");
|
||||
expect(inputs.manifestFile).toBeUndefined();
|
||||
expect(inputs.resolutionStrategy).toBe("highest");
|
||||
});
|
||||
|
||||
it("resolves a single relative path", async () => {
|
||||
it("uses cache-dir from pyproject.toml when present", () => {
|
||||
mockInputs["working-directory"] = createTempProject({
|
||||
"pyproject.toml": `[project]
|
||||
name = "uv-project"
|
||||
version = "0.1.0"
|
||||
|
||||
[tool.uv]
|
||||
cache-dir = "/tmp/pyproject-toml-defined-cache-path"
|
||||
`,
|
||||
});
|
||||
|
||||
const inputs = loadInputs();
|
||||
|
||||
expect(inputs.cacheLocalPath).toEqual({
|
||||
path: "/tmp/pyproject-toml-defined-cache-path",
|
||||
source: CacheLocalSource.Config,
|
||||
});
|
||||
expect(mockInfo).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Found cache-dir in"),
|
||||
);
|
||||
});
|
||||
|
||||
it("uses UV_CACHE_DIR from the environment", () => {
|
||||
mockInputs["working-directory"] = createTempProject({});
|
||||
process.env.UV_CACHE_DIR = "/env/cache-dir";
|
||||
|
||||
const inputs = loadInputs();
|
||||
|
||||
expect(inputs.cacheLocalPath).toEqual({
|
||||
path: "/env/cache-dir",
|
||||
source: CacheLocalSource.Env,
|
||||
});
|
||||
expect(mockInfo).toHaveBeenCalledWith(
|
||||
"UV_CACHE_DIR is already set to /env/cache-dir",
|
||||
);
|
||||
});
|
||||
|
||||
it("uses UV_PYTHON_INSTALL_DIR from the environment", () => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
mockInputs["cache-dependency-glob"] = "requirements.txt";
|
||||
const { cacheDependencyGlob } = await importInputsModule();
|
||||
expect(cacheDependencyGlob).toBe("/workspace/requirements.txt");
|
||||
process.env.UV_PYTHON_INSTALL_DIR = "/env/python-dir";
|
||||
|
||||
const inputs = loadInputs();
|
||||
|
||||
expect(inputs.pythonDir).toBe("/env/python-dir");
|
||||
expect(mockInfo).toHaveBeenCalledWith(
|
||||
"UV_PYTHON_INSTALL_DIR is already set to /env/python-dir",
|
||||
);
|
||||
});
|
||||
|
||||
it("strips leading ./ from relative path", async () => {
|
||||
it("warns when parsing a malformed pyproject.toml for cache-dir", () => {
|
||||
mockInputs["working-directory"] = createTempProject({
|
||||
"pyproject.toml": `[project]
|
||||
name = "malformed-pyproject-toml-project"
|
||||
version = "0.1.0"
|
||||
|
||||
[malformed-toml
|
||||
`,
|
||||
});
|
||||
|
||||
const inputs = loadInputs();
|
||||
|
||||
expect(inputs.cacheLocalPath).toBeUndefined();
|
||||
expect(mockWarning).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Error while parsing pyproject.toml:"),
|
||||
);
|
||||
});
|
||||
|
||||
it("throws for an invalid resolution strategy", () => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
mockInputs["cache-dependency-glob"] = "./uv.lock";
|
||||
const { cacheDependencyGlob } = await importInputsModule();
|
||||
expect(cacheDependencyGlob).toBe("/workspace/uv.lock");
|
||||
mockInputs["resolution-strategy"] = "middle";
|
||||
|
||||
expect(() => loadInputs()).toThrow(
|
||||
"Invalid resolution-strategy: middle. Must be 'highest' or 'lowest'.",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("cacheDependencyGlob", () => {
|
||||
it("returns empty string when input not provided", () => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
|
||||
const inputs = loadInputs();
|
||||
|
||||
expect(inputs.cacheDependencyGlob).toBe("");
|
||||
});
|
||||
|
||||
it("handles multiple lines, trimming whitespace, tilde expansion and absolute paths", async () => {
|
||||
it.each([
|
||||
["requirements.txt", "/workspace/requirements.txt"],
|
||||
["./uv.lock", "/workspace/uv.lock"],
|
||||
])("resolves %s to %s", (globInput, expected) => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
mockInputs["cache-dependency-glob"] = globInput;
|
||||
|
||||
const inputs = loadInputs();
|
||||
|
||||
expect(inputs.cacheDependencyGlob).toBe(expected);
|
||||
});
|
||||
|
||||
it("handles multiple lines, trimming whitespace, tilde expansion and absolute paths", () => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
mockInputs["cache-dependency-glob"] =
|
||||
" ~/.cache/file1\n ./rel/file2 \nfile3.txt";
|
||||
const { cacheDependencyGlob } = await importInputsModule();
|
||||
expect(cacheDependencyGlob).toBe(
|
||||
|
||||
const inputs = loadInputs();
|
||||
|
||||
expect(inputs.cacheDependencyGlob).toBe(
|
||||
[
|
||||
"/home/testuser/.cache/file1", // expanded tilde, absolute path unchanged
|
||||
"/workspace/rel/file2", // ./ stripped and resolved
|
||||
"/workspace/file3.txt", // relative path resolved
|
||||
"/home/testuser/.cache/file1",
|
||||
"/workspace/rel/file2",
|
||||
"/workspace/file3.txt",
|
||||
].join("\n"),
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps absolute path unchanged in multiline input", async () => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
mockInputs["cache-dependency-glob"] = "/abs/path.lock\nrelative.lock";
|
||||
const { cacheDependencyGlob } = await importInputsModule();
|
||||
expect(cacheDependencyGlob).toBe(
|
||||
it.each([
|
||||
[
|
||||
"/abs/path.lock\nrelative.lock",
|
||||
["/abs/path.lock", "/workspace/relative.lock"].join("\n"),
|
||||
);
|
||||
});
|
||||
|
||||
it("handles exclusions in relative paths correct", async () => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
mockInputs["cache-dependency-glob"] = "!/abs/path.lock\n!relative.lock";
|
||||
const { cacheDependencyGlob } = await importInputsModule();
|
||||
expect(cacheDependencyGlob).toBe(
|
||||
],
|
||||
[
|
||||
"!/abs/path.lock\n!relative.lock",
|
||||
["!/abs/path.lock", "!/workspace/relative.lock"].join("\n"),
|
||||
);
|
||||
],
|
||||
])("normalizes multiline glob %s", (globInput, expected) => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
mockInputs["cache-dependency-glob"] = globInput;
|
||||
|
||||
const inputs = loadInputs();
|
||||
|
||||
expect(inputs.cacheDependencyGlob).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe("tool directories", () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.clearAllMocks();
|
||||
mockInputs = {};
|
||||
process.env.HOME = "/home/testuser";
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env.HOME = ORIGINAL_HOME;
|
||||
});
|
||||
|
||||
it("expands tilde for tool-bin-dir and tool-dir", async () => {
|
||||
it("expands tilde for tool-bin-dir and tool-dir", () => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
mockInputs["tool-bin-dir"] = "~/tool-bin-dir";
|
||||
mockInputs["tool-dir"] = "~/tool-dir";
|
||||
|
||||
const { toolBinDir, toolDir } = await importInputsModule();
|
||||
const inputs = loadInputs();
|
||||
|
||||
expect(toolBinDir).toBe("/home/testuser/tool-bin-dir");
|
||||
expect(toolDir).toBe("/home/testuser/tool-dir");
|
||||
expect(inputs.toolBinDir).toBe("/home/testuser/tool-bin-dir");
|
||||
expect(inputs.toolDir).toBe("/home/testuser/tool-dir");
|
||||
});
|
||||
});
|
||||
|
||||
describe("cacheLocalPath", () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.clearAllMocks();
|
||||
mockInputs = {};
|
||||
process.env.HOME = "/home/testuser";
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env.HOME = ORIGINAL_HOME;
|
||||
});
|
||||
|
||||
it("expands tilde in cache-local-path", async () => {
|
||||
it("expands tilde in cache-local-path", () => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
mockInputs["cache-local-path"] = "~/uv-cache/cache-local-path";
|
||||
|
||||
const { CacheLocalSource, cacheLocalPath } = await importInputsModule();
|
||||
const inputs = loadInputs();
|
||||
|
||||
expect(cacheLocalPath).toEqual({
|
||||
expect(inputs.cacheLocalPath).toEqual({
|
||||
path: "/home/testuser/uv-cache/cache-local-path",
|
||||
source: CacheLocalSource.Input,
|
||||
});
|
||||
@@ -146,63 +263,37 @@ describe("cacheLocalPath", () => {
|
||||
});
|
||||
|
||||
describe("venvPath", () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.clearAllMocks();
|
||||
mockInputs = {};
|
||||
process.env.HOME = "/home/testuser";
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env.HOME = ORIGINAL_HOME;
|
||||
});
|
||||
|
||||
it("defaults to .venv in the working directory", async () => {
|
||||
it("defaults to .venv in the working directory", () => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
const { venvPath } = await importInputsModule();
|
||||
expect(venvPath).toBe("/workspace/.venv");
|
||||
|
||||
const inputs = loadInputs();
|
||||
|
||||
expect(inputs.venvPath).toBe("/workspace/.venv");
|
||||
});
|
||||
|
||||
it("resolves a relative venv-path", async () => {
|
||||
it.each([
|
||||
["custom-venv", "/workspace/custom-venv"],
|
||||
["custom-venv/", "/workspace/custom-venv"],
|
||||
["/tmp/custom-venv", "/tmp/custom-venv"],
|
||||
["~/.venv", "/home/testuser/.venv"],
|
||||
])("resolves venv-path %s to %s", (venvPathInput, expected) => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
mockInputs["activate-environment"] = "true";
|
||||
mockInputs["venv-path"] = "custom-venv";
|
||||
const { venvPath } = await importInputsModule();
|
||||
expect(venvPath).toBe("/workspace/custom-venv");
|
||||
mockInputs["venv-path"] = venvPathInput;
|
||||
|
||||
const inputs = loadInputs();
|
||||
|
||||
expect(inputs.venvPath).toBe(expected);
|
||||
});
|
||||
|
||||
it("normalizes venv-path with trailing slash", async () => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
mockInputs["activate-environment"] = "true";
|
||||
mockInputs["venv-path"] = "custom-venv/";
|
||||
const { venvPath } = await importInputsModule();
|
||||
expect(venvPath).toBe("/workspace/custom-venv");
|
||||
});
|
||||
|
||||
it("keeps an absolute venv-path unchanged", async () => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
mockInputs["activate-environment"] = "true";
|
||||
mockInputs["venv-path"] = "/tmp/custom-venv";
|
||||
const { venvPath } = await importInputsModule();
|
||||
expect(venvPath).toBe("/tmp/custom-venv");
|
||||
});
|
||||
|
||||
it("expands tilde in venv-path", async () => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
mockInputs["activate-environment"] = "true";
|
||||
mockInputs["venv-path"] = "~/.venv";
|
||||
const { venvPath } = await importInputsModule();
|
||||
expect(venvPath).toBe("/home/testuser/.venv");
|
||||
});
|
||||
|
||||
it("warns when venv-path is set but activate-environment is false", async () => {
|
||||
it("warns when venv-path is set but activate-environment is false", () => {
|
||||
mockInputs["working-directory"] = "/workspace";
|
||||
mockInputs["venv-path"] = "custom-venv";
|
||||
|
||||
const { activateEnvironment, venvPath } = await importInputsModule();
|
||||
const inputs = loadInputs();
|
||||
|
||||
expect(activateEnvironment).toBe(false);
|
||||
expect(venvPath).toBe("/workspace/custom-venv");
|
||||
expect(inputs.activateEnvironment).toBe(false);
|
||||
expect(inputs.venvPath).toBe("/workspace/custom-venv");
|
||||
expect(mockWarning).toHaveBeenCalledWith(
|
||||
"venv-path is only used when activate-environment is true",
|
||||
);
|
||||
|
||||
656
dist/save-cache/index.cjs
generated
vendored
656
dist/save-cache/index.cjs
generated
vendored
File diff suppressed because it is too large
Load Diff
2971
dist/setup/index.cjs
generated
vendored
2971
dist/setup/index.cjs
generated
vendored
File diff suppressed because it is too large
Load Diff
49
src/cache/restore-cache.ts
vendored
49
src/cache/restore-cache.ts
vendored
@@ -1,15 +1,7 @@
|
||||
import * as cache from "@actions/cache";
|
||||
import * as core from "@actions/core";
|
||||
import { hashFiles } from "../hash/hash-files";
|
||||
import {
|
||||
cacheDependencyGlob,
|
||||
cacheLocalPath,
|
||||
cachePython,
|
||||
cacheSuffix,
|
||||
pruneCache,
|
||||
pythonDir,
|
||||
restoreCache as shouldRestoreCache,
|
||||
} from "../utils/inputs";
|
||||
import type { SetupInputs } from "../utils/inputs";
|
||||
import { getArch, getOSNameVersion, getPlatform } from "../utils/platforms";
|
||||
|
||||
export const STATE_CACHE_KEY = "cache-key";
|
||||
@@ -18,18 +10,21 @@ export const STATE_PYTHON_CACHE_MATCHED_KEY = "python-cache-matched-key";
|
||||
|
||||
const CACHE_VERSION = "2";
|
||||
|
||||
export async function restoreCache(pythonVersion?: string): Promise<void> {
|
||||
const cacheKey = await computeKeys(pythonVersion);
|
||||
export async function restoreCache(
|
||||
inputs: SetupInputs,
|
||||
pythonVersion?: string,
|
||||
): Promise<void> {
|
||||
const cacheKey = await computeKeys(inputs, pythonVersion);
|
||||
core.saveState(STATE_CACHE_KEY, cacheKey);
|
||||
core.setOutput("cache-key", cacheKey);
|
||||
|
||||
if (!shouldRestoreCache) {
|
||||
if (!inputs.restoreCache) {
|
||||
core.info("restore-cache is false. Skipping restore cache step.");
|
||||
core.setOutput("python-cache-hit", false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cacheLocalPath === undefined) {
|
||||
if (inputs.cacheLocalPath === undefined) {
|
||||
throw new Error(
|
||||
"cache-local-path is not set. Cannot restore cache without a valid cache path.",
|
||||
);
|
||||
@@ -37,15 +32,15 @@ export async function restoreCache(pythonVersion?: string): Promise<void> {
|
||||
|
||||
await restoreCacheFromKey(
|
||||
cacheKey,
|
||||
cacheLocalPath.path,
|
||||
inputs.cacheLocalPath.path,
|
||||
STATE_CACHE_MATCHED_KEY,
|
||||
"cache-hit",
|
||||
);
|
||||
|
||||
if (cachePython) {
|
||||
if (inputs.cachePython) {
|
||||
await restoreCacheFromKey(
|
||||
`${cacheKey}-python`,
|
||||
pythonDir,
|
||||
inputs.pythonDir,
|
||||
STATE_PYTHON_CACHE_MATCHED_KEY,
|
||||
"python-cache-hit",
|
||||
);
|
||||
@@ -76,28 +71,34 @@ async function restoreCacheFromKey(
|
||||
handleMatchResult(matchedKey, cacheKey, stateKey, outputKey);
|
||||
}
|
||||
|
||||
async function computeKeys(pythonVersion?: string): Promise<string> {
|
||||
async function computeKeys(
|
||||
inputs: SetupInputs,
|
||||
pythonVersion?: string,
|
||||
): Promise<string> {
|
||||
let cacheDependencyPathHash = "-";
|
||||
if (cacheDependencyGlob !== "") {
|
||||
if (inputs.cacheDependencyGlob !== "") {
|
||||
core.info(
|
||||
`Searching files using cache dependency glob: ${cacheDependencyGlob.split("\n").join(",")}`,
|
||||
`Searching files using cache dependency glob: ${inputs.cacheDependencyGlob.split("\n").join(",")}`,
|
||||
);
|
||||
cacheDependencyPathHash += await hashFiles(
|
||||
inputs.cacheDependencyGlob,
|
||||
true,
|
||||
);
|
||||
cacheDependencyPathHash += await hashFiles(cacheDependencyGlob, true);
|
||||
if (cacheDependencyPathHash === "-") {
|
||||
core.warning(
|
||||
`No file matched to [${cacheDependencyGlob.split("\n").join(",")}]. The cache will never get invalidated. Make sure you have checked out the target repository and configured the cache-dependency-glob input correctly.`,
|
||||
`No file matched to [${inputs.cacheDependencyGlob.split("\n").join(",")}]. The cache will never get invalidated. Make sure you have checked out the target repository and configured the cache-dependency-glob input correctly.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (cacheDependencyPathHash === "-") {
|
||||
cacheDependencyPathHash = "-no-dependency-glob";
|
||||
}
|
||||
const suffix = cacheSuffix ? `-${cacheSuffix}` : "";
|
||||
const suffix = inputs.cacheSuffix ? `-${inputs.cacheSuffix}` : "";
|
||||
const version = pythonVersion ?? "unknown";
|
||||
const platform = await getPlatform();
|
||||
const osNameVersion = getOSNameVersion();
|
||||
const pruned = pruneCache ? "-pruned" : "";
|
||||
const python = cachePython ? "-py" : "";
|
||||
const pruned = inputs.pruneCache ? "-pruned" : "";
|
||||
const python = inputs.cachePython ? "-py" : "";
|
||||
return `setup-uv-${CACHE_VERSION}-${getArch()}-${platform}-${osNameVersion}-${version}${pruned}${python}${cacheDependencyPathHash}${suffix}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ export async function downloadVersion(
|
||||
platform: Platform,
|
||||
arch: Architecture,
|
||||
version: string,
|
||||
checkSum: string | undefined,
|
||||
checksum: string | undefined,
|
||||
githubToken: string,
|
||||
manifestUrl?: string,
|
||||
): Promise<{ version: string; cachedToolDir: string }> {
|
||||
@@ -47,10 +47,10 @@ export async function downloadVersion(
|
||||
|
||||
// For the default astral-sh/versions source, checksum validation relies on
|
||||
// user input or the built-in KNOWN_CHECKSUMS table, not manifest sha256 values.
|
||||
const checksum =
|
||||
const resolvedChecksum =
|
||||
manifestUrl === undefined
|
||||
? checkSum
|
||||
: resolveChecksum(checkSum, artifact.checksum);
|
||||
? checksum
|
||||
: resolveChecksum(checksum, artifact.checksum);
|
||||
|
||||
const mirrorUrl = rewriteToMirror(artifact.downloadUrl);
|
||||
const downloadUrl = mirrorUrl ?? artifact.downloadUrl;
|
||||
@@ -64,7 +64,7 @@ export async function downloadVersion(
|
||||
platform,
|
||||
arch,
|
||||
version,
|
||||
checksum,
|
||||
resolvedChecksum,
|
||||
downloadToken,
|
||||
);
|
||||
} catch (err) {
|
||||
@@ -82,7 +82,7 @@ export async function downloadVersion(
|
||||
platform,
|
||||
arch,
|
||||
version,
|
||||
checksum,
|
||||
resolvedChecksum,
|
||||
githubToken,
|
||||
);
|
||||
}
|
||||
@@ -161,11 +161,11 @@ function getMissingArtifactMessage(
|
||||
}
|
||||
|
||||
function resolveChecksum(
|
||||
checkSum: string | undefined,
|
||||
checksum: string | undefined,
|
||||
manifestChecksum: string,
|
||||
): string {
|
||||
return checkSum !== undefined && checkSum !== ""
|
||||
? checkSum
|
||||
return checksum !== undefined && checksum !== ""
|
||||
? checksum
|
||||
: manifestChecksum;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,21 +9,14 @@ import {
|
||||
STATE_PYTHON_CACHE_MATCHED_KEY,
|
||||
} from "./cache/restore-cache";
|
||||
import { STATE_UV_PATH, STATE_UV_VERSION } from "./utils/constants";
|
||||
import {
|
||||
cacheLocalPath,
|
||||
cachePython,
|
||||
enableCache,
|
||||
ignoreNothingToCache,
|
||||
pythonDir,
|
||||
pruneCache as shouldPruneCache,
|
||||
saveCache as shouldSaveCache,
|
||||
} from "./utils/inputs";
|
||||
import { loadInputs, type SetupInputs } from "./utils/inputs";
|
||||
|
||||
export async function run(): Promise<void> {
|
||||
try {
|
||||
if (enableCache) {
|
||||
if (shouldSaveCache) {
|
||||
await saveCache();
|
||||
const inputs = loadInputs();
|
||||
if (inputs.enableCache) {
|
||||
if (inputs.saveCache) {
|
||||
await saveCache(inputs);
|
||||
} else {
|
||||
core.info("save-cache is false. Skipping save cache step.");
|
||||
}
|
||||
@@ -43,7 +36,7 @@ export async function run(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function saveCache(): Promise<void> {
|
||||
async function saveCache(inputs: SetupInputs): Promise<void> {
|
||||
const cacheKey = core.getState(STATE_CACHE_KEY);
|
||||
const matchedKey = core.getState(STATE_CACHE_MATCHED_KEY);
|
||||
|
||||
@@ -54,13 +47,13 @@ async function saveCache(): Promise<void> {
|
||||
if (matchedKey === cacheKey) {
|
||||
core.info(`Cache hit occurred on key ${cacheKey}, not saving cache.`);
|
||||
} else {
|
||||
if (shouldPruneCache) {
|
||||
if (inputs.pruneCache) {
|
||||
await pruneCache();
|
||||
}
|
||||
|
||||
const actualCachePath = getUvCachePath();
|
||||
const actualCachePath = getUvCachePath(inputs);
|
||||
if (!fs.existsSync(actualCachePath)) {
|
||||
if (ignoreNothingToCache) {
|
||||
if (inputs.ignoreNothingToCache) {
|
||||
core.info(
|
||||
"No cacheable uv cache paths were found. Ignoring because ignore-nothing-to-cache is enabled.",
|
||||
);
|
||||
@@ -79,10 +72,10 @@ async function saveCache(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
if (cachePython) {
|
||||
if (!fs.existsSync(pythonDir)) {
|
||||
if (inputs.cachePython) {
|
||||
if (!fs.existsSync(inputs.pythonDir)) {
|
||||
core.warning(
|
||||
`Python cache path ${pythonDir} does not exist on disk. Skipping Python cache save because no managed Python installation was found. If you want uv to install managed Python instead of using a system interpreter, set UV_PYTHON_PREFERENCE=only-managed.`,
|
||||
`Python cache path ${inputs.pythonDir} does not exist on disk. Skipping Python cache save because no managed Python installation was found. If you want uv to install managed Python instead of using a system interpreter, set UV_PYTHON_PREFERENCE=only-managed.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -90,7 +83,7 @@ async function saveCache(): Promise<void> {
|
||||
const pythonCacheKey = `${cacheKey}-python`;
|
||||
await saveCacheToKey(
|
||||
pythonCacheKey,
|
||||
pythonDir,
|
||||
inputs.pythonDir,
|
||||
STATE_PYTHON_CACHE_MATCHED_KEY,
|
||||
"Python cache",
|
||||
);
|
||||
@@ -113,22 +106,22 @@ async function pruneCache(): Promise<void> {
|
||||
await exec.exec(uvPath, execArgs, options);
|
||||
}
|
||||
|
||||
function getUvCachePath(): string {
|
||||
if (cacheLocalPath === undefined) {
|
||||
function getUvCachePath(inputs: SetupInputs): string {
|
||||
if (inputs.cacheLocalPath === undefined) {
|
||||
throw new Error(
|
||||
"cache-local-path is not set. Cannot save cache without a valid cache path.",
|
||||
);
|
||||
}
|
||||
if (
|
||||
process.env.UV_CACHE_DIR &&
|
||||
process.env.UV_CACHE_DIR !== cacheLocalPath.path
|
||||
process.env.UV_CACHE_DIR !== inputs.cacheLocalPath.path
|
||||
) {
|
||||
core.warning(
|
||||
`The environment variable UV_CACHE_DIR has been changed to "${process.env.UV_CACHE_DIR}", by an action or step running after astral-sh/setup-uv. This can lead to unexpected behavior. If you expected this to happen set the cache-local-path input to "${process.env.UV_CACHE_DIR}" instead of "${cacheLocalPath.path}".`,
|
||||
`The environment variable UV_CACHE_DIR has been changed to "${process.env.UV_CACHE_DIR}", by an action or step running after astral-sh/setup-uv. This can lead to unexpected behavior. If you expected this to happen set the cache-local-path input to "${process.env.UV_CACHE_DIR}" instead of "${inputs.cacheLocalPath.path}".`,
|
||||
);
|
||||
return process.env.UV_CACHE_DIR;
|
||||
}
|
||||
return cacheLocalPath.path;
|
||||
return inputs.cacheLocalPath.path;
|
||||
}
|
||||
|
||||
async function saveCacheToKey(
|
||||
|
||||
173
src/setup-uv.ts
173
src/setup-uv.ts
@@ -9,26 +9,7 @@ import {
|
||||
tryGetFromToolCache,
|
||||
} from "./download/download-version";
|
||||
import { STATE_UV_PATH, STATE_UV_VERSION } from "./utils/constants";
|
||||
import {
|
||||
activateEnvironment as activateEnvironmentInput,
|
||||
addProblemMatchers,
|
||||
CacheLocalSource,
|
||||
cacheLocalPath,
|
||||
checkSum,
|
||||
enableCache,
|
||||
githubToken,
|
||||
ignoreEmptyWorkdir,
|
||||
manifestFile,
|
||||
pythonDir,
|
||||
pythonVersion,
|
||||
resolutionStrategy,
|
||||
toolBinDir,
|
||||
toolDir,
|
||||
venvPath,
|
||||
versionFile as versionFileInput,
|
||||
version as versionInput,
|
||||
workingDirectory,
|
||||
} from "./utils/inputs";
|
||||
import { CacheLocalSource, loadInputs, type SetupInputs } from "./utils/inputs";
|
||||
import {
|
||||
type Architecture,
|
||||
getArch,
|
||||
@@ -39,9 +20,9 @@ import { getUvVersionFromFile } from "./version/resolve";
|
||||
|
||||
const sourceDir = __dirname;
|
||||
|
||||
async function getPythonVersion(): Promise<string> {
|
||||
if (pythonVersion !== "") {
|
||||
return pythonVersion;
|
||||
async function getPythonVersion(inputs: SetupInputs): Promise<string> {
|
||||
if (inputs.pythonVersion !== "") {
|
||||
return inputs.pythonVersion;
|
||||
}
|
||||
|
||||
let output = "";
|
||||
@@ -55,7 +36,7 @@ async function getPythonVersion(): Promise<string> {
|
||||
};
|
||||
|
||||
try {
|
||||
const execArgs = ["python", "find", "--directory", workingDirectory];
|
||||
const execArgs = ["python", "find", "--directory", inputs.workingDirectory];
|
||||
await exec.exec("uv", execArgs, options);
|
||||
const pythonPath = output.trim();
|
||||
|
||||
@@ -71,37 +52,38 @@ async function getPythonVersion(): Promise<string> {
|
||||
}
|
||||
|
||||
async function run(): Promise<void> {
|
||||
detectEmptyWorkdir();
|
||||
try {
|
||||
const inputs = loadInputs();
|
||||
detectEmptyWorkdir(inputs);
|
||||
const platform = await getPlatform();
|
||||
const arch = getArch();
|
||||
|
||||
try {
|
||||
if (platform === undefined) {
|
||||
throw new Error(`Unsupported platform: ${process.platform}`);
|
||||
}
|
||||
if (arch === undefined) {
|
||||
throw new Error(`Unsupported architecture: ${process.arch}`);
|
||||
}
|
||||
const setupResult = await setupUv(platform, arch, checkSum, githubToken);
|
||||
const setupResult = await setupUv(inputs, platform, arch);
|
||||
|
||||
addToolBinToPath();
|
||||
addToolBinToPath(inputs);
|
||||
addUvToPathAndOutput(setupResult.uvDir);
|
||||
setToolDir();
|
||||
addPythonDirToPath();
|
||||
setupPython();
|
||||
await activateEnvironment();
|
||||
addMatchers();
|
||||
setCacheDir();
|
||||
setToolDir(inputs);
|
||||
addPythonDirToPath(inputs);
|
||||
setupPython(inputs);
|
||||
await activateEnvironment(inputs);
|
||||
addMatchers(inputs);
|
||||
setCacheDir(inputs);
|
||||
|
||||
core.setOutput("uv-version", setupResult.version);
|
||||
core.saveState(STATE_UV_VERSION, setupResult.version);
|
||||
core.info(`Successfully installed uv version ${setupResult.version}`);
|
||||
|
||||
const pythonVersion = await getPythonVersion();
|
||||
core.setOutput("python-version", pythonVersion);
|
||||
const detectedPythonVersion = await getPythonVersion(inputs);
|
||||
core.setOutput("python-version", detectedPythonVersion);
|
||||
|
||||
if (enableCache) {
|
||||
await restoreCache(pythonVersion);
|
||||
if (inputs.enableCache) {
|
||||
await restoreCache(inputs, detectedPythonVersion);
|
||||
}
|
||||
// https://github.com/nodejs/node/issues/56645#issuecomment-3077594952
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
@@ -111,9 +93,9 @@ async function run(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
function detectEmptyWorkdir(): void {
|
||||
if (fs.readdirSync(workingDirectory).length === 0) {
|
||||
if (ignoreEmptyWorkdir) {
|
||||
function detectEmptyWorkdir(inputs: SetupInputs): void {
|
||||
if (fs.readdirSync(inputs.workingDirectory).length === 0) {
|
||||
if (inputs.ignoreEmptyWorkdir) {
|
||||
core.info(
|
||||
"Empty workdir detected. Ignoring because ignore-empty-workdir is enabled",
|
||||
);
|
||||
@@ -126,12 +108,11 @@ function detectEmptyWorkdir(): void {
|
||||
}
|
||||
|
||||
async function setupUv(
|
||||
inputs: SetupInputs,
|
||||
platform: Platform,
|
||||
arch: Architecture,
|
||||
checkSum: string | undefined,
|
||||
githubToken: string,
|
||||
): Promise<{ uvDir: string; version: string }> {
|
||||
const resolvedVersion = await determineVersion();
|
||||
const resolvedVersion = await determineVersion(inputs);
|
||||
const toolCacheResult = tryGetFromToolCache(arch, resolvedVersion);
|
||||
if (toolCacheResult.installedPath) {
|
||||
core.info(`Found uv in tool-cache for ${toolCacheResult.version}`);
|
||||
@@ -145,9 +126,9 @@ async function setupUv(
|
||||
platform,
|
||||
arch,
|
||||
resolvedVersion,
|
||||
checkSum,
|
||||
githubToken,
|
||||
manifestFile,
|
||||
inputs.checksum,
|
||||
inputs.githubToken,
|
||||
inputs.manifestFile,
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -156,34 +137,34 @@ async function setupUv(
|
||||
};
|
||||
}
|
||||
|
||||
async function determineVersion(): Promise<string> {
|
||||
async function determineVersion(inputs: SetupInputs): Promise<string> {
|
||||
return await resolveVersion(
|
||||
getRequestedVersion(),
|
||||
manifestFile,
|
||||
resolutionStrategy,
|
||||
getRequestedVersion(inputs),
|
||||
inputs.manifestFile,
|
||||
inputs.resolutionStrategy,
|
||||
);
|
||||
}
|
||||
|
||||
function getRequestedVersion(): string {
|
||||
if (versionInput !== "") {
|
||||
return versionInput;
|
||||
function getRequestedVersion(inputs: SetupInputs): string {
|
||||
if (inputs.version !== "") {
|
||||
return inputs.version;
|
||||
}
|
||||
|
||||
if (versionFileInput !== "") {
|
||||
const versionFromFile = getUvVersionFromFile(versionFileInput);
|
||||
if (inputs.versionFile !== "") {
|
||||
const versionFromFile = getUvVersionFromFile(inputs.versionFile);
|
||||
if (versionFromFile === undefined) {
|
||||
throw new Error(
|
||||
`Could not determine uv version from file: ${versionFileInput}`,
|
||||
`Could not determine uv version from file: ${inputs.versionFile}`,
|
||||
);
|
||||
}
|
||||
return versionFromFile;
|
||||
}
|
||||
|
||||
const versionFromUvToml = getUvVersionFromFile(
|
||||
`${workingDirectory}${path.sep}uv.toml`,
|
||||
`${inputs.workingDirectory}${path.sep}uv.toml`,
|
||||
);
|
||||
const versionFromPyproject = getUvVersionFromFile(
|
||||
`${workingDirectory}${path.sep}pyproject.toml`,
|
||||
`${inputs.workingDirectory}${path.sep}pyproject.toml`,
|
||||
);
|
||||
|
||||
if (versionFromUvToml === undefined && versionFromPyproject === undefined) {
|
||||
@@ -207,15 +188,17 @@ function addUvToPathAndOutput(cachedPath: string): void {
|
||||
}
|
||||
}
|
||||
|
||||
function addToolBinToPath(): void {
|
||||
if (toolBinDir !== undefined) {
|
||||
core.exportVariable("UV_TOOL_BIN_DIR", toolBinDir);
|
||||
core.info(`Set UV_TOOL_BIN_DIR to ${toolBinDir}`);
|
||||
function addToolBinToPath(inputs: SetupInputs): void {
|
||||
if (inputs.toolBinDir !== undefined) {
|
||||
core.exportVariable("UV_TOOL_BIN_DIR", inputs.toolBinDir);
|
||||
core.info(`Set UV_TOOL_BIN_DIR to ${inputs.toolBinDir}`);
|
||||
if (process.env.UV_NO_MODIFY_PATH !== undefined) {
|
||||
core.info(`UV_NO_MODIFY_PATH is set, not adding ${toolBinDir} to path`);
|
||||
core.info(
|
||||
`UV_NO_MODIFY_PATH is set, not adding ${inputs.toolBinDir} to path`,
|
||||
);
|
||||
} else {
|
||||
core.addPath(toolBinDir);
|
||||
core.info(`Added ${toolBinDir} to the path`);
|
||||
core.addPath(inputs.toolBinDir);
|
||||
core.info(`Added ${inputs.toolBinDir} to the path`);
|
||||
}
|
||||
} else {
|
||||
if (process.env.UV_NO_MODIFY_PATH !== undefined) {
|
||||
@@ -235,73 +218,73 @@ function addToolBinToPath(): void {
|
||||
}
|
||||
}
|
||||
|
||||
function setToolDir(): void {
|
||||
if (toolDir !== undefined) {
|
||||
core.exportVariable("UV_TOOL_DIR", toolDir);
|
||||
core.info(`Set UV_TOOL_DIR to ${toolDir}`);
|
||||
function setToolDir(inputs: SetupInputs): void {
|
||||
if (inputs.toolDir !== undefined) {
|
||||
core.exportVariable("UV_TOOL_DIR", inputs.toolDir);
|
||||
core.info(`Set UV_TOOL_DIR to ${inputs.toolDir}`);
|
||||
}
|
||||
}
|
||||
|
||||
function addPythonDirToPath(): void {
|
||||
core.exportVariable("UV_PYTHON_INSTALL_DIR", pythonDir);
|
||||
core.info(`Set UV_PYTHON_INSTALL_DIR to ${pythonDir}`);
|
||||
function addPythonDirToPath(inputs: SetupInputs): void {
|
||||
core.exportVariable("UV_PYTHON_INSTALL_DIR", inputs.pythonDir);
|
||||
core.info(`Set UV_PYTHON_INSTALL_DIR to ${inputs.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`);
|
||||
core.addPath(inputs.pythonDir);
|
||||
core.info(`Added ${inputs.pythonDir} to the path`);
|
||||
}
|
||||
}
|
||||
|
||||
function setupPython(): void {
|
||||
if (pythonVersion !== "") {
|
||||
core.exportVariable("UV_PYTHON", pythonVersion);
|
||||
core.info(`Set UV_PYTHON to ${pythonVersion}`);
|
||||
function setupPython(inputs: SetupInputs): void {
|
||||
if (inputs.pythonVersion !== "") {
|
||||
core.exportVariable("UV_PYTHON", inputs.pythonVersion);
|
||||
core.info(`Set UV_PYTHON to ${inputs.pythonVersion}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function activateEnvironment(): Promise<void> {
|
||||
if (activateEnvironmentInput) {
|
||||
async function activateEnvironment(inputs: SetupInputs): Promise<void> {
|
||||
if (inputs.activateEnvironment) {
|
||||
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}...`);
|
||||
core.info(`Creating and activating python venv at ${inputs.venvPath}...`);
|
||||
await exec.exec("uv", [
|
||||
"venv",
|
||||
venvPath,
|
||||
inputs.venvPath,
|
||||
"--directory",
|
||||
workingDirectory,
|
||||
inputs.workingDirectory,
|
||||
"--clear",
|
||||
]);
|
||||
|
||||
let venvBinPath = `${venvPath}${path.sep}bin`;
|
||||
let venvBinPath = `${inputs.venvPath}${path.sep}bin`;
|
||||
if (process.platform === "win32") {
|
||||
venvBinPath = `${venvPath}${path.sep}Scripts`;
|
||||
venvBinPath = `${inputs.venvPath}${path.sep}Scripts`;
|
||||
}
|
||||
core.addPath(path.resolve(venvBinPath));
|
||||
core.exportVariable("VIRTUAL_ENV", venvPath);
|
||||
core.setOutput("venv", venvPath);
|
||||
core.exportVariable("VIRTUAL_ENV", inputs.venvPath);
|
||||
core.setOutput("venv", inputs.venvPath);
|
||||
}
|
||||
}
|
||||
|
||||
function setCacheDir(): void {
|
||||
if (cacheLocalPath !== undefined) {
|
||||
if (cacheLocalPath.source === CacheLocalSource.Config) {
|
||||
function setCacheDir(inputs: SetupInputs): void {
|
||||
if (inputs.cacheLocalPath !== undefined) {
|
||||
if (inputs.cacheLocalPath.source === CacheLocalSource.Config) {
|
||||
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}`);
|
||||
core.exportVariable("UV_CACHE_DIR", inputs.cacheLocalPath.path);
|
||||
core.info(`Set UV_CACHE_DIR to ${inputs.cacheLocalPath.path}`);
|
||||
}
|
||||
}
|
||||
|
||||
function addMatchers(): void {
|
||||
if (addProblemMatchers) {
|
||||
function addMatchers(inputs: SetupInputs): void {
|
||||
if (inputs.addProblemMatchers) {
|
||||
const matchersPath = path.join(sourceDir, "..", "..", ".github");
|
||||
core.info(`##[add-matcher]${path.join(matchersPath, "python.json")}`);
|
||||
}
|
||||
|
||||
@@ -9,68 +9,152 @@ export enum CacheLocalSource {
|
||||
Default,
|
||||
}
|
||||
|
||||
export const workingDirectory = core.getInput("working-directory");
|
||||
export const version = core.getInput("version");
|
||||
export const versionFile = getVersionFile();
|
||||
export const pythonVersion = core.getInput("python-version");
|
||||
export const activateEnvironment = core.getBooleanInput("activate-environment");
|
||||
export const venvPath = getVenvPath();
|
||||
export const checkSum = core.getInput("checksum");
|
||||
export const enableCache = getEnableCache();
|
||||
export const restoreCache = core.getInput("restore-cache") === "true";
|
||||
export const saveCache = core.getInput("save-cache") === "true";
|
||||
export const cacheSuffix = core.getInput("cache-suffix") || "";
|
||||
export const cacheLocalPath = getCacheLocalPath();
|
||||
export const cacheDependencyGlob = getCacheDependencyGlob();
|
||||
export const pruneCache = core.getInput("prune-cache") === "true";
|
||||
export const cachePython = core.getInput("cache-python") === "true";
|
||||
export const ignoreNothingToCache =
|
||||
core.getInput("ignore-nothing-to-cache") === "true";
|
||||
export const ignoreEmptyWorkdir =
|
||||
core.getInput("ignore-empty-workdir") === "true";
|
||||
export const toolBinDir = getToolBinDir();
|
||||
export const toolDir = getToolDir();
|
||||
export const pythonDir = getUvPythonDir();
|
||||
export const githubToken = core.getInput("github-token");
|
||||
export const manifestFile = getManifestFile();
|
||||
export const addProblemMatchers =
|
||||
core.getInput("add-problem-matchers") === "true";
|
||||
export const resolutionStrategy = getResolutionStrategy();
|
||||
export interface CacheLocalPath {
|
||||
path: string;
|
||||
source: CacheLocalSource;
|
||||
}
|
||||
|
||||
function getVersionFile(): string {
|
||||
const versionFileInput = core.getInput("version-file");
|
||||
export interface SetupInputs {
|
||||
workingDirectory: string;
|
||||
version: string;
|
||||
versionFile: string;
|
||||
pythonVersion: string;
|
||||
activateEnvironment: boolean;
|
||||
venvPath: string;
|
||||
checksum: string;
|
||||
enableCache: boolean;
|
||||
restoreCache: boolean;
|
||||
saveCache: boolean;
|
||||
cacheSuffix: string;
|
||||
cacheLocalPath?: CacheLocalPath;
|
||||
cacheDependencyGlob: string;
|
||||
pruneCache: boolean;
|
||||
cachePython: boolean;
|
||||
ignoreNothingToCache: boolean;
|
||||
ignoreEmptyWorkdir: boolean;
|
||||
toolBinDir?: string;
|
||||
toolDir?: string;
|
||||
pythonDir: string;
|
||||
githubToken: string;
|
||||
manifestFile?: string;
|
||||
addProblemMatchers: boolean;
|
||||
resolutionStrategy: "highest" | "lowest";
|
||||
}
|
||||
|
||||
export function loadInputs(): SetupInputs {
|
||||
const workingDirectory = core.getInput("working-directory");
|
||||
const version = core.getInput("version");
|
||||
const versionFile = getVersionFile(
|
||||
workingDirectory,
|
||||
core.getInput("version-file"),
|
||||
);
|
||||
const pythonVersion = core.getInput("python-version");
|
||||
const activateEnvironment = core.getBooleanInput("activate-environment");
|
||||
const venvPath = getVenvPath(
|
||||
workingDirectory,
|
||||
core.getInput("venv-path"),
|
||||
activateEnvironment,
|
||||
);
|
||||
const checksum = core.getInput("checksum");
|
||||
const enableCache = getEnableCache(core.getInput("enable-cache"));
|
||||
const restoreCache = core.getInput("restore-cache") === "true";
|
||||
const saveCache = core.getInput("save-cache") === "true";
|
||||
const cacheSuffix = core.getInput("cache-suffix") || "";
|
||||
const cacheLocalPath = getCacheLocalPath(
|
||||
workingDirectory,
|
||||
versionFile,
|
||||
enableCache,
|
||||
);
|
||||
const cacheDependencyGlob = getCacheDependencyGlob(
|
||||
workingDirectory,
|
||||
core.getInput("cache-dependency-glob"),
|
||||
);
|
||||
const pruneCache = core.getInput("prune-cache") === "true";
|
||||
const cachePython = core.getInput("cache-python") === "true";
|
||||
const ignoreNothingToCache =
|
||||
core.getInput("ignore-nothing-to-cache") === "true";
|
||||
const ignoreEmptyWorkdir = core.getInput("ignore-empty-workdir") === "true";
|
||||
const toolBinDir = getToolBinDir(
|
||||
workingDirectory,
|
||||
core.getInput("tool-bin-dir"),
|
||||
);
|
||||
const toolDir = getToolDir(workingDirectory, core.getInput("tool-dir"));
|
||||
const pythonDir = getUvPythonDir();
|
||||
const githubToken = core.getInput("github-token");
|
||||
const manifestFile = getManifestFile(core.getInput("manifest-file"));
|
||||
const addProblemMatchers = core.getInput("add-problem-matchers") === "true";
|
||||
const resolutionStrategy = getResolutionStrategy(
|
||||
core.getInput("resolution-strategy"),
|
||||
);
|
||||
|
||||
return {
|
||||
activateEnvironment,
|
||||
addProblemMatchers,
|
||||
cacheDependencyGlob,
|
||||
cacheLocalPath,
|
||||
cachePython,
|
||||
cacheSuffix,
|
||||
checksum,
|
||||
enableCache,
|
||||
githubToken,
|
||||
ignoreEmptyWorkdir,
|
||||
ignoreNothingToCache,
|
||||
manifestFile,
|
||||
pruneCache,
|
||||
pythonDir,
|
||||
pythonVersion,
|
||||
resolutionStrategy,
|
||||
restoreCache,
|
||||
saveCache,
|
||||
toolBinDir,
|
||||
toolDir,
|
||||
venvPath,
|
||||
version,
|
||||
versionFile,
|
||||
workingDirectory,
|
||||
};
|
||||
}
|
||||
|
||||
function getVersionFile(
|
||||
workingDirectory: string,
|
||||
versionFileInput: string,
|
||||
): string {
|
||||
if (versionFileInput !== "") {
|
||||
const tildeExpanded = expandTilde(versionFileInput);
|
||||
return resolveRelativePath(tildeExpanded);
|
||||
return resolveRelativePath(workingDirectory, tildeExpanded);
|
||||
}
|
||||
return versionFileInput;
|
||||
}
|
||||
|
||||
function getVenvPath(): string {
|
||||
const venvPathInput = core.getInput("venv-path");
|
||||
function getVenvPath(
|
||||
workingDirectory: string,
|
||||
venvPathInput: string,
|
||||
activateEnvironment: boolean,
|
||||
): string {
|
||||
if (venvPathInput !== "") {
|
||||
if (!activateEnvironment) {
|
||||
core.warning("venv-path is only used when activate-environment is true");
|
||||
}
|
||||
const tildeExpanded = expandTilde(venvPathInput);
|
||||
return normalizePath(resolveRelativePath(tildeExpanded));
|
||||
return normalizePath(resolveRelativePath(workingDirectory, tildeExpanded));
|
||||
}
|
||||
return normalizePath(resolveRelativePath(".venv"));
|
||||
return normalizePath(resolveRelativePath(workingDirectory, ".venv"));
|
||||
}
|
||||
|
||||
function getEnableCache(): boolean {
|
||||
const enableCacheInput = core.getInput("enable-cache");
|
||||
function getEnableCache(enableCacheInput: string): boolean {
|
||||
if (enableCacheInput === "auto") {
|
||||
return process.env.RUNNER_ENVIRONMENT === "github-hosted";
|
||||
}
|
||||
return enableCacheInput === "true";
|
||||
}
|
||||
|
||||
function getToolBinDir(): string | undefined {
|
||||
const toolBinDirInput = core.getInput("tool-bin-dir");
|
||||
function getToolBinDir(
|
||||
workingDirectory: string,
|
||||
toolBinDirInput: string,
|
||||
): string | undefined {
|
||||
if (toolBinDirInput !== "") {
|
||||
const tildeExpanded = expandTilde(toolBinDirInput);
|
||||
return resolveRelativePath(tildeExpanded);
|
||||
return resolveRelativePath(workingDirectory, tildeExpanded);
|
||||
}
|
||||
if (process.platform === "win32") {
|
||||
if (process.env.RUNNER_TEMP !== undefined) {
|
||||
@@ -83,11 +167,13 @@ function getToolBinDir(): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getToolDir(): string | undefined {
|
||||
const toolDirInput = core.getInput("tool-dir");
|
||||
function getToolDir(
|
||||
workingDirectory: string,
|
||||
toolDirInput: string,
|
||||
): string | undefined {
|
||||
if (toolDirInput !== "") {
|
||||
const tildeExpanded = expandTilde(toolDirInput);
|
||||
return resolveRelativePath(tildeExpanded);
|
||||
return resolveRelativePath(workingDirectory, tildeExpanded);
|
||||
}
|
||||
if (process.platform === "win32") {
|
||||
if (process.env.RUNNER_TEMP !== undefined) {
|
||||
@@ -100,21 +186,23 @@ function getToolDir(): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getCacheLocalPath():
|
||||
| {
|
||||
path: string;
|
||||
source: CacheLocalSource;
|
||||
}
|
||||
| undefined {
|
||||
function getCacheLocalPath(
|
||||
workingDirectory: string,
|
||||
versionFile: string,
|
||||
enableCache: boolean,
|
||||
): CacheLocalPath | undefined {
|
||||
const cacheLocalPathInput = core.getInput("cache-local-path");
|
||||
if (cacheLocalPathInput !== "") {
|
||||
const tildeExpanded = expandTilde(cacheLocalPathInput);
|
||||
return {
|
||||
path: resolveRelativePath(tildeExpanded),
|
||||
path: resolveRelativePath(workingDirectory, tildeExpanded),
|
||||
source: CacheLocalSource.Input,
|
||||
};
|
||||
}
|
||||
const cacheDirFromConfig = getCacheDirFromConfig();
|
||||
const cacheDirFromConfig = getCacheDirFromConfig(
|
||||
workingDirectory,
|
||||
versionFile,
|
||||
);
|
||||
if (cacheDirFromConfig !== undefined) {
|
||||
return { path: cacheDirFromConfig, source: CacheLocalSource.Config };
|
||||
}
|
||||
@@ -122,7 +210,7 @@ function getCacheLocalPath():
|
||||
core.info(`UV_CACHE_DIR is already set to ${process.env.UV_CACHE_DIR}`);
|
||||
return { path: process.env.UV_CACHE_DIR, source: CacheLocalSource.Env };
|
||||
}
|
||||
if (getEnableCache()) {
|
||||
if (enableCache) {
|
||||
if (process.env.RUNNER_ENVIRONMENT === "github-hosted") {
|
||||
if (process.env.RUNNER_TEMP !== undefined) {
|
||||
return {
|
||||
@@ -147,9 +235,12 @@ function getCacheLocalPath():
|
||||
}
|
||||
}
|
||||
|
||||
function getCacheDirFromConfig(): string | undefined {
|
||||
function getCacheDirFromConfig(
|
||||
workingDirectory: string,
|
||||
versionFile: string,
|
||||
): string | undefined {
|
||||
for (const filePath of [versionFile, "uv.toml", "pyproject.toml"]) {
|
||||
const resolvedPath = resolveRelativePath(filePath);
|
||||
const resolvedPath = resolveRelativePath(workingDirectory, filePath);
|
||||
try {
|
||||
const cacheDir = getConfigValueFromTomlFile(resolvedPath, "cache-dir");
|
||||
if (cacheDir !== undefined) {
|
||||
@@ -175,9 +266,8 @@ export function getUvPythonDir(): string {
|
||||
if (process.env.RUNNER_ENVIRONMENT !== "github-hosted") {
|
||||
if (process.platform === "win32") {
|
||||
return `${process.env.APPDATA}${path.sep}uv${path.sep}python`;
|
||||
} else {
|
||||
return `${process.env.HOME}${path.sep}.local${path.sep}share${path.sep}uv${path.sep}python`;
|
||||
}
|
||||
return `${process.env.HOME}${path.sep}.local${path.sep}share${path.sep}uv${path.sep}python`;
|
||||
}
|
||||
if (process.env.RUNNER_TEMP !== undefined) {
|
||||
return `${process.env.RUNNER_TEMP}${path.sep}uv-python-dir`;
|
||||
@@ -187,14 +277,16 @@ export function getUvPythonDir(): string {
|
||||
);
|
||||
}
|
||||
|
||||
function getCacheDependencyGlob(): string {
|
||||
const cacheDependencyGlobInput = core.getInput("cache-dependency-glob");
|
||||
function getCacheDependencyGlob(
|
||||
workingDirectory: string,
|
||||
cacheDependencyGlobInput: string,
|
||||
): string {
|
||||
if (cacheDependencyGlobInput !== "") {
|
||||
return cacheDependencyGlobInput
|
||||
.split("\n")
|
||||
.map((part) => part.trim())
|
||||
.map((part) => expandTilde(part))
|
||||
.map((part) => resolveRelativePath(part))
|
||||
.map((part) => resolveRelativePath(workingDirectory, part))
|
||||
.join("\n");
|
||||
}
|
||||
return cacheDependencyGlobInput;
|
||||
@@ -220,7 +312,10 @@ function normalizePath(inputPath: string): string {
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
function resolveRelativePath(inputPath: string): string {
|
||||
function resolveRelativePath(
|
||||
workingDirectory: string,
|
||||
inputPath: string,
|
||||
): string {
|
||||
const hasNegation = inputPath.startsWith("!");
|
||||
const pathWithoutNegation = hasNegation ? inputPath.substring(1) : inputPath;
|
||||
|
||||
@@ -232,16 +327,16 @@ function resolveRelativePath(inputPath: string): string {
|
||||
return hasNegation ? `!${resolvedPath}` : resolvedPath;
|
||||
}
|
||||
|
||||
function getManifestFile(): string | undefined {
|
||||
const manifestFileInput = core.getInput("manifest-file");
|
||||
function getManifestFile(manifestFileInput: string): string | undefined {
|
||||
if (manifestFileInput !== "") {
|
||||
return manifestFileInput;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getResolutionStrategy(): "highest" | "lowest" {
|
||||
const resolutionStrategyInput = core.getInput("resolution-strategy");
|
||||
function getResolutionStrategy(
|
||||
resolutionStrategyInput: string,
|
||||
): "highest" | "lowest" {
|
||||
if (resolutionStrategyInput === "lowest") {
|
||||
return "lowest";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user