diff --git a/docs/common/env.go b/docs/common/env.go index f94704677..2f5cc2d4b 100644 --- a/docs/common/env.go +++ b/docs/common/env.go @@ -82,6 +82,10 @@ const ( JfrogCliEncryptionKey = ` JFROG_CLI_ENCRYPTION_KEY If provided, encrypt the sensitive data stored in the config with the provided key. Must be exactly 32 characters.` + + JfrogCliAvoidNewVersionWarning = ` JFROG_CLI_AVOID_NEW_VERSION_WARNING + [Default: false] + Set to true if you'd like to avoid checking the latest available JFrog CLI version and printing warning when it newer than the current one. ` ) var ( @@ -118,7 +122,8 @@ func GetGlobalEnvVars() string { JfrogCliBuildUrl, JfrogCliEnvExclude, JfrogCliFailNoOp, - JfrogCliEncryptionKey) + JfrogCliEncryptionKey, + JfrogCliAvoidNewVersionWarning) } func CreateEnvVars(envVars ...string) string { diff --git a/go.mod b/go.mod index 21c89572f..8a22d36de 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/agnivade/levenshtein v1.1.1 github.com/buger/jsonparser v1.1.1 github.com/go-git/go-git/v5 v5.6.1 - github.com/gocarina/gocsv v0.0.0-20230406101422-6445c2b15027 + github.com/gocarina/gocsv v0.0.0-20230513223533-9ddd7fd60602 github.com/jfrog/build-info-go v1.9.3 github.com/jfrog/gofrog v1.3.0 github.com/jfrog/jfrog-cli-core/v2 v2.32.1 @@ -15,14 +15,13 @@ require ( github.com/mholt/archiver/v3 v3.5.1 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.8.2 - github.com/testcontainers/testcontainers-go v0.20.0 + github.com/testcontainers/testcontainers-go v0.20.1 github.com/urfave/cli v1.22.13 github.com/vbauerster/mpb/v7 v7.5.3 github.com/xeipuuv/gojsonschema v1.2.0 - golang.org/x/exp v0.0.0-20230418202329-0354be287a23 - golang.org/x/term v0.7.0 + golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea + golang.org/x/term v0.8.0 gopkg.in/yaml.v2 v2.4.0 - ) require ( @@ -110,7 +109,7 @@ require ( golang.org/x/crypto v0.8.0 // indirect golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.9.0 // indirect - golang.org/x/sys v0.7.0 // indirect + golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect google.golang.org/grpc v1.52.0 // indirect diff --git a/go.sum b/go.sum index e18a1c220..e3efdc169 100644 --- a/go.sum +++ b/go.sum @@ -152,8 +152,8 @@ github.com/go-git/go-git/v5 v5.6.1/go.mod h1:mvyoL6Unz0PiTQrGQfSfiLFhBH1c1e84ylC github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/gocarina/gocsv v0.0.0-20230406101422-6445c2b15027 h1:LCGzZb4kMUUjMUzLxxqSJBwo9szUO0tK8cOxnEOT4Jc= -github.com/gocarina/gocsv v0.0.0-20230406101422-6445c2b15027/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= +github.com/gocarina/gocsv v0.0.0-20230513223533-9ddd7fd60602 h1:HSpPf+lPYwzoJNup34uegmOQk5Qm83S+wpu8anTDJkg= +github.com/gocarina/gocsv v0.0.0-20230513223533-9ddd7fd60602/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -387,8 +387,8 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/testcontainers/testcontainers-go v0.20.0 h1:ASrcJee7vcWNw43yUgL2n8KA5IOywrF031GawlrkVkE= -github.com/testcontainers/testcontainers-go v0.20.0/go.mod h1:zb+NOlCQBkZ7RQp4QI+YMIHyO2CQ/qsXzNF5eLJ24SY= +github.com/testcontainers/testcontainers-go v0.20.1 h1:mK15UPJ8c5P+NsQKmkqzs/jMdJt6JMs5vlw2y4j92c0= +github.com/testcontainers/testcontainers-go v0.20.1/go.mod h1:zb+NOlCQBkZ7RQp4QI+YMIHyO2CQ/qsXzNF5eLJ24SY= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= @@ -452,8 +452,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230418202329-0354be287a23 h1:4NKENAGIctmZYLK9W+X1kDK8ObBFqOSCJM6WE7CvkJY= -golang.org/x/exp v0.0.0-20230418202329-0354be287a23/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4= +golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -612,16 +612,16 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/go_test.go b/go_test.go index ffe779e27..f13da8359 100644 --- a/go_test.go +++ b/go_test.go @@ -238,7 +238,7 @@ func prepareGoProject(projectName, configDestDir string, t *testing.T, copyDirs _, err = tests.ReplaceTemplateVariables(filepath.Join(configFileDir, "go.yaml"), filepath.Join(configDestDir, "projects")) assert.NoError(t, err) clientTestUtils.ChangeDirAndAssert(t, projectPath) - log.Info("Using Go project located at ", projectPath) + log.Info("Using Go project located at", projectPath) return projectPath } diff --git a/main.go b/main.go index 25c598a0c..ddb09b4d8 100644 --- a/main.go +++ b/main.go @@ -90,7 +90,11 @@ func execMain() error { cli.AppHelpTemplate = getAppHelpTemplate() cli.SubcommandHelpTemplate = subcommandHelpTemplate app.CommandNotFound = func(c *cli.Context, command string) { - fmt.Fprintf(c.App.Writer, "'"+c.App.Name+" "+command+"' is not a jf command. See --help\n") + _, err := fmt.Fprintf(c.App.Writer, "'"+c.App.Name+" "+command+"' is not a jf command. See --help\n") + if err != nil { + clientlog.Debug(err) + os.Exit(1) + } if bestSimilarity := searchSimilarCmds(c.App.Commands, command); len(bestSimilarity) > 0 { text := "The most similar " if len(bestSimilarity) == 1 { @@ -99,13 +103,23 @@ func execMain() error { sort.Strings(bestSimilarity) text += "commands are:\n\tjf " + strings.Join(bestSimilarity, "\n\tjf ") } - fmt.Fprintln(c.App.Writer, text) + _, err = fmt.Fprintln(c.App.Writer, text) + if err != nil { + clientlog.Debug(err) + } } os.Exit(1) } app.Before = func(ctx *cli.Context) error { clientlog.Debug("JFrog CLI version:", app.Version) clientlog.Debug("OS/Arch:", runtime.GOOS+"/"+runtime.GOARCH) + warningMessage, err := cliutils.CheckNewCliVersionAvailable(app.Version) + if err != nil { + clientlog.Debug("failed while trying to check latest JFrog CLI version:", err.Error()) + } + if warningMessage != "" { + clientlog.Warn(warningMessage) + } return nil } err := app.Run(args) diff --git a/plugins/commands/install.go b/plugins/commands/install.go index 46ef0c5a4..f3e35681f 100644 --- a/plugins/commands/install.go +++ b/plugins/commands/install.go @@ -119,7 +119,7 @@ func getServerDetails() (string, config.ServerDetails, error) { return rtDetails.ArtifactoryUrl, *rtDetails, nil } -// Checks if the requested plugin exists in registry and does not exists locally. +// Checks if the requested plugin exists in registry and does not exist locally. func shouldDownloadPlugin(pluginsDir, pluginName, downloadUrl string, httpDetails httputils.HttpClientDetails) (bool, error) { exists, err := fileutils.IsDirExists(filepath.Join(pluginsDir, pluginName), false) if err != nil { @@ -133,13 +133,13 @@ func shouldDownloadPlugin(pluginsDir, pluginName, downloadUrl string, httpDetail if err != nil { return false, err } - log.Debug("Fetching plugin details from: ", downloadUrl) + log.Debug("Fetching plugin details from:", downloadUrl) details, resp, err := client.GetRemoteFileDetails(downloadUrl, httpDetails) if err != nil { return false, err } - log.Debug("Artifactory response: ", resp.Status) + log.Debug("Artifactory response:", resp.Status) err = errorutils.CheckResponseStatus(resp, http.StatusOK) if err != nil { return false, err @@ -216,7 +216,7 @@ func downloadPluginExec(downloadUrl, pluginName, pluginsDir string, httpDetails LocalFileName: exeName, RelativePath: exeName, } - log.Debug("Downloading plugin's executable from: ", downloadDetails.DownloadPath) + log.Debug("Downloading plugin's executable from:", downloadDetails.DownloadPath) response, err := downloadFromArtifactory(downloadDetails, httpDetails, progressMgr) if err != nil { return @@ -241,7 +241,7 @@ func downloadPluginsResources(downloadUrl, pluginName, pluginsDir string, httpDe LocalFileName: coreutils.PluginsResourcesDirName + ".zip", RelativePath: coreutils.PluginsResourcesDirName + ".zip", } - log.Debug("Downloading plugin's resources from: ", downloadDetails.DownloadPath) + log.Debug("Downloading plugin's resources from:", downloadDetails.DownloadPath) response, err := downloadFromArtifactory(downloadDetails, httpDetails, progressMgr) if err != nil { return diff --git a/plugins/commands/publish.go b/plugins/commands/publish.go index efa04e17e..8d740b62c 100644 --- a/plugins/commands/publish.go +++ b/plugins/commands/publish.go @@ -170,7 +170,7 @@ func verifyUniqueVersion(pluginName, pluginVersion string, rtDetails *config.Ser if err != nil { return err } - log.Debug("Artifactory response: ", resp.Status) + log.Debug("Artifactory response:", resp.Status) if resp.StatusCode == http.StatusOK { return errorutils.CheckErrorf("plugin version already exists on server") } diff --git a/poetry_test.go b/poetry_test.go index af0d7f344..582c607a0 100644 --- a/poetry_test.go +++ b/poetry_test.go @@ -17,13 +17,11 @@ import ( ) func TestPoetryInstall(t *testing.T) { + tests.SkipKnownFailingTest(t) + // Init poetry test. initPoetryTest(t) - if coreutils.IsWindows() { - tests.SkipKnownFailingTest(t) - } - // Populate cli config with 'default' server. oldHomeDir, newHomeDir := prepareHomeDir(t) defer func() { diff --git a/unit_test.go b/unit_test.go index 9cc17f64f..a78360424 100644 --- a/unit_test.go +++ b/unit_test.go @@ -12,7 +12,6 @@ import ( ) const ( - JfrogTestsHome = ".jfrogTest" CliIntegrationTests = "github.com/jfrog/jfrog-cli" ) diff --git a/utils/cliutils/cli_consts.go b/utils/cliutils/cli_consts.go index aab23edb0..181707a0c 100644 --- a/utils/cliutils/cli_consts.go +++ b/utils/cliutils/cli_consts.go @@ -1,5 +1,7 @@ package cliutils +import "time" + const ( // General CLI constants CliVersion = "2.37.1" @@ -23,15 +25,17 @@ const ( DownloadMaxSplitCount = 15 // Common - Retries = 3 - RetryWaitMilliSecs = 0 - Threads = 3 - TokenExpiry = 3600 - DefaultLicenseCount = 1 + Retries = 3 + RetryWaitMilliSecs = 0 + Threads = 3 + TokenExpiry = 3600 + DefaultLicenseCount = 1 + LatestCliVersionCheckInterval = time.Hour * 6 // Env BuildUrl = "JFROG_CLI_BUILD_URL" EnvExclude = "JFROG_CLI_ENV_EXCLUDE" UserAgent = "JFROG_CLI_USER_AGENT" JfrogCliAvoidDeprecationWarnings = "JFROG_CLI_AVOID_DEPRECATION_WARNINGS" + JfrogCliAvoidNewVersionWarning = "JFROG_CLI_AVOID_NEW_VERSION_WARNING" ) diff --git a/utils/cliutils/utils.go b/utils/cliutils/utils.go index 2a176861e..45a3538ba 100644 --- a/utils/cliutils/utils.go +++ b/utils/cliutils/utils.go @@ -1,11 +1,18 @@ package cliutils import ( + "encoding/json" "fmt" + "github.com/jfrog/gofrog/version" + "github.com/jfrog/jfrog-client-go/http/httpclient" + "github.com/jfrog/jfrog-client-go/utils/io/httputils" "io" + "net/http" "os" + "path" "path/filepath" "strings" + "time" corecontainercmds "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/container" commandUtils "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/utils" @@ -36,6 +43,11 @@ const ( // Error modes (how should the application behave when the CheckError function is invoked): type OnError string +type githubResponse struct { + TagName string `json:"tag_name,omitempty"` + URL string `json:"html_url"` +} + func init() { // Initialize cli-core values. cliUserAgent := os.Getenv(UserAgent) @@ -762,3 +774,65 @@ func CleanupResult(result *commandUtils.Result, originError *error) { } } } + +// Checks if the requested plugin exists in registry and does not exist locally. +func CheckNewCliVersionAvailable(currentVersion string) (warningMessage string, err error) { + shouldCheck, err := shouldCheckLatestCliVersion() + if err != nil || !shouldCheck { + return + } + githubVersionInfo, err := getLatestCliVersionFromGithubAPI() + if err != nil { + return + } + latestVersion := strings.TrimPrefix(githubVersionInfo.TagName, "v") + if version.NewVersion(latestVersion).Compare(currentVersion) < 0 { + warningMessage = strings.Join([]string{ + fmt.Sprintf("You are using JFrog CLI version %s, however version %s is available.", coreutils.PrintComment(currentVersion), coreutils.PrintTitle(latestVersion)), + fmt.Sprintf("To install the latest version, visit: %sgetcli", coreutils.JFrogComUrl), + "To see the release notes, visit: " + githubVersionInfo.URL, + fmt.Sprintf("To ignore this message you can use %s=TRUE", JfrogCliAvoidNewVersionWarning), + }, + "\n") + } + return +} + +func shouldCheckLatestCliVersion() (shouldCheck bool, err error) { + if strings.ToLower(os.Getenv(JfrogCliAvoidNewVersionWarning)) == "true" { + return + } + homeDir, err := coreutils.GetJfrogHomeDir() + if err != nil { + return + } + indicatorFile := path.Join(homeDir, "Latest_Cli_Version_Check_Indicator") + fileInfo, err := os.Stat(indicatorFile) + if err != nil && !os.IsNotExist(err) { + err = fmt.Errorf("couldn't get indicator file %s info: %s", indicatorFile, err.Error()) + return + } + if err == nil && (time.Now().UnixMilli()-fileInfo.ModTime().UnixMilli()) < LatestCliVersionCheckInterval.Milliseconds() { + // Timestamp file exists and updated less than 6 hours ago, therefor no need to check version again + return + } + return true, os.WriteFile(indicatorFile, []byte{}, 0666) +} + +func getLatestCliVersionFromGithubAPI() (githubVersionInfo githubResponse, err error) { + client, err := httpclient.ClientBuilder().Build() + if err != nil { + return + } + resp, body, _, err := client.SendGet("https://api.github.com/repos/jfrog/jfrog-cli/releases/latest", true, httputils.HttpClientDetails{HttpTimeout: time.Second * 2}, "") + if err != nil { + err = errors.New("couldn't get latest JFrog CLI latest version info from GitHub API: " + err.Error()) + return + } + err = errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK) + if err != nil { + return + } + err = json.Unmarshal(body, &githubVersionInfo) + return +} diff --git a/utils/cliutils/utils_test.go b/utils/cliutils/utils_test.go index 529833550..64993a982 100644 --- a/utils/cliutils/utils_test.go +++ b/utils/cliutils/utils_test.go @@ -2,6 +2,8 @@ package cliutils import ( "fmt" + configtests "github.com/jfrog/jfrog-cli-core/v2/utils/config/tests" + clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" "path/filepath" "strings" "testing" @@ -45,10 +47,10 @@ func TestPrintCommandSummary(t *testing.T) { result := &commandUtils.Result{} result.SetSuccessCount(1) result.SetFailCount(0) - teastdata := filepath.Join(tests.GetTestResourcesPath(), "reader", "printcommandsummary.json") + testdata := filepath.Join(tests.GetTestResourcesPath(), "reader", "printcommandsummary.json") tmpDir, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) defer createTempDirCallback() - err := fileutils.CopyFile(tmpDir, teastdata) + err := fileutils.CopyFile(tmpDir, testdata) assert.NoError(t, err) reader := content.NewContentReader(filepath.Join(tmpDir, "printcommandsummary.json"), content.DefaultKey) @@ -83,3 +85,53 @@ func TestPrintCommandSummary(t *testing.T) { assert.True(t, strings.Contains(string(output), test.expectedString), fmt.Sprintf("cant find '%s' in '%s'", test.expectedString, string(output))) } } + +func TestCheckNewCliVersionAvailable(t *testing.T) { + // Run the following tests on Artifactory tests suite only, to avoid reaching the GitHub API allowed rate limit (60 requests per hour) + // More info on https://docs.github.com/en/rest/overview/resources-in-the-rest-api?#rate-limiting + if !*tests.TestArtifactory { + return + } + + testCheckNewCliVersionAvailable(t, "0.0.0", true) + testCheckNewCliVersionAvailable(t, "100.100.100", false) +} + +func testCheckNewCliVersionAvailable(t *testing.T, version string, shouldWarn bool) { + // Create temp JFROG_HOME + cleanUpTempEnv := configtests.CreateTempEnv(t, false) + defer cleanUpTempEnv() + + // First run, should warn if needed + warningMessage, err := CheckNewCliVersionAvailable(version) + assert.NoError(t, err) + assert.Equal(t, warningMessage != "", shouldWarn) + + // Second run, shouldn't warn + warningMessage, err = CheckNewCliVersionAvailable(version) + assert.NoError(t, err) + assert.Empty(t, warningMessage) +} + +func TestShouldCheckLatestCliVersion(t *testing.T) { + // Create temp JFROG_HOME + cleanUpTempEnv := configtests.CreateTempEnv(t, false) + defer cleanUpTempEnv() + + // Validate that avoiding the version check using an environment variable is working + setEnvCallback := clientTestUtils.SetEnvWithCallbackAndAssert(t, JfrogCliAvoidNewVersionWarning, "true") + shouldCheck, err := shouldCheckLatestCliVersion() + assert.NoError(t, err) + assert.False(t, shouldCheck) + setEnvCallback() + + // First run, should be true + shouldCheck, err = shouldCheckLatestCliVersion() + assert.NoError(t, err) + assert.True(t, shouldCheck) + + // Second run, less than 6 hours between runs, so should return false + shouldCheck, err = shouldCheckLatestCliVersion() + assert.NoError(t, err) + assert.False(t, shouldCheck) +} diff --git a/utils/tests/utils.go b/utils/tests/utils.go index eaeb5c60b..bd16fa265 100644 --- a/utils/tests/utils.go +++ b/utils/tests/utils.go @@ -674,7 +674,7 @@ func CleanUpOldItems(baseItemNames []string, getActualItems func() ([]string, er itemTimestamp, err := strconv.ParseInt(regexGroups[len(regexGroups)-1], 10, 64) if err != nil { - log.Warn("Error while parsing timestamp of ", item, err) + log.Warn("Error while parsing timestamp of", item, err) continue }