Skip to content

Commit 662e850

Browse files
fix: reduce generator churn by removing goa version from file headers (#3902)
Remove the goa version string from per-file generated headers (Go and proto) so that updating the goa dependency no longer causes every generated file to change. The version is now recorded once in gen/goa.json instead. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent adf0d75 commit 662e850

42 files changed

Lines changed: 109 additions & 58 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

codegen/generator/generate.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,18 @@ func Generate(dir, cmd string, debug bool) (outputs []string, err1 error) {
156156
}
157157
}
158158

159-
// 8. Write the files (in parallel).
159+
// 8. Emit goa.json version file (gen command only).
160+
if cmd == "gen" {
161+
genfiles = append(genfiles, codegen.VersionFile())
162+
}
163+
164+
// 9. Write the files (in parallel).
160165
written := make(map[string]struct{})
161166
{
162167
start := time.Now()
163168
numWorkers := runtime.NumCPU()
164169
if debug {
165-
fmt.Fprintf(os.Stderr, "[TIMING] [generate] Stage 8: Starting parallel file writing with %d workers\n", numWorkers)
170+
fmt.Fprintf(os.Stderr, "[TIMING] [generate] Stage 9: Starting parallel file writing with %d workers\n", numWorkers)
166171
}
167172

168173
// Channel for work items
@@ -234,11 +239,11 @@ func Generate(dir, cmd string, debug bool) (outputs []string, err1 error) {
234239
}
235240

236241
if debug {
237-
fmt.Fprintf(os.Stderr, "[TIMING] [generate] Stage 8: Write files took %v (%d files written, %d slow renders)\n", time.Since(start), len(written), slowRenders)
242+
fmt.Fprintf(os.Stderr, "[TIMING] [generate] Stage 9: Write files took %v (%d files written, %d slow renders)\n", time.Since(start), len(written), slowRenders)
238243
}
239244
}
240245

241-
// 9. Compute all output filenames.
246+
// 10. Compute all output filenames.
242247
{
243248
start := time.Now()
244249
outputs = make([]string, len(written))
@@ -256,7 +261,7 @@ func Generate(dir, cmd string, debug bool) (outputs []string, err1 error) {
256261
i++
257262
}
258263
if debug {
259-
fmt.Fprintf(os.Stderr, "[TIMING] [generate] Stage 9: Compute output filenames took %v\n", time.Since(start))
264+
fmt.Fprintf(os.Stderr, "[TIMING] [generate] Stage 10: Compute output filenames took %v\n", time.Since(start))
260265
}
261266
}
262267
sort.Strings(outputs)

codegen/generator/generate_merge_test.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package generator
22

33
import (
4+
"encoding/json"
45
"os"
56
"path/filepath"
67
"strings"
78
"testing"
89

910
"goa.design/goa/v3/codegen"
1011
"goa.design/goa/v3/eval"
12+
goa "goa.design/goa/v3/pkg"
1113
)
1214

