Skip to content

Commit 043a872

Browse files
authored
Update python.yml
1 parent ee38ee2 commit 043a872

File tree

1 file changed

+231
-26
lines changed

1 file changed

+231
-26
lines changed

.github/workflows/python.yml

Lines changed: 231 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,249 @@
1-
name: Python Checks
1+
name: Code Quality & Auto-Format Checks
22

3+
# Trigger on push to main/master or PRs targeting these branches
34
on:
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

1414
jobs:
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

Comments
 (0)