feat: add venv-path input for custom virtual environment location

Allow users to specify a custom path for the virtual environment when
using activate-environment. This is useful for CI workflows that need
to create isolated venvs outside the working directory (e.g., in
$RUNNER_TEMP) to avoid conflicts with project-level configurations.

When venv-path is not specified, the default behavior remains unchanged
(.venv in the working directory).
This commit is contained in:
Eli Uriegas 2026-01-20 13:15:57 -08:00
parent 9cfd029643
commit c19391797c
6 changed files with 89 additions and 10 deletions

View file

@ -386,6 +386,50 @@ 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 points to custom path
run: |
if [[ "$VIRTUAL_ENV" != "${{ runner.temp }}/custom-venv" ]]; then
echo "VIRTUAL_ENV is not set to custom path: $VIRTUAL_ENV"
exit 1
fi
shell: bash
- name: Verify packages can be installed
run: uv pip install pip
shell: bash
- name: Verify python runs from custom venv
run: |
PYTHON_PATH=$(which python)
if [[ "$PYTHON_PATH" != *"custom-venv"* ]]; then
echo "Python is not running from custom venv: $PYTHON_PATH"
exit 1
fi
shell: bash
- name: Verify output venv matches custom path
run: |
if [ "$UV_VENV" != "${{ runner.temp }}/custom-venv" ]; then
echo "output venv does not match custom path: $UV_VENV"
exit 1
fi
shell: bash
env:
UV_VENV: ${{ steps.setup-uv.outputs.venv }}
test-musl: test-musl:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: alpine container: alpine
@ -1069,6 +1113,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

View file

@ -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 }}

11
dist/save-cache/index.js generated vendored
View file

@ -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,14 @@ function getVersionFile() {
} }
return versionFileInput; return versionFileInput;
} }
function getVenvPath() {
const venvPathInput = core.getInput("venv-path");
if (venvPathInput !== "") {
const tildeExpanded = expandTilde(venvPathInput);
return resolveRelativePath(tildeExpanded);
}
return undefined;
}
function getEnableCache() { function getEnableCache() {
const enableCacheInput = core.getInput("enable-cache"); const enableCacheInput = core.getInput("enable-cache");
if (enableCacheInput === "auto") { if (enableCacheInput === "auto") {

19
dist/setup/index.js generated vendored
View file

@ -96496,10 +96496,10 @@ 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]; // Use custom venv path if provided, otherwise default to .venv in working directory
core.info("Activating python venv..."); const venvPath = inputs_1.venvPath ?? path.resolve(`${inputs_1.workingDirectory}${path.sep}.venv`);
await exec.exec("uv", execArgs); core.info(`Activating python venv at ${venvPath}...`);
const venvPath = path.resolve(`${inputs_1.workingDirectory}${path.sep}.venv`); await exec.exec("uv", ["venv", venvPath]);
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`;
@ -96678,7 +96678,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));
@ -96695,6 +96695,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";
@ -96721,6 +96722,14 @@ function getVersionFile() {
} }
return versionFileInput; return versionFileInput;
} }
function getVenvPath() {
const venvPathInput = core.getInput("venv-path");
if (venvPathInput !== "") {
const tildeExpanded = expandTilde(venvPathInput);
return resolveRelativePath(tildeExpanded);
}
return undefined;
}
function getEnableCache() { function getEnableCache() {
const enableCacheInput = core.getInput("enable-cache"); const enableCacheInput = core.getInput("enable-cache");
if (enableCacheInput === "auto") { if (enableCacheInput === "auto") {

View file

@ -24,6 +24,7 @@ import {
resolutionStrategy, resolutionStrategy,
toolBinDir, toolBinDir,
toolDir, toolDir,
venvPath as venvPathInput,
versionFile as versionFileInput, versionFile as versionFileInput,
version as versionInput, version as versionInput,
workingDirectory, workingDirectory,
@ -269,12 +270,14 @@ 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..."); // Use custom venv path if provided, otherwise default to .venv in working directory
await exec.exec("uv", execArgs); const venvPath =
venvPathInput ?? path.resolve(`${workingDirectory}${path.sep}.venv`);
core.info(`Activating python venv at ${venvPath}...`);
await exec.exec("uv", ["venv", venvPath]);
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`;

View file

@ -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,15 @@ function getVersionFile(): string {
return versionFileInput; return versionFileInput;
} }
function getVenvPath(): string | undefined {
const venvPathInput = core.getInput("venv-path");
if (venvPathInput !== "") {
const tildeExpanded = expandTilde(venvPathInput);
return resolveRelativePath(tildeExpanded);
}
return undefined;
}
function getEnableCache(): boolean { function getEnableCache(): boolean {
const enableCacheInput = core.getInput("enable-cache"); const enableCacheInput = core.getInput("enable-cache");
if (enableCacheInput === "auto") { if (enableCacheInput === "auto") {