github.com/jfrog/jfrog-cli-go@v1.22.1-0.20200318093948-4826ef344ffd/utils/cliutils/utils.go (about)

     1  package cliutils
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  	"path"
     8  	"path/filepath"
     9  	"runtime"
    10  	"strings"
    11  
    12  	"github.com/codegangsta/cli"
    13  	"github.com/jfrog/jfrog-cli-go/utils/summary"
    14  	"github.com/jfrog/jfrog-client-go/utils"
    15  	"github.com/jfrog/jfrog-client-go/utils/errorutils"
    16  	"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
    17  	"github.com/jfrog/jfrog-client-go/utils/log"
    18  	"github.com/pkg/errors"
    19  
    20  	clientutils "github.com/jfrog/jfrog-client-go/utils"
    21  )
    22  
    23  // Error modes (how should the application behave when the CheckError function is invoked):
    24  type OnError string
    25  
    26  var cliTempDir string
    27  var cliUserAgent string
    28  
    29  func init() {
    30  	// Initialize error handling.
    31  	if os.Getenv(ErrorHandling) == string(OnErrorPanic) {
    32  		errorutils.CheckError = PanicOnError
    33  	}
    34  
    35  	// Initialize the temp base-dir path of the CLI executions.
    36  	cliTempDir = os.Getenv(TempDir)
    37  	if cliTempDir == "" {
    38  		cliTempDir = os.TempDir()
    39  	}
    40  	fileutils.SetTempDirBase(cliTempDir)
    41  
    42  	// Initialize agent name and version.
    43  	cliUserAgent = os.Getenv(UserAgent)
    44  	if cliUserAgent == "" {
    45  		cliUserAgent = ClientAgent + "/" + CliVersion
    46  	}
    47  }
    48  
    49  // Exit codes:
    50  type ExitCode struct {
    51  	Code int
    52  }
    53  
    54  var ExitCodeNoError = ExitCode{0}
    55  var ExitCodeError = ExitCode{1}
    56  var ExitCodeFailNoOp = ExitCode{2}
    57  var ExitCodeVulnerableBuild = ExitCode{3}
    58  
    59  type CliError struct {
    60  	ExitCode
    61  	ErrorMsg string
    62  }
    63  
    64  func (err CliError) Error() string {
    65  	return err.ErrorMsg
    66  }
    67  
    68  func PanicOnError(err error) error {
    69  	if err != nil {
    70  		panic(err)
    71  	}
    72  	return err
    73  }
    74  
    75  func ExitOnErr(err error) {
    76  	if err, ok := err.(CliError); ok {
    77  		traceExit(err.ExitCode, err)
    78  	}
    79  	if exitCode := GetExitCode(err, 0, 0, false); exitCode != ExitCodeNoError {
    80  		traceExit(exitCode, err)
    81  	}
    82  }
    83  
    84  func GetCliError(err error, success, failed int, failNoOp bool) error {
    85  	switch GetExitCode(err, success, failed, failNoOp) {
    86  	case ExitCodeError:
    87  		{
    88  			var errorMessage string
    89  			if err != nil {
    90  				errorMessage = err.Error()
    91  			}
    92  			return CliError{ExitCodeError, errorMessage}
    93  		}
    94  	case ExitCodeFailNoOp:
    95  		return CliError{ExitCodeFailNoOp, "No errors, but also no files affected (fail-no-op flag)."}
    96  	default:
    97  		return nil
    98  	}
    99  }
   100  
   101  func GetExitCode(err error, success, failed int, failNoOp bool) ExitCode {
   102  	// Error occurred - Return 1
   103  	if err != nil || failed > 0 {
   104  		return ExitCodeError
   105  	}
   106  	// No errors, but also no files affected - Return 2 if failNoOp
   107  	if success == 0 && failNoOp {
   108  		return ExitCodeFailNoOp
   109  	}
   110  	// Otherwise - Return 0
   111  	return ExitCodeNoError
   112  }
   113  
   114  func traceExit(exitCode ExitCode, err error) {
   115  	if err != nil && len(err.Error()) > 0 {
   116  		log.Error(err)
   117  	}
   118  	os.Exit(exitCode.Code)
   119  }
   120  
   121  // Print summary report.
   122  // The given error will pass through and be returned as is if no other errors are raised.
   123  func PrintSummaryReport(success, failed int, err error) error {
   124  	summaryReport := summary.New(err)
   125  	summaryReport.Totals.Success = success
   126  	summaryReport.Totals.Failure = failed
   127  	if err == nil && summaryReport.Totals.Failure != 0 {
   128  		summaryReport.Status = summary.Failure
   129  	}
   130  	content, mErr := summaryReport.Marshal()
   131  	if errorutils.CheckError(mErr) != nil {
   132  		log.Error(mErr)
   133  		return err
   134  	}
   135  	log.Output(utils.IndentJson(content))
   136  	return err
   137  }
   138  
   139  func PrintHelpAndReturnError(msg string, context *cli.Context) error {
   140  	log.Error(msg + " " + GetDocumentationMessage())
   141  	cli.ShowCommandHelp(context, context.Command.Name)
   142  	return errors.New(msg)
   143  }
   144  
   145  // This function indicates whether the command should be executed without
   146  // confirmation warning or not.
   147  // If the --quiet option was sent, it is used to determine whether to prompt the confirmation or not.
   148  // If not, the command will prompt the confirmation, unless the CI environment variable was set to true.
   149  func GetQuietValue(c *cli.Context) bool {
   150  	if c.IsSet("quiet") {
   151  		return c.Bool("quiet")
   152  	}
   153  
   154  	return getCiValue()
   155  }
   156  
   157  // This function indicates whether the command should be executed in
   158  // an interactive mode.
   159  // If the --interactive option was sent, it is used to determine the mode.
   160  // If not, the mode will be interactive, unless the CI environment variable was set to true.
   161  func GetInteractiveValue(c *cli.Context) bool {
   162  	if c.IsSet("interactive") {
   163  		return c.BoolT("interactive")
   164  	}
   165  
   166  	return !getCiValue()
   167  }
   168  
   169  // Return the true if the CI environment variable was set to true.
   170  func getCiValue() bool {
   171  	var ci bool
   172  	var err error
   173  	if ci, err = clientutils.GetBoolEnvValue(CI, false); err != nil {
   174  		return false
   175  	}
   176  	return ci
   177  }
   178  
   179  func InteractiveConfirm(message string) bool {
   180  	var confirm string
   181  	fmt.Print(message + " (y/n): ")
   182  	fmt.Scanln(&confirm)
   183  	return confirmAnswer(confirm)
   184  }
   185  
   186  func confirmAnswer(answer string) bool {
   187  	answer = strings.ToLower(answer)
   188  	return answer == "y" || answer == "yes"
   189  }
   190  
   191  func GetVersion() string {
   192  	return CliVersion
   193  }
   194  
   195  func GetConfigVersion() string {
   196  	return "1"
   197  }
   198  
   199  func GetDocumentationMessage() string {
   200  	return "You can read the documentation at https://www.jfrog.com/confluence/display/CLI/JFrog+CLI"
   201  }
   202  
   203  func SumTrueValues(boolArr []bool) int {
   204  	counter := 0
   205  	for _, val := range boolArr {
   206  		counter += utils.Bool2Int(val)
   207  	}
   208  	return counter
   209  }
   210  
   211  func SpecVarsStringToMap(rawVars string) map[string]string {
   212  	if len(rawVars) == 0 {
   213  		return nil
   214  	}
   215  	varCandidates := strings.Split(rawVars, ";")
   216  	varsList := []string{}
   217  	for _, v := range varCandidates {
   218  		if len(varsList) > 0 && isEndsWithEscapeChar(varsList[len(varsList)-1]) {
   219  			currentLastVar := varsList[len(varsList)-1]
   220  			varsList[len(varsList)-1] = strings.TrimSuffix(currentLastVar, "\\") + ";" + v
   221  			continue
   222  		}
   223  		varsList = append(varsList, v)
   224  	}
   225  	return varsAsMap(varsList)
   226  }
   227  
   228  func isEndsWithEscapeChar(lastVar string) bool {
   229  	return strings.HasSuffix(lastVar, "\\")
   230  }
   231  
   232  func varsAsMap(vars []string) map[string]string {
   233  	result := map[string]string{}
   234  	for _, v := range vars {
   235  		keyVal := strings.SplitN(v, "=", 2)
   236  		if len(keyVal) != 2 {
   237  			continue
   238  		}
   239  		result[keyVal[0]] = keyVal[1]
   240  	}
   241  	return result
   242  }
   243  
   244  func IsWindows() bool {
   245  	return runtime.GOOS == "windows"
   246  }
   247  
   248  // Return the path of CLI temp dir.
   249  // This path should be persistent, meaning - should not be cleared at the end of a CLI run.
   250  func GetCliPersistentTempDirPath() string {
   251  	return cliTempDir
   252  }
   253  
   254  func GetUserAgent() string {
   255  	return cliUserAgent
   256  }
   257  
   258  type Credentials interface {
   259  	SetUser(string)
   260  	SetPassword(string)
   261  	GetUser() string
   262  	GetPassword() string
   263  }
   264  
   265  func ReplaceVars(content []byte, specVars map[string]string) []byte {
   266  	log.Debug("Replacing variables in the provided content: \n" + string(content))
   267  	for key, val := range specVars {
   268  		key = "${" + key + "}"
   269  		log.Debug(fmt.Sprintf("Replacing '%s' with '%s'", key, val))
   270  		content = bytes.Replace(content, []byte(key), []byte(val), -1)
   271  	}
   272  	log.Debug("The reformatted content is: \n" + string(content))
   273  	return content
   274  }
   275  
   276  func GetJfrogHomeDir() (string, error) {
   277  	// The JfrogHomeEnv environment variable has been deprecated and replaced with HomeDir
   278  	if os.Getenv(HomeDir) != "" {
   279  		return os.Getenv(HomeDir), nil
   280  	} else if os.Getenv(JfrogHomeEnv) != "" {
   281  		return path.Join(os.Getenv(JfrogHomeEnv), ".jfrog"), nil
   282  	}
   283  
   284  	userHomeDir := fileutils.GetHomeDir()
   285  	if userHomeDir == "" {
   286  		err := errorutils.CheckError(errors.New("couldn't find home directory. Make sure your HOME environment variable is set"))
   287  		if err != nil {
   288  			return "", err
   289  		}
   290  	}
   291  	return filepath.Join(userHomeDir, ".jfrog"), nil
   292  }
   293  
   294  func CreateDirInJfrogHome(dirName string) (string, error) {
   295  	homeDir, err := GetJfrogHomeDir()
   296  	if err != nil {
   297  		return "", err
   298  	}
   299  	folderName := filepath.Join(homeDir, dirName)
   300  	err = fileutils.CreateDirIfNotExist(folderName)
   301  	return folderName, err
   302  }
   303  
   304  func GetJfrogSecurityDir() (string, error) {
   305  	homeDir, err := GetJfrogHomeDir()
   306  	if err != nil {
   307  		return "", err
   308  	}
   309  	return filepath.Join(homeDir, "security"), nil
   310  }