cmd/gorelease: skip circular imports when inspecting go.sum and go.mod differences

Fixes golang/go#44440

Change-Id: I4ac7927e4b250f89cf7c0184fe1e46f6574cad1b
Reviewed-on: https://go-review.googlesource.com/c/exp/+/310369
Trust: Jean de Klerk <deklerk@google.com>
Trust: Jay Conrod <jayconrod@google.com>
Run-TryBot: Jean de Klerk <deklerk@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
diff --git a/cmd/gorelease/gorelease.go b/cmd/gorelease/gorelease.go
index edf3ed9..49d7e87 100644
--- a/cmd/gorelease/gorelease.go
+++ b/cmd/gorelease/gorelease.go
@@ -80,7 +80,6 @@
 package main
 
 import (
-	"bytes"
 	"context"
 	"encoding/json"
 	"errors"
@@ -1137,7 +1136,7 @@
 			// "empty go.sum".
 		}
 
-		if bytes.Compare(goSumData, newGoSumData) != 0 {
+		if !sumsMatchIgnoringPath(string(goSumData), string(newGoSumData), modPath) {
 			diagnostics = append(diagnostics, "go.sum: one or more sums are missing. Run 'go mod tidy' to add missing sums.")
 		}
 	}
@@ -1145,6 +1144,60 @@
 	return dir, goModData, goSumData, imps, diagnostics, nil
 }
 
+// sumsMatchIgnoringPath checks whether the two sums match. It ignores any lines
+// which contains the given modPath.
+func sumsMatchIgnoringPath(sum1, sum2, modPathToIgnore string) bool {
+	lines1 := make(map[string]bool)
+	for _, line := range strings.Split(string(sum1), "\n") {
+		if line == "" {
+			continue
+		}
+		lines1[line] = true
+	}
+	for _, line := range strings.Split(string(sum2), "\n") {
+		if line == "" {
+			continue
+		}
+		parts := strings.Fields(line)
+		if len(parts) < 1 {
+			panic(fmt.Sprintf("go.sum malformed: unexpected line %s", line))
+		}
+		if parts[0] == modPathToIgnore {
+			continue
+		}
+
+		if !lines1[line] {
+			return false
+		}
+	}
+
+	lines2 := make(map[string]bool)
+	for _, line := range strings.Split(string(sum2), "\n") {
+		if line == "" {
+			continue
+		}
+		lines2[line] = true
+	}
+	for _, line := range strings.Split(string(sum1), "\n") {
+		if line == "" {
+			continue
+		}
+		parts := strings.Fields(line)
+		if len(parts) < 1 {
+			panic(fmt.Sprintf("go.sum malformed: unexpected line %s", line))
+		}
+		if parts[0] == modPathToIgnore {
+			continue
+		}
+
+		if !lines2[line] {
+			return false
+		}
+	}
+
+	return true
+}
+
 // collectImportPaths visits the given root and traverses its directories
 // recursively, collecting the import paths of all importable packages in each
 // directory along the way.
diff --git a/cmd/gorelease/testdata/cycle/cycle_suggest.test b/cmd/gorelease/testdata/cycle/cycle_suggest.test
index 3e8252f..940897d 100644
--- a/cmd/gorelease/testdata/cycle/cycle_suggest.test
+++ b/cmd/gorelease/testdata/cycle/cycle_suggest.test
@@ -1,7 +1,5 @@
-mod=example.com/cycle
-base=v1.0.0
-version=v1.0.0
-success=false
+mod=example.com/cycle/v2
+version=v2.0.0
 -- want --
-Cannot suggest a release version.
-Module indirectly depends on a higher version of itself (v1.5.0) than the base version (v1.0.0).
\ No newline at end of file
+Inferred base version: v2.0.0
+Suggested version: v2.0.1
diff --git a/cmd/gorelease/testdata/cycle/cycle_suggest_error.test b/cmd/gorelease/testdata/cycle/cycle_suggest_error.test
new file mode 100644
index 0000000..3e8252f
--- /dev/null
+++ b/cmd/gorelease/testdata/cycle/cycle_suggest_error.test
@@ -0,0 +1,7 @@
+mod=example.com/cycle
+base=v1.0.0
+version=v1.0.0
+success=false
+-- want --
+Cannot suggest a release version.
+Module indirectly depends on a higher version of itself (v1.5.0) than the base version (v1.0.0).
\ No newline at end of file
diff --git a/cmd/gorelease/testdata/mod/example.com_cycle_v1.0.0.txt b/cmd/gorelease/testdata/mod/example.com_cycle_v1.0.0.txt
index 55e152d..8f073b0 100644
--- a/cmd/gorelease/testdata/mod/example.com_cycle_v1.0.0.txt
+++ b/cmd/gorelease/testdata/mod/example.com_cycle_v1.0.0.txt
@@ -1,6 +1,7 @@
 -- go.sum --
