GODT-1244: Refactor switching stable-early and factory reset
This commit is contained in:
parent
b5b477a3ce
commit
41e15db442
|
@ -23,6 +23,7 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/ProtonMail/proton-bridge/internal/config/settings"
|
||||
"github.com/ProtonMail/proton-bridge/internal/constants"
|
||||
"github.com/ProtonMail/proton-bridge/internal/metrics"
|
||||
|
@ -131,54 +132,54 @@ func (b *Bridge) GetUpdateChannel() updater.UpdateChannel {
|
|||
}
|
||||
|
||||
// SetUpdateChannel switches update channel.
|
||||
// Downgrading to previous version (by switching from early to stable, for example)
|
||||
// requires clearing all data including update files due to possibility of
|
||||
// inconsistency between versions and absence of backwards migration scripts.
|
||||
func (b *Bridge) SetUpdateChannel(channel updater.UpdateChannel) (needRestart bool, err error) {
|
||||
func (b *Bridge) SetUpdateChannel(channel updater.UpdateChannel) {
|
||||
b.settings.Set(settings.UpdateChannelKey, string(channel))
|
||||
}
|
||||
|
||||
func (b *Bridge) resetToLatestStable() error {
|
||||
version, err := b.updater.Check()
|
||||
if err != nil {
|
||||
return false, err
|
||||
// If we can not check for updates - just remove all local updates and reset to base installer version.
|
||||
// Not using `b.locations.ClearUpdates()` because `versioner.RemoveOtherVersions` can also handle
|
||||
// case when it is needed to remove currently running verion.
|
||||
if err := b.versioner.RemoveOtherVersions(semver.MustParse("0.0.0")); err != nil {
|
||||
log.WithError(err).Error("Failed to clear updates while downgrading channel")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// We have to deal right away only with downgrade - that action needs to
|
||||
// clear data and updates, and install bridge right away. But regular
|
||||
// upgrade can be left out for periodic check.
|
||||
if !b.updater.IsDowngrade(version) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if err := b.Users.ClearData(); err != nil {
|
||||
log.WithError(err).Error("Failed to clear data while downgrading channel")
|
||||
}
|
||||
|
||||
if err := b.locations.ClearUpdates(); err != nil {
|
||||
log.WithError(err).Error("Failed to clear updates while downgrading channel")
|
||||
// If current version is same as upstream stable version - do nothing.
|
||||
if version.Version.Equal(semver.MustParse(constants.Version)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := b.updater.InstallUpdate(version); err != nil {
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
|
||||
return true, b.versioner.RemoveOtherVersions(version.Version)
|
||||
return b.versioner.RemoveOtherVersions(version.Version)
|
||||
}
|
||||
|
||||
// FactoryReset will remove all local cache and settings.
|
||||
// We want to downgrade to latest stable version if user is early higher than stable.
|
||||
// Setting the channel back to stable will do this for us.
|
||||
// It will also downgrade to latest stable version if user is on early version.
|
||||
func (b *Bridge) FactoryReset() {
|
||||
if _, err := b.SetUpdateChannel(updater.StableChannel); err != nil {
|
||||
log.WithError(err).Error("Failed to revert to stable update channel")
|
||||
}
|
||||
wasEarly := b.GetUpdateChannel() == updater.EarlyChannel
|
||||
|
||||
if err := b.Users.ClearUsers(); err != nil {
|
||||
log.WithError(err).Error("Failed to remove bridge users")
|
||||
b.settings.Set(settings.UpdateChannelKey, string(updater.StableChannel))
|
||||
|
||||
if wasEarly {
|
||||
if err := b.resetToLatestStable(); err != nil {
|
||||
log.WithError(err).Error("Failed to reset to latest stable version")
|
||||
}
|
||||
}
|
||||
|
||||
if err := b.Users.ClearData(); err != nil {
|
||||
log.WithError(err).Error("Failed to remove bridge data")
|
||||
}
|
||||
|
||||
if err := b.Users.ClearUsers(); err != nil {
|
||||
log.WithError(err).Error("Failed to remove bridge users")
|
||||
}
|
||||
}
|
||||
|
||||
// GetKeychainApp returns current keychain helper.
|
||||
|
|
|
@ -81,13 +81,7 @@ func (f *frontendCLI) selectEarlyChannel(c *ishell.Context) {
|
|||
f.Println("Bridge is currently on the stable update channel.")
|
||||
|
||||
if f.yesNoQuestion("Are you sure you want to switch to the early-access update channel") {
|
||||
needRestart, err := f.bridge.SetUpdateChannel(updater.EarlyChannel)
|
||||
if err != nil {
|
||||
f.Println("There was a problem switching update channel.")
|
||||
}
|
||||
if needRestart {
|
||||
f.restarter.SetToRestart()
|
||||
}
|
||||
f.bridge.SetUpdateChannel(updater.EarlyChannel)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,12 +95,6 @@ func (f *frontendCLI) selectStableChannel(c *ishell.Context) {
|
|||
f.Println("Switching to the stable channel may reset all data!")
|
||||
|
||||
if f.yesNoQuestion("Are you sure you want to switch to the stable update channel") {
|
||||
needRestart, err := f.bridge.SetUpdateChannel(updater.StableChannel)
|
||||
if err != nil {
|
||||
f.Println("There was a problem switching update channel.")
|
||||
}
|
||||
if needRestart {
|
||||
f.restarter.SetToRestart()
|
||||
}
|
||||
f.bridge.SetUpdateChannel(updater.StableChannel)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ SettingsView {
|
|||
if (!beta.checked) {
|
||||
root.notifications.askEnableBeta()
|
||||
} else {
|
||||
root.notifications.askDisableBeta()
|
||||
root.backend.toggleBeta(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -66,11 +66,6 @@ Item {
|
|||
notification: root.notifications.updateForceError
|
||||
}
|
||||
|
||||
NotificationDialog {
|
||||
colorScheme: root.colorScheme
|
||||
notification: root.notifications.disableBeta
|
||||
}
|
||||
|
||||
NotificationDialog {
|
||||
colorScheme: root.colorScheme
|
||||
notification: root.notifications.enableBeta
|
||||
|
|
|
@ -29,7 +29,6 @@ QtObject {
|
|||
property StatusWindow frontendStatus
|
||||
property SystemTrayIcon frontendTray
|
||||
|
||||
signal askDisableBeta()
|
||||
signal askEnableBeta()
|
||||
signal askEnableSplitMode(var user)
|
||||
signal askDisableLocalCache()
|
||||
|
@ -58,7 +57,6 @@ QtObject {
|
|||
root.updateIsLatestVersion,
|
||||
root.loginConnectionError,
|
||||
root.onlyPaidUsers,
|
||||
root.disableBeta,
|
||||
root.enableBeta,
|
||||
root.bugReportSendSuccess,
|
||||
root.bugReportSendError,
|
||||
|
@ -337,41 +335,6 @@ QtObject {
|
|||
}
|
||||
}
|
||||
|
||||
property Notification disableBeta: Notification {
|
||||
text: qsTr("Disable beta access?")
|
||||
description: qsTr("This resets Bridge to the current release and will restart the app. Your preferences, cached data, and email client configurations will be cleared. ")
|
||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||
type: Notification.NotificationType.Warning
|
||||
group: Notifications.Group.Update | Notifications.Group.Dialogs
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
onAskDisableBeta: {
|
||||
root.disableBeta.active = true
|
||||
}
|
||||
}
|
||||
|
||||
action: [
|
||||
Action {
|
||||
id: disableBeta_remindLater
|
||||
text: qsTr("Remind me later")
|
||||
|
||||
onTriggered: {
|
||||
root.disableBeta.active = false
|
||||
}
|
||||
},
|
||||
Action {
|
||||
id: disableBeta_disable
|
||||
text: qsTr("Disable and restart")
|
||||
onTriggered: {
|
||||
root.backend.toggleBeta(false)
|
||||
disableBeta_disable.loading = true
|
||||
disableBeta_remindLater.enabled = false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
property Notification enableBeta: Notification {
|
||||
text: qsTr("Enable Beta access")
|
||||
description: qsTr("Be the first to get new updates and use new features. Bridge will update to the latest beta version.")
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build build_qt
|
||||
// +build build_qt
|
||||
|
||||
package qt
|
||||
|
@ -100,31 +101,15 @@ func (f *FrontendQt) setIsBetaEnabled() {
|
|||
}
|
||||
|
||||
func (f *FrontendQt) toggleBeta(makeItEnabled bool) {
|
||||
channel := f.bridge.GetUpdateChannel()
|
||||
|
||||
if makeItEnabled == (channel == updater.EarlyChannel) {
|
||||
f.qml.SetIsBetaEnabled(makeItEnabled)
|
||||
return
|
||||
}
|
||||
|
||||
channel = updater.StableChannel
|
||||
channel := updater.StableChannel
|
||||
if makeItEnabled {
|
||||
channel = updater.EarlyChannel
|
||||
}
|
||||
|
||||
needRestart, err := f.bridge.SetUpdateChannel(channel)
|
||||
f.bridge.SetUpdateChannel(channel)
|
||||
|
||||
f.setIsBetaEnabled()
|
||||
|
||||
if err != nil {
|
||||
f.log.WithError(err).Warn("Switching udpate channel failed.")
|
||||
f.qml.UpdateManualError()
|
||||
return
|
||||
}
|
||||
|
||||
if needRestart {
|
||||
f.restart()
|
||||
return
|
||||
}
|
||||
|
||||
f.checkUpdatesAndNotify(false)
|
||||
// Immediately check the updates to set the correct landing page link.
|
||||
f.checkUpdates()
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ type Bridger interface {
|
|||
DisableCache() error
|
||||
MigrateCache(from, to string) error
|
||||
GetUpdateChannel() updater.UpdateChannel
|
||||
SetUpdateChannel(updater.UpdateChannel) (needRestart bool, err error)
|
||||
SetUpdateChannel(updater.UpdateChannel)
|
||||
GetKeychainApp() string
|
||||
SetKeychainApp(keychain string)
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build !darwin
|
||||
// +build !darwin
|
||||
|
||||
package versioner
|
||||
|
@ -23,6 +24,7 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/ProtonMail/proton-bridge/internal/constants"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
@ -58,6 +60,12 @@ func (v *Versioner) RemoveOtherVersions(versionToKeep *semver.Version) error {
|
|||
if version.Equal(versionToKeep) {
|
||||
continue
|
||||
}
|
||||
if version.Equal(semver.MustParse(constants.Version)) {
|
||||
if err := v.RemoveCurrentVersion(); err != nil {
|
||||
logrus.WithError(err).Error("Failed to remove current app version")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err := os.RemoveAll(version.path); err != nil {
|
||||
logrus.WithError(err).Error("Failed to remove old app version")
|
||||
}
|
|
@ -15,6 +15,9 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package versioner
|
||||
|
||||
import "github.com/Masterminds/semver/v3"
|
||||
|
@ -30,3 +33,9 @@ func (v *Versioner) RemoveOtherVersions(versionToKeep *semver.Version) error {
|
|||
// darwin does not use the versioner; removal is a noop.
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveOtherVersions removes current app version unless it is base installed version.
|
||||
func (v *Versioner) RemoveCurrentVersion() error {
|
||||
// darwin does not use the versioner; removal is a noop.
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package versioner
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (v *Versioner) RemoveCurrentVersion() error {
|
||||
// get current executable
|
||||
exec, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check that current executtable is update package so we won't
|
||||
// delete base version (that is controlled by package manager).
|
||||
// Get absolute paths to ensure there is no crazy stuff there.
|
||||
absExec, err := filepath.Abs(exec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
absRoot, err := filepath.Abs(v.root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !strings.HasPrefix(absExec, absRoot) {
|
||||
return ErrNoRemoveBase
|
||||
}
|
||||
|
||||
return os.RemoveAll(filepath.Dir(absExec))
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package versioner
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (v *Versioner) RemoveCurrentVersion() error {
|
||||
// get current executable
|
||||
exec, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check that current executtable is update package so we won't
|
||||
// delete base version (that is controlled by package manager).
|
||||
// Get absolute paths to ensure there is no crazy stuff there.
|
||||
absExec, err := filepath.Abs(exec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
absRoot, err := filepath.Abs(v.root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !strings.HasPrefix(absExec, absRoot) {
|
||||
return ErrNoRemoveBase
|
||||
}
|
||||
|
||||
// It is impossible delete running executable on Windows, so instead
|
||||
// we rename it. Next time launcher will start it will remove this version
|
||||
// as checksum won't match.
|
||||
return os.Rename(absExec, filepath.Join(filepath.Dir(absExec), "_"+filepath.Base(absExec)))
|
||||
}
|
|
@ -29,6 +29,7 @@ import (
|
|||
var (
|
||||
ErrNoVersions = errors.New("no available versions")
|
||||
ErrNoExecutable = errors.New("no executable found")
|
||||
ErrNoRemoveBase = errors.New("can't remove base version")
|
||||
)
|
||||
|
||||
// Versioner manages a directory of versioned app directories.
|
||||
|
|
Loading…
Reference in New Issue