Skip to content

Commit fb798ba

Browse files
committed
Implement upgrade command
1 parent 4c05d39 commit fb798ba

File tree

3 files changed

+136
-95
lines changed

3 files changed

+136
-95
lines changed

cmd/root.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,17 @@ package cmd
22

33
import (
44
"errors"
5+
"fmt"
56
"os"
67
"os/exec"
8+
"path/filepath"
9+
"strconv"
10+
"strings"
11+
"time"
712

813
"github.com/Bananenpro/cli"
14+
"github.com/adrg/xdg"
15+
"github.com/code-game-project/go-utils/external"
916
"github.com/spf13/cobra"
1017
)
1118

@@ -20,12 +27,63 @@ func Execute(version string) {
2027
`)
2128
rootCmd.Version = version
2229
rootCmd.InitDefaultVersionFlag()
30+
31+
if len(os.Args) <= 1 || (os.Args[1] != "upgrade" && os.Args[1] != "uninstall") {
32+
versionCheck(true, false)
33+
}
34+
2335
err := rootCmd.Execute()
2436
if err != nil {
2537
os.Exit(1)
2638
}
2739
}
2840

41+
// versionCheck returns true if a new version is available
42+
func versionCheck(printWarning, ignoreCache bool) bool {
43+
if rootCmd.Version == "dev" {
44+
return false
45+
}
46+
47+
latest, err := getLatestVersion(ignoreCache)
48+
if err != nil {
49+
cli.Error("Failed to fetch latest version number: %s", err)
50+
os.Exit(1)
51+
}
52+
53+
if rootCmd.Version != latest {
54+
if printWarning {
55+
cli.Warn("A new version of codegame-cli is available. Run 'codegame upgrade' to install the latest version.")
56+
}
57+
return true
58+
}
59+
return false
60+
}
61+
62+
func getLatestVersion(ignoreCache bool) (string, error) {
63+
cacheDir := filepath.Join(xdg.CacheHome, "codegame", "cli")
64+
os.MkdirAll(cacheDir, 0o755)
65+
66+
if ignoreCache {
67+
content, err := os.ReadFile(filepath.Join(cacheDir, "latest_version"))
68+
if err == nil {
69+
parts := strings.Split(string(content), "\n")
70+
if len(parts) >= 2 {
71+
cacheTime, err := strconv.Atoi(parts[0])
72+
if err == nil && time.Now().Unix()-int64(cacheTime) <= 60*60*3 {
73+
return parts[1], nil
74+
}
75+
}
76+
}
77+
}
78+
79+
tag, err := external.LatestGithubTag("code-game-project", "codegame-cli")
80+
if err != nil {
81+
return "", err
82+
}
83+
os.WriteFile(filepath.Join(cacheDir, "latest_version"), []byte(fmt.Sprintf("%d\n%s", time.Now().Unix(), tag)), 0o644)
84+
return tag, nil
85+
}
86+
2987
func init() {
3088
rootCmd.CompletionOptions.HiddenDefaultCmd = true
3189
}

cmd/upgrade.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"runtime"
8+
9+
"github.com/Bananenpro/cli"
10+
"github.com/spf13/cobra"
11+
)
12+
13+
// upgradeCmd represents the upgrade command
14+
var upgradeCmd = &cobra.Command{
15+
Use: "upgrade",
16+
Short: "Update codegame-cli to the latest version.",
17+
Run: func(cmd *cobra.Command, args []string) {
18+
if rootCmd.Version == "dev" {
19+
cli.Error("Cannot update dev version.")
20+
os.Exit(1)
21+
}
22+
23+
if !versionCheck(false, true) {
24+
cli.Success("codegame-cli is already up-to-date.")
25+
return
26+
}
27+
28+
switch runtime.GOOS {
29+
case "windows":
30+
upgradeWindows()
31+
case "darwin", "linux":
32+
upgradeUnix()
33+
default:
34+
cli.Error("Automatic updates are not supported for your operating system.")
35+
os.Exit(1)
36+
}
37+
},
38+
}
39+
40+
func upgradeWindows() {
41+
fmt.Println("Downloading latest installer...")
42+
cmd := exec.Command("Powershell.exe", "-Command", "iwr -useb https://raw.githubusercontent.com/code-game-project/codegame-cli/main/install.ps1 | iex")
43+
cmd.Stdout = os.Stdout
44+
cmd.Stderr = os.Stderr
45+
err := cmd.Run()
46+
if err != nil {
47+
cli.Error("Failed to update codegame-cli.")
48+
os.Exit(1)
49+
}
50+
}
51+
52+
func upgradeUnix() {
53+
fmt.Println("Downloading latest installer...")
54+
55+
var installCmd string
56+
if _, err := exec.LookPath("wget"); err == nil {
57+
installCmd = "wget -q --show-progress https://raw.githubusercontent.com/code-game-project/codegame-cli/main/install.sh -O- | bash"
58+
} else if _, err := exec.LookPath("curl"); err == nil {
59+
installCmd = "curl -L https://raw.githubusercontent.com/code-game-project/codegame-cli/main/install.sh | bash"
60+
} else {
61+
cli.Error("Please install either wget or curl.")
62+
os.Exit(1)
63+
}
64+
65+
cmd := exec.Command("bash", "-c", installCmd)
66+
cmd.Stdout = os.Stdout
67+
cmd.Stderr = os.Stderr
68+
err := cmd.Run()
69+
if err != nil {
70+
cli.Error("Failed to update codegame-cli.")
71+
os.Exit(1)
72+
}
73+
}
74+
75+
func init() {
76+
rootCmd.AddCommand(upgradeCmd)
77+
}

main.go

Lines changed: 1 addition & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,11 @@
11
package main
22

33
import (
4-
"fmt"
5-
"os"
6-
"os/exec"
7-
"path/filepath"
8-
"runtime"
9-
"strconv"
10-
"strings"
11-
"time"
12-
13-
"github.com/Bananenpro/cli"
14-
"github.com/adrg/xdg"
15-
"github.com/code-game-project/go-utils/external"
16-
"github.com/code-game-project/go-utils/semver"
17-
18-
cgExec "github.com/code-game-project/go-utils/exec"
19-
204
"github.com/code-game-project/codegame-cli/cmd"
215
)
226

23-
const version = "dev"
7+
const version = "v0.8.0"
248

259
func main() {
26-
checkVersion()
2710
cmd.Execute(version)
2811
}
29-
30-
// checkVersion prints a warning, if there is a newer version of codegame-cli available.
31-
// On macOS and linux the user is offered to update automatically.
32-
func checkVersion() {
33-
if version == "dev" {
34-
return
35-
}
36-
37-
latest, err := getLatestVersion()
38-
if err != nil {
39-
return
40-
}
41-
42-
currentMajor, currentMinor, currentPatch, err := semver.ParseVersion(version)
43-
if err != nil {
44-
return
45-
}
46-
47-
latestMajor, latestMinor, latestPatch, err := semver.ParseVersion(latest)
48-
if err != nil {
49-
return
50-
}
51-
52-
if latestMajor > currentMajor || (latestMajor == currentMajor && latestMinor > currentMinor) || (latestMajor == currentMajor && latestMinor == currentMinor && latestPatch > currentPatch) {
53-
_, shErr := exec.LookPath("sh")
54-
_, sudoErr := exec.LookPath("sudo")
55-
_, curlErr := exec.LookPath("curl")
56-
_, tarErr := exec.LookPath("tar")
57-
cgBin, codegameErr := os.Stat("/usr/local/bin/codegame")
58-
if codegameErr == nil && !cgBin.IsDir() && shErr == nil && sudoErr == nil && curlErr == nil && tarErr == nil && (runtime.GOOS == "darwin" || runtime.GOOS == "linux") {
59-
update()
60-
} else {
61-
cli.Warn("You are using an old version of codegame-cli (%s).\nUpdate to the latest version (%s): https://github.com/code-game-project/codegame-cli#installation", version, latest)
62-
}
63-
}
64-
}
65-
66-
func update() {
67-
yes, err := cli.YesNo("A new version is available. Do you want to update now?", true)
68-
if err != nil {
69-
os.Exit(0)
70-
}
71-
if !yes {
72-
return
73-
}
74-
75-
_, err = cgExec.Execute(false, "sh", "-c", fmt.Sprintf("curl -L https://github.com/code-game-project/codegame-cli/releases/latest/download/codegame-cli-%s-%s.tar.gz | tar -xz codegame && sudo mv codegame /usr/local/bin", runtime.GOOS, runtime.GOARCH))
76-
if err != nil {
77-
cli.Error("Update failed.")
78-
os.Exit(1)
79-
}
80-
cli.Success("Update successful.")
81-
os.Exit(0)
82-
}
83-
84-
func getLatestVersion() (string, error) {
85-
cacheDir := filepath.Join(xdg.CacheHome, "codegame", "cli")
86-
os.MkdirAll(cacheDir, 0o755)
87-
88-
content, err := os.ReadFile(filepath.Join(cacheDir, "latest_version"))
89-
if err == nil {
90-
parts := strings.Split(string(content), "\n")
91-
if len(parts) >= 2 {
92-
cacheTime, err := strconv.Atoi(parts[0])
93-
if err == nil && time.Now().Unix()-int64(cacheTime) <= 60*60*3 {
94-
return parts[1], nil
95-
}
96-
}
97-
}
98-
99-
tag, err := external.LatestGithubTag("code-game-project", "codegame-cli")
100-
if err != nil {
101-
return "", err
102-
}
103-
os.WriteFile(filepath.Join(cacheDir, "latest_version"), []byte(fmt.Sprintf("%d\n%s", time.Now().Unix(), version)), 0o644)
104-
return tag, nil
105-
}

0 commit comments

Comments
 (0)