mirror of
https://github.com/rclone/rclone.git
synced 2024-11-22 16:34:30 +01:00
version: print the release and beta versions with --check - Fixes #2348
This commit is contained in:
parent
1cccfa7331
commit
4eefd05dcf
@ -1,19 +1,196 @@
|
|||||||
package version
|
package version
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ncw/rclone/cmd"
|
"github.com/ncw/rclone/cmd"
|
||||||
|
"github.com/ncw/rclone/fs"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
check = false
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cmd.Root.AddCommand(commandDefinition)
|
cmd.Root.AddCommand(commandDefinition)
|
||||||
|
flags := commandDefinition.Flags()
|
||||||
|
flags.BoolVarP(&check, "check", "", false, "Check for new version.")
|
||||||
}
|
}
|
||||||
|
|
||||||
var commandDefinition = &cobra.Command{
|
var commandDefinition = &cobra.Command{
|
||||||
Use: "version",
|
Use: "version",
|
||||||
Short: `Show the version number.`,
|
Short: `Show the version number.`,
|
||||||
|
Long: `
|
||||||
|
Show the version number, the go version and the architecture.
|
||||||
|
|
||||||
|
Eg
|
||||||
|
|
||||||
|
$ rclone version
|
||||||
|
rclone v1.41
|
||||||
|
- os/arch: linux/amd64
|
||||||
|
- go version: go1.10
|
||||||
|
|
||||||
|
If you supply the --check flag, then it will do an online check to
|
||||||
|
compare your version with the latest release and the latest beta.
|
||||||
|
|
||||||
|
$ rclone version --check
|
||||||
|
yours: 1.42.0.6
|
||||||
|
latest: 1.42 (released 2018-06-16)
|
||||||
|
beta: 1.42.0.5 (released 2018-06-17)
|
||||||
|
|
||||||
|
Or
|
||||||
|
|
||||||
|
$ rclone version --check
|
||||||
|
yours: 1.41
|
||||||
|
latest: 1.42 (released 2018-06-16)
|
||||||
|
upgrade: https://downloads.rclone.org/v1.42
|
||||||
|
beta: 1.42.0.5 (released 2018-06-17)
|
||||||
|
upgrade: https://beta.rclone.org/v1.42-005-g56e1e820
|
||||||
|
|
||||||
|
`,
|
||||||
Run: func(command *cobra.Command, args []string) {
|
Run: func(command *cobra.Command, args []string) {
|
||||||
cmd.CheckArgs(0, 0, command, args)
|
cmd.CheckArgs(0, 0, command, args)
|
||||||
|
if check {
|
||||||
|
checkVersion()
|
||||||
|
} else {
|
||||||
cmd.ShowVersion()
|
cmd.ShowVersion()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var parseVersion = regexp.MustCompile(`^(?:rclone )?v(\d+)\.(\d+)(?:\.(\d+))?(?:-(\d+)(?:-(g[\wβ-]+))?)?$`)
|
||||||
|
|
||||||
|
type version []int
|
||||||
|
|
||||||
|
func newVersion(in string) (v version, err error) {
|
||||||
|
r := parseVersion.FindStringSubmatch(in)
|
||||||
|
if r == nil {
|
||||||
|
return v, errors.Errorf("failed to match version string %q", in)
|
||||||
|
}
|
||||||
|
atoi := func(s string) int {
|
||||||
|
i, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
fs.Errorf(nil, "Failed to parse %q as int from %q: %v", s, in, err)
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
v = version{
|
||||||
|
atoi(r[1]), // major
|
||||||
|
atoi(r[2]), // minor
|
||||||
|
}
|
||||||
|
if r[3] != "" {
|
||||||
|
v = append(v, atoi(r[3])) // patch
|
||||||
|
} else if r[4] != "" {
|
||||||
|
v = append(v, 0) // patch
|
||||||
|
}
|
||||||
|
if r[4] != "" {
|
||||||
|
v = append(v, atoi(r[4])) // dev
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String converts v to a string
|
||||||
|
func (v version) String() string {
|
||||||
|
var out []string
|
||||||
|
for _, vv := range v {
|
||||||
|
out = append(out, fmt.Sprint(vv))
|
||||||
|
}
|
||||||
|
return strings.Join(out, ".")
|
||||||
|
}
|
||||||
|
|
||||||
|
// cmp compares two versions returning >0, <0 or 0
|
||||||
|
func (v version) cmp(o version) (d int) {
|
||||||
|
n := len(v)
|
||||||
|
if n > len(o) {
|
||||||
|
n = len(o)
|
||||||
|
}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
d = v[i] - o[i]
|
||||||
|
if d != 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(v) - len(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getVersion gets the version by checking the download repository passed in
|
||||||
|
func getVersion(url string) (v version, vs string, date time.Time, err error) {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return v, vs, date, err
|
||||||
|
}
|
||||||
|
defer fs.CheckClose(resp.Body, &err)
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return v, vs, date, errors.New(resp.Status)
|
||||||
|
}
|
||||||
|
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return v, vs, date, err
|
||||||
|
}
|
||||||
|
vs = strings.TrimSpace(string(bodyBytes))
|
||||||
|
if strings.HasPrefix(vs, "rclone ") {
|
||||||
|
vs = vs[7:]
|
||||||
|
}
|
||||||
|
vs = strings.TrimRight(vs, "β")
|
||||||
|
date, err = http.ParseTime(resp.Header.Get("Last-Modified"))
|
||||||
|
if err != nil {
|
||||||
|
return v, vs, date, err
|
||||||
|
}
|
||||||
|
v, err = newVersion(vs)
|
||||||
|
return v, vs, date, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the current version against available versions
|
||||||
|
func checkVersion() {
|
||||||
|
// Get Current version
|
||||||
|
currentVersion := fs.Version
|
||||||
|
currentIsGit := strings.HasSuffix(currentVersion, "-DEV")
|
||||||
|
if currentIsGit {
|
||||||
|
currentVersion = currentVersion[:len(currentVersion)-4]
|
||||||
|
}
|
||||||
|
vCurrent, err := newVersion(currentVersion)
|
||||||
|
if err != nil {
|
||||||
|
fs.Errorf(nil, "Failed to get parse version: %v", err)
|
||||||
|
}
|
||||||
|
if currentIsGit {
|
||||||
|
vCurrent = append(vCurrent, 999, 999)
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeFormat = "2006-01-02"
|
||||||
|
|
||||||
|
printVersion := func(what, url string) {
|
||||||
|
v, vs, t, err := getVersion(url + "version.txt")
|
||||||
|
if err != nil {
|
||||||
|
fs.Errorf(nil, "Failed to get rclone %s version: %v", what, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("%-8s%-13v %20s\n",
|
||||||
|
what+":",
|
||||||
|
v,
|
||||||
|
"(released "+t.Format(timeFormat)+")",
|
||||||
|
)
|
||||||
|
if v.cmp(vCurrent) > 0 {
|
||||||
|
fmt.Printf(" upgrade: %s\n", url+vs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("yours: %-13s\n", vCurrent)
|
||||||
|
printVersion(
|
||||||
|
"latest",
|
||||||
|
"https://downloads.rclone.org/",
|
||||||
|
)
|
||||||
|
printVersion(
|
||||||
|
"beta",
|
||||||
|
"https://beta.rclone.org/",
|
||||||
|
)
|
||||||
|
if currentIsGit {
|
||||||
|
fmt.Println("Your version is compiled from git so comparisons may be wrong.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package version
|
package version
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
@ -36,8 +37,71 @@ func TestVersionWorksWithoutAccessibleConfigFile(t *testing.T) {
|
|||||||
assert.NoError(t, cmd.Root.Execute())
|
assert.NoError(t, cmd.Root.Execute())
|
||||||
})
|
})
|
||||||
|
|
||||||
cmd.Root.SetArgs([]string{"--version"})
|
// This causes rclone to exit and the tests to stop!
|
||||||
assert.NotPanics(t, func() {
|
// cmd.Root.SetArgs([]string{"--version"})
|
||||||
assert.NoError(t, cmd.Root.Execute())
|
// assert.NotPanics(t, func() {
|
||||||
})
|
// assert.NoError(t, cmd.Root.Execute())
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionNew(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
in string
|
||||||
|
want version
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"v1.41", version{1, 41}, false},
|
||||||
|
{"rclone v1.41", version{1, 41}, false},
|
||||||
|
{"rclone v1.41.23", version{1, 41, 23}, false},
|
||||||
|
{"rclone v1.41.23-100", version{1, 41, 23, 100}, false},
|
||||||
|
{"rclone v1.41-100", version{1, 41, 0, 100}, false},
|
||||||
|
{"rclone v1.41.23-100-g12312a", version{1, 41, 23, 100}, false},
|
||||||
|
{"rclone v1.41-100-g12312a", version{1, 41, 0, 100}, false},
|
||||||
|
{"rclone v1.42-005-g56e1e820β", version{1, 42, 0, 5}, false},
|
||||||
|
{"rclone v1.42-005-g56e1e820-feature-branchβ", version{1, 42, 0, 5}, false},
|
||||||
|
|
||||||
|
{"v1.41s", nil, true},
|
||||||
|
{"rclone v1-41", nil, true},
|
||||||
|
{"rclone v1.41.2c3", nil, true},
|
||||||
|
{"rclone v1.41.23-100 potato", nil, true},
|
||||||
|
{"rclone 1.41-100", nil, true},
|
||||||
|
{"rclone v1.41.23-100-12312a", nil, true},
|
||||||
|
} {
|
||||||
|
what := fmt.Sprintf("in=%q", test.in)
|
||||||
|
got, err := newVersion(test.in)
|
||||||
|
if test.wantErr {
|
||||||
|
assert.Error(t, err, what)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err, what)
|
||||||
|
}
|
||||||
|
assert.Equal(t, test.want, got, what)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionCmp(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
a, b version
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{version{1}, version{1}, 0},
|
||||||
|
{version{1}, version{2}, -1},
|
||||||
|
{version{2}, version{1}, 1},
|
||||||
|
{version{2}, version{2, 1}, -1},
|
||||||
|
{version{2, 1}, version{2}, 1},
|
||||||
|
{version{2, 1}, version{2, 1}, 0},
|
||||||
|
{version{2, 1}, version{2, 2}, -1},
|
||||||
|
{version{2, 2}, version{2, 1}, 1},
|
||||||
|
} {
|
||||||
|
got := test.a.cmp(test.b)
|
||||||
|
if got < 0 {
|
||||||
|
got = -1
|
||||||
|
} else if got > 0 {
|
||||||
|
got = 1
|
||||||
|
}
|
||||||
|
assert.Equal(t, test.want, got, fmt.Sprintf("%v cmp %v", test.a, test.b))
|
||||||
|
// test the reverse
|
||||||
|
got = -test.b.cmp(test.a)
|
||||||
|
assert.Equal(t, test.want, got, fmt.Sprintf("%v cmp %v", test.b, test.a))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user