Skip to content

Commit bc998ed

Browse files
silvanocerzaper1234cmaglie
authored
Add ways to let users verify if new CLI released (arduino#1416)
* Add ways to let users verify if new CLI released * Code review fixes Co-authored-by: per1234 <accounts@perglass.com> * Enhance docs Co-authored-by: per1234 <accounts@perglass.com> * Fix version check for git-snapshots and nightlies * Change method to request latest release * Remove ansi library in favor of color * Fix go mod errors * Remove useless function Co-authored-by: Cristian Maglie <c.maglie@arduino.cc> Co-authored-by: per1234 <accounts@perglass.com> Co-authored-by: Cristian Maglie <c.maglie@arduino.cc>
1 parent ff4eb92 commit bc998ed

File tree

11 files changed

+248
-42
lines changed

11 files changed

+248
-42
lines changed

cli/cli.go

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939
"github.com/arduino/arduino-cli/cli/output"
4040
"github.com/arduino/arduino-cli/cli/sketch"
4141
"github.com/arduino/arduino-cli/cli/update"
42+
"github.com/arduino/arduino-cli/cli/updater"
4243
"github.com/arduino/arduino-cli/cli/upgrade"
4344
"github.com/arduino/arduino-cli/cli/upload"
4445
"github.com/arduino/arduino-cli/cli/version"
@@ -50,12 +51,14 @@ import (
5051
"github.com/rifflock/lfshook"
5152
"github.com/sirupsen/logrus"
5253
"github.com/spf13/cobra"
54+
semver "go.bug.st/relaxed-semver"
5355
)
5456

5557
var (
56-
verbose bool
57-
outputFormat string
58-
configFile string
58+
verbose bool
59+
outputFormat string
60+
configFile string
61+
updaterMessageChan chan *semver.Version = make(chan *semver.Version)
5962
)
6063

6164
// NewCommand creates a new ArduinoCli command root
@@ -64,11 +67,12 @@ func NewCommand() *cobra.Command {
6467

6568
// ArduinoCli is the root command
6669
arduinoCli := &cobra.Command{
67-
Use: "arduino-cli",
68-
Short: tr("Arduino CLI."),
69-
Long: tr("Arduino Command Line Interface (arduino-cli)."),
70-
Example: fmt.Sprintf(" %s <%s> [%s...]", os.Args[0], tr("command"), tr("flags")),
71-
PersistentPreRun: preRun,
70+
Use: "arduino-cli",
71+
Short: tr("Arduino CLI."),
72+
Long: tr("Arduino Command Line Interface (arduino-cli)."),
73+
Example: fmt.Sprintf(" %s <%s> [%s...]", os.Args[0], tr("command"), tr("flags")),
74+
PersistentPreRun: preRun,
75+
PersistentPostRun: postRun,
7276
}
7377

7478
arduinoCli.SetUsageTemplate(usageTemplate)
@@ -151,6 +155,20 @@ func preRun(cmd *cobra.Command, args []string) {
151155
feedback.SetOut(colorable.NewColorableStdout())
152156
feedback.SetErr(colorable.NewColorableStderr())
153157

158+
updaterMessageChan = make(chan *semver.Version)
159+
go func() {
160+
if cmd.Name() == "version" {
161+
// The version command checks by itself if there's a new available version
162+
updaterMessageChan <- nil
163+
}
164+
// Starts checking for updates
165+
currentVersion, err := semver.Parse(globals.VersionInfo.VersionString)
166+
if err != nil {
167+
updaterMessageChan <- nil
168+
}
169+
updaterMessageChan <- updater.CheckForUpdate(currentVersion)
170+
}()
171+
154172
//
155173
// Prepare logging
156174
//
@@ -236,3 +254,11 @@ func preRun(cmd *cobra.Command, args []string) {
236254
})
237255
}
238256
}
257+
258+
func postRun(cmd *cobra.Command, args []string) {
259+
latestVersion := <-updaterMessageChan
260+
if latestVersion != nil {
261+
// Notify the user a new version is available
262+
updater.NotifyNewVersionIsAvailable(latestVersion.String())
263+
}
264+
}

cli/config/validate.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ var validMap = map[string]reflect.Kind{
3636
"network.proxy": reflect.String,
3737
"network.user_agent_ext": reflect.String,
3838
"output.no_color": reflect.Bool,
39+
"updater.enable_notification": reflect.Bool,
3940
}
4041

