I have 2 Github Actions workflows in my repository and one of the steps requires getting all the files that have been modified (except deleted files) in a PR. I use this in the first one:
on:
pull_request:
branches: [ main ]
jobs:
get_files:
name: run_on_pr
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@master
with:
fetch-depth: 0
- name: Modified files
run: |
git fetch origin main:main
git diff --name-only --diff-filter=d main~ main
This one works okay and I am able to get a list of all the files that have been modified. However, in the second workflow, which is supposed to run when the PR is merged, this does not work.
on:
push:
branches: [ main ]
jobs:
get_files:
name: run_when_pr_is_merged
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@master
with:
fetch-depth: 0
- name: Modified files
run: |
git fetch origin main:main
git diff --name-only --diff-filter=d main~ main
I get the error
fatal: refusing to fetch into branch 'refs/heads/main' checked out at '/home/runner/....'
. I think the error is coming from the git fetch origin main:main
as the workflow is running off the main
branch and I am trying to do a fetch in there. I have removed that but still didn't get the list I needed. Any help or better way to get the list of modified files in a PR in both workflows?
CodePudding user response:
It's a bit more complicated than this.
For PR's GitHub action/checkout
is created a detached head which is simulating a merge of the PR into the target branch. You can see it in the logs of the checkout action itself. You can alter this behavior by using a different ref, but I don't recommend it - it's actually making things easier, especially for forked PRs.
To get a list of changed files in PR, you just have to checkout with fetch-depth: 2 to get previous commits and then get files modified by a merge:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 2
- name: Get changes
run: git diff --name-only -r HEAD^1 HEAD
For push
events, it's also a bit more complicated as you can have multiple commits in single push, so here you have to fetch-depth: 0 and then use the GitHub context values to figure out the difference for the push
:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Get changes
run: git diff --name-only ${{ github.event.before }} ${{ github.event.after }}
If you want a single workflow to handle both, you can do something like this:
- uses: actions/checkout@v3
with:
fetch-depth: ${{ github.event_name == 'pull_request' && 2 || 0 }}
- name: Get changed files
id: changed-files
run: |
if ${{ github.event_name == 'pull_request' }}; then
echo "changed_files=$(git diff --name-only -r HEAD^1 HEAD | xargs)" >> $GITHUB_OUTPUT
else
echo "changed_files=$(git diff --name-only ${{ github.event.before }} ${{ github.event.after }} | xargs)" >> $GITHUB_OUTPUT
fi
- name: List changed files
run: |
for file in ${{ steps.changed-files.outputs.changed_files }}; do
echo "$file was changed"
done