diff --git a/gui/default/syncthing/core/syncthingController.js b/gui/default/syncthing/core/syncthingController.js index 6a77e514e..adca42026 100755 --- a/gui/default/syncthing/core/syncthingController.js +++ b/gui/default/syncthing/core/syncthingController.js @@ -3051,7 +3051,11 @@ angular.module('syncthing.core') arch += " Container"; } - return $scope.version.version + ', ' + os + ' (' + arch + ')'; + var verStr = $scope.version.version; + if ($scope.version.extra) { + verStr += ' (' + $scope.version.extra + ')'; + } + return verStr + ', ' + os + ' (' + arch + ')'; }; $scope.versionBase = function () { diff --git a/lib/api/api.go b/lib/api/api.go index 38b5d2c92..1d4116ef7 100644 --- a/lib/api/api.go +++ b/lib/api/api.go @@ -713,6 +713,7 @@ func (*service) getSystemVersion(w http.ResponseWriter, _ *http.Request) { "version": build.Version, "codename": build.Codename, "longVersion": build.LongVersion, + "extra": build.Extra, "os": runtime.GOOS, "arch": runtime.GOARCH, "isBeta": build.IsBeta, diff --git a/lib/build/build.go b/lib/build/build.go index 285cf65e7..a389ac146 100644 --- a/lib/build/build.go +++ b/lib/build/build.go @@ -35,6 +35,7 @@ var ( IsCandidate bool IsBeta bool LongVersion string + Extra string allowedVersionExp = regexp.MustCompile(`^v\d+\.\d+\.\d+(-[a-z0-9]+)*(\.\d+)*(\+\d+-g[0-9a-f]+)?(-[^\s]+)?$`) @@ -46,6 +47,8 @@ var ( } ) +const versionExtraAllowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-. " + func init() { if Version != "unknown-dev" { // If not a generic dev build, version string should come from git describe @@ -75,6 +78,7 @@ func setBuildData() { IsRelease = exp.MatchString(Version) IsCandidate = strings.Contains(Version, "-rc.") IsBeta = strings.Contains(Version, "-") + Extra = filterString(os.Getenv("STVERSIONEXTRA"), versionExtraAllowedChars) stamp, _ := strconv.Atoi(Stamp) Date = time.Unix(int64(stamp), 0) @@ -103,7 +107,22 @@ func TagsList() []string { tags = append(tags, strings.ToLower(envVar)) } } + if Extra != "" { + tags = append(tags, Extra) + } sort.Strings(tags) return tags } + +// filterString returns a copy of s with all characters not in allowedChars +// removed. +func filterString(s, allowedChars string) string { + var res strings.Builder + for _, c := range s { + if strings.ContainsRune(allowedChars, c) { + res.WriteRune(c) + } + } + return res.String() +} diff --git a/lib/build/build_test.go b/lib/build/build_test.go index 797611694..e2918c018 100644 --- a/lib/build/build_test.go +++ b/lib/build/build_test.go @@ -35,3 +35,23 @@ func TestAllowedVersions(t *testing.T) { } } } + +func TestFilterString(t *testing.T) { + cases := []struct { + input string + filter string + output string + }{ + {"abcba", "abc", "abcba"}, + {"abcba", "ab", "abba"}, + {"abcba", "c", "c"}, + {"abcba", "!", ""}, + {"Foo (v1.5)", versionExtraAllowedChars, "Foo v1.5"}, + } + + for i, c := range cases { + if out := filterString(c.input, c.filter); out != c.output { + t.Errorf("%d: %q != %q", i, out, c.output) + } + } +}