Browse Source

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

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3939
Jakob Borg 8 years ago
parent
commit
35e87e23fd

+ 4 - 2
cmd/syncthing/gui.go

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

+ 4 - 4
cmd/syncthing/main.go

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

+ 5 - 0
gui/default/assets/lang/lang-en.json

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

+ 21 - 8
gui/default/syncthing/core/syncthingController.js

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

+ 14 - 9
gui/default/syncthing/settings/settingsModalView.html

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

+ 1 - 0
lib/config/optionsconfiguration.go

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

+ 11 - 13
lib/upgrade/upgrade_supported.go

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

+ 17 - 14
lib/upgrade/upgrade_test.go

@@ -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")
 	}
@@ -66,22 +66,25 @@ func TestErrorRelease(t *testing.T) {
 
 func TestSelectedRelease(t *testing.T) {
 	testcases := []struct {
-		current    string
-		candidates []string
-		selected   string
+		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)
 		}

+ 1 - 1
lib/upgrade/upgrade_unsupp.go

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