Skip to content

Commit 2f1aff8

Browse files
disqhermanschaaf
andauthored
feat(package): Check for Version variable (#1359)
Co-authored-by: Kemal Hadimli <[email protected]> Co-authored-by: Herman Schaaf <[email protected]>
1 parent f5cd387 commit 2f1aff8

File tree

2 files changed

+143
-3
lines changed

2 files changed

+143
-3
lines changed

serve/package.go

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ package serve
22

33
import (
44
"archive/zip"
5+
"bufio"
56
"bytes"
67
"context"
78
"crypto/sha256"
89
"encoding/json"
10+
"errors"
911
"fmt"
1012
"io"
1113
"os"
@@ -111,7 +113,7 @@ func (s *PluginServe) build(pluginDirectory, goos, goarch, distPath, pluginVersi
111113
if err != nil {
112114
return nil, err
113115
}
114-
ldFlags := fmt.Sprintf("-s -w -X %s/plugin.Version=%s", importPath, pluginVersion)
116+
ldFlags := fmt.Sprintf("-s -w -X %[1]s/plugin.Version=%[2]s -X %[1]s/resources/plugin.Version=%[2]s", importPath, pluginVersion)
115117
if s.plugin.IsStaticLinkingEnabled() && strings.EqualFold(goos, plugin.GoOSLinux) {
116118
ldFlags += " -linkmode external -extldflags=-static"
117119
}
@@ -262,6 +264,70 @@ func (*PluginServe) copyDocs(distPath, docsPath string) error {
262264
return nil
263265
}
264266

267+
func (*PluginServe) versionRegex() *regexp.Regexp {
268+
return regexp.MustCompile(`^(var)?\s?Version\s*=`)
269+
}
270+
271+
func (s *PluginServe) validatePluginExports(pluginPath string) error {
272+
st, err := os.Stat(pluginPath)
273+
if err != nil {
274+
return err
275+
}
276+
if !st.IsDir() {
277+
return errors.New("plugin path must be a directory")
278+
}
279+
280+
checkRelativeDirs := []string{"resources" + string(filepath.Separator) + "plugin", "plugin"}
281+
foundDirs := []string{}
282+
for _, dir := range checkRelativeDirs {
283+
p := filepath.Join(pluginPath, dir)
284+
s, err := os.Stat(p)
285+
if err == nil && s.IsDir() {
286+
foundDirs = append(foundDirs, dir)
287+
}
288+
}
289+
if len(foundDirs) == 0 {
290+
return fmt.Errorf("plugin directory must contain at least one of the following directories: %s", strings.Join(checkRelativeDirs, ", "))
291+
}
292+
293+
findVersion := s.versionRegex()
294+
295+
foundVersion := false
296+
for _, dir := range foundDirs {
297+
p := filepath.Join(pluginPath, dir)
298+
if err := filepath.WalkDir(p, func(path string, d os.DirEntry, err error) error {
299+
if err != nil {
300+
return err
301+
}
302+
if d.IsDir() || foundVersion {
303+
return nil
304+
}
305+
if !strings.HasSuffix(strings.ToLower(d.Name()), ".go") {
306+
return nil
307+
}
308+
309+
f, err := os.Open(path)
310+
if err != nil {
311+
return err
312+
}
313+
defer f.Close()
314+
if ok, err := containsRegex(f, findVersion); err != nil {
315+
return err
316+
} else if ok {
317+
foundVersion = true
318+
}
319+
return nil
320+
}); err != nil {
321+
return err
322+
}
323+
}
324+
if !foundVersion {
325+
return fmt.Errorf("could not find `Version` global variable in package")
326+
}
327+
328+
return nil
329+
}
330+
265331
func copyFile(src, dst string) error {
266332
srcFile, err := os.Open(src)
267333
if err != nil {
@@ -280,6 +346,16 @@ func copyFile(src, dst string) error {
280346
return nil
281347
}
282348

349+
func containsRegex(r io.Reader, needle *regexp.Regexp) (bool, error) {
350+
scanner := bufio.NewScanner(r)
351+
for scanner.Scan() {
352+
if needle.MatchString(scanner.Text()) {
353+
return true, nil
354+
}
355+
}
356+
return false, scanner.Err()
357+
}
358+
283359
func (s *PluginServe) newCmdPluginPackage() *cobra.Command {
284360
cmd := &cobra.Command{
285361
Use: "package -m <message> <version> <plugin_directory>",
@@ -320,11 +396,16 @@ func (s *PluginServe) newCmdPluginPackage() *cobra.Command {
320396
return fmt.Errorf("plugin name is required for packaging")
321397
}
322398
if s.plugin.Team() == "" {
323-
return fmt.Errorf("plugin team is required (hint: use the plugin.WithTeam() option")
399+
return fmt.Errorf("plugin team is required (hint: use the plugin.WithTeam() option)")
324400
}
325401
if s.plugin.Kind() == "" {
326-
return fmt.Errorf("plugin kind is required (hint: use the plugin.WithKind() option")
402+
return fmt.Errorf("plugin kind is required (hint: use the plugin.WithKind() option)")
327403
}
404+
405+
if err := s.validatePluginExports(pluginDirectory); err != nil {
406+
return err
407+
}
408+
328409
if s.plugin.Kind() == plugin.KindSource {
329410
if err := s.plugin.Init(cmd.Context(), nil, plugin.NewClientOptions{
330411
NoConnection: true,

serve/package_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os"
1010
"path/filepath"
1111
"runtime"
12+
"strings"
1213
"testing"
1314

1415
"github.com/cloudquery/plugin-sdk/v4/internal/memdb"
@@ -208,6 +209,64 @@ with multiple lines and **markdown**`
208209
}
209210
}
210211

212+
func TestVersionRegex(t *testing.T) {
213+
t.Parallel()
214+
re := Plugin(&plugin.Plugin{}).versionRegex()
215+
testCases := []struct {
216+
input string
217+
want bool
218+
}{
219+
{
220+
input: `var Version = ""`,
221+
want: true,
222+
},
223+
{
224+
input: `var (
225+
Version = ""
226+
)
227+
`,
228+
want: true,
229+
},
230+
{
231+
input: ` var Version = ""`,
232+
want: false,
233+
},
234+
{
235+
input: `var (
236+
Version = ""
237+
)
238+
`,
239+
},
240+
{
241+
input: `var (
242+
Version = ""
243+
)
244+
`,
245+
want: true,
246+
},
247+
}
248+
for i, tc := range testCases {
249+
tc := tc
250+
t.Run(fmt.Sprintf("Case %d", i+1), func(t *testing.T) {
251+
t.Parallel()
252+
// only match the line with "Version"
253+
lines := strings.Split(tc.input, "\n")
254+
realInput := ""
255+
for _, line := range lines {
256+
if strings.Contains(line, "Version") {
257+
realInput = line
258+
break
259+
}
260+
}
261+
if realInput == "" {
262+
t.Fatalf("failed to find line with Version: %q", tc.input)
263+
}
264+
got := re.MatchString(realInput)
265+
require.Equalf(t, tc.want, got, "input: %q", realInput)
266+
})
267+
}
268+
}
269+
211270
func sha256sum(filename string) string {
212271
f, err := os.Open(filename)
213272
if err != nil {

0 commit comments

Comments
 (0)