I am setting up continuous integration using GitHub Actions. One of the prerequisites (samtools) is most easily installed by conda. The standard way to use installed packages is to activate the corresponding conda environment. I am looking for a way to activate the conda environment. The common methods to activate it failed, see details below. My current workaround is to add to the PATH a hardcoded path to samtools, installed by conda. But this is not maintainable if the number of installed packages increases. It is also not the standard way to use packages installed with conda.
DETAILS:
.github/workflows/conda.yml
name: Conda
on: [push]
jobs:
# label of the job
tests:
name: Tests
# containers must run in Linux based operating systems
runs-on: ubuntu-latest
# Docker Hub image that `postgres-job` executes in
container: node:latest
# service containers to run with `postgres-job`
steps:
- uses: conda-incubator/setup-miniconda#v2
with:
miniconda-version: "latest"
channels: bioconda, conda-forge, defaults
use-only-tar-bz2: true # IMPORTANT: This needs to be set for caching to work properly!
auto-update-conda: true
auto-activate-base: true
- name: Install samtools
run: |
set -x
echo "begin: PATH=$PATH;"
conda create -y --name samtools samtools
conda activate samtools || true
echo "after conda activate samtools: PATH=$PATH;"
which samtools || true
$CONDA/bin/activate samtools || true
echo "after CONDA/bin/activate samtools: PATH=$PATH;"
which samtools || true
export PATH="3/envs/samtools/bin:$PATH"
echo "after export PATH=3/envs/samtools/bin:PATH: PATH=$PATH;"
which samtools || true
Output:
Run set -x
begin: PATH=3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin;
+ echo begin: PATH=3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin;
+ conda create -y --name samtools samtools
Collecting package metadata (repodata.json): ...working... done
Solving environment: ...working... done
[...]
# To activate this environment, use
#
# $ conda activate samtools
#
# To deactivate an active environment, use
#
# $ conda deactivate
+ conda activate samtools
CommandNotFoundError: Your shell has not been properly configured to use 'conda activate'.
To initialize your shell, run
$ conda init <SHELL_NAME>
Currently supported shells are:
- bash
- fish
- tcsh
- xonsh
- zsh
- powershell
See 'conda init --help' for more information and options.
IMPORTANT: You may need to close and restart your shell after running 'conda init'.
+ true
after conda activate samtools: PATH=3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin;
+ echo after conda activate samtools: PATH=3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin;
+ which samtools
+ true
+ 3/bin/activate samtools
+ echo after CONDA/bin/activate samtools: PATH=3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin;
+ which samtools
after CONDA/bin/activate samtools: PATH=3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin;
+ true
after export PATH=3/envs/samtools/bin:PATH: PATH=3/envs/samtools/bin:3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin;
+ export PATH=3/envs/samtools/bin:3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+ echo after export PATH=3/envs/samtools/bin:PATH: PATH=3/envs/samtools/bin:3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin;
+ which samtools
3/envs/samtools/bin/samtools
Automatic Activation
The documentation also gives examples of automatic activation. Specifically, Example #3 demonstrates this with a couple key components:
As noted in the IMPORTANT section of the documentation and in the other answer, the shell needs to use login mode, which can be set globally within a job, using
jobs:
example-3:
defaults:
run:
shell: bash -l {0}
An environment definition is provided to setup-miniconda GHA. In the example it's etc/example-environment.yml, which defines the environment anaconda-client-env and this is set to activate using the activate-environment argument.
steps:
- uses: conda-incubator/setup-miniconda#v2
with:
activate-environment: anaconda-client-env
environment-file: etc/example-environment.yml
auto-activate-base: false
- run: |
conda info
conda list
Samtools Example
I have a repository where I test environment definitions, so here's an explicit example for samtools. Note that I prefer Mamba, and also recommend capturing an explicit mamba env export to document the environment.
envs/so-samtools.yaml
name: so-samtools
channels:
- conda-forge
- bioconda
- defaults
dependencies:
- samtools
.github/workflows/so-samtools.yaml
name: so-samtools
on:
push:
paths:
- 'envs/so-samtools.yaml'
- '.github/workflows/so-samtools.yaml'
jobs:
create-env:
name: ${{ matrix.os }}
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash -l {0}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- name: checkout repository
uses: actions/checkout#v2
- name: create environment with mamba
uses: conda-incubator/setup-miniconda#v2
with:
mamba-version: "*"
channels: conda-forge,bioconda,defaults
auto-activate-base: false
activate-environment: so-samtools
environment-file: envs/so-samtools.yaml
- name: check solution
run: |
mamba env export
- name: test samtools
run: |
which samtools
samtools help
Add this to not ignore bash profile files (fix from: https://github.com/marketplace/actions/setup-miniconda ):
defaults:
run:
shell: bash -l {0}
Complete GitHub Action yml file, .github/workflows/conda.yml:
name: Conda
on: [push]
jobs:
# label of the job
tests:
name: Tests
# containers must run in Linux based operating systems
runs-on: ubuntu-latest
# Do not ignore bash profile files. From:
# https://github.com/marketplace/actions/setup-miniconda
defaults:
run:
shell: bash -l {0}
# Docker Hub image that `postgres-job` executes in
container: node:latest
# service containers to run with `postgres-job`
steps:
- uses: conda-incubator/setup-miniconda#v2
with:
miniconda-version: "latest"
channels: bioconda, conda-forge, defaults
use-only-tar-bz2: true # IMPORTANT: This needs to be set for caching to work properly!
auto-update-conda: true
auto-activate-base: true
- name: Install samtools
run: |
echo "begin: PATH=$PATH;"
conda create -y --name samtools samtools
conda activate samtools || true
echo "after conda activate samtools: PATH=$PATH;"
which samtools || true
# Use samtools here.
Output:
begin: PATH=/__w/ngs-aggregate_results/ngs-aggregate_results/3/envs/test/bin:/__w/ngs-aggregate_results/ngs-aggregate_results/3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin;
...
after conda activate samtools: PATH=/__w/ngs-aggregate_results/ngs-aggregate_results/3/envs/samtools/bin:/__w/ngs-aggregate_results/ngs-aggregate_results/3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin;
/__w/ngs-aggregate_results/ngs-aggregate_results/3/envs/samtools/bin/samtools
Related
I am used to work with virtualenvs. However for some reason I am not able to activate an env in a github action job.
In order to debug I added this step:
- name: Activate virtualenv
run: |
echo $PATH
. .venv/bin/activate
ls /home/runner/work/<APP>/<APP>/.venv/bin
echo $PATH
On the action logs I can see
/opt/hostedtoolcache/Python/3.9.13/x64/bin:/opt/hostedtoolcache/Python/3.9.13/x64:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
[...] # Cut here because a lot of lines are displayed. My executables are present including the one I'm trying to execute : pre-commit.
/home/runner/work/<APP>/<APP>/.venv/bin:/opt/hostedtoolcache/Python/3.9.13/x64/bin:/opt/hostedtoolcache/Python/3.9.13/x64:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
So it should work...
But the next steps which is
- name: Linters
run: pre-commit
Generates those error logs
Run pre-commit
pre-commit
shell: /usr/bin/bash -e {0}
env:
[...] # private
/home/runner/work/_temp/8e893c8d-5032-4dbb-8a15-59be68cb0f5d.sh: line 1: pre-commit: command not found
Error: Process completed with exit code 127.
I have no issue if I transform the step above this way :
- name: Linters
run: .venv/bin/pre-commit
For some reason bash is not able to find my executable while the folder containing it is referenced in $PATH.
I'm sure you know that activation of a virtualenv is not magic — it just prepends …/.venv/bin/ to $PATH. Now the problematic thing in Github Action is that every run is executed by a different shell and hence every run has a default PATH as if the virtualenv was deactivated.
I see 3 ways to overcome that. The 1st you already mentioned — just use .venv/bin/<command>.
The 2nd is to activate the venv in every step:
- name: Linters
run: |
. .venv/bin/activate
pre-commit
The 3rd is: activate it once and store $PATH in a file that Actions use to restore environment variables at every step. The file is described in the docs.
So your entire workflow should looks like this:
- name: Activate virtualenv
run: |
. .venv/bin/activate
echo PATH=$PATH >> $GITHUB_ENV
- name: Linters
run: pre-commit
I have like this job in my gitlab ci cd configuration file:
jobName:
stage: dev
script:
- export
- env
- sshpass -p $SSH_PASSWORD ssh -o StrictHostKeyChecking=no $SSH_LOGIN 'bash -s' < script.sh
when: manual
I tried share/pass current job env vars to my custom bash script file by adding this command in my job:
- export
- env
But my jon can't access (don't see) to job env vars. How I can correctly share job all env vars to bash script?
I believe dotenv might be suitable for this.
job1:
stage: stage1
script:
- export VAR=123
- echo $VAR
- echo "VAR=$VAR" > variables.env
artifacts:
reports:
dotenv: variables.env
job2:
stage: stage2
script:
- echo $VAR
And your VAR should be available in downstream jobs.
I want to achieve the following build process:
decide the value of environment var depending on the build branch
persist this value through diff build steps
use this var to pass it as build-arg to docker build
Here is some of the cloudbuild config I've got:
- id: 'Get env from branch'
name: bash
args:
- '-c'
- |-
environment="dev"
if [[ "${BRANCH_NAME}" == "staging" ]]; then
environment="stg"
elif [[ "${BRANCH_NAME}" == "master" ]]; then
environment="prd"
fi
echo $environment > /workspace/environment.txt
- id: 'Build Docker image'
name: bash
dir: $_SERVICE_DIR
args:
- '-c'
- |-
environment=$(cat /workspace/environment.txt)
echo "===== ENV: $environment"
docker build --build-arg ENVIRONMENT=$environment -t gcr.io/${_GCR_PROJECT_ID}/${_SERVICE_NAME}/${COMMIT_SHA} .
The problem lies in the 2nd step. If I use bash step image, then I've got no docker executable in order to build my custom image.
And if I use gcr.io/cloud-builders/docker step image, then I can't execute bash scripts. In the args field I can only pass arguments for the docker executable. And this way I cannot extract the value of environment that I've persisted through the steps of the build.
The way I managed to accomplish both is to use my own, custom, pre-built image, which contains both bash and docker executables. I have that image in the container registry and I use it as the build step image. But this requires some custom work from my side. I was wondering if there is a better, more standardized way with built-in tools from cloudbuild.
Sources:
how to run inline bash scripts
how to persist values through build steps
You can change the default entrypoint by adding entrypoint: parameter
- name: 'gcr.io/cloud-builders/docker'
entrypoint: 'bash'
args:
- -c
- |
echo $PROJECT_ID
environment=$(cat /workspace/environment.txt)
echo "===== ENV: $environment"
docker build --build-arg ENVIRONMENT=$environment -t gcr.io/${_GCR_PROJECT_ID}/${_SERVICE_NAME}/${COMMIT_SHA} .
- job: build_package
dependsOn: test
displayName: Build Package
pool:
vmImage: 'ubuntu-18.04'
steps:
- bash: echo "##vso[task.prependpath]$CONDA/bin"
displayName: Add Conda to PATH
- bash: conda env create -f environment.yml --name $(Agent.Id)
displayName: Create Conda Environment
- bash: |
source activate $(Agent.Id)
conda build . --output-folder ./
displayName: Build Package In the Build Directory
there are 3 bash steps above, I wonder what is the best practice for crating the bash steps, should I group them under the same bash step or like above to make a 3 separate bash steps?
What's the difference between create one bash with several scripts and put those scripts into different bash steps? Is each bash step creates a new shell environment?
Each step/task will creates the new session, so some data will be missing, such as session level environment variable.
So if the scripts are related, you can just run in a task. It also save time.
I basically want to run the npm install and grunt build command within the newly added repo.
inputs:
- name: repo
- path:
run:
path: repo/
args:
- npm install
- grunt build
path: refers to the path in the container to the binary / script to execute.
Check out this example on the Tasks documentation here : https://concourse-ci.org/tasks.html#task-environment
run:
path: sh
args:
- -exc
- |
whoami
env
sh is the program to execute, and args are passed to the sh program
slight variation of Topher Bullock's answer
run:
path: sh
args:
- -exc
- whoami && env
which will run env if only whoami doesn't return error
This will run env even if whoami fails.
run:
path: sh
args:
- -exc
- whoami || env