diff --git a/.github/workflows/proto-version-check.yml b/.github/workflows/proto-version-check.yml index ea300419d..b888f98c1 100644 --- a/.github/workflows/proto-version-check.yml +++ b/.github/workflows/proto-version-check.yml @@ -3,60 +3,73 @@ name: Proto Version Check on: pull_request: paths: + - "**/*.proto" - "**/*.pb.go" + - "**/generate.sh" + - "proto-tools.env" + - ".github/workflows/proto-version-check.yml" jobs: - check-proto-versions: + regenerate-and-diff: + name: Regenerate proto and verify no drift runs-on: ubuntu-latest steps: - - name: Check for proto tool version changes - uses: actions/github-script@v7 + - name: Checkout + uses: actions/checkout@v4 + + - name: Load pinned proto toolchain versions + run: | + # shellcheck source=/dev/null + . ./proto-tools.env + { + echo "PROTOC_VERSION=${PROTOC_VERSION}" + echo "PROTOC_GEN_GO_VERSION=${PROTOC_GEN_GO_VERSION}" + echo "PROTOC_GEN_GO_GRPC_VERSION=${PROTOC_GEN_GO_GRPC_VERSION}" + } >> "$GITHUB_ENV" + + - name: Setup Go + uses: actions/setup-go@v5 with: - script: | - const files = await github.paginate(github.rest.pulls.listFiles, { - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number, - per_page: 100, - }); + go-version-file: go.mod - const pbFiles = files.filter(f => f.filename.endsWith('.pb.go')); - const missingPatch = pbFiles.filter(f => !f.patch).map(f => f.filename); - if (missingPatch.length > 0) { - core.setFailed( - `Cannot inspect patch data for:\n` + - missingPatch.map(f => `- ${f}`).join('\n') + - `\nThis can happen with very large PRs. Verify proto versions manually.` - ); - return; - } - const versionPattern = /^[+-]\s*\/\/\s+protoc(?:-gen-go)?\s+v[\d.]+/; - const violations = []; + - name: Setup protoc + uses: arduino/setup-protoc@v3 + with: + version: ${{ env.PROTOC_VERSION }} + repo-token: ${{ secrets.GITHUB_TOKEN }} - for (const file of pbFiles) { - const changed = file.patch - .split('\n') - .filter(line => versionPattern.test(line)); - if (changed.length > 0) { - violations.push({ - file: file.filename, - lines: changed, - }); - } - } + - name: Install protoc plugins + run: | + go install "google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOC_GEN_GO_VERSION}" + go install "google.golang.org/grpc/cmd/protoc-gen-go-grpc@${PROTOC_GEN_GO_GRPC_VERSION}" + echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" - if (violations.length > 0) { - const details = violations.map(v => - `${v.file}:\n${v.lines.map(l => ' ' + l).join('\n')}` - ).join('\n\n'); + - name: Verify protoc version matches pin + run: | + actual=$(protoc --version | awk '{print $2}') + if [ "$actual" != "$PROTOC_VERSION" ]; then + echo "::error::protoc $actual does not match pinned $PROTOC_VERSION" + exit 1 + fi - core.setFailed( - `Proto version strings changed in generated files.\n` + - `This usually means the wrong protoc or protoc-gen-go version was used.\n` + - `Regenerate with the matching tool versions.\n\n` + - details - ); - return; - } + - name: Regenerate all proto bindings + run: | + set -euo pipefail + for script in \ + client/proto/generate.sh \ + shared/signal/proto/generate.sh \ + shared/management/proto/generate.sh \ + flow/proto/generate.sh \ + encryption/testprotos/generate.sh; do + echo "::group::$script" + bash "$script" + echo "::endgroup::" + done - console.log('No proto version string changes detected'); + - name: Fail if regeneration changed any tracked file + run: | + if ! git diff --exit-code; then + echo "::error::Generated proto files drift from .proto sources or pinned tool versions." + echo "Run the generate.sh scripts locally with the toolchain in proto-tools.env and commit the result." + exit 1 + fi