check

package
v0.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 31, 2025 License: Apache-2.0 Imports: 22 Imported by: 14

Documentation

Overview

Package check implements the SDK for custom lint and breaking change plugins.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CompareAnnotations

func CompareAnnotations(one Annotation, two Annotation) int

CompareAnnotations returns -1 if one < two, 1 if one > two, 0 otherwise.

func CompareCategories

func CompareCategories(one Category, two Category) int

CompareCategories returns -1 if one < two, 1 if one > two, 0 otherwise.

func CompareRules

func CompareRules(one Rule, two Rule) int

CompareRules returns -1 if one < two, 1 if one > two, 0 otherwise.

func Main

func Main(spec *Spec, options ...MainOption)

Main is the main entrypoint for a plugin that implements the given Spec.

A plugin just needs to provide a Spec, and then call this function within main.

func main() {
	check.Main(
		&check.Spec {
			Rules: []*check.RuleSpec{
				{
					ID:      "TIMESTAMP_SUFFIX",
					Default: true,
					Purpose: "Checks that all google.protobuf.Timestamps end in _time.",
					Type:    check.RuleTypeLint,
					Handler: check.RuleHandlerFunc(handleTimestampSuffix),
				},
			},
		},
	)
}

func NewCheckServiceHandler

func NewCheckServiceHandler(spec *Spec, options ...CheckServiceHandlerOption) (v1pluginrpc.CheckServiceHandler, error)

NewCheckServiceHandler returns a new v1pluginrpc.CheckServiceHandler for the given Spec.

The Spec will be validated.

func NewServer added in v0.4.0

func NewServer(spec *Spec, options ...ServerOption) (pluginrpc.Server, error)

NewServer is a convenience function that creates a new pluginrpc.Server for the given Spec.

This registers:

- The Check RPC on the command "check". - The ListRules RPC on the command "list-rules". - The ListCategories RPC on the command "list-categories". - The GetPluginInfo RPC on the command "info" (if spec.Info is present).

func ValidateSpec

func ValidateSpec(spec *Spec) error

ValidateSpec validates all values on a Spec.

This is exposed publicly so it can be run as part of plugin tests. This will verify that your Spec will result in a valid plugin.

Types

type AddAnnotationOption

type AddAnnotationOption func(*addAnnotationOptions)

AddAnnotationOption is an option with adding an Annotation to a ResponseWriter.

func WithAgainstDescriptor

func WithAgainstDescriptor(againstDescriptor protoreflect.Descriptor) AddAnnotationOption

WithAgainstDescriptor will set the AgainstLocation on the Annotation by extracting file and source path information from the descriptor itself.

It is not valid to use WithAgainstDescriptor if also using either WithAgainstFileName or WithAgainstSourcePath.

func WithAgainstFileName

func WithAgainstFileName(againstFileName string) AddAnnotationOption

WithAgainstFileName will set the FileName on the Annotation's AgainstLocation directly.

Typically, most users will use WithAgainstDescriptor to accomplish this task.

This will not set any line/column information. To do so, use WithAgainstFileNameAndSourcePath.

It is not valid to use WithAgainstDescriptor if also using either WithAgainstFileName or WithAgainstFileNameAndSourcePath.

func WithAgainstFileNameAndSourcePath

func WithAgainstFileNameAndSourcePath(againstFileName string, againstSourcePath protoreflect.SourcePath) AddAnnotationOption

WithAgainstFileNameAndSourcePath will set the Filename and SourcePath on the Annotation's AgainstLocation directly.

Typically, most users will use WithAgainstDescriptor to accomplish this task.

It is not valid to use WithAgainstDescriptor if also using either WithAgainstFileName or WithAgainstFileNameAndSourcePath.

func WithDescriptor

func WithDescriptor(descriptor protoreflect.Descriptor) AddAnnotationOption

WithDescriptor will set the Location on the Annotation by extracting file and source path information from the descriptor itself.

It is not valid to use WithDescriptor if also using either WithFileName or WithSourcePath.

func WithFileName

func WithFileName(fileName string) AddAnnotationOption

WithFileName will set the FileName on the Annotation's Location directly.

Typically, most users will use WithDescriptor to accomplish this task.

This will not set any line/column information. To do so, use WithFileNameAndSourcePath.

