Comparison of GitHub Actions with competitors
Deep dive into workflow syntax, triggers, and job configuration
Explore matrices, reusable workflows, and composite actions
•Runner Types and Execution Environments
•Persisting Build Outputs with Artifacts
•Controlling GitHub Permissions
•Authenticating to Third-Party Systems
•Matrix Strategies, Conditionals, and Concurrency Controls
Discover and integrate community actions from the GitHub Marketplace
Build custom JavaScript and Docker actions from scratch
•JavaScript and TypeScript Actions
Optimize logs, secrets, environments, and permissions for teams
•Developer Experience (Actions)
Harden workflows with security, reliability, and cost-saving techniques
•Maintainable Workflow Patterns
Apply course concepts by automating a real-world deployment pipeline
GitHub Actions provides a built-in mechanism for handling secrets and variables to be used within and across workflows.
Configuration that must stay private belongs in secrets, whereas non-sensitive values can live in variables. Both can be defined at the repository, organization, or environment level. Use environments when you need deployment gates, manual approvals, or differentiated credentials for staging vs. production.
The
03-core-features--07-secrets-and-variables.yaml
workflow demonstrates injecting each tier into the runtime shell. GitHub automatically masks secret values in the logs—if you
echo
a secret, you will see ***
instead of the raw value. Variables, on the other hand, are printed as-is, so treat them with
care if they contain sensitive data.
When referencing secrets and variables in YAML you must use expression syntax (${{ secrets.MY_SECRET }}
and ${{ vars.MY_VARIABLE }}
) and typically assign
them to environment variables for the command that needs them.
jobs:
staging-environment:
runs-on: ubuntu-24.04
environment: staging
env:
# Inject repository-level secret & variable into the shell
EXAMPLE_REPOSITORY_SECRET: ${{ secrets.EXAMPLE_REPOSITORY_SECRET }}
EXAMPLE_REPOSITORY_VARIABLE: ${{ vars.EXAMPLE_REPOSITORY_VARIABLE }}
# Inject environment-level items into the shell
EXAMPLE_ENVIRONMENT_SECRET: ${{ secrets.EXAMPLE_ENVIRONMENT_SECRET }}
EXAMPLE_ENVIRONMENT_VARIABLE: ${{ vars.EXAMPLE_ENVIRONMENT_VARIABLE }}
steps:
- name: Inspect values inside job
run: |
echo "Repo secret (masked): $EXAMPLE_REPOSITORY_SECRET"
echo "Repo variable: $EXAMPLE_REPOSITORY_VARIABLE"
echo "Env secret (masked): $EXAMPLE_ENVIRONMENT_SECRET"
echo "Env variable: $EXAMPLE_ENVIRONMENT_VARIABLE"
production-environment:
runs-on: ubuntu-24.04
environment: production
env:
# Inject repository-level secret & variable into the shell
EXAMPLE_REPOSITORY_SECRET: ${{ secrets.EXAMPLE_REPOSITORY_SECRET }}
EXAMPLE_REPOSITORY_VARIABLE: ${{ vars.EXAMPLE_REPOSITORY_VARIABLE }}
# Inject environment-level items into the shell
EXAMPLE_ENVIRONMENT_SECRET: ${{ secrets.EXAMPLE_ENVIRONMENT_SECRET }}
EXAMPLE_ENVIRONMENT_VARIABLE: ${{ vars.EXAMPLE_ENVIRONMENT_VARIABLE }}
steps:
- name: Inspect values inside job
run: |
echo "Repo secret (masked): $EXAMPLE_REPOSITORY_SECRET"
echo "Repo variable: $EXAMPLE_REPOSITORY_VARIABLE"
echo "Env secret (masked): $EXAMPLE_ENVIRONMENT_SECRET"
echo "Env variable: $EXAMPLE_ENVIRONMENT_VARIABLE"
Many configuration values are exposed through contexts, structured data that you reference with the ${{ ... }}
expression
syntax. Common contexts include:
github
: metadata about the repository, commit, event, and actor.env
: the environment variables available at the current scope.secrets
and vars
: user-managed configuration.needs
: outputs from prerequisite jobs.Expressions let you perform simple logic (if: ${{ github.ref == 'refs/heads/main' }}
), interpolate values, and compose dynamic
strings for commands or notifications.
The full set of contexts can be found here: Reference Docs