Files
renovate/lib/modules/manager/gomod/readme.md
Thinh Nguyen ab105e6e42 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

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>

* refactor(gomod): use named capture groups and direct fs util imports

* fix(gomod): use `go -C` for dependent module tidy commands

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.

* refactor(gomod): simplify parsing of local `replace` directives

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.

* docs(gomod): warn against combining `gomodTidyAll` with `gomodMassage`

Massaging comments out the relative `replace` directives that
`gomodTidyAll` follows, so the two options work against each other.

* refactor(fs): use `matchRegexOrGlob` in `getMatchingFiles`

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-13 08:35:38 +00:00

124 lines
6.3 KiB
Markdown

### Updating of Go mod and toolchain directives
In `go.mod`, the `go` directive essentially means "Compatible with this version or later".
It is generally recommended for Go projects to _not_ bump this version unless necessary, and therefore Renovate's default behavior is not to propose upgrades to it.
If you need to update it once or irregularly, it's recommended to do it manually.
If you are sure you want to always bump it to latest, then you need the following configuration:
```json
{
"packageRules": [
{
"matchManagers": ["gomod"],
"matchDepNames": ["go"],
"matchDepTypes": ["golang"],
"rangeStrategy": "bump"
}
]
}
```
In `go.mod`, the `toolchain` directive essentially means "Use this exact version of go".
Unlike the `go` directive, it's valid to keep bumping this, and you should see updates to it proposed by default.
### Only suggesting updates that match your `go` directive
In `go.mod`, the `go` directive essentially means "Compatible with this version or later".
It is generally recommended for Go projects to _not_ bump this version unless necessary.
As [noted above](#updating-of-go-mod-and-toolchain-directives), Renovate doesn't update this directly, but dependency updates can lead to a package you rely on bumping their `go` directive, which means you can't accept the proposed update without also bumping your `go` directive.
To make this simpler, you can use [`constraintsFiltering=strict`](../../../configuration-options.md#constraintsfiltering) to make sure that Renovate will only propose updates that match the [`constraints`](../../../configuration-options.md#constraints) you specify.
For example, given the `go.mod`:
```gomod
module example
go 1.24.0
require golang.org/x/mod v0.32.0
```
We can then configure Renovate to set the same constraint when updating any Go dependencies:
```json
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"packageRules": [
{
"description": "Ensure source compatibility with Go version used by this project",
"matchManagers": ["gomod"],
"constraintsFiltering": "strict"
}
]
}
```
In this case, Renovate would only propose an update to a version of `golang.org/x/mod` that supports Go 1.24.x.
!!! note
It is recommended to read the [`constraintsFiltering`](../../../configuration-options.md#constraintsfiltering) documentation and [Language constraints and upgrading](../../../language-constraints-and-upgrading.md) for more information about how the filtering works and how _strict_ this can be.
### Post-Update Options
You might be interested in the following `postUpdateOptions`:
1. `gomodTidy` - if you'd like Renovate to run `go mod tidy` after every update before raising the PR
1. This is implicitly enabled for major updates if the user has enabled the option `gomodUpdateImportPaths`
1. `gomodTidy1.17` - if you'd like Renovate to run `go mod tidy -compat=1.17` after every update before raising the PR
1. `gomodTidyE` - if you'd like Renovate to run `go mod tidy -e` after every update before raising the PR
1. `gomodTidyAll` - if you'd like Renovate to also run `go mod tidy` on every other module which depends on the updated one through a local `replace` directive
- This implies `gomodTidy`, needs Go 1.20 or later, and should not be combined with `gomodMassage`
1. `gomodUpdateImportPaths` - if you'd like Renovate to update your source import paths on major updates before raising the PR
1. `gomodMassage` - to enable massaging of all `replace` statements prior to running `go` so that they will be ignored
1. `goGenerate` - to run `go generate ./...` after vendoring.
- Will only run if the [`allowedUnsafeExecutions`](../../../self-hosted-configuration.md#allowedunsafeexecutions) global option includes `goGenerate`
- This will not install any additional tools required to run `go generate`, you can use [tools](https://tip.golang.org/doc/go1.24#tools) as one option
- If `gomodTidy` is not set, you may encounter failures in the Go toolchain due to `missing go.sum entry`
When Renovate is running using `binarySource=install` (such as in the Mend Renovate App) then it will pick the latest compatible version of Go to run, i.e. the latest `1.x` release.
Even if the `go.mod` has a version like `go 1.22`, Renovate will treat it as a `^1.22` constraint and not `=1.22`.
Unless matching `tool` directives exist, indirect updates are disabled by default. To enable them, add a package rule such as:
```json
{
"packageRules": [
{
"matchManagers": ["gomod"],
"matchDepTypes": ["indirect"],
"enabled": true
}
]
}
```
### Private Modules Authentication
Before running the `go` commands to update the `go.sum`, Renovate exports `git` [`insteadOf`](https://git-scm.com/docs/git-config#Documentation/git-config.txt-urlltbasegtinsteadOf) directives in environment variables.
The following logic is executed prior to "artifacts" updating:
The token from the `hostRules` entry matching `hostType=github` and `matchHost=api.github.com` is added as the default authentication for `github.com`.
For those running against `github.com`, this token will be the default platform token.
Next, all `hostRules` with both a token or username/password and `matchHost` will be fetched, except for any github.com one from above.
Rules from this list are converted to environment variable directives if they match _any_ of the following characteristics:
- No `hostType` is defined, or
- `hostType` is `go`, or
- `hostType` is a platform (`github`, `gitlab`, `azure`, etc.)
### Major upgrades of dependencies
Major upgrades in Go are different from most other ecosystems, because both the version and module name need to be changed.
It is very common that such upgrades require changes to application code, which Renovate doesn't do.
By default, Renovate will make such change in the `go.mod` files but nothing else - the rest is up to you.
If you add `gomodUpdateImportPaths` to `postUpdateOptions` then Renovate will also use a third-party tool to migrate import paths within application code, but there may still be actual application logic which needs to be changed too.
Ultimately: it is known and unavoidable that the majority of major Go upgrades won't be immediately mergeable.
You might prefer to configure such major updates with `dependencyDashboardApproval=true` so that you can request them on demand, on supported platforms.