Skip to content

Commit 83b47aa

Browse files
authored
Merge pull request #321 from twitchdev/update-checker
Added notice for when a new version is available to upgrade to
2 parents 538b48a + 64ed68e commit 83b47aa

File tree

5 files changed

+192
-3
lines changed

5 files changed

+192
-3
lines changed

README.md

+53-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,39 @@ To install via Winget, run:
4444
winget install Twitch.TwitchCLI
4545
```
4646

47-
To update, run:
47+
### Manual Download
48+
49+
To download, go to the [Releases tab of GitHub](https://github.com/twitchdev/twitch-cli/releases). The examples in the documentation assume you have put this into your PATH and renamed to `twitch` (or symlinked as such).
50+
51+
**Note**: If using MacOS and downloading manually, you may need to adjust the permissions of the file to allow for execution.
52+
53+
To do so, please run: `chmod 755 <filename>` where the filename is the name of the downloaded binary.
54+
55+
## Updating
56+
57+
To update the Twitch CLI, run the command relevant to your installation method.
58+
59+
**NOTE:** Once a day the program will make an HTTP call to Github to check if the application is of the latest version. For information on disabling this, see *Disabling release version checks and notices* below.
60+
61+
### Homebrew
62+
63+
To update using Homebrew, run:
64+
65+
```sh
66+
brew upgrade twitchdev/twitch/twitch-cli
67+
```
68+
69+
### Scoop
70+
71+
To update using Scoop, run:
72+
73+
```sh
74+
scoop update twitch-cli
75+
```
76+
77+
### WinGet
78+
79+
To update using WinGet, run:
4880

4981
```sh
5082
winget update Twitch.TwitchCLI
@@ -56,7 +88,26 @@ To download, go to the [Releases tab of GitHub](https://github.com/twitchdev/twi
5688

5789
**Note**: If using MacOS and downloading manually, you may need to adjust the permissions of the file to allow for execution.
5890

59-
To do so, please run: `chmod 755 <filename>` where the filename is the name of the downloaded binary.
91+
To do so, please run: `chmod 755 <filename>` where the filename is the name of the downloaded binary.
92+
93+
## Disabling release version checks and notices
94+
95+
When the Twitch CLI exits successfully, the application will automatically check the Twitch CLI's Github releases at the following URL:
96+
97+
```
98+
https://api.github.com/repos/twitchdev/twitch-cli/releases/latest
99+
```
100+
101+
If the version of the Twitch CLI you are running is older than the latest released version, a notice will be printed to the console.
102+
103+
To prevent this from happening, make one of the following changes:
104+
105+
- Set the environment variable `CI` to `true`
106+
- Set the environment variable `TWITCH_DISABLE_UPDATE_CHECKS` to `true`
107+
- Add `DISABLE_UPDATE_CHECKS=true` to your **.twitch-cli.env** configuration file
108+
- SET `LAST_UPDATE_CHECK` to `3000-01-01` in your **.twitch-cli.env** configuration file, which will prevent it from running until the year 3000
109+
110+
If you're running the Twitch CLI in a CI/CD environment, most environments will have already set the `CI` environment variable to `true`.
60111

61112
## Usage
62113

cmd/root.go

+4
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,14 @@ var rootCmd = &cobra.Command{
2323
// Execute adds all child commands to the root command and sets flags appropriately.
2424
// This is called by main.main(). It only needs to happen once to the rootCmd.
2525
func Execute() {
26+
// Execute normally; Exit without checking for updates if there's an error.
2627
if err := rootCmd.Execute(); err != nil {
2728
fmt.Println(err)
2829
os.Exit(1)
2930
}
31+
32+
// Check Github's Releases API to see if we're running the latest version.
33+
util.CheckForUpdatesAndPrintNotice()
3034
}
3135

3236
func init() {

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ require (
1919
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
2020
github.com/davecgh/go-spew v1.1.1 // indirect
2121
github.com/fsnotify/fsnotify v1.4.9 // indirect
22+
github.com/hashicorp/go-version v1.6.0 // indirect
2223
github.com/hashicorp/hcl v1.0.0 // indirect
2324
github.com/hokaccha/go-prettyjson v0.0.0-20201222001619-a42f9ac2ec8e // indirect
2425
github.com/inconshreveable/mousetrap v1.0.0 // indirect

go.sum

+2-1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX
9898
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
9999
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
100100
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
101+
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
102+
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
101103
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
102104
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
103105
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -149,7 +151,6 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
149151
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
150152
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
151153
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
152-
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
153154
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
154155
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
155156
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=

internal/util/version_checker.go

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package util
4+
5+
import (
6+
"encoding/json"
7+
"fmt"
8+
"io"
9+
"net/http"
10+
"os"
11+
"regexp"
12+
"strings"
13+
"time"
14+
15+
"github.com/fatih/color"
16+
"github.com/spf13/viper"
17+
18+
vrs "github.com/hashicorp/go-version"
19+
)
20+
21+
type updateCheckerReleasesResponse struct {
22+
// Only thing we really care about here is the tag name
23+
TagName string `json:"tag_name"`
24+
}
25+
26+
// Check Github's Releases API to see if we're running the latest version.
27+
// If there's any errors, quietly allow it to fail.
28+
func CheckForUpdatesAndPrintNotice() {
29+
// Don't bother running this if current application is built from source
30+
if strings.EqualFold(GetVersion(), "source") {
31+
return
32+
}
33+
34+
// Don't run if program is running in CI/CD (CI env variable will be set), or if TWITCH_DISABLE_UPDATE_CHECKS is set to true.
35+
if viper.GetBool("disable_update_checks") || strings.EqualFold(os.Getenv("CI"), "true") {
36+
return
37+
}
38+
39+
// Don't run if this already ran successfully today
40+
today, _ := time.Parse(time.DateOnly, time.Now().Format(time.DateOnly))
41+
lastRunDate, _ := time.Parse(time.DateOnly, viper.GetString("last_update_check"))
42+
if !today.After(lastRunDate) {
43+
return
44+
}
45+
46+
runningLatestVersion, latestVersionTag, err := areWeRunningLatestVersion()
47+
if err != nil {
48+
return // Drop errors without notifying
49+
}
50+
51+
if !runningLatestVersion {
52+
// Messages to be displayed
53+
messages := []string{
54+
" A new Twitch CLI release is available: " + latestVersionTag + " ",
55+
"",
56+
" See upgrade instructions at: ",
57+
" https://github.com/twitchdev/twitch-cli/blob/main/README.md#updating ",
58+
"",
59+
" Check out the release notes at: ",
60+
" https://github.com/twitchdev/twitch-cli/releases/latest ",
61+
}
62+
63+
// Find longest message
64+
longestMessageLength := 0
65+
for _, str := range messages {
66+
if len(str) > longestMessageLength {
67+
longestMessageLength = len(str)
68+
}
69+
}
70+
71+
// Print messages
72+
shaded := color.New(color.BgWhite, color.FgBlack).SprintfFunc()
73+
74+
fmt.Println()
75+
for _, str := range messages {
76+
fmt.Printf(" %v\n", shaded(str+strings.Repeat(" ", longestMessageLength-len(str))))
77+
}
78+
fmt.Println()
79+
80+
// Update config so this isn't repeated until tomorrow
81+
viper.Set("last_update_check", GetTimestamp().Format(time.DateOnly))
82+
configPath, err := GetConfigPath()
83+
if err != nil {
84+
return
85+
}
86+
_ = viper.WriteConfigAs(configPath)
87+
}
88+
}
89+
90+
// Makes the call to Github's Releases API to check for the latest release version, and compares it to the current version.
91+
func areWeRunningLatestVersion() (bool, string, error) {
92+
REGEX_TAG_NAME_VERSION := regexp.MustCompile("^(?:v)(.+)") // Removes the v from the start of the tag, if it exists
93+
94+
client := &http.Client{
95+
Timeout: time.Second * 2,
96+
}
97+
98+
req, err := http.NewRequest("GET", "https://api.github.com/repos/twitchdev/twitch-cli/releases/latest", nil)
99+
if err != nil {
100+
return false, "", err
101+
}
102+
req.Header.Set("User-Agent", "twitch-cli/"+GetVersion())
103+
104+
response, err := client.Do(req)
105+
if err != nil {
106+
return false, "", err
107+
}
108+
defer response.Body.Close()
109+
110+
body, err := io.ReadAll(response.Body)
111+
if err != nil {
112+
return false, "", err
113+
}
114+
115+
var obj updateCheckerReleasesResponse
116+
err = json.Unmarshal(body, &obj)
117+
if err != nil {
118+
return false, "", err
119+
}
120+
121+
latestReleaseVersion, err := vrs.NewVersion(REGEX_TAG_NAME_VERSION.FindAllStringSubmatch(obj.TagName, -1)[0][1])
122+
if err != nil {
123+
return false, "", err
124+
}
125+
126+
currentVersion, err := vrs.NewVersion(GetVersion())
127+
if err != nil {
128+
return false, "", err
129+
}
130+
131+
return currentVersion.GreaterThanOrEqual(latestReleaseVersion), obj.TagName, nil
132+
}

0 commit comments

Comments
 (0)