Skip to content

Fix gomod tag resolution for monorepos with path-prefixed tags#15121

Open
alkalescent wants to merge 1 commit into
dependabot:mainfrom
alkalescent:fix/gomod-monorepo-tag-prefix
Open

Fix gomod tag resolution for monorepos with path-prefixed tags#15121
alkalescent wants to merge 1 commit into
dependabot:mainfrom
alkalescent:fix/gomod-monorepo-tag-prefix

Conversation

@alkalescent
Copy link
Copy Markdown

What are you trying to accomplish?

In Go monorepos with multiple independently versioned modules (like opentdf/platform), Dependabot generates wrong compare links and includes release notes from the wrong component in PR descriptions.

The root cause: when two modules share the same version number (e.g., otdfctl/v0.31.0 and protocol/go/v0.31.0), CommitsFinder and ReleaseFinder can't disambiguate the tags and fall back to the shortest or first match. This produces compare links like protocol/go/v0.30.0...otdfctl/v0.31.0 instead of the correct protocol/go/v0.30.0...protocol/go/v0.31.0.

This is the gomod equivalent of #11286 which fixed the same bug for the github_actions ecosystem.

Fixes #15119

Anything you want to highlight for special attention from reviewers?

The fix is split across three layers, all additive:

  1. GoModules MetadataFinder now derives the module's subdirectory path from the dependency name and sets source.directory. For github.com/opentdf/platform/protocol/go, this is "protocol/go". Root-level modules get no directory set, so existing behavior is preserved.

  2. CommitsFinder gets a new preferred_tag_by_directory helper that tries to match tags starting with the source.directory prefix. This sits between the existing include?(dependency.name) check and the tags.first fallback, so it only activates when the directory is set.

  3. ReleaseFinder gets a similar fallback in all_dep_releases that filters releases by the directory prefix before falling through to returning all releases.

I considered putting all the logic in the gomod-specific MetadataFinder, but the tag selection happens in the common layer's CommitsFinder and ReleaseFinder. Overriding those at the ecosystem level would have required subclassing or monkey-patching, which felt heavier than adding a clean fallback path. The common layer changes only activate when source.directory is set, so other ecosystems are unaffected.

Note that go_modules is intentionally NOT added to PACKAGE_MANAGERS_WITH_RELIABLE_DIRECTORIES. This means part_of_monorepo? stays false and the compare URL format remains compare/tag1...tag2 (not the directory-scoped commits view).

How will you know you've accomplished your goal?

The PR that originally surfaced this issue (opentdf/platform#3496) shows otdfctl: v0.31.0 release notes in a PR about protocol/go. With this fix, the tag resolution would correctly pick protocol/go/v0.31.0 and only show the relevant release notes.

I've added tests for all three changes:

  • MetadataFinder spec: verifies source.directory is set for subdirectory modules and nil for root modules
  • CommitsFinder spec: verifies the compare URL uses the correct path-prefixed tags in a Go monorepo scenario
  • ReleaseFinder spec: verifies only the correct module's releases are included when filtering by directory prefix

Checklist

  • I have run the complete test suite to ensure all tests and linters pass.
  • I have thoroughly tested my code changes to ensure they work as expected, including adding additional tests for new functionality.
  • I have written clear and descriptive commit messages.
  • I have provided a detailed description of the changes in the pull request, including the problem it addresses, how it fixes the problem, and any relevant details about the implementation.
  • I have ensured that the code is well-documented and easy to understand.

In Go monorepos where multiple modules share the same version number
(e.g., otdfctl/v0.31.0 and protocol/go/v0.31.0), Dependabot's
CommitsFinder and ReleaseFinder pick the wrong tag for compare links
and release notes. The existing heuristic falls back to the shortest
matching tag, which may belong to a different component.

This commit fixes the issue by:

1. Setting source.directory in GoModules::MetadataFinder to the
   module's subdirectory path (e.g., "protocol/go" for
   github.com/owner/repo/protocol/go).

2. Adding a preferred_tag_by_directory fallback in CommitsFinder that
   matches tags by the source directory prefix before falling back to
   the first tag.

3. Adding a similar directory-based filter in ReleaseFinder's
   all_dep_releases to scope release notes to the correct module.

This is the gomod equivalent of dependabot#11286, which fixed the same class of
bug for the github_actions ecosystem.

Fixes dependabot#15119

Signed-off-by: Krish Suchak <suchak.krish@gmail.com>
@alkalescent alkalescent requested a review from a team as a code owner May 22, 2026 18:51
Copilot AI review requested due to automatic review settings May 22, 2026 18:51
@github-actions github-actions Bot added the L: go:modules Golang modules label May 22, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR improves Go module metadata discovery for monorepos by recognizing subdirectory modules and using path-prefixed tags/releases when generating changelogs and compare URLs.

Changes:

  • Set source.directory for Go module dependencies that live in a repository subdirectory.
  • Prefer tags/releases that match the Go module subdirectory prefix (e.g., protocol/go/vX.Y.Z) when dependency-name-prefixed tags aren’t available.
  • Add fixtures and specs covering path-prefixed releases/tags for a Go monorepo.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
go_modules/lib/dependabot/go_modules/metadata_finder.rb Derives and sets source.directory for subdirectory Go modules in monorepos.
go_modules/spec/dependabot/go_modules/metadata_finder_spec.rb Adds specs validating source.directory behavior for subdirectory vs root modules.
common/lib/dependabot/metadata_finders/base/release_finder.rb Filters GitHub releases by module directory prefix when dependency-name prefix isn’t present.
common/lib/dependabot/metadata_finders/base/commits_finder.rb Prefers directory-prefixed tags when selecting compare endpoints for monorepo modules.
common/spec/dependabot/metadata_finders/base/release_finder_spec.rb Adds spec ensuring only the correct module’s releases are included.
common/spec/dependabot/metadata_finders/base/commits_finder_spec.rb Adds spec ensuring compare URLs use the correct directory-prefixed tags.
common/spec/fixtures/github/go_monorepo_releases.json Adds fixture data for a monorepo with directory-prefixed release tags.

Comment on lines +25 to +28
repo_import_path = url.gsub(%r{^https?://}, "").chomp("/").chomp(".git")
if dependency.name.start_with?("#{repo_import_path}/")
source.directory = dependency.name.delete_prefix("#{repo_import_path}/")
end
Comment on lines +113 to +120
if source&.directory && ![".", "/"].include?(source.directory)
releases_with_dir_prefix =
releases
.reject { |r| r.tag_name.nil? }
.select { |r| r.tag_name.start_with?("#{T.must(source).directory}/") }

return releases_with_dir_prefix if releases_with_dir_prefix.any?
end
Comment on lines +88 to +90
tags.find { |t| t.include?(dependency.name) } ||
preferred_tag_by_directory(tags) ||
tags.first
Comment on lines +110 to +112
tags.find { |t| t.include?(dependency.name) } ||
preferred_tag_by_directory(tags) ||
tags.first
Comment on lines +127 to +129
tags.find { |t| t.include?(dependency.name) } ||
preferred_tag_by_directory(tags) ||
tags.first
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

L: go:modules Golang modules

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gomod: wrong tag resolved for Go modules in monorepos with path-prefixed tags

2 participants