跳到主要内容

Python CI

Complete Python project continuous integration workflow, including multi-version matrix, pip cache, lint, test and coverage.

name: Python CI

on:
push:
branches:
- main
- 'feature/**'
paths:
- 'src/**'
- 'tests/**'
- 'pyproject.toml'
- 'setup.py'
- 'requirements*.txt'
pull_request:
branches:
- main

concurrency:
max: 1
exceed-action: QUEUE

permissions:
repository: read

jobs:
lint:
name: Code Quality Check
runs-on: {ubuntu-24,x64,slim}

steps:
- name: Checkout code
uses: checkout

- name: Setup Python
uses: setup-python
with:
python-version: '3.12'
cache: 'pip'

- name: Install lint tools
run: pip install flake8 black isort mypy

- name: Flake8 Check
run: flake8 src/ --max-line-length=120 --statistics

- name: Black Format Check
run: black --check --diff src/

- name: isort Import Sort Check
run: isort --check-only --diff src/

- name: mypy Type Check
run: mypy src/ --ignore-missing-imports

test:
name: Python ${{ matrix.python-version }} Test
runs-on: {ubuntu-24,x64,small}
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
fail-fast: false
max-parallel: 3

steps:
- name: Checkout code
uses: checkout

- name: Setup Python ${{ matrix.python-version }}
uses: setup-python
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'

- name: Install dependencies
run: |
pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt

- name: Unit Test + Coverage
run: |
pytest tests/ \
--cov=src \
--cov-report=term-missing \
--cov-report=xml:coverage.xml \
--junitxml=test-results.xml \
-v

- name: Test Results Summary
if: ${{ always }}
run: |
echo "## Python Test Results - Python ${{ matrix.python-version }}" >> $ATOMGIT_STEP_SUMMARY
echo "Status: ${{ job.status }}" >> $ATOMGIT_STEP_SUMMARY

- name: Upload Coverage Report
if: ${{ always }}
uses: upload-artifact
with:
name: coverage-py${{ matrix.python-version }}
path: |
coverage.xml
test-results.xml
retention-days: 7

build:
name: Build Package
needs: [lint, test]
runs-on: {ubuntu-24,x64,small}
if: ${{ atomgit.ref == 'refs/heads/main' }}

steps:
- name: Checkout code
uses: checkout

- name: Setup Python
uses: setup-python
with:
python-version: '3.12'

- name: Install build tools
run: pip install build twine

- name: Build Python package
run: python -m build

- name: Upload artifact
uses: upload-artifact
with:
name: python-package
path: dist/
retention-days: 7