Skip to content

Commit

Permalink
Merge pull request #87 from bufdev/buf-plugin-aep
Browse files Browse the repository at this point in the history
Add buf-plugin-aep
  • Loading branch information
rofrankel authored Sep 14, 2024
2 parents 14856ea + 03cf51a commit 70e81ee
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 7 deletions.
175 changes: 175 additions & 0 deletions cmd/buf-plugin-aep/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package main

import (
"context"
"errors"
"fmt"
"log"
"strings"

"buf.build/go/bufplugin/check"
"github.com/aep-dev/api-linter/lint"
"github.com/aep-dev/api-linter/rules"
"github.com/jhump/protoreflect/desc"
"google.golang.org/protobuf/reflect/protoreflect"
)

const (
aepCategoryID = "AEP"
aepCoreCategoryID = "AEP_CORE"
)

type fileDescriptorsContextKey struct{}

func main() {
spec, err := newSpec()
if err != nil {
log.Fatalln(err)
}
// AEP rules cannot be run in parallel as there is thread-unsafe code in
// this repository that causes concurrent read and write access to a map.
check.Main(spec, check.MainWithParallelism(1))
}

func newSpec() (*check.Spec, error) {
ruleRegistry := lint.NewRuleRegistry()
if err := rules.Add(ruleRegistry); err != nil {
return nil, err
}
ruleSpecs := make([]*check.RuleSpec, 0, len(ruleRegistry))
for _, protoRule := range ruleRegistry {
ruleSpec, err := newRuleSpec(protoRule)
if err != nil {
return nil, err
}
ruleSpecs = append(ruleSpecs, ruleSpec)
}
return &check.Spec{
Rules: ruleSpecs,
Categories: []*check.CategorySpec{
{
ID: aepCategoryID,
Purpose: "Checks all API Enhancement proposals as specified at https://aep.dev.",
},
{
ID: aepCoreCategoryID,
Purpose: "Checks all core API Enhancement proposals as specified at https://aep.dev.",
},
},
Before: before,
}, nil
}

func newRuleSpec(protoRule lint.ProtoRule) (*check.RuleSpec, error) {
ruleName := protoRule.GetName()
if !ruleName.IsValid() {
return nil, fmt.Errorf("lint.RuleName is invalid: %q", ruleName)
}

split := strings.Split(string(ruleName), "::")
if len(split) != 3 {
return nil, fmt.Errorf("unknown lint.RuleName format, expected three parts split by '::' : %q", ruleName)
}
categoryIDs := []string{aepCategoryID}
switch extraCategoryID := split[0]; extraCategoryID {
case "core":
categoryIDs = append(categoryIDs, aepCoreCategoryID)
default:
return nil, fmt.Errorf("unknown lint.RuleName format: unknown category %q : %q", extraCategoryID, ruleName)
}

// The allowed characters for RuleName are a-z, 0-9, -.
// The separator :: is also allowed.
// We do a translation of these into valid check.Rule IDs.
ruleID := "AEP_" + strings.Join(split[1:3], "_")
ruleID = strings.ReplaceAll(ruleID, "-", "_")
ruleID = strings.ToUpper(ruleID)

return &check.RuleSpec{
ID: ruleID,
CategoryIDs: categoryIDs,
Default: true,
Purpose: fmt.Sprintf("Checks AEP rule %s.", ruleName),
Type: check.RuleTypeLint,
Handler: newRuleHandler(protoRule),
}, nil
}

func newRuleHandler(protoRule lint.ProtoRule) check.RuleHandler {
return check.RuleHandlerFunc(
func(ctx context.Context, responseWriter check.ResponseWriter, request check.Request) error {
fileDescriptors, _ := ctx.Value(fileDescriptorsContextKey{}).([]*desc.FileDescriptor)
for _, fileDescriptor := range fileDescriptors {
for _, problem := range protoRule.Lint(fileDescriptor) {
if err := addProblem(responseWriter, problem); err != nil {
return err
}
}
}
return nil
},
)
}

func addProblem(responseWriter check.ResponseWriter, problem lint.Problem) error {
addAnnotationOptions := []check.AddAnnotationOption{
check.WithMessage(problem.Message),
}
descriptor := problem.Descriptor
if descriptor == nil {
// This should never happen.
return errors.New("got nil problem.Descriptor")
}
fileDescriptor := descriptor.GetFile()
if fileDescriptor == nil {
// If we do not have a FileDescriptor, we cannot report a location.
responseWriter.AddAnnotation(addAnnotationOptions...)
return nil
}
// If a location is available from the problem, we use that directly.
if location := problem.Location; location != nil {
addAnnotationOptions = append(
addAnnotationOptions,
check.WithFileNameAndSourcePath(
fileDescriptor.GetName(),
protoreflect.SourcePath(location.GetPath()),
),
)
} else {
// Otherwise we check the source info for the descriptor from the problem.
if location := descriptor.GetSourceInfo(); location != nil {
addAnnotationOptions = append(
addAnnotationOptions,
check.WithFileNameAndSourcePath(
fileDescriptor.GetName(),
protoreflect.SourcePath(location.GetPath()),
),
)
}
}
responseWriter.AddAnnotation(addAnnotationOptions...)
return nil
}

func before(ctx context.Context, request check.Request) (context.Context, check.Request, error) {
fileDescriptors, err := nonImportFileDescriptorsForFiles(request.Files())
if err != nil {
return nil, nil, err
}
ctx = context.WithValue(ctx, fileDescriptorsContextKey{}, fileDescriptors)
return ctx, request, nil
}

