Github Branch Protection rules vs. workflow permissions - github

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?

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

How to make Github Actions workflow that is triggered by a pr comment to be reported in the pr checks

I have a called workflow that deploys the app to Heroku. It is triggered by a pr comment.
name: Preview App Deployment
on:
issue_comment:
types: [created]
jobs:
call-heroku-preview-app:
name: Deploy to Heroku
if: ${{ contains(github.event.comment.body, '/deploy') }}
uses: ./.github/workflows/heroku-preview-app.yml
However, it doesn't show up in the pr checks as it does for jobs that are triggered by
on:
pull_request:
types: [synchronize, opened, reopened, ready_for_review]
branches-ignore: [master, production]
which hurts visibility. Also, I'm planning to add another workflow triggered by a comment that runs unit and e2e tests. If the result won't be included in the Github checks it will be a problem.
So is it really impossible to include them there or I'm missing something?
PR Checks are attached to a commit, but when you're running a workflow on issue_comment what would the context be? Intuitively, comment in a PR is not a thing related to a commit.
In fact, when looking at ${{ github.sha }} inside a workflow triggered by issue_comment, the SHA is actually that of the default branch.
You could manually add a check using the Check Runs API.
However, when triggering this on issue_comment, you're going to have to replace the value github.sha with your own logic to identify the tip of the branch associated with the PR for which the comment was made.
- uses: actions/checkout#v3
- run: echo ${{ secrets.GITHUB_TOKEN }} | gh auth login --with-token
- run: echo '{"name":"Test Check","head_sha":"${{ github.sha }}"}' | gh api /repos/{owner}/{repo}/check-runs --input -
Is there are reason you're only deploying when a comment is made? Why not deploy on every push? As you mentioned end-to-end tests as well, this would be another type of thing that is usually run automatically rather than when a person adds a comment.

Ensure that a workflow in Github Actions is only ever triggered once

I have workflow that is triggered when a specific file is changed. Is it possible to ensure that this workflow is only triggered the first time that file is changed?
The use case is:
a repository is created from a template repository
to initialize the README and other things in the repo, some variables can be set in a JSON config file
once that file is committed, the workflow runs, creates the README from a template etc.
I have tried to do let the workflow delete itself before it commits and pushes the changed files. That doesn't work: fatal: not in a git directory and fatal: unsafe repository ('/github/workspace' is owned by someone else).
What might work is adding a topic like initialized to the repo at the end of the workflow and check for the presence of this topic at the beginning of the workflow. However, this is feels like a hack in that I'm abusing topics for something they're probably not meant to do.
So my question remains: is there a good way to only run the workflow once?
With the help of the comment by frennky I managed to do solve this by using the GitHub CLI to disable the workflow.
Here is the step at the end of the workflow that does this:
- name: Disable this workflow
shell: bash
run: |
gh workflow disable -R $GITHUB_REPOSITORY "${{ github.workflow }}"
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

Using env variable github.ref_name doesn't give me branch name

When I use in my workflow github.ref_name it doesn't provide me a branch name from where I triggered this workflow. I get 16/merge. Why? When I use github.ref_type I clearly see that my ref type is branch so why I don't get branch name?
Also it's showing when I use $GITHUB_REF or git symbolic-ref HEAD (and separating refs/heads/ off). Ah and I tried github.event.push.ref but it's not showing me anything.
How to get always branch name from where I triggered the workflow?
For following code:
Run echo running on branch ${GITHUB_REF##*/} ${GITHUB_REF}
When your workflow runs becuase of push event you will get:
running on branch main refs/heads/main
But for pulr request event it would be:
running on branch merge refs/pull/3/merge
Why's that?
If the repository is on GitHub and you have any Pull Requests that have been opened, you’ll get these references that are prefixed with refs/pull/. These are basically branches, but since they’re not under refs/heads/ you don’t get them normally when you clone or fetch from the server — the process of fetching ignores them normally.
You can also check this question here

GitHub action branch creation for code review pull request

I'm trying to create a GitHub workflow that will run ONLY when a new branch is created with a pattern. The purpose of this is to create a Code Review Pull Request when a new branch is pushed to origin, but only on the first time the branch is created, so using a push event will not work and why I'm looking at create.
All of these combinations fail where any new branch created will run, instead of those just matching the pattern
name: "Create Code Review PR"
on:
create:
branches: ['feature/**']
or
name: "Create Code Reivew PR"
on:
create:
branches:
- 'feature/**'
- 'support/**'
- 'hotfix/**'
In both of these scenarios, if push a new branch called no-code-review, the above workflow will still run, but my expected behavior is that it wont run, but it should when a new branch such as these: feature/new-branch, support/new-support-branch or hotfix/fix-this ONLY.
The create event does not support a branch filter.
The alternative would be using an if condition on your step or job:
if: ${{ contains(github.ref, 'refs/heads/releases/') }}
Here's more information: https://github.community/t/trigger-job-on-branch-created/16878/5

Can I filter a GitHub action step based on the commit message?

I've recently added a static analysis step to my GitHub actions. However, it is somewhat expensive so I'm trying to reduce the number of times that this step is run. I've already added an appropriate filter so that it only runs on one OS and so that it only runs on my "feature" branches. However, I would like to also filter out any checkins that include "WIP" in their commit message. (The theory being that there is no point in performing the full analysis until it is no longer a "Work In Progress".)
I've searched through the docs, expecting I would find an object I could use as part of the github context object, but to no avail.
Any ideas on how I can accomplish this goal?
If you want to see exactly what I'm doing, the action Yaml is as follows. I'm hoping to find some change I can make to the if statement on the Static Analysis item that would accomplish my goal.
name: On Push
on: [push]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout#v1
- name: Build
run: |
./configure
make -j 4
- name: Run tests
run: |
make -j 4 check
- name: Static Analysis
if: runner.os == 'macOS' && startsWith(github.ref, 'refs/heads/feature/')
run: |
make analyze
You can make use of the event property on the github context to access the push payload.
Add the following to the step that does the analysis:
if: !startsWith(github.event.head_commit.message, 'WIP')
Related to your issue, you now (Feb. 2021) have:
GitHub Actions: Skip pull request and push workflows
with [skip ci]
GitHub Actions now supports skipping push and pull_request workflows by looking for some common keywords in your commit message.
If any commit message in your push or the HEAD commit of your PR contains the strings [skip ci], [ci skip], [no ci], [skip actions], or [actions skip] workflows triggered on the push or pull_request events will be skipped.
This is not as flexible as a custom commit message (like your "WIP"), but it still can help.
if: ${{ !startsWith(github.event.head_commit.message, '#NORUN') }}
Syntax should be like this