1- name : Code Quality (PR-Mandatory)
1+ name : Mandatory PR Code Quality Checks
22
3- # Trigger explicitly for PRs + retain push events
3+ # Force trigger for ALL PR events + retain push (for post-merge validation)
44on :
55 push :
6- branches : [ main, master ]
6+ branches : [ main, master ] # Only for merged PR validation
77 pull_request :
88 branches : [ main, master ]
9- types : [ opened, synchronize, reopened ] # Trigger on PR create/update/reopen
9+ types : [ opened, synchronize, reopened, edited ] # Trigger on ANY PR change
1010
1111env :
1212 PYTHON_VERSION : ' 3.13.7'
1313
1414jobs :
15- # 1. PR-Adapted : Ruff Auto-Formatting (critical: commits to PR source branch)
16- ruff-auto-format-pr :
17- name : " 📝 Ruff Format (PR-Safe) "
15+ # 1. Mandatory PR Step : Ruff Auto-Format (pushes back to PR source branch)
16+ pr- ruff-auto-format :
17+ name : " 📝 PR: Ruff Auto-Format "
1818 runs-on : ubuntu-latest
1919 permissions :
20- contents : write # Required for auto-commits to PRs
21- pull-requests : read # Required to fetch PR branch info
20+ contents : write # Critical for pushing format fixes to PR
21+ pull-requests : write # Required to update PR status
2222 outputs :
2323 changes_made : ${{ steps.format-check.outputs.changes_made }}
2424 steps :
25- - name : Checkout PR Source Branch
25+ - name : Checkout PR SOURCE BRANCH (MANDATORY FOR PR)
2626 uses : actions/checkout@v4
2727 with :
28- token : ${{ secrets.GITHUB_TOKEN }} # Works for internal PRs; use PAT for forked PRs
28+ token : ${{ secrets.PR_ACCESS_PAT || secrets. GITHUB_TOKEN }} # Use PAT for forked PRs
2929 fetch-depth : 0
30- ref : ${{ github.head_ref }} # Force checkout PR source branch (not target main)
30+ ref : ${{ github.head_ref }} # MUST target PR source (not main)
3131 path : .
3232
3333 - name : Set up Python
3434 uses : actions/setup-python@v4
3535 with :
3636 python-version : ${{ env.PYTHON_VERSION }}
37- cache : ' pip' # Cache pip packages for faster installs
37+ cache : ' pip'
3838
3939 - name : Install ruff
4040 run : pip install ruff
4141 env :
42- PIP_DISABLE_PIP_VERSION_CHECK : 1 # Skip pip version check to speed up installs
42+ PIP_DISABLE_PIP_VERSION_CHECK : 1
4343
44- - name : Run ruff format & detect changes
44+ - name : Run format & detect changes
4545 id : format-check
4646 run : |
4747 ruff format .
4848 if git diff --quiet --exit-code; then
4949 echo "changes_made=false" >> $GITHUB_OUTPUT
5050 else
5151 echo "changes_made=true" >> $GITHUB_OUTPUT
52- git diff --name-only # Show modified files in PR logs for review
52+ git diff --name-only >> pr_format_changes.txt # Log changes for PR review
5353 fi
5454
55- - name : Auto-commit format changes to PR
55+ - name : Push fixes to PR source branch
5656 if : steps.format-check.outputs.changes_made == 'true'
5757 run : |
58- git config --local user.name "GitHub Actions (PR Format )"
59- git config --local user.email "pr-format @github.com"
58+ git config --local user.name "GitHub Actions (PR Bot )"
59+ git config --local user.email "pr-bot @github.com"
6060 git add .
61- git commit -m "[PR-auto] Fix code formatting with ruff"
62- git push # Pushes to PR source branch; PR updates automatically
63-
64- # 2. PR Control: Run checks only if PR has format changes or is merged
65- setup-checks-pr :
66- name : " ⚙️ Setup Tools (PR-Triggered)"
67- needs : ruff-auto-format-pr
68- # Condition: Run on push OR PR (with format changes OR merged status)
69- if : >
70- (github.event_name == 'push') ||
71- (github.event_name == 'pull_request' &&
72- (needs.ruff-auto-format-pr.outputs.changes_made == 'true' ||
73- github.event.pull_request.merged == true))
61+ git commit -m "[PR AUTO-FIX] Code formatting via ruff"
62+ git push # Updates PR automatically—no manual push needed
63+
64+ - name : Comment format changes on PR (MANDATORY VISIBILITY)
65+ if : steps.format-check.outputs.changes_made == 'true' && github.event_name == 'pull_request'
66+ uses : actions/github-script@v7
67+ with :
68+ script : |
69+ const changes = require('fs').readFileSync('pr_format_changes.txt', 'utf8');
70+ github.rest.issues.createComment({
71+ owner: context.repo.owner,
72+ repo: context.repo.repo,
73+ issue_number: context.issue.number,
74+ body: `🔄 Auto-formatting changes applied to these files:\n\`\`\`\n${changes}\n\`\`\``
75+ });
76+
77+ # 2. Mandatory PR Step: Setup tools (ONLY runs for PRs)
78+ pr-setup-tools :
79+ name : " ⚙️ PR: Setup Check Tools"
80+ needs : pr-ruff-auto-format
81+ if : github.event_name == 'pull_request' # MANDATORY: Only execute for PRs
7482 runs-on : ubuntu-latest
7583 steps :
76- - name : Checkout PR Source Branch
84+ - name : Checkout PR source branch
7785 uses : actions/checkout@v4
7886 with :
79- ref : ${{ github.head_ref || github.ref }} # Use PR source branch (or push branch)
87+ ref : ${{ github.head_ref }}
8088 path : .
8189
8290 - name : Set up Python
@@ -85,112 +93,100 @@ jobs:
8593 python-version : ${{ env.PYTHON_VERSION }}
8694 cache : ' pip'
8795
88- - name : Install check tools directly (no dependency files)
96+ - name : Install PR check tools
8997 run : pip install codespell bandit mypy ruff pytest
9098 env :
9199 PIP_DISABLE_PIP_VERSION_CHECK : 1
92100
93- # 3. PR Checks: All tools synced to PR "Checks" tab
94- spell-check-pr :
95- name : " 🔍 Spell Check (PR )"
96- needs : setup-checks-pr
101+ # 3. Mandatory PR Checks (all sync to PR "Checks" tab)
102+ pr- spell-check :
103+ name : " 🔍 PR: Spell Check (Non-Blocking )"
104+ needs : pr- setup-tools
97105 runs-on : ubuntu-latest
98106 steps :
99- - name : Checkout PR Source Branch
107+ - name : Checkout PR source branch
100108 uses : actions/checkout@v4
101109 with :
102110 ref : ${{ github.head_ref }}
103111 path : .
104- - name : Set up Python
105- uses : actions/setup-python@v4
106- with :
107- python-version : ${{ env.PYTHON_VERSION }}
108- cache : ' pip'
109- - name : Run codespell (Non-Blocking in PR)
112+ - name : Run codespell
110113 run : codespell --skip="*.json,*.lock,*.csv" --ignore-words-list="xxx,yyy,zzz" --quiet-level=2 || true
111114
112- security-check-pr :
113- name : " 🔒 Security Check (PR )"
114- needs : setup-checks-pr
115+ pr- security-check :
116+ name : " 🔒 PR: Security Check (Non-Blocking )"
117+ needs : pr- setup-tools
115118 runs-on : ubuntu-latest
116119 steps :
117- - name : Checkout PR Source Branch
120+ - name : Checkout PR source branch
118121 uses : actions/checkout@v4
119122 with :
120123 ref : ${{ github.head_ref }}
121124 path : .
122- - name : Set up Python
123- uses : actions/setup-python@v4
125+ - name : Run bandit
126+ run : bandit -r . -f human -o pr_bandit_results.txt -f json -o pr_bandit_results.json || true
127+ pr-security-check :
128+ name : " 🔒 PR: Security Check (Non-Blocking)"
129+ needs : pr-setup-tools
130+ runs-on : ubuntu-latest
131+ steps :
132+ - name : Checkout PR source branch
133+ uses : actions/checkout@v4
124134 with :
125- python-version : ${{ env.PYTHON_VERSION }}
126- cache : ' pip '
127- - name : Run bandit (Non-Blocking in PR)
128- run : bandit -r . -f human -o bandit-pr-results .txt -f json -o bandit-pr-results .json || true
135+ ref : ${{ github.head_ref }}
136+ path : .
137+ - name : Run bandit
138+ run : bandit -r . -f human -o pr_bandit_results .txt -f json -o pr_bandit_results .json || true
129139
130- type-check-pr :
131- name : " 🎯 Type Check (PR )"
132- needs : setup-checks-pr
140+ pr- type-check :
141+ name : " 🎯 PR: Type Check (Non-Blocking )"
142+ needs : pr- setup-tools
133143 runs-on : ubuntu-latest
134144 steps :
135- - name : Checkout PR Source Branch
145+ - name : Checkout PR source branch
136146 uses : actions/checkout@v4
137147 with :
138148 ref : ${{ github.head_ref }}
139149 path : .
140- - name : Set up Python
141- uses : actions/setup-python@v4
142- with :
143- python-version : ${{ env.PYTHON_VERSION }}
144- cache : ' pip'
145- - name : Run mypy (Non-Blocking in PR)
150+ - name : Run mypy
146151 run : mypy --ignore-missing-imports --show-error-codes . || true
147- lint-check-pr :
148- name : " 🧹 Lint Check (PR-Blocking)"
149- needs : setup-checks-pr
152+
153+ pr-lint-check :
154+ name : " 🧹 PR: Lint Check (BLOCKING)"
155+ needs : pr-setup-tools
150156 runs-on : ubuntu-latest
151157 steps :
152- - name : Checkout PR Source Branch
158+ - name : Checkout PR source branch
153159 uses : actions/checkout@v4
154160 with :
155161 ref : ${{ github.head_ref }}
156162 path : .
157- - name : Set up Python
158- uses : actions/setup-python@v4
159- with :
160- python-version : ${{ env.PYTHON_VERSION }}
161- cache : ' pip'
162- - name : Run ruff check (Blocking in PR: Fix lint errors first)
163- run : ruff check --output-format=concise .
163+ - name : Run ruff check
164+ run : ruff check --output-format=concise . # Fails PR if lint errors exist
164165
165- test-pr :
166- name : " 🧪 Unit Tests (PR-Blocking )"
167- needs : setup-checks-pr
166+ pr-unit-tests :
167+ name : " 🧪 PR: Unit Tests (BLOCKING )"
168+ needs : pr- setup-tools
168169 runs-on : ubuntu-latest
169170 steps :
170- - name : Checkout PR Source Branch
171+ - name : Checkout PR source branch
171172 uses : actions/checkout@v4
172173 with :
173174 ref : ${{ github.head_ref }}
174175 path : .
175- - name : Set up Python
176- uses : actions/setup-python@v4
177- with :
178- python-version : ${{ env.PYTHON_VERSION }}
179- cache : ' pip'
180- - name : Run pytest (Blocking in PR: Fix test failures first)
181- run : pytest
176+ - name : Run pytest
177+ run : pytest # Fails PR if test failures exist
182178
183- # 4. PR Security Analysis : CodeQL results synced to PR "Security" tab
184- codeql-pr :
185- name : " 🛡️ CodeQL (PR) "
186- needs : setup-checks-pr
179+ # 4. Mandatory PR Security: CodeQL (syncs to PR "Security" tab)
180+ pr-codeql :
181+ name : " 🛡️ PR: CodeQL Analysis "
182+ needs : pr- setup-tools
187183 runs-on : ubuntu-latest
188184 permissions :
189185 actions : read
190186 contents : read
191- security-events : write # Required to sync results to PR Security tab
187+ security-events : write # Mandatory for PR security alerts
192188 steps :
193- - name : Checkout PR Source Branch
189+ - name : Checkout PR source branch
194190 uses : actions/checkout@v4
195191 with :
196192 ref : ${{ github.head_ref }}
@@ -201,24 +197,22 @@ lint-check-pr:
201197 languages : python
202198 - name : Autobuild
203199 uses : github/codeql-action/autobuild@v2
204- - name : Perform CodeQL Analysis
200+ - name : Analyze
205201 uses : github/codeql-action/analyze@v2
206202
207- # 5. PR Summary: Clear status in PR "Checks" tab
208- pr-checks-summary :
209- name : " ✅ PR All Checks Summary "
210- needs : [spell-check-pr, security-check-pr, type-check-pr, lint-check-pr, test-pr, codeql-pr ]
211- if : always()
203+ # 5. Mandatory PR Step: Block invalid merges
204+ pr-merge-gate :
205+ name : " 🚫 PR: Merge Gate (MANDATORY) "
206+ needs : [pr- spell-check, pr- security-check, pr- type-check, pr- lint-check, pr-unit-tests, pr-codeql ]
207+ if : github.event_name == 'pull_request'
212208 runs-on : ubuntu-latest
213209 steps :
214- - name : Print PR Check Summary
210+ - name : Check PR validity
215211 run : |
216- echo "PR Source Branch: ${{ github.head_ref }}"
217- echo "Formatting Changes Applied: ${{ needs.ruff-auto-format-pr.outputs.changes_made }}"
218- # Block PR merge if critical checks (lint/tests) fail
219- if [[ "${{ contains(needs.lint-check-pr.result, 'failure') || contains(needs.test-pr.result, 'failure') }}" == "true" ]]; then
220- echo "❌ Critical PR Checks Failed (lint/tests) - Fix Before Merging"
212+ # Block merge if ANY blocking check fails
213+ if [[ "${{ contains(needs.pr-lint-check.result, 'failure') || contains(needs.pr-unit-tests.result, 'failure') || contains(needs.pr-codeql.result, 'failure') }}" == "true" ]]; then
214+ echo "❌ PR CANNOT be merged: Blocking checks (lint/tests/CodeQL) failed."
221215 exit 1
222216 else
223- echo "✅ Critical PR Checks Passed - Non- blocking issues (spelling/type) are optional to fix "
217+ echo "✅ PR is merge-ready: All blocking checks passed. "
224218 fi
0 commit comments