Skip to content

Commit 4b28576

Browse files
committed
cmd/relnote: find cutoff date automatically
Look for the date of CL that opened the tree to find the cutoff for TODOs. Add a flag for the date in case that doesn't work. For golang/go#64169. Change-Id: I756e5622339f5e1963c39b8e0bbd7eeb3fc23d85 Reviewed-on: https://go-review.googlesource.com/c/build/+/584401 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent c639adb commit 4b28576

File tree

3 files changed

+73
-30
lines changed

3 files changed

+73
-30
lines changed

cmd/relnote/relnote.go

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,18 @@ import (
1919
)
2020

2121
var (
22-
verbose = flag.Bool("v", false, "print verbose logging")
23-
goroot = flag.String("goroot", runtime.GOROOT(), "root of Go repo containing docs")
22+
verbose = flag.Bool("v", false, "print verbose logging")
23+
goroot = flag.String("goroot", runtime.GOROOT(), "root of Go repo containing docs")
24+
todosSince = flag.String("since", "", "earliest to look for TODOs, in YYYY-MM-DD format")
2425
)
2526

2627
func usage() {
2728
out := flag.CommandLine.Output()
2829
fmt.Fprintf(out, "usage:\n")
2930
fmt.Fprintf(out, " relnote generate\n")
3031
fmt.Fprintf(out, " generate release notes from doc/next\n")
31-
fmt.Fprintf(out, " relnote todo PREVIOUS_RELEASE_DATE\n")
32-
fmt.Fprintf(out, " report which release notes need to be written; use YYYY-MM-DD format for date of last release\n")
32+
fmt.Fprintf(out, " relnote todo\n")
33+
fmt.Fprintf(out, " report which release notes need to be written\n")
3334
flag.PrintDefaults()
3435
}
3536

