diff --git a/build/python.go b/build/python.go index ee036c01..a8e62427 100644 --- a/build/python.go +++ b/build/python.go @@ -33,7 +33,7 @@ func (pm *PythonModule) RunInstallAndCollectDependencies(commandArgs []string) e if err != nil { return err } - dependenciesGraph, topLevelPackagesList, err := pythonutils.GetPythonDependencies(pm.tool, pm.srcPath, pm.localDependenciesPath) + dependenciesGraph, topLevelPackagesList, err := pythonutils.GetPythonDependencies(pm.tool, pm.srcPath, pm.localDependenciesPath, pm.containingBuild.logger) if err != nil { return fmt.Errorf("failed while attempting to get %s dependencies graph: %s", pm.tool, err.Error()) } diff --git a/utils/pythonutils/pipenvutils.go b/utils/pythonutils/pipenvutils.go index c2c7647d..1dfd8993 100644 --- a/utils/pythonutils/pipenvutils.go +++ b/utils/pythonutils/pipenvutils.go @@ -1,29 +1,49 @@ package pythonutils import ( + "bytes" "encoding/json" + "fmt" + "github.com/jfrog/build-info-go/utils" "github.com/jfrog/gofrog/io" ) +var ( + windowsReplaceBytes = []byte("\r\n") + unixReplaceBytes = []byte("\n") + replacementBytes = []byte("") +) + // Executes pipenv graph. // Returns a dependency map of all the installed pipenv packages in the current environment and also another list of the top level dependencies // 'dependenciesGraph' - map between all parent modules and their child dependencies // 'topLevelPackagesList' - list of all top level dependencies ( root dependencies only) -func getPipenvDependencies(srcPath string) (dependenciesGraph map[string][]string, topLevelDependencies []string, err error) { +func getPipenvDependencies(srcPath string, logger utils.Log) (dependenciesGraph map[string][]string, topLevelDependencies []string, err error) { // Run pipenv graph pipenvGraphCmd := io.NewCommand("pipenv", "graph", []string{"--json"}) pipenvGraphCmd.Dir = srcPath output, err := pipenvGraphCmd.RunWithOutput() if err != nil { + err = fmt.Errorf("failed to run pipenv graph --json: %s", err.Error()) return } + logger.Debug("pipenv graph --json output:\n" + string(output)) // Parse into array. packages := make([]pythonDependencyPackage, 0) - err = json.Unmarshal(output, &packages) + err = json.Unmarshal(cleanJsonOutput(output), &packages) if err != nil { + err = fmt.Errorf("failed to parse pipenv graph --json output: %s", err.Error()) return } dependenciesGraph, topLevelDependencies, err = parseDependenciesToGraph(packages) return } + +// Sometimes, `pipenv graph --json` command returns output with new line characters in between (not valid json) So, we need to remove them before unmarshaling. +func cleanJsonOutput(output []byte) []byte { + // For Windows + output = bytes.ReplaceAll(output, windowsReplaceBytes, replacementBytes) + // For Unix + return bytes.ReplaceAll(output, unixReplaceBytes, replacementBytes) +} diff --git a/utils/pythonutils/utils.go b/utils/pythonutils/utils.go index 9db3657b..d83b3ca0 100644 --- a/utils/pythonutils/utils.go +++ b/utils/pythonutils/utils.go @@ -86,12 +86,12 @@ func GetPythonDependenciesFiles(tool PythonTool, args []string, buildName, build } } -func GetPythonDependencies(tool PythonTool, srcPath, localDependenciesPath string) (dependenciesGraph map[string][]string, topLevelDependencies []string, err error) { +func GetPythonDependencies(tool PythonTool, srcPath, localDependenciesPath string, logger utils.Log) (dependenciesGraph map[string][]string, topLevelDependencies []string, err error) { switch tool { case Pip: return getPipDependencies(srcPath, localDependenciesPath) case Pipenv: - return getPipenvDependencies(srcPath) + return getPipenvDependencies(srcPath, logger) case Poetry: return getPoetryDependencies(srcPath) default: