github.com/jfrog/jfrog-cli-core/v2@v2.52.0/artifactory/commands/buildinfo/publish.go (about)

     1  package buildinfo
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net/url"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  
    11  	buildinfo "github.com/jfrog/build-info-go/entities"
    12  	"github.com/jfrog/jfrog-cli-core/v2/artifactory/formats"
    13  	"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
    14  	"github.com/jfrog/jfrog-cli-core/v2/common/build"
    15  	"github.com/jfrog/jfrog-cli-core/v2/utils/config"
    16  	"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
    17  	"github.com/jfrog/jfrog-client-go/artifactory"
    18  	biconf "github.com/jfrog/jfrog-client-go/artifactory/buildinfo"
    19  	"github.com/jfrog/jfrog-client-go/artifactory/services"
    20  	artclientutils "github.com/jfrog/jfrog-client-go/artifactory/services/utils"
    21  	clientutils "github.com/jfrog/jfrog-client-go/utils"
    22  	"github.com/jfrog/jfrog-client-go/utils/errorutils"
    23  	"github.com/jfrog/jfrog-client-go/utils/log"
    24  )
    25  
    26  type BuildPublishCommand struct {
    27  	buildConfiguration *build.BuildConfiguration
    28  	serverDetails      *config.ServerDetails
    29  	config             *biconf.Configuration
    30  	detailedSummary    bool
    31  	summary            *clientutils.Sha256Summary
    32  }
    33  
    34  func NewBuildPublishCommand() *BuildPublishCommand {
    35  	return &BuildPublishCommand{}
    36  }
    37  
    38  func (bpc *BuildPublishCommand) SetConfig(config *biconf.Configuration) *BuildPublishCommand {
    39  	bpc.config = config
    40  	return bpc
    41  }
    42  
    43  func (bpc *BuildPublishCommand) SetServerDetails(serverDetails *config.ServerDetails) *BuildPublishCommand {
    44  	bpc.serverDetails = serverDetails
    45  	return bpc
    46  }
    47  
    48  func (bpc *BuildPublishCommand) SetBuildConfiguration(buildConfiguration *build.BuildConfiguration) *BuildPublishCommand {
    49  	bpc.buildConfiguration = buildConfiguration
    50  	return bpc
    51  }
    52  
    53  func (bpc *BuildPublishCommand) SetSummary(summary *clientutils.Sha256Summary) *BuildPublishCommand {
    54  	bpc.summary = summary
    55  	return bpc
    56  }
    57  
    58  func (bpc *BuildPublishCommand) GetSummary() *clientutils.Sha256Summary {
    59  	return bpc.summary
    60  }
    61  
    62  func (bpc *BuildPublishCommand) SetDetailedSummary(detailedSummary bool) *BuildPublishCommand {
    63  	bpc.detailedSummary = detailedSummary
    64  	return bpc
    65  }
    66  
    67  func (bpc *BuildPublishCommand) IsDetailedSummary() bool {
    68  	return bpc.detailedSummary
    69  }
    70  
    71  func (bpc *BuildPublishCommand) CommandName() string {
    72  	return "rt_build_publish"
    73  }
    74  
    75  func (bpc *BuildPublishCommand) ServerDetails() (*config.ServerDetails, error) {
    76  	return bpc.serverDetails, nil
    77  }
    78  
    79  func (bpc *BuildPublishCommand) Run() error {
    80  	servicesManager, err := utils.CreateServiceManager(bpc.serverDetails, -1, 0, bpc.config.DryRun)
    81  	if err != nil {
    82  		return err
    83  	}
    84  
    85  	buildInfoService := build.CreateBuildInfoService()
    86  	buildName, err := bpc.buildConfiguration.GetBuildName()
    87  	if err != nil {
    88  		return err
    89  	}
    90  	buildNumber, err := bpc.buildConfiguration.GetBuildNumber()
    91  	if err != nil {
    92  		return err
    93  	}
    94  	build, err := buildInfoService.GetOrCreateBuildWithProject(buildName, buildNumber, bpc.buildConfiguration.GetProject())
    95  	if errorutils.CheckError(err) != nil {
    96  		return err
    97  	}
    98  
    99  	build.SetAgentName(coreutils.GetCliUserAgentName())
   100  	build.SetAgentVersion(coreutils.GetCliUserAgentVersion())
   101  	build.SetBuildAgentVersion(coreutils.GetClientAgentVersion())
   102  	build.SetPrincipal(bpc.serverDetails.User)
   103  	build.SetBuildUrl(bpc.config.BuildUrl)
   104  
   105  	buildInfo, err := build.ToBuildInfo()
   106  	if errorutils.CheckError(err) != nil {
   107  		return err
   108  	}
   109  	err = buildInfo.IncludeEnv(strings.Split(bpc.config.EnvInclude, ";")...)
   110  	if errorutils.CheckError(err) != nil {
   111  		return err
   112  	}
   113  	err = buildInfo.ExcludeEnv(strings.Split(bpc.config.EnvExclude, ";")...)
   114  	if errorutils.CheckError(err) != nil {
   115  		return err
   116  	}
   117  	if bpc.buildConfiguration.IsLoadedFromConfigFile() {
   118  		buildInfo.Number, err = bpc.getNextBuildNumber(buildInfo.Name, servicesManager)
   119  		if errorutils.CheckError(err) != nil {
   120  			return err
   121  		}
   122  		bpc.buildConfiguration.SetBuildNumber(buildInfo.Number)
   123  	}
   124  	summary, err := servicesManager.PublishBuildInfo(buildInfo, bpc.buildConfiguration.GetProject())
   125  	if bpc.IsDetailedSummary() {
   126  		bpc.SetSummary(summary)
   127  	}
   128  	if err != nil || bpc.config.DryRun {
   129  		return err
   130  	}
   131  
   132  	buildLink, err := bpc.constructBuildInfoUiUrl(servicesManager, buildInfo.Started)
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	err = build.Clean()
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	logMsg := "Build info successfully deployed."
   143  	if bpc.IsDetailedSummary() {
   144  		log.Info(logMsg + " Browse it in Artifactory under " + buildLink)
   145  		return nil
   146  	}
   147  
   148  	log.Info(logMsg)
   149  	return logJsonOutput(buildLink)
   150  }
   151  
   152  func logJsonOutput(buildInfoUiUrl string) error {
   153  	output := formats.BuildPublishOutput{BuildInfoUiUrl: buildInfoUiUrl}
   154  	results, err := output.JSON()
   155  	if err != nil {
   156  		return errorutils.CheckError(err)
   157  	}
   158  	log.Output(clientutils.IndentJson(results))
   159  	return nil
   160  }
   161  
   162  func (bpc *BuildPublishCommand) constructBuildInfoUiUrl(servicesManager artifactory.ArtifactoryServicesManager, buildInfoStarted string) (string, error) {
   163  	buildTime, err := time.Parse(buildinfo.TimeFormat, buildInfoStarted)
   164  	if errorutils.CheckError(err) != nil {
   165  		return "", err
   166  	}
   167  	artVersion, err := servicesManager.GetVersion()
   168  	if err != nil {
   169  		return "", err
   170  	}
   171  	artVersionSlice := strings.Split(artVersion, ".")
   172  	majorVersion, err := strconv.Atoi(artVersionSlice[0])
   173  	if errorutils.CheckError(err) != nil {
   174  		return "", err
   175  	}
   176  	return bpc.getBuildInfoUiUrl(majorVersion, buildTime)
   177  }
   178  
   179  func (bpc *BuildPublishCommand) getBuildInfoUiUrl(majorVersion int, buildTime time.Time) (string, error) {
   180  	buildName, err := bpc.buildConfiguration.GetBuildName()
   181  	if err != nil {
   182  		return "", err
   183  	}
   184  	buildNumber, err := bpc.buildConfiguration.GetBuildNumber()
   185  	if err != nil {
   186  		return "", err
   187  	}
   188  
   189  	baseUrl := bpc.serverDetails.GetUrl()
   190  	if baseUrl == "" {
   191  		baseUrl = strings.TrimSuffix(strings.TrimSuffix(bpc.serverDetails.GetArtifactoryUrl(), "/"), "artifactory")
   192  	}
   193  	baseUrl = clientutils.AddTrailingSlashIfNeeded(baseUrl)
   194  
   195  	project := bpc.buildConfiguration.GetProject()
   196  	buildName, buildNumber, project = url.PathEscape(buildName), url.PathEscape(buildNumber), url.QueryEscape(project)
   197  
   198  	if majorVersion <= 6 {
   199  		return fmt.Sprintf("%vartifactory/webapp/#/builds/%v/%v",
   200  			baseUrl, buildName, buildNumber), nil
   201  	}
   202  	timestamp := buildTime.UnixMilli()
   203  	if project != "" {
   204  		return fmt.Sprintf("%vui/builds/%v/%v/%v/published?buildRepo=%v-build-info&projectKey=%v",
   205  			baseUrl, buildName, buildNumber, strconv.FormatInt(timestamp, 10), project, project), nil
   206  	}
   207  	return fmt.Sprintf("%vui/builds/%v/%v/%v/published?buildRepo=artifactory-build-info",
   208  		baseUrl, buildName, buildNumber, strconv.FormatInt(timestamp, 10)), nil
   209  }
   210  
   211  // Return the next build number based on the previously published build.
   212  // Return "1" if no build is found
   213  func (bpc *BuildPublishCommand) getNextBuildNumber(buildName string, servicesManager artifactory.ArtifactoryServicesManager) (string, error) {
   214  	publishedBuildInfo, found, err := servicesManager.GetBuildInfo(services.BuildInfoParams{BuildName: buildName, BuildNumber: artclientutils.LatestBuildNumberKey})
   215  	if err != nil {
   216  		return "", err
   217  	}
   218  	if !found || publishedBuildInfo.BuildInfo.Number == "" {
   219  		return "1", nil
   220  	}
   221  	latestBuildNumber, err := strconv.Atoi(publishedBuildInfo.BuildInfo.Number)
   222  	if errorutils.CheckError(err) != nil {
   223  		if errors.Is(err, strconv.ErrSyntax) {
   224  			log.Warn("The latest build number is " + publishedBuildInfo.BuildInfo.Number + ". Since it is not an integer, and therefore cannot be incremented to automatically generate the next build number, setting the next build number to 1.")
   225  			return "1", nil
   226  		}
   227  		return "", err
   228  	}
   229  	latestBuildNumber++
   230  	return strconv.Itoa(latestBuildNumber), nil
   231  }