From 122fade9547da5668911695b7607bf72b07b50e9 Mon Sep 17 00:00:00 2001 From: Veillax <126523099+Veillax@users.noreply.github.com> Date: Mon, 26 Oct 2026 15:59:26 -0600 Subject: [PATCH] selfupdate: add Check to see the newest release without installing Co-Authored-By: Claude Sonnet 5.5 --- selfupdate/selfupdate.go | 57 ++++++++++++++++++++++++++--------- selfupdate/selfupdate_test.go | 17 +++++++++++ 2 files changed, 60 insertions(+), 14 deletions(-) diff --git a/selfupdate/selfupdate.go b/selfupdate/selfupdate.go index 9662dbd..96ee6b3 100644 --- a/selfupdate/selfupdate.go +++ b/selfupdate/selfupdate.go @@ -111,21 +111,9 @@ func CheckOnce(ctx context.Context, cfg Config) (string, error) { if !ok { return "", fmt.Errorf("%q is not a release version", cfg.Current) } - var rel struct { - Tag string `json:"tag_name"` - Prerelease bool `json:"prerelease"` - Draft bool `json:"draft"` - Assets []struct { - Name string `json:"name"` - URL string `json:"browser_download_url"` - } `json:"assets"` - } - body, err := get(ctx, cfg.HTTP, cfg.Server+"/api/v1/repos/"+cfg.Repo+"/releases/latest", 1<<20) + rel, err := latestRelease(ctx, cfg) if err != nil { - return "", fmt.Errorf("checking for a new release: %w", err) - } - if err := json.Unmarshal(body, &rel); err != nil { - return "", fmt.Errorf("reading the latest release: %w", err) + return "", err } if rel.Prerelease || rel.Draft { return "", nil @@ -208,6 +196,47 @@ func CheckOnce(ctx context.Context, cfg Config) (string, error) { return rel.Tag, nil } +type release struct { + Tag string `json:"tag_name"` + Prerelease bool `json:"prerelease"` + Draft bool `json:"draft"` + Assets []struct { + Name string `json:"name"` + URL string `json:"browser_download_url"` + } `json:"assets"` +} + +func latestRelease(ctx context.Context, cfg Config) (release, error) { + var rel release + body, err := get(ctx, cfg.HTTP, cfg.Server+"/api/v1/repos/"+cfg.Repo+"/releases/latest", 1<<20) + if err != nil { + return rel, fmt.Errorf("checking for a new release: %w", err) + } + if err := json.Unmarshal(body, &rel); err != nil { + return rel, fmt.Errorf("reading the latest release: %w", err) + } + return rel, nil +} + +// Check reports the newest full release and whether it is newer than cfg.Current, without +// installing anything. A development build (a Current that isn't vX.Y.Z) is never "newer". +func Check(ctx context.Context, cfg Config) (latest string, newer bool, err error) { + cfg.defaults() + rel, err := latestRelease(ctx, cfg) + if err != nil { + return "", false, err + } + if rel.Prerelease || rel.Draft { + return "", false, nil + } + l, ok := parseVersion(rel.Tag) + if !ok { + return rel.Tag, false, nil + } + cur, ok := parseVersion(cfg.Current) + return rel.Tag, ok && l.newerThan(cur), nil +} + func get(ctx context.Context, c *http.Client, url string, max int64) ([]byte, error) { req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { diff --git a/selfupdate/selfupdate_test.go b/selfupdate/selfupdate_test.go index 4c60730..5733480 100644 --- a/selfupdate/selfupdate_test.go +++ b/selfupdate/selfupdate_test.go @@ -135,3 +135,20 @@ func TestVersionOrdering(t *testing.T) { } } } + +func TestCheckReportsWithoutInstalling(t *testing.T) { + srv := fakeGitea(t, "v0.2.0", false, false) + for _, c := range []struct { + current string + newer bool + }{{"v0.1.0", true}, {"v0.2.0", false}, {"v0.3.0", false}, {"dev", false}, {"v0.1.0-local", false}} { + cfg, exe, restarted := setup(t, srv, c.current) + tag, newer, err := Check(context.Background(), cfg) + if err != nil || tag != "v0.2.0" || newer != c.newer { + t.Errorf("%s: tag=%q newer=%v err=%v", c.current, tag, newer, err) + } + if b, _ := os.ReadFile(exe); string(b) != "OLD-BINARY" || *restarted != "" { + t.Errorf("%s: Check changed things", c.current) + } + } +}