github.com/jfrog/jfrog-cli-core@v1.12.1/artifactory/commands/buildinfo/adddependencies.go (about)

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