Skip to content
Merged
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
110 changes: 73 additions & 37 deletions packages/util/check-for-update.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os/exec"
"runtime"
"strings"
"time"

"github.com/fatih/color"
"github.com/rs/zerolog/log"
Expand All @@ -24,36 +25,42 @@ func CheckForUpdateWithWriter(w io.Writer) {
if checkEnv := os.Getenv("INFISICAL_DISABLE_UPDATE_CHECK"); checkEnv != "" {
return
}
latestVersion, _, err := getLatestTag("Infisical", "cli")
latestVersion, _, isUrgent, err := getLatestTag("Infisical", "cli")
if err != nil {
log.Debug().Err(err)
// do nothing and continue
return
}

// daysSinceRelease, _ := daysSinceDate(publishedDate)
if latestVersion == CLI_VERSION {
return
}

if latestVersion != CLI_VERSION {
yellow := color.New(color.FgYellow).SprintFunc()
blue := color.New(color.FgCyan).SprintFunc()
black := color.New(color.FgBlack).SprintFunc()
// Only prompt if the user's current version is at least 48 hours old, unless urgent.
// This avoids nagging users who recently updated.
currentVersionPublishedAt, err := getReleasePublishedAt("Infisical", "cli", CLI_VERSION)
if err == nil && !isUrgent && time.Since(currentVersionPublishedAt).Hours() < 48 {
return
}

msg := fmt.Sprintf("%s %s %s %s",
yellow("A new release of infisical is available:"),
blue(CLI_VERSION),
black("->"),
blue(latestVersion),
)
yellow := color.New(color.FgYellow).SprintFunc()
blue := color.New(color.FgCyan).SprintFunc()
black := color.New(color.FgBlack).SprintFunc()

fmt.Fprintln(w, msg)
msg := fmt.Sprintf("%s %s %s %s",
yellow("A new release of infisical is available:"),
blue(CLI_VERSION),
black("->"),
blue(latestVersion),
)

updateInstructions := GetUpdateInstructions()
fmt.Fprintln(w, msg)

if updateInstructions != "" {
msg = fmt.Sprintf("\n%s\n", GetUpdateInstructions())
fmt.Fprintln(w, msg)
}
updateInstructions := GetUpdateInstructions()

if updateInstructions != "" {
msg = fmt.Sprintf("\n%s\n", GetUpdateInstructions())
fmt.Fprintln(w, msg)
}
}

Expand All @@ -80,38 +87,80 @@ func DisplayAptInstallationChangeBannerWithWriter(isSilent bool, w io.Writer) {
}
}

func getLatestTag(repoOwner string, repoName string) (string, string, error) {
func getLatestTag(repoOwner string, repoName string) (string, time.Time, bool, error) {
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", repoOwner, repoName)
resp, err := http.Get(url)
if err != nil {
return "", "", err
return "", time.Time{}, false, err
}
if resp.StatusCode != 200 {
return "", "", errors.New(fmt.Sprintf("gitHub API returned status code %d", resp.StatusCode))
return "", time.Time{}, false, errors.New(fmt.Sprintf("gitHub API returned status code %d", resp.StatusCode))
}

defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
return "", "", err
return "", time.Time{}, false, err
}

var releaseDetails struct {
TagName string `json:"tag_name"`
PublishedAt string `json:"published_at"`
Body string `json:"body"`
}

if err := json.Unmarshal(body, &releaseDetails); err != nil {
return "", "", fmt.Errorf("failed to unmarshal github response: %w", err)
return "", time.Time{}, false, fmt.Errorf("failed to unmarshal github response: %w", err)
}

publishedAt, err := time.Parse(time.RFC3339, releaseDetails.PublishedAt)
if err != nil {
return "", time.Time{}, false, fmt.Errorf("failed to parse release time: %w", err)
}

isUrgent := strings.Contains(releaseDetails.Body, "#urgent")

tag_prefix := "v"

// Extract the version from the first valid tag
version := strings.TrimPrefix(releaseDetails.TagName, tag_prefix)

return version, releaseDetails.PublishedAt, nil
return version, publishedAt, isUrgent, nil
}

func getReleasePublishedAt(repoOwner string, repoName string, version string) (time.Time, error) {
tag := "v" + version
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/tags/%s", repoOwner, repoName, tag)
resp, err := http.Get(url)
if err != nil {
return time.Time{}, err
}
if resp.StatusCode != 200 {
return time.Time{}, errors.New(fmt.Sprintf("gitHub API returned status code %d", resp.StatusCode))
}

defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
return time.Time{}, err
}

var releaseDetails struct {
PublishedAt string `json:"published_at"`
}

if err := json.Unmarshal(body, &releaseDetails); err != nil {
return time.Time{}, fmt.Errorf("failed to unmarshal github response: %w", err)
}

publishedAt, err := time.Parse(time.RFC3339, releaseDetails.PublishedAt)
if err != nil {
return time.Time{}, fmt.Errorf("failed to parse release time: %w", err)
}

return publishedAt, nil
}

func GetUpdateInstructions() string {
Expand Down Expand Up @@ -176,16 +225,3 @@ func IsRunningInDocker() bool {

return strings.Contains(string(cgroup), "docker")
}

// func daysSinceDate(dateString string) (int, error) {
// layout := "2006-01-02T15:04:05Z"
// parsedDate, err := time.Parse(layout, dateString)
// if err != nil {
// return 0, err
// }

// currentTime := time.Now()
// difference := currentTime.Sub(parsedDate)
// days := int(difference.Hours() / 24)
// return days, nil
// }
Loading