It is not valid to use WithDescriptor if also using either WithFileName or WithFileNameAndSourcePath.

func WithFileNameAndSourcePath

func WithFileNameAndSourcePath(fileName string, sourcePath protoreflect.SourcePath) AddAnnotationOption

WithFileNameAndSourcePath will set the SourcePath on the Annotation's Location directly.

Typically, most users will use WithDescriptor to accomplish this task.

It is not valid to use WithDescriptor if also using either WithFileName or WithFileNameAndSourcePath.

func WithMessage

func WithMessage(message string) AddAnnotationOption

WithMessage sets the message on the Annotation.

If there are multiple calls to WithMessage or WithMessagef, the last one wins.

func WithMessagef

func WithMessagef(format string, args ...any) AddAnnotationOption

WithMessagef sets the message on the Annotation.

If there are multiple calls to WithMessage or WithMessagef, the last one wins.

type Annotation

type Annotation interface {
	// RuleID is the ID of the Rule that failed.
	//
	// This will always be present.
	RuleID() string
	// Message is a user-readable message describing the failure.
	Message() string
	// FileLocation is the location of the failure.
	FileLocation() descriptor.FileLocation
	// AgainstFileLocation is the FileLocation of the failure in the against FileDescriptors.
	//
	// Will only potentially be produced for breaking change rules.
	AgainstFileLocation() descriptor.FileLocation
	// contains filtered or unexported methods
}

Annotation represents a rule Failure.

An annotation always contains the ID of the Rule that failed. It also optionally contains a user-readable message, a location of the failure, and a location of the failure in the against FileDescriptors.

Annotations are created on the server-side via ResponseWriters, and returned from Clients on Responses.

type Category

type Category interface {
	// ID is the ID of the Category.
	//
	// Always present.
	//
	// This uniquely identifies the Category.
	ID() string
	// A user-displayable purpose of the category.
	//
	// Always present.
	Purpose() string
	// Deprecated returns whether or not this Category is deprecated.
	//
	// If the Category is deprecated, it may be replaced by zero or more Categories. These will
	// be denoted by ReplacementIDs.
	Deprecated() bool
	// ReplacementIDs returns the IDs of the Categories that replace this Category, if this Category is deprecated.
	//
	// This means that the combination of the Categories specified by ReplacementIDs replace this Category entirely,
	// and this Category is considered equivalent to the AND of the categories specified by ReplacementIDs.
	//
	// This will only be non-empty if Deprecated is true.
	//
	// It is not valid for a deprecated Category to specfiy another deprecated Category as a replacement.
	ReplacementIDs() []string
	// contains filtered or unexported methods
}

Category is rule category.

Categories have unique IDs. On the server-side (i.e. the plugin), Categories are created by CategorySpecs. Clients can list all available plugin Categories by calling ListCategories.

type CategorySpec

type CategorySpec struct {
	// Required.
	ID string
	// Required.
	Purpose        string
	Deprecated     bool
	ReplacementIDs []string
}

CategorySpec is the spec for a Category.

It is used to construct a Category on the server-side (i.e. within the plugin). It specifies the ID, purpose, and a CategoryHandler to actually run the Category logic.

Generally, these are provided to Main. This library will handle Check and ListCategories calls based on the provided CategorySpecs.

type CheckCallOption

type CheckCallOption func(*checkCallOptions)

CheckCallOption is an option for a Client.Check call.

type CheckServiceHandlerOption

type CheckServiceHandlerOption func(*checkServiceHandlerOptions)

CheckServiceHandlerOption is an option for CheckServiceHandler.

func CheckServiceHandlerWithParallelism

func CheckServiceHandlerWithParallelism(parallelism int) CheckServiceHandlerOption

CheckServiceHandlerWithParallelism returns a new CheckServiceHandlerOption that sets the parallelism by which Rules will be run.

If this is set to a value >= 1, this many concurrent Rules can be run at the same time. A value of 0 indicates the default behavior, which is to use runtime.GOMAXPROCS(0).

A value if < 0 has no effect.

type Client

type Client interface {
	info.Client

	// Check invokes a check using the plugin..
	Check(ctx context.Context, request Request, options ...CheckCallOption) (Response, error)
	// ListRules lists all available Rules from the plugin.
	//
	// The Rules will be sorted by Rule ID.
	// Returns error if duplicate Rule IDs were detected from the underlying source.
	ListRules(ctx context.Context, options ...ListRulesCallOption) ([]Rule, error)
	// ListCategories lists all available Categories from the plugin.
	//
	// The Categories will be sorted by Category ID.
	// Returns error if duplicate Category IDs were detected from the underlying source.
	ListCategories(ctx context.Context, options ...ListCategoriesCallOption) ([]Category, error)
	// contains filtered or unexported methods
}

Client is a client for a custom lint or breaking change plugin.

All calls with pluginrpc.Error with CodeUnimplemented if any procedure is not implemented.

func NewClient

func NewClient(pluginrpcClient pluginrpc.Client, options ...ClientOption) Client

NewClient returns a new Client for the given pluginrpc.Client.

func NewClientForSpec

func NewClientForSpec(spec *Spec, options ...ClientForSpecOption) (Client, error)

NewClientForSpec return a new Client that directly uses the given Spec.

This should primarily be used for testing.

type ClientForSpecOption

type ClientForSpecOption interface {
	// contains filtered or unexported methods
}

ClientForSpecOption is an option for a new Client constructed with NewClientForSpec.

type ClientOption

type ClientOption interface {
	ClientForSpecOption
	// contains filtered or unexported methods
}

ClientOption is an option for a new Client.

func ClientWithCaching added in v0.4.0

func ClientWithCaching() ClientOption

ClientWithCaching returns a new ClientOption that will result caching for items expected to be static:

- The Rules from ListRules. - The Categories from ListCategories. - PluginInfo from GetPluginInfo.

The default is to not cache.

type ListCategoriesCallOption

type ListCategoriesCallOption func(*listCategoriesCallOptions)

ListCategoriesCallOption is an option for a Client.ListCategories call.

type ListRulesCallOption

type ListRulesCallOption func(*listRulesCallOptions)

ListRulesCallOption is an option for a Client.ListRules call.

type MainOption

type MainOption func(*mainOptions)

MainOption is an option for Main.

func MainWithParallelism

func MainWithParallelism(parallelism int) MainOption

MainWithParallelism returns a new MainOption that sets the parallelism by which Rules will be run.

If this is set to a value >= 1, this many concurrent Rules can be run at the same time. A value of 0 indicates the default behavior, which is to use runtime.GOMAXPROCS(0).

A value if < 0 has no effect.

type Request

type Request interface {
	// FileDescriptors contains the FileDescriptors to check.
	//
	// Will never be nil or empty.
	//
	// FileDescriptors are guaranteed to be unique with respect to their name.
	FileDescriptors() []descriptor.FileDescriptor
	// AgainstFileDescriptors contains the FileDescriptors to check against, in the
	// case of breaking change plugins.
	//
	// May be empty, including in the case where we did actually specify against
	// FileDescriptors.
	//
	// FileDescriptors are guaranteed to be unique with respect to their name.
	AgainstFileDescriptors() []descriptor.FileDescriptor
	// Options contains any options passed to the plugin.
	//
	// Will never be nil, but may have no values.
	Options() option.Options
	// RuleIDs returns the specific IDs the of Rules to use.
	//
	// If empty, all default Rules will be used.
	// The returned RuleIDs will be sorted.
	//
	// This may return more than 250 IDs; the underlying Client implemention is required to do
	// any necessary chunking.
	//
	// RuleHandlers can safely ignore this - the handling of RuleIDs will have already
	// been performed prior to the Request reaching the RuleHandler.
	RuleIDs() []string
	// contains filtered or unexported methods
}

Request is a request to a plugin to run checks.

func NewRequest

func NewRequest(
	fileDescriptors []descriptor.FileDescriptor,
	options ...RequestOption,
) (Request, error)

NewRequest returns a new Request for the given FileDescriptors.

FileDescriptors are always required. To set against FileDescriptors or options, use WithAgainstFileDescriptors and WithOption.

func RequestForProtoRequest

func RequestForProtoRequest(protoRequest *checkv1.CheckRequest) (Request, error)

RequestForProtoRequest returns a new Request for the given checkv1.Request.

type RequestOption

type RequestOption func(*requestOptions)

RequestOption is an option for a new Request.

func WithAgainstFileDescriptors added in v0.3.0

func WithAgainstFileDescriptors(againstFileDescriptors []descriptor.FileDescriptor) RequestOption

WithAgainstFileDescriptors adds the given against FileDescriptors to the Request.

func WithOptions

func WithOptions(options option.Options) RequestOption

WithOption adds the given Options to the Request.

func WithRuleIDs

func WithRuleIDs(ruleIDs ...string) RequestOption

WithRuleIDs specifies that the given rule IDs should be used on the Request.

Multiple calls to WithRuleIDs will result in the new rule IDs being appended. If duplicate rule IDs are specified, this will result in an error.

type Response

type Response interface {
	// Annotations returns all of the Annotations.
	//
	// The returned annotations will be sorted.
	Annotations() []Annotation
	// contains filtered or unexported methods
}

Response is a response from a plugin for a check call.

type ResponseWriter

type ResponseWriter interface {
	// AddAnnotation adds an Annotation with the rule ID that is tied to this ResponseWriter.
	//
	// Fields of the Annotation are controlled with AddAnnotationOptions, of which there are several:
	//
	//   - WithMessage/WithMessagef: Add a message to the Annotation.
	//   - WithDescriptor/WithAgainstDescriptor: Use the protoreflect.Descriptor to determine Location information.
	//   - WithFileName/WithAgainstFileName: Use the given file name on the Location.
	//   - WithFileNameAndSourcePath/WithAgainstFileNameAndSourcePath: Use the given explicit file name and source path on the Location.
	//
	// There are some rules to note when using AddAnnotationOptions:
	//
	//   - Multiple calls of WithMessage/WithMessagef will overwrite previous calls.
	//   - You must either use WithDescriptor, or use WithFileName/WithSourcePath, but you cannot
	//     use these together. Location information is determined either from the Descriptor, or
	//     from explicit setting via WithFileName/WithFileNameAndSourcePath. Same applies to the Against equivalents.
	//
	// Don't worry, these rules are verified when building a Response.
	//
	// Most users will use WithDescriptor/WithAgainstDescriptor as opposed to their lower-level variants.
	AddAnnotation(options ...AddAnnotationOption)
	// contains filtered or unexported methods
}

ResponseWriter is used by plugin implmentations to add Annotations to responses.

A ResponseWriter is tied to a specific rule, and is passed to a RuleHandler. The ID of the Rule will be automatically populated for any added Annotations.

type Rule

type Rule interface {
	// ID is the ID of the Rule.
	//
	// Always present.
	//
	// This uniquely identifies the Rule.
	ID() string
	// The categories that the Rule is a part of.
	//
	// Optional.
	//
	// Buf uses categories to include or exclude sets of rules via configuration.
	Categories() []Category
	// Whether or not the Rule is a default Rule.
	//
	// If a Rule is a default Rule, it will be called if a Request specifies no specific Rule IDs.
	//
	// A deprecated rule cannot be a default rule.
	Default() bool
	// A user-displayable purpose of the rule.
	//
	// Always present.
	//
	// This should be a proper sentence that starts with a capital letter and ends in a period.
	Purpose() string
	// Type is the type of the Rule.
	Type() RuleType
	// Deprecated returns whether or not this Rule is deprecated.
	//
	// If the Rule is deprecated, it may be replaced by 0 or more Rules. These will be denoted
	// by ReplacementIDs.
	Deprecated() bool
	// ReplacementIDs returns the IDs of the Rules that replace this Rule, if this Rule is deprecated.
	//
	// This means that the combination of the Rules specified by ReplacementIDs replace this Rule entirely,
	// and this Rule is considered equivalent to the AND of the rules specified by ReplacementIDs.
	//
	// This will only be non-empty if Deprecated is true.
	//
	// It is not valid for a deprecated Rule to specfiy another deprecated Rule as a replacement.
	ReplacementIDs() []string
	// contains filtered or unexported methods
}

Rule is a single lint or breaking change rule.

Rules have unique IDs. On the server-side (i.e. the plugin), Rules are created by RuleSpecs. Clients can list all available plugin Rules by calling ListRules.

type RuleHandler

type RuleHandler interface {
	Handle(ctx context.Context, responseWriter ResponseWriter, request Request) error
}

RuleHandler implements the check logic for a single Rule.

A RuleHandler takes in a Request, and writes Annotations to the ResponseWriter.

type RuleHandlerFunc

type RuleHandlerFunc func(context.Context, ResponseWriter, Request) error

RuleHandlerFunc is a function that implements RuleHandler.

func (RuleHandlerFunc) Handle

func (r RuleHandlerFunc) Handle(ctx context.Context, responseWriter ResponseWriter, request Request) error

Handle implements RuleHandler.

type RuleSpec

type RuleSpec struct {
	// Required.
	ID          string
	CategoryIDs []string
	Default     bool
	// Required.
	Purpose string
	// Required.
	Type           RuleType
	Deprecated     bool
	ReplacementIDs []string
	// Required.
	Handler RuleHandler
}

RuleSpec is the spec for a Rule.

It is used to construct a Rule on the server-side (i.e. within the plugin). It specifies the ID, categories, purpose, type, and a RuleHandler to actually run the Rule logic.

Generally, these are provided to Main. This library will handle Check and ListRules calls based on the provided RuleSpecs.

type RuleType

type RuleType int

RuleType is the type of Rule.

const (
	// RuleTypeLint is a lint Rule.
	RuleTypeLint RuleType = 1
	// RuleTypeBreaking is a breaking change Rule.
	RuleTypeBreaking RuleType = 2
)

func (RuleType) String

func (t RuleType) String() string

String implements fmt.Stringer.

type ServerOption added in v0.4.0

type ServerOption func(*serverOptions)

ServerOption is an option for Server.

func ServerWithParallelism added in v0.4.0

func ServerWithParallelism(parallelism int) ServerOption

ServerWithParallelism returns a new ServerOption that sets the parallelism by which Rules will be run.

If this is set to a value >= 1, this many concurrent Rules can be run at the same time. A value of 0 indicates the default behavior, which is to use runtime.GOMAXPROCS(0).

A value if < 0 has no effect.

type Spec

type Spec struct {
	// Required.
	//
	// All RuleSpecs must have Category IDs that match a CategorySpec within Categories.
	//
	// No IDs can overlap with Category IDs in Categories.
	Rules []*RuleSpec
	// Required if any RuleSpec specifies a category.
	//
	// All CategorySpecs must have an ID that matches at least one Category ID on a
	// RuleSpec within Rules.
	//
	// No IDs can overlap with Rule IDs in Rules.
	Categories []*CategorySpec

	// Info contains information about a plugin.
	//
	// Optional.
	//
	// If not set, the resulting server will not implement the PluginInfoService.
	Info *info.Spec

	// Before is a function that will be executed before any RuleHandlers are
	// invoked that returns a new Context and Request. This new Context and
	// Request will be passed to the RuleHandlers. This allows for any
	// pre-processing that needs to occur.
	Before func(ctx context.Context, request Request) (context.Context, Request, error)
}

Spec is the spec for a plugin.

It is used to construct a plugin on the server-side (i.e. within the plugin).

Generally, this is provided to Main. This library will handle Check and ListRules calls based on the provided RuleSpecs.

Directories

Path Synopsis
Package checktest provides testing helpers when writing lint and breaking change plugins.
Package checktest provides testing helpers when writing lint and breaking change plugins.
Package checkutil implements helpers for the check package.
Package checkutil implements helpers for the check package.
internal
example/cmd/buf-plugin-field-lower-snake-case
Package main implements a simple plugin that checks that all field names are lower_snake_case.
Package main implements a simple plugin that checks that all field names are lower_snake_case.
example/cmd/buf-plugin-field-option-safe-for-ml
Package main implements a plugin that implements two Rules:
Package main implements a plugin that implements two Rules:
example/cmd/buf-plugin-syntax-specified
Package main implements a simple plugin that checks that syntax is specified in every file.
Package main implements a simple plugin that checks that syntax is specified in every file.
example/cmd/buf-plugin-timestamp-suffix
Package main implements a simple plugin that checks that all google.protobuf.Timestamp fields end in a specific suffix.
Package main implements a simple plugin that checks that all google.protobuf.Timestamp fields end in a specific suffix.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
JackTT - Gopher 🇻🇳