1- # GitHub Actions CI/CD Quality Pipeline
2- # Generated by BMad TEA Agent - Test Architect Module
3- # Optimized for: Quality Gates, Coverage Thresholds, Parallel Execution
4- # Stack: backend (Python) | Framework: pytest
5- #
6- # Quality Gates:
7- # - Lint: ruff check (zero errors)
8- # - Format: ruff format --check
9- # - Type: mypy (strict mode)
10- # - Coverage: fail_under=40 (verified full-suite baseline threshold)
11- # - Security: bandit, pip-audit
12-
131name : Tests
142
153on :
197 branches : [main, master, develop]
208 workflow_dispatch :
219 inputs :
22- test-markers :
23- description : ' Pytest markers to filter tests (e.g., "unit", "not slow")'
24- required : false
25- default : ' '
2610 coverage-threshold :
27- description : ' Minimum coverage percentage'
11+ description : " Minimum coverage percentage for the Ubuntu baseline suite "
2812 required : false
29- default : ' 40 '
13+ default : " 40 "
3014
3115concurrency :
3216 group : ${{ github.workflow }}-${{ github.ref }}
3317 cancel-in-progress : true
3418
3519env :
36- PYTHON_VERSION_DEFAULT : ' 3.11'
37- COVERAGE_THRESHOLD : 40
20+ PYTHON_VERSION_DEFAULT : " 3.11"
21+ COVERAGE_THRESHOLD : " 40"
22+ SMOKE_TEST_PATHS : >-
23+ tests/test_bt_api_quality.py
24+ tests/test_event_bus.py
25+ tests/core/test_async_context.py
26+ tests/gateway/test_config.py
27+ FULL_SUITE_MARKERS : " not network and not integration and not performance and not e2e"
3828
3929jobs :
40- # ============================================================================
41- # STAGE 1: FAST FEEDBACK (< 5 minutes)
42- # Code quality checks - lint, format, type, security
43- # ============================================================================
4430 quality :
4531 name : Quality Gates
4632 runs-on : ubuntu-latest
47- timeout-minutes : 10
33+ timeout-minutes : 15
4834
4935 steps :
5036 - uses : actions/checkout@v4
5339 with :
5440 python-version : ${{ env.PYTHON_VERSION_DEFAULT }}
5541 cache : pip
42+ cache-dependency-path : pyproject.toml
5643
5744 - name : Install package + quality tools
5845 run : |
@@ -76,21 +63,21 @@ jobs:
7663 run : pip-audit || true
7764 continue-on-error : true
7865
79- # ============================================================================
80- # STAGE 2: COMPREHENSIVE TESTING (10-20 minutes)
81- # Full test suite with coverage reporting
82- # ============================================================================
83- test :
84- name : Test (Python ${{ matrix.python-version }}, ${{ matrix.os }})
66+ compatibility :
67+ name : Compatibility (Python ${{ matrix.python-version }}, ${{ matrix.os }})
8568 runs-on : ${{ matrix.os }}
8669 timeout-minutes : 30
8770 needs : quality
8871
8972 strategy :
9073 fail-fast : false
9174 matrix :
92- os : [ubuntu-latest]
93- python-version : ['3.11']
75+ os : [ubuntu-latest, macos-latest, windows-latest]
76+ python-version : ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
77+
78+ defaults :
79+ run :
80+ shell : bash
9481
9582 steps :
9683 - uses : actions/checkout@v4
@@ -99,163 +86,110 @@ jobs:
9986 with :
10087 python-version : ${{ matrix.python-version }}
10188 cache : pip
89+ cache-dependency-path : pyproject.toml
10290
10391 - name : Install build tools (Linux)
10492 if : runner.os == 'Linux'
10593 run : sudo apt-get update && sudo apt-get install -y build-essential
10694
95+ # GitHub-hosted runners do not expose a generic Windows 11 x64 label.
96+ # `windows-latest` is the supported hosted Windows image for this matrix.
10797 - name : Install package + dev deps
10898 run : |
10999 python -m pip install --upgrade pip
110100 pip install -e ".[dev]"
111101
112- - name : Run unit tests
102+ - name : Run cross-platform compatibility smoke suite
113103 env :
114- # Security: inputs passed through env: to prevent script injection
115- TEST_MARKERS : ${{ github.event.inputs.test-markers }}
104+ SKIP_LIVE_TESTS : " true"
105+ run : pytest $SMOKE_TEST_PATHS -q
106+
107+ full-suite :
108+ name : Full Suite (Python ${{ env.PYTHON_VERSION_DEFAULT }}, Ubuntu)
109+ runs-on : ubuntu-latest
110+ timeout-minutes : 45
111+ needs : quality
112+
113+ defaults :
114+ run :
115+ shell : bash
116+
117+ steps :
118+ - uses : actions/checkout@v4
119+
120+ - uses : actions/setup-python@v5
121+ with :
122+ python-version : ${{ env.PYTHON_VERSION_DEFAULT }}
123+ cache : pip
124+ cache-dependency-path : pyproject.toml
125+
126+ - name : Install build tools
127+ run : sudo apt-get update && sudo apt-get install -y build-essential
128+
129+ - name : Install package + dev deps
116130 run : |
117- # Security: inputs passed through env: to prevent script injection
118- if [ -n "$TEST_MARKERS" ]; then
119- pytest tests -v -m "$TEST_MARKERS" \
120- -n 8 \
121- --dist=loadgroup
122- else
123- pytest tests -v -m "unit" \
124- -n 8 \
125- --dist=loadgroup
126- fi
131+ python -m pip install --upgrade pip
132+ pip install -e ".[dev]"
127133
128- - name : Run full test suite with coverage gate
134+ - name : Run baseline suite with coverage gate
129135 env :
130136 SKIP_LIVE_TESTS : " true"
131137 run : |
132138 pytest tests -v \
139+ -m "$FULL_SUITE_MARKERS" \
133140 --cov=bt_api_py \
134141 --cov-report=xml:coverage-full.xml \
135142 --cov-report=html \
136143 --cov-report=term-missing \
137- --cov-fail-under=${{ github.event.inputs.coverage-threshold || env.COVERAGE_THRESHOLD }} \
138- -n 8 \
139- --dist=loadgroup
144+ --cov-fail-under=${{ github.event.inputs.coverage-threshold || env.COVERAGE_THRESHOLD }}
140145
141146 - name : Upload coverage to Codecov
142147 if : always()
143148 uses : codecov/codecov-action@v5
144149 with :
145150 files : ./coverage-full.xml
146- flags : ${{ matrix.os }} -py${{ matrix.python-version }}
151+ flags : ubuntu -py${{ env.PYTHON_VERSION_DEFAULT }}
147152 fail_ci_if_error : false
148153 verbose : true
149154
150155 - name : Archive coverage report
151156 if : always()
152157 uses : actions/upload-artifact@v4
153158 with :
154- name : coverage-report-${{ matrix.os }} -py${{ matrix.python-version }}
159+ name : coverage-report-ubuntu -py${{ env.PYTHON_VERSION_DEFAULT }}
155160 path : htmlcov/
156161 retention-days : 30
157162
158- - name : Archive test results
159- if : failure()
160- uses : actions/upload-artifact@v4
161- with :
162- name : test-results-${{ matrix.os }}-py${{ matrix.python-version }}
163- path : |
164- .pytest_cache/
165- test-results/
166- retention-days : 30
167-
168- # ============================================================================
169- # STAGE 3: QUALITY GATE ENFORCEMENT
170- # Ensure coverage threshold is met across all matrix combinations
171- # ============================================================================
172163 quality-gate :
173164 name : Quality Gate
174165 runs-on : ubuntu-latest
175- needs : [quality, test ]
166+ needs : [quality, compatibility, full-suite ]
176167 if : always()
177168
178169 steps :
179- - name : Check test results
170+ - name : Check job results
180171 run : |
181- if [ "${{ needs.test.result }}" == "failure" ]; then
182- echo "::error::Tests failed - Quality gate not passed"
172+ if [ "${{ needs.quality.result }}" != "success" ]; then
173+ echo "::error::Quality checks failed"
174+ exit 1
175+ fi
176+ if [ "${{ needs.compatibility.result }}" != "success" ]; then
177+ echo "::error::Compatibility matrix failed"
178+ exit 1
179+ fi
180+ if [ "${{ needs.full-suite.result }}" != "success" ]; then
181+ echo "::error::Full baseline suite failed"
183182 exit 1
184183 fi
185- echo "✅ All quality gates passed"
186184
187185 - name : Generate summary
188186 run : |
189- echo "## 🎯 Quality Gate Summary" >> $GITHUB_STEP_SUMMARY
190- echo "" >> $GITHUB_STEP_SUMMARY
191- echo "| Gate | Status |" >> $GITHUB_STEP_SUMMARY
192- echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
193- echo "| Lint | ${{ needs.quality.result == 'success' && '✅ Passed' || '❌ Failed' }} |" >> $GITHUB_STEP_SUMMARY
194- echo "| Tests | ${{ needs.test.result == 'success' && '✅ Passed' || '❌ Failed' }} |" >> $GITHUB_STEP_SUMMARY
195- echo "| Coverage | ✅ ≥${{ github.event.inputs.coverage-threshold || env.COVERAGE_THRESHOLD }}% |" >> $GITHUB_STEP_SUMMARY
196- echo "" >> $GITHUB_STEP_SUMMARY
197- echo "**Required CI platform**: ubuntu-latest" >> $GITHUB_STEP_SUMMARY
198- echo "**Required CI Python version**: 3.11" >> $GITHUB_STEP_SUMMARY
199-
200- # ============================================================================
201- # BURN-IN: FLAKY TEST DETECTION (scheduled only)
202- # Runs 5 iterations to detect flaky tests
203- # ============================================================================
204- burn-in :
205- name : Burn-In (Flaky Detection)
206- runs-on : ubuntu-latest
207- timeout-minutes : 60
208- needs : quality
209- if : github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
210-
211- steps :
212- - uses : actions/checkout@v4
213-
214- - uses : actions/setup-python@v5
215- with :
216- python-version : ${{ env.PYTHON_VERSION_DEFAULT }}
217- cache : pip
218-
219- - name : Install build tools
220- run : sudo apt-get update && sudo apt-get install -y build-essential
221-
222- - name : Install package + dev deps
223- run : |
224- python -m pip install --upgrade pip
225- pip install -e ".[dev]"
226-
227- - name : Run burn-in loop (5 iterations)
228- env :
229- SKIP_LIVE_TESTS : " true"
230- run : |
231- echo "🔥 Starting burn-in loop - detecting flaky tests"
232- for i in {1..5}; do
233- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
234- echo "🔥 Burn-in iteration $i/5"
235- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
236- pytest tests -v -m "unit" -n 8 --dist=loadgroup || exit 1
237- done
238- echo "✅ Burn-in complete - no flaky tests detected"
239-
240- - name : Upload burn-in failure artifacts
241- if : failure()
242- uses : actions/upload-artifact@v4
243- with :
244- name : burn-in-failures
245- path : |
246- .pytest_cache/
247- htmlcov/
248- retention-days : 30
249-
250- # ============================================================================
251- # EXTENSION PATTERNS — Script Injection Prevention
252- # ============================================================================
253- # When extending this template into reusable workflows, manual dispatch
254- # workflows, or composite actions, NEVER use ${{ inputs.* }} directly in
255- # run: blocks. Always pass through env: intermediaries.
256- #
257- # KEY PRINCIPLE: Inputs must be DATA, not COMMANDS.
258- # Pass inputs through env: and interpolate as quoted arguments into fixed
259- # commands. NEVER accept command-shaped inputs (e.g., install-command,
260- # test-command) that get executed as shell code — even through env:.
261- # ============================================================================
187+ echo "## CI Summary" >> "$GITHUB_STEP_SUMMARY"
188+ echo "" >> "$GITHUB_STEP_SUMMARY"
189+ echo "| Job | Status |" >> "$GITHUB_STEP_SUMMARY"
190+ echo "|-----|--------|" >> "$GITHUB_STEP_SUMMARY"
191+ echo "| Quality | ${{ needs.quality.result == 'success' && 'Passed' || 'Failed' }} |" >> "$GITHUB_STEP_SUMMARY"
192+ echo "| Compatibility matrix | ${{ needs.compatibility.result == 'success' && 'Passed' || 'Failed' }} |" >> "$GITHUB_STEP_SUMMARY"
193+ echo "| Ubuntu baseline suite | ${{ needs.full-suite.result == 'success' && 'Passed' || 'Failed' }} |" >> "$GITHUB_STEP_SUMMARY"
194+ echo "" >> "$GITHUB_STEP_SUMMARY"
195+ echo "Compatibility matrix: macOS, Linux, Windows x Python 3.9-3.14." >> "$GITHUB_STEP_SUMMARY"
0 commit comments