github.com/jfrog/jfrog-cli-go@v1.22.1-0.20200318093948-4826ef344ffd/artifactory/commands/buildinfo/publish.go (about)

     1  package buildinfo
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/jfrog/jfrog-cli-go/artifactory/utils"
     6  	"github.com/jfrog/jfrog-cli-go/utils/cliutils"
     7  	"github.com/jfrog/jfrog-cli-go/utils/config"
     8  	"github.com/jfrog/jfrog-client-go/artifactory/buildinfo"
     9  	"github.com/jfrog/jfrog-client-go/utils/errorutils"
    10  	"path/filepath"
    11  	"sort"
    12  	"strings"
    13  )
    14  
    15  type BuildPublishCommand struct {
    16  	buildConfiguration *utils.BuildConfiguration
    17  	rtDetails          *config.ArtifactoryDetails
    18  	config             *buildinfo.Configuration
    19  }
    20  
    21  func NewBuildPublishCommand() *BuildPublishCommand {
    22  	return &BuildPublishCommand{}
    23  }
    24  
    25  func (bpc *BuildPublishCommand) SetConfig(config *buildinfo.Configuration) *BuildPublishCommand {
    26  	bpc.config = config
    27  	return bpc
    28  }
    29  
    30  func (bpc *BuildPublishCommand) SetRtDetails(rtDetails *config.ArtifactoryDetails) *BuildPublishCommand {
    31  	bpc.rtDetails = rtDetails
    32  	return bpc
    33  }
    34  
    35  func (bpc *BuildPublishCommand) SetBuildConfiguration(buildConfiguration *utils.BuildConfiguration) *BuildPublishCommand {
    36  	bpc.buildConfiguration = buildConfiguration
    37  	return bpc
    38  }
    39  
    40  func (bpc *BuildPublishCommand) CommandName() string {
    41  	return "rt_build_publish"
    42  }
    43  
    44  func (bpc *BuildPublishCommand) RtDetails() (*config.ArtifactoryDetails, error) {
    45  	return bpc.rtDetails, nil
    46  }
    47  
    48  func (bpc *BuildPublishCommand) Run() error {
    49  	servicesManager, err := utils.CreateServiceManager(bpc.rtDetails, bpc.config.DryRun)
    50  	if err != nil {
    51  		return err
    52  	}
    53  
    54  	buildInfo, err := bpc.createBuildInfoFromPartials()
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	generatedBuildsInfo, err := utils.GetGeneratedBuildsInfo(bpc.buildConfiguration.BuildName, bpc.buildConfiguration.BuildNumber)
    60  	if err != nil {
    61  		return err
    62  	}
    63  
    64  	for _, v := range generatedBuildsInfo {
    65  		buildInfo.Append(v)
    66  	}
    67  
    68  	if err = servicesManager.PublishBuildInfo(buildInfo); err != nil {
    69  		return err
    70  	}
    71  
    72  	if err = utils.RemoveBuildDir(bpc.buildConfiguration.BuildName, bpc.buildConfiguration.BuildNumber); err != nil {
    73  		return err
    74  	}
    75  	return nil
    76  }
    77  
    78  func (bpc *BuildPublishCommand) createBuildInfoFromPartials() (*buildinfo.BuildInfo, error) {
    79  	buildName := bpc.buildConfiguration.BuildName
    80  	buildNumber := bpc.buildConfiguration.BuildNumber
    81  	partials, err := utils.ReadPartialBuildInfoFiles(buildName, buildNumber)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	sort.Sort(partials)
    86  
    87  	buildInfo := buildinfo.New()
    88  	buildInfo.SetAgentName(cliutils.ClientAgent)
    89  	buildInfo.SetAgentVersion(cliutils.GetVersion())
    90  	buildInfo.SetBuildAgentVersion(cliutils.GetVersion())
    91  	buildInfo.SetArtifactoryPluginVersion(cliutils.GetUserAgent())
    92  	buildInfo.Name = buildName
    93  	buildInfo.Number = buildNumber
    94  	buildGeneralDetails, err := utils.ReadBuildInfoGeneralDetails(buildName, buildNumber)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	buildInfo.Started = buildGeneralDetails.Timestamp.Format("2006-01-02T15:04:05.000-0700")
    99  	modules, env, vcs, issues, err := extractBuildInfoData(partials, createIncludeFilter(bpc.config.EnvInclude), createExcludeFilter(bpc.config.EnvExclude))
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  	if len(env) != 0 {
   104  		buildInfo.Properties = env
   105  	}
   106  	buildInfo.ArtifactoryPrincipal = bpc.rtDetails.User
   107  	buildInfo.BuildUrl = bpc.config.BuildUrl
   108  	if vcs != (buildinfo.Vcs{}) {
   109  		buildInfo.Revision = vcs.Revision
   110  		buildInfo.Url = vcs.Url
   111  	}
   112  	// Check for Tracker as it must be set
   113  	if issues.Tracker != nil && issues.Tracker.Name != "" {
   114  		buildInfo.Issues = &issues
   115  	}
   116  	for _, module := range modules {
   117  		if module.Id == "" {
   118  			module.Id = buildName
   119  		}
   120  		buildInfo.Modules = append(buildInfo.Modules, module)
   121  	}
   122  	return buildInfo, nil
   123  }
   124  
   125  func extractBuildInfoData(partials buildinfo.Partials, includeFilter, excludeFilter filterFunc) ([]buildinfo.Module, buildinfo.Env, buildinfo.Vcs, buildinfo.Issues, error) {
   126  	var vcs buildinfo.Vcs
   127  	var issues buildinfo.Issues
   128  	env := make(map[string]string)
   129  	partialModules := make(map[string]partialModule)
   130  	issuesMap := make(map[string]*buildinfo.AffectedIssue)
   131  	for _, partial := range partials {
   132  		switch {
   133  		case partial.Artifacts != nil:
   134  			for _, artifact := range partial.Artifacts {
   135  				addArtifactToPartialModule(artifact, partial.ModuleId, partialModules)
   136  			}
   137  		case partial.Dependencies != nil:
   138  			for _, dependency := range partial.Dependencies {
   139  				addDependencyToPartialModule(dependency, partial.ModuleId, partialModules)
   140  			}
   141  		case partial.Vcs != nil:
   142  			vcs = *partial.Vcs
   143  			if partial.Issues == nil {
   144  				continue
   145  			}
   146  			// Collect issues.
   147  			issues.Tracker = partial.Issues.Tracker
   148  			issues.AggregateBuildIssues = partial.Issues.AggregateBuildIssues
   149  			issues.AggregationBuildStatus = partial.Issues.AggregationBuildStatus
   150  			// If affected issues exist, add them to issues map
   151  			if partial.Issues.AffectedIssues != nil {
   152  				for i, issue := range partial.Issues.AffectedIssues {
   153  					issuesMap[issue.Key] = &partial.Issues.AffectedIssues[i]
   154  				}
   155  			}
   156  		case partial.Env != nil:
   157  			envAfterIncludeFilter, e := includeFilter(partial.Env)
   158  			if errorutils.CheckError(e) != nil {
   159  				return partialModulesToModules(partialModules), env, vcs, issues, e
   160  			}
   161  			envAfterExcludeFilter, e := excludeFilter(envAfterIncludeFilter)
   162  			if errorutils.CheckError(e) != nil {
   163  				return partialModulesToModules(partialModules), env, vcs, issues, e
   164  			}
   165  			for k, v := range envAfterExcludeFilter {
   166  				env[k] = v
   167  			}
   168  		}
   169  	}
   170  	return partialModulesToModules(partialModules), env, vcs, issuesMapToArray(issues, issuesMap), nil
   171  }
   172  
   173  func partialModulesToModules(partialModules map[string]partialModule) []buildinfo.Module {
   174  	var modules []buildinfo.Module
   175  	for moduleId, singlePartialModule := range partialModules {
   176  		moduleArtifacts := artifactsMapToList(singlePartialModule.artifacts)
   177  		moduleDependencies := dependenciesMapToList(singlePartialModule.dependencies)
   178  		modules = append(modules, *createModule(moduleId, moduleArtifacts, moduleDependencies))
   179  	}
   180  	return modules
   181  }
   182  
   183  func issuesMapToArray(issues buildinfo.Issues, issuesMap map[string]*buildinfo.AffectedIssue) buildinfo.Issues {
   184  	for _, issue := range issuesMap {
   185  		issues.AffectedIssues = append(issues.AffectedIssues, *issue)
   186  	}
   187  	return issues
   188  }
   189  
   190  func addDependencyToPartialModule(dependency buildinfo.Dependency, moduleId string, partialModules map[string]partialModule) {
   191  	// init map if needed
   192  	if partialModules[moduleId].dependencies == nil {
   193  		partialModules[moduleId] =
   194  			partialModule{artifacts: partialModules[moduleId].artifacts,
   195  				dependencies: make(map[string]buildinfo.Dependency)}
   196  	}
   197  	key := fmt.Sprintf("%s-%s-%s-%s", dependency.Id, dependency.Sha1, dependency.Md5, dependency.Scopes)
   198  	partialModules[moduleId].dependencies[key] = dependency
   199  }
   200  
   201  func addArtifactToPartialModule(artifact buildinfo.Artifact, moduleId string, partialModules map[string]partialModule) {
   202  	// init map if needed
   203  	if partialModules[moduleId].artifacts == nil {
   204  		partialModules[moduleId] =
   205  			partialModule{artifacts: make(map[string]buildinfo.Artifact),
   206  				dependencies: partialModules[moduleId].dependencies}
   207  	}
   208  	key := fmt.Sprintf("%s-%s-%s", artifact.Name, artifact.Sha1, artifact.Md5)
   209  	partialModules[moduleId].artifacts[key] = artifact
   210  }
   211  
   212  func artifactsMapToList(artifactsMap map[string]buildinfo.Artifact) []buildinfo.Artifact {
   213  	var artifacts []buildinfo.Artifact
   214  	for _, artifact := range artifactsMap {
   215  		artifacts = append(artifacts, artifact)
   216  	}
   217  	return artifacts
   218  }
   219  
   220  func dependenciesMapToList(dependenciesMap map[string]buildinfo.Dependency) []buildinfo.Dependency {
   221  	var dependencies []buildinfo.Dependency
   222  	for _, dependency := range dependenciesMap {
   223  		dependencies = append(dependencies, dependency)
   224  	}
   225  	return dependencies
   226  }
   227  
   228  func createModule(moduleId string, artifacts []buildinfo.Artifact, dependencies []buildinfo.Dependency) *buildinfo.Module {
   229  	module := createDefaultModule(moduleId)
   230  	if artifacts != nil && len(artifacts) > 0 {
   231  		module.Artifacts = append(module.Artifacts, artifacts...)
   232  	}
   233  	if dependencies != nil && len(dependencies) > 0 {
   234  		module.Dependencies = append(module.Dependencies, dependencies...)
   235  	}
   236  	return module
   237  }
   238  
   239  func createDefaultModule(moduleId string) *buildinfo.Module {
   240  	return &buildinfo.Module{
   241  		Id:           moduleId,
   242  		Properties:   map[string][]string{},
   243  		Artifacts:    []buildinfo.Artifact{},
   244  		Dependencies: []buildinfo.Dependency{},
   245  	}
   246  }
   247  
   248  type filterFunc func(map[string]string) (map[string]string, error)
   249  
   250  func createIncludeFilter(pattern string) filterFunc {
   251  	includePattern := strings.Split(pattern, ";")
   252  	return func(tempMap map[string]string) (map[string]string, error) {
   253  		result := make(map[string]string)
   254  		for k, v := range tempMap {
   255  			for _, filterPattern := range includePattern {
   256  				matched, err := filepath.Match(strings.ToLower(filterPattern), strings.ToLower(k))
   257  				if errorutils.CheckError(err) != nil {
   258  					return nil, err
   259  				}
   260  				if matched {
   261  					result[k] = v
   262  					break
   263  				}
   264  			}
   265  		}
   266  		return result, nil
   267  	}
   268  }
   269  
   270  func createExcludeFilter(pattern string) filterFunc {
   271  	excludePattern := strings.Split(pattern, ";")
   272  	return func(tempMap map[string]string) (map[string]string, error) {
   273  		result := make(map[string]string)
   274  		for k, v := range tempMap {
   275  			include := true
   276  			for _, filterPattern := range excludePattern {
   277  				matched, err := filepath.Match(strings.ToLower(filterPattern), strings.ToLower(k))
   278  				if errorutils.CheckError(err) != nil {
   279  					return nil, err
   280  				}
   281  				if matched {
   282  					include = false
   283  					break
   284  				}
   285  			}
   286  			if include {
   287  				result[k] = v
   288  			}
   289  		}
   290  		return result, nil
   291  	}
   292  }
   293  
   294  type partialModule struct {
   295  	artifacts    map[string]buildinfo.Artifact
   296  	dependencies map[string]buildinfo.Dependency
   297  }