relnote: improve error messages for API files

In the error messages for CheckAPIFile, display the full pathname of
the problematic file and refer to the documentation for API release
notes.

For golang/go#64169.

Change-Id: I2e8c6d3952d9e234ffc5e7aec9d55dacaca15828
Reviewed-on: https://go-review.googlesource.com/c/build/+/559695
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
This commit is contained in:
Jonathan Amsterdam 2024-01-31 09:38:51 -05:00
Родитель dd327c64a7
Коммит 3723b26e8a
3 изменённых файлов: 19 добавлений и 16 удалений

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

@ -37,13 +37,13 @@ func NewParser() *md.Parser {
// CheckFragment reports problems in a release-note fragment.
func CheckFragment(data string) error {
doc := NewParser().Parse(data)
if len(doc.Blocks) == 0 {
return errors.New("empty content")
}
// Check that the content of the document contains either a TODO or at least one sentence.
txt := text(doc)
txt := ""
if len(doc.Blocks) > 0 {
txt = text(doc)
}
if !strings.Contains(txt, "TODO") && !strings.ContainsAny(txt, ".?!") {
return errors.New("needs a TODO or a sentence")
return errors.New("File must contain a complete sentence or a TODO.")
}
return nil
}
@ -381,8 +381,8 @@ func GroupAPIFeaturesByFile(fs []APIFeature) (map[string][]APIFeature, error) {
// CheckAPIFile reads the api file at filename in apiFS, and checks the corresponding
// release-note files under docFS. It checks that the files exist and that they have
// some minimal content (see [CheckFragment]).
func CheckAPIFile(apiFS fs.FS, filename string, docFS fs.FS) error {
// some minimal content (see [CheckFragment]). The docRoot argument is used in error messages.
func CheckAPIFile(apiFS fs.FS, filename string, docFS fs.FS, docRoot string) error {
features, err := parseAPIFile(apiFS, filename)
if err != nil {
return err
@ -397,10 +397,11 @@ func CheckAPIFile(apiFS fs.FS, filename string, docFS fs.FS) error {
}
slices.Sort(filenames)
var errs []error
for _, filename := range filenames {
for _, fn := range filenames {
// TODO(jba): check that the file mentions each feature?
if err := checkFragmentFile(docFS, filename); err != nil {
errs = append(errs, fmt.Errorf("%s: %v", filename, err))
if err := checkFragmentFile(docFS, fn); err != nil {
// Use path.Join for consistency with io/fs pathnames.
errs = append(errs, fmt.Errorf("%s: %v\nSee doc/README.md for more information.", path.Join(docRoot, fn), err))
}
}
return errors.Join(errs...)
@ -410,7 +411,7 @@ func checkFragmentFile(fsys fs.FS, filename string) error {
f, err := fsys.Open(filename)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
err = fs.ErrNotExist
err = errors.New("File does not exist. Every API change must have a corresponding release note file.")
}
return err
}

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

@ -47,11 +47,11 @@ func TestCheckFragment(t *testing.T) {
},
{
"",
"empty",
"must contain a complete sentence",
},
{
"# heading",
"needs",
"must contain a complete sentence",
},
} {
got := CheckFragment(test.in)
@ -252,7 +252,7 @@ func TestCheckAPIFile(t *testing.T) {
t.Fatal(err)
}
var got string
gotErr := CheckAPIFile(fsys, "api.txt", fsys)
gotErr := CheckAPIFile(fsys, "api.txt", fsys, "doc/next/*stdlib/*minor")
if gotErr != nil {
got = gotErr.Error()
}

6
relnote/testdata/checkAPIFile/fail.txt поставляемый
Просмотреть файл

@ -7,5 +7,7 @@ pkg bar, type T #123
-- bar/123.md --
Not a sentence
-- want --
bar/123.md: needs a TODO or a sentence
foo/123.md: file does not exist
doc/next/*stdlib/*minor/bar/123.md: File must contain a complete sentence or a TODO.
See doc/README.md for more information.
doc/next/*stdlib/*minor/foo/123.md: File does not exist. Every API change must have a corresponding release note file.
See doc/README.md for more information.