github.com/jfrog/jfrog-cli-core@v1.12.1/artifactory/commands/golang/go.go (about) 1 package golang 2 3 import ( 4 "errors" 5 "fmt" 6 "path" 7 "path/filepath" 8 "strings" 9 10 "github.com/jfrog/gocmd" 11 executersutils "github.com/jfrog/gocmd/executers/utils" 12 "github.com/jfrog/gocmd/params" 13 "github.com/jfrog/jfrog-cli-core/artifactory/utils" 14 "github.com/jfrog/jfrog-cli-core/artifactory/utils/golang" 15 "github.com/jfrog/jfrog-cli-core/artifactory/utils/golang/project" 16 "github.com/jfrog/jfrog-cli-core/utils/config" 17 "github.com/jfrog/jfrog-client-go/artifactory" 18 _go "github.com/jfrog/jfrog-client-go/artifactory/services/go" 19 "github.com/jfrog/jfrog-client-go/auth" 20 "github.com/jfrog/jfrog-client-go/utils/errorutils" 21 "github.com/jfrog/jfrog-client-go/utils/io/fileutils" 22 "github.com/jfrog/jfrog-client-go/utils/version" 23 ) 24 25 const GoCommandName = "rt_go" 26 27 type GoCommand struct { 28 noRegistry bool 29 publishDeps bool 30 goArg []string 31 buildConfiguration *utils.BuildConfiguration 32 deployerParams *utils.RepositoryConfig 33 resolverParams *utils.RepositoryConfig 34 } 35 36 func NewGoCommand() *GoCommand { 37 return &GoCommand{} 38 } 39 40 func (gc *GoCommand) SetResolverParams(resolverParams *utils.RepositoryConfig) *GoCommand { 41 gc.resolverParams = resolverParams 42 return gc 43 } 44 45 func (gc *GoCommand) SetDeployerParams(deployerParams *utils.RepositoryConfig) *GoCommand { 46 gc.deployerParams = deployerParams 47 return gc 48 } 49 50 func (gc *GoCommand) SetBuildConfiguration(buildConfiguration *utils.BuildConfiguration) *GoCommand { 51 gc.buildConfiguration = buildConfiguration 52 return gc 53 } 54 55 func (gc *GoCommand) SetNoRegistry(noRegistry bool) *GoCommand { 56 gc.noRegistry = noRegistry 57 return gc 58 } 59 60 func (gc *GoCommand) SetPublishDeps(publishDeps bool) *GoCommand { 61 gc.publishDeps = publishDeps 62 return gc 63 } 64 65 func (gc *GoCommand) SetGoArg(goArg []string) *GoCommand { 66 gc.goArg = goArg 67 return gc 68 } 69 70 func (gc *GoCommand) ServerDetails() (*config.ServerDetails, error) { 71 if gc.deployerParams != nil && !gc.deployerParams.IsServerDetailsEmpty() { 72 return gc.deployerParams.ServerDetails() 73 } 74 return gc.resolverParams.ServerDetails() 75 } 76 77 func (gc *GoCommand) CommandName() string { 78 return GoCommandName 79 } 80 81 func (gc *GoCommand) Run() error { 82 err := golang.LogGoVersion() 83 if err != nil { 84 return err 85 } 86 buildName := gc.buildConfiguration.BuildName 87 buildNumber := gc.buildConfiguration.BuildNumber 88 projectKey := gc.buildConfiguration.Project 89 isCollectBuildInfo := len(buildName) > 0 && len(buildNumber) > 0 90 if isCollectBuildInfo { 91 err = utils.SaveBuildGeneralDetails(buildName, buildNumber, projectKey) 92 if err != nil { 93 return err 94 } 95 } 96 97 resolverDetails, err := gc.resolverParams.ServerDetails() 98 if err != nil { 99 return err 100 } 101 resolverServiceManager, err := utils.CreateServiceManager(resolverDetails, -1, false) 102 if err != nil { 103 return err 104 } 105 resolverParams := ¶ms.Params{} 106 resolverParams.SetRepo(gc.resolverParams.TargetRepo()).SetServiceManager(resolverServiceManager) 107 goInfo := ¶ms.ResolverDeployer{} 108 goInfo.SetResolver(resolverParams) 109 var targetRepo string 110 var deployerServiceManager artifactory.ArtifactoryServicesManager 111 if gc.publishDeps { 112 deployerDetails, err := gc.deployerParams.ServerDetails() 113 if err != nil { 114 return err 115 } 116 deployerServiceManager, err = utils.CreateServiceManager(deployerDetails, -1, false) 117 if err != nil { 118 return err 119 } 120 targetRepo = gc.deployerParams.TargetRepo() 121 deployerParams := ¶ms.Params{} 122 deployerParams.SetRepo(targetRepo).SetServiceManager(deployerServiceManager) 123 goInfo.SetDeployer(deployerParams) 124 } 125 126 err = gocmd.RunWithFallbacksAndPublish(gc.goArg, gc.noRegistry, gc.publishDeps, goInfo) 127 if err != nil { 128 return err 129 } 130 if isCollectBuildInfo { 131 tempDirPath := "" 132 if isGoGetCommand := len(gc.goArg) > 0 && gc.goArg[0] == "get"; isGoGetCommand { 133 if len(gc.goArg) < 2 { 134 // Package name was not supplied. Invalid go get commend 135 return errorutils.CheckError(errors.New("Invalid get command. Package name is missing")) 136 } 137 tempDirPath, err = fileutils.CreateTempDir() 138 if err != nil { 139 return err 140 } 141 // Cleanup the temp working directory at the end. 142 defer fileutils.RemoveTempDir(tempDirPath) 143 // Get Artifactory config in order to extract the package version. 144 serverDetails, err := resolverDetails.CreateArtAuthConfig() 145 if err != nil { 146 return err 147 } 148 err = copyGoPackageFiles(tempDirPath, gc.goArg[1], gc.resolverParams.TargetRepo(), serverDetails) 149 if err != nil { 150 return err 151 } 152 } 153 goProject, err := project.Load("-", tempDirPath) 154 if err != nil { 155 return err 156 } 157 includeInfoFiles, err := shouldIncludeInfoFiles(deployerServiceManager, resolverServiceManager) 158 if err != nil { 159 return err 160 } 161 err = goProject.LoadDependencies() 162 if err != nil { 163 return err 164 } 165 err = goProject.CreateBuildInfoDependencies(includeInfoFiles) 166 if err != nil { 167 return err 168 } 169 err = utils.SaveBuildInfo(buildName, buildNumber, projectKey, goProject.BuildInfo(false, gc.buildConfiguration.Module, targetRepo)) 170 } 171 172 return err 173 } 174 175 // copyGoPackageFiles copies the package files from the go mod cache directory to the given destPath. 176 // The path to those chache files is retrived using the supplied package name and Artifactory details. 177 func copyGoPackageFiles(destPath, packageName, rtTargetRepo string, authArtDetails auth.ServiceDetails) error { 178 packageFilesPath, err := getPackageFilePathFromArtifactory(packageName, rtTargetRepo, authArtDetails) 179 if err != nil { 180 return err 181 } 182 // Copy the entire content of the relevant Go pkg directory to the requested destination path. 183 err = fileutils.CopyDir(packageFilesPath, destPath, true, nil) 184 if err != nil { 185 return fmt.Errorf("Couldn't find suitable package files: %s", packageFilesPath) 186 } 187 return nil 188 } 189 190 // getPackageFilePathFromArtifactory returns a string that represents the package files chache path. 191 // In most cases the path to those chache files is retrived using the supplied package name and Artifactory details. 192 // However if the user asked for a specifc version (package@vX.Y.Z) the unnecessary call to Artifactpry is avoided. 193 func getPackageFilePathFromArtifactory(packageName, rtTargetRepo string, authArtDetails auth.ServiceDetails) (packageFilesPath string, err error) { 194 var version string 195 packageCachePath, err := executersutils.GetGoModCachePath() 196 if err != nil { 197 return 198 } 199 packageNameSplitted := strings.Split(packageName, "@") 200 name := packageNameSplitted[0] 201 // The case the user asks for a specifc version 202 if len(packageNameSplitted) == 2 && strings.HasPrefix(packageNameSplitted[1], "v") { 203 version = packageNameSplitted[1] 204 } else { 205 branchName := "" 206 // The case the user asks for a specifc branch 207 if len(packageNameSplitted) == 2 { 208 branchName = packageNameSplitted[1] 209 } 210 packageVersionRequest := buildPackageVersionRequest(name, branchName) 211 // Retrive the package version using Artifactory 212 version, err = executersutils.GetPackageVersion(rtTargetRepo, packageVersionRequest, authArtDetails) 213 if err != nil { 214 return 215 } 216 } 217 path, err := getFileSystemPackagePath(packageCachePath, name, version) 218 if err != nil { 219 return "", err 220 } 221 return path, nil 222 223 } 224 225 // getFileSystemPackagePath returns a string that represents the package files cache path. 226 // In some cases when the path isn't represented by the package name, instead the name represents a specific project's directory's path. 227 // In this case we will scan the path until we find the package directory. 228 // Example : When running 'go get github.com/golang/mock/mockgen@v1.4.1' 229 // * "mockgen" is a directory inside "mock" package ("mockgen" doesn't contain "go.mod"). 230 // * go download and save the whole "mock" package in local cache under 'github.com/golang/mock@v1.4.1' -- > 231 // "go get" downloads and saves the whole "mock" package in the local cache under 'github.com/golang/mock@v1.4.1' 232 func getFileSystemPackagePath(packageCachePath, name, version string) (string, error) { 233 separator := string(filepath.Separator) 234 // For Windows OS 235 path := filepath.Join(name) 236 for path != "" { 237 packagePath := filepath.Join(packageCachePath, path+"@"+version) 238 exists, err := fileutils.IsDirExists(packagePath, false) 239 if err != nil { 240 return "", err 241 } 242 if exists { 243 return packagePath, nil 244 } 245 // Remove path's last element and check again 246 path, _ = filepath.Split(path) 247 path = strings.TrimSuffix(path, separator) 248 } 249 return "", errors.New("Could not find package:" + name + " in:" + packageCachePath) 250 } 251 252 // buildPackageVersionRequest returns a string representing the version request to Artifactory. 253 // The resulted string is in the following format: "<Package Name>/@V/<Branch Name>.info". 254 // If a branch name is not given, the branch name will be replaced with the "latest" keyword. 255 // ("<Package Name>/@V/latest.info"). 256 func buildPackageVersionRequest(name, branchName string) string { 257 packageVersionRequest := path.Join(name, "@v") 258 if branchName != "" { 259 // A branch name was given by the user 260 return path.Join(packageVersionRequest, branchName+".info") 261 } 262 // No version was given to "go get" command, so the latest version should be requested 263 return path.Join(packageVersionRequest, "latest.info") 264 } 265 266 // Returns true/false if info files should be included in the build info. 267 func shouldIncludeInfoFiles(deployerServiceManager artifactory.ArtifactoryServicesManager, resolverServiceManager artifactory.ArtifactoryServicesManager) (bool, error) { 268 var artifactoryVersion string 269 var err error 270 if deployerServiceManager != nil { 271 artifactoryVersion, err = deployerServiceManager.GetConfig().GetServiceDetails().GetVersion() 272 } else { 273 artifactoryVersion, err = resolverServiceManager.GetConfig().GetServiceDetails().GetVersion() 274 } 275 if err != nil { 276 return false, err 277 } 278 version := version.NewVersion(artifactoryVersion) 279 includeInfoFiles := version.AtLeast(_go.ArtifactoryMinSupportedVersionForInfoFile) 280 return includeInfoFiles, nil 281 }