From c84989184c53a35d5f05bf36fe143b4cc8db9e8a Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Wed, 30 Dec 2020 10:19:12 -0500 Subject: [PATCH] allow activate-environment to be a path --- .github/workflows/example-6.yml | 11 +++- README.md | 17 +++++-- action.yml | 11 ++-- dist/setup/index.js | 90 ++++++++++++++++++++------------- src/base-tools/index.ts | 9 +--- src/conda.ts | 24 ++++++--- src/env/explicit.ts | 4 +- src/env/index.ts | 16 ------ src/env/simple.ts | 3 +- src/env/yaml.ts | 5 +- src/tools.ts | 21 -------- 11 files changed, 109 insertions(+), 102 deletions(-) delete mode 100644 src/tools.ts diff --git a/.github/workflows/example-6.yml b/.github/workflows/example-6.yml index bea7b048..c004b9d7 100644 --- a/.github/workflows/example-6.yml +++ b/.github/workflows/example-6.yml @@ -23,6 +23,13 @@ jobs: fail-fast: false matrix: os: ["ubuntu-latest", "macos-latest", "windows-latest"] + include: + - os: ubuntu-latest + activate-environment: anaconda-client-env + - os: macos-latest + activate-environment: /tmp/anaconda-client-env + - os: windows-latest + activate-environment: c:\ace defaults: run: shell: bash -l {0} @@ -32,9 +39,9 @@ jobs: with: python-version: "3.7" mamba-version: "*" - channels: conda-forge,defaults + channels: conda-forge,nodefaults channel-priority: true - activate-environment: anaconda-client-env + activate-environment: ${{ matrix.activate-environment }} environment-file: etc/example-environment.yml - run: | conda info diff --git a/README.md b/README.md index 97206333..2982c87e 100644 --- a/README.md +++ b/README.md @@ -31,10 +31,10 @@ This action will by default will not activate the `base`environment and activate This enforces the idea of not using the `base` environment to install packages used for the action and leave the `base` environment untouched, with only `conda` (or `mamba`) in it. -### Use a different environment name +### Use a different environment name or path -You can change the default `test` environment to have a different name bu setting the -`activate-environment` input option. +You can change the default `test` environment to have a different name or path by +setting the `activate-environment` input option. ```yaml - uses: conda-incubator/setup-miniconda@v2 @@ -42,6 +42,17 @@ You can change the default `test` environment to have a different name bu settin activate-environment: whatever ``` +This will be create a _named_ env in `$CONDA/envs/whatever`, where `$CONDA` is the +path to the infrequently-updated, but **very fast** to start, "bundled" Miniconda +installation. + +> - If `activate-environment` contains either POSIX or Windows slashes, it will be +> interpreted as a path, or "prefix" in `conda` terminology. Use this to avoid +> "path too long"-style errors, especially on windows. +> - Self-hosted runners can emulate the "bundled" Miniconda approach by pre-installing +> _a_ Miniconda-like installer and ensuring `$CONDA` is set prior to starting +> `setup-miniconda` + ### Activate `base` environment If your specific workflow still needs to activate and use `base` you will need to set the diff --git a/action.yml b/action.yml index 3984d685..d0aed8f6 100644 --- a/action.yml +++ b/action.yml @@ -59,12 +59,13 @@ inputs: default: "" activate-environment: description: - 'Environment to activate by default on all shells. Default is "test". If - an empty string is used, no environment is activated by default (For - "base" activation see the "auto-activate-base" option). If the environment - does not exist, it will be created and activated. If "environment-file" is + 'Environment name (or path) to activate on all shells. Default is + `test` which will be created in `$CONDA/envs/test`. If an empty string is used, + no environment is activated by default (For `base` activation see the + `auto-activate-base` option). If the environment + does not exist, it will be created and activated. If `environment-file` is used and you want that to be the environment used, you need to explicitely - provide the name of that environment on "activate-environment". If using + provide the name of that environment on `activate-environment`. If using sh/bash/cmd.exe shells please read the IMPORTANT! section on the README.md! to properly activate conda environments on these shells.' required: false diff --git a/dist/setup/index.js b/dist/setup/index.js index c42e2bb3..97252693 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -1151,6 +1151,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.ensureSimple = void 0; const core = __importStar(__webpack_require__(470)); const utils = __importStar(__webpack_require__(163)); +const conda = __importStar(__webpack_require__(259)); /** * Install an environment with `conda create` when no `envSpec` is detected * @@ -1167,7 +1168,7 @@ exports.ensureSimple = { Object.keys(((_c = options.envSpec) === null || _c === void 0 ? void 0 : _c.yaml) || {}).length); }), condaArgs: (inputs, options) => __awaiter(void 0, void 0, void 0, function* () { - const args = ["create", "--name", inputs.activateEnvironment]; + const args = ["create", ...conda.envCommandFlag(inputs)]; if (inputs.pythonVersion) { const spec = utils.makeSpec("python", inputs.pythonVersion); core.info(`Adding spec: ${spec}`); @@ -2126,16 +2127,13 @@ function installBaseTools(inputs, options) { tools.push(...toolUpdates.tools); postInstallOptions = Object.assign(Object.assign({}, postInstallOptions), toolUpdates.options); if (provider.postInstall) { - core.info(`... we will perform post-intall steps after we ${provider.label}.`); + core.info(`... we will perform post-install steps after we ${provider.label}.`); postInstallActions.push(provider.postInstall); } } } if (tools.length) { - yield conda.condaCommand(["install", "--name", "base", ...tools], - // Use the original `options`, as we can't guarantee `mamba` is available - // TODO: allow declaring that the installer already has `mamba` - options); + yield conda.condaCommand(["install", "--name", "base", ...tools], 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); @@ -7086,7 +7084,7 @@ var attributeRules = { if(len === 0){ return falseFunc; } - + if(data.ignoreCase){ value = value.toLowerCase(); @@ -7254,7 +7252,7 @@ exports.prepend = function(elem, prev){ if(elem.prev){ elem.prev.next = prev; } - + prev.parent = parent; prev.prev = elem.prev; prev.next = elem; @@ -8525,7 +8523,7 @@ Parser.prototype.onclosetag = function(name) { if (this._lowerCaseTagNames) { name = name.toLowerCase(); } - + if (name in foreignContextElements || name in htmlIntegrationElements) { this._foreignContext.pop(); } @@ -8574,7 +8572,7 @@ Parser.prototype._closeCurrentTag = function() { this._cbs.onclosetag(name); } this._stack.pop(); - + } }; @@ -13019,7 +13017,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.condaInit = exports.applyCondaConfiguration = exports.copyConfig = exports.bootstrapConfig = exports.condaCommand = exports.isMambaInstalled = exports.condaExecutable = exports.condaBasePath = void 0; +exports.condaInit = exports.applyCondaConfiguration = exports.copyConfig = exports.bootstrapConfig = exports.condaCommand = exports.isMambaInstalled = exports.condaExecutable = exports.envCommandFlag = exports.condaBasePath = void 0; const fs = __importStar(__webpack_require__(747)); const path = __importStar(__webpack_require__(622)); const os = __importStar(__webpack_require__(87)); @@ -13043,6 +13041,19 @@ function condaBasePath(options) { return condaPath; } exports.condaBasePath = condaBasePath; +/** + * Provide conda CLI arguments for identifying an env by name or prefix/path + * + * ### Note + * Only really detects by presence of a path separator, as the path may not yet exist + */ +function envCommandFlag(inputs) { + return [ + inputs.activateEnvironment.match(/(\\|\/)/) ? "--prefix" : "--name", + inputs.activateEnvironment, + ]; +} +exports.envCommandFlag = envCommandFlag; /** * Provide cross platform location of conda/mamba executable */ @@ -13142,9 +13153,7 @@ exports.applyCondaConfiguration = applyCondaConfiguration; function condaInit(inputs, options) { return __awaiter(this, void 0, void 0, function* () { let ownPath; - const isValidActivate = inputs.activateEnvironment !== "base" && - inputs.activateEnvironment !== "root" && - inputs.activateEnvironment !== ""; + const isValidActivate = !utils.isBaseEnv(inputs.activateEnvironment); const autoActivateBase = options.condaConfig["auto_activate_base"] === "true"; // Fix ownership of folders if (options.useBundled) { @@ -13207,7 +13216,7 @@ function condaInit(inputs, options) { if (isValidActivate) { powerExtraText += ` # Conda Setup Action: Custom activation - conda activate ${inputs.activateEnvironment}`; + conda activate "${inputs.activateEnvironment}"`; } powerExtraText += ` # ----------------------------------------------------------------------------`; @@ -13219,7 +13228,7 @@ function condaInit(inputs, options) { if (isValidActivate) { bashExtraText += ` # Conda Setup Action: Custom activation - conda activate ${inputs.activateEnvironment}`; + conda activate "${inputs.activateEnvironment}"`; bashExtraText += ` # ----------------------------------------------------------------------------`; } @@ -13234,7 +13243,7 @@ function condaInit(inputs, options) { if (isValidActivate) { batchExtraText += ` :: Conda Setup Action: Custom activation - @CALL "%CONDA_BAT%" activate ${inputs.activateEnvironment}`; + @CALL "%CONDA_BAT%" activate "${inputs.activateEnvironment}"`; } batchExtraText += ` :: Conda Setup Action: Basic configuration @@ -15654,7 +15663,7 @@ DomHandler.prototype.onerror = function(error){ DomHandler.prototype.onclosetag = function(){ //if(this._tagStack.pop().name !== name) this._handleCallback(Error("Tagname didn't match!")); - + var elem = this._tagStack.pop(); if(this._options.withEndIndices && elem){ @@ -15819,10 +15828,29 @@ module.exports = DomHandler; /* 283 */, /* 284 */, /* 285 */ -/***/ (function(__unusedmodule, exports) { +/***/ (function(__unusedmodule, exports, __webpack_require__) { "use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { @@ -15834,6 +15862,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ensureExplicit = void 0; +const conda = __importStar(__webpack_require__(259)); /** * Install an environment from an explicit file generated `conda list --explicit` * or `conda-lock` @@ -15847,8 +15876,7 @@ exports.ensureExplicit = { } return [ "create", - "--name", - inputs.activateEnvironment, + ...conda.envCommandFlag(inputs), "--file", inputs.environmentFile, ]; @@ -20585,6 +20613,7 @@ const path = __importStar(__webpack_require__(622)); const yaml = __importStar(__webpack_require__(414)); const core = __importStar(__webpack_require__(470)); const constants = __importStar(__webpack_require__(211)); +const conda = __importStar(__webpack_require__(259)); const utils = __importStar(__webpack_require__(163)); /** * The current known providers of patches to `environment.yml` @@ -20663,8 +20692,7 @@ exports.ensureYaml = { return [ "env", "update", - "--name", - inputs.activateEnvironment, + ...conda.envCommandFlag(inputs), "--file", envFile, ]; @@ -35395,7 +35423,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.getEnvSpec = exports.environmentExists = exports.ensureEnvironment = void 0; +exports.getEnvSpec = exports.ensureEnvironment = void 0; const path = __importStar(__webpack_require__(622)); const fs = __importStar(__webpack_require__(747)); const yaml = __importStar(__webpack_require__(414)); @@ -35436,14 +35464,6 @@ function ensureEnvironment(inputs, options) { }); } exports.ensureEnvironment = ensureEnvironment; -/** - * Check if a given conda environment exists - */ -function environmentExists(inputs, options) { - const condaMetaPath = path.join(conda.condaBasePath(options), "envs", inputs.activateEnvironment, "conda-meta"); - return fs.existsSync(condaMetaPath); -} -exports.environmentExists = environmentExists; /** * Read and potentially parse the `environment-file` * @@ -38321,7 +38341,7 @@ LocationInfoTokenizerMixin.prototype._getOverriddenMethods = function (mxn, orig /******/ ], /******/ function(__webpack_require__) { // webpackRuntimeModules /******/ "use strict"; -/******/ +/******/ /******/ /* webpack/runtime/node module decorator */ /******/ !function() { /******/ __webpack_require__.nmd = function(module) { @@ -38338,6 +38358,6 @@ LocationInfoTokenizerMixin.prototype._getOverriddenMethods = function (mxn, orig /******/ return module; /******/ }; /******/ }(); -/******/ +/******/ /******/ } -); +); \ No newline at end of file diff --git a/src/base-tools/index.ts b/src/base-tools/index.ts index 48f0af35..44ee4a9a 100644 --- a/src/base-tools/index.ts +++ b/src/base-tools/index.ts @@ -47,7 +47,7 @@ export async function installBaseTools( postInstallOptions = { ...postInstallOptions, ...toolUpdates.options }; if (provider.postInstall) { core.info( - `... we will perform post-intall steps after we ${provider.label}.` + `... we will perform post-install steps after we ${provider.label}.` ); postInstallActions.push(provider.postInstall); } @@ -55,12 +55,7 @@ export async function installBaseTools( } if (tools.length) { - await conda.condaCommand( - ["install", "--name", "base", ...tools], - // Use the original `options`, as we can't guarantee `mamba` is available - // TODO: allow declaring that the installer already has `mamba` - options - ); + await conda.condaCommand(["install", "--name", "base", ...tools], 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/conda.ts b/src/conda.ts index 04e1f5e7..ed4d72f4 100644 --- a/src/conda.ts +++ b/src/conda.ts @@ -28,6 +28,19 @@ export function condaBasePath(options: types.IDynamicOptions): string { return condaPath; } +/** + * Provide conda CLI arguments for identifying an env by name or prefix/path + * + * ### Note + * Only really detects by presence of a path separator, as the path may not yet exist + */ +export function envCommandFlag(inputs: types.IActionInputs): string[] { + return [ + inputs.activateEnvironment.match(/(\\|\/)/) ? "--prefix" : "--name", + inputs.activateEnvironment, + ]; +} + /** * Provide cross platform location of conda/mamba executable */ @@ -144,10 +157,7 @@ export async function condaInit( options: types.IDynamicOptions ): Promise { let ownPath: string; - const isValidActivate: boolean = - inputs.activateEnvironment !== "base" && - inputs.activateEnvironment !== "root" && - inputs.activateEnvironment !== ""; + const isValidActivate = !utils.isBaseEnv(inputs.activateEnvironment); const autoActivateBase: boolean = options.condaConfig["auto_activate_base"] === "true"; @@ -213,7 +223,7 @@ export async function condaInit( if (isValidActivate) { powerExtraText += ` # Conda Setup Action: Custom activation - conda activate ${inputs.activateEnvironment}`; + conda activate "${inputs.activateEnvironment}"`; } powerExtraText += ` # ----------------------------------------------------------------------------`; @@ -226,7 +236,7 @@ export async function condaInit( if (isValidActivate) { bashExtraText += ` # Conda Setup Action: Custom activation - conda activate ${inputs.activateEnvironment}`; + conda activate "${inputs.activateEnvironment}"`; bashExtraText += ` # ----------------------------------------------------------------------------`; } @@ -242,7 +252,7 @@ export async function condaInit( if (isValidActivate) { batchExtraText += ` :: Conda Setup Action: Custom activation - @CALL "%CONDA_BAT%" activate ${inputs.activateEnvironment}`; + @CALL "%CONDA_BAT%" activate "${inputs.activateEnvironment}"`; } batchExtraText += ` :: Conda Setup Action: Basic configuration diff --git a/src/env/explicit.ts b/src/env/explicit.ts index b42b6864..f6275f5f 100644 --- a/src/env/explicit.ts +++ b/src/env/explicit.ts @@ -1,4 +1,5 @@ import * as types from "../types"; +import * as conda from "../conda"; /** * Install an environment from an explicit file generated `conda list --explicit` @@ -16,8 +17,7 @@ export const ensureExplicit: types.IEnvProvider = { return [ "create", - "--name", - inputs.activateEnvironment, + ...conda.envCommandFlag(inputs), "--file", inputs.environmentFile, ]; diff --git a/src/env/index.ts b/src/env/index.ts index 6c706207..c10c50d3 100644 --- a/src/env/index.ts +++ b/src/env/index.ts @@ -52,22 +52,6 @@ export async function ensureEnvironment( ); } -/** - * Check if a given conda environment exists - */ -export function environmentExists( - inputs: types.IActionInputs, - options: types.IDynamicOptions -): boolean { - const condaMetaPath = path.join( - conda.condaBasePath(options), - "envs", - inputs.activateEnvironment, - "conda-meta" - ); - return fs.existsSync(condaMetaPath); -} - /** * Read and potentially parse the `environment-file` * diff --git a/src/env/simple.ts b/src/env/simple.ts index 9d8b125f..26e3c2c1 100644 --- a/src/env/simple.ts +++ b/src/env/simple.ts @@ -2,6 +2,7 @@ import * as core from "@actions/core"; import * as types from "../types"; import * as utils from "../utils"; +import * as conda from "../conda"; /** * Install an environment with `conda create` when no `envSpec` is detected @@ -20,7 +21,7 @@ export const ensureSimple: types.IEnvProvider = { ); }, condaArgs: async (inputs, options) => { - const args = ["create", "--name", inputs.activateEnvironment]; + const args = ["create", ...conda.envCommandFlag(inputs)]; if (inputs.pythonVersion) { const spec = utils.makeSpec("python", inputs.pythonVersion); diff --git a/src/env/yaml.ts b/src/env/yaml.ts index 9847c3d4..a8e762f2 100644 --- a/src/env/yaml.ts +++ b/src/env/yaml.ts @@ -5,9 +5,9 @@ import * as path from "path"; import * as yaml from "js-yaml"; import * as core from "@actions/core"; - import * as types from "../types"; import * as constants from "../constants"; +import * as conda from "../conda"; import * as utils from "../utils"; /** @@ -121,8 +121,7 @@ export const ensureYaml: types.IEnvProvider = { return [ "env", "update", - "--name", - inputs.activateEnvironment, + ...conda.envCommandFlag(inputs), "--file", envFile, ]; diff --git a/src/tools.ts b/src/tools.ts deleted file mode 100644 index 56185874..00000000 --- a/src/tools.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { condaCommand } from "./conda"; - -import * as types from "./types"; - -/** - * Setup python test environment - */ -export async function setupPython( - inputs: types.IActionInputs, - options: types.IDynamicOptions -): Promise { - return await condaCommand( - [ - "install", - "--name", - inputs.activateEnvironment, - `python=${inputs.pythonVersion}`, - ], - options - ); -}