Skip to content

chore(deps): update dependency hypothesis to v6.167.1 #189

chore(deps): update dependency hypothesis to v6.167.1

chore(deps): update dependency hypothesis to v6.167.1 #189

Workflow file for this run

name: CI Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
env:
PYTHON_VERSION: "3.14"
PYTHONDONTWRITEBYTECODE: 1
FORCE_COLOR: 1
jobs:
# ============================================================================
# STAGE 1: Code Quality Gates (fail-fast)
# ============================================================================
lint:
name: Lint and Type Check
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v7
with:
ref: ${{ github.head_ref }}
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v7
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: latest
virtualenvs-create: true
virtualenvs-in-project: true
- name: Cache Poetry dependencies
uses: actions/cache@v5
with:
path: .venv
key: venv-lint-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('**/poetry.lock') }}
restore-keys: |
venv-lint-${{ runner.os }}-${{ env.PYTHON_VERSION }}-
- name: Install dependencies
run: poetry install --only dev --no-interaction
- name: Lint with Ruff
run: poetry run ruff check .
- name: Auto-format with Ruff
if: github.event_name == 'pull_request'
run: |
poetry run ruff format .
if ! git diff --quiet; then
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git commit -am "style: auto-format with ruff"
git push
fi
- name: Check code formatting with Ruff
run: poetry run ruff format --check --diff .
- name: Type checking with mypy
run: poetry run mypy app/
# ============================================================================
# STAGE 2: Test Matrix (parallel execution)
# ============================================================================
test-unit:
name: Unit Tests
runs-on: ubuntu-latest
needs: lint
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v7
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: latest
virtualenvs-create: true
virtualenvs-in-project: true
- name: Cache Poetry dependencies
uses: actions/cache@v5
with:
path: .venv
key: venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('**/poetry.lock') }}
restore-keys: |
venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-
- name: Install dependencies
run: poetry install --with dev --no-interaction
- name: Run unit tests
run: |
poetry run pytest tests/ \
-m "not integration and not slow and not benchmark and not property" \
--cov=app \
--cov-report= \
--cov-fail-under=0 \
--junitxml=test-results-unit.xml \
--benchmark-disable \
-n auto \
-v
- name: Rename coverage data
run: mv .coverage .coverage.unit
- name: Upload coverage data
uses: actions/upload-artifact@v7
with:
name: coverage-data-unit
path: .coverage.unit
include-hidden-files: true
- name: Upload test results
uses: actions/upload-artifact@v7
if: always()
with:
name: test-results-unit
path: test-results-unit.xml
test-integration:
name: Integration Tests
runs-on: ubuntu-latest
needs: lint
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v7
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: latest
virtualenvs-create: true
virtualenvs-in-project: true
- name: Cache Poetry dependencies
uses: actions/cache@v5
with:
path: .venv
key: venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('**/poetry.lock') }}
restore-keys: |
venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-
- name: Install dependencies
run: poetry install --with dev --no-interaction
- name: Run integration tests
run: |
poetry run pytest tests/ \
-m "integration" \
--cov=app \
--cov-report= \
--cov-fail-under=0 \
--junitxml=test-results-integration.xml \
-v
- name: Rename coverage data
run: mv .coverage .coverage.integration
- name: Upload coverage data
uses: actions/upload-artifact@v7
with:
name: coverage-data-integration
path: .coverage.integration
include-hidden-files: true
- name: Upload test results
uses: actions/upload-artifact@v7
if: always()
with:
name: test-results-integration
path: test-results-integration.xml
test-security:
name: Security Scan
runs-on: ubuntu-latest
needs: lint
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v7
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: latest
virtualenvs-create: true
virtualenvs-in-project: true
- name: Cache Poetry dependencies
uses: actions/cache@v5
with:
path: .venv
key: venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('**/poetry.lock') }}
restore-keys: |
venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-
- name: Install dependencies
run: poetry install --only dev --no-interaction
- name: Upgrade pip to patched version
run: poetry run pip install --upgrade pip
- name: Audit dependencies for known vulnerabilities
run: poetry run pip-audit
- name: Bandit security lint
run: poetry run ruff check . --select S
test-property:
name: Property-Based Tests
runs-on: ubuntu-latest
needs: lint
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v7
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: latest
virtualenvs-create: true
virtualenvs-in-project: true
- name: Cache Poetry dependencies
uses: actions/cache@v5
with:
path: .venv
key: venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('**/poetry.lock') }}
restore-keys: |
venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-
- name: Install dependencies
run: poetry install --with dev --no-interaction
- name: Run property-based tests
run: |
poetry run pytest tests/ \
-m "property" \
--cov=app \
--cov-report= \
--cov-fail-under=0 \
--junitxml=test-results-property.xml \
-v
- name: Rename coverage data
run: mv .coverage .coverage.property
- name: Upload coverage data
uses: actions/upload-artifact@v7
with:
name: coverage-data-property
path: .coverage.property
include-hidden-files: true
- name: Upload test results
uses: actions/upload-artifact@v7
if: always()
with:
name: test-results-property
path: test-results-property.xml
test-performance:
name: Performance Tests
runs-on: ubuntu-latest
needs: lint
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v7
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: latest
virtualenvs-create: true
virtualenvs-in-project: true
- name: Cache Poetry dependencies
uses: actions/cache@v5
with:
path: .venv
key: venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('**/poetry.lock') }}
restore-keys: |
venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-
- name: Install dependencies
run: poetry install --with dev --no-interaction
- name: Run performance tests
run: |
poetry run pytest tests/ \
-m "benchmark or slow" \
--junitxml=test-results-performance.xml \
--timeout=120 \
--no-cov \
-v
- name: Upload test results
uses: actions/upload-artifact@v7
if: always()
with:
name: test-results-performance
path: test-results-performance.xml
test-e2e:
name: E2E Tests (Playwright)
runs-on: ubuntu-latest
needs: lint
timeout-minutes: 25
strategy:
fail-fast: false
matrix:
browser: [chromium, firefox]
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v7
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: latest
virtualenvs-create: true
virtualenvs-in-project: true
- name: Cache Poetry dependencies
uses: actions/cache@v5
with:
path: .venv
key: venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('**/poetry.lock') }}
restore-keys: |
venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-
- name: Install dependencies
run: poetry install --with dev --no-interaction
- name: Install Playwright browser (${{ matrix.browser }})
run: poetry run playwright install ${{ matrix.browser }} --with-deps
- name: Start Streamlit server
run: poetry run streamlit run main.py --server.headless=true --server.port=8501 > /tmp/streamlit.log 2>&1 &
- name: Wait for server to be ready
run: |
poetry run python - <<'EOF'
import sys
import time
import urllib.error
import urllib.request
url = "http://localhost:8501"
for attempt in range(30):
try:
urllib.request.urlopen(url, timeout=2)
print(f"Server ready after {attempt + 1}s", flush=True)
sys.exit(0)
except (urllib.error.URLError, OSError):
time.sleep(1)
print("ERROR: Streamlit server did not start within 30 seconds.")
sys.exit(1)
EOF
- name: Run E2E tests (${{ matrix.browser }})
run: |
mkdir -p test-results-e2e-${{ matrix.browser }}/artifacts
poetry run pytest tests/e2e/ \
-m "e2e" \
-p no:xdist \
--browser ${{ matrix.browser }} \
--screenshot only-on-failure \
--tracing retain-on-failure \
--output test-results-e2e-${{ matrix.browser }}/artifacts \
--junitxml=test-results-e2e-${{ matrix.browser }}.xml \
--html=test-results-e2e-${{ matrix.browser }}/report.html \
--self-contained-html \
--reruns 2 \
--reruns-delay 1 \
--only-rerun "TimeoutError|Page closed|Target closed" \
--timeout=60 \
-v
- name: Link HTML report from job summary
if: always()
run: |
echo "## E2E report — ${{ matrix.browser }}" >> "$GITHUB_STEP_SUMMARY"
echo "Download \`test-results-e2e-${{ matrix.browser }}\` artifact and open \`report.html\`." >> "$GITHUB_STEP_SUMMARY"
- name: Upload test results
uses: actions/upload-artifact@v7
if: always()
with:
name: test-results-e2e-${{ matrix.browser }}
path: |
test-results-e2e-${{ matrix.browser }}/
test-results-e2e-${{ matrix.browser }}.xml
retention-days: 7
- name: Upload E2E artifacts on failure
uses: actions/upload-artifact@v7
if: failure()
with:
name: e2e-artifacts-${{ matrix.browser }}
path: |
test-results-e2e-${{ matrix.browser }}/
/tmp/streamlit.log
retention-days: 7
# ============================================================================
# STAGE 3: Coverage Report and Summary
# ============================================================================
coverage-report:
name: Coverage Report
runs-on: ubuntu-latest
needs: [test-unit, test-integration, test-security, test-property, test-performance, test-e2e]
if: always()
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v7
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: latest
virtualenvs-create: true
virtualenvs-in-project: true
- name: Cache Poetry dependencies
uses: actions/cache@v5
with:
path: .venv
key: venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('**/poetry.lock') }}
restore-keys: |
venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-
- name: Install dependencies
run: poetry install --with dev --no-interaction
- name: Download all coverage data
uses: actions/download-artifact@v8
continue-on-error: true
with:
pattern: coverage-data-*
merge-multiple: true
path: .
- name: Combine and generate coverage report
run: |
poetry run coverage combine
poetry run coverage json
poetry run coverage html
poetry run coverage xml
echo "## Coverage Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ -f coverage.json ]; then
COVERAGE=$(python3 -c "import json; data=json.load(open('coverage.json')); print(data['totals']['percent_covered_display'])")
echo "**Total Coverage: ${COVERAGE}%**" >> $GITHUB_STEP_SUMMARY
fi
- name: Upload final coverage report
uses: actions/upload-artifact@v7
with:
name: coverage-report-final
path: |
coverage.xml
coverage.json
htmlcov/
retention-days: 30
# ============================================================================
# STAGE 4: CI Summary
# ============================================================================
ci-summary:
name: CI Summary
runs-on: ubuntu-latest
needs: [lint, test-unit, test-integration, test-security, test-property, test-performance, test-e2e]
if: always()
steps:
- name: Generate CI Summary
run: |
echo "## CI Pipeline Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Stage | Status |" >> $GITHUB_STEP_SUMMARY
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Lint and Type Check | ${{ needs.lint.result == 'success' && 'PASSED' || 'FAILED' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Unit Tests | ${{ needs.test-unit.result == 'success' && 'PASSED' || 'FAILED' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Integration Tests | ${{ needs.test-integration.result == 'success' && 'PASSED' || 'FAILED' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Security Scan | ${{ needs.test-security.result == 'success' && 'PASSED' || 'FAILED' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Property Tests | ${{ needs.test-property.result == 'success' && 'PASSED' || 'FAILED' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Performance Tests | ${{ needs.test-performance.result == 'success' && 'PASSED' || 'FAILED' }} |" >> $GITHUB_STEP_SUMMARY
echo "| E2E Tests (Playwright) | ${{ needs.test-e2e.result == 'success' && 'PASSED' || 'FAILED' }} |" >> $GITHUB_STEP_SUMMARY
- name: Fail if any job failed
if: |
needs.lint.result == 'failure' ||
needs.test-unit.result == 'failure' ||
needs.test-integration.result == 'failure' ||
needs.test-security.result == 'failure' ||
needs.test-property.result == 'failure' ||
needs.test-performance.result == 'failure' ||
needs.test-e2e.result == 'failure'
run: exit 1