fix(golang): reduce required co-package updates to minimal necessary set#63
Merged
kbsteere merged 17 commits intochainguard-dev:mainfrom May 4, 2026
Merged
Conversation
… set - Filter CheckTransitiveRequirements to only flag direct project deps; indirect deps are resolved automatically by Go's MVS and cannot cause API breakage in the project's own code - Add FindVersionGroupPackages to detect tightly-coupled module ecosystems (e.g. all go.opentelemetry.io/otel/* packages) using module family prefix heuristics, covering both co-released and version-drifted siblings - Add second API compat pass over discovered co-updates so community packages that import a co-updated dep (e.g. otelgrpc importing otel) are surfaced as API compat alerts
When CheckAPICompatibilityWithCache flags a package (e.g. go-ldap/ldap/v3) that imports a dep being updated (e.g. go-ntlmssp), walk the package's version history to find the lowest release whose go.mod requires the updated dep at >= the target version. This converts API compat alerts from no-op suggestions (pkg@current) into actionable version recommendations (pkg@min-compatible), directly addressing build failures where a dep's API signature changed between versions.
Add CheckAPIBreakingChanges which compares the exported API of a package between two versions using golang.org/x/exp/apidiff. This provides a precise signal for whether a dep version bump introduces incompatible changes (e.g. ProcessChallenge gaining a fourth argument in go-ntlmssp), as opposed to compatible additions (new functions, new fields). The function type-checks both versions using golang.org/x/tools/go/packages in isolated temp modules, then runs apidiff.Changes to classify each exported symbol change as compatible or incompatible. This can be used alongside findMinCompatibleVersion and CheckAPICompatibilityWithCache to distinguish false-positive compat alerts from confirmed breaking changes that require downstream packages to update.
- resolveAndFilterPackages now skips any package whose path matches the main module of the go.mod being analyzed, logging a warning instead of letting it reach gobump where it would fail with 'bumping the main module is not allowed' - checkMissingTransitiveDeps applies the same guard as a second line of defence for co-updates discovered through transitive checks - CheckAPIBreakingChanges now treats an unloadable new version (removed package or internally broken release) as a breaking change rather than returning an error, giving callers a clear signal not to bump to that version
- Add sentinel errors errPackageNotFound and errNoTypeInfo to replace dynamic fmt.Errorf calls in loadPackageTypes (err113) - Wrap packages.Error with %w in loadPackageTypes (errorlint) - Wrap defer os.RemoveAll in closure discarding return value (errcheck) - Fix stdlib import grouping for go/types and errors (gci, gofumpt) - Extract runCoUpdateAPICompatChecks helper from checkMissingTransitiveDeps to reduce cyclomatic complexity below threshold (gocyclo)
…oUpdates Extract the co-update detection core from checkMissingTransitiveDeps into a new detectCoUpdates function so both the update path and analyzer path use identical logic. Previously checkTransitiveRequirementsForStrategy in analyzer.go had its own diverged implementation: it used the deprecated CheckAPICompatibility, had no version-group ecosystem detection, no findMinCompatibleVersion, no main module skip, and included indirect project deps in version checks. Both paths now share: FindVersionGroupPackages for ecosystem grouping, CheckAPICompatibilityWithCache with findMinCompatibleVersion for actionable version recommendations, main module skip, direct-only dep filtering, and the second API compat pass for co-update deps.
RecommendStrategy was adding the main module to strategy.DirectUpdates without checking whether the package being updated is the go.mod's own module. The update path had this guard in resolveAndFilterPackages; now both paths produce consistent output.
When findMinCompatibleVersion identifies a concrete minimum version for an API compat alert (e.g. otelgrpc@v0.68.0 when bumping otel to v1.43.0), add it to strategy.DirectUpdates so it appears in JSON, YAML, and deps file output — not just as a human-readable warning string. Packages already being updated are skipped to avoid duplicates.
- Set initial capacity on allMissingDeps and apiCompatibilityAlerts in detectCoUpdates to avoid realloc when co-updates match update count - Parse go.mod once in RecommendStrategy and pass it to checkTransitiveRequirementsForStrategy to avoid reading the same file twice; fall back to parsing internally when caller passes nil
tcnghia
previously approved these changes
May 1, 2026
…rack Packages like go.opentelemetry.io/otel/exporters/* use a v0.x cadence while core otel uses v1.x. FindVersionGroupPackages was including them in the version group and recommending them at the wrong target version (e.g. v1.43.0 instead of v0.19.0). Skip version group candidates whose current major differs from the target major. The second-pass API compat check handles these packages correctly via findMinCompatibleVersion, which finds the right v0.x version.
Add TestDetectCoUpdates_CrossMajorVersionGroupSkipped to guard against recommending the wrong version for otel exporter packages. The otel exporter family (go.opentelemetry.io/otel/exporters/*) uses v0.x while core otel uses v1.x — the fix must find v0.19.0 via the API compat chain rather than v1.43.0 from the version group. Add a FindVersionGroupPackages test case documenting that the function itself returns cross-major family members; the filter lives in detectCoUpdates.
tcnghia
previously approved these changes
May 4, 2026
…ersionGroupPackages Move the cross-major version guard into FindVersionGroupPackages itself so that all callers are protected, not just detectCoUpdates. Previously, otel/exporters/prometheus@v0.60.0 would be included in the version group when bumping core otel to v1.43.0, and the function would return it with a target of v1.43.0 — a version that does not exist, causing go mod tidy to fail. The fix compares each candidate's semver major against the source package's current version major and excludes any mismatch. The second-pass API compat chain (findMinCompatibleVersion) then finds the correct v0.x version instead. Removes the now-redundant duplicate guard that was in detectCoUpdates.
…packages When FindVersionGroupPackages returns a package on a different major version track (e.g. otel/exporters/prometheus v0.60.0 when targeting otel v1.43.0), detectCoUpdates now calls findMinCompatibleVersion to find the correct v0.x version (e.g. v0.65.0) rather than either applying the wrong v1.x target or relying on the second-pass API compat chain to catch it. FindVersionGroupPackages returns all family members regardless of major version — the caller is responsible for resolving the right version.
…pdates - Move familyRoot declaration inside the cross-major branch where it is used - Remove duplicate familyRoot argument from log message - Use distinct Reason string for cross-major packages so it doesn't incorrectly say 'both at X' when the versions differ
tcnghia
approved these changes
May 4, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The co-update detection was flagging test-only and indirect transitive deps (testify, go-cmp, sys) that Go's MVS handles automatically, while missing the packages that actually matter.
Two additional fixes: the main module is skipped when it appears in a bump list to prevent gobump errors, and unloadable new versions are treated as breaking changes covering removed packages and broken releases.