-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
3,814 additions
and
2,582 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,283 @@ | ||
//----------------------------------------------------------------------- | ||
// Conda helpers | ||
//----------------------------------------------------------------------- | ||
|
||
import * as fs from "fs"; | ||
import * as path from "path"; | ||
import * as os from "os"; | ||
|
||
import * as core from "@actions/core"; | ||
import * as io from "@actions/io"; | ||
|
||
import { IS_LINUX, IS_MAC, IS_WINDOWS, MINICONDA_DIR_PATH } from "./constants"; | ||
import { IShells, Result, TCondaConfig } from "./types"; | ||
import { execute } from "./utils"; | ||
|
||
/** | ||
* Provide current location of miniconda or location where it will be installed | ||
*/ | ||
export function minicondaPath(useBundled: boolean = true): string { | ||
let condaPath: string = MINICONDA_DIR_PATH; | ||
if (!useBundled) { | ||
if (IS_MAC) { | ||
condaPath = "/Users/runner/miniconda3"; | ||
} else { | ||
condaPath += "3"; | ||
} | ||
} | ||
return condaPath; | ||
} | ||
|
||
/** | ||
* Provide cross platform location of conda/mamba executable | ||
*/ | ||
export function condaExecutable( | ||
useBundled: boolean, | ||
useMamba: boolean = false | ||
): string { | ||
const dir: string = minicondaPath(useBundled); | ||
let condaExe: string; | ||
let commandName: string; | ||
commandName = useMamba ? "mamba" : "conda"; | ||
commandName = IS_WINDOWS ? commandName + ".bat" : commandName; | ||
condaExe = path.join(dir, "condabin", commandName); | ||
return condaExe; | ||
} | ||
|
||
/** | ||
* Run Conda command | ||
*/ | ||
export async function condaCommand( | ||
cmd: string[], | ||
useBundled: boolean, | ||
useMamba: boolean = false | ||
): Promise<Result> { | ||
const command = [condaExecutable(useBundled, useMamba), ...cmd]; | ||
return await execute(command); | ||
} | ||
|
||
/** | ||
* Setup Conda configuration | ||
*/ | ||
export async function applyCondaConfiguration( | ||
condaConfig: TCondaConfig, | ||
useBundled: boolean | ||
): Promise<Result> { | ||
let result: Result; | ||
try { | ||
for (const key of Object.keys(condaConfig)) { | ||
core.info(`"${key}": "${condaConfig[key]}"`); | ||
if (condaConfig[key].length !== 0) { | ||
if (key === "channels") { | ||
// Split by comma and reverse order to preserve higher priority | ||
// as listed in the option | ||
let channels: Array<string> = condaConfig[key].split(",").reverse(); | ||
let channel: string; | ||
for (channel of channels) { | ||
result = await condaCommand( | ||
["config", "--add", key, channel], | ||
useBundled, | ||
false | ||
); | ||
if (!result.ok) return result; | ||
} | ||
} else { | ||
result = await condaCommand( | ||
["config", "--set", key, condaConfig[key]], | ||
useBundled, | ||
false | ||
); | ||
if (!result.ok) return result; | ||
} | ||
} | ||
} | ||
|
||
result = await condaCommand( | ||
["config", "--show-sources"], | ||
useBundled, | ||
false | ||
); | ||
if (!result.ok) return result; | ||
|
||
result = await condaCommand(["config", "--show"], useBundled, false); | ||
if (!result.ok) return result; | ||
} catch (err) { | ||
return { ok: false, error: err }; | ||
} | ||
return { ok: true, data: "ok" }; | ||
} | ||
|
||
/** | ||
* Initialize Conda | ||
*/ | ||
export async function condaInit( | ||
activateEnvironment: string, | ||
useBundled: boolean, | ||
condaConfig: TCondaConfig, | ||
removeProfiles: string | ||
): Promise<Result> { | ||
let result: Result; | ||
let ownPath: string; | ||
const isValidActivate: boolean = | ||
activateEnvironment !== "base" && | ||
activateEnvironment !== "root" && | ||
activateEnvironment !== ""; | ||
const autoActivateBase: boolean = | ||
condaConfig["auto_activate_base"] === "true"; | ||
|
||
// Fix ownership of folders | ||
if (useBundled) { | ||
if (IS_MAC) { | ||
core.startGroup("Fixing conda folders ownership"); | ||
const userName: string = process.env.USER as string; | ||
result = await execute([ | ||
"sudo", | ||
"chown", | ||
"-R", | ||
`${userName}:staff`, | ||
minicondaPath(useBundled), | ||
]); | ||
core.endGroup(); | ||
if (!result.ok) return result; | ||
} else if (IS_WINDOWS) { | ||
for (let folder of [ | ||
"condabin/", | ||
"Scripts/", | ||
"shell/", | ||
"etc/profile.d/", | ||
"/Lib/site-packages/xonsh/", | ||
]) { | ||
ownPath = path.join(minicondaPath(useBundled), folder); | ||
if (fs.existsSync(ownPath)) { | ||
core.startGroup(`Fixing ${folder} ownership`); | ||
result = await execute(["takeown", "/f", ownPath, "/r", "/d", "y"]); | ||
core.endGroup(); | ||
if (!result.ok) return result; | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Remove profile files | ||
if (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", | ||
]) { | ||
try { | ||
let file: string = path.join(os.homedir(), rc); | ||
if (fs.existsSync(file)) { | ||
core.info(`Removing "${file}"`); | ||
await io.rmRF(file); | ||
} | ||
} catch (err) { | ||
core.warning(err); | ||
} | ||
} | ||
} | ||
|
||
// Run conda init | ||
for (let cmd of ["--all"]) { | ||
await execute([condaExecutable(useBundled, false), "init", cmd]); | ||
} | ||
|
||
// Rename files | ||
if (IS_LINUX) { | ||
let source: string = "~/.bashrc".replace("~", os.homedir()); | ||
let dest: string = "~/.profile".replace("~", os.homedir()); | ||
core.info(`Renaming "${source}" to "${dest}"\n`); | ||
await io.mv(source, dest); | ||
} else if (IS_MAC) { | ||
let source: string = "~/.bash_profile".replace("~", os.homedir()); | ||
let dest: string = "~/.profile".replace("~", os.homedir()); | ||
core.info(`Renaming "${source}" to "${dest}"\n`); | ||
await io.mv(source, dest); | ||
} | ||
|
||
// PowerShell profiles | ||
let powerExtraText = ` | ||
# ----------------------------------------------------------------------------`; | ||
if (isValidActivate) { | ||
powerExtraText += ` | ||
# Conda Setup Action: Custom activation | ||
conda activate ${activateEnvironment}`; | ||
} | ||
powerExtraText += ` | ||
# ----------------------------------------------------------------------------`; | ||
|
||
// Bash profiles | ||
let bashExtraText: string = ` | ||
# ---------------------------------------------------------------------------- | ||
# Conda Setup Action: Basic configuration | ||
set -eo pipefail`; | ||
if (isValidActivate) { | ||
bashExtraText += ` | ||
# Conda Setup Action: Custom activation | ||
conda activate ${activateEnvironment}`; | ||
bashExtraText += ` | ||
# ----------------------------------------------------------------------------`; | ||
} | ||
|
||
// Batch profiles | ||
let batchExtraText = ` | ||
:: ---------------------------------------------------------------------------`; | ||
if (autoActivateBase) { | ||
batchExtraText += ` | ||
:: Conda Setup Action: Activate base | ||
@CALL "%CONDA_BAT%" activate base`; | ||
} | ||
if (isValidActivate) { | ||
batchExtraText += ` | ||
:: Conda Setup Action: Custom activation | ||
@CALL "%CONDA_BAT%" activate ${activateEnvironment}`; | ||
} | ||
batchExtraText += ` | ||
:: Conda Setup Action: Basic configuration | ||
@SETLOCAL EnableExtensions | ||
@SETLOCAL DisableDelayedExpansion | ||
:: ---------------------------------------------------------------------------`; | ||
|
||
let extraShells: IShells; | ||
const shells: IShells = { | ||
"~/.bash_profile": bashExtraText, | ||
"~/.profile": bashExtraText, | ||
"~/.zshrc": bashExtraText, | ||
"~/.config/fish/config.fish": bashExtraText, | ||
"~/.tcshrc": bashExtraText, | ||
"~/.xonshrc": bashExtraText, | ||
"~/.config/powershell/profile.ps1": powerExtraText, | ||
"~/Documents/PowerShell/profile.ps1": powerExtraText, | ||
"~/Documents/WindowsPowerShell/profile.ps1": powerExtraText, | ||
}; | ||
if (useBundled) { | ||
extraShells = { | ||
"C:/Miniconda/etc/profile.d/conda.sh": bashExtraText, | ||
"C:/Miniconda/etc/fish/conf.d/conda.fish": bashExtraText, | ||
"C:/Miniconda/condabin/conda_hook.bat": batchExtraText, | ||
}; | ||
} else { | ||
extraShells = { | ||
"C:/Miniconda3/etc/profile.d/conda.sh": bashExtraText, | ||
"C:/Miniconda3/etc/fish/conf.d/conda.fish": bashExtraText, | ||
"C:/Miniconda3/condabin/conda_hook.bat": batchExtraText, | ||
}; | ||
} | ||
const allShells: IShells = { ...shells, ...extraShells }; | ||
Object.keys(allShells).forEach((key) => { | ||
let filePath: string = key.replace("~", os.homedir()); | ||
const text = allShells[key]; | ||
if (fs.existsSync(filePath)) { | ||
core.info(`Append to "${filePath}":\n ${text} \n`); | ||
fs.appendFileSync(filePath, text); | ||
} | ||
}); | ||
return { ok: true, data: "ok" }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import * as os from "os"; | ||
import * as path from "path"; | ||
|
||
import { IArchitectures, IOperatingSystems } from "./types"; | ||
|
||
//----------------------------------------------------------------------- | ||
// Constants | ||
//----------------------------------------------------------------------- | ||
export const MINICONDA_DIR_PATH: string = process.env["CONDA"] || ""; | ||
export const IS_WINDOWS: boolean = process.platform === "win32"; | ||
export const IS_MAC: boolean = process.platform === "darwin"; | ||
export const IS_LINUX: boolean = process.platform === "linux"; | ||
export const IS_UNIX: boolean = IS_MAC || IS_LINUX; | ||
export const MINICONDA_BASE_URL: string = | ||
"https://repo.anaconda.com/miniconda/"; | ||
|
||
export const ARCHITECTURES: IArchitectures = { | ||
x64: "x86_64", | ||
x86: "x86", | ||
ARM64: "aarch64", // To be supported by github runners | ||
ARM32: "armv7l", // To be supported by github runners | ||
}; | ||
|
||
export const OS_NAMES: IOperatingSystems = { | ||
win32: "Windows", | ||
darwin: "MacOSX", | ||
linux: "Linux", | ||
}; | ||
|
||
export const KNOWN_EXTENSIONS = [".exe", ".sh"]; | ||
|
||
/** | ||
* errors that are always probably spurious | ||
*/ | ||
export const IGNORED_WARNINGS = [ | ||
// appear on win install, we can swallow them | ||
`menuinst_win32`, | ||
`Unable to register environment`, | ||
`0%|`, | ||
// appear on certain Linux/OSX installers | ||
`Please run using "bash"`, | ||
// old condas don't know what to do with these | ||
`Key 'use_only_tar_bz2' is not a known primitive parameter.`, | ||
]; | ||
|
||
/** | ||
* warnings that should be errors | ||
*/ | ||
export const FORCED_ERRORS = [ | ||
// conda env create will ignore invalid sections and move on | ||
`EnvironmentSectionNotValid`, | ||
]; | ||
|
||
/** | ||
* avoid spurious conda warnings before we have a chance to update them | ||
*/ | ||
export const BOOTSTRAP_CONDARC = "notify_outdated_conda: false"; | ||
|
||
/** | ||
* the conda config file | ||
*/ | ||
export const CONDARC_PATH = path.join(os.homedir(), ".condarc"); | ||
|
||
/** Where to put files. Should eventually be configurable */ | ||
export const CONDA_CACHE_FOLDER = "conda_pkgs_dir"; | ||
|
||
/** the environment variable exported */ | ||
export const ENV_VAR_CONDA_PKGS = "CONDA_PKGS_DIR"; |
Oops, something went wrong.