Skip to content

Commit

Permalink
🩹 Fix env activation (#130)
Browse files Browse the repository at this point in the history
* 🧪 Added workflow job using bash login shell and activate TEST env

* 🧪 Added test for custom env activate with none login shell

* 🩹 Removed env vars from conda activation

Adding the env vars did lead to 'source activate <env_name>' not working anymore see issue 125

* 🧹 Moved parseActivationScriptOutput definition before its first usage
  • Loading branch information
s-weigand authored Jul 5, 2021
1 parent d2c7409 commit 705d889
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 106 deletions.
82 changes: 82 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,88 @@ jobs:
run: |
python -m pytest -v integrationtests
test-integration-test-env:
name: Env login shell
runs-on: ${{ matrix.os }}
needs: jest-tests
env:
CONDA_CHANNELS: 'defaults'
ENV_PYTHON: 3.8
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- uses: actions/checkout@v2
- name: Prepare tests
run: |
python --version
python integrationtests/prepare_tests.py
- name: Download built dist
uses: actions/download-artifact@v2
with:
name: dist-built
path: dist
- name: Run setup-conda
uses: ./
with:
activate-conda: false
- name: Create Test env
shell: bash -l {0}
run: |
conda create --name TEST python=3.8
source activate TEST
conda install pandoc graphviz
pip install -q pytest
which pip
- name: Check env
run: printenv
- name: Run tests
shell: bash -l {0}
run: |
source activate TEST
python -m pytest -v integrationtests
test-integration-test-env-no-login:
name: Env no login shell
runs-on: ${{ matrix.os }}
needs: jest-tests
env:
CONDA_CHANNELS: 'defaults'
ENV_PYTHON: 3.8
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- uses: actions/checkout@v2
- name: Prepare tests
run: |
python --version
python integrationtests/prepare_tests.py
- name: Download built dist
uses: actions/download-artifact@v2
with:
name: dist-built
path: dist
- name: Run setup-conda
uses: ./
with:
activate-conda: false
- name: Create Test env
shell: bash
run: |
conda create --name TEST python=3.8
source activate TEST
conda install pandoc graphviz
pip install -q pytest
which pip
- name: Check env
run: printenv
- name: Run tests
shell: bash
run: |
source activate TEST
python -m pytest -v integrationtests
test-integration-custom:
name: Custom Python
runs-on: ${{ matrix.os }}
Expand Down
57 changes: 16 additions & 41 deletions __tests__/conda_actions.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import * as fs from 'fs'
import * as path from 'path'
import {
parseActivationScriptOutput,
ParsedActivationScriptOutput,
} from '../src/conda_actions'
import { parseActivationScriptOutput } from '../src/conda_actions'

describe('Parse evn activation output', () => {
it('Parse linux activation', async () => {
Expand All @@ -12,59 +9,37 @@ describe('Parse evn activation output', () => {
path.resolve(__dirname, 'data/linux_conda_bash_activation.sh')
)
.toString('utf8')
const result: ParsedActivationScriptOutput =
await parseActivationScriptOutput(activationStr, 'export ', ':')
const { condaPaths, envVars } = result
expect(condaPaths.length).toBe(3)
expect(envVars['CONDA_PREFIX']).toEqual('/usr/share/miniconda')
expect(envVars['CONDA_SHLVL']).toEqual('1')
expect(envVars['CONDA_DEFAULT_ENV']).toEqual('base')
expect(envVars['CONDA_PROMPT_MODIFIER']).toEqual('(base)')
expect(envVars['CONDA_EXE']).toEqual('/usr/share/miniconda/bin/conda')
expect(envVars['_CE_M']).toEqual('')
expect(envVars['_CE_CONDA']).toEqual('')
expect(envVars['CONDA_PYTHON_EXE']).toEqual(
'/usr/share/miniconda/bin/python'
const condaPaths: string[] = await parseActivationScriptOutput(
activationStr,
'export ',
':'
)
expect(condaPaths.length).toBe(3)
})
it('Parse macOs activation', async () => {
const activationStr = fs
.readFileSync(
path.resolve(__dirname, 'data/mac_conda_bash_activation.sh')
)
.toString('utf8')
const result: ParsedActivationScriptOutput =
await parseActivationScriptOutput(activationStr, 'export ', ':')
const { condaPaths, envVars } = result
expect(condaPaths.length).toBe(3)
expect(envVars['CONDA_PREFIX']).toEqual('/usr/local/miniconda')
expect(envVars['CONDA_SHLVL']).toEqual('1')
expect(envVars['CONDA_DEFAULT_ENV']).toEqual('base')
expect(envVars['CONDA_PROMPT_MODIFIER']).toEqual('(base)')
expect(envVars['CONDA_EXE']).toEqual('/usr/local/miniconda/bin/conda')
expect(envVars['_CE_M']).toEqual('')
expect(envVars['_CE_CONDA']).toEqual('')
expect(envVars['CONDA_PYTHON_EXE']).toEqual(
'/usr/local/miniconda/bin/python'
const condaPaths: string[] = await parseActivationScriptOutput(
activationStr,
'export ',
':'
)
expect(condaPaths.length).toBe(3)
})
it('Parse windows activation', async () => {
const activationStr = fs
.readFileSync(
path.resolve(__dirname, 'data/windows_conda_powershell_activation.ps1')
)
.toString('utf8')
const result: ParsedActivationScriptOutput =
await parseActivationScriptOutput(activationStr, '$Env:', ';')
const { condaPaths, envVars } = result
const condaPaths: string[] = await parseActivationScriptOutput(
activationStr,
'$Env:',
';'
)
expect(condaPaths.length).toBe(7)
expect(envVars['CONDA_PREFIX']).toEqual('C:\\Miniconda')
expect(envVars['CONDA_SHLVL']).toEqual('1')
expect(envVars['CONDA_DEFAULT_ENV']).toEqual('base')
expect(envVars['CONDA_PROMPT_MODIFIER']).toEqual('(base)')
expect(envVars['CONDA_EXE']).toEqual('C:\\Miniconda\\Scripts\\conda.exe')
expect(envVars['_CE_M']).toEqual('')
expect(envVars['_CE_CONDA']).toEqual('')
expect(envVars['CONDA_PYTHON_EXE']).toEqual('C:\\Miniconda\\python.exe')
})
})
9 changes: 0 additions & 9 deletions integrationtests/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,3 @@ def test_conda_channels():
assert returncode == 0
assert channel_list == expected
assert stderr == b""


def test_conda_env_vars_set():
"""Conda env_vars are set"""
assert "miniconda" in os.environ["CONDA_PREFIX"].lower()
assert "miniconda" in os.environ["CONDA_EXE"].lower()
assert "miniconda" in os.environ["CONDA_PYTHON_EXE"].lower()
assert os.environ["CONDA_SHLVL"] == "1"
assert os.environ["CONDA_DEFAULT_ENV"] != ""
90 changes: 34 additions & 56 deletions src/conda_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,6 @@ import * as core from '@actions/core'

import { ConfigObject } from './load_config'

/**
* Container holding the results of parseActivationScriptOutput
*/
export interface ParsedActivationScriptOutput {
condaPaths: string[]
envVars: Record<string, string>
}

/**
* Sets up conda to be later used.
*
Expand Down Expand Up @@ -74,13 +66,42 @@ const addCondaToPath = async (config: ConfigObject): Promise<void> => {
}

/**
* Activates the conda base env.
* Parse `conda shell.<shell_name> activate <env_name>`scripts outputs
*
* @param activationStr Output of the activation script
* @param envExport Prefix to which is used to export an env variable
* @param osPathSep Character to separate path in the PATH variable
* @returns condaPaths
*/
export const parseActivationScriptOutput = async (
activationStr: string,
envExport: string,
osPathSep: string
): Promise<string[]> => {
let condaPaths: string[] = []
const lines = activationStr.split(envExport)
for (const line of lines) {
if (line.startsWith('PATH')) {
const paths = line.replace(/PATH\s?=|'|"|\n|\s/g, '').split(osPathSep)
condaPaths = paths
.filter((path) => path.toLowerCase().indexOf('miniconda') !== -1)
.filter(
(orig, index, self) =>
index === self.findIndex((subSetItem) => subSetItem === orig)
)
}
}
return condaPaths
}

/**
* Activates the conda base env by changing the path and env variables.
*
* @param config Configuration of the action
*/
const activate_conda = async (config: ConfigObject): Promise<void> => {
core.startGroup('Activating conda base')
let envVarsAndCondaPaths: Promise<ParsedActivationScriptOutput>
let condaPaths: string[]
let activationStr = ''

const options = { listeners: {} }
Expand All @@ -92,65 +113,22 @@ const activate_conda = async (config: ConfigObject): Promise<void> => {
console.log('Conda activate script:')
if (config.os === 'win32') {
await exec.exec('conda', ['shell.powershell', 'activate', 'base'], options)
envVarsAndCondaPaths = parseActivationScriptOutput(
activationStr,
'$Env:',
';'
)
condaPaths = await parseActivationScriptOutput(activationStr, '$Env:', ';')
} else {
await exec.exec('conda', ['shell.bash', 'activate', 'base'], options)
envVarsAndCondaPaths = parseActivationScriptOutput(
condaPaths = await parseActivationScriptOutput(
activationStr,
'export ',
':'
)
}
const { condaPaths, envVars } = await envVarsAndCondaPaths
console.log('\n\nData used for activation:\n', { condaPaths, envVars })
console.log('\n\nData used for activation:\n', { condaPaths })
for (const condaPath of condaPaths) {
sane_add_path(condaPath)
}
for (const varName in envVars) {
core.exportVariable(varName, envVars[varName])
}
core.endGroup()
}

/**
* Parse `conda shell.<shell_name> activate <env_name>`scripts outputs
*
* @param activationStr Output of the activation script
* @param envExport Prefix to which is used to export an env variable
* @param osPathSep Character to separate path in the PATH variable
* @returns
*/
export const parseActivationScriptOutput = async (
activationStr: string,
envExport: string,
osPathSep: string
): Promise<ParsedActivationScriptOutput> => {
let condaPaths: string[] = []
const envVars: Record<string, string> = {}
const lines = activationStr.split(envExport)
for (const line of lines) {
if (line.startsWith('PATH')) {
const paths = line.replace(/PATH\s?=|'|"|\n|\s/g, '').split(osPathSep)
condaPaths = paths
.filter((path) => path.toLowerCase().indexOf('miniconda') !== -1)
.filter(
(orig, index, self) =>
index === self.findIndex((subSetItem) => subSetItem === orig)
)
} else {
const [varName, varValue] = line.replace(/'|"|\n|\s/g, '').split('=')
if (varValue !== undefined) {
envVars[varName.trim()] = varValue.trim()
}
}
}
return { condaPaths, envVars }
}

const get_python_location = async (): Promise<string> => {
core.startGroup('Getting original pythonLocation')
let pythonLocation = ''
Expand Down

0 comments on commit 705d889

Please sign in to comment.