From 30cc4dc0c9d568a4ce15141b323e98114def8459 Mon Sep 17 00:00:00 2001 From: Iain McGinniss <309153+iainmcgin@users.noreply.github.com> Date: Mon, 24 Feb 2025 14:53:44 -0800 Subject: [PATCH] Clarify error message for missing plugin versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using buf generate, provide specific error messages based on whether the plugin is unknown, or if it is just the version that is unknown. 1. For a valid plugin with non-existent version (e.g., v0.4.1 instead of v0.4.0): "unknown version v0.4.1 for plugin community/neoeinstein-prost. The latest version is v0.4.0. Check https://buf.build/community/neoeinstein-prost for other available versions" 2. For a completely non-existent plugin (e.g., a typo like "proust" instead of "prost"): "unknown plugin community/neoeinstein-proust, check https://buf.build/plugins for the list of available plugins" This provides more clarity to users about whether they need to fix a version number or if they have a completely incorrect plugin reference. Fixes #3650 🤖 Generated with [Claude Code](https://docs.anthropic.com/s/claude-code) Co-Authored-By: Claude --- private/buf/bufgen/generator.go | 63 ++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/private/buf/bufgen/generator.go b/private/buf/bufgen/generator.go index 34802ab484..c7755d04fa 100644 --- a/private/buf/bufgen/generator.go +++ b/private/buf/bufgen/generator.go @@ -20,12 +20,14 @@ import ( "fmt" "log/slog" "path/filepath" + "regexp" connect "connectrpc.com/connect" "github.com/bufbuild/buf/private/buf/bufprotopluginexec" "github.com/bufbuild/buf/private/bufpkg/bufconfig" "github.com/bufbuild/buf/private/bufpkg/bufimage" "github.com/bufbuild/buf/private/bufpkg/bufimage/bufimagemodify" + "github.com/bufbuild/buf/private/bufpkg/bufparse" "github.com/bufbuild/buf/private/bufpkg/bufprotoplugin" "github.com/bufbuild/buf/private/bufpkg/bufprotoplugin/bufprotopluginos" "github.com/bufbuild/buf/private/bufpkg/bufremoteplugin" @@ -392,7 +394,7 @@ func (g *generator) execRemotePluginsV2( ), ) if err != nil { - return nil, err + return nil, g.enrichPluginResolutionError(ctx, err, requests, remote) } responses := response.Msg.GetResponses() if len(responses) != len(requests) { @@ -412,6 +414,65 @@ func (g *generator) execRemotePluginsV2( return result, nil } +func (g *generator) enrichPluginResolutionError(ctx context.Context, err error, requests []*registryv1alpha1.PluginGenerationRequest, remote string) error { + var connectErr *connect.Error + if !errors.As(err, &connectErr) && connectErr.Code() != connect.CodeNotFound { + // we only attempt to enrich NotFound errors + return err + } + + // the error message for NotFound errors is expected to contain the specific failing plugin reference + // unfortunately we have to parse this out, it is not provided in a more direct form + pluginNotFoundErrorRegexp := regexp.MustCompile(`^plugin "([^"]*)" was not found$`) + matched := pluginNotFoundErrorRegexp.FindStringSubmatch(connectErr.Message()) + if len(matched) <= 1 { + return err + } + + pluginRefStr := matched[1] + pluginRef, parseErr := bufparse.ParseRef(pluginRefStr) + if parseErr != nil { + // we can't parse the plugin ref, so just return the original error + return err + } + + repository := pluginRef.FullName().Registry() + owner := pluginRef.FullName().Owner() + name := pluginRef.FullName().Name() + + // find the matching plugin in the request to determine what version was used + var missingVersion string = "" + for _, x := range requests { + if x.GetPluginReference().GetOwner() == owner && x.GetPluginReference().GetName() == name { + missingVersion = x.GetPluginReference().GetVersion() + } + } + + if missingVersion == "" { + // no match in the requests, so just return the original error + return err + } + + curationClientService := connectclient.Make(g.clientConfig, remote, registryv1alpha1connect.NewPluginCurationServiceClient) + + response, requestErr := curationClientService.GetLatestCuratedPlugin(ctx, connect.NewRequest( + registryv1alpha1.GetLatestCuratedPluginRequest_builder{ + Owner: owner, + Name: name, + }.Build(), + )) + + if requestErr != nil { + // failed to get plugin information from the plugin curation service, so this plugin must not exist + return fmt.Errorf("unknown plugin %s/%s, check https://%s/plugins for the list of available plugins", owner, name, repository) + } + + plugin := response.Msg.GetPlugin() + latestVersion := plugin.GetVersion() + + return fmt.Errorf("unknown version %[1]s for plugin %[2]s/%[3]s. The latest version is %[4]s. Check https://%[5]s/%[2]s/%[3]s for other available versions", missingVersion, owner, name, latestVersion, repository) +} + func getPluginGenerationRequest( pluginConfig bufconfig.GeneratePluginConfig, includeImports bool,