name: CI on: push: branches: [ main ] tags-ignore: [ "**" ] paths-ignore: - 'doc/**' - '*.md' - 'packaging/**' pull_request: branches: [ main ] workflow_dispatch: {} permissions: contents: read issues: write id-token: write jobs: tests: name: tests runs-on: ubuntu-latest strategy: matrix: python-version: ["3.10", "3.11", "3.12"] steps: - name: Checkout repository uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Cache pip uses: actions/cache@v5 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }} restore-keys: | ${{ runner.os }}-pip-${{ matrix.python-version }}- ${{ runner.os }}-pip- - name: Install system dependencies run: | sudo apt-get update sudo apt-get install -y libgl1 libegl1 ffmpeg - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e '.[dev]' pyright==1.1.408 - name: Lint with ruff run: python -m ruff check . - name: Type check with pyright run: pyright src/ - name: Run tests with coverage env: QT_QPA_PLATFORM: offscreen run: | pytest tests/ --cov=trcc --cov-report=term-missing --cov-branch --tb=short -q 2>&1 | tee pytest-output.txt - name: Upload test metrics if: matrix.python-version == '3.12' uses: actions/upload-artifact@v7 with: name: pytest-output path: pytest-output.txt update-badges: name: Update README badges runs-on: ubuntu-latest needs: tests if: github.ref == 'refs/heads/main' && github.event_name == 'push' permissions: contents: write steps: - name: Checkout repository uses: actions/checkout@v6 - name: Download test metrics uses: actions/download-artifact@v8 with: name: pytest-output path: . - name: Parse and update badges run: | TESTS=$(grep -oP '\d+ passed' pytest-output.txt | grep -oP '\d+' || true) COV=$(grep -oP 'TOTAL.*\K\d+(?=%)' pytest-output.txt || true) if [ -n "$TESTS" ]; then sed -i "s/tests-[0-9]*_passed/tests-${TESTS}_passed/" README.md fi if [ -n "$COV" ]; then sed -i "s/coverage-[0-9]*%25/coverage-${COV}%25/" README.md fi - name: Commit if changed run: | git diff --quiet README.md && exit 0 git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add README.md git commit -m "ci: update test badges [skip ci]" git push security-scans: name: Security scans (pip-audit, bandit, safety) — non-blocking runs-on: ubuntu-latest needs: tests steps: - name: Checkout repository uses: actions/checkout@v6 - name: Set up Python 3.11 uses: actions/setup-python@v6 with: python-version: '3.11' - name: Install security tools run: | python -m pip install --upgrade pip pip install pip-audit bandit safety - name: Run pip-audit (JSON output) run: | python -m pip_audit --format json > pip_audit_results.json || true - name: Run bandit (JSON output) run: | bandit -r src -f json -o bandit_report.json || true - name: Run safety check (JSON output) run: | safety check --full-report --json -o safety_report.json || true - name: Produce findings summary file id: summary run: python .github/scripts/security_summary.py - name: Upload security reports and summary if: always() uses: actions/upload-artifact@v7 with: name: security-reports path: | bandit_report.json pip_audit_results.json safety_report.json security_findings.json security-triage: name: Security triage (create GitHub issue when findings) runs-on: ubuntu-latest needs: security-scans if: needs.security-scans.result == 'success' steps: - name: Checkout repository uses: actions/checkout@v6 with: sparse-checkout: .github/scripts - name: Download security reports uses: actions/download-artifact@v8 with: name: security-reports path: security_reports - name: Inspect findings & decide id: inspect run: python .github/scripts/security_inspect.py - name: Create issue for security findings if: steps.inspect.outputs.found == 'true' env: GH_TOKEN: ${{ github.token }} run: | gh issue create \ --title '[SECURITY] High/Critical findings in CI scans' \ --label 'security,automated' \ --body "The automated security scans detected **high/critical** findings. Summary: \`\`\`json ${{ steps.inspect.outputs.summary }} \`\`\` Full scan reports are attached to the workflow run as artifacts (security-reports). Please triage and assign as appropriate. If this issue is a false positive, add the reason and close." build-artifacts: name: Build artifacts (wheel + sdist) runs-on: ubuntu-latest needs: tests if: github.event_name != 'pull_request' # only build on push or manual steps: - name: Checkout repository uses: actions/checkout@v6 - name: Set up Python 3.11 uses: actions/setup-python@v6 with: python-version: '3.11' - name: Install build tools run: | python -m pip install --upgrade pip pip install build - name: Build sdist & wheel run: | python -m build --sdist --wheel --outdir dist - name: Upload dist artifacts uses: actions/upload-artifact@v7 with: name: dist-artifacts path: dist/*