How to create and access environment variables with GitHub Actions? - github

Here is a part of the workflow file:
env:
resourceGroupName: 'rg-${GITHUB_REF#refs/heads/}'
I am trying to create an environment variable that concats a string and another environment variable but in the logs all I see from an echo from either way I can see is the following:
echo "$resourceGroupName" -> rg-$***GITHUB_REF#refs/heads/***
echo "{{ env.resourceGroupName}}" -> *** env.resourceGroupName***
Instead of what I'd have expected such as:
rg-the-name-of-the-branch
The docs do not seem particularly good in this regard. It also seems to be trying and failing to mask the variables as it's placing erroneous extra asterisks.

You need to have ${{ github.ref }} for it to work with the GitHub Actions vars. The linux envs are also exposed, so you can also use $GITHUB_REF directly without the brackets.
Alternatively, you can also use the format option to combine strings if you like that more:
format("rb-{0}", ${{ github.ref }})

Related

Azure Pipeline root variables in templates

I am trying to use variables defined at the root level on a YAML pipeline inside Azure DevOps inside templates via the template syntax, but it seems that the variables are not available inside the templates, but when adding the steps directly to the pipeline, the exact same thing works perfectly.
So with a pipeline snippet like that
variables:
- name: test
value: asdf
stages:
- stage:
jobs:
- job: test_job
steps:
- script: echo "${{ variables.test }}"
- template: ./test.yaml
And a test.yaml like that
jobs:
- job: test
steps:
- script: echo "${{ variables.test }}"
The script inside the test_job job writes out asdf while the job inside the template just resolves to echo "".
Since my understanding of pipeline templates is, that those basically get inserted into the main pipeline, this seems like a bug. Any ideas on how to use the root variables in a template syntax inside templates or why this is not working? (Macro synatx is not an option as I need the variable inside a templated condition like ${{ if eq(variables['test'], 'asdf') }})
For security reasons, we only allow you to pass information into
templated code via explicit parameters.
https://docs.microsoft.com/en-us/azure/devops/pipelines/process/templates?view=azure-devops
The means the author of the pipeline using your template needs to
commit changes where they explicitly pass the needed info into your
template code.
There are some exceptions to this, where the variable is statically
defined in the same file or at pipeline compile time, but generally
speaking, it’s probably better to use parameters for everything that
does not involve system-defined read-only dynamic variable and
custom-defined dynamic output variables.
This behavior is by design, check this thread in the developer community
So you can either pass the variable as a parameter to the template or define a centralized variables file to include in the template like here

Evaluating environment variables in github actions workflow

I'm positive that this is something easy but I'm not able to track down exactly what I'm looking for. I have a workflow that performs a build and creates an artifact. The artifact uses an environment variable in the filename. So far so good. Then when I try to pass this file name to S3 upload action, it isn't found because the environment variable isn't evaluated. Here is the relevant part of my workflow:
- name: Build project
run: ./build_project.sh
- run: ls -l "${GITHUB_WORKSPACE}/build/${FILE_NAME}.zip" # file exists in directory
- run: echo "${GITHUB_WORKSPACE}/build/${FILE_NAME}.zip" # echo returns the location properly
- uses: hkusu/s3-upload-action#v2
id: upload # specify some ID for use in subsequent steps
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: "eu-west-2"
aws-bucket: ${{ secrets.AWS_BUCKET }}
file-path: "${GITHUB_WORKSPACE}/build/${FILE_NAME}.zip" # Error: file does not exist
output-file-url: "true" # specify true
- name: Show URL
run: echo '${{ steps.upload.outputs.file-url }}' # use this output
My actual question is how to replace "${GITHUB_WORKSPACE}/build/${FILE_NAME}.zip" with the file path and name when it actually runs the workflow. Also, I have tried a few of different combinations of things - no quotes, no curly braces, neither, both.
Since these parameters are passed through to the s3-upload-action, the behaviour depends on whether the action expands shell parameters or not, but the input value will be literally
${GITHUB_WORKSPACE}/build/${FILE_NAME}.zip
i.e., unexpanded.
You can use expressions to work around this:
file-path: ${{ github.workspace }}/build/${{ env.FILE_NAME }}.zip
You maybe have assumed that environment variables expand everywhere as they do when evaluated by a shell, such as in a run: step, but they don't.

