diff --git a/utils/pythonutils/utils.go b/utils/pythonutils/utils.go index 084d9f41..77aa0dc0 100644 --- a/utils/pythonutils/utils.go +++ b/utils/pythonutils/utils.go @@ -3,6 +3,7 @@ package pythonutils import ( "errors" "fmt" + "github.com/jfrog/gofrog/version" "path/filepath" "regexp" "strings" @@ -167,6 +168,8 @@ func InstallWithLogParsing(tool PythonTool, commandArgs []string, log utils.Log, downloadingRegexp := regexp.MustCompile(`^\s*Downloading\s([^\s]*)\s\(`) usingCachedRegexp := regexp.MustCompile(`^\s*Using\scached\s([\S]+)\s\(`) alreadySatisfiedRegexp := regexp.MustCompile(`^Requirement\salready\ssatisfied:\s(\w[\w-.]+)`) + pipEnvUsingCached := regexp.MustCompile(`^\s*Using\s*cached\s*`) + pipEnvRegexp := regexp.MustCompile(`.*`) var packageName string expectingPackageFilePath := false @@ -253,11 +256,91 @@ func InstallWithLogParsing(tool PythonTool, commandArgs []string, log utils.Log, return pattern.Line, nil }, } - - // Execute command. - _, errorOut, _, err := gofrogcmd.RunCmdWithOutputParser(installCmd, true, &dependencyNameParser, &downloadedFileParser, &cachedFileParser, &installedPackagesParser) + pipEnvVersion, err := getPipEnvVersion() if err != nil { - return nil, fmt.Errorf("failed running %s command with error: '%s - %s'", string(tool), err.Error(), errorOut) + return nil, err + } + verifyPipEnvVersion := pipEnvVersion.Compare("2023.7.23") == -1 || pipEnvVersion.Compare("2023.7.23") == 0 + if tool == Pipenv && verifyPipEnvVersion { + // Add verbosity flag to pipenv commands to collect necessary data + var usingCachedMode bool = false + var cache string + + // Extract already installed packages names. + pipEnvUsingCachedPattern := gofrogcmd.CmdOutputPattern{ + RegExp: pipEnvUsingCached, + ExecFunc: func(pattern *gofrogcmd.CmdOutputPattern) (string, error) { + // Check for out of bound results. + if len(pattern.MatchedResults)-1 < 0 { + log.Debug(fmt.Sprintf("Failed extracting package name from line: %s", pattern.Line)) + return pattern.Line, nil + } + usingCachedMode = true + return pattern.Line, nil + }, + } + + // Extract already installed packages names. + pipEnvUsingCached := gofrogcmd.CmdOutputPattern{ + RegExp: pipEnvRegexp, + ExecFunc: func(pattern *gofrogcmd.CmdOutputPattern) (string, error) { + // Check for out of bound results. + if len(pattern.MatchedResults)-1 < 0 { + log.Debug(fmt.Sprintf("Failed extracting package name from line: %s", pattern.Line)) + return pattern.Line, nil + } + if usingCachedMode { + cache = strings.Join([]string{cache, strings.Join(pattern.MatchedResults, "")}, "") + if strings.Contains(cache, "(") { + usingCachedMode = false + cache, _, _ = strings.Cut(cache, "(") + _, cache, _ = strings.Cut(cache, "Using cached") + cache = strings.ReplaceAll(cache, " ", "") + // Save dependency information. + filePath := cache + lastSlashIndex := strings.LastIndex(filePath, "/") + var fileName string + if lastSlashIndex == -1 { + fileName = filePath + } else { + fileName = filePath[lastSlashIndex+1:] + } + dependenciesMap[strings.ToLower(packageName)] = entities.Dependency{Id: fileName} + cache = "" + expectingPackageFilePath = false + + log.Debug(fmt.Sprintf("Found package: %s installed with: %s", packageName, fileName)) + return pattern.Line, nil + } + } + return pattern.Line, nil + }, + } + _, errorOut, _, err := gofrogcmd.RunCmdWithOutputParser(installCmd, true, &dependencyNameParser, &downloadedFileParser, &pipEnvUsingCachedPattern, &pipEnvUsingCached, &installedPackagesParser) + if err != nil { + return nil, fmt.Errorf("failed running %s command with error: '%s - %s'", string(tool), err.Error(), errorOut) + } + + } else { + // Execute command. + _, errorOut, _, err := gofrogcmd.RunCmdWithOutputParser(installCmd, true, &dependencyNameParser, &downloadedFileParser, &cachedFileParser, &installedPackagesParser) + if err != nil { + return nil, fmt.Errorf("failed running %s command with error: '%s - %s'", string(tool), err.Error(), errorOut) + } } + return dependenciesMap, nil } + +func getPipEnvVersion() (*version.Version, error) { + versionData, err := gofrogcmd.RunCmdOutput(utils.NewCommand(string(Pipenv), "--version", []string{})) + if err != nil { + return nil, err + } + _, versionData, found := strings.Cut(versionData, "version ") + if !found { + return nil, errors.New("couldn't find pipenv version") + } + versionData = strings.ReplaceAll(versionData, "\n", "") + return version.NewVersion(versionData), nil +}