Skip to content

Commit b655336

Browse files
committed
gencopy: print a ProTip with the .update target when a check fails
When `bazel test //path:foo_proto_compile_test` fails because a checked-in generated file has drifted from what `protoc` would produce, the existing error tells you which file pair mismatched and shows the diff, but leaves the developer to figure out which target regenerates the source-of-truth copy. Append a one-line ProTip to the mismatch error message, naming the `.update` target so it can be copy-pasted into `bazel run`: gencopy mismatch "foo.pb.go.gen" vs. "foo.pb.go" (-want +got): ... ProTip: to regenerate, run: bazel run //proto:foo_proto_compile.update The label is derived from pkg.TargetLabel (the proto_compile rule's full label, including any external-repo prefix) by replacing the rule-name component with cfg.UpdateTargetLabelName -- both already populated by the proto_compile_gencopy_test Starlark rule via gencopy_config().
1 parent 7932580 commit b655336

2 files changed

Lines changed: 58 additions & 2 deletions

File tree

cmd/gencopy/gencopy.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"os"
1212
"path/filepath"
1313
"strconv"
14+
"strings"
1415

1516
"github.com/google/go-cmp/cmp"
1617
)
@@ -105,7 +106,7 @@ func readFileAsString(filename string) (string, error) {
105106
return string(bytes), nil
106107
}
107108

108-
func check(_ *Config, pkg *PackageConfig, pairs []*SrcDst) error {
109+
func check(cfg *Config, pkg *PackageConfig, pairs []*SrcDst) error {
109110
for _, pair := range pairs {
110111
expected, err := readFileAsString(pair.Src)
111112
if err != nil {
@@ -117,7 +118,10 @@ func check(_ *Config, pkg *PackageConfig, pairs []*SrcDst) error {
117118
}
118119

119120
if diff := cmp.Diff(expected, actual); diff != "" {
120-
return fmt.Errorf("gencopy mismatch %q vs. %q (-want +got):\n%s", pair.Src, pair.Dst, diff)
121+
return fmt.Errorf(
122+
"gencopy mismatch %q vs. %q (-want +got):\n%s\n%s",
123+
pair.Src, pair.Dst, diff, regenerateProTip(pkg.TargetLabel, cfg.UpdateTargetLabelName),
124+
)
121125
}
122126
}
123127

@@ -129,6 +133,22 @@ func check(_ *Config, pkg *PackageConfig, pairs []*SrcDst) error {
129133
return nil
130134
}
131135

136+
// regenerateProTip returns a friendly hint pointing the developer at the
137+
// `.update` target that regenerates the checked-in copies. targetLabel is the
138+
// proto_compile rule's label (e.g. "//proto:foo_proto_compile" or
139+
// "@@repo//proto:foo_proto_compile"); updateName is the .update target's
140+
// rule name (e.g. "foo_proto_compile.update").
141+
func regenerateProTip(targetLabel, updateName string) string {
142+
if updateName == "" {
143+
return ""
144+
}
145+
updateLabel := updateName
146+
if idx := strings.LastIndex(targetLabel, ":"); idx >= 0 {
147+
updateLabel = targetLabel[:idx+1] + updateName
148+
}
149+
return fmt.Sprintf("\nProTip: to regenerate, run:\n bazel run %s\n", updateLabel)
150+
}
151+
132152
func update(cfg *Config, pkg *PackageConfig, pairs []*SrcDst) error {
133153
for _, pair := range pairs {
134154
pair.Dst += cfg.Extension

cmd/gencopy/gencopy_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,42 @@ func TestRunPkg(t *testing.T) {
144144
}
145145
}
146146

147+
func TestRegenerateProTip(t *testing.T) {
148+
for name, tc := range map[string]struct {
149+
targetLabel string
150+
updateName string
151+
want string
152+
}{
153+
"empty update name": {
154+
targetLabel: "//proto:foo_proto_compile",
155+
updateName: "",
156+
want: "",
157+
},
158+
"local target": {
159+
targetLabel: "//proto:foo_proto_compile",
160+
updateName: "foo_proto_compile.update",
161+
want: "\nProTip: to regenerate, run:\n bazel run //proto:foo_proto_compile.update\n",
162+
},
163+
"external repo target": {
164+
targetLabel: "@@some_repo//pkg:foo_proto_compile",
165+
updateName: "foo_proto_compile.update",
166+
want: "\nProTip: to regenerate, run:\n bazel run @@some_repo//pkg:foo_proto_compile.update\n",
167+
},
168+
"target with no colon": {
169+
targetLabel: "raw_label_no_colon",
170+
updateName: "foo_proto_compile.update",
171+
want: "\nProTip: to regenerate, run:\n bazel run foo_proto_compile.update\n",
172+
},
173+
} {
174+
t.Run(name, func(t *testing.T) {
175+
got := regenerateProTip(tc.targetLabel, tc.updateName)
176+
if got != tc.want {
177+
t.Errorf("regenerateProTip: got %q, want %q", got, tc.want)
178+
}
179+
})
180+
}
181+
}
182+
147183
// listFiles - convenience debugging function to log the files under a given dir
148184
func listFiles(t *testing.T, dir string) error {
149185
return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {

0 commit comments

Comments
 (0)