From 6abeee1b5dc6d596d4737c91347a006ec7a4410e Mon Sep 17 00:00:00 2001 From: Oleg Atamanenko Date: Tue, 4 Apr 2023 21:34:20 -0700 Subject: [PATCH] Add remote version check --- cmd/version.go | 38 ++++++++++++++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 3 ++- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/cmd/version.go b/cmd/version.go index 6fbe9c07e..a97986543 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -1,9 +1,13 @@ package cmd import ( + "context" + "errors" "fmt" "log" + "github.com/google/go-github/v35/github" + "github.com/hashicorp/go-version" "github.com/spf13/afero" "github.com/terraform-linters/tflint/plugin" "github.com/terraform-linters/tflint/tflint" @@ -12,6 +16,8 @@ import ( func (cli *CLI) printVersion(opts Options) int { fmt.Fprintf(cli.outStream, "TFLint version %s\n", tflint.Version) + cli.printLatestReleaseVersion() + workingDirs, err := findWorkingDirs(opts) if err != nil { cli.formatter.Print(tflint.Issues{}, fmt.Errorf("Failed to find workspaces; %w", err), map[string][]byte{}) @@ -81,3 +87,35 @@ func getPluginVersions(opts Options) []string { return versions } + +// Checks GitHub releases and prints new version, if current version is outdated. +// requires GitHub releases to follow semver. +func (cli *CLI) printLatestReleaseVersion() { + latest, err := getLatestVersion() + if err != nil { + cli.formatter.Print(tflint.Issues{}, fmt.Errorf("Failed to check updates; %w", err), map[string][]byte{}) + } + latestVersion, err := version.NewSemver(*latest.Name) + compare := tflint.Version.Compare(latestVersion) + if compare < 0 { + fmt.Fprintf(cli.outStream, "New version available: %s\n", *latest.HTMLURL) + } +} + +func getLatestVersion() (*github.RepositoryRelease, error) { + ghClient := github.NewClient(nil) + releases, _, err := ghClient.Repositories.ListReleases(context.Background(), + "terraform-linters", "tflint", &github.ListOptions{}) + if err != nil { + return nil, err + } + + // GitHub sorts releases results. Select first non-prerelease version and return it. + for i := range releases { + release := releases[i] + if !*release.Prerelease { + return release, nil + } + } + return nil, errors.New("not found") +} diff --git a/go.mod b/go.mod index deae091b0..9a7fd00e5 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-querystring v1.0.0 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect github.com/googleapis/gax-go/v2 v2.7.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect diff --git a/go.sum b/go.sum index 587ed29b1..71a2937c1 100644 --- a/go.sum +++ b/go.sum @@ -304,8 +304,9 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v35 v35.3.0 h1:fU+WBzuukn0VssbayTT+Zo3/ESKX9JYWjbZTLOTEyho= github.com/google/go-github/v35 v35.3.0/go.mod h1:yWB7uCcVWaUbUP74Aq3whuMySRMatyRmq5U9FTNlbio= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=