Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Distribute micromamba executable for plugins #1707

Merged
merged 13 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/setup_env/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ runs:
cat environment.yml

- name: Setup conda environment
uses: mamba-org/setup-micromamba@v1
uses: mamba-org/setup-micromamba@v2
with:
environment-file: environment.yml
environment-name: env
Expand Down
25 changes: 12 additions & 13 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -367,22 +367,21 @@ jobs:
yarn config set network-timeout 600000 -g
yarn install

- name: Download mamba installer for distribution (MacOS)
- name: Download micromamba for distribution (MacOS)
if: matrix.os == 'macos-13'
run: curl -fsSLo Miniforge3-MacOSX-x86_64.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-MacOSX-x86_64.sh"

- name: Install mamba for MacOS
if: matrix.os == 'macos-13'
run: bash Miniforge3-MacOSX-x86_64.sh -b -p dist/miniforge3

- name: Download mamba installer for distribution (Windows)
if: matrix.os == 'windows-latest'
run: curl -fsSLo Miniforge3-Windows-x86_64.exe "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Windows-x86_64.exe"
run: |
curl -Ls https://micro.mamba.pm/api/micromamba/osx-64/latest | tar -xvj bin/micromamba
mv bin/micromamba dist/
./dist/micromamba --help # make sure the executable works

- name: Install mamba for Windows
- name: Download micromamba for distribution (Windows)
if: matrix.os == 'windows-latest'
shell: cmd
run: Miniforge3-Windows-x86_64.exe /InstallationType=JustMe /RegisterPython=0 /S /D=%cd%\dist\miniforge3
shell: pwsh
run: |
Invoke-Webrequest -URI https://micro.mamba.pm/api/micromamba/win-64/latest -OutFile micromamba.tar.bz2
tar xf micromamba.tar.bz2
MOVE -Force Library\bin\micromamba.exe dist\micromamba.exe
.\dist\micromamba.exe --help # make sure the executable works

