cmd/syncthing, gui, lib/config, lib/upgrade: Add option to upgrade to pre-releases

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3939
This commit is contained in:
Jakob Borg 2017-01-27 12:17:06 +00:00 committed by Audrius Butkevicius
parent e03be9158b
commit 35e87e23fd
9 changed files with 78 additions and 51 deletions

View File

@ -1042,7 +1042,8 @@ func (s *apiService) getSystemUpgrade(w http.ResponseWriter, r *http.Request) {
http.Error(w, upgrade.ErrUpgradeUnsupported.Error(), 500)
return
}
rel, err := upgrade.LatestRelease(s.cfg.Options().ReleasesURL, Version)
opts := s.cfg.Options()
rel, err := upgrade.LatestRelease(opts.ReleasesURL, Version, opts.UpgradeToPreReleases)
if err != nil {
http.Error(w, err.Error(), 500)
return
@ -1083,7 +1084,8 @@ func (s *apiService) getLang(w http.ResponseWriter, r *http.Request) {
}
func (s *apiService) postSystemUpgrade(w http.ResponseWriter, r *http.Request) {
rel, err := upgrade.LatestRelease(s.cfg.Options().ReleasesURL, Version)
opts := s.cfg.Options()
rel, err := upgrade.LatestRelease(opts.ReleasesURL, Version, opts.UpgradeToPreReleases)
if err != nil {
l.Warnln("getting latest release:", err)
http.Error(w, err.Error(), 500)

View File

@ -481,8 +481,8 @@ func debugFacilities() string {
func checkUpgrade() upgrade.Release {
cfg, _ := loadConfig()
releasesURL := cfg.Options().ReleasesURL
release, err := upgrade.LatestRelease(releasesURL, Version)
opts := cfg.Options()
release, err := upgrade.LatestRelease(opts.ReleasesURL, Version, opts.UpgradeToPreReleases)
if err != nil {
l.Fatalln("Upgrade:", err)
}
@ -1158,8 +1158,8 @@ func autoUpgrade(cfg *config.Wrapper) {
l.Infof("Connected to device %s with a newer version (current %q < remote %q). Checking for upgrades.", data["id"], Version, data["clientVersion"])
case <-timer.C:
}
rel, err := upgrade.LatestRelease(cfg.Options().ReleasesURL, Version)
opts := cfg.Options()
rel, err := upgrade.LatestRelease(opts.ReleasesURL, Version, opts.UpgradeToPreReleases)
if err == upgrade.ErrUpgradeUnsupported {
events.Default.Unsubscribe(sub)
return

View File

@ -26,6 +26,7 @@
"Be careful!": "Be careful!",
"Bugs": "Bugs",
"CPU Utilization": "CPU Utilization",
"Candidate releases": "Candidate releases",
"Changelog": "Changelog",
"Clean out after": "Clean out after",
"Close": "Close",
@ -81,6 +82,7 @@
"GUI Authentication Password": "GUI Authentication Password",
"GUI Authentication User": "GUI Authentication User",
"GUI Listen Addresses": "GUI Listen Addresses",
"GUI Theme": "GUI Theme",
"Generate": "Generate",
"Global Changes": "Global Changes",
"Global Discovery": "Global Discovery",
@ -120,6 +122,7 @@
"Newest First": "Newest First",
"No": "No",
"No File Versioning": "No File Versioning",
"No upgrades": "No upgrades",
"Normal": "Normal",
"Notice": "Notice",
"OK": "OK",
@ -181,6 +184,7 @@
"Single level wildcard (matches within a directory only)": "Single level wildcard (matches within a directory only)",
"Smallest First": "Smallest First",
"Source Code": "Source Code",
"Stable releases only": "Stable releases only",
"Staggered File Versioning": "Staggered File Versioning",
"Start Browser": "Start Browser",
"Statistics": "Statistics",
@ -234,6 +238,7 @@
"Upgrading": "Upgrading",
"Upload Rate": "Upload Rate",
"Uptime": "Uptime",
"Usage reporting is always enabled for candidate releases.": "Usage reporting is always enabled for candidate releases.",
"Use HTTPS for GUI": "Use HTTPS for GUI",
"Version": "Version",
"Versions Path": "Versions Path",

View File

@ -1004,7 +1004,13 @@ angular.module('syncthing.core')
$scope.tmpOptions = angular.copy($scope.config.options);
$scope.tmpOptions.urEnabled = ($scope.tmpOptions.urAccepted > 0);
$scope.tmpOptions.deviceName = $scope.thisDevice().name;
$scope.tmpOptions.autoUpgradeEnabled = ($scope.tmpOptions.autoUpgradeIntervalH > 0);
$scope.tmpOptions.upgrades = "none";
if ($scope.tmpOptions.autoUpgradeIntervalH > 0) {
$scope.tmpOptions.upgrades = "stable";
}
if ($scope.tmpOptions.upgradeToPreReleases) {
$scope.tmpOptions.upgrades = "candidate";
}
$scope.tmpGUI = angular.copy($scope.config.gui);
$('#settings').modal();
};
@ -1028,6 +1034,20 @@ angular.module('syncthing.core')
var changed = !angular.equals($scope.config.options, $scope.tmpOptions) || !angular.equals($scope.config.gui, $scope.tmpGUI);
var themeChanged = $scope.config.gui.theme !== $scope.tmpGUI.theme;
if (changed) {
// Check if auto-upgrade has been enabled or disabled. This
// also has an effect on usage reporting, so do the check
// for that later.
if ($scope.tmpOptions.upgrades == "candidate") {
$scope.tmpOptions.autoUpgradeIntervalH = $scope.tmpOptions.autoUpgradeIntervalH || 12;
$scope.tmpOptions.upgradeToPreReleases = true;
$scope.tmpOptions.urEnabled = true;
} else if ($scope.tmpOptions.upgrades == "stable") {
$scope.tmpOptions.autoUpgradeIntervalH = $scope.tmpOptions.autoUpgradeIntervalH || 12;
$scope.tmpOptions.upgradeToPreReleases = false;
} else {
$scope.tmpOptions.autoUpgradeIntervalH = 0;
}
// Check if usage reporting has been enabled or disabled
if ($scope.tmpOptions.urEnabled && $scope.tmpOptions.urAccepted <= 0) {
$scope.tmpOptions.urAccepted = 1000;
@ -1035,13 +1055,6 @@ angular.module('syncthing.core')
$scope.tmpOptions.urAccepted = -1;
}
// Check if auto-upgrade has been enabled or disabled
if ($scope.tmpOptions.autoUpgradeEnabled) {
$scope.tmpOptions.autoUpgradeIntervalH = $scope.tmpOptions.autoUpgradeIntervalH || 12;
} else {
$scope.tmpOptions.autoUpgradeIntervalH = 0;
}
// Check if protocol will need to be changed on restart
if ($scope.config.gui.useTLS !== $scope.tmpGUI.useTLS) {
$scope.protocolChanged = true;

View File

@ -107,19 +107,24 @@
</label>
</div>
</div>
<div class="form-group" ng-if="upgradeInfo">
<label translate>Automatic upgrades</label>&emsp;<a href="https://docs.syncthing.net/users/releases.html" target="_blank"><span class="fa fa-fw fa-book"></span>&nbsp;<span translate>Help</span></a>
<select class="form-control" ng-model="tmpOptions.upgrades">
<option value="none" translate>No upgrades</option>
<option value="stable" translate>Stable releases only</option>
<option value="candidate" translate>Candidate releases</option>
</select>
</div>
<div class="form-group">
<div class="checkbox">
<div class="checkbox" ng-if="tmpOptions.upgrades != 'candidate'">
<label>
<input id="UREnabled" type="checkbox" ng-model="tmpOptions.urEnabled"> <span translate>Anonymous Usage Reporting</span> (<a href="" translate data-toggle="modal" data-target="#urPreview">Preview</a>)
</label>
</div>
</div>
<div class="form-group" ng-if="upgradeInfo">
<div class="checkbox">
<label>
<input id="AutoUpgradeEnabled" type="checkbox" ng-model="tmpOptions.autoUpgradeEnabled"> <span translate>Automatic upgrades</span>
</label>
</div>
<p class="help-block" ng-if="tmpOptions.upgrades == 'candidate'">
<span translate>Usage reporting is always enabled for candidate releases.</span> (<a href="" translate data-toggle="modal" data-target="#urPreview">Preview</a>)
</p>
</div>
<hr />
@ -133,7 +138,7 @@
</div>
<div class="form-group" ng-if="themes.length > 1">
<label>GUI Theme</label>
<label translate>GUI Theme</label>
<select class="form-control" ng-model="tmpGUI.theme">
<option ng-repeat="theme in themes.sort()" value="{{ theme }}">
{{ themeName(theme) }}

View File

@ -30,6 +30,7 @@ type OptionsConfiguration struct {
URInitialDelayS int `xml:"urInitialDelayS" json:"urInitialDelayS" default:"1800"`
RestartOnWakeup bool `xml:"restartOnWakeup" json:"restartOnWakeup" default:"true"`
AutoUpgradeIntervalH int `xml:"autoUpgradeIntervalH" json:"autoUpgradeIntervalH" default:"12"` // 0 for off
UpgradeToPreReleases bool `xml:"upgradeToPreReleases" json:"upgradeToPreReleases"` // when auto upgrades are enabled
KeepTemporariesH int `xml:"keepTemporariesH" json:"keepTemporariesH" default:"24"` // 0 for off
CacheIgnoredFiles bool `xml:"cacheIgnoredFiles" json:"cacheIgnoredFiles" default:"false"`
ProgressUpdateIntervalS int `xml:"progressUpdateIntervalS" json:"progressUpdateIntervalS" default:"5"`

View File

@ -84,10 +84,10 @@ func insecureGet(url, version string) (*http.Response, error) {
return insecureHTTP.Do(req)
}
// FetchLatestReleases returns the latest releases, including prereleases or
// not depending on the argument
func FetchLatestReleases(releasesURL, version string) []Release {
resp, err := insecureGet(releasesURL, version)
// FetchLatestReleases returns the latest releases. The "current" parameter
// is used for setting the User-Agent only.
func FetchLatestReleases(releasesURL, current string) []Release {
resp, err := insecureGet(releasesURL, current)
if err != nil {
l.Infoln("Couldn't fetch release information:", err)
return nil
@ -119,24 +119,22 @@ func (s SortByRelease) Less(i, j int) bool {
return CompareVersions(s[i].Tag, s[j].Tag) > 0
}
func LatestRelease(releasesURL, version string) (Release, error) {
rels := FetchLatestReleases(releasesURL, version)
return SelectLatestRelease(version, rels)
func LatestRelease(releasesURL, current string, upgradeToPreReleases bool) (Release, error) {
rels := FetchLatestReleases(releasesURL, current)
return SelectLatestRelease(rels, current, upgradeToPreReleases)
}
func SelectLatestRelease(version string, rels []Release) (Release, error) {
func SelectLatestRelease(rels []Release, current string, upgradeToPreReleases bool) (Release, error) {
if len(rels) == 0 {
return Release{}, ErrNoVersionToSelect
}
// Sort the releases, lowest version number first
sort.Sort(sort.Reverse(SortByRelease(rels)))
// Check for a beta build
beta := strings.Contains(version, "-")
var selected Release
for _, rel := range rels {
switch CompareVersions(rel.Tag, version) {
switch CompareVersions(rel.Tag, current) {
case Older, MajorOlder:
// This is older than what we're already running
continue
@ -145,7 +143,7 @@ func SelectLatestRelease(version string, rels []Release) (Release, error) {
// We've found a new major version. That's fine, but if we've
// already found a minor upgrade that is acceptable we should go
// with that one first and then revisit in the future.
if selected.Tag != "" && CompareVersions(selected.Tag, version) == Newer {
if selected.Tag != "" && CompareVersions(selected.Tag, current) == Newer {
return selected, nil
}
// else it may be viable, do the needful below
@ -154,7 +152,7 @@ func SelectLatestRelease(version string, rels []Release) (Release, error) {
// New minor release, do the usual processing
}
if rel.Prerelease && !beta {
if rel.Prerelease && !upgradeToPreReleases {
continue
}
for _, asset := range rel.Assets {

View File

@ -58,7 +58,7 @@ func TestCompareVersions(t *testing.T) {
}
func TestErrorRelease(t *testing.T) {
_, err := SelectLatestRelease("v0.11.0-beta", nil)
_, err := SelectLatestRelease(nil, "v0.11.0-beta", false)
if err == nil {
t.Error("Should return an error when no release were available")
}
@ -67,21 +67,24 @@ func TestErrorRelease(t *testing.T) {
func TestSelectedRelease(t *testing.T) {
testcases := []struct {
current string
upgradeToPre bool
candidates []string
selected string
}{
// Within the same "major" (minor, in this case) select the newest
{"v0.12.24", []string{"v0.12.23", "v0.12.24", "v0.12.25", "v0.12.26"}, "v0.12.26"},
// Do no select beta versions when we are not a beta
{"v0.12.24", []string{"v0.12.26", "v0.12.27-beta.42"}, "v0.12.26"},
// Do select beta versions when we are a beta
{"v0.12.24-beta.0", []string{"v0.12.26", "v0.12.27-beta.42"}, "v0.12.27-beta.42"},
{"v0.12.24", false, []string{"v0.12.23", "v0.12.24", "v0.12.25", "v0.12.26"}, "v0.12.26"},
// Do no select beta versions when we are not allowed to
{"v0.12.24", false, []string{"v0.12.26", "v0.12.27-beta.42"}, "v0.12.26"},
{"v0.12.24-beta.0", false, []string{"v0.12.26", "v0.12.27-beta.42"}, "v0.12.26"},
// Do select beta versions when we can
{"v0.12.24", true, []string{"v0.12.26", "v0.12.27-beta.42"}, "v0.12.27-beta.42"},
{"v0.12.24-beta.0", true, []string{"v0.12.26", "v0.12.27-beta.42"}, "v0.12.27-beta.42"},
// Select the best within the current major when there is a minor upgrade available
{"v0.12.24", []string{"v0.12.23", "v0.12.24", "v0.12.25", "v0.13.0"}, "v0.12.25"},
{"v1.12.24", []string{"v1.12.23", "v1.12.24", "v1.14.2", "v2.0.0"}, "v1.14.2"},
{"v0.12.24", false, []string{"v0.12.23", "v0.12.24", "v0.12.25", "v0.13.0"}, "v0.12.25"},
{"v1.12.24", false, []string{"v1.12.23", "v1.12.24", "v1.14.2", "v2.0.0"}, "v1.14.2"},
// Select the next major when we are at the best minor
{"v0.12.25", []string{"v0.12.23", "v0.12.24", "v0.12.25", "v0.13.0"}, "v0.13.0"},
{"v1.14.2", []string{"v0.12.23", "v0.12.24", "v1.14.2", "v2.0.0"}, "v2.0.0"},
{"v0.12.25", true, []string{"v0.12.23", "v0.12.24", "v0.12.25", "v0.13.0"}, "v0.13.0"},
{"v1.14.2", true, []string{"v0.12.23", "v0.12.24", "v1.14.2", "v2.0.0"}, "v2.0.0"},
}
for i, tc := range testcases {
@ -99,7 +102,7 @@ func TestSelectedRelease(t *testing.T) {
}
// Check the selection
sel, err := SelectLatestRelease(tc.current, rels)
sel, err := SelectLatestRelease(rels, tc.current, tc.upgradeToPre)
if err != nil {
t.Fatal("Unexpected error:", err)
}

View File

@ -18,6 +18,6 @@ func upgradeToURL(archiveName, binary, url string) error {
return ErrUpgradeUnsupported
}
func LatestRelease(releasesURL, version string) (Release, error) {
func LatestRelease(releasesURL, current string, upgradeToPreRelease bool) (Release, error) {
return Release{}, ErrUpgradeUnsupported
}