Skip to content

Commit 933698d

Browse files
authored
feat: Support publishing plugins with team and kind metadata set (#1313)
This allows us to package the plugin with team and kind metadata set, needed for publishing to the new registry and checking usage and limits against the new cloud API. The package command no longer needs to take a `kind` argument. Also adds an `info` command to print this information, useful for debugging, otherwise it won't be visible anywhere. We can consider adding this to the `--version` output instead, but that would technically be breaking?
1 parent 16e36d6 commit 933698d

File tree

8 files changed

+122
-44
lines changed

8 files changed

+122
-44
lines changed

examples/simple_plugin/client/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,4 @@ func MultiplexBySpec(meta schema.ClientMeta) []schema.ClientMeta {
3535
func ResolveClientID(_ context.Context, meta schema.ClientMeta, resource *schema.Resource, c schema.Column) error {
3636
cl := meta.(*TestClient)
3737
return resource.Set(c.Name, cl.ClientID)
38-
}
38+
}

examples/simple_plugin/plugin/plugin.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,21 @@ import (
44
"github.com/cloudquery/plugin-sdk/v4/plugin"
55
)
66

7+
// These variables are used by the `package` command and for checking
8+
// usage limits.
79
var (
10+
Team = "example-team"
11+
Kind = "source"
12+
Name = "example"
813
Version = "development"
914
)
1015

1116
func Plugin() *plugin.Plugin {
1217
return plugin.NewPlugin(
13-
"test",
18+
Name,
1419
Version,
1520
Configure,
21+
plugin.WithKind(Kind),
22+
plugin.WithTeam(Team),
1623
)
17-
}
24+
}

plugin/options.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,23 @@ func WithStaticLinking() Option {
3535
}
3636
}
3737

38+
func WithKind(kind string) Option {
39+
k := Kind(kind)
40+
err := k.Validate()
41+
if err != nil {
42+
panic(err)
43+
}
44+
return func(p *Plugin) {
45+
p.kind = k
46+
}
47+
}
48+
49+
func WithTeam(team string) Option {
50+
return func(p *Plugin) {
51+
p.team = team
52+
}
53+
}
54+
3855
type TableOptions struct {
3956
Tables []string
4057
SkipTables []string

plugin/plugin.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,12 @@ func (UnimplementedSource) Tables(context.Context, TableOptions) (schema.Tables,
4949
// Plugin is the base structure required to pass to sdk.serve
5050
// We take a declarative approach to API here similar to Cobra
5151
type Plugin struct {
52-
// Name of plugin i.e aws, gcp, azure etc
52+
// Name of plugin, e.g. aws, gcp, azure etc
5353
name string
54+
// Kind of plugin, e.g. source, destination
55+
kind Kind
56+
// Team name of author of the plugin, e.g. cloudquery, vercel, github, etc
57+
team string
5458
// Version of the plugin
5559
version string
5660
// targets to build plugin for
@@ -100,11 +104,21 @@ func NewPlugin(name string, version string, newClient NewClientFunc, options ...
100104
return &p
101105
}
102106

103-
// Name return the name of this plugin
107+
// Name returns the name of this plugin
104108
func (p *Plugin) Name() string {
105109
return p.name
106110
}
107111

112+
// Kind returns the kind of this plugin
113+
func (p *Plugin) Kind() Kind {
114+
return p.kind
115+
}
116+
117+
// Team returns the name of the team that authored this plugin
118+
func (p *Plugin) Team() string {
119+
return p.team
120+
}
121+
108122
// Version returns the version of this plugin
109123
func (p *Plugin) Version() string {
110124
return p.version

serve/info.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package serve
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/spf13/cobra"
7+
)
8+
9+
const (
10+
pluginInfoShort = "Print build information about this plugin"
11+
pluginInfoLong = "Print build information about this plugin"
12+
)
13+
14+
func (s *PluginServe) newCmdPluginInfo() *cobra.Command {
15+
cmd := &cobra.Command{
16+
Use: "info",
17+
Short: pluginInfoShort,
18+
Long: pluginInfoLong,
19+
Args: cobra.ExactArgs(0),
20+
RunE: func(cmd *cobra.Command, _ []string) error {
21+
cmd.Println(fmt.Sprintf("Package and version: %s/%s/%s@%s", s.plugin.Team(), s.plugin.Kind(), s.plugin.Name(), s.plugin.Version()))
22+
return nil
23+
},
24+
}
25+
return cmd
26+
}

serve/package.go

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ This creates a directory with the plugin binaries, package.json and documentatio
3232
// to be able to package the plugin with all the needed metadata.
3333
type PackageJSON struct {
3434
SchemaVersion int `json:"schema_version"`
35-
Name string `json:"name"`
35+
Team string `json:"team"`
3636
Kind plugin.Kind `json:"kind"`
37+
Name string `json:"name"`
3738
Message string `json:"message"`
3839
Version string `json:"version"`
3940
Protocols []int `json:"protocols"`
@@ -103,8 +104,8 @@ func (s *PluginServe) writeTablesJSON(ctx context.Context, dir string) error {
103104
}
104105

105106
func (s *PluginServe) build(pluginDirectory, goos, goarch, distPath, pluginVersion string) (*TargetBuild, error) {
106-
pluginName := fmt.Sprintf("plugin-%s-%s-%s-%s", s.plugin.Name(), pluginVersion, goos, goarch)
107-
pluginPath := path.Join(distPath, pluginName)
107+
pluginFileName := fmt.Sprintf("plugin-%s-%s-%s-%s", s.plugin.Name(), pluginVersion, goos, goarch)
108+
pluginPath := path.Join(distPath, pluginFileName)
108109
importPath, err := s.getModuleName(pluginDirectory)
109110
if err != nil {
110111
return nil, err
@@ -144,7 +145,7 @@ func (s *PluginServe) build(pluginDirectory, goos, goarch, distPath, pluginVersi
144145
zipWriter := zip.NewWriter(zipPluginFile)
145146
defer zipWriter.Close()
146147

147-
pluginZip, err := zipWriter.Create(pluginName)
148+
pluginZip, err := zipWriter.Create(pluginFileName)
148149
if err != nil {
149150
zipWriter.Close()
150151
return nil, fmt.Errorf("failed to create file in zip archive: %w", err)
@@ -166,7 +167,7 @@ func (s *PluginServe) build(pluginDirectory, goos, goarch, distPath, pluginVersi
166167
return nil, fmt.Errorf("failed to remove plugin file: %w", err)
167168
}
168169

169-
targetZip := fmt.Sprintf(pluginName + ".zip")
170+
targetZip := fmt.Sprintf(pluginFileName + ".zip")
170171
checksum, err := calcChecksum(path.Join(distPath, targetZip))
171172
if err != nil {
172173
return nil, fmt.Errorf("failed to calculate checksum: %w", err)
@@ -211,13 +212,14 @@ func (*PluginServe) getModuleName(pluginDirectory string) (string, error) {
211212
return strings.TrimSpace(importPath), nil
212213
}
213214

214-
func (s *PluginServe) writePackageJSON(dir string, pluginKind plugin.Kind, pluginVersion, message string, targets []TargetBuild) error {
215+
func (s *PluginServe) writePackageJSON(dir, version, message string, targets []TargetBuild) error {
215216
packageJSON := PackageJSON{
216217
SchemaVersion: 1,
217218
Name: s.plugin.Name(),
218219
Message: message,
219-
Kind: pluginKind,
220-
Version: pluginVersion,
220+
Team: s.plugin.Team(),
221+
Kind: s.plugin.Kind(),
222+
Version: version,
221223
Protocols: s.versions,
222224
SupportedTargets: targets,
223225
PackageType: plugin.PackageTypeNative,
@@ -279,17 +281,13 @@ func copyFile(src, dst string) error {
279281

280282
func (s *PluginServe) newCmdPluginPackage() *cobra.Command {
281283
cmd := &cobra.Command{
282-
Use: "package -m <message> <source|destination> <version> <plugin_directory>",
284+
Use: "package -m <message> <version> <plugin_directory>",
283285
Short: pluginPackageShort,
284286
Long: pluginPackageLong,
285-
Args: cobra.ExactArgs(3),
287+
Args: cobra.ExactArgs(2),
286288
RunE: func(cmd *cobra.Command, args []string) error {
287-
pluginKind := plugin.Kind(args[0])
288-
if err := pluginKind.Validate(); err != nil {
289-
return err
290-
}
291-
pluginVersion := args[1]
292-
pluginDirectory := args[2]
289+
pluginVersion := args[0]
290+
pluginDirectory := args[1]
293291
distPath := path.Join(pluginDirectory, "dist")
294292
if cmd.Flag("dist-dir").Changed {
295293
distPath = cmd.Flag("dist-dir").Value.String()
@@ -317,7 +315,16 @@ func (s *PluginServe) newCmdPluginPackage() *cobra.Command {
317315
return err
318316
}
319317

320-
if pluginKind == plugin.KindSource {
318+
if s.plugin.Name() == "" {
319+
return fmt.Errorf("plugin name is required for packaging")
320+
}
321+
if s.plugin.Team() == "" {
322+
return fmt.Errorf("plugin team is required (hint: use the plugin.WithTeam() option")
323+
}
324+
if s.plugin.Kind() == "" {
325+
return fmt.Errorf("plugin kind is required (hint: use the plugin.WithKind() option")
326+
}
327+
if s.plugin.Kind() == plugin.KindSource {
321328
if err := s.plugin.Init(cmd.Context(), nil, plugin.NewClientOptions{
322329
NoConnection: true,
323330
}); err != nil {
@@ -327,6 +334,7 @@ func (s *PluginServe) newCmdPluginPackage() *cobra.Command {
327334
return err
328335
}
329336
}
337+
330338
targets := []TargetBuild{}
331339
for _, target := range s.plugin.Targets() {
332340
fmt.Println("Building for OS: " + target.OS + ", ARCH: " + target.Arch)
@@ -336,7 +344,7 @@ func (s *PluginServe) newCmdPluginPackage() *cobra.Command {
336344
}
337345
targets = append(targets, *targetBuild)
338346
}
339-
if err := s.writePackageJSON(distPath, pluginKind, pluginVersion, message, targets); err != nil {
347+
if err := s.writePackageJSON(distPath, pluginVersion, message, targets); err != nil {
340348
return fmt.Errorf("failed to write manifest: %w", err)
341349
}
342350
if err := s.copyDocs(distPath, docsPath); err != nil {

serve/package_test.go

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package serve
22

33
import (
44
"crypto/sha256"
5+
_ "embed"
56
"encoding/json"
67
"fmt"
78
"io"
@@ -10,8 +11,6 @@ import (
1011
"runtime"
1112
"testing"
1213

13-
_ "embed"
14-
1514
"github.com/cloudquery/plugin-sdk/v4/internal/memdb"
1615
"github.com/cloudquery/plugin-sdk/v4/plugin"
1716
"github.com/google/go-cmp/cmp"
@@ -30,13 +29,15 @@ func TestPluginPackage_Source(t *testing.T) {
3029
simplePluginPath := filepath.Join(dir, "examples/simple_plugin")
3130
packageVersion := "v1.2.3"
3231
p := plugin.NewPlugin(
33-
"testPlugin",
32+
"test-plugin",
3433
"development",
3534
memdb.NewMemDBClient,
3635
plugin.WithBuildTargets([]plugin.BuildTarget{
3736
{OS: plugin.GoOSLinux, Arch: plugin.GoArchAmd64},
3837
{OS: plugin.GoOSWindows, Arch: plugin.GoArchAmd64},
3938
}),
39+
plugin.WithKind("source"),
40+
plugin.WithTeam("test-team"),
4041
)
4142
msg := `Test message
4243
with multiple lines and **markdown**`
@@ -60,7 +61,7 @@ with multiple lines and **markdown**`
6061
srv := Plugin(p)
6162
cmd := srv.newCmdPluginRoot()
6263
distDir := t.TempDir()
63-
cmd.SetArgs([]string{"package", "--dist-dir", distDir, "-m", tc.message, "source", packageVersion, simplePluginPath})
64+
cmd.SetArgs([]string{"package", "--dist-dir", distDir, "-m", tc.message, packageVersion, simplePluginPath})
6465
err := cmd.Execute()
6566
if tc.wantErr && err == nil {
6667
t.Fatalf("expected error, got nil")
@@ -74,30 +75,31 @@ with multiple lines and **markdown**`
7475
expect := []string{
7576
"docs",
7677
"package.json",
77-
"plugin-testPlugin-v1.2.3-linux-amd64.zip",
78-
"plugin-testPlugin-v1.2.3-windows-amd64.zip",
78+
"plugin-test-plugin-v1.2.3-linux-amd64.zip",
79+
"plugin-test-plugin-v1.2.3-windows-amd64.zip",
7980
"tables.json",
8081
}
8182
if diff := cmp.Diff(expect, fileNames(files)); diff != "" {
8283
t.Fatalf("unexpected files in dist directory (-want +got):\n%s", diff)
8384
}
8485
// expect SHA-256 for the zip files to differ
85-
sha1 := sha256sum(filepath.Join(distDir, "plugin-testPlugin-v1.2.3-linux-amd64.zip"))
86-
sha2 := sha256sum(filepath.Join(distDir, "plugin-testPlugin-v1.2.3-windows-amd64.zip"))
86+
sha1 := sha256sum(filepath.Join(distDir, "plugin-test-plugin-v1.2.3-linux-amd64.zip"))
87+
sha2 := sha256sum(filepath.Join(distDir, "plugin-test-plugin-v1.2.3-windows-amd64.zip"))
8788
if sha1 == sha2 {
8889
t.Fatalf("expected SHA-256 for linux and windows zip files to differ, but they are the same: %s", sha1)
8990
}
9091

9192
expectPackage := PackageJSON{
9293
SchemaVersion: 1,
93-
Name: "testPlugin",
94+
Name: "test-plugin",
95+
Team: "test-team",
9496
Kind: "source",
9597
Message: msg,
96-
Version: "v1.2.3",
98+
Version: packageVersion,
9799
Protocols: []int{3},
98100
SupportedTargets: []TargetBuild{
99-
{OS: plugin.GoOSLinux, Arch: plugin.GoArchAmd64, Path: "plugin-testPlugin-v1.2.3-linux-amd64.zip", Checksum: "sha256:" + sha256sum(filepath.Join(distDir, "plugin-testPlugin-v1.2.3-linux-amd64.zip"))},
100-
{OS: plugin.GoOSWindows, Arch: plugin.GoArchAmd64, Path: "plugin-testPlugin-v1.2.3-windows-amd64.zip", Checksum: "sha256:" + sha256sum(filepath.Join(distDir, "plugin-testPlugin-v1.2.3-windows-amd64.zip"))},
101+
{OS: plugin.GoOSLinux, Arch: plugin.GoArchAmd64, Path: "plugin-test-plugin-v1.2.3-linux-amd64.zip", Checksum: "sha256:" + sha256sum(filepath.Join(distDir, "plugin-test-plugin-v1.2.3-linux-amd64.zip"))},
102+
{OS: plugin.GoOSWindows, Arch: plugin.GoArchAmd64, Path: "plugin-test-plugin-v1.2.3-windows-amd64.zip", Checksum: "sha256:" + sha256sum(filepath.Join(distDir, "plugin-test-plugin-v1.2.3-windows-amd64.zip"))},
101103
},
102104
PackageType: plugin.PackageTypeNative,
103105
}
@@ -122,13 +124,15 @@ func TestPluginPackage_Destination(t *testing.T) {
122124
simplePluginPath := filepath.Join(dir, "examples/simple_plugin")
123125
packageVersion := "v1.2.3"
124126
p := plugin.NewPlugin(
125-
"testPlugin",
127+
"test-plugin",
126128
"development",
127129
memdb.NewMemDBClient,
128130
plugin.WithBuildTargets([]plugin.BuildTarget{
129131
{OS: plugin.GoOSWindows, Arch: plugin.GoArchAmd64},
130132
{OS: plugin.GoOSDarwin, Arch: plugin.GoArchAmd64},
131133
}),
134+
plugin.WithKind("destination"),
135+
plugin.WithTeam("test-team"),
132136
)
133137
msg := `Test message
134138
with multiple lines and **markdown**`
@@ -152,7 +156,7 @@ with multiple lines and **markdown**`
152156
srv := Plugin(p)
153157
cmd := srv.newCmdPluginRoot()
154158
distDir := t.TempDir()
155-
cmd.SetArgs([]string{"package", "--dist-dir", distDir, "-m", tc.message, "destination", packageVersion, simplePluginPath})
159+
cmd.SetArgs([]string{"package", "--dist-dir", distDir, "-m", tc.message, packageVersion, simplePluginPath})
156160
err := cmd.Execute()
157161
if tc.wantErr && err == nil {
158162
t.Fatalf("expected error, got nil")
@@ -166,29 +170,30 @@ with multiple lines and **markdown**`
166170
expect := []string{
167171
"docs",
168172
"package.json",
169-
"plugin-testPlugin-v1.2.3-darwin-amd64.zip",
170-
"plugin-testPlugin-v1.2.3-windows-amd64.zip",
173+
"plugin-test-plugin-v1.2.3-darwin-amd64.zip",
174+
"plugin-test-plugin-v1.2.3-windows-amd64.zip",
171175
}
172176
if diff := cmp.Diff(expect, fileNames(files)); diff != "" {
173177
t.Fatalf("unexpected files in dist directory (-want +got):\n%s", diff)
174178
}
175179
// expect SHA-256 for the zip files to differ
176-
sha1 := sha256sum(filepath.Join(distDir, "plugin-testPlugin-v1.2.3-windows-amd64.zip"))
177-
sha2 := sha256sum(filepath.Join(distDir, "plugin-testPlugin-v1.2.3-darwin-amd64.zip"))
180+
sha1 := sha256sum(filepath.Join(distDir, "plugin-test-plugin-v1.2.3-windows-amd64.zip"))
181+
sha2 := sha256sum(filepath.Join(distDir, "plugin-test-plugin-v1.2.3-darwin-amd64.zip"))
178182
if sha1 == sha2 {
179183
t.Fatalf("expected SHA-256 for windows and darwin zip files to differ, but they are the same: %s", sha1)
180184
}
181185

182186
expectPackage := PackageJSON{
183187
SchemaVersion: 1,
184-
Name: "testPlugin",
188+
Team: "test-team",
185189
Kind: "destination",
190+
Name: "test-plugin",
186191
Message: msg,
187192
Version: "v1.2.3",
188193
Protocols: []int{3},
189194
SupportedTargets: []TargetBuild{
190-
{OS: plugin.GoOSWindows, Arch: plugin.GoArchAmd64, Path: "plugin-testPlugin-v1.2.3-windows-amd64.zip", Checksum: "sha256:" + sha256sum(filepath.Join(distDir, "plugin-testPlugin-v1.2.3-windows-amd64.zip"))},
191-
{OS: plugin.GoOSDarwin, Arch: plugin.GoArchAmd64, Path: "plugin-testPlugin-v1.2.3-darwin-amd64.zip", Checksum: "sha256:" + sha256sum(filepath.Join(distDir, "plugin-testPlugin-v1.2.3-darwin-amd64.zip"))},
195+
{OS: plugin.GoOSWindows, Arch: plugin.GoArchAmd64, Path: "plugin-test-plugin-v1.2.3-windows-amd64.zip", Checksum: "sha256:" + sha256sum(filepath.Join(distDir, "plugin-test-plugin-v1.2.3-windows-amd64.zip"))},
196+
{OS: plugin.GoOSDarwin, Arch: plugin.GoArchAmd64, Path: "plugin-test-plugin-v1.2.3-darwin-amd64.zip", Checksum: "sha256:" + sha256sum(filepath.Join(distDir, "plugin-test-plugin-v1.2.3-darwin-amd64.zip"))},
192197
},
193198
PackageType: plugin.PackageTypeNative,
194199
}

serve/plugin.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ func (s *PluginServe) newCmdPluginRoot() *cobra.Command {
292292
cmd.AddCommand(s.newCmdPluginServe())
293293
cmd.AddCommand(s.newCmdPluginDoc())
294294
cmd.AddCommand(s.newCmdPluginPackage())
295+
cmd.AddCommand(s.newCmdPluginInfo())
295296
cmd.CompletionOptions.DisableDefaultCmd = true
296297
cmd.Version = s.plugin.Version()
297298
return cmd

0 commit comments

Comments
 (0)