Define a list variable in azure devops pipeline on runtime

I have an azure pipeline YAML file and I can't figure out how to declare a list variable in a script and use it on the next step.
What I would like is something like that:
- script: echo "##vso[task.setvariable variable=packages]#('a','b')"
- template: publish-template.yml
parameters:
packages: packages
or
- script: echo "##vso[task.setvariable variable=packages]#('a','b')"
- ${{ each package in parameters.packages }}:
- script: echo ${{ package }}
But I can't find out how it's possible.
Thanks :)
You can't do such thing. Variables are kept as strings. So this script: echo "##vso[task.setvariable variable=packages]#('a','b')" will produce a string ('a','b') and not an array. You can parse is script task and treat as array but there is no way to have array served at to level as you show in your example.
Please check it here:
Yaml variables have always been string: string mappings. The doc appears to be currently correct, though we may have had a bug when last you visited.
We are preparing to release a feature in the near future to allow you to pass more complex structures. Stay tuned!

How to extend environment variables in github actions with existing ones?

A step in a job requires the setting of an environment variable. is something like the code example below possible to achieve with github actions?
Example:
- name: Do Something
env:
NEW_VAR: $EXISTING_VAR/subdir
run: |
echo $NEW_VAR
I know, that i can directly use the environment variable, but it would be nice to construct new env vars by concatenating old ones.
There is also ${{ env.EXISTING_VAR }}, but it looks like that this expression is not evaluated.
Update 2022
The syntax has changed: ::set-env has been deprecated and is replaced with environment files. This means that you can set the new env variable within run in one step and use it in a subsequent step. The syntax would be:
...
- name: Set env variable
run: |
echo "NEW_VAR=${{ env.EXISTING_VAR }}/subdir/${{ env.OTHER_EXISTING_VAR }}" >> $GITHUB_ENV
- name: Use env variable
run: |
echo ${{ env.NEW_VAR }}
...
One caveat is that I was unable to set and use a new environment variable within the same step. Here is a non-working example of what I mean
...
- name: Set env variable
run: |
echo "VAR_FROM_THE_SAME_STEP=${{ env.EXISTING_VAR }}" >> $GITHUB_ENV
echo "NEW_VAR=${{ env.EXISTING_VAR }}/subdir/${{ env.VAR_FROM_THE_SAME_STEP }}" >> $GITHUB_ENV
...
Resolving ${{ env.VAR_FROM_THE_SAME_STEP }} will not work in this case, you would need to do it in a subsequent step, which is of course no big deal in most cases.
There is another way to set env vars.
Try something like that:
- name: Set env to qa
run: |
echo "::set-env name=ENVIRONMENT::qa"
Although this works for me too:
ECS_DOCKER_COMPOSE: "docker-compose-$ENVIRONMENT.yml"
I guess that GitHub actions not work with "/".
Have you tried without slash?

How do I use an env file with GitHub Actions?