func nonImportFileDescriptorsForFiles(files []check.File) ([]*desc.FileDescriptor, error) {
if len(files) == 0 {
return nil, nil
}
reflectFileDescriptors := make([]protoreflect.FileDescriptor, 0, len(files))
for _, file := range files {
if file.IsImport() {
continue
}
reflectFileDescriptors = append(reflectFileDescriptors, file.FileDescriptor())
}
return desc.WrapFiles(reflectFileDescriptors)
}
14 changes: 10 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
module github.com/aep-dev/api-linter

go 1.21
go 1.22.0

toolchain go1.23.1

require (
bitbucket.org/creachadair/stringset v0.0.14
buf.build/gen/go/aep/api/protocolbuffers/go v1.34.2-20240717204542-6b47820e6610.2
buf.build/go/bufplugin v0.2.0
cloud.google.com/go/longrunning v0.6.1
github.com/bmatcuk/doublestar/v4 v4.6.1
github.com/gertd/go-pluralize v0.2.1
Expand All @@ -23,18 +24,23 @@ require (
)

require (
buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.34.2-20240904181154-a0be11449112.2 // indirect
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.2-20240717164558-a6c49f84cc0f.2 // indirect
buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.34.2-20240828222655-5345c0a56177.2 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/bufbuild/protocompile v0.14.1 // indirect
github.com/bufbuild/protovalidate-go v0.6.5 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/google/cel-go v0.21.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/grpc v1.66.2 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
pluginrpc.com/pluginrpc v0.3.0 // indirect
)
24 changes: 21 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,47 @@ bitbucket.org/creachadair/stringset v0.0.14 h1:t1ejQyf8utS4GZV/4fM+1gvYucggZkfhb
bitbucket.org/creachadair/stringset v0.0.14/go.mod h1:Ej8fsr6rQvmeMDf6CCWMWGb14H9mz8kmDgPPTdiVT0w=
buf.build/gen/go/aep/api/protocolbuffers/go v1.34.2-20240717204542-6b47820e6610.2 h1:BweVF7lr8aI/rVFwYhrieMYXEKWfALRIAygphjVTJOg=
buf.build/gen/go/aep/api/protocolbuffers/go v1.34.2-20240717204542-6b47820e6610.2/go.mod h1:7GnupzUM4BaoyPcUUTZZwrcVZvCBNcyn6iR/kddRTuU=
buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.34.2-20240904181154-a0be11449112.2 h1:X9qBPcvWGOJs/CeRVLoxxLJwC/eKyWDS/G4nj+3KGMY=
buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.34.2-20240904181154-a0be11449112.2/go.mod h1:B+9TKHRYqoAUW57pLjhkLOnBCu0DQYMV+f7imQ9nXwI=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.2-20240717164558-a6c49f84cc0f.2 h1:SZRVx928rbYZ6hEKUIN+vtGDkl7uotABRWGY4OAg5gM=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.2-20240717164558-a6c49f84cc0f.2/go.mod h1:ylS4c28ACSI59oJrOdW4pHS4n0Hw4TgSPHn8rpHl4Yw=
buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.34.2-20240828222655-5345c0a56177.2 h1:oSi+Adw4xvIjXrW8eY8QGR3sBdfWeY5HN/RefnRt52M=
buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.34.2-20240828222655-5345c0a56177.2/go.mod h1:GjH0gjlY/ns16X8d6eaXV2W+6IFwsO5Ly9WVnzyd1E0=
buf.build/go/bufplugin v0.2.0 h1:nnNvWzUgQXitRDmjWWIkuXj9klreAAE94sVCsL+0v5g=
buf.build/go/bufplugin v0.2.0/go.mod h1:ZZYGt6PDcjbBSywdz/G8NdXkIuWi2rzR8CduGzbCPdk=
cloud.google.com/go/longrunning v0.6.1 h1:lOLTFxYpr8hcRtcwWir5ITh1PAKUD/sG2lKrTSYjyMc=
cloud.google.com/go/longrunning v0.6.1/go.mod h1:nHISoOZpBcmlwbJmiVk5oDRz0qG/ZxPynEGs1iZ79s0=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/bufbuild/protovalidate-go v0.6.5 h1:WucDKXIbK22WjkO8A8J6Yyxxy0jl91Oe9LSMduq3YEE=
github.com/bufbuild/protovalidate-go v0.6.5/go.mod h1:LHDiGCWSM3GagZEnyEZ1sPtFwi6Ja4tVTi/DCc+iDFI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
github.com/gertd/go-pluralize v0.2.1 h1:M3uASbVjMnTsPb0PNqg+E/24Vwigyo/tvyMTtAlLgiA=
github.com/gertd/go-pluralize v0.2.1/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI=
github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94=
github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
Expand All @@ -52,10 +65,13 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
Expand All @@ -78,3 +94,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
pluginrpc.com/pluginrpc v0.3.0 h1:9BSGRIdNLf/+EU3JrkISLwgIAuYxBrMEzSfNyeQ/Gp0=
pluginrpc.com/pluginrpc v0.3.0/go.mod h1:UNWZ941hcVAoOZUn8YZsMmOZBzbUjQa3XMns8RQLp9o=

0 comments on commit 70e81ee

Please sign in to comment.