internal/task: read nested module's own go.mod files

As of the last change, it becomes easy to read the nested module's own
go.mod file and determine whether it had a toolchain directive. Before,
it was reusing the top-level go.mod file's decision for nested modules.

I didn't realize earlier there is a way to use the lower-level 'go mod
edit' command to drop a toolchain directive, but it turns out there is.
Switch to it now - it's equivalent but fits slightly better in context.

Fixes golang/go#68873.

Change-Id: I1ea4bfd9e5deb4e72843887d7f8d68a4b2a67f3e
Reviewed-on: https://go-review.googlesource.com/c/build/+/622396
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Dmitri Shuralyov 2024-10-24 12:49:43 -04:00 коммит произвёл Gopher Robot
Родитель fa508ab9e9
Коммит d28f1065dd
2 изменённых файлов: 44 добавлений и 17 удалений

Просмотреть файл

@ -71,7 +71,6 @@ type TagRepo struct {
Name string // Gerrit project name, e.g., "tools".
ModPath string // Module path, e.g., "golang.org/x/tools".
Deps []*TagDep // Dependency modules.
HasToolchain bool // Whether the go.mod file has a toolchain directive when the workflow started.
StartVersion string // The version of the module when the workflow started. Empty string means repo hasn't begun release version tagging yet.
NewerVersion string // The version of the module that will be tagged, or the empty string when the repo is being updated only and not tagged.
}
@ -192,7 +191,6 @@ func (x *TagXReposTasks) readRepo(ctx *wf.TaskContext, project string) (*TagRepo
result := &TagRepo{
Name: project,
ModPath: mf.Module.Mod.Path,
HasToolchain: mf.Toolchain != nil,
StartVersion: currentTag,
}
@ -385,10 +383,6 @@ func (x *TagXReposTasks) UpdateGoMod(ctx *wf.TaskContext, repo TagRepo, deps []T
for _, dep := range deps {
script.WriteString(" " + dep.ModPath + "@" + dep.NewerVersion)
}
if !repo.HasToolchain {
// Don't introduce a toolchain directive if it wasn't already there.
script.WriteString(" toolchain@none")
}
script.WriteString("\n")
// Tidy the root module and nested modules.
@ -418,10 +412,11 @@ func (x *TagXReposTasks) UpdateGoMod(ctx *wf.TaskContext, repo TagRepo, deps []T
if d.Name() == "go.mod" && !d.IsDir() { // A go.mod file.
dir := pathpkg.Dir(path)
dropToolchain := ""
if !repo.HasToolchain {
if had, err := hasToolchain(rootFS, path); err != nil {
return err
} else if !had {
// Don't introduce a toolchain directive if it wasn't already there.
// TODO(go.dev/issue/68873): Read the nested module's go.mod. For now, re-use decision from the top-level module.
dropToolchain = " && go get toolchain@none"
dropToolchain = " && go mod edit -toolchain=none"
}
script.WriteString(fmt.Sprintf("(cd %v && touch go.sum && go mod tidy%s)\n", dir, dropToolchain))
outputs = append(outputs, dir+"/go.mod", dir+"/go.sum")
@ -439,6 +434,20 @@ func (x *TagXReposTasks) UpdateGoMod(ctx *wf.TaskContext, repo TagRepo, deps []T
return buildToOutputs(ctx, x.CloudBuild, build)
}
// hasToolchain parses the specified go.mod file, and
// reports whether it has a toolchain directive in it.
func hasToolchain(fsys fs.FS, goModPath string) (has bool, _ error) {
b, err := fs.ReadFile(fsys, goModPath)
if err != nil {
return false, err
}
f, err := modfile.Parse(goModPath, b, nil)
if err != nil {
return false, err
}
return f.Toolchain != nil, nil
}
func buildToOutputs(ctx *wf.TaskContext, buildClient CloudBuildClient, build CloudBuild) (map[string]string, error) {
if _, err := AwaitCondition(ctx, 10*time.Second, func() (string, bool, error) {
return buildClient.Completed(ctx, build)

Просмотреть файл

@ -255,8 +255,15 @@ case "$1" in
done
;;
"mod")
ls go.mod go.sum >/dev/null
echo "tidied! $*" >> go.mod
case "$2" in
"tidy")
ls go.mod go.sum >/dev/null
echo "tidied! $*" >> go.mod
;;
"edit")
echo "edited! $*" >> go.mod
;;
esac
;;
*)
echo unexpected command $@
@ -307,10 +314,11 @@ func TestTagXRepos(t *testing.T) {
mod.Tag("v1.0.0", mod1)
tools := NewFakeRepo(t, "tools")
tools1 := tools.Commit(map[string]string{
"go.mod": "module golang.org/x/tools\nrequire golang.org/x/mod v1.0.0\ngo 1.18\nrequire golang.org/x/sys v0.1.0\nrequire golang.org/x/build v0.0.0\n",
"go.sum": "\n",
"gopls/go.mod": "module golang.org/x/tools/gopls\nrequire golang.org/x/mod v1.0.0\n",
"gopls/go.sum": "\n",
"go.mod": "module golang.org/x/tools\nrequire golang.org/x/mod v1.0.0\ngo 1.18\nrequire golang.org/x/sys v0.1.0\nrequire golang.org/x/build v0.0.0\n",
"go.sum": "\n",
"gopls/go.mod": "module golang.org/x/tools/gopls\nrequire golang.org/x/mod v1.0.0\n",
"gopls/go.sum": "\n",
"withtoolchain/go.mod": "module golang.org/x/tools/withtoolchain\ngo 1.23.1\ntoolchain go1.23.2\n",
})
tools.Tag("v1.1.5", tools1)
build := NewFakeRepo(t, "build")
@ -364,12 +372,22 @@ func TestTagXRepos(t *testing.T) {
if !strings.Contains(string(goMod), "tidied!") {
t.Error("tools go.mod should be tidied")
}
if !strings.Contains(string(goMod), "edited! mod edit -toolchain=none") {
t.Error("tools go.mod should be edited with -toolchain=none")
}
goplsMod, err := deps.gerrit.ReadFile(ctx, "tools", tag.Revision, "gopls/go.mod")
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(goplsMod), "tidied!") || strings.Contains(string(goplsMod), "upgraded") {
t.Errorf("gopls go.mod should be tidied and not upgraded:\n%s", goplsMod)
if !strings.Contains(string(goplsMod), "tidied!") || !strings.Contains(string(goplsMod), "edited!") || strings.Contains(string(goplsMod), "upgraded") {
t.Errorf("gopls go.mod should be tidied+edited and not upgraded:\n%s", goplsMod)
}
withtoolchainMod, err := deps.gerrit.ReadFile(ctx, "tools", tag.Revision, "withtoolchain/go.mod")
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(withtoolchainMod), "tidied!") || strings.Contains(string(withtoolchainMod), "edited!") {
t.Errorf("withtoolchain go.mod should be tidied and not edited:\n%s", withtoolchainMod)
}
tags, err = deps.gerrit.ListTags(ctx, "build")