-example.com/cycle v1.5.0 h1:j2Bju3xKUT09utc7WwS5sXwrOSVUr5a7vOzOyB4ivac=
+example.com/cycle v1.5.0 h1:OkE6KLRVRM5XqIH9MMFIvoYCVjxMh8kqsxUzx5481s4=
 example.com/cycle v1.5.0/go.mod h1://AqZbyNHeLOKZB3J/UPPXaBvk3nCqvqVRbPkffDx60=
+example.com/cycledep v1.0.0 h1:5UDqvIlbZsKzzbZCOaHkxV+X0H6Fi4othxBS57NtjSs=
 example.com/cycledep v1.0.0/go.mod h1:Gc4hO1S1BMZaxOcGHwCRmdVcQP8+jAu/PyEgLdGe0xU=
 -- go.mod --
 module example.com/cycle
@@ -8,7 +9,7 @@
 go 1.12
 
 require example.com/cycledep v1.0.0
--- cycle/main.go --
+-- main.go --
 package main
 
 import _ "example.com/cycledep"
\ No newline at end of file
diff --git a/cmd/gorelease/testdata/mod/example.com_cycle_v1.5.0.txt b/cmd/gorelease/testdata/mod/example.com_cycle_v1.5.0.txt
index be01d7f..98e8fe8 100644
--- a/cmd/gorelease/testdata/mod/example.com_cycle_v1.5.0.txt
+++ b/cmd/gorelease/testdata/mod/example.com_cycle_v1.5.0.txt
@@ -2,5 +2,5 @@
 module example.com/cycle
 
 go 1.12
--- cycle/a.go --
+-- a.go --
 package a
\ No newline at end of file
diff --git a/cmd/gorelease/testdata/mod/example.com_cycle_v2_v2.0.0.txt b/cmd/gorelease/testdata/mod/example.com_cycle_v2_v2.0.0.txt
new file mode 100644
index 0000000..95ae8d4
--- /dev/null
+++ b/cmd/gorelease/testdata/mod/example.com_cycle_v2_v2.0.0.txt
@@ -0,0 +1,19 @@
+# Note: "go get -d ." will add another example.com/cycle/v2 line. That line is
+# non-deterministic, since the module is generate by prepareLoadDir each time.
+# However, gorelease should ignore new go.sum entries for the module it's
+# testing, since the requirement on that module is fake (a simulation: the user
+# isn't actually relying on their own module).
+-- go.sum --
+example.com/cycle/v2 v2.0.0/go.mod h1:lkmoN54Yqku+pnE3i6U+PjV87yiHyv3Rbei+phlzGGU=
+example.com/cycledep/v2 v2.0.0 h1:B8tgq8pxH4IbvvozFpGx7k+HUeLoAPcmCixOXPZiuTE=
+example.com/cycledep/v2 v2.0.0/go.mod h1:wBHRfgrlyovU4csu71ja8ySemxEOKOr8PpAiMU82nLw=
+-- go.mod --
+module example.com/cycle/v2
+
+go 1.12
+
+require example.com/cycledep/v2 v2.0.0
+-- a.go --
+package a
+
+import _ "example.com/cycledep/v2"
\ No newline at end of file
diff --git a/cmd/gorelease/testdata/mod/example.com_cycledep_v1.0.0.txt b/cmd/gorelease/testdata/mod/example.com_cycledep_v1.0.0.txt
index 6ea4610..83f7e34 100644
--- a/cmd/gorelease/testdata/mod/example.com_cycledep_v1.0.0.txt
+++ b/cmd/gorelease/testdata/mod/example.com_cycledep_v1.0.0.txt
@@ -4,7 +4,7 @@
 go 1.12
 
 require example.com/cycle v1.5.0
--- cycledep/a.go --
+-- a.go --
 package a
 
 import _ "example.com/cycle"
diff --git a/cmd/gorelease/testdata/mod/example.com_cycledep_v2_v2.0.0.txt b/cmd/gorelease/testdata/mod/example.com_cycledep_v2_v2.0.0.txt
new file mode 100644
index 0000000..7496524
--- /dev/null
+++ b/cmd/gorelease/testdata/mod/example.com_cycledep_v2_v2.0.0.txt
@@ -0,0 +1,12 @@
+-- go.mod --
+module example.com/cycledep/v2
+
+go 1.12
+
+require example.com/cycle/v2 v2.0.0
+-- b.go --
+package b
+-- c/c.go --
+package c
+
+import _ "example.com/cycle/v2"