GODT-1977: fix launcher for v2 to v3 updates.

This commit is contained in:
Jakub
2022-11-03 14:01:26 +01:00
committed by James Houlahan
parent 34213d1607
commit c08d0eff7a
8 changed files with 79 additions and 17 deletions

View File

@ -56,6 +56,7 @@ const (
func main() { //nolint:funlen func main() { //nolint:funlen
logrus.SetLevel(logrus.DebugLevel) logrus.SetLevel(logrus.DebugLevel)
l := logrus.WithField("launcher_version", constants.Version) l := logrus.WithField("launcher_version", constants.Version)
reporter := sentry.NewReporter(appName, constants.Version, useragent.New()) reporter := sentry.NewReporter(appName, constants.Version, useragent.New())
crashHandler := crash.NewHandler(reporter.ReportException) crashHandler := crash.NewHandler(reporter.ReportException)
@ -75,7 +76,7 @@ func main() { //nolint:funlen
crashHandler.AddRecoveryAction(logging.DumpStackTrace(logsPath)) crashHandler.AddRecoveryAction(logging.DumpStackTrace(logsPath))
if err := logging.Init(logsPath, os.Getenv("VERBOSITY")); err != nil { if err := logging.Init(logsPath, os.Getenv("VERBOSITY")); err != nil {
logrus.WithError(err).Fatal("Failed to setup logging") l.WithError(err).Fatal("Failed to setup logging")
} }
updatesPath, err := locations.ProvideUpdatesPath() updatesPath, err := locations.ProvideUpdatesPath()
@ -240,7 +241,7 @@ func getPathToUpdatedExecutable(
} }
// Skip versions that are less or equal to launcher version. // Skip versions that are less or equal to launcher version.
if currentVersion != nil && !version.SemVer().GreaterThan(currentVersion) { if currentVersion != nil && !versioner.IsNewerIgnorePrerelease(version.SemVer(), currentVersion) {
continue continue
} }

View File

@ -19,10 +19,12 @@ package bridge
import ( import (
"context" "context"
"errors"
"github.com/ProtonMail/proton-bridge/v2/internal/events" "github.com/ProtonMail/proton-bridge/v2/internal/events"
"github.com/ProtonMail/proton-bridge/v2/internal/safe" "github.com/ProtonMail/proton-bridge/v2/internal/safe"
"github.com/ProtonMail/proton-bridge/v2/internal/updater" "github.com/ProtonMail/proton-bridge/v2/internal/updater"
"github.com/ProtonMail/proton-bridge/v2/internal/versioner"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -58,7 +60,7 @@ func (bridge *Bridge) handleUpdate(version updater.VersionInfo) {
}) })
switch { switch {
case !version.Version.GreaterThan(bridge.curVersion): case !versioner.IsNewerIgnorePrerelease(version.Version, bridge.curVersion):
log.Debug("No update available") log.Debug("No update available")
bridge.publish(events.UpdateNotAvailable{}) bridge.publish(events.UpdateNotAvailable{})
@ -68,7 +70,7 @@ func (bridge *Bridge) handleUpdate(version updater.VersionInfo) {
bridge.publish(events.UpdateNotAvailable{}) bridge.publish(events.UpdateNotAvailable{})
case bridge.curVersion.LessThan(version.MinAuto): case versioner.IsNewerIgnorePrerelease(version.MinAuto, bridge.curVersion):
log.Info("An update is available but is incompatible with this version") log.Info("An update is available but is incompatible with this version")
bridge.publish(events.UpdateAvailable{ bridge.publish(events.UpdateAvailable{
@ -88,7 +90,7 @@ func (bridge *Bridge) handleUpdate(version updater.VersionInfo) {
default: default:
safe.RLock(func() { safe.RLock(func() {
if version.Version.GreaterThan(bridge.newVersion) { if versioner.IsNewerIgnorePrerelease(version.Version, bridge.newVersion) {
log.Info("An update is available") log.Info("An update is available")
select { select {
@ -127,15 +129,21 @@ func (bridge *Bridge) installUpdate(ctx context.Context, job installJob) {
Silent: job.silent, Silent: job.silent,
}) })
if err := bridge.updater.InstallUpdate(ctx, bridge.api, job.version); err != nil { err := bridge.updater.InstallUpdate(ctx, bridge.api, job.version)
log.Error("The update could not be installed")
switch {
case errors.Is(err, updater.ErrUpdateAlreadyInstalled):
log.Info("The update was already installed")
case err != nil:
log.WithError(err).Error("The update could not be installed")
bridge.publish(events.UpdateFailed{ bridge.publish(events.UpdateFailed{
Version: job.version, Version: job.version,
Silent: job.silent, Silent: job.silent,
Error: err, Error: err,
}) })
} else { default:
log.Info("The update was installed successfully") log.Info("The update was installed successfully")
bridge.publish(events.UpdateInstalled{ bridge.publish(events.UpdateInstalled{

View File

@ -61,3 +61,7 @@ func (i *InstallerDarwin) InstallUpdate(_ *semver.Version, r io.Reader) error {
return syncFolders(oldBundle, newBundle) return syncFolders(oldBundle, newBundle)
} }
func (i *InstallerDarwin) IsAlreadyInstalled(version *semver.Version) bool {
return false
}

View File

@ -25,6 +25,7 @@ import (
"github.com/Masterminds/semver/v3" "github.com/Masterminds/semver/v3"
"github.com/ProtonMail/proton-bridge/v2/internal/versioner" "github.com/ProtonMail/proton-bridge/v2/internal/versioner"
"github.com/sirupsen/logrus"
) )
type InstallerDefault struct { type InstallerDefault struct {
@ -40,3 +41,15 @@ func NewInstaller(versioner *versioner.Versioner) *InstallerDefault {
func (i *InstallerDefault) 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) return i.versioner.InstallNewVersion(version, r)
} }
func (i *InstallerDefault) IsAlreadyInstalled(version *semver.Version) bool {
versions, err := i.versioner.ListVersions()
if err != nil {
logrus.WithField("version", version).
WithError(err).Error("Failed to determine whether version is installed")
return false
}
return versions.HasVersion(version)
}

View File

@ -30,8 +30,9 @@ import (
) )
var ( var (
ErrDownloadVerify = errors.New("failed to download or verify the update") ErrDownloadVerify = errors.New("failed to download or verify the update")
ErrInstall = errors.New("failed to install the update") ErrInstall = errors.New("failed to install the update")
ErrUpdateAlreadyInstalled = errors.New("update is already installed")
) )
type Downloader interface { type Downloader interface {
@ -39,6 +40,7 @@ type Downloader interface {
} }
type Installer interface { type Installer interface {
IsAlreadyInstalled(*semver.Version) bool
InstallUpdate(*semver.Version, io.Reader) error InstallUpdate(*semver.Version, io.Reader) error
} }
@ -84,6 +86,10 @@ func (u *Updater) GetVersionInfo(ctx context.Context, downloader Downloader, cha
} }
func (u *Updater) InstallUpdate(ctx context.Context, downloader Downloader, update VersionInfo) error { func (u *Updater) InstallUpdate(ctx context.Context, downloader Downloader, update VersionInfo) error {
if u.installer.IsAlreadyInstalled(update.Version) {
return ErrUpdateAlreadyInstalled
}
b, err := downloader.DownloadAndVerify( b, err := downloader.DownloadAndVerify(
ctx, ctx,
u.verifier, u.verifier,

View File

@ -50,6 +50,16 @@ func (v Versions) Swap(i, j int) {
v[i], v[j] = v[j], v[i] v[i], v[j] = v[j], v[i]
} }
func (v Versions) HasVersion(want *semver.Version) bool {
for i := range v {
if v[i].version.Equal(want) {
return true
}
}
return false
}
func (v *Version) String() string { func (v *Version) String() string {
return fmt.Sprintf("%v", v.version) return fmt.Sprintf("%v", v.version)
} }
@ -101,13 +111,7 @@ func (v *Version) VerifyFiles(kr *crypto.KeyRing) error {
// GetExecutable returns the full path to the executable of the given version. // GetExecutable returns the full path to the executable of the given version.
// It returns an error if the executable is missing or does not have executable permissions set. // It returns an error if the executable is missing or does not have executable permissions set.
func (v *Version) GetExecutable(name string) (string, error) { func (v *Version) GetExecutable(name string) (string, error) {
exe := filepath.Join(v.path, getExeName(name)) return getExecutableInDirectory(name, v.path)
if !fileExists(exe) || !fileIsExecutable(exe) {
return "", ErrNoExecutable
}
return exe, nil
} }
// Remove removes this version directory. // Remove removes this version directory.

View File

@ -72,6 +72,10 @@ func (v *Versioner) ListVersions() (Versions, error) {
// GetExecutableInDirectory returns the full path to the executable in the given directory, if present. // GetExecutableInDirectory returns the full path to the executable in the given directory, if present.
// It returns an error if the executable is missing or does not have executable permissions set. // It returns an error if the executable is missing or does not have executable permissions set.
func (v *Versioner) GetExecutableInDirectory(name, directory string) (string, error) { func (v *Versioner) GetExecutableInDirectory(name, directory string) (string, error) {
return getExecutableInDirectory(name, directory)
}
func getExecutableInDirectory(name, directory string) (string, error) {
exe := filepath.Join(directory, getExeName(name)) exe := filepath.Join(directory, getExeName(name))
if !fileExists(exe) || !fileIsExecutable(exe) { if !fileExists(exe) || !fileIsExecutable(exe) {
@ -80,3 +84,10 @@ func (v *Versioner) GetExecutableInDirectory(name, directory string) (string, er
return exe, nil return exe, nil
} }
func IsNewerIgnorePrerelease(a, b *semver.Version) bool {
aN, _ := a.SetPrerelease("")
bN, _ := b.SetPrerelease("")
return aN.GreaterThan(&bN)
}

View File

@ -27,6 +27,21 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestIsNewerIgnorePrelease(t *testing.T) {
// older
assert.False(t, IsNewerIgnorePrerelease(semver.MustParse("2.5.0"), semver.MustParse("2.5.1")))
assert.False(t, IsNewerIgnorePrerelease(semver.MustParse("2.5.0"), semver.MustParse("2.5.0")))
assert.False(t, IsNewerIgnorePrerelease(semver.MustParse("2.5.0"), semver.MustParse("2.5.0+qa")))
assert.False(t, IsNewerIgnorePrerelease(semver.MustParse("2.5.0"), semver.MustParse("2.5.0-dev")))
assert.False(t, IsNewerIgnorePrerelease(semver.MustParse("2.5.0"), semver.MustParse("2.5.0-dev+qa")))
assert.False(t, IsNewerIgnorePrerelease(semver.MustParse("2.5.0+qa"), semver.MustParse("2.5.0-dev")))
assert.False(t, IsNewerIgnorePrerelease(semver.MustParse("2.5.0-dev"), semver.MustParse("2.5.0+qa")))
// not older
assert.True(t, IsNewerIgnorePrerelease(semver.MustParse("2.5.0"), semver.MustParse("2.4.9-dev+qa")))
assert.True(t, IsNewerIgnorePrerelease(semver.MustParse("2.5.0-dev+qa"), semver.MustParse("2.4.9")))
}
func TestListVersions(t *testing.T) { func TestListVersions(t *testing.T) {
dir := t.TempDir() dir := t.TempDir()