mirror of
https://github.com/astral-sh/setup-uv.git
synced 2026-02-07 18:17:24 +00:00
feat: add venv-path input for activate-environment (#746)
Allow customizing the venv location while preserving working-directory semantics via --directory. Supersedes: #736
This commit is contained in:
parent
99b0f0474b
commit
3511ff7054
10 changed files with 229 additions and 15 deletions
55
.github/workflows/test.yml
vendored
55
.github/workflows/test.yml
vendored
|
|
@ -386,6 +386,60 @@ jobs:
|
||||||
env:
|
env:
|
||||||
UV_VENV: ${{ steps.setup-uv.outputs.venv }}
|
UV_VENV: ${{ steps.setup-uv.outputs.venv }}
|
||||||
|
|
||||||
|
test-activate-environment-custom-path:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Install latest version
|
||||||
|
id: setup-uv
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
python-version: 3.13.1t
|
||||||
|
activate-environment: true
|
||||||
|
venv-path: ${{ runner.temp }}/custom-venv
|
||||||
|
- name: Verify VIRTUAL_ENV matches output
|
||||||
|
run: |
|
||||||
|
if [ "$VIRTUAL_ENV" != "$UV_VENV" ]; then
|
||||||
|
echo "VIRTUAL_ENV does not match venv output: $VIRTUAL_ENV vs $UV_VENV"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
UV_VENV: ${{ steps.setup-uv.outputs.venv }}
|
||||||
|
- name: Verify venv location is runner.temp/custom-venv
|
||||||
|
run: |
|
||||||
|
python - <<'PY'
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
venv = Path(os.environ["VIRTUAL_ENV"]).resolve()
|
||||||
|
temp = Path(os.environ["RUNNER_TEMP"]).resolve()
|
||||||
|
|
||||||
|
if venv.name != "custom-venv":
|
||||||
|
raise SystemExit(f"Expected venv name 'custom-venv', got: {venv}")
|
||||||
|
if venv.parent != temp:
|
||||||
|
raise SystemExit(f"Expected venv under {temp}, got: {venv}")
|
||||||
|
if not venv.is_dir():
|
||||||
|
raise SystemExit(f"Venv directory does not exist: {venv}")
|
||||||
|
PY
|
||||||
|
shell: bash
|
||||||
|
- name: Verify packages can be installed
|
||||||
|
run: uv pip install pip
|
||||||
|
shell: bash
|
||||||
|
- name: Verify python runs from custom venv
|
||||||
|
run: |
|
||||||
|
python - <<'PY'
|
||||||
|
import sys
|
||||||
|
if "custom-venv" not in sys.executable:
|
||||||
|
raise SystemExit(f"Python is not running from custom venv: {sys.executable}")
|
||||||
|
PY
|
||||||
|
shell: bash
|
||||||
|
|
||||||
test-musl:
|
test-musl:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: alpine
|
container: alpine
|
||||||
|
|
@ -1069,6 +1123,7 @@ jobs:
|
||||||
- test-tilde-expansion-tool-dirs
|
- test-tilde-expansion-tool-dirs
|
||||||
- test-python-version
|
- test-python-version
|
||||||
- test-activate-environment
|
- test-activate-environment
|
||||||
|
- test-activate-environment-custom-path
|
||||||
- test-musl
|
- test-musl
|
||||||
- test-cache-key-os-version
|
- test-cache-key-os-version
|
||||||
- test-cache-local
|
- test-cache-local
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,9 @@ Have a look under [Advanced Configuration](#advanced-configuration) for detailed
|
||||||
# Use uv venv to activate a venv ready to be used by later steps
|
# Use uv venv to activate a venv ready to be used by later steps
|
||||||
activate-environment: "false"
|
activate-environment: "false"
|
||||||
|
|
||||||
|
# Custom path for the virtual environment when using activate-environment (default: .venv in the working directory)
|
||||||
|
venv-path: ""
|
||||||
|
|
||||||
# The directory to execute all commands in and look for files such as pyproject.toml
|
# The directory to execute all commands in and look for files such as pyproject.toml
|
||||||
working-directory: ""
|
working-directory: ""
|
||||||
|
|
||||||
|
|
@ -167,7 +170,7 @@ You can set the working directory with the `working-directory` input.
|
||||||
This controls where we look for `pyproject.toml`, `uv.toml` and `.python-version` files
|
This controls where we look for `pyproject.toml`, `uv.toml` and `.python-version` files
|
||||||
which are used to determine the version of uv and python to install.
|
which are used to determine the version of uv and python to install.
|
||||||
|
|
||||||
It also controls where [the venv gets created](#activate-environment).
|
It also controls where [the venv gets created](#activate-environment), unless `venv-path` is set.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Install uv based on the config files in the working-directory
|
- name: Install uv based on the config files in the working-directory
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ jest.mock("@actions/core", () => {
|
||||||
(name: string) => (mockInputs[name] ?? "") === "true",
|
(name: string) => (mockInputs[name] ?? "") === "true",
|
||||||
),
|
),
|
||||||
getInput: jest.fn((name: string) => mockInputs[name] ?? ""),
|
getInput: jest.fn((name: string) => mockInputs[name] ?? ""),
|
||||||
|
warning: jest.fn(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -24,6 +25,7 @@ const ORIGINAL_HOME = process.env.HOME;
|
||||||
describe("cacheDependencyGlob", () => {
|
describe("cacheDependencyGlob", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.resetModules();
|
jest.resetModules();
|
||||||
|
jest.clearAllMocks();
|
||||||
mockInputs = {};
|
mockInputs = {};
|
||||||
process.env.HOME = "/home/testuser";
|
process.env.HOME = "/home/testuser";
|
||||||
});
|
});
|
||||||
|
|
@ -84,3 +86,74 @@ describe("cacheDependencyGlob", () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 () => {
|
||||||
|
mockInputs["working-directory"] = "/workspace";
|
||||||
|
const { venvPath } = await import("../../src/utils/inputs");
|
||||||
|
expect(venvPath).toBe("/workspace/.venv");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves a relative venv-path", async () => {
|
||||||
|
mockInputs["working-directory"] = "/workspace";
|
||||||
|
mockInputs["activate-environment"] = "true";
|
||||||
|
mockInputs["venv-path"] = "custom-venv";
|
||||||
|
const { venvPath } = await import("../../src/utils/inputs");
|
||||||
|
expect(venvPath).toBe("/workspace/custom-venv");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("normalizes venv-path with trailing slash", async () => {
|
||||||
|
mockInputs["working-directory"] = "/workspace";
|
||||||
|
mockInputs["activate-environment"] = "true";
|
||||||
|
mockInputs["venv-path"] = "custom-venv/";
|
||||||
|
const { venvPath } = await import("../../src/utils/inputs");
|
||||||
|
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 import("../../src/utils/inputs");
|
||||||
|
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 import("../../src/utils/inputs");
|
||||||
|
expect(venvPath).toBe("/home/testuser/.venv");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("warns when venv-path is set but activate-environment is false", async () => {
|
||||||
|
mockInputs["working-directory"] = "/workspace";
|
||||||
|
mockInputs["venv-path"] = "custom-venv";
|
||||||
|
|
||||||
|
const { activateEnvironment, venvPath } = await import(
|
||||||
|
"../../src/utils/inputs"
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(activateEnvironment).toBe(false);
|
||||||
|
expect(venvPath).toBe("/workspace/custom-venv");
|
||||||
|
|
||||||
|
const mockedCore = jest.requireMock("@actions/core") as {
|
||||||
|
warning: jest.Mock;
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(mockedCore.warning).toHaveBeenCalledWith(
|
||||||
|
"venv-path is only used when activate-environment is true",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ inputs:
|
||||||
type: string
|
type: string
|
||||||
activate-environment:
|
activate-environment:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
venv-path:
|
||||||
|
type: string
|
||||||
working-directory:
|
working-directory:
|
||||||
type: string
|
type: string
|
||||||
checksum:
|
checksum:
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@ inputs:
|
||||||
activate-environment:
|
activate-environment:
|
||||||
description: "Use uv venv to activate a venv ready to be used by later steps. "
|
description: "Use uv venv to activate a venv ready to be used by later steps. "
|
||||||
default: "false"
|
default: "false"
|
||||||
|
venv-path:
|
||||||
|
description: "Custom path for the virtual environment when using activate-environment. Defaults to '.venv' in the working directory."
|
||||||
|
default: ""
|
||||||
working-directory:
|
working-directory:
|
||||||
description: "The directory to execute all commands in and look for files such as pyproject.toml"
|
description: "The directory to execute all commands in and look for files such as pyproject.toml"
|
||||||
default: ${{ github.workspace }}
|
default: ${{ github.workspace }}
|
||||||
|
|
|
||||||
24
dist/save-cache/index.js
generated
vendored
24
dist/save-cache/index.js
generated
vendored
|
|
@ -91032,7 +91032,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.resolutionStrategy = exports.addProblemMatchers = exports.manifestFile = exports.githubToken = exports.pythonDir = exports.toolDir = exports.toolBinDir = exports.ignoreEmptyWorkdir = exports.ignoreNothingToCache = exports.cachePython = exports.pruneCache = exports.cacheDependencyGlob = exports.cacheLocalPath = exports.cacheSuffix = exports.saveCache = exports.restoreCache = exports.enableCache = exports.checkSum = exports.activateEnvironment = exports.pythonVersion = exports.versionFile = exports.version = exports.workingDirectory = exports.CacheLocalSource = void 0;
|
exports.resolutionStrategy = exports.addProblemMatchers = exports.manifestFile = exports.githubToken = exports.pythonDir = exports.toolDir = exports.toolBinDir = exports.ignoreEmptyWorkdir = exports.ignoreNothingToCache = exports.cachePython = exports.pruneCache = exports.cacheDependencyGlob = exports.cacheLocalPath = exports.cacheSuffix = exports.saveCache = exports.restoreCache = exports.enableCache = exports.checkSum = exports.venvPath = exports.activateEnvironment = exports.pythonVersion = exports.versionFile = exports.version = exports.workingDirectory = exports.CacheLocalSource = void 0;
|
||||||
exports.getUvPythonDir = getUvPythonDir;
|
exports.getUvPythonDir = getUvPythonDir;
|
||||||
const node_path_1 = __importDefault(__nccwpck_require__(6760));
|
const node_path_1 = __importDefault(__nccwpck_require__(6760));
|
||||||
const core = __importStar(__nccwpck_require__(7484));
|
const core = __importStar(__nccwpck_require__(7484));
|
||||||
|
|
@ -91049,6 +91049,7 @@ exports.version = core.getInput("version");
|
||||||
exports.versionFile = getVersionFile();
|
exports.versionFile = getVersionFile();
|
||||||
exports.pythonVersion = core.getInput("python-version");
|
exports.pythonVersion = core.getInput("python-version");
|
||||||
exports.activateEnvironment = core.getBooleanInput("activate-environment");
|
exports.activateEnvironment = core.getBooleanInput("activate-environment");
|
||||||
|
exports.venvPath = getVenvPath();
|
||||||
exports.checkSum = core.getInput("checksum");
|
exports.checkSum = core.getInput("checksum");
|
||||||
exports.enableCache = getEnableCache();
|
exports.enableCache = getEnableCache();
|
||||||
exports.restoreCache = core.getInput("restore-cache") === "true";
|
exports.restoreCache = core.getInput("restore-cache") === "true";
|
||||||
|
|
@ -91075,6 +91076,17 @@ function getVersionFile() {
|
||||||
}
|
}
|
||||||
return versionFileInput;
|
return versionFileInput;
|
||||||
}
|
}
|
||||||
|
function getVenvPath() {
|
||||||
|
const venvPathInput = core.getInput("venv-path");
|
||||||
|
if (venvPathInput !== "") {
|
||||||
|
if (!exports.activateEnvironment) {
|
||||||
|
core.warning("venv-path is only used when activate-environment is true");
|
||||||
|
}
|
||||||
|
const tildeExpanded = expandTilde(venvPathInput);
|
||||||
|
return normalizePath(resolveRelativePath(tildeExpanded));
|
||||||
|
}
|
||||||
|
return normalizePath(resolveRelativePath(".venv"));
|
||||||
|
}
|
||||||
function getEnableCache() {
|
function getEnableCache() {
|
||||||
const enableCacheInput = core.getInput("enable-cache");
|
const enableCacheInput = core.getInput("enable-cache");
|
||||||
if (enableCacheInput === "auto") {
|
if (enableCacheInput === "auto") {
|
||||||
|
|
@ -91203,6 +91215,16 @@ function expandTilde(input) {
|
||||||
}
|
}
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
function normalizePath(inputPath) {
|
||||||
|
const normalized = node_path_1.default.normalize(inputPath);
|
||||||
|
const root = node_path_1.default.parse(normalized).root;
|
||||||
|
// Remove any trailing path separators, except when the whole path is the root.
|
||||||
|
let trimmed = normalized;
|
||||||
|
while (trimmed.length > root.length && trimmed.endsWith(node_path_1.default.sep)) {
|
||||||
|
trimmed = trimmed.slice(0, -1);
|
||||||
|
}
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
function resolveRelativePath(inputPath) {
|
function resolveRelativePath(inputPath) {
|
||||||
const hasNegation = inputPath.startsWith("!");
|
const hasNegation = inputPath.startsWith("!");
|
||||||
const pathWithoutNegation = hasNegation ? inputPath.substring(1) : inputPath;
|
const pathWithoutNegation = hasNegation ? inputPath.substring(1) : inputPath;
|
||||||
|
|
|
||||||
38
dist/setup/index.js
generated
vendored
38
dist/setup/index.js
generated
vendored
|
|
@ -96550,17 +96550,15 @@ async function activateEnvironment() {
|
||||||
if (process.env.UV_NO_MODIFY_PATH !== undefined) {
|
if (process.env.UV_NO_MODIFY_PATH !== undefined) {
|
||||||
throw new Error("UV_NO_MODIFY_PATH and activate-environment cannot be used together.");
|
throw new Error("UV_NO_MODIFY_PATH and activate-environment cannot be used together.");
|
||||||
}
|
}
|
||||||
const execArgs = ["venv", ".venv", "--directory", inputs_1.workingDirectory];
|
core.info(`Creating and activating python venv at ${inputs_1.venvPath}...`);
|
||||||
core.info("Activating python venv...");
|
await exec.exec("uv", ["venv", inputs_1.venvPath, "--directory", inputs_1.workingDirectory]);
|
||||||
await exec.exec("uv", execArgs);
|
let venvBinPath = `${inputs_1.venvPath}${path.sep}bin`;
|
||||||
const venvPath = path.resolve(`${inputs_1.workingDirectory}${path.sep}.venv`);
|
|
||||||
let venvBinPath = `${venvPath}${path.sep}bin`;
|
|
||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
venvBinPath = `${venvPath}${path.sep}Scripts`;
|
venvBinPath = `${inputs_1.venvPath}${path.sep}Scripts`;
|
||||||
}
|
}
|
||||||
core.addPath(path.resolve(venvBinPath));
|
core.addPath(path.resolve(venvBinPath));
|
||||||
core.exportVariable("VIRTUAL_ENV", venvPath);
|
core.exportVariable("VIRTUAL_ENV", inputs_1.venvPath);
|
||||||
core.setOutput("venv", venvPath);
|
core.setOutput("venv", inputs_1.venvPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function setCacheDir() {
|
function setCacheDir() {
|
||||||
|
|
@ -96732,7 +96730,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.resolutionStrategy = exports.addProblemMatchers = exports.manifestFile = exports.githubToken = exports.pythonDir = exports.toolDir = exports.toolBinDir = exports.ignoreEmptyWorkdir = exports.ignoreNothingToCache = exports.cachePython = exports.pruneCache = exports.cacheDependencyGlob = exports.cacheLocalPath = exports.cacheSuffix = exports.saveCache = exports.restoreCache = exports.enableCache = exports.checkSum = exports.activateEnvironment = exports.pythonVersion = exports.versionFile = exports.version = exports.workingDirectory = exports.CacheLocalSource = void 0;
|
exports.resolutionStrategy = exports.addProblemMatchers = exports.manifestFile = exports.githubToken = exports.pythonDir = exports.toolDir = exports.toolBinDir = exports.ignoreEmptyWorkdir = exports.ignoreNothingToCache = exports.cachePython = exports.pruneCache = exports.cacheDependencyGlob = exports.cacheLocalPath = exports.cacheSuffix = exports.saveCache = exports.restoreCache = exports.enableCache = exports.checkSum = exports.venvPath = exports.activateEnvironment = exports.pythonVersion = exports.versionFile = exports.version = exports.workingDirectory = exports.CacheLocalSource = void 0;
|
||||||
exports.getUvPythonDir = getUvPythonDir;
|
exports.getUvPythonDir = getUvPythonDir;
|
||||||
const node_path_1 = __importDefault(__nccwpck_require__(6760));
|
const node_path_1 = __importDefault(__nccwpck_require__(6760));
|
||||||
const core = __importStar(__nccwpck_require__(7484));
|
const core = __importStar(__nccwpck_require__(7484));
|
||||||
|
|
@ -96749,6 +96747,7 @@ exports.version = core.getInput("version");
|
||||||
exports.versionFile = getVersionFile();
|
exports.versionFile = getVersionFile();
|
||||||
exports.pythonVersion = core.getInput("python-version");
|
exports.pythonVersion = core.getInput("python-version");
|
||||||
exports.activateEnvironment = core.getBooleanInput("activate-environment");
|
exports.activateEnvironment = core.getBooleanInput("activate-environment");
|
||||||
|
exports.venvPath = getVenvPath();
|
||||||
exports.checkSum = core.getInput("checksum");
|
exports.checkSum = core.getInput("checksum");
|
||||||
exports.enableCache = getEnableCache();
|
exports.enableCache = getEnableCache();
|
||||||
exports.restoreCache = core.getInput("restore-cache") === "true";
|
exports.restoreCache = core.getInput("restore-cache") === "true";
|
||||||
|
|
@ -96775,6 +96774,17 @@ function getVersionFile() {
|
||||||
}
|
}
|
||||||
return versionFileInput;
|
return versionFileInput;
|
||||||
}
|
}
|
||||||
|
function getVenvPath() {
|
||||||
|
const venvPathInput = core.getInput("venv-path");
|
||||||
|
if (venvPathInput !== "") {
|
||||||
|
if (!exports.activateEnvironment) {
|
||||||
|
core.warning("venv-path is only used when activate-environment is true");
|
||||||
|
}
|
||||||
|
const tildeExpanded = expandTilde(venvPathInput);
|
||||||
|
return normalizePath(resolveRelativePath(tildeExpanded));
|
||||||
|
}
|
||||||
|
return normalizePath(resolveRelativePath(".venv"));
|
||||||
|
}
|
||||||
function getEnableCache() {
|
function getEnableCache() {
|
||||||
const enableCacheInput = core.getInput("enable-cache");
|
const enableCacheInput = core.getInput("enable-cache");
|
||||||
if (enableCacheInput === "auto") {
|
if (enableCacheInput === "auto") {
|
||||||
|
|
@ -96903,6 +96913,16 @@ function expandTilde(input) {
|
||||||
}
|
}
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
function normalizePath(inputPath) {
|
||||||
|
const normalized = node_path_1.default.normalize(inputPath);
|
||||||
|
const root = node_path_1.default.parse(normalized).root;
|
||||||
|
// Remove any trailing path separators, except when the whole path is the root.
|
||||||
|
let trimmed = normalized;
|
||||||
|
while (trimmed.length > root.length && trimmed.endsWith(node_path_1.default.sep)) {
|
||||||
|
trimmed = trimmed.slice(0, -1);
|
||||||
|
}
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
function resolveRelativePath(inputPath) {
|
function resolveRelativePath(inputPath) {
|
||||||
const hasNegation = inputPath.startsWith("!");
|
const hasNegation = inputPath.startsWith("!");
|
||||||
const pathWithoutNegation = hasNegation ? inputPath.substring(1) : inputPath;
|
const pathWithoutNegation = hasNegation ? inputPath.substring(1) : inputPath;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,17 @@ This allows directly using it in later steps:
|
||||||
- run: uv pip install pip
|
- run: uv pip install pip
|
||||||
```
|
```
|
||||||
|
|
||||||
|
By default, the venv is created at `.venv` inside the `working-directory`.
|
||||||
|
|
||||||
|
You can customize the venv location with `venv-path`, for example to place it in the runner temp directory:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: astral-sh/setup-uv@v7
|
||||||
|
with:
|
||||||
|
activate-environment: true
|
||||||
|
venv-path: ${{ runner.temp }}/custom-venv
|
||||||
|
```
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
>
|
>
|
||||||
> Activating the environment adds your dependencies to the `PATH`, which could break some workflows.
|
> Activating the environment adds your dependencies to the `PATH`, which could break some workflows.
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import {
|
||||||
resolutionStrategy,
|
resolutionStrategy,
|
||||||
toolBinDir,
|
toolBinDir,
|
||||||
toolDir,
|
toolDir,
|
||||||
|
venvPath,
|
||||||
versionFile as versionFileInput,
|
versionFile as versionFileInput,
|
||||||
version as versionInput,
|
version as versionInput,
|
||||||
workingDirectory,
|
workingDirectory,
|
||||||
|
|
@ -269,12 +270,10 @@ async function activateEnvironment(): Promise<void> {
|
||||||
"UV_NO_MODIFY_PATH and activate-environment cannot be used together.",
|
"UV_NO_MODIFY_PATH and activate-environment cannot be used together.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const execArgs = ["venv", ".venv", "--directory", workingDirectory];
|
|
||||||
|
|
||||||
core.info("Activating python venv...");
|
core.info(`Creating and activating python venv at ${venvPath}...`);
|
||||||
await exec.exec("uv", execArgs);
|
await exec.exec("uv", ["venv", venvPath, "--directory", workingDirectory]);
|
||||||
|
|
||||||
const venvPath = path.resolve(`${workingDirectory}${path.sep}.venv`);
|
|
||||||
let venvBinPath = `${venvPath}${path.sep}bin`;
|
let venvBinPath = `${venvPath}${path.sep}bin`;
|
||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
venvBinPath = `${venvPath}${path.sep}Scripts`;
|
venvBinPath = `${venvPath}${path.sep}Scripts`;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ export const version = core.getInput("version");
|
||||||
export const versionFile = getVersionFile();
|
export const versionFile = getVersionFile();
|
||||||
export const pythonVersion = core.getInput("python-version");
|
export const pythonVersion = core.getInput("python-version");
|
||||||
export const activateEnvironment = core.getBooleanInput("activate-environment");
|
export const activateEnvironment = core.getBooleanInput("activate-environment");
|
||||||
|
export const venvPath = getVenvPath();
|
||||||
export const checkSum = core.getInput("checksum");
|
export const checkSum = core.getInput("checksum");
|
||||||
export const enableCache = getEnableCache();
|
export const enableCache = getEnableCache();
|
||||||
export const restoreCache = core.getInput("restore-cache") === "true";
|
export const restoreCache = core.getInput("restore-cache") === "true";
|
||||||
|
|
@ -45,6 +46,18 @@ function getVersionFile(): string {
|
||||||
return versionFileInput;
|
return versionFileInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getVenvPath(): string {
|
||||||
|
const venvPathInput = core.getInput("venv-path");
|
||||||
|
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(".venv"));
|
||||||
|
}
|
||||||
|
|
||||||
function getEnableCache(): boolean {
|
function getEnableCache(): boolean {
|
||||||
const enableCacheInput = core.getInput("enable-cache");
|
const enableCacheInput = core.getInput("enable-cache");
|
||||||
if (enableCacheInput === "auto") {
|
if (enableCacheInput === "auto") {
|
||||||
|
|
@ -194,6 +207,19 @@ function expandTilde(input: string): string {
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizePath(inputPath: string): string {
|
||||||
|
const normalized = path.normalize(inputPath);
|
||||||
|
const root = path.parse(normalized).root;
|
||||||
|
|
||||||
|
// Remove any trailing path separators, except when the whole path is the root.
|
||||||
|
let trimmed = normalized;
|
||||||
|
while (trimmed.length > root.length && trimmed.endsWith(path.sep)) {
|
||||||
|
trimmed = trimmed.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
function resolveRelativePath(inputPath: string): string {
|
function resolveRelativePath(inputPath: string): string {
|
||||||
const hasNegation = inputPath.startsWith("!");
|
const hasNegation = inputPath.startsWith("!");
|
||||||
const pathWithoutNegation = hasNegation ? inputPath.substring(1) : inputPath;
|
const pathWithoutNegation = hasNegation ? inputPath.substring(1) : inputPath;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue