Quality gates: coverage, pre-commit, markers, e2e tests #21
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # =============================================================== | |
| # 🔧 CI Workflow — Lint, Type Check, Test, Pre-commit & Package | |
| # =============================================================== | |
| # | |
| # This workflow: | |
| # - Runs ruff + black format checks (lint) | |
| # - Runs mypy static type analysis (typecheck) | |
| # - Runs pytest with 85% coverage threshold (test) | |
| # - Validates all pre-commit hooks (pre-commit) | |
| # - Builds wheel/sdist and validates with twine (package) | |
| # | |
| # All jobs run in parallel. Skips draft PRs. | |
| # Cancels stale runs on the same branch. | |
| # | |
| # References | |
| # ────────── | |
| # - Poetry: https://python-poetry.org/docs/ | |
| # - Ruff: https://docs.astral.sh/ruff/ | |
| # - Pre-commit: https://pre-commit.com/ | |
| # | |
| # Author: Manav Gupta <manavg@gmail.com> | |
| # =============================================================== | |
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| types: [opened, synchronize, ready_for_review] | |
| branches: [main] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| # ----------------------------------------------------------------- | |
| # Minimal permissions — principle of least privilege | |
| # ----------------------------------------------------------------- | |
| permissions: | |
| contents: read | |
| # ----------------------------------------------------------------- | |
| # Shared environment | |
| # ----------------------------------------------------------------- | |
| env: | |
| PYTHONUNBUFFERED: "1" | |
| PIP_DISABLE_PIP_VERSION_CHECK: "1" | |
| PYTHON_VERSION: "3.12" | |
| jobs: | |
| # ========================================================================= | |
| # 🔍 Lint & Format | |
| # ========================================================================= | |
| lint: | |
| name: Lint & Format | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| if: github.event_name != 'pull_request' || !github.event.pull_request.draft | |
| steps: | |
| # ─────────── Setup ─────────── | |
| - name: ⬇️ Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: 🐍 Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: 📦 Install Poetry | |
| run: pipx install poetry | |
| - name: 💾 Cache Poetry dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pypoetry | |
| key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }} | |
| restore-keys: ${{ runner.os }}-poetry- | |
| - name: 📦 Install dependencies | |
| run: poetry install --no-interaction | |
| # ─────────── Checks ─────────── | |
| - name: 🔍 Ruff lint | |
| run: poetry run ruff check . | |
| - name: 🎨 Black format check | |
| run: poetry run black --check . | |
| # ========================================================================= | |
| # 🔎 Type Check | |
| # ========================================================================= | |
| typecheck: | |
| name: Type Check | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| if: github.event_name != 'pull_request' || !github.event.pull_request.draft | |
| steps: | |
| # ─────────── Setup ─────────── | |
| - name: ⬇️ Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: 🐍 Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: 📦 Install Poetry | |
| run: pipx install poetry | |
| - name: 💾 Cache Poetry dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pypoetry | |
| key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }} | |
| restore-keys: ${{ runner.os }}-poetry- | |
| - name: 📦 Install dependencies | |
| run: poetry install --no-interaction | |
| # ─────────── Check ─────────── | |
| - name: 🔎 Mypy type check | |
| run: poetry run mypy faststack_core/ cli/ | |
| # ========================================================================= | |
| # 🧪 Tests & Coverage | |
| # ========================================================================= | |
| test: | |
| name: Test (py${{ matrix.python }}) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| if: github.event_name != 'pull_request' || !github.event.pull_request.draft | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python: ["3.12"] | |
| steps: | |
| # ─────────── Setup ─────────── | |
| - name: ⬇️ Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: 🐍 Setup Python ${{ matrix.python }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python }} | |
| - name: 📦 Install Poetry | |
| run: pipx install poetry | |
| - name: 💾 Cache Poetry dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pypoetry | |
| key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }} | |
| restore-keys: ${{ runner.os }}-poetry- | |
| - name: 📦 Install dependencies | |
| run: poetry install --no-interaction | |
| # ─────────── Test ─────────── | |
| - name: 🧪 Run tests with coverage | |
| run: | | |
| poetry run pytest \ | |
| --cov \ | |
| --cov-report=term \ | |
| --cov-report=xml \ | |
| --cov-fail-under=85 \ | |
| -v --tb=short | |
| - name: 📊 Upload coverage report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-py${{ matrix.python }} | |
| path: coverage.xml | |
| # ========================================================================= | |
| # ✅ Pre-commit | |
| # ========================================================================= | |
| pre-commit: | |
| name: Pre-commit | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| if: github.event_name != 'pull_request' || !github.event.pull_request.draft | |
| steps: | |
| # ─────────── Setup ─────────── | |
| - name: ⬇️ Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: 🐍 Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: 💾 Cache pre-commit environments | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pre-commit | |
| key: ${{ runner.os }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} | |
| restore-keys: ${{ runner.os }}-pre-commit- | |
| # ─────────── Check ─────────── | |
| - name: ✅ Run pre-commit hooks | |
| uses: pre-commit/action@v3.0.1 | |
| # ========================================================================= | |
| # 📦 Package Build | |
| # ========================================================================= | |
| package: | |
| name: Package Build | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| if: github.event_name != 'pull_request' || !github.event.pull_request.draft | |
| steps: | |
| # ─────────── Setup ─────────── | |
| - name: ⬇️ Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: 🐍 Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: 📦 Install Poetry | |
| run: pipx install poetry | |
| # ─────────── Build & Validate ─────────── | |
| - name: 🔨 Build distributions | |
| run: poetry build | |
| - name: ✅ Validate package metadata (twine) | |
| run: | | |
| pip install twine | |
| twine check dist/* |