github.com/jfrog/jfrog-cli-go@v1.22.1-0.20200318093948-4826ef344ffd/artifactory/utils/pip/installer.go (about) 1 package pip 2 3 import ( 4 "fmt" 5 gofrogcmd "github.com/jfrog/gofrog/io" 6 "github.com/jfrog/jfrog-cli-go/utils/config" 7 "github.com/jfrog/jfrog-client-go/auth" 8 clientutils "github.com/jfrog/jfrog-client-go/utils" 9 "github.com/jfrog/jfrog-client-go/utils/errorutils" 10 "github.com/jfrog/jfrog-client-go/utils/log" 11 "net/url" 12 "strings" 13 ) 14 15 type PipInstaller struct { 16 RtDetails *config.ArtifactoryDetails 17 Args []string 18 Repository string 19 ShouldParseLogs bool 20 DependencyToFileMap map[string]string 21 } 22 23 func (pi *PipInstaller) Install() error { 24 // Prepare for running. 25 pipExecutablePath, pipIndexUrl, err := pi.prepare() 26 if err != nil { 27 return err 28 } 29 30 // Run pip install. 31 err = pi.runPipInstall(pipExecutablePath, pipIndexUrl) 32 if err != nil { 33 return err 34 } 35 36 return nil 37 } 38 39 func (pi *PipInstaller) prepare() (pipExecutablePath, pipIndexUrl string, err error) { 40 log.Debug("Preparing prerequisites.") 41 42 pipExecutablePath, err = GetExecutablePath("pip") 43 if err != nil { 44 return 45 } 46 47 pipIndexUrl, err = getArtifactoryUrlWithCredentials(pi.RtDetails, pi.Repository) 48 if err != nil { 49 return 50 } 51 52 return 53 } 54 55 func getArtifactoryUrlWithCredentials(rtDetails *config.ArtifactoryDetails, repository string) (string, error) { 56 rtUrl, err := url.Parse(rtDetails.GetUrl()) 57 if err != nil { 58 return "", errorutils.CheckError(err) 59 } 60 61 username := rtDetails.GetUser() 62 password := rtDetails.GetPassword() 63 64 // Get credentials from access-token if exists. 65 if rtDetails.GetAccessToken() != "" { 66 username, err = auth.ExtractUsernameFromAccessToken(rtDetails.GetAccessToken()) 67 if err != nil { 68 return "", err 69 } 70 password = rtDetails.GetAccessToken() 71 } 72 73 if username != "" && password != "" { 74 rtUrl.User = url.UserPassword(username, password) 75 } 76 rtUrl.Path += "api/pypi/" + repository + "/simple" 77 78 return rtUrl.String(), nil 79 } 80 81 func (pi *PipInstaller) runPipInstall(pipExecutablePath, pipIndexUrl string) error { 82 pipInstallCmd := &PipCmd{ 83 Executable: pipExecutablePath, 84 Command: "install", 85 CommandArgs: append(pi.Args, "-i", pipIndexUrl), 86 } 87 88 // Check if need to run with log parsing. 89 if pi.ShouldParseLogs { 90 return pi.runPipInstallWithLogParsing(pipInstallCmd) 91 } 92 93 // Run without log parsing. 94 return gofrogcmd.RunCmd(pipInstallCmd) 95 } 96 97 // Run pip-install command while parsing the logs for downloaded packages. 98 // Supports running pip either in non-verbose and verbose mode. 99 // Populates 'dependencyToFileMap' with downloaded package-name and its actual downloaded file (wheel/egg/zip...). 100 func (pi *PipInstaller) runPipInstallWithLogParsing(pipInstallCmd *PipCmd) error { 101 // Create regular expressions for log parsing. 102 collectingPackageRegexp, err := clientutils.GetRegExp(`^Collecting\s(\w[\w-\.]+)`) 103 if err != nil { 104 return err 105 } 106 downloadFileRegexp, err := clientutils.GetRegExp(`^\s\sDownloading\s[^\s]*\/packages\/[^\s]*\/([^\s]*)`) 107 if err != nil { 108 return err 109 } 110 installedPackagesRegexp, err := clientutils.GetRegExp(`^Requirement\salready\ssatisfied\:\s(\w[\w-\.]+)`) 111 if err != nil { 112 return err 113 } 114 115 downloadedDependencies := make(map[string]string) 116 var packageName string 117 expectingPackageFilePath := false 118 119 // Extract downloaded package name. 120 dependencyNameParser := gofrogcmd.CmdOutputPattern{ 121 RegExp: collectingPackageRegexp, 122 ExecFunc: func(pattern *gofrogcmd.CmdOutputPattern) (string, error) { 123 // If this pattern matched a second time before downloaded-file-name was found, prompt a message. 124 if expectingPackageFilePath { 125 // This may occur when a package-installation file is saved in pip-cache-dir, thus not being downloaded during the installation. 126 // Re-running pip-install with 'no-cache-dir' fixes this issue. 127 log.Debug(fmt.Sprintf("Could not resolve download path for package: %s, continuing...", packageName)) 128 129 // Save package with empty file path. 130 downloadedDependencies[strings.ToLower(packageName)] = "" 131 } 132 133 // Check for out of bound results. 134 if len(pattern.MatchedResults)-1 < 0 { 135 log.Debug(fmt.Sprintf("Failed extracting package name from line: %s", pattern.Line)) 136 return pattern.Line, nil 137 } 138 139 // Save dependency information. 140 expectingPackageFilePath = true 141 packageName = pattern.MatchedResults[1] 142 143 return pattern.Line, nil 144 }, 145 } 146 147 // Extract downloaded file, stored in Artifactory. 148 dependencyFileParser := gofrogcmd.CmdOutputPattern{ 149 RegExp: downloadFileRegexp, 150 ExecFunc: func(pattern *gofrogcmd.CmdOutputPattern) (string, error) { 151 // Check for out of bound results. 152 if len(pattern.MatchedResults)-1 < 0 { 153 log.Debug(fmt.Sprintf("Failed extracting download path from line: %s", pattern.Line)) 154 return pattern.Line, nil 155 } 156 157 // If this pattern matched before package-name was found, do not collect this path. 158 if !expectingPackageFilePath { 159 log.Debug(fmt.Sprintf("Could not resolve package name for download path: %s , continuing...", packageName)) 160 return pattern.Line, nil 161 } 162 163 // Save dependency information. 164 filePath := pattern.MatchedResults[1] 165 downloadedDependencies[strings.ToLower(packageName)] = filePath 166 expectingPackageFilePath = false 167 168 log.Debug(fmt.Sprintf("Found package: %s installed with: %s", packageName, filePath)) 169 return pattern.Line, nil 170 }, 171 } 172 173 // Extract already installed packages names. 174 installedPackagesParser := gofrogcmd.CmdOutputPattern{ 175 RegExp: installedPackagesRegexp, 176 ExecFunc: func(pattern *gofrogcmd.CmdOutputPattern) (string, error) { 177 // Check for out of bound results. 178 if len(pattern.MatchedResults)-1 < 0 { 179 log.Debug(fmt.Sprintf("Failed extracting package name from line: %s", pattern.Line)) 180 return pattern.Line, nil 181 } 182 183 // Save dependency with empty file name. 184 downloadedDependencies[strings.ToLower(pattern.MatchedResults[1])] = "" 185 186 log.Debug(fmt.Sprintf("Found package: %s already installed", pattern.MatchedResults[1])) 187 return pattern.Line, nil 188 }, 189 } 190 191 // Execute command. 192 _, _, _, err = gofrogcmd.RunCmdWithOutputParser(pipInstallCmd, true, &dependencyNameParser, &dependencyFileParser, &installedPackagesParser) 193 if errorutils.CheckError(err) != nil { 194 return err 195 } 196 197 // Update dependencyToFileMap. 198 pi.DependencyToFileMap = downloadedDependencies 199 200 return nil 201 }