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