Building a Job Using Different Secrets per Environment in a Single GitHub Actions Workflow File
Introduction
When setting up workflows in GitHub Actions, there are cases where you want to create workflows for both the production and staging environments, but the only difference is the secret values, while the workflow content remains the same.
To make the workflow more efficient and reusable, I needed to use something I hadn’t tried before: reusable workflows. Here’s a record of how I implemented it.
Method
As an example, let’s say you have a workflow for deploying to both the production and staging environments, where the deployment process is the same, but the destination is controlled by the secret values. The keys for each secret are as follows:
- Production environment:
secrets.DEPLOY_KEY_PRODUCTION
- Staging environment:
secrets.DEPLOY_KEY_STAGING
If you were to naively construct this workflow, the following patterns could emerge:
- Use
if
statements within the same workflow file to switch secrets - Create separate workflow files like
deploy-production.yml
anddeploy-staging.yml
for each environment
Both methods can work, but the former increases the number of lines, making the control logic more complex and harder to read. The latter increases the number of duplicated files, making changes and management more cumbersome.
To address these issues, I wanted to use the same workflow while allowing different secrets to be used under the same key. This can be achieved using reusable workflows, which allow you to share common processes across multiple workflows.
First, I create the usual workflow, triggered by a push to either the main
or develop
branches.
name: Deploy
on:
push:
branches:
- main
- develop
jobs:
staging:
if: github.ref == 'refs/heads/develop'
uses: ./.github/workflows/_deploy.yml
with:
environment: staging
secrets:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY_STAGING }}
production:
if: github.ref == 'refs/heads/main'
uses: ./.github/workflows/_deploy.yml
with:
environment: production
secrets:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY_PRODUCTION }}
Here, I call the jobs for the staging
and production
environments, controlling them with if
statements. The staging
job is triggered when a push is made to the develop
branch, and the production
job is triggered when a push is made to the main
branch. Then, using uses: ./.github/workflows/_deploy.yml
, I call the reusable workflow. At this point, I pass the variables and secrets to be used for each environment using with
and secrets
.
The _deploy.yml
is configured as follows:
name: _Deploy
on:
workflow_call:
inputs:
environment:
type: string
description: Name of target environment
required: true
secrets:
DEPLOY_KEY:
description: Deploy key
required: true
jobs:
deploy:
runs-on: ubuntu-latest
steps:
run: |
echo ${{ inputs.environment }}
echo ${{ secrets.DEPLOY_KEY }}
In the workflow_call
, I set the trigger to allow the workflow to be called from another workflow. I also define the variables and secrets passed from the workflow using inputs
and secrets
.
With this setup, I can now use the separate secrets DEPLOY_KEY_PRODUCTION
and DEPLOY_KEY_STAGING
as DEPLOY_KEY
within the same workflow.
Conclusion
At first, I tried to use outputs
to absorb the differences in secrets for each environment and standardize the workflow. However, I found that secrets couldn’t be passed to the workflow, and even if I tried, it would have required encryption and decryption, which would have made the process overly complicated.
I realized that I still need more experience with GitHub Actions.