-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
883 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"runtime" | ||
"time" | ||
|
||
"github.com/blang/semver" | ||
) | ||
|
||
const locallyBuilt = "(Locally Built)" | ||
const autoUpdateFile = "TGFAutoUpdate" | ||
|
||
//go:generate moq -out runner_updater_moq_test.go . RunnerUpdater | ||
|
||
// RunnerUpdater allows flexibility for testing | ||
type RunnerUpdater interface { | ||
LogDebug(format string, args ...interface{}) | ||
GetUpdateVersion() (string, error) | ||
GetLastRefresh(file string) time.Duration | ||
SetLastRefresh(file string) | ||
ShouldUpdate() bool | ||
DoUpdate(url string) (err error) | ||
Run() int | ||
Restart() int | ||
} | ||
|
||
// RunWithUpdateCheck checks if an update is due, checks if current version is outdated and performs update if needed | ||
func RunWithUpdateCheck(c RunnerUpdater) int { | ||
if !c.ShouldUpdate() { | ||
return c.Run() | ||
} | ||
|
||
c.LogDebug("Comparing local and latest versions...") | ||
c.SetLastRefresh(autoUpdateFile) | ||
updateVersion, err := c.GetUpdateVersion() | ||
if err != nil { | ||
printError("Error fetching update version: %v", err) | ||
return c.Run() | ||
} | ||
latestVersion, err := semver.Make(updateVersion) | ||
if err != nil { | ||
printError(`Semver error on retrieved version "%s" : %v`, updateVersion, err) | ||
return c.Run() | ||
} | ||
|
||
currentVersion, err := semver.Make(version) | ||
if err != nil { | ||
printWarning(`Semver error on current version "%s": %v`, version, err) | ||
return c.Run() | ||
} | ||
|
||
if currentVersion.GTE(latestVersion) { | ||
c.LogDebug("Your current version (%v) is up to date.", currentVersion) | ||
return c.Run() | ||
} | ||
|
||
url := PlatformZipURL(latestVersion.String()) | ||
|
||
executablePath, err := os.Executable() | ||
if err != nil { | ||
printError("Executable path error: %v", err) | ||
} | ||
|
||
printWarning("Updating %s from %s ==> %v", executablePath, version, latestVersion) | ||
if err := c.DoUpdate(url); err != nil { | ||
printError("Failed update for %s: %v", url, err) | ||
return c.Run() | ||
} | ||
|
||
c.LogDebug("TGF updated to %v", latestVersion) | ||
printWarning("TGF is restarting...") | ||
return c.Restart() | ||
} | ||
|
||
// PlatformZipURL compute the uri pointing at the given version of tgf zip | ||
func PlatformZipURL(version string) string { | ||
name := runtime.GOOS | ||
if name == "darwin" { | ||
name = "macOS" | ||
} | ||
return fmt.Sprintf("https://github.com/coveo/tgf/releases/download/v%[1]s/tgf_%[1]s_%[2]s_64-bits.zip", version, name) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func setupUpdaterMock(localVersion string, latestVersion string) *RunnerUpdaterMock { | ||
version = localVersion | ||
mockUpdater := &RunnerUpdaterMock{ | ||
GetUpdateVersionFunc: func() (string, error) { return latestVersion, nil }, // Remote version | ||
LogDebugFunc: func(format string, args ...interface{}) {}, | ||
GetLastRefreshFunc: func(string) time.Duration { return 0 * time.Hour }, // Force update | ||
SetLastRefreshFunc: func(string) {}, | ||
ShouldUpdateFunc: func() bool { return true }, | ||
RunFunc: func() int { return 0 }, | ||
RestartFunc: func() int { return 0 }, | ||
DoUpdateFunc: func(url string) (err error) { return nil }, | ||
} | ||
|
||
return mockUpdater | ||
} | ||
|
||
func (mockUpdater *RunnerUpdaterMock) LogDebugCalledWith(arg string) bool { | ||
for _, call := range mockUpdater.LogDebugCalls() { | ||
if call.Format == arg { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func TestAutoUpdateLower(t *testing.T) { | ||
mockUpdater := setupUpdaterMock("1.20.0", "1.21.0") | ||
RunWithUpdateCheck(mockUpdater) | ||
assert.True(t, mockUpdater.LogDebugCalledWith("TGF updated to %v"), `"TGF updated" never logged`) | ||
assert.Equal(t, len(mockUpdater.RunCalls()), 0, "Auto update bypassed") | ||
assert.NotEqual(t, len(mockUpdater.RestartCalls()), 0, "Application did not restart") | ||
} | ||
|
||
func TestAutoUpdateEqual(t *testing.T) { | ||
mockUpdater := setupUpdaterMock("1.21.0", "1.21.0") | ||
RunWithUpdateCheck(mockUpdater) | ||
assert.True(t, mockUpdater.LogDebugCalledWith("Your current version (%v) is up to date."), `"TGF updated" never logged`) | ||
assert.NotEqual(t, len(mockUpdater.RunCalls()), 0, "Auto update was not bypassed") | ||
assert.Equal(t, len(mockUpdater.RestartCalls()), 0, "Application was restarted") | ||
} | ||
|
||
func TestAutoUpdateHigher(t *testing.T) { | ||
mockUpdater := setupUpdaterMock("1.21.0", "1.20.0") | ||
RunWithUpdateCheck(mockUpdater) | ||
assert.True(t, mockUpdater.LogDebugCalledWith("Your current version (%v) is up to date."), `"TGF updated" never logged`) | ||
assert.NotEqual(t, len(mockUpdater.RunCalls()), 0, "Auto update was not bypassed") | ||
assert.Equal(t, len(mockUpdater.RestartCalls()), 0, "Application was restarted") | ||
} | ||
|
||
func ExampleRunWithUpdateCheck_githubApiError() { | ||
mockUpdater := setupUpdaterMock("1.20.0", "1.21.0") | ||
mockUpdater.GetUpdateVersionFunc = func() (string, error) { return "", fmt.Errorf("API error") } | ||
ErrPrintln = fmt.Println | ||
RunWithUpdateCheck(mockUpdater) | ||
// Output: | ||
// Error fetching update version: API error | ||
} | ||
|
||
func ExampleRunWithUpdateCheck_githubApiBadVersionString() { | ||
mockUpdater := setupUpdaterMock("1.20.0", "not a number") | ||
ErrPrintln = fmt.Println | ||
RunWithUpdateCheck(mockUpdater) | ||
// Output: | ||
// Semver error on retrieved version "not a number" : No Major.Minor.Patch elements found | ||
} | ||
|
||
func ExampleRunWithUpdateCheck_badVersionStringLocal() { | ||
mockUpdater := setupUpdaterMock("not a number", "1.21.0") | ||
ErrPrintln = fmt.Println | ||
RunWithUpdateCheck(mockUpdater) | ||
// Output: | ||
// Semver error on current version "not a number": No Major.Minor.Patch elements found | ||
|
||
} | ||
|
||
func ExampleTGFConfig_ShouldUpdate_forceConfiglocal() { | ||
version = locallyBuilt | ||
cfg := &TGFConfig{ | ||
tgf: &TGFApplication{ | ||
DebugMode: true, | ||
}, | ||
AutoUpdate: true, | ||
} | ||
|
||
ErrPrintf = fmt.Printf | ||
cfg.ShouldUpdate() | ||
// Output: | ||
// Running locally. Bypassing update version check. | ||
} | ||
|
||
func ExampleTGFConfig_ShouldUpdate_forceCliLocal() { | ||
version = locallyBuilt | ||
cfg := &TGFConfig{ | ||
tgf: &TGFApplication{ | ||
AutoUpdateSet: true, | ||
AutoUpdate: true, | ||
DebugMode: true, | ||
}, | ||
} | ||
|
||
ErrPrintf = fmt.Printf | ||
cfg.ShouldUpdate() | ||
// Output: | ||
// Auto update is forced locally. Checking version... | ||
} | ||
|
||
func ExampleTGFConfig_ShouldUpdate_forceOffCli() { | ||
cfg := &TGFConfig{ | ||
tgf: &TGFApplication{ | ||
AutoUpdateSet: true, | ||
AutoUpdate: false, | ||
DebugMode: true, | ||
}, | ||
} | ||
|
||
ErrPrintf = fmt.Printf | ||
cfg.ShouldUpdate() | ||
// Output: | ||
// Auto update is force disabled. Bypassing update version check. | ||
} | ||
|
||
func ExampleTGFConfig_ShouldUpdate_forceConfig() { | ||
version = "1.1.1" | ||
cfg := &TGFConfig{ | ||
tgf: &TGFApplication{ | ||
AutoUpdateSet: false, | ||
DebugMode: true, | ||
}, | ||
AutoUpdate: true, | ||
AutoUpdateDelay: 0 * time.Hour, | ||
} | ||
|
||
ErrPrintf = fmt.Printf | ||
cfg.ShouldUpdate() | ||
// Output: | ||
// An update is due. Checking version... | ||
} | ||
|
||
func ExampleTGFConfig_ShouldUpdate_forceOffConfig() { | ||
cfg := &TGFConfig{ | ||
tgf: &TGFApplication{ | ||
AutoUpdateSet: false, | ||
DebugMode: true, | ||
}, | ||
AutoUpdate: false, | ||
} | ||
|
||
ErrPrintf = fmt.Printf | ||
cfg.ShouldUpdate() | ||
// Output: | ||
// Auto update is disabled in the config. Bypassing update version check. | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.