1- name : Python Checks
1+ name : Code Quality & Auto-Format Checks
22
3+ # Trigger on push to main/master or PRs targeting these branches
34on :
4- pull_request :
5- types : [opened, synchronize, reopened]
65 push :
7- branches :
8- - main
6+ branches : [ main, master ]
7+ pull_request :
8+ branches : [ main, master ]
99
10- concurrency :
11- group : ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
12- cancel-in-progress : true
10+ # Shared environment variables (avoid duplicate hardcoding)
11+ env :
12+ PYTHON_VERSION : ' 3.13.7 '
1313
1414jobs :
15- Test :
15+ # Phase 1: Auto-format with ruff (runs first, controls downstream jobs)
16+ ruff-auto-format :
17+ name : " 📝 Ruff Auto-Format (With Auto-Commit)"
18+ runs-on : ubuntu-latest
19+ # Grant write permission for auto-commit (critical for push)
20+ permissions :
21+ contents : write # Allows workflow to push formatting changes
22+ pull-requests : read # Optional: Reads PR info for branch targeting
23+ outputs :
24+ changes_made : ${{ steps.format-check.outputs.changes_made }} # Track if formatting changes were applied
25+ steps :
26+ - name : Checkout repository code
27+ uses : actions/checkout@v4
28+ with :
29+ token : ${{ secrets.GITHUB_TOKEN }} # Uses default token (works with 'contents: write' permission)
30+ fetch-depth : 0 # Required for full Git history (needed for commits)
31+ ref : ${{ github.head_ref || github.ref }} # Targets PR source branch (avoids merging to main directly)
32+
33+ - name : Set up Python ${{ env.PYTHON_VERSION }}
34+ uses : actions/setup-python@v4
35+ with :
36+ python-version : ${{ env.PYTHON_VERSION }}
37+ cache : ' pip' # Cache dependencies to speed up installs
38+
39+ - name : Install ruff (code formatter)
40+ run : pip install ruff
41+ env :
42+ PIP_DISABLE_PIP_VERSION_CHECK : 1 # Skip pip version check for faster installs
43+
44+ - name : Run ruff format & detect changes
45+ id : format-check
46+ run : |
47+ echo "Running ruff format to fix code styling..."
48+ ruff format . # Apply formatting fixes
49+
50+ # Check if any files were modified (avoids empty commits)
51+ if git diff --quiet --exit-code; then
52+ echo "changes_made=false" >> $GITHUB_OUTPUT
53+ echo "✅ No formatting issues found. No commit needed."
54+ else
55+ echo "changes_made=true" >> $GITHUB_OUTPUT
56+ echo "🔄 Formatting changes detected in these files:"
57+ git diff --name-only # List modified files for debugging
58+ fi
59+
60+ - name : Auto-commit & push formatting changes
61+ if : steps.format-check.outputs.changes_made == 'true'
62+ run : |
63+ # Configure Git committer info (required for commits)
64+ git config --local user.name "GitHub Actions (Ruff Format)"
65+ git config --local user.email "actions-ruff-format@github.com"
66+
67+ # Commit and push changes
68+ git add .
69+ git commit -m "[auto] style: Fix code formatting with ruff" # Clear commit message
70+ git push
71+ echo "✅ Formatting changes pushed successfully."
72+
73+ # Phase 2: Install check tools (runs only after valid ruff-format triggers)
74+ setup-check-tools :
75+ name : " ⚙️ Setup Code Check Tools"
76+ needs : ruff-auto-format # Depends on ruff-format completion
77+ # Trigger conditions:
78+ # - Run on direct pushes to main/master
79+ # - Run on PRs only if: 1) ruff made changes, OR 2) PR was merged
80+ if : >
81+ (github.event_name == 'push') ||
82+ (github.event_name == 'pull_request' &&
83+ (needs.ruff-auto-format.outputs.changes_made == 'true' ||
84+ github.event.pull_request.merged == true))
85+ runs-on : ubuntu-latest
86+ steps :
87+ - name : Checkout repository code
88+ uses : actions/checkout@v4
89+
90+ - name : Set up Python ${{ env.PYTHON_VERSION }}
91+ uses : actions/setup-python@v4
92+ with :
93+ python-version : ${{ env.PYTHON_VERSION }}
94+ cache : ' pip' # Reuse cache from ruff-format job
95+
96+ - name : Install all code check tools
97+ run : |
98+ pip install codespell bandit mypy ruff pytest
99+ env :
100+ PIP_DISABLE_PIP_VERSION_CHECK : 1
101+
102+ # Non-blocking check: Spell check (fails won't stop workflow)
103+ spell-check :
104+ name : " 🔍 Spell Check (Non-Blocking)"
105+ needs : setup-check-tools
106+ runs-on : ubuntu-latest
107+ steps :
108+ - name : Checkout repository code
109+ uses : actions/checkout@v4
110+
111+ - name : Set up Python ${{ env.PYTHON_VERSION }}
112+ uses : actions/setup-python@v4
113+ with :
114+ python-version : ${{ env.PYTHON_VERSION }}
115+ cache : ' pip'
116+
117+ - name : Run codespell (ignore common false positives)
118+ run : |
119+ codespell \
120+ --skip="*.json,*.lock,*.csv" \ # Skip non-code files
121+ --ignore-words-list="xxx,yyy,zzz" \ # Ignore custom false positives
122+ --quiet-level=2 || true # Non-blocking: continue if errors exist
123+
124+ # Non-blocking check: Security scan (fails won't stop workflow)
125+ security-scan :
126+ name : " 🔒 Security Scan (Non-Blocking)"
127+ needs : setup-check-tools
16128 runs-on : ubuntu-latest
17129 steps :
18- - name : Checkout repository
130+ - name : Checkout repository code
19131 uses : actions/checkout@v4
20132
21- - name : Set up Python
22- uses : actions/setup-python@v5
133+ - name : Set up Python ${{ env.PYTHON_VERSION }}
134+ uses : actions/setup-python@v4
23135 with :
24- python-version : ' 3.13.7'
136+ python-version : ${{ env.PYTHON_VERSION }}
137+ cache : ' pip'
25138
26- - name : Install all dependencies and tools
139+ - name : Run bandit (security linter for Python)
27140 run : |
28- python -m pip install --upgrade pip
29- pip install ruff bandit mypy pytest codespell requests-mock colorama
141+ bandit \
142+ -r . \ # Scan all Python files recursively
143+ -f human -o bandit-results.txt \ # Human-readable report
144+ -f json -o bandit-results.json || true # JSON report (for tools) + non-blocking
30145
31- - name : Run Codespell check
32- run : codespell --skip "*.json,*.txt,*.pdf" || true
146+ # Non-blocking check: Type check (fails won't stop workflow)
147+ type-check :
148+ name : " 🎯 Type Check (Non-Blocking)"
149+ needs : setup-check-tools
150+ runs-on : ubuntu-latest
151+ steps :
152+ - name : Checkout repository code
153+ uses : actions/checkout@v4
154+
155+ - name : Set up Python ${{ env.PYTHON_VERSION }}
156+ uses : actions/setup-python@v4
157+ with :
158+ python-version : ${{ env.PYTHON_VERSION }}
159+ cache : ' pip'
160+
161+ - name : Run mypy (static type checker)
162+ run : |
163+ mypy \
164+ --ignore-missing-imports \ # Ignore unresolved imports (e.g., third-party libs)
165+ --show-error-codes . || true # Show error codes for debugging + non-blocking
166+
167+ # Blocking check: Lint check (fails stop workflow)
168+ lint-check :
169+ name : " 🧹 Lint Check (Blocking)"
170+ needs : setup-check-tools
171+ runs-on : ubuntu-latest
172+ steps :
173+ - name : Checkout repository code
174+ uses : actions/checkout@v4
175+
176+ - name : Set up Python ${{ env.PYTHON_VERSION }}
177+ uses : actions/setup-python@v4
178+ with :
179+ python-version : ${{ env.PYTHON_VERSION }}
180+ cache : ' pip'
181+
182+ - name : Run ruff check (code linter)
183+ run : ruff check --output-format=concise . # Blocking: fails on lint errors
184+
185+ # Blocking check: Unit tests (fails stop workflow)
186+ unit-tests :
187+ name : " 🧪 Unit Tests (Blocking)"
188+ needs : setup-check-tools
189+ runs-on : ubuntu-latest
190+ steps :
191+ - name : Checkout repository code
192+ uses : actions/checkout@v4
33193
34- - name : Run Bandit security scan
35- run : bandit -r . --skip B101,B105 || true
194+ - name : Set up Python ${{ env.PYTHON_VERSION }}
195+ uses : actions/setup-python@v4
196+ with :
197+ python-version : ${{ env.PYTHON_VERSION }}
198+ cache : ' pip'
36199
37- - name : Run Pytest tests
38- run : pytest
200+ - name : Run pytest (unit test framework)
201+ run : pytest # Blocking: fails on test failures
39202
40- - name : Run Ruff checks with ignored rules
41- run : ruff check . --ignore B904,B905,EM101,EXE001,G004,ISC001,PLC0415,PLC1901,PLW060,PLW1641,PLW2901,PT011,PT018,PT028,S101,S311,SIM905,SLF001
203+ # Security analysis: CodeQL (for vulnerability detection)
204+ codeql-analysis :
205+ name : " 🛡️ CodeQL Security Analysis"
206+ needs : setup-check-tools # Controlled by ruff-format pre-condition
207+ runs-on : ubuntu-latest
208+ permissions :
209+ actions : read
210+ contents : read
211+ security-events : write # Required to upload CodeQL results
212+ steps :
213+ - name : Checkout repository code
214+ uses : actions/checkout@v4
42215
43- - name : Run Mypy type checks
44- run : mypy . --ignore-missing-imports || true
216+ - name : Initialize CodeQL
217+ uses : github/codeql-action/init@v2
218+ with :
219+ languages : python # Analyze Python code
220+
221+ - name : Autobuild (auto-configure build for CodeQL)
222+ uses : github/codeql-action/autobuild@v2
223+
224+ - name : Run CodeQL analysis
225+ uses : github/codeql-action/analyze@v2
226+ with :
227+ output : sarif-results/ # Export results for debugging
228+
229+ # Final summary: Verify all checks completed
230+ all-checks-summary :
231+ name : " ✅ All Checks Summary"
232+ needs : [spell-check, security-scan, type-check, lint-check, unit-tests, codeql-analysis]
233+ if : always() # Run even if some checks fail
234+ runs-on : ubuntu-latest
235+ steps :
236+ - name : Print workflow summary
237+ run : |
238+ echo "==================== Workflow Summary ===================="
239+ echo "Ruff auto-format made changes: ${{ needs.ruff-auto-format.outputs.changes_made }}"
240+ echo "---------------------------------------------------------"
241+
242+ # Check for blocking failures (lint/tests/CodeQL)
243+ if [[ "${{ contains(needs.lint-check.result, 'failure') || contains(needs.unit-tests.result, 'failure') || contains(needs.codeql-analysis.result, 'failure') }}" == "true" ]]; then
244+ echo "❌ Critical failure detected (lint/tests/CodeQL). Fix required."
245+ exit 1 # Block workflow on critical failures
246+ else
247+ echo "✅ No critical failures. Non-blocking issues (spelling/type) may exist."
248+ fi
249+
0 commit comments