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