Skip to content

Commit 72c561d

Browse files
committed
bisync: auto-generate rc help docs
This adds a go generate ./cmd/bisync command to autogenerate the bisync rc docs, including the list of params.
1 parent b864c4f commit 72c561d

File tree

7 files changed

+178
-57
lines changed

7 files changed

+178
-57
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ MANUAL.txt: MANUAL.md
146146

147147
commanddocs: rclone
148148
go generate ./lib/transform
149+
go generate ./cmd/bisync
149150
-@rmdir -p '$$HOME/.config/rclone'
150151
XDG_CACHE_HOME="" XDG_CONFIG_HOME="" HOME="\$$HOME" USER="\$$USER" rclone gendocs --config=/notfound docs/content/
151152
@[ ! -e '$$HOME' ] || (echo 'Error: created unwanted directory named $$HOME' && exit 1)

cmd/bisync/cmd.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,14 +127,14 @@ func init() {
127127
// and the Command line syntax section of docs/content/bisync.md (it doesn't update automatically)
128128
flags.BoolVarP(cmdFlags, &Opt.Resync, "resync", "1", Opt.Resync, "Performs the resync run. Equivalent to --resync-mode path1. Consider using --verbose or --dry-run first.", "")
129129
flags.FVarP(cmdFlags, &Opt.ResyncMode, "resync-mode", "", "During resync, prefer the version that is: path1, path2, newer, older, larger, smaller (default: path1 if --resync, otherwise none for no resync.)", "")
130-
flags.BoolVarP(cmdFlags, &Opt.CheckAccess, "check-access", "", Opt.CheckAccess, makeHelp("Ensure expected {CHECKFILE} files are found on both Path1 and Path2 filesystems, else abort."), "")
131-
flags.StringVarP(cmdFlags, &Opt.CheckFilename, "check-filename", "", Opt.CheckFilename, makeHelp("Filename for --check-access (default: {CHECKFILE})"), "")
130+
flags.BoolVarP(cmdFlags, &Opt.CheckAccess, "check-access", "", Opt.CheckAccess, MakeHelp("Ensure expected {CHECKFILE} files are found on both Path1 and Path2 filesystems, else abort."), "")
131+
flags.StringVarP(cmdFlags, &Opt.CheckFilename, "check-filename", "", Opt.CheckFilename, MakeHelp("Filename for --check-access (default: {CHECKFILE})"), "")
132132
flags.BoolVarP(cmdFlags, &Opt.Force, "force", "", Opt.Force, "Bypass --max-delete safety check and run the sync. Consider using with --verbose", "")
133133
flags.FVarP(cmdFlags, &Opt.CheckSync, "check-sync", "", "Controls comparison of final listings: true|false|only (default: true)", "")
134134
flags.BoolVarP(cmdFlags, &Opt.CreateEmptySrcDirs, "create-empty-src-dirs", "", Opt.CreateEmptySrcDirs, "Sync creation and deletion of empty directories. (Not compatible with --remove-empty-dirs)", "")
135135
flags.BoolVarP(cmdFlags, &Opt.RemoveEmptyDirs, "remove-empty-dirs", "", Opt.RemoveEmptyDirs, "Remove ALL empty directories at the final cleanup step.", "")
136136
flags.StringVarP(cmdFlags, &Opt.FiltersFile, "filters-file", "", Opt.FiltersFile, "Read filtering patterns from a file", "")
137-
flags.StringVarP(cmdFlags, &Opt.Workdir, "workdir", "", Opt.Workdir, makeHelp("Use custom working dir - useful for testing. (default: {WORKDIR})"), "")
137+
flags.StringVarP(cmdFlags, &Opt.Workdir, "workdir", "", Opt.Workdir, MakeHelp("Use custom working dir - useful for testing. (default: {WORKDIR})"), "")
138138
flags.StringVarP(cmdFlags, &Opt.BackupDir1, "backup-dir1", "", Opt.BackupDir1, "--backup-dir for Path1. Must be a non-overlapping path on the same remote.", "")
139139
flags.StringVarP(cmdFlags, &Opt.BackupDir2, "backup-dir2", "", Opt.BackupDir2, "--backup-dir for Path2. Must be a non-overlapping path on the same remote.", "")
140140
flags.StringVarP(cmdFlags, &Opt.DebugName, "debugname", "", Opt.DebugName, "Debug by tracking one file at various points throughout a bisync run (when -v or -vv)", "")
@@ -153,6 +153,7 @@ func init() {
153153
flags.StringVarP(cmdFlags, &Opt.ConflictSuffixFlag, "conflict-suffix", "", Opt.ConflictSuffixFlag, "Suffix to use when renaming a --conflict-loser. Can be either one string or two comma-separated strings to assign different suffixes to Path1/Path2. (default: 'conflict')", "")
154154
_ = cmdFlags.MarkHidden("debugname")
155155
_ = cmdFlags.MarkHidden("localtime")
156+
addRC()
156157
}
157158

158159
// bisync command definition

cmd/bisync/help.go

Lines changed: 65 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,77 @@
1-
package bisync
1+
//go:build none
2+
3+
// Create the help text for the rc
4+
//
5+
// Run with go generate ./cmd/bisync (defined in rc.go)
6+
package main
27

38
import (
4-
"strconv"
9+
"fmt"
10+
"os"
511
"strings"
12+
13+
"github.com/muesli/reflow/wordwrap"
14+
"github.com/rclone/rclone/cmd"
15+
"github.com/rclone/rclone/cmd/bisync"
16+
"github.com/rclone/rclone/fs"
17+
"github.com/spf13/pflag"
18+
"golang.org/x/text/cases"
19+
"golang.org/x/text/language"
620
)
721

8-
func makeHelp(help string) string {
9-
replacer := strings.NewReplacer(
10-
"|", "`",
11-
"{MAXDELETE}", strconv.Itoa(DefaultMaxDelete),
12-
"{CHECKFILE}", DefaultCheckFilename,
13-
// "{WORKDIR}", DefaultWorkdir,
14-
)
15-
return replacer.Replace(help)
22+
// Output the help to stdout
23+
func main() {
24+
out := os.Stdout
25+
if len(os.Args) > 1 {
26+
var err error
27+
out, err = os.Create(os.Args[1])
28+
if err != nil {
29+
fs.Fatalf(nil, "Open output failed: %v", err)
30+
}
31+
defer out.Close()
32+
}
33+
fmt.Fprintf(out, "<!--- Docs generated by help.go - use go generate to rebuild - DO NOT EDIT --->\n\n")
34+
fmt.Fprint(out, RcHelp())
1635
}
1736

18-
var shortHelp = `Perform bidirectional synchronization between two paths.`
19-
20-
var rcHelp = makeHelp(`This takes the following parameters
21-
22-
- path1 - a remote directory string e.g. |drive:path1|
23-
- path2 - a remote directory string e.g. |drive:path2|
24-
- dryRun - dry-run mode
25-
- resync - performs the resync run
26-
- checkAccess - abort if {CHECKFILE} files are not found on both filesystems
27-
- checkFilename - file name for checkAccess (default: {CHECKFILE})
28-
- maxDelete - abort sync if percentage of deleted files is above
29-
this threshold (default: {MAXDELETE})
30-
- force - Bypass maxDelete safety check and run the sync
31-
- checkSync - |true| by default, |false| disables comparison of final listings,
32-
|only| will skip sync, only compare listings from the last run
33-
- createEmptySrcDirs - Sync creation and deletion of empty directories.
34-
(Not compatible with --remove-empty-dirs)
35-
- removeEmptyDirs - remove empty directories at the final cleanup step
36-
- filtersFile - read filtering patterns from a file
37-
- ignoreListingChecksum - Do not use checksums for listings
38-
- resilient - Allow future runs to retry after certain less-serious errors, instead of requiring resync.
39-
- workdir - server directory for history files (default: |~/.cache/rclone/bisync|)
40-
- backupdir1 - --backup-dir for Path1. Must be a non-overlapping path on the same remote.
41-
- backupdir2 - --backup-dir for Path2. Must be a non-overlapping path on the same remote.
42-
- noCleanup - retain working files
37+
// RcHelp returns the rc help
38+
func RcHelp() string {
39+
return wordwrap.String(bisync.MakeHelp(`This takes the following parameters:
4340
41+
- path1 (required) - (string) a remote directory string e.g. ||drive:path1||
42+
- path2 (required) - (string) a remote directory string e.g. ||drive:path2||
43+
- dryRun - (bool) dry-run mode
44+
`+GenerateParams()+`
4445
See [bisync command help](https://rclone.org/commands/rclone_bisync/)
4546
and [full bisync description](https://rclone.org/bisync/)
46-
for more information.`)
47-
48-
var longHelp = shortHelp + makeHelp(`
49-
50-
[Bisync](https://rclone.org/bisync/) provides a
51-
bidirectional cloud sync solution in rclone.
52-
It retains the Path1 and Path2 filesystem listings from the prior run.
53-
On each successive run it will:
54-
55-
- list files on Path1 and Path2, and check for changes on each side.
56-
Changes include |New|, |Newer|, |Older|, and |Deleted| files.
57-
- Propagate changes on Path1 to Path2, and vice-versa.
47+
for more information.
48+
`), 76)
49+
}
5850

59-
Bisync is considered an **advanced command**, so use with care.
60-
Make sure you have read and understood the entire [manual](https://rclone.org/bisync)
61-
(especially the [Limitations](https://rclone.org/bisync/#limitations) section)
62-
before using, or data loss can result. Questions can be asked in the
63-
[Rclone Forum](https://forum.rclone.org/).
51+
// example: "create-empty-src-dirs" -> "createEmptySrcDirs"
52+
func toCamel(s string) string {
53+
split := strings.Split(s, "-")
54+
builder := strings.Builder{}
55+
for i, word := range split {
56+
if i == 0 { // first word always all lowercase
57+
builder.WriteString(strings.ToLower(word))
58+
continue
59+
}
60+
builder.WriteString(cases.Title(language.AmericanEnglish).String(word))
61+
}
62+
return builder.String()
63+
}
6464

65-
See [full bisync description](https://rclone.org/bisync/) for details.`)
65+
// GenerateParams automatically generates the param list from commandDefinition.Flags
66+
func GenerateParams() string {
67+
builder := strings.Builder{}
68+
fn := func(flag *pflag.Flag) {
69+
if flag.Hidden {
70+
return
71+
}
72+
builder.WriteString(fmt.Sprintf("- %s - (%s) %s \n", toCamel(flag.Name), flag.Value.Type(), flag.Usage))
73+
}
74+
commandDefinition, _, _ := cmd.Root.Find([]string{"bisync"})
75+
commandDefinition.Flags().VisitAll(fn)
76+
return builder.String()
77+
}

cmd/bisync/rc.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
1+
//go:generate go run help.go rc.md
12
package bisync
23

34
import (
45
"context"
6+
_ "embed"
57
"errors"
68
"log"
79
"path/filepath"
10+
"strconv"
11+
"strings"
812

913
"github.com/rclone/rclone/cmd/bisync/bilib"
1014
"github.com/rclone/rclone/fs"
1115
fslog "github.com/rclone/rclone/fs/log"
1216
"github.com/rclone/rclone/fs/rc"
1317
)
1418

15-
func init() {
19+
func addRC() {
1620
rc.Add(rc.Call{
1721
Path: "sync/bisync",
1822
AuthRequired: true,
@@ -22,6 +26,42 @@ func init() {
2226
})
2327
}
2428

29+
//go:embed rc.md
30+
var rcHelp string
31+
32+
var shortHelp = `Perform bidirectional synchronization between two paths.`
33+
34+
var longHelp = shortHelp + MakeHelp(`
35+
36+
[Bisync](https://rclone.org/bisync/) provides a
37+
bidirectional cloud sync solution in rclone.
38+
It retains the Path1 and Path2 filesystem listings from the prior run.
39+
On each successive run it will:
40+
41+
- list files on Path1 and Path2, and check for changes on each side.
42+
Changes include ||New||, ||Newer||, ||Older||, and ||Deleted|| files.
43+
- Propagate changes on Path1 to Path2, and vice-versa.
44+
45+
Bisync is considered an **advanced command**, so use with care.
46+
Make sure you have read and understood the entire [manual](https://rclone.org/bisync)
47+
(especially the [Limitations](https://rclone.org/bisync/#limitations) section)
48+
before using, or data loss can result. Questions can be asked in the
49+
[Rclone Forum](https://forum.rclone.org/).
50+
51+
See [full bisync description](https://rclone.org/bisync/) for details.
52+
`)
53+
54+
// MakeHelp replaces some dynamic variables for the help docs
55+
func MakeHelp(help string) string {
56+
replacer := strings.NewReplacer(
57+
"||", "`",
58+
"{MAXDELETE}", strconv.Itoa(DefaultMaxDelete),
59+
"{CHECKFILE}", DefaultCheckFilename,
60+
"{WORKDIR}", DefaultWorkdir,
61+
)
62+
return replacer.Replace(help)
63+
}
64+
2565
func rcBisync(ctx context.Context, in rc.Params) (out rc.Params, err error) {
2666
opt := &Options{}
2767
octx, ci := fs.AddConfig(ctx)

cmd/bisync/rc.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<!--- Docs generated by help.go - use go generate to rebuild - DO NOT EDIT --->
2+
3+
This takes the following parameters:
4+
5+
- path1 (required) - (string) a remote directory string e.g. `drive:path1`
6+
- path2 (required) - (string) a remote directory string e.g. `drive:path2`
7+
- dryRun - (bool) dry-run mode
8+
- backupDir1 - (string) --backup-dir for Path1. Must be a non-overlapping path on
9+
the same remote.
10+
- backupDir2 - (string) --backup-dir for Path2. Must be a non-overlapping path on
11+
the same remote.
12+
- checkAccess - (bool) Ensure expected RCLONE_TEST files are found on both
13+
Path1 and Path2 filesystems, else abort.
14+
- checkFilename - (string) Filename for --check-access (default: RCLONE_TEST)
15+
- checkSync - (string) Controls comparison of final listings: true|false|only
16+
(default: true)
17+
- compare - (string) Comma-separated list of bisync-specific compare options ex.
18+
'size,modtime,checksum' (default: 'size,modtime')
19+
- conflictLoser - (ConflictLoserAction) Action to take on the loser of a sync
20+
conflict (when there is a winner) or on both files (when there is no
21+
winner): , num, pathname, delete (default: num)
22+
- conflictResolve - (string) Automatically resolve conflicts by preferring the
23+
version that is: none, path1, path2, newer, older, larger, smaller (default:
24+
none)
25+
- conflictSuffix - (string) Suffix to use when renaming a --conflict-loser. Can
26+
be either one string or two comma-separated strings to assign different
27+
suffixes to Path1/Path2. (default: 'conflict')
28+
- createEmptySrcDirs - (bool) Sync creation and deletion of empty directories.
29+
(Not compatible with --remove-empty-dirs)
30+
- downloadHash - (bool) Compute hash by downloading when otherwise
31+
unavailable. (warning: may be slow and use lots of data!)
32+
- filtersFile - (string) Read filtering patterns from a file
33+
- force - (bool) Bypass --max-delete safety check and run the sync. Consider
34+
using with --verbose
35+
- ignoreListingChecksum - (bool) Do not use checksums for listings (add --ignore-
36+
checksum to additionally skip post-copy checksum checks)
37+
- maxLock - (Duration) Consider lock files older than this to be expired
38+
(default: 0 (never expire)) (minimum: 2m)
39+
- noCleanup - (bool) Retain working files (useful for troubleshooting and
40+
testing).
41+
- noSlowHash - (bool) Ignore listing checksums only on backends where they are
42+
slow
43+
- recover - (bool) Automatically recover from interruptions without requiring --
44+
resync.
45+
- removeEmptyDirs - (bool) Remove ALL empty directories at the final cleanup
46+
step.
47+
- resilient - (bool) Allow future runs to retry after certain less-serious
48+
errors, instead of requiring --resync.
49+
- resync - (bool) Performs the resync run. Equivalent to --resync-mode path1.
50+
Consider using --verbose or --dry-run first.
51+
- resyncMode - (string) During resync, prefer the version that is: path1,
52+
path2, newer, older, larger, smaller (default: path1 if --resync, otherwise
53+
none for no resync.)
54+
- slowHashSyncOnly - (bool) Ignore slow checksums for listings and deltas, but
55+
still consider them during sync calls.
56+
- workdir - (string) Use custom working dir - useful for testing. (default:
57+
~/.cache/rclone/bisync)
58+
59+
See [bisync command help](https://rclone.org/commands/rclone_bisync/)
60+
and [full bisync description](https://rclone.org/bisync/)
61+
for more information.

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ require (
6060
github.com/minio/minio-go/v7 v7.0.98
6161
github.com/mitchellh/go-homedir v1.1.0
6262
github.com/moby/sys/mountinfo v0.7.2
63+
github.com/muesli/reflow v0.3.0
6364
github.com/ncw/swift/v2 v2.0.5
6465
github.com/oracle/oci-go-sdk/v65 v65.108.2
6566
github.com/patrickmn/go-cache v2.1.0+incompatible

go.sum

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,7 @@ github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stg
499499
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
500500
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
501501
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
502+
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
502503
github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ=
503504
github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
504505
github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ=
@@ -523,6 +524,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
523524
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
524525
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
525526
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
527+
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
528+
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
526529
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
527530
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
528531
github.com/ncw/swift/v2 v2.0.5 h1:9o5Gsd7bInAFEqsGPcaUdsboMbqf8lnNtxqWKFT9iz8=
@@ -598,6 +601,8 @@ github.com/relvacode/iso8601 v1.7.0 h1:BXy+V60stMP6cpswc+a93Mq3e65PfXCgDFfhvNNGr
598601
github.com/relvacode/iso8601 v1.7.0/go.mod h1:FlNp+jz+TXpyRqgmM7tnzHHzBnz776kmAH2h3sZCn0I=
599602
github.com/rfjakob/eme v1.2.0 h1:8dAHL+WVAw06+7DkRKnRiFp1JL3QjcJEZFqDnndUaSI=
600603
github.com/rfjakob/eme v1.2.0/go.mod h1:cVvpasglm/G3ngEfcfT/Wt0GwhkuO32pf/poW6Nyk1k=
604+
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
605+
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
601606
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
602607
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
603608
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=

0 commit comments

Comments
 (0)