Github Branch Protection rules vs. workflow permissions

Viewed 209

I would like to implement the rule that every (human) user has to open a PR to change something on the protected branch while the workflow to release the new version is able to increase the version number on that same protected branch.

The branch protection is in place, also the "include administrators" checkbox is checked. So noone can accidentially push to that branch.

Now, when I want to push something from a workflow I get the same error message that I get as a user

name: Build pipeline
"on":
  push:
    branches:
    - 'master'
defaults:
  run:
    shell: bash
jobs:
  release:
    runs-on:
    - self-hosted
    - default-runner
    needs: []
    steps:
    - name: Checkout code
      uses: actions/checkout@v2
      with:
        fetch-depth: 0
        clean: true
    - name: demo push
      if: github.ref == 'refs/heads/dev'
      run: |
        git config --global user.email "runner@xxx.com"
        git config --global user.name "Github Actions Runner"
        
        # normally we would generate the release notes here etc, increase the version,... though lets keep the example simple
        date >> test.txt
        git add test.txt
        git commit -m "test2" test.txt
        git push

while setting up the job, the permission of the job are printed out:

GITHUB_TOKEN Permissions
  Actions: write
  Checks: write
  Contents: write
  Deployments: write
  Discussions: write
  Issues: write
  Metadata: read
  Packages: write
  PullRequests: write
  RepositoryProjects: write
  SecurityEvents: write
  Statuses: write

and then the step fails with the following output:

user.email=runner@xxx.com
user.name=Github Actions Runner
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
remote.origin.url=https://github.xxx.com/xx/xxx
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
gc.auto=0
http.https://github.xxx.com/.extraheader=AUTHORIZATION: basic ***
branch.dev.remote=origin
branch.dev.merge=refs/heads/dev
[dev 7ddff59] test2
 1 file changed, 1 insertion(+)
 create mode 100644 test.txt
remote: error: GH006: Protected branch update failed for refs/heads/dev.        
remote: error: You're not authorized to push to this branch. Visit https://docs.github.com/enterprise/3.2/articles/about-protected-branches/ for more information.        
To https://github.xxx.com/xx/xxx
 ! [remote rejected] dev -> dev (protected branch hook declined)
error: failed to push some refs to 'https://github.xxx.com/xx/xxx'
Error: Process completed with exit code 1.

So the question is: how can I enforce the human users to open a PR, let it reviewed and checked before it can be merged, while the workflows can directly manipulate the (protected) branch?

1 Answers

After some research I found out that there is currenly (June 2022) no straight forward solution (source).

There are two workarounds. One is to remove the zranch protection in the workflow and restore it afterwards. The risk is that the workflow breaks in between (e.g. when the workflow runner crashes) and the branch stays unprotected. Another risk is that it could happen that a user accidentially or deliberately pushes to the now unprotected branch. (E.g. trigger a release build wait until the branch protection is removed and push to that branch).

The alternative solution is to remove the admin rights from the human users so that they need a PR to change the protected branch. During a release build a PAT of a technical user with admin rights is used (not ${{ secrets.GITHUB_TOKEN }}). In the branch protection the "enforce admins" option is disabled.

Though there is a feature request covering this topic.

Related