Merge branch 'rclone:master' into mct-install-set-modes-mac
This commit is contained in:
commit
eab83baf6d
30 changed files with 194 additions and 103 deletions
8
Makefile
8
Makefile
|
@ -245,18 +245,18 @@ retag:
|
||||||
startdev:
|
startdev:
|
||||||
@echo "Version is $(VERSION)"
|
@echo "Version is $(VERSION)"
|
||||||
@echo "Next version is $(NEXT_VERSION)"
|
@echo "Next version is $(NEXT_VERSION)"
|
||||||
echo -e "package fs\n\n// Version of rclone\nvar Version = \"$(NEXT_VERSION)-DEV\"\n" | gofmt > fs/version.go
|
echo -e "package fs\n\n// VersionTag of rclone\nvar VersionTag = \"$(NEXT_VERSION)\"\n" | gofmt > fs/versiontag.go
|
||||||
echo -n "$(NEXT_VERSION)" > docs/layouts/partials/version.html
|
echo -n "$(NEXT_VERSION)" > docs/layouts/partials/version.html
|
||||||
echo "$(NEXT_VERSION)" > VERSION
|
echo "$(NEXT_VERSION)" > VERSION
|
||||||
git commit -m "Start $(NEXT_VERSION)-DEV development" fs/version.go VERSION docs/layouts/partials/version.html
|
git commit -m "Start $(NEXT_VERSION)-DEV development" fs/versiontag.go VERSION docs/layouts/partials/version.html
|
||||||
|
|
||||||
startstable:
|
startstable:
|
||||||
@echo "Version is $(VERSION)"
|
@echo "Version is $(VERSION)"
|
||||||
@echo "Next stable version is $(NEXT_PATCH_VERSION)"
|
@echo "Next stable version is $(NEXT_PATCH_VERSION)"
|
||||||
echo -e "package fs\n\n// Version of rclone\nvar Version = \"$(NEXT_PATCH_VERSION)-DEV\"\n" | gofmt > fs/version.go
|
echo -e "package fs\n\n// VersionTag of rclone\nvar VersionTag = \"$(NEXT_PATCH_VERSION)\"\n" | gofmt > fs/versiontag.go
|
||||||
echo -n "$(NEXT_PATCH_VERSION)" > docs/layouts/partials/version.html
|
echo -n "$(NEXT_PATCH_VERSION)" > docs/layouts/partials/version.html
|
||||||
echo "$(NEXT_PATCH_VERSION)" > VERSION
|
echo "$(NEXT_PATCH_VERSION)" > VERSION
|
||||||
git commit -m "Start $(NEXT_PATCH_VERSION)-DEV development" fs/version.go VERSION docs/layouts/partials/version.html
|
git commit -m "Start $(NEXT_PATCH_VERSION)-DEV development" fs/versiontag.go VERSION docs/layouts/partials/version.html
|
||||||
|
|
||||||
winzip:
|
winzip:
|
||||||
zip -9 rclone-$(TAG).zip rclone.exe
|
zip -9 rclone-$(TAG).zip rclone.exe
|
||||||
|
|
|
@ -515,7 +515,7 @@ func (f *Fs) setChunkNameFormat(pattern string) error {
|
||||||
|
|
||||||
strRegex := regexp.QuoteMeta(pattern)
|
strRegex := regexp.QuoteMeta(pattern)
|
||||||
strRegex = reHashes.ReplaceAllLiteralString(strRegex, reDataOrCtrl)
|
strRegex = reHashes.ReplaceAllLiteralString(strRegex, reDataOrCtrl)
|
||||||
strRegex = strings.Replace(strRegex, "\\*", mainNameRegStr, -1)
|
strRegex = strings.ReplaceAll(strRegex, "\\*", mainNameRegStr)
|
||||||
strRegex = fmt.Sprintf("^%s(?:%s|%s)?$", strRegex, tempSuffixRegStr, tempSuffixRegOld)
|
strRegex = fmt.Sprintf("^%s(?:%s|%s)?$", strRegex, tempSuffixRegStr, tempSuffixRegOld)
|
||||||
f.nameRegexp = regexp.MustCompile(strRegex)
|
f.nameRegexp = regexp.MustCompile(strRegex)
|
||||||
|
|
||||||
|
@ -524,7 +524,7 @@ func (f *Fs) setChunkNameFormat(pattern string) error {
|
||||||
if numDigits > 1 {
|
if numDigits > 1 {
|
||||||
fmtDigits = fmt.Sprintf("%%0%dd", numDigits)
|
fmtDigits = fmt.Sprintf("%%0%dd", numDigits)
|
||||||
}
|
}
|
||||||
strFmt := strings.Replace(pattern, "%", "%%", -1)
|
strFmt := strings.ReplaceAll(pattern, "%", "%%")
|
||||||
strFmt = strings.Replace(strFmt, "*", "%s", 1)
|
strFmt = strings.Replace(strFmt, "*", "%s", 1)
|
||||||
f.dataNameFmt = reHashes.ReplaceAllLiteralString(strFmt, fmtDigits)
|
f.dataNameFmt = reHashes.ReplaceAllLiteralString(strFmt, fmtDigits)
|
||||||
f.ctrlNameFmt = reHashes.ReplaceAllLiteralString(strFmt, "_%s")
|
f.ctrlNameFmt = reHashes.ReplaceAllLiteralString(strFmt, "_%s")
|
||||||
|
|
|
@ -70,7 +70,7 @@ const (
|
||||||
// 1<<18 is the minimum size supported by the Google uploader, and there is no maximum.
|
// 1<<18 is the minimum size supported by the Google uploader, and there is no maximum.
|
||||||
minChunkSize = fs.SizeSuffix(googleapi.MinUploadChunkSize)
|
minChunkSize = fs.SizeSuffix(googleapi.MinUploadChunkSize)
|
||||||
defaultChunkSize = 8 * fs.Mebi
|
defaultChunkSize = 8 * fs.Mebi
|
||||||
partialFields = "id,name,size,md5Checksum,trashed,explicitlyTrashed,modifiedTime,createdTime,mimeType,parents,webViewLink,shortcutDetails,exportLinks"
|
partialFields = "id,name,size,md5Checksum,trashed,explicitlyTrashed,modifiedTime,createdTime,mimeType,parents,webViewLink,shortcutDetails,exportLinks,resourceKey"
|
||||||
listRGrouping = 50 // number of IDs to search at once when using ListR
|
listRGrouping = 50 // number of IDs to search at once when using ListR
|
||||||
listRInputBuffer = 1000 // size of input buffer when using ListR
|
listRInputBuffer = 1000 // size of input buffer when using ListR
|
||||||
defaultXDGIcon = "text-html"
|
defaultXDGIcon = "text-html"
|
||||||
|
@ -660,6 +660,7 @@ type baseObject struct {
|
||||||
mimeType string // The object MIME type
|
mimeType string // The object MIME type
|
||||||
bytes int64 // size of the object
|
bytes int64 // size of the object
|
||||||
parents []string // IDs of the parent directories
|
parents []string // IDs of the parent directories
|
||||||
|
resourceKey *string // resourceKey is needed for link shared objects
|
||||||
}
|
}
|
||||||
type documentObject struct {
|
type documentObject struct {
|
||||||
baseObject
|
baseObject
|
||||||
|
@ -829,8 +830,8 @@ func (f *Fs) list(ctx context.Context, dirIDs []string, title string, directorie
|
||||||
if title != "" {
|
if title != "" {
|
||||||
searchTitle := f.opt.Enc.FromStandardName(title)
|
searchTitle := f.opt.Enc.FromStandardName(title)
|
||||||
// Escaping the backslash isn't documented but seems to work
|
// Escaping the backslash isn't documented but seems to work
|
||||||
searchTitle = strings.Replace(searchTitle, `\`, `\\`, -1)
|
searchTitle = strings.ReplaceAll(searchTitle, `\`, `\\`)
|
||||||
searchTitle = strings.Replace(searchTitle, `'`, `\'`, -1)
|
searchTitle = strings.ReplaceAll(searchTitle, `'`, `\'`)
|
||||||
|
|
||||||
var titleQuery bytes.Buffer
|
var titleQuery bytes.Buffer
|
||||||
_, _ = fmt.Fprintf(&titleQuery, "(name='%s'", searchTitle)
|
_, _ = fmt.Fprintf(&titleQuery, "(name='%s'", searchTitle)
|
||||||
|
@ -1319,12 +1320,16 @@ func (f *Fs) newRegularObject(remote string, info *drive.File) fs.Object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &Object{
|
o := &Object{
|
||||||
baseObject: f.newBaseObject(remote, info),
|
baseObject: f.newBaseObject(remote, info),
|
||||||
url: fmt.Sprintf("%sfiles/%s?alt=media", f.svc.BasePath, actualID(info.Id)),
|
url: fmt.Sprintf("%sfiles/%s?alt=media", f.svc.BasePath, actualID(info.Id)),
|
||||||
md5sum: strings.ToLower(info.Md5Checksum),
|
md5sum: strings.ToLower(info.Md5Checksum),
|
||||||
v2Download: f.opt.V2DownloadMinSize != -1 && info.Size >= int64(f.opt.V2DownloadMinSize),
|
v2Download: f.opt.V2DownloadMinSize != -1 && info.Size >= int64(f.opt.V2DownloadMinSize),
|
||||||
}
|
}
|
||||||
|
if info.ResourceKey != "" {
|
||||||
|
o.resourceKey = &info.ResourceKey
|
||||||
|
}
|
||||||
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
// newDocumentObject creates an fs.Object for a google docs drive.File
|
// newDocumentObject creates an fs.Object for a google docs drive.File
|
||||||
|
@ -2429,11 +2434,12 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
||||||
|
|
||||||
var info *drive.File
|
var info *drive.File
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
info, err = f.svc.Files.Copy(id, createInfo).
|
copy := f.svc.Files.Copy(id, createInfo).
|
||||||
Fields(partialFields).
|
Fields(partialFields).
|
||||||
SupportsAllDrives(true).
|
SupportsAllDrives(true).
|
||||||
KeepRevisionForever(f.opt.KeepRevisionForever).
|
KeepRevisionForever(f.opt.KeepRevisionForever)
|
||||||
Context(ctx).Do()
|
srcObj.addResourceKey(copy.Header())
|
||||||
|
info, err = copy.Context(ctx).Do()
|
||||||
return f.shouldRetry(ctx, err)
|
return f.shouldRetry(ctx, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -3530,6 +3536,14 @@ func (o *baseObject) Storable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addResourceKey adds a X-Goog-Drive-Resource-Keys header for this
|
||||||
|
// object if required.
|
||||||
|
func (o *baseObject) addResourceKey(header http.Header) {
|
||||||
|
if o.resourceKey != nil {
|
||||||
|
header.Add("X-Goog-Drive-Resource-Keys", fmt.Sprintf("%s/%s", o.id, *o.resourceKey))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// httpResponse gets an http.Response object for the object
|
// httpResponse gets an http.Response object for the object
|
||||||
// using the url and method passed in
|
// using the url and method passed in
|
||||||
func (o *baseObject) httpResponse(ctx context.Context, url, method string, options []fs.OpenOption) (req *http.Request, res *http.Response, err error) {
|
func (o *baseObject) httpResponse(ctx context.Context, url, method string, options []fs.OpenOption) (req *http.Request, res *http.Response, err error) {
|
||||||
|
@ -3545,6 +3559,7 @@ func (o *baseObject) httpResponse(ctx context.Context, url, method string, optio
|
||||||
// Don't supply range requests for 0 length objects as they always fail
|
// Don't supply range requests for 0 length objects as they always fail
|
||||||
delete(req.Header, "Range")
|
delete(req.Header, "Range")
|
||||||
}
|
}
|
||||||
|
o.addResourceKey(req.Header)
|
||||||
err = o.fs.pacer.Call(func() (bool, error) {
|
err = o.fs.pacer.Call(func() (bool, error) {
|
||||||
res, err = o.fs.client.Do(req)
|
res, err = o.fs.client.Do(req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
|
@ -562,7 +562,7 @@ func (f *Fs) list(ctx context.Context, filter api.SearchFilter, fn listFn) (err
|
||||||
for i := range items {
|
for i := range items {
|
||||||
item := &result.MediaItems[i]
|
item := &result.MediaItems[i]
|
||||||
remote := item.Filename
|
remote := item.Filename
|
||||||
remote = strings.Replace(remote, "/", "/", -1)
|
remote = strings.ReplaceAll(remote, "/", "/")
|
||||||
err = fn(remote, item, false)
|
err = fn(remote, item, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -119,7 +119,7 @@ func (f *Fs) getCredentials(ctx context.Context) (err error) {
|
||||||
defer fs.CheckClose(resp.Body, &err)
|
defer fs.CheckClose(resp.Body, &err)
|
||||||
if resp.StatusCode < 200 || resp.StatusCode > 299 {
|
if resp.StatusCode < 200 || resp.StatusCode > 299 {
|
||||||
body, _ := ioutil.ReadAll(resp.Body)
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
bodyStr := strings.TrimSpace(strings.Replace(string(body), "\n", " ", -1))
|
bodyStr := strings.TrimSpace(strings.ReplaceAll(string(body), "\n", " "))
|
||||||
return fmt.Errorf("failed to get credentials: %s: %s", resp.Status, bodyStr)
|
return fmt.Errorf("failed to get credentials: %s: %s", resp.Status, bodyStr)
|
||||||
}
|
}
|
||||||
decoder := json.NewDecoder(resp.Body)
|
decoder := json.NewDecoder(resp.Body)
|
||||||
|
|
|
@ -191,7 +191,7 @@ machines.`)
|
||||||
m.Set("auth_code", "")
|
m.Set("auth_code", "")
|
||||||
return fs.ConfigGoto("legacy_do_auth")
|
return fs.ConfigGoto("legacy_do_auth")
|
||||||
case "legacy_auth_code":
|
case "legacy_auth_code":
|
||||||
authCode := strings.Replace(config.Result, "-", "", -1) // remove any "-" contained in the code so we have a 6 digit number
|
authCode := strings.ReplaceAll(config.Result, "-", "") // remove any "-" contained in the code so we have a 6 digit number
|
||||||
m.Set("auth_code", authCode)
|
m.Set("auth_code", authCode)
|
||||||
return fs.ConfigGoto("legacy_do_auth")
|
return fs.ConfigGoto("legacy_do_auth")
|
||||||
case "legacy_do_auth":
|
case "legacy_do_auth":
|
||||||
|
@ -649,7 +649,7 @@ func errorHandler(resp *http.Response) error {
|
||||||
|
|
||||||
// Jottacloud wants '+' to be URL encoded even though the RFC states it's not reserved
|
// Jottacloud wants '+' to be URL encoded even though the RFC states it's not reserved
|
||||||
func urlPathEscape(in string) string {
|
func urlPathEscape(in string) string {
|
||||||
return strings.Replace(rest.URLPathEscape(in), "+", "%2B", -1)
|
return strings.ReplaceAll(rest.URLPathEscape(in), "+", "%2B")
|
||||||
}
|
}
|
||||||
|
|
||||||
// filePathRaw returns an unescaped file path (f.root, file)
|
// filePathRaw returns an unescaped file path (f.root, file)
|
||||||
|
|
|
@ -1294,7 +1294,7 @@ For more info visit https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview
|
||||||
|
|
||||||
Note that this ACL is applied when server-side copying objects as S3
|
Note that this ACL is applied when server-side copying objects as S3
|
||||||
doesn't copy the ACL from the source but rather writes a fresh one.`,
|
doesn't copy the ACL from the source but rather writes a fresh one.`,
|
||||||
Provider: "!Storj",
|
Provider: "!Storj,Cloudflare",
|
||||||
Examples: []fs.OptionExample{{
|
Examples: []fs.OptionExample{{
|
||||||
Value: "default",
|
Value: "default",
|
||||||
Help: "Owner gets Full_CONTROL.\nNo one else has access rights (default).",
|
Help: "Owner gets Full_CONTROL.\nNo one else has access rights (default).",
|
||||||
|
@ -2966,7 +2966,7 @@ func (f *Fs) Precision() time.Duration {
|
||||||
// pathEscape escapes s as for a URL path. It uses rest.URLPathEscape
|
// pathEscape escapes s as for a URL path. It uses rest.URLPathEscape
|
||||||
// but also escapes '+' for S3 and Digital Ocean spaces compatibility
|
// but also escapes '+' for S3 and Digital Ocean spaces compatibility
|
||||||
func pathEscape(s string) string {
|
func pathEscape(s string) string {
|
||||||
return strings.Replace(rest.URLPathEscape(s), "+", "%2B", -1)
|
return strings.ReplaceAll(rest.URLPathEscape(s), "+", "%2B")
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy does a server-side copy
|
// copy does a server-side copy
|
||||||
|
|
|
@ -1311,7 +1311,7 @@ var shellEscapeRegex = regexp.MustCompile("[^A-Za-z0-9_.,:/\\@\u0080-\uFFFFFFFF\
|
||||||
// when sending it to a shell.
|
// when sending it to a shell.
|
||||||
func shellEscape(str string) string {
|
func shellEscape(str string) string {
|
||||||
safe := shellEscapeRegex.ReplaceAllString(str, `\$0`)
|
safe := shellEscapeRegex.ReplaceAllString(str, `\$0`)
|
||||||
return strings.Replace(safe, "\n", "'\n'", -1)
|
return strings.ReplaceAll(safe, "\n", "'\n'")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts a byte array from the SSH session returned by
|
// Converts a byte array from the SSH session returned by
|
||||||
|
|
|
@ -173,8 +173,8 @@ func buildZip(dir string) string {
|
||||||
func buildDebAndRpm(dir, version, goarch string) []string {
|
func buildDebAndRpm(dir, version, goarch string) []string {
|
||||||
// Make internal version number acceptable to .deb and .rpm
|
// Make internal version number acceptable to .deb and .rpm
|
||||||
pkgVersion := version[1:]
|
pkgVersion := version[1:]
|
||||||
pkgVersion = strings.Replace(pkgVersion, "β", "-beta", -1)
|
pkgVersion = strings.ReplaceAll(pkgVersion, "β", "-beta")
|
||||||
pkgVersion = strings.Replace(pkgVersion, "-", ".", -1)
|
pkgVersion = strings.ReplaceAll(pkgVersion, "-", ".")
|
||||||
nfpmArch, ok := goarchToNfpm[goarch]
|
nfpmArch, ok := goarchToNfpm[goarch]
|
||||||
if !ok {
|
if !ok {
|
||||||
nfpmArch = goarch
|
nfpmArch = goarch
|
||||||
|
|
|
@ -79,7 +79,7 @@ rclone.org website.`,
|
||||||
var description = map[string]string{}
|
var description = map[string]string{}
|
||||||
var addDescription func(root *cobra.Command)
|
var addDescription func(root *cobra.Command)
|
||||||
addDescription = func(root *cobra.Command) {
|
addDescription = func(root *cobra.Command) {
|
||||||
name := strings.Replace(root.CommandPath(), " ", "_", -1) + ".md"
|
name := strings.ReplaceAll(root.CommandPath(), " ", "_") + ".md"
|
||||||
description[name] = root.Short
|
description[name] = root.Short
|
||||||
for _, c := range root.Commands() {
|
for _, c := range root.Commands() {
|
||||||
addDescription(c)
|
addDescription(c)
|
||||||
|
@ -93,11 +93,11 @@ rclone.org website.`,
|
||||||
base := strings.TrimSuffix(name, path.Ext(name))
|
base := strings.TrimSuffix(name, path.Ext(name))
|
||||||
data := frontmatter{
|
data := frontmatter{
|
||||||
Date: now,
|
Date: now,
|
||||||
Title: strings.Replace(base, "_", " ", -1),
|
Title: strings.ReplaceAll(base, "_", " "),
|
||||||
Description: description[name],
|
Description: description[name],
|
||||||
Slug: base,
|
Slug: base,
|
||||||
URL: "/commands/" + strings.ToLower(base) + "/",
|
URL: "/commands/" + strings.ToLower(base) + "/",
|
||||||
Source: strings.Replace(strings.Replace(base, "rclone", "cmd", -1), "_", "/", -1) + "/",
|
Source: strings.ReplaceAll(strings.ReplaceAll(base, "rclone", "cmd"), "_", "/") + "/",
|
||||||
}
|
}
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
err := frontmatterTemplate.Execute(&buf, data)
|
err := frontmatterTemplate.Execute(&buf, data)
|
||||||
|
|
|
@ -290,7 +290,7 @@ func list(ctx context.Context) error {
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("bad JSON")
|
return errors.New("bad JSON")
|
||||||
}
|
}
|
||||||
fmt.Printf("### %s: %s {#%s}\n\n", info["Path"], info["Title"], strings.Replace(info["Path"].(string), "/", "-", -1))
|
fmt.Printf("### %s: %s {#%s}\n\n", info["Path"], info["Title"], strings.ReplaceAll(info["Path"].(string), "/", "-"))
|
||||||
fmt.Printf("%s\n\n", info["Help"])
|
fmt.Printf("%s\n\n", info["Help"])
|
||||||
if authRequired := info["AuthRequired"]; authRequired != nil {
|
if authRequired := info["AuthRequired"]; authRequired != nil {
|
||||||
if authRequired.(bool) {
|
if authRequired.(bool) {
|
||||||
|
|
|
@ -43,7 +43,7 @@ var shellUnEscapeRegex = regexp.MustCompile(`\\(.)`)
|
||||||
|
|
||||||
// Unescape a string that was escaped by rclone
|
// Unescape a string that was escaped by rclone
|
||||||
func shellUnEscape(str string) string {
|
func shellUnEscape(str string) string {
|
||||||
str = strings.Replace(str, "'\n'", "\n", -1)
|
str = strings.ReplaceAll(str, "'\n'", "\n")
|
||||||
str = shellUnEscapeRegex.ReplaceAllString(str, `$1`)
|
str = shellUnEscapeRegex.ReplaceAllString(str, `$1`)
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
|
@ -593,3 +593,6 @@ put them back in again.` >}}
|
||||||
* Kaspian <34658474+KaspianDev@users.noreply.github.com>
|
* Kaspian <34658474+KaspianDev@users.noreply.github.com>
|
||||||
* Werner <EvilOlaf@users.noreply.github.com>
|
* Werner <EvilOlaf@users.noreply.github.com>
|
||||||
* Hugal31 <hugo.laloge@gmail.com>
|
* Hugal31 <hugo.laloge@gmail.com>
|
||||||
|
* Christian Galo <36752715+cgalo5758@users.noreply.github.com>
|
||||||
|
* Erik van Velzen <erik@evanv.nl>
|
||||||
|
* Derek Battams <derek@battams.ca>
|
||||||
|
|
|
@ -3,11 +3,11 @@ title: "Install"
|
||||||
description: "Rclone Installation"
|
description: "Rclone Installation"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Install #
|
# Install
|
||||||
|
|
||||||
Rclone is a Go program and comes as a single binary file.
|
Rclone is a Go program and comes as a single binary file.
|
||||||
|
|
||||||
## Quickstart ##
|
## Quickstart
|
||||||
|
|
||||||
* [Download](/downloads/) the relevant binary.
|
* [Download](/downloads/) the relevant binary.
|
||||||
* Extract the `rclone` executable, `rclone.exe` on Windows, from the archive.
|
* Extract the `rclone` executable, `rclone.exe` on Windows, from the archive.
|
||||||
|
@ -22,7 +22,7 @@ run `rclone -h`.
|
||||||
Already installed rclone can be easily updated to the latest version
|
Already installed rclone can be easily updated to the latest version
|
||||||
using the [rclone selfupdate](/commands/rclone_selfupdate/) command.
|
using the [rclone selfupdate](/commands/rclone_selfupdate/) command.
|
||||||
|
|
||||||
## Script installation ##
|
## Script installation
|
||||||
|
|
||||||
To install rclone on Linux/macOS/BSD systems, run:
|
To install rclone on Linux/macOS/BSD systems, run:
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ For beta installation, run:
|
||||||
Note that this script checks the version of rclone installed first and
|
Note that this script checks the version of rclone installed first and
|
||||||
won't re-download if not needed.
|
won't re-download if not needed.
|
||||||
|
|
||||||
## Linux installation from precompiled binary ##
|
## Linux installation from precompiled binary
|
||||||
|
|
||||||
Fetch and unpack
|
Fetch and unpack
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ Run `rclone config` to setup. See [rclone config docs](/docs/) for more details.
|
||||||
|
|
||||||
rclone config
|
rclone config
|
||||||
|
|
||||||
## macOS installation with brew ##
|
## macOS installation with brew
|
||||||
|
|
||||||
brew install rclone
|
brew install rclone
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ NOTE: This version of rclone will not support `mount` any more (see
|
||||||
on macOS, either install a precompiled binary or enable the relevant option
|
on macOS, either install a precompiled binary or enable the relevant option
|
||||||
when [installing from source](#install-from-source).
|
when [installing from source](#install-from-source).
|
||||||
|
|
||||||
## macOS installation from precompiled binary, using curl ##
|
## macOS installation from precompiled binary, using curl
|
||||||
|
|
||||||
To avoid problems with macOS gatekeeper enforcing the binary to be signed and
|
To avoid problems with macOS gatekeeper enforcing the binary to be signed and
|
||||||
notarized it is enough to download with `curl`.
|
notarized it is enough to download with `curl`.
|
||||||
|
@ -96,20 +96,20 @@ Run `rclone config` to setup. See [rclone config docs](/docs/) for more details.
|
||||||
|
|
||||||
rclone config
|
rclone config
|
||||||
|
|
||||||
## macOS installation from precompiled binary, using a web browser ##
|
## macOS installation from precompiled binary, using a web browser
|
||||||
|
|
||||||
When downloading a binary with a web browser, the browser will set the macOS
|
When downloading a binary with a web browser, the browser will set the macOS
|
||||||
gatekeeper quarantine attribute. Starting from Catalina, when attempting to run
|
gatekeeper quarantine attribute. Starting from Catalina, when attempting to run
|
||||||
`rclone`, a pop-up will appear saying:
|
`rclone`, a pop-up will appear saying:
|
||||||
|
|
||||||
“rclone” cannot be opened because the developer cannot be verified.
|
"rclone" cannot be opened because the developer cannot be verified.
|
||||||
macOS cannot verify that this app is free from malware.
|
macOS cannot verify that this app is free from malware.
|
||||||
|
|
||||||
The simplest fix is to run
|
The simplest fix is to run
|
||||||
|
|
||||||
xattr -d com.apple.quarantine rclone
|
xattr -d com.apple.quarantine rclone
|
||||||
|
|
||||||
## Install with docker ##
|
## Install with docker
|
||||||
|
|
||||||
The rclone maintains a [docker image for rclone](https://hub.docker.com/r/rclone/rclone).
|
The rclone maintains a [docker image for rclone](https://hub.docker.com/r/rclone/rclone).
|
||||||
These images are autobuilt by docker hub from the rclone source based
|
These images are autobuilt by docker hub from the rclone source based
|
||||||
|
@ -188,39 +188,93 @@ ls ~/data/mount
|
||||||
kill %1
|
kill %1
|
||||||
```
|
```
|
||||||
|
|
||||||
## Install from source ##
|
## Install from source
|
||||||
|
|
||||||
Make sure you have at least [Go](https://golang.org/) go1.16
|
Make sure you have git and [Go](https://golang.org/) installed.
|
||||||
installed. [Download go](https://golang.org/dl/) if necessary. The
|
Go version 1.16 or newer is required, latest release is recommended.
|
||||||
latest release is recommended. Then
|
You can get it from your package manager, or download it from
|
||||||
|
[golang.org/dl](https://golang.org/dl/). Then you can run the following:
|
||||||
|
|
||||||
```sh
|
```
|
||||||
git clone https://github.com/rclone/rclone.git
|
git clone https://github.com/rclone/rclone.git
|
||||||
cd rclone
|
cd rclone
|
||||||
go build
|
go build
|
||||||
# If on macOS and mount is wanted, instead run: make GOTAGS=cmount
|
|
||||||
./rclone version
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This will leave you a checked out version of rclone you can modify and
|
This will check out the rclone source in subfolder rclone, which you can later
|
||||||
send pull requests with. If you use `make` instead of `go build` then
|
modify and send pull requests with. Then it will build the rclone executable
|
||||||
the rclone build will have the correct version information in it.
|
in the same folder. As an initial check you can now run `./rclone version`
|
||||||
|
(`.\rclone version` on Windows).
|
||||||
|
|
||||||
You can also build the latest stable rclone with:
|
Note that on macOS and Windows the [mount](https://rclone.org/commands/rclone_mount/)
|
||||||
|
command will not be available unless you specify additional build tag `cmount`.
|
||||||
|
|
||||||
|
```
|
||||||
|
go build -tags cmount
|
||||||
|
```
|
||||||
|
|
||||||
|
This assumes you have a GCC compatible C compiler (GCC or Clang) in your PATH,
|
||||||
|
as it uses [cgo](https://pkg.go.dev/cmd/cgo). But on Windows, the
|
||||||
|
[cgofuse](https://github.com/winfsp/cgofuse) library that the cmount
|
||||||
|
implementation is based on, also supports building
|
||||||
|
[without cgo](https://github.com/golang/go/wiki/WindowsDLLs), i.e. by setting
|
||||||
|
environment variable CGO_ENABLED to value 0 (static linking). This is how the
|
||||||
|
official Windows release of rclone is being built, starting with version 1.59.
|
||||||
|
It is still possible to build with cgo on Windows as well, by using the MinGW
|
||||||
|
port of GCC, e.g. by installing it in a [MSYS2](https://www.msys2.org)
|
||||||
|
distribution (make sure you install it in the classic mingw64 subsystem, the
|
||||||
|
ucrt64 version is not compatible).
|
||||||
|
|
||||||
|
Additionally, on Windows, you must install the third party utility
|
||||||
|
[WinFsp](http://www.secfs.net/winfsp/), with the "Developer" feature selected.
|
||||||
|
If building with cgo, you must also set environment variable CPATH pointing to
|
||||||
|
the fuse include directory within the WinFsp installation
|
||||||
|
(normally `C:\Program Files (x86)\WinFsp\inc\fuse`).
|
||||||
|
|
||||||
|
You may also add arguments `-ldflags -s` (with or without `-tags cmount`),
|
||||||
|
to omit symbol table and debug information, making the executable file smaller,
|
||||||
|
and `-trimpath` to remove references to local file system paths. This is how
|
||||||
|
the official rclone releases are built.
|
||||||
|
|
||||||
|
```
|
||||||
|
go build -trimpath -ldflags -s -tags cmount
|
||||||
|
```
|
||||||
|
|
||||||
|
Instead of executing the `go build` command directly, you can run it via the
|
||||||
|
Makefile, which also sets version information and copies the resulting rclone
|
||||||
|
executable into your GOPATH bin folder (`$(go env GOPATH)/bin`, which
|
||||||
|
corresponds to `~/go/bin/rclone` by default).
|
||||||
|
|
||||||
|
```
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
To include mount command on macOS and Windows with Makefile build:
|
||||||
|
|
||||||
|
```
|
||||||
|
make GOTAGS=cmount
|
||||||
|
```
|
||||||
|
|
||||||
|
As an alternative you can download the source, build and install rclone in one
|
||||||
|
operation, as a regular Go package. The source will be stored it in the Go
|
||||||
|
module cache, and the resulting executable will be in your GOPATH bin folder
|
||||||
|
(`$(go env GOPATH)/bin`, which corresponds to `~/go/bin/rclone` by default).
|
||||||
|
|
||||||
|
With Go version 1.17 or newer:
|
||||||
|
|
||||||
|
```
|
||||||
|
go install github.com/rclone/rclone@latest
|
||||||
|
```
|
||||||
|
|
||||||
|
With Go versions older than 1.17 (do **not** use the `-u` flag, it causes Go to
|
||||||
|
try to update the dependencies that rclone uses and sometimes these don't work
|
||||||
|
with the current version):
|
||||||
|
|
||||||
|
```
|
||||||
go get github.com/rclone/rclone
|
go get github.com/rclone/rclone
|
||||||
|
```
|
||||||
|
|
||||||
or the latest version (equivalent to the beta) with
|
## Installation with Ansible
|
||||||
|
|
||||||
go get github.com/rclone/rclone@master
|
|
||||||
|
|
||||||
These will build the binary in `$(go env GOPATH)/bin`
|
|
||||||
(`~/go/bin/rclone` by default) after downloading the source to the go
|
|
||||||
module cache. Note - do **not** use the `-u` flag here. This causes go
|
|
||||||
to try to update the dependencies that rclone uses and sometimes these
|
|
||||||
don't work with the current version of rclone.
|
|
||||||
|
|
||||||
## Installation with Ansible ##
|
|
||||||
|
|
||||||
This can be done with [Stefan Weichinger's ansible
|
This can be done with [Stefan Weichinger's ansible
|
||||||
role](https://github.com/stefangweichinger/ansible-rclone).
|
role](https://github.com/stefangweichinger/ansible-rclone).
|
||||||
|
@ -236,7 +290,7 @@ Instructions
|
||||||
- rclone
|
- rclone
|
||||||
```
|
```
|
||||||
|
|
||||||
## Portable installation ##
|
## Portable installation
|
||||||
|
|
||||||
As mentioned [above](https://rclone.org/install/#quickstart), rclone is single
|
As mentioned [above](https://rclone.org/install/#quickstart), rclone is single
|
||||||
executable (`rclone`, or `rclone.exe` on Windows) that you can download as a
|
executable (`rclone`, or `rclone.exe` on Windows) that you can download as a
|
||||||
|
@ -314,7 +368,7 @@ the [PsExec](https://docs.microsoft.com/en-us/sysinternals/downloads/psexec)
|
||||||
utility from Microsoft's Sysinternals suite, which takes option `-s` to
|
utility from Microsoft's Sysinternals suite, which takes option `-s` to
|
||||||
execute commands as the `SYSTEM` user.
|
execute commands as the `SYSTEM` user.
|
||||||
|
|
||||||
#### Start from Startup folder ###
|
#### Start from Startup folder
|
||||||
|
|
||||||
To quickly execute an rclone command you can simply create a standard
|
To quickly execute an rclone command you can simply create a standard
|
||||||
Windows Explorer shortcut for the complete rclone command you want to run. If you
|
Windows Explorer shortcut for the complete rclone command you want to run. If you
|
||||||
|
@ -329,7 +383,7 @@ functionality to set it to run as different user, or to set conditions or
|
||||||
actions on certain events. Setting up a scheduled task as described below
|
actions on certain events. Setting up a scheduled task as described below
|
||||||
will often give you better results.
|
will often give you better results.
|
||||||
|
|
||||||
#### Start from Task Scheduler ###
|
#### Start from Task Scheduler
|
||||||
|
|
||||||
Task Scheduler is an administrative tool built into Windows, and it can be used to
|
Task Scheduler is an administrative tool built into Windows, and it can be used to
|
||||||
configure rclone to be started automatically in a highly configurable way, e.g.
|
configure rclone to be started automatically in a highly configurable way, e.g.
|
||||||
|
@ -339,14 +393,14 @@ be available to all users it can run as the `SYSTEM` user.
|
||||||
For technical information, see
|
For technical information, see
|
||||||
https://docs.microsoft.com/windows/win32/taskschd/task-scheduler-start-page.
|
https://docs.microsoft.com/windows/win32/taskschd/task-scheduler-start-page.
|
||||||
|
|
||||||
#### Run as service ###
|
#### Run as service
|
||||||
|
|
||||||
For running rclone at system startup, you can create a Windows service that executes
|
For running rclone at system startup, you can create a Windows service that executes
|
||||||
your rclone command, as an alternative to scheduled task configured to run at startup.
|
your rclone command, as an alternative to scheduled task configured to run at startup.
|
||||||
|
|
||||||
##### Mount command built-in service integration ####
|
##### Mount command built-in service integration
|
||||||
|
|
||||||
For mount commands, Rclone has a built-in Windows service integration via the third-party
|
For mount commands, rclone has a built-in Windows service integration via the third-party
|
||||||
WinFsp library it uses. Registering as a regular Windows service easy, as you just have to
|
WinFsp library it uses. Registering as a regular Windows service easy, as you just have to
|
||||||
execute the built-in PowerShell command `New-Service` (requires administrative privileges).
|
execute the built-in PowerShell command `New-Service` (requires administrative privileges).
|
||||||
|
|
||||||
|
@ -366,7 +420,7 @@ Windows standard methods for managing network drives. This is currently not
|
||||||
officially supported by Rclone, but with WinFsp version 2019.3 B2 / v1.5B2 or later
|
officially supported by Rclone, but with WinFsp version 2019.3 B2 / v1.5B2 or later
|
||||||
it should be possible through path rewriting as described [here](https://github.com/rclone/rclone/issues/3340).
|
it should be possible through path rewriting as described [here](https://github.com/rclone/rclone/issues/3340).
|
||||||
|
|
||||||
##### Third-party service integration #####
|
##### Third-party service integration
|
||||||
|
|
||||||
To Windows service running any rclone command, the excellent third-party utility
|
To Windows service running any rclone command, the excellent third-party utility
|
||||||
[NSSM](http://nssm.cc), the "Non-Sucking Service Manager", can be used.
|
[NSSM](http://nssm.cc), the "Non-Sucking Service Manager", can be used.
|
||||||
|
|
|
@ -150,6 +150,16 @@ use these methods. The alternative is to use `--rc-user` and
|
||||||
|
|
||||||
Default Off.
|
Default Off.
|
||||||
|
|
||||||
|
### --rc-baseurl
|
||||||
|
|
||||||
|
Prefix for URLs.
|
||||||
|
|
||||||
|
Default is root
|
||||||
|
|
||||||
|
### --rc-template
|
||||||
|
|
||||||
|
User-specified template.
|
||||||
|
|
||||||
## Accessing the remote control via the rclone rc command {#api-rc}
|
## Accessing the remote control via the rclone rc command {#api-rc}
|
||||||
|
|
||||||
Rclone itself implements the remote control protocol in its `rclone
|
Rclone itself implements the remote control protocol in its `rclone
|
||||||
|
|
|
@ -2575,6 +2575,10 @@ Here is an example of making a Cloudflare R2 configuration. First run:
|
||||||
|
|
||||||
This will guide you through an interactive setup process.
|
This will guide you through an interactive setup process.
|
||||||
|
|
||||||
|
Note that all buckets are private, and all are stored in the same
|
||||||
|
"auto" region. It is necessary to use Cloudflare workers to share the
|
||||||
|
content of a bucket publicly.
|
||||||
|
|
||||||
```
|
```
|
||||||
No remotes found, make a new one?
|
No remotes found, make a new one?
|
||||||
n) New remote
|
n) New remote
|
||||||
|
@ -2631,19 +2635,6 @@ Endpoint for S3 API.
|
||||||
Required when using an S3 clone.
|
Required when using an S3 clone.
|
||||||
Enter a value. Press Enter to leave empty.
|
Enter a value. Press Enter to leave empty.
|
||||||
endpoint> https://ACCOUNT_ID.r2.cloudflarestorage.com
|
endpoint> https://ACCOUNT_ID.r2.cloudflarestorage.com
|
||||||
Option acl.
|
|
||||||
Canned ACL used when creating buckets and storing or copying objects.
|
|
||||||
This ACL is used for creating objects and if bucket_acl isn't set, for creating buckets too.
|
|
||||||
For more info visit https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl
|
|
||||||
Note that this ACL is applied when server-side copying objects as S3
|
|
||||||
doesn't copy the ACL from the source but rather writes a fresh one.
|
|
||||||
Choose a number from below, or type in your own value.
|
|
||||||
Press Enter to leave empty.
|
|
||||||
/ Owner gets FULL_CONTROL.
|
|
||||||
1 | No one else has access rights (default).
|
|
||||||
\ (private)
|
|
||||||
...
|
|
||||||
acl> 1
|
|
||||||
Edit advanced config?
|
Edit advanced config?
|
||||||
y) Yes
|
y) Yes
|
||||||
n) No (default)
|
n) No (default)
|
||||||
|
|
|
@ -230,7 +230,7 @@ func ConfigChoose(state string, name string, help string, n int, getItem func(i
|
||||||
// StatePush pushes a new values onto the front of the config string
|
// StatePush pushes a new values onto the front of the config string
|
||||||
func StatePush(state string, values ...string) string {
|
func StatePush(state string, values ...string) string {
|
||||||
for i := range values {
|
for i := range values {
|
||||||
values[i] = strings.Replace(values[i], ",", ",", -1) // replace comma with unicode wide version
|
values[i] = strings.ReplaceAll(values[i], ",", ",") // replace comma with unicode wide version
|
||||||
}
|
}
|
||||||
if state != "" {
|
if state != "" {
|
||||||
values = append(values[:len(values):len(values)], state)
|
values = append(values[:len(values):len(values)], state)
|
||||||
|
@ -262,7 +262,7 @@ func StatePop(state string) (newState string, value string) {
|
||||||
return "", state
|
return "", state
|
||||||
}
|
}
|
||||||
value, newState = state[:comma], state[comma+1:]
|
value, newState = state[:comma], state[comma+1:]
|
||||||
value = strings.Replace(value, ",", ",", -1) // replace unicode wide comma with comma
|
value = strings.ReplaceAll(value, ",", ",") // replace unicode wide comma with comma
|
||||||
return newState, value
|
return newState, value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -248,11 +248,11 @@ func AddConfig(ctx context.Context) (context.Context, *ConfigInfo) {
|
||||||
// "ignore-size") into an environment name
|
// "ignore-size") into an environment name
|
||||||
// "RCLONE_CONFIG_MY-REMOTE_IGNORE_SIZE"
|
// "RCLONE_CONFIG_MY-REMOTE_IGNORE_SIZE"
|
||||||
func ConfigToEnv(section, name string) string {
|
func ConfigToEnv(section, name string) string {
|
||||||
return "RCLONE_CONFIG_" + strings.ToUpper(section+"_"+strings.Replace(name, "-", "_", -1))
|
return "RCLONE_CONFIG_" + strings.ToUpper(section+"_"+strings.ReplaceAll(name, "-", "_"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// OptionToEnv converts an option name, e.g. "ignore-size" into an
|
// OptionToEnv converts an option name, e.g. "ignore-size" into an
|
||||||
// environment name "RCLONE_IGNORE_SIZE"
|
// environment name "RCLONE_IGNORE_SIZE"
|
||||||
func OptionToEnv(name string) string {
|
func OptionToEnv(name string) string {
|
||||||
return "RCLONE_" + strings.ToUpper(strings.Replace(name, "-", "_", -1))
|
return "RCLONE_" + strings.ToUpper(strings.ReplaceAll(name, "-", "_"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ func setConfigFile(t *testing.T, data string) func() {
|
||||||
// toUnix converts \r\n to \n in buf
|
// toUnix converts \r\n to \n in buf
|
||||||
func toUnix(buf string) string {
|
func toUnix(buf string) string {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
return strings.Replace(buf, "\r\n", "\n", -1)
|
return strings.ReplaceAll(buf, "\r\n", "\n")
|
||||||
}
|
}
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
|
@ -417,7 +417,7 @@ func ChooseOption(o *fs.Option, name string) string {
|
||||||
fmt.Printf("Option %s.\n", o.Name)
|
fmt.Printf("Option %s.\n", o.Name)
|
||||||
if o.Help != "" {
|
if o.Help != "" {
|
||||||
// Show help string without empty lines.
|
// Show help string without empty lines.
|
||||||
help := strings.Replace(strings.TrimSpace(o.Help), "\n\n", "\n", -1)
|
help := strings.ReplaceAll(strings.TrimSpace(o.Help), "\n\n", "\n")
|
||||||
fmt.Println(help)
|
fmt.Println(help)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -208,7 +208,7 @@ loop:
|
||||||
value := path[prev : i-1]
|
value := path[prev : i-1]
|
||||||
// replace any doubled quotes if there were any
|
// replace any doubled quotes if there were any
|
||||||
if doubled {
|
if doubled {
|
||||||
value = strings.Replace(value, string(quote)+string(quote), string(quote), -1)
|
value = strings.ReplaceAll(value, string(quote)+string(quote), string(quote))
|
||||||
}
|
}
|
||||||
prev = i + 1
|
prev = i + 1
|
||||||
parsed.Config[param] = value
|
parsed.Config[param] = value
|
||||||
|
|
|
@ -1407,7 +1407,7 @@ func TestDirMove(t *testing.T) {
|
||||||
require.NoError(t, operations.DirMove(ctx, r.Fremote, "A1", "A2"))
|
require.NoError(t, operations.DirMove(ctx, r.Fremote, "A1", "A2"))
|
||||||
|
|
||||||
for i := range files {
|
for i := range files {
|
||||||
files[i].Path = strings.Replace(files[i].Path, "A1/", "A2/", -1)
|
files[i].Path = strings.ReplaceAll(files[i].Path, "A1/", "A2/")
|
||||||
}
|
}
|
||||||
|
|
||||||
fstest.CheckListingWithPrecision(
|
fstest.CheckListingWithPrecision(
|
||||||
|
@ -1432,7 +1432,7 @@ func TestDirMove(t *testing.T) {
|
||||||
require.NoError(t, operations.DirMove(ctx, r.Fremote, "A2", "A3"))
|
require.NoError(t, operations.DirMove(ctx, r.Fremote, "A2", "A3"))
|
||||||
|
|
||||||
for i := range files {
|
for i := range files {
|
||||||
files[i].Path = strings.Replace(files[i].Path, "A2/", "A3/", -1)
|
files[i].Path = strings.ReplaceAll(files[i].Path, "A2/", "A3/")
|
||||||
}
|
}
|
||||||
|
|
||||||
fstest.CheckListingWithPrecision(
|
fstest.CheckListingWithPrecision(
|
||||||
|
|
|
@ -44,7 +44,7 @@ type RegInfo struct {
|
||||||
|
|
||||||
// FileName returns the on disk file name for this backend
|
// FileName returns the on disk file name for this backend
|
||||||
func (ri *RegInfo) FileName() string {
|
func (ri *RegInfo) FileName() string {
|
||||||
return strings.Replace(ri.Name, " ", "", -1)
|
return strings.ReplaceAll(ri.Name, " ", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options is a slice of configuration Option for a backend
|
// Options is a slice of configuration Option for a backend
|
||||||
|
@ -210,7 +210,7 @@ func (o *Option) Type() string {
|
||||||
|
|
||||||
// FlagName for the option
|
// FlagName for the option
|
||||||
func (o *Option) FlagName(prefix string) string {
|
func (o *Option) FlagName(prefix string) string {
|
||||||
name := strings.Replace(o.Name, "_", "-", -1) // convert snake_case to kebab-case
|
name := strings.ReplaceAll(o.Name, "_", "-") // convert snake_case to kebab-case
|
||||||
if !o.NoPrefix {
|
if !o.NoPrefix {
|
||||||
name = prefix + "-" + name
|
name = prefix + "-" + name
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,14 @@
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
// Version of rclone
|
// Version of rclone containing the complete version string
|
||||||
var Version = "v1.59.0-DEV"
|
var Version string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if Version == "" {
|
||||||
|
if VersionSuffix == "" {
|
||||||
|
Version = VersionTag
|
||||||
|
} else {
|
||||||
|
Version = VersionTag + "-" + VersionSuffix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
4
fs/versionsuffix.go
Normal file
4
fs/versionsuffix.go
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
package fs
|
||||||
|
|
||||||
|
// VersionSuffix of rclone containing the pre-release label if any
|
||||||
|
var VersionSuffix = "DEV"
|
4
fs/versiontag.go
Normal file
4
fs/versiontag.go
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
package fs
|
||||||
|
|
||||||
|
// VersionTag of rclone
|
||||||
|
var VersionTag = "v1.59.0"
|
|
@ -317,7 +317,7 @@ func (r *Run) RemoveTestBinary() {
|
||||||
func (r *Run) Name() string {
|
func (r *Run) Name() string {
|
||||||
ns := []string{
|
ns := []string{
|
||||||
r.Backend,
|
r.Backend,
|
||||||
strings.Replace(r.Path, "/", ".", -1),
|
strings.ReplaceAll(r.Path, "/", "."),
|
||||||
r.Remote,
|
r.Remote,
|
||||||
}
|
}
|
||||||
if r.FastList {
|
if r.FastList {
|
||||||
|
@ -325,7 +325,7 @@ func (r *Run) Name() string {
|
||||||
}
|
}
|
||||||
ns = append(ns, fmt.Sprintf("%d", r.Try))
|
ns = append(ns, fmt.Sprintf("%d", r.Try))
|
||||||
s := strings.Join(ns, "-")
|
s := strings.Join(ns, "-")
|
||||||
s = strings.Replace(s, ":", "", -1)
|
s = strings.ReplaceAll(s, ":", "")
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ func Add(fileName string, t time.Time) string {
|
||||||
base, ext := splitExt(fileName)
|
base, ext := splitExt(fileName)
|
||||||
s := t.Format(versionFormat)
|
s := t.Format(versionFormat)
|
||||||
// Replace the '.' with a '-'
|
// Replace the '.' with a '-'
|
||||||
s = strings.Replace(s, ".", "-", -1)
|
s = strings.ReplaceAll(s, ".", "-")
|
||||||
return base + s + ext
|
return base + s + ext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -365,32 +365,32 @@ func rename(osOldPath, osNewPath string) error {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("Failed to stat source: %s: %w", osOldPath, err)
|
return fmt.Errorf("failed to stat source: %s: %w", osOldPath, err)
|
||||||
}
|
}
|
||||||
if !sfi.Mode().IsRegular() {
|
if !sfi.Mode().IsRegular() {
|
||||||
// cannot copy non-regular files (e.g., directories, symlinks, devices, etc.)
|
// cannot copy non-regular files (e.g., directories, symlinks, devices, etc.)
|
||||||
return fmt.Errorf("Non-regular source file: %s (%q)", sfi.Name(), sfi.Mode().String())
|
return fmt.Errorf("non-regular source file: %s (%q)", sfi.Name(), sfi.Mode().String())
|
||||||
}
|
}
|
||||||
dfi, err := os.Stat(osNewPath)
|
dfi, err := os.Stat(osNewPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !os.IsNotExist(err) {
|
if !os.IsNotExist(err) {
|
||||||
return fmt.Errorf("Failed to stat destination: %s: %w", osNewPath, err)
|
return fmt.Errorf("failed to stat destination: %s: %w", osNewPath, err)
|
||||||
}
|
}
|
||||||
parent := vfscommon.OsFindParent(osNewPath)
|
parent := vfscommon.OSFindParent(osNewPath)
|
||||||
err = createDir(parent)
|
err = createDir(parent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to create parent dir: %s: %w", parent, err)
|
return fmt.Errorf("failed to create parent dir: %s: %w", parent, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !(dfi.Mode().IsRegular()) {
|
if !(dfi.Mode().IsRegular()) {
|
||||||
return fmt.Errorf("Non-regular destination file: %s (%q)", dfi.Name(), dfi.Mode().String())
|
return fmt.Errorf("non-regular destination file: %s (%q)", dfi.Name(), dfi.Mode().String())
|
||||||
}
|
}
|
||||||
if os.SameFile(sfi, dfi) {
|
if os.SameFile(sfi, dfi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err = os.Rename(osOldPath, osNewPath); err != nil {
|
if err = os.Rename(osOldPath, osNewPath); err != nil {
|
||||||
return fmt.Errorf("Failed to rename in cache: %s to %s: %w", osOldPath, osNewPath, err)
|
return fmt.Errorf("failed to rename in cache: %s to %s: %w", osOldPath, osNewPath, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,11 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OsFindParent returns the parent directory of name, or "" for the
|
// OSFindParent returns the parent directory of name, or "" for the
|
||||||
// root for OS native paths.
|
// root for OS native paths.
|
||||||
func OsFindParent(name string) string {
|
func OSFindParent(name string) string {
|
||||||
parent := filepath.Dir(name)
|
parent := filepath.Dir(name)
|
||||||
if parent == "." || parent == "/" {
|
if parent == "." || (len(parent) == 1 && parent[0] == filepath.Separator) {
|
||||||
parent = ""
|
parent = ""
|
||||||
}
|
}
|
||||||
return parent
|
return parent
|
||||||
|
|
Loading…
Add table
Reference in a new issue