Skip to content

Commit 86e3e80

Browse files
committed
Fix CI and add Python 3.9-3.14 compatibility matrix
1 parent 70e5a58 commit 86e3e80

1,337 files changed

Lines changed: 3331 additions & 719 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/optimized-tests.yml

Lines changed: 135 additions & 212 deletions
Large diffs are not rendered by default.

.github/workflows/tests.yml

Lines changed: 78 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,3 @@
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-
131
name: Tests
142

153
on:
@@ -19,32 +7,30 @@ on:
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

3115
concurrency:
3216
group: ${{ github.workflow }}-${{ github.ref }}
3317
cancel-in-progress: true
3418

3519
env:
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

3929
jobs:
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
@@ -53,6 +39,7 @@ jobs:
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"

bt_api_py/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
and traditional financial markets (CTP, Interactive Brokers).
55
"""
66

7+
from __future__ import annotations
8+
79
from bt_api_py._version import __version__
810
from bt_api_py.auth_config import (
911
AuthConfig,

bt_api_py/_compat.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from __future__ import annotations
2+
3+
from datetime import timezone
4+
from enum import Enum
5+
6+
try:
7+
from typing import Never as _Never
8+
from typing import ParamSpec as _ParamSpec
9+
from typing import Self as _Self
10+
except ImportError:
11+
from typing_extensions import Never as _Never
12+
from typing_extensions import ParamSpec as _ParamSpec
13+
from typing_extensions import Self as _Self
14+
15+
try:
16+
from enum import StrEnum as _StrEnum
17+
except ImportError:
18+
19+
class _StrEnum(str, Enum):
20+
"""Backport for Python < 3.11."""
21+
22+
23+
Never = _Never
24+
ParamSpec = _ParamSpec
25+
Self = _Self
26+
StrEnum = _StrEnum
27+
UTC = timezone.utc
28+
29+
__all__ = ["Never", "ParamSpec", "Self", "StrEnum", "UTC"]

bt_api_py/_version.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
"""Package version metadata."""
22

3+
from __future__ import annotations
4+
35
__version__ = "0.15"

bt_api_py/auth_config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
加密货币交易所使用 API Key,CTP 使用 Broker/User/Password,IB 使用 TWS 连接参数.
44
"""
55

6+
from __future__ import annotations
7+
68
from typing import Any
79
from urllib.parse import urlparse
810

0 commit comments

Comments
 (0)