From 2aa2cfc70e933f8f5b7d0168a0be4a0c156d5d24 Mon Sep 17 00:00:00 2001 From: albertony <12441419+albertony@users.noreply.github.com> Date: Sun, 2 Nov 2025 13:19:36 +0100 Subject: [PATCH] docs: fix markdownlint issues and other styling improvements in backend command docs --- backend/b2/b2.go | 23 +- backend/crypt/crypt.go | 10 +- backend/doi/doi.go | 6 +- backend/drive/drive.go | 79 ++-- backend/hasher/commands.go | 16 +- backend/http/http.go | 5 +- backend/local/local.go | 7 +- backend/netstorage/netstorage.go | 7 +- backend/oracleobjectstorage/command.go | 550 ++++++++++++------------- backend/pikpak/pikpak.go | 18 +- backend/s3/s3.go | 73 ++-- 11 files changed, 386 insertions(+), 408 deletions(-) diff --git a/backend/b2/b2.go b/backend/b2/b2.go index d26de0745..b18a9b890 100644 --- a/backend/b2/b2.go +++ b/backend/b2/b2.go @@ -2360,8 +2360,6 @@ var lifecycleHelp = fs.CommandHelp{ Short: "Read or set the lifecycle for a bucket.", Long: `This command can be used to read or set the lifecycle for a bucket. -Usage Examples: - To show the current lifecycle rules: ` + "```console" + ` @@ -2381,7 +2379,7 @@ This will dump something like this showing the lifecycle rules. ] ` + "```" + ` -If there are no lifecycle rules (the default) then it will just return []. +If there are no lifecycle rules (the default) then it will just return ` + "`[]`" + `. To reset the current lifecycle rules: @@ -2404,12 +2402,13 @@ overwrites will still cause versions to be made. rclone backend lifecycle b2:bucket -o daysFromHidingToDeleting=1 ` + "```" + ` -See: https://www.backblaze.com/docs/cloud-storage-lifecycle-rules -`, +See: `, Opts: map[string]string{ - "daysFromHidingToDeleting": "After a file has been hidden for this many days it is deleted. 0 is off.", - "daysFromUploadingToHiding": "This many days after uploading a file is hidden", - "daysFromStartingToCancelingUnfinishedLargeFiles": "Cancels any unfinished large file versions after this many days", + "daysFromHidingToDeleting": `After a file has been hidden for this many days +it is deleted. 0 is off.`, + "daysFromUploadingToHiding": `This many days after uploading a file is hidden.`, + "daysFromStartingToCancelingUnfinishedLargeFiles": `Cancels any unfinished +large file versions after this many days.`, }, } @@ -2497,10 +2496,9 @@ rclone backend cleanup b2:bucket/path/to/object rclone backend cleanup -o max-age=7w b2:bucket/path/to/object ` + "```" + ` -Durations are parsed as per the rest of rclone, 2h, 7d, 7w etc. -`, +Durations are parsed as per the rest of rclone, 2h, 7d, 7w etc.`, Opts: map[string]string{ - "max-age": "Max age of upload to delete", + "max-age": "Max age of upload to delete.", }, } @@ -2525,8 +2523,7 @@ it would do. ` + "```console" + ` rclone backend cleanup-hidden b2:bucket/path/to/dir -` + "```" + ` -`, +` + "```", } func (f *Fs) cleanupHiddenCommand(ctx context.Context, name string, arg []string, opt map[string]string) (out any, err error) { diff --git a/backend/crypt/crypt.go b/backend/crypt/crypt.go index c61eeff75..1c02aaa7d 100644 --- a/backend/crypt/crypt.go +++ b/backend/crypt/crypt.go @@ -927,13 +927,12 @@ var commandHelp = []fs.CommandHelp{ Long: `This encodes the filenames given as arguments returning a list of strings of the encoded results. -Usage Example: +Usage examples: ` + "```console" + ` rclone backend encode crypt: file1 [file2...] rclone rc backend/command command=encode fs=crypt: file1 [file2...] -` + "```" + ` -`, +` + "```", }, { Name: "decode", @@ -942,13 +941,12 @@ rclone rc backend/command command=encode fs=crypt: file1 [file2...] strings of the decoded results. It will return an error if any of the inputs are invalid. -Usage Example: +Usage examples: ` + "```console" + ` rclone backend decode crypt: encryptedfile1 [encryptedfile2...] rclone rc backend/command command=decode fs=crypt: encryptedfile1 [encryptedfile2...] -` + "```" + ` -`, +` + "```", }, } diff --git a/backend/doi/doi.go b/backend/doi/doi.go index af9a7c498..2811df559 100644 --- a/backend/doi/doi.go +++ b/backend/doi/doi.go @@ -569,8 +569,7 @@ Usage example: rclone backend metadata doi: ` + "```" + ` -It returns a JSON object representing metadata about the DOI. -`, +It returns a JSON object representing metadata about the DOI.`, }, { Name: "set", Short: "Set command for updating the config parameters.", @@ -591,8 +590,7 @@ This rebuilds the connection to the doi backend when it is called with the new parameters. Only new parameters need be passed as the values will default to those currently in use. -It doesn't return anything. -`, +It doesn't return anything.`, }} // Command the backend to run a named command diff --git a/backend/drive/drive.go b/backend/drive/drive.go index 8d58b5de2..ffccadbd5 100644 --- a/backend/drive/drive.go +++ b/backend/drive/drive.go @@ -3665,41 +3665,41 @@ func (f *Fs) rescue(ctx context.Context, dirID string, delete bool) (err error) var commandHelp = []fs.CommandHelp{{ Name: "get", Short: "Get command for fetching the drive config parameters.", - Long: `This is a get command which will be used to fetch the various drive config parameters + Long: `This is a get command which will be used to fetch the various drive config +parameters. -Usage Examples: +Usage examples: ` + "```console" + ` rclone backend get drive: [-o service_account_file] [-o chunk_size] rclone rc backend/command command=get fs=drive: [-o service_account_file] [-o chunk_size] -` + "```" + ` -`, +` + "```", Opts: map[string]string{ - "chunk_size": "show the current upload chunk size", - "service_account_file": "show the current service account file", + "chunk_size": "Show the current upload chunk size.", + "service_account_file": "Show the current service account file.", }, }, { Name: "set", Short: "Set command for updating the drive config parameters.", - Long: `This is a set command which will be used to update the various drive config parameters + Long: `This is a set command which will be used to update the various drive config +parameters. -Usage Examples: +Usage examples: ` + "```console" + ` rclone backend set drive: [-o service_account_file=sa.json] [-o chunk_size=67108864] rclone rc backend/command command=set fs=drive: [-o service_account_file=sa.json] [-o chunk_size=67108864] -` + "```" + ` -`, +` + "```", Opts: map[string]string{ - "chunk_size": "update the current upload chunk size", - "service_account_file": "update the current service account file", + "chunk_size": "Update the current upload chunk size.", + "service_account_file": "Update the current service account file.", }, }, { Name: "shortcut", Short: "Create shortcuts from files or directories.", Long: `This command creates shortcuts from files or directories. -Usage: +Usage examples: ` + "```console" + ` rclone backend shortcut drive: source_item destination_shortcut @@ -3714,10 +3714,9 @@ from "drive:" In the second example this creates a shortcut from the "source_item" relative to "drive:" to the "destination_shortcut" relative to "drive2:". This may fail with a permission error if the user -authenticated with "drive2:" can't read files from "drive:". -`, +authenticated with "drive2:" can't read files from "drive:".`, Opts: map[string]string{ - "target": "optional target remote for the shortcut destination", + "target": "Optional target remote for the shortcut destination.", }, }, { Name: "drives", @@ -3725,7 +3724,7 @@ authenticated with "drive2:" can't read files from "drive:". Long: `This command lists the Shared Drives (Team Drives) available to this account. -Usage: +Usage example: ` + "```console" + ` rclone backend [-o config] drives drive: @@ -3770,15 +3769,14 @@ Adding this to the rclone config file will cause those team drives to be accessible with the aliases shown. Any illegal characters will be substituted with "_" and duplicate names will have numbers suffixed. It will also add a remote called AllDrives which shows all the shared -drives combined into one directory tree. -`, +drives combined into one directory tree.`, }, { Name: "untrash", Short: "Untrash files and directories.", Long: `This command untrashes all the files and directories in the directory passed in recursively. -Usage: +Usage example: ` + "```console" + ` rclone backend untrash drive:directory @@ -3788,7 +3786,8 @@ rclone backend --interactive untrash drive:directory subdir This takes an optional directory to trash which make this easier to use via the API. -Use the --interactive/-i or --dry-run flag to see what would be restored before restoring it. +Use the --interactive/-i or --dry-run flag to see what would be restored before +restoring it. Result: @@ -3797,14 +3796,13 @@ Result: "Untrashed": 17, "Errors": 0 } -` + "```" + ` -`, +` + "```", }, { Name: "copyid", Short: "Copy files by ID.", - Long: `This command copies files by ID + Long: `This command copies files by ID. -Usage: +Usage examples: ` + "```console" + ` rclone backend copyid drive: ID path @@ -3822,14 +3820,14 @@ component will be used as the file name. If the destination is a drive backend then server-side copying will be attempted if possible. -Use the --interactive/-i or --dry-run flag to see what would be copied before copying. -`, +Use the --interactive/-i or --dry-run flag to see what would be copied before +copying.`, }, { Name: "moveid", Short: "Move files by ID.", - Long: `This command moves files by ID + Long: `This command moves files by ID. -Usage: +Usage examples: ` + "```console" + ` rclone backend moveid drive: ID path @@ -3846,8 +3844,7 @@ component will be used as the file name. If the destination is a drive backend then server-side moving will be attempted if possible. -Use the --interactive/-i or --dry-run flag to see what would be moved beforehand. -`, +Use the --interactive/-i or --dry-run flag to see what would be moved beforehand.`, }, { Name: "exportformats", Short: "Dump the export formats for debug purposes.", @@ -3857,9 +3854,9 @@ Use the --interactive/-i or --dry-run flag to see what would be moved beforehand }, { Name: "query", Short: "List files using Google Drive query language.", - Long: `This command lists files based on a query + Long: `This command lists files based on a query. -Usage: +Usage example: ` + "```console" + ` rclone backend query drive: query @@ -3902,8 +3899,7 @@ The result is a JSON array of matches, for example: "webViewLink": "https://drive.google.com/file/d/0AxBe_CDEF4zkGHI4d0FjYko2QkD/view?usp=drivesdk\u0026resourcekey=0-ABCDEFGHIXJQpIGqBJq3MC" } ] -` + "```console" + ` -`, +` + "```console", }, { Name: "rescue", Short: "Rescue or delete any orphaned files.", @@ -3915,34 +3911,31 @@ are no longer in any folder in Google Drive. This command finds those files and either rescues them to a directory you specify or deletes them. -Usage: - This can be used in 3 ways. -First, list all orphaned files +First, list all orphaned files: ` + "```console" + ` rclone backend rescue drive: ` + "```" + ` -Second rescue all orphaned files to the directory indicated +Second rescue all orphaned files to the directory indicated: ` + "```console" + ` rclone backend rescue drive: "relative/path/to/rescue/directory" ` + "```" + ` -e.g. To rescue all orphans to a directory called "Orphans" in the top level +E.g. to rescue all orphans to a directory called "Orphans" in the top level: ` + "```console" + ` rclone backend rescue drive: Orphans ` + "```" + ` -Third delete all orphaned files to the trash +Third delete all orphaned files to the trash: ` + "```console" + ` rclone backend rescue drive: -o delete -` + "```" + ` -`, +` + "```", }} // Command the backend to run a named command diff --git a/backend/hasher/commands.go b/backend/hasher/commands.go index 046149b38..6afc9acb5 100644 --- a/backend/hasher/commands.go +++ b/backend/hasher/commands.go @@ -45,20 +45,20 @@ var commandHelp = []fs.CommandHelp{{ Name: "drop", Short: "Drop cache.", Long: `Completely drop checksum cache. -Usage Example: + +Usage example: ` + "```console" + ` rclone backend drop hasher: -` + "```" + ` -`, +` + "```", }, { Name: "dump", Short: "Dump the database.", - Long: "Dump cache records covered by the current remote", + Long: "Dump cache records covered by the current remote.", }, { Name: "fulldump", Short: "Full dump of the database.", - Long: "Dump all cache records in the database", + Long: "Dump all cache records in the database.", }, { Name: "import", Short: "Import a SUM file.", @@ -68,8 +68,7 @@ Usage example: ` + "```console" + ` rclone backend import hasher:subdir md5 /path/to/sum.md5 -` + "```" + ` -`, +` + "```", }, { Name: "stickyimport", Short: "Perform fast import of a SUM file.", @@ -79,8 +78,7 @@ Usage example: ` + "```console" + ` rclone backend stickyimport hasher:subdir md5 remote:path/to/sum.md5 -` + "```" + ` -`, +` + "```", }} func (f *Fs) dbDump(ctx context.Context, full bool, root string) error { diff --git a/backend/http/http.go b/backend/http/http.go index b98182b06..0a3cdb7b5 100644 --- a/backend/http/http.go +++ b/backend/http/http.go @@ -722,7 +722,7 @@ var commandHelp = []fs.CommandHelp{{ Long: `This set command can be used to update the config parameters for a running http backend. -Usage Examples: +Usage examples: ` + "```console" + ` rclone backend set remote: [-o opt_name=opt_value] [-o opt_name2=opt_value2] @@ -736,8 +736,7 @@ This rebuilds the connection to the http backend when it is called with the new parameters. Only new parameters need be passed as the values will default to those currently in use. -It doesn't return anything. -`, +It doesn't return anything.`, }} // Command the backend to run a named command diff --git a/backend/local/local.go b/backend/local/local.go index b7ee82da6..da21c483f 100644 --- a/backend/local/local.go +++ b/backend/local/local.go @@ -1071,11 +1071,10 @@ var commandHelp = []fs.CommandHelp{ { Name: "noop", Short: "A null operation for testing backend commands.", - Long: `This is a test command which has some options -you can try to change the output.`, + Long: `This is a test command which has some options you can try to change the output.`, Opts: map[string]string{ - "echo": "echo the input arguments", - "error": "return an error based on option value", + "echo": "Echo the input arguments.", + "error": "Return an error based on option value.", }, }, } diff --git a/backend/netstorage/netstorage.go b/backend/netstorage/netstorage.go index 592cb0535..812eb1e27 100755 --- a/backend/netstorage/netstorage.go +++ b/backend/netstorage/netstorage.go @@ -96,7 +96,12 @@ files stored in any sub-directories that may exist.`, Long: `The desired path location (including applicable sub-directories) ending in the object that will be the target of the symlink (for example, /links/mylink). Include the file extension for the object, if applicable. -` + "`rclone backend symlink `", + +Usage example: + +` + "```console" + ` +rclone backend symlink +` + "```", }, } diff --git a/backend/oracleobjectstorage/command.go b/backend/oracleobjectstorage/command.go index 90117bd71..9c1cf8739 100644 --- a/backend/oracleobjectstorage/command.go +++ b/backend/oracleobjectstorage/command.go @@ -3,18 +3,18 @@ package oracleobjectstorage import ( - "context" - "fmt" - "sort" - "strconv" - "strings" - "sync" - "time" + "context" + "fmt" + "sort" + "strconv" + "strings" + "sync" + "time" - "github.com/oracle/oci-go-sdk/v65/common" - "github.com/oracle/oci-go-sdk/v65/objectstorage" - "github.com/rclone/rclone/fs" - "github.com/rclone/rclone/fs/operations" + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/objectstorage" + "github.com/rclone/rclone/fs" + "github.com/rclone/rclone/fs/operations" ) // ------------------------------------------------------------ @@ -22,30 +22,29 @@ import ( // ------------------------------------------------------------ const ( - operationRename = "rename" - operationListMultiPart = "list-multipart-uploads" - operationCleanup = "cleanup" - operationRestore = "restore" + operationRename = "rename" + operationListMultiPart = "list-multipart-uploads" + operationCleanup = "cleanup" + operationRestore = "restore" ) var commandHelp = []fs.CommandHelp{{ - Name: operationRename, - Short: "change the name of an object.", - Long: `This command can be used to rename a object. + Name: operationRename, + Short: "change the name of an object.", + Long: `This command can be used to rename a object. -Usage Examples: +Usage example: ` + "```console" + ` rclone backend rename oos:bucket relative-object-path-under-bucket object-new-name -` + "```" + ` -`, - Opts: nil, +` + "```", + Opts: nil, }, { - Name: operationListMultiPart, - Short: "List the unfinished multipart uploads.", - Long: `This command lists the unfinished multipart uploads in JSON format. + Name: operationListMultiPart, + Short: "List the unfinished multipart uploads.", + Long: `This command lists the unfinished multipart uploads in JSON format. -Usage Examples: +Usage example: ` + "```console" + ` rclone backend list-multipart-uploads oos:bucket/path/to/object @@ -57,7 +56,6 @@ multipart uploads. You can call it with no bucket in which case it lists all bucket, with a bucket or with a bucket and path. - ` + "```json" + ` { "test-bucket": [ @@ -70,35 +68,34 @@ a bucket or with a bucket and path. "storageTier": "Standard" } ] -} -`, +}`, }, { - Name: operationCleanup, - Short: "Remove unfinished multipart uploads.", - Long: `This command removes unfinished multipart uploads of age greater than + Name: operationCleanup, + Short: "Remove unfinished multipart uploads.", + Long: `This command removes unfinished multipart uploads of age greater than max-age which defaults to 24 hours. -Note that you can use --interactive/-i or --dry-run with this command to see what -it would do. +Note that you can use --interactive/-i or --dry-run with this command to see +what it would do. -Usage Examples: +Usage examples: ` + "```console" + ` rclone backend cleanup oos:bucket/path/to/object rclone backend cleanup -o max-age=7w oos:bucket/path/to/object ` + "```" + ` -Durations are parsed as per the rest of rclone, 2h, 7d, 7w etc. -`, - Opts: map[string]string{ - "max-age": "Max age of upload to delete", - }, +Durations are parsed as per the rest of rclone, 2h, 7d, 7w etc.`, + Opts: map[string]string{ + "max-age": "Max age of upload to delete.", + }, }, { - Name: operationRestore, - Short: "Restore objects from Archive to Standard storage.", - Long: `This command can be used to restore one or more objects from Archive to Standard storage. + Name: operationRestore, + Short: "Restore objects from Archive to Standard storage.", + Long: `This command can be used to restore one or more objects from Archive to +Standard storage. -Usage Examples: +Usage examples: ` + "```console" + ` rclone backend restore oos:bucket/path/to/directory -o hours=HOURS @@ -111,15 +108,14 @@ This flag also obeys the filters. Test first with --interactive/-i or --dry-run rclone --interactive backend restore --include "*.txt" oos:bucket/path -o hours=72 ` + "```" + ` -All the objects shown will be marked for restore, then +All the objects shown will be marked for restore, then: ` + "```console" + ` rclone backend restore --include "*.txt" oos:bucket/path -o hours=72 ` + "```" + ` -It returns a list of status dictionaries with Object Name and Status -keys. The Status will be "RESTORED"" if it was successful or an error message -if not. +It returns a list of status dictionaries with Object Name and Status keys. +The Status will be "RESTORED"" if it was successful or an error message if not. ` + "```json" + ` [ @@ -132,11 +128,11 @@ if not. "Status": "RESTORED", } ] -` + "```" + ` -`, - Opts: map[string]string{ - "hours": "The number of hours for which this object will be restored. Default is 24 hrs.", - }, +` + "```", + Opts: map[string]string{ + "hours": `The number of hours for which this object will be restored. +Default is 24 hrs.`, + }, }, } @@ -152,104 +148,104 @@ If it is a string or a []string it will be shown to the user otherwise it will be JSON encoded and shown to the user like that */ func (f *Fs) Command(ctx context.Context, commandName string, args []string, - opt map[string]string) (result any, err error) { - // fs.Debugf(f, "command %v, args: %v, opts:%v", commandName, args, opt) - switch commandName { - case operationRename: - if len(args) < 2 { - return nil, fmt.Errorf("path to object or its new name to rename is empty") - } - remote := args[0] - newName := args[1] - return f.rename(ctx, remote, newName) - case operationListMultiPart: - return f.listMultipartUploadsAll(ctx) - case operationCleanup: - maxAge := 24 * time.Hour - if opt["max-age"] != "" { - maxAge, err = fs.ParseDuration(opt["max-age"]) - if err != nil { - return nil, fmt.Errorf("bad max-age: %w", err) - } - } - return nil, f.cleanUp(ctx, maxAge) - case operationRestore: - return f.restore(ctx, opt) - default: - return nil, fs.ErrorCommandNotFound - } + opt map[string]string) (result any, err error) { + // fs.Debugf(f, "command %v, args: %v, opts:%v", commandName, args, opt) + switch commandName { + case operationRename: + if len(args) < 2 { + return nil, fmt.Errorf("path to object or its new name to rename is empty") + } + remote := args[0] + newName := args[1] + return f.rename(ctx, remote, newName) + case operationListMultiPart: + return f.listMultipartUploadsAll(ctx) + case operationCleanup: + maxAge := 24 * time.Hour + if opt["max-age"] != "" { + maxAge, err = fs.ParseDuration(opt["max-age"]) + if err != nil { + return nil, fmt.Errorf("bad max-age: %w", err) + } + } + return nil, f.cleanUp(ctx, maxAge) + case operationRestore: + return f.restore(ctx, opt) + default: + return nil, fs.ErrorCommandNotFound + } } func (f *Fs) rename(ctx context.Context, remote, newName string) (any, error) { - if remote == "" { - return nil, fmt.Errorf("path to object file cannot be empty") - } - if newName == "" { - return nil, fmt.Errorf("the object's new name cannot be empty") - } - o := &Object{ - fs: f, - remote: remote, - } - bucketName, objectPath := o.split() - err := o.readMetaData(ctx) - if err != nil { - fs.Errorf(f, "failed to read object:%v %v ", objectPath, err) - if strings.HasPrefix(objectPath, bucketName) { - fs.Errorf(f, "warn: ensure object path: %v is relative to bucket:%v and doesn't include the bucket name", - objectPath, bucketName) - } - return nil, fs.ErrorNotAFile - } - details := objectstorage.RenameObjectDetails{ - SourceName: common.String(objectPath), - NewName: common.String(newName), - } - request := objectstorage.RenameObjectRequest{ - NamespaceName: common.String(f.opt.Namespace), - BucketName: common.String(bucketName), - RenameObjectDetails: details, - OpcClientRequestId: nil, - RequestMetadata: common.RequestMetadata{}, - } - var response objectstorage.RenameObjectResponse - err = f.pacer.Call(func() (bool, error) { - response, err = f.srv.RenameObject(ctx, request) - return shouldRetry(ctx, response.HTTPResponse(), err) - }) - if err != nil { - return nil, err - } - fs.Infof(f, "success: renamed object-path: %v to %v", objectPath, newName) - return "renamed successfully", nil + if remote == "" { + return nil, fmt.Errorf("path to object file cannot be empty") + } + if newName == "" { + return nil, fmt.Errorf("the object's new name cannot be empty") + } + o := &Object{ + fs: f, + remote: remote, + } + bucketName, objectPath := o.split() + err := o.readMetaData(ctx) + if err != nil { + fs.Errorf(f, "failed to read object:%v %v ", objectPath, err) + if strings.HasPrefix(objectPath, bucketName) { + fs.Errorf(f, "warn: ensure object path: %v is relative to bucket:%v and doesn't include the bucket name", + objectPath, bucketName) + } + return nil, fs.ErrorNotAFile + } + details := objectstorage.RenameObjectDetails{ + SourceName: common.String(objectPath), + NewName: common.String(newName), + } + request := objectstorage.RenameObjectRequest{ + NamespaceName: common.String(f.opt.Namespace), + BucketName: common.String(bucketName), + RenameObjectDetails: details, + OpcClientRequestId: nil, + RequestMetadata: common.RequestMetadata{}, + } + var response objectstorage.RenameObjectResponse + err = f.pacer.Call(func() (bool, error) { + response, err = f.srv.RenameObject(ctx, request) + return shouldRetry(ctx, response.HTTPResponse(), err) + }) + if err != nil { + return nil, err + } + fs.Infof(f, "success: renamed object-path: %v to %v", objectPath, newName) + return "renamed successfully", nil } func (f *Fs) listMultipartUploadsAll(ctx context.Context) (uploadsMap map[string][]*objectstorage.MultipartUpload, - err error) { - uploadsMap = make(map[string][]*objectstorage.MultipartUpload) - bucket, directory := f.split("") - if bucket != "" { - uploads, err := f.listMultipartUploads(ctx, bucket, directory) - if err != nil { - return uploadsMap, err - } - uploadsMap[bucket] = uploads - return uploadsMap, nil - } - entries, err := f.listBuckets(ctx) - if err != nil { - return uploadsMap, err - } - for _, entry := range entries { - bucket := entry.Remote() - uploads, listErr := f.listMultipartUploads(ctx, bucket, "") - if listErr != nil { - err = listErr - fs.Errorf(f, "%v", err) - } - uploadsMap[bucket] = uploads - } - return uploadsMap, err + err error) { + uploadsMap = make(map[string][]*objectstorage.MultipartUpload) + bucket, directory := f.split("") + if bucket != "" { + uploads, err := f.listMultipartUploads(ctx, bucket, directory) + if err != nil { + return uploadsMap, err + } + uploadsMap[bucket] = uploads + return uploadsMap, nil + } + entries, err := f.listBuckets(ctx) + if err != nil { + return uploadsMap, err + } + for _, entry := range entries { + bucket := entry.Remote() + uploads, listErr := f.listMultipartUploads(ctx, bucket, "") + if listErr != nil { + err = listErr + fs.Errorf(f, "%v", err) + } + uploadsMap[bucket] = uploads + } + return uploadsMap, err } // listMultipartUploads lists all outstanding multipart uploads for (bucket, key) @@ -258,8 +254,8 @@ func (f *Fs) listMultipartUploadsAll(ctx context.Context) (uploadsMap map[string // directories and objects. This could surprise the user if they ask // for "dir" and it returns "dirKey" func (f *Fs) listMultipartUploads(ctx context.Context, bucketName, directory string) ( - uploads []*objectstorage.MultipartUpload, err error) { - return f.listMultipartUploadsObject(ctx, bucketName, directory, false) + uploads []*objectstorage.MultipartUpload, err error) { + return f.listMultipartUploadsObject(ctx, bucketName, directory, false) } // listMultipartUploads finds first outstanding multipart uploads for (bucket, key) @@ -268,147 +264,147 @@ func (f *Fs) listMultipartUploads(ctx context.Context, bucketName, directory str // directories and objects. This could surprise the user if they ask // for "dir" and it returns "dirKey" func (f *Fs) findLatestMultipartUpload(ctx context.Context, bucketName, directory string) ( - uploads []*objectstorage.MultipartUpload, err error) { - pastUploads, err := f.listMultipartUploadsObject(ctx, bucketName, directory, true) - if err != nil { - return nil, err - } + uploads []*objectstorage.MultipartUpload, err error) { + pastUploads, err := f.listMultipartUploadsObject(ctx, bucketName, directory, true) + if err != nil { + return nil, err + } - if len(pastUploads) > 0 { - sort.Slice(pastUploads, func(i, j int) bool { - return pastUploads[i].TimeCreated.After(pastUploads[j].TimeCreated.Time) - }) - return pastUploads[:1], nil - } - return nil, err + if len(pastUploads) > 0 { + sort.Slice(pastUploads, func(i, j int) bool { + return pastUploads[i].TimeCreated.After(pastUploads[j].TimeCreated.Time) + }) + return pastUploads[:1], nil + } + return nil, err } func (f *Fs) listMultipartUploadsObject(ctx context.Context, bucketName, directory string, exact bool) ( - uploads []*objectstorage.MultipartUpload, err error) { + uploads []*objectstorage.MultipartUpload, err error) { - uploads = []*objectstorage.MultipartUpload{} - req := objectstorage.ListMultipartUploadsRequest{ - NamespaceName: common.String(f.opt.Namespace), - BucketName: common.String(bucketName), - } + uploads = []*objectstorage.MultipartUpload{} + req := objectstorage.ListMultipartUploadsRequest{ + NamespaceName: common.String(f.opt.Namespace), + BucketName: common.String(bucketName), + } - var response objectstorage.ListMultipartUploadsResponse - for { - err = f.pacer.Call(func() (bool, error) { - response, err = f.srv.ListMultipartUploads(ctx, req) - return shouldRetry(ctx, response.HTTPResponse(), err) - }) - if err != nil { - // fs.Debugf(f, "failed to list multi part uploads %v", err) - return uploads, err - } - for index, item := range response.Items { - if directory != "" && item.Object != nil && !strings.HasPrefix(*item.Object, directory) { - continue - } - if exact { - if *item.Object == directory { - uploads = append(uploads, &response.Items[index]) - } - } else { - uploads = append(uploads, &response.Items[index]) - } - } - if response.OpcNextPage == nil { - break - } - req.Page = response.OpcNextPage - } - return uploads, nil + var response objectstorage.ListMultipartUploadsResponse + for { + err = f.pacer.Call(func() (bool, error) { + response, err = f.srv.ListMultipartUploads(ctx, req) + return shouldRetry(ctx, response.HTTPResponse(), err) + }) + if err != nil { + // fs.Debugf(f, "failed to list multi part uploads %v", err) + return uploads, err + } + for index, item := range response.Items { + if directory != "" && item.Object != nil && !strings.HasPrefix(*item.Object, directory) { + continue + } + if exact { + if *item.Object == directory { + uploads = append(uploads, &response.Items[index]) + } + } else { + uploads = append(uploads, &response.Items[index]) + } + } + if response.OpcNextPage == nil { + break + } + req.Page = response.OpcNextPage + } + return uploads, nil } func (f *Fs) listMultipartUploadParts(ctx context.Context, bucketName, bucketPath string, uploadID string) ( - uploadedParts map[int]objectstorage.MultipartUploadPartSummary, err error) { - uploadedParts = make(map[int]objectstorage.MultipartUploadPartSummary) - req := objectstorage.ListMultipartUploadPartsRequest{ - NamespaceName: common.String(f.opt.Namespace), - BucketName: common.String(bucketName), - ObjectName: common.String(bucketPath), - UploadId: common.String(uploadID), - Limit: common.Int(1000), - } + uploadedParts map[int]objectstorage.MultipartUploadPartSummary, err error) { + uploadedParts = make(map[int]objectstorage.MultipartUploadPartSummary) + req := objectstorage.ListMultipartUploadPartsRequest{ + NamespaceName: common.String(f.opt.Namespace), + BucketName: common.String(bucketName), + ObjectName: common.String(bucketPath), + UploadId: common.String(uploadID), + Limit: common.Int(1000), + } - var response objectstorage.ListMultipartUploadPartsResponse - for { - err = f.pacer.Call(func() (bool, error) { - response, err = f.srv.ListMultipartUploadParts(ctx, req) - return shouldRetry(ctx, response.HTTPResponse(), err) - }) - if err != nil { - return uploadedParts, err - } - for _, item := range response.Items { - uploadedParts[*item.PartNumber] = item - } - if response.OpcNextPage == nil { - break - } - req.Page = response.OpcNextPage - } - return uploadedParts, nil + var response objectstorage.ListMultipartUploadPartsResponse + for { + err = f.pacer.Call(func() (bool, error) { + response, err = f.srv.ListMultipartUploadParts(ctx, req) + return shouldRetry(ctx, response.HTTPResponse(), err) + }) + if err != nil { + return uploadedParts, err + } + for _, item := range response.Items { + uploadedParts[*item.PartNumber] = item + } + if response.OpcNextPage == nil { + break + } + req.Page = response.OpcNextPage + } + return uploadedParts, nil } func (f *Fs) restore(ctx context.Context, opt map[string]string) (any, error) { - req := objectstorage.RestoreObjectsRequest{ - NamespaceName: common.String(f.opt.Namespace), - RestoreObjectsDetails: objectstorage.RestoreObjectsDetails{}, - } - if hours := opt["hours"]; hours != "" { - ihours, err := strconv.Atoi(hours) - if err != nil { - return nil, fmt.Errorf("bad value for hours: %w", err) - } - req.RestoreObjectsDetails.Hours = &ihours - } - type status struct { - Object string - Status string - } - var ( - outMu sync.Mutex - out = []status{} - err error - ) - err = operations.ListFn(ctx, f, func(obj fs.Object) { - // Remember this is run --checkers times concurrently - o, ok := obj.(*Object) - st := status{Object: obj.Remote(), Status: "RESTORED"} - defer func() { - outMu.Lock() - out = append(out, st) - outMu.Unlock() - }() - if !ok { - st.Status = "Not an OCI Object Storage object" - return - } - if o.storageTier == nil || (*o.storageTier != "archive") { - st.Status = "Object not in Archive storage tier" - return - } - if operations.SkipDestructive(ctx, obj, "restore") { - return - } - bucket, bucketPath := o.split() - reqCopy := req - reqCopy.BucketName = &bucket - reqCopy.ObjectName = &bucketPath - var response objectstorage.RestoreObjectsResponse - err = f.pacer.Call(func() (bool, error) { - response, err = f.srv.RestoreObjects(ctx, reqCopy) - return shouldRetry(ctx, response.HTTPResponse(), err) - }) - if err != nil { - st.Status = err.Error() - } - }) - if err != nil { - return out, err - } - return out, nil + req := objectstorage.RestoreObjectsRequest{ + NamespaceName: common.String(f.opt.Namespace), + RestoreObjectsDetails: objectstorage.RestoreObjectsDetails{}, + } + if hours := opt["hours"]; hours != "" { + ihours, err := strconv.Atoi(hours) + if err != nil { + return nil, fmt.Errorf("bad value for hours: %w", err) + } + req.RestoreObjectsDetails.Hours = &ihours + } + type status struct { + Object string + Status string + } + var ( + outMu sync.Mutex + out = []status{} + err error + ) + err = operations.ListFn(ctx, f, func(obj fs.Object) { + // Remember this is run --checkers times concurrently + o, ok := obj.(*Object) + st := status{Object: obj.Remote(), Status: "RESTORED"} + defer func() { + outMu.Lock() + out = append(out, st) + outMu.Unlock() + }() + if !ok { + st.Status = "Not an OCI Object Storage object" + return + } + if o.storageTier == nil || (*o.storageTier != "archive") { + st.Status = "Object not in Archive storage tier" + return + } + if operations.SkipDestructive(ctx, obj, "restore") { + return + } + bucket, bucketPath := o.split() + reqCopy := req + reqCopy.BucketName = &bucket + reqCopy.ObjectName = &bucketPath + var response objectstorage.RestoreObjectsResponse + err = f.pacer.Call(func() (bool, error) { + response, err = f.srv.RestoreObjects(ctx, reqCopy) + return shouldRetry(ctx, response.HTTPResponse(), err) + }) + if err != nil { + st.Status = err.Error() + } + }) + if err != nil { + return out, err + } + return out, nil } diff --git a/backend/pikpak/pikpak.go b/backend/pikpak/pikpak.go index 4a7433442..7cce6695e 100644 --- a/backend/pikpak/pikpak.go +++ b/backend/pikpak/pikpak.go @@ -1681,30 +1681,29 @@ var commandHelp = []fs.CommandHelp{{ Short: "Add offline download task for url.", Long: `This command adds offline download task for url. -Usage: +Usage example: ` + "```console" + ` rclone backend addurl pikpak:dirpath url ` + "```" + ` -Downloads will be stored in 'dirpath'. If 'dirpath' is invalid, -download will fallback to default 'My Pack' folder. -`, +Downloads will be stored in 'dirpath'. If 'dirpath' is invalid, +download will fallback to default 'My Pack' folder.`, }, { Name: "decompress", Short: "Request decompress of a file/files in a folder.", Long: `This command requests decompress of file/files in a folder. -Usage: +Usage examples: ` + "```console" + ` rclone backend decompress pikpak:dirpath {filename} -o password=password rclone backend decompress pikpak:dirpath {filename} -o delete-src-file ` + "```" + ` -An optional argument 'filename' can be specified for a file located in -'pikpak:dirpath'. You may want to pass '-o password=password' for a -password-protected files. Also, pass '-o delete-src-file' to delete +An optional argument 'filename' can be specified for a file located in +'pikpak:dirpath'. You may want to pass '-o password=password' for a +password-protected files. Also, pass '-o delete-src-file' to delete source files after decompression finished. Result: @@ -1715,8 +1714,7 @@ Result: "SourceDeleted": 0, "Errors": 0 } -` + "```" + ` -`, +` + "```", }} // Command the backend to run a named command diff --git a/backend/s3/s3.go b/backend/s3/s3.go index 333165d3d..137b31adb 100644 --- a/backend/s3/s3.go +++ b/backend/s3/s3.go @@ -2903,10 +2903,11 @@ func (f *Fs) PublicLink(ctx context.Context, remote string, expire fs.Duration, var commandHelp = []fs.CommandHelp{{ Name: "restore", Short: "Restore objects from GLACIER or INTELLIGENT-TIERING archive tier.", - Long: `This command can be used to restore one or more objects from GLACIER to normal storage -or from INTELLIGENT-TIERING Archive Access / Deep Archive Access tier to the Frequent Access tier. + Long: `This command can be used to restore one or more objects from GLACIER to normal +storage or from INTELLIGENT-TIERING Archive Access / Deep Archive Access tier +to the Frequent Access tier. -Usage Examples: +Usage examples: ` + "```console" + ` rclone backend restore s3:bucket/path/to/ --include /object -o priority=PRIORITY -o lifetime=DAYS @@ -2915,13 +2916,14 @@ rclone backend restore s3:bucket -o priority=PRIORITY -o lifetime=DAYS rclone backend restore s3:bucket/path/to/directory -o priority=PRIORITY ` + "```" + ` -This flag also obeys the filters. Test first with --interactive/-i or --dry-run flags +This flag also obeys the filters. Test first with --interactive/-i or --dry-run +flags. ` + "```console" + ` rclone --interactive backend restore --include "*.txt" s3:bucket/path -o priority=Standard -o lifetime=1 ` + "```" + ` -All the objects shown will be marked for restore, then +All the objects shown will be marked for restore, then: ` + "```console" + ` rclone backend restore --include "*.txt" s3:bucket/path -o priority=Standard -o lifetime=1 @@ -2942,20 +2944,21 @@ if not. "Remote": "test/file4.txt" } ] -` + "```" + ` -`, +` + "```", Opts: map[string]string{ - "priority": "Priority of restore: Standard|Expedited|Bulk", - "lifetime": "Lifetime of the active copy in days, ignored for INTELLIGENT-TIERING storage", + "priority": "Priority of restore: Standard|Expedited|Bulk", + "lifetime": `Lifetime of the active copy in days, ignored for INTELLIGENT-TIERING +storage.`, "description": "The optional description for the job.", }, }, { Name: "restore-status", - Short: "Show the restore status for objects being restored from GLACIER or INTELLIGENT-TIERING storage.", - Long: `This command can be used to show the status for objects being restored from GLACIER to normal storage -or from INTELLIGENT-TIERING Archive Access / Deep Archive Access tier to the Frequent Access tier. + Short: "Show the status for objects being restored from GLACIER or INTELLIGENT-TIERING.", + Long: `This command can be used to show the status for objects being restored from +GLACIER to normal storage or from INTELLIGENT-TIERING Archive Access / Deep +Archive Access tier to the Frequent Access tier. -Usage Examples: +Usage examples: ` + "```console" + ` rclone backend restore-status s3:bucket/path/to/object @@ -2965,7 +2968,7 @@ rclone backend restore-status -o all s3:bucket/path/to/directory This command does not obey the filters. -It returns a list of status dictionaries. +It returns a list of status dictionaries: ` + "```json" + ` [ @@ -2997,17 +3000,16 @@ It returns a list of status dictionaries. "StorageClass": "INTELLIGENT_TIERING" } ] -` + "```" + ` -`, +` + "```", Opts: map[string]string{ - "all": "if set then show all objects, not just ones with restore status", + "all": "If set then show all objects, not just ones with restore status.", }, }, { Name: "list-multipart-uploads", Short: "List the unfinished multipart uploads.", Long: `This command lists the unfinished multipart uploads in JSON format. -Usage Examples: +Usage examples: ` + "```console" + ` rclone backend list-multipart s3:bucket/path/to/object @@ -3040,28 +3042,26 @@ a bucket or with a bucket and path. "rclone-1000files": [], "rclone-dst": [] } -` + "```" + ` -`, +` + "```", }, { Name: "cleanup", Short: "Remove unfinished multipart uploads.", Long: `This command removes unfinished multipart uploads of age greater than max-age which defaults to 24 hours. -Note that you can use --interactive/-i or --dry-run with this command to see what -it would do. +Note that you can use --interactive/-i or --dry-run with this command to see +what it would do. -Usage Examples: +Usage examples: ` + "```console" + ` rclone backend cleanup s3:bucket/path/to/object rclone backend cleanup -o max-age=7w s3:bucket/path/to/object ` + "```" + ` -Durations are parsed as per the rest of rclone, 2h, 7d, 7w etc. -`, +Durations are parsed as per the rest of rclone, 2h, 7d, 7w etc.`, Opts: map[string]string{ - "max-age": "Max age of upload to delete", + "max-age": "Max age of upload to delete.", }, }, { Name: "cleanup-hidden", @@ -3069,15 +3069,14 @@ Durations are parsed as per the rest of rclone, 2h, 7d, 7w etc. Long: `This command removes any old hidden versions of files on a versions enabled bucket. -Note that you can use --interactive/-i or --dry-run with this command to see what -it would do. +Note that you can use --interactive/-i or --dry-run with this command to see +what it would do. -Usage Examples: +Usage example: ` + "```console" + ` rclone backend cleanup-hidden s3:bucket/path/to/dir -` + "```" + ` -`, +` + "```", }, { Name: "versioning", Short: "Set/get versioning support for a bucket.", @@ -3085,7 +3084,7 @@ rclone backend cleanup-hidden s3:bucket/path/to/dir passed and then returns the current versioning status for the bucket supplied. -Usage Examples: +Usage examples: ` + "```console" + ` rclone backend versioning s3:bucket # read status only @@ -3093,16 +3092,15 @@ rclone backend versioning s3:bucket Enabled rclone backend versioning s3:bucket Suspended ` + "```" + ` -It may return "Enabled", "Suspended" or "Unversioned". Note that once versioning -has been enabled the status can't be set back to "Unversioned". -`, +It may return "Enabled", "Suspended" or "Unversioned". Note that once +versioning has been enabled the status can't be set back to "Unversioned".`, }, { Name: "set", Short: "Set command for updating the config parameters.", Long: `This set command can be used to update the config parameters for a running s3 backend. -Usage Examples: +Usage examples: ` + "```console" + ` rclone backend set s3: [-o opt_name=opt_value] [-o opt_name2=opt_value2] @@ -3116,8 +3114,7 @@ This rebuilds the connection to the s3 backend when it is called with the new parameters. Only new parameters need be passed as the values will default to those currently in use. -It doesn't return anything. -`, +It doesn't return anything.`, }} // Command the backend to run a named command