github.com/jfrog/jfrog-cli-core/v2@v2.51.0/common/spec/specfiles.go (about)

     1  package spec
     2  
     3  import (
     4  	"encoding/json"
     5  
     6  	"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
     7  	"github.com/jfrog/jfrog-client-go/artifactory/services/utils"
     8  	clientutils "github.com/jfrog/jfrog-client-go/utils"
     9  	"github.com/jfrog/jfrog-client-go/utils/errorutils"
    10  	"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
    11  )
    12  
    13  type SpecFiles struct {
    14  	Files []File
    15  }
    16  
    17  func (spec *SpecFiles) Get(index int) *File {
    18  	if index < len(spec.Files) {
    19  		return &spec.Files[index]
    20  	}
    21  	return new(File)
    22  }
    23  
    24  func CreateSpecFromFile(specFilePath string, specVars map[string]string) (spec *SpecFiles, err error) {
    25  	spec = new(SpecFiles)
    26  	content, err := fileutils.ReadFile(specFilePath)
    27  	if errorutils.CheckError(err) != nil {
    28  		return
    29  	}
    30  
    31  	if len(specVars) > 0 {
    32  		content = coreutils.ReplaceVars(content, specVars)
    33  	}
    34  
    35  	err = json.Unmarshal(content, spec)
    36  	if errorutils.CheckError(err) != nil {
    37  		return
    38  	}
    39  	return
    40  }
    41  
    42  type File struct {
    43  	Aql                     utils.Aql
    44  	PathMapping             utils.PathMapping
    45  	Pattern                 string
    46  	Exclusions              []string
    47  	Target                  string
    48  	Explode                 string
    49  	BypassArchiveInspection string
    50  	Props                   string
    51  	TargetProps             string
    52  	ExcludeProps            string
    53  	SortOrder               string
    54  	SortBy                  []string
    55  	Offset                  int
    56  	Limit                   int
    57  	Build                   string
    58  	Project                 string
    59  	ExcludeArtifacts        string
    60  	IncludeDeps             string
    61  	Bundle                  string
    62  	PublicGpgKey            string `json:"gpg-key,omitempty"`
    63  	Recursive               string
    64  	Flat                    string
    65  	Regexp                  string
    66  	Ant                     string
    67  	IncludeDirs             string
    68  	ArchiveEntries          string
    69  	ValidateSymlinks        string
    70  	Archive                 string
    71  	Symlinks                string
    72  	Transitive              string
    73  	TargetPathInArchive     string
    74  	include                 []string
    75  }
    76  
    77  func (f File) GetInclude() []string {
    78  	return f.include
    79  }
    80  
    81  func (f File) IsFlat(defaultValue bool) (bool, error) {
    82  	return clientutils.StringToBool(f.Flat, defaultValue)
    83  }
    84  
    85  func (f File) IsExplode(defaultValue bool) (bool, error) {
    86  	return clientutils.StringToBool(f.Explode, defaultValue)
    87  }
    88  
    89  func (f File) IsBypassArchiveInspection(defaultValue bool) (bool, error) {
    90  	return clientutils.StringToBool(f.BypassArchiveInspection, defaultValue)
    91  }
    92  
    93  func (f File) IsRegexp(defaultValue bool) (bool, error) {
    94  	return clientutils.StringToBool(f.Regexp, defaultValue)
    95  }
    96  
    97  func (f File) IsAnt(defaultValue bool) (bool, error) {
    98  	return clientutils.StringToBool(f.Ant, defaultValue)
    99  }
   100  
   101  func (f File) IsRecursive(defaultValue bool) (bool, error) {
   102  	return clientutils.StringToBool(f.Recursive, defaultValue)
   103  }
   104  
   105  func (f File) IsIncludeDirs(defaultValue bool) (bool, error) {
   106  	return clientutils.StringToBool(f.IncludeDirs, defaultValue)
   107  }
   108  
   109  func (f File) IsValidateSymlinks(defaultValue bool) (bool, error) {
   110  	return clientutils.StringToBool(f.ValidateSymlinks, defaultValue)
   111  }
   112  
   113  func (f File) IsExcludeArtifacts(defaultValue bool) (bool, error) {
   114  	return clientutils.StringToBool(f.ExcludeArtifacts, defaultValue)
   115  }
   116  
   117  func (f File) IsIncludeDeps(defaultValue bool) (bool, error) {
   118  	return clientutils.StringToBool(f.IncludeDeps, defaultValue)
   119  }
   120  
   121  func (f File) IsSymlinks(defaultValue bool) (bool, error) {
   122  	return clientutils.StringToBool(f.Symlinks, defaultValue)
   123  }
   124  
   125  func (f File) IsTransitive(defaultValue bool) (bool, error) {
   126  	return clientutils.StringToBool(f.Transitive, defaultValue)
   127  }
   128  
   129  func (f File) GetPatternType() (patternType clientutils.PatternType) {
   130  	if regex, _ := f.IsRegexp(false); regex {
   131  		return clientutils.RegExp
   132  	}
   133  	if ant, _ := f.IsAnt(false); ant {
   134  		return clientutils.AntPattern
   135  	}
   136  	return clientutils.WildCardPattern
   137  }
   138  
   139  func (f File) GetPublicGpgKey() string {
   140  	return f.PublicGpgKey
   141  }
   142  
   143  func (f *File) ToCommonParams() (*utils.CommonParams, error) {
   144  	var err error
   145  	params := new(utils.CommonParams)
   146  	params.TargetProps, err = utils.ParseProperties(f.TargetProps)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	params.Aql = f.Aql
   152  	params.PathMapping = f.PathMapping
   153  	params.Pattern = f.Pattern
   154  	params.Exclusions = f.Exclusions
   155  	params.Target = f.Target
   156  	params.Props = f.Props
   157  	params.ExcludeProps = f.ExcludeProps
   158  	params.Build = f.Build
   159  	params.Project = f.Project
   160  	params.Bundle = f.Bundle
   161  	params.SortOrder = f.SortOrder
   162  	params.SortBy = f.SortBy
   163  	params.Offset = f.Offset
   164  	params.Limit = f.Limit
   165  	params.ArchiveEntries = f.ArchiveEntries
   166  	return params, nil
   167  }
   168  
   169  func ValidateSpec(files []File, isTargetMandatory, isSearchBasedSpec bool) error {
   170  	if len(files) == 0 {
   171  		return errorutils.CheckErrorf("spec must include at least one file group")
   172  	}
   173  
   174  	for _, file := range files {
   175  		isAql := len(file.Aql.ItemsFind) > 0
   176  		isPathMapping := len(file.PathMapping.Input) > 0 && len(file.PathMapping.Output) > 0
   177  		isPattern := len(file.Pattern) > 0
   178  		isExclusions := len(file.Exclusions) > 0 && len(file.Exclusions[0]) > 0
   179  		isTarget := len(file.Target) > 0
   180  		isSortOrder := len(file.SortOrder) > 0
   181  		isSortBy := len(file.SortBy) > 0
   182  		isBuild := len(file.Build) > 0
   183  		isExcludeArtifacts, _ := file.IsExcludeArtifacts(false)
   184  		isIncludeDeps, _ := file.IsIncludeDeps(false)
   185  		isBundle := len(file.Bundle) > 0
   186  		isGPGKey := len(file.PublicGpgKey) > 0
   187  		isOffset := file.Offset > 0
   188  		isLimit := file.Limit > 0
   189  		isValidSortOrder := file.SortOrder == "asc" || file.SortOrder == "desc"
   190  		isExcludeProps := len(file.ExcludeProps) > 0
   191  		isArchive := len(file.Archive) > 0
   192  		isValidArchive := file.Archive == "zip"
   193  		isSymlinks, _ := file.IsSymlinks(false)
   194  		isRegexp := file.Regexp == "true"
   195  		isAnt := file.Ant == "true"
   196  		isExplode, _ := file.IsExplode(false)
   197  		isBypassArchiveInspection, _ := file.IsBypassArchiveInspection(false)
   198  		isTransitive, _ := file.IsTransitive(false)
   199  		if isPathMapping {
   200  			if !isAql {
   201  				return errorutils.CheckErrorf("pathMapping is supported only with aql")
   202  			}
   203  			if isTarget {
   204  				return fileSpecValidationError("pathMapping", "target")
   205  			}
   206  			if isPattern {
   207  				return fileSpecValidationError("pathMapping", "pattern")
   208  			}
   209  		}
   210  		if isTargetMandatory && !isTarget {
   211  			return errorutils.CheckErrorf("spec must include target")
   212  		}
   213  		if !isSearchBasedSpec && !isPattern {
   214  			return errorutils.CheckErrorf("spec must include a pattern")
   215  		}
   216  		if isBuild && isBundle {
   217  			return fileSpecValidationError("build", "bundle")
   218  		}
   219  		if isSearchBasedSpec && !isAql && !isPattern && !isBuild && !isBundle {
   220  			return errorutils.CheckErrorf("spec must include either aql, pattern, build or bundle")
   221  		}
   222  		if isOffset {
   223  			if isBuild {
   224  				return fileSpecValidationError("build", "offset")
   225  			}
   226  			if isBundle {
   227  				return fileSpecValidationError("bundle", "offset")
   228  			}
   229  		}
   230  		if isTransitive && isOffset {
   231  			return fileSpecValidationError("transitive", "offset")
   232  		}
   233  		if isLimit {
   234  			if isBuild {
   235  				return fileSpecValidationError("build", "limit")
   236  			}
   237  			if isBundle {
   238  				return fileSpecValidationError("bundle", "limit")
   239  			}
   240  		}
   241  		if isAql && isPattern {
   242  			return fileSpecValidationError("aql", "pattern")
   243  		}
   244  		if isAql && isExclusions {
   245  			return fileSpecValidationError("aql", "exclusions")
   246  		}
   247  		if isAql && isExcludeProps {
   248  			return fileSpecValidationError("aql", "excludeProps")
   249  		}
   250  		if !isSortBy && isSortOrder {
   251  			return errorutils.CheckErrorf("spec cannot include 'sort-order' if 'sort-by' is not included")
   252  		}
   253  		if isSortOrder && !isValidSortOrder {
   254  			return errorutils.CheckErrorf("the value of 'sort-order' can only be 'asc' or 'desc'")
   255  		}
   256  		if isTransitive && isSortBy {
   257  			return fileSpecValidationError("transitive", "sort-by")
   258  		}
   259  		if !isBuild && (isExcludeArtifacts || isIncludeDeps) {
   260  			return errorutils.CheckErrorf("spec cannot include 'exclude-artifacts' or 'include-deps' if 'build' is not included")
   261  		}
   262  		if isRegexp && isAnt {
   263  			return errorutils.CheckErrorf("can not use the option of regexp and ant together")
   264  		}
   265  		if isArchive && isSymlinks && isExplode {
   266  			return errorutils.CheckErrorf("symlinks cannot be stored in an archive that will be exploded in artifactory.\\nWhen uploading a symlink to Artifactory, the symlink is represented in Artifactory as 0 size filewith properties describing the symlink.\\nThis symlink representation is not yet supported by Artifactory when exploding symlinks from a zip")
   267  		}
   268  		if isArchive && !isValidArchive {
   269  			return errorutils.CheckErrorf("the value of 'archive' (if provided) must be 'zip'")
   270  		}
   271  		if isGPGKey && !isBundle {
   272  			return errorutils.CheckErrorf("spec cannot include 'gpg-key' if 'bundle' is not included")
   273  		}
   274  		if isBypassArchiveInspection && !isExplode {
   275  			return errorutils.CheckErrorf("spec cannot include 'bypass-archive-inspection' if 'explode' is not included")
   276  		}
   277  	}
   278  	return nil
   279  }
   280  
   281  func fileSpecValidationError(fieldA, fieldB string) error {
   282  	return errorutils.CheckErrorf("spec cannot include both '%s' and '%s'", fieldA, fieldB)
   283  }