-
Notifications
You must be signed in to change notification settings - Fork 285
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
Add configurable base plugin support in buf.plugin.yaml #3513
Changes from all commits
57be7b9
86ec011
f2ad326
a2edfb7
a8d1340
e27a190
2f9dbb7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ package bufremotepluginconfig | |
import ( | ||
"errors" | ||
"fmt" | ||
"slices" | ||
"strings" | ||
|
||
"buf.build/go/spdx" | ||
|
@@ -60,7 +61,7 @@ func newConfig(externalConfig ExternalConfig, options []ConfigOption) (*Config, | |
dependencies = append(dependencies, reference) | ||
} | ||
} | ||
registryConfig, err := newRegistryConfig(externalConfig.Registry) | ||
registryConfig, err := newRegistryConfig(externalConfig.Registry, dependencies, opts.overrideRemote) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
@@ -87,7 +88,11 @@ func newConfig(externalConfig ExternalConfig, options []ConfigOption) (*Config, | |
}, nil | ||
} | ||
|
||
func newRegistryConfig(externalRegistryConfig ExternalRegistryConfig) (*RegistryConfig, error) { | ||
func newRegistryConfig( | ||
externalRegistryConfig ExternalRegistryConfig, | ||
pluginDependencies []bufremotepluginref.PluginReference, | ||
overrideRemote string, | ||
) (*RegistryConfig, error) { | ||
var ( | ||
isGoEmpty = externalRegistryConfig.Go == nil | ||
isNPMEmpty = externalRegistryConfig.NPM == nil | ||
|
@@ -125,7 +130,7 @@ func newRegistryConfig(externalRegistryConfig ExternalRegistryConfig) (*Registry | |
options := OptionsSliceToPluginOptions(externalRegistryConfig.Opts) | ||
switch { | ||
case !isGoEmpty: | ||
goRegistryConfig, err := newGoRegistryConfig(externalRegistryConfig.Go) | ||
goRegistryConfig, err := newGoRegistryConfig(externalRegistryConfig.Go, pluginDependencies, overrideRemote) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
@@ -242,14 +247,18 @@ func newNPMRegistryConfig(externalNPMRegistryConfig *ExternalNPMRegistryConfig) | |
}, nil | ||
} | ||
|
||
func newGoRegistryConfig(externalGoRegistryConfig *ExternalGoRegistryConfig) (*GoRegistryConfig, error) { | ||
func newGoRegistryConfig( | ||
externalGoRegistryConfig *ExternalGoRegistryConfig, | ||
pluginDependencies []bufremotepluginref.PluginReference, | ||
overrideRemote string, | ||
) (*GoRegistryConfig, error) { | ||
if externalGoRegistryConfig == nil { | ||
return nil, nil | ||
} | ||
if externalGoRegistryConfig.MinVersion != "" && !modfile.GoVersionRE.MatchString(externalGoRegistryConfig.MinVersion) { | ||
return nil, fmt.Errorf("the go minimum version %q must be a valid semantic version in the form of <major>.<minor>", externalGoRegistryConfig.MinVersion) | ||
} | ||
var dependencies []*GoRegistryDependencyConfig | ||
var runtimeDependencies []*GoRegistryDependencyConfig | ||
for _, dep := range externalGoRegistryConfig.Deps { | ||
if dep.Module == "" { | ||
return nil, errors.New("go runtime dependency requires a non-empty module name") | ||
|
@@ -260,17 +269,37 @@ func newGoRegistryConfig(externalGoRegistryConfig *ExternalGoRegistryConfig) (*G | |
if !semver.IsValid(dep.Version) { | ||
return nil, fmt.Errorf("go runtime dependency %s:%s does not have a valid semantic version", dep.Module, dep.Version) | ||
} | ||
dependencies = append( | ||
dependencies, | ||
runtimeDependencies = append( | ||
runtimeDependencies, | ||
&GoRegistryDependencyConfig{ | ||
Module: dep.Module, | ||
Version: dep.Version, | ||
}, | ||
) | ||
} | ||
var basePlugin bufremotepluginref.PluginIdentity | ||
if externalGoRegistryConfig.BasePlugin != "" { | ||
var err error | ||
basePlugin, err = pluginIdentityForStringWithOverrideRemote(externalGoRegistryConfig.BasePlugin, overrideRemote) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to parse base plugin: %w", err) | ||
} | ||
// Validate the base plugin is included as one of the plugin dependencies when both are | ||
// specified. This ensures there's exactly one base type and it has a known dependency to | ||
// generate imports correctly and build a correct Go mod file. | ||
if len(pluginDependencies) > 0 { | ||
ok := slices.ContainsFunc(pluginDependencies, func(ref bufremotepluginref.PluginReference) bool { | ||
return ref.IdentityString() == basePlugin.IdentityString() | ||
}) | ||
if !ok { | ||
return nil, fmt.Errorf("base plugin %q not found in plugin dependencies", externalGoRegistryConfig.BasePlugin) | ||
} | ||
} | ||
Comment on lines
+287
to
+297
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the intention that if there's no plugin dependency, assigning There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not quite, we still want to support option 1 in the description. Suppose you have a multi-plugin that slams ALL the generators into a single multi-plugin. In this scenario, there are no plugin deps (all plugins output to the same package), but we still need to generate the correct module imports. E.g., think of package petv1
import (
- v1alpha1 "remote/gen/go/acme/paymentapis/protocolbuffers/go/payment/v1alpha1"
+ v1alpha1 "remote/gen/go/acme/paymentapis/custom-plugins/go/payment/v1alpha1" Likewise, we need to build a module remote/gen/go/acme/petapis/custom-plugins/go
go 1.21
require (
- remote/gen/go/acme/paymentapis/protocolbuffers/go v0.2.0-20241204162507-076bca1034ed.1
+ remote/gen/go/acme/paymentapis/custom-plugins/go v0.2.0-20241204162507-076bca1034ed.1 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah, I see, was not thinking about the multi-plugin. That makes sense to me. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if the BSR should support option 1 with no further configuration. If there are no plugin dependencies, use the plugin name itself (instead of Effectively, the same as manually setting just so version: v1
name: remote/org/go-multi # --- SAME
plugin_version: v0.1.0
output_languages:
- go
registry:
go:
base_plugin: remote/org/go-multi # --- SAME
min_version: "1.21"
deps:
- module: google.golang.org/protobuf
version: v1.35.2
- module: github.com/planetscale/vtprotobuf
version: v0.6.0
# Add the options to invoke each plugin for the generated SDK.
opts:
- --go_out=.
- --go_opt=paths=source_relative
- --go-json_out=.
- --go-json_opt=paths=source_relative,emit_defaults=true
- --go-vtproto_out=.
- --go-vtproto_opt=paths=source_relative,features=marshal+unmarshal+size |
||
} | ||
return &GoRegistryConfig{ | ||
MinVersion: externalGoRegistryConfig.MinVersion, | ||
Deps: dependencies, | ||
Deps: runtimeDependencies, | ||
BasePlugin: basePlugin, | ||
}, nil | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An alternative way to configure this might be to expose an
is_base: true
withindeps
, e.g.,And allowing exactly one plugin within deps to have this be set to true.
However, I don't like this because it leaks a Generated SDK configuration beyond the registry config. (which at the moment is Go-specific).