-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for update notifications
Include a copy of the autoupdater package as an internal package, since its API is still in flux and likely to break
- Loading branch information
1 parent
e91c22c
commit 670d51e
Showing
10 changed files
with
497 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package autoupdater | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"log" | ||
"sort" | ||
"sync" | ||
"time" | ||
|
||
"github.com/hashicorp/go-version" | ||
"github.com/pkg/browser" | ||
) | ||
|
||
type Options struct { | ||
// Version overrides the app version returned automatically | ||
Version string | ||
AcceptPreleases bool | ||
// NoSkipRelease disables the "Skip this release" option | ||
NoSkipRelease bool | ||
Source Source | ||
Dialog Dialog | ||
} | ||
|
||
type AutoUpdater struct { | ||
opts *Options | ||
} | ||
|
||
// New returns a new AutoUpdater. See the Options | ||
// type for the available options. | ||
func New(opts *Options) (*AutoUpdater, error) { | ||
if opts == nil || opts.Source == nil { | ||
panic(errors.New("Source cannot be nil")) | ||
} | ||
if opts == nil || opts.Dialog == nil { | ||
panic(errors.New("Dialog cannot be nil")) | ||
} | ||
return &AutoUpdater{ | ||
opts: opts, | ||
}, nil | ||
} | ||
|
||
func (au *AutoUpdater) currentVersion() (string, error) { | ||
return au.opts.Version, nil | ||
} | ||
|
||
func (au *AutoUpdater) CheckForUpdates(ctx context.Context) error { | ||
var allReleases []*Release | ||
var releases []*Release | ||
var next string | ||
var err error | ||
for { | ||
releases, next, err = au.opts.Source.AvailableVersions(ctx, next) | ||
if err != nil { | ||
return err | ||
} | ||
foundNonPrerelease := false | ||
for _, r := range releases { | ||
if !r.IsPrerelease { | ||
foundNonPrerelease = true | ||
} | ||
allReleases = append(allReleases, r) | ||
} | ||
if foundNonPrerelease { | ||
break | ||
} | ||
if next == "" { | ||
break | ||
} | ||
} | ||
if !au.opts.AcceptPreleases { | ||
var nonPrereleases []*Release | ||
for _, r := range allReleases { | ||
if !r.IsPrerelease { | ||
nonPrereleases = append(nonPrereleases, r) | ||
} | ||
} | ||
allReleases = nonPrereleases | ||
} | ||
if len(allReleases) == 0 { | ||
// No updates found | ||
return nil | ||
} | ||
sort.Slice(allReleases, func(i, j int) bool { | ||
ri := allReleases[i] | ||
rj := allReleases[j] | ||
vi := version.Must(version.NewVersion(ri.Version)) | ||
vj := version.Must(version.NewVersion(rj.Version)) | ||
|
||
return vi.GreaterThan(vj) | ||
}) | ||
currentVersion, err := au.currentVersion() | ||
if err != nil { | ||
return err | ||
} | ||
var responses []DialogResponse | ||
if au.opts.NoSkipRelease { | ||
responses = append(responses, DialogResponseCancel) | ||
} else { | ||
responses = append(responses, DialogResponseSkipRelease) | ||
} | ||
responses = append(responses, DialogResponseRemindLater) | ||
if au.canInstallUpdates() { | ||
responses = append(responses, DialogResponseDownloadAndInstall) | ||
} else { | ||
responses = append(responses, DialogResponseDownload) | ||
} | ||
var resp DialogResponse | ||
var wg sync.WaitGroup | ||
wg.Add(1) | ||
opts := &DialogOptions{ | ||
CurrentVersion: currentVersion, | ||
AvailableRelease: allReleases[0], | ||
Responses: responses, | ||
Response: func(r DialogResponse) { | ||
defer wg.Done() | ||
resp = r | ||
}, | ||
} | ||
go au.opts.Dialog.ShowUpdaterDialog(opts) | ||
wg.Wait() | ||
switch resp { | ||
case DialogResponseCancel: | ||
return nil | ||
case DialogResponseSkipRelease: | ||
return au.skipRelease(opts.AvailableRelease) | ||
case DialogResponseRemindLater: | ||
// Nothing to do, we'll remind on next check | ||
return nil | ||
case DialogResponseDownload: | ||
return browser.OpenURL(opts.AvailableRelease.URL) | ||
case DialogResponseDownloadAndInstall: | ||
// TODO | ||
} | ||
return nil | ||
} | ||
|
||
func (au *AutoUpdater) skipRelease(rel *Release) error { | ||
// TODO: Skip | ||
return nil | ||
} | ||
|
||
// ScheduleCheckingForUpdates checks for updates once, then starts | ||
// checking again indefinitely at the given interval. | ||
func (au *AutoUpdater) ScheduleCheckingForUpdates(interval time.Duration) { | ||
doCheck := func() { | ||
ctx := context.Background() | ||
if err := au.CheckForUpdates(ctx); err != nil { | ||
log.Printf("error checking for updates: %v", err) | ||
} | ||
} | ||
go doCheck() | ||
for range time.Tick(interval) { | ||
doCheck() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package autoupdater | ||
|
||
import "fmt" | ||
|
||
type DialogOptions struct { | ||
CurrentVersion string | ||
AvailableRelease *Release | ||
Responses []DialogResponse | ||
Response func(DialogResponse) | ||
} | ||
|
||
func (opts *DialogOptions) AllowsResponse(r DialogResponse) bool { | ||
for _, v := range opts.Responses { | ||
if v == r { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
type DialogResponse int | ||
|
||
const ( | ||
DialogResponseSkipRelease DialogResponse = iota | ||
DialogResponseCancel | ||
DialogResponseRemindLater | ||
DialogResponseDownload | ||
DialogResponseDownloadAndInstall | ||
) | ||
|
||
func (r DialogResponse) String() string { | ||
switch r { | ||
case DialogResponseSkipRelease: | ||
return "Skip this release" | ||
case DialogResponseCancel: | ||
return "Cancel" | ||
case DialogResponseRemindLater: | ||
return "Remind me later" | ||
case DialogResponseDownload: | ||
return "Download" | ||
case DialogResponseDownloadAndInstall: | ||
return "Download and install" | ||
} | ||
return fmt.Sprintf("unknown %T = %d", r, int(r)) | ||
} | ||
|
||
type Dialog interface { | ||
ShowUpdaterDialog(opts *DialogOptions) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// Package autoupdater implements an autoupdater mechanism | ||
// for apps. | ||
package autoupdater |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package autoupdater | ||
|
||
import "errors" | ||
|
||
func (au *AutoUpdater) canInstallUpdates() bool { | ||
// For now no platform supports autoinstall | ||
return false | ||
} | ||
|
||
func (au *AutoUpdater) installUpdate(downloadedPkgPath string) error { | ||
return errors.New("unimplemented") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package autoupdater | ||
|
||
// Asset represents a release asset that can be downloaded | ||
type Asset struct { | ||
Name string | ||
URL string | ||
} | ||
|
||
// Release represents a release of the app | ||
type Release struct { | ||
Version string | ||
IsPrerelease bool | ||
Notes string | ||
NotesHTML string | ||
URL string | ||
Assets []*Asset | ||
} |
Oops, something went wrong.