Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 2 additions & 21 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"log"
"os"

compareVersion "github.com/hashicorp/go-version"
"github.com/spf13/cobra"
"github.com/spf13/viper"

Expand Down Expand Up @@ -144,24 +143,6 @@ func initConfig() {
// They are loaded on-demand by getAPIClient() based on --instance or default instance
// This allows --instance flag to work correctly

// Check for updates
latestVersionStr, err := version.CheckLatestVersionOfCli(Debug)
if err != nil {
if Debug {
log.Println("Failed to check for updates:", err)
}
}

// Compare versions properly using semantic versioning
if latestVersionStr != "" {
latestVersion, err := compareVersion.NewVersion(latestVersionStr)
if err == nil {
currentVersion, err := compareVersion.NewVersion(version.GetVersion())
if err == nil && latestVersion.GreaterThan(currentVersion) {
if Debug {
log.Printf("New version of Coolify CLI is available: %s\n", latestVersionStr)
}
}
}
}
// Check for updates (errors are handled silently inside the function)
_, _ = version.CheckLatestVersionOfCli(Debug)
}
82 changes: 40 additions & 42 deletions internal/version/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,92 +5,90 @@ import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"sort"
"time"

compareVersion "github.com/hashicorp/go-version"
"github.com/spf13/viper"
)

// Version variables injected by GoReleaser at build time via ldflags
var (
version = "v1.1"
)

// GitHubAPIURL is the URL for fetching CLI version tags (exported for testing)
var GitHubAPIURL = "https://api.github.com/repos/coollabsio/coolify-cli/git/refs/tags"

func GetVersion() string {
return version
}

// CheckInterval for version checking
const CheckInterval = 10 * time.Minute

// Tag represents a git tag for version checking
type Tag struct {
Ref string `json:"ref"`
}

// CheckLatestVersionOfCli checks for CLI updates
func CheckLatestVersionOfCli(debug bool) (string, error) {
lastCheck := viper.GetString("lastupdatechecktime")
if lastCheck != "" {
lastCheckTime, err := time.Parse(time.RFC3339, lastCheck)
if err == nil && lastCheckTime.Add(CheckInterval).After(time.Now()) {
if debug {
log.Println("Skipping update check. Last check was less than 10 minutes ago.")
}
return GetVersion(), nil
}
}

// Update check time
viper.Set("lastupdatechecktime", time.Now().Format(time.RFC3339))
if err := viper.WriteConfig(); err != nil {
log.Printf("Failed to write config: %v\n", err)
}
// CheckLatestVersionOfCli checks for CLI updates on every command.
// Errors are handled silently - the function returns without printing anything
// if the GitHub API call fails.
func CheckLatestVersionOfCli(_ bool) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

url := "https://api.github.com/repos/coollabsio/coolify-cli/git/refs/tags"
ctx := context.Background()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
req, err := http.NewRequestWithContext(ctx, "GET", GitHubAPIURL, nil)
if err != nil {
return "", err
return "", nil // Silent fail
}

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", err
return "", nil // Silent fail
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
if resp.StatusCode != 200 {
return "", nil // Silent fail
}

if resp.StatusCode != 200 {
return "", fmt.Errorf("%d - Failed to fetch data from %s. Error: %s", resp.StatusCode, url, string(body))
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", nil // Silent fail
}

var tags []Tag
if err := json.Unmarshal(body, &tags); err != nil {
return "", err
return "", nil // Silent fail
}

if len(tags) == 0 {
return "", nil // Silent fail
}

versionsRaw := make([]string, 0, len(tags))
for _, tag := range tags {
versionStr := tag.Ref[10:]
versionsRaw = append(versionsRaw, versionStr)
if len(tag.Ref) > 10 {
versionStr := tag.Ref[10:]
versionsRaw = append(versionsRaw, versionStr)
}
}

versions := make([]*compareVersion.Version, len(versionsRaw))
for i, raw := range versionsRaw {
if len(versionsRaw) == 0 {
return "", nil // Silent fail
}

versions := make([]*compareVersion.Version, 0, len(versionsRaw))
for _, raw := range versionsRaw {
v, err := compareVersion.NewVersion(raw)
if err != nil {
return "", err
continue // Skip invalid versions
}
versions[i] = v
versions = append(versions, v)
}

if len(versions) == 0 {
return "", nil // Silent fail
}

sort.Sort(compareVersion.Collection(versions))
Expand All @@ -99,11 +97,11 @@ func CheckLatestVersionOfCli(debug bool) (string, error) {
// Compare versions properly using semantic versioning
currentVersion, err := compareVersion.NewVersion(GetVersion())
if err != nil {
return latestVersion.String(), err
return "", nil // Silent fail
}

if latestVersion.GreaterThan(currentVersion) {
fmt.Printf("There is a new version of Coolify CLI available.\nPlease update with 'coolify update'.\n\n")
fmt.Printf("A new version (%s) is available. Update with: coolify update\n", latestVersion.String())
}
return latestVersion.String(), nil
}
Loading