Skip to content

ci: add check-generated-code guard for checked-in generated Rust #95

@iainmcgin

Description

@iainmcgin

Problem

connect-rust has checked-in generated Rust in four places, regenerated by task generate:all:

  • examples/eliza/src/generated/
  • examples/multiservice/src/generated/
  • conformance/src/generated/
  • benches/rpc/src/generated/

CI doesn't verify those are up to date. A codegen change to protoc-gen-connect-rust, connectrpc-codegen, or the bundled protoc-gen-buffa/protoc-gen-buffa-packaging toolchain can merge without task generate:all having been re-run, leaving the checked-in code stale until someone notices the diff in a later, unrelated PR.

This matters more than usual here because the examples are documentation — examples/eliza is what a downstream consumer copies to get started, and conformance/src/generated/ is what the conformance suite actually exercises. Stale generated code means the examples no longer demonstrate what the current toolchain produces, and the conformance suite isn't testing the current codegen.

Concrete motivating case: anthropics/buffa#102 changes buffa_codegen::ALLOW_LINTS (adds unused_qualifications), which changes the #[allow(...)] block protoc-gen-buffa-packaging writes into every generated mod.rs. In the buffa repo, a check-generated-code CI job caught the stale checked-in code immediately. In connect-rust, the same change would land via the next protoc-gen-buffa bump and nothing would notice that examples/eliza/src/generated/buffa/mod.rs (and every other mod.rs in the four trees) no longer matches what the toolchain produces.

Proposal

Add a check-generated-code job to CI that mirrors task generate:all and fails on any diff in the four checked-in trees, modeled on buffa's check-generated-code job.

check-generated-code:
  runs-on: ubuntu-latest
  timeout-minutes: 15
  steps:
    - uses: actions/checkout@v4
    - uses: actions/checkout@v4
      with:
        repository: anthropics/buffa
        path: buffa-sibling
        # Pin to the buffa version in Cargo.lock so the sibling toolchain
        # matches what consumers would resolve from the registry.
        ref: v0.5.1
    - uses: dtolnay/rust-toolchain@stable
    - uses: Swatinem/rust-cache@v2
    - uses: bufbuild/buf-action@v1
      with: { setup_only: true }
    - name: Build codegen plugins
      run: |
        cargo build --release -p connectrpc-codegen --bin protoc-gen-connect-rust
        cargo build --release --manifest-path buffa-sibling/Cargo.toml \
          -p protoc-gen-buffa -p protoc-gen-buffa-packaging
    - name: Regenerate
      # The Taskfile expects ../buffa; symlink or override the path var.
      run: |
        ln -sfn "$PWD/buffa-sibling" ../buffa
        task generate:all
    - name: Check for differences
      run: |
        if ! git diff --exit-code examples/*/src/generated/ conformance/src/generated/ benches/rpc/src/generated/; then
          echo "::error::Checked-in generated code is stale. Run 'task generate:all' and commit."
          exit 1
        fi

Open questions

  • Where to source the sibling buffa toolchain. task generate:all builds protoc-gen-buffa/protoc-gen-buffa-packaging from ../buffa, which CI doesn't have. Options: (a) clone anthropics/buffa at the tag matching Cargo.lock (sketched above), (b) download the pre-built plugin binaries from the buffa GitHub release matching the locked version, (c) cargo install protoc-gen-buffa protoc-gen-buffa-packaging --version <locked> from crates.io. (b) is fastest and avoids a sibling-repo build; (a) is most robust to a buffa version not yet on the release page.
  • Whether benches/rpc/src/generated/ should be in scope. It's checked in but only used for benchmarks; staleness is lower-risk than the examples and conformance trees. Cheap to include either way.
  • Pin discipline. The job should resolve the buffa toolchain version from Cargo.lock rather than hardcode a tag, so a buffa dep bump and the regen land atomically.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions