Skip to content

build(go): declare the Go toolchain once and derive it everywhere - #473

Merged
balajinvda merged 3 commits into
mainfrom
feat/go-toolchain-single-source
Jul 27, 2026
Merged

build(go): declare the Go toolchain once and derive it everywhere#473
balajinvda merged 3 commits into
mainfrom
feat/go-toolchain-single-source

Conversation

@balajinvda

@balajinvda balajinvda commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

TL;DR

The repository carried four different answers to "which Go do we use", and two were live in the same pipeline: CI linted with 1.26.0 while Bazel compiled with 1.25.0.

Where Was
license-dependencies.yml, build-test.yml setup-go 1.26.0
root MODULE.bazel go_sdk.download 1.25.0
go.work.bazel 1.25.0
bazel-ci container image 1.25.6
image-credential-helper/go.mod 1.25.11

tools/go-toolchain/go.mod is now the single declaration, at 1.26.5.

Additional Details

The anchor has no source and is never built. It exists so consumers derive the version instead of repeating it:

  • Root MODULE.bazel reads it via go_sdk.from_file. rules_go requires the file be named exactly go.mod (_check_go_mod_name), which is why the anchor is a module rather than a .go-version file.
  • Every setup-go reads it via go-version-file. Five literal pins are gone.

Two places genuinely cannot derive it, so tools/ci/check-go-version asserts them instead: go.work.bazel, where Bazel requires a literal, and the bazel-ci image, which is built in another repository and is therefore reported rather than checked.

Worth noting: the workflows were pinned to 1.26.0, which is five patches behind. go.dev currently reports 1.26.5 and 1.25.12. "Latest" had already drifted, which is the failure mode this change is meant to prevent.

For the Reviewer

The risk here is the toolchain bump, not the plumbing. Verified on the derived 1.26.5:

  • bazel build //... completes, 278 targets.
  • bazel test //... gives 104 passing and 4 failing. Those four fail identically at origin/main and are unrelated: two cloud-tasks Testcontainers suites, go/lib:gazelle_test, and icms-translate.

There are deliberately two toolchains here, and an earlier revision of this PR conflated them. Correcting that is 0bdcb89c.

byoo-otel-collector's otelcol genrule shells out to go build against $PATH, on purpose, because the collector's 250-module graph does not resolve under rules_go. It compiles with whatever Go the bazel-ci image ships, and byoo/go.work declares 1.25.6 to match. That pairing determines how the shipped collector binary is built.

So:

  • Hermetic Bazel SDK: single-sourced at 1.26.5, governs everything built through rules_go.
  • Host toolchain in the CI image: paired with byoo/go.work at 1.25.6, governs one non-hermetic genrule.

The check reports the host requirement rather than asserting equality with the SDK. My first version asserted the image "must ship 1.26.5", which would have broken the pairing and silently recompiled the collector with a different toolchain. The bazel-ci image does not need bumping for this PR.

Service go.mod files are also left alone. Their go directives are minimum-version declarations, not toolchain selection, and Go permits them to be lower. Only the toolchain is single-sourced here.

For QA

tools/ci/check-go-version
tools/scripts/test/test-check-go-version
bazel build //...

The check runs in the GitHub release helper job. Its tests cover the drifts that actually happened: go.work.bazel disagreeing, a workflow reintroducing a literal, MODULE.bazel reverting to go_sdk.download, and a missing toolchain directive. Each was verified to fail the check.

Issues

Relates to #451

Checklist

  • I am familiar with the Contributing Guidelines.
  • I have signed off my commits for Developer Certificate of Origin (DCO) compliance.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

Summary by CodeRabbit

  • New Features

    • Centralized Go toolchain version management across development, Bazel, and CI using a single source of truth.
    • Added automated CI checks to detect Go version drift across configuration files.
  • Bug Fixes

    • Prevented mismatches between workspace and build/toolchain declarations by enforcing consistent versioning.
  • Tests

    • Added fixture-based tests to verify both passing consistency cases and expected failures for drift, pinned versions, and missing toolchain declarations.

The repository carried four different answers to "which Go do we use", and two
of them were live at the same time in the same pipeline: CI linted with 1.26.0
via setup-go while Bazel compiled with 1.25.0 via go_sdk.download. The bazel-ci
container image ships 1.25.6, and image-credential-helper's go.mod asks for
1.25.11. Nothing reconciled them because each was maintained separately.

tools/go-toolchain/go.mod is now the single declaration. It has no source and is
never built; it exists so consumers derive the version instead of repeating it:

- The root MODULE.bazel reads it via go_sdk.from_file. rules_go requires the
  file to be named exactly go.mod, which is why the anchor is a module rather
  than a .go-version file.
- Every GitHub Actions setup-go reads it via go-version-file. Five literal pins
  are gone, three in license-dependencies.yml and two in build-test.yml.

Two places cannot derive it and are asserted instead by tools/ci/check-go-version:
go.work.bazel, where Bazel requires a literal, and the bazel-ci image, which is
built in another repository and so is reported rather than checked.

Standardized on 1.26.5, the current stable release. Worth noting the workflows
were pinned to 1.26.0, which is five patches behind; "latest" had already
drifted.

Verified on the derived toolchain: bazel build //... completes over 278 targets,
and bazel test //... gives 104 passing with the same 4 failures that occur at
origin/main, namely two cloud-tasks Testcontainers suites, go/lib gazelle_test,
and icms-translate.

tools/scripts/test/test-check-go-version covers the drifts that actually
happened: go.work.bazel disagreeing, a workflow reintroducing a literal pin,
MODULE.bazel reverting to go_sdk.download, and a missing toolchain directive.
Each case was verified to fail the check.

Relates to #451

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
Signed-off-by: Balaji Ganesan <bganesan@nvidia.com>
@balajinvda
balajinvda requested review from a team as code owners July 27, 2026 05:14
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 13730c8e-8649-49e7-bb83-f4eb6239ac22

📥 Commits

Reviewing files that changed from the base of the PR and between 0bdcb89 and ce9c85a.

📒 Files selected for processing (1)
  • tools/scripts/test/test-check-go-version
🚧 Files skipped from review as they are similar to previous changes (1)
  • tools/scripts/test/test-check-go-version

📝 Walkthrough

Walkthrough

Changes

The repository centralizes the Go toolchain version in tools/go-toolchain/go.mod. Bazel, workspace configuration, and GitHub Actions consume that declaration, while a CI checker detects version drift and is covered by fixture-based tests.

Go toolchain consistency

Layer / File(s) Summary
Shared toolchain declaration and consumers
tools/go-toolchain/go.mod, tools/go-toolchain/BUILD.bazel, MODULE.bazel, go.work.bazel
Go 1.26.5 is declared in a dedicated module and consumed by Bazel and workspace configuration instead of duplicated literals.
Declaration consistency checker and tests
tools/ci/check-go-version, tools/scripts/test/test-check-go-version
The checker validates the shared version against workspace, workflow, and Bazel declarations; tests cover consistent and drifted fixtures, including BYOO host-toolchain cases.
CI toolchain integration
.github/workflows/build-test.yml, .github/workflows/license-dependencies.yml
GitHub Actions setup steps read the shared go.mod, and the release-helper job runs the consistency checker.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: kristinapathak

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title follows Conventional Commits and accurately reflects the main build/toolchain-focused changes in the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/go-toolchain-single-source

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tools/ci/check-go-version`:
- Line 42: Make drift detection formatting-independent: in
tools/ci/check-go-version at lines 42-42, inspect every active go-version: key
across both .yml and .yaml workflow files, accepting quoted, double-quoted, and
unquoted values with flexible whitespace; at lines 51-51, detect go_sdk.download
calls with a version argument across arbitrary spacing and line breaks; in
tools/scripts/test/test-check-go-version at lines 48-55, add regression fixtures
covering quoted, unquoted, .yaml, and whitespace/multiline variants.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 33b2680d-4507-4834-a95c-4ba1d1478631

📥 Commits

Reviewing files that changed from the base of the PR and between 1f8ea71 and d080276.

⛔ Files ignored due to path filters (1)
  • MODULE.bazel.lock is excluded by !**/*.lock, !**/MODULE.bazel.lock
📒 Files selected for processing (8)
  • .github/workflows/build-test.yml
  • .github/workflows/license-dependencies.yml
  • MODULE.bazel
  • go.work.bazel
  • tools/ci/check-go-version
  • tools/go-toolchain/BUILD.bazel
  • tools/go-toolchain/go.mod
  • tools/scripts/test/test-check-go-version

Comment thread tools/ci/check-go-version
The first version of this change flattened two toolchains into one and would
have caused the drift it was meant to prevent.

byoo-otel-collector's otelcol genrule shells out to `go build` against $PATH,
deliberately, because the collector's 250-module graph does not resolve under
rules_go. It therefore compiles with whatever Go the bazel-ci container image
ships, and byoo/go.work declares 1.25.6, matching that image. The pairing is not
incidental; it is what determines how the shipped collector binary is built.

The check previously asserted that the image "must ship Go 1.26.5", which would
have broken that pairing and silently recompiled the collector with a different
toolchain. It now describes two separate concerns:

- The hermetic Bazel SDK, single-sourced in tools/go-toolchain/go.mod at 1.26.5,
  governing everything built through rules_go.
- The host toolchain in the CI image, paired with byoo/go.work at 1.25.6,
  governing exactly one non-hermetic genrule.

The check reports the host requirement rather than asserting equality with the
SDK, and the two are explicitly allowed to differ. The requirement is only
enforced when byoo-otel-collector is present, so removing that service does not
fail the check, while having it without a go.work does.

The anchor's own comment made the same conflation and is corrected.

Two tests added: a host toolchain differing from the Bazel SDK must be reported
and not rejected, and byoo present without a go.work must fail.

I found this by checking whether anything consumed the image's Go, concluded
from grepping workflow run: steps that nothing did, and was wrong: the consumer
is inside a Bazel action reaching out to $PATH, not a workflow step.

Relates to #451

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
Signed-off-by: Balaji Ganesan <bganesan@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tools/scripts/test/test-check-go-version`:
- Around line 81-84: Strengthen the failure branch in test-check-go-version by
asserting that out contains the specific diagnostic for a missing go.work file
before calling pass. Keep the existing failure expectation, but ensure unrelated
checker errors cannot make the test pass.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 62fee717-a45c-476f-a5c1-dbc831443a93

📥 Commits

Reviewing files that changed from the base of the PR and between d080276 and 0bdcb89.

📒 Files selected for processing (3)
  • tools/ci/check-go-version
  • tools/go-toolchain/go.mod
  • tools/scripts/test/test-check-go-version
🚧 Files skipped from review as they are similar to previous changes (2)
  • tools/go-toolchain/go.mod
  • tools/ci/check-go-version

Comment thread tools/scripts/test/test-check-go-version Outdated
The byoo-without-go.work case passed on any non-zero exit, so an unrelated
regression could have kept it green while the missing-file handling was broken.
It now asserts the specific diagnostic.

Verified by replacing that diagnostic with an unrelated one: the test fails with
"checker failed, but not with the missing-go.work diagnostic".

Co-authored-by: Balaji Ganesan <bganesan@nvidia.com>
Signed-off-by: Balaji Ganesan <bganesan@nvidia.com>
@balajinvda
balajinvda removed the request for review from Max-NV July 27, 2026 15:53
@balajinvda
balajinvda added this pull request to the merge queue Jul 27, 2026
Merged via the queue into main with commit df3ee05 Jul 27, 2026
20 checks passed
@balajinvda
balajinvda deleted the feat/go-toolchain-single-source branch July 27, 2026 22:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants