diff --git a/.gitignore b/.gitignore index 162f33f8..e039ea05 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ test/resolve/testdata/gradle/*/** **.gradle-init-script.debricked.groovy test/resolve/testdata/gradle/gradle.debricked.lock /mvnproj/target +debricked-call-graph-golang diff --git a/go.mod b/go.mod index 09773f45..1e425036 100644 --- a/go.mod +++ b/go.mod @@ -54,13 +54,15 @@ require ( github.com/stretchr/objx v0.5.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - golang.org/x/crypto v0.16.0 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect + github.com/yuin/goldmark v1.7.0 // indirect + golang.org/x/crypto v0.19.0 // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/telemetry v0.0.0-20240222153655-3df865e588ac // indirect + golang.org/x/term v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.13.0 // indirect + golang.org/x/tools v0.18.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 54fe8fe6..4dceaa9f 100644 --- a/go.sum +++ b/go.sum @@ -261,6 +261,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA= +github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -280,6 +282,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -317,6 +321,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -355,6 +361,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -377,6 +385,7 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -426,6 +435,10 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240222153655-3df865e588ac h1:ki77bVtFouxWgn3Q21wz6tOBcbD4Yhn21/KrwRrHeM4= +golang.org/x/telemetry v0.0.0-20240222153655-3df865e588ac/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -433,6 +446,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -500,6 +515,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/callgraph/cgexec/command.go b/internal/callgraph/cgexec/command.go index cb252120..adc910df 100644 --- a/internal/callgraph/cgexec/command.go +++ b/internal/callgraph/cgexec/command.go @@ -19,14 +19,19 @@ type ICommand interface { GetArgs() []string GetDir() string Signal(process *os.Process, signal os.Signal) error + GetStdOut() *bytes.Buffer + GetStdErr() *bytes.Buffer } type Command struct { - osCmd *exec.Cmd + osCmd *exec.Cmd + stdoutBuf *bytes.Buffer + stderrBuf *bytes.Buffer } func NewCommand(osCmd *exec.Cmd) *Command { - return &Command{osCmd} + var stdoutBuf, stderrBuf bytes.Buffer + return &Command{osCmd, &stdoutBuf, &stderrBuf} } func (cmd Command) SetStderr(stderr *bytes.Buffer) { @@ -65,9 +70,17 @@ func (cmd Command) Signal(process *os.Process, signal os.Signal) error { return process.Signal(signal) } +func (cmd Command) GetStdOut() *bytes.Buffer { + return cmd.stdoutBuf +} + +func (cmd Command) GetStdErr() *bytes.Buffer { + return cmd.stderrBuf +} + func RunCommand(cmd ICommand, ctx IContext) error { args := strings.Join(cmd.GetArgs(), " ") - var stdoutBuf, stderrBuf bytes.Buffer + var err error var outputCmd []byte if ctx == nil { @@ -79,8 +92,8 @@ func RunCommand(cmd ICommand, ctx IContext) error { return err } - cmd.SetStderr(&stderrBuf) - cmd.SetStdout(&stdoutBuf) + cmd.SetStderr(cmd.GetStdErr()) + cmd.SetStdout(cmd.GetStdOut()) // Start the external process if err := cmd.Start(); err != nil { @@ -95,7 +108,7 @@ func RunCommand(cmd ICommand, ctx IContext) error { go func() { err := cmd.Wait() if err != nil { - err = fmt.Errorf("Command '%s' executed in folder '%s' gave the following error: \n%s\n%s", args, cmd.GetDir(), stdoutBuf.String(), stderrBuf.String()) + err = fmt.Errorf("Command '%s' executed in folder '%s' gave the following error: \n%s\n%s", args, cmd.GetDir(), cmd.GetStdOut().String(), cmd.GetStdErr().String()) } done <- err diff --git a/internal/callgraph/cgexec/testdata/command_mock.go b/internal/callgraph/cgexec/testdata/command_mock.go index a4290309..f3f80ad5 100644 --- a/internal/callgraph/cgexec/testdata/command_mock.go +++ b/internal/callgraph/cgexec/testdata/command_mock.go @@ -63,3 +63,11 @@ func (m CommandMock) GetDir() string { func (m CommandMock) Signal(process *os.Process, signal os.Signal) error { return m.SignalError } + +func (m CommandMock) GetStdOut() *bytes.Buffer { + return &bytes.Buffer{} +} + +func (m CommandMock) GetStdErr() *bytes.Buffer { + return &bytes.Buffer{} +} diff --git a/internal/callgraph/finder/finder.go b/internal/callgraph/finder/finder.go index dfdd49b9..16d870ec 100644 --- a/internal/callgraph/finder/finder.go +++ b/internal/callgraph/finder/finder.go @@ -1,7 +1,7 @@ package finder type IFinder interface { - FindMavenRoots(files []string) ([]string, error) - FindJavaClassDirs(files []string, findJars bool) ([]string, error) + FindRoots(files []string) ([]string, error) + FindDependencyDirs(files []string, findJars bool) ([]string, error) FindFiles(paths []string, exclusions []string) ([]string, error) } diff --git a/internal/callgraph/finder/golangfinder/finder.go b/internal/callgraph/finder/golangfinder/finder.go new file mode 100644 index 00000000..65a26b70 --- /dev/null +++ b/internal/callgraph/finder/golangfinder/finder.go @@ -0,0 +1,60 @@ +package golanfinder + +import ( + "os" + "path/filepath" + + "github.com/debricked/cli/internal/callgraph/finder" + "github.com/debricked/cli/internal/file" +) + +type GolangFinder struct{} + +func (f GolangFinder) FindRoots(files []string) ([]string, error) { + mainFiles := finder.FilterFiles(files, "main.go") + return mainFiles, nil +} + +func (f GolangFinder) FindDependencyDirs(files []string, findJars bool) ([]string, error) { + // Not needed for golang + return []string{}, nil +} + +func (f GolangFinder) FindFiles(roots []string, exclusions []string) ([]string, error) { + files := make(map[string]bool) + var err error = nil + + for _, root := range roots { + err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + + if err != nil { + return err + } + + excluded := file.Excluded(exclusions, path) + + if info.IsDir() && excluded { + return filepath.SkipDir + } + + if !info.IsDir() && !excluded && filepath.Ext(path) == ".go" { + files[path] = true + } + + return nil + }) + + if err != nil { + break + } + } + + fileList := make([]string, len(files)) + i := 0 + for k := range files { + fileList[i] = k + i++ + } + + return fileList, err +} diff --git a/internal/callgraph/finder/golangfinder/finder_test.go b/internal/callgraph/finder/golangfinder/finder_test.go new file mode 100644 index 00000000..f304d39a --- /dev/null +++ b/internal/callgraph/finder/golangfinder/finder_test.go @@ -0,0 +1,12 @@ +package golanfinder + +import ( + "testing" + + "github.com/debricked/cli/internal/callgraph/finder" + "github.com/stretchr/testify/assert" +) + +func TestGolangFinderImplementsFinder(t *testing.T) { + assert.Implements(t, (*finder.IFinder)(nil), new(GolangFinder)) +} diff --git a/internal/callgraph/finder/javafinder/finder.go b/internal/callgraph/finder/javafinder/finder.go index bef450be..9a18622f 100644 --- a/internal/callgraph/finder/javafinder/finder.go +++ b/internal/callgraph/finder/javafinder/finder.go @@ -10,7 +10,7 @@ import ( type JavaFinder struct{} -func (f JavaFinder) FindMavenRoots(files []string) ([]string, error) { +func (f JavaFinder) FindRoots(files []string) ([]string, error) { pomFiles := finder.FilterFiles(files, "pom.xml") ps := PomService{} rootFiles := ps.GetRootPomFiles(pomFiles) @@ -18,7 +18,7 @@ func (f JavaFinder) FindMavenRoots(files []string) ([]string, error) { return rootFiles, nil } -func (f JavaFinder) FindJavaClassDirs(files []string, findJars bool) ([]string, error) { +func (f JavaFinder) FindDependencyDirs(files []string, findJars bool) ([]string, error) { filteredFiles := finder.FilterFiles(files, ".*\\.class") dirsWithClassFiles := make(map[string]bool) for _, file := range filteredFiles { diff --git a/internal/callgraph/finder/javafinder/finder_test.go b/internal/callgraph/finder/javafinder/finder_test.go index a14c8b3c..afd02700 100644 --- a/internal/callgraph/finder/javafinder/finder_test.go +++ b/internal/callgraph/finder/javafinder/finder_test.go @@ -11,16 +11,16 @@ func TestFindMavenRoots(t *testing.T) { files := []string{"test/asd/pom.xml", "test2/pom.xml", "test2/test/asd/pom.xml", "test3/tes"} f := JavaFinder{} - roots, err := f.FindMavenRoots(files) + roots, err := f.FindRoots(files) assert.Nil(t, err) assert.Len(t, roots, 0) } -func TestFindJavaClassDirs(t *testing.T) { +func TestFindDependencyDirs(t *testing.T) { files := []string{"test/asd/pom.xml", "test2/basd/qwe/asd.class", "test2/test/asd", "test3/tes.jar"} f := JavaFinder{} - files, err := f.FindJavaClassDirs(files, false) + files, err := f.FindDependencyDirs(files, false) assert.Nil(t, err) assert.Len(t, files, 1) @@ -28,7 +28,7 @@ func TestFindJavaClassDirs(t *testing.T) { assert.Equal(t, files[0], gt) files = []string{"test/asd/pom.xml", "test2/basd/qwe/asd.class", "test2/test/asd", "test3/tes.jar"} - files, err = f.FindJavaClassDirs(files, true) + files, err = f.FindDependencyDirs(files, true) assert.Nil(t, err) assert.Len(t, files, 2) diff --git a/internal/callgraph/finder/testdata/finder_mock.go b/internal/callgraph/finder/testdata/finder_mock.go index 30d50b8b..40c45139 100644 --- a/internal/callgraph/finder/testdata/finder_mock.go +++ b/internal/callgraph/finder/testdata/finder_mock.go @@ -1,27 +1,27 @@ package testdata type FinderMock struct { - FindJavaClassDirsNames []string - FindJavaClassDirsErr error - FindMavenRootsNames []string - FindMavenRootsErr error - FindFilesNames []string - FindFilesErr error + FindDependencyDirsNames []string + FindDependencyDirsErr error + FindMavenRootsNames []string + FindMavenRootsErr error + FindFilesNames []string + FindFilesErr error } func NewEmptyFinderMock() FinderMock { return FinderMock{ - FindJavaClassDirsNames: []string{}, - FindMavenRootsNames: []string{}, - FindFilesNames: []string{}, + FindDependencyDirsNames: []string{}, + FindMavenRootsNames: []string{}, + FindFilesNames: []string{}, } } -func (f FinderMock) FindJavaClassDirs(_ []string, _ bool) ([]string, error) { - return f.FindJavaClassDirsNames, f.FindJavaClassDirsErr +func (f FinderMock) FindDependencyDirs(_ []string, _ bool) ([]string, error) { + return f.FindDependencyDirsNames, f.FindDependencyDirsErr } -func (f FinderMock) FindMavenRoots(_ []string) ([]string, error) { +func (f FinderMock) FindRoots(_ []string) ([]string, error) { return f.FindMavenRootsNames, f.FindMavenRootsErr } diff --git a/internal/callgraph/language/golang/README.md b/internal/callgraph/language/golang/README.md new file mode 100644 index 00000000..867d867e --- /dev/null +++ b/internal/callgraph/language/golang/README.md @@ -0,0 +1 @@ +# TODO: add docs to this readme on how we generate go CGs \ No newline at end of file diff --git a/internal/callgraph/language/golang/callgraph.go b/internal/callgraph/language/golang/callgraph.go new file mode 100644 index 00000000..d7125603 --- /dev/null +++ b/internal/callgraph/language/golang/callgraph.go @@ -0,0 +1,61 @@ +package golang + +import ( + "github.com/debricked/cli/internal/callgraph/cgexec" + ioFs "github.com/debricked/cli/internal/io" +) + +type ICallgraph interface { + RunCallGraph() error +} + +type Callgraph struct { + cmdFactory ICmdFactory + filesystem ioFs.IFileSystem + workingDirectory string + mainFile string + outputName string + ctx cgexec.IContext +} + +func NewCallgraph( + cmdFactory ICmdFactory, + workingDirectory string, + mainFile string, + outputName string, + filesystem ioFs.IFileSystem, + ctx cgexec.IContext, +) Callgraph { + return Callgraph{ + cmdFactory: cmdFactory, + workingDirectory: workingDirectory, + mainFile: mainFile, + outputName: outputName, + filesystem: filesystem, + ctx: ctx, + } +} + +func (cg *Callgraph) RunCallGraph() error { + osCmd, err := cg.cmdFactory.MakeCallGraphGenerationCmd(cg.mainFile, cg.workingDirectory, cg.ctx) + if err != nil { + return err + } + + cmd := cgexec.NewCommand(osCmd) + err = cgexec.RunCommand(*cmd, cg.ctx) + if err != nil { + return err + } + + output := cmd.GetStdOut() + + err = cg.filesystem.FsWriteFile(cg.outputName, output.Bytes(), 0600) + if err != nil { + + return err + } + _ = cmd.Wait() + + return err +} diff --git a/internal/callgraph/language/golang/callgraph_test.go b/internal/callgraph/language/golang/callgraph_test.go new file mode 100644 index 00000000..d62f57e7 --- /dev/null +++ b/internal/callgraph/language/golang/callgraph_test.go @@ -0,0 +1,82 @@ +package golang + +// import ( +// "fmt" +// "testing" + +// "github.com/debricked/cli/internal/callgraph/language/java11/testdata" +// ioTestData "github.com/debricked/cli/internal/io/testdata" +// "github.com/stretchr/testify/assert" +// ) + +// func TestRunCallGraphWithSetupMock(t *testing.T) { + +// cmdMock := testdata.NewEchoCmdFactory() +// fsMock := ioTestData.FileSystemMock{} +// cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, nil) + +// err := cg.RunCallGraphWithSetup() + +// assert.Nil(t, err) +// } + +// func TestFsOpenEmbedError(t *testing.T) { +// cmdMock := testdata.NewEchoCmdFactory() +// fsMock := ioTestData.FileSystemMock{FsOpenEmbedError: fmt.Errorf("error")} +// cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, nil) + +// err := cg.RunCallGraphWithSetup() + +// assert.NotNil(t, err) +// } + +// func TestMkdirTempError(t *testing.T) { +// cmdMock := testdata.NewEchoCmdFactory() +// fsMock := ioTestData.FileSystemMock{MkdirTempError: fmt.Errorf("error")} +// cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, nil) + +// err := cg.RunCallGraphWithSetup() + +// assert.NotNil(t, err) +// } + +// func TestReadAllError(t *testing.T) { +// cmdMock := testdata.NewEchoCmdFactory() +// fsMock := ioTestData.FileSystemMock{FsReadAllError: fmt.Errorf("error")} +// cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, nil) + +// err := cg.RunCallGraphWithSetup() + +// assert.NotNil(t, err) +// } + +// func TestWriteFileError(t *testing.T) { +// cmdMock := testdata.NewEchoCmdFactory() +// fsMock := ioTestData.FileSystemMock{FsWriteFileError: fmt.Errorf("error")} +// cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, nil) + +// err := cg.RunCallGraphWithSetup() + +// assert.NotNil(t, err) +// } + +// func TestRunCallGraphMock(t *testing.T) { +// cmdMock := testdata.NewEchoCmdFactory() +// fsMock := ioTestData.FileSystemMock{} +// cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, nil) + +// err := cg.RunCallGraph(".") + +// assert.Nil(t, err) +// } + +// func TestRunCallGraphErrorMock(t *testing.T) { +// cmdMock := testdata.NewEchoCmdFactory() +// cmdMock.CallGraphGenErr = fmt.Errorf("error") +// fsMock := ioTestData.FileSystemMock{} +// cg := NewCallgraph(cmdMock, ".", []string{"."}, ".", ".", fsMock, nil) + +// err := cg.RunCallGraph(".") + +// assert.NotNil(t, err) +// } diff --git a/internal/callgraph/language/golang/cmd_factory.go b/internal/callgraph/language/golang/cmd_factory.go new file mode 100644 index 00000000..78266f07 --- /dev/null +++ b/internal/callgraph/language/golang/cmd_factory.go @@ -0,0 +1,28 @@ +package golang + +import ( + "os/exec" + + "github.com/debricked/cli/internal/callgraph/cgexec" +) + +type ICmdFactory interface { + MakeCallGraphGenerationCmd(pathToMain string, workingDirectory string, ctx cgexec.IContext) (*exec.Cmd, error) +} + +type CmdFactory struct{} + +func (_ CmdFactory) MakeCallGraphGenerationCmd(pathToMain string, workingDirectory string, ctx cgexec.IContext) (*exec.Cmd, error) { + + path, err := exec.LookPath("callgraph") + + args := []string{ + "callgraph", + "-format='{{.Caller}} file:{{.Filename}} {{.Line}} {{.Column}}--->{{.Callee}}'", + "-algo", + "cha", + pathToMain, + } + + return cgexec.MakeCommand(workingDirectory, path, args, ctx), err +} diff --git a/internal/callgraph/language/golang/cmd_factory_test.go b/internal/callgraph/language/golang/cmd_factory_test.go new file mode 100644 index 00000000..a4d931d9 --- /dev/null +++ b/internal/callgraph/language/golang/cmd_factory_test.go @@ -0,0 +1,9 @@ +package golang + +import ( + "testing" +) + +func TestMakeCallGraphGenerationCmd(t *testing.T) { + +} diff --git a/internal/callgraph/language/golang/job.go b/internal/callgraph/language/golang/job.go new file mode 100644 index 00000000..12bb3e49 --- /dev/null +++ b/internal/callgraph/language/golang/job.go @@ -0,0 +1,63 @@ +package golang + +import ( + "github.com/debricked/cli/internal/callgraph/cgexec" + conf "github.com/debricked/cli/internal/callgraph/config" + "github.com/debricked/cli/internal/callgraph/job" + "github.com/debricked/cli/internal/io" + ioFs "github.com/debricked/cli/internal/io" +) + +const ( + outputName = "debricked-call-graph-golang" +) + +type Job struct { + job.BaseJob + mainFile string + cmdFactory ICmdFactory + config conf.IConfig + archive io.IArchive + ctx cgexec.IContext + fs ioFs.IFileSystem +} + +func NewJob(dir string, mainFile string, cmdFactory ICmdFactory, writer ioFs.IFileWriter, archive io.IArchive, config conf.IConfig, ctx cgexec.IContext, fs ioFs.IFileSystem) *Job { + return &Job{ + BaseJob: job.NewBaseJob(dir, []string{mainFile}), + mainFile: mainFile, + cmdFactory: cmdFactory, + config: config, + archive: archive, + ctx: ctx, + fs: fs, + } +} + +func (j *Job) Run() { + workingDirectory := j.GetDir() + callgraph := NewCallgraph( + j.cmdFactory, + workingDirectory, + j.mainFile, + outputName, + j.fs, + j.ctx, + ) + j.SendStatus("generating call graph") + j.runCallGraph(&callgraph) + if j.Errors().HasError() { + + return + } + +} + +func (j *Job) runCallGraph(callgraph ICallgraph) { + err := callgraph.RunCallGraph() + + if err != nil { + j.Errors().Critical(err) + + } +} diff --git a/internal/callgraph/language/golang/job_test.go b/internal/callgraph/language/golang/job_test.go new file mode 100644 index 00000000..e7f0e39a --- /dev/null +++ b/internal/callgraph/language/golang/job_test.go @@ -0,0 +1,274 @@ +package golang + +// import ( +// "errors" +// "fmt" +// "os" +// "syscall" +// "testing" + +// ctxTestdata "github.com/debricked/cli/internal/callgraph/cgexec/testdata" +// conf "github.com/debricked/cli/internal/callgraph/config" +// jobTestdata "github.com/debricked/cli/internal/callgraph/job/testdata" +// "github.com/debricked/cli/internal/callgraph/language/java11/testdata" +// io "github.com/debricked/cli/internal/io" +// ioTestData "github.com/debricked/cli/internal/io/testdata" +// "github.com/stretchr/testify/assert" +// ) + +// const ( +// badName = "bad-name" +// dir = "dir" +// ) + +// var files = []string{"file"} + +// func TestNewJob(t *testing.T) { +// cmdFactoryMock := testdata.NewEchoCmdFactory() +// writer := io.FileWriter{} +// config := conf.Config{} +// ctx, _ := ctxTestdata.NewContextMock() + +// fsMock := ioTestData.FileSystemMock{} +// zip := ioTestData.ZipMock{} +// archiveMock := io.NewArchiveWithStructs("dir", fsMock, zip) + +// fs := io.FileSystem{} + +// j := NewJob(dir, files, cmdFactoryMock, writer, archiveMock, config, ctx, fs) +// assert.Equal(t, []string{"file"}, j.GetFiles()) +// assert.Equal(t, "dir", j.GetDir()) +// assert.False(t, j.Errors().HasError()) +// } + +// func TestRunMakeMavenCopyDependenciesCmdErr(t *testing.T) { +// cmdErr := errors.New("cmd-error") +// cmdFactoryMock := testdata.NewEchoCmdFactory() +// cmdFactoryMock.MvnCopyDepErr = cmdErr +// fileWriterMock := &ioTestData.FileWriterMock{} + +// fsMock := ioTestData.FileSystemMock{} +// zip := ioTestData.ZipMock{} +// archiveMock := io.NewArchiveWithStructs("dir", fsMock, zip) + +// fs := io.FileSystem{} + +// config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") +// ctx, _ := ctxTestdata.NewContextMock() +// j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) + +// go jobTestdata.WaitStatus(j) +// j.Run() + +// assert.Len(t, j.Errors().GetAll(), 1) +// assert.Contains(t, j.Errors().GetAll(), cmdErr) +// } + +// func TestRun(t *testing.T) { +// fileWriterMock := &ioTestData.FileWriterMock{} +// cmdFactoryMock := testdata.NewEchoCmdFactory() +// config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") +// ctx, _ := ctxTestdata.NewContextMock() + +// fsMock := ioTestData.FileSystemMock{} +// zip := ioTestData.ZipMock{} +// archiveMock := io.NewArchiveWithStructs("dir", fsMock, zip) + +// fs := io.FileSystem{} + +// j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) + +// go jobTestdata.WaitStatus(j) +// j.Run() + +// fmt.Println(j.Errors().GetAll()) +// assert.False(t, j.Errors().HasError()) +// } + +// func TestRunCallgraphMock(t *testing.T) { +// fileWriterMock := &ioTestData.FileWriterMock{} +// cmdFactoryMock := testdata.NewEchoCmdFactory() +// config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") +// ctx, _ := ctxTestdata.NewContextMock() +// callgraphMock := testdata.CallgraphMock{} + +// fsMock := ioTestData.FileSystemMock{} +// zip := ioTestData.ZipMock{} +// archiveMock := io.NewArchiveWithStructs("dir", fsMock, zip) + +// fs := io.FileSystem{} + +// j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) +// j.runCallGraph(callgraphMock) + +// assert.False(t, j.Errors().HasError()) +// } + +// func TestRunCallgraphMockError(t *testing.T) { +// fileWriterMock := &ioTestData.FileWriterMock{} +// cmdFactoryMock := testdata.NewEchoCmdFactory() +// config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") +// ctx, _ := ctxTestdata.NewContextMock() +// callgraphMock := testdata.CallgraphMock{RunCallGraphWithSetupError: fmt.Errorf("error")} + +// fsMock := ioTestData.FileSystemMock{} +// zip := ioTestData.ZipMock{} +// archiveMock := io.NewArchiveWithStructs("dir", fsMock, zip) + +// fs := io.FileSystem{} + +// j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) +// j.runCallGraph(callgraphMock) + +// assert.True(t, j.Errors().HasError()) +// } + +// func TestRunPostProcessMock(t *testing.T) { +// fileWriterMock := &ioTestData.FileWriterMock{} +// cmdFactoryMock := testdata.NewEchoCmdFactory() +// config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") +// ctx, _ := ctxTestdata.NewContextMock() + +// fsMock := ioTestData.FileSystemMock{} +// zip := ioTestData.ZipMock{} +// archiveMock := io.NewArchiveWithStructs(dir, fsMock, zip) + +// fs := io.FileSystem{} + +// j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) +// go jobTestdata.WaitStatus(j) +// j.runPostProcess() + +// assert.False(t, j.Errors().HasError()) +// } + +// func TestRunPostProcessZipFileError(t *testing.T) { +// fileWriterMock := &ioTestData.FileWriterMock{} +// cmdFactoryMock := testdata.NewEchoCmdFactory() +// config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") +// ctx, _ := ctxTestdata.NewContextMock() +// archiveMock := ioTestData.ArchiveMock{ZipFileError: fmt.Errorf("error")} +// fs := io.FileSystem{} + +// j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) +// go jobTestdata.WaitStatus(j) +// j.runPostProcess() + +// assert.True(t, j.Errors().HasError()) +// } + +// func TestRunPostProcessB64Error(t *testing.T) { +// fileWriterMock := &ioTestData.FileWriterMock{} +// cmdFactoryMock := testdata.NewEchoCmdFactory() +// config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") +// ctx, _ := ctxTestdata.NewContextMock() +// fs := io.FileSystem{} + +// archiveMock := ioTestData.ArchiveMock{B64Error: fmt.Errorf("error")} + +// j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) +// go jobTestdata.WaitStatus(j) +// j.runPostProcess() + +// assert.True(t, j.Errors().HasError()) +// } + +// func TestRunPostProcessCleanupError(t *testing.T) { +// fileWriterMock := &ioTestData.FileWriterMock{} +// cmdFactoryMock := testdata.NewEchoCmdFactory() +// config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") +// ctx, _ := ctxTestdata.NewContextMock() +// fs := io.FileSystem{} + +// archiveMock := ioTestData.ArchiveMock{CleanupError: fmt.Errorf("error")} + +// j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) +// go jobTestdata.WaitStatus(j) +// j.runPostProcess() + +// assert.True(t, j.Errors().HasError()) +// } + +// func TestRunPostProcessCleanupNoFileExistError(t *testing.T) { +// fileWriterMock := &ioTestData.FileWriterMock{} +// cmdFactoryMock := testdata.NewEchoCmdFactory() +// config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") +// ctx, _ := ctxTestdata.NewContextMock() +// fs := io.FileSystem{} + +// err := &os.PathError{} +// err.Err = syscall.ENOENT +// archiveMock := ioTestData.ArchiveMock{CleanupError: err} + +// j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) +// go jobTestdata.WaitStatus(j) +// j.runPostProcess() + +// assert.False(t, j.Errors().HasError()) +// } + +// func TestRunPostProcessFromRoot(t *testing.T) { +// fileWriterMock := &ioTestData.FileWriterMock{} +// cmdFactoryMock := testdata.NewEchoCmdFactory() +// config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") +// ctx, _ := ctxTestdata.NewContextMock() +// fs := io.FileSystem{} + +// err := &os.PathError{} +// err.Err = syscall.ENOENT +// archiveMock := ioTestData.ArchiveMock{PathError: err, Dir: "."} + +// j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) +// go jobTestdata.WaitStatus(j) +// j.runPostProcess() + +// jobErrors := j.Errors().GetAll() +// assert.True(t, jobErrors[0] == err) + +// } + +// func TestRunWithErrorsIsNotExistFalse(t *testing.T) { +// fileWriterMock := &ioTestData.FileWriterMock{} +// cmdFactoryMock := testdata.NewEchoCmdFactory() + +// config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") +// ctx, _ := ctxTestdata.NewContextMock() + +// fs := ioTestData.FileSystemMock{} +// zip := ioTestData.ZipMock{} +// archiveMock := io.NewArchiveWithStructs("dir", fs, zip) + +// fs.IsNotExistBool = false + +// j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) +// j.Errors().Critical(fmt.Errorf("error")) + +// go jobTestdata.WaitStatus(j) +// j.Run() + +// assert.True(t, j.Errors().HasError()) + +// fs.IsNotExistBool = true +// } + +// func TestRunWithErrorsIsNotExistTrue(t *testing.T) { +// fileWriterMock := &ioTestData.FileWriterMock{} +// cmdFactoryMock := testdata.NewEchoCmdFactory() + +// config := conf.NewConfig("java", nil, map[string]string{"pm": maven}, true, "maven") +// ctx, _ := ctxTestdata.NewContextMock() + +// fs := ioTestData.FileSystemMock{} +// zip := ioTestData.ZipMock{} +// archiveMock := io.NewArchiveWithStructs("dir", fs, zip) + +// fs.IsNotExistBool = true + +// j := NewJob(dir, files, cmdFactoryMock, fileWriterMock, archiveMock, config, ctx, fs) +// j.Errors().Critical(fmt.Errorf("error")) + +// go jobTestdata.WaitStatus(j) +// j.Run() + +// assert.True(t, j.Errors().HasError()) +// } diff --git a/internal/callgraph/language/golang/language.go b/internal/callgraph/language/golang/language.go new file mode 100644 index 00000000..658760e7 --- /dev/null +++ b/internal/callgraph/language/golang/language.go @@ -0,0 +1,24 @@ +package golang + +const Name = "golang" +const StandardVersion = "1" + +type Language struct { + name string + version string +} + +func NewLanguage() Language { + return Language{ + name: Name, + version: StandardVersion, + } +} + +func (language Language) Name() string { + return language.name +} + +func (language Language) Version() string { + return language.version +} diff --git a/internal/callgraph/language/golang/language_test.go b/internal/callgraph/language/golang/language_test.go new file mode 100644 index 00000000..394865ac --- /dev/null +++ b/internal/callgraph/language/golang/language_test.go @@ -0,0 +1,23 @@ +package golang + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewLanguage(t *testing.T) { + pm := NewLanguage() + assert.Equal(t, Name, pm.name) + assert.Equal(t, StandardVersion, pm.version) +} + +func TestName(t *testing.T) { + pm := NewLanguage() + assert.Equal(t, Name, pm.Name()) +} + +func TestVersion(t *testing.T) { + pm := NewLanguage() + assert.Equal(t, StandardVersion, pm.Version()) +} diff --git a/internal/callgraph/language/golang/stategy.go b/internal/callgraph/language/golang/stategy.go new file mode 100644 index 00000000..2ede3ae6 --- /dev/null +++ b/internal/callgraph/language/golang/stategy.go @@ -0,0 +1,89 @@ +package golang + +import ( + "fmt" + "log" + + "github.com/debricked/cli/internal/callgraph/cgexec" + conf "github.com/debricked/cli/internal/callgraph/config" + "github.com/debricked/cli/internal/callgraph/finder" + "github.com/debricked/cli/internal/callgraph/job" + "github.com/debricked/cli/internal/io" + "github.com/fatih/color" +) + +type Strategy struct { + config conf.IConfig + cmdFactory ICmdFactory + paths []string + exclusions []string + finder finder.IFinder + ctx cgexec.IContext +} + +func (s Strategy) Invoke() ([]job.IJob, error) { + var jobs []job.IJob + + if s.config == nil { + strategyWarning("No config is setup") + + return jobs, nil + } + + for _, path := range s.paths { + + files, err := s.finder.FindFiles([]string{path}, s.exclusions) + if err != nil { + strategyWarning("Error while finding files: " + err.Error()) + + return jobs, err + } + + roots, err := s.finder.FindRoots(files) + if err != nil { + strategyWarning("Error while finding roots: " + err.Error()) + + return jobs, err + } + + if len(roots) == 0 { + strategyWarning("No main.go found") + + return jobs, nil + } + + if path != "." { + for i, _ := range roots { + roots[i] = roots[i][len(path)+1:] + } + } + + for _, rootFile := range roots { + + jobs = append(jobs, NewJob( + path, + rootFile, + s.cmdFactory, + io.FileWriter{}, + io.NewArchive("."), + s.config, + s.ctx, + io.FileSystem{}, + ), + ) + } + } + return jobs, nil +} + +func NewStrategy(config conf.IConfig, paths []string, exclusions []string, finder finder.IFinder, ctx cgexec.IContext) Strategy { + return Strategy{config, CmdFactory{}, paths, exclusions, finder, ctx} +} + +func strategyWarning(errMsg string) { + err := fmt.Errorf(errMsg) + warningColor := color.New(color.FgYellow, color.Bold).SprintFunc() + defaultOutputWriter := log.Writer() + log.Println(warningColor("Warning: ") + err.Error()) + log.SetOutput(defaultOutputWriter) +} diff --git a/internal/callgraph/language/golang/strategy_test.go b/internal/callgraph/language/golang/strategy_test.go new file mode 100644 index 00000000..398b6df3 --- /dev/null +++ b/internal/callgraph/language/golang/strategy_test.go @@ -0,0 +1,81 @@ +package golang + +import ( + "path/filepath" + "testing" + + ctxTestdata "github.com/debricked/cli/internal/callgraph/cgexec/testdata" + "github.com/debricked/cli/internal/callgraph/config" + "github.com/debricked/cli/internal/callgraph/finder/testdata" + "github.com/stretchr/testify/assert" +) + +func TestNewStrategy(t *testing.T) { + s := NewStrategy(nil, nil, nil, nil, nil) + assert.NotNil(t, s) + + s = NewStrategy(nil, []string{}, []string{}, nil, nil) + assert.NotNil(t, s) + + s = NewStrategy(nil, []string{"file"}, []string{}, nil, nil) + assert.NotNil(t, s) + + s = NewStrategy(nil, []string{"file-1", "file-2"}, []string{}, nil, nil) + assert.NotNil(t, s) + + conf := config.NewConfig("java", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "maven") + finder := testdata.NewEmptyFinderMock() + testFiles := []string{"file-1"} + finder.FindMavenRootsNames = testFiles + ctx, _ := ctxTestdata.NewContextMock() + s = NewStrategy(conf, testFiles, []string{}, finder, ctx) + assert.NotNil(t, s) + assert.Equal(t, s.config, conf) +} + +func TestInvokeNoFiles(t *testing.T) { + s := NewStrategy(nil, []string{}, []string{}, nil, nil) + jobs, _ := s.Invoke() + assert.Empty(t, jobs) +} + +func TestInvokeOneFile(t *testing.T) { + conf := config.NewConfig("java", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "maven") + finder := testdata.NewEmptyFinderMock() + testFiles := []string{"file-1"} + finder.FindMavenRootsNames = testFiles + ctx, _ := ctxTestdata.NewContextMock() + s := NewStrategy(conf, testFiles, []string{}, finder, ctx) + jobs, _ := s.Invoke() + assert.Len(t, jobs, 0) +} + +func TestInvokeManyFiles(t *testing.T) { + conf := config.NewConfig("java", []string{"arg1"}, map[string]string{"kwarg": "val"}, true, "maven") + finder := testdata.NewEmptyFinderMock() + testFiles := []string{"file-1", "file-2"} + finder.FindMavenRootsNames = testFiles + ctx, _ := ctxTestdata.NewContextMock() + s := NewStrategy(conf, testFiles, []string{}, finder, ctx) + jobs, _ := s.Invoke() + assert.Len(t, jobs, 0) +} + +func TestInvokeManyFilesWCorrectFilters(t *testing.T) { + conf := config.NewConfig("java", []string{"arg1"}, map[string]string{"kwarg": "val"}, false, "maven") + finder := testdata.NewEmptyFinderMock() + testFiles := []string{"file-1", "file-2", "file-3"} + finder.FindMavenRootsNames = []string{"file-3/pom.xml"} + finder.FindDependencyDirsNames = []string{"file-3/test.class"} + ctx, _ := ctxTestdata.NewContextMock() + s := NewStrategy(conf, testFiles, []string{"test"}, finder, ctx) + jobs, _ := s.Invoke() + assert.Len(t, jobs, 1) + for _, job := range jobs { + file, _ := filepath.Abs("file-3/test.class") + dir, _ := filepath.Abs("file-3/") + assert.Equal(t, job.GetFiles(), []string{file}) + assert.Equal(t, job.GetDir(), dir) + + } +} diff --git a/internal/callgraph/language/java11/stategy.go b/internal/callgraph/language/java11/stategy.go index f7069d61..194cc5f1 100644 --- a/internal/callgraph/language/java11/stategy.go +++ b/internal/callgraph/language/java11/stategy.go @@ -52,7 +52,7 @@ func (s Strategy) Invoke() ([]job.IJob, error) { // roots, err = s.finder.FindMavenRoots(s.files) // } - roots, err = s.finder.FindMavenRoots(files) + roots, err = s.finder.FindRoots(files) if err != nil { strategyWarning("Error while finding roots: " + err.Error()) @@ -70,7 +70,7 @@ func (s Strategy) Invoke() ([]job.IJob, error) { files, _ = s.finder.FindFiles(s.paths, s.exclusions) } - javaClassDirs, _ := s.finder.FindJavaClassDirs(files, false) + javaClassDirs, _ := s.finder.FindDependencyDirs(files, false) absRoots, _ := finder.ConvertPathsToAbsPaths(roots) absClassDirs, _ := finder.ConvertPathsToAbsPaths(javaClassDirs) rootClassMapping := finder.MapFilesToDir(absRoots, absClassDirs) diff --git a/internal/callgraph/language/java11/strategy_test.go b/internal/callgraph/language/java11/strategy_test.go index af7c26c9..76235d72 100644 --- a/internal/callgraph/language/java11/strategy_test.go +++ b/internal/callgraph/language/java11/strategy_test.go @@ -68,7 +68,7 @@ func TestInvokeManyFilesWCorrectFilters(t *testing.T) { finder := testdata.NewEmptyFinderMock() testFiles := []string{"file-1", "file-2", "file-3"} finder.FindMavenRootsNames = []string{"file-3/pom.xml"} - finder.FindJavaClassDirsNames = []string{"file-3/test.class"} + finder.FindDependencyDirsNames = []string{"file-3/test.class"} ctx, _ := ctxTestdata.NewContextMock() s := NewStrategy(conf, testFiles, []string{"test"}, finder, ctx) jobs, _ := s.Invoke() @@ -87,7 +87,7 @@ func TestBuildProjectsError(t *testing.T) { finder := testdata.NewEmptyFinderMock() testFiles := []string{"file-1", "file-2", "file-3"} finder.FindMavenRootsNames = []string{"file-3/pom.xml"} - finder.FindJavaClassDirsNames = []string{"file-3/test.class"} + finder.FindDependencyDirsNames = []string{"file-3/test.class"} ctx, _ := ctxTestdata.NewContextMock() s := NewStrategy(conf, testFiles, []string{"test"}, finder, ctx) factoryMock := javaTestdata.NewEchoCmdFactory() diff --git a/internal/callgraph/strategy/strategy_factory.go b/internal/callgraph/strategy/strategy_factory.go index 61d11ff4..da4d34a8 100644 --- a/internal/callgraph/strategy/strategy_factory.go +++ b/internal/callgraph/strategy/strategy_factory.go @@ -5,7 +5,9 @@ import ( "github.com/debricked/cli/internal/callgraph/cgexec" conf "github.com/debricked/cli/internal/callgraph/config" + golanfinder "github.com/debricked/cli/internal/callgraph/finder/golangfinder" "github.com/debricked/cli/internal/callgraph/finder/javafinder" + "github.com/debricked/cli/internal/callgraph/language/golang" java "github.com/debricked/cli/internal/callgraph/language/java11" ) @@ -24,8 +26,8 @@ func (sf Factory) Make(config conf.IConfig, paths []string, exclusions []string, switch name { case java.Name: return java.NewStrategy(config, paths, exclusions, javafinder.JavaFinder{}, ctx), nil - // case python.Name: - // return python.NewStrategy(config, paths, exclusions, finder, ctx), nil + case golang.Name: + return golang.NewStrategy(config, paths, exclusions, golanfinder.GolangFinder{}, ctx), nil default: return nil, fmt.Errorf("failed to make strategy from %s", name) } diff --git a/internal/cmd/callgraph/callgraph.go b/internal/cmd/callgraph/callgraph.go index 9d8d5285..3e04aef2 100644 --- a/internal/cmd/callgraph/callgraph.go +++ b/internal/cmd/callgraph/callgraph.go @@ -26,6 +26,13 @@ var buildDisabled bool var generateTimeout int var languages string +var supportedLanguages = []string{"java", "golang"} + +var languageMap = map[string]string{ + "java": "maven", + "golang": "go", +} + func NewCallgraphCmd(generator callgraph.IGenerator) *cobra.Command { cmd := &cobra.Command{ Use: "callgraph [path]", @@ -65,19 +72,13 @@ $ debricked callgraph . `+exampleFlags) This option requires a pre-built project. For more detailed documentation on the callgraph generation, visit our portal: https://portal.debricked.com/debricked-cli-63/debricked-cli-documentation-298?tid=298&fid=63#callgraph`) cmd.Flags().IntVar(&generateTimeout, GenerateTimeoutFlag, 60*60, "Timeout (in seconds) on call graph generation.") - cmd.Flags().StringVarP(&languages, LanguagesFlag, "l", "java,python", "Colon separated list of languages to create a call graph for.") + cmd.Flags().StringVarP(&languages, LanguagesFlag, "l", strings.Join(supportedLanguages, ","), "Colon separated list of languages to create a call graph for.") viper.MustBindEnv(ExclusionFlag) return cmd } -var supportedLanguages = []string{"java"} - -var languageMap = map[string]string{ - "java": "maven", -} - func parseAndValidateLanguages(languages string) ([]string, error) { parsedLanguages := strings.Split(languages, ",")