Skip to content

Commit 4ab6ffe

Browse files
committed
cmd/relnote: use Gerrit API to find RELNOTE markers in inline comments
Also filter out CLs on development or release branches. Fixes golang/go#41849. Change-Id: Ie6da4356d5f323dc15bd31b396889bfbf550d30b Reviewed-on: https://go-review.googlesource.com/c/build/+/272907 Trust: Dmitri Shuralyov <[email protected]> Run-TryBot: Dmitri Shuralyov <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Alexander Rakoczy <[email protected]>
1 parent 20e7ccc commit 4ab6ffe

File tree

1 file changed

+84
-23
lines changed

1 file changed

+84
-23
lines changed

cmd/relnote/relnote.go

Lines changed: 84 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,16 @@ import (
1414
"html"
1515
"io/ioutil"
1616
"log"
17+
"path"
1718
"regexp"
1819
"sort"
1920
"strings"
2021
"time"
2122

23+
"golang.org/x/build/gerrit"
2224
"golang.org/x/build/maintner"
2325
"golang.org/x/build/maintner/godata"
26+
"golang.org/x/build/repos"
2427
)
2528

2629
var (
@@ -55,6 +58,15 @@ func main() {
5558
// Previous release was 6 months earlier.
5659
cutoff = cutoff.AddDate(0, -6, 0)
5760

61+
// The maintner corpus doesn't track inline comments. See golang.org/issue/24863.
62+
// So we need to use a Gerrit API client to fetch them instead. If maintner starts
63+
// tracking inline comments in the future, this extra complexity can be dropped.
64+
gerritClient := gerrit.NewClient("https://go-review.googlesource.com", gerrit.NoAuth)
65+
matchedCLs, err := findCLsWithRelNote(gerritClient, cutoff)
66+
if err != nil {
67+
log.Fatal(err)
68+
}
69+
5870
var existingHTML []byte
5971
if *exclFile != "" {
6072
var err error
@@ -68,21 +80,34 @@ func main() {
6880
if err != nil {
6981
log.Fatal(err)
7082
}
71-
ger := corpus.Gerrit()
7283
changes := map[string][]change{} // keyed by pkg
73-
ger.ForeachProjectUnsorted(func(gp *maintner.GerritProject) error {
84+
corpus.Gerrit().ForeachProjectUnsorted(func(gp *maintner.GerritProject) error {
7485
if gp.Server() != "go.googlesource.com" {
7586
return nil
7687
}
7788
gp.ForeachCLUnsorted(func(cl *maintner.GerritCL) error {
7889
if cl.Status != "merged" {
7990
return nil
8091
}
92+
if cl.Branch() != "master" {
93+
// Ignore CLs sent to development or release branches.
94+
return nil
95+
}
8196
if cl.Commit.CommitTime.Before(cutoff) {
8297
// Was in a previous release; not for this one.
8398
return nil
8499
}
85-
relnote := clRelNote(cl)
100+
_, ok := matchedCLs[int(cl.Number)]
101+
if !ok {
102+
// Wasn't matched by the Gerrit API search query.
103+
// Return before making further Gerrit API calls.
104+
return nil
105+
}
106+
comments, err := gerritClient.ListChangeComments(context.Background(), fmt.Sprint(cl.Number))
107+
if err != nil {
108+
return err
109+
}
110+
relnote := clRelNote(cl, comments)
86111
if relnote == "" ||
87112
bytes.Contains(existingHTML, []byte(fmt.Sprintf("CL %d", cl.Number))) {
88113
return nil
@@ -119,7 +144,7 @@ func main() {
119144
if strings.HasPrefix(pkg, "cmd/") {
120145
continue
121146
}
122-
fmt.Printf("<dl id=%q><dt><a href=%q>%s</a></dt>\n <dd>",
147+
fmt.Printf("\n<dl id=%q><dt><a href=%q>%s</a></dt>\n <dd>",
123148
pkg, "/pkg/"+pkg+"/", pkg)
124149
for _, change := range changes[pkg] {
125150
changeURL := fmt.Sprintf("https://golang.org/cl/%d", change.CL.Number)
@@ -128,9 +153,8 @@ func main() {
128153
fmt.Printf("\n <p><!-- CL %d -->\n TODO: <a href=%q>%s</a>: %s\n </p>\n",
129154
change.CL.Number, changeURL, changeURL, html.EscapeString(subj))
130155
}
131-
fmt.Printf(" </dd>\n</dl><!-- %s -->\n\n", pkg)
156+
fmt.Printf(" </dd>\n</dl><!-- %s -->\n", pkg)
132157
}
133-
134158
} else {
135159
for _, pkg := range pkgs {
136160
fmt.Printf("%s\n", pkg)
@@ -141,34 +165,71 @@ func main() {
141165
}
142166
}
143167

144-
// clPackage returns the package name from the CL's commit message,
145-
// or "??" if it's formatted unconventionally.
146-
func clPackage(cl *maintner.GerritCL) string {
147-
subj := cl.Subject()
148-
if i := strings.Index(subj, ":"); i != -1 {
149-
return subj[:i]
168+
// findCLsWithRelNote finds CLs that contain a RELNOTE marker by
169+
// using a Gerrit API client. Returned map is keyed by CL number.
170+
func findCLsWithRelNote(client *gerrit.Client, since time.Time) (map[int]*gerrit.ChangeInfo, error) {
171+
// Gerrit search operators are documented at
172+
// https://gerrit-review.googlesource.com/Documentation/user-search.html#search-operators.
173+
query := fmt.Sprintf(`status:merged branch:master since:%s (comment:"RELNOTE" OR comment:"RELNOTES")`,
174+
since.Format("2006-01-02"))
175+
cs, err := client.QueryChanges(context.Background(), query)
176+
if err != nil {
177+
return nil, err
178+
}
179+
m := make(map[int]*gerrit.ChangeInfo) // CL Number → CL.
180+
for _, c := range cs {
181+
m[c.ChangeNumber] = c
150182
}
151-
return "??"
183+
return m, nil
152184
}
153185

154-
var relNoteRx = regexp.MustCompile(`RELNOTES?=(.+)`)
155-
156-
func parseRelNote(s string) string {
157-
if m := relNoteRx.FindStringSubmatch(s); m != nil {
158-
return m[1]
186+
// clPackage returns the package import path from the CL's commit message,
187+
// or "??" if it's formatted unconventionally.
188+
func clPackage(cl *maintner.GerritCL) string {
189+
var pkg string
190+
if i := strings.Index(cl.Subject(), ":"); i == -1 {
191+
return "??"
192+
} else {
193+
pkg = cl.Subject()[:i]
159194
}
160-
return ""
195+
if r := repos.ByGerritProject[cl.Project.Project()]; r == nil {
196+
return "??"
197+
} else {
198+
pkg = path.Join(r.ImportPath, pkg)
199+
}
200+
return pkg
161201
}
162202

163-
func clRelNote(cl *maintner.GerritCL) string {
203+
// clRelNote extracts a RELNOTE note from a Gerrit CL commit
204+
// message and any inline comments. If there isn't a RELNOTE
205+
// note, it returns the empty string.
206+
func clRelNote(cl *maintner.GerritCL, comments map[string][]gerrit.CommentInfo) string {
164207
msg := cl.Commit.Msg
165208
if strings.Contains(msg, "RELNOTE") {
166209
return parseRelNote(msg)
167210
}
168-
for _, comment := range cl.Messages {
169-
if strings.Contains(comment.Message, "RELNOTE") {
170-
return parseRelNote(comment.Message)
211+
// Since July 2020, Gerrit UI has replaced top-level comments
212+
// with patchset-level inline comments, so don't bother looking
213+
// for RELNOTE= in cl.Messages—there won't be any. Instead, do
214+
// look through all inline comments that we got via Gerrit API.
215+
for _, cs := range comments {
216+
for _, c := range cs {
217+
if strings.Contains(c.Message, "RELNOTE") {
218+
return parseRelNote(c.Message)
219+
}
171220
}
172221
}
173222
return ""
174223
}
224+
225+
// parseRelNote parses a RELNOTE annotation from the string s.
226+
// It returns the empty string if no such annotation exists.
227+
func parseRelNote(s string) string {
228+
m := relNoteRx.FindStringSubmatch(s)
229+
if m == nil {
230+
return ""
231+
}
232+
return m[1]
233+
}
234+
235+
var relNoteRx = regexp.MustCompile(`RELNOTES?=(.+)`)

0 commit comments

Comments
 (0)