Skip to content

Commit 381726a

Browse files
authored
Implement buf registry sdk info command (#3862)
1 parent 05019d4 commit 381726a

File tree

6 files changed

+358
-0
lines changed

6 files changed

+358
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- Promote `buf beta stats` to `buf stats`.
66
- Update built-in Well-Known Types to Protobuf v31.1.
7+
- Add `buf registry sdk info` command.
78

89
## [v1.54.0] - 2025-05-12
910

private/buf/bufprint/bufprint.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,16 @@ func NewStatsPrinter(writer io.Writer) StatsPrinter {
322322
return newStatsPrinter(writer)
323323
}
324324

325+
// SDKInfoPrinter is a printer for SDK info.
326+
type SDKInfoPrinter interface {
327+
PrintSDKInfo(ctx context.Context, format Format, sdkInfo *registryv1alpha1.GetSDKInfoResponse) error
328+
}
329+
330+
// NewSDKInfoPrinter returns a new SDKInfoPrinter.
331+
func NewSDKInfoPrinter(writer io.Writer) SDKInfoPrinter {
332+
return newSDKInfoPrinter(writer)
333+
}
334+
325335
// TabWriter is a tab writer.
326336
type TabWriter interface {
327337
Write(values ...string) error
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// Copyright 2020-2025 Buf Technologies, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package bufprint
16+
17+
import (
18+
"context"
19+
"encoding/json"
20+
"fmt"
21+
"io"
22+
"time"
23+
24+
registryv1alpha1 "github.com/bufbuild/buf/private/gen/proto/go/buf/alpha/registry/v1alpha1"
25+
)
26+
27+
type sdkInfoPrinter struct {
28+
writer io.Writer
29+
}
30+
31+
func newSDKInfoPrinter(writer io.Writer) *sdkInfoPrinter {
32+
return &sdkInfoPrinter{
33+
writer: writer,
34+
}
35+
}
36+
37+
func (p *sdkInfoPrinter) PrintSDKInfo(
38+
ctx context.Context,
39+
format Format,
40+
sdkInfo *registryv1alpha1.GetSDKInfoResponse,
41+
) error {
42+
output := newOutputSDKInfo(sdkInfo)
43+
switch format {
44+
case FormatText:
45+
if _, err := fmt.Fprintf(
46+
p.writer,
47+
`Module
48+
Owner: %s
49+
Name: %s
50+
Commit: %s
51+
52+
`,
53+
output.ModuleInfo.Owner,
54+
output.ModuleInfo.Name,
55+
output.ModuleInfo.Commit,
56+
); err != nil {
57+
return err
58+
}
59+
if _, err := fmt.Fprintf(
60+
p.writer,
61+
`Plugin
62+
Owner: %s
63+
Name: %s
64+
Version: %s
65+
Revision: %d
66+
67+
`,
68+
output.PluginInfo.Owner,
69+
output.PluginInfo.Name,
70+
output.PluginInfo.Version,
71+
output.PluginInfo.Revision,
72+
); err != nil {
73+
return err
74+
}
75+
_, err := fmt.Fprintf(p.writer, "Version: %s\n", output.Version)
76+
return err
77+
case FormatJSON:
78+
return json.NewEncoder(p.writer).Encode(output)
79+
default:
80+
return fmt.Errorf("unknown format: %v", format)
81+
}
82+
}
83+
84+
type outputSDKInfo struct {
85+
ModuleInfo outputSDKModuleInfo `json:"module,omitempty"`
86+
PluginInfo outputSDKPluginInfo `json:"plugin,omitempty"`
87+
Version string `json:"version,omitempty"`
88+
}
89+
90+
type outputSDKModuleInfo struct {
91+
Owner string `json:"owner,omitempty"`
92+
Name string `json:"name,omitempty"`
93+
Commit string `json:"commit,omitempty"`
94+
CommitCreateTime time.Time `json:"commit_create_time,omitempty"`
95+
}
96+
97+
type outputSDKPluginInfo struct {
98+
Owner string `json:"owner,omitempty"`
99+
Name string `json:"name,omitempty"`
100+
Version string `json:"version,omitempty"`
101+
Revision uint32 `json:"revision,omitempty"`
102+
}
103+
104+
func newOutputSDKInfo(sdkInfo *registryv1alpha1.GetSDKInfoResponse) outputSDKInfo {
105+
return outputSDKInfo{
106+
ModuleInfo: outputSDKModuleInfo{
107+
Owner: sdkInfo.GetModuleInfo().GetOwner(),
108+
Name: sdkInfo.GetModuleInfo().GetName(),
109+
Commit: sdkInfo.GetModuleInfo().GetCommit(),
110+
CommitCreateTime: sdkInfo.GetModuleInfo().GetModuleCommitCreateTime().AsTime(),
111+
},
112+
PluginInfo: outputSDKPluginInfo{
113+
Owner: sdkInfo.GetPluginInfo().GetOwner(),
114+
Name: sdkInfo.GetPluginInfo().GetName(),
115+
Version: sdkInfo.GetPluginInfo().GetVersion(),
116+
Revision: sdkInfo.GetPluginInfo().GetPluginRevision(),
117+
},
118+
Version: sdkInfo.GetSdkVersion(),
119+
}
120+
}

private/buf/cmd/buf/buf.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ import (
101101
"github.com/bufbuild/buf/private/buf/cmd/buf/command/registry/registrycc"
102102
"github.com/bufbuild/buf/private/buf/cmd/buf/command/registry/registrylogin"
103103
"github.com/bufbuild/buf/private/buf/cmd/buf/command/registry/registrylogout"
104+
"github.com/bufbuild/buf/private/buf/cmd/buf/command/registry/sdk/sdkinfo"
104105
"github.com/bufbuild/buf/private/buf/cmd/buf/command/registry/sdk/version"
105106
"github.com/bufbuild/buf/private/buf/cmd/buf/command/registry/whoami"
106107
"github.com/bufbuild/buf/private/buf/cmd/buf/command/stats"
@@ -221,6 +222,7 @@ func NewRootCommand(name string) *appcmd.Command {
221222
Short: "Manage Generated SDKs",
222223
SubCommands: []*appcmd.Command{
223224
version.NewCommand("version", builder),
225+
sdkinfo.NewCommand("info", builder),
224226
},
225227
},
226228
{
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
// Copyright 2020-2025 Buf Technologies, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package sdkinfo
16+
17+
import (
18+
"context"
19+
"fmt"
20+
21+
"buf.build/go/app/appcmd"
22+
"buf.build/go/app/appext"
23+
"connectrpc.com/connect"
24+
"github.com/bufbuild/buf/private/buf/bufcli"
25+
"github.com/bufbuild/buf/private/buf/bufprint"
26+
"github.com/bufbuild/buf/private/bufpkg/bufparse"
27+
"github.com/bufbuild/buf/private/bufpkg/bufremoteplugin/bufremotepluginref"
28+
"github.com/bufbuild/buf/private/gen/proto/connect/buf/alpha/registry/v1alpha1/registryv1alpha1connect"
29+
registryv1alpha1 "github.com/bufbuild/buf/private/gen/proto/go/buf/alpha/registry/v1alpha1"
30+
"github.com/bufbuild/buf/private/pkg/connectclient"
31+
"github.com/spf13/pflag"
32+
)
33+
34+
const (
35+
formatFlagName = "format"
36+
moduleFlagName = "module"
37+
pluginFlagName = "plugin"
38+
versionFlagName = "version"
39+
)
40+
41+
// NewCommand returns a new Command.
42+
func NewCommand(
43+
name string,
44+
builder appext.SubCommandBuilder,
45+
) *appcmd.Command {
46+
flags := newFlags()
47+
return &appcmd.Command{
48+
Use: name + " --module=<remote/owner/repository[:ref]> --plugin=<remote/owner/plugin[:version]>",
49+
Short: "Get SDK information for the given module, plugin, and optionally version.",
50+
Long: `This command returns the version information for a Generated SDK based on the specified information.
51+
In order to resolve the SDK information, a module and plugin must be specified.
52+
53+
Examples:
54+
55+
To get the SDK information for the latest commit of a module and latest version of a plugin, you only need to specify the module and plugin.
56+
The following will resolve the SDK information for the latest commit of the connectrpc/eliza module and the latest version of the bufbuild/es plugin:
57+
58+
$ buf registry sdk info --module=buf.build/connectrpc/eliza --plugin=buf.build/connectrpc/es
59+
Module
60+
Owner: connectrpc
61+
Name: eliza
62+
Commit: <latest commit on default label>
63+
64+
Plugin
65+
Owner: bufbuild
66+
Name: es
67+
Version: <latest version of plugin>
68+
Revision: <latest revision of the plugin version>
69+
70+
Version: <SDK version for the resolved module commit and plugin version>
71+
72+
To get the SDK information for a specific commit of a module and/or a specific version of a plugin, you can specify the commit with the module and/or the version with the plugin.
73+
The following will resolve the SDK information for the specified commit of the connectrpc/eliza module and specified version of the bufbuild/es plugin:
74+
75+
$ buf registry sdk info --module=buf.build/connectrpc/eliza:d8fbf2620c604277a0ece1ff3a26f2ff --plugin=buf.build/bufbuild/es:v1.2.1
76+
Module
77+
Owner: connectrpc
78+
Name: eliza
79+
Commit: d8fbf2620c604277a0ece1ff3a26f2ff
80+
81+
Plugin
82+
Owner: bufbuild
83+
Name: es
84+
Version: v1.2.1
85+
Revision: 1
86+
87+
Version: 1.2.1-20230727062025-d8fbf2620c60.1
88+
89+
If you have a SDK version and want to know the corresponding module commit and plugin version information for the SDK, you can specify the module and plugin with the version string.
90+
The following will resolve the SDK information for the specified SDK version of the connectrpc/eliza module and bufbuild/es plugin.
91+
92+
$ buf registry sdk --module=buf.build/connectrpc/eliza --plugin=buf.build/bufbuild/es --version=1.2.1-20230727062025-d8fbf2620c60.1
93+
Module
94+
Owner: connectrpc
95+
Name: eliza
96+
Commit: d8fbf2620c604277a0ece1ff3a26f2ff
97+
98+
Plugin
99+
Owner: bufbuild
100+
Name: es
101+
Version: v1.2.1
102+
Revision: 1
103+
104+
Version: 1.2.1-20230727062025-d8fbf2620c60.1
105+
106+
The module commit and plugin version information are resolved based on the specified SDK version string.
107+
108+
If a module reference and/or plugin version are specified along with the SDK version, then the SDK version will be validated against the specified module reference and/or plugin version.
109+
If there is a mismatch, this command will error.
110+
111+
$ buf registry sdk info \
112+
--module=buf.build/connectrpc/eliza:8b8b971d6fde4dc8ba5d96f9fda7d53c \
113+
--plugin=buf.build/bufbuild/es \
114+
--version=1.2.1-20230727062025-d8fbf2620c60.1
115+
Failure: invalid_argument: invalid SDK version v1.2.1-20230727062025-d8fbf2620c60.1 with module short commit d8fbf2620c60 for resolved module reference connectrpc/eliza:8b8b971d6fde4dc8ba5d96f9fda7d53c
116+
117+
In this case, the SDK version provided resolves to a different commit than the commit provided for the module.`,
118+
Args: appcmd.NoArgs,
119+
Run: builder.NewRunFunc(
120+
func(ctx context.Context, container appext.Container) error {
121+
return run(ctx, container, flags)
122+
},
123+
),
124+
BindFlags: flags.Bind,
125+
}
126+
}
127+
128+
type flags struct {
129+
Format string
130+
Module string
131+
Plugin string
132+
Version string
133+
}
134+
135+
func newFlags() *flags {
136+
return &flags{}
137+
}
138+
139+
func (f *flags) Bind(flagSet *pflag.FlagSet) {
140+
flagSet.StringVar(
141+
&f.Format,
142+
formatFlagName,
143+
bufprint.FormatText.String(),
144+
fmt.Sprintf("The output format to use. Must be one of %s", bufprint.AllFormatsString),
145+
)
146+
flagSet.StringVar(&f.Module, moduleFlagName, "", "The module reference for the SDK.")
147+
flagSet.StringVar(&f.Plugin, pluginFlagName, "", "The plugin reference for the SDK.")
148+
flagSet.StringVar(&f.Version, versionFlagName, "", "The version of the SDK.")
149+
_ = appcmd.MarkFlagRequired(flagSet, moduleFlagName)
150+
_ = appcmd.MarkFlagRequired(flagSet, pluginFlagName)
151+
}
152+
153+
func run(
154+
ctx context.Context,
155+
container appext.Container,
156+
flags *flags,
157+
) error {
158+
moduleRef, err := bufparse.ParseRef(flags.Module)
159+
if err != nil {
160+
return appcmd.WrapInvalidArgumentError(err)
161+
}
162+
pluginIdentity, pluginVersion, err := bufremotepluginref.ParsePluginIdentityOptionalVersion(flags.Plugin)
163+
if err != nil {
164+
return appcmd.WrapInvalidArgumentError(err)
165+
}
166+
if moduleRef.FullName().Registry() != pluginIdentity.Remote() {
167+
return appcmd.NewInvalidArgumentError("module and plugin must be from the same BSR instance")
168+
}
169+
clientConfig, err := bufcli.NewConnectClientConfig(container)
170+
if err != nil {
171+
return err
172+
}
173+
resolveServiceClient := connectclient.Make(
174+
clientConfig,
175+
moduleRef.FullName().Registry(),
176+
registryv1alpha1connect.NewResolveServiceClient,
177+
)
178+
res, err := resolveServiceClient.GetSDKInfo(
179+
ctx,
180+
connect.NewRequest(registryv1alpha1.GetSDKInfoRequest_builder{
181+
ModuleReference: registryv1alpha1.LocalModuleReference_builder{
182+
Owner: moduleRef.FullName().Owner(),
183+
Repository: moduleRef.FullName().Name(),
184+
Reference: moduleRef.Ref(),
185+
}.Build(),
186+
PluginReference: registryv1alpha1.GetRemotePackageVersionPlugin_builder{
187+
Owner: pluginIdentity.Owner(),
188+
Name: pluginIdentity.Plugin(),
189+
Version: pluginVersion,
190+
}.Build(),
191+
SdkVersion: flags.Version,
192+
}.Build()),
193+
)
194+
if err != nil {
195+
return err
196+
}
197+
format, err := bufprint.ParseFormat(flags.Format)
198+
if err != nil {
199+
return err
200+
}
201+
return bufprint.NewSDKInfoPrinter(container.Stdout()).PrintSDKInfo(
202+
ctx,
203+
format,
204+
res.Msg,
205+
)
206+
}

private/buf/cmd/buf/command/registry/sdk/sdkinfo/usage.gen.go

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)