name: CI on: push: branches: [main] pull_request: workflow_dispatch: jobs: lint: runs-on: ubuntu-latest name: Lint (ruff) steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Python 3.12 uses: actions/setup-python@v5 with: python-version: "3.12" - name: Install ruff run: pip install ruff - name: ruff check run: ruff check . - name: ruff format --check run: ruff format --check . test: runs-on: ${{ matrix.os }} name: Tests (py${{ matrix.python }} / ${{ matrix.os }}) strategy: fail-fast: false matrix: os: [ubuntu-latest] python: ["3.10", "3.12", "3.13"] include: # Windows tests on 3.12 only — the version the release binaries ship # with. The self-hosted Windows runner blocks setup-python's install # script (PowerShell execution policy), so it uses the host's `py` # launcher + venv, same as release.yml. - os: windows-latest python: "3.12" steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python }} (Linux) if: runner.os == 'Linux' uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: Set up Python venv (Windows) if: runner.os == 'Windows' shell: pwsh run: | py -${{ matrix.python }} -m venv .venv Add-Content -Path $env:GITHUB_PATH -Value "$env:GITHUB_WORKSPACE\.venv\Scripts" # bcc_core has no GUI imports, so the test suite needs no PySide6 — # keeps CI fast and avoids Qt system-library headaches on the runner. # cryptography is for tests/test_checksums.py (release signing helper). - name: Install test dependencies run: pip install pytest cryptography - name: Run tests run: python -m pytest -v # ── Catalog signature gate (#61) ───────────────────────────────────────── # # data/catalog.json is a list of command+args entries that BCC writes into # the user's Claude config, which Claude then EXECUTES. The catalog is only # trusted if it carries a valid Ed25519 signature from the maintainer key. # # The threat this gate exists for is NOT an outsider pushing to the repo — # it is the maintainer merging a friendly-looking PR without really reading # it. A contributor can change catalog.json but cannot produce a matching # signature, so a blindly-merged PR lands here as a RED BUILD within a # minute, instead of quietly riding into the next release. # # Public-key verification only. No secret is used or needed. catalog-signature: name: Catalog signature runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" - name: Install dependencies run: pip install cryptography - name: Verify data/catalog.json.sig run: | python - <<'PY' import pathlib, sys import bcc_core as c raw = pathlib.Path("data/catalog.json").read_bytes() sig_path = pathlib.Path("data/catalog.json.sig") if not sig_path.exists(): sys.exit("FAIL: data/catalog.json.sig is missing. The catalog must be " "signed via the Catalog Console (#62) before it can land.") if b"\x00" * 32 in c.CATALOG_PUBKEYS: sys.exit("FAIL: CATALOG_PUBKEYS still holds the placeholder key.") if not c.verify_catalog_signature(raw, sig_path.read_bytes(), c.CATALOG_PUBKEYS): sys.exit( "FAIL: data/catalog.json does NOT match its signature.\n" "\n" "The catalog changed without being re-signed. Either someone edited\n" "it directly (a PR you merged?), or a signing pass was forgotten.\n" "Re-review and re-sign with the Catalog Console — do not bypass this." ) problems = c.validate_catalog(c.load_catalog(raw)) if problems: sys.exit("FAIL: catalog failed validation:\n " + "\n ".join(problems)) print("OK: catalog signature verifies and the catalog validates clean.") PY