Skip to content

Support unvendored repositories: precise go.mod/go.sum dependency diffing (rebased on #73)#71

Open
bts-bastion wants to merge 12 commits into
digitalocean:masterfrom
bts-bastion:bts/go-unvendored
Open

Support unvendored repositories: precise go.mod/go.sum dependency diffing (rebased on #73)#71
bts-bastion wants to merge 12 commits into
digitalocean:masterfrom
bts-bastion:bts/go-unvendored

Conversation

@bts-bastion

@bts-bastion bts-bastion commented Mar 27, 2026

Copy link
Copy Markdown

Summary

Detect go.mod/go.sum changes precisely and mark only the dependent packages
that actually changed, rather than falling back to marking every package when
no vendor/ tree is present.

Rebased onto current master, which includes the single-root go.work support
from #73. The fork's multi-root go.work commit is dropped — #73 supersedes it,
and the cross-module tests added here confirm workspace detection works on the
single-root base without multi-root code. The companion go.work PR is closed.

Changes

Graph construction under module/workspace loads:

  • isLocalPackage / LocalImportersOf on the package context.
  • Skip packages with load errors during dependency-graph construction.
  • Tolerate PackageFromImport errors for unresolvable dependents.
  • Filter non-local dependents out of reverse-graph traversal.

Precise go.mod/go.sum diffing:

  • gomod.go: diff go.mod require directives and go.sum entries.
  • BaseFileReader interface plus SetBaseGoMod / SetBaseGoSum, sourcing old
    module-file content from the git differ or from explicitly supplied files.
  • markedPackages() diffs changed go.mod/go.sum, resolves the affected
    module paths to their local importers, and marks those. The mark-all path
    remains as fallback when no base is available; go.work changes still force
    full re-evaluation.

Path resolution:

  • BaseFileReader.ReadBaseFile takes an absolute path; the git differ resolves
    it against the repository toplevel from diff() and rejects paths outside the
    repo. Removes the roots-based computation, which was correct only when the
    module root coincided with the git toplevel.
  • Regression test pinning the contract.

Tests:

  • Cross-module workspace integration: a change in one module marks dependents in
    sibling modules; an isolated module reports only itself; a go.work change
    marks all workspace packages.

Notes

  • Workspace opt-out is GOWORK=off; there is no gta flag.
  • BaseFileReader is new in this PR, so its absolute-path signature is not a
    breaking change.

Testing

go test -count=1 -race ./... and go vet ./... pass.

These methods enable distinguishing local (main module) packages from
external dependencies and mapping changed dependency modules to the
local packages that import them. Foundation for unvendored repo support.

Co-authored by: Blue Thunder Somogyi
When packages.Load returns packages with errors (common for external
dependencies in unvendored repos), skip them when building the
forward/reverse dependency graphs. Module info is still recorded.

Co-authored by: Blue Thunder Somogyi
When a package in the dependency graph cannot be resolved (e.g., an
external dependency in an unvendored repo), skip it instead of
returning an error from ChangedPackages(). This prevents GTA from
crashing on unvendored repositories.

Co-authored by: Blue Thunder Somogyi
When traversing the dependency graph in markedPackages(), skip edges
to external (non-local) packages. Uses a type assertion so custom
Packager implementations retain the existing traversal behavior.
In GOPATH mode (no modules), all packages are treated as local.

Co-authored by: Blue Thunder Somogyi
New file gomod.go provides diffGoMod() and diffGoSum() for precise
dependency change detection. diffGoMod compares Require and Replace
directives using modfile.Parse with ParseLax fallback. diffGoSum does
line-level comparison to catch transitive dependency changes.

Co-authored by: Blue Thunder Somogyi
BaseFileReader enables reading file content at the git merge-base,
used to retrieve old go.mod/go.sum for dependency diff analysis.
SetBaseGoMod and SetBaseGoSum options provide the same capability
for file-differ mode (no git access).

Co-authored by: Blue Thunder Somogyi
When go.mod or go.sum changes in an unvendored repo, GTA now parses
the diff to identify exactly which dependency modules changed, then
marks only the local packages that import those modules. This replaces
the previous "mark all packages" approach which negated GTA's value.

Falls back to mark-all when base file content is unavailable (e.g.,
custom Differ without BaseFileReader, no SetBaseGoMod option).

go.work changes continue to use mark-all since workspace structural
changes warrant full re-evaluation.

Co-authored by: Blue Thunder Somogyi
…aces

Originally written against the fork's multi-root workspace implementation,
where it demonstrated a false negative: relativeModFilePath() computed
module-relative paths ("go.mod") while BaseFileReader.ReadBaseFile resolves
git-repo-root-relative paths, silently diffing the repo-root go.mod against
the module's new go.mod and marking nothing for a dependency bump.

On this branch (upstream single workspace root = directory containing
go.work) the computed path is correct and the test passes. It remains as a
regression guard for the path-resolution contract, which Task 6 hardens by
moving path resolution into the differ itself.
BaseFileReader.ReadBaseFile now takes an absolute path and the git differ
resolves it against the repo toplevel it already discovers during diff().
This removes relativeModFilePath(), whose g.roots-based computation was
only correct when the workspace/module root coincided with the git
toplevel, and rejects paths outside the repository.
Ported from the fork's go.work support work: verifies that in a Go
workspace, a change in one module marks dependent packages in sibling
modules, an isolated module reports only itself, and a go.work change
marks all workspace packages. These pass against upstream's single-root
workspace handling (PR digitalocean#73) without any multi-root machinery; gta relies
on packages.Load workspace awareness for cross-module resolution.
@bts-bastion bts-bastion changed the title Add unvendored repository support with precise dependency diff analysis Support unvendored repositories: precise go.mod/go.sum dependency diffing (rebased on #73) Jun 16, 2026
@bts-bastion

Copy link
Copy Markdown
Author

Revised to build on #73

Wrap the toplevel() error with %w so an inconsistent-vendoring error
(from `go list -m`) surfaces to the caller instead of being replaced
with a generic "could not get top level directory" message.
packages.Load reports Module.Dir with symlinks resolved, but the git
differ root, GOWORK-derived roots, and caller-supplied differ paths may
be un-resolved. On systems where the repo path differs from its resolved
form (macOS /tmp -> /private/tmp, container bind mounts, automounts) the
equality/prefix comparisons in resolveLocal and isIgnoredByGo never
match, so a changed package silently resolves to nothing.

Add canonicalDir (EvalSymlinks with a deepest-existing-ancestor fallback
for deleted dirs) and normalize incoming paths in resolveLocal, the git
differ root, and roots before comparison.
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.

1 participant