@@ -2,11 +2,24 @@ name: Get Changed Files
22
33on :
44 workflow_call :
5+ inputs :
6+ include-push-diff :
7+ description : |
8+ When true, on push events the output is the diff between
9+ `github.event.before` and `github.sha` (computed via the
10+ GitHub Compare API). Default is false: push events emit '*',
11+ matching the historical behavior.
12+ type : boolean
13+ required : false
14+ default : false
515 outputs :
616 changed-files :
7- description : " List of changed files (space-separated) or '*' if not in a PR "
17+ description : " Space-separated list of changed files for PR events (and push events when include-push-diff=true); '*' otherwise. "
818 value : ${{ jobs.get-changed-files.outputs.changed-files }}
919
20+ permissions :
21+ contents : read
22+
1023jobs :
1124 get-changed-files :
1225 runs-on : ubuntu-latest
@@ -18,26 +31,65 @@ jobs:
1831 id : get-files
1932 env :
2033 GH_TOKEN : ${{ github.token }}
34+ INCLUDE_PUSH_DIFF : ${{ inputs.include-push-diff }}
2135 run : |
22- # Check if we're in a pull request context
23- if [ "${{ github.event_name }}" = "pull_request" ] || [ "${{ github.event_name }}" = "pull_request_target" ]; then
24- echo "Running in PR context"
36+ set -eu
2537
26- # Get the PR number from the github context
27- PR_NUMBER ="${{ github.event.number }}"
38+ EVENT_NAME="${{ github.event_name }}"
39+ REPO ="${{ github.repository }}"
2840
29- # Use gh CLI to get changed files in the PR with explicit repo
30- CHANGED_FILES=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER/files --paginate --jq '.[] | select(.status != "removed") | .filename' | tr '\n' ' ' | sed 's/ $//')
41+ # PR context: list files modified by the PR.
42+ if [ "$EVENT_NAME" = "pull_request" ] || [ "$EVENT_NAME" = "pull_request_target" ]; then
43+ echo "Running in PR context"
44+ PR_NUMBER="${{ github.event.number }}"
45+ CHANGED_FILES=$(gh api "repos/$REPO/pulls/$PR_NUMBER/files" --paginate \
46+ --jq '.[] | select(.status != "removed") | .filename' | tr '\n' ' ' | sed 's/ $//')
3147
3248 if [ -z "$CHANGED_FILES" ]; then
3349 echo "No changed files found, setting to '*'"
3450 CHANGED_FILES="*"
3551 fi
36-
3752 echo "Changed files: $CHANGED_FILES"
3853 echo "changed-files=$CHANGED_FILES" >> "$GITHUB_OUTPUT"
54+ exit 0
55+ fi
3956
40- else
41- echo "Not in PR context, setting changed files to '*'"
42- echo "changed-files=*" >> "$GITHUB_OUTPUT"
57+ # Push context with opt-in: diff between previous tip and new
58+ # tip via the GitHub Compare API. This is what lets path-
59+ # filtered jobs skip on push commits that don't touch their
60+ # relevant paths. Callers must explicitly request this with
61+ # `include-push-diff: true` because some workflows (e.g.
62+ # lint.yml) historically rely on the '*' value to take a
63+ # broader code path.
64+ if [ "$EVENT_NAME" = "push" ] && [ "$INCLUDE_PUSH_DIFF" = "true" ]; then
65+ BEFORE="${{ github.event.before }}"
66+ AFTER="${{ github.sha }}"
67+ ZERO_SHA="0000000000000000000000000000000000000000"
68+
69+ if [ -z "$BEFORE" ] || [ "$BEFORE" = "$ZERO_SHA" ]; then
70+ echo "No 'before' SHA on push event (tag/branch creation or initial push); setting changed files to '*'"
71+ echo "changed-files=*" >> "$GITHUB_OUTPUT"
72+ exit 0
73+ fi
74+
75+ echo "Running in push context: comparing $BEFORE..$AFTER"
76+ CHANGED_FILES=$(gh api "repos/$REPO/compare/$BEFORE...$AFTER" --paginate \
77+ --jq '.files[]? | select(.status != "removed") | .filename' 2>/dev/null \
78+ | tr '\n' ' ' | sed 's/ $//' || echo "")
79+
80+ if [ -z "$CHANGED_FILES" ]; then
81+ echo "Compare returned empty; setting changed files to '*'"
82+ echo "changed-files=*" >> "$GITHUB_OUTPUT"
83+ exit 0
84+ fi
85+
86+ echo "Changed files: $CHANGED_FILES"
87+ echo "changed-files=$CHANGED_FILES" >> "$GITHUB_OUTPUT"
88+ exit 0
4389 fi
90+
91+ # Default for non-PR events (push without opt-in,
92+ # workflow_dispatch, schedule, etc.): no diff. Emit '*' to
93+ # preserve the historical behavior.
94+ echo "Event '$EVENT_NAME' (or include-push-diff=false): emitting '*'"
95+ echo "changed-files=*" >> "$GITHUB_OUTPUT"
0 commit comments