feat(gomod): add gomodTidyAll option for indirect dependency updates in Go monorepos - #37138
feat(gomod): add gomodTidyAll option for indirect dependency updates in Go monorepos#37138ducthinh993 wants to merge 7 commits into
gomodTidyAll option for indirect dependency updates in Go monorepos#37138Conversation
gomodTidyAll option for indirect dependency updates in Go monorepos #36848
gomodTidyAll option for indirect dependency updates in Go monorepos #36848gomodTidyAll option for indirect dependency updates in Go monorepos
There was a problem hiding this comment.
Pull Request Overview
This PR implements the gomodTidyAll option for Go monorepos to handle indirect dependency updates across modules with local replace directives. The implementation adds graph-based dependency resolution to determine the proper update order for interdependent Go modules.
- Adds a new
package-tree.tsmodule for graph-based dependency resolution and topological ordering - Integrates
gomodTidyAlllogic into the artifacts processing pipeline to update dependent modules in dependency order - Updates documentation and configuration to include the new
gomodTidyAlloption
Reviewed Changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| lib/modules/manager/gomod/readme.md | Adds documentation for the new gomodTidyAll post-update option |
| lib/modules/manager/gomod/package-tree.ts | New module implementing graph-based dependency resolution for Go modules |
| lib/modules/manager/gomod/package-tree.spec.ts | Unit tests for the package-tree functionality |
| lib/modules/manager/gomod/artifacts.ts | Integrates gomodTidyAll processing and adds functions to tidy dependent modules |
| lib/modules/manager/gomod/artifacts.spec.ts | Tests for the new gomodTidyAll functionality in artifacts processing |
| lib/config/options/index.ts | Adds gomodTidyAll to the list of valid post-update options |
| docs/usage/golang.md | Updates Go documentation to describe the gomodTidyAll option behavior |
Comments suppressed due to low confidence (4)
lib/modules/manager/gomod/package-tree.ts:58
- [nitpick] The variable name
depsis ambiguous in this context. Consider renaming it tovisitedModulesordependentModuleFlagsto better reflect that it contains module names with their leaf status flags.
return Array.from(deps).map(([name, isLeaf]) => ({ name, isLeaf }));
lib/modules/manager/gomod/package-tree.ts:68
- [nitpick] The parameter name
depsis ambiguous. Consider renaming it tovisitedModulesormoduleFlagsto better indicate it's a map tracking visited modules and their leaf status.
): void {
lib/modules/manager/gomod/package-tree.ts:84
- The variable name
depis misleading here as it represents a dependent module, not a dependency. Consider renaming it todependentordependentModulefor clarity.
for (const dep of dependents) {
lib/modules/manager/gomod/package-tree.ts:85
- The variable name
depshould bedependentanddepsshould be renamed tovisitedModulesfor consistency with the previous naming suggestion.
recursivelyGetDependentGoModFiles(dep, graph, deps);
|
Hi @viceice , could you please help review? This issue is also blocking our organization. Thanks |
|
needs deconflicting |
a91d263 to
1f5f019
Compare
100f76e to
0bc6cea
Compare
0bc6cea to
749946e
Compare
c33d932 to
9ee3560
Compare
9ee3560 to
9a8f320
Compare
|
Hello @viceice, could you please kindly help review the PR? Thanks |
…s in Go monorepos In Go monorepos it is common for one module to depend on another in the same repo via a local `replace` directive (e.g. `replace example.com/shared => ../shared`). When Renovate updates the shared module, today `go mod tidy` runs only in that module, leaving dependent `go.sum` files stale. Add a new `gomodTidyAll` post-update option that: - Discovers every `go.mod` in the repo and parses its local `replace` directives (both inline and block form). - Builds a dependency graph with `graph-data-structure`, with edges from dependency → dependent, matching the existing NuGet convention. - Runs `go mod tidy` on every transitive dependent in topological order, propagating any `gomodTidy1.17` / `gomodTidyE` flags to the dependent commands. - Collects updated `go.mod` and `go.sum` files from each dependent directory into the artifact result. - Implies `gomodTidy` so users only need to enable one option. Supporting changes: - Extract `getMatchingFiles` and `resolveRelativePathToRoot` into `lib/util/fs/util.ts` so NuGet and gomod can share them instead of each having its own glob/resolve helper. - Refactor `lib/modules/manager/nuget/package-tree.ts` to use the shared `getMatchingFiles` helper and rename local variables for clarity (`deps` → `visited`, `dep` → `dependent`) per review feedback. Docs and config: - Register `gomodTidyAll` in `postUpdateOptions` `allowedValues`. - Add a row for `gomodTidyAll` to the `postUpdateOptions` table in `docs/usage/configuration-options.md`. - Add a "Monorepo tidying for local `replace` directives" subsection to `docs/usage/golang.md` describing when to use the option. Tests and coverage: - `lib/modules/manager/gomod/package-tree.spec.ts` — parse/graph/topo order, 100% line/branch/function coverage. - `lib/modules/manager/gomod/artifacts-gomodtidyall.spec.ts` — six integration cases covering subshell commands, `tidyOpts` propagation, result collection, no-op with no dependents, error handling, and the disabled path. - Full `lib/modules/manager/gomod` suite (151 tests) and NuGet package-tree suite (9 tests) pass; new code is fully covered. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
019982c to
8b5a63e
Compare
|
@ducthinh993 Could you please resolve the conflicts and review the feedback? |
# Conflicts: # docs/usage/configuration-options.md
A subshell only works in Docker mode, where all commands are concatenated into a single `bash -l -c`. With `binarySource=install` or `global` each command is split with shlex and run without a shell, so `(cd dir && ...)` is treated as the name of a binary.
A single regex now covers both the single line form and the lines inside a `replace (...)` block. The previous block pattern stopped at the first `)`, so a block containing a comment with a bracket was skipped, and the single line pattern did not allow a trailing comment. Also keeps the NuGet package tree unchanged apart from the two helpers it now shares.
Massaging comments out the relative `replace` directives that `gomodTidyAll` follows, so the two options work against each other.
|
@secustor Could you please re-review? |
|
Hi there, This is intended as a polite, automated request that users avoid We know it might be common elsewhere but we participate in hundreds of discussions a week and would need to turn off GitHub mobile notifications if we were mentioned in every one. As a general rule, we will read and respond to all discussions in this repository, so there is no need to mention us. Thanks, the Renovate team |
06ca274 to
989642c
Compare
Changes
Adds a
gomodTidyAllpost-update option for Go monorepos that use localreplacedirectives.When a shared module is updated in a Go monorepo,
go mod tidycurrently runs only on the directly updated module.Dependent modules that reference it through a local
replacedirective (for examplereplace shared => ../shared) keep a stalego.sum, which breaks their build.gomodTidyAllsolves this by:go.modfiles and their localreplacedirectivesgo mod tidycommand for each dependent module, in topological order (dependencies before dependents)New file
lib/modules/manager/gomod/package-tree.tsexportsgetGoModulesInTidyOrder(), which returns the transitive dependents of ago.modin topological order.It builds the graph from
parseLocalReplacePaths(), which reads the target of everyreplacedirective that points at a local directory.A single regex covers both the single line form and the lines inside a
replace (...)block, and skips comment lines.Two path helpers move out of the NuGet manager into
lib/util/fs/util.tsso both managers share them:resolveRelativePathToRoot()— resolves a path reference that is relative to a package file into a repo-relative path, used by NuGet forProjectReferenceand by Go forreplacegetMatchingFiles()— filters a repo file list by a minimatch patternlib/modules/manager/nuget/package-tree.tscalls these two helpers instead of its own copies, and is otherwise unchanged.Key behaviors
execCommandsarray, so everything still runs in oneexeccall.This matters in Docker mode, where every extra
execstarts a new containergo -C <dir> mod tidy, so the commands need no shell.A subshell would only work in Docker mode, where all commands are concatenated into a single
bash -l -c.With
binarySource=installorglobaleach command is split with shlex and run without a shell.This means the option needs Go 1.20 or later
gomodTidyAllalone impliesgomodTidyon the primary modulegomodMassageonly rewrites thego.modof the updated module, and Go ignoresreplacedirectives outside the main module, so the tidy commands for the dependent modules are unaffected by it.The two options still work against each other, because massaging comments out the relative
replacedirectives that this option follows, so the docs tell users not to combine themtidyOpts(-compat=1.17,-e) propagate to the dependent module tidy commandsUsage:
{ "postUpdateOptions": ["gomodTidyAll"] }Context
AI assistance disclosure
Did you use AI tools to create any part of this pull request?
Please select one option and, if yes, briefly describe how AI was used (e.g., code, tests, docs) and which tool(s) you used.
Documentation (please check one with an [x])
How I've tested my work (please select one)
I have verified these changes via:
Test coverage:
lib/util/fs/util.spec.ts— 8 new tests forresolveRelativePathToRootandgetMatchingFileslib/modules/manager/gomod/package-tree.spec.ts— 5 tests for localreplaceparsing and tidy orderinglib/modules/manager/gomod/artifacts-gomodtidyall.spec.ts— 6 tests for the integration (dependent modules, error handling,tidyOptspropagation, standalonegomodTidyAll, feature disabled)pnpm check --allpasses, and the new code is at 100% coverage