I have a workflow with several jobs which perform deployment tasks. I use matrix strategy to deploy them across multiple environments, say dev
, tst
and stg
. So each job has strategy configured as below
strategy:
matrix:
target_env: ${{ fromJson(needs.determine_target_env.outputs.target_env) }}
Also, I need someone to review deployments if the environment it is being deployed to is tst or stg (no review required for dev). I have created environments in GitHub as test-environment
and stg-environment
and use them as below
environment: >-
${{
matrix.target_env == 'tst' && 'test-environment' ||
matrix.target_env == 'stg' && 'stage-environment' ||
''
}}
This triggers approvals and reviewrs can approve/reject deployment. Here is the issue with this:
Since I have several jobs, it keeps asking for approvals for all of them
If I move the environment to a separate job which executed first (see below), then there is no way of capturing the output of that rejection or approval, because when rejected, the workflow just fails and if I provide a continue-on-error
to that job, it marks it as success
even if it rejected.
environment_approval:
needs: determine_target_env
runs-on: [default]
strategy:
matrix:
target_env: ${{ fromJson(needs.determine_target_env.outputs.target_env) }}
fail-fast: false
continue-on-error: true //If this is not set, rejections marks this job as failure. If it is set, the job is always a success.
outputs:
approval_status: ${{ steps.check_approval.outcome }}
environment: >-
${{
matrix.target_env == 'tst' && 'test-environment' ||
matrix.target_env == 'stg' && 'stage-environment' ||
''
}}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Check approval status
continue-on-error: true
id: check_approval
run: |
echo "status=success" >> $GITHUB_OUTPUT
Is there a central way of handling deployment protection and applying it to all jobs within a workflow?
I also tried to use
environment:
contextOK, I ended up solving it myself. I understand this is a unique case of using environments with deployment protection and then also using strategy:matrix
, but here is the solution if anyone is interested .
Have another task in the same job (environment-approval
)job which queries GH API deployments
Something like this
DEPLOYMENTS_JSON=$(curl -s -H "Authorization: token ${{ steps.generate-token.outputs.token }}" \
https://api.github.com/repos/${{ github.repository }}/deployments)
DEPLOYMENT_ID=$(echo "$DEPLOYMENTS_JSON" | jq -r --arg DEPLOYMENT_NAME "$DEPLOYMENT_NAME" 'map(select(.environment == $DEPLOYMENT_NAME)) | sort_by(.created_at) | last | .id // empty')
if [ -n "$DEPLOYMENT_ID" ]; then
DEPLOYMENT_STATUS=$(curl -s -H "Authorization: token ${{ steps.generate-token.outputs.token }}" \
https://api.github.com/repos/${{ github.repository }}/deployments/$DEPLOYMENT_ID/statuses | \
jq -r '.[0].state') # Get the state of the most recent status
echo "Deployment ID: $DEPLOYMENT_ID"
echo "Deployment Status: $DEPLOYMENT_STATUS"
if [[ "$DEPLOYMENT_STATUS" == "success" || "$DEPLOYMENT_STATUS" == "in_progress" ]]; then
echo "approved" > ${{ matrix.target_env }}_approval_status.txt
else
echo "rejected" > ${{ matrix.target_env }}_approval_status.txt
fi
else
echo "Deployment not found."
exit 1
fi
Output this into a file and have another job following this to process these files into respective environment related variable (You need another job because the latest run overrides the variable set part of strategy:matrix
.
Use this new variable in all subsequent jobs to validate deployment approvals.
Hope this helps