github.com/jfrog/jfrog-cli-core/v2@v2.51.0/artifactory/commands/buildinfo/buildappend.go (about) 1 package buildinfo 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "io" 8 "strconv" 9 "time" 10 11 buildinfo "github.com/jfrog/build-info-go/entities" 12 "github.com/jfrog/jfrog-client-go/artifactory" 13 servicesutils "github.com/jfrog/jfrog-client-go/artifactory/services/utils" 14 15 "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" 16 "github.com/jfrog/jfrog-cli-core/v2/common/build" 17 "github.com/jfrog/jfrog-cli-core/v2/utils/config" 18 "github.com/jfrog/jfrog-client-go/artifactory/services" 19 "github.com/jfrog/jfrog-client-go/utils/errorutils" 20 "github.com/jfrog/jfrog-client-go/utils/log" 21 ) 22 23 type BuildAppendCommand struct { 24 buildConfiguration *build.BuildConfiguration 25 serverDetails *config.ServerDetails 26 buildNameToAppend string 27 buildNumberToAppend string 28 } 29 30 func NewBuildAppendCommand() *BuildAppendCommand { 31 return &BuildAppendCommand{} 32 } 33 34 func (bac *BuildAppendCommand) CommandName() string { 35 return "rt_build_append" 36 } 37 38 func (bac *BuildAppendCommand) ServerDetails() (*config.ServerDetails, error) { 39 return config.GetDefaultServerConf() 40 } 41 42 func (bac *BuildAppendCommand) Run() error { 43 log.Info("Running Build Append command...") 44 buildName, err := bac.buildConfiguration.GetBuildName() 45 if err != nil { 46 return err 47 } 48 buildNumber, err := bac.buildConfiguration.GetBuildNumber() 49 if err != nil { 50 return err 51 } 52 if err = build.SaveBuildGeneralDetails(buildName, buildNumber, bac.buildConfiguration.GetProject()); err != nil { 53 return err 54 } 55 56 // Create services manager to get build-info from Artifactory. 57 servicesManager, err := utils.CreateServiceManager(bac.serverDetails, -1, 0, false) 58 if err != nil { 59 return err 60 } 61 62 // Calculate build timestamp 63 timestamp, err := bac.getBuildTimestamp(servicesManager) 64 if err != nil { 65 return err 66 } 67 68 // Get checksum values from the build info artifact 69 checksumDetails, err := bac.getChecksumDetails(servicesManager, timestamp) 70 if err != nil { 71 return err 72 } 73 74 log.Debug("Appending build", bac.buildNameToAppend+"/"+bac.buildNumberToAppend, "to build info") 75 populateFunc := func(partial *buildinfo.Partial) { 76 partial.ModuleType = buildinfo.Build 77 partial.ModuleId = bac.buildNameToAppend + "/" + bac.buildNumberToAppend 78 partial.Checksum = buildinfo.Checksum{ 79 Sha1: checksumDetails.Sha1, 80 Md5: checksumDetails.Md5, 81 } 82 } 83 err = build.SavePartialBuildInfo(buildName, buildNumber, bac.buildConfiguration.GetProject(), populateFunc) 84 if err == nil { 85 log.Info("Build", bac.buildNameToAppend+"/"+bac.buildNumberToAppend, "successfully appended to", buildName+"/"+buildNumber) 86 } 87 return err 88 } 89 90 func (bac *BuildAppendCommand) SetServerDetails(serverDetails *config.ServerDetails) *BuildAppendCommand { 91 bac.serverDetails = serverDetails 92 return bac 93 } 94 95 func (bac *BuildAppendCommand) SetBuildConfiguration(buildConfiguration *build.BuildConfiguration) *BuildAppendCommand { 96 bac.buildConfiguration = buildConfiguration 97 return bac 98 } 99 100 func (bac *BuildAppendCommand) SetBuildNameToAppend(buildName string) *BuildAppendCommand { 101 bac.buildNameToAppend = buildName 102 return bac 103 } 104 105 func (bac *BuildAppendCommand) SetBuildNumberToAppend(buildNumber string) *BuildAppendCommand { 106 bac.buildNumberToAppend = buildNumber 107 return bac 108 } 109 110 // Get build timestamp of the build to append. The build timestamp has to be converted to milliseconds from epoch. 111 // For example, start time of: 2020-11-27T14:33:38.538+0200 should be converted to 1606480418538. 112 func (bac *BuildAppendCommand) getBuildTimestamp(servicesManager artifactory.ArtifactoryServicesManager) (int64, error) { 113 // Get published build-info from Artifactory. 114 buildInfoParams := services.BuildInfoParams{BuildName: bac.buildNameToAppend, BuildNumber: bac.buildNumberToAppend, ProjectKey: bac.buildConfiguration.GetProject()} 115 buildInfo, found, err := servicesManager.GetBuildInfo(buildInfoParams) 116 if err != nil { 117 return 0, err 118 } 119 buildString := fmt.Sprintf("Build %s/%s", bac.buildNameToAppend, bac.buildNumberToAppend) 120 if bac.buildConfiguration.GetProject() != "" { 121 buildString = buildString + " of project: " + bac.buildConfiguration.GetProject() 122 } 123 if !found { 124 return 0, errorutils.CheckErrorf(buildString + " not found in Artifactory.") 125 } 126 127 buildTime, err := time.Parse(buildinfo.TimeFormat, buildInfo.BuildInfo.Started) 128 if errorutils.CheckError(err) != nil { 129 return 0, err 130 } 131 132 // Convert from nanoseconds to milliseconds 133 timestamp := buildTime.UnixNano() / 1000000 134 log.Debug(buildString + ". Started: " + buildInfo.BuildInfo.Started + ". Calculated timestamp: " + strconv.FormatInt(timestamp, 10)) 135 136 return timestamp, err 137 } 138 139 // Download MD5 and SHA1 from the build info artifact. 140 func (bac *BuildAppendCommand) getChecksumDetails(servicesManager artifactory.ArtifactoryServicesManager, timestamp int64) (buildinfo.Checksum, error) { 141 // Run AQL query for build 142 stringTimestamp := strconv.FormatInt(timestamp, 10) 143 aqlQuery := servicesutils.CreateAqlQueryForBuildInfoJson(bac.buildConfiguration.GetProject(), bac.buildNameToAppend, bac.buildNumberToAppend, stringTimestamp) 144 stream, err := servicesManager.Aql(aqlQuery) 145 if err != nil { 146 return buildinfo.Checksum{}, err 147 } 148 defer func() { 149 err = errors.Join(err, errorutils.CheckError(stream.Close())) 150 }() 151 152 // Parse AQL results 153 aqlResults, err := io.ReadAll(stream) 154 if err != nil { 155 return buildinfo.Checksum{}, errorutils.CheckError(err) 156 } 157 parsedResult := new(servicesutils.AqlSearchResult) 158 if err = json.Unmarshal(aqlResults, parsedResult); err != nil { 159 return buildinfo.Checksum{}, errorutils.CheckError(err) 160 } 161 if len(parsedResult.Results) == 0 { 162 return buildinfo.Checksum{}, errorutils.CheckErrorf("Build '%s/%s' could not be found", bac.buildNameToAppend, bac.buildNumberToAppend) 163 } 164 165 // Verify checksum exist 166 sha1 := parsedResult.Results[0].Actual_Sha1 167 md5 := parsedResult.Results[0].Actual_Md5 168 if sha1 == "" || md5 == "" { 169 return buildinfo.Checksum{}, errorutils.CheckErrorf("Missing checksums for build-info: '%s/%s', sha1: '%s', md5: '%s'", bac.buildNameToAppend, bac.buildNumberToAppend, sha1, md5) 170 } 171 172 // Return checksums 173 return buildinfo.Checksum{Sha1: sha1, Md5: md5}, nil 174 }