internal/task: extend best-effort updating to nested modules (part 1 of 2)

Changing the workflow to tag nested golang.org/x modules is hard and
not something I plan to consider now. On the other hand, including the
nested golang.org/x modules in scope of best-effort updating is easy.
Do that, since there's no good reason to treat them differently from
top-level golang.org/x modules.

For golang/go#73264.

Change-Id: I7b7543602200ccb7801eb6e39e028f9838aa739b
Reviewed-on: https://go-review.googlesource.com/c/build/+/681438
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/internal/task/tagx.go b/internal/task/tagx.go
index 5ec38f6..045015b 100644
--- a/internal/task/tagx.go
+++ b/internal/task/tagx.go
@@ -172,14 +172,14 @@
 		return nil, err
 	}
 
-	// TODO(heschi): ignoring nested modules for now. We should find and handle
-	// x/exp/event, maybe by reading release tags? But don't tag gopls...
+	// TODO(dmitshur,heschi): ignoring nested modules for purposes of tagging for now.
+	// We should find and handle x/exp/event, maybe by reading release tags? But don't tag gopls...
 	isXRoot := func(path string) bool {
 		return strings.HasPrefix(path, "golang.org/x/") &&
 			!strings.Contains(strings.TrimPrefix(path, "golang.org/x/"), "/")
 	}
 	if !isXRoot(mf.Module.Mod.Path) {
-		ctx.Printf("ignoring %v: not golang.org/x", project)
+		ctx.Printf("ignoring %v: not a golang.org/x root module", project)
 		return nil, nil
 	}
 
@@ -194,21 +194,33 @@
 		StartVersion: currentTag,
 	}
 
+	// Collect module dependencies to update.
 	for _, req := range mf.Require {
-		if !isXRoot(req.Mod.Path) {
+		if !strings.HasPrefix(req.Mod.Path, "golang.org/x/") {
+			// At this time, we only apply automatic module version updates to all golang.org/x modules.
+			// We own them fully and can be certain newer versions have gone through our review process.
+			// Skip others.
 			continue
 		} else if x.IgnoreProjects[strings.TrimPrefix(req.Mod.Path, "golang.org/x/")] {
 			ctx.Printf("Dependency %v is ignored", req.Mod.Path)
 			continue
 		}
 		wait := true
-		for _, c := range req.Syntax.Comments.Suffix {
+		if !isXRoot(req.Mod.Path) {
+			ctx.Printf("not waiting on %v: not a golang.org/x root module", project)
+			wait = false
+		} else {
 			// We have cycles in the x repo dependency graph. Allow a magic
-			// comment, `// tagx:ignore`, to exclude requirements from
-			// consideration.
-			if strings.Contains(c.Token, "tagx:ignore") {
-				ctx.Printf("ignoring %v's requirement on %v: %q", project, req.Mod, c.Token)
-				wait = false
+			// comment, `// tagx:ignore`, to not wait on those requirements.
+			//
+			// A better name by now would be "tagx:notwait" or so, but it's
+			// not worth renaming all of its instances.
+			for _, c := range req.Syntax.Comments.Suffix {
+				if strings.Contains(c.Token, "tagx:ignore") {
+					ctx.Printf("not waiting on %v's requirement on %v: %q", project, req.Mod, c.Token)
+					wait = false
+					break
+				}
 			}
 		}
 		result.DepsToUpdate = append(result.DepsToUpdate, &TagDep{
diff --git a/internal/task/tagx_test.go b/internal/task/tagx_test.go
index 11fdbfa..6026e20 100644
--- a/internal/task/tagx_test.go
+++ b/internal/task/tagx_test.go
@@ -325,6 +325,9 @@
 	// The x/build repo isn't being tagged.
 	golang.org/x/build v0.0.0
 
+	// An example of a nested golang.org/x module.
+	golang.org/x/exp/event v0.0.0
+
 	// An example of a tagx:ignore'd repo.
 	golang.org/x/net v0.21.0 // tagx:ignore
 
@@ -387,8 +390,9 @@
 		t.Errorf("tools should use sys v0.2.0 and mod v1.0.0. go.mod: %v", string(goMod))
 	}
 	if !strings.Contains(string(goMod), "we've upgraded to golang.org/x/build@upgrade") ||
-		!strings.Contains(string(goMod), "we've upgraded to golang.org/x/net@upgrade") {
-		t.Errorf("tools should have upgraded x/build and x/net: %v", string(goMod))
+		!strings.Contains(string(goMod), "we've upgraded to golang.org/x/net@upgrade") ||
+		!strings.Contains(string(goMod), "we've upgraded to golang.org/x/exp/event@upgrade") {
+		t.Errorf("tools should have upgraded x/build, x/net, x/exp/event: %v", string(goMod))
 	}
 	if strings.Contains(string(goMod), "we've upgraded to external.example.com") {
 		t.Errorf("tools should not have upgraded external.example.com: %v", string(goMod))