From ebe8ff91b9b4cc9dd81bc5239c86b6342baf7899 Mon Sep 17 00:00:00 2001 From: suraj-jadhav Date: Tue, 27 Jan 2026 12:21:44 +0530 Subject: [PATCH 1/7] 7752 : Add UV CLI support for generating lock file from manifest on client side --- build/docker/alpine.Dockerfile | 8 +- build/docker/debian.Dockerfile | 5 ++ internal/client/deb_client.go | 2 +- .../resolution/file/file_batch_factory.go | 60 ++++++++++++- internal/resolution/pm/pm.go | 2 + internal/resolution/pm/uv/cmd_factory.go | 43 ++++++++++ internal/resolution/pm/uv/cmd_factory_test.go | 27 ++++++ internal/resolution/pm/uv/job.go | 86 +++++++++++++++++++ internal/resolution/pm/uv/job_test.go | 41 +++++++++ internal/resolution/pm/uv/pm.go | 23 +++++ internal/resolution/pm/uv/pm_test.go | 16 ++++ internal/resolution/pm/uv/strategy.go | 23 +++++ internal/resolution/pm/uv/strategy_test.go | 29 +++++++ .../pm/uv/testdata/cmd_factory_mock.go | 24 ++++++ .../resolution/strategy/strategy_factory.go | 3 + 15 files changed, 387 insertions(+), 5 deletions(-) create mode 100644 internal/resolution/pm/uv/cmd_factory.go create mode 100644 internal/resolution/pm/uv/cmd_factory_test.go create mode 100644 internal/resolution/pm/uv/job.go create mode 100644 internal/resolution/pm/uv/job_test.go create mode 100644 internal/resolution/pm/uv/pm.go create mode 100644 internal/resolution/pm/uv/pm_test.go create mode 100644 internal/resolution/pm/uv/strategy.go create mode 100644 internal/resolution/pm/uv/strategy_test.go create mode 100644 internal/resolution/pm/uv/testdata/cmd_factory_mock.go diff --git a/build/docker/alpine.Dockerfile b/build/docker/alpine.Dockerfile index 780f8aba..73da00f4 100644 --- a/build/docker/alpine.Dockerfile +++ b/build/docker/alpine.Dockerfile @@ -88,8 +88,12 @@ 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 + ln -s /root/.local/bin/poetry /usr/local/bin/poetry && \ + poetry --version + +# Install uv for Python resolution (pyproject.toml managed by uv) +RUN python3 -m pip install --no-cache-dir uv && \ + uv --version CMD [ "debricked", "scan" ] diff --git a/build/docker/debian.Dockerfile b/build/docker/debian.Dockerfile index 84a9911f..8ddbbc04 100644 --- a/build/docker/debian.Dockerfile +++ b/build/docker/debian.Dockerfile @@ -99,6 +99,7 @@ RUN apt -y update && apt -y upgrade && apt -y install ca-certificates && \ apt -y install -t unstable \ python3.13 \ python3.13-venv \ + python3-pip \ openjdk-21-jdk && \ apt -y clean && rm -rf /var/lib/apt/lists/* && \ ln -s /usr/bin/python3.13 /usr/bin/python @@ -139,6 +140,10 @@ RUN curl -sSL https://install.python-poetry.org | python3 - && \ ln -s /root/.local/bin/poetry /usr/local/bin/poetry && \ poetry --version +# Install uv for Python resolution (pyproject.toml managed by uv) +RUN python3 -m pip install --no-cache-dir uv && \ + uv --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/resolution/file/file_batch_factory.go b/internal/resolution/file/file_batch_factory.go index f2cfe9c1..0629f501 100644 --- a/internal/resolution/file/file_batch_factory.go +++ b/internal/resolution/file/file_batch_factory.go @@ -1,11 +1,15 @@ package file import ( - "path" + "os" + "path/filepath" "regexp" + "strings" "github.com/debricked/cli/internal/resolution/pm" "github.com/debricked/cli/internal/resolution/pm/npm" + "github.com/debricked/cli/internal/resolution/pm/poetry" + "github.com/debricked/cli/internal/resolution/pm/uv" "github.com/debricked/cli/internal/resolution/pm/yarn" ) @@ -32,14 +36,23 @@ func (bf *BatchFactory) SetNpmPreferred(npmPreferred bool) { func (bf *BatchFactory) Make(files []string) []IBatch { batchMap := make(map[string]IBatch) for _, file := range files { + base := filepath.Base(file) for _, p := range bf.pms { if bf.skipPackageManager(p) { continue } for _, manifest := range p.Manifests() { + // Special handling for Python pyproject.toml, which may belong to either Poetry or uv. + if manifest == "pyproject.toml" && strings.EqualFold(base, "pyproject.toml") { + pmName := detectPyprojectPm(file) + if pmName != p.Name() { + continue + } + } + compiledRegex, _ := regexp.Compile(manifest) - if compiledRegex.MatchString(path.Base(file)) { + if compiledRegex.MatchString(base) { batch, ok := batchMap[p.Name()] if !ok { batch = NewBatch(p) @@ -72,3 +85,46 @@ func (bf *BatchFactory) skipPackageManager(p pm.IPm) bool { return false } + +func detectPyprojectPm(pyprojectPath string) string { + dir := filepath.Dir(pyprojectPath) + + if fileExists(filepath.Join(dir, "uv.lock")) { + return uv.Name + } + + if fileExists(filepath.Join(dir, "poetry.lock")) { + return poetry.Name + } + + content, err := os.ReadFile(pyprojectPath) + if err == nil { + data := string(content) + hasPoetry := strings.Contains(data, "[tool.poetry]") || + strings.Contains(data, "tool.poetry") + hasProject := strings.Contains(data, "[project]") + + if hasPoetry && hasProject { + // Ambiguous: both Poetry and UV indicators present + return "" + } + if hasPoetry { + return poetry.Name + } + if hasProject { + return uv.Name + } + } + + // If no indicators found, cannot determine PM + return "" +} + +func fileExists(path string) bool { + info, err := os.Stat(path) + if err != nil { + return false + } + + return !info.IsDir() +} diff --git a/internal/resolution/pm/pm.go b/internal/resolution/pm/pm.go index 8d4790e9..39ad5c89 100644 --- a/internal/resolution/pm/pm.go +++ b/internal/resolution/pm/pm.go @@ -11,6 +11,7 @@ import ( "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/uv" "github.com/debricked/cli/internal/resolution/pm/yarn" ) @@ -26,6 +27,7 @@ func Pms() []IPm { gomod.NewPm(), pip.NewPm(), poetry.NewPm(), + uv.NewPm(), yarn.NewPm(), npm.NewPm(), bower.NewPm(), diff --git a/internal/resolution/pm/uv/cmd_factory.go b/internal/resolution/pm/uv/cmd_factory.go new file mode 100644 index 00000000..8928944e --- /dev/null +++ b/internal/resolution/pm/uv/cmd_factory.go @@ -0,0 +1,43 @@ +package uv + +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) { + uvPath, err := cmdf.execPath.LookPath("uv") + if err != nil { + return nil, err + } + + workingDir := filepath.Dir(filepath.Clean(manifestFile)) + + env := os.Environ() + + return &exec.Cmd{ + Path: uvPath, + Args: []string{"uv", "lock"}, + Dir: workingDir, + Env: env, + }, nil +} diff --git a/internal/resolution/pm/uv/cmd_factory_test.go b/internal/resolution/pm/uv/cmd_factory_test.go new file mode 100644 index 00000000..e4fd22a8 --- /dev/null +++ b/internal/resolution/pm/uv/cmd_factory_test.go @@ -0,0 +1,27 @@ +package uv + +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/uv", cmd.Path) + assert.Contains(t, cmd.Args, "uv") + assert.Contains(t, cmd.Args, "lock") + assert.Equal(t, filepath.Dir(manifest), cmd.Dir) +} diff --git a/internal/resolution/pm/uv/job.go b/internal/resolution/pm/uv/job.go new file mode 100644 index 00000000..9c06eb49 --- /dev/null +++ b/internal/resolution/pm/uv/job.go @@ -0,0 +1,86 @@ +package uv + +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 uv.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("uv") + } + + cmdError.SetDocumentation(documentation) + + return cmdError +} diff --git a/internal/resolution/pm/uv/job_test.go b/internal/resolution/pm/uv/job_test.go new file mode 100644 index 00000000..1d9e85e4 --- /dev/null +++ b/internal/resolution/pm/uv/job_test.go @@ -0,0 +1,41 @@ +package uv + +import ( + "errors" + "testing" + + jobTestdata "github.com/debricked/cli/internal/resolution/job/testdata" + "github.com/debricked/cli/internal/resolution/pm/uv/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: \"uv\": 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(), "uv 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/uv/pm.go b/internal/resolution/pm/uv/pm.go new file mode 100644 index 00000000..5a88491c --- /dev/null +++ b/internal/resolution/pm/uv/pm.go @@ -0,0 +1,23 @@ +package uv + +const Name = "uv" + +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/uv/pm_test.go b/internal/resolution/pm/uv/pm_test.go new file mode 100644 index 00000000..92fd0e73 --- /dev/null +++ b/internal/resolution/pm/uv/pm_test.go @@ -0,0 +1,16 @@ +package uv + +import "testing" + +func TestManifests(t *testing.T) { + pm := NewPm() + manifests := pm.Manifests() + + if len(manifests) != 1 { + t.Fatalf("expected 1 manifest, got %d", len(manifests)) + } + + if manifests[0] != "pyproject.toml" { + t.Fatalf("expected pyproject.toml, got %s", manifests[0]) + } +} diff --git a/internal/resolution/pm/uv/strategy.go b/internal/resolution/pm/uv/strategy.go new file mode 100644 index 00000000..49d3dcd9 --- /dev/null +++ b/internal/resolution/pm/uv/strategy.go @@ -0,0 +1,23 @@ +package uv + +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/uv/strategy_test.go b/internal/resolution/pm/uv/strategy_test.go new file mode 100644 index 00000000..223b4f99 --- /dev/null +++ b/internal/resolution/pm/uv/strategy_test.go @@ -0,0 +1,29 @@ +package uv + +import ( + "testing" + + "github.com/debricked/cli/internal/resolution/job" + "github.com/stretchr/testify/assert" +) + +type jobMock struct { + job.IJob +} + +func TestNewStrategy(t *testing.T) { + files := []string{"pyproject.toml"} + strategy := NewStrategy(files) + + assert.Equal(t, files, strategy.files) +} + +func TestInvoke(t *testing.T) { + files := []string{"pyproject.toml"} + strategy := NewStrategy(files) + + jobs, err := strategy.Invoke() + + assert.NoError(t, err) + assert.Len(t, jobs, 1) +} diff --git a/internal/resolution/pm/uv/testdata/cmd_factory_mock.go b/internal/resolution/pm/uv/testdata/cmd_factory_mock.go new file mode 100644 index 00000000..833a4ff0 --- /dev/null +++ b/internal/resolution/pm/uv/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 963cf0fe..04543026 100644 --- a/internal/resolution/strategy/strategy_factory.go +++ b/internal/resolution/strategy/strategy_factory.go @@ -14,6 +14,7 @@ import ( "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/uv" "github.com/debricked/cli/internal/resolution/pm/yarn" ) @@ -41,6 +42,8 @@ func (sf Factory) Make(pmFileBatch file.IBatch, paths []string) (IStrategy, erro return pip.NewStrategy(pmFileBatch.Files()), nil case poetry.Name: return poetry.NewStrategy(pmFileBatch.Files()), nil + case uv.Name: + return uv.NewStrategy(pmFileBatch.Files()), nil case yarn.Name: return yarn.NewStrategy(pmFileBatch.Files()), nil case npm.Name: From d4e1551c61d759e8305e3a9b4acdc35b9988cbbd Mon Sep 17 00:00:00 2001 From: suraj-jadhav Date: Tue, 27 Jan 2026 12:29:55 +0530 Subject: [PATCH 2/7] 7752 : Add UV CLI support for generating lock file from manifest on client side --- internal/client/deb_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" From f108af1cd1a9124c82361030fa9ea6c86bc30e26 Mon Sep 17 00:00:00 2001 From: suraj-jadhav Date: Tue, 27 Jan 2026 14:15:53 +0530 Subject: [PATCH 3/7] 7752 : Add UV CLI support for generating lock file from manifest on client side --- build/docker/alpine.Dockerfile | 8 +++++--- build/docker/debian.Dockerfile | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/build/docker/alpine.Dockerfile b/build/docker/alpine.Dockerfile index 73da00f4..2e3d70e2 100644 --- a/build/docker/alpine.Dockerfile +++ b/build/docker/alpine.Dockerfile @@ -6,7 +6,7 @@ RUN go mod download && go mod verify COPY . . RUN mkdir -p internal/file/embedded && \ wget -O internal/file/embedded/supported_formats.json https://debricked.com/api/1.0/open/files/supported-formats -RUN apk add --no-cache make curl && make install && apk del make curl +RUN apk add --no-cache make curl && sed -i 's/\r$//' scripts/install.sh scripts/fetch_supported_formats.sh && make install && apk del make curl CMD [ "debricked" ] FROM alpine:latest AS cli-base @@ -92,8 +92,10 @@ RUN curl -sSL https://install.python-poetry.org | python3 - && \ poetry --version # Install uv for Python resolution (pyproject.toml managed by uv) -RUN python3 -m pip install --no-cache-dir uv && \ - uv --version +RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \ + /root/.cargo/bin/uv --version + +ENV PATH="/root/.cargo/bin:$PATH" CMD [ "debricked", "scan" ] diff --git a/build/docker/debian.Dockerfile b/build/docker/debian.Dockerfile index 8ddbbc04..a6708ed2 100644 --- a/build/docker/debian.Dockerfile +++ b/build/docker/debian.Dockerfile @@ -141,8 +141,10 @@ RUN curl -sSL https://install.python-poetry.org | python3 - && \ poetry --version # Install uv for Python resolution (pyproject.toml managed by uv) -RUN python3 -m pip install --no-cache-dir uv && \ - uv --version +RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \ + /root/.cargo/bin/uv --version + +ENV PATH="/root/.cargo/bin:$PATH" CMD [ "debricked", "scan" ] From 17b15db0b00a58e9716ed5358bd88ebc182b9912 Mon Sep 17 00:00:00 2001 From: suraj-jadhav Date: Tue, 27 Jan 2026 14:29:36 +0530 Subject: [PATCH 4/7] 7752 : Add UV CLI support for generating lock file from manifest on client side --- build/docker/alpine.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/docker/alpine.Dockerfile b/build/docker/alpine.Dockerfile index 2e3d70e2..b0b88f77 100644 --- a/build/docker/alpine.Dockerfile +++ b/build/docker/alpine.Dockerfile @@ -6,7 +6,7 @@ RUN go mod download && go mod verify COPY . . RUN mkdir -p internal/file/embedded && \ wget -O internal/file/embedded/supported_formats.json https://debricked.com/api/1.0/open/files/supported-formats -RUN apk add --no-cache make curl && sed -i 's/\r$//' scripts/install.sh scripts/fetch_supported_formats.sh && make install && apk del make curl +RUN apk add --no-cache make curl && make install && apk del make curl CMD [ "debricked" ] FROM alpine:latest AS cli-base From e812179db1ba6fb05ae3d36c976fb632976a7fe1 Mon Sep 17 00:00:00 2001 From: suraj-jadhav Date: Tue, 27 Jan 2026 15:11:31 +0530 Subject: [PATCH 5/7] 7752 : Add UV CLI support for generating lock file from manifest on client side --- build/docker/alpine.Dockerfile | 2 +- build/docker/debian.Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/docker/alpine.Dockerfile b/build/docker/alpine.Dockerfile index b0b88f77..2e3d70e2 100644 --- a/build/docker/alpine.Dockerfile +++ b/build/docker/alpine.Dockerfile @@ -6,7 +6,7 @@ RUN go mod download && go mod verify COPY . . RUN mkdir -p internal/file/embedded && \ wget -O internal/file/embedded/supported_formats.json https://debricked.com/api/1.0/open/files/supported-formats -RUN apk add --no-cache make curl && make install && apk del make curl +RUN apk add --no-cache make curl && sed -i 's/\r$//' scripts/install.sh scripts/fetch_supported_formats.sh && make install && apk del make curl CMD [ "debricked" ] FROM alpine:latest AS cli-base diff --git a/build/docker/debian.Dockerfile b/build/docker/debian.Dockerfile index a6708ed2..539f0198 100644 --- a/build/docker/debian.Dockerfile +++ b/build/docker/debian.Dockerfile @@ -10,7 +10,7 @@ RUN mkdir -p internal/file/embedded && \ wget -O internal/file/embedded/supported_formats.json https://debricked.com/api/1.0/open/files/supported-formats RUN go mod download && go mod verify COPY . . -RUN make install +RUN sed -i 's/\r$//' scripts/install.sh scripts/fetch_supported_formats.sh && make install CMD [ "debricked" ] FROM debian:bookworm-slim AS cli-base From 1f1af159db90eb28ff5c31da8b67285375394edf Mon Sep 17 00:00:00 2001 From: suraj-jadhav Date: Tue, 27 Jan 2026 15:24:12 +0530 Subject: [PATCH 6/7] 7752 : Lint issues --- internal/resolution/pm/uv/strategy_test.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/internal/resolution/pm/uv/strategy_test.go b/internal/resolution/pm/uv/strategy_test.go index 223b4f99..1ed9f326 100644 --- a/internal/resolution/pm/uv/strategy_test.go +++ b/internal/resolution/pm/uv/strategy_test.go @@ -3,14 +3,9 @@ package uv import ( "testing" - "github.com/debricked/cli/internal/resolution/job" "github.com/stretchr/testify/assert" ) -type jobMock struct { - job.IJob -} - func TestNewStrategy(t *testing.T) { files := []string{"pyproject.toml"} strategy := NewStrategy(files) From 3b8947fcd1804d03ed2ae979608357cf930742b2 Mon Sep 17 00:00:00 2001 From: suraj-jadhav Date: Tue, 27 Jan 2026 15:55:26 +0530 Subject: [PATCH 7/7] 7752 : Batch - Cyclop issue --- .../resolution/file/file_batch_factory.go | 58 +++++++++++-------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/internal/resolution/file/file_batch_factory.go b/internal/resolution/file/file_batch_factory.go index 0629f501..6649381c 100644 --- a/internal/resolution/file/file_batch_factory.go +++ b/internal/resolution/file/file_batch_factory.go @@ -33,44 +33,54 @@ func (bf *BatchFactory) SetNpmPreferred(npmPreferred bool) { bf.npmPreferred = npmPreferred } +//nolint:cyclop func (bf *BatchFactory) Make(files []string) []IBatch { batchMap := make(map[string]IBatch) for _, file := range files { - base := filepath.Base(file) - for _, p := range bf.pms { - if bf.skipPackageManager(p) { - continue - } + bf.processFile(file, batchMap) + } - for _, manifest := range p.Manifests() { - // Special handling for Python pyproject.toml, which may belong to either Poetry or uv. - if manifest == "pyproject.toml" && strings.EqualFold(base, "pyproject.toml") { - pmName := detectPyprojectPm(file) - if pmName != p.Name() { - continue - } - } + batches := make([]IBatch, 0, len(batchMap)) + for _, batch := range batchMap { + batches = append(batches, batch) + } + + return batches +} +func (bf *BatchFactory) processFile(file string, batchMap map[string]IBatch) { + base := filepath.Base(file) + for _, p := range bf.pms { + if bf.skipPackageManager(p) { + continue + } + + for _, manifest := range p.Manifests() { + if bf.shouldProcessManifest(manifest, base, file, p) { compiledRegex, _ := regexp.Compile(manifest) if compiledRegex.MatchString(base) { - batch, ok := batchMap[p.Name()] - if !ok { - batch = NewBatch(p) - batchMap[p.Name()] = batch - } - batch.Add(file) + bf.addToBatch(p, file, batchMap) } } } } +} - batches := make([]IBatch, 0, len(batchMap)) - - for _, batch := range batchMap { - batches = append(batches, batch) +func (bf *BatchFactory) shouldProcessManifest(manifest, base, file string, p pm.IPm) bool { + if manifest == "pyproject.toml" && strings.EqualFold(base, "pyproject.toml") { + pmName := detectPyprojectPm(file) + return pmName == p.Name() } + return true +} - return batches +func (bf *BatchFactory) addToBatch(p pm.IPm, file string, batchMap map[string]IBatch) { + batch, ok := batchMap[p.Name()] + if !ok { + batch = NewBatch(p) + batchMap[p.Name()] = batch + } + batch.Add(file) } func (bf *BatchFactory) skipPackageManager(p pm.IPm) bool {