- name: Authenticate GCP
if: github.event_name != 'pull_request'
Expand Down
8 changes: 6 additions & 2 deletions workbench/electron-builder-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,12 @@ const config = {
to: 'invest',
},
{
from: '../dist/Miniforge3',
to: 'Miniforge3',
from: '../dist/micromamba', // mac
to: 'micromamba',
},
{
from: '../dist/micromamba.exe', // windows
to: 'micromamba.exe',
},
{
from: '../dist/userguide',
Expand Down
6 changes: 3 additions & 3 deletions workbench/src/main/createPythonFlaskProcess.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,14 @@ export async function createPluginServerProcess(modelName, _port = undefined) {
}

logger.debug('creating invest plugin server process');
const mamba = settingsStore.get('mamba');
const micromamba = settingsStore.get('micromamba');
const modelEnvPath = settingsStore.get(`plugins.${modelName}.env`);
const args = [
'run', '--prefix', `"${modelEnvPath}"`,
'invest', '--debug', 'serve', '--port', port];
logger.debug('spawning command:', mamba, args);
logger.debug('spawning command:', micromamba, args);
// shell mode is necessary in dev mode & relying on a conda env
const pythonServerProcess = spawn(mamba, args, { shell: true });
const pythonServerProcess = spawn(micromamba, args, { shell: true });
settingsStore.set(`plugins.${modelName}.port`, port);
settingsStore.set(`plugins.${modelName}.pid`, pythonServerProcess.pid);

Expand Down
26 changes: 10 additions & 16 deletions workbench/src/main/findBinaries.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,32 +54,26 @@ export function findInvestBinaries(isDevMode) {
return investExe;
}


/**
* Return the available mamba executable.
* Return the available micromamba executable.
*
* @param {boolean} isDevMode - a boolean designating dev mode or not.
* @returns {string} mamba executable.
* @returns {string} micromamba executable.
*/
export function findMambaExecutable(isDevMode) {
let mambaExe;
export function findMicromambaExecutable(isDevMode) {
let micromambaExe;
if (isDevMode) {
mambaExe = 'mamba'; // assume that mamba is available
micromambaExe = 'micromamba'; // assume that micromamba is available
} else {
if (process.platform === 'win32') {
mambaExe = `"${upath.join(process.resourcesPath, 'miniforge3', 'condabin', 'mamba.bat')}"`;
} else {
// Quote the path in case of spaces
mambaExe = `"${upath.join(process.resourcesPath, 'miniforge3', 'condabin', 'mamba')}"`;
}
micromambaExe = `"${upath.join(process.resourcesPath, 'micromamba')}"`;
}
// Check that the executable is working
const { stderr, error } = spawnSync(mambaExe, ['--help'], { shell: true });
const { stderr, error } = spawnSync(micromambaExe, ['--help'], { shell: true });
if (error) {
logger.error(stderr.toString());
logger.error('mamba executable is not where we expected it.');
logger.error('micromamba executable is not where we expected it.');
throw error;
}
logger.info(`using mamba executable '${mambaExe}'`);
return mambaExe;
logger.info(`using micromamba executable '${micromambaExe}'`);
return micromambaExe;
}
4 changes: 2 additions & 2 deletions workbench/src/main/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
createCoreServerProcess,
shutdownPythonProcess
} from './createPythonFlaskProcess';
import { findInvestBinaries, findMambaExecutable } from './findBinaries';
import { findInvestBinaries, findMicromambaExecutable } from './findBinaries';
import setupDownloadHandlers from './setupDownloadHandlers';
import setupDialogs from './setupDialogs';
import setupContextMenu from './setupContextMenu';
Expand Down Expand Up @@ -80,7 +80,7 @@ export const createWindow = async () => {
splashScreen.loadURL(path.join(BASE_URL, 'splash.html'));

settingsStore.set('investExe', findInvestBinaries(ELECTRON_DEV_MODE));
settingsStore.set('mamba', findMambaExecutable(ELECTRON_DEV_MODE));
settingsStore.set('micromamba', findMicromambaExecutable(ELECTRON_DEV_MODE));
// No plugin server processes should persist between workbench sessions
// In case any were left behind, remove them
const plugins = settingsStore.get('plugins');
Expand Down
18 changes: 9 additions & 9 deletions workbench/src/main/setupAddRemovePlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export function setupAddPlugin() {
async (e, pluginURL) => {
try {
logger.info('adding plugin at', pluginURL);
const mamba = settingsStore.get('mamba');
const micromamba = settingsStore.get('micromamba');
// Create a temporary directory and check out the plugin's pyproject.toml
const tmpPluginDir = fs.mkdtempSync(upath.join(tmpdir(), 'natcap-invest-'));
await spawnWithLogging(
Expand All @@ -81,19 +81,19 @@ export function setupAddPlugin() {
// Create a conda env containing the plugin and its dependencies
const envName = `invest_plugin_${pluginID}`;
await spawnWithLogging(
mamba,
micromamba,
['create', '--yes', '--name', envName, '-c', 'conda-forge', '"python<3.12"', '"gdal<3.6"']
);
logger.info('created mamba env for plugin');
logger.info('created micromamba env for plugin');
await spawnWithLogging(
mamba,
['run', '--verbose', '--no-capture-output', '--name', envName, 'pip', 'install', `git+${pluginURL}`]
micromamba,
['run', '--name', envName, 'pip', 'install', `git+${pluginURL}`]
);
logger.info('installed plugin into its env');
// Write plugin metadata to the workbench's config.json
const envInfo = execSync(`${mamba} info --envs`, { windowsHide: true }).toString();
const envInfo = execSync(`${micromamba} info --name ${envName}`, { windowsHide: true }).toString();
logger.info(`env info:\n${envInfo}`);
const regex = new RegExp(String.raw`^${envName} +(.+)$`, 'm');
const regex = /env location : (.+)/;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I've seen this syntax before. Is the /.../ syntax for a regex, without needing it to be wrapped in a string?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's a regex literal. My linter recommended this over a string.

const envPath = envInfo.match(regex)[1];
logger.info(`env path:\n${envPath}`);
logger.info('writing plugin info to settings store');
Expand Down Expand Up @@ -123,8 +123,8 @@ export function setupRemovePlugin() {
try {
// Delete the plugin's conda env
const env = settingsStore.get(`plugins.${pluginID}.env`);
const mamba = settingsStore.get('mamba');
await spawnWithLogging(mamba, ['remove', '--yes', '--prefix', `"${env}"`, '--all']);
const micromamba = settingsStore.get('micromamba');
await spawnWithLogging(micromamba, ['remove', '--yes', '--prefix', `"${env}"`, '--all']);
// Delete the plugin's data from storage
settingsStore.delete(`plugins.${pluginID}`);
logger.info('successfully removed plugin');
Expand Down
3 changes: 1 addition & 2 deletions workbench/src/main/setupInvestHandlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,10 @@ export function setupInvestRunHandlers() {
let port;
const plugins = settingsStore.get('plugins');
if (plugins && Object.keys(plugins).includes(modelRunName)) {
cmd = settingsStore.get('mamba');
cmd = settingsStore.get('micromamba');
cmdArgs = [
'run',
`--prefix "${settingsStore.get(`plugins.${modelRunName}.env`)}"`,
'--live-stream',
'invest',
LOGLEVELMAP[loggingLevel],
TGLOGLEVELMAP[taskgraphLoggingLevel],
Expand Down
Loading