Introduction
In the realm of continuous integration and continuous deployment (CI/CD), the robustness of a workflow is often determined by the precise management of its execution environment. For developers constructing shell scripts, build jobs, or complex workflows within GitHub Actions, the ability to leverage environment variables is not merely a convenience; it is a fundamental requirement for creating functional and dynamic automation pipelines. While standard documentation provides a baseline understanding of the default variables prefixed with GITHUB, such as GITHUB_WORKFLOW and GITHUB_RUN_ID, this represents only a fraction of the available configuration space. Each container running continuous integration builds—whether on Ubuntu, Linux, or macOS—offers a distinct suite of environment variables that developers must master to optimize their builds. Understanding the full spectrum of these variables, from user-defined scopes to platform-specific defaults, is essential for writing resilient workflows that adapt to diverse runner environments.
Understanding Variable Scopes and Hierarchy
GitHub Actions allows developers to define environment variables at three distinct levels within the workflow YAML configuration file: workflow, job, and step. These levels determine the scope and visibility of the variables, establishing a clear hierarchy that governs how data is passed through the execution pipeline.
Workflow-level environment variables are defined at the top level of the YAML file and apply to the entire workflow. For instance, a variable defined in the root env section is accessible to all jobs and steps within that workflow. This is ideal for global configurations, such as a shared base path or a universal API endpoint, that remain consistent across the entire build process.
Job-level variables provide a more granular scope, applying specifically to a designated job. This is useful when different parts of the workflow require distinct configurations, such as when running tests on different operating systems where specific environment paths or flags are necessary.
Step-level environment variables offer the most restricted scope, applying only to a single step within a job. This level of isolation is particularly useful for tasks that require temporary configurations, such as defining file paths for input or output files specific to that step. When a developer attempts to access a step-level variable from a subsequent step or action without proper context handling, they may encounter errors because the initial action does not have access to the same environment. To resolve this, variables must often be accessed using contexts, ensuring the variable is available to the specific action being executed.
Default Environment Variables and Contexts
GitHub sets a number of default environment variables that are available to every step in a workflow. Because these variables are set by GitHub itself and not defined in the workflow file, they are not accessible through the env context. Instead, most of these default variables have a corresponding, similarly named context property that allows developers to read their values during workflow processing.
For example, the value of the GITHUB_REF variable can be read using the ${{ github.ref }} context property. This distinction between environment variables and context properties is critical for proper variable retrieval in complex workflows. Developers cannot overwrite the value of default environment variables named GITHUB_* and RUNNER_*. While it is currently possible to overwrite the CI variable, this behavior is not guaranteed to persist in future updates.
The following table outlines the 18 default environment variables available to any GitHub Actions workflow or shell script:
- CI
- GITHUB_WORKFLOW
- GITHUBRUNID
- GITHUBRUNNUMBER
- GITHUB_ACTION
- GITHUB_ACTIONS
- GITHUB_ACTOR
- GITHUB_REPOSITORY
- GITHUBEVENTNAME
- GITHUBEVENTPATH
- GITHUB_WORKSPACE
- GITHUB_SHA
- GITHUB_REF
- GITHUBHEADREF
- GITHUBBASEREF
- GITHUBSERVERURL
- GITHUBAPIURL
- GITHUBGRAPHQLURL
Platform-Specific Variables and Runner Environments
Beyond the standard default variables, the full list of environment variables available to a developer depends heavily on the runner environment. A build running on the ubuntu-latest image, for example, has access to over 60 additional environment variables beyond the standard 18 defaults. These variables differ from distribution to distribution; a macos-latest runner may have a slightly different set of variables compared to ubuntu-latest, and a windows-latest runner includes variables specific to the Windows operating system, such as the PATH variable configured for Windows environments.
GitHub strongly recommends that actions use variables to access the filesystem rather than relying on hardcoded file paths. This approach ensures that workflows remain portable and resilient across different runner environments. By leveraging these platform-specific variables, developers can write scripts that automatically adapt to the underlying operating system, reducing the need for conditional logic based on hardcoded paths.
Inspecting the Environment in Practice
To fully understand the environment variables available in a specific workflow, developers can create a simple diagnostic workflow. This involves creating a YAML file that triggers on a push to the main or master branch. The workflow should include three jobs, one for each of the major operating systems: Ubuntu, Windows, and macOS.
Within each job, a single GitHub Action step is used to invoke the env command. This command forces the container of interest to print out all its environment variables to the console. When the workflow runs, the output provides a complete list of environment variables for each operating system. This method is particularly useful when a new version of a runner container is released, as it allows developers to inspect the environment and identify any new variables that have been added or removed.
The following code snippet demonstrates a workflow structure designed to inspect environment variables across different platforms:
```yaml
name: Publish GitHub Actions Artifacts Example
on:
push:
branches: [ main ]
jobs:
github-actions-environment-variables-ubuntu:
runs-on: ubuntu-latest
steps:
- name: List of the GitHub Actions environment variables on Ubuntu
run: env
github-actions-environment-variables-windows:
runs-on: windows-latest
steps:
- name: Windows GitHub Actions environment variables List
run: env
github-actions-environment-variables-macos:
runs-on: macos-latest
steps:
- name: MacOS List of GitHub Actions environment variables
run: env
```
Integrating Variables into Workflow Logic
When integrating environment variables into actual build steps, such as compiling a Java application using Maven, developers must carefully manage how these variables are accessed. In a typical workflow YAML file, a job might define a job-level variable, while a specific step might define a step-level variable.
For instance, if a setup-java action is used, it may not have direct access to environment variables defined in subsequent steps if they are not passed through the correct context. If a developer attempts to use an environment variable without the appropriate context syntax, they may receive an error indicating that the variable is not found. This is because the action operates in a specific environment context, and variables must be explicitly made available to it.
The use of GitHub Secrets represents another critical layer of environment variable management. Secrets allow developers to store sensitive information, such as API keys or passwords, in a secure manner. These secrets can be accessed within workflows as environment variables, ensuring that sensitive data is not exposed in the workflow log or the YAML file itself. By combining default variables, user-defined variables at various scopes, and secrets, developers can create highly secure and flexible CI/CD pipelines.
Conclusion
The management of environment variables in GitHub Actions is a nuanced aspect of CI/CD pipeline development that requires a deep understanding of scope, context, and platform-specific behaviors. While the 18 default variables provide a consistent foundation, the true power of GitHub Actions lies in the ability to leverage hundreds of additional variables that vary by runner environment. Developers must move beyond the standard documentation to inspect and understand the full suite of variables available on Ubuntu, Windows, and macOS runners. By utilizing the env command for inspection and adhering to best practices regarding variable scopes and context usage, developers can create robust, portable, and secure workflows that adapt dynamically to the underlying infrastructure. The distinction between default variables, user-defined scopes, and secrets ensures that pipelines remain both flexible and secure, capable of handling complex build and deployment tasks across diverse technological landscapes.