Skip to content

Commit

Permalink
Instruct users to install through brew if used
Browse files Browse the repository at this point in the history
Authored-by: Owen Nelson <[email protected]>
  • Loading branch information
tw-owen-nelson committed Feb 19, 2024
1 parent 6b68ae5 commit d3ca4b9
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 23 deletions.
39 changes: 27 additions & 12 deletions cmd/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,24 @@ import (
"fmt"
"io"
"os"
"strings"

"github.com/blang/semver"
"github.com/creativeprojects/go-selfupdate"
log "github.com/sirupsen/logrus"
)

func UpdateMessage(newVersion string) string {
return fmt.Sprintf(
`Talisman version %s is available.
const MessageTemplate = `Talisman version %s is available.
To upgrade, run:
talisman -u
%s
`, newVersion)
`

type UpdateManager struct {
updater *selfupdate.Updater
repository selfupdate.Repository
output io.Writer
}

func NewUpdater() *UpdateManager {
Expand All @@ -34,12 +38,6 @@ func NewUpdater() *UpdateManager {
}
}

type UpdateManager struct {
updater *selfupdate.Updater
repository selfupdate.Repository
output io.Writer
}

func (um *UpdateManager) Check(ctx context.Context, currentVersion string) {
if _, err := semver.ParseTolerant(currentVersion); err != nil {
return
Expand All @@ -49,7 +47,11 @@ func (um *UpdateManager) Check(ctx context.Context, currentVersion string) {
return
}
if release.GreaterThan(currentVersion) {
fmt.Fprint(um.output, UpdateMessage(release.Version()))
executable, err := os.Executable()
if err != nil {
executable = ""
}
fmt.Fprint(um.output, UpdateMessage(executable, release.Version()))
}
}

Expand All @@ -66,3 +68,16 @@ func (um *UpdateManager) Update(ctx context.Context, currentVersion string) int
fmt.Fprintf(um.output, "Talisman updated to %s\n", updated.Version())
return EXIT_SUCCESS
}

func UpdateMessage(path string, newVersion string) string {
upgradeCommand := "talisman -u"
if IsHomebrewInstall(path) {
upgradeCommand = "brew upgrade talisman"
}
return fmt.Sprintf(MessageTemplate, newVersion, upgradeCommand)
}

func IsHomebrewInstall(path string) bool {
link, _ := os.Readlink(path)
return link != "" && strings.Contains(link, "Cellar")
}
90 changes: 79 additions & 11 deletions cmd/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,115 @@ package main
import (
"bytes"
"context"
"os"
"path/filepath"
"testing"

"github.com/creativeprojects/go-selfupdate"
"github.com/stretchr/testify/assert"
)

func TestIfUpdateAvailable(t *testing.T) {
var output bytes.Buffer
um := UpdateManager{updater: selfupdate.DefaultUpdater(), repository: selfupdate.ParseSlug("thoughtworks/talisman"), output: &output}
um := NewUpdater()
output := SpyOnUpdater(um)
oldVersion := "v1.20.0"
currentRelease, _, _ := selfupdate.DetectLatest(context.Background(), selfupdate.ParseSlug("thoughtworks/talisman"))
um.Check(context.Background(), oldVersion)
assert.True(t, currentRelease.GreaterThan(oldVersion), "There is an update available for this old version.")
assert.Equal(t, UpdateMessage(currentRelease.Version()), output.String(), "There is an update available for this old version.")
assert.Equal(t, UpdateMessage("", currentRelease.Version()), output.String(), "There is an update available for this old version.")
}

func TestNoUpdateAvailableForInvalidRepository(t *testing.T) {
var output bytes.Buffer
invalidUm := UpdateManager{updater: selfupdate.DefaultUpdater(), repository: selfupdate.ParseSlug("/bad-repo"), output: &output}
invalidUm := UpdateManager{updater: selfupdate.DefaultUpdater(), repository: selfupdate.ParseSlug("/bad-repo")}
output := SpyOnUpdater(&invalidUm)
invalidUm.Check(context.Background(), "v1.32.0")
assert.True(t, output.String() == "", "We should not suggest updating if there might not be an update. This simulates network errors or GitHub rate limiting.")
}

func TestNoUpdateAvailableIfNoReleaseFound(t *testing.T) {
var output bytes.Buffer
invalidUm := UpdateManager{updater: selfupdate.DefaultUpdater(), repository: selfupdate.ParseSlug("thoughtworks/thoughtworks.github.io"), output: &output}
invalidUm := UpdateManager{updater: selfupdate.DefaultUpdater(), repository: selfupdate.ParseSlug("thoughtworks/thoughtworks.github.io")}
output := SpyOnUpdater(&invalidUm)
invalidUm.Check(context.Background(), "v0.0.0")
assert.True(t, output.String() == "", "There is no update available if there are no releases")
}

func TestNoUpdateAvailableIfOnCurrentVersion(t *testing.T) {
currentRelease, _, _ := selfupdate.DetectLatest(context.Background(), selfupdate.ParseSlug("thoughtworks/talisman"))
var output bytes.Buffer
um := UpdateManager{updater: selfupdate.DefaultUpdater(), repository: selfupdate.ParseSlug("thoughtworks/talisman"), output: &output}
um := NewUpdater()
output := SpyOnUpdater(um)
um.Check(context.Background(), currentRelease.Version())
assert.True(t, output.String() == "", "There is no update available if on the current version")
}

func TestNoUpdateIfUnexpectedCurrentVersion(t *testing.T) {
var output bytes.Buffer
um := UpdateManager{updater: selfupdate.DefaultUpdater(), repository: selfupdate.ParseSlug("thoughtworks/talisman"), output: &output}
um := NewUpdater()
output := SpyOnUpdater(um)
um.Check(context.Background(), "Local dev version")
assert.True(t, output.String() == "", "There is no update available if not on a published version")
}

func SpyOnUpdater(um *UpdateManager) *bytes.Buffer {
var output bytes.Buffer
um.output = &output
return &output
}

func TestInstallThroughTalismanForNativeInstall(t *testing.T) {
nativeInstallation, cleanUp := InstallTalisman()
defer cleanUp()
talismanUpgradeMessage := `Talisman version v1.32.0 is available.
To upgrade, run:
talisman -u
`
assert.Equal(t, talismanUpgradeMessage, UpdateMessage(nativeInstallation, "v1.32.0"), "Should give homebrew command if installed by homebrew")
}

func TestAssumeNativeInstallIfUnableToDetectPath(t *testing.T) {
talismanUpgradeMessage := `Talisman version v1.32.0 is available.
To upgrade, run:
talisman -u
`
assert.Equal(t, talismanUpgradeMessage, UpdateMessage("", "v1.32.0"), "Should give homebrew command if installed by homebrew")
}

func TestDeferToPackageManagerForManagedInstall(t *testing.T) {
brewTalisman, cleanUp := BrewInstallTalisman()
defer cleanUp()

brewUpgradeMessage := `Talisman version v1.32.0 is available.
To upgrade, run:
brew upgrade talisman
`
assert.Equal(t, brewUpgradeMessage, UpdateMessage(brewTalisman, "v1.32.0"), "Should give homebrew command if installed by homebrew")
}

func InstallTalisman() (string, func()) {
usrLocalBin, _ := os.MkdirTemp(os.TempDir(), "talisman-test-bin")
nativeInstallation := filepath.Join(usrLocalBin, "talisman")
os.WriteFile(nativeInstallation, []byte(""), 0755)
return nativeInstallation, func() { os.RemoveAll(usrLocalBin) }
}

func BrewInstallTalisman() (string, func()) {
brewHome, _ := os.MkdirTemp(os.TempDir(), "talisman-test-homebrew")

brewCellar := filepath.Join(brewHome, "Cellar")
_ = os.Mkdir(brewCellar, 0755)

brewInstallation := filepath.Join(brewCellar, "talisman")
os.WriteFile(brewInstallation, []byte(""), 0755)

brewBins := filepath.Join(brewHome, "bin")
_ = os.Mkdir(brewBins, 0755)

brewTalisman := filepath.Join(brewBins, "talisman")
_ = os.Symlink(brewInstallation, brewTalisman)

return brewTalisman, func() { os.RemoveAll(brewHome) }
}

0 comments on commit d3ca4b9

Please sign in to comment.