Add Go CI coverage and a cross-language proto drift check #56
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
| name: CodebaseQA CI | |
| on: | |
| push: | |
| branches: [ main ] | |
| pull_request: | |
| branches: [ main ] | |
| jobs: | |
| backend-test: | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: ./apps/api | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python 3.11 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| cache: 'pip' | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| if [ -f requirements.txt ]; then pip install -r requirements.txt; fi | |
| # Only the linter is not a project dependency; everything the tests need | |
| # (pytest, pytest-asyncio, pytest-cov, httpx) is declared in requirements.txt | |
| # so a fresh clone can run the suite too. Pinned so an unrelated PR cannot | |
| # go red the day ruff ships a new rule. | |
| pip install ruff==0.16.2 | |
| - name: Lint with Ruff | |
| run: | | |
| ruff check src tests | |
| - name: Run Tests | |
| # `tests`, not `tests/unit tests/integration`: tests/test_parser.py sits at the | |
| # tests/ root and was silently excluded, so CI never built a single tree-sitter | |
| # parser and local runs (pyproject testpaths = ["tests"]) covered more than CI. | |
| run: | | |
| pytest tests --cov=src --cov-report=xml | |
| - name: Upload coverage reports | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| frontend-check: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v3 | |
| with: | |
| version: 10.28.2 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'pnpm' | |
| # --frozen-lockfile matches apps/web/vercel.json. With --no-frozen-lockfile CI | |
| # silently repaired lockfile drift that the Vercel build then rejected, so a PR | |
| # could go green and the production deploy fail on the same commit. | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Lint | |
| run: pnpm --filter web lint | |
| - name: Type Check | |
| run: pnpm --filter web type-check | |
| - name: Build | |
| run: pnpm --filter web build | |
| - name: Verify Compiled CSS | |
| run: pnpm web:verify-css | |
| - name: Run Tests | |
| run: pnpm --filter web test | |
| indexer-go: | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: ./services/indexer | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: '1.23' | |
| cache-dependency-path: services/indexer/go.sum | |
| # Every tree-sitter grammar is a cgo package with its own generated parser.c, so this | |
| # job cannot use CGO_ENABLED=0 and needs a C toolchain. ubuntu-latest ships gcc, but | |
| # pinning the expectation here makes the failure obvious if that ever changes. | |
| - name: Verify C toolchain is present (grammars are cgo) | |
| run: cc --version | |
| - name: Formatting | |
| run: | | |
| # gen/ is protoc output and is checked by the codegen-drift step instead. | |
| unformatted=$(gofmt -l . | grep -v '^gen/' || true) | |
| if [ -n "$unformatted" ]; then | |
| echo "::error::gofmt needed for: $unformatted" | |
| gofmt -d $unformatted | |
| exit 1 | |
| fi | |
| - name: Vet | |
| run: CGO_ENABLED=1 go vet ./... | |
| - name: Test | |
| run: CGO_ENABLED=1 go test -race ./... | |
| - name: Build | |
| run: CGO_ENABLED=1 go build -o /tmp/indexer . | |
| # The contract is shared between two languages that generate stubs independently. Without | |
| # this, editing indexer.proto and regenerating only one side produces a mismatch that | |
| # nothing catches until runtime -- the Go server and Python client would simply disagree. | |
| proto-codegen-drift: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: '1.23' | |
| cache-dependency-path: services/indexer/go.sum | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Install protoc and plugins | |
| run: | | |
| PROTOC_VERSION=29.3 | |
| curl -fsSL -o /tmp/protoc.zip \ | |
| "https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip" | |
| unzip -q /tmp/protoc.zip -d /tmp/protoc | |
| echo "/tmp/protoc/bin" >> "$GITHUB_PATH" | |
| # Pinned, not @latest. Generated files embed the generator version | |
| # ("protoc-gen-go v1.36.12", "Protobuf Python Version: 7.35.1"), so an unpinned | |
| # plugin would eventually change the output and fail this check for a reason that | |
| # has nothing to do with the proto. These are the exact versions that produced the | |
| # committed stubs; bump them and the stubs together. | |
| go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.12 | |
| go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.6.2 | |
| echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" | |
| python -m pip install --quiet "grpcio-tools==1.83.0" | |
| - name: Regenerate Go stubs | |
| working-directory: ./services/indexer | |
| run: | | |
| protoc --proto_path=proto --go_out=gen --go_opt=paths=source_relative \ | |
| --go-grpc_out=gen --go-grpc_opt=paths=source_relative proto/indexer.proto | |
| - name: Regenerate Python stubs | |
| working-directory: ./apps/api | |
| run: | | |
| python -m grpc_tools.protoc \ | |
| --proto_path=../../services/indexer/proto \ | |
| --python_out=src/core/indexer --grpc_python_out=src/core/indexer \ | |
| ../../services/indexer/proto/indexer.proto | |
| # The generated grpc stub uses a flat import; the committed copy is package-relative. | |
| sed -i 's/^import indexer_pb2 as indexer__pb2$/from src.core.indexer import indexer_pb2 as indexer__pb2/' \ | |
| src/core/indexer/indexer_pb2_grpc.py | |
| - name: Fail if committed stubs differ from the proto | |
| run: | | |
| # A strict whole-file diff is safe because protoc, both Go plugins and grpcio-tools | |
| # are all pinned above; the round-trip was verified byte-identical. | |
| if ! git diff --quiet -- services/indexer/gen apps/api/src/core/indexer; then | |
| echo "::error::Generated stubs are out of date with proto/indexer.proto." | |
| echo "Regenerate both sides and commit; see services/indexer/README.md." | |
| git diff --stat -- services/indexer/gen apps/api/src/core/indexer | |
| git diff -- services/indexer/gen apps/api/src/core/indexer | head -60 | |
| exit 1 | |
| fi | |
| echo "Generated stubs match the proto on both sides." |