diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 48545fe94..0d91db0b1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,43 +35,7 @@ jobs: if: steps.check-assets.outputs.missing == 'true' run: sleep 90 - check-proto: - runs-on: ubuntu-latest - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name - steps: - - name: Checkout codebase - uses: actions/checkout@v7 - - name: Set up Go - uses: actions/setup-go@v7 - with: - go-version-file: go.mod - - name: Cache protoc - id: cache-protoc - uses: actions/cache@v6 - with: - path: ${{ runner.temp }}/protoc - key: protoc-linux-x86_64-33.5 - - name: Download protoc - if: steps.cache-protoc.outputs.cache-hit != 'true' - working-directory: ${{ runner.temp }} - run: | - curl -fsSL --retry 3 -o protoc.zip https://github.com/protocolbuffers/protobuf/releases/download/v33.5/protoc-33.5-linux-x86_64.zip - unzip -q protoc.zip -d protoc - - name: Install pinned protobuf tools - run: | - go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.11 - go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.6.0 - - name: Check generated protobuf files - run: | - export PATH="$RUNNER_TEMP/protoc/bin:$PATH" - # Remove old outputs so orphaned pb files are also detected. - git ls-files -z -- '*.pb.go' | xargs -0 -r rm -- - go run ./infra/vprotogen - # Include newly generated files in the diff without committing. - git add --intent-to-add --force -- '*.pb.go' - git diff --exit-code HEAD -- '*.pb.go' - - check-format: + check-format-and-proto: runs-on: ubuntu-latest if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name permissions: @@ -87,6 +51,8 @@ jobs: cache: false - name: Check Format run: go run ./infra/vformat/main.go -mode check -pwd ./ + - name: Check Protobuf + run: go run ./infra/vprotogen/main.go -mode check -pwd ./ test: needs: check-assets diff --git a/infra/vprotogen/main.go b/infra/vprotogen/main.go index 9233b1185..b5911cc42 100644 --- a/infra/vprotogen/main.go +++ b/infra/vprotogen/main.go @@ -19,7 +19,10 @@ import ( "google.golang.org/protobuf/types/pluginpb" ) -var directory = flag.String("pwd", "", "Working directory of Xray vprotogen.") +var ( + directory = flag.String("pwd", "", "Working directory of Xray vprotogen.") + mode = flag.String("mode", "generate", "Execution mode. 'generate' (default) compiles and saves changes. 'check' reports out-of-date files and exits with non-zero.") +) // topoSort sorts file descriptors so each dependency appears before files importing it. func topoSort(files []protoreflect.FileDescriptor) []*descriptorpb.FileDescriptorProto { @@ -98,6 +101,21 @@ func main() { } pwd := *directory + isCheck := false + switch *mode { + case "generate", "": + isCheck = false + case "check": + isCheck = true + default: + fmt.Fprintf(os.Stderr, "Unrecognized 'mode': %s. Use 'generate' or 'check'.\n", *mode) + os.Exit(1) + } + + if isCheck { + fmt.Println("Checking for out-of-date Protobuf generated files...") + } + var protoFiles []string err := filepath.WalkDir(pwd, func(path string, d fs.DirEntry, err error) error { if err != nil || d.IsDir() { @@ -147,6 +165,7 @@ func main() { }, } + var diffCount int for _, plugin := range []string{"protoc-gen-go", "protoc-gen-go-grpc"} { resp, err := runPlugin(plugin, pwd, req) if err != nil { @@ -158,26 +177,48 @@ func main() { targetPath := filepath.Join(pwd, filepath.FromSlash(f.GetName())) content := []byte(f.GetContent()) - if existing, err := os.ReadFile(targetPath); err == nil { + hasDiff := false + existing, err := os.ReadFile(targetPath) + if err != nil { + hasDiff = true + } else { oldNorm := bytes.ReplaceAll(existing, []byte("\r\n"), []byte("\n")) newNorm := bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n")) - if bytes.Equal(oldNorm, newNorm) { - continue + if !bytes.Equal(oldNorm, newNorm) { + hasDiff = true } } - if err := os.MkdirAll(filepath.Dir(targetPath), 0o755); err != nil { - fmt.Fprintf(os.Stderr, "Error creating directory: %v\n", err) - os.Exit(1) - } - mode := os.FileMode(0o644) - if stat, err := os.Stat(targetPath); err == nil { - mode = stat.Mode().Perm() - } - if err := os.WriteFile(targetPath, content, mode); err != nil { - fmt.Fprintf(os.Stderr, "Error writing %s: %v\n", targetPath, err) - os.Exit(1) + if hasDiff { + diffCount++ + fmt.Println(targetPath) + + if isCheck { + continue + } + + if err := os.MkdirAll(filepath.Dir(targetPath), 0o755); err != nil { + fmt.Fprintf(os.Stderr, "Error creating directory: %v\n", err) + os.Exit(1) + } + mode := os.FileMode(0o644) + if stat, err := os.Stat(targetPath); err == nil { + mode = stat.Mode().Perm() + } + if err := os.WriteFile(targetPath, content, mode); err != nil { + fmt.Fprintf(os.Stderr, "Error writing %s: %v\n", targetPath, err) + os.Exit(1) + } } } } + + if isCheck { + if diffCount > 0 { + fmt.Println("Protobuf generated file problem(s) found.") + fmt.Println("Please run 'go run ./infra/vprotogen' to regenerate them.") + os.Exit(1) + } + fmt.Println("All Protobuf generated file check has been passed.") + } }