From 47e029e5f3c240ff3a9a4d9fe3cbcf7db573b687 Mon Sep 17 00:00:00 2001 From: suraj-jadhav Date: Tue, 20 Jan 2026 15:33:09 +0530 Subject: [PATCH 1/6] Add Poetry CLI support for generating lock file from manifest on client side --- build/docker/alpine.Dockerfile | 5 ++ build/docker/debian.Dockerfile | 5 ++ internal/client/deb_client.go | 2 +- .../temp-fingerprint-264121728.txt | 0 .../project/.debricked.multiprojects.txt | 1 + .../subproject/.debricked.multiprojects.txt | 1 + internal/resolution/pm/pm.go | 2 + internal/resolution/pm/poetry/cmd_factory.go | 44 ++++++++++ .../resolution/pm/poetry/cmd_factory_test.go | 27 ++++++ internal/resolution/pm/poetry/job.go | 86 +++++++++++++++++++ internal/resolution/pm/poetry/job_test.go | 41 +++++++++ internal/resolution/pm/poetry/pm.go | 23 +++++ internal/resolution/pm/poetry/pm_test.go | 24 ++++++ internal/resolution/pm/poetry/strategy.go | 23 +++++ .../resolution/pm/poetry/strategy_test.go | 35 ++++++++ .../pm/poetry/testdata/cmd_factory_mock.go | 24 ++++++ .../resolution/strategy/strategy_factory.go | 3 + .../strategy/strategy_factory_test.go | 2 + 18 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 internal/fingerprint/testdata/fingerprinter/temp-fingerprint-264121728.txt create mode 100644 internal/resolution/pm/gradle/testdata/project/.debricked.multiprojects.txt create mode 100644 internal/resolution/pm/gradle/testdata/project/subproject/.debricked.multiprojects.txt create mode 100644 internal/resolution/pm/poetry/cmd_factory.go create mode 100644 internal/resolution/pm/poetry/cmd_factory_test.go create mode 100644 internal/resolution/pm/poetry/job.go create mode 100644 internal/resolution/pm/poetry/job_test.go create mode 100644 internal/resolution/pm/poetry/pm.go create mode 100644 internal/resolution/pm/poetry/pm_test.go create mode 100644 internal/resolution/pm/poetry/strategy.go create mode 100644 internal/resolution/pm/poetry/strategy_test.go create mode 100644 internal/resolution/pm/poetry/testdata/cmd_factory_mock.go diff --git a/build/docker/alpine.Dockerfile b/build/docker/alpine.Dockerfile index 1cdae474..780f8aba 100644 --- a/build/docker/alpine.Dockerfile +++ b/build/docker/alpine.Dockerfile @@ -86,6 +86,11 @@ RUN apk add --no-cache --virtual build-dependencies curl && \ RUN php -v && composer --version && sbt --version +# Install Poetry for Python resolution (pyproject.toml) +RUN curl -sSL https://install.python-poetry.org | python3 - && \ + ln -s /root/.local/bin/poetry /usr/local/bin/poetry && \ + poetry --version + CMD [ "debricked", "scan" ] # Put copy at the end to speedup Docker build by caching previous RUNs and run those concurrently diff --git a/build/docker/debian.Dockerfile b/build/docker/debian.Dockerfile index 3eb8caeb..84a9911f 100644 --- a/build/docker/debian.Dockerfile +++ b/build/docker/debian.Dockerfile @@ -134,6 +134,11 @@ RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin - RUN ln -sf /usr/bin/python3.13 /usr/bin/python3 && php -v && composer --version && python3 --version +# Install Poetry for Python resolution (pyproject.toml) +RUN curl -sSL https://install.python-poetry.org | python3 - && \ + ln -s /root/.local/bin/poetry /usr/local/bin/poetry && \ + poetry --version + CMD [ "debricked", "scan" ] # Put copy at the end to speedup Docker build by caching previous RUNs and run those concurrently diff --git a/internal/client/deb_client.go b/internal/client/deb_client.go index a1714cef..80c58500 100644 --- a/internal/client/deb_client.go +++ b/internal/client/deb_client.go @@ -13,7 +13,7 @@ import ( "github.com/fatih/color" ) -const DefaultDebrickedUri = "https://debricked.com" +const DefaultDebrickedUri = "http://localhost:8888" const DefaultTimeout = 15 const enterpriseCheckUri = "/api/1.0/open/user-profile/get-billing-info" diff --git a/internal/fingerprint/testdata/fingerprinter/temp-fingerprint-264121728.txt b/internal/fingerprint/testdata/fingerprinter/temp-fingerprint-264121728.txt new file mode 100644 index 00000000..e69de29b diff --git a/internal/resolution/pm/gradle/testdata/project/.debricked.multiprojects.txt b/internal/resolution/pm/gradle/testdata/project/.debricked.multiprojects.txt new file mode 100644 index 00000000..8f5eacb3 --- /dev/null +++ b/internal/resolution/pm/gradle/testdata/project/.debricked.multiprojects.txt @@ -0,0 +1 @@ +C:\Users\sjadhav2\Debricked\cli\internal\resolution\pm\gradle\testdata\project \ No newline at end of file diff --git a/internal/resolution/pm/gradle/testdata/project/subproject/.debricked.multiprojects.txt b/internal/resolution/pm/gradle/testdata/project/subproject/.debricked.multiprojects.txt new file mode 100644 index 00000000..9cc18254 --- /dev/null +++ b/internal/resolution/pm/gradle/testdata/project/subproject/.debricked.multiprojects.txt @@ -0,0 +1 @@ +C:\Users\sjadhav2\Debricked\cli\internal\resolution\pm\gradle\testdata\project\subproject \ No newline at end of file diff --git a/internal/resolution/pm/pm.go b/internal/resolution/pm/pm.go index c8312526..8d4790e9 100644 --- a/internal/resolution/pm/pm.go +++ b/internal/resolution/pm/pm.go @@ -9,6 +9,7 @@ import ( "github.com/debricked/cli/internal/resolution/pm/npm" "github.com/debricked/cli/internal/resolution/pm/nuget" "github.com/debricked/cli/internal/resolution/pm/pip" + "github.com/debricked/cli/internal/resolution/pm/poetry" "github.com/debricked/cli/internal/resolution/pm/sbt" "github.com/debricked/cli/internal/resolution/pm/yarn" ) @@ -24,6 +25,7 @@ func Pms() []IPm { gradle.NewPm(), gomod.NewPm(), pip.NewPm(), + poetry.NewPm(), yarn.NewPm(), npm.NewPm(), bower.NewPm(), diff --git a/internal/resolution/pm/poetry/cmd_factory.go b/internal/resolution/pm/poetry/cmd_factory.go new file mode 100644 index 00000000..a975283b --- /dev/null +++ b/internal/resolution/pm/poetry/cmd_factory.go @@ -0,0 +1,44 @@ +package poetry + +import ( + "os" + "os/exec" + "path/filepath" +) + +type ICmdFactory interface { + MakeLockCmd(manifestFile string) (*exec.Cmd, error) +} + +type IExecPath interface { + LookPath(file string) (string, error) +} + +type ExecPath struct{} + +func (_ ExecPath) LookPath(file string) (string, error) { + return exec.LookPath(file) +} + +type CmdFactory struct { + execPath IExecPath +} + +func (cmdf CmdFactory) MakeLockCmd(manifestFile string) (*exec.Cmd, error) { + poetryPath, err := cmdf.execPath.LookPath("poetry") + if err != nil { + return nil, err + } + + workingDir := filepath.Dir(filepath.Clean(manifestFile)) + + env := os.Environ() + env = append(env, "POETRY_VIRTUALENVS_CREATE=false") + + return &exec.Cmd{ + Path: poetryPath, + Args: []string{"poetry", "lock"}, + Dir: workingDir, + Env: env, + }, nil +} diff --git a/internal/resolution/pm/poetry/cmd_factory_test.go b/internal/resolution/pm/poetry/cmd_factory_test.go new file mode 100644 index 00000000..8ff50e6e --- /dev/null +++ b/internal/resolution/pm/poetry/cmd_factory_test.go @@ -0,0 +1,27 @@ +package poetry + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +type execPathMock struct{} + +func (execPathMock) LookPath(file string) (string, error) { + return "/usr/bin/" + file, nil +} + +func TestMakeLockCmd(t *testing.T) { + factory := CmdFactory{execPath: execPathMock{}} + manifest := filepath.Join("some", "path", "pyproject.toml") + + cmd, err := factory.MakeLockCmd(manifest) + assert.NoError(t, err) + assert.NotNil(t, cmd) + assert.Equal(t, "/usr/bin/poetry", cmd.Path) + assert.Contains(t, cmd.Args, "poetry") + assert.Contains(t, cmd.Args, "lock") + assert.Equal(t, filepath.Dir(manifest), cmd.Dir) +} diff --git a/internal/resolution/pm/poetry/job.go b/internal/resolution/pm/poetry/job.go new file mode 100644 index 00000000..54614f62 --- /dev/null +++ b/internal/resolution/pm/poetry/job.go @@ -0,0 +1,86 @@ +package poetry + +import ( + "regexp" + "strings" + + "github.com/debricked/cli/internal/resolution/job" + "github.com/debricked/cli/internal/resolution/pm/util" +) + +const ( + executableNotFoundErrRegex = `executable file not found` +) + +type Job struct { + job.BaseJob + cmdFactory ICmdFactory +} + +func NewJob(file string, cmdFactory ICmdFactory) *Job { + return &Job{ + BaseJob: job.NewBaseJob(file), + cmdFactory: cmdFactory, + } +} + +func (j *Job) Run() { + status := "generating poetry.lock" + j.SendStatus(status) + + lockCmd, err := j.cmdFactory.MakeLockCmd(j.GetFile()) + if err != nil { + j.handleError(j.createError(err.Error(), "", status)) + + return + } + + if output, err := lockCmd.Output(); err != nil { + exitErr := j.GetExitError(err, string(output)) + errorMessage := strings.Join([]string{string(output), exitErr.Error()}, "") + j.handleError(j.createError(errorMessage, lockCmd.String(), status)) + + return + } +} + +func (j *Job) createError(errorStr string, cmd string, status string) job.IError { + cmdError := util.NewPMJobError(errorStr) + cmdError.SetCommand(cmd) + cmdError.SetStatus(status) + + return cmdError +} + +func (j *Job) handleError(cmdError job.IError) { + expressions := []string{ + executableNotFoundErrRegex, + } + + for _, expression := range expressions { + regex := regexp.MustCompile(expression) + matches := regex.FindAllStringSubmatch(cmdError.Error(), -1) + + if len(matches) > 0 { + cmdError = j.addDocumentation(expression, matches, cmdError) + j.Errors().Append(cmdError) + + return + } + } + + j.Errors().Append(cmdError) +} + +func (j *Job) addDocumentation(expr string, _ [][]string, cmdError job.IError) job.IError { + documentation := cmdError.Documentation() + + switch expr { + case executableNotFoundErrRegex: + documentation = j.GetExecutableNotFoundErrorDocumentation("Poetry") + } + + cmdError.SetDocumentation(documentation) + + return cmdError +} diff --git a/internal/resolution/pm/poetry/job_test.go b/internal/resolution/pm/poetry/job_test.go new file mode 100644 index 00000000..11ebc69b --- /dev/null +++ b/internal/resolution/pm/poetry/job_test.go @@ -0,0 +1,41 @@ +package poetry + +import ( + "errors" + "testing" + + jobTestdata "github.com/debricked/cli/internal/resolution/job/testdata" + "github.com/debricked/cli/internal/resolution/pm/poetry/testdata" + "github.com/stretchr/testify/assert" +) + +func TestNewJob(t *testing.T) { + j := NewJob("file", testdata.CmdFactoryMock{}) + assert.Equal(t, "file", j.GetFile()) + assert.False(t, j.Errors().HasError()) +} + +func TestRunCmdErrExecutableNotFound(t *testing.T) { + execErr := errors.New("exec: \"poetry\": executable file not found in $PATH") + j := NewJob("file", testdata.CmdFactoryMock{Err: execErr}) + + go jobTestdata.WaitStatus(j) + + j.Run() + + errs := j.Errors().GetAll() + assert.Len(t, errs, 1) + assert.Contains(t, errs[0].Error(), "executable file not found") + assert.Contains(t, errs[0].Documentation(), "Poetry wasn't found") +} + +func TestRunSuccess(t *testing.T) { + j := NewJob("file", testdata.CmdFactoryMock{Name: "echo", Arg: "ok"}) + + go jobTestdata.WaitStatus(j) + + j.Run() + + assert.False(t, j.Errors().HasError()) + assert.Len(t, j.Errors().GetAll(), 0) +} diff --git a/internal/resolution/pm/poetry/pm.go b/internal/resolution/pm/poetry/pm.go new file mode 100644 index 00000000..c01dc1d8 --- /dev/null +++ b/internal/resolution/pm/poetry/pm.go @@ -0,0 +1,23 @@ +package poetry + +const Name = "poetry" + +type Pm struct { + name string +} + +func NewPm() Pm { + return Pm{ + name: Name, + } +} + +func (pm Pm) Name() string { + return pm.name +} + +func (_ Pm) Manifests() []string { + return []string{ + "pyproject.toml", + } +} diff --git a/internal/resolution/pm/poetry/pm_test.go b/internal/resolution/pm/poetry/pm_test.go new file mode 100644 index 00000000..91d5545c --- /dev/null +++ b/internal/resolution/pm/poetry/pm_test.go @@ -0,0 +1,24 @@ +package poetry + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewPm(t *testing.T) { + pm := NewPm() + assert.Equal(t, Name, pm.name) +} + +func TestName(t *testing.T) { + pm := NewPm() + assert.Equal(t, Name, pm.Name()) +} + +func TestManifests(t *testing.T) { + pm := Pm{} + manifests := pm.Manifests() + assert.Len(t, manifests, 1) + assert.Equal(t, "pyproject.toml", manifests[0]) +} diff --git a/internal/resolution/pm/poetry/strategy.go b/internal/resolution/pm/poetry/strategy.go new file mode 100644 index 00000000..dc544325 --- /dev/null +++ b/internal/resolution/pm/poetry/strategy.go @@ -0,0 +1,23 @@ +package poetry + +import "github.com/debricked/cli/internal/resolution/job" + +type Strategy struct { + files []string +} + +func NewStrategy(files []string) Strategy { + return Strategy{files: files} +} + +func (s Strategy) Invoke() ([]job.IJob, error) { + var jobs []job.IJob + for _, file := range s.files { + jobs = append(jobs, NewJob( + file, + CmdFactory{execPath: ExecPath{}}, + )) + } + + return jobs, nil +} diff --git a/internal/resolution/pm/poetry/strategy_test.go b/internal/resolution/pm/poetry/strategy_test.go new file mode 100644 index 00000000..a8abcdbc --- /dev/null +++ b/internal/resolution/pm/poetry/strategy_test.go @@ -0,0 +1,35 @@ +package poetry + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewStrategy(t *testing.T) { + s := NewStrategy(nil) + assert.NotNil(t, s) + assert.Len(t, s.files, 0) + + s = NewStrategy([]string{"file"}) + assert.Len(t, s.files, 1) +} + +func TestStrategyInvoke(t *testing.T) { + cases := [][]string{ + {}, + {"pyproject.toml"}, + {"a/pyproject.toml", "b/pyproject.toml"}, + } + + for _, files := range cases { + filesCopy := append([]string{}, files...) + name := "len=" + string(rune(len(filesCopy))) + t.Run(name, func(t *testing.T) { + s := NewStrategy(filesCopy) + jobs, err := s.Invoke() + assert.NoError(t, err) + assert.Len(t, jobs, len(filesCopy)) + }) + } +} diff --git a/internal/resolution/pm/poetry/testdata/cmd_factory_mock.go b/internal/resolution/pm/poetry/testdata/cmd_factory_mock.go new file mode 100644 index 00000000..833a4ff0 --- /dev/null +++ b/internal/resolution/pm/poetry/testdata/cmd_factory_mock.go @@ -0,0 +1,24 @@ +package testdata + +import ( + "os/exec" + "runtime" +) + +type CmdFactoryMock struct { + Err error + Name string + Arg string +} + +func (f CmdFactoryMock) MakeLockCmd(_ string) (*exec.Cmd, error) { + if len(f.Arg) == 0 { + f.Arg = `"MakeLockCmd"` + } + + if runtime.GOOS == "windows" && f.Name == "echo" { + return exec.Command("cmd", "/C", f.Name, f.Arg), f.Err + } + + return exec.Command(f.Name, f.Arg), f.Err +} diff --git a/internal/resolution/strategy/strategy_factory.go b/internal/resolution/strategy/strategy_factory.go index 20f4148a..963cf0fe 100644 --- a/internal/resolution/strategy/strategy_factory.go +++ b/internal/resolution/strategy/strategy_factory.go @@ -12,6 +12,7 @@ import ( "github.com/debricked/cli/internal/resolution/pm/npm" "github.com/debricked/cli/internal/resolution/pm/nuget" "github.com/debricked/cli/internal/resolution/pm/pip" + "github.com/debricked/cli/internal/resolution/pm/poetry" "github.com/debricked/cli/internal/resolution/pm/sbt" "github.com/debricked/cli/internal/resolution/pm/yarn" ) @@ -38,6 +39,8 @@ func (sf Factory) Make(pmFileBatch file.IBatch, paths []string) (IStrategy, erro return gomod.NewStrategy(pmFileBatch.Files()), nil case pip.Name: return pip.NewStrategy(pmFileBatch.Files()), nil + case poetry.Name: + return poetry.NewStrategy(pmFileBatch.Files()), nil case yarn.Name: return yarn.NewStrategy(pmFileBatch.Files()), nil case npm.Name: diff --git a/internal/resolution/strategy/strategy_factory_test.go b/internal/resolution/strategy/strategy_factory_test.go index 11538a98..1b82db6b 100644 --- a/internal/resolution/strategy/strategy_factory_test.go +++ b/internal/resolution/strategy/strategy_factory_test.go @@ -10,6 +10,7 @@ import ( "github.com/debricked/cli/internal/resolution/pm/maven" "github.com/debricked/cli/internal/resolution/pm/nuget" "github.com/debricked/cli/internal/resolution/pm/pip" + "github.com/debricked/cli/internal/resolution/pm/poetry" "github.com/debricked/cli/internal/resolution/pm/sbt" "github.com/debricked/cli/internal/resolution/pm/testdata" "github.com/debricked/cli/internal/resolution/pm/yarn" @@ -35,6 +36,7 @@ func TestMake(t *testing.T) { gradle.Name: gradle.NewStrategy(nil, nil), gomod.Name: gomod.NewStrategy(nil), pip.Name: pip.NewStrategy(nil), + poetry.Name: poetry.NewStrategy(nil), yarn.Name: yarn.NewStrategy(nil), nuget.Name: nuget.NewStrategy(nil), composer.Name: composer.NewStrategy(nil), From d13d9188f458736eddb6e993b6ffb1e13a274199 Mon Sep 17 00:00:00 2001 From: suraj-jadhav Date: Tue, 20 Jan 2026 15:45:12 +0530 Subject: [PATCH 2/6] Add Poetry CLI support for generating lock file from manifest on client side --- internal/client/deb_client.go | 2 +- .../testdata/fingerprinter/temp-fingerprint-264121728.txt | 0 .../pm/gradle/testdata/project/.debricked.multiprojects.txt | 1 - .../testdata/project/subproject/.debricked.multiprojects.txt | 1 - 4 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 internal/fingerprint/testdata/fingerprinter/temp-fingerprint-264121728.txt delete mode 100644 internal/resolution/pm/gradle/testdata/project/.debricked.multiprojects.txt delete mode 100644 internal/resolution/pm/gradle/testdata/project/subproject/.debricked.multiprojects.txt diff --git a/internal/client/deb_client.go b/internal/client/deb_client.go index 80c58500..a1714cef 100644 --- a/internal/client/deb_client.go +++ b/internal/client/deb_client.go @@ -13,7 +13,7 @@ import ( "github.com/fatih/color" ) -const DefaultDebrickedUri = "http://localhost:8888" +const DefaultDebrickedUri = "https://debricked.com" const DefaultTimeout = 15 const enterpriseCheckUri = "/api/1.0/open/user-profile/get-billing-info" diff --git a/internal/fingerprint/testdata/fingerprinter/temp-fingerprint-264121728.txt b/internal/fingerprint/testdata/fingerprinter/temp-fingerprint-264121728.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/internal/resolution/pm/gradle/testdata/project/.debricked.multiprojects.txt b/internal/resolution/pm/gradle/testdata/project/.debricked.multiprojects.txt deleted file mode 100644 index 8f5eacb3..00000000 --- a/internal/resolution/pm/gradle/testdata/project/.debricked.multiprojects.txt +++ /dev/null @@ -1 +0,0 @@ -C:\Users\sjadhav2\Debricked\cli\internal\resolution\pm\gradle\testdata\project \ No newline at end of file diff --git a/internal/resolution/pm/gradle/testdata/project/subproject/.debricked.multiprojects.txt b/internal/resolution/pm/gradle/testdata/project/subproject/.debricked.multiprojects.txt deleted file mode 100644 index 9cc18254..00000000 --- a/internal/resolution/pm/gradle/testdata/project/subproject/.debricked.multiprojects.txt +++ /dev/null @@ -1 +0,0 @@ -C:\Users\sjadhav2\Debricked\cli\internal\resolution\pm\gradle\testdata\project\subproject \ No newline at end of file From 1c5e12fc5186023158187dd86b4852edec76260d Mon Sep 17 00:00:00 2001 From: suraj-jadhav Date: Wed, 21 Jan 2026 10:50:19 +0530 Subject: [PATCH 3/6] Add Poetry CLI support for generating lock file from manifest on client side --- scripts/test_e2e_callgraph_java_version.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/test_e2e_callgraph_java_version.sh b/scripts/test_e2e_callgraph_java_version.sh index 9c9744db..89684739 100755 --- a/scripts/test_e2e_callgraph_java_version.sh +++ b/scripts/test_e2e_callgraph_java_version.sh @@ -5,4 +5,5 @@ else fi sed -i "s/[0-9]\+<\/java.version>/$DEBRICKED_JAVA_VERSION<\/java.version>/" test/callgraph/testdata/mvnproj-build/pom.xml +mvn clean -f test/callgraph/testdata/mvnproj-build/pom.xml go test -v ./test/callgraph/maven_test.go From 2b3cd374a23906836de00ab4d89aa3915e0719d4 Mon Sep 17 00:00:00 2001 From: suraj-jadhav Date: Wed, 21 Jan 2026 11:02:27 +0530 Subject: [PATCH 4/6] Add Poetry CLI support for generating lock file from manifest on client side --- scripts/test_e2e_callgraph_java_version.sh | 2 +- test/callgraph/testdata/mvnproj-build/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/test_e2e_callgraph_java_version.sh b/scripts/test_e2e_callgraph_java_version.sh index 89684739..d659dc63 100755 --- a/scripts/test_e2e_callgraph_java_version.sh +++ b/scripts/test_e2e_callgraph_java_version.sh @@ -4,6 +4,6 @@ else DEBRICKED_JAVA_VERSION=$1 fi -sed -i "s/[0-9]\+<\/java.version>/$DEBRICKED_JAVA_VERSION<\/java.version>/" test/callgraph/testdata/mvnproj-build/pom.xml +sed -i '' -E "s/[0-9]+<\/java\.version>/$DEBRICKED_JAVA_VERSION<\/java.version>/" test/callgraph/testdata/mvnproj-build/pom.xml mvn clean -f test/callgraph/testdata/mvnproj-build/pom.xml go test -v ./test/callgraph/maven_test.go diff --git a/test/callgraph/testdata/mvnproj-build/pom.xml b/test/callgraph/testdata/mvnproj-build/pom.xml index a606c4ac..8db0dc49 100644 --- a/test/callgraph/testdata/mvnproj-build/pom.xml +++ b/test/callgraph/testdata/mvnproj-build/pom.xml @@ -8,7 +8,7 @@ 1.0 - 11 + 17 ${java.version} ${java.version} From 6f5d8a63c67645bfaefdf11f61ce586faced4648 Mon Sep 17 00:00:00 2001 From: suraj-jadhav Date: Wed, 21 Jan 2026 11:47:09 +0530 Subject: [PATCH 5/6] Add Poetry CLI support for generating lock file from manifest on client side --- internal/callgraph/language/java/job.go | 27 +++++++++++++++++++++++++ internal/io/archive.go | 10 +++++++++ 2 files changed, 37 insertions(+) diff --git a/internal/callgraph/language/java/job.go b/internal/callgraph/language/java/job.go index 42bfa04f..8c2ad5ae 100644 --- a/internal/callgraph/language/java/job.go +++ b/internal/callgraph/language/java/job.go @@ -1,9 +1,13 @@ package java import ( + "errors" + "fmt" "os" "os/exec" "path" + "path/filepath" + "strings" "syscall" "github.com/debricked/cli/internal/callgraph/cgexec" @@ -82,6 +86,8 @@ func (j *Job) Run() { return } + + j.cleanBadJars(targetDir) } callgraph := NewCallgraph( j.cmdFactory, @@ -112,6 +118,27 @@ func (j *Job) runCopyDependencies(osCmd *exec.Cmd) { } } +func (j *Job) cleanBadJars(targetDir string) { + files, err := os.ReadDir(targetDir) + if err != nil { + j.Errors().Warning(errors.New(fmt.Sprintf("failed to read target dir %s: %v", targetDir, err))) + return + } + for _, file := range files { + if strings.HasSuffix(file.Name(), ".jar") { + jarPath := filepath.Join(targetDir, file.Name()) + if !j.isValidJar(jarPath) { + j.Errors().Warning(errors.New(fmt.Sprintf("removing invalid jar: %s", jarPath))) + j.fs.Remove(jarPath) + } + } + } +} + +func (j *Job) isValidJar(jarPath string) bool { + return j.archive.IsValidZip(jarPath) +} + func (j *Job) runCallGraph(callgraph ICallgraph) { err := callgraph.RunCallGraphWithSetup() diff --git a/internal/io/archive.go b/internal/io/archive.go index 553b07da..34c8f6df 100644 --- a/internal/io/archive.go +++ b/internal/io/archive.go @@ -11,6 +11,7 @@ type IArchive interface { UnzipFile(sourcePath string, targetPath string) error B64(sourceName string, targetName string) error Cleanup(targetName string) error + IsValidZip(path string) bool } type Archive struct { @@ -140,3 +141,12 @@ func (arc *Archive) Cleanup(fileName string) error { return arc.fs.Remove(path.Join(arc.workingDirectory, fileName)) } + +func (arc *Archive) IsValidZip(path string) bool { + r, err := arc.zip.OpenReader(path) + if err != nil { + return false + } + defer arc.zip.CloseReader(r) + return true +} From d36c7d24712dca81bc873408d4693dd89cba5b15 Mon Sep 17 00:00:00 2001 From: suraj-jadhav Date: Wed, 21 Jan 2026 11:55:05 +0530 Subject: [PATCH 6/6] Add Poetry CLI support for generating lock file from manifest on client side --- internal/callgraph/language/java/job.go | 27 ------------------- internal/io/archive.go | 10 ------- scripts/test_e2e_callgraph_java_version.sh | 2 +- test/callgraph/testdata/mvnproj-build/pom.xml | 2 +- 4 files changed, 2 insertions(+), 39 deletions(-) diff --git a/internal/callgraph/language/java/job.go b/internal/callgraph/language/java/job.go index 8c2ad5ae..42bfa04f 100644 --- a/internal/callgraph/language/java/job.go +++ b/internal/callgraph/language/java/job.go @@ -1,13 +1,9 @@ package java import ( - "errors" - "fmt" "os" "os/exec" "path" - "path/filepath" - "strings" "syscall" "github.com/debricked/cli/internal/callgraph/cgexec" @@ -86,8 +82,6 @@ func (j *Job) Run() { return } - - j.cleanBadJars(targetDir) } callgraph := NewCallgraph( j.cmdFactory, @@ -118,27 +112,6 @@ func (j *Job) runCopyDependencies(osCmd *exec.Cmd) { } } -func (j *Job) cleanBadJars(targetDir string) { - files, err := os.ReadDir(targetDir) - if err != nil { - j.Errors().Warning(errors.New(fmt.Sprintf("failed to read target dir %s: %v", targetDir, err))) - return - } - for _, file := range files { - if strings.HasSuffix(file.Name(), ".jar") { - jarPath := filepath.Join(targetDir, file.Name()) - if !j.isValidJar(jarPath) { - j.Errors().Warning(errors.New(fmt.Sprintf("removing invalid jar: %s", jarPath))) - j.fs.Remove(jarPath) - } - } - } -} - -func (j *Job) isValidJar(jarPath string) bool { - return j.archive.IsValidZip(jarPath) -} - func (j *Job) runCallGraph(callgraph ICallgraph) { err := callgraph.RunCallGraphWithSetup() diff --git a/internal/io/archive.go b/internal/io/archive.go index 34c8f6df..553b07da 100644 --- a/internal/io/archive.go +++ b/internal/io/archive.go @@ -11,7 +11,6 @@ type IArchive interface { UnzipFile(sourcePath string, targetPath string) error B64(sourceName string, targetName string) error Cleanup(targetName string) error - IsValidZip(path string) bool } type Archive struct { @@ -141,12 +140,3 @@ func (arc *Archive) Cleanup(fileName string) error { return arc.fs.Remove(path.Join(arc.workingDirectory, fileName)) } - -func (arc *Archive) IsValidZip(path string) bool { - r, err := arc.zip.OpenReader(path) - if err != nil { - return false - } - defer arc.zip.CloseReader(r) - return true -} diff --git a/scripts/test_e2e_callgraph_java_version.sh b/scripts/test_e2e_callgraph_java_version.sh index d659dc63..89684739 100755 --- a/scripts/test_e2e_callgraph_java_version.sh +++ b/scripts/test_e2e_callgraph_java_version.sh @@ -4,6 +4,6 @@ else DEBRICKED_JAVA_VERSION=$1 fi -sed -i '' -E "s/[0-9]+<\/java\.version>/$DEBRICKED_JAVA_VERSION<\/java.version>/" test/callgraph/testdata/mvnproj-build/pom.xml +sed -i "s/[0-9]\+<\/java.version>/$DEBRICKED_JAVA_VERSION<\/java.version>/" test/callgraph/testdata/mvnproj-build/pom.xml mvn clean -f test/callgraph/testdata/mvnproj-build/pom.xml go test -v ./test/callgraph/maven_test.go diff --git a/test/callgraph/testdata/mvnproj-build/pom.xml b/test/callgraph/testdata/mvnproj-build/pom.xml index 8db0dc49..a606c4ac 100644 --- a/test/callgraph/testdata/mvnproj-build/pom.xml +++ b/test/callgraph/testdata/mvnproj-build/pom.xml @@ -8,7 +8,7 @@ 1.0 - 17 + 11 ${java.version} ${java.version}