diff --git a/action.yml b/action.yml index 2ec29f7c..d3b07466 100644 --- a/action.yml +++ b/action.yml @@ -29,6 +29,11 @@ inputs: installers" required: false default: "" + install-dir: + description: + "If provided, the installer will be installed in the given directory." + required: false + default: "" miniconda-version: description: "If provided, this version of Miniconda3 will be downloaded and installed. diff --git a/dist/setup/index.js b/dist/setup/index.js index 03b5828e..873e8f32 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -47592,7 +47592,7 @@ function installBaseTools(inputs, options) { } } if (tools.length) { - yield conda.condaCommand(["install", "--name", "base", ...tools], options); + yield conda.condaCommand(["install", "--name", "base", ...tools], inputs, options); // *Now* use the new options, as we may have a new conda/mamba with more supported // options that previously failed yield conda.applyCondaConfiguration(inputs, postInstallOptions); @@ -47797,7 +47797,7 @@ exports.updateMamba = { } core.info("Creating bash wrapper for `mamba`..."); // Add bat-less forwarder for bash users on Windows - const mambaBat = conda.condaExecutable(options).replace(/\\/g, "/"); + const mambaBat = conda.condaExecutable(inputs, options).replace(/\\/g, "/"); const contents = `bash.exe -c "exec '${mambaBat}' $*" || exit 1`; fs.writeFileSync(mambaBat.slice(0, -4), contents); core.info(`... wrote ${mambaBat}:\n${contents}`); @@ -47919,11 +47919,14 @@ const utils = __importStar(__nccwpck_require__(1314)); /** * Provide current location of miniconda or location where it will be installed */ -function condaBasePath(options) { +function condaBasePath(inputs, options) { let condaPath; if (options.useBundled) { condaPath = constants.MINICONDA_DIR_PATH; } + else if (inputs.installDir) { + condaPath = inputs.installDir; + } else { condaPath = path.join(os.homedir(), "miniconda3"); } @@ -47946,8 +47949,8 @@ exports.envCommandFlag = envCommandFlag; /** * Provide cross platform location of conda/mamba executable */ -function condaExecutable(options, subcommand) { - const dir = condaBasePath(options); +function condaExecutable(inputs, options, subcommand) { + const dir = condaBasePath(inputs, options); let condaExe; let commandName = "conda"; if (options.useMamba && @@ -47960,17 +47963,17 @@ function condaExecutable(options, subcommand) { } exports.condaExecutable = condaExecutable; /** Detect the presence of mamba */ -function isMambaInstalled(options) { - const mamba = condaExecutable(Object.assign(Object.assign({}, options), { useMamba: true })); +function isMambaInstalled(inputs, options) { + const mamba = condaExecutable(inputs, Object.assign(Object.assign({}, options), { useMamba: true })); return fs.existsSync(mamba); } exports.isMambaInstalled = isMambaInstalled; /** * Run Conda command */ -function condaCommand(cmd, options) { +function condaCommand(cmd, inputs, options) { return __awaiter(this, void 0, void 0, function* () { - const command = [condaExecutable(options, cmd[0]), ...cmd]; + const command = [condaExecutable(inputs, options, cmd[0]), ...cmd]; return yield utils.execute(command); }); } @@ -48016,7 +48019,7 @@ function applyCondaConfiguration(inputs, options) { // .slice ensures working against a copy for (const channel of channels.slice().reverse()) { core.info(`Adding channel '${channel}'`); - yield condaCommand(["config", "--add", "channels", channel], options); + yield condaCommand(["config", "--add", "channels", channel], inputs, options); } // All other options are just passed as their string representations for (const [key, value] of configEntries) { @@ -48025,15 +48028,15 @@ function applyCondaConfiguration(inputs, options) { } core.info(`${key}: ${value}`); try { - yield condaCommand(["config", "--set", key, value], options); + yield condaCommand(["config", "--set", key, value], inputs, options); } catch (err) { core.warning(err); } } // Log all configuration information - yield condaCommand(["config", "--show-sources"], options); - yield condaCommand(["config", "--show"], options); + yield condaCommand(["config", "--show-sources"], inputs, options); + yield condaCommand(["config", "--show"], inputs, options); }); } exports.applyCondaConfiguration = applyCondaConfiguration; @@ -48055,12 +48058,12 @@ function condaInit(inputs, options) { "chown", "-R", `${userName}:staff`, - condaBasePath(options), + condaBasePath(inputs, options), ]); } else if (constants.IS_WINDOWS) { for (let folder of constants.WIN_PERMS_FOLDERS) { - ownPath = path.join(condaBasePath(options), folder); + ownPath = path.join(condaBasePath(inputs, options), folder); if (fs.existsSync(ownPath)) { core.info(`Fixing ${folder} ownership`); yield utils.execute(["takeown", "/f", ownPath, "/r", "/d", "y"]); @@ -48085,7 +48088,7 @@ function condaInit(inputs, options) { } // Run conda init for (let cmd of ["--all"]) { - yield condaCommand(["init", cmd], options); + yield condaCommand(["init", cmd], inputs, options); } if (inputs.removeProfiles == "true") { // Rename files @@ -48504,7 +48507,7 @@ function ensureEnvironment(inputs, options) { if (yield provider.provides(inputs, options)) { core.info(`... will ${provider.label}.`); const args = yield provider.condaArgs(inputs, options); - return yield core.group(`Updating '${inputs.activateEnvironment}' env from ${provider.label}...`, () => conda.condaCommand(args, options)); + return yield core.group(`Updating '${inputs.activateEnvironment}' env from ${provider.label}...`, () => conda.condaCommand(args, inputs, options)); } } throw Error(`'activate-environment: ${inputs.activateEnvironment}' could not be created`); @@ -48839,6 +48842,7 @@ function parseInputs() { condaVersion: core.getInput("conda-version"), environmentFile: core.getInput("environment-file"), installerUrl: core.getInput("installer-url"), + installDir: core.getInput("install-dir"), mambaVersion: core.getInput("mamba-version"), useMamba: core.getInput("use-mamba"), minicondaVersion: core.getInput("miniconda-version"), @@ -49445,7 +49449,7 @@ function runInstaller(installerPath, outputPath, inputs, options) { } yield utils.execute(command); // The installer may have provisioned `mamba` in `base`: use now if requested - const mambaInInstaller = conda.isMambaInstalled(options); + const mambaInInstaller = conda.isMambaInstalled(inputs, options); if (mambaInInstaller) { core.info("Mamba was found in the `base` env"); options = Object.assign(Object.assign({}, options), { mambaInInstaller, useMamba: mambaInInstaller && inputs.useMamba === "true" }); @@ -49508,10 +49512,10 @@ const utils = __importStar(__nccwpck_require__(1314)); /** * Add Conda executable to PATH environment variable */ -function setPathVariables(options) { +function setPathVariables(inputs, options) { return __awaiter(this, void 0, void 0, function* () { - const condaBin = path.join(conda.condaBasePath(options), "condabin"); - const condaPath = conda.condaBasePath(options); + const condaBin = path.join(conda.condaBasePath(inputs, options), "condabin"); + const condaPath = conda.condaBasePath(inputs, options); core.info(`Add "${condaBin}" to PATH`); core.addPath(condaBin); if (!options.useBundled) { @@ -49524,10 +49528,10 @@ exports.setPathVariables = setPathVariables; /** * Ensure the conda cache path is available as an environment variable */ -function setCacheVariable(options) { +function setCacheVariable(inputs, options) { return __awaiter(this, void 0, void 0, function* () { const folder = utils.cacheFolder(); - yield conda.condaCommand(["config", "--add", "pkgs_dirs", folder], options); + yield conda.condaCommand(["config", "--add", "pkgs_dirs", folder], inputs, options); core.exportVariable(constants.ENV_VAR_CONDA_PKGS, folder); }); } @@ -49608,7 +49612,7 @@ function setupMiniconda(inputs) { const installerInfo = yield core.group("Ensuring installer...", () => installer.getLocalInstallerPath(inputs, options)); // The desired installer may change the options options = Object.assign(Object.assign({}, options), installerInfo.options); - const basePath = conda.condaBasePath(options); + const basePath = conda.condaBasePath(inputs, options); if (installerInfo.localInstallerPath && !options.useBundled) { options = yield core.group("Running installer...", () => installer.runInstaller(installerInfo.localInstallerPath, basePath, inputs, options)); } @@ -49620,13 +49624,13 @@ function setupMiniconda(inputs) { 'Miniforge for you, add `miniconda-version: "latest"` or `miniforge-version: "latest"`, ' + "respectively, to the parameters for this action."); } - yield core.group("Setup environment variables...", () => outputs.setPathVariables(options)); + yield core.group("Setup environment variables...", () => outputs.setPathVariables(inputs, options)); if (inputs.condaConfigFile) { yield core.group("Copying condarc file...", () => conda.copyConfig(inputs)); } // For potential 'channels' that may alter configuration options.envSpec = yield core.group("Parsing environment...", () => env.getEnvSpec(inputs)); - yield core.group("Configuring conda package cache...", () => outputs.setCacheVariable(options)); + yield core.group("Configuring conda package cache...", () => outputs.setCacheVariable(inputs, options)); yield core.group("Applying initial configuration...", () => conda.applyCondaConfiguration(inputs, options)); yield core.group("Initializing conda shell integration...", () => conda.condaInit(inputs, options)); // New base tools may change options diff --git a/src/base-tools/index.ts b/src/base-tools/index.ts index 5d41a6bf..9ddecca4 100644 --- a/src/base-tools/index.ts +++ b/src/base-tools/index.ts @@ -55,7 +55,11 @@ export async function installBaseTools( } if (tools.length) { - await conda.condaCommand(["install", "--name", "base", ...tools], options); + await conda.condaCommand( + ["install", "--name", "base", ...tools], + inputs, + options, + ); // *Now* use the new options, as we may have a new conda/mamba with more supported // options that previously failed diff --git a/src/base-tools/update-mamba.ts b/src/base-tools/update-mamba.ts index d866eec9..44bc840d 100644 --- a/src/base-tools/update-mamba.ts +++ b/src/base-tools/update-mamba.ts @@ -29,7 +29,7 @@ export const updateMamba: types.IToolProvider = { } core.info("Creating bash wrapper for `mamba`..."); // Add bat-less forwarder for bash users on Windows - const mambaBat = conda.condaExecutable(options).replace(/\\/g, "/"); + const mambaBat = conda.condaExecutable(inputs, options).replace(/\\/g, "/"); const contents = `bash.exe -c "exec '${mambaBat}' $*" || exit 1`; fs.writeFileSync(mambaBat.slice(0, -4), contents); diff --git a/src/conda.ts b/src/conda.ts index 3cf527a9..426970fc 100644 --- a/src/conda.ts +++ b/src/conda.ts @@ -16,10 +16,15 @@ import * as utils from "./utils"; /** * Provide current location of miniconda or location where it will be installed */ -export function condaBasePath(options: types.IDynamicOptions): string { +export function condaBasePath( + inputs: types.IActionInputs, + options: types.IDynamicOptions, +): string { let condaPath: string; if (options.useBundled) { condaPath = constants.MINICONDA_DIR_PATH; + } else if (inputs.installDir) { + condaPath = inputs.installDir; } else { condaPath = path.join(os.homedir(), "miniconda3"); } @@ -43,10 +48,11 @@ export function envCommandFlag(inputs: types.IActionInputs): string[] { * Provide cross platform location of conda/mamba executable */ export function condaExecutable( + inputs: types.IActionInputs, options: types.IDynamicOptions, subcommand?: string, ): string { - const dir: string = condaBasePath(options); + const dir: string = condaBasePath(inputs, options); let condaExe: string; let commandName = "conda"; if ( @@ -61,8 +67,11 @@ export function condaExecutable( } /** Detect the presence of mamba */ -export function isMambaInstalled(options: types.IDynamicOptions) { - const mamba = condaExecutable({ ...options, useMamba: true }); +export function isMambaInstalled( + inputs: types.IActionInputs, + options: types.IDynamicOptions, +) { + const mamba = condaExecutable(inputs, { ...options, useMamba: true }); return fs.existsSync(mamba); } @@ -71,9 +80,10 @@ export function isMambaInstalled(options: types.IDynamicOptions) { */ export async function condaCommand( cmd: string[], + inputs: types.IActionInputs, options: types.IDynamicOptions, ): Promise { - const command = [condaExecutable(options, cmd[0]), ...cmd]; + const command = [condaExecutable(inputs, options, cmd[0]), ...cmd]; return await utils.execute(command); } @@ -127,7 +137,11 @@ export async function applyCondaConfiguration( // .slice ensures working against a copy for (const channel of channels.slice().reverse()) { core.info(`Adding channel '${channel}'`); - await condaCommand(["config", "--add", "channels", channel], options); + await condaCommand( + ["config", "--add", "channels", channel], + inputs, + options, + ); } // All other options are just passed as their string representations @@ -137,15 +151,15 @@ export async function applyCondaConfiguration( } core.info(`${key}: ${value}`); try { - await condaCommand(["config", "--set", key, value], options); + await condaCommand(["config", "--set", key, value], inputs, options); } catch (err) { core.warning(err as Error); } } // Log all configuration information - await condaCommand(["config", "--show-sources"], options); - await condaCommand(["config", "--show"], options); + await condaCommand(["config", "--show-sources"], inputs, options); + await condaCommand(["config", "--show"], inputs, options); } /** @@ -170,11 +184,11 @@ export async function condaInit( "chown", "-R", `${userName}:staff`, - condaBasePath(options), + condaBasePath(inputs, options), ]); } else if (constants.IS_WINDOWS) { for (let folder of constants.WIN_PERMS_FOLDERS) { - ownPath = path.join(condaBasePath(options), folder); + ownPath = path.join(condaBasePath(inputs, options), folder); if (fs.existsSync(ownPath)) { core.info(`Fixing ${folder} ownership`); await utils.execute(["takeown", "/f", ownPath, "/r", "/d", "y"]); @@ -200,7 +214,7 @@ export async function condaInit( // Run conda init for (let cmd of ["--all"]) { - await condaCommand(["init", cmd], options); + await condaCommand(["init", cmd], inputs, options); } if (inputs.removeProfiles == "true") { diff --git a/src/env/index.ts b/src/env/index.ts index 06db6ae5..8b188022 100644 --- a/src/env/index.ts +++ b/src/env/index.ts @@ -42,7 +42,7 @@ export async function ensureEnvironment( const args = await provider.condaArgs(inputs, options); return await core.group( `Updating '${inputs.activateEnvironment}' env from ${provider.label}...`, - () => conda.condaCommand(args, options), + () => conda.condaCommand(args, inputs, options), ); } } diff --git a/src/input.ts b/src/input.ts index a605d80a..4523c8cb 100644 --- a/src/input.ts +++ b/src/input.ts @@ -87,6 +87,7 @@ export async function parseInputs(): Promise { condaVersion: core.getInput("conda-version"), environmentFile: core.getInput("environment-file"), installerUrl: core.getInput("installer-url"), + installDir: core.getInput("install-dir"), mambaVersion: core.getInput("mamba-version"), useMamba: core.getInput("use-mamba"), minicondaVersion: core.getInput("miniconda-version"), diff --git a/src/installer/index.ts b/src/installer/index.ts index 8ba30670..d7108090 100644 --- a/src/installer/index.ts +++ b/src/installer/index.ts @@ -85,7 +85,7 @@ export async function runInstaller( await utils.execute(command); // The installer may have provisioned `mamba` in `base`: use now if requested - const mambaInInstaller = conda.isMambaInstalled(options); + const mambaInInstaller = conda.isMambaInstalled(inputs, options); if (mambaInInstaller) { core.info("Mamba was found in the `base` env"); options = { diff --git a/src/outputs.ts b/src/outputs.ts index 16066f6b..93c41e22 100644 --- a/src/outputs.ts +++ b/src/outputs.ts @@ -14,10 +14,14 @@ import * as utils from "./utils"; * Add Conda executable to PATH environment variable */ export async function setPathVariables( + inputs: types.IActionInputs, options: types.IDynamicOptions, ): Promise { - const condaBin: string = path.join(conda.condaBasePath(options), "condabin"); - const condaPath: string = conda.condaBasePath(options); + const condaBin: string = path.join( + conda.condaBasePath(inputs, options), + "condabin", + ); + const condaPath: string = conda.condaBasePath(inputs, options); core.info(`Add "${condaBin}" to PATH`); core.addPath(condaBin); if (!options.useBundled) { @@ -29,9 +33,16 @@ export async function setPathVariables( /** * Ensure the conda cache path is available as an environment variable */ -export async function setCacheVariable(options: types.IDynamicOptions) { +export async function setCacheVariable( + inputs: types.IActionInputs, + options: types.IDynamicOptions, +) { const folder = utils.cacheFolder(); - await conda.condaCommand(["config", "--add", "pkgs_dirs", folder], options); + await conda.condaCommand( + ["config", "--add", "pkgs_dirs", folder], + inputs, + options, + ); core.exportVariable(constants.ENV_VAR_CONDA_PKGS, folder); } diff --git a/src/setup.ts b/src/setup.ts index 3ff1b0c5..be3376ef 100644 --- a/src/setup.ts +++ b/src/setup.ts @@ -34,7 +34,7 @@ async function setupMiniconda(inputs: types.IActionInputs): Promise { // The desired installer may change the options options = { ...options, ...installerInfo.options }; - const basePath = conda.condaBasePath(options); + const basePath = conda.condaBasePath(inputs, options); if (installerInfo.localInstallerPath && !options.useBundled) { options = await core.group("Running installer...", () => @@ -59,7 +59,7 @@ async function setupMiniconda(inputs: types.IActionInputs): Promise { } await core.group("Setup environment variables...", () => - outputs.setPathVariables(options), + outputs.setPathVariables(inputs, options), ); if (inputs.condaConfigFile) { @@ -72,7 +72,7 @@ async function setupMiniconda(inputs: types.IActionInputs): Promise { ); await core.group("Configuring conda package cache...", () => - outputs.setCacheVariable(options), + outputs.setCacheVariable(inputs, options), ); await core.group("Applying initial configuration...", () => diff --git a/src/types.ts b/src/types.ts index 814451ce..09a716a1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -80,6 +80,7 @@ export interface IActionInputs { readonly condaVersion: string; readonly environmentFile: string; readonly installerUrl: string; + readonly installDir: string; readonly mambaVersion: string; readonly minicondaVersion: string; readonly miniforgeVariant: string;