Skip to content

go_modules: run go list -m all after workspace updates to stabilize committed go.work.sum on Go < 1.28 #16518

Open

Description

@koba1t

Is there an existing issue for this?

  • I have searched the existing issues

Package ecosystem

gomod

Package manager version

Go command 1.27.1

Language version

The workspace declares:

go 1.27.0

toolchain go1.27.1

Manifest location and content before the Dependabot update

This repository commits both go.work and go.work.sum:

Base commit used by both reproducing PRs:

kubernetes-sigs/kustomize@0b060a1

The workspace contains multiple modules. Their go.mod and go.sum files are also committed.

dependabot.yml content

Full file:

https://github.com/kubernetes-sigs/kustomize/blob/0b060a1ff5d6356673765b614b2fe434a4c4cb72/.github/dependabot.yml

version: 2
updates:
- package-ecosystem: gomod
  directory: "/"
  schedule:
    interval: "weekly"
  open-pull-requests-limit: 10

Updated dependency

Two public Dependabot PRs reproduce the problem:

  1. k8s.io/cli-runtime 0.37.0 -> 0.37.1
    • Head commit: 7687fd72f8ec4196de6ede1bf2a8068404d2a25e
  2. k8s.io/apimachinery 0.37.0 -> 0.37.1
    • Head commit: 62a15c17c2fc23b0c6a1bd8ef647ea67ccabaef9

What you expected to see, versus what you actually saw

Expected

For a repository that commits go.work and go.work.sum, a Dependabot workspace update should include the go.work.sum entries needed to load the complete updated workspace module graph.

After checking out a generated PR, the following should be a no-op:

go list -m all > /dev/null
git diff --exit-code

Actual

Both Dependabot PRs leave go.work.sum incomplete.

The repository's CI runs make workspace-sync, which performs:

go work sync
./hack/doGoMod.sh tidy
go list -m all > /dev/null

go work sync followed by go mod tidy for every workspace module produces no diff. The final go list -m all command is the command that modifies go.work.sum.

For PR #6297, it adds:

+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+k8s.io/streaming v0.37.1/go.mod h1:APlJR26ZWRcVy5bIEj0QRrKUXROtBHPcxl2NT7EAzPU=

For PR #6298, it adds:

+k8s.io/streaming v0.37.1/go.mod h1:APlJR26ZWRcVy5bIEj0QRrKUXROtBHPcxl2NT7EAzPU=

As a result, the check-synced-go-modules CI job fails even though the other Go test jobs pass.

This is the behavior described by golang/go#63901. The Go fix, CL 815760, is targeted at Go 1.28. Its regression test verifies that go list -m all no longer changes go.work.sum after go work sync.

Repositories and Dependabot updates using Go 1.27 and earlier still need compatibility behavior.

Dependabot's current workspace updater runs go work sync, optionally tidies the workspace modules, and then collects their files and go.work.sum, but it does not load the complete module list before collecting the result:

sig { returns(T::Hash[String, String]) }
def update_workspace_files
in_repo_path do
dependency_files.each do |file|
path = Pathname.new(file.name).expand_path
FileUtils.mkdir_p(path.dirname)
File.write(path, file.content)
end
# Run `go get dep@version` in each module directory so every go.mod
# that requires the dependency gets the version bump, not just the first.
# Follow with a bare `go get` validation pass per module, matching the
# single-module update path's intent (see run_go_get comment).
workspace_module_paths.each do |mod_dir|
Dir.chdir(mod_dir) do
run_go_get(dependencies)
run_go_get
end
end
run_go_work_sync
run_workspace_tidy
collect_workspace_file_contents
end
end
sig { void }
def run_go_work_sync
command = "go work sync"
_, stderr, status = Open3.capture3(command)
return if status.success?
handle_subprocess_error(stderr)
end
sig { void }
def run_workspace_tidy
return unless tidy?
workspace_module_paths.each do |mod_path|
Dir.chdir(mod_path) do
run_go_mod_tidy(context: mod_path)
end
end
end
sig { returns(T::Array[String]) }
def workspace_module_paths
go_work_file = dependency_files.find { |f| f.name.end_with?("go.work") }
return ["."] unless go_work_file
fetched_mod_names = dependency_files.select { |f| f.name.end_with?("go.mod") }
.to_set(&:name)
GoWorkParser.use_paths(T.must(go_work_file.content))
.select { |p| valid_workspace_path?(p) && fetched_mod_names.include?(workspace_mod_name(p)) }
.map { |p| p == "." ? "." : "./#{p}" }
end
sig { params(path: String).returns(T::Boolean) }
def valid_workspace_path?(path)
return false if Pathname.new(path).absolute?
!Pathname.new(path).cleanpath.to_s.start_with?("../")
end
sig { params(use_path: String).returns(String) }
def workspace_mod_name(use_path)
use_path == "." ? "go.mod" : "#{use_path}/go.mod"
end
sig { returns(T::Hash[String, String]) }
def collect_workspace_file_contents
results = T.let({}, T::Hash[String, String])
workspace_module_paths.each do |mod_path|
relative_base = mod_path.delete_prefix("./")
mod_file = File.join(mod_path, "go.mod")
if File.exist?(mod_file)
key = relative_base.empty? || relative_base == "." ? "go.mod" : "#{relative_base}/go.mod"
results[key] = File.read(mod_file)
end
sum_file = File.join(mod_path, "go.sum")
next unless File.exist?(sum_file)
key = relative_base.empty? || relative_base == "." ? "go.sum" : "#{relative_base}/go.sum"
results[key] = File.read(sum_file)
end
results["go.work.sum"] = File.read("go.work.sum") if File.exist?("go.work.sum")
results
end

Suggested compatibility fix

After the workspace modules have been updated and tidied, and before collecting the resulting files, run at the workspace root:

go list -m all

The output can be discarded, but any resulting go.work.sum update should be included in the Dependabot PR. At minimum, this should be done when an existing go.work.sum was fetched as a committed dependency file.

The order is important: this should be the final Go operation after workspace tidy, so it records checksums for the final workspace module graph.

On Go 1.28 and later, this should be a no-op after the fixed go work sync, so keeping it as compatibility behavior would also avoid version-detection logic.

This is not a request to restore go list -m -json all during dependency discovery for every Go project, which was removed in #2880. The proposed scope is the post-update path for an already detected workspace with a committed go.work.sum.

Native package manager behavior

Using either Dependabot PR head with Go 1.27.1:

go work sync
./hack/doGoMod.sh tidy
git diff --exit-code

This succeeds with no diff.

Then:

go list -m all > /dev/null
git diff --exit-code

This fails because only go.work.sum has changed. Re-running go list -m all after applying those checksum entries is stable.

Images of the diff or a link to the PR, issue, or logs

Smallest manifest that reproduces the issue

The two public PR heads above provide deterministic Dependabot reproductions.

The Go issue also contains a reduced testscript reproducer:

golang/go#63901

The Go 1.28 change includes a focused regression test:

https://go.googlesource.com/go/+/b614756d7b7e663f950de75d502462277785a635

Metadata

Metadata

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions