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

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