Skip to content

Set top-level LintConfig and BreakingConfig and use for image inputs #3111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 43 additions & 13 deletions private/buf/bufctl/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"io"
"io/fs"
"net/http"
"path/filepath"
"sort"

"github.com/bufbuild/buf/private/buf/buffetch"
Expand Down Expand Up @@ -393,20 +394,49 @@ func (c *controller) GetTargetImageWithConfigs(
// We did not find a buf.yaml in our current directory, and there was no config override.
// Use the defaults.
} else {
switch fileVersion := bufYAMLFile.FileVersion(); fileVersion {
case bufconfig.FileVersionV1Beta1, bufconfig.FileVersionV1:
moduleConfigs := bufYAMLFile.ModuleConfigs()
if len(moduleConfigs) != 1 {
return nil, fmt.Errorf("expected 1 ModuleConfig for FileVersion %v, got %d", len(moduleConfigs), fileVersion)
// We take the top-level lint and breaking config from buf.yaml.
//
// In the case of v1 buf.yaml files, there is only ever one top-level config, so it
// should be populated and we use that.
// In the case of v2 buf.yaml files, this may be empty, since there may not be a
// top-level config. If that happens, we print a warning to the user and let them
// know that we are using v2 defaults.
// In the case where there is a top-level config and individual module configs, we
// print out a message to the user to let them know we are using the top-level config.
var configInfo string
// If configuration is read from an override, we determine if it was a raw config or
// a path to a valid override file.
if override := functionOptions.configOverride; override != "" {
switch filepath.Ext(override) {
case ".json", ".yaml", ".yml":
configInfo = functionOptions.configOverride
default:
configInfo = "from --config"
}
lintConfig = moduleConfigs[0].LintConfig()
breakingConfig = moduleConfigs[0].BreakingConfig()
case bufconfig.FileVersionV2:
// Do nothing. Use the default LintConfig and BreakingConfig. With
// the new buf.yamls with multiple modules, we don't know what lint or
// breaking config to apply.
default:
return nil, syserror.Newf("unknown FileVersion: %v", fileVersion)
} else {
// Otherwise we use the file path
configInfo = bufYAMLFile.ObjectData().Name()
}
if topLevelModuleConfig := bufYAMLFile.TopLevelModuleConfig(); topLevelModuleConfig != nil {
if bufYAMLFile.FileVersion() == bufconfig.FileVersionV2 && len(bufYAMLFile.ModuleConfigs()) > 1 {
c.logger.Sugar().Warnf(
`Configuration %s has multiple modules. The top-level lint/breaking configuration is used with your image, since Buf cannot deduce which specific module configuration to use otherwise.`,
configInfo,
)
}
lintConfig = topLevelModuleConfig.LintConfig()
breakingConfig = topLevelModuleConfig.BreakingConfig()
} else {
// Ensure that this is a v2 config
if fileVersion := bufYAMLFile.FileVersion(); fileVersion != bufconfig.FileVersionV2 {
return nil, syserror.Newf("non-v2 version with no top-level module config: %s", fileVersion)
}
c.logger.Sugar().Warnf(
`Configuration %s does not have a top-level lint/breaking configuration. Buf cannot deduce which specific module configuration to use with your image, so the default configuration is used instead.`,
configInfo,
)
lintConfig = bufconfig.DefaultLintConfigV2
breakingConfig = bufconfig.DefaultBreakingConfigV2
}
}
return []ImageWithConfig{
Expand Down
2 changes: 1 addition & 1 deletion private/buf/buffetch/buffetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ type Ref interface {
internalRef() internal.Ref
}

// MessageRef is an message file reference.
// MessageRef is a message file reference.
type MessageRef interface {
Ref
MessageEncoding() MessageEncoding
Expand Down
17 changes: 17 additions & 0 deletions private/buf/cmd/buf/buf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2464,6 +2464,23 @@ a/v3/a.proto:7:10:Field "2" on message "Foo" changed name from "value" to "Value
"--exclude-path",
filepath.Join("a", "v3", "foo"),
)
testRunStdoutStderrNoWarn(
t,
nil,
bufctl.ExitCodeFileAnnotation,
`a/v3/a.proto:6:3:Field "1" with name "key" on message "Foo" changed type from "string" to "int32". See https://developers.google.com/protocol-buffers/docs/proto3#updating for wire compatibility rules.`,
"",
"breaking",
filepath.Join(tempDir, "current.binpb"),
"--against",
filepath.Join(tempDir, "previous.binpb"),
"--path",
filepath.Join("a", "v3"),
"--exclude-path",
filepath.Join("a", "v3", "foo"),
"--config",
`{"version":"v2","breaking":{"use":["WIRE"]}}`,
)
}

func TestVersion(t *testing.T) {
Expand Down
64 changes: 63 additions & 1 deletion private/bufpkg/bufconfig/buf_yaml_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ type BufYAMLFile interface {
// All ModuleConfigs will have unique ModuleFullNames.
// Sorted by DirPath.
ModuleConfigs() []ModuleConfig
// TopLevelModuleConfig returns the top-level ModuleConfig for the File.
//
// For v1 buf.yaml files, there is only ever a single ModuleConfig, so this is returned.
// For v2 buf.yaml files, if a top-level module config exists, then it will be the top-level
// module config. Otherwise, this will return nil, so callers should be aware this may be
// empty.
TopLevelModuleConfig() ModuleConfig
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a ModuleConfig? We only want LintConfig and BreakingConfig, I don't know what a top-level ModuleConfig is in terms of v2

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used ModuleConfig because it worked better for the warnings and also to account for situations like:

version: v2
name: <remote>/<owner>/<module>

But in that case, it would be the default anyway, so it doesn't super matter, and also since we're not printing out the warnings, I can swap it back to LintConfig and BreakingConfig.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated back to LintConfig and BreakingConfig, and updated the PR title + description.

// ConfiguredDepModuleRefs returns the configured dependencies of the Workspace as ModuleRefs.
//
// These come from buf.yaml files.
Expand All @@ -93,7 +100,13 @@ func NewBufYAMLFile(
moduleConfigs []ModuleConfig,
configuredDepModuleRefs []bufmodule.ModuleRef,
) (BufYAMLFile, error) {
return newBufYAMLFile(fileVersion, nil, moduleConfigs, configuredDepModuleRefs)
return newBufYAMLFile(
fileVersion,
nil,
moduleConfigs,
nil, // Do not set top-level module config, use only module configs
configuredDepModuleRefs,
)
}

// GetBufYAMLFileForPrefix gets the buf.yaml file at the given bucket prefix.
Expand Down Expand Up @@ -190,13 +203,15 @@ type bufYAMLFile struct {
fileVersion FileVersion
objectData ObjectData
moduleConfigs []ModuleConfig
topLevelModuleConfig ModuleConfig
configuredDepModuleRefs []bufmodule.ModuleRef
}

func newBufYAMLFile(
fileVersion FileVersion,
objectData ObjectData,
moduleConfigs []ModuleConfig,
topLevelModuleConfig ModuleConfig,
configuredDepModuleRefs []bufmodule.ModuleRef,
) (*bufYAMLFile, error) {
if (fileVersion == FileVersionV1Beta1 || fileVersion == FileVersionV1) && len(moduleConfigs) > 1 {
Expand Down Expand Up @@ -255,6 +270,7 @@ func newBufYAMLFile(
fileVersion: fileVersion,
objectData: objectData,
moduleConfigs: moduleConfigs,
topLevelModuleConfig: topLevelModuleConfig,
configuredDepModuleRefs: configuredDepModuleRefs,
}, nil
}
Expand All @@ -275,6 +291,10 @@ func (c *bufYAMLFile) ModuleConfigs() []ModuleConfig {
return slicesext.Copy(c.moduleConfigs)
}

func (c *bufYAMLFile) TopLevelModuleConfig() ModuleConfig {
return c.topLevelModuleConfig
}

func (c *bufYAMLFile) ConfiguredDepModuleRefs() []bufmodule.ModuleRef {
return slicesext.Copy(c.configuredDepModuleRefs)
}
Expand Down Expand Up @@ -351,6 +371,7 @@ func readBufYAMLFile(
[]ModuleConfig{
moduleConfig,
},
moduleConfig,
configuredDepModuleRefs,
)
case FileVersionV2:
Expand Down Expand Up @@ -464,6 +485,46 @@ func readBufYAMLFile(
}
moduleConfigs = append(moduleConfigs, moduleConfig)
}
// If either the top-level name, lint or breaking config is non-empty, we construct a top-level
// module config.
var topLevelModuleConfig ModuleConfig
if externalBufYAMLFile.Name != "" || !defaultExternalLintConfig.isEmpty() || !defaultExternalBreakingConfig.isEmpty() {
topLevelLintConfig, err := getLintConfigForExternalLintV2(
fileVersion,
defaultExternalLintConfig,
".", // The top-level module config always has the root "."
false, // Not module-specific configuration
)
if err != nil {
return nil, err
}
topLevelBreakingConfig, err := getBreakingConfigForExternalBreaking(
fileVersion,
defaultExternalBreakingConfig,
".", // The top-level module config always has the root "."
false, // Not module-specific configuration
)
if err != nil {
return nil, err
}
var topLevelModuleFullName bufmodule.ModuleFullName
if externalBufYAMLFile.Name != "" {
topLevelModuleFullName, err = bufmodule.ParseModuleFullName(externalBufYAMLFile.Name)
if err != nil {
return nil, err
}
}
topLevelModuleConfig, err = newModuleConfig(
".", // The top-level module path is always "."
topLevelModuleFullName,
map[string][]string{".": {}}, // No top-level module config for excludes
topLevelLintConfig,
topLevelBreakingConfig,
)
if err != nil {
return nil, err
}
}
configuredDepModuleRefs, err := getConfiguredDepModuleRefsForExternalDeps(externalBufYAMLFile.Deps)
if err != nil {
return nil, err
Expand All @@ -472,6 +533,7 @@ func readBufYAMLFile(
fileVersion,
objectData,
moduleConfigs,
topLevelModuleConfig,
configuredDepModuleRefs,
)
default:
Expand Down
2 changes: 1 addition & 1 deletion private/bufpkg/bufconfig/lint_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ var (
false,
false,
"",
false,
true, // We default to allowing comment ignores in v2
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pkwarren caught an issue with the default v2 lint config, which should default to allowing comment ignores (https://buf.build/docs/configuration/v2/buf-yaml)

  # v1 configurations had an allow_comment_ignores option to opt-in to comment ignores.
  #
  # In v2, we allow comment ignores by default, and allow opt-out from comment ignores
  # with the disallow_comment_ignores option.
  disallow_comment_ignores: false

)
)

Expand Down