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

🩹 Fix env activation #130

Merged
merged 4 commits into from
Jul 5, 2021
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
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