Avoid workflow loops on GitHub Actions when committing to a protected branch.
Continuous Deployment (CD) is part of almost every modern application development workflow. Since the software is deployed using an automated CD workflow, it only makes sense to also auto-increment at least the patch version or build number.
This can be easily achieved on GitHub Actions
with a workflow that will
- Check out the latest code on the current branch.
- Increment the patch version or the build number or use any other strategy (like semantic versioning) to update the current version.
- Build the application and deploy it to production (or any appropriate environment).
- Commit changes made to the version to the same branch.
name: CD
on:
push:
branches:
- main
jobs:
deploy:
name: Deploy to PROD
runs-on: ubuntu-latest
steps:
# Checkout current branch
- uses: actions/checkout@v2
# Increment the patch version / build number.
- name: Bump Version
run: scripts/bump-build-number.sh
# Build and deploy app to production.
- name: Build and Deploy
run: ...
# After a successful deployment, commit changes made to the version.
- name: Commit Version Change
run: |
git config user.name "Github Actions CD"
git config user.email "<>"
git add --all
git commit -m "Bump Version to $NEW_VERSION"
git push origin main
Here we have a workflow that is triggered by a push on the main branch. In this workflow, we are creating a commit and pushing it to the main branch.
Since this workflow is triggered on push to the main
branch and the workflow itself pushed to the main
branch, we should have ended with a workflow that continuously triggers itself. Why does that not happen?
It is because GitHub Actions generates a GITHUB_TOKEN for each workflow run. This GITHUB_TOKEN is used to set up git on the workflow. Any changes made using that token do not trigger the workflow.
Committing to Protected Branches
Branches that trigger important workflows like CD usually need to be protected so that we do not end up releasing any unintended changes to the public. We can protect these branches using the Branch Protection Rules and add the Required Pull Request Reviews rule.
Running the above workflow on a protected branch will fail with an error saying that pushing to the protected branch failed.
To fix this, we can edit the Branch Protection Rules to allow specific users to commit to the protected branch.
Now we can create a Personal Access Token for the user allowed to bypass the rules and save that token to GitHub Secrets.
Update the CD workflow to use this new token instead of the default
GITHUB_TOKEN
.
name: CD
on:
push:
branches:
- main
jobs:
deploy:
name: Deploy to PROD
runs-on: ubuntu-latest
steps:
# Checkout current branch
- uses: actions/checkout@v2
with:
token: ${{ secrets.PROTECTED_BRANCH_PUSH_TOKEN }}
...
Using the PROTECTED_BRANCH_PUSH_TOKEN allows us to push to a protected branch for the workflow. But since we are no longer using the GITHUB_TOKEN, any commits made to the branch the workflow is triggered on, will re-trigger this workflow. We end up in an infinite loop.
Preventing Workflow Loops
Similar to the GITHUB_TOKEN
, we need a way to stop the re-triggering of the same workflow.
This is where we can leverage the ability of Github Actions to skip workflow runs if it detects specific commands in the commit message.
By adding any of the following commands to our commit message, the workflow triggered on push will not run for that commit.
[skip ci]
[ci skip]
[no ci]
[skip actions]
[actions skip]
We can update the workflow so that the commit message includes one of these commands and we do not end up in an infinite loop.
name: CD
on:
push:
branches:
- main
jobs:
deploy:
name: Deploy to PROD
runs-on: ubuntu-latest
steps:
# Checkout current branch
- uses: actions/checkout@v2
with:
token: ${{ secrets.PROTECTED_BRANCH_PUSH_TOKEN }}
...
# After successful deployment, commit changes made to version.
- name: Commit Version Change
run: |
git config user.name "Github Actions CD"
git config user.email "<>"
git add --all
git commit -m "[skip ci] Bump Version to $NEW_VERSION"
git push origin main
In Summary
To push to a protected branch from CD
- Update the Branch Protection Rule to allow specific users to bypass the protection.
- Create a Personal Access Token(PAT) for that user with access to the repository.
- Use the PAT in the CD workflow.
- Use commands like
[skip ci]
in the commit message to skip workflow run for that commit.