From 6fae41069544451ad6de0b60ffabe0a6af616eaa Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Thu, 14 May 2026 10:26:01 +0800 Subject: [PATCH 1/5] ci: update Go setup and clean up workflow formatting --- .github/workflows/release.yml | 3 ++- .github/workflows/testing.yml | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6a72dc0a..e8ef061d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,7 +28,8 @@ jobs: - name: Install Go uses: actions/setup-go@v6 with: - go-version: "1.24.x" + go-version-file: go.mod + cache-dependency-path: go.sum - name: Download dependencies run: go mod download diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 4ed484ad..b963d1b5 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -16,7 +16,6 @@ jobs: test: strategy: matrix: - go-version: [1.24.x] os: [ubuntu-latest, windows-latest] targetplatform: [x86, x64] @@ -26,7 +25,8 @@ jobs: - name: Install Go uses: actions/setup-go@v6 with: - go-version: ${{ matrix.go-version }} + go-version-file: go.mod + cache-dependency-path: go.sum - name: Checkout Code uses: actions/checkout@v7 From f758864e593c41957751ac33d88e2e6936bf22ab Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Thu, 9 Jul 2026 16:23:52 +0800 Subject: [PATCH 2/5] perf: lazily compute proto registry with generated files --- internal/protogen/exporter.go | 2 +- internal/protogen/exporter_test.go | 10 +++---- internal/protogen/protogen.go | 42 ++++++++++++++++++++++++------ 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/internal/protogen/exporter.go b/internal/protogen/exporter.go index aab7489c..890a9487 100644 --- a/internal/protogen/exporter.go +++ b/internal/protogen/exporter.go @@ -391,7 +391,7 @@ func (x *sheetExporter) findMDFromGeneratedProtos(name string) protoreflect.Mess return nil } fullName := protoreflect.FullName(x.be.ProtoPackage).Append(protoreflect.Name(name)) - descriptor, err := x.be.gen.ProtoRegistryFilesWithGenerated.FindDescriptorByName(fullName) + descriptor, err := x.be.gen.GetProtoRegistryFilesWithGenerated().FindDescriptorByName(fullName) if err != nil { return nil } diff --git a/internal/protogen/exporter_test.go b/internal/protogen/exporter_test.go index e688ca0f..02634423 100644 --- a/internal/protogen/exporter_test.go +++ b/internal/protogen/exporter_test.go @@ -628,7 +628,7 @@ func Test_sheetExporter_exportStruct(t *testing.T) { OutputOpt: &options.ProtoOutputOption{ PreserveFieldNumbers: true, }, - ProtoRegistryFilesWithGenerated: protoregistry.GlobalFiles, + protoRegistryFilesWithGenerated: protoregistry.GlobalFiles, }, }, typeInfos: &xproto.TypeInfos{}, @@ -665,7 +665,7 @@ func Test_sheetExporter_exportStruct(t *testing.T) { OutputOpt: &options.ProtoOutputOption{ PreserveFieldNumbers: true, }, - ProtoRegistryFilesWithGenerated: protoregistry.GlobalFiles, + protoRegistryFilesWithGenerated: protoregistry.GlobalFiles, }, }, typeInfos: &xproto.TypeInfos{}, @@ -793,7 +793,7 @@ func Test_sheetExporter_exportUnion(t *testing.T) { OutputOpt: &options.ProtoOutputOption{ PreserveFieldNumbers: true, }, - ProtoRegistryFilesWithGenerated: protoregistry.GlobalFiles, + protoRegistryFilesWithGenerated: protoregistry.GlobalFiles, }, }, typeInfos: &xproto.TypeInfos{}, @@ -1015,7 +1015,7 @@ func Test_sheetExporter_exportMessager(t *testing.T) { OutputOpt: &options.ProtoOutputOption{ PreserveFieldNumbers: true, }, - ProtoRegistryFilesWithGenerated: protoregistry.GlobalFiles, + protoRegistryFilesWithGenerated: protoregistry.GlobalFiles, }, }, typeInfos: &xproto.TypeInfos{}, @@ -1098,7 +1098,7 @@ func Test_sheetExporter_exportMessager(t *testing.T) { OutputOpt: &options.ProtoOutputOption{ PreserveFieldNumbers: true, }, - ProtoRegistryFilesWithGenerated: protoregistry.GlobalFiles, + protoRegistryFilesWithGenerated: protoregistry.GlobalFiles, }, }, typeInfos: &xproto.TypeInfos{}, diff --git a/internal/protogen/protogen.go b/internal/protogen/protogen.go index cb97cc83..5adad407 100644 --- a/internal/protogen/protogen.go +++ b/internal/protogen/protogen.go @@ -47,13 +47,17 @@ type Generator struct { InputOpt *options.ProtoInputOption OutputOpt *options.ProtoOutputOption - ProtoRegistryFiles, ProtoRegistryFilesWithGenerated *protoregistry.Files - ProtoRegistryTypes *dynamicpb.Types + ProtoRegistryFiles *protoregistry.Files + ProtoRegistryTypes *dynamicpb.Types // internal typeInfos *xproto.TypeInfos // predefined type infos collector *xerrors.Collector // concurrent error collector shared across the generator. + // used in advanced mode or when preserveFieldNumbers is set to true + registryWithGeneratedOnce sync.Once + protoRegistryFilesWithGenerated *protoregistry.Files + cacheMu sync.RWMutex // guard fields below cachedImporters map[string]importer.Importer // absolute file path -> importer } @@ -86,15 +90,32 @@ func NewGeneratorWithOptions(protoPackage, indir, outdir string, opts *options.O panic(err) } gen.ProtoRegistryFiles = registryFiles - registryFiles, err = gen.parseProtoRegistryFiles(true) - if err != nil { - panic(err) - } - gen.ProtoRegistryFilesWithGenerated = registryFiles gen.ProtoRegistryTypes = dynamicpb.NewTypes(registryFiles) + // NOTE: ProtoRegistryFilesWithGenerated is lazily computed by + // GetProtoRegistryFilesWithGenerated() on first use. return gen } +// GetProtoRegistryFilesWithGenerated returns a registry that includes both +// the user-specified imported protos and the previously generated protos +// under outdir. It parses on first call (under sync.Once) and caches the +// result in [Generator.ProtoRegistryFilesWithGenerated]; subsequent calls +// return the cached value. If the field is already set (e.g. by tests), +// it is returned as-is without invoking the parser. +func (gen *Generator) GetProtoRegistryFilesWithGenerated() *protoregistry.Files { + if gen.protoRegistryFilesWithGenerated != nil { + return gen.protoRegistryFilesWithGenerated + } + gen.registryWithGeneratedOnce.Do(func() { + files, err := gen.parseProtoRegistryFiles(true) + if err != nil { + panic(err) + } + gen.protoRegistryFilesWithGenerated = files + }) + return gen.protoRegistryFilesWithGenerated +} + func (gen *Generator) parseProtoRegistryFiles(useGeneratedProtos bool) (*protoregistry.Files, error) { outdir := filepath.Join(gen.OutputDir, gen.OutputOpt.Subdir) var protoFiles []string @@ -117,7 +138,12 @@ func (gen *Generator) preprocess(useGeneratedProtos, delExisted bool) error { // parse custom imported proto files protoRegistryFiles := gen.ProtoRegistryFiles if useGeneratedProtos { - protoRegistryFiles = gen.ProtoRegistryFilesWithGenerated + protoRegistryFiles = gen.GetProtoRegistryFilesWithGenerated() + } + if gen.OutputOpt.PreserveFieldNumbers { + // if preserveFieldNumbers enabled, parse generated protos before they + // are deleted + _ = gen.GetProtoRegistryFilesWithGenerated() } gen.typeInfos = xproto.GetAllTypeInfo(protoRegistryFiles, gen.ProtoPackage) return prepareOutdir(outdir, gen.InputOpt.ProtoFiles, delExisted) From 9332767b291307852ef167f2a611521aabaf3845 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Thu, 14 May 2026 10:36:08 +0800 Subject: [PATCH 3/5] ci: update golangci-lint version and add go generate check --- .github/workflows/lint.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 650dfcc0..409d0132 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -23,7 +23,7 @@ jobs: uses: golangci/golangci-lint-action@v9.3.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} - version: v2.2.1 + version: latest - name: Install Buf uses: bufbuild/buf-action@v1 @@ -40,3 +40,14 @@ jobs: git diff exit 1 fi + + - name: Go Generate and Check + run: | + go generate ./... + if ! git diff --quiet; then + echo "Error: 'go generate ./...' produced uncommitted changes." + echo "Run 'go generate ./...' locally and commit the result." + git status --short + git diff + exit 1 + fi From 2d3245e31bbecfe382dc54e6d3c1d9b3162512dc Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Thu, 9 Jul 2026 16:03:39 +0800 Subject: [PATCH 4/5] ci: checkout before installing Go so go-version-file: go.mod can be read --- .github/workflows/testing.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index b963d1b5..ccd085a5 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -22,15 +22,15 @@ jobs: runs-on: ${{ matrix.os }} steps: + - name: Checkout Code + uses: actions/checkout@v7 + - name: Install Go uses: actions/setup-go@v6 with: go-version-file: go.mod cache-dependency-path: go.sum - - name: Checkout Code - uses: actions/checkout@v7 - - name: Vet run: go vet ./... From 79d66e7a157a12350d3e598735e8119ef03a29f1 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Thu, 9 Jul 2026 20:50:32 +0800 Subject: [PATCH 5/5] refactor(protogen): unexport getProtoRegistryFilesWithGenerated and simplify preprocess condition Address review comments on PR #435: - Rename GetProtoRegistryFilesWithGenerated to getProtoRegistryFilesWithGenerated since it's only used within the protogen package, no need to export it as public API. - Merge the two separate if-checks in preprocess into a single condition (useGeneratedProtos || gen.OutputOpt.PreserveFieldNumbers) that decides whether to parse generated protos. - Simplify the doc comment accordingly. --- internal/protogen/exporter.go | 2 +- internal/protogen/protogen.go | 26 ++++++++++---------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/internal/protogen/exporter.go b/internal/protogen/exporter.go index 890a9487..bf712135 100644 --- a/internal/protogen/exporter.go +++ b/internal/protogen/exporter.go @@ -391,7 +391,7 @@ func (x *sheetExporter) findMDFromGeneratedProtos(name string) protoreflect.Mess return nil } fullName := protoreflect.FullName(x.be.ProtoPackage).Append(protoreflect.Name(name)) - descriptor, err := x.be.gen.GetProtoRegistryFilesWithGenerated().FindDescriptorByName(fullName) + descriptor, err := x.be.gen.getProtoRegistryFilesWithGenerated().FindDescriptorByName(fullName) if err != nil { return nil } diff --git a/internal/protogen/protogen.go b/internal/protogen/protogen.go index 5adad407..dd312a03 100644 --- a/internal/protogen/protogen.go +++ b/internal/protogen/protogen.go @@ -91,18 +91,15 @@ func NewGeneratorWithOptions(protoPackage, indir, outdir string, opts *options.O } gen.ProtoRegistryFiles = registryFiles gen.ProtoRegistryTypes = dynamicpb.NewTypes(registryFiles) - // NOTE: ProtoRegistryFilesWithGenerated is lazily computed by - // GetProtoRegistryFilesWithGenerated() on first use. + // NOTE: protoRegistryFilesWithGenerated is lazily computed by + // getProtoRegistryFilesWithGenerated() on first use. return gen } -// GetProtoRegistryFilesWithGenerated returns a registry that includes both -// the user-specified imported protos and the previously generated protos -// under outdir. It parses on first call (under sync.Once) and caches the -// result in [Generator.ProtoRegistryFilesWithGenerated]; subsequent calls -// return the cached value. If the field is already set (e.g. by tests), -// it is returned as-is without invoking the parser. -func (gen *Generator) GetProtoRegistryFilesWithGenerated() *protoregistry.Files { +// getProtoRegistryFilesWithGenerated returns a registry including both the +// imported and previously generated protos, computing and caching it on +// first use. +func (gen *Generator) getProtoRegistryFilesWithGenerated() *protoregistry.Files { if gen.protoRegistryFilesWithGenerated != nil { return gen.protoRegistryFilesWithGenerated } @@ -137,13 +134,10 @@ func (gen *Generator) preprocess(useGeneratedProtos, delExisted bool) error { outdir := filepath.Join(gen.OutputDir, gen.OutputOpt.Subdir) // parse custom imported proto files protoRegistryFiles := gen.ProtoRegistryFiles - if useGeneratedProtos { - protoRegistryFiles = gen.GetProtoRegistryFilesWithGenerated() - } - if gen.OutputOpt.PreserveFieldNumbers { - // if preserveFieldNumbers enabled, parse generated protos before they - // are deleted - _ = gen.GetProtoRegistryFilesWithGenerated() + // preserveFieldNumbers also needs generated protos parsed before they + // are deleted below. + if useGeneratedProtos || gen.OutputOpt.PreserveFieldNumbers { + protoRegistryFiles = gen.getProtoRegistryFilesWithGenerated() } gen.typeInfos = xproto.GetAllTypeInfo(protoRegistryFiles, gen.ProtoPackage) return prepareOutdir(outdir, gen.InputOpt.ProtoFiles, delExisted)