forked from Silverfish/proton-bridge
[GODT-274] GUI changes for autoupdates
[GODT-275] Add enable/disable auto updates GUI option Refactor Updater module GODT-805 Changed manual update information bar layout GODT-806, GODT-875 Change update dialogs Refactor InformationBar
This commit is contained in:
committed by
James Houlahan
parent
b7b2297635
commit
98ab794f13
@ -30,13 +30,13 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Installer struct{}
|
||||
type InstallerDarwin struct{}
|
||||
|
||||
func NewInstaller(*versioner.Versioner) *Installer {
|
||||
return &Installer{}
|
||||
func NewInstaller(*versioner.Versioner) *InstallerDarwin {
|
||||
return &InstallerDarwin{}
|
||||
}
|
||||
|
||||
func (i *Installer) InstallUpdate(_ *semver.Version, r io.Reader) error {
|
||||
func (i *InstallerDarwin) InstallUpdate(_ *semver.Version, r io.Reader) error {
|
||||
gr, err := gzip.NewReader(r)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@ -26,16 +26,16 @@ import (
|
||||
"github.com/ProtonMail/proton-bridge/internal/versioner"
|
||||
)
|
||||
|
||||
type Installer struct {
|
||||
type InstallerDefault struct {
|
||||
versioner *versioner.Versioner
|
||||
}
|
||||
|
||||
func NewInstaller(versioner *versioner.Versioner) *Installer {
|
||||
return &Installer{
|
||||
func NewInstaller(versioner *versioner.Versioner) *InstallerDefault {
|
||||
return &InstallerDefault{
|
||||
versioner: versioner,
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Installer) InstallUpdate(version *semver.Version, r io.Reader) error {
|
||||
func (i *InstallerDefault) InstallUpdate(version *semver.Version, r io.Reader) error {
|
||||
return i.versioner.InstallNewVersion(version, r)
|
||||
}
|
||||
|
||||
@ -20,7 +20,6 @@ package updater
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||
@ -29,17 +28,19 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type clientProvider interface {
|
||||
var ErrManualUpdateRequired = errors.New("manual update is required")
|
||||
|
||||
type ClientProvider interface {
|
||||
GetAnonymousClient() pmapi.Client
|
||||
}
|
||||
|
||||
type installer interface {
|
||||
type Installer interface {
|
||||
InstallUpdate(*semver.Version, io.Reader) error
|
||||
}
|
||||
|
||||
type Updater struct {
|
||||
cm clientProvider
|
||||
installer installer
|
||||
cm ClientProvider
|
||||
installer Installer
|
||||
kr *crypto.KeyRing
|
||||
|
||||
curVer *semver.Version
|
||||
@ -51,8 +52,8 @@ type Updater struct {
|
||||
}
|
||||
|
||||
func New(
|
||||
cm clientProvider,
|
||||
installer installer,
|
||||
cm ClientProvider,
|
||||
installer Installer,
|
||||
kr *crypto.KeyRing,
|
||||
curVer *semver.Version,
|
||||
updateURLName, platform string,
|
||||
@ -70,56 +71,44 @@ func New(
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Updater) Watch(
|
||||
period time.Duration,
|
||||
handleUpdate func(VersionInfo) error,
|
||||
handleError func(error),
|
||||
) func() {
|
||||
logrus.WithField("period", period).Info("Watching for updates")
|
||||
|
||||
ticker := time.NewTicker(period)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
u.watch(handleUpdate, handleError)
|
||||
<-ticker.C
|
||||
}
|
||||
}()
|
||||
|
||||
return ticker.Stop
|
||||
}
|
||||
|
||||
func (u *Updater) watch(
|
||||
handleUpdate func(VersionInfo) error,
|
||||
handleError func(error),
|
||||
) {
|
||||
func (u *Updater) Check() (VersionInfo, error) {
|
||||
logrus.Info("Checking for updates")
|
||||
|
||||
latest, err := u.fetchVersionInfo()
|
||||
client := u.cm.GetAnonymousClient()
|
||||
defer client.Logout()
|
||||
|
||||
r, err := client.DownloadAndVerify(
|
||||
u.getVersionFileURL(),
|
||||
u.getVersionFileURL()+".sig",
|
||||
u.kr,
|
||||
)
|
||||
if err != nil {
|
||||
handleError(errors.Wrap(err, "failed to fetch version info"))
|
||||
return
|
||||
return VersionInfo{}, err
|
||||
}
|
||||
|
||||
if !latest.Version.GreaterThan(u.curVer) || u.rollout > latest.Rollout {
|
||||
logrus.WithError(err).Debug("No need to update")
|
||||
return
|
||||
var versionMap VersionMap
|
||||
|
||||
if err := json.NewDecoder(r).Decode(&versionMap); err != nil {
|
||||
return VersionInfo{}, err
|
||||
}
|
||||
|
||||
if u.curVer.LessThan(latest.MinAuto) {
|
||||
logrus.Debug("A manual update is required")
|
||||
// NOTE: Need to notify user that they must update manually.
|
||||
return
|
||||
return versionMap[Channel], nil
|
||||
}
|
||||
|
||||
func (u *Updater) IsUpdateApplicable(version VersionInfo) bool {
|
||||
if !version.Version.GreaterThan(u.curVer) {
|
||||
return false
|
||||
}
|
||||
|
||||
logrus.
|
||||
WithField("latest", latest.Version).
|
||||
WithField("current", u.curVer).
|
||||
Info("An update is available")
|
||||
|
||||
if err := handleUpdate(latest); err != nil {
|
||||
handleError(errors.Wrap(err, "failed to handle update"))
|
||||
if u.rollout > version.Rollout {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (u *Updater) CanInstall(version VersionInfo) bool {
|
||||
return !u.curVer.LessThan(version.MinAuto)
|
||||
}
|
||||
|
||||
func (u *Updater) InstallUpdate(update VersionInfo) error {
|
||||
@ -143,25 +132,3 @@ func (u *Updater) InstallUpdate(update VersionInfo) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (u *Updater) fetchVersionInfo() (VersionInfo, error) {
|
||||
client := u.cm.GetAnonymousClient()
|
||||
defer client.Logout()
|
||||
|
||||
r, err := client.DownloadAndVerify(
|
||||
u.getVersionFileURL(),
|
||||
u.getVersionFileURL()+".sig",
|
||||
u.kr,
|
||||
)
|
||||
if err != nil {
|
||||
return VersionInfo{}, err
|
||||
}
|
||||
|
||||
var versionMap VersionMap
|
||||
|
||||
if err := json.NewDecoder(r).Decode(&versionMap); err != nil {
|
||||
return VersionInfo{}, err
|
||||
}
|
||||
|
||||
return versionMap[Channel], nil
|
||||
}
|
||||
|
||||
@ -34,13 +34,13 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestWatch(t *testing.T) {
|
||||
func TestCheck(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
client := mocks.NewMockClient(c)
|
||||
|
||||
updater := newTestUpdater(client, "1.4.0")
|
||||
updater := newTestUpdater(client, "1.1.0")
|
||||
|
||||
versionMap := VersionMap{
|
||||
"live": VersionInfo{
|
||||
@ -59,119 +59,19 @@ func TestWatch(t *testing.T) {
|
||||
|
||||
client.EXPECT().Logout()
|
||||
|
||||
updateCh := make(chan VersionInfo)
|
||||
version, err := updater.Check()
|
||||
|
||||
defer updater.Watch(
|
||||
time.Minute,
|
||||
func(update VersionInfo) error {
|
||||
updateCh <- update
|
||||
return nil
|
||||
},
|
||||
func(err error) {
|
||||
t.Fatal(err)
|
||||
},
|
||||
)()
|
||||
|
||||
assert.Equal(t, semver.MustParse("1.5.0"), (<-updateCh).Version)
|
||||
assert.Equal(t, semver.MustParse("1.5.0"), version.Version)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestWatchIgnoresCurrentVersion(t *testing.T) {
|
||||
func TestCheckBadSignature(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
client := mocks.NewMockClient(c)
|
||||
|
||||
updater := newTestUpdater(client, "1.5.0")
|
||||
|
||||
versionMap := VersionMap{
|
||||
"live": VersionInfo{
|
||||
Version: semver.MustParse("1.5.0"),
|
||||
MinAuto: semver.MustParse("1.4.0"),
|
||||
Package: "https://protonmail.com/download/bridge/update_1.5.0_linux.tgz",
|
||||
Rollout: 1.0,
|
||||
},
|
||||
}
|
||||
|
||||
client.EXPECT().DownloadAndVerify(
|
||||
updater.getVersionFileURL(),
|
||||
updater.getVersionFileURL()+".sig",
|
||||
gomock.Any(),
|
||||
).Return(bytes.NewReader(mustMarshal(t, versionMap)), nil)
|
||||
|
||||
client.EXPECT().Logout()
|
||||
|
||||
updateCh := make(chan VersionInfo)
|
||||
|
||||
defer updater.Watch(
|
||||
time.Minute,
|
||||
func(update VersionInfo) error {
|
||||
updateCh <- update
|
||||
return nil
|
||||
},
|
||||
func(err error) {
|
||||
t.Fatal(err)
|
||||
},
|
||||
)()
|
||||
|
||||
select {
|
||||
case <-updateCh:
|
||||
t.Fatal("We shouldn't update because we are already up to date")
|
||||
case <-time.After(1500 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatchIgnoresVerionsThatRequireManualUpdate(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
client := mocks.NewMockClient(c)
|
||||
|
||||
updater := newTestUpdater(client, "1.4.0")
|
||||
|
||||
versionMap := VersionMap{
|
||||
"live": VersionInfo{
|
||||
Version: semver.MustParse("1.5.0"),
|
||||
MinAuto: semver.MustParse("1.5.0"),
|
||||
Package: "https://protonmail.com/download/bridge/update_1.5.0_linux.tgz",
|
||||
Rollout: 1.0,
|
||||
},
|
||||
}
|
||||
|
||||
client.EXPECT().DownloadAndVerify(
|
||||
updater.getVersionFileURL(),
|
||||
updater.getVersionFileURL()+".sig",
|
||||
gomock.Any(),
|
||||
).Return(bytes.NewReader(mustMarshal(t, versionMap)), nil)
|
||||
|
||||
client.EXPECT().Logout()
|
||||
|
||||
updateCh := make(chan VersionInfo)
|
||||
|
||||
defer updater.Watch(
|
||||
time.Minute,
|
||||
func(update VersionInfo) error {
|
||||
updateCh <- update
|
||||
return nil
|
||||
},
|
||||
func(err error) {
|
||||
t.Fatal(err)
|
||||
},
|
||||
)()
|
||||
|
||||
select {
|
||||
case <-updateCh:
|
||||
t.Fatal("We shouldn't update because this version requires a manual update")
|
||||
case <-time.After(1500 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatchBadSignature(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
client := mocks.NewMockClient(c)
|
||||
|
||||
updater := newTestUpdater(client, "1.4.0")
|
||||
updater := newTestUpdater(client, "1.2.0")
|
||||
|
||||
client.EXPECT().DownloadAndVerify(
|
||||
updater.getVersionFileURL(),
|
||||
@ -181,21 +81,72 @@ func TestWatchBadSignature(t *testing.T) {
|
||||
|
||||
client.EXPECT().Logout()
|
||||
|
||||
updateCh := make(chan VersionInfo)
|
||||
errorsCh := make(chan error)
|
||||
_, err := updater.Check()
|
||||
|
||||
defer updater.Watch(
|
||||
time.Minute,
|
||||
func(update VersionInfo) error {
|
||||
updateCh <- update
|
||||
return nil
|
||||
},
|
||||
func(err error) {
|
||||
errorsCh <- err
|
||||
},
|
||||
)()
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
assert.Error(t, <-errorsCh)
|
||||
func TestIsUpdateApplicable(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
client := mocks.NewMockClient(c)
|
||||
|
||||
updater := newTestUpdater(client, "1.4.0")
|
||||
|
||||
versionOld := VersionInfo{
|
||||
Version: semver.MustParse("1.3.0"),
|
||||
MinAuto: semver.MustParse("1.3.0"),
|
||||
Package: "https://protonmail.com/download/bridge/update_1.3.0_linux.tgz",
|
||||
Rollout: 1.0,
|
||||
}
|
||||
|
||||
assert.Equal(t, false, updater.IsUpdateApplicable(versionOld))
|
||||
|
||||
versionEqual := VersionInfo{
|
||||
Version: semver.MustParse("1.4.0"),
|
||||
MinAuto: semver.MustParse("1.3.0"),
|
||||
Package: "https://protonmail.com/download/bridge/update_1.4.0_linux.tgz",
|
||||
Rollout: 1.0,
|
||||
}
|
||||
|
||||
assert.Equal(t, false, updater.IsUpdateApplicable(versionEqual))
|
||||
|
||||
versionNew := VersionInfo{
|
||||
Version: semver.MustParse("1.5.0"),
|
||||
MinAuto: semver.MustParse("1.3.0"),
|
||||
Package: "https://protonmail.com/download/bridge/update_1.5.0_linux.tgz",
|
||||
Rollout: 1.0,
|
||||
}
|
||||
|
||||
assert.Equal(t, true, updater.IsUpdateApplicable(versionNew))
|
||||
}
|
||||
|
||||
func TestCanInstall(t *testing.T) {
|
||||
c := gomock.NewController(t)
|
||||
defer c.Finish()
|
||||
|
||||
client := mocks.NewMockClient(c)
|
||||
|
||||
updater := newTestUpdater(client, "1.4.0")
|
||||
|
||||
versionManual := VersionInfo{
|
||||
Version: semver.MustParse("1.5.0"),
|
||||
MinAuto: semver.MustParse("1.5.0"),
|
||||
Package: "https://protonmail.com/download/bridge/update_1.5.0_linux.tgz",
|
||||
Rollout: 1.0,
|
||||
}
|
||||
|
||||
assert.Equal(t, false, updater.CanInstall(versionManual))
|
||||
|
||||
versionAuto := VersionInfo{
|
||||
Version: semver.MustParse("1.5.0"),
|
||||
MinAuto: semver.MustParse("1.3.0"),
|
||||
Package: "https://protonmail.com/download/bridge/update_1.5.0_linux.tgz",
|
||||
Rollout: 1.0,
|
||||
}
|
||||
|
||||
assert.Equal(t, true, updater.CanInstall(versionAuto))
|
||||
}
|
||||
|
||||
func TestInstallUpdate(t *testing.T) {
|
||||
@ -221,7 +172,9 @@ func TestInstallUpdate(t *testing.T) {
|
||||
|
||||
client.EXPECT().Logout()
|
||||
|
||||
assert.NoError(t, updater.InstallUpdate(latestVersion))
|
||||
err := updater.InstallUpdate(latestVersion)
|
||||
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestInstallUpdateBadSignature(t *testing.T) {
|
||||
@ -247,7 +200,9 @@ func TestInstallUpdateBadSignature(t *testing.T) {
|
||||
|
||||
client.EXPECT().Logout()
|
||||
|
||||
assert.Error(t, updater.InstallUpdate(latestVersion))
|
||||
err := updater.InstallUpdate(latestVersion)
|
||||
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestInstallUpdateAlreadyOngoing(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user