Configuring Renovate to only suggest updates that match your go directive.

In the Go ecosystem, there are two key parts of the go.mod to note (aside from the dependencies themselves) which are the go and toolchain directives.
The go directive allows specifying the minimum version of the Go language that needs to be used to work with this module.
The toolchain directive allows specifying the minimum version of the Go toolchain that needs to be run to build the module, which also controls the version of Go's standard library that is used with the module (and therefore which security patches the standard library has applied).
In Go 1.21, the Go team introduced toolchain, and moved the go directive from being a "suggested" minimum version to a mandatory version. Splitting the two of these allow you to use a newer version of Go (for instance on your local machine, or as part of your Docker image) while still being clear on what the compatibility of the code should be and allowing for code from a much older version of Go to be used, and it allows for two levels of control over how a given module is used.
When building applications (such as command-line tools that users install separately, or when building web services), it's common to keep the go directive as high as the author would like, so you can take advantage of new features of Go, and the toolchain as high as possible, to make sure you're receiving security patches.
When building a library (or tool that is a dependency in the project, for instance via go tool), the inverse is true - it is common to be quite reserved with the go directive, and not set the toolchain directive at all.
This comes out of a "ripple effect" that can occur when a library updates its go directive, requiring each of its consumers to also update their own go directive. Because Go requires the go directive to be a minimum version, if a dependency was to require a new minor version of Go, it means each and every of its dependencies also need to do so, at the point they upgrade to the new version.
For instance, with oapi-codegen, we recommend the use of go tool as the means to track us as a tool dependency, which means that we appear in the go.mod of our user's projects, and each time we require a new Go version, that also means all of our users must do the same. To avoid this, we aim to sparingly update the go directive, which reduces the fancy new Go language features we can use, but does give us the consistency for our users.
For the most part, we're using versions of Go that are now officially unsupported, but in some organisations, it requires a fair bit of effort to (technically or organisationally) update the Go versions across the org, so being quite reserved with our go directive is something we've had much positive feedback about.
Long before I started working upstream officially, I had been using Renovate with oapi-codegen to update our dependencies, and it was doing the job well. But as new dependency updates started upgrading their go directive, this started leading to a lot of PRs from Renovate that we wouldn't be able to merge.
We had a few, slightly hacky, alternatives we could apply, but I felt there would be a better way, and about a month ago I started looking into what options we would have for this.
As of last week Renovate 43.159.2 is now able to only raise updates to dependencies that stay within the go directive's minor version.
This allows you to write Renovate config such as:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"packageRules": [
{
"description": "Ensure that dependency updates do not bump the `go` directive, and only suggests updates that stay within the `go` directive (allowing a patch bump at most)",
"matchManagers": [
"gomod"
],
"constraintsFiltering": "strict"
}
]
}
With this, we take advantage of constraintsFiltering=strict to perform filtering on the Datasource (in this case, the Go module proxy, or a custom proxy) and look up the go.mod for the new version, skipping the update if it doesn't match the current minor version.
Note that - as the docs suggest - constraintsFiltering=strict is strict. It is worth considering if there are any trade-offs you're unwilling to make, such as no longer receiving security update PRs, if they were to bump the go directive.
This is something that I'm very much enjoying already, and it's taken us from ~40 pending PRs to ~10, and now gives us only the actionable PRs!