diff --git a/.github/workflows/example-6.yml b/.github/workflows/example-6.yml new file mode 100644 index 00000000..7699240c --- /dev/null +++ b/.github/workflows/example-6.yml @@ -0,0 +1,33 @@ +name: "Example 6: Mamba" + +on: + pull_request: + branches: + - '*' + push: + branches: + - 'master' + +jobs: + example-6: + name: Ex6 Mamba + runs-on: 'ubuntu-latest' + steps: + - uses: actions/checkout@v2 + - uses: ./ + with: + python-version: 3.6 + mamba-version: "*" + channels: conda-forge,defaults + channel-priority: true + activate-environment: anaconda-client-env + environment-file: etc/example-environment.yml + - shell: bash -l {0} + run: | + conda info + conda list + conda config --show-sources + conda config --show + printenv | sort + - shell: bash -l {0} + run: mamba install jupyterlab diff --git a/README.md b/README.md index 0b288012..531e71ad 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ ![Example 3: Other options](https://github.com/goanpeca/setup-miniconda/workflows/Example%203:%20Other%20options/badge.svg?branch=master) ![Example 4: Channels](https://github.com/goanpeca/setup-miniconda/workflows/Example%204:%20Channels/badge.svg?branch=master) ![Example 5: Custom installer](https://github.com/goanpeca/setup-miniconda/workflows/Example%205:%20Custom%20installer/badge.svg?branch=master) +![Example 6: Mamba](https://github.com/goanpeca/setup-miniconda/workflows/Example%206:%20Mamba/badge.svg?branch=master) ![Caching Example](https://github.com/goanpeca/setup-miniconda/workflows/Caching%20Example/badge.svg?branch=master) ![Linting](https://github.com/goanpeca/setup-miniconda/workflows/Linting/badge.svg?branch=master) @@ -217,6 +218,36 @@ jobs: conda config --show ``` +#### Example 6: Mamba + +Experimental! Use `mamba` to handle conda installs in a faster way. `mamba-version` accepts a version string `x.y` (including `"*"`). It requires you specify `conda-forge` as part of the channels, ideally with the highest priority. + +```yaml +jobs: + example-6: + name: Ex6 Mamba + runs-on: "ubuntu-latest" + steps: + - uses: actions/checkout@v2 + - uses: ./ + with: + python-version: 3.6 + mamba-version: "*" + channels: conda-forge,defaults + channel-priority: true + activate-environment: anaconda-client-env + environment-file: etc/example-environment.yml + - shell: bash -l {0} + run: | + conda info + conda list + conda config --show-sources + conda config --show + printenv | sort + - shell: bash -l {0} + run: mamba install jupyterlab +``` + ## Caching If you want to enable package caching for conda you can use the [cache action](https://github.com/actions/cache) using `~/conda_pkgs_dir` as path for conda packages. diff --git a/action.yml b/action.yml index ff4a26e2..28abc1c2 100644 --- a/action.yml +++ b/action.yml @@ -78,6 +78,10 @@ inputs: description: 'Advanced. Prior to runnning "conda init" all shell profiles will be removed from the runner. Default is "true".' required: false default: "true" + mamba-version: + description: 'Experimental. Use mamba (https://github.com/QuantStack/mamba) as a faster drop-in replacement for conda installs. Disabled by default. To enable, use "*" or a "x.y" version string.' + required: false + default: "" runs: using: "node12" main: "dist/setup/index.js" diff --git a/dist/setup/index.js b/dist/setup/index.js index c53b8c54..2b7e4cab 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -21368,12 +21368,16 @@ function minicondaPath(useBundled = true) { return condaPath; } /** - * Provide cross platform location of conda executable + * Provide cross platform location of conda/mamba executable */ -function condaExecutable(useBundled) { +function condaExecutable(useBundled, useMamba = false) { const dir = minicondaPath(useBundled); let condaExe; - condaExe = IS_UNIX ? `${dir}/condabin/conda` : `${dir}\\condabin\\conda.bat`; + let commandName; + commandName = useMamba ? "mamba" : "conda"; + condaExe = IS_UNIX + ? `${dir}/condabin/${commandName}` + : `${dir}\\condabin\\${commandName}.bat`; return condaExe; } /** @@ -21510,9 +21514,9 @@ function installMiniconda(installerPath, useBundled) { /** * Run Conda command */ -function condaCommand(cmd, useBundled) { +function condaCommand(cmd, useBundled, useMamba = false) { return __awaiter(this, void 0, void 0, function* () { - const command = `${condaExecutable(useBundled)} ${cmd}`; + const command = `${condaExecutable(useBundled, useMamba)} ${cmd}`; return yield execute(command); }); } @@ -21541,7 +21545,7 @@ function setVariables(useBundled) { /** * Create test environment */ -function createTestEnvironment(activateEnvironment, useBundled) { +function createTestEnvironment(activateEnvironment, useBundled, useMamba) { return __awaiter(this, void 0, void 0, function* () { let result; if (activateEnvironment !== "root" && @@ -21549,7 +21553,7 @@ function createTestEnvironment(activateEnvironment, useBundled) { activateEnvironment !== "") { if (!environmentExists(activateEnvironment, useBundled)) { utils.consoleLog("Create test environment..."); - result = yield condaCommand(`create --name ${activateEnvironment}`, useBundled); + result = yield condaCommand(`create --name ${activateEnvironment}`, useBundled, useMamba); if (!result.ok) return result; } @@ -21633,7 +21637,7 @@ function condaInit(activateEnvironment, useBundled, condaConfig, removeProfiles) // Run conda init core.info("\n"); for (let cmd of ["--all"]) { - const command = `${condaExecutable(useBundled)} init ${cmd}`; + const command = `${condaExecutable(useBundled, false)} init ${cmd}`; yield execute(command); } // Rename files @@ -21730,9 +21734,9 @@ conda activate ${activateEnvironment}`; /** * Setup python test environment */ -function setupPython(activateEnvironment, pythonVersion, useBundled) { +function setupPython(activateEnvironment, pythonVersion, useBundled, useMamba) { return __awaiter(this, void 0, void 0, function* () { - return yield condaCommand(`install --name ${activateEnvironment} python=${pythonVersion}`, useBundled); + return yield condaCommand(`install --name ${activateEnvironment} python=${pythonVersion}`, useBundled, useMamba); }); } /** @@ -21751,22 +21755,22 @@ function applyCondaConfiguration(condaConfig, useBundled) { let channels = condaConfig[key].split(",").reverse(); let channel; for (channel of channels) { - result = yield condaCommand(`config --add ${key} ${channel}`, useBundled); + result = yield condaCommand(`config --add ${key} ${channel}`, useBundled, false); if (!result.ok) return result; } } else { - result = yield condaCommand(`config --set ${key} ${condaConfig[key]}`, useBundled); + result = yield condaCommand(`config --set ${key} ${condaConfig[key]}`, useBundled, false); if (!result.ok) return result; } } } - result = yield condaCommand(`config --show-sources`, useBundled); + result = yield condaCommand(`config --show-sources`, useBundled, false); if (!result.ok) return result; - result = yield condaCommand(`config --show`, useBundled); + result = yield condaCommand(`config --show`, useBundled, false); if (!result.ok) return result; } @@ -21779,10 +21783,11 @@ function applyCondaConfiguration(condaConfig, useBundled) { /** * Main conda setup method to handle all configuration options */ -function setupMiniconda(installerUrl, minicondaVersion, architecture, condaVersion, condaBuildVersion, pythonVersion, activateEnvironment, environmentFile, condaConfigFile, condaConfig, removeProfiles) { +function setupMiniconda(installerUrl, minicondaVersion, architecture, condaVersion, condaBuildVersion, pythonVersion, activateEnvironment, environmentFile, condaConfigFile, condaConfig, removeProfiles, mambaVersion) { return __awaiter(this, void 0, void 0, function* () { let result; let useBundled = true; + let useMamba = false; try { // Check for consistency if (condaConfig["auto_update_conda"] == "true" && condaVersion) { @@ -21794,6 +21799,12 @@ function setupMiniconda(installerUrl, minicondaVersion, architecture, condaVersi error: new Error(`"python-version=${pythonVersion}" was provided but "activate-environment" is not defined!`) }; } + if (!condaConfig["channels"].includes("conda-forge") && mambaVersion) { + return { + ok: false, + error: new Error(`"mamba-version=${mambaVersion}" requires "conda-forge" to be included in "channels!"`) + }; + } if (installerUrl !== "") { if (minicondaVersion !== "") { return { @@ -21856,7 +21867,7 @@ function setupMiniconda(installerUrl, minicondaVersion, architecture, condaVersi } let cacheFolder = "~/conda_pkgs_dir"; cacheFolder = cacheFolder.replace("~", os.homedir().replace("\\", "/")); - result = yield condaCommand(`config --add pkgs_dirs ${cacheFolder}`, useBundled); + result = yield condaCommand(`config --add pkgs_dirs ${cacheFolder}`, useBundled, useMamba); if (!result.ok) return result; core.exportVariable("CONDA_PKGS_DIR", cacheFolder); @@ -21877,7 +21888,7 @@ function setupMiniconda(installerUrl, minicondaVersion, architecture, condaVersi // if (!result.ok) return result; } utils.consoleLog("Setup Conda basic configuration..."); - result = yield condaCommand("config --set always_yes yes --set changeps1 no", useBundled); + result = yield condaCommand("config --set always_yes yes --set changeps1 no", useBundled, useMamba); if (!result.ok) return result; utils.consoleLog("Initialize Conda and fix ownership..."); @@ -21886,13 +21897,13 @@ function setupMiniconda(installerUrl, minicondaVersion, architecture, condaVersi return result; if (condaVersion) { utils.consoleLog("Installing Conda..."); - result = yield condaCommand(`install --name base conda=${condaVersion}`, useBundled); + result = yield condaCommand(`install --name base conda=${condaVersion}`, useBundled, useMamba); if (!result.ok) return result; } if (condaConfig["auto_update_conda"] == "true") { utils.consoleLog("Updating conda..."); - result = yield condaCommand("update conda", useBundled); + result = yield condaCommand("update conda", useBundled, useMamba); if (!result.ok) return result; if (condaConfig) { @@ -21903,20 +21914,33 @@ function setupMiniconda(installerUrl, minicondaVersion, architecture, condaVersi } } // Any conda commands run here after init and setup + if (mambaVersion) { + utils.consoleLog("Installing Mamba..."); + core.warning(`Mamba support is still experimental and can result in differently solved environments!`); + if (mambaVersion) { + result = yield condaCommand(`install --name base mamba=${mambaVersion}`, useBundled, useMamba); + } + if (result.ok) { + useMamba = true; + } + else { + return result; + } + } if (condaBuildVersion) { utils.consoleLog("Installing Conda Build..."); - result = yield condaCommand(`install --name base conda-build=${condaBuildVersion}`, useBundled); + result = yield condaCommand(`install --name base conda-build=${condaBuildVersion}`, useBundled, useMamba); if (!result.ok) return result; } if (activateEnvironment) { - result = yield createTestEnvironment(activateEnvironment, useBundled); + result = yield createTestEnvironment(activateEnvironment, useBundled, useMamba); if (!result.ok) return result; } if (pythonVersion && activateEnvironment) { utils.consoleLog(`Installing Python="${pythonVersion}" on "${activateEnvironment}" environment...`); - result = yield setupPython(activateEnvironment, pythonVersion, useBundled); + result = yield setupPython(activateEnvironment, pythonVersion, useBundled, useMamba); if (!result.ok) return result; } @@ -21945,7 +21969,7 @@ function setupMiniconda(installerUrl, minicondaVersion, architecture, condaVersi else { condaAction = "create"; } - result = yield condaCommand(`env ${condaAction} -f ${environmentFile}`, useBundled); + result = yield condaCommand(`env ${condaAction} -f ${environmentFile}`, useBundled, useMamba); if (!result.ok) return result; } @@ -21983,6 +22007,8 @@ function run() { let removeProfiles = core.getInput("remove-profiles"); let showChannelUrls = core.getInput("show-channel-urls"); let useOnlyTarBz2 = core.getInput("use-only-tar-bz2"); + // Mamba + let mambaVersion = core.getInput("mamba-version"); const condaConfig = { add_anaconda_token: addAnacondaToken, add_pip_as_python_dependency: addPipAsPythonDependency, @@ -21995,7 +22021,7 @@ function run() { show_channel_urls: showChannelUrls, use_only_tar_bz2: useOnlyTarBz2 }; - const result = yield setupMiniconda(installerUrl, minicondaVersion, "x64", condaVersion, condaBuildVersion, pythonVersion, activateEnvironment, environmentFile, condaFile, condaConfig, removeProfiles); + const result = yield setupMiniconda(installerUrl, minicondaVersion, "x64", condaVersion, condaBuildVersion, pythonVersion, activateEnvironment, environmentFile, condaFile, condaConfig, removeProfiles, mambaVersion); if (!result.ok) { throw result.error; } @@ -29040,7 +29066,7 @@ module.exports = defaults; /* 771 */ /***/ (function(module) { -module.exports = {"_args":[["cheerio@1.0.0-rc.3","/Users/goanpeca/Dropbox (Personal)/develop/quansight/setup-miniconda"]],"_from":"cheerio@1.0.0-rc.3","_id":"cheerio@1.0.0-rc.3","_inBundle":false,"_integrity":"sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==","_location":"/cheerio","_phantomChildren":{},"_requested":{"type":"version","registry":true,"raw":"cheerio@1.0.0-rc.3","name":"cheerio","escapedName":"cheerio","rawSpec":"1.0.0-rc.3","saveSpec":null,"fetchSpec":"1.0.0-rc.3"},"_requiredBy":["/get-hrefs"],"_resolved":"https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz","_spec":"1.0.0-rc.3","_where":"/Users/goanpeca/Dropbox (Personal)/develop/quansight/setup-miniconda","author":{"name":"Matt Mueller","email":"mattmuelle@gmail.com","url":"mat.io"},"bugs":{"url":"https://github.com/cheeriojs/cheerio/issues"},"dependencies":{"css-select":"~1.2.0","dom-serializer":"~0.1.1","entities":"~1.1.1","htmlparser2":"^3.9.1","lodash":"^4.15.0","parse5":"^3.0.1"},"description":"Tiny, fast, and elegant implementation of core jQuery designed specifically for the server","devDependencies":{"benchmark":"^2.1.0","coveralls":"^2.11.9","expect.js":"~0.3.1","istanbul":"^0.4.3","jquery":"^3.0.0","jsdom":"^9.2.1","jshint":"^2.9.2","mocha":"^3.1.2","xyz":"~1.1.0"},"engines":{"node":">= 0.6"},"files":["index.js","lib"],"homepage":"https://github.com/cheeriojs/cheerio#readme","keywords":["htmlparser","jquery","selector","scraper","parser","html"],"license":"MIT","main":"./index.js","name":"cheerio","repository":{"type":"git","url":"git://github.com/cheeriojs/cheerio.git"},"scripts":{"test":"make test"},"version":"1.0.0-rc.3"}; +module.exports = {"_args":[["cheerio@1.0.0-rc.3","/home/jaime/devel/py/jaimergp/setup-miniconda"]],"_from":"cheerio@1.0.0-rc.3","_id":"cheerio@1.0.0-rc.3","_inBundle":false,"_integrity":"sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==","_location":"/cheerio","_phantomChildren":{},"_requested":{"type":"version","registry":true,"raw":"cheerio@1.0.0-rc.3","name":"cheerio","escapedName":"cheerio","rawSpec":"1.0.0-rc.3","saveSpec":null,"fetchSpec":"1.0.0-rc.3"},"_requiredBy":["/get-hrefs"],"_resolved":"https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz","_spec":"1.0.0-rc.3","_where":"/home/jaime/devel/py/jaimergp/setup-miniconda","author":{"name":"Matt Mueller","email":"mattmuelle@gmail.com","url":"mat.io"},"bugs":{"url":"https://github.com/cheeriojs/cheerio/issues"},"dependencies":{"css-select":"~1.2.0","dom-serializer":"~0.1.1","entities":"~1.1.1","htmlparser2":"^3.9.1","lodash":"^4.15.0","parse5":"^3.0.1"},"description":"Tiny, fast, and elegant implementation of core jQuery designed specifically for the server","devDependencies":{"benchmark":"^2.1.0","coveralls":"^2.11.9","expect.js":"~0.3.1","istanbul":"^0.4.3","jquery":"^3.0.0","jsdom":"^9.2.1","jshint":"^2.9.2","mocha":"^3.1.2","xyz":"~1.1.0"},"engines":{"node":">= 0.6"},"files":["index.js","lib"],"homepage":"https://github.com/cheeriojs/cheerio#readme","keywords":["htmlparser","jquery","selector","scraper","parser","html"],"license":"MIT","main":"./index.js","name":"cheerio","repository":{"type":"git","url":"git://github.com/cheeriojs/cheerio.git"},"scripts":{"test":"make test"},"version":"1.0.0-rc.3"}; /***/ }), /* 772 */ diff --git a/package-lock.json b/package-lock.json index ab7a006d..bde8767e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "action-setup-conda", - "version": "1.4.1", + "version": "1.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/setup.ts b/src/setup.ts index ebf9e3e5..bdbb8b3a 100644 --- a/src/setup.ts +++ b/src/setup.ts @@ -111,12 +111,19 @@ function minicondaPath(useBundled: boolean = true): string { } /** - * Provide cross platform location of conda executable + * Provide cross platform location of conda/mamba executable */ -function condaExecutable(useBundled: boolean): string { +function condaExecutable( + useBundled: boolean, + useMamba: boolean = false +): string { const dir: string = minicondaPath(useBundled); let condaExe: string; - condaExe = IS_UNIX ? `${dir}/condabin/conda` : `${dir}\\condabin\\conda.bat`; + let commandName: string; + commandName = useMamba ? "mamba" : "conda"; + condaExe = IS_UNIX + ? `${dir}/condabin/${commandName}` + : `${dir}\\condabin\\${commandName}.bat`; return condaExe; } @@ -282,8 +289,12 @@ async function installMiniconda( /** * Run Conda command */ -async function condaCommand(cmd: string, useBundled: boolean): Promise { - const command = `${condaExecutable(useBundled)} ${cmd}`; +async function condaCommand( + cmd: string, + useBundled: boolean, + useMamba: boolean = false +): Promise { + const command = `${condaExecutable(useBundled, useMamba)} ${cmd}`; return await execute(command); } @@ -312,7 +323,8 @@ async function setVariables(useBundled: boolean): Promise { */ async function createTestEnvironment( activateEnvironment: string, - useBundled: boolean + useBundled: boolean, + useMamba: boolean ): Promise { let result: Result; if ( @@ -324,7 +336,8 @@ async function createTestEnvironment( utils.consoleLog("Create test environment..."); result = await condaCommand( `create --name ${activateEnvironment}`, - useBundled + useBundled, + useMamba ); if (!result.ok) return result; } @@ -419,7 +432,7 @@ async function condaInit( // Run conda init core.info("\n"); for (let cmd of ["--all"]) { - const command = `${condaExecutable(useBundled)} init ${cmd}`; + const command = `${condaExecutable(useBundled, false)} init ${cmd}`; await execute(command); } @@ -522,11 +535,13 @@ conda activate ${activateEnvironment}`; async function setupPython( activateEnvironment: string, pythonVersion: string, - useBundled: boolean + useBundled: boolean, + useMamba: boolean ): Promise { return await condaCommand( `install --name ${activateEnvironment} python=${pythonVersion}`, - useBundled + useBundled, + useMamba ); } @@ -550,24 +565,26 @@ async function applyCondaConfiguration( for (channel of channels) { result = await condaCommand( `config --add ${key} ${channel}`, - useBundled + useBundled, + false ); if (!result.ok) return result; } } else { result = await condaCommand( `config --set ${key} ${condaConfig[key]}`, - useBundled + useBundled, + false ); if (!result.ok) return result; } } } - result = await condaCommand(`config --show-sources`, useBundled); + result = await condaCommand(`config --show-sources`, useBundled, false); if (!result.ok) return result; - result = await condaCommand(`config --show`, useBundled); + result = await condaCommand(`config --show`, useBundled, false); if (!result.ok) return result; } catch (err) { return { ok: false, error: err }; @@ -589,10 +606,12 @@ async function setupMiniconda( environmentFile: string, condaConfigFile: string, condaConfig: TCondaConfig, - removeProfiles: string + removeProfiles: string, + mambaVersion: string ): Promise { let result: Result; let useBundled: boolean = true; + let useMamba: boolean = false; try { // Check for consistency if (condaConfig["auto_update_conda"] == "true" && condaVersion) { @@ -608,6 +627,14 @@ async function setupMiniconda( ) }; } + if (!condaConfig["channels"].includes("conda-forge") && mambaVersion) { + return { + ok: false, + error: new Error( + `"mamba-version=${mambaVersion}" requires "conda-forge" to be included in "channels!"` + ) + }; + } if (installerUrl !== "") { if (minicondaVersion !== "") { @@ -680,7 +707,8 @@ async function setupMiniconda( cacheFolder = cacheFolder.replace("~", os.homedir().replace("\\", "/")); result = await condaCommand( `config --add pkgs_dirs ${cacheFolder}`, - useBundled + useBundled, + useMamba ); if (!result.ok) return result; core.exportVariable("CONDA_PKGS_DIR", cacheFolder); @@ -707,7 +735,8 @@ async function setupMiniconda( utils.consoleLog("Setup Conda basic configuration..."); result = await condaCommand( "config --set always_yes yes --set changeps1 no", - useBundled + useBundled, + useMamba ); if (!result.ok) return result; @@ -724,14 +753,15 @@ async function setupMiniconda( utils.consoleLog("Installing Conda..."); result = await condaCommand( `install --name base conda=${condaVersion}`, - useBundled + useBundled, + useMamba ); if (!result.ok) return result; } if (condaConfig["auto_update_conda"] == "true") { utils.consoleLog("Updating conda..."); - result = await condaCommand("update conda", useBundled); + result = await condaCommand("update conda", useBundled, useMamba); if (!result.ok) return result; if (condaConfig) { @@ -742,17 +772,41 @@ async function setupMiniconda( } // Any conda commands run here after init and setup + if (mambaVersion) { + utils.consoleLog("Installing Mamba..."); + core.warning( + `Mamba support is still experimental and can result in differently solved environments!` + ); + if (mambaVersion) { + result = await condaCommand( + `install --name base mamba=${mambaVersion}`, + useBundled, + useMamba + ); + } + if (result.ok) { + useMamba = true; + } else { + return result; + } + } + if (condaBuildVersion) { utils.consoleLog("Installing Conda Build..."); result = await condaCommand( `install --name base conda-build=${condaBuildVersion}`, - useBundled + useBundled, + useMamba ); if (!result.ok) return result; } if (activateEnvironment) { - result = await createTestEnvironment(activateEnvironment, useBundled); + result = await createTestEnvironment( + activateEnvironment, + useBundled, + useMamba + ); if (!result.ok) return result; } @@ -763,7 +817,8 @@ async function setupMiniconda( result = await setupPython( activateEnvironment, pythonVersion, - useBundled + useBundled, + useMamba ); if (!result.ok) return result; } @@ -803,7 +858,8 @@ async function setupMiniconda( } result = await condaCommand( `env ${condaAction} -f ${environmentFile}`, - useBundled + useBundled, + useMamba ); if (!result.ok) return result; } @@ -844,6 +900,9 @@ async function run(): Promise { let showChannelUrls: string = core.getInput("show-channel-urls"); let useOnlyTarBz2: string = core.getInput("use-only-tar-bz2"); + // Mamba + let mambaVersion: string = core.getInput("mamba-version"); + const condaConfig: TCondaConfig = { add_anaconda_token: addAnacondaToken, add_pip_as_python_dependency: addPipAsPythonDependency, @@ -867,7 +926,8 @@ async function run(): Promise { environmentFile, condaFile, condaConfig, - removeProfiles + removeProfiles, + mambaVersion ); if (!result.ok) { throw result.error;