1315
// TestGenerateMergesSamePathFiles verifies that when two generators emit content
@@ -106,8 +108,9 @@ func TestGenerateParallelManyFiles(t *testing.T) {
106108
if err != nil {
107109
t.Fatalf("Generate failed: %v", err)
108110
}
111+
outputs = assertVersionFile(t, dir, outputs)
109112

110-
// Verify all files were written
113+
// Verify all generator files were written
111114
if len(outputs) != numFiles {
112115
t.Fatalf("expected %d output files, got %d", numFiles, len(outputs))
113116
}
@@ -169,6 +172,7 @@ func TestGenerateParallelWithMerge(t *testing.T) {
169172
if err != nil {
170173
t.Fatalf("Generate failed: %v", err)
171174
}
175+
outputs = assertVersionFile(t, dir, outputs)
172176

173177
if len(outputs) != 2 {
174178
t.Fatalf("expected 2 output files, got %d", len(outputs))
@@ -268,6 +272,7 @@ func TestGenerateParallelSingleFile(t *testing.T) {
268272
if err != nil {
269273
t.Fatalf("Generate failed: %v", err)
270274
}
275+
outputs = assertVersionFile(t, dir, outputs)
271276

272277
if len(outputs) != 1 {
273278
t.Fatalf("expected 1 output file, got %d", len(outputs))
@@ -283,3 +288,33 @@ func TestGenerateParallelSingleFile(t *testing.T) {
283288
t.Fatalf("file missing expected content:\n%s", content)
284289
}
285290
}
291+
292+
// assertVersionFile checks that goa.json was emitted with the correct version
293+
// and returns the remaining outputs (excluding goa.json) for further assertions.
294+
func assertVersionFile(t *testing.T, dir string, outputs []string) []string {
295+
t.Helper()
296+
297+
versionPath := filepath.Join(codegen.Gendir, "goa.json")
298+
299+
// Read and validate goa.json content.
300+
bs, err := os.ReadFile(filepath.Join(dir, versionPath))
301+
if err != nil {
302+
t.Fatalf("failed reading goa.json: %v", err)
303+
}
304+
var data map[string]string
305+
if err := json.Unmarshal(bs, &data); err != nil {
306+
t.Fatalf("goa.json is not valid JSON: %v", err)
307+
}
308+
if v := data["goa_version"]; v != goa.Version() {
309+
t.Fatalf("goa.json version = %q, want %q", v, goa.Version())
310+
}
311+
312+
// Filter goa.json out of outputs.
313+
var rest []string
314+
for _, o := range outputs {
315+
if filepath.Base(o) != "goa.json" {
316+
rest = append(rest, o)
317+
}
318+
}
319+
return rest
320+
}

codegen/header.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package codegen
22

33
import (
4+
"encoding/json"
5+
"path/filepath"
6+
47
goa "goa.design/goa/v3/pkg"
58
)
69

@@ -10,14 +13,27 @@ func Header(title, pack string, imports []*ImportSpec) *SectionTemplate {
1013
Name: "source-header",
1114
Source: codegenTemplates.Read(headerT),
1215
Data: map[string]any{
13-
"Title": title,
14-
"ToolVersion": goa.Version(),
15-
"Pkg": pack,
16-
"Imports": imports,
16+
"Title": title,
17+
"Pkg": pack,
18+
"Imports": imports,
1719
},
1820
}
1921
}
2022

23+
// VersionFile returns a file that contains the goa version used to generate
24+
// the code. The file is written to gen/goa.json.
25+
func VersionFile() *File {
26+
data := map[string]string{"goa_version": goa.Version()}
27+
b, _ := json.MarshalIndent(data, "", " ")
28+
return &File{
29+
Path: filepath.Join(Gendir, "goa.json"),
30+
SectionTemplates: []*SectionTemplate{{
31+
Name: "goa-version",
32+
Source: string(b),
33+
}},
34+
}
35+
}
36+
2137
// AddImport adds imports to a section template that was generated with
2238
// Header.
2339
func AddImport(section *SectionTemplate, imprts ...*ImportSpec) {

codegen/sections_test.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@ package codegen
22

33
import (
44
"bytes"
5-
"fmt"
65
"strings"
76
"testing"
8-
9-
goa "goa.design/goa/v3/pkg"
107
)
118

129
// normalizeLineEndings converts Windows line endings to Unix line endings
@@ -64,7 +61,7 @@ import (
6461
`
6562
)
6663
var (
67-
titleHeader = fmt.Sprintf(`// Code generated by goa %s, DO NOT EDIT.
64+
titleHeader = `// Code generated by goa, DO NOT EDIT.
6865
//
6966
// test title
7067
//
@@ -73,7 +70,7 @@ import (
7370
7471
package testpackage
7572
76-
`, goa.Version())
73+
`
7774
imprt = []*ImportSpec{{Path: "test"}}
7875
imports = append(imprt, &ImportSpec{Path: "other"})
7976
pathImport = []*ImportSpec{{Path: "import/with/slashes"}}

codegen/templates/header.go.tpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{{if .Title}}// Code generated by goa {{.ToolVersion}}, DO NOT EDIT.
1+
{{if .Title}}// Code generated by goa, DO NOT EDIT.
22
//
33
// {{.Title}}
44
//

codegen/testutil/golden.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,19 @@ type GoldenFile struct {
4040
}
4141

4242
// goHeaderVersionRE matches the generated header line that contains the goa version (Go files).
43-
var goHeaderVersionRE = regexp.MustCompile(`(?m)^// Code generated by goa v[^,]+, DO NOT EDIT\.`)
43+
var goHeaderVersionRE = regexp.MustCompile(`(?m)^// Code generated by goa(?: v[^,]+)?, DO NOT EDIT\.`)
4444

4545
// protoHeaderVersionRE matches the generated header line that contains the goa version (proto files).
46-
var protoHeaderVersionRE = regexp.MustCompile(`(?m)^// Code generated with goa v[^,]+, DO NOT EDIT\.`)
46+
var protoHeaderVersionRE = regexp.MustCompile(`(?m)^// Code generated with goa(?: v[^,]+)?, DO NOT EDIT\.`)
4747

4848
// normalizeGoHeader replaces the versioned header with a version-agnostic placeholder.
4949
func normalizeGoHeader(b []byte) []byte {
50-
return goHeaderVersionRE.ReplaceAll(b, []byte("// Code generated by goa vX, DO NOT EDIT."))
50+
return goHeaderVersionRE.ReplaceAll(b, []byte("// Code generated by goa, DO NOT EDIT."))
5151
}
5252

5353
// normalizeProtoHeader replaces the versioned header in .proto files with a version-agnostic placeholder.
5454
func normalizeProtoHeader(b []byte) []byte {
55-
return protoHeaderVersionRE.ReplaceAll(b, []byte("// Code generated with goa vX, DO NOT EDIT."))
55+
return protoHeaderVersionRE.ReplaceAll(b, []byte("// Code generated with goa, DO NOT EDIT."))
5656
}
5757

5858
// NewGoldenFile creates a new GoldenFile instance with default options

grpc/codegen/proto.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99

1010
"goa.design/goa/v3/codegen"
1111
"goa.design/goa/v3/expr"
12-
goa "goa.design/goa/v3/pkg"
1312
)
1413

1514
const (
@@ -51,8 +50,7 @@ func protoFile(genpkg string, svc *expr.GRPCServiceExpr, services *ServicesData)
5150
Name: "proto-header",
5251
Source: grpcTemplates.Read(grpcProtoHeaderT),
5352
Data: map[string]any{
54-
"Title": fmt.Sprintf("%s protocol buffer definition", svc.Name()),
55-
"ToolVersion": goa.Version(),
53+
"Title": fmt.Sprintf("%s protocol buffer definition", svc.Name()),
5654
},
5755
},
5856
// proto syntax and package

grpc/codegen/templates/proto_header.go.tpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{{ if .Title -}}
2-
// Code generated with goa {{ .ToolVersion }}, DO NOT EDIT.
2+
// Code generated with goa, DO NOT EDIT.
33
//
44
// {{ .Title }}
55
//

grpc/codegen/testdata/golden/proto_array.proto.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Code generated with goa vX, DO NOT EDIT.
1+
// Code generated with goa, DO NOT EDIT.
22
//
33
// ServiceMessageArray protocol buffer definition
44
//

grpc/codegen/testdata/golden/proto_map.proto.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Code generated with goa vX, DO NOT EDIT.
1+
// Code generated with goa, DO NOT EDIT.
22
//
33
// ServiceMessageMap protocol buffer definition
44
//

0 commit comments

Comments
 (0)