github.com/jfrog/jfrog-cli-core@v1.12.1/artifactory/commands/buildinfo/adddependencies.go (about) 1 package buildinfo 2 3 import ( 4 "errors" 5 regxp "regexp" 6 "strconv" 7 8 commandsutils "github.com/jfrog/jfrog-cli-core/artifactory/commands/utils" 9 "github.com/jfrog/jfrog-cli-core/artifactory/spec" 10 "github.com/jfrog/jfrog-cli-core/artifactory/utils" 11 "github.com/jfrog/jfrog-cli-core/utils/config" 12 "github.com/jfrog/jfrog-client-go/artifactory" 13 "github.com/jfrog/jfrog-client-go/artifactory/buildinfo" 14 "github.com/jfrog/jfrog-client-go/artifactory/services" 15 "github.com/jfrog/jfrog-client-go/artifactory/services/fspatterns" 16 specutils "github.com/jfrog/jfrog-client-go/artifactory/services/utils" 17 clientutils "github.com/jfrog/jfrog-client-go/utils" 18 "github.com/jfrog/jfrog-client-go/utils/errorutils" 19 "github.com/jfrog/jfrog-client-go/utils/io/content" 20 "github.com/jfrog/jfrog-client-go/utils/io/fileutils" 21 "github.com/jfrog/jfrog-client-go/utils/log" 22 ) 23 24 type BuildAddDependenciesCommand struct { 25 buildConfiguration *utils.BuildConfiguration 26 dependenciesSpec *spec.SpecFiles 27 dryRun bool 28 result *commandsutils.Result 29 serverDetails *config.ServerDetails 30 } 31 32 func NewBuildAddDependenciesCommand() *BuildAddDependenciesCommand { 33 return &BuildAddDependenciesCommand{result: new(commandsutils.Result)} 34 } 35 36 func (badc *BuildAddDependenciesCommand) Result() *commandsutils.Result { 37 return badc.result 38 } 39 40 func (badc *BuildAddDependenciesCommand) CommandName() string { 41 return "rt_build_add_dependencies" 42 } 43 44 func (badc *BuildAddDependenciesCommand) ServerDetails() (*config.ServerDetails, error) { 45 if badc.serverDetails != nil { 46 return badc.serverDetails, nil 47 } 48 return config.GetDefaultServerConf() 49 } 50 51 func (badc *BuildAddDependenciesCommand) Run() error { 52 log.Info("Running Build Add Dependencies command...") 53 success, fail := 0, 0 54 var err error 55 if !badc.dryRun { 56 if err = utils.SaveBuildGeneralDetails(badc.buildConfiguration.BuildName, badc.buildConfiguration.BuildNumber, badc.buildConfiguration.Project); err != nil { 57 return err 58 } 59 } 60 if badc.serverDetails != nil { 61 log.Debug("Searching dependencies on Artifactory...") 62 success, fail, err = badc.collectRemoteDependencies() 63 } else { 64 log.Debug("Searching dependencies on local file system...") 65 success, fail, err = badc.collectLocalDependencies() 66 } 67 badc.result.SetSuccessCount(success) 68 badc.result.SetFailCount(fail) 69 return err 70 } 71 72 func (badc *BuildAddDependenciesCommand) SetDryRun(dryRun bool) *BuildAddDependenciesCommand { 73 badc.dryRun = dryRun 74 return badc 75 } 76 77 func (badc *BuildAddDependenciesCommand) SetDependenciesSpec(dependenciesSpec *spec.SpecFiles) *BuildAddDependenciesCommand { 78 badc.dependenciesSpec = dependenciesSpec 79 return badc 80 } 81 82 func (badc *BuildAddDependenciesCommand) SetServerDetails(serverDetails *config.ServerDetails) *BuildAddDependenciesCommand { 83 badc.serverDetails = serverDetails 84 return badc 85 } 86 87 func (badc *BuildAddDependenciesCommand) SetBuildConfiguration(buildConfiguration *utils.BuildConfiguration) *BuildAddDependenciesCommand { 88 badc.buildConfiguration = buildConfiguration 89 return badc 90 } 91 92 func collectDependenciesChecksums(dependenciesPaths map[string]string) (map[string]*fileutils.FileDetails, int) { 93 failures := 0 94 dependenciesDetails := make(map[string]*fileutils.FileDetails) 95 for _, dependencyPath := range dependenciesPaths { 96 var details *fileutils.FileDetails 97 var err error 98 if fileutils.IsPathSymlink(dependencyPath) { 99 log.Info("Adding symlink dependency:", dependencyPath) 100 details, err = fspatterns.CreateSymlinkFileDetails() 101 } else { 102 log.Info("Adding dependency:", dependencyPath) 103 details, err = fileutils.GetFileDetails(dependencyPath) 104 } 105 if err != nil { 106 log.Error(err) 107 failures++ 108 continue 109 } 110 dependenciesDetails[dependencyPath] = details 111 } 112 return dependenciesDetails, failures 113 } 114 115 func (badc *BuildAddDependenciesCommand) collectLocalDependencies() (success, fail int, err error) { 116 var dependenciesDetails map[string]*fileutils.FileDetails 117 dependenciesPaths, errorOccurred := badc.doCollectLocalDependencies() 118 dependenciesDetails, fail = collectDependenciesChecksums(dependenciesPaths) 119 if !badc.dryRun { 120 buildInfoDependencies := convertFileInfoToDependencies(dependenciesDetails) 121 err = badc.savePartialBuildInfo(buildInfoDependencies) 122 if err != nil { 123 // Mark all as failures. 124 fail = len(dependenciesDetails) 125 return 126 } 127 } 128 success = len(dependenciesDetails) 129 if errorOccurred || fail > 0 { 130 err = errors.New("Build Add Dependencies command finished with errors. Please review the logs.") 131 } 132 return 133 } 134 135 func (badc *BuildAddDependenciesCommand) collectRemoteDependencies() (success, fail int, err error) { 136 servicesManager, err := utils.CreateServiceManager(badc.serverDetails, -1, false) 137 if err != nil { 138 return 139 } 140 reader, err := searchItems(badc.dependenciesSpec, servicesManager) 141 if err != nil { 142 return 143 } 144 success, fail, err = badc.readRemoteDependencies(reader) 145 return 146 } 147 148 func (badc *BuildAddDependenciesCommand) doCollectLocalDependencies() (map[string]string, bool) { 149 errorOccurred := false 150 dependenciesPaths := make(map[string]string) 151 for _, specFile := range badc.dependenciesSpec.Files { 152 params, err := prepareArtifactoryParams(specFile) 153 if err != nil { 154 errorOccurred = true 155 log.Error(err) 156 continue 157 } 158 paths, err := getLocalDependencies(params) 159 if err != nil { 160 errorOccurred = true 161 log.Error(err) 162 continue 163 } 164 for _, path := range paths { 165 log.Debug("Found matching path:", path) 166 dependenciesPaths[path] = path 167 } 168 } 169 return dependenciesPaths, errorOccurred 170 } 171 172 func (badc *BuildAddDependenciesCommand) readRemoteDependencies(reader *content.ContentReader) (success, fail int, err error) { 173 if badc.dryRun { 174 success, err = reader.Length() 175 return 176 } 177 count := 0 178 var buildInfoDependencies []buildinfo.Dependency 179 for resultItem := new(specutils.ResultItem); reader.NextRecord(resultItem) == nil; resultItem = new(specutils.ResultItem) { 180 buildInfoDependencies = append(buildInfoDependencies, convertSearchResultToDependency(*resultItem)) 181 count++ 182 if count > clientutils.MaxBufferSize { 183 if err = badc.savePartialBuildInfo(buildInfoDependencies); err != nil { 184 return 185 } 186 success += count 187 count = 0 188 buildInfoDependencies = nil 189 } 190 } 191 if err = reader.GetError(); err != nil { 192 return 193 } 194 if count > 0 { 195 if err = badc.savePartialBuildInfo(buildInfoDependencies); err != nil { 196 fail += len(buildInfoDependencies) 197 return 198 } 199 } 200 success += count 201 return 202 } 203 204 func prepareArtifactoryParams(specFile spec.File) (*specutils.ArtifactoryCommonParams, error) { 205 params, err := specFile.ToArtifactoryCommonParams() 206 if err != nil { 207 return nil, err 208 } 209 210 recursive, err := clientutils.StringToBool(specFile.Recursive, true) 211 if err != nil { 212 return nil, err 213 } 214 215 params.Recursive = recursive 216 regexp, err := clientutils.StringToBool(specFile.Regexp, false) 217 if err != nil { 218 return nil, err 219 } 220 221 params.Regexp = regexp 222 return params, nil 223 } 224 225 func getLocalDependencies(addDepsParams *specutils.ArtifactoryCommonParams) ([]string, error) { 226 addDepsParams.SetPattern(clientutils.ReplaceTildeWithUserHome(addDepsParams.GetPattern())) 227 // Save parentheses index in pattern, witch have corresponding placeholder. 228 rootPath, err := fspatterns.GetRootPath(addDepsParams.GetPattern(), addDepsParams.GetTarget(), addDepsParams.GetPatternType(), false) 229 if err != nil { 230 return nil, err 231 } 232 233 isDir, err := fileutils.IsDirExists(rootPath, false) 234 if err != nil { 235 return nil, err 236 } 237 238 if !isDir || fileutils.IsPathSymlink(addDepsParams.GetPattern()) { 239 artifact, err := fspatterns.GetSingleFileToUpload(rootPath, "", false, false) 240 if err != nil { 241 return nil, err 242 } 243 return []string{artifact.LocalPath}, nil 244 } 245 return collectPatternMatchingFiles(addDepsParams, rootPath) 246 } 247 248 func collectPatternMatchingFiles(addDepsParams *specutils.ArtifactoryCommonParams, rootPath string) ([]string, error) { 249 addDepsParams.SetPattern(clientutils.PrepareLocalPathForUpload(addDepsParams.Pattern, addDepsParams.GetPatternType())) 250 excludePathPattern := fspatterns.PrepareExcludePathPattern(addDepsParams) 251 patternRegex, err := regxp.Compile(addDepsParams.Pattern) 252 if errorutils.CheckError(err) != nil { 253 return nil, err 254 } 255 256 paths, err := fspatterns.GetPaths(rootPath, addDepsParams.IsRecursive(), addDepsParams.IsIncludeDirs(), true) 257 if err != nil { 258 return nil, err 259 } 260 result := []string{} 261 262 for _, path := range paths { 263 matches, _, _, err := fspatterns.PrepareAndFilterPaths(path, excludePathPattern, true, false, patternRegex) 264 if err != nil { 265 log.Error(err) 266 continue 267 } 268 if len(matches) > 0 { 269 result = append(result, path) 270 } 271 } 272 return result, nil 273 } 274 275 func (badc *BuildAddDependenciesCommand) savePartialBuildInfo(dependencies []buildinfo.Dependency) error { 276 log.Debug("Saving", strconv.Itoa(len(dependencies)), "dependencies.") 277 populateFunc := func(partial *buildinfo.Partial) { 278 partial.Dependencies = dependencies 279 } 280 return utils.SavePartialBuildInfo(badc.buildConfiguration.BuildName, badc.buildConfiguration.BuildNumber, badc.buildConfiguration.Project, populateFunc) 281 } 282 283 func convertFileInfoToDependencies(files map[string]*fileutils.FileDetails) []buildinfo.Dependency { 284 var buildDependencies []buildinfo.Dependency 285 for filePath, fileInfo := range files { 286 dependency := buildinfo.Dependency{Checksum: buildinfo.Checksum{}} 287 dependency.Md5 = fileInfo.Checksum.Md5 288 dependency.Sha1 = fileInfo.Checksum.Sha1 289 filename, _ := fileutils.GetFileAndDirFromPath(filePath) 290 dependency.Id = filename 291 buildDependencies = append(buildDependencies, dependency) 292 } 293 return buildDependencies 294 } 295 296 func convertSearchResultToDependency(resultItem specutils.ResultItem) buildinfo.Dependency { 297 dependency := buildinfo.Dependency{Checksum: buildinfo.Checksum{Md5: resultItem.Actual_Md5, Sha1: resultItem.Actual_Sha1}} 298 dependency.Id = resultItem.Name 299 return dependency 300 } 301 302 func searchItems(spec *spec.SpecFiles, servicesManager artifactory.ArtifactoryServicesManager) (resultReader *content.ContentReader, err error) { 303 temp := []*content.ContentReader{} 304 var searchParams services.SearchParams 305 var reader *content.ContentReader 306 defer func() { 307 for _, reader := range temp { 308 e := reader.Close() 309 if err == nil { 310 err = e 311 } 312 } 313 }() 314 for i := 0; i < len(spec.Files); i++ { 315 searchParams, err = utils.GetSearchParams(spec.Get(i)) 316 if err != nil { 317 return 318 } 319 reader, err = servicesManager.SearchFiles(searchParams) 320 if err != nil { 321 return 322 } 323 temp = append(temp, reader) 324 } 325 resultReader, err = content.MergeReaders(temp, content.DefaultKey) 326 return 327 }