github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/cmd/version/version.go (about)

     1  // Package version provides the version command.
     2  package version
     3  
     4  import (
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"net/http"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/coreos/go-semver/semver"
    14  	"github.com/rclone/rclone/cmd"
    15  	"github.com/rclone/rclone/fs"
    16  	"github.com/rclone/rclone/fs/config/flags"
    17  	"github.com/rclone/rclone/fs/fshttp"
    18  	"github.com/spf13/cobra"
    19  )
    20  
    21  var (
    22  	check = false
    23  )
    24  
    25  func init() {
    26  	cmd.Root.AddCommand(commandDefinition)
    27  	cmdFlags := commandDefinition.Flags()
    28  	flags.BoolVarP(cmdFlags, &check, "check", "", false, "Check for new version", "")
    29  }
    30  
    31  var commandDefinition = &cobra.Command{
    32  	Use:   "version",
    33  	Short: `Show the version number.`,
    34  	Long: `
    35  Show the rclone version number, the go version, the build target
    36  OS and architecture, the runtime OS and kernel version and bitness,
    37  build tags and the type of executable (static or dynamic).
    38  
    39  For example:
    40  
    41      $ rclone version
    42      rclone v1.55.0
    43      - os/version: ubuntu 18.04 (64 bit)
    44      - os/kernel: 4.15.0-136-generic (x86_64)
    45      - os/type: linux
    46      - os/arch: amd64
    47      - go/version: go1.16
    48      - go/linking: static
    49      - go/tags: none
    50  
    51  Note: before rclone version 1.55 the os/type and os/arch lines were merged,
    52        and the "go/version" line was tagged as "go version".
    53  
    54  If you supply the --check flag, then it will do an online check to
    55  compare your version with the latest release and the latest beta.
    56  
    57      $ rclone version --check
    58      yours:  1.42.0.6
    59      latest: 1.42          (released 2018-06-16)
    60      beta:   1.42.0.5      (released 2018-06-17)
    61  
    62  Or
    63  
    64      $ rclone version --check
    65      yours:  1.41
    66      latest: 1.42          (released 2018-06-16)
    67        upgrade: https://downloads.rclone.org/v1.42
    68      beta:   1.42.0.5      (released 2018-06-17)
    69        upgrade: https://beta.rclone.org/v1.42-005-g56e1e820
    70  
    71  `,
    72  	Annotations: map[string]string{
    73  		"versionIntroduced": "v1.33",
    74  	},
    75  	Run: func(command *cobra.Command, args []string) {
    76  		ctx := context.Background()
    77  		cmd.CheckArgs(0, 0, command, args)
    78  		if check {
    79  			CheckVersion(ctx)
    80  		} else {
    81  			cmd.ShowVersion()
    82  		}
    83  	},
    84  }
    85  
    86  // strip a leading v off the string
    87  func stripV(s string) string {
    88  	if len(s) > 0 && s[0] == 'v' {
    89  		return s[1:]
    90  	}
    91  	return s
    92  }
    93  
    94  // GetVersion gets the version available for download
    95  func GetVersion(ctx context.Context, url string) (v *semver.Version, vs string, date time.Time, err error) {
    96  	resp, err := fshttp.NewClient(ctx).Get(url)
    97  	if err != nil {
    98  		return v, vs, date, err
    99  	}
   100  	defer fs.CheckClose(resp.Body, &err)
   101  	if resp.StatusCode != http.StatusOK {
   102  		return v, vs, date, errors.New(resp.Status)
   103  	}
   104  	bodyBytes, err := io.ReadAll(resp.Body)
   105  	if err != nil {
   106  		return v, vs, date, err
   107  	}
   108  	vs = strings.TrimSpace(string(bodyBytes))
   109  	vs = strings.TrimPrefix(vs, "rclone ")
   110  	vs = strings.TrimRight(vs, "β")
   111  	date, err = http.ParseTime(resp.Header.Get("Last-Modified"))
   112  	if err != nil {
   113  		return v, vs, date, err
   114  	}
   115  	v, err = semver.NewVersion(stripV(vs))
   116  	return v, vs, date, err
   117  }
   118  
   119  // CheckVersion checks the installed version against available downloads
   120  func CheckVersion(ctx context.Context) {
   121  	vCurrent, err := semver.NewVersion(stripV(fs.Version))
   122  	if err != nil {
   123  		fs.Errorf(nil, "Failed to parse version: %v", err)
   124  	}
   125  	const timeFormat = "2006-01-02"
   126  
   127  	printVersion := func(what, url string) {
   128  		v, vs, t, err := GetVersion(ctx, url+"version.txt")
   129  		if err != nil {
   130  			fs.Errorf(nil, "Failed to get rclone %s version: %v", what, err)
   131  			return
   132  		}
   133  		fmt.Printf("%-8s%-40v %20s\n",
   134  			what+":",
   135  			v,
   136  			"(released "+t.Format(timeFormat)+")",
   137  		)
   138  		if v.Compare(*vCurrent) > 0 {
   139  			fmt.Printf("  upgrade: %s\n", url+vs)
   140  		}
   141  	}
   142  	fmt.Printf("yours:  %-13s\n", vCurrent)
   143  	printVersion(
   144  		"latest",
   145  		"https://downloads.rclone.org/",
   146  	)
   147  	printVersion(
   148  		"beta",
   149  		"https://beta.rclone.org/",
   150  	)
   151  	if strings.HasSuffix(fs.Version, "-DEV") {
   152  		fmt.Println("Your version is compiled from git so comparisons may be wrong.")
   153  	}
   154  }