From 4a7cc5eb594860803f4ab79fe5514138ba31ac62 Mon Sep 17 00:00:00 2001 From: Marius Kimmina <38843153+mariuskimmina@users.noreply.github.com> Date: Fri, 3 Nov 2023 12:25:05 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20cnquery=20status=20show=20latest=20?= =?UTF-8?q?version=20available=20(#2439)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ cnquery status show latest version available --- apps/cnquery/cmd/status.go | 41 +++++++++++++++++++++------------ cnquery.go | 47 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 14 deletions(-) diff --git a/apps/cnquery/cmd/status.go b/apps/cnquery/cmd/status.go index e8ee51d6ae..6d22493547 100644 --- a/apps/cnquery/cmd/status.go +++ b/apps/cnquery/cmd/status.go @@ -149,37 +149,50 @@ type ClientStatus struct { func (s Status) RenderCliStatus() { if s.Client.Platform != nil { agent := s.Client - log.Info().Msg("Platform:\t" + agent.Platform.Name) - log.Info().Msg("Version:\t" + agent.Platform.Version) - log.Info().Msg("Hostname:\t" + agent.Hostname) - log.Info().Msg("IP:\t\t" + agent.IP) + log.Info().Msg("Platform:\t\t" + agent.Platform.Name) + log.Info().Msg("Version:\t\t" + agent.Platform.Version) + log.Info().Msg("Hostname:\t\t" + agent.Hostname) + log.Info().Msg("IP:\t\t\t" + agent.IP) } else { log.Warn().Msg("could not determine client platform information") } - log.Info().Msg("Time:\t\t" + s.Client.Timestamp) - log.Info().Msg("Version:\t" + cnquery.GetVersion() + " (API Version: " + cnquery.APIVersion() + ")") + log.Info().Msg("Time:\t\t\t" + s.Client.Timestamp) + log.Info().Msg("Version:\t\t" + cnquery.GetVersion() + " (API Version: " + cnquery.APIVersion() + ")") + + latestVersion, err := cnquery.GetLatestVersion() + if err != nil { + log.Warn().Err(err).Msg("failed to get latest version") + } + + if latestVersion != "" { + log.Info().Msg("Latest Version:\t" + latestVersion) + + if cnquery.GetVersion() != latestVersion && cnquery.GetVersion() != "unstable" { + log.Warn().Msg("A newer version is available") + } + } log.Info().Msg("API ConnectionConfig:\t" + s.Upstream.API.Endpoint) - log.Info().Msg("API Status:\t" + s.Upstream.API.Status) - log.Info().Msg("API Time:\t" + s.Upstream.API.Timestamp) - log.Info().Msg("API Version:\t" + s.Upstream.API.Version) + log.Info().Msg("API Status:\t\t" + s.Upstream.API.Status) + log.Info().Msg("API Time:\t\t" + s.Upstream.API.Timestamp) + log.Info().Msg("API Version:\t\t" + s.Upstream.API.Version) if s.Upstream.API.Version != cnquery.APIVersion() { log.Warn().Msg("API versions do not match, please update the client") } if len(s.Upstream.Features) > 0 { - log.Info().Msg("Features:\t" + strings.Join(s.Upstream.Features, ",")) + log.Info().Msg("Features:\t\t" + strings.Join(s.Upstream.Features, ",")) } - if s.Client.ParentMrn == "" { - log.Info().Msg("Owner:\t" + s.Client.ParentMrn) + if s.Client.ParentMrn != "" { + log.Info().Msg("Owner:\t\t" + s.Client.ParentMrn) } if s.Client.Registered { - log.Info().Msg("Client:\t" + s.Client.Mrn) - log.Info().Msg("Service Account:\t" + s.Client.ServiceAccount) + log.Info().Msg("Client:\t\t" + s.Client.Mrn) + log.Info().Msg("Service Account:\t\t" + s.Client.ServiceAccount) log.Info().Msg(theme.DefaultTheme.Success("client is registered")) } else { log.Error().Msg("client is not registered") diff --git a/cnquery.go b/cnquery.go index 0453b85c02..194c8b8ed8 100644 --- a/cnquery.go +++ b/cnquery.go @@ -4,7 +4,12 @@ package cnquery import ( + "encoding/json" + "fmt" + "io" + "net/http" "regexp" + "strings" ) // Version is set via ldflags @@ -42,6 +47,48 @@ func GetVersion() string { return Version } +// Release represents a GitHub release +type Release struct { + Name string `json:"name"` +} + +var cnqueryGithubReleaseUrl = "https://api.github.com/repos/mondoohq/cnquery/releases/latest" + +// GetLatestReleaseName fetches the name of the latest release from the specified GitHub repository +func GetLatestReleaseName(url string) (string, error) { + resp, err := http.Get(url) + if err != nil { + return "", fmt.Errorf("error fetching latest release: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("received non-OK response status: %s", resp.Status) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", fmt.Errorf("error reading response body: %v", err) + } + + var release Release + if err := json.Unmarshal(body, &release); err != nil { + return "", fmt.Errorf("error unmarshalling response: %v", err) + } + + return release.Name, nil +} + +// GetLatestVersion returns the latest version available on Github +func GetLatestVersion() (string, error) { + releaseName, err := GetLatestReleaseName(cnqueryGithubReleaseUrl) + if err != nil { + return "", err + } + cleanVersion := strings.TrimPrefix(releaseName, "v") + return cleanVersion, nil +} + var coreSemverRegex = regexp.MustCompile(`^(\d+.\d+.\d+)`) // GetCoreVersion returns the semver core (i.e. major.minor.patch)