Environment variables are a form of configuration that is predefined for each GitHub Actions workflow run. In this topic, we will look into how we can work with environment variables and secrets. We will start with how we can use environment variables within GitHub Actions workflows. Next, we will look at using secrets via environment variables within GitHub Actions workflows.
Environment variable
Environment variables are a way of storing pieces of data that you can reference later in your workflows. They are non-null string values stored in a key-value list in the environment. You can configure environment variables in multiple places, depending on the scope they should have.
Workflow level: Variables on the workflow level are available to all jobs and all steps within those jobs.
Job level: Variables on the job level can only be accessed by all steps within the specific job.
Step level: Variables on the step level can only be accessed by a specific step.
When there are two environment variables with the same name, GitHub uses the recent environment variable that is in the scope. For example, an environment variable that is defined in the scope of a step will replace a variable from the job and workflow level with the same name when the step is executed.
There are two ways to access an environment variable:
Interpolation: You can access variables using interpolation with the syntax;
${{ ... }}. For example:-echo ${{ env.VARIABLE }}Variable reference: You can access variables using variable reference with the syntax;
$VARNAME. For example:-echo $VARIABLE
Let's look at how you can use either of the two in any scenario.
name: environment_variable_tutorial
on: [push]
env:
# This custom environment variable is available to all the jobs and steps
TOPIC: Envs and secrets
jobs:
job1:
env:
# This custom environment variable is available for all the steps
section: Dev tools
runs-on: ubuntu-latest
steps:
- name: step 1
env:
# This custom environment variable is available to only this specific step
platform: the platform
# Referencing the environment variables
run: echo "Welcome to the ${{ env.TOPIC }} topic from $section section at $platform"There are three custom environment variables in this workflow. Each of them is defined in different scopes of the workflow file. We used both interpolation and variable reference to access these variables.
Interpolation retrieves the value from the env context object by invoking the relevant key. Then the value is made available to the runner so that it can be executed. Whereas variable reference works by the runner's instance looking up the environment variable to access the value.
In this example, the workflow specifies ubuntu-latest as the runner. So bash is used to read the variable, which is $VARIABLE_NAME. Bash is also used for macOS, so the variable reference is also $VARIABLE_NAME. For Windows, it is $env:VARIABLE_NAME. If we want to use an environment variable that is independent of all the runners, we use interpolation ${{ env.VARIABLE_NAME }}. This means the environment variable works for all the runners.
When creating custom environment variables, make sure that they do not have the same name as default environment variables.
Default environment variable
Runners that are used in the workflows (Windows, macOS, and Ubuntu) also come with their respective default environment variables that GitHub sets. They are available at every step in a workflow.
It is a good practice to incorporate environment variables when using actions to access the file system rather than using hard-coded file paths.
There are some default environment variables that help fulfill this purpose:
Default environment variable | Description |
|---|---|
GITHUB_ ACTOR | Returns the GitHub username or app name that started the workflow. |
GITHUB_REPOSITORY | Returns the name of the repository and owner. |
GITHUB_REF | Branch or tag ref that triggered the workflow. For example, it will return |
You can take a look at the list of default environment variables that are available to every step in a workflow. Take a look at the code snippet below and its subsequent output. This workflow utilizes the default environment variables we just talked about.
name: env_tutorial
on: [push]
jobs:
job1:
runs-on: macos-latest
steps:
- name: step 1
run: |
echo "The job ID is $GITHUB_JOB"
echo "The name of the event triggered by the workflow is $GITHUB_EVENT_NAME"
echo "The name of the workflow is $GITHUB_WORKFLOW"
echo "The runner of the workflow is $RUNNER_OS"Most default variables have a corresponding context property that shares a similar name, and this context property is the key to accessing the variable's value using interpolation. For instance, you cannot simply interpolate the value of the GITHUB_REF variable. Instead, you must use the ${{ github.ref }} context property.
Environment variables written in workflow files are visible to the public. To securely store your environment variables, you can use GitHub secrets. The values you add as secret environment variables will be encrypted by GitHub Actions. This will obfuscate the environment variables, and they will not be publicly visible.
Secrets
Secrets are environment variables that cannot be shared with an unauthorized party. It is sensitive data, usually specific to a context. For example, an organization, a repository, or a team.
Secrets may be:
the username and password to access a tool;
the key to consuming an API;
an access token or credential.
Secrets must follow certain conditions naming them:
Secret names must contain alphanumeric characters ([a-z], [A-Z], [0-9]).
Secret names cannot contain space. Underscores (_) are allowed.
Secret names cannot begin with a number.
Secret names are not case-sensitive.
There are three types of secrets in GitHub Actions, which are classified according to the scope they are available in:
Environment — They are available in the scope of a specific environment. As an example, for production, we have production environment secrets. Similarly, we have development environment secrets for development.
Repository — They are available in the scope of the whole repository.
Organizational-level — They are available within the scope of the whole organization. Many repositories exist under a single organization. Organizational secrets can be shared between repositories.
When GitHub Actions looks up to access secrets, it will first look into the environment secrets. If no environment secret exists with the given name, it will look into the repository secrets and use the repository if the secret exists. Similarly, if it doesn't find the secret in the repository secrets, it will move to look into the organizational-level secrets (if the repository is under an organization) and use those.
Creating encrypted secrets for a repository
Now, let's look into how to create secrets for a repository.
Go to the Settings tab of your GitHub repository.
Scroll to the Security section and select Secrets.
Select Actions.
You will be taken to the action secret page. Select New repository secret.
You will be prompted to create a secret. Provide a name and value for the GitHub secret token.
To use the value of an environment variable inside a runner, it is necessary to use the env context ${{ env.VARIABLE_NAME }}. The env context; ${{ env.VARIABLE_NAME }}, allows you to access the variable.
For secrets, the operation is the same as environment variables. You need to use the context of the secret as shown in the example below:
steps:
- name: Encrypted secret for repository
with: # We can configure the secret to be set as an input
secret: ${{ secrets.SECRET_VALUE }}
env: # Or we can configure the secret as an environment variable
secret: ${{ secrets.SECRET_VALUE }}You can see in the example above that it is possible to configure a secret as an environment variable or as an input.
We recommend using interpolation instead of variable reference of the runner to access secrets. This is done for security purposes.
Let's create a workflow where a SECRET_VALUE is the name of the secret. Then manipulate this variable in a step by using shell commands.
name: secret_workflow_repository
on: [push]
jobs:
job1:
runs-on: ubuntu-latest
steps:
- name: Step with a secret
run: |
echo "Hello World"
echo "This is a secret value: ${{ secrets.SECRET_VALUE }}"You may have noticed that the secret is hidden with asterisks. Once the secret is saved, you cannot retrieve it manually. Only the GitHub Actions engine can access the secret.
Creating encrypted secrets for an environment
To use environment secrets, you need to add one extra keyword with the name of the environment we created in the settings (Settings → Code and automation → Environments). The scope of the environment keyword needs to be on the job level only.
In this workflow example, we create a new environment called production_secret. Within this environment, we create a secret with the name SECRET_VAL.
name: secret_workflow_environment
on: [push]
jobs:
job1:
environment: production_secret
runs-on: ubuntu-latest
steps:
- name: Run Commands with a Secret
run: |
echo "Hello World!"
echo "This is a secret value: ${{ secrets.SECRET_VAL }}"The output of this workflow is the same as the workflow above in encrypted secrets using the repository.
Conclusion
In this topic, we discussed how you can use environment variables and make use of the default environment variables provided by GitHub Actions. We also looked at how you can securely store these environment variables using secrets. You can incorporate knowledge gained from this topic by storing API keys, login credentials, and tokens required for deploying an application.