[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:
Alexander Bilyak
2020-11-10 08:09:17 +00:00
committed by James Houlahan
parent b7b2297635
commit 98ab794f13
34 changed files with 1069 additions and 612 deletions

View File

@ -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

View File

@ -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)
}

View File

@ -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
}

View File

@ -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) {