diff --git a/internal/updates/updates.go b/internal/updates/updates.go index feeedfbfd..f42ffa29c 100644 --- a/internal/updates/updates.go +++ b/internal/updates/updates.go @@ -297,6 +297,21 @@ func install(artifactArch, downloadURL, installLocation string, timeout int) err return nil } +// isStableRelease returns true if the release is a full semver release (not a +// draft, not marked as pre-release, and has no pre-release component in its tag). +func isStableRelease(release *github.RepositoryRelease) bool { + if release.GetDraft() || release.GetPrerelease() { + return false + } + + v, err := version.NewVersion(release.GetTagName()) + if err != nil { + return false + } + + return v.Prerelease() == "" +} + func getLatestRelease(ctx context.Context, artifactArch string, timeout time.Duration) (*github.RepositoryRelease, *github.ReleaseAsset, error) { client := github.NewClient(&http.Client{ Timeout: timeout, @@ -328,6 +343,10 @@ func getLatestRelease(ctx context.Context, artifactArch string, timeout time.Dur } for _, release := range releases { + if !isStableRelease(release) { + continue + } + for _, asset := range release.Assets { if strings.Contains(strings.ToLower(asset.GetName()), strings.ToLower(artifactArch)) { _ = releaseCache.Store(&ReleaseCache{ diff --git a/internal/updates/updates_test.go b/internal/updates/updates_test.go new file mode 100644 index 000000000..45d42a2a4 --- /dev/null +++ b/internal/updates/updates_test.go @@ -0,0 +1,91 @@ +package updates + +import ( + "testing" + + "github.com/google/go-github/v63/github" + "github.com/stretchr/testify/assert" +) + +func TestIsStableRelease(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + release *github.RepositoryRelease + wantStable bool + }{ + { + name: "stable release", + release: &github.RepositoryRelease{ + TagName: github.String("v1.2.3"), + Draft: github.Bool(false), + Prerelease: github.Bool(false), + }, + wantStable: true, + }, + { + name: "pre-release tag with dash suffix", + release: &github.RepositoryRelease{ + TagName: github.String("v1.2.3-pre.0"), + Draft: github.Bool(false), + Prerelease: github.Bool(false), + }, + wantStable: false, + }, + { + name: "pre-release tag with alpha suffix", + release: &github.RepositoryRelease{ + TagName: github.String("v1.2.3-alpha.1"), + Draft: github.Bool(false), + Prerelease: github.Bool(false), + }, + wantStable: false, + }, + { + name: "pre-release tag with rc suffix", + release: &github.RepositoryRelease{ + TagName: github.String("v2.0.0-rc.1"), + Draft: github.Bool(false), + Prerelease: github.Bool(false), + }, + wantStable: false, + }, + { + name: "github marked as pre-release", + release: &github.RepositoryRelease{ + TagName: github.String("v1.2.3"), + Draft: github.Bool(false), + Prerelease: github.Bool(true), + }, + wantStable: false, + }, + { + name: "draft release", + release: &github.RepositoryRelease{ + TagName: github.String("v1.2.3"), + Draft: github.Bool(true), + Prerelease: github.Bool(false), + }, + wantStable: false, + }, + { + name: "invalid tag", + release: &github.RepositoryRelease{ + TagName: github.String("not-a-version"), + Draft: github.Bool(false), + Prerelease: github.Bool(false), + }, + wantStable: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + got := isStableRelease(tt.release) + assert.Equal(t, tt.wantStable, got) + }) + } +}