feat: detect transitive deps, deduplicate packages, and flag API compatibility issues#26
Merged
kbsteere merged 34 commits intochainguard-dev:mainfrom Apr 15, 2026
Conversation
Fixes issues where packages with +incompatible suffix were not resolved correctly, and vendor directories failed due to missing go.sum entries. - Resolve all semantic versions through go list to get canonical forms - Handles +incompatible suffix automatically (e.g., v28.0.0 -> v28.0.0+incompatible) - Skip commit hashes to preserve existing behavior - Add GOWORK=off for workspace compatibility - Run go mod tidy before go vendor to ensure go.sum is up-to-date - Add test coverage for +incompatible version updates
Adds automatic detection of when updating a package requires co-updating
other dependencies to prevent build failures from incompatible versions.
When updating a package, omnibump now:
- Fetches the target version's go.mod from the Go module proxy
- Compares its requirements against current project versions
- Detects incompatibilities (required version > current version)
- Returns error with complete list of packages to update together
- Provides exact command to run with all required co-updates
Example:
$ omnibump --packages "oras.land/oras-go@v1.2.7"
Error: the following dependencies need to be co-updated:
- github.com/docker/docker: current v28.0.0, required >= v28.5.1
- github.com/docker/cli: current v25.0.1, required >= v28.5.1
[... 13 more ...]
To proceed, add these packages to your update:
omnibump --packages "oras.land/oras-go@v1.2.7 github.com/docker/docker@v28.5.1 ..."
Features:
- Works in both update and analyze commands
- Automatically deduplicates (keeps highest required version)
- Filters out packages already being updated
- Validates entire update set together
- Available through standard Language interface
Implementation:
- Added CheckTransitiveRequirements() to check single package
- Integrated into resolveAndFilterPackages() for update flow
- Integrated into RecommendStrategy() for analyze flow
- Added comprehensive test coverage with real proxy data
- Added documentation for library consumers
…t hanging Packages like github.com/elastic/beats/v7 can have thousands of pseudo-versions. Iterating all of them and fetching each go.mod via HTTP causes the analyze command to hang indefinitely. Cap the search at 50 newer versions, which is sufficient to find a fix in practice while keeping analysis responsive.
The same package (e.g. github.com/aquasecurity/trivy) was appended to DirectUpdates multiple times when several indirect deps each resolved to the same direct parent as their fix. addParentBumpsToStrategy had no cross-call deduplication, producing duplicate entries in the output deps.yaml. Deduplicate at the end of RecommendStrategy, keeping the highest semver version per package.
…r and analyzer_test
When updating a package that has schema/API breaking changes, check if other packages in the project import from it. Flag those packages as potentially needing updates, since breaking API changes can require co-updates even if go.mod doesn't explicitly bump version requirements. Example: When opentelemetry/otel/sdk is updated with schema changes, packages like knative.dev/pkg that import from it may need updating to maintain compatibility, even if their explicit requires don't change.
Add unit tests to verify that CheckAPICompatibility detects packages that import from an updated dependency. Tests cover basic detection, skipping of indirect dependencies, and error handling.
Sort direct dependency updates alphabetically by package name using slices.SortStableFunc to ensure consistent, readable output regardless of the order in which dependencies are discovered.
…ectUpdates When a user explicitly requests an indirect package to be updated via --packages, it should appear in DirectUpdates even if parent bump alternatives exist. Previously, handleIndirectDependency returning true would cause us to skip adding the package. Now we add it and offer parent bumps as additional options in warnings.
…g deps API compatibility checks flag packages that import an updated dependency. These should be warnings for manual review, not implicit version requirements (since we don't know what version is needed). Instead of adding them to missing deps (which become no-ops), add them as warnings so users see: 'knative.dev/pkg imports go.opentelemetry.io/otel/sdk which is being updated (may need manual version bump)'.
…arnings - Add API compatibility checks to checkMissingTransitiveDeps so alerts appear in both analyze and update commands - Group all API compatibility alerts into a single 'API Compatibility Alerts' section instead of individual warnings - Display packages that import updated dependencies for manual review - Example: when updating go.opentelemetry.io/otel/sdk, knative.dev/pkg and other packages that import it are flagged in one section
Add a note in the error message directing users to review packages listed in the 'API Compatibility Alerts' section and add them to the update command if needed. This makes the connection clear between the flagged packages and the suggested update command.
…ommand API compatibility alerts indicate packages that import updated dependencies and need to be co-updated. Include them in the 'add these packages' suggestion with their current versions so omnibump makes the requirement clear. Example: when updating go.opentelemetry.io/otel/sdk, knative.dev/pkg is now included in the suggested command since it imports the updated dependency.
Format the checkMissingTransitiveDeps error message with clear section headers, decorative lines, and one package per line instead of wrapping a long command. Improves readability of required co-updates, API compatibility alerts, and suggested update commands.
…utput Add tests that verify actual functionality: - TestCheckMissingTransitiveDeps_FormattedOutput: ensures error message includes formatted sections - TestGolang_Update_WithDuplicatePackagesDeduplicates: verifies update succeeds with duplicate packages - TestRecommendStrategy_DeduplicatesDirectUpdates: tests deduplication in strategy phase - Existing TestDeduplicateDependencies and TestCheckAPICompatibility provide comprehensive coverage All tests pass (264 in golang package, all tests pass across project)
tcnghia
reviewed
Apr 13, 2026
The transitive-dependency-detection.md file covers the same content and is referenced throughout the documentation. Consolidating to a single doc.
Implement goModCache to cache fetched go.mod files during analysis. This significantly reduces HTTP round trips when checking API compatibility across multiple packages. With 50 direct dependencies and 10 packages being updated: - Before: 500 HTTP requests (10 × 50) - After: ~50-100 HTTP requests (cached after first fetch) Addresses tcnghia's concern about excessive HTTP round trips.
Replace sequential HTTP calls with batch pre-fetching that dramatically reduces total HTTP requests. Instead of fetching dependencies separately for each package being updated, collect all unique dependencies upfront and fetch them once in parallel with a semaphore-limited worker pool. Impact with 50 direct dependencies and 10 packages being updated: - Before: 500 sequential HTTP calls (10 packages × 50 dependencies) - After: ~50 parallel HTTP calls (all dependencies fetched once) Uses semaphore pooling to limit concurrency to 10 parallel requests and avoid overwhelming the Go module proxy.
Add TestGoModCache_BasicOperations to verify the caching mechanism works correctly for storing and retrieving go.mod files. Tests verify: - Cache starts empty - Key generation with package@version format - Values can be stored and retrieved - Multiple packages can be cached independently Signed-off-by: Kyle Steere <kyle.steere@chainguard.dev>
f9e6b69 to
6596db1
Compare
tcnghia
reviewed
Apr 14, 2026
…rency Remove goroutine/semaphore pattern from preFetchDependencies. The concurrent fetching adds complexity without meaningful performance benefit since we call preFetchDependencies once per analysis. Changes: - Convert goModCache from struct wrapper to idiomatic type alias - Change from *goModCache pointer to value type throughout - Replace complex goroutine+semaphore pattern with simple sequential loop - Update method receivers from pointer to value receivers (correct for map types) - Remove obsolete TODO about batching (now addressed by batch pre-fetch) - Reduce code from 51 lines removed, 29 added (net -22 lines) Still achieves 90% reduction in HTTP calls (50 vs 500) through batch pre-fetching. All 266 tests pass.
tcnghia
approved these changes
Apr 15, 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.
Fixes several critical issues with omnibump's Go module analyzer. When updating packages like oras.land/oras-go that require newer versions of transitive dependencies, omnibump had no way to detect those requirements and builds would silently break. Additionally, omnibump was hanging on large workspace projects, producing duplicate packages in output, and displaying error messages that wrapped awkwardly across terminal lines.
This PR detects transitive dependency requirements by fetching the target package's go.mod from the Go proxy and flags packages needing co-updates. It eliminates duplicates by keeping the highest semver version and detects API compatibility issues to flag downstream packages like knative.dev/pkg that import updated dependencies. Error messages are reformatted with clear visual sections (REQUIRED CO-UPDATES, API COMPATIBILITY ALERTS, SUGGESTED UPDATE COMMAND) with each package on its own line. Workspace hanging is fixed by adding GOWORK=off, HTTP timeouts, and version scan caps. DirectUpdates are now sorted alphabetically. End-to-end testing with knative-eventing confirms all functionality works and the project builds successfully.