github.com/cobalt77/jfrog-client-go@v0.14.5/utils/utils.go (about) 1 package utils 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "os" 8 "path/filepath" 9 "regexp" 10 "runtime" 11 "strconv" 12 "strings" 13 14 "github.com/cobalt77/jfrog-client-go/utils/errorutils" 15 "github.com/cobalt77/jfrog-client-go/utils/log" 16 ) 17 18 const ( 19 Development = "development" 20 Agent = "jfrog-client-go" 21 Version = "0.14.1" 22 ) 23 24 // In order to limit the number of items loaded from a reader into the memory, we use a buffers with this size limit. 25 var MaxBufferSize = 50000 26 27 var userAgent = getDefaultUserAgent() 28 29 func getVersion() string { 30 return Version 31 } 32 33 func GetUserAgent() string { 34 return userAgent 35 } 36 37 func SetUserAgent(newUserAgent string) { 38 userAgent = newUserAgent 39 } 40 41 func getDefaultUserAgent() string { 42 return fmt.Sprintf("%s/%s", Agent, getVersion()) 43 } 44 45 // Get the local root path, from which to start collecting artifacts to be used for: 46 // 1. Uploaded to Artifactory, 47 // 2. Adding to the local build-info, to be later published to Artifactory. 48 func GetRootPath(path string, useRegExp bool, parentheses ParenthesesSlice) string { 49 // The first step is to split the local path pattern into sections, by the file separator. 50 separator := "/" 51 sections := strings.Split(path, separator) 52 if len(sections) == 1 { 53 separator = "\\" 54 sections = strings.Split(path, separator) 55 } 56 57 // Now we start building the root path, making sure to leave out the sub-directory that includes the pattern. 58 rootPath := "" 59 for _, section := range sections { 60 if section == "" { 61 continue 62 } 63 if useRegExp { 64 if strings.Index(section, "(") != -1 { 65 break 66 } 67 } else { 68 if strings.Index(section, "*") != -1 { 69 break 70 } 71 if strings.Index(section, "(") != -1 { 72 temp := rootPath + section 73 if isWildcardParentheses(temp, parentheses) { 74 break 75 } 76 } 77 } 78 if rootPath != "" { 79 rootPath += separator 80 } 81 if section == "~" { 82 rootPath += GetUserHomeDir() 83 } else { 84 rootPath += section 85 } 86 } 87 if len(sections) > 0 && sections[0] == "" { 88 rootPath = separator + rootPath 89 } 90 if rootPath == "" { 91 return "." 92 } 93 return rootPath 94 } 95 96 // Return true if the ‘str’ argument contains open parentasis, that is related to a placeholder. 97 // The ‘parentheses’ argument contains all the indexes of placeholder parentheses. 98 func isWildcardParentheses(str string, parentheses ParenthesesSlice) bool { 99 toFind := "(" 100 currStart := 0 101 for { 102 idx := strings.Index(str, toFind) 103 if idx == -1 { 104 break 105 } 106 if parentheses.IsPresent(idx) { 107 return true 108 } 109 currStart += idx + len(toFind) 110 str = str[idx+len(toFind):] 111 } 112 return false 113 } 114 115 func StringToBool(boolVal string, defaultValue bool) (bool, error) { 116 if len(boolVal) > 0 { 117 result, err := strconv.ParseBool(boolVal) 118 errorutils.CheckError(err) 119 return result, err 120 } 121 return defaultValue, nil 122 } 123 124 func AddTrailingSlashIfNeeded(url string) string { 125 if url != "" && !strings.HasSuffix(url, "/") { 126 url += "/" 127 } 128 return url 129 } 130 131 func IndentJson(jsonStr []byte) string { 132 return doIndentJson(jsonStr, "", " ") 133 } 134 135 func IndentJsonArray(jsonStr []byte) string { 136 return doIndentJson(jsonStr, " ", " ") 137 } 138 139 func doIndentJson(jsonStr []byte, prefix, indent string) string { 140 var content bytes.Buffer 141 err := json.Indent(&content, jsonStr, prefix, indent) 142 if err == nil { 143 return content.String() 144 } 145 return string(jsonStr) 146 } 147 148 func MergeMaps(src map[string]string, dst map[string]string) { 149 for k, v := range src { 150 dst[k] = v 151 } 152 } 153 154 func CopyMap(src map[string]string) (dst map[string]string) { 155 dst = make(map[string]string) 156 for k, v := range src { 157 dst[k] = v 158 } 159 return 160 } 161 162 func PrepareLocalPathForUpload(localPath string, useRegExp bool) string { 163 if localPath == "./" || localPath == ".\\" { 164 return "^.*$" 165 } 166 if strings.HasPrefix(localPath, "./") { 167 localPath = localPath[2:] 168 } else if strings.HasPrefix(localPath, ".\\") { 169 localPath = localPath[3:] 170 } 171 if !useRegExp { 172 localPath = pathToRegExp(cleanPath(localPath)) 173 } 174 return localPath 175 } 176 177 // Clean /../ | /./ using filepath.Clean. 178 func cleanPath(path string) string { 179 temp := path[len(path)-1:] 180 path = filepath.Clean(path) 181 if temp == `\` || temp == "/" { 182 path += temp 183 } 184 // Since filepath.Clean replaces \\ with \, we revert this action. 185 path = strings.Replace(path, `\`, `\\`, -1) 186 return path 187 } 188 189 func pathToRegExp(localPath string) string { 190 var SPECIAL_CHARS = []string{".", "^", "$", "+"} 191 for _, char := range SPECIAL_CHARS { 192 localPath = strings.Replace(localPath, char, "\\"+char, -1) 193 } 194 var wildcard = ".*" 195 localPath = strings.Replace(localPath, "*", wildcard, -1) 196 if strings.HasSuffix(localPath, "/") || strings.HasSuffix(localPath, "\\") { 197 localPath += wildcard 198 } 199 return "^" + localPath + "$" 200 } 201 202 // Replaces matched regular expression from path to corresponding placeholder {i} at target. 203 // Example 1: 204 // pattern = "repoA/1(.*)234" ; path = "repoA/1hello234" ; target = "{1}" ; ignoreRepo = false 205 // returns "hello" 206 // Example 2: 207 // pattern = "repoA/1(.*)234" ; path = "repoB/1hello234" ; target = "{1}" ; ignoreRepo = true 208 // returns "hello" 209 func BuildTargetPath(pattern, path, target string, ignoreRepo bool) (string, error) { 210 asteriskIndex := strings.Index(pattern, "*") 211 slashIndex := strings.Index(pattern, "/") 212 if shouldRemoveRepo(ignoreRepo, asteriskIndex, slashIndex) { 213 // Removing the repository part of the path is required when working with virtual repositories, as the pattern 214 // may contain the virtual-repository name, but the path contains the local-repository name. 215 pattern = removeRepoFromPath(pattern) 216 path = removeRepoFromPath(path) 217 } 218 pattern = addEscapingParentheses(pattern, target) 219 pattern = pathToRegExp(pattern) 220 if slashIndex < 0 { 221 // If '/' doesn't exist, add an optional trailing-slash to support cases in which the provided pattern 222 // is only the repository name. 223 dollarIndex := strings.LastIndex(pattern, "$") 224 pattern = pattern[:dollarIndex] 225 pattern += "(/.*)?$" 226 } 227 228 r, err := regexp.Compile(pattern) 229 err = errorutils.CheckError(err) 230 if err != nil { 231 return "", err 232 } 233 234 groups := r.FindStringSubmatch(path) 235 size := len(groups) 236 if size > 0 { 237 for i := 1; i < size; i++ { 238 group := strings.Replace(groups[i], "\\", "/", -1) 239 target = strings.Replace(target, "{"+strconv.Itoa(i)+"}", group, -1) 240 } 241 } 242 return target, nil 243 } 244 245 func GetLogMsgPrefix(threadId int, dryRun bool) string { 246 var strDryRun string 247 if dryRun { 248 strDryRun = "[Dry run] " 249 } 250 return "[Thread " + strconv.Itoa(threadId) + "] " + strDryRun 251 } 252 253 func TrimPath(path string) string { 254 path = strings.Replace(path, "\\", "/", -1) 255 path = strings.Replace(path, "//", "/", -1) 256 path = strings.Replace(path, "../", "", -1) 257 path = strings.Replace(path, "./", "", -1) 258 return path 259 } 260 261 func Bool2Int(b bool) int { 262 if b { 263 return 1 264 } 265 return 0 266 } 267 268 func ReplaceTildeWithUserHome(path string) string { 269 if len(path) > 1 && path[0:1] == "~" { 270 return GetUserHomeDir() + path[1:] 271 } 272 return path 273 } 274 275 func GetUserHomeDir() string { 276 if IsWindows() { 277 home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") 278 if home == "" { 279 home = os.Getenv("USERPROFILE") 280 } 281 return strings.Replace(home, "\\", "\\\\", -1) 282 } 283 return os.Getenv("HOME") 284 } 285 286 func GetBoolEnvValue(flagName string, defValue bool) (bool, error) { 287 envVarValue := os.Getenv(flagName) 288 if envVarValue == "" { 289 return defValue, nil 290 } 291 val, err := strconv.ParseBool(envVarValue) 292 err = CheckErrorWithMessage(err, "can't parse environment variable "+flagName) 293 return val, err 294 } 295 296 func CheckErrorWithMessage(err error, message string) error { 297 if err != nil { 298 log.Error(message) 299 err = errorutils.CheckError(err) 300 } 301 return err 302 } 303 304 func ConvertSliceToMap(slice []string) map[string]bool { 305 mapFromSlice := make(map[string]bool) 306 for _, value := range slice { 307 mapFromSlice[value] = true 308 } 309 return mapFromSlice 310 } 311 312 func removeRepoFromPath(path string) string { 313 if idx := strings.Index(path, "/"); idx != -1 { 314 return path[idx:] 315 } 316 return path 317 } 318 319 func shouldRemoveRepo(ignoreRepo bool, asteriskIndex, slashIndex int) bool { 320 if !ignoreRepo || slashIndex < 0 { 321 return false 322 } 323 if asteriskIndex < 0 { 324 return true 325 } 326 return IsSlashPrecedeAsterisk(asteriskIndex, slashIndex) 327 } 328 329 func IsSlashPrecedeAsterisk(asteriskIndex, slashIndex int) bool { 330 return slashIndex < asteriskIndex && slashIndex >= 0 331 } 332 333 // Split str by the provided separator, escaping the separator if it is prefixed by a back-slash. 334 func SplitWithEscape(str string, separator rune) []string { 335 var parts []string 336 var current bytes.Buffer 337 escaped := false 338 for _, char := range str { 339 if char == '\\' { 340 if escaped { 341 current.WriteRune(char) 342 } 343 escaped = true 344 } else if char == separator && !escaped { 345 parts = append(parts, current.String()) 346 current.Reset() 347 } else { 348 escaped = false 349 current.WriteRune(char) 350 } 351 } 352 parts = append(parts, current.String()) 353 return parts 354 } 355 356 func IsWindows() bool { 357 return runtime.GOOS == "windows" 358 } 359 360 type Artifact struct { 361 LocalPath string 362 TargetPath string 363 Symlink string 364 }