@@ -54,18 +55,16 @@ func main() {
5455
if cmd := flag.Arg(0); cmd != "" {
5556
switch cmd {
5657
case "generate":
57-
err = generate(version, flag.Arg(1))
58+
err = generate(version, *goroot)
5859
case "todo":
59-
prevDate := flag.Arg(1)
60-
if prevDate == "" {
61-
log.Fatal("need previous release date")
60+
var sinceDate time.Time
61+
if *todosSince != "" {
62+
sinceDate, err = time.Parse(time.DateOnly, *todosSince)
63+
if err != nil {
64+
log.Fatalf("-since flag: %v", err)
65+
}
6266
}
63-
prevDateTime, err := time.Parse("2006-01-02", prevDate)
64-
if err != nil {
65-
log.Fatalf("previous release date: %s", err)
66-
}
67-
nextDir := filepath.Join(*goroot, "doc", "next")
68-
err = todo(os.Stdout, os.DirFS(nextDir), prevDateTime)
67+
err = todo(os.Stdout, *goroot, sinceDate)
6968
default:
7069
err = fmt.Errorf("unknown command %q", cmd)
7170
}

cmd/relnote/todo.go

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import (
1010
"fmt"
1111
"io"
1212
"io/fs"
13+
"log"
14+
"os"
15+
"os/exec"
16+
"path/filepath"
1317
"regexp"
1418
"slices"
1519
"strconv"
@@ -29,24 +33,59 @@ type ToDo struct {
2933

3034
// todo prints a report to w on which release notes need to be written.
3135
// It takes the doc/next directory of the repo and the date of the last release.
32-
func todo(w io.Writer, fsys fs.FS, prevRelDate time.Time) error {
36+
func todo(w io.Writer, goroot string, treeOpenDate time.Time) error {
37+
// If not provided, determine when the tree was opened by looking
38+
// at when the version file was updated.
39+
if treeOpenDate.IsZero() {
40+
var err error
41+
treeOpenDate, err = findTreeOpenDate(goroot)
42+
if err != nil {
43+
return err
44+
}
45+
}
46+
log.Printf("collecting TODOs from %s since %s", goroot, treeOpenDate.Format(time.DateOnly))
47+
3348
var todos []ToDo
3449
addToDo := func(td ToDo) { todos = append(todos, td) }
3550

3651
mentionedIssues := map[int]bool{} // issues mentioned in the existing relnotes
3752
addIssue := func(num int) { mentionedIssues[num] = true }
3853

39-
if err := infoFromDocFiles(fsys, addToDo, addIssue); err != nil {
54+
nextDir := filepath.Join(goroot, "doc", "next")
55+
if err := infoFromDocFiles(os.DirFS(nextDir), addToDo, addIssue); err != nil {
4056
return err
4157
}
42-
if !prevRelDate.IsZero() {
43-
if err := todosFromCLs(prevRelDate, mentionedIssues, addToDo); err != nil {
44-
return err
45-
}
58+
if err := todosFromCLs(treeOpenDate, mentionedIssues, addToDo); err != nil {
59+
return err
4660
}
4761
return writeToDos(w, todos)
4862
}
4963

64+
// findTreeOpenDate returns the time of the most recent commit to the file that
65+
// determines the version of Go under development.
66+
func findTreeOpenDate(goroot string) (time.Time, error) {
67+
versionFilePath := filepath.FromSlash("src/internal/goversion/goversion.go")
68+
if _, err := exec.LookPath("git"); err != nil {
69+
return time.Time{}, fmt.Errorf("looking for git binary: %v", err)
70+
}
71+
// List the most recent commit to versionFilePath, displaying the date and subject.
72+
outb, err := exec.Command("git", "-C", goroot, "log", "-n", "1",
73+
"--format=%cs %s", "--", versionFilePath).Output()
74+
if err != nil {
75+
return time.Time{}, err
76+
}
77+
out := string(outb)
78+
// The commit messages follow a standard form. Check for the right words to avoid mistakenly
79+
// choosing the wrong commit.
80+
const updateString = "update version to"
81+
if !strings.Contains(strings.ToLower(out), updateString) {
82+
return time.Time{}, fmt.Errorf("cannot determine tree-open date: most recent commit for %s does not contain %q",
83+
versionFilePath, updateString)
84+
}
85+
dateString, _, _ := strings.Cut(out, " ")
86+
return time.Parse(time.DateOnly, dateString)
87+
}
88+
5089
// Collect TODOs and issue numbers from the markdown files in the main repo.
5190
func infoFromDocFiles(fsys fs.FS, addToDo func(ToDo), addIssue func(int)) error {
5291
// This is essentially a grep.

cmd/relnote/todo_test.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@
55
package main
66

77
import (
8-
"bytes"
8+
"slices"
99
"testing"
1010
"testing/fstest"
11-
"time"
1211
)
1312

14-
func TestToDo(t *testing.T) {
13+
func TestInfoFromDocFiles(t *testing.T) {
1514
files := map[string]string{
1615
"a.md": "TODO: write something",
1716
"b.md": "nothing to do",
@@ -22,14 +21,20 @@ func TestToDo(t *testing.T) {
2221
for name, contents := range files {
2322
dir[name] = &fstest.MapFile{Data: []byte(contents)}
2423
}
25-
var buf bytes.Buffer
26-
if err := todo(&buf, dir, time.Time{}); err != nil {
24+
var got []ToDo
25+
addToDo := func(td ToDo) { got = append(got, td) }
26+
addIssue := func(int) {}
27+
if err := infoFromDocFiles(dir, addToDo, addIssue); err != nil {
2728
t.Fatal(err)
2829
}
29-
got := buf.String()
30-
want := `TODO: write something (from a.md:1)
31-
`
32-
if got != want {
33-
t.Errorf("\ngot:\n%s\nwant:\n%s", got, want)
30+
want := []ToDo{
31+
{
32+
message: "TODO: write something",
33+
provenance: "a.md:1",
34+
},
35+
}
36+
37+
if !slices.Equal(got, want) {
38+
t.Errorf("\ngot:\n%+v\nwant:\n%+v", got, want)
3439
}
3540
}

0 commit comments

Comments
 (0)