github.com/jfrog/jfrog-cli-core@v1.12.1/artifactory/commands/buildinfo/publish.go (about) 1 package buildinfo 2 3 import ( 4 "fmt" 5 "github.com/jfrog/jfrog-client-go/artifactory" 6 clientutils "github.com/jfrog/jfrog-client-go/utils" 7 "github.com/jfrog/jfrog-client-go/utils/log" 8 "sort" 9 "strconv" 10 "strings" 11 "time" 12 13 "github.com/jfrog/jfrog-cli-core/artifactory/utils" 14 "github.com/jfrog/jfrog-cli-core/utils/config" 15 "github.com/jfrog/jfrog-cli-core/utils/coreutils" 16 "github.com/jfrog/jfrog-client-go/artifactory/buildinfo" 17 "github.com/jfrog/jfrog-client-go/utils/errorutils" 18 ) 19 20 type BuildPublishCommand struct { 21 buildConfiguration *utils.BuildConfiguration 22 serverDetails *config.ServerDetails 23 config *buildinfo.Configuration 24 detailedSummary bool 25 summary *clientutils.Sha256Summary 26 } 27 28 func NewBuildPublishCommand() *BuildPublishCommand { 29 return &BuildPublishCommand{} 30 } 31 32 func (bpc *BuildPublishCommand) SetConfig(config *buildinfo.Configuration) *BuildPublishCommand { 33 bpc.config = config 34 return bpc 35 } 36 37 func (bpc *BuildPublishCommand) SetServerDetails(serverDetails *config.ServerDetails) *BuildPublishCommand { 38 bpc.serverDetails = serverDetails 39 return bpc 40 } 41 42 func (bpc *BuildPublishCommand) SetBuildConfiguration(buildConfiguration *utils.BuildConfiguration) *BuildPublishCommand { 43 bpc.buildConfiguration = buildConfiguration 44 return bpc 45 } 46 47 func (bpc *BuildPublishCommand) SetSummary(summary *clientutils.Sha256Summary) *BuildPublishCommand { 48 bpc.summary = summary 49 return bpc 50 } 51 52 func (bpc *BuildPublishCommand) GetSummary() *clientutils.Sha256Summary { 53 return bpc.summary 54 } 55 56 func (bpc *BuildPublishCommand) SetDetailedSummary(detailedSummary bool) *BuildPublishCommand { 57 bpc.detailedSummary = detailedSummary 58 return bpc 59 } 60 61 func (bpc *BuildPublishCommand) IsDetailedSummary() bool { 62 return bpc.detailedSummary 63 } 64 65 func (bpc *BuildPublishCommand) CommandName() string { 66 return "rt_build_publish" 67 } 68 69 func (bpc *BuildPublishCommand) ServerDetails() (*config.ServerDetails, error) { 70 return bpc.serverDetails, nil 71 } 72 73 func (bpc *BuildPublishCommand) Run() error { 74 servicesManager, err := utils.CreateServiceManager(bpc.serverDetails, -1, bpc.config.DryRun) 75 if err != nil { 76 return err 77 } 78 79 buildInfo, err := bpc.createBuildInfoFromPartials() 80 if err != nil { 81 return err 82 } 83 84 generatedBuildsInfo, err := utils.GetGeneratedBuildsInfo(bpc.buildConfiguration.BuildName, bpc.buildConfiguration.BuildNumber, bpc.buildConfiguration.Project) 85 if err != nil { 86 return err 87 } 88 89 for _, v := range generatedBuildsInfo { 90 buildInfo.Append(v) 91 } 92 summary, err := servicesManager.PublishBuildInfo(buildInfo, bpc.buildConfiguration.Project) 93 if bpc.IsDetailedSummary() { 94 bpc.SetSummary(summary) 95 } 96 if err != nil || bpc.config.DryRun { 97 return err 98 } 99 100 buildLink, err := bpc.constructBuildInfoUiUrl(servicesManager, buildInfo.Started) 101 if err != nil { 102 return err 103 } 104 log.Info("Build info successfully deployed. Browse it in Artifactory under " + buildLink) 105 return utils.RemoveBuildDir(bpc.buildConfiguration.BuildName, bpc.buildConfiguration.BuildNumber, bpc.buildConfiguration.Project) 106 } 107 108 func (bpc *BuildPublishCommand) constructBuildInfoUiUrl(servicesManager artifactory.ArtifactoryServicesManager, buildInfoStarted string) (string, error) { 109 buildTime, err := time.Parse(buildinfo.TimeFormat, buildInfoStarted) 110 if errorutils.CheckError(err) != nil { 111 return "", err 112 } 113 artVersion, err := servicesManager.GetVersion() 114 if err != nil { 115 return "", err 116 } 117 artVersionSlice := strings.Split(artVersion, ".") 118 majorVersion, err := strconv.Atoi(artVersionSlice[0]) 119 if errorutils.CheckError(err) != nil { 120 return "", err 121 } 122 return bpc.getBuildInfoUiUrl(majorVersion, buildTime), nil 123 } 124 125 func (bpc *BuildPublishCommand) getBuildInfoUiUrl(majorVersion int, buildTime time.Time) string { 126 if majorVersion <= 6 { 127 return fmt.Sprintf("%vartifactory/webapp/#/builds/%v/%v", 128 bpc.serverDetails.GetUrl(), bpc.buildConfiguration.BuildName, bpc.buildConfiguration.BuildNumber) 129 } else if bpc.buildConfiguration.Project != "" { 130 timestamp := buildTime.UnixNano() / 1000000 131 return fmt.Sprintf("%vui/builds/%v/%v/%v/published?buildRepo=%v-build-info&projectKey=%v", 132 bpc.serverDetails.GetUrl(), bpc.buildConfiguration.BuildName, bpc.buildConfiguration.BuildNumber, strconv.FormatInt(timestamp, 10), bpc.buildConfiguration.Project, bpc.buildConfiguration.Project) 133 } 134 timestamp := buildTime.UnixNano() / 1000000 135 return fmt.Sprintf("%vui/builds/%v/%v/%v/published?buildRepo=artifactory-build-info", 136 bpc.serverDetails.GetUrl(), bpc.buildConfiguration.BuildName, bpc.buildConfiguration.BuildNumber, strconv.FormatInt(timestamp, 10)) 137 } 138 139 func (bpc *BuildPublishCommand) createBuildInfoFromPartials() (*buildinfo.BuildInfo, error) { 140 buildName := bpc.buildConfiguration.BuildName 141 buildNumber := bpc.buildConfiguration.BuildNumber 142 projectKey := bpc.buildConfiguration.Project 143 partials, err := utils.ReadPartialBuildInfoFiles(buildName, buildNumber, projectKey) 144 if err != nil { 145 return nil, err 146 } 147 sort.Sort(partials) 148 149 buildInfo := buildinfo.New() 150 buildInfo.SetAgentName(coreutils.GetCliUserAgentName()) 151 buildInfo.SetAgentVersion(coreutils.GetCliUserAgentVersion()) 152 buildInfo.SetBuildAgentVersion(coreutils.GetClientAgentVersion()) 153 buildInfo.Name = buildName 154 buildInfo.Number = buildNumber 155 buildGeneralDetails, err := utils.ReadBuildInfoGeneralDetails(buildName, buildNumber, projectKey) 156 if err != nil { 157 return nil, err 158 } 159 buildInfo.Started = buildGeneralDetails.Timestamp.Format(buildinfo.TimeFormat) 160 modules, env, vcsList, issues, err := extractBuildInfoData(partials, bpc.config.IncludeFilter(), bpc.config.ExcludeFilter()) 161 if err != nil { 162 return nil, err 163 } 164 if len(env) != 0 { 165 buildInfo.Properties = env 166 } 167 buildInfo.ArtifactoryPrincipal = bpc.serverDetails.User 168 buildInfo.BuildUrl = bpc.config.BuildUrl 169 for _, vcs := range vcsList { 170 buildInfo.VcsList = append(buildInfo.VcsList, vcs) 171 } 172 173 // Check for Tracker as it must be set 174 if issues.Tracker != nil && issues.Tracker.Name != "" { 175 buildInfo.Issues = &issues 176 } 177 for _, module := range modules { 178 if module.Id == "" { 179 module.Id = buildName 180 } 181 buildInfo.Modules = append(buildInfo.Modules, module) 182 } 183 return buildInfo, nil 184 } 185 186 func extractBuildInfoData(partials buildinfo.Partials, includeFilter, excludeFilter buildinfo.Filter) ([]buildinfo.Module, buildinfo.Env, []buildinfo.Vcs, buildinfo.Issues, error) { 187 var vcs []buildinfo.Vcs 188 var issues buildinfo.Issues 189 env := make(map[string]string) 190 partialModules := make(map[string]*partialModule) 191 issuesMap := make(map[string]*buildinfo.AffectedIssue) 192 for _, partial := range partials { 193 moduleId := partial.ModuleId 194 if partialModules[moduleId] == nil { 195 partialModules[moduleId] = &partialModule{moduleType: partial.ModuleType} 196 } 197 switch { 198 case partial.Artifacts != nil: 199 for _, artifact := range partial.Artifacts { 200 addArtifactToPartialModule(artifact, moduleId, partialModules) 201 } 202 case partial.Dependencies != nil: 203 for _, dependency := range partial.Dependencies { 204 addDependencyToPartialModule(dependency, moduleId, partialModules) 205 } 206 case partial.VcsList != nil: 207 for _, partialVcs := range partial.VcsList { 208 vcs = append(vcs, partialVcs) 209 } 210 if partial.Issues == nil { 211 continue 212 } 213 // Collect issues. 214 issues.Tracker = partial.Issues.Tracker 215 issues.AggregateBuildIssues = partial.Issues.AggregateBuildIssues 216 issues.AggregationBuildStatus = partial.Issues.AggregationBuildStatus 217 // If affected issues exist, add them to issues map 218 if partial.Issues.AffectedIssues != nil { 219 for i, issue := range partial.Issues.AffectedIssues { 220 issuesMap[issue.Key] = &partial.Issues.AffectedIssues[i] 221 } 222 } 223 case partial.Env != nil: 224 envAfterIncludeFilter, e := includeFilter(partial.Env) 225 if errorutils.CheckError(e) != nil { 226 return partialModulesToModules(partialModules), env, vcs, issues, e 227 } 228 envAfterExcludeFilter, e := excludeFilter(envAfterIncludeFilter) 229 if errorutils.CheckError(e) != nil { 230 return partialModulesToModules(partialModules), env, vcs, issues, e 231 } 232 for k, v := range envAfterExcludeFilter { 233 env[k] = v 234 } 235 case partial.ModuleType == buildinfo.Build: 236 partialModules[moduleId].checksum = partial.Checksum 237 } 238 } 239 return partialModulesToModules(partialModules), env, vcs, issuesMapToArray(issues, issuesMap), nil 240 } 241 242 func partialModulesToModules(partialModules map[string]*partialModule) []buildinfo.Module { 243 var modules []buildinfo.Module 244 for moduleId, singlePartialModule := range partialModules { 245 moduleArtifacts := artifactsMapToList(singlePartialModule.artifacts) 246 moduleDependencies := dependenciesMapToList(singlePartialModule.dependencies) 247 modules = append(modules, *createModule(moduleId, singlePartialModule.moduleType, singlePartialModule.checksum, moduleArtifacts, moduleDependencies)) 248 } 249 return modules 250 } 251 252 func issuesMapToArray(issues buildinfo.Issues, issuesMap map[string]*buildinfo.AffectedIssue) buildinfo.Issues { 253 for _, issue := range issuesMap { 254 issues.AffectedIssues = append(issues.AffectedIssues, *issue) 255 } 256 return issues 257 } 258 259 func addDependencyToPartialModule(dependency buildinfo.Dependency, moduleId string, partialModules map[string]*partialModule) { 260 // init map if needed 261 if partialModules[moduleId].dependencies == nil { 262 partialModules[moduleId].dependencies = make(map[string]buildinfo.Dependency) 263 } 264 key := fmt.Sprintf("%s-%s-%s-%s", dependency.Id, dependency.Sha1, dependency.Md5, dependency.Scopes) 265 partialModules[moduleId].dependencies[key] = dependency 266 } 267 268 func addArtifactToPartialModule(artifact buildinfo.Artifact, moduleId string, partialModules map[string]*partialModule) { 269 // init map if needed 270 if partialModules[moduleId].artifacts == nil { 271 partialModules[moduleId].artifacts = make(map[string]buildinfo.Artifact) 272 } 273 key := fmt.Sprintf("%s-%s-%s", artifact.Name, artifact.Sha1, artifact.Md5) 274 partialModules[moduleId].artifacts[key] = artifact 275 } 276 277 func artifactsMapToList(artifactsMap map[string]buildinfo.Artifact) []buildinfo.Artifact { 278 var artifacts []buildinfo.Artifact 279 for _, artifact := range artifactsMap { 280 artifacts = append(artifacts, artifact) 281 } 282 return artifacts 283 } 284 285 func dependenciesMapToList(dependenciesMap map[string]buildinfo.Dependency) []buildinfo.Dependency { 286 var dependencies []buildinfo.Dependency 287 for _, dependency := range dependenciesMap { 288 dependencies = append(dependencies, dependency) 289 } 290 return dependencies 291 } 292 293 func createModule(moduleId string, moduleType buildinfo.ModuleType, checksum buildinfo.Checksum, artifacts []buildinfo.Artifact, dependencies []buildinfo.Dependency) *buildinfo.Module { 294 module := createDefaultModule(moduleId) 295 module.Type = moduleType 296 module.Checksum = checksum 297 if artifacts != nil && len(artifacts) > 0 { 298 module.Artifacts = append(module.Artifacts, artifacts...) 299 } 300 if dependencies != nil && len(dependencies) > 0 { 301 module.Dependencies = append(module.Dependencies, dependencies...) 302 } 303 return module 304 } 305 306 func createDefaultModule(moduleId string) *buildinfo.Module { 307 return &buildinfo.Module{ 308 Id: moduleId, 309 Properties: map[string][]string{}, 310 Artifacts: []buildinfo.Artifact{}, 311 Dependencies: []buildinfo.Dependency{}, 312 } 313 } 314 315 type partialModule struct { 316 moduleType buildinfo.ModuleType 317 artifacts map[string]buildinfo.Artifact 318 dependencies map[string]buildinfo.Dependency 319 checksum buildinfo.Checksum 320 }