4142
func typeOf(key string) (reflect.Kind, error) {

cli/updater/updater.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to license@arduino.cc.
15+
16+
package updater
17+
18+
import (
19+
"os"
20+
"strings"
21+
"time"
22+
23+
"github.com/arduino/arduino-cli/cli/feedback"
24+
"github.com/arduino/arduino-cli/cli/globals"
25+
"github.com/arduino/arduino-cli/configuration"
26+
"github.com/arduino/arduino-cli/httpclient"
27+
"github.com/arduino/arduino-cli/i18n"
28+
"github.com/arduino/arduino-cli/inventory"
29+
"github.com/fatih/color"
30+
semver "go.bug.st/relaxed-semver"
31+
)
32+
33+
var tr = i18n.Tr
34+
35+
// CheckForUpdate returns the latest available version if greater than
36+
// the one running and it makes sense to check for an update, nil in all other cases
37+
func CheckForUpdate(currentVersion *semver.Version) *semver.Version {
38+
if !shouldCheckForUpdate(currentVersion) {
39+
return nil
40+
}
41+
42+
return ForceCheckForUpdate(currentVersion)
43+
}
44+
45+
// ForceCheckForUpdate always returns the latest available version if greater than
46+
// the one running, nil in all other cases
47+
func ForceCheckForUpdate(currentVersion *semver.Version) *semver.Version {
48+
defer func() {
49+
// Always save the last time we checked for updates at the end
50+
inventory.Store.Set("updater.last_check_time", time.Now())
51+
inventory.WriteStore()
52+
}()
53+
54+
latestVersion, err := semver.Parse(getLatestRelease())
55+
if err != nil {
56+
return nil
57+
}
58+
59+
if currentVersion.GreaterThanOrEqual(latestVersion) {
60+
// Current version is already good enough
61+
return nil
62+
}
63+
64+
return latestVersion
65+
}
66+
67+
// NotifyNewVersionIsAvailable prints information about the new latestVersion
68+
func NotifyNewVersionIsAvailable(latestVersion string) {
69+
feedback.Errorf("\n\n%s %s → %s\n%s",
70+
color.YellowString(tr("A new release of Arduino CLI is available:")),
71+
color.CyanString(globals.VersionInfo.VersionString),
72+
color.CyanString(latestVersion),
73+
color.YellowString("https://arduino.github.io/arduino-cli/latest/installation/#latest-packages"))
74+
}
75+
76+
// shouldCheckForUpdate return true if it actually makes sense to check for new updates,
77+
// false in all other cases.
78+
func shouldCheckForUpdate(currentVersion *semver.Version) bool {
79+
if strings.Contains(currentVersion.String(), "git-snapshot") || strings.Contains(currentVersion.String(), "nightly") {
80+
// This is a dev build, no need to check for updates
81+
return false
82+
}
83+
84+
if !configuration.Settings.GetBool("updater.enable_notification") {
85+
// Don't check if the user disabled the notification
86+
return false
87+
}
88+
89+
if inventory.Store.IsSet("updater.last_check_time") && time.Since(inventory.Store.GetTime("updater.last_check_time")).Hours() < 24 {
90+
// Checked less than 24 hours ago, let's wait
91+
return false
92+
}
93+
94+
// Don't check when running on CI or on non interactive consoles
95+
return !isCI() && configuration.IsInteractive && configuration.HasConsole
96+
}
97+
98+
// based on https://github.com/watson/ci-info/blob/HEAD/index.js
99+
func isCI() bool {
100+
return os.Getenv("CI") != "" || // GitHub Actions, Travis CI, CircleCI, Cirrus CI, GitLab CI, AppVeyor, CodeShip, dsari
101+
os.Getenv("BUILD_NUMBER") != "" || // Jenkins, TeamCity
102+
os.Getenv("RUN_ID") != "" // TaskCluster, dsari
103+
}
104+
105+
// getLatestRelease queries the official Arduino download server for the latest release,
106+
// if there are no errors or issues a version string is returned, in all other case an empty string.
107+
func getLatestRelease() string {
108+
client, err := httpclient.New()
109+
if err != nil {
110+
return ""
111+
}
112+
113+
// We just use this URL to check if there's a new release available and
114+
// never show it to the user, so it's fine to use the Linux one for all OSs.
115+
URL := "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Linux_64bit.tar.gz"
116+
res, err := client.Head(URL)
117+
if err != nil {
118+
// Yes, we ignore it
119+
return ""
120+
}
121+
122+
// Get redirected URL
123+
location := res.Request.URL.String()
124+
125+
// The location header points to the the latest release of the CLI, it's supposed to be formatted like this:
126+
// https://downloads.arduino.cc/arduino-cli/arduino-cli_0.18.3_Linux_64bit.tar.gz
127+
// so we split it to get the version, if there are not enough splits something must have gone wrong.
128+
split := strings.Split(location, "_")
129+
if len(split) < 2 {
130+
return ""
131+
}
132+
133+
return split[1]
134+
}

cli/version/version.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,15 @@ package version
1717

1818
import (
1919
"os"
20+
"strings"
2021

22+
"github.com/arduino/arduino-cli/cli/errorcodes"
2123
"github.com/arduino/arduino-cli/cli/feedback"
2224
"github.com/arduino/arduino-cli/cli/globals"
25+
"github.com/arduino/arduino-cli/cli/updater"
2326
"github.com/arduino/arduino-cli/i18n"
2427
"github.com/spf13/cobra"
28+
semver "go.bug.st/relaxed-semver"
2529
)
2630

2731
var tr = i18n.Tr
@@ -39,5 +43,29 @@ func NewCommand() *cobra.Command {
3943
}
4044

4145
func run(cmd *cobra.Command, args []string) {
42-
feedback.Print(globals.VersionInfo)
46+
if strings.Contains(globals.VersionInfo.VersionString, "git-snapshot") || strings.Contains(globals.VersionInfo.VersionString, "nightly") {
47+
// We're using a development version, no need to check if there's a
48+
// new release available
49+
feedback.Print(globals.VersionInfo)
50+
return
51+
}
52+
53+
currentVersion, err := semver.Parse(globals.VersionInfo.VersionString)
54+
if err != nil {
55+
feedback.Errorf("Error parsing current version: %s", err)
56+
os.Exit(errorcodes.ErrGeneric)
57+
}
58+
latestVersion := updater.ForceCheckForUpdate(currentVersion)
59+
60+
versionInfo := globals.VersionInfo
61+
if feedback.GetFormat() == feedback.JSON && latestVersion != nil {
62+
// Set this only we managed to get the latest version
63+
versionInfo.LatestVersion = latestVersion.String()
64+
}
65+
66+
feedback.Print(versionInfo)
67+
68+
if feedback.GetFormat() == feedback.Text && latestVersion != nil {
69+
updater.NotifyNewVersionIsAvailable(latestVersion.String())
70+
}
4371
}

configuration/defaults.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ func SetDefaults(settings *viper.Viper) {
5252
// output settings
5353
settings.SetDefault("output.no_color", false)
5454

55+
// updater settings
56+
settings.SetDefault("updater.enable_notification", true)
57+
5558
// Bind env vars
5659
settings.SetEnvPrefix("ARDUINO")
5760
settings.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))

docs/configuration.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
- `sketch` - configuration options relating to [Arduino sketches][sketch specification].
2525
- `always_export_binaries` - set to `true` to make [`arduino-cli compile`][arduino-cli compile] always save binaries
2626
to the sketch folder. This is the equivalent of using the [`--export-binaries`][arduino-cli compile options] flag.
27+
- `updater` - configuration options related to Arduino CLI updates
28+
- `enable_notification` - set to `false` to disable notifications of new Arduino CLI releases, defaults to `true`
2729

2830
## Configuration methods
2931

docs/installation.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ as a parameter like this:
4040
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh -s 0.9.0
4141
```
4242

43+
Arduino CLI checks for new releases every 24 hours. If you don't like this behaviour you can disable it by setting the
44+
[`updater.enable_notification` config](configuration.md#configuration-keys) or the
45+
[env var `ARDUINO_UPDATER_ENABLE_NOTIFICATION`](configuration.md#environment-variables) to `false`.
46+
4347
### Download
4448

4549
Pre-built binaries for all the supported platforms are available for download from the links below.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy