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  }