Skip to content

Commit 0c7eb0f

Browse files
authored
Merge pull request #43 from marklogic/feat/PDP-1182-copyright-check-org-ruleset-support
PDP-1182: Add pull_request_target trigger to copyright-check.yml for org ruleset support
2 parents f2c1edd + 500c96b commit 0c7eb0f

File tree

1 file changed

+38
-21
lines changed

1 file changed

+38
-21
lines changed

.github/workflows/copyright-check.yml

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
name: Copyright Validation
22

33
on:
4+
# Direct trigger for org-level repository rulesets — works for PRs from forks
5+
# since pull_request_target runs with base repo context. The GITHUB_TOKEN is
6+
# explicitly scoped to least privilege in the job permissions block below.
7+
# Fork code is only read as text by the trusted copyrightcheck.py script;
8+
# no fork code is executed. Worst case from a malicious .copyrightconfig
9+
# is the check passes (policy bypass), not code execution.
10+
pull_request_target:
11+
types: [opened, edited, synchronize, reopened]
12+
13+
# Also support being called as a reusable workflow from individual repos
414
workflow_call:
515

616
jobs:
@@ -9,19 +19,23 @@ jobs:
919
runs-on: ubuntu-latest
1020
permissions:
1121
contents: read
12-
pull-requests: write
22+
pull-requests: read
23+
# issues: write is needed for the PR comment step (workflow_call path only).
24+
# pull_request_target skips that step, but GitHub Actions has no per-event
25+
# conditional permissions within a single job — splitting into two jobs would
26+
# require artifact sharing and add significant complexity for minimal gain.
1327
issues: write
1428

1529
steps:
1630
- name: Checkout PR head
17-
uses: actions/checkout@v4
31+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
1832
with:
19-
ref: ${{ github.event.pull_request.head.sha }}
33+
ref: refs/pull/${{ github.event.pull_request.number }}/head
2034
path: target-repo
2135
persist-credentials: false
2236

2337
- name: Checkout pr-workflows repo
24-
uses: actions/checkout@v4
38+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
2539
with:
2640
repository: ${{ github.repository_owner }}/pr-workflows
2741
ref: main
@@ -47,21 +61,22 @@ jobs:
4761
echo "config-file=$cfg" >> $GITHUB_OUTPUT
4862
4963
- name: Set up Python
50-
uses: actions/setup-python@v4
64+
uses: actions/setup-python@7f4fc3e22c37d6ff65e88745f38bd3157c663f7c # v4
5165
with:
5266
python-version: '3.11'
5367

5468
- name: Get changed files
5569
id: changed-files
5670
env:
57-
BASE_SHA: ${{ github.event.pull_request.base.sha }}
58-
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
59-
BASE_REF: ${{ github.event.pull_request.base.ref }}
71+
PR_NUMBER: ${{ github.event.pull_request.number }}
72+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
73+
GH_REPO: ${{ github.repository }}
6074
run: |
61-
cd target-repo
62-
git fetch origin "$BASE_REF"
63-
git diff --name-only --diff-filter=AMR "$BASE_SHA" "$HEAD_SHA" | while read f; do [ -f "$f" ] && echo "$f"; done > ../files_to_check.txt
64-
count=$(wc -l < ../files_to_check.txt | tr -d ' ')
75+
gh api "repos/${GH_REPO}/pulls/${PR_NUMBER}/files" --paginate \
76+
--jq '.[].filename' | while IFS= read -r f; do
77+
if [ -f "target-repo/$f" ]; then echo "$f"; fi
78+
done > files_to_check.txt
79+
count=$(wc -l < files_to_check.txt | tr -d ' ')
6580
if [ "$count" -eq 0 ]; then echo "skip-validation=true" >> $GITHUB_OUTPUT; else echo "skip-validation=false" >> $GITHUB_OUTPUT; fi
6681
echo "files-count=$count" >> $GITHUB_OUTPUT
6782
@@ -70,18 +85,17 @@ jobs:
7085
if: steps.changed-files.outputs.skip-validation != 'true'
7186
continue-on-error: true
7287
env:
73-
COPYRIGHT_CHECK_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
7488
CONFIG_FILE: ${{ steps.setup-config.outputs.config-file }}
7589
run: |
7690
script="pr-workflows/scripts/copyrightcheck.py"
7791
cfg="$CONFIG_FILE"
7892
[ -f "$script" ] || { echo "script missing"; exit 1; }
7993
chmod +x "$script"
80-
files=$(tr '\n' ' ' < files_to_check.txt)
81-
python3 "$script" --config "$cfg" --working-dir target-repo $files > validation_output.txt 2>&1
82-
ec=$?
83-
if [ $ec -eq 0 ]; then echo "status=success" >> $GITHUB_OUTPUT; else echo "status=failed" >> $GITHUB_OUTPUT; fi
84-
exit $ec
94+
export COPYRIGHT_CHECK_COMMIT_SHA=$(git -C target-repo rev-parse HEAD)
95+
python3 "$script" --config "$cfg" --working-dir target-repo \
96+
--files-from-stdin < files_to_check.txt > validation_output.txt 2>&1 \
97+
&& echo "status=success" >> "$GITHUB_OUTPUT" \
98+
|| { echo "status=failed" >> "$GITHUB_OUTPUT"; exit 1; }
8599
86100
- name: Extract Markdown summary
87101
if: always() && steps.changed-files.outputs.skip-validation != 'true'
@@ -94,8 +108,11 @@ jobs:
94108
95109
- name: Post / Update PR comment with summary
96110
id: pr-comment
97-
if: always() && steps.changed-files.outputs.skip-validation != 'true'
98-
uses: actions/github-script@v7
111+
# workflow_call: token is scoped to the calling repo — write access works.
112+
# pull_request_target (org ruleset): token is read-only for the triggering repo —
113+
# createComment/updateComment will 403. Skip and rely on Job Summary instead.
114+
if: always() && steps.changed-files.outputs.skip-validation != 'true' && github.event_name == 'workflow_call'
115+
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
99116
env:
100117
VALIDATION_STATUS: ${{ steps.validate.outputs.status }}
101118
with:
@@ -138,7 +155,7 @@ jobs:
138155
if [ "$COMMENT_ACTION" = "updated" ] || [ "$COMMENT_ACTION" = "created" ]; then
139156
echo "::error title=Copyright Validation Failed::See the $COMMENT_ACTION PR comment for detailed results.";
140157
else
141-
echo "::error title=Copyright Validation Failed::See the PR comment (unavailable or failed to post).";
158+
echo "::error title=Copyright Validation Failed::Copyright headers are missing or invalid — see the Job Summary for details.";
142159
fi
143160
exit 1
144161
fi

0 commit comments

Comments
 (0)