X Tutup
Skip to content

Commit 67f0cf3

Browse files
committed
Improvements to update notifier
- Only report an update available if the version number of the release is greater than the current version - Removes `command` dependency from `update` package; instead, pass current version as an argument - Remove `brew upgrade` instructions since we can't be certain that gh was installed via Homebrew in the first place. - Does not check for updates unless stderr is a tty - Preserve stderr color output even if stdout is not a tty - Fixes stderr color output on Windows
1 parent fb7ea2c commit 67f0cf3

File tree

6 files changed

+119
-59
lines changed

6 files changed

+119
-59
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.13
44

55
require (
66
github.com/AlecAivazis/survey/v2 v2.0.4
7+
github.com/hashicorp/go-version v1.2.0
78
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
89
github.com/mattn/go-colorable v0.1.2
910
github.com/mattn/go-isatty v0.0.9

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
1313
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1414
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1515
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
16+
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
17+
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
1618
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
1719
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
1820
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=

main.go

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,20 @@ import (
77

88
"github.com/github/gh-cli/command"
99
"github.com/github/gh-cli/update"
10+
"github.com/github/gh-cli/utils"
11+
"github.com/mattn/go-isatty"
12+
"github.com/mgutz/ansi"
1013
)
1114

1215
var updaterEnabled = "no"
1316

1417
func main() {
15-
updateMessageChan := make(chan *string)
16-
go updateInBackground(updateMessageChan)
18+
currentVersion := command.Version
19+
updateMessageChan := make(chan *update.ReleaseInfo)
20+
go func() {
21+
rel, _ := checkForUpdate(currentVersion)
22+
updateMessageChan <- rel
23+
}()
1724

1825
if cmd, err := command.RootCmd.ExecuteC(); err != nil {
1926
fmt.Fprintln(os.Stderr, err)
@@ -24,23 +31,29 @@ func main() {
2431
os.Exit(1)
2532
}
2633

27-
updateMessage := <-updateMessageChan
28-
if updateMessage != nil {
29-
fmt.Fprintf(os.Stderr, *updateMessage)
34+
newRelease := <-updateMessageChan
35+
if newRelease != nil {
36+
msg := fmt.Sprintf(`A new release of gh is available: %s → %s
37+
Release notes: %s`, currentVersion, newRelease.Version, newRelease.URL)
38+
stderr := utils.NewColorable(os.Stderr)
39+
fmt.Fprintf(stderr, "\n\n%s\n\n", ansi.Color(msg, "cyan"))
3040
}
3141
}
3242

33-
func updateInBackground(updateMessageChan chan *string) {
34-
if updaterEnabled != "yes" {
35-
updateMessageChan <- nil
36-
return
43+
func shouldCheckForUpdate() bool {
44+
errFd := os.Stderr.Fd()
45+
return updaterEnabled == "yes" && (isatty.IsTerminal(errFd) || isatty.IsCygwinTerminal(errFd))
46+
}
47+
48+
func checkForUpdate(currentVersion string) (*update.ReleaseInfo, error) {
49+
if !shouldCheckForUpdate() {
50+
return nil, nil
3751
}
3852

3953
client, err := command.BasicClient()
4054
if err != nil {
41-
updateMessageChan <- nil
42-
return
55+
return nil, err
4356
}
4457

45-
updateMessageChan <- update.UpdateMessage(client)
58+
return update.CheckForUpdate(client, "github/homebrew-gh", currentVersion)
4659
}

test/fixtures/latestRelease.json

Lines changed: 0 additions & 4 deletions
This file was deleted.

update/update.go

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,35 @@ package update
22

33
import (
44
"fmt"
5-
"os"
65

76
"github.com/github/gh-cli/api"
8-
"github.com/github/gh-cli/command"
9-
"github.com/github/gh-cli/utils"
7+
"github.com/hashicorp/go-version"
108
)
119

12-
const nwo = "github/homebrew-gh"
13-
14-
type releaseInfo struct {
10+
// ReleaseInfo stores information about a release
11+
type ReleaseInfo struct {
1512
Version string `json:"tag_name"`
1613
URL string `json:"html_url"`
1714
}
1815

19-
func UpdateMessage(client *api.Client) *string {
20-
latestRelease, err := getLatestRelease(client)
16+
// CheckForUpdate checks whether this software has had a newer relase on GitHub
17+
func CheckForUpdate(client *api.Client, repo, currentVersion string) (*ReleaseInfo, error) {
18+
latestRelease := ReleaseInfo{}
19+
err := client.REST("GET", fmt.Sprintf("repos/%s/releases/latest", repo), nil, &latestRelease)
2120
if err != nil {
22-
fmt.Fprintf(os.Stderr, "%+v", err)
23-
return nil
21+
return nil, err
2422
}
2523

26-
updateAvailable := latestRelease.Version != command.Version
27-
if updateAvailable {
28-
alertMsg := fmt.Sprintf(utils.Cyan(`
29-
A new version of gh is available! %s → %s
30-
Changelog: %s
31-
Run 'brew upgrade gh' to upgrade to the latest version!`)+"\n\n", command.Version, latestRelease.Version, latestRelease.URL)
32-
return &alertMsg
24+
if versionGreaterThan(latestRelease.Version, currentVersion) {
25+
return &latestRelease, nil
3326
}
3427

35-
return nil
28+
return nil, nil
3629
}
3730

38-
func getLatestRelease(client *api.Client) (*releaseInfo, error) {
39-
path := fmt.Sprintf("repos/%s/releases/latest", nwo)
40-
var r releaseInfo
41-
err := client.REST("GET", path, nil, &r)
42-
return &r, err
31+
func versionGreaterThan(v, w string) bool {
32+
vv, ve := version.NewVersion(v)
33+
vw, we := version.NewVersion(w)
34+
35+
return ve == nil && we == nil && vv.GreaterThan(vw)
4336
}

update/update_test.go

Lines changed: 75 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,89 @@
11
package update
22

33
import (
4-
"os"
5-
"strings"
4+
"bytes"
5+
"fmt"
66
"testing"
77

88
"github.com/github/gh-cli/api"
9-
"github.com/github/gh-cli/command"
109
)
1110

12-
func TestRunWhileCheckingForUpdate(t *testing.T) {
13-
originalVersion := command.Version
14-
command.Version = "v0.0.0"
15-
defer func() {
16-
command.Version = originalVersion
17-
}()
11+
func TestCheckForUpdate(t *testing.T) {
12+
scenarios := []struct {
13+
Name string
14+
CurrentVersion string
15+
LatestVersion string
16+
LatestURL string
17+
ExpectsResult bool
18+
}{
19+
{
20+
Name: "latest is newer",
21+
CurrentVersion: "v0.0.1",
22+
LatestVersion: "v1.0.0",
23+
LatestURL: "https://www.spacejam.com/archive/spacejam/movie/jam.htm",
24+
ExpectsResult: true,
25+
},
26+
{
27+
Name: "current is prerelease",
28+
CurrentVersion: "v1.0.0-pre.1",
29+
LatestVersion: "v1.0.0",
30+
LatestURL: "https://www.spacejam.com/archive/spacejam/movie/jam.htm",
31+
ExpectsResult: true,
32+
},
33+
{
34+
Name: "latest is current",
35+
CurrentVersion: "v1.0.0",
36+
LatestVersion: "v1.0.0",
37+
LatestURL: "https://www.spacejam.com/archive/spacejam/movie/jam.htm",
38+
ExpectsResult: false,
39+
},
40+
{
41+
Name: "latest is older",
42+
CurrentVersion: "v0.10.0-pre.1",
43+
LatestVersion: "v0.9.0",
44+
LatestURL: "https://www.spacejam.com/archive/spacejam/movie/jam.htm",
45+
ExpectsResult: false,
46+
},
47+
}
1848

19-
http := &api.FakeHTTP{}
20-
jsonFile, _ := os.Open("../test/fixtures/latestRelease.json")
21-
defer jsonFile.Close()
22-
http.StubResponse(200, jsonFile)
49+
for _, s := range scenarios {
50+
t.Run(s.Name, func(t *testing.T) {
51+
http := &api.FakeHTTP{}
52+
client := api.NewClient(api.ReplaceTripper(http))
53+
http.StubResponse(200, bytes.NewBufferString(fmt.Sprintf(`{
54+
"tag_name": "%s",
55+
"html_url": "%s"
56+
}`, s.LatestVersion, s.LatestURL)))
2357

24-
client := api.NewClient(api.ReplaceTripper(http))
25-
alertMsg := *UpdateMessage(client)
58+
rel, err := CheckForUpdate(client, "OWNER/REPO", s.CurrentVersion)
59+
if err != nil {
60+
t.Fatal(err)
61+
}
2662

27-
if !strings.Contains(alertMsg, command.Version) {
28-
t.Errorf("expected: \"%v\" to contain \"%v\"", alertMsg, command.Version)
29-
}
63+
if len(http.Requests) != 1 {
64+
t.Fatalf("expected 1 HTTP request, got %d", len(http.Requests))
65+
}
66+
requestPath := http.Requests[0].URL.Path
67+
if requestPath != "/repos/OWNER/REPO/releases/latest" {
68+
t.Errorf("HTTP path: %q", requestPath)
69+
}
70+
71+
if !s.ExpectsResult {
72+
if rel != nil {
73+
t.Fatal("expected no new release")
74+
}
75+
return
76+
}
77+
if rel == nil {
78+
t.Fatal("expected to report new release")
79+
}
3080

31-
if !strings.Contains(alertMsg, "v1.0.0") {
32-
t.Errorf("expected: \"%v\" to contain \"%v\"", alertMsg, "v1.0.0")
81+
if rel.Version != s.LatestVersion {
82+
t.Errorf("Version: %q", rel.Version)
83+
}
84+
if rel.URL != s.LatestURL {
85+
t.Errorf("URL: %q", rel.URL)
86+
}
87+
})
3388
}
3489
}

0 commit comments

Comments
 (0)
X Tutup