diff --git a/dist/delete/index.js b/dist/delete/index.js index 8782cbe6..3a878d60 100644 --- a/dist/delete/index.js +++ b/dist/delete/index.js @@ -1063,17 +1063,25 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.makeSpec = exports.execute = exports.cacheFolder = void 0; +exports.makeSpec = exports.execute = exports.isBaseEnv = exports.cacheFolder = void 0; const os = __importStar(__webpack_require__(87)); const path = __importStar(__webpack_require__(622)); const stream = __importStar(__webpack_require__(413)); const exec = __importStar(__webpack_require__(986)); const core = __importStar(__webpack_require__(470)); -const constants_1 = __webpack_require__(211); +const constants = __importStar(__webpack_require__(211)); +/** The folder to use as the conda package cache */ function cacheFolder() { - return path.join(os.homedir(), constants_1.CONDA_CACHE_FOLDER); + return path.join(os.homedir(), constants.CONDA_CACHE_FOLDER); } exports.cacheFolder = cacheFolder; +/** + * Whether the given env is a conda `base` env + */ +function isBaseEnv(envName) { + return constants.BASE_ENV_NAMES.includes(envName); +} +exports.isBaseEnv = isBaseEnv; /** * Run exec.exec with error handling */ @@ -1084,7 +1092,7 @@ function execute(command) { listeners: { stdout: (data) => { const stringData = data.toString(); - for (const forced_error of constants_1.FORCED_ERRORS) { + for (const forced_error of constants.FORCED_ERRORS) { if (stringData.includes(forced_error)) { throw new Error(`"${command}" failed with "${forced_error}"`); } @@ -1093,7 +1101,7 @@ function execute(command) { }, stderr: (data) => { const stringData = data.toString(); - for (const ignore of constants_1.IGNORED_WARNINGS) { + for (const ignore of constants.IGNORED_WARNINGS) { if (stringData.includes(ignore)) { return; } @@ -1153,7 +1161,7 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.PYTHON_SPEC = exports.ENV_VAR_CONDA_PKGS = exports.CONDA_CACHE_FOLDER = exports.CONDARC_PATH = exports.BOOTSTRAP_CONDARC = exports.FORCED_ERRORS = exports.IGNORED_WARNINGS = exports.KNOWN_EXTENSIONS = exports.OS_NAMES = exports.ARCHITECTURES = exports.MINICONDA_BASE_URL = exports.IS_UNIX = exports.IS_LINUX = exports.IS_MAC = exports.IS_WINDOWS = exports.MINICONDA_DIR_PATH = void 0; +exports.PYTHON_SPEC = exports.WIN_PERMS_FOLDERS = exports.PROFILES = exports.ENV_VAR_CONDA_PKGS = exports.CONDA_CACHE_FOLDER = exports.CONDARC_PATH = exports.BOOTSTRAP_CONDARC = exports.FORCED_ERRORS = exports.IGNORED_WARNINGS = exports.KNOWN_EXTENSIONS = exports.BASE_ENV_NAMES = exports.OS_NAMES = exports.ARCHITECTURES = exports.MINICONDA_BASE_URL = exports.IS_UNIX = exports.IS_LINUX = exports.IS_MAC = exports.IS_WINDOWS = exports.MINICONDA_DIR_PATH = void 0; const os = __importStar(__webpack_require__(87)); const path = __importStar(__webpack_require__(622)); //----------------------------------------------------------------------- @@ -1176,6 +1184,8 @@ exports.OS_NAMES = { darwin: "MacOSX", linux: "Linux", }; +/** Names for a conda `base` env */ +exports.BASE_ENV_NAMES = ["root", "base", ""]; /** * Known extensions for `constructor`-generated installers supported */ @@ -1184,20 +1194,22 @@ exports.KNOWN_EXTENSIONS = [".exe", ".sh"]; * Errors that are always probably spurious */ exports.IGNORED_WARNINGS = [ - // appear on win install, we can swallow them + // Appear on win install, we can swallow them `menuinst_win32`, `Unable to register environment`, `0%|`, - // appear on certain Linux/OSX installers + // Appear on certain Linux/OSX installers `Please run using "bash"`, - // old condas don't know what to do with these + // Old condas don't know what to do with these `Key 'use_only_tar_bz2' is not a known primitive parameter.`, + // Channel warnings are very boring and noisy + `moving to the top`, ]; /** * Warnings that should be errors */ exports.FORCED_ERRORS = [ - // conda env create will ignore invalid sections and move on + // `conda env create` will ignore invalid sections and move on `EnvironmentSectionNotValid`, ]; /** @@ -1212,6 +1224,27 @@ exports.CONDARC_PATH = path.join(os.homedir(), ".condarc"); exports.CONDA_CACHE_FOLDER = "conda_pkgs_dir"; /** The environment variable exported */ exports.ENV_VAR_CONDA_PKGS = "CONDA_PKGS_DIR"; +/** Shell profiles names to update so `conda` works for *login shells* */ +exports.PROFILES = [ + ".bashrc", + ".bash_profile", + ".config/fish/config.fish", + ".profile", + ".tcshrc", + ".xonshrc", + ".zshrc", + ".config/powershell/profile.ps1", + "Documents/PowerShell/profile.ps1", + "Documents/WindowsPowerShell/profile.ps1", +]; +/** Folders that need user ownership on windows */ +exports.WIN_PERMS_FOLDERS = [ + "condabin/", + "Scripts/", + "shell/", + "etc/profile.d/", + "/Lib/site-packages/xonsh/", +]; /** * A regular expression for detecting whether a spec is the python package, not * all of which are valid in all settings. diff --git a/dist/setup/index.js b/dist/setup/index.js index 5bffecda..659f0167 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -1160,7 +1160,7 @@ const utils = __importStar(__webpack_require__(163)); * In the future, this may need its own providers of environment patches. */ exports.ensureSimple = { - label: "create (simple)", + label: "conda create (simple)", provides: (inputs, options) => __awaiter(void 0, void 0, void 0, function* () { var _a, _b, _c; return !(((_b = (_a = options.envSpec) === null || _a === void 0 ? void 0 : _a.explicit) === null || _b === void 0 ? void 0 : _b.length) || @@ -2051,7 +2051,106 @@ module.exports = createReadableStreamAsyncIterator; /***/ }), /* 47 */, /* 48 */, -/* 49 */, +/* 49 */ +/***/ (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) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.installBaseTools = void 0; +const core = __importStar(__webpack_require__(470)); +const conda = __importStar(__webpack_require__(259)); +const update_conda_1 = __webpack_require__(715); +const update_mamba_1 = __webpack_require__(269); +const update_python_1 = __webpack_require__(113); +const update_conda_build_1 = __webpack_require__(234); +/** + * The providers of tool updates: order isn't _really_ important + * + * ### Note + * To add a new tool provider, + * - implement IToolProvider and add it here + * - probably add inputs to `../../action.yaml` + * - add any new RULEs in ../input.ts, for example if certain inputs make no sense + * - add a test! + */ +const TOOL_PROVIDERS = [ + update_conda_1.updateConda, + update_mamba_1.updateMamba, + update_python_1.updatePython, + update_conda_build_1.updateCondaBuild, +]; +/** + * Update the 'base' env with relevant tools + * + * Do this in one step to avoid multiple solves + */ +function installBaseTools(inputs, options) { + return __awaiter(this, void 0, void 0, function* () { + let tools = []; + let postInstallOptions = Object.assign({}, options); + let postInstallActions = []; + for (const provider of TOOL_PROVIDERS) { + core.info(`Do we need to ${provider.label}?`); + if (yield provider.provides(inputs, options)) { + core.info(`... will ${provider.label}.`); + const toolUpdates = yield provider.toolPackages(inputs, options); + tools.push(...toolUpdates.tools); + postInstallOptions = Object.assign(Object.assign({}, postInstallOptions), toolUpdates.options); + if (provider.postInstall) { + 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); + // *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); + if (postInstallActions.length) { + for (const action of postInstallActions) { + yield action(inputs, postInstallOptions); + } + } + } + return postInstallOptions; + }); +} +exports.installBaseTools = installBaseTools; + + +/***/ }), /* 50 */, /* 51 */ /***/ (function(module, __unusedexports, __webpack_require__) { @@ -6323,7 +6422,61 @@ module.exports = assignMergeValue; /***/ }), /* 111 */, /* 112 */, -/* 113 */, +/* 113 */ +/***/ (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) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.updatePython = void 0; +const utils = __importStar(__webpack_require__(163)); +/** Install `python` in the `base` env at a specified version + * + * ### Note + * The env providers have separate mechanisms for updating conda. + */ +exports.updatePython = { + label: "update python", + provides: (inputs, options) => __awaiter(void 0, void 0, void 0, function* () { return !!(inputs.pythonVersion && utils.isBaseEnv(inputs.activateEnvironment)); }), + toolPackages: (inputs, options) => __awaiter(void 0, void 0, void 0, function* () { + let updates = { + tools: [utils.makeSpec("python", inputs.pythonVersion)], + options, + }; + return updates; + }), +}; + + +/***/ }), /* 114 */, /* 115 */, /* 116 */, @@ -6497,7 +6650,7 @@ const conda = __importStar(__webpack_require__(259)); */ function ensureLocalInstaller(options) { return __awaiter(this, void 0, void 0, function* () { - core.startGroup("Ensuring Installer..."); + core.info("Ensuring Installer..."); const url = new url_1.URL(options.url); const installerName = path.basename(url.pathname); // As a URL, we assume posix paths @@ -6531,7 +6684,6 @@ function ensureLocalInstaller(options) { const cacheResult = yield tc.cacheFile(executablePath, installerName, tool, version, ...(options.arch ? [options.arch] : [])); core.info(`Cached ${tool}@${version}: ${cacheResult}!`); } - core.endGroup(); if (executablePath === "") { throw Error("Could not determine an executable path from installer-url"); } @@ -8036,17 +8188,25 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.makeSpec = exports.execute = exports.cacheFolder = void 0; +exports.makeSpec = exports.execute = exports.isBaseEnv = exports.cacheFolder = void 0; const os = __importStar(__webpack_require__(87)); const path = __importStar(__webpack_require__(622)); const stream = __importStar(__webpack_require__(413)); const exec = __importStar(__webpack_require__(986)); const core = __importStar(__webpack_require__(470)); -const constants_1 = __webpack_require__(211); +const constants = __importStar(__webpack_require__(211)); +/** The folder to use as the conda package cache */ function cacheFolder() { - return path.join(os.homedir(), constants_1.CONDA_CACHE_FOLDER); + return path.join(os.homedir(), constants.CONDA_CACHE_FOLDER); } exports.cacheFolder = cacheFolder; +/** + * Whether the given env is a conda `base` env + */ +function isBaseEnv(envName) { + return constants.BASE_ENV_NAMES.includes(envName); +} +exports.isBaseEnv = isBaseEnv; /** * Run exec.exec with error handling */ @@ -8057,7 +8217,7 @@ function execute(command) { listeners: { stdout: (data) => { const stringData = data.toString(); - for (const forced_error of constants_1.FORCED_ERRORS) { + for (const forced_error of constants.FORCED_ERRORS) { if (stringData.includes(forced_error)) { throw new Error(`"${command}" failed with "${forced_error}"`); } @@ -8066,7 +8226,7 @@ function execute(command) { }, stderr: (data) => { const stringData = data.toString(); - for (const ignore of constants_1.IGNORED_WARNINGS) { + for (const ignore of constants.IGNORED_WARNINGS) { if (stringData.includes(ignore)) { return; } @@ -9227,7 +9387,7 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.PYTHON_SPEC = exports.ENV_VAR_CONDA_PKGS = exports.CONDA_CACHE_FOLDER = exports.CONDARC_PATH = exports.BOOTSTRAP_CONDARC = exports.FORCED_ERRORS = exports.IGNORED_WARNINGS = exports.KNOWN_EXTENSIONS = exports.OS_NAMES = exports.ARCHITECTURES = exports.MINICONDA_BASE_URL = exports.IS_UNIX = exports.IS_LINUX = exports.IS_MAC = exports.IS_WINDOWS = exports.MINICONDA_DIR_PATH = void 0; +exports.PYTHON_SPEC = exports.WIN_PERMS_FOLDERS = exports.PROFILES = exports.ENV_VAR_CONDA_PKGS = exports.CONDA_CACHE_FOLDER = exports.CONDARC_PATH = exports.BOOTSTRAP_CONDARC = exports.FORCED_ERRORS = exports.IGNORED_WARNINGS = exports.KNOWN_EXTENSIONS = exports.BASE_ENV_NAMES = exports.OS_NAMES = exports.ARCHITECTURES = exports.MINICONDA_BASE_URL = exports.IS_UNIX = exports.IS_LINUX = exports.IS_MAC = exports.IS_WINDOWS = exports.MINICONDA_DIR_PATH = void 0; const os = __importStar(__webpack_require__(87)); const path = __importStar(__webpack_require__(622)); //----------------------------------------------------------------------- @@ -9250,6 +9410,8 @@ exports.OS_NAMES = { darwin: "MacOSX", linux: "Linux", }; +/** Names for a conda `base` env */ +exports.BASE_ENV_NAMES = ["root", "base", ""]; /** * Known extensions for `constructor`-generated installers supported */ @@ -9258,20 +9420,22 @@ exports.KNOWN_EXTENSIONS = [".exe", ".sh"]; * Errors that are always probably spurious */ exports.IGNORED_WARNINGS = [ - // appear on win install, we can swallow them + // Appear on win install, we can swallow them `menuinst_win32`, `Unable to register environment`, `0%|`, - // appear on certain Linux/OSX installers + // Appear on certain Linux/OSX installers `Please run using "bash"`, - // old condas don't know what to do with these + // Old condas don't know what to do with these `Key 'use_only_tar_bz2' is not a known primitive parameter.`, + // Channel warnings are very boring and noisy + `moving to the top`, ]; /** * Warnings that should be errors */ exports.FORCED_ERRORS = [ - // conda env create will ignore invalid sections and move on + // `conda env create` will ignore invalid sections and move on `EnvironmentSectionNotValid`, ]; /** @@ -9286,6 +9450,27 @@ exports.CONDARC_PATH = path.join(os.homedir(), ".condarc"); exports.CONDA_CACHE_FOLDER = "conda_pkgs_dir"; /** The environment variable exported */ exports.ENV_VAR_CONDA_PKGS = "CONDA_PKGS_DIR"; +/** Shell profiles names to update so `conda` works for *login shells* */ +exports.PROFILES = [ + ".bashrc", + ".bash_profile", + ".config/fish/config.fish", + ".profile", + ".tcshrc", + ".xonshrc", + ".zshrc", + ".config/powershell/profile.ps1", + "Documents/PowerShell/profile.ps1", + "Documents/WindowsPowerShell/profile.ps1", +]; +/** Folders that need user ownership on windows */ +exports.WIN_PERMS_FOLDERS = [ + "condabin/", + "Scripts/", + "shell/", + "etc/profile.d/", + "/Lib/site-packages/xonsh/", +]; /** * A regular expression for detecting whether a spec is the python package, not * all of which are valid in all settings. @@ -11086,7 +11271,56 @@ module.exports = { /***/ }), /* 233 */, -/* 234 */, +/* 234 */ +/***/ (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) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.updateCondaBuild = void 0; +const utils = __importStar(__webpack_require__(163)); +/** Install `conda-build` in the `base` env at a specified version */ +exports.updateCondaBuild = { + label: "update conda-build", + provides: (inputs, options) => __awaiter(void 0, void 0, void 0, function* () { return inputs.condaBuildVersion !== ""; }), + toolPackages: (inputs, options) => __awaiter(void 0, void 0, void 0, function* () { + return { + tools: [utils.makeSpec("conda-build", inputs.condaBuildVersion)], + options, + }; + }), +}; + + +/***/ }), /* 235 */, /* 236 */, /* 237 */, @@ -12916,7 +13150,7 @@ function condaInit(inputs, options) { // Fix ownership of folders if (options.useBundled) { if (constants.IS_MAC) { - core.startGroup("Fixing conda folders ownership"); + core.info("Fixing conda folders ownership"); const userName = process.env.USER; yield utils.execute([ "sudo", @@ -12925,39 +13159,20 @@ function condaInit(inputs, options) { `${userName}:staff`, condaBasePath(options), ]); - core.endGroup(); } else if (constants.IS_WINDOWS) { - for (let folder of [ - "condabin/", - "Scripts/", - "shell/", - "etc/profile.d/", - "/Lib/site-packages/xonsh/", - ]) { + for (let folder of constants.WIN_PERMS_FOLDERS) { ownPath = path.join(condaBasePath(options), folder); if (fs.existsSync(ownPath)) { - core.startGroup(`Fixing ${folder} ownership`); + core.info(`Fixing ${folder} ownership`); yield utils.execute(["takeown", "/f", ownPath, "/r", "/d", "y"]); - core.endGroup(); } } } } // Remove profile files if (inputs.removeProfiles == "true") { - for (let rc of [ - ".bashrc", - ".bash_profile", - ".config/fish/config.fish", - ".profile", - ".tcshrc", - ".xonshrc", - ".zshrc", - ".config/powershell/profile.ps1", - "Documents/PowerShell/profile.ps1", - "Documents/WindowsPowerShell/profile.ps1", - ]) { + for (let rc of constants.PROFILES) { try { let file = path.join(os.homedir(), rc); if (fs.existsSync(file)) { @@ -13455,7 +13670,72 @@ module.exports = listCacheSet; /***/ }), -/* 269 */, +/* 269 */ +/***/ (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) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.updateMamba = void 0; +const fs = __importStar(__webpack_require__(747)); +const core = __importStar(__webpack_require__(470)); +const constants = __importStar(__webpack_require__(211)); +const utils = __importStar(__webpack_require__(163)); +const conda = __importStar(__webpack_require__(259)); +/** Install `mamba` in the `base` env at a specified version */ +exports.updateMamba = { + label: "update mamba", + provides: (inputs, options) => __awaiter(void 0, void 0, void 0, function* () { return inputs.mambaVersion !== ""; }), + toolPackages: (inputs, options) => __awaiter(void 0, void 0, void 0, function* () { + core.warning(`Mamba support is still experimental and can result in differently solved environments!`); + return { + tools: [utils.makeSpec("mamba", inputs.mambaVersion)], + options: Object.assign(Object.assign({}, options), { useMamba: true }), + }; + }), + postInstall: (inputs, options) => __awaiter(void 0, void 0, void 0, function* () { + if (!constants.IS_WINDOWS) { + return; + } + core.info("Creating bash wrapper for `mamba`..."); + // Add bat-less forwarder for bash users on Windows + const mambaBat = conda.condaExecutable(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}`); + }), +}; + + +/***/ }), /* 270 */, /* 271 */, /* 272 */ @@ -15557,7 +15837,7 @@ exports.ensureExplicit = void 0; * or `conda-lock` */ exports.ensureExplicit = { - label: "create (explicit)", + label: "conda create (from explicit)", provides: (inputs, options) => __awaiter(void 0, void 0, void 0, function* () { var _a, _b; return !!((_b = (_a = options.envSpec) === null || _a === void 0 ? void 0 : _a.explicit) === null || _b === void 0 ? void 0 : _b.length); }), condaArgs: (inputs, options) => __awaiter(void 0, void 0, void 0, function* () { if (inputs.pythonVersion) { @@ -20201,6 +20481,7 @@ const outputs = __importStar(__webpack_require__(405)); const installer = __importStar(__webpack_require__(555)); const conda = __importStar(__webpack_require__(259)); const env = __importStar(__webpack_require__(956)); +const baseTools = __importStar(__webpack_require__(49)); /** * Main conda setup method to handle all configuration options */ @@ -20229,50 +20510,14 @@ function setupMiniconda(inputs) { options.envSpec = yield core.group("Parsing environment...", () => env.getEnvSpec(inputs)); yield core.group("Configuring conda package cache...", () => outputs.setCacheVariable(options)); yield core.group("Applying initial configuration...", () => conda.applyCondaConfiguration(inputs, options)); - core.startGroup("Setup Conda basic configuration..."); - yield conda.condaCommand(["config", "--set", "always_yes", "yes", "--set", "changeps1", "no"], options); - core.endGroup(); - core.startGroup("Initialize Conda and fix ownership..."); - yield conda.condaInit(inputs, options); - core.endGroup(); - if (inputs.condaVersion) { - core.startGroup("Installing Conda..."); - yield conda.condaCommand(["install", "--name", "base", `conda=${inputs.condaVersion}`], options); - core.endGroup(); - } - if (options.condaConfig["auto_update_conda"] == "true") { - core.startGroup("Updating conda..."); - yield conda.condaCommand(["update", "conda"], options); - core.endGroup(); - if (options.condaConfig) { - core.startGroup("Applying conda configuration after update..."); - yield conda.applyCondaConfiguration(inputs, options); - core.endGroup(); - } - } - // Any conda commands run here after init and setup - if (inputs.mambaVersion) { - core.startGroup("Installing Mamba..."); - core.warning(`Mamba support is still experimental and can result in differently solved environments!`); - yield conda.condaCommand(["install", "--name", "base", `mamba=${inputs.mambaVersion}`], options); - if (constants.IS_WINDOWS) { - // add bat-less forwarder for bash users on Windows - const mambaBat = conda - .condaExecutable(Object.assign(Object.assign({}, options), { useMamba: true })) - .replace("\\", "/"); - const contents = `bash.exe -c "exec '${mambaBat}' $*"`; - fs.writeFileSync(mambaBat.slice(0, -4), contents); - } - options.useMamba = true; - } - if (inputs.condaBuildVersion) { - core.startGroup("Installing Conda Build..."); - yield conda.condaCommand(["install", "--name", "base", `conda-build=${inputs.condaBuildVersion}`], options); - core.endGroup(); - } + yield core.group("Initializing conda shell integration...", () => conda.condaInit(inputs, options)); + const toolOptions = yield core.group("Adding tools to 'base' env", () => baseTools.installBaseTools(inputs, options)); + // `useMamba` may have changed + options = Object.assign(Object.assign({}, options), toolOptions); if (inputs.activateEnvironment) { yield core.group("Ensuring environment...", () => env.ensureEnvironment(inputs, options)); } + core.info("setup-miniconda ran successfully"); }); } /** @@ -20344,7 +20589,7 @@ const utils = __importStar(__webpack_require__(163)); * To add a new patch * - implement IEnvPatchProvider and add it here * - probably add inputs to `../../action.yaml` - * - any any new RULEs in ../input.ts, for example if certain inputs make no sense + * - add any new RULEs in ../input.ts, for example if certain inputs make no sense * - add a test! */ const PATCH_PROVIDERS = [ @@ -20364,7 +20609,7 @@ const PATCH_PROVIDERS = [ * If patched, a temporary file will be created with the patches */ exports.ensureYaml = { - label: "env update", + label: "conda env update", provides: (inputs, options) => __awaiter(void 0, void 0, void 0, function* () { var _a; return !!Object.keys(((_a = options.envSpec) === null || _a === void 0 ? void 0 : _a.yaml) || {}).length; }), condaArgs: (inputs, options) => __awaiter(void 0, void 0, void 0, function* () { var _b; @@ -21849,7 +22094,7 @@ const bundled_miniconda_1 = __webpack_require__(468); * To add a new installer, * - implement IInstallerProvider and add it here * - add to `../../action.yaml` - * - any any new RULEs in ../input.ts, for example if the installer is not + * - add any new RULEs in ../input.ts, for example if the installer is not * compatible with some architectures * - add a test! */ @@ -21862,9 +22107,9 @@ const INSTALLER_PROVIDERS = [ function getLocalInstallerPath(inputs, options) { return __awaiter(this, void 0, void 0, function* () { for (const provider of INSTALLER_PROVIDERS) { - core.info(`Can we ${provider.label}?`); + core.info(`Can we use ${provider.label}?`); if (yield provider.provides(inputs, options)) { - core.info(`... will ${provider.label}`); + core.info(`... will use ${provider.label}.`); return provider.installerPath(inputs, options); } } @@ -26179,7 +26424,64 @@ module.exports = hashClear; /* 712 */, /* 713 */, /* 714 */, -/* 715 */, +/* 715 */ +/***/ (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) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.updateConda = void 0; +const utils = __importStar(__webpack_require__(163)); +/** Install `conda` in the `base` env at a specified version */ +exports.updateConda = { + label: "update conda", + provides: (inputs, options) => __awaiter(void 0, void 0, void 0, function* () { + return inputs.condaVersion !== "" || + inputs.condaConfig.auto_update_conda === "yes"; + }), + toolPackages: (inputs, options) => __awaiter(void 0, void 0, void 0, function* () { + let updates = { + tools: [ + inputs.condaVersion !== "" + ? utils.makeSpec("conda", inputs.condaVersion) + : "conda", + ], + options, + }; + return updates; + }), +}; + + +/***/ }), /* 716 */, /* 717 */, /* 718 */, @@ -34962,7 +35264,7 @@ const simple_1 = __webpack_require__(21); * To add a new env creation mechanism, * - implement IEnvProvider and add it here * - probably add inputs to `../../action.yaml` - * - any any new RULEs in ../input.ts, for example if certain inputs make no sense + * - add any new RULEs in ../input.ts, for example if certain inputs make no sense * - add a test! */ const ENV_PROVIDERS = [ @@ -34976,11 +35278,11 @@ const ENV_PROVIDERS = [ function ensureEnvironment(inputs, options) { return __awaiter(this, void 0, void 0, function* () { for (const provider of ENV_PROVIDERS) { - core.info(`Can we use ${provider.label}...`); + core.info(`Can we use ${provider.label}?`); if (yield provider.provides(inputs, options)) { - core.info(`... will ${provider.label}`); + core.info(`... will use ${provider.label}.`); const args = yield provider.condaArgs(inputs, options); - return yield core.group(`Updating env from ${provider.label}...`, () => conda.condaCommand(args, options)); + return yield core.group(`Updating '${inputs.activateEnvironment}' env from ${provider.label}...`, () => conda.condaCommand(args, options)); } } throw Error(`'activate-environment: ${inputs.activateEnvironment}' could not be created`); diff --git a/src/base-tools/index.ts b/src/base-tools/index.ts new file mode 100644 index 00000000..83300cc5 --- /dev/null +++ b/src/base-tools/index.ts @@ -0,0 +1,74 @@ +import * as types from "../types"; + +import * as core from "@actions/core"; + +import * as conda from "../conda"; + +import { updateConda } from "./update-conda"; +import { updateMamba } from "./update-mamba"; +import { updatePython } from "./update-python"; +import { updateCondaBuild } from "./update-conda-build"; + +/** + * The providers of tool updates: order isn't _really_ important + * + * ### Note + * To add a new tool provider, + * - implement IToolProvider and add it here + * - probably add inputs to `../../action.yaml` + * - add any new RULEs in ../input.ts, for example if certain inputs make no sense + * - add a test! + */ +const TOOL_PROVIDERS: types.IToolProvider[] = [ + updateConda, + updateMamba, + updatePython, + updateCondaBuild, +]; + +/** + * Update the 'base' env with relevant tools + * + * Do this in one step to avoid multiple solves + */ +export async function installBaseTools( + inputs: types.IActionInputs, + options: types.IDynamicOptions +) { + let tools = []; + let postInstallOptions = { ...options }; + let postInstallActions = []; + for (const provider of TOOL_PROVIDERS) { + core.info(`Do we need to ${provider.label}?`); + if (await provider.provides(inputs, options)) { + core.info(`... will ${provider.label}.`); + const toolUpdates = await provider.toolPackages(inputs, options); + tools.push(...toolUpdates.tools); + postInstallOptions = { ...postInstallOptions, ...toolUpdates.options }; + if (provider.postInstall) { + postInstallActions.push(provider.postInstall); + } + } + } + + 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 + ); + + // *Now* use the new options, as we may have a new conda/mamba with more supported + // options that previously failed + await conda.applyCondaConfiguration(inputs, postInstallOptions); + + if (postInstallActions.length) { + for (const action of postInstallActions) { + await action(inputs, postInstallOptions); + } + } + } + + return postInstallOptions; +} diff --git a/src/base-tools/update-conda-build.ts b/src/base-tools/update-conda-build.ts new file mode 100644 index 00000000..c587e874 --- /dev/null +++ b/src/base-tools/update-conda-build.ts @@ -0,0 +1,14 @@ +import * as types from "../types"; +import * as utils from "../utils"; + +/** Install `conda-build` in the `base` env at a specified version */ +export const updateCondaBuild: types.IToolProvider = { + label: "update conda-build", + provides: async (inputs, options) => inputs.condaBuildVersion !== "", + toolPackages: async (inputs, options) => { + return { + tools: [utils.makeSpec("conda-build", inputs.condaBuildVersion)], + options, + }; + }, +}; diff --git a/src/base-tools/update-conda.ts b/src/base-tools/update-conda.ts new file mode 100644 index 00000000..6eed0ed1 --- /dev/null +++ b/src/base-tools/update-conda.ts @@ -0,0 +1,22 @@ +import * as types from "../types"; +import * as utils from "../utils"; + +/** Install `conda` in the `base` env at a specified version */ +export const updateConda: types.IToolProvider = { + label: "update conda", + provides: async (inputs, options) => + inputs.condaVersion !== "" || + inputs.condaConfig.auto_update_conda === "yes", + toolPackages: async (inputs, options) => { + let updates: types.IToolUpdates = { + tools: [ + inputs.condaVersion !== "" + ? utils.makeSpec("conda", inputs.condaVersion) + : "conda", + ], + options, + }; + + return updates; + }, +}; diff --git a/src/base-tools/update-mamba.ts b/src/base-tools/update-mamba.ts new file mode 100644 index 00000000..da57a2cf --- /dev/null +++ b/src/base-tools/update-mamba.ts @@ -0,0 +1,36 @@ +import * as fs from "fs"; + +import * as core from "@actions/core"; + +import * as constants from "../constants"; +import * as types from "../types"; +import * as utils from "../utils"; + +import * as conda from "../conda"; + +/** Install `mamba` in the `base` env at a specified version */ +export const updateMamba: types.IToolProvider = { + label: "update mamba", + provides: async (inputs, options) => inputs.mambaVersion !== "", + toolPackages: async (inputs, options) => { + core.warning( + `Mamba support is still experimental and can result in differently solved environments!` + ); + return { + tools: [utils.makeSpec("mamba", inputs.mambaVersion)], + options: { ...options, useMamba: true }, + }; + }, + postInstall: async (inputs, options) => { + if (!constants.IS_WINDOWS) { + return; + } + core.info("Creating bash wrapper for `mamba`..."); + // Add bat-less forwarder for bash users on Windows + const mambaBat = conda.condaExecutable(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}`); + }, +}; diff --git a/src/base-tools/update-python.ts b/src/base-tools/update-python.ts new file mode 100644 index 00000000..b8d7a578 --- /dev/null +++ b/src/base-tools/update-python.ts @@ -0,0 +1,21 @@ +import * as types from "../types"; +import * as utils from "../utils"; + +/** Install `python` in the `base` env at a specified version + * + * ### Note + * The env providers have separate mechanisms for updating conda. + */ +export const updatePython: types.IToolProvider = { + label: "update python", + provides: async (inputs, options) => + !!(inputs.pythonVersion && utils.isBaseEnv(inputs.activateEnvironment)), + toolPackages: async (inputs, options) => { + let updates: types.IToolUpdates = { + tools: [utils.makeSpec("python", inputs.pythonVersion)], + options, + }; + + return updates; + }, +}; diff --git a/src/conda.ts b/src/conda.ts index b7e27dda..09ad16f1 100644 --- a/src/conda.ts +++ b/src/conda.ts @@ -140,7 +140,7 @@ export async function condaInit( // Fix ownership of folders if (options.useBundled) { if (constants.IS_MAC) { - core.startGroup("Fixing conda folders ownership"); + core.info("Fixing conda folders ownership"); const userName: string = process.env.USER as string; await utils.execute([ "sudo", @@ -149,20 +149,12 @@ export async function condaInit( `${userName}:staff`, condaBasePath(options), ]); - core.endGroup(); } else if (constants.IS_WINDOWS) { - for (let folder of [ - "condabin/", - "Scripts/", - "shell/", - "etc/profile.d/", - "/Lib/site-packages/xonsh/", - ]) { + for (let folder of constants.WIN_PERMS_FOLDERS) { ownPath = path.join(condaBasePath(options), folder); if (fs.existsSync(ownPath)) { - core.startGroup(`Fixing ${folder} ownership`); + core.info(`Fixing ${folder} ownership`); await utils.execute(["takeown", "/f", ownPath, "/r", "/d", "y"]); - core.endGroup(); } } } @@ -170,18 +162,7 @@ export async function condaInit( // Remove profile files if (inputs.removeProfiles == "true") { - for (let rc of [ - ".bashrc", - ".bash_profile", - ".config/fish/config.fish", - ".profile", - ".tcshrc", - ".xonshrc", - ".zshrc", - ".config/powershell/profile.ps1", - "Documents/PowerShell/profile.ps1", - "Documents/WindowsPowerShell/profile.ps1", - ]) { + for (let rc of constants.PROFILES) { try { let file: string = path.join(os.homedir(), rc); if (fs.existsSync(file)) { diff --git a/src/constants.ts b/src/constants.ts index 614e1ee8..4180e91d 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -27,6 +27,9 @@ export const OS_NAMES: types.IOperatingSystems = { linux: "Linux", }; +/** Names for a conda `base` env */ +export const BASE_ENV_NAMES = ["root", "base", ""]; + /** * Known extensions for `constructor`-generated installers supported */ @@ -36,21 +39,23 @@ export const KNOWN_EXTENSIONS = [".exe", ".sh"]; * Errors that are always probably spurious */ export const IGNORED_WARNINGS = [ - // appear on win install, we can swallow them + // Appear on win install, we can swallow them `menuinst_win32`, `Unable to register environment`, `0%|`, - // appear on certain Linux/OSX installers + // Appear on certain Linux/OSX installers `Please run using "bash"`, - // old condas don't know what to do with these + // Old condas don't know what to do with these `Key 'use_only_tar_bz2' is not a known primitive parameter.`, + // Channel warnings are very boring and noisy + `moving to the top`, ]; /** * Warnings that should be errors */ export const FORCED_ERRORS = [ - // conda env create will ignore invalid sections and move on + // `conda env create` will ignore invalid sections and move on `EnvironmentSectionNotValid`, ]; @@ -70,6 +75,29 @@ export const CONDA_CACHE_FOLDER = "conda_pkgs_dir"; /** The environment variable exported */ export const ENV_VAR_CONDA_PKGS = "CONDA_PKGS_DIR"; +/** Shell profiles names to update so `conda` works for *login shells* */ +export const PROFILES = [ + ".bashrc", + ".bash_profile", + ".config/fish/config.fish", + ".profile", + ".tcshrc", + ".xonshrc", + ".zshrc", + ".config/powershell/profile.ps1", + "Documents/PowerShell/profile.ps1", + "Documents/WindowsPowerShell/profile.ps1", +]; + +/** Folders that need user ownership on windows */ +export const WIN_PERMS_FOLDERS = [ + "condabin/", + "Scripts/", + "shell/", + "etc/profile.d/", + "/Lib/site-packages/xonsh/", +]; + /** * A regular expression for detecting whether a spec is the python package, not * all of which are valid in all settings. diff --git a/src/env/explicit.ts b/src/env/explicit.ts index 5be5e3bb..b42b6864 100644 --- a/src/env/explicit.ts +++ b/src/env/explicit.ts @@ -5,7 +5,7 @@ import * as types from "../types"; * or `conda-lock` */ export const ensureExplicit: types.IEnvProvider = { - label: "create (explicit)", + label: "conda create (from explicit)", provides: async (inputs, options) => !!options.envSpec?.explicit?.length, condaArgs: async (inputs, options) => { if (inputs.pythonVersion) { diff --git a/src/env/index.ts b/src/env/index.ts index 342bb1fa..d9641a35 100644 --- a/src/env/index.ts +++ b/src/env/index.ts @@ -19,7 +19,7 @@ import { ensureSimple } from "./simple"; * To add a new env creation mechanism, * - implement IEnvProvider and add it here * - probably add inputs to `../../action.yaml` - * - any any new RULEs in ../input.ts, for example if certain inputs make no sense + * - add any new RULEs in ../input.ts, for example if certain inputs make no sense * - add a test! */ const ENV_PROVIDERS: types.IEnvProvider[] = [ @@ -36,12 +36,13 @@ export async function ensureEnvironment( options: types.IDynamicOptions ): Promise { for (const provider of ENV_PROVIDERS) { - core.info(`Can we use ${provider.label}...`); + core.info(`Can we use ${provider.label}?`); if (await provider.provides(inputs, options)) { - core.info(`... will ${provider.label}`); + core.info(`... will use ${provider.label}.`); const args = await provider.condaArgs(inputs, options); - return await core.group(`Updating env from ${provider.label}...`, () => - conda.condaCommand(args, options) + return await core.group( + `Updating '${inputs.activateEnvironment}' env from ${provider.label}...`, + () => conda.condaCommand(args, options) ); } } diff --git a/src/env/simple.ts b/src/env/simple.ts index c73d6996..9d8b125f 100644 --- a/src/env/simple.ts +++ b/src/env/simple.ts @@ -12,7 +12,7 @@ import * as utils from "../utils"; * In the future, this may need its own providers of environment patches. */ export const ensureSimple: types.IEnvProvider = { - label: "create (simple)", + label: "conda create (simple)", provides: async (inputs, options) => { return !( options.envSpec?.explicit?.length || diff --git a/src/env/yaml.ts b/src/env/yaml.ts index 9bee7687..1484dab4 100644 --- a/src/env/yaml.ts +++ b/src/env/yaml.ts @@ -38,7 +38,7 @@ interface IYAMLEnvPatchProvider { * To add a new patch * - implement IEnvPatchProvider and add it here * - probably add inputs to `../../action.yaml` - * - any any new RULEs in ../input.ts, for example if certain inputs make no sense + * - add any new RULEs in ../input.ts, for example if certain inputs make no sense * - add a test! */ const PATCH_PROVIDERS: IYAMLEnvPatchProvider[] = [ @@ -59,7 +59,7 @@ const PATCH_PROVIDERS: IYAMLEnvPatchProvider[] = [ * If patched, a temporary file will be created with the patches */ export const ensureYaml: types.IEnvProvider = { - label: "env update", + label: "conda env update", provides: async (inputs, options) => !!Object.keys(options.envSpec?.yaml || {}).length, condaArgs: async (inputs, options) => { diff --git a/src/installer/base.ts b/src/installer/base.ts index ac2cf315..71ac809e 100644 --- a/src/installer/base.ts +++ b/src/installer/base.ts @@ -24,7 +24,7 @@ import * as conda from "../conda"; export async function ensureLocalInstaller( options: types.ILocalInstallerOpts ): Promise { - core.startGroup("Ensuring Installer..."); + core.info("Ensuring Installer..."); const url = new URL(options.url); @@ -72,8 +72,6 @@ export async function ensureLocalInstaller( core.info(`Cached ${tool}@${version}: ${cacheResult}!`); } - core.endGroup(); - if (executablePath === "") { throw Error("Could not determine an executable path from installer-url"); } diff --git a/src/installer/index.ts b/src/installer/index.ts index dec8d2bf..3c78017d 100644 --- a/src/installer/index.ts +++ b/src/installer/index.ts @@ -16,7 +16,7 @@ import { bundledMinicondaUser } from "./bundled-miniconda"; * To add a new installer, * - implement IInstallerProvider and add it here * - add to `../../action.yaml` - * - any any new RULEs in ../input.ts, for example if the installer is not + * - add any new RULEs in ../input.ts, for example if the installer is not * compatible with some architectures * - add a test! */ @@ -32,9 +32,9 @@ export async function getLocalInstallerPath( options: types.IDynamicOptions ) { for (const provider of INSTALLER_PROVIDERS) { - core.info(`Can we ${provider.label}?`); + core.info(`Can we use ${provider.label}?`); if (await provider.provides(inputs, options)) { - core.info(`... will ${provider.label}`); + core.info(`... will use ${provider.label}.`); return provider.installerPath(inputs, options); } } diff --git a/src/setup.ts b/src/setup.ts index d603c5ee..3dc1501e 100644 --- a/src/setup.ts +++ b/src/setup.ts @@ -9,6 +9,7 @@ import * as outputs from "./outputs"; import * as installer from "./installer"; import * as conda from "./conda"; import * as env from "./env"; +import * as baseTools from "./base-tools"; /** * Main conda setup method to handle all configuration options @@ -64,76 +65,24 @@ async function setupMiniconda(inputs: types.IActionInputs): Promise { conda.applyCondaConfiguration(inputs, options) ); - core.startGroup("Setup Conda basic configuration..."); - await conda.condaCommand( - ["config", "--set", "always_yes", "yes", "--set", "changeps1", "no"], - options + await core.group("Initializing conda shell integration...", () => + conda.condaInit(inputs, options) ); - core.endGroup(); - core.startGroup("Initialize Conda and fix ownership..."); - await conda.condaInit(inputs, options); - core.endGroup(); - - if (inputs.condaVersion) { - core.startGroup("Installing Conda..."); - await conda.condaCommand( - ["install", "--name", "base", `conda=${inputs.condaVersion}`], - options - ); - core.endGroup(); - } - - if (options.condaConfig["auto_update_conda"] == "true") { - core.startGroup("Updating conda..."); - await conda.condaCommand(["update", "conda"], options); - core.endGroup(); - - if (options.condaConfig) { - core.startGroup("Applying conda configuration after update..."); - await conda.applyCondaConfiguration(inputs, options); - core.endGroup(); - } - } - - // Any conda commands run here after init and setup - if (inputs.mambaVersion) { - core.startGroup("Installing Mamba..."); - core.warning( - `Mamba support is still experimental and can result in differently solved environments!` - ); - - await conda.condaCommand( - ["install", "--name", "base", `mamba=${inputs.mambaVersion}`], - options - ); - - if (constants.IS_WINDOWS) { - // add bat-less forwarder for bash users on Windows - const mambaBat = conda - .condaExecutable({ ...options, useMamba: true }) - .replace("\\", "/"); - const contents = `bash.exe -c "exec '${mambaBat}' $*"`; - fs.writeFileSync(mambaBat.slice(0, -4), contents); - } - - options.useMamba = true; - } + const toolOptions = await core.group("Adding tools to 'base' env", () => + baseTools.installBaseTools(inputs, options) + ); - if (inputs.condaBuildVersion) { - core.startGroup("Installing Conda Build..."); - await conda.condaCommand( - ["install", "--name", "base", `conda-build=${inputs.condaBuildVersion}`], - options - ); - core.endGroup(); - } + // `useMamba` may have changed + options = { ...options, ...toolOptions }; if (inputs.activateEnvironment) { await core.group("Ensuring environment...", () => env.ensureEnvironment(inputs, options) ); } + + core.info("setup-miniconda ran successfully"); } /** diff --git a/src/types.ts b/src/types.ts index 8f1abb82..66784e97 100644 --- a/src/types.ts +++ b/src/types.ts @@ -153,3 +153,37 @@ export interface IEnvProvider { options: IDynamicOptions ) => Promise; } + +/** The options and package specs to add to the base environment */ +export interface IToolUpdates { + options: IDynamicOptions; + tools: string[]; +} + +/** + * A strategy for ensuring a tool is available in the conda 'base' + */ +export interface IToolProvider { + label: string; + /** + * Whether this provider is requested by action inputs + */ + provides: ( + inputs: IActionInputs, + options: IDynamicOptions + ) => Promise; + /** + * Conda package specs and option updates for tools to install after updating + */ + toolPackages: ( + inputs: IActionInputs, + options: IDynamicOptions + ) => Promise; + /** + * Steps to perform after the env is updated, and potentially reconfigured + */ + postInstall?: ( + inputs: IActionInputs, + options: IDynamicOptions + ) => Promise; +} diff --git a/src/utils.ts b/src/utils.ts index 9285efe1..8a309a85 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -4,15 +4,18 @@ import * as stream from "stream"; import * as exec from "@actions/exec"; import * as core from "@actions/core"; +import * as constants from "./constants"; -import { - CONDA_CACHE_FOLDER, - FORCED_ERRORS, - IGNORED_WARNINGS, -} from "./constants"; - +/** The folder to use as the conda package cache */ export function cacheFolder() { - return path.join(os.homedir(), CONDA_CACHE_FOLDER); + return path.join(os.homedir(), constants.CONDA_CACHE_FOLDER); +} + +/** + * Whether the given env is a conda `base` env + */ +export function isBaseEnv(envName: string) { + return constants.BASE_ENV_NAMES.includes(envName); } /** @@ -24,7 +27,7 @@ export async function execute(command: string[]): Promise { listeners: { stdout: (data: Buffer) => { const stringData = data.toString(); - for (const forced_error of FORCED_ERRORS) { + for (const forced_error of constants.FORCED_ERRORS) { if (stringData.includes(forced_error)) { throw new Error(`"${command}" failed with "${forced_error}"`); } @@ -33,7 +36,7 @@ export async function execute(command: string[]): Promise { }, stderr: (data: Buffer) => { const stringData = data.toString(); - for (const ignore of IGNORED_WARNINGS) { + for (const ignore of constants.IGNORED_WARNINGS) { if (stringData.includes(ignore)) { return; }