diff --git a/.github/workflows/lint_markdown.yml b/.github/workflows/lint_markdown.yml index aabcd9530d..d4c2d29782 100644 --- a/.github/workflows/lint_markdown.yml +++ b/.github/workflows/lint_markdown.yml @@ -16,7 +16,7 @@ jobs: - name: Vale uses: errata-ai/vale-action@v2 with: - vale_flags: "--glob=!{docs/testdata/*,CHANGELOG.md,.github/styles/proselint/README.md}" + vale_flags: "--glob=!{docs/testdata/*,CHANGELOG.md,.github/styles/proselint/README.md,examples/simple_plugin/docs/*.md}" filter_mode: nofilter env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} @@ -31,4 +31,4 @@ jobs: with: files: . config_file: .markdownlint.yaml - ignore_files: "{docs/testdata/*,CHANGELOG.md}" + ignore_files: "{docs/testdata/*,CHANGELOG.md,examples/simple_plugin/docs/*.md}" diff --git a/examples/simple_plugin/docs/configuration.md b/examples/simple_plugin/docs/configuration.md new file mode 100644 index 0000000000..95a0a9698f --- /dev/null +++ b/examples/simple_plugin/docs/configuration.md @@ -0,0 +1,9 @@ +--- +title: Configuration +slug: configuration +description: How to configure the simple_plugin example. +--- + +# Configuration + +This plugin requires no configuration. \ No newline at end of file diff --git a/examples/simple_plugin/docs/overview.md b/examples/simple_plugin/docs/overview.md new file mode 100644 index 0000000000..d5ffcbb3cf --- /dev/null +++ b/examples/simple_plugin/docs/overview.md @@ -0,0 +1,9 @@ +--- +title: Overview +slug: overview +description: Overview of the simple_plugin example. +--- + +# Overview + +This is a simple example of a plugin that is used to test the SDK. diff --git a/plugin/options.go b/plugin/options.go index 7db78df8f9..12e1500558 100644 --- a/plugin/options.go +++ b/plugin/options.go @@ -17,21 +17,9 @@ func (m MigrateMode) String() string { type Option func(*Plugin) -func WithTitle(title string) Option { +func WithBuildTargets(targets []BuildTarget) Option { return func(p *Plugin) { - p.title = title - } -} - -func WithDescription(description string) Option { - return func(p *Plugin) { - p.description = description - } -} - -func WithShortDescription(shortDescription string) Option { - return func(p *Plugin) { - p.shortDescription = shortDescription + p.targets = targets } } diff --git a/plugin/plugin.go b/plugin/plugin.go index 5d2a805711..6f3374dad3 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -47,18 +47,10 @@ func (UnimplementedSource) Tables(context.Context, TableOptions) (schema.Tables, // Plugin is the base structure required to pass to sdk.serve // We take a declarative approach to API here similar to Cobra type Plugin struct { - // Name of plugin i.e aws,gcp, azure etc' + // Name of plugin i.e aws, gcp, azure etc name string // Version of the plugin version string - // Title of the plugin as appears in CloudQuery registry - title string - // Short description of the plugin as appears in CloudQuery registry - shortDescription string - // Long description of the plugin as appears in CloudQuery registry - description string - // categories of the plugin as appears in CloudQuery registry - categories []string // targets to build plugin for targets []BuildTarget // Called upon init call to validate and init configuration @@ -84,9 +76,7 @@ func NewPlugin(name string, version string, newClient NewClientFunc, options ... version: version, internalColumns: true, newClient: newClient, - title: name, - categories: []string{}, - targets: buildTargets, + targets: DefaultBuildTargets, } for _, opt := range options { opt(&p) @@ -104,22 +94,6 @@ func (p *Plugin) Version() string { return p.version } -func (p *Plugin) Title() string { - return p.title -} - -func (p *Plugin) Description() string { - return p.description -} - -func (p *Plugin) ShortDescription() string { - return p.shortDescription -} - -func (p *Plugin) Categories() []string { - return p.categories -} - func (p *Plugin) Targets() []BuildTarget { return p.targets } diff --git a/plugin/plugin_package.go b/plugin/plugin_package.go new file mode 100644 index 0000000000..d4f43f4b07 --- /dev/null +++ b/plugin/plugin_package.go @@ -0,0 +1,28 @@ +package plugin + +const ( + GoOSLinux = "linux" + GoOSWindows = "windows" + GoOSDarwin = "darwin" + + GoArchAmd64 = "amd64" + GoArchArm64 = "arm64" +) + +type PackageType string + +const ( + PackageTypeNative PackageType = "native" +) + +type BuildTarget struct { + OS string `json:"os"` + Arch string `json:"arch"` +} + +var DefaultBuildTargets = []BuildTarget{ + {GoOSLinux, GoArchAmd64}, + {GoOSWindows, GoArchAmd64}, + {GoOSDarwin, GoArchAmd64}, + {GoOSDarwin, GoArchArm64}, +} diff --git a/plugin/plugin_publish.go b/plugin/plugin_publish.go deleted file mode 100644 index 17886c2395..0000000000 --- a/plugin/plugin_publish.go +++ /dev/null @@ -1,29 +0,0 @@ -package plugin - -const ( - GoOslinux = "linux" - GoOswindows = "windows" - GoOsDarwin = "darwin" - - GoArchAmd64 = "amd64" - GoArchArm64 = "arm64" -) - -type PackageType string - -const ( - PackageTypeNative PackageType = "native" - PackageTypeDocker PackageType = "docker" -) - -type BuildTarget struct { - OS string `json:"os"` - Arch string `json:"arch"` -} - -var buildTargets = []BuildTarget{ - {GoOslinux, GoArchAmd64}, - {GoOswindows, GoArchAmd64}, - {GoOsDarwin, GoArchAmd64}, - {GoOsDarwin, GoArchArm64}, -} diff --git a/serve/package.go b/serve/package.go new file mode 100644 index 0000000000..495b7a43f6 --- /dev/null +++ b/serve/package.go @@ -0,0 +1,251 @@ +package serve + +import ( + "archive/zip" + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "os" + "os/exec" + "path" + "path/filepath" + "regexp" + "strings" + + "github.com/cloudquery/plugin-sdk/v4/plugin" + "github.com/spf13/cobra" +) + +const ( + pluginPackageShort = "Package plugin for publishing to CloudQuery registry." + pluginPackageLong = `Package plugin for publishing to CloudQuery registry. + +This creates a directory with the plugin binaries, package.json and documentation. +` +) + +// PackageJSON is the package.json file inside the dist directory. It is used by the CloudQuery package command +// to be able to package the plugin with all the needed metadata. +type PackageJSON struct { + Name string `json:"name"` + Version string `json:"version"` + Protocols []int `json:"protocols"` + SupportedTargets []TargetBuild `json:"supported_targets"` + PackageType plugin.PackageType `json:"package_type"` +} + +type TargetBuild struct { + OS string `json:"os"` + Arch string `json:"arch"` + Path string `json:"path"` +} + +func (s *PluginServe) writeTablesJSON(ctx context.Context, dir string) error { + tables, err := s.plugin.Tables(ctx, plugin.TableOptions{ + Tables: []string{"*"}, + }) + if err != nil { + return err + } + buffer := &bytes.Buffer{} + m := json.NewEncoder(buffer) + m.SetIndent("", " ") + m.SetEscapeHTML(false) + err = m.Encode(tables) + if err != nil { + return err + } + outputPath := filepath.Join(dir, "tables.json") + return os.WriteFile(outputPath, buffer.Bytes(), 0644) +} + +func (s *PluginServe) build(pluginDirectory, goos, goarch, distPath, pluginVersion string) error { + pluginName := fmt.Sprintf("plugin-%s-%s-%s-%s", s.plugin.Name(), pluginVersion, goos, goarch) + pluginPath := path.Join(distPath, pluginName) + args := []string{"build", "-o", pluginPath} + importPath, err := s.getModuleName(pluginDirectory) + if err != nil { + return err + } + args = append(args, "-buildmode=exe") + args = append(args, "-ldflags", fmt.Sprintf("-s -w -X %s/plugin.Version=%s", importPath, pluginVersion)) + cmd := exec.Command("go", args...) + cmd.Dir = pluginDirectory + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Env = os.Environ() + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to build plugin with `go %v`: %w", args, err) + } + + pluginFile, err := os.Open(pluginPath) + if err != nil { + return fmt.Errorf("failed to open plugin file: %w", err) + } + defer pluginFile.Close() + + zipPluginPath := pluginPath + ".zip" + zipPluginFile, err := os.Create(zipPluginPath) + if err != nil { + return fmt.Errorf("failed to create zip file: %w", err) + } + defer zipPluginFile.Close() + + zipWriter := zip.NewWriter(zipPluginFile) + defer zipWriter.Close() + + pluginZip, err := zipWriter.Create(pluginName) + if err != nil { + return fmt.Errorf("failed to create file in zip archive: %w", err) + } + _, err = io.Copy(pluginZip, pluginFile) + if err != nil { + return fmt.Errorf("failed to copy plugin file to zip archive: %w", err) + } + if err := pluginFile.Close(); err != nil { + return err + } + if err := os.Remove(pluginPath); err != nil { + return fmt.Errorf("failed to remove plugin file: %w", err) + } + return nil +} + +func (*PluginServe) getModuleName(pluginDirectory string) (string, error) { + goMod, err := os.ReadFile(path.Join(pluginDirectory, "go.mod")) + if err != nil { + return "", fmt.Errorf("failed to open go.mod: %w", err) + } + reMod := regexp.MustCompile(`module\s+(.+)\n`) + importPathMatches := reMod.FindStringSubmatch(string(goMod)) + if len(importPathMatches) != 2 { + return "", fmt.Errorf("failed to parse import path from go.mod") + } + importPath := importPathMatches[1] + if err != nil { + return "", fmt.Errorf("failed to get import path: %w", err) + } + return strings.TrimSpace(importPath), nil +} + +func (s *PluginServe) writePackageJSON(dir, pluginVersion string) error { + targets := []TargetBuild{} + for _, target := range s.plugin.Targets() { + pluginName := fmt.Sprintf("plugin-%s-%s-%s-%s", s.plugin.Name(), pluginVersion, target.OS, target.Arch) + targets = append(targets, TargetBuild{ + OS: target.OS, + Arch: target.Arch, + Path: pluginName + ".zip", + }) + } + packageJSON := PackageJSON{ + Name: s.plugin.Name(), + Version: pluginVersion, + Protocols: s.versions, + SupportedTargets: targets, + PackageType: plugin.PackageTypeNative, + } + buffer := &bytes.Buffer{} + m := json.NewEncoder(buffer) + m.SetIndent("", " ") + m.SetEscapeHTML(false) + err := m.Encode(packageJSON) + if err != nil { + return err + } + outputPath := filepath.Join(dir, "package.json") + return os.WriteFile(outputPath, buffer.Bytes(), 0644) +} + +func (*PluginServe) copyDocs(distPath, docsPath string) error { + err := os.MkdirAll(filepath.Join(distPath, "docs"), 0755) + if err != nil { + return err + } + dirEntry, err := os.ReadDir(docsPath) + if err != nil { + return err + } + for _, entry := range dirEntry { + if entry.IsDir() { + continue + } + if strings.HasSuffix(entry.Name(), ".md") { + src := filepath.Join(docsPath, entry.Name()) + dst := filepath.Join(distPath, "docs", entry.Name()) + err := copyFile(src, dst) + if err != nil { + return err + } + } + } + return nil +} + +func copyFile(src, dst string) error { + srcFile, err := os.Open(src) + if err != nil { + return err + } + defer srcFile.Close() + dstFile, err := os.Create(dst) + if err != nil { + return err + } + defer dstFile.Close() + _, err = io.Copy(dstFile, srcFile) + if err != nil { + return err + } + return nil +} + +func (s *PluginServe) newCmdPluginPackage() *cobra.Command { + cmd := &cobra.Command{ + Use: "package ", + Short: pluginPackageShort, + Long: pluginPackageLong, + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + pluginDirectory := args[0] + pluginVersion := args[1] + distPath := path.Join(pluginDirectory, "dist") + if cmd.Flag("dist").Changed { + distPath = cmd.Flag("dist").Value.String() + } + docsPath := path.Join(pluginDirectory, "docs") + if cmd.Flag("docs").Changed { + docsPath = cmd.Flag("docs").Value.String() + } + if err := os.MkdirAll(distPath, 0755); err != nil { + return err + } + if err := s.plugin.Init(cmd.Context(), nil, plugin.NewClientOptions{ + NoConnection: true, + }); err != nil { + return err + } + if err := s.writeTablesJSON(cmd.Context(), distPath); err != nil { + return err + } + for _, target := range s.plugin.Targets() { + fmt.Println("Building for OS: " + target.OS + ", ARCH: " + target.Arch) + if err := s.build(pluginDirectory, target.OS, target.Arch, distPath, pluginVersion); err != nil { + return fmt.Errorf("failed to build plugin for %s/%s: %w", target.OS, target.Arch, err) + } + } + if err := s.writePackageJSON(distPath, pluginVersion); err != nil { + return fmt.Errorf("failed to write manifest: %w", err) + } + if err := s.copyDocs(distPath, docsPath); err != nil { + return fmt.Errorf("failed to copy docs: %w", err) + } + return nil + }, + } + cmd.Flags().String("dist", "", "dist directory to output the built plugin. (default: /dist)") + cmd.Flags().String("docs", "", "docs directory to copy to the dist directory. (default: /docs)") + return cmd +} diff --git a/serve/package_test.go b/serve/package_test.go new file mode 100644 index 0000000000..dcebcd5cc2 --- /dev/null +++ b/serve/package_test.go @@ -0,0 +1,113 @@ +package serve + +import ( + "encoding/json" + "io" + "os" + "path/filepath" + "runtime" + "testing" + + "github.com/cloudquery/plugin-sdk/v4/internal/memdb" + "github.com/cloudquery/plugin-sdk/v4/plugin" + "github.com/google/go-cmp/cmp" +) + +func TestPluginPackage(t *testing.T) { + _, filename, _, ok := runtime.Caller(0) + if !ok { + t.Fatal("failed to get current file path") + } + dir := filepath.Dir(filepath.Dir(filename)) + simplePluginPath := filepath.Join(dir, "examples/simple_plugin") + packageVersion := "v1.2.3" + p := plugin.NewPlugin( + "testPlugin", + "development", + memdb.NewMemDBClient, + plugin.WithBuildTargets([]plugin.BuildTarget{ + {OS: plugin.GoOSLinux, Arch: plugin.GoArchAmd64}, + {OS: plugin.GoOSWindows, Arch: plugin.GoArchAmd64}, + {OS: plugin.GoOSDarwin, Arch: plugin.GoArchAmd64}, + }), + ) + srv := Plugin(p) + cmd := srv.newCmdPluginRoot() + distDir := t.TempDir() + cmd.SetArgs([]string{"package", "--dist", distDir, simplePluginPath, packageVersion}) + if err := cmd.Execute(); err != nil { + t.Fatal(err) + } + files, err := os.ReadDir(distDir) + if err != nil { + t.Fatal(err) + } + expect := []string{ + "docs", + "package.json", + "plugin-testPlugin-v1.2.3-darwin-amd64.zip", + "plugin-testPlugin-v1.2.3-linux-amd64.zip", + "plugin-testPlugin-v1.2.3-windows-amd64.zip", + "tables.json", + } + if diff := cmp.Diff(expect, fileNames(files)); diff != "" { + t.Fatalf("unexpected files in dist directory (-want +got):\n%s", diff) + } + + expectPackage := PackageJSON{ + Name: "testPlugin", + Version: "v1.2.3", + Protocols: []int{3}, + SupportedTargets: []TargetBuild{ + {OS: plugin.GoOSLinux, Arch: plugin.GoArchAmd64, Path: "plugin-testPlugin-v1.2.3-linux-amd64.zip"}, + {OS: plugin.GoOSWindows, Arch: plugin.GoArchAmd64, Path: "plugin-testPlugin-v1.2.3-windows-amd64.zip"}, + {OS: plugin.GoOSDarwin, Arch: plugin.GoArchAmd64, Path: "plugin-testPlugin-v1.2.3-darwin-amd64.zip"}, + }, + PackageType: plugin.PackageTypeNative, + } + checkPackageJSONContents(t, filepath.Join(distDir, "package.json"), expectPackage) + + expectDocs := []string{ + "configuration.md", + "overview.md", + } + checkDocs(t, filepath.Join(distDir, "docs"), expectDocs) +} + +func checkDocs(t *testing.T, dir string, expect []string) { + files, err := os.ReadDir(dir) + if err != nil { + t.Fatal(err) + } + if diff := cmp.Diff(expect, fileNames(files)); diff != "" { + t.Fatalf("unexpected files in docs directory (-want +got):\n%s", diff) + } +} + +func checkPackageJSONContents(t *testing.T, filename string, expect PackageJSON) { + f, err := os.Open(filename) + if err != nil { + t.Fatalf("failed to open package.json: %v", err) + } + defer f.Close() + b, err := io.ReadAll(f) + if err != nil { + t.Fatalf("failed to read package.json: %v", err) + } + j := PackageJSON{} + err = json.Unmarshal(b, &j) + if err != nil { + t.Fatalf("failed to unmarshal package.json: %v", err) + } + if diff := cmp.Diff(expect, j); diff != "" { + t.Fatalf("package.json contents mismatch (-want +got):\n%s", diff) + } +} + +func fileNames(files []os.DirEntry) []string { + names := make([]string, 0, len(files)) + for _, file := range files { + names = append(names, file.Name()) + } + return names +} diff --git a/serve/plugin.go b/serve/plugin.go index a2d06945c8..fa60005e97 100644 --- a/serve/plugin.go +++ b/serve/plugin.go @@ -291,7 +291,7 @@ func (s *PluginServe) newCmdPluginRoot() *cobra.Command { } cmd.AddCommand(s.newCmdPluginServe()) cmd.AddCommand(s.newCmdPluginDoc()) - cmd.AddCommand(s.newCmdPluginPublish()) + cmd.AddCommand(s.newCmdPluginPackage()) cmd.CompletionOptions.DisableDefaultCmd = true cmd.Version = s.plugin.Version() return cmd diff --git a/serve/publish.go b/serve/publish.go deleted file mode 100644 index 52e9a17334..0000000000 --- a/serve/publish.go +++ /dev/null @@ -1,193 +0,0 @@ -package serve - -import ( - "archive/zip" - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "os" - "os/exec" - "path/filepath" - - "github.com/cloudquery/plugin-sdk/v4/plugin" - "github.com/spf13/cobra" -) - -const ( - pluginPublishShort = "Publish plugin to CloudQuery registry" - pluginPublishLong = `Publish plugin to CloudQuery registry - -To just build the plugin without publishing, use the --dry-run flag. -Example: -go run main.go publish --dry-run -` -) - -type PackageType string - -const ( - PackageTypeNative PackageType = "native" - PackageTypeDocker PackageType = "docker" -) - -// manifest is the plugin.json file inside the dist directory. It is used by CloudQuery registry -// to be able to publish the plugin with all the needed metadata. -type Manifest struct { - Name string `json:"name"` - Version string `json:"version"` - Title string `json:"title"` - ShortDescription string `json:"short_description"` - Description string `json:"description"` - Categories []string `json:"categories"` - Protocols []int `json:"protocols"` - SupportedTargets []plugin.BuildTarget `json:"supported_targets"` - PackageType PackageType `json:"package_type"` -} - -func isDirEmpty(name string) (bool, error) { - entries, err := os.ReadDir(name) - if err != nil { - return false, err - } - return len(entries) == 0, nil -} - -func (s *PluginServe) writeTablesJSON(ctx context.Context, dir string) error { - tables, err := s.plugin.Tables(ctx, plugin.TableOptions{ - Tables: []string{"*"}, - }) - if err != nil { - return err - } - buffer := &bytes.Buffer{} - m := json.NewEncoder(buffer) - m.SetIndent("", " ") - m.SetEscapeHTML(false) - err = m.Encode(tables) - if err != nil { - return err - } - outputPath := filepath.Join(dir, "tables.json") - return os.WriteFile(outputPath, buffer.Bytes(), 0644) -} - -func (*PluginServe) build(pluginDirectory string, goos string, goarch string) error { - pluginName := "plugin" + "_" + goos + "_" + goarch - distPath := pluginDirectory + "/dist" - - pluginPath := distPath + "/" + pluginName - args := []string{"build", "-o", pluginPath} - cmd := exec.Command("go", args...) - cmd.Dir = pluginDirectory - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Env = os.Environ() - if err := cmd.Run(); err != nil { - return fmt.Errorf("failed to build plugin with `go %v`: %w", args, err) - } - - pluginFile, err := os.Open(pluginPath) - if err != nil { - return fmt.Errorf("failed to open plugin file: %w", err) - } - defer pluginFile.Close() - - zipPluginPath := pluginPath + ".zip" - zipPluginFile, err := os.Create(zipPluginPath) - if err != nil { - return fmt.Errorf("failed to create zip file: %w", err) - } - defer zipPluginFile.Close() - - zipWriter := zip.NewWriter(zipPluginFile) - defer zipWriter.Close() - - pluginZip, err := zipWriter.Create(pluginName) - if err != nil { - return fmt.Errorf("failed to create file in zip archive: %w", err) - } - _, err = io.Copy(pluginZip, pluginFile) - if err != nil { - return fmt.Errorf("failed to copy plugin file to zip archive: %w", err) - } - if err := pluginFile.Close(); err != nil { - return err - } - if err := os.Remove(pluginPath); err != nil { - return fmt.Errorf("failed to remove plugin file: %w", err) - } - return nil -} - -func (s *PluginServe) writeManifest(dir string) error { - manifest := Manifest{ - Name: s.plugin.Name(), - Version: s.plugin.Version(), - Title: s.plugin.Title(), - ShortDescription: s.plugin.ShortDescription(), - Description: s.plugin.Description(), - Categories: s.plugin.Categories(), - Protocols: s.versions, - SupportedTargets: s.plugin.Targets(), - PackageType: PackageTypeNative, - } - buffer := &bytes.Buffer{} - m := json.NewEncoder(buffer) - m.SetIndent("", " ") - m.SetEscapeHTML(false) - err := m.Encode(manifest) - if err != nil { - return err - } - outputPath := filepath.Join(dir, "plugin.json") - return os.WriteFile(outputPath, buffer.Bytes(), 0644) -} - -func (s *PluginServe) newCmdPluginPublish() *cobra.Command { - var distDirectory string - cmd := &cobra.Command{ - Use: "publish ", - Short: pluginPublishShort, - Long: pluginPublishLong, - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - pluginDirectory := args[0] - distPath := pluginDirectory + "/dist" - if distDirectory != "" { - distPath = distDirectory - } - empty, err := isDirEmpty(distPath) - if err != nil { - return err - } - if !empty { - return fmt.Errorf("dist directory is not empty: %s", distPath) - } - if err := os.MkdirAll(distPath, 0755); err != nil { - return err - } - if err := s.plugin.Init(cmd.Context(), nil, plugin.NewClientOptions{ - NoConnection: true, - }); err != nil { - return err - } - if err := s.writeTablesJSON(cmd.Context(), distPath); err != nil { - return err - } - for _, target := range s.plugin.Targets() { - fmt.Println("Building for OS: " + target.OS + ", ARCH: " + target.Arch) - if err := s.build(pluginDirectory, target.OS, target.Arch); err != nil { - return fmt.Errorf("failed to build plugin for %s/%s: %w", target.OS, target.Arch, err) - } - } - if err := s.writeManifest(distPath); err != nil { - return fmt.Errorf("failed to write manifest: %w", err) - } - return nil - }, - } - cmd.Flags().StringVar(&distDirectory, "dist-dir", "", "dist directory to output the built plugin. (default: )") - return cmd -} diff --git a/serve/publish_test.go b/serve/publish_test.go deleted file mode 100644 index f0ef4a2ff1..0000000000 --- a/serve/publish_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package serve - -import ( - "path/filepath" - "runtime" - "testing" - - "github.com/cloudquery/plugin-sdk/v4/internal/memdb" - "github.com/cloudquery/plugin-sdk/v4/plugin" -) - -func TestPluginPublish(t *testing.T) { - _, filename, _, ok := runtime.Caller(0) - if !ok { - t.Fatal("failed to get current file path") - } - dir := filepath.Dir(filepath.Dir(filename)) - simplePluginPath := filepath.Join(dir, "examples/simple_plugin") - p := plugin.NewPlugin( - "testPlugin", - "v1.0.0", - memdb.NewMemDBClient) - srv := Plugin(p) - cmd := srv.newCmdPluginRoot() - cmd.SetArgs([]string{"publish", simplePluginPath, "--dist-dir", t.TempDir()}) - if err := cmd.Execute(); err != nil { - t.Fatal(err) - } -}