I have multiple environments (dev, qa, prod) and I'm using .env files to store secrets etc... Now I'm switching to GitHub Actions, and I want to use my .env files and declare them into the env section of the github actions yml.
But from what I've seen so far, it seems that I can not set a file path and I have to manually re-declare all variables.
How should I proceed as best practice?
A quick solution here could be having a step to manually create the .env file before you need it.
- name: 'Create env file'
run: |
touch .env
echo API_ENDPOINT="https://xxx.execute-api.us-west-2.amazonaws.com" >> .env
echo API_KEY=${{ secrets.API_KEY }} >> .env
cat .env
Better method for multiple variables
If you have a lot of env variables simply paste the whole file into a github secret named ENV_FILE and just echo the whole file.:
- name: 'Create env file'
run: |
echo "${{ secrets.ENV_FILE }}" > .env
The easiest way to do this is to create the .env file as a github secret and then create the .env file in your action.
So step 1 is to create the .env files as a secret in github as a base64 encoded string:
openssl base64 -A -in qa.env -out qa.txt
or
cat qa.env | base64 -w 0 > qa.txt
Then in you action, you can do something like
- name: Do Something with env files
env:
QA_ENV_FILE: ${{ secrets.QA_ENV_FILE }}
PROD_ENV_FILE: ${{ secrets.PROD_ENV_FILE }}
run: |
[ "$YOUR_ENVIRONMENT" = qa ] && echo $QA_ENV_FILE | base64 --decode > .env
[ "$YOUR_ENVIRONMENT" = prod ] && echo $PROD_ENV_FILE | base64 --decode > .env
There are a number of ways for determining $YOUR_ENVIRONMENT but usually this can be extracted from the GITHUB_REF object. You applications should be able to read from the .env files as needed.
I would suggest 3 pretty simple ways to engage your .env file variables in the GitHub Actions workflow. They differ based on whether you store the file in your repository (the worst practice) or keep it out of it (the best practice).
You keep your .env file in the repository:
There are some ready-made actions that allow to read the .env variables (e.g. Dotenv Action,Simple Dotenv).
(simple, manual, annoying when update .env variables) You keep your file out of your repository:
You manually copy the content of the respective .env files (say .env.stage, .env.production) into the respective GitHub Actions secret variables (say WEBSITE_ENV_STAGE, WEBSITE_ENV_PRODUCTION).
Then at your GitHub Actions workflow script create the .env file from the desired variable like this echo "${{secrets.WEBSITE_ENV_STAGE }}" > .env and use it in the workflow.
(a bit more involved though prepare it once, then change your .env variables at the local machine, then sync these at GitHub with one click) As in item 2 above, the file is out of the repository.
Now you use the GitHub Actions API to create or update the secrets. On your local machine in the dev environment you write the NodeJS script that calls the API endpoint and write the .env files to the desired GitHub Actions secret variable (say as above into WEBSITE_ENV_STAGE or to both stage and production variables at once);
This is pretty wide choice of ways to engage the .env files's variables in the workflow. Use any matching your preference and circumstances.
Just for information, there is the 4th way which engages some 3rd party services like Dotenv Vault or HasiCorp Vault (there are more of the kind) where you keep you secret variables to read these to create .env file at build time with your CI/CD pipeline. Read there for details.
Edit:
You were using Circleci Contexts, so with that you had a set of secrets of each env. I know they are working to bring secrets to org level, and maybe team level... there is no info if they will create sort of contexts like we have in CCI.
I have thought on adding the env as prefix of the secret name like STAGE_GITHUB_KEY or INTEGRATION_GITHUB_KEY using ${env}_GITHUB_KEY on the yml as a workaround for now... What do you think?
--- Original answer:
If I understand you well, you already have the dotenv files stored somewhere and you want to inject all those secrets into the steps, without having to manually add them to github secrets and do the mapping in each workflow you migrate... right?
There is an action made by someone that reads a dotenv file and put its values into ouputs, so you can use them linked in further steps. Here is the link: https://github.com/marketplace/actions/dotenv-action
Whatever is present in the .env file will be converted into an output variable. For example .env file with content:
VERSION=1.0
AUTHOR=Mickey Mouse
You do:
id: dotenv
uses: ./.github/actions/dotenv-action
Then later you can refer to the alpine version like this ${{ steps.dotenv.outputs.version }}
You can also use a dedicated github action from github-marketplace to create .env files.
Example usage:
name: Create envfile
on: [push]
jobs:
create-envfile:
runs-on: ubuntu-18.04
steps:
- name: Make envfile
uses: SpicyPizza/create-envfile#v1
with:
envkey_DEBUG: false
envkey_SOME_API_KEY: "123456abcdef"
envkey_SECRET_KEY: ${{ secrets.SECRET_KEY }}
file_name: .env
Depending on your values defined for secrets in github repo, this will create a .env file like below:
DEBUG: false
SOME_API_KEY: "123456abcdef"
SECRET_KEY: password123
More info: https://github.com/marketplace/actions/create-env-file
Another alternative is to use the Environments feature from github. Although that isn't available on private repos in the free plan.
You could have scoped variables, at repository, profile/organization level and environment. The configuration variables closer to the repository takes precedence over the others.
I tried using the accepted solution but GitHub actions was complaining about the shell commands. I kept getting this error: line 3: unexpected EOF while looking for matching ``'
Instead of referencing the secrets directly in the shell script, I had to pass them in separately.
- name: Create env file
run: |
touch .env
echo POSTGRES_USER=${POSTGRES_USER} > .env
echo POSTGRES_PASSWORD=${POSTGRES_PASSWORD} > .env
cat .env
env:
POSTGRES_USER: ${{ secrets.POSTGRES_USER }}
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
You can export all secrets to environment variables and do everything from a script.
I created an action exactly for that - takes all the secrets and exports them to environment variables.
An example would be:
- run: echo "Value of MY_SECRET1: $MY_SECRET1"
env:
MY_SECRET1: ${{ secrets.MY_SECRET1 }}
MY_SECRET2: ${{ secrets.MY_SECRET2 }}
MY_SECRET3: ${{ secrets.MY_SECRET3 }}
MY_SECRET4: ${{ secrets.MY_SECRET4 }}
MY_SECRET5: ${{ secrets.MY_SECRET5 }}
MY_SECRET6: ${{ secrets.MY_SECRET6 }}
...
You could convert it to:
- uses: oNaiPs/secrets-to-env-action#v1
with:
secrets: ${{ toJSON(secrets) }}
- run: echo "Value of MY_SECRET1: $MY_SECRET1"
Link to the action, which contains more documentation about configuration: https://github.com/oNaiPs/secrets-to-env-action
I was having the same issue. What I wanted was to upload a .env file to my server instead of defining the env variables in my Github repo. Since I was not tracking my .env file so every time my workflow ran the .env file got deleted. So what I did was :
Added the .env file in the project root directory in my server.
Added clean: false under with key in my actions/checkout#v2 in my workflow
eg:
jobs:
build:
runs-on: self-hosted
strategy:
matrix:
node-version: [14.x]
- uses: actions/checkout#v2
with:
clean: 'false'
This prevents git from deleting untracked files like .env. For more info see: actions/checkout
One more approach would be doing something as described in https://docs.github.com/en/actions/security-guides/encrypted-secrets#limits-for-secrets
So basically treating your .env file as a "large secret". In this case, the encrypted .env file is kept commited in your repo, which should be fine. Then in your action have a step to decrypt the .env file.
This removes the overhead of having to create each individual secret inside your .env as a Github secret. The only Github secret to maintain in this case, is one for the encryption password. If you have multiple .env files such as qa.env, prod.env, etc... I would strongly suggest using a different encryption password for each, and then store each encryption passwords as an "environment secret" in Github instead of "repo secret" (if using Github environments is your thing. See https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment).
If you don't want to commit the (encrypted) .env file in you repo, then I would go with the base64 approach described in https://stackoverflow.com/a/64452700/1806782 (which is simmilar to what's in https://docs.github.com/en/actions/security-guides/encrypted-secrets#storing-base64-binary-blobs-as-secrets) and then create a new Github secret to host the encoded contents.
For those like me with aversion to manual repetitive tasks, Github secret creation can these days easily be scripted with the Github CLI tool. See
https://cli.github.com/manual/gh_secret_set . It also supports 'batch' creation of secrets from env files (see the -f, --env-file flags)
inspired by Valentine Shis answer above, I created a GitHub Action for this use-case and the one I had at the time while reading this thread.
GitHub Action: next-env
GitHub Action to read .env.[development|test|production][.local] files in Next.js (but also non Next.js) projects and add variables as secrets to GITHUB_ENV.
Despite the name, it also works in non-Next.js projects as it uses a decoupled package of the Next ecosystem.
You need to define your environment variables in "Secrets" section of your repository. Then you can simply use your secrets in your workflow.
Example usage:
- uses: some-action#v1
env:
API_KEY: ${{ secrets.API_KEY }}
SECRET_ID: ${{ secrets.SECRET_ID }}
with:
password: ${{ secrets.MY_PASSWORD }}
Here is the documentation:
https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets