diff --git a/private/buf/bufctl/controller.go b/private/buf/bufctl/controller.go index 67bcb18f23..94f994bdc8 100644 --- a/private/buf/bufctl/controller.go +++ b/private/buf/bufctl/controller.go @@ -65,6 +65,7 @@ type ImageWithConfig interface { LintConfig() bufconfig.LintConfig BreakingConfig() bufconfig.BreakingConfig PluginConfigs() []bufconfig.PluginConfig + PolicyConfigs() []bufconfig.PolicyConfig isImageWithConfig() } @@ -404,7 +405,10 @@ func (c *controller) GetTargetImageWithConfigsAndCheckClient( } lintConfig := bufconfig.DefaultLintConfigV1 breakingConfig := bufconfig.DefaultBreakingConfigV1 - var pluginConfigs []bufconfig.PluginConfig + var ( + pluginConfigs []bufconfig.PluginConfig + policyConfigs []bufconfig.PolicyConfig + ) pluginKeyProvider := bufplugin.NopPluginKeyProvider bufYAMLFile, err := bufconfig.GetBufYAMLFileForPrefixOrOverride( ctx, @@ -442,6 +446,7 @@ func (c *controller) GetTargetImageWithConfigsAndCheckClient( // buf.yaml file is found, the PluginConfigs from the buf.yaml file and the PluginKeys // from the buf.lock file are resolved to create the PluginKeyProvider. pluginConfigs = bufYAMLFile.PluginConfigs() + policyConfigs = bufYAMLFile.PolicyConfigs() // If a config override is provided, the PluginConfig remote Refs use the BSR // to resolve the PluginKeys. No buf.lock is required. // If the buf.yaml file is not found, the bufplugin.NopPluginKeyProvider is returned. @@ -485,6 +490,7 @@ func (c *controller) GetTargetImageWithConfigsAndCheckClient( lintConfig, breakingConfig, pluginConfigs, + policyConfigs, ), } checkClient, err := bufcheck.NewClient( @@ -495,6 +501,7 @@ func (c *controller) GetTargetImageWithConfigsAndCheckClient( ), bufcheck.ClientWithLocalWasmPluginsFromOS(), bufcheck.ClientWithRemoteWasmPlugins(pluginKeyProvider, c.pluginDataProvider), + bufcheck.ClientWithLocalPolicies(bucket), ) if err != nil { return nil, nil, err @@ -801,6 +808,13 @@ func (c *controller) GetCheckClientForWorkspace( if err != nil { return nil, err } + bucket, err := c.storageosProvider.NewReadWriteBucket( + ".", + storageos.ReadWriteBucketWithSymlinksIfSupported(), + ) + if err != nil { + return nil, err + } return bufcheck.NewClient( c.logger, bufcheck.ClientWithStderr(c.container.Stderr()), @@ -812,6 +826,7 @@ func (c *controller) GetCheckClientForWorkspace( pluginKeyProvider, c.pluginDataProvider, ), + bufcheck.ClientWithLocalPolicies(bucket), ) } @@ -1182,6 +1197,7 @@ func (c *controller) buildTargetImageWithConfigs( workspace.GetLintConfigForOpaqueID(module.OpaqueID()), workspace.GetBreakingConfigForOpaqueID(module.OpaqueID()), workspace.PluginConfigs(), + workspace.PolicyConfigs(), ), ) } diff --git a/private/buf/bufctl/image_with_config.go b/private/buf/bufctl/image_with_config.go index cdba3d54b3..6e3e59ed4c 100644 --- a/private/buf/bufctl/image_with_config.go +++ b/private/buf/bufctl/image_with_config.go @@ -28,6 +28,7 @@ type imageWithConfig struct { lintConfig bufconfig.LintConfig breakingConfig bufconfig.BreakingConfig pluginConfigs []bufconfig.PluginConfig + policyConfigs []bufconfig.PolicyConfig } func newImageWithConfig( @@ -37,6 +38,7 @@ func newImageWithConfig( lintConfig bufconfig.LintConfig, breakingConfig bufconfig.BreakingConfig, pluginConfigs []bufconfig.PluginConfig, + policyConfigs []bufconfig.PolicyConfig, ) *imageWithConfig { return &imageWithConfig{ Image: image, @@ -45,6 +47,7 @@ func newImageWithConfig( lintConfig: lintConfig, breakingConfig: breakingConfig, pluginConfigs: pluginConfigs, + policyConfigs: policyConfigs, } } @@ -68,4 +71,8 @@ func (i *imageWithConfig) PluginConfigs() []bufconfig.PluginConfig { return i.pluginConfigs } +func (i *imageWithConfig) PolicyConfigs() []bufconfig.PolicyConfig { + return i.policyConfigs +} + func (*imageWithConfig) isImageWithConfig() {} diff --git a/private/buf/buflsp/file.go b/private/buf/buflsp/file.go index 1b424cd568..0f65281d51 100644 --- a/private/buf/buflsp/file.go +++ b/private/buf/buflsp/file.go @@ -721,6 +721,7 @@ func (f *file) RunLints(ctx context.Context) bool { f.workspace.GetLintConfigForOpaqueID(f.module.OpaqueID()), f.image, bufcheck.WithPluginConfigs(f.workspace.PluginConfigs()...), + bufcheck.WithPolicyConfigs(f.workspace.PolicyConfigs()...), )) } @@ -749,6 +750,7 @@ func (f *file) RunBreaking(ctx context.Context) bool { f.image, f.againstImage, bufcheck.WithPluginConfigs(f.workspace.PluginConfigs()...), + bufcheck.WithPolicyConfigs(f.workspace.PolicyConfigs()...), )) } diff --git a/private/buf/bufworkspace/workspace.go b/private/buf/bufworkspace/workspace.go index 7fdcd3be14..bcacdaafce 100644 --- a/private/buf/bufworkspace/workspace.go +++ b/private/buf/bufworkspace/workspace.go @@ -21,6 +21,7 @@ import ( "github.com/bufbuild/buf/private/bufpkg/bufmodule" "github.com/bufbuild/buf/private/bufpkg/bufparse" "github.com/bufbuild/buf/private/bufpkg/bufplugin" + "github.com/bufbuild/buf/private/bufpkg/bufpolicy" ) // Workspace is a buf workspace. @@ -81,6 +82,14 @@ type Workspace interface { // // These come from the buf.lock file. Only v2 supports plugins. RemotePluginKeys() []bufplugin.PluginKey + // PolicyConfigs gets the configured PolicyConfigs of the Workspace. + // + // These come from the buf.yaml files. + PolicyConfigs() []bufconfig.PolicyConfig + // RemotePolicyKeys gets the remote PolicyKeys of the Workspace. + // + // These come from the buf.lock file. Only v2 supports policies. + RemotePolicyKeys() []bufpolicy.PolicyKey // ConfiguredDepModuleRefs returns the configured dependencies of the Workspace as Refs. // // These come from buf.yaml files. @@ -114,6 +123,8 @@ type workspace struct { opaqueIDToBreakingConfig map[string]bufconfig.BreakingConfig pluginConfigs []bufconfig.PluginConfig remotePluginKeys []bufplugin.PluginKey + policyConfigs []bufconfig.PolicyConfig + remotePolicyKeys []bufpolicy.PolicyKey configuredDepModuleRefs []bufparse.Ref // If true, the workspace was created from v2 buf.yamls. @@ -127,6 +138,7 @@ func newWorkspace( opaqueIDToBreakingConfig map[string]bufconfig.BreakingConfig, pluginConfigs []bufconfig.PluginConfig, remotePluginKeys []bufplugin.PluginKey, + policyConfigs []bufconfig.PolicyConfig, configuredDepModuleRefs []bufparse.Ref, isV2 bool, ) *workspace { @@ -136,6 +148,7 @@ func newWorkspace( opaqueIDToBreakingConfig: opaqueIDToBreakingConfig, pluginConfigs: pluginConfigs, remotePluginKeys: remotePluginKeys, + policyConfigs: policyConfigs, configuredDepModuleRefs: configuredDepModuleRefs, isV2: isV2, } @@ -157,6 +170,14 @@ func (w *workspace) RemotePluginKeys() []bufplugin.PluginKey { return slices.Clone(w.remotePluginKeys) } +func (w *workspace) PolicyConfigs() []bufconfig.PolicyConfig { + return slices.Clone(w.policyConfigs) +} + +func (w *workspace) RemotePolicyKeys() []bufpolicy.PolicyKey { + return slices.Clone(w.remotePolicyKeys) +} + func (w *workspace) ConfiguredDepModuleRefs() []bufparse.Ref { return slices.Clone(w.configuredDepModuleRefs) } diff --git a/private/buf/bufworkspace/workspace_provider.go b/private/buf/bufworkspace/workspace_provider.go index 5b52d118a2..68127f5a06 100644 --- a/private/buf/bufworkspace/workspace_provider.go +++ b/private/buf/bufworkspace/workspace_provider.go @@ -149,6 +149,7 @@ func (w *workspaceProvider) GetWorkspaceForModuleKey( var ( pluginConfigs []bufconfig.PluginConfig remotePluginKeys []bufplugin.PluginKey + policyConfigs []bufconfig.PolicyConfig ) if config.configOverride != "" { bufYAMLFile, err := bufconfig.GetBufYAMLFileForOverride(config.configOverride) @@ -205,6 +206,8 @@ func (w *workspaceProvider) GetWorkspaceForModuleKey( return nil, err } } + + policyConfigs = bufYAMLFile.PolicyConfigs() } } @@ -243,6 +246,7 @@ func (w *workspaceProvider) GetWorkspaceForModuleKey( opaqueIDToBreakingConfig, pluginConfigs, remotePluginKeys, + policyConfigs, nil, false, ), nil @@ -408,6 +412,7 @@ func (w *workspaceProvider) getWorkspaceForBucketAndModuleDirPathsV1Beta1OrV1( v1WorkspaceTargeting.bucketIDToModuleConfig, nil, // No PluginConfigs for v1 nil, // No remote PluginKeys for v1 + nil, // No PolicyConfigs for v1 v1WorkspaceTargeting.allConfiguredDepModuleRefs, false, ) @@ -507,6 +512,7 @@ func (w *workspaceProvider) getWorkspaceForBucketBufYAMLV2( v2Targeting.bucketIDToModuleConfig, v2Targeting.bufYAMLFile.PluginConfigs(), remotePluginKeys, + v2Targeting.bufYAMLFile.PolicyConfigs(), v2Targeting.bufYAMLFile.ConfiguredDepModuleRefs(), true, ) @@ -518,6 +524,7 @@ func (w *workspaceProvider) getWorkspaceForBucketModuleSet( bucketIDToModuleConfig map[string]bufconfig.ModuleConfig, pluginConfigs []bufconfig.PluginConfig, remotePluginKeys []bufplugin.PluginKey, + policyConfigs []bufconfig.PolicyConfig, // Expected to already be unique by FullName. configuredDepModuleRefs []bufparse.Ref, isV2 bool, @@ -544,6 +551,7 @@ func (w *workspaceProvider) getWorkspaceForBucketModuleSet( opaqueIDToBreakingConfig, pluginConfigs, remotePluginKeys, + policyConfigs, configuredDepModuleRefs, isV2, ), nil diff --git a/private/buf/cmd/buf/command/breaking/breaking.go b/private/buf/cmd/buf/command/breaking/breaking.go index 9319c62b7a..a7ceeaadc9 100644 --- a/private/buf/cmd/buf/command/breaking/breaking.go +++ b/private/buf/cmd/buf/command/breaking/breaking.go @@ -286,6 +286,7 @@ func run( for i, imageWithConfig := range imageWithConfigs { breakingOptions := []bufcheck.BreakingOption{ bufcheck.WithPluginConfigs(imageWithConfig.PluginConfigs()...), + bufcheck.WithPolicyConfigs(imageWithConfig.PolicyConfigs()...), bufcheck.WithRelatedCheckConfigs(allCheckConfigs...), } if flags.ExcludeImports { diff --git a/private/buf/cmd/buf/command/lint/lint.go b/private/buf/cmd/buf/command/lint/lint.go index 9be6b7ba11..809c0c51e7 100644 --- a/private/buf/cmd/buf/command/lint/lint.go +++ b/private/buf/cmd/buf/command/lint/lint.go @@ -150,6 +150,7 @@ func run( for _, imageWithConfig := range imageWithConfigs { lintOptions := []bufcheck.LintOption{ bufcheck.WithPluginConfigs(imageWithConfig.PluginConfigs()...), + bufcheck.WithPolicyConfigs(imageWithConfig.PolicyConfigs()...), bufcheck.WithRelatedCheckConfigs(allCheckConfigs...), } if err := checkClient.Lint( diff --git a/private/bufpkg/bufanalysis/bufanalysis.go b/private/bufpkg/bufanalysis/bufanalysis.go index cfcbfce5f9..9a3aea0ba7 100644 --- a/private/bufpkg/bufanalysis/bufanalysis.go +++ b/private/bufpkg/bufanalysis/bufanalysis.go @@ -149,6 +149,11 @@ type FileAnnotation interface { // May be empty if this annotation did not originate from a plugin. // This may be added to the printed message field for certain printers. PluginName() string + // PolicyName is the name of the policy that the annotation originated from. + // + // May be empty if this annotation did not originate from a policy. + // This may be added to the printed message field for certain printers. + PolicyName() string isFileAnnotation() } @@ -163,6 +168,7 @@ func NewFileAnnotation( typeString string, message string, pluginName string, + policyName string, ) FileAnnotation { return newFileAnnotation( fileInfo, @@ -173,6 +179,7 @@ func NewFileAnnotation( typeString, message, pluginName, + policyName, ) } diff --git a/private/bufpkg/bufanalysis/bufanalysistesting/bufanalysistesting.go b/private/bufpkg/bufanalysis/bufanalysistesting/bufanalysistesting.go index 630b3933a8..7aa07c1971 100644 --- a/private/bufpkg/bufanalysis/bufanalysistesting/bufanalysistesting.go +++ b/private/bufpkg/bufanalysis/bufanalysistesting/bufanalysistesting.go @@ -44,6 +44,7 @@ func NewFileAnnotationNoLocation( t *testing.T, path string, typeString string, + options ...FileAnnotationOption, ) bufanalysis.FileAnnotation { return NewFileAnnotation( t, @@ -53,6 +54,7 @@ func NewFileAnnotationNoLocation( 1, 1, typeString, + options..., ) } @@ -65,6 +67,7 @@ func NewFileAnnotation( endLine int, endColumn int, typeString string, + options ...FileAnnotationOption, ) bufanalysis.FileAnnotation { return newFileAnnotation( t, @@ -74,11 +77,27 @@ func NewFileAnnotation( endLine, endColumn, typeString, - "", - "", + "", // message + options..., ) } +type FileAnnotationOption func(*fileAnnotationOptions) + +// WithPluginName returns a FileAnnotationOption that sets the plugin name. +func WithPluginName(pluginName string) FileAnnotationOption { + return func(options *fileAnnotationOptions) { + options.pluginName = pluginName + } +} + +// WithPolicyName returns a FileAnnotationOption that sets the policy name. +func WithPolicyName(policyName string) FileAnnotationOption { + return func(options *fileAnnotationOptions) { + options.policyName = policyName + } +} + func newFileAnnotation( t *testing.T, path string, @@ -88,8 +107,12 @@ func newFileAnnotation( endColumn int, typeString string, message string, - pluginName string, + options ...FileAnnotationOption, ) bufanalysis.FileAnnotation { + fileAnnotationOptions := &fileAnnotationOptions{} + for _, option := range options { + option(fileAnnotationOptions) + } var fileInfo bufanalysis.FileInfo if path != "" { fileInfo = newFileInfo(path) @@ -102,10 +125,17 @@ func newFileAnnotation( endColumn, typeString, message, - pluginName, + fileAnnotationOptions.pluginName, + fileAnnotationOptions.policyName, ) } +// fileAnnotationOptions holds the options for a FileAnnotation. +type fileAnnotationOptions struct { + pluginName string + policyName string +} + // AssertFileAnnotationsEqual asserts that the annotations are equal minus the message. func AssertFileAnnotationsEqual( t *testing.T, @@ -138,13 +168,15 @@ func AssertFileAnnotationsEqual( ) } } else { - t.Logf(" bufanalysistesting.NewFileAnnotation(t, %q, %d, %d, %d, %d, %q),", + t.Logf(" bufanalysistesting.NewFileAnnotation(t, %q, %d, %d, %d, %d, %q, %q %q),", path, annotation.StartLine(), annotation.StartColumn(), annotation.EndLine(), annotation.EndColumn(), annotation.Type(), + annotation.PluginName(), + annotation.PolicyName(), ) } } @@ -172,7 +204,8 @@ func normalizeFileAnnotations( a.EndColumn(), a.Type(), "", - "", + a.PluginName(), + a.PolicyName(), ) } return normalizedFileAnnotations diff --git a/private/bufpkg/bufanalysis/bufanalysistesting/bufanalysistesting_test.go b/private/bufpkg/bufanalysis/bufanalysistesting/bufanalysistesting_test.go index 6cddd34562..9c0bf36430 100644 --- a/private/bufpkg/bufanalysis/bufanalysistesting/bufanalysistesting_test.go +++ b/private/bufpkg/bufanalysis/bufanalysistesting/bufanalysistesting_test.go @@ -35,7 +35,6 @@ func TestBasic(t *testing.T) { 0, "FOO", "Hello.", - "", ), newFileAnnotation( t, @@ -46,7 +45,7 @@ func TestBasic(t *testing.T) { 1, "FOO", "Hello.", - "buf-plugin-foo", + WithPluginName("buf-plugin-foo"), ), } sb := &strings.Builder{} @@ -110,7 +109,6 @@ path/to/file.proto(2,1) : error FOO : Hello. (buf-plugin-foo) 0, "FOO", "Hello.", - "", ), newFileAnnotation( t, @@ -121,7 +119,7 @@ path/to/file.proto(2,1) : error FOO : Hello. (buf-plugin-foo) 0, "FOO", "Hello.", - "buf-plugin-foo", + WithPluginName("buf-plugin-foo"), ), )..., ), diff --git a/private/bufpkg/bufanalysis/file_annotation.go b/private/bufpkg/bufanalysis/file_annotation.go index d4ddd9616b..f85f8189ce 100644 --- a/private/bufpkg/bufanalysis/file_annotation.go +++ b/private/bufpkg/bufanalysis/file_annotation.go @@ -28,6 +28,7 @@ type fileAnnotation struct { typeString string message string pluginName string + policyName string } func newFileAnnotation( @@ -39,6 +40,7 @@ func newFileAnnotation( typeString string, message string, pluginName string, + policyName string, ) *fileAnnotation { return &fileAnnotation{ fileInfo: fileInfo, @@ -49,6 +51,7 @@ func newFileAnnotation( typeString: typeString, message: message, pluginName: pluginName, + policyName: policyName, } } @@ -84,6 +87,10 @@ func (f *fileAnnotation) PluginName() string { return f.pluginName } +func (f *fileAnnotation) PolicyName() string { + return f.policyName +} + func (f *fileAnnotation) String() string { if f == nil { return "" @@ -110,9 +117,17 @@ func (f *fileAnnotation) String() string { _, _ = buffer.WriteString(strconv.Itoa(column)) _, _ = buffer.WriteRune(':') _, _ = buffer.WriteString(message) - if f.pluginName != "" { + if f.pluginName != "" || f.policyName != "" { _, _ = buffer.WriteString(" (") - _, _ = buffer.WriteString(f.pluginName) + if f.pluginName != "" { + _, _ = buffer.WriteString(f.pluginName) + } + if f.pluginName != "" && f.policyName != "" { + _, _ = buffer.WriteString(", ") + } + if f.policyName != "" { + _, _ = buffer.WriteString(f.policyName) + } _, _ = buffer.WriteRune(')') } return buffer.String() diff --git a/private/bufpkg/bufanalysis/file_annotation_set.go b/private/bufpkg/bufanalysis/file_annotation_set.go index 6cee03b3a3..f04e746d59 100644 --- a/private/bufpkg/bufanalysis/file_annotation_set.go +++ b/private/bufpkg/bufanalysis/file_annotation_set.go @@ -178,5 +178,7 @@ func hash(fileAnnotation FileAnnotation) string { _, _ = hash.Write([]byte(strconv.Itoa(fileAnnotation.EndColumn()))) _, _ = hash.Write([]byte(fileAnnotation.Type())) _, _ = hash.Write([]byte(fileAnnotation.Message())) + _, _ = hash.Write([]byte(fileAnnotation.PluginName())) + _, _ = hash.Write([]byte(fileAnnotation.PolicyName())) return string(hash.Sum(nil)) } diff --git a/private/bufpkg/bufanalysis/print.go b/private/bufpkg/bufanalysis/print.go index 74cbc19645..a8c1a5e280 100644 --- a/private/bufpkg/bufanalysis/print.go +++ b/private/bufpkg/bufanalysis/print.go @@ -194,9 +194,17 @@ func printFileAnnotationAsMSVS(buffer *bytes.Buffer, f FileAnnotation) error { _, _ = buffer.WriteString(typeString) _, _ = buffer.WriteString(" : ") _, _ = buffer.WriteString(message) - if pluginName := f.PluginName(); pluginName != "" { + if pluginName, policyName := f.PluginName(), f.PolicyName(); pluginName != "" || policyName != "" { _, _ = buffer.WriteString(" (") - _, _ = buffer.WriteString(pluginName) + if pluginName != "" { + _, _ = buffer.WriteString(pluginName) + } + if pluginName != "" && policyName != "" { + _, _ = buffer.WriteString(", ") + } + if policyName != "" { + _, _ = buffer.WriteString(policyName) + } _, _ = buffer.WriteRune(')') } return nil @@ -251,9 +259,17 @@ func printFileAnnotationAsGithubActions(buffer *bytes.Buffer, f FileAnnotation) _, _ = buffer.WriteString("::") _, _ = buffer.WriteString(f.Message()) - if pluginName := f.PluginName(); pluginName != "" { + if pluginName, policyName := f.PluginName(), f.PolicyName(); pluginName != "" || policyName != "" { _, _ = buffer.WriteString(" (") - _, _ = buffer.WriteString(pluginName) + if pluginName != "" { + _, _ = buffer.WriteString(pluginName) + } + if pluginName != "" && policyName != "" { + _, _ = buffer.WriteString(", ") + } + if policyName != "" { + _, _ = buffer.WriteString(policyName) + } _, _ = buffer.WriteRune(')') } return nil @@ -268,6 +284,7 @@ type externalFileAnnotation struct { Type string `json:"type,omitempty" yaml:"type,omitempty"` Message string `json:"message,omitempty" yaml:"message,omitempty"` Plugin string `json:"plugin,omitempty" yaml:"plugin,omitempty"` + Policy string `json:"policy,omitempty" yaml:"policy,omitempty"` } func newExternalFileAnnotation(f FileAnnotation) externalFileAnnotation { @@ -284,6 +301,7 @@ func newExternalFileAnnotation(f FileAnnotation) externalFileAnnotation { Type: f.Type(), Message: f.Message(), Plugin: f.PluginName(), + Policy: f.PolicyName(), } } diff --git a/private/bufpkg/bufcheck/annotation.go b/private/bufpkg/bufcheck/annotation.go index b35b27843e..bb1daf2879 100644 --- a/private/bufpkg/bufcheck/annotation.go +++ b/private/bufpkg/bufcheck/annotation.go @@ -24,12 +24,14 @@ type annotation struct { check.Annotation pluginName string + policyName string } -func newAnnotation(checkAnnotation check.Annotation, pluginName string) *annotation { +func newAnnotation(checkAnnotation check.Annotation, pluginName string, policyName string) *annotation { return &annotation{ Annotation: checkAnnotation, pluginName: pluginName, + policyName: policyName, } } @@ -37,6 +39,10 @@ func (a *annotation) PluginName() string { return a.pluginName } +func (a *annotation) PolicyName() string { + return a.policyName +} + func annotationsToFileAnnotations( pathToExternalPath map[string]string, annotations []*annotation, @@ -65,6 +71,7 @@ func annotationToFileAnnotation( annotation.RuleID(), annotation.Message(), annotation.PluginName(), + annotation.PolicyName(), ) } path := fileLocation.FileDescriptor().ProtoreflectFileDescriptor().Path() @@ -84,5 +91,6 @@ func annotationToFileAnnotation( annotation.RuleID(), annotation.Message(), annotation.PluginName(), + annotation.PolicyName(), ) } diff --git a/private/bufpkg/bufcheck/breaking_test.go b/private/bufpkg/bufcheck/breaking_test.go index 1db9020661..6ef987e7cd 100644 --- a/private/bufpkg/bufcheck/breaking_test.go +++ b/private/bufpkg/bufcheck/breaking_test.go @@ -1257,9 +1257,45 @@ func TestRunBreakingWithCustomPlugins(t *testing.T) { testBreaking( t, "breaking_custom_plugins", - bufanalysistesting.NewFileAnnotation(t, "a.proto", 3, 1, 6, 2, "SERVICE_SUFFIXES_NO_CHANGE"), - bufanalysistesting.NewFileAnnotation(t, "b.proto", 11, 3, 14, 4, "ENUM_SUFFIXES_NO_CHANGE"), - bufanalysistesting.NewFileAnnotation(t, "b.proto", 15, 3, 19, 4, "MESSAGE_SUFFIXES_NO_CHANGE"), + bufanalysistesting.NewFileAnnotation( + t, "a.proto", 3, 1, 6, 2, "SERVICE_SUFFIXES_NO_CHANGE", + bufanalysistesting.WithPluginName("buf-plugin-suffix"), + ), + bufanalysistesting.NewFileAnnotation( + t, "b.proto", 11, 3, 14, 4, "ENUM_SUFFIXES_NO_CHANGE", + bufanalysistesting.WithPluginName("buf-plugin-suffix"), + ), + bufanalysistesting.NewFileAnnotation( + t, "b.proto", 15, 3, 19, 4, "MESSAGE_SUFFIXES_NO_CHANGE", + bufanalysistesting.WithPluginName("buf-plugin-suffix"), + ), + ) +} + +func TestRunBreakingPolicyLocal(t *testing.T) { + t.Parallel() + testBreaking( + t, + "breaking_policy_empty", + ) + testBreaking( + t, + "breaking_policy_local", + bufanalysistesting.NewFileAnnotation( + t, "a.proto", 3, 1, 6, 2, "SERVICE_SUFFIXES_NO_CHANGE", + bufanalysistesting.WithPluginName("buf-plugin-suffix.wasm"), + bufanalysistesting.WithPolicyName("buf.policy1.yaml"), + ), + bufanalysistesting.NewFileAnnotation( + t, "b.proto", 11, 3, 14, 4, "ENUM_SUFFIXES_NO_CHANGE", + bufanalysistesting.WithPluginName("buf-plugin-suffix.wasm"), + bufanalysistesting.WithPolicyName("buf.policy2.yaml"), + ), + bufanalysistesting.NewFileAnnotation( + t, "b.proto", 15, 3, 19, 4, "MESSAGE_SUFFIXES_NO_CHANGE", + bufanalysistesting.WithPluginName("buf-plugin-suffix.wasm"), + bufanalysistesting.WithPolicyName("buf.policy2.yaml"), + ), ) } @@ -1268,7 +1304,7 @@ func testBreaking( relDirPath string, expectedFileAnnotations ...bufanalysis.FileAnnotation, ) { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) // Increased timeout for Wasm runtime defer cancel() logger := slogtestext.NewLogger(t) @@ -1347,9 +1383,16 @@ func testBreaking( require.NoError(t, err) breakingConfig := workspace.GetBreakingConfigForOpaqueID(opaqueID) require.NotNil(t, breakingConfig) + wasmRuntime, err := wasm.NewRuntime(ctx) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, wasmRuntime.Close(ctx)) + }) client, err := bufcheck.NewClient( logger, - bufcheck.ClientWithRunnerProvider(bufcheck.NewLocalRunnerProvider(wasm.UnimplementedRuntime)), + bufcheck.ClientWithRunnerProvider(bufcheck.NewLocalRunnerProvider(wasmRuntime)), + bufcheck.ClientWithLocalWasmPluginsFromOS(), + bufcheck.ClientWithLocalPolicies(readWriteBucket), ) require.NoError(t, err) err = client.Breaking( @@ -1359,6 +1402,7 @@ func testBreaking( previousImage, bufcheck.BreakingWithExcludeImports(), bufcheck.WithPluginConfigs(workspace.PluginConfigs()...), + bufcheck.WithPolicyConfigs(workspace.PolicyConfigs()...), ) if len(expectedFileAnnotations) == 0 { assert.NoError(t, err) diff --git a/private/bufpkg/bufcheck/bufcheck.go b/private/bufpkg/bufcheck/bufcheck.go index a0f4f3f588..9164a593e9 100644 --- a/private/bufpkg/bufcheck/bufcheck.go +++ b/private/bufpkg/bufcheck/bufcheck.go @@ -25,6 +25,7 @@ import ( "github.com/bufbuild/buf/private/bufpkg/bufplugin" "github.com/bufbuild/buf/private/pkg/pluginrpcutil" "github.com/bufbuild/buf/private/pkg/slicesext" + "github.com/bufbuild/buf/private/pkg/storage" "github.com/bufbuild/buf/private/pkg/syserror" "github.com/bufbuild/buf/private/pkg/wasm" "pluginrpc.com/pluginrpc" @@ -175,13 +176,20 @@ func WithPluginConfigs(pluginConfigs ...bufconfig.PluginConfig) ClientFunctionOp } } -// RunnerProvider provides pluginrpc.Runners for a given plugin config. +// WithPolicyConfigs returns a new ClientFunctionOption that says to also use the given policies. +func WithPolicyConfigs(policyConfigs ...bufconfig.PolicyConfig) ClientFunctionOption { + return &policyConfigsOption{ + policyConfigs: policyConfigs, + } +} + +// RunnerProvider provides pluginrpc.Runners for a given plugin config and an optional policy config. type RunnerProvider interface { NewRunner(plugin bufplugin.Plugin) (pluginrpc.Runner, error) } // RunnerProviderFunc is a function that implements RunnerProvider. -type RunnerProviderFunc func(plugin bufplugin.Plugin) (pluginrpc.Runner, error) +type RunnerProviderFunc func(bufplugin.Plugin) (pluginrpc.Runner, error) // NewRunner implements RunnerProvider. // @@ -267,6 +275,14 @@ func ClientWithRemoteWasmPlugins( } } +// ClientWithLocalPolicies returns a new ClientOption that specifies the read bucket +// for local policies. +func ClientWithLocalPolicies(readBucket storage.ReadBucket) ClientOption { + return func(clientOptions *clientOptions) { + clientOptions.policyReadBucket = readBucket + } +} + // PrintRules prints the rules to the Writer. func PrintRules(writer io.Writer, rules []Rule, options ...PrintRulesOption) (retErr error) { return printRules(writer, rules, options...) diff --git a/private/bufpkg/bufcheck/check_client_spec.go b/private/bufpkg/bufcheck/check_client_spec.go index 465779d5c0..e788d08770 100644 --- a/private/bufpkg/bufcheck/check_client_spec.go +++ b/private/bufpkg/bufcheck/check_client_spec.go @@ -25,13 +25,15 @@ import ( // This allows us to take a bufconfig.PluginConfig and turn it into a client/options pair. type checkClientSpec struct { PluginName string + PolicyName string Client check.Client Options option.Options } -func newCheckClientSpec(pluginName string, client check.Client, options option.Options) *checkClientSpec { +func newCheckClientSpec(pluginName string, policyName string, client check.Client, options option.Options) *checkClientSpec { return &checkClientSpec{ PluginName: pluginName, + PolicyName: policyName, Client: client, Options: options, } diff --git a/private/bufpkg/bufcheck/client.go b/private/bufpkg/bufcheck/client.go index 4ad79e78be..6db4d9ab58 100644 --- a/private/bufpkg/bufcheck/client.go +++ b/private/bufpkg/bufcheck/client.go @@ -15,6 +15,7 @@ package bufcheck import ( + "bytes" "context" "fmt" "io" @@ -30,11 +31,13 @@ import ( "github.com/bufbuild/buf/private/bufpkg/bufimage" "github.com/bufbuild/buf/private/bufpkg/bufparse" "github.com/bufbuild/buf/private/bufpkg/bufplugin" + "github.com/bufbuild/buf/private/bufpkg/bufpolicy/bufpolicyconfig" "github.com/bufbuild/buf/private/pkg/normalpath" "github.com/bufbuild/buf/private/pkg/protosourcepath" "github.com/bufbuild/buf/private/pkg/protoversion" "github.com/bufbuild/buf/private/pkg/slicesext" "github.com/bufbuild/buf/private/pkg/slogext" + "github.com/bufbuild/buf/private/pkg/storage" "github.com/bufbuild/buf/private/pkg/stringutil" "github.com/bufbuild/buf/private/pkg/syserror" "pluginrpc.com/pluginrpc" @@ -48,6 +51,7 @@ type client struct { pluginReadFile func(string) ([]byte, error) pluginKeyProvider bufplugin.PluginKeyProvider pluginDataProvider bufplugin.PluginDataProvider + policyReadBucket storage.ReadBucket } func newClient( @@ -84,6 +88,7 @@ func newClient( pluginReadFile: clientOptions.pluginReadFile, pluginKeyProvider: clientOptions.pluginKeyProvider, pluginDataProvider: clientOptions.pluginDataProvider, + policyReadBucket: clientOptions.policyReadBucket, }, nil } @@ -95,31 +100,90 @@ func (c *client) Lint( ) error { defer slogext.DebugProfile(c.logger)() - if lintConfig.Disabled() { - return nil - } lintOptions := newLintOptions() for _, option := range options { option.applyToLint(lintOptions) } - allRules, allCategories, err := c.allRulesAndCategories( + // Run lint checks. + var annotations []*annotation + lintAnnotations, err := c.lint( ctx, - lintConfig.FileVersion(), + image, + lintConfig, lintOptions.pluginConfigs, - lintConfig.DisableBuiltin(), + nil, // policyConfig. + lintOptions.relatedCheckConfigs, ) if err != nil { return err } - config, err := configForLintConfig(lintConfig, allRules, allCategories, lintOptions.relatedCheckConfigs) + annotations = append(annotations, lintAnnotations...) + // Run lint policy checks. + policyFiles, err := c.getPolicyFiles(ctx, lintOptions.policyConfigs) if err != nil { return err } + for index, policyFile := range policyFiles { + policyConfig := lintOptions.policyConfigs[index] + policyLintConfig, err := newPolicyLintConfig(policyConfig, policyFile) + if err != nil { + return err + } + policyAnnotations, err := c.lint( + ctx, + image, + policyLintConfig, + policyFile.PluginConfigs(), + policyConfig, + nil, // relatedCheckConfigs. + ) + if err != nil { + return err + } + annotations = append(annotations, policyAnnotations...) + } + if len(annotations) == 0 { + return nil + } + return bufanalysis.NewFileAnnotationSet( + annotationsToFileAnnotations( + imageToPathToExternalPath( + image, + ), + annotations, + )..., + ) +} + +func (c *client) lint( + ctx context.Context, + image bufimage.Image, + lintConfig bufconfig.LintConfig, + pluginConfigs []bufconfig.PluginConfig, + policyConfig bufconfig.PolicyConfig, + relatedCheckConfigs []bufconfig.CheckConfig, +) ([]*annotation, error) { + if lintConfig.Disabled() { + return nil, nil + } + allRules, allCategories, err := c.allRulesAndCategories( + ctx, + lintConfig.FileVersion(), + pluginConfigs, + lintConfig.DisableBuiltin(), + ) + if err != nil { + return nil, err + } + config, err := configForLintConfig(lintConfig, allRules, allCategories, relatedCheckConfigs) + if err != nil { + return nil, err + } logRulesConfig(c.logger, config.rulesConfig) files, err := descriptor.FileDescriptorsForProtoFileDescriptors(imageToProtoFileDescriptors(image)) if err != nil { // If a validated Image results in an error, this is a system error. - return syserror.Wrap(err) + return nil, syserror.Wrap(err) } request, err := check.NewRequest( files, @@ -127,23 +191,27 @@ func (c *client) Lint( check.WithOptions(config.DefaultOptions), ) if err != nil { - return err + return nil, err } multiClient, err := c.getMultiClient( ctx, lintConfig.FileVersion(), - lintOptions.pluginConfigs, + pluginConfigs, + policyConfig, lintConfig.DisableBuiltin(), config.DefaultOptions, ) if err != nil { - return err + return nil, err } annotations, err := multiClient.Check(ctx, request) if err != nil { - return err + return nil, err + } + if len(annotations) == 0 { + return nil, nil } - return annotationsToFilteredFileAnnotationSetOrError(config, image, annotations) + return filterAnnotations(config, annotations) } func (c *client) Breaking( @@ -155,42 +223,108 @@ func (c *client) Breaking( ) error { defer slogext.DebugProfile(c.logger)() - if breakingConfig.Disabled() { - return nil - } breakingOptions := newBreakingOptions() for _, option := range options { option.applyToBreaking(breakingOptions) } + // Run breaking checks. + var annotations []*annotation + breakingAnnotations, err := c.breaking( + ctx, + image, + againstImage, + breakingConfig, + breakingOptions.pluginConfigs, + nil, // policyConfig. + breakingOptions.excludeImports, + breakingOptions.relatedCheckConfigs, + ) + if err != nil { + return err + } + annotations = append(annotations, breakingAnnotations...) + // Run breaking policy checks. + policyFiles, err := c.getPolicyFiles(ctx, breakingOptions.policyConfigs) + if err != nil { + return err + } + for index, policyFile := range policyFiles { + policyConfig := breakingOptions.policyConfigs[index] + policyBreakingConfig, err := newPolicyBreakingConfig(policyConfig, policyFile) + if err != nil { + return err + } + policyAnnotations, err := c.breaking( + ctx, + image, + againstImage, + policyBreakingConfig, + policyFile.PluginConfigs(), + policyConfig, + breakingOptions.excludeImports, + nil, // relatedCheckConfigs. + ) + if err != nil { + return err + } + annotations = append(annotations, policyAnnotations...) + } + + if len(annotations) == 0 { + return nil + } + return bufanalysis.NewFileAnnotationSet( + annotationsToFileAnnotations( + imageToPathToExternalPath( + image, + ), + annotations, + )..., + ) +} + +func (c *client) breaking( + ctx context.Context, + image bufimage.Image, + againstImage bufimage.Image, + breakingConfig bufconfig.BreakingConfig, + pluginConfigs []bufconfig.PluginConfig, + policyConfig bufconfig.PolicyConfig, + excludeImports bool, + relatedCheckConfigs []bufconfig.CheckConfig, +) ([]*annotation, error) { + if breakingConfig.Disabled() { + return nil, nil + } allRules, allCategories, err := c.allRulesAndCategories( ctx, breakingConfig.FileVersion(), - breakingOptions.pluginConfigs, + pluginConfigs, breakingConfig.DisableBuiltin(), ) if err != nil { - return err + return nil, err } config, err := configForBreakingConfig( breakingConfig, allRules, allCategories, - breakingOptions.excludeImports, - breakingOptions.relatedCheckConfigs, + excludeImports, + relatedCheckConfigs, ) if err != nil { - return err + return nil, err } logRulesConfig(c.logger, config.rulesConfig) fileDescriptors, err := descriptor.FileDescriptorsForProtoFileDescriptors(imageToProtoFileDescriptors(image)) if err != nil { // If a validated Image results in an error, this is a system error. - return syserror.Wrap(err) + return nil, syserror.Wrap(err) } againstFileDescriptors, err := descriptor.FileDescriptorsForProtoFileDescriptors(imageToProtoFileDescriptors(againstImage)) if err != nil { // If a validated Image results in an error, this is a system error. - return syserror.Wrap(err) + return nil, syserror.Wrap(err) } request, err := check.NewRequest( fileDescriptors, @@ -199,23 +333,27 @@ func (c *client) Breaking( check.WithOptions(config.DefaultOptions), ) if err != nil { - return err + return nil, err } multiClient, err := c.getMultiClient( ctx, breakingConfig.FileVersion(), - breakingOptions.pluginConfigs, + pluginConfigs, + policyConfig, breakingConfig.DisableBuiltin(), config.DefaultOptions, ) if err != nil { - return err + return nil, err } annotations, err := multiClient.Check(ctx, request) if err != nil { - return err + return nil, err + } + if len(annotations) == 0 { + return nil, nil } - return annotationsToFilteredFileAnnotationSetOrError(config, image, annotations) + return filterAnnotations(config, annotations) } func (c *client) ConfiguredRules( @@ -290,7 +428,7 @@ func (c *client) allRulesAndCategories( // Just passing through to fulfill all contracts, ie checkClientSpec has non-nil Options. // Options are not used here. // config struct really just needs refactoring. - multiClient, err := c.getMultiClient(ctx, fileVersion, pluginConfigs, disableBuiltin, option.EmptyOptions) + multiClient, err := c.getMultiClient(ctx, fileVersion, pluginConfigs, nil, disableBuiltin, option.EmptyOptions) if err != nil { return nil, nil, err } @@ -301,9 +439,14 @@ func (c *client) getMultiClient( ctx context.Context, fileVersion bufconfig.FileVersion, pluginConfigs []bufconfig.PluginConfig, + policyConfig bufconfig.PolicyConfig, disableBuiltin bool, defaultOptions option.Options, ) (*multiClient, error) { + var policyConfigName string + if policyConfig != nil { + policyConfigName = policyConfig.Name() + } var checkClientSpecs []*checkClientSpec if !disableBuiltin { defaultCheckClient, ok := c.fileVersionToDefaultCheckClient[fileVersion] @@ -313,10 +456,10 @@ func (c *client) getMultiClient( checkClientSpecs = append( checkClientSpecs, // We do not set PluginName for default check.Clients. - newCheckClientSpec("", defaultCheckClient, defaultOptions), + newCheckClientSpec("", policyConfigName, defaultCheckClient, defaultOptions), ) } - plugins, err := c.getPlugins(ctx, pluginConfigs) + plugins, err := c.getPlugins(ctx, pluginConfigs, policyConfig) if err != nil { return nil, err } @@ -347,13 +490,13 @@ func (c *client) getMultiClient( ) checkClientSpecs = append( checkClientSpecs, - newCheckClientSpec(pluginConfig.Name(), checkClient, options), + newCheckClientSpec(pluginConfig.Name(), policyConfigName, checkClient, options), ) } return newMultiClient(c.logger, checkClientSpecs), nil } -func (c *client) getPlugins(ctx context.Context, pluginConfigs []bufconfig.PluginConfig) ([]bufplugin.Plugin, error) { +func (c *client) getPlugins(ctx context.Context, pluginConfigs []bufconfig.PluginConfig, policyConfig bufconfig.PolicyConfig) ([]bufplugin.Plugin, error) { plugins := make([]bufplugin.Plugin, len(pluginConfigs)) var indexedPluginRefs []slicesext.Indexed[bufparse.Ref] @@ -404,6 +547,10 @@ func (c *client) getPlugins(ctx context.Context, pluginConfigs []bufconfig.Plugi } // Load the remote plugin data for each plugin ref. if len(indexedPluginRefs) > 0 { + // TODO: remove plugins are not yet supported with policies. + if policyConfig != nil { + return nil, fmt.Errorf("remote plugins are not supported with policies") + } pluginRefs := slicesext.IndexedToValues(indexedPluginRefs) pluginKeys, err := c.pluginKeyProvider.GetPluginKeysForPluginRefs(ctx, pluginRefs, bufplugin.DigestTypeP1) if err != nil { @@ -436,32 +583,51 @@ func (c *client) getPlugins(ctx context.Context, pluginConfigs []bufconfig.Plugi return plugins, nil } -func annotationsToFilteredFileAnnotationSetOrError( - config *config, - image bufimage.Image, - annotations []*annotation, -) error { - if len(annotations) == 0 { - return nil +func (c *client) getPolicyFiles( + ctx context.Context, + policyConfigs []bufconfig.PolicyConfig, +) ([]bufpolicyconfig.BufPolicyYAMLFile, error) { + if len(policyConfigs) == 0 { + return nil, nil + } + policyBytes := make([][]byte, len(policyConfigs)) + + var indexedPolicyRefs []slicesext.Indexed[bufparse.Ref] + for index, policyConfig := range policyConfigs { + if ref := policyConfig.Ref(); ref != nil { + indexedPolicyRefs = append(indexedPolicyRefs, slicesext.Indexed[bufparse.Ref]{ + Value: ref, + Index: index, + }) + continue + } + // Local policy config. + if c.policyReadBucket == nil { + return nil, fmt.Errorf("local policy config %q is not supported by this client", policyConfig.Name()) + } + policyData, err := storage.ReadPath(ctx, c.policyReadBucket, policyConfig.Name()) + if err != nil { + return nil, fmt.Errorf("could not read local policy config %q: %w", policyConfig.Name(), err) + } + policyBytes[index] = policyData } - annotations, err := filterAnnotations(config, annotations) - if err != nil { - return err + + // Load the remote policy data for each policy ref. + if len(indexedPolicyRefs) > 0 { + // TODO: Add support for remote policy configs. + return nil, fmt.Errorf("remote policy configs are not supported") } - if len(annotations) == 0 { - return nil + + policyFiles := make([]bufpolicyconfig.BufPolicyYAMLFile, len(policyConfigs)) + for index, policyConfig := range policyConfigs { + reader := bytes.NewReader(policyBytes[index]) + policyFile, err := bufpolicyconfig.ReadBufPolicyYAMLFile(reader, policyConfig.Name()) + if err != nil { + return nil, fmt.Errorf("could not read policy file %q: %w", policyConfig.Name(), err) + } + policyFiles[index] = policyFile } - // Note that NewFileAnnotationSet does its own sorting and deduplication. - // The bufplugin SDK does this as well, but we don't need to worry about the sort - // order being different. - return bufanalysis.NewFileAnnotationSet( - annotationsToFileAnnotations( - imageToPathToExternalPath( - image, - ), - annotations, - )..., - ) + return policyFiles, nil } func filterAnnotations( @@ -578,6 +744,7 @@ func checkCommentLineForCheckIgnore( type lintOptions struct { pluginConfigs []bufconfig.PluginConfig + policyConfigs []bufconfig.PolicyConfig relatedCheckConfigs []bufconfig.CheckConfig } @@ -587,6 +754,7 @@ func newLintOptions() *lintOptions { type breakingOptions struct { pluginConfigs []bufconfig.PluginConfig + policyConfigs []bufconfig.PolicyConfig excludeImports bool relatedCheckConfigs []bufconfig.CheckConfig } @@ -597,6 +765,7 @@ func newBreakingOptions() *breakingOptions { type configuredRulesOptions struct { pluginConfigs []bufconfig.PluginConfig + policyConfigs []bufconfig.PolicyConfig relatedCheckConfigs []bufconfig.CheckConfig } @@ -606,6 +775,7 @@ func newConfiguredRulesOptions() *configuredRulesOptions { type allRulesOptions struct { pluginConfigs []bufconfig.PluginConfig + policyConfigs []bufconfig.PolicyConfig } func newAllRulesOptions() *allRulesOptions { @@ -614,6 +784,7 @@ func newAllRulesOptions() *allRulesOptions { type allCategoriesOptions struct { pluginConfigs []bufconfig.PluginConfig + policyConfigs []bufconfig.PolicyConfig } func newAllCategoriesOptions() *allCategoriesOptions { @@ -623,15 +794,17 @@ func newAllCategoriesOptions() *allCategoriesOptions { type clientOptions struct { stderr io.Writer runnerProvider RunnerProvider - pluginReadFile func(name string) ([]byte, error) + pluginReadFile func(string) ([]byte, error) pluginKeyProvider bufplugin.PluginKeyProvider pluginDataProvider bufplugin.PluginDataProvider + policyReadBucket storage.ReadBucket } func newClientOptions() *clientOptions { return &clientOptions{ pluginKeyProvider: bufplugin.NopPluginKeyProvider, pluginDataProvider: bufplugin.NopPluginDataProvider, + policyReadBucket: nil, } } @@ -665,6 +838,30 @@ func (p *pluginConfigsOption) applyToAllCategories(allCategoriesOptions *allCate allCategoriesOptions.pluginConfigs = append(allCategoriesOptions.pluginConfigs, p.pluginConfigs...) } +type policyConfigsOption struct { + policyConfigs []bufconfig.PolicyConfig +} + +func (p *policyConfigsOption) applyToLint(lintOptions *lintOptions) { + lintOptions.policyConfigs = append(lintOptions.policyConfigs, p.policyConfigs...) +} + +func (p *policyConfigsOption) applyToBreaking(breakingOptions *breakingOptions) { + breakingOptions.policyConfigs = append(breakingOptions.policyConfigs, p.policyConfigs...) +} + +func (p *policyConfigsOption) applyToConfiguredRules(configuredRulesOptions *configuredRulesOptions) { + configuredRulesOptions.policyConfigs = append(configuredRulesOptions.policyConfigs, p.policyConfigs...) +} + +func (p *policyConfigsOption) applyToAllRules(allRulesOptions *allRulesOptions) { + allRulesOptions.policyConfigs = append(allRulesOptions.policyConfigs, p.policyConfigs...) +} + +func (p *policyConfigsOption) applyToAllCategories(allCategoriesOptions *allCategoriesOptions) { + allCategoriesOptions.policyConfigs = append(allCategoriesOptions.policyConfigs, p.policyConfigs...) +} + type relatedCheckConfigsOption struct { relatedCheckConfigs []bufconfig.CheckConfig } diff --git a/private/bufpkg/bufcheck/lint_test.go b/private/bufpkg/bufcheck/lint_test.go index ca6557af5d..08879f05c3 100644 --- a/private/bufpkg/bufcheck/lint_test.go +++ b/private/bufpkg/bufcheck/lint_test.go @@ -1229,15 +1229,42 @@ func TestRunLintCustomPlugins(t *testing.T) { t, "custom_plugins", bufanalysistesting.NewFileAnnotationNoLocation(t, "a.proto", "PACKAGE_DEFINED"), - bufanalysistesting.NewFileAnnotation(t, "a.proto", 8, 1, 10, 2, "SERVICE_BANNED_SUFFIXES"), - bufanalysistesting.NewFileAnnotation(t, "a.proto", 15, 1, 17, 2, "PAGE_REQUEST_HAS_TOKEN"), - bufanalysistesting.NewFileAnnotation(t, "a.proto", 19, 1, 25, 2, "PAGE_RESPONSE_HAS_TOKEN"), - bufanalysistesting.NewFileAnnotation(t, "a.proto", 21, 5, 21, 19, "VALIDATE_ID_DASHLESS"), - bufanalysistesting.NewFileAnnotation(t, "a.proto", 27, 1, 27, 26, "PAGE_REQUEST_HAS_TOKEN"), - bufanalysistesting.NewFileAnnotation(t, "a.proto", 28, 1, 28, 27, "PAGE_RESPONSE_HAS_TOKEN"), - bufanalysistesting.NewFileAnnotation(t, "b.proto", 6, 3, 6, 66, "RPC_BANNED_SUFFIXES"), - bufanalysistesting.NewFileAnnotation(t, "b.proto", 14, 5, 14, 24, "ENUM_VALUE_BANNED_SUFFIXES"), - bufanalysistesting.NewFileAnnotation(t, "b.proto", 19, 5, 19, 23, "FIELD_BANNED_SUFFIXES"), + bufanalysistesting.NewFileAnnotation( + t, "a.proto", 8, 1, 10, 2, "SERVICE_BANNED_SUFFIXES", + bufanalysistesting.WithPluginName("buf-plugin-suffix"), + ), + bufanalysistesting.NewFileAnnotation( + t, "a.proto", 15, 1, 17, 2, "PAGE_REQUEST_HAS_TOKEN", + bufanalysistesting.WithPluginName("buf-plugin-rpc-ext"), + ), + bufanalysistesting.NewFileAnnotation( + t, "a.proto", 19, 1, 25, 2, "PAGE_RESPONSE_HAS_TOKEN", + bufanalysistesting.WithPluginName("buf-plugin-rpc-ext"), + ), + bufanalysistesting.NewFileAnnotation( + t, "a.proto", 21, 5, 21, 19, "VALIDATE_ID_DASHLESS", + bufanalysistesting.WithPluginName("buf-plugin-protovalidate-ext"), + ), + bufanalysistesting.NewFileAnnotation( + t, "a.proto", 27, 1, 27, 26, "PAGE_REQUEST_HAS_TOKEN", + bufanalysistesting.WithPluginName("buf-plugin-rpc-ext"), + ), + bufanalysistesting.NewFileAnnotation( + t, "a.proto", 28, 1, 28, 27, "PAGE_RESPONSE_HAS_TOKEN", + bufanalysistesting.WithPluginName("buf-plugin-rpc-ext"), + ), + bufanalysistesting.NewFileAnnotation( + t, "b.proto", 6, 3, 6, 66, "RPC_BANNED_SUFFIXES", + bufanalysistesting.WithPluginName("buf-plugin-suffix"), + ), + bufanalysistesting.NewFileAnnotation( + t, "b.proto", 14, 5, 14, 24, "ENUM_VALUE_BANNED_SUFFIXES", + bufanalysistesting.WithPluginName("buf-plugin-suffix"), + ), + bufanalysistesting.NewFileAnnotation( + t, "b.proto", 19, 5, 19, 23, "FIELD_BANNED_SUFFIXES", + bufanalysistesting.WithPluginName("buf-plugin-suffix"), + ), ) } @@ -1252,10 +1279,22 @@ func TestRunLintCustomWasmPlugins(t *testing.T) { "", nil, bufanalysistesting.NewFileAnnotationNoLocation(t, "a.proto", "PACKAGE_DEFINED"), - bufanalysistesting.NewFileAnnotation(t, "a.proto", 8, 1, 10, 2, "SERVICE_BANNED_SUFFIXES"), - bufanalysistesting.NewFileAnnotation(t, "b.proto", 6, 3, 6, 66, "RPC_BANNED_SUFFIXES"), - bufanalysistesting.NewFileAnnotation(t, "b.proto", 14, 5, 14, 24, "ENUM_VALUE_BANNED_SUFFIXES"), - bufanalysistesting.NewFileAnnotation(t, "b.proto", 19, 5, 19, 23, "FIELD_BANNED_SUFFIXES"), + bufanalysistesting.NewFileAnnotation( + t, "a.proto", 8, 1, 10, 2, "SERVICE_BANNED_SUFFIXES", + bufanalysistesting.WithPluginName("buf-plugin-suffix.wasm"), + ), + bufanalysistesting.NewFileAnnotation( + t, "b.proto", 6, 3, 6, 66, "RPC_BANNED_SUFFIXES", + bufanalysistesting.WithPluginName("buf-plugin-suffix.wasm"), + ), + bufanalysistesting.NewFileAnnotation( + t, "b.proto", 14, 5, 14, 24, "ENUM_VALUE_BANNED_SUFFIXES", + bufanalysistesting.WithPluginName("buf-plugin-suffix.wasm"), + ), + bufanalysistesting.NewFileAnnotation( + t, "b.proto", 19, 5, 19, 23, "FIELD_BANNED_SUFFIXES", + bufanalysistesting.WithPluginName("buf-plugin-suffix.wasm"), + ), ) } @@ -1268,6 +1307,129 @@ func TestRunLintEditionsGoFeatures(t *testing.T) { ) } +func TestRunLintPolicyEmpty(t *testing.T) { + t.Parallel() + testLintWithOptions( + t, + "policy_empty", + "", + nil, + ) +} + +func TestRunLintPolicyLocal(t *testing.T) { + t.Parallel() + testLintWithOptions( + t, + "policy_local", + "", + nil, + bufanalysistesting.NewFileAnnotationNoLocation(t, "a.proto", "PACKAGE_DEFINED"), + bufanalysistesting.NewFileAnnotation( + t, "a.proto", 8, 1, 10, 2, "SERVICE_BANNED_SUFFIXES", + bufanalysistesting.WithPluginName("buf-plugin-suffix.wasm"), + bufanalysistesting.WithPolicyName("buf.policy1.yaml"), + ), + bufanalysistesting.NewFileAnnotation( + t, "b.proto", 6, 3, 6, 66, "RPC_BANNED_SUFFIXES", + bufanalysistesting.WithPluginName("buf-plugin-suffix.wasm"), + bufanalysistesting.WithPolicyName("buf.policy1.yaml"), + ), + bufanalysistesting.NewFileAnnotation( + t, "b.proto", 14, 5, 14, 24, "ENUM_VALUE_BANNED_SUFFIXES", + bufanalysistesting.WithPluginName("buf-plugin-suffix.wasm"), + bufanalysistesting.WithPolicyName("buf.policy2.yaml"), + ), + bufanalysistesting.NewFileAnnotation( + t, "b.proto", 19, 5, 19, 23, "FIELD_BANNED_SUFFIXES", + bufanalysistesting.WithPluginName("buf-plugin-suffix.wasm"), + bufanalysistesting.WithPolicyName("buf.policy2.yaml"), + ), + ) +} + +func TestRunLintPolicyIgnores1(t *testing.T) { + t.Parallel() + testLint( + t, + "policy_ignores1", + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar.proto", 9, 9, 9, 12, "MESSAGE_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar.proto", 13, 6, 13, 9, "ENUM_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar2.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar2.proto", 9, 9, 9, 13, "MESSAGE_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar2.proto", 13, 6, 13, 10, "ENUM_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/buf.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/buf.proto", 9, 9, 9, 12, "MESSAGE_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/buf.proto", 13, 6, 13, 9, "ENUM_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/bar/bar.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/bar/bar.proto", 9, 9, 9, 12, "MESSAGE_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/bar/bar.proto", 13, 6, 13, 9, "ENUM_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/baz/baz.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/baz/baz.proto", 9, 9, 9, 12, "MESSAGE_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/baz/baz.proto", 13, 6, 13, 9, "ENUM_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/buf.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/buf.proto", 9, 9, 9, 12, "MESSAGE_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/buf.proto", 13, 6, 13, 9, "ENUM_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + ) +} + +func TestRunLintPolicyIgnores2(t *testing.T) { + t.Parallel() + testLint( + t, + "policy_ignores2", + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar.proto", 9, 9, 9, 12, "MESSAGE_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar.proto", 13, 6, 13, 9, "ENUM_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/buf.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/buf.proto", 9, 9, 9, 12, "MESSAGE_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/buf.proto", 13, 6, 13, 9, "ENUM_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + ) +} + +func TestRunLintPolicyIgnores3(t *testing.T) { + t.Parallel() + testLint( + t, + "policy_ignores3", + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar2.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar2.proto", 9, 9, 9, 13, "MESSAGE_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar2.proto", 13, 6, 13, 10, "ENUM_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/buf.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/buf.proto", 9, 9, 9, 12, "MESSAGE_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/buf.proto", 13, 6, 13, 9, "ENUM_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/baz/baz.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/baz/baz.proto", 9, 9, 9, 12, "MESSAGE_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/baz/baz.proto", 13, 6, 13, 9, "ENUM_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/buf.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/buf.proto", 9, 9, 9, 12, "MESSAGE_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/buf.proto", 13, 6, 13, 9, "ENUM_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + ) +} + +func TestRunLintPolicyIgnores4(t *testing.T) { + t.Parallel() + testLint( + t, + "policy_ignores4", + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar2.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar2.proto", 9, 9, 9, 13, "MESSAGE_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/bar/bar2.proto", 13, 6, 13, 10, "ENUM_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/buf.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/buf.proto", 9, 9, 9, 12, "MESSAGE_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/buf.proto", 13, 6, 13, 9, "ENUM_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/baz/baz.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/baz/baz.proto", 9, 9, 9, 12, "MESSAGE_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/baz/baz.proto", 13, 6, 13, 9, "ENUM_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/buf.proto", 6, 9, 6, 15, "FIELD_LOWER_SNAKE_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/buf.proto", 9, 9, 9, 12, "MESSAGE_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + bufanalysistesting.NewFileAnnotation(t, "buf/foo/buf.proto", 13, 6, 13, 9, "ENUM_PASCAL_CASE", bufanalysistesting.WithPolicyName("policy.yaml")), + ) +} + func testLint( t *testing.T, relDirPath string, @@ -1357,6 +1519,7 @@ func testLintWithOptions( logger, bufcheck.ClientWithRunnerProvider(bufcheck.NewLocalRunnerProvider(wasmRuntime)), bufcheck.ClientWithLocalWasmPluginsFromOS(), + bufcheck.ClientWithLocalPolicies(readWriteBucket), ) require.NoError(t, err) err = client.Lint( @@ -1364,6 +1527,7 @@ func testLintWithOptions( lintConfig, image, bufcheck.WithPluginConfigs(workspace.PluginConfigs()...), + bufcheck.WithPolicyConfigs(workspace.PolicyConfigs()...), ) if len(expectedFileAnnotations) == 0 { assert.NoError(t, err) diff --git a/private/bufpkg/bufcheck/multi_client.go b/private/bufpkg/bufcheck/multi_client.go index 58cd15d22d..6dd0cb73a2 100644 --- a/private/bufpkg/bufcheck/multi_client.go +++ b/private/bufpkg/bufcheck/multi_client.go @@ -105,7 +105,7 @@ func (c *multiClient) Check(ctx context.Context, request check.Request) ([]*anno annotations := slicesext.Map( delegateResponse.Annotations(), func(checkAnnotation check.Annotation) *annotation { - return newAnnotation(checkAnnotation, delegate.PluginName) + return newAnnotation(checkAnnotation, delegate.PluginName, delegate.PolicyName) }, ) lock.Lock() diff --git a/private/bufpkg/bufcheck/multi_client_test.go b/private/bufpkg/bufcheck/multi_client_test.go index 92f4c772ed..42b77e9c68 100644 --- a/private/bufpkg/bufcheck/multi_client_test.go +++ b/private/bufpkg/bufcheck/multi_client_test.go @@ -106,8 +106,8 @@ func testMultiClientSimple(t *testing.T, cacheRules bool) { multiClient := newMultiClient( slogtestext.NewLogger(t), []*checkClientSpec{ - newCheckClientSpec("buf-plugin-field-lower-snake-case", fieldLowerSnakeCaseClient, emptyOptions), - newCheckClientSpec("buf-plugin-timestamp-suffix", timestampSuffixClient, emptyOptions), + newCheckClientSpec("buf-plugin-field-lower-snake-case", "", fieldLowerSnakeCaseClient, emptyOptions), + newCheckClientSpec("buf-plugin-timestamp-suffix", "", timestampSuffixClient, emptyOptions), }, ) @@ -166,8 +166,8 @@ func TestMultiClientCannotHaveOverlappingRules(t *testing.T) { multiClient := newMultiClient( slogtestext.NewLogger(t), []*checkClientSpec{ - newCheckClientSpec("buf-plugin-field-lower-snake-case", fieldLowerSnakeCaseClient, emptyOptions), - newCheckClientSpec("buf-plugin-field-lower-snake-case", fieldLowerSnakeCaseClient, emptyOptions), + newCheckClientSpec("buf-plugin-field-lower-snake-case", "", fieldLowerSnakeCaseClient, emptyOptions), + newCheckClientSpec("buf-plugin-field-lower-snake-case", "", fieldLowerSnakeCaseClient, emptyOptions), }, ) @@ -200,6 +200,7 @@ func TestMultiClientCannotHaveOverlappingRulesWithBuiltIn(t *testing.T) { []bufconfig.PluginConfig{ duplicateBuiltInRulePluginConfig, }, + nil, false, emptyOptions, ) @@ -260,8 +261,8 @@ func TestMultiClientCannotHaveOverlappingCategories(t *testing.T) { multiClient := newMultiClient( slogtestext.NewLogger(t), []*checkClientSpec{ - newCheckClientSpec("buf-plugin-1", client1, emptyOptions), - newCheckClientSpec("buf-plugin-2", client2, emptyOptions), + newCheckClientSpec("buf-plugin-1", "", client1, emptyOptions), + newCheckClientSpec("buf-plugin-2", "", client2, emptyOptions), }, ) @@ -294,6 +295,7 @@ func TestMultiClientCannotHaveOverlappingCategoriesWithBuiltIn(t *testing.T) { []bufconfig.PluginConfig{ duplicateBuiltInRulePluginConfig, }, + nil, false, emptyOptions, ) diff --git a/private/bufpkg/bufcheck/policy_config.go b/private/bufpkg/bufcheck/policy_config.go new file mode 100644 index 0000000000..4bb1b4df64 --- /dev/null +++ b/private/bufpkg/bufcheck/policy_config.go @@ -0,0 +1,69 @@ +// Copyright 2020-2025 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bufcheck + +import ( + "github.com/bufbuild/buf/private/bufpkg/bufconfig" + "github.com/bufbuild/buf/private/bufpkg/bufpolicy/bufpolicyconfig" +) + +func newPolicyLintConfig( + policyConfig bufconfig.PolicyConfig, + policyFile bufpolicyconfig.BufPolicyYAMLFile, +) (bufconfig.LintConfig, error) { + policyFileLintConfig := policyFile.LintConfig() + checkConfig, err := bufconfig.NewEnabledCheckConfig( + policyFileLintConfig.FileVersion(), + policyFileLintConfig.UseIDsAndCategories(), + policyFileLintConfig.ExceptIDsAndCategories(), + policyConfig.IgnorePaths(), + policyConfig.IgnoreIDOrCategoryToPaths(), + policyFileLintConfig.DisableBuiltin(), + ) + if err != nil { + return nil, err + } + return bufconfig.NewLintConfig( + checkConfig, + policyFileLintConfig.EnumZeroValueSuffix(), + policyFileLintConfig.RPCAllowSameRequestResponse(), + policyFileLintConfig.RPCAllowGoogleProtobufEmptyRequests(), + policyFileLintConfig.RPCAllowGoogleProtobufEmptyResponses(), + policyFileLintConfig.ServiceSuffix(), + policyFileLintConfig.AllowCommentIgnores(), + ), nil +} + +func newPolicyBreakingConfig( + policyConfig bufconfig.PolicyConfig, + policyFile bufpolicyconfig.BufPolicyYAMLFile, +) (bufconfig.BreakingConfig, error) { + policyFileBreakingConfig := policyFile.BreakingConfig() + checkConfig, err := bufconfig.NewEnabledCheckConfig( + policyFileBreakingConfig.FileVersion(), + policyFileBreakingConfig.UseIDsAndCategories(), + policyFileBreakingConfig.ExceptIDsAndCategories(), + policyConfig.IgnorePaths(), + policyConfig.IgnoreIDOrCategoryToPaths(), + policyFileBreakingConfig.DisableBuiltin(), + ) + if err != nil { + return nil, err + } + return bufconfig.NewBreakingConfig( + checkConfig, + policyFileBreakingConfig.IgnoreUnstablePackages(), + ), nil +} diff --git a/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_empty/a.proto b/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_empty/a.proto new file mode 100644 index 0000000000..bc894d5c37 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_empty/a.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +package a; + +message A {} diff --git a/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_empty/buf.yaml b/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_empty/buf.yaml new file mode 100644 index 0000000000..75519cc6fa --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_empty/buf.yaml @@ -0,0 +1,5 @@ +version: v2 +breaking: + disable_builtin: true +policies: + - policy: empty.policy.yaml diff --git a/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_empty/empty.policy.yaml b/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_empty/empty.policy.yaml new file mode 100644 index 0000000000..b4d4e478b7 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_empty/empty.policy.yaml @@ -0,0 +1 @@ +version: v2 diff --git a/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_local/a.proto b/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_local/a.proto new file mode 100644 index 0000000000..bf60ac054e --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_local/a.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +service ALegacy { + rpc GetA(GetARequest) returns (GetAResponse); + rpc ListA(ListARequest) returns (ListAResponse); +} + +message GetARequest {} +message GetAResponse {} + +message ListARequest { + uint32 page_size = 1; + string page_token = 2; +} + +message ListAResponse { + message Value { + string id = 1; + bytes content = 2; + } + repeated Value values = 1; + string page_token = 2; +} diff --git a/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_local/b.proto b/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_local/b.proto new file mode 100644 index 0000000000..fa683036f3 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_local/b.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package b; + +service B { + rpc GetB(GetBRequest) returns (GetBResponse); +} + +message GetBRequest {} +message GetBResponse { + enum BStatus { + B_STATUS_UNSPECIFIED = 0; + B_STATUS_VALID = 1; + } + message ValueStable { + string name = 1; + BStatus status = 2; + string a_uuid = 3; + } + ValueStable value = 1; +} diff --git a/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_local/buf.policy1.yaml b/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_local/buf.policy1.yaml new file mode 100644 index 0000000000..9d184217e3 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_local/buf.policy1.yaml @@ -0,0 +1,15 @@ +version: v2 +breaking: + use: + - ENUM_NO_DELETE + - SERVICE_SUFFIXES_NO_CHANGE +plugins: + - plugin: buf-plugin-suffix.wasm + options: + service_no_change_suffixes: + - Legacy + - Bridge + message_no_change_suffixes: + - Stable + enum_no_change_suffixes: + - Status diff --git a/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_local/buf.policy2.yaml b/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_local/buf.policy2.yaml new file mode 100644 index 0000000000..3bb4602044 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_local/buf.policy2.yaml @@ -0,0 +1,15 @@ +version: v2 +breaking: + use: + - MESSAGE_SUFFIXES_NO_CHANGE + - ENUM_SUFFIXES_NO_CHANGE +plugins: + - plugin: buf-plugin-suffix.wasm + options: + service_no_change_suffixes: + - Legacy + - Bridge + message_no_change_suffixes: + - Stable + enum_no_change_suffixes: + - Status diff --git a/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_local/buf.yaml b/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_local/buf.yaml new file mode 100644 index 0000000000..c197f0910b --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/breaking/current/breaking_policy_local/buf.yaml @@ -0,0 +1,7 @@ +version: v2 +breaking: + use: + - ENUM_NO_DELETE +policies: + - policy: buf.policy1.yaml + - policy: buf.policy2.yaml diff --git a/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_empty/a.proto b/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_empty/a.proto new file mode 100644 index 0000000000..bf67801def --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_empty/a.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +package a; + +message B {} diff --git a/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_empty/buf.yaml b/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_empty/buf.yaml new file mode 100644 index 0000000000..3604157ab6 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_empty/buf.yaml @@ -0,0 +1,5 @@ +version: v2 +lint: + disable_builtin: true +policies: + - policy: empty.policy.yaml diff --git a/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_empty/empty.policy.yaml b/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_empty/empty.policy.yaml new file mode 100644 index 0000000000..b4d4e478b7 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_empty/empty.policy.yaml @@ -0,0 +1 @@ +version: v2 diff --git a/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_local/a.proto b/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_local/a.proto new file mode 100644 index 0000000000..3e37208aea --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_local/a.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +service ALegacy { + rpc GetA(GetARequest) returns (GetAResponse); + rpc ListA(ListARequest) returns (ListAResponse); + rpc GetATwo(GetATwoRequest) returns (GetATwoResponse); +} + +message GetARequest {} +message GetAResponse {} + +message ListARequest { + uint32 page_size = 1; + string page_token = 2; +} + +message ListAResponse { + message Value { + string id = 1; + bytes content = 2; + } + repeated Value values = 1; + string page_token = 2; +} + +message GetATwoRequest {} +message GetATwoResponse {} diff --git a/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_local/b.proto b/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_local/b.proto new file mode 100644 index 0000000000..6b3ca7e206 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_local/b.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package b; + +service B { + rpc GetB(GetBRequest) returns (GetBResponse); +} + +message GetBRequest {} +message GetBResponse { + enum BStatus { + B_STATUS_UNSPECIFIED = 0; + B_STATUS_VALID = 1; + B_STATUS_NEW = 2; + } + message ValueStable { + string name = 1; + BStatus status = 2; + string b_uuid = 3; + } + ValueStable value = 1; +} diff --git a/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_local/buf.yaml b/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_local/buf.yaml new file mode 100644 index 0000000000..2be2bf1127 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/breaking/previous/breaking_policy_local/buf.yaml @@ -0,0 +1,11 @@ +version: v2 +plugins: + - plugin: buf-plugin-suffix + options: + service_no_change_suffixes: + - Legacy + - Bridge + message_no_change_suffixes: + - Stable + enum_no_change_suffixes: + - Status diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_empty/a.proto b/private/bufpkg/bufcheck/testdata/lint/policy_empty/a.proto new file mode 100644 index 0000000000..bc894d5c37 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_empty/a.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +package a; + +message A {} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_empty/buf.yaml b/private/bufpkg/bufcheck/testdata/lint/policy_empty/buf.yaml new file mode 100644 index 0000000000..3604157ab6 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_empty/buf.yaml @@ -0,0 +1,5 @@ +version: v2 +lint: + disable_builtin: true +policies: + - policy: empty.policy.yaml diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_empty/empty.policy.yaml b/private/bufpkg/bufcheck/testdata/lint/policy_empty/empty.policy.yaml new file mode 100644 index 0000000000..b4d4e478b7 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_empty/empty.policy.yaml @@ -0,0 +1 @@ +version: v2 diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf.yaml b/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf.yaml new file mode 100644 index 0000000000..f986e698a0 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf.yaml @@ -0,0 +1,5 @@ +version: v2 +lint: + disable_builtin: true +policies: + - policy: policy.yaml diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/bar/bar.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/bar/bar.proto new file mode 100644 index 0000000000..6d5d3e30af --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/bar/bar.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.bar; + +message Foo { + int64 oneTwo = 1; +} + +message bar { + int64 three = 3; +} + +enum baz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/bar/bar2.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/bar/bar2.proto new file mode 100644 index 0000000000..17544ec801 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/bar/bar2.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.bar; + +message Foo2 { + int64 oneTwo = 1; +} + +message bar2 { + int64 three = 3; +} + +enum baz2 { + BAZ2_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/buf.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/buf.proto new file mode 100644 index 0000000000..70c80e9a98 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/buf.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf; + +message Foo { + int64 oneTwo = 1; +} + +message biz { + int64 three = 3; +} + +enum buz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/foo/bar/bar.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/foo/bar/bar.proto new file mode 100644 index 0000000000..2b8e863d2e --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/foo/bar/bar.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.foo.bar; + +message Foo { + int64 oneTwo = 1; +} + +message bar { + int64 three = 3; +} + +enum baz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/foo/baz/baz.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/foo/baz/baz.proto new file mode 100644 index 0000000000..7142516895 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/foo/baz/baz.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.foo.baz; + +message Foo { + int64 oneTwo = 1; +} + +message bar { + int64 three = 3; +} + +enum baz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/foo/buf.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/foo/buf.proto new file mode 100644 index 0000000000..514b9fe0e3 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/buf/foo/buf.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.foo; + +message Foo { + int64 oneTwo = 1; +} + +message biz { + int64 three = 3; +} + +enum buz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/policy.yaml b/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/policy.yaml new file mode 100644 index 0000000000..fa23fc128c --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores1/policy.yaml @@ -0,0 +1,7 @@ +version: v2 +lint: + use: + - PACKAGE_DIRECTORY_MATCH + - ENUM_PASCAL_CASE + - FIELD_LOWER_SNAKE_CASE + - MESSAGE_PASCAL_CASE diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf.yaml b/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf.yaml new file mode 100644 index 0000000000..3a20d97123 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf.yaml @@ -0,0 +1,8 @@ +version: v2 +lint: + disable_builtin: true +policies: + - policy: policy.yaml + ignore: + - buf/bar/bar2.proto + - buf/foo diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/bar/bar.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/bar/bar.proto new file mode 100644 index 0000000000..6d5d3e30af --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/bar/bar.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.bar; + +message Foo { + int64 oneTwo = 1; +} + +message bar { + int64 three = 3; +} + +enum baz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/bar/bar2.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/bar/bar2.proto new file mode 100644 index 0000000000..17544ec801 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/bar/bar2.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.bar; + +message Foo2 { + int64 oneTwo = 1; +} + +message bar2 { + int64 three = 3; +} + +enum baz2 { + BAZ2_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/buf.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/buf.proto new file mode 100644 index 0000000000..70c80e9a98 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/buf.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf; + +message Foo { + int64 oneTwo = 1; +} + +message biz { + int64 three = 3; +} + +enum buz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/foo/bar/bar.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/foo/bar/bar.proto new file mode 100644 index 0000000000..2b8e863d2e --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/foo/bar/bar.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.foo.bar; + +message Foo { + int64 oneTwo = 1; +} + +message bar { + int64 three = 3; +} + +enum baz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/foo/baz/baz.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/foo/baz/baz.proto new file mode 100644 index 0000000000..7142516895 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/foo/baz/baz.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.foo.baz; + +message Foo { + int64 oneTwo = 1; +} + +message bar { + int64 three = 3; +} + +enum baz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/foo/buf.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/foo/buf.proto new file mode 100644 index 0000000000..514b9fe0e3 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/buf/foo/buf.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.foo; + +message Foo { + int64 oneTwo = 1; +} + +message biz { + int64 three = 3; +} + +enum buz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/policy.yaml b/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/policy.yaml new file mode 100644 index 0000000000..fa23fc128c --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores2/policy.yaml @@ -0,0 +1,7 @@ +version: v2 +lint: + use: + - PACKAGE_DIRECTORY_MATCH + - ENUM_PASCAL_CASE + - FIELD_LOWER_SNAKE_CASE + - MESSAGE_PASCAL_CASE diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf.yaml b/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf.yaml new file mode 100644 index 0000000000..72b55b4197 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf.yaml @@ -0,0 +1,14 @@ +version: v2 +lint: + disable_builtin: true +policies: + - policy: policy.yaml + ignore_only: + ENUM_PASCAL_CASE: + - buf/bar/bar.proto + - buf/foo/bar + - buf/foo/bar + MESSAGE_PASCAL_CASE: + - buf/bar/bar.proto + BASIC: + - buf/foo/bar diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/bar/bar.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/bar/bar.proto new file mode 100644 index 0000000000..6d5d3e30af --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/bar/bar.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.bar; + +message Foo { + int64 oneTwo = 1; +} + +message bar { + int64 three = 3; +} + +enum baz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/bar/bar2.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/bar/bar2.proto new file mode 100644 index 0000000000..17544ec801 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/bar/bar2.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.bar; + +message Foo2 { + int64 oneTwo = 1; +} + +message bar2 { + int64 three = 3; +} + +enum baz2 { + BAZ2_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/buf.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/buf.proto new file mode 100644 index 0000000000..70c80e9a98 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/buf.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf; + +message Foo { + int64 oneTwo = 1; +} + +message biz { + int64 three = 3; +} + +enum buz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/foo/bar/bar.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/foo/bar/bar.proto new file mode 100644 index 0000000000..2b8e863d2e --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/foo/bar/bar.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.foo.bar; + +message Foo { + int64 oneTwo = 1; +} + +message bar { + int64 three = 3; +} + +enum baz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/foo/baz/baz.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/foo/baz/baz.proto new file mode 100644 index 0000000000..7142516895 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/foo/baz/baz.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.foo.baz; + +message Foo { + int64 oneTwo = 1; +} + +message bar { + int64 three = 3; +} + +enum baz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/foo/buf.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/foo/buf.proto new file mode 100644 index 0000000000..514b9fe0e3 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/buf/foo/buf.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.foo; + +message Foo { + int64 oneTwo = 1; +} + +message biz { + int64 three = 3; +} + +enum buz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/policy.yaml b/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/policy.yaml new file mode 100644 index 0000000000..fa23fc128c --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores3/policy.yaml @@ -0,0 +1,7 @@ +version: v2 +lint: + use: + - PACKAGE_DIRECTORY_MATCH + - ENUM_PASCAL_CASE + - FIELD_LOWER_SNAKE_CASE + - MESSAGE_PASCAL_CASE diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf.yaml b/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf.yaml new file mode 100644 index 0000000000..72b55b4197 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf.yaml @@ -0,0 +1,14 @@ +version: v2 +lint: + disable_builtin: true +policies: + - policy: policy.yaml + ignore_only: + ENUM_PASCAL_CASE: + - buf/bar/bar.proto + - buf/foo/bar + - buf/foo/bar + MESSAGE_PASCAL_CASE: + - buf/bar/bar.proto + BASIC: + - buf/foo/bar diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/bar/bar.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/bar/bar.proto new file mode 100644 index 0000000000..6d5d3e30af --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/bar/bar.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.bar; + +message Foo { + int64 oneTwo = 1; +} + +message bar { + int64 three = 3; +} + +enum baz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/bar/bar2.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/bar/bar2.proto new file mode 100644 index 0000000000..17544ec801 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/bar/bar2.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.bar; + +message Foo2 { + int64 oneTwo = 1; +} + +message bar2 { + int64 three = 3; +} + +enum baz2 { + BAZ2_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/buf.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/buf.proto new file mode 100644 index 0000000000..70c80e9a98 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/buf.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf; + +message Foo { + int64 oneTwo = 1; +} + +message biz { + int64 three = 3; +} + +enum buz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/foo/bar/bar.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/foo/bar/bar.proto new file mode 100644 index 0000000000..2b8e863d2e --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/foo/bar/bar.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.foo.bar; + +message Foo { + int64 oneTwo = 1; +} + +message bar { + int64 three = 3; +} + +enum baz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/foo/baz/baz.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/foo/baz/baz.proto new file mode 100644 index 0000000000..7142516895 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/foo/baz/baz.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.foo.baz; + +message Foo { + int64 oneTwo = 1; +} + +message bar { + int64 three = 3; +} + +enum baz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/foo/buf.proto b/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/foo/buf.proto new file mode 100644 index 0000000000..514b9fe0e3 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/buf/foo/buf.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package buf.foo; + +message Foo { + int64 oneTwo = 1; +} + +message biz { + int64 three = 3; +} + +enum buz { + BAZ_UNSPECIFIED = 0; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/policy.yaml b/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/policy.yaml new file mode 100644 index 0000000000..fa23fc128c --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_ignores4/policy.yaml @@ -0,0 +1,7 @@ +version: v2 +lint: + use: + - PACKAGE_DIRECTORY_MATCH + - ENUM_PASCAL_CASE + - FIELD_LOWER_SNAKE_CASE + - MESSAGE_PASCAL_CASE diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_local/a.proto b/private/bufpkg/bufcheck/testdata/lint/policy_local/a.proto new file mode 100644 index 0000000000..94d6ddf2a1 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_local/a.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; + +service A { + rpc GetA(GetARequest) returns (GetAResponse); + rpc ListA(ListARequest) returns (ListAResponse); +} + +service AMock { + rpc GetAllA(GetAllARequest) returns (GetAllAResponse); +} + +message GetARequest {} +message GetAResponse {} + +message ListARequest { + uint32 page_size = 1; +} + +message ListAResponse { + message Value { + string id = 1; + bytes content = 2; + } + repeated Value values = 1; +} + +message GetAllARequest {} +message GetAllAResponse {} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_local/b.proto b/private/bufpkg/bufcheck/testdata/lint/policy_local/b.proto new file mode 100644 index 0000000000..2dba34b847 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_local/b.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package b; + +service B { + rpc GetElement(GetElementRequest) returns (GetElementResponse); +} + +message GetElementRequest {} +message GetElementResponse { + enum Status { + STATUS_UNSPECIFIED = 0; + STATUS_VALID = 1; + STATUS_INVALID = 2; + } + message Value { + string name = 1; + Status status = 2; + string a_uuid = 3; + } + Value value = 1; +} diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_local/buf.policy1.yaml b/private/bufpkg/bufcheck/testdata/lint/policy_local/buf.policy1.yaml new file mode 100644 index 0000000000..d425722e70 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_local/buf.policy1.yaml @@ -0,0 +1,17 @@ +version: v2 +lint: + use: + - SERVICE_BANNED_SUFFIXES + - RPC_BANNED_SUFFIXES +plugins: + - plugin: buf-plugin-suffix.wasm + options: + service_banned_suffixes: + - Mock + - Test + rpc_banned_suffixes: + - Element + field_banned_suffixes: + - _uuid + enum_value_banned_suffixes: + - _INVALID diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_local/buf.policy2.yaml b/private/bufpkg/bufcheck/testdata/lint/policy_local/buf.policy2.yaml new file mode 100644 index 0000000000..e5367b6ab9 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_local/buf.policy2.yaml @@ -0,0 +1,17 @@ +version: v2 +lint: + use: + - FIELD_BANNED_SUFFIXES + - ENUM_VALUE_BANNED_SUFFIXES +plugins: + - plugin: buf-plugin-suffix.wasm + options: + service_banned_suffixes: + - Mock + - Test + rpc_banned_suffixes: + - Element + field_banned_suffixes: + - _uuid + enum_value_banned_suffixes: + - _INVALID diff --git a/private/bufpkg/bufcheck/testdata/lint/policy_local/buf.yaml b/private/bufpkg/bufcheck/testdata/lint/policy_local/buf.yaml new file mode 100644 index 0000000000..23d0479928 --- /dev/null +++ b/private/bufpkg/bufcheck/testdata/lint/policy_local/buf.yaml @@ -0,0 +1,19 @@ +version: v2 +lint: + use: + - PACKAGE_DEFINED +plugins: + - plugin: buf-plugin-suffix.wasm + options: + service_banned_suffixes: + - Mock + - Test + rpc_banned_suffixes: + - Element + field_banned_suffixes: + - _uuid + enum_value_banned_suffixes: + - _INVALID +policies: + - policy: buf.policy1.yaml + - policy: buf.policy2.yaml diff --git a/private/bufpkg/bufconfig/policy_config.go b/private/bufpkg/bufconfig/policy_config.go index 0668213818..783656ef1a 100644 --- a/private/bufpkg/bufconfig/policy_config.go +++ b/private/bufpkg/bufconfig/policy_config.go @@ -16,8 +16,10 @@ package bufconfig import ( "errors" + "os" "slices" + "github.com/bufbuild/buf/private/bufpkg/bufparse" "github.com/bufbuild/buf/private/pkg/slicesext" "github.com/bufbuild/buf/private/pkg/syserror" ) @@ -36,17 +38,30 @@ type PolicyConfig interface { // Paths are relative to roots. // Paths are sorted. IgnoreIDOrCategoryToPaths() map[string][]string + // Ref returns the policy reference. + // + // This is only non-nil when the plugin is remote. + Ref() bufparse.Ref isPolicyConfig() } -// NewPolicyConfig returns a new PolicyConfig. -func NewPolicyConfig( +// NewLocalPolicyConfig returns a new PolicyConfig for a local policy. +func NewLocalPolicyConfig( name string, ignore []string, ignoreOnly map[string][]string, ) (PolicyConfig, error) { - return newPolicyConfig(name, ignore, ignoreOnly) + return newPolicyConfig(name, ignore, ignoreOnly, nil) +} + +// NewRemotePolicyConfig returns a new PolicyConfig for a remote policy. +func NewRemotePolicyConfig( + ref bufparse.Ref, + ignore []string, + ignoreOnly map[string][]string, +) (PolicyConfig, error) { + return newPolicyConfig(ref.String(), ignore, ignoreOnly, ref) } // *** PRIVATE *** @@ -55,15 +70,27 @@ type policyConfig struct { name string ignore []string ignoreOnly map[string][]string + ref bufparse.Ref } func newPolicyConfigForExternalV2( externalConfig externalBufYAMLFilePolicyV2, ) (*policyConfig, error) { + var policyRef bufparse.Ref + name := externalConfig.Policy + if ref, err := bufparse.ParseRef(name); err == nil { + // Check if the local filepath exists, if it does presume its + // not a remote reference. Okay to use os.Stat instead of + // os.Lstat. + if _, err := os.Stat(name); os.IsNotExist(err) { + policyRef = ref + } + } return newPolicyConfig( - externalConfig.Policy, + name, externalConfig.Ignore, externalConfig.IgnoreOnly, + policyRef, ) } @@ -71,6 +98,7 @@ func newPolicyConfig( name string, ignore []string, ignoreOnly map[string][]string, + policyRef bufparse.Ref, ) (*policyConfig, error) { if name == "" { return nil, errors.New("must specify a name to the policy") @@ -94,6 +122,7 @@ func newPolicyConfig( name: name, ignore: ignore, ignoreOnly: ignoreOnly, + ref: policyRef, }, nil } @@ -109,6 +138,10 @@ func (p *policyConfig) IgnoreIDOrCategoryToPaths() map[string][]string { return copyStringToStringSliceMap(p.ignoreOnly) } +func (p *policyConfig) Ref() bufparse.Ref { + return p.ref +} + func (p *policyConfig) isPolicyConfig() {} func newExternalV2ForPolicyConfig( diff --git a/private/bufpkg/bufplugin/bufpluginstore/module_data_store.go b/private/bufpkg/bufplugin/bufpluginstore/policy_data_store.go similarity index 100% rename from private/bufpkg/bufplugin/bufpluginstore/module_data_store.go rename to private/bufpkg/bufplugin/bufpluginstore/policy_data_store.go diff --git a/private/bufpkg/bufpolicy/bufpolicyconfig/buf_policy_yaml_file.go b/private/bufpkg/bufpolicy/bufpolicyconfig/buf_policy_yaml_file.go index b230ef4814..70e03b52c0 100644 --- a/private/bufpkg/bufpolicy/bufpolicyconfig/buf_policy_yaml_file.go +++ b/private/bufpkg/bufpolicy/bufpolicyconfig/buf_policy_yaml_file.go @@ -30,6 +30,32 @@ import ( "github.com/bufbuild/buf/private/pkg/syserror" ) +var ( + // defaultLintConfigV2 is the default lint config for v2. + defaultLintConfigV2 bufconfig.LintConfig = bufconfig.NewLintConfig( + bufconfig.NewEnabledCheckConfigForUseIDsAndCategories( + bufconfig.FileVersionV2, + nil, + true, // Disable builtin is true by default. + ), + "", + false, + false, + false, + "", + false, // Policy configs do not allow comment ignores. + ) + + defaultBreakingConfigV2 bufconfig.BreakingConfig = bufconfig.NewBreakingConfig( + bufconfig.NewEnabledCheckConfigForUseIDsAndCategories( + bufconfig.FileVersionV2, + nil, + true, // Disable builtin is true by default. + ), + false, + ) +) + // BufPolicyYAMLFile represents a Policy config file. type BufPolicyYAMLFile interface { File @@ -134,10 +160,10 @@ func newBufPolicyYAMLFile( if breakingConfig.FileVersion() != bufconfig.FileVersionV2 { validationErr = errors.Join(validationErr, fmt.Errorf("breakingConfig.FileVersion() must be %s", bufconfig.FileVersionV2)) } - if len(lintConfig.IgnorePaths()) > 0 { + if len(breakingConfig.IgnorePaths()) > 0 { validationErr = errors.Join(validationErr, fmt.Errorf("breakingConfig.IgnorePaths() must be empty")) } - if len(lintConfig.IgnoreIDOrCategoryToPaths()) > 0 { + if len(breakingConfig.IgnoreIDOrCategoryToPaths()) > 0 { validationErr = errors.Join(validationErr, fmt.Errorf("breakingConfig.IgnoreIDOrCategoryToPaths() must be empty")) } if breakingConfig.DisableBuiltin() { @@ -169,10 +195,16 @@ func (p *bufPolicyYAMLFile) Name() string { } func (p *bufPolicyYAMLFile) LintConfig() bufconfig.LintConfig { + if p.lintConfig == nil { + return defaultLintConfigV2 + } return p.lintConfig } func (p *bufPolicyYAMLFile) BreakingConfig() bufconfig.BreakingConfig { + if p.breakingConfig == nil { + return defaultBreakingConfigV2 + } return p.breakingConfig } diff --git a/private/bufpkg/bufpolicy/policy_data_provider.go b/private/bufpkg/bufpolicy/policy_data_provider.go index 44f764c9ef..244d3311b5 100644 --- a/private/bufpkg/bufpolicy/policy_data_provider.go +++ b/private/bufpkg/bufpolicy/policy_data_provider.go @@ -39,10 +39,7 @@ type PolicyDataProvider interface { // If there is no error, the length of the PolicyDatas returned will match the length of the PolicyKeys. // If there is an error, no PolicyDatas will be returned. // If any PolicyKey is not found, an error with fs.ErrNotExist will be returned. - GetPolicyDatasForPolicyKeys( - context.Context, - []PolicyKey, - ) ([]PolicyData, error) + GetPolicyDatasForPolicyKeys(context.Context, []PolicyKey) ([]PolicyData, error) } // *** PRIVATE *** diff --git a/private/bufpkg/bufprotocompile/bufprotocompile.go b/private/bufpkg/bufprotocompile/bufprotocompile.go index 68e5722fa9..0df57d50bf 100644 --- a/private/bufpkg/bufprotocompile/bufprotocompile.go +++ b/private/bufpkg/bufprotocompile/bufprotocompile.go @@ -88,7 +88,8 @@ func FileAnnotationForErrorWithPos( endColumn, typeString, message, - "", + "", // pluginName + "", // policyName ), nil } diff --git a/private/pkg/pluginrpcutil/wasm_runner.go b/private/pkg/pluginrpcutil/wasm_runner.go index 0a3478d037..e7050701a6 100644 --- a/private/pkg/pluginrpcutil/wasm_runner.go +++ b/private/pkg/pluginrpcutil/wasm_runner.go @@ -83,7 +83,7 @@ func (r *wasmRunner) loadCompiledModuleOnce(ctx context.Context) (wasm.CompiledM func (r *wasmRunner) loadCompiledModule(ctx context.Context) (wasm.CompiledModule, error) { moduleWasm, err := r.getData() if err != nil { - return nil, fmt.Errorf("could not read plugin %q: %err", r.programName, err) + return nil, fmt.Errorf("could not read plugin %q: %w", r.programName, err) } // Compile the module. This CompiledModule is never released, so // subsequent calls to this function will benefit from the cached