refactor to use constructor functions to create cobra commands

This allows getting rid of the global options variables
This commit is contained in:
Michael Eischer 2025-02-06 23:02:00 +01:00
parent dc9b6378f3
commit aacd6a47e3
35 changed files with 755 additions and 602 deletions

View File

@ -31,7 +31,10 @@ import (
"github.com/restic/restic/internal/ui/termstatus"
)
var cmdBackup = &cobra.Command{
func newBackupCommand() *cobra.Command {
var opts BackupOptions
cmd := &cobra.Command{
Use: "backup [flags] [FILE/DIR] ...",
Short: "Create a new backup of files and/or directories",
Long: `
@ -49,13 +52,13 @@ Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect.
`,
PreRun: func(_ *cobra.Command, _ []string) {
if backupOptions.Host == "" {
if opts.Host == "" {
hostname, err := os.Hostname()
if err != nil {
debug.Log("os.Hostname() returned err: %v", err)
return
}
backupOptions.Host = hostname
opts.Host = hostname
}
},
GroupID: cmdGroupDefault,
@ -63,10 +66,18 @@ Exit status is 12 if the password is incorrect.
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runBackup(cmd.Context(), backupOptions, globalOptions, term, args)
return runBackup(cmd.Context(), opts, globalOptions, term, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newBackupCommand())
}
// BackupOptions bundles all options for the backup command.
type BackupOptions struct {
filter.ExcludePatternOptions
@ -147,17 +158,11 @@ func (opts *BackupOptions) AddFlags(f *pflag.FlagSet) {
}
}
var backupOptions BackupOptions
var backupFSTestHook func(fs fs.FS) fs.FS
// ErrInvalidSourceData is used to report an incomplete backup
var ErrInvalidSourceData = errors.New("at least one source file could not be read")
func init() {
cmdRoot.AddCommand(cmdBackup)
backupOptions.AddFlags(cmdBackup.Flags())
}
// filterExisting returns a slice of all existing items, or an error if no
// items exist at all.
func filterExisting(items []string) (result []string, err error) {

View File

@ -16,7 +16,10 @@ import (
"github.com/spf13/pflag"
)
var cmdCache = &cobra.Command{
func newCacheCommand() *cobra.Command {
var opts CacheOptions
cmd := &cobra.Command{
Use: "cache",
Short: "Operate on local cache directories",
Long: `
@ -31,10 +34,18 @@ Exit status is 1 if there was any error.
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(_ *cobra.Command, args []string) error {
return runCache(cacheOptions, globalOptions, args)
return runCache(opts, globalOptions, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newCacheCommand())
}
// CacheOptions bundles all options for the snapshots command.
type CacheOptions struct {
Cleanup bool
@ -48,13 +59,6 @@ func (opts *CacheOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.NoSize, "no-size", false, "do not output the size of the cache directories")
}
var cacheOptions CacheOptions
func init() {
cmdRoot.AddCommand(cmdCache)
cacheOptions.AddFlags(cmdCache.Flags())
}
func runCache(opts CacheOptions, gopts GlobalOptions, args []string) error {
if len(args) > 0 {
return errors.Fatal("the cache command expects no arguments, only options - please see `restic help cache` for usage and flags")

View File

@ -14,7 +14,8 @@ import (
var catAllowedCmds = []string{"config", "index", "snapshot", "key", "masterkey", "lock", "pack", "blob", "tree"}
var cmdCat = &cobra.Command{
func newCatCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "cat [flags] [masterkey|config|pack ID|blob ID|snapshot ID|index ID|key ID|lock ID|tree snapshot:subfolder]",
Short: "Print internal objects to stdout",
Long: `
@ -36,9 +37,11 @@ Exit status is 12 if the password is incorrect.
},
ValidArgs: catAllowedCmds,
}
return cmd
}
func init() {
cmdRoot.AddCommand(cmdCat)
cmdRoot.AddCommand(newCatCommand())
}
func validateCatArgs(args []string) error {

View File

@ -23,7 +23,9 @@ import (
"github.com/restic/restic/internal/ui/termstatus"
)
var cmdCheck = &cobra.Command{
func newCheckCommand() *cobra.Command {
var opts CheckOptions
cmd := &cobra.Command{
Use: "check [flags]",
Short: "Check the repository for errors",
Long: `
@ -47,7 +49,7 @@ Exit status is 12 if the password is incorrect.
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
summary, err := runCheck(cmd.Context(), checkOptions, globalOptions, args, term)
summary, err := runCheck(cmd.Context(), opts, globalOptions, args, term)
if globalOptions.JSON {
if err != nil && summary.NumErrors == 0 {
summary.NumErrors = 1
@ -57,10 +59,18 @@ Exit status is 12 if the password is incorrect.
return err
},
PreRunE: func(_ *cobra.Command, _ []string) error {
return checkFlags(checkOptions)
return checkFlags(opts)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newCheckCommand())
}
// CheckOptions bundles all options for the 'check' command.
type CheckOptions struct {
ReadData bool
@ -82,13 +92,6 @@ func (opts *CheckOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.WithCache, "with-cache", false, "use existing cache, only read uncached data from repository")
}
var checkOptions CheckOptions
func init() {
cmdRoot.AddCommand(cmdCheck)
checkOptions.AddFlags(cmdCheck.Flags())
}
func checkFlags(opts CheckOptions) error {
if opts.ReadData && opts.ReadDataSubset != "" {
return errors.Fatal("check flags --read-data and --read-data-subset cannot be used together")

View File

@ -14,7 +14,9 @@ import (
"github.com/spf13/pflag"
)
var cmdCopy = &cobra.Command{
func newCopyCommand() *cobra.Command {
var opts CopyOptions
cmd := &cobra.Command{
Use: "copy [flags] [snapshotID ...]",
Short: "Copy snapshots from one repository to another",
Long: `
@ -44,10 +46,18 @@ Exit status is 12 if the password is incorrect.
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runCopy(cmd.Context(), copyOptions, globalOptions, args)
return runCopy(cmd.Context(), opts, globalOptions, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newCopyCommand())
}
// CopyOptions bundles all options for the copy command.
type CopyOptions struct {
secondaryRepoOptions
@ -59,13 +69,6 @@ func (opts *CopyOptions) AddFlags(f *pflag.FlagSet) {
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
}
var copyOptions CopyOptions
func init() {
cmdRoot.AddCommand(cmdCopy)
copyOptions.AddFlags(cmdCopy.Flags())
}
func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args []string) error {
secondaryGopts, isFromRepo, err := fillSecondaryGlobalOpts(ctx, opts.secondaryRepoOptions, gopts, "destination")
if err != nil {

View File

@ -29,14 +29,24 @@ import (
"github.com/restic/restic/internal/restic"
)
var cmdDebug = &cobra.Command{
func newDebugCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "debug",
Short: "Debug commands",
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
}
cmd.AddCommand(newDebugDumpCommand())
cmd.AddCommand(newDebugExamineCommand())
return cmd
}
var cmdDebugDump = &cobra.Command{
func init() {
cmdRoot.AddCommand(newDebugCommand())
}
func newDebugDumpCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "dump [indexes|snapshots|all|packs]",
Short: "Dump data structures",
Long: `
@ -57,6 +67,24 @@ Exit status is 12 if the password is incorrect.
return runDebugDump(cmd.Context(), globalOptions, args)
},
}
return cmd
}
func newDebugExamineCommand() *cobra.Command {
var opts DebugExamineOptions
cmd := &cobra.Command{
Use: "examine pack-ID...",
Short: "Examine a pack file",
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runDebugExamine(cmd.Context(), globalOptions, opts, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
type DebugExamineOptions struct {
TryRepair bool
@ -72,15 +100,6 @@ func (opts *DebugExamineOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.RepairByte, "repair-byte", false, "try to repair broken blobs by trying bytes")
}
var debugExamineOpts DebugExamineOptions
func init() {
cmdRoot.AddCommand(cmdDebug)
cmdDebug.AddCommand(cmdDebugDump)
cmdDebug.AddCommand(cmdDebugExamine)
debugExamineOpts.AddFlags(cmdDebugExamine.Flags())
}
func prettyPrintJSON(wr io.Writer, item interface{}) error {
buf, err := json.MarshalIndent(item, "", " ")
if err != nil {
@ -197,15 +216,6 @@ func runDebugDump(ctx context.Context, gopts GlobalOptions, args []string) error
}
}
var cmdDebugExamine = &cobra.Command{
Use: "examine pack-ID...",
Short: "Examine a pack file",
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runDebugExamine(cmd.Context(), globalOptions, debugExamineOpts, args)
},
}
func tryRepairWithBitflip(ctx context.Context, key *crypto.Key, input []byte, bytewise bool) []byte {
if bytewise {
Printf(" trying to repair blob by finding a broken byte\n")

View File

@ -15,7 +15,10 @@ import (
"github.com/spf13/pflag"
)
var cmdDiff = &cobra.Command{
func newDiffCommand() *cobra.Command {
var opts DiffOptions
cmd := &cobra.Command{
Use: "diff [flags] snapshotID snapshotID",
Short: "Show differences between two snapshots",
Long: `
@ -49,10 +52,18 @@ Exit status is 12 if the password is incorrect.
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runDiff(cmd.Context(), diffOptions, globalOptions, args)
return runDiff(cmd.Context(), opts, globalOptions, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newDiffCommand())
}
// DiffOptions collects all options for the diff command.
type DiffOptions struct {
ShowMetadata bool
@ -62,13 +73,6 @@ func (opts *DiffOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.ShowMetadata, "metadata", false, "print changes in metadata")
}
var diffOptions DiffOptions
func init() {
cmdRoot.AddCommand(cmdDiff)
diffOptions.AddFlags(cmdDiff.Flags())
}
func loadSnapshot(ctx context.Context, be restic.Lister, repo restic.LoaderUnpacked, desc string) (*restic.Snapshot, string, error) {
sn, subfolder, err := restic.FindSnapshot(ctx, be, repo, desc)
if err != nil {

View File

@ -16,7 +16,9 @@ import (
"github.com/spf13/pflag"
)
var cmdDump = &cobra.Command{
func newDumpCommand() *cobra.Command {
var opts DumpOptions
cmd := &cobra.Command{
Use: "dump [flags] snapshotID file",
Short: "Print a backed-up file to stdout",
Long: `
@ -44,10 +46,18 @@ Exit status is 12 if the password is incorrect.
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runDump(cmd.Context(), dumpOptions, globalOptions, args)
return runDump(cmd.Context(), opts, globalOptions, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newDumpCommand())
}
// DumpOptions collects all options for the dump command.
type DumpOptions struct {
restic.SnapshotFilter
@ -61,13 +71,6 @@ func (opts *DumpOptions) AddFlags(f *pflag.FlagSet) {
f.StringVarP(&opts.Target, "target", "t", "", "write the output to target `path`")
}
var dumpOptions DumpOptions
func init() {
cmdRoot.AddCommand(cmdDump)
dumpOptions.AddFlags(cmdDump.Flags())
}
func splitPath(p string) []string {
d, f := path.Split(p)
if d == "" || d == "/" {

View File

@ -10,7 +10,8 @@ import (
"github.com/spf13/cobra"
)
var featuresCmd = &cobra.Command{
func newFeaturesCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "features",
Short: "Print list of feature flags",
Long: `
@ -54,6 +55,9 @@ Exit status is 1 if there was any error.
},
}
func init() {
cmdRoot.AddCommand(featuresCmd)
return cmd
}
func init() {
cmdRoot.AddCommand(newFeaturesCommand())
}

View File

@ -17,7 +17,10 @@ import (
"github.com/restic/restic/internal/walker"
)
var cmdFind = &cobra.Command{
func newFindCommand() *cobra.Command {
var opts FindOptions
cmd := &cobra.Command{
Use: "find [flags] PATTERN...",
Short: "Find a file, a directory or restic IDs",
Long: `
@ -45,10 +48,18 @@ Exit status is 12 if the password is incorrect.
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runFind(cmd.Context(), findOptions, globalOptions, args)
return runFind(cmd.Context(), opts, globalOptions, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newFindCommand())
}
// FindOptions bundles all options for the find command.
type FindOptions struct {
Oldest string
@ -79,13 +90,6 @@ func (opts *FindOptions) AddFlags(f *pflag.FlagSet) {
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
}
var findOptions FindOptions
func init() {
cmdRoot.AddCommand(cmdFind)
findOptions.AddFlags(cmdFind.Flags())
}
type findPattern struct {
oldest, newest time.Time
pattern []string

View File

@ -14,7 +14,11 @@ import (
"github.com/spf13/pflag"
)
var cmdForget = &cobra.Command{
func newForgetCommand() *cobra.Command {
var opts ForgetOptions
var pruneOpts PruneOptions
cmd := &cobra.Command{
Use: "forget [flags] [snapshot ID] [...]",
Short: "Remove snapshots from the repository",
Long: `
@ -46,10 +50,19 @@ Exit status is 12 if the password is incorrect.
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runForget(cmd.Context(), forgetOptions, forgetPruneOptions, globalOptions, term, args)
return runForget(cmd.Context(), opts, pruneOpts, globalOptions, term, args)
},
}
opts.AddFlags(cmd.Flags())
pruneOpts.AddLimitedFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newForgetCommand())
}
type ForgetPolicyCount int
var ErrNegativePolicyCount = errors.New("negative values not allowed, use 'unlimited' instead")
@ -145,15 +158,6 @@ func (opts *ForgetOptions) AddFlags(f *pflag.FlagSet) {
f.SortFlags = false
}
var forgetOptions ForgetOptions
var forgetPruneOptions PruneOptions
func init() {
cmdRoot.AddCommand(cmdForget)
forgetOptions.AddFlags(cmdForget.Flags())
forgetPruneOptions.AddLimitedFlags(cmdForget.Flags())
}
func verifyForgetOptions(opts *ForgetOptions) error {
if opts.Last < -1 || opts.Hourly < -1 || opts.Daily < -1 || opts.Weekly < -1 ||
opts.Monthly < -1 || opts.Yearly < -1 {

View File

@ -11,7 +11,10 @@ import (
"github.com/spf13/pflag"
)
var cmdGenerate = &cobra.Command{
func newGenerateCommand() *cobra.Command {
var opts generateOptions
cmd := &cobra.Command{
Use: "generate [flags]",
Short: "Generate manual pages and auto-completion files (bash, fish, zsh, powershell)",
Long: `
@ -26,9 +29,16 @@ Exit status is 1 if there was any error.
`,
DisableAutoGenTag: true,
RunE: func(_ *cobra.Command, args []string) error {
return runGenerate(genOpts, args)
return runGenerate(opts, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newGenerateCommand())
}
type generateOptions struct {
ManDir string
@ -46,13 +56,6 @@ func (opts *generateOptions) AddFlags(f *pflag.FlagSet) {
f.StringVar(&opts.PowerShellCompletionFile, "powershell-completion", "", "write powershell completion `file` (`-` for stdout)")
}
var genOpts generateOptions
func init() {
cmdRoot.AddCommand(cmdGenerate)
genOpts.AddFlags(cmdGenerate.Flags())
}
func writeManpages(dir string) error {
// use a fixed date for the man pages so that generating them is deterministic
date, err := time.Parse("Jan 2006", "Jan 2017")

View File

@ -15,7 +15,10 @@ import (
"github.com/spf13/pflag"
)
var cmdInit = &cobra.Command{
func newInitCommand() *cobra.Command {
var opts InitOptions
cmd := &cobra.Command{
Use: "init",
Short: "Initialize a new repository",
Long: `
@ -30,9 +33,16 @@ Exit status is 1 if there was any error.
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runInit(cmd.Context(), initOptions, globalOptions, args)
return runInit(cmd.Context(), opts, globalOptions, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newInitCommand())
}
// InitOptions bundles all options for the init command.
type InitOptions struct {
@ -47,13 +57,6 @@ func (opts *InitOptions) AddFlags(f *pflag.FlagSet) {
f.StringVar(&opts.RepositoryVersion, "repository-version", "stable", "repository format version to use, allowed values are a format version, 'latest' and 'stable'")
}
var initOptions InitOptions
func init() {
cmdRoot.AddCommand(cmdInit)
initOptions.AddFlags(cmdInit.Flags())
}
func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args []string) error {
if len(args) > 0 {
return errors.Fatal("the init command expects no arguments, only options - please see `restic help init` for usage and flags")

View File

@ -10,7 +10,10 @@ import (
"github.com/spf13/pflag"
)
var cmdKeyAdd = &cobra.Command{
func newKeyAddCommand() *cobra.Command {
var opts KeyAddOptions
cmd := &cobra.Command{
Use: "add",
Short: "Add a new key (password) to the repository; returns the new key ID",
Long: `
@ -26,6 +29,13 @@ Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runKeyAdd(cmd.Context(), globalOptions, opts, args)
},
}
opts.Add(cmd.Flags())
return cmd
}
type KeyAddOptions struct {
@ -43,13 +53,7 @@ func (opts *KeyAddOptions) Add(flags *pflag.FlagSet) {
}
func init() {
cmdKey.AddCommand(cmdKeyAdd)
var keyAddOpts KeyAddOptions
keyAddOpts.Add(cmdKeyAdd.Flags())
cmdKeyAdd.RunE = func(cmd *cobra.Command, args []string) error {
return runKeyAdd(cmd.Context(), globalOptions, keyAddOpts, args)
}
cmdKey.AddCommand(newKeyAddCommand())
}
func runKeyAdd(ctx context.Context, gopts GlobalOptions, opts KeyAddOptions, args []string) error {

View File

@ -12,7 +12,8 @@ import (
"github.com/spf13/cobra"
)
var cmdKeyList = &cobra.Command{
func newKeyListCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List keys (passwords)",
Long: `
@ -34,9 +35,11 @@ Exit status is 12 if the password is incorrect.
return runKeyList(cmd.Context(), globalOptions, args)
},
}
return cmd
}
func init() {
cmdKey.AddCommand(cmdKeyList)
cmdKey.AddCommand(newKeyListCommand())
}
func runKeyList(ctx context.Context, gopts GlobalOptions, args []string) error {

View File

@ -7,9 +7,13 @@ import (
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
var cmdKeyPasswd = &cobra.Command{
func newKeyPasswdCommand() *cobra.Command {
var opts KeyPasswdOptions
cmd := &cobra.Command{
Use: "passwd",
Short: "Change key (password); creates a new key ID and removes the old key ID, returns new key ID",
Long: `
@ -26,20 +30,25 @@ Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runKeyPasswd(cmd.Context(), globalOptions, opts, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdKey.AddCommand(newKeyPasswdCommand())
}
type KeyPasswdOptions struct {
KeyAddOptions
}
func init() {
cmdKey.AddCommand(cmdKeyPasswd)
var keyPasswdOpts KeyPasswdOptions
keyPasswdOpts.KeyAddOptions.Add(cmdKeyPasswd.Flags())
cmdKeyPasswd.RunE = func(cmd *cobra.Command, args []string) error {
return runKeyPasswd(cmd.Context(), globalOptions, keyPasswdOpts, args)
}
func (opts *KeyPasswdOptions) AddFlags(flags *pflag.FlagSet) {
opts.KeyAddOptions.Add(flags)
}
func runKeyPasswd(ctx context.Context, gopts GlobalOptions, opts KeyPasswdOptions, args []string) error {

View File

@ -10,7 +10,8 @@ import (
"github.com/spf13/cobra"
)
var cmdKeyRemove = &cobra.Command{
func newKeyRemoveCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "remove [ID]",
Short: "Remove key ID (password) from the repository.",
Long: `
@ -31,9 +32,11 @@ Exit status is 12 if the password is incorrect.
return runKeyRemove(cmd.Context(), globalOptions, args)
},
}
return cmd
}
func init() {
cmdKey.AddCommand(cmdKeyRemove)
cmdKey.AddCommand(newKeyRemoveCommand())
}
func runKeyRemove(ctx context.Context, gopts GlobalOptions, args []string) error {

View File

@ -11,10 +11,11 @@ import (
"github.com/spf13/cobra"
)
func newListCommand() *cobra.Command {
var listAllowedArgs = []string{"blobs", "packs", "index", "snapshots", "keys", "locks"}
var listAllowedArgsUseString = strings.Join(listAllowedArgs, "|")
var cmdList = &cobra.Command{
cmd := &cobra.Command{
Use: "list [flags] [" + listAllowedArgsUseString + "]",
Short: "List objects in the repository",
Long: `
@ -37,9 +38,11 @@ Exit status is 12 if the password is incorrect.
ValidArgs: listAllowedArgs,
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
}
return cmd
}
func init() {
cmdRoot.AddCommand(cmdList)
cmdRoot.AddCommand(newListCommand())
}
func runList(ctx context.Context, gopts GlobalOptions, args []string) error {

View File

@ -21,7 +21,10 @@ import (
"github.com/restic/restic/internal/walker"
)
var cmdLs = &cobra.Command{
func newLsCommand() *cobra.Command {
var opts LsOptions
cmd := &cobra.Command{
Use: "ls [flags] snapshotID [dir...]",
Short: "List files in a snapshot",
Long: `
@ -56,9 +59,16 @@ Exit status is 12 if the password is incorrect.
DisableAutoGenTag: true,
GroupID: cmdGroupDefault,
RunE: func(cmd *cobra.Command, args []string) error {
return runLs(cmd.Context(), lsOptions, globalOptions, args)
return runLs(cmd.Context(), opts, globalOptions, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newLsCommand())
}
// LsOptions collects all options for the ls command.
type LsOptions struct {
@ -81,13 +91,6 @@ func (opts *LsOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.Reverse, "reverse", false, "reverse sorted output")
}
var lsOptions LsOptions
func init() {
cmdRoot.AddCommand(cmdLs)
lsOptions.AddFlags(cmdLs.Flags())
}
type lsPrinter interface {
Snapshot(sn *restic.Snapshot) error
Node(path string, node *restic.Node, isPrefixDirectory bool) error

View File

@ -12,7 +12,10 @@ import (
"github.com/spf13/pflag"
)
var cmdMigrate = &cobra.Command{
func newMigrateCommand() *cobra.Command {
var opts MigrateOptions
cmd := &cobra.Command{
Use: "migrate [flags] [migration name] [...]",
Short: "Apply migrations",
Long: `
@ -34,10 +37,18 @@ Exit status is 12 if the password is incorrect.
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runMigrate(cmd.Context(), migrateOptions, globalOptions, args, term)
return runMigrate(cmd.Context(), opts, globalOptions, args, term)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newMigrateCommand())
}
// MigrateOptions bundles all options for the 'check' command.
type MigrateOptions struct {
Force bool
@ -47,13 +58,6 @@ func (opts *MigrateOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVarP(&opts.Force, "force", "f", false, `apply a migration a second time`)
}
var migrateOptions MigrateOptions
func init() {
cmdRoot.AddCommand(cmdMigrate)
migrateOptions.AddFlags(cmdMigrate.Flags())
}
func checkMigrations(ctx context.Context, repo restic.Repository, printer progress.Printer) error {
printer.P("available migrations:\n")
found := false

View File

@ -22,7 +22,10 @@ import (
"github.com/anacrolix/fuse/fs"
)
var cmdMount = &cobra.Command{
func newMountCommand() *cobra.Command {
var opts MountOptions
cmd := &cobra.Command{
Use: "mount [flags] mountpoint",
Short: "Mount the repository",
Long: `
@ -73,10 +76,18 @@ Exit status is 12 if the password is incorrect.
DisableAutoGenTag: true,
GroupID: cmdGroupDefault,
RunE: func(cmd *cobra.Command, args []string) error {
return runMount(cmd.Context(), mountOptions, globalOptions, args)
return runMount(cmd.Context(), opts, globalOptions, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newMountCommand())
}
// MountOptions collects all options for the mount command.
type MountOptions struct {
OwnerRoot bool
@ -100,13 +111,6 @@ func (opts *MountOptions) AddFlags(f *pflag.FlagSet) {
_ = f.MarkDeprecated("snapshot-template", "use --time-template")
}
var mountOptions MountOptions
func init() {
cmdRoot.AddCommand(cmdMount)
mountOptions.AddFlags(cmdMount.Flags())
}
func runMount(ctx context.Context, opts MountOptions, gopts GlobalOptions, args []string) error {
if opts.TimeTemplate == "" {
return errors.Fatal("time template string cannot be empty")

View File

@ -8,7 +8,8 @@ import (
"github.com/spf13/cobra"
)
var optionsCmd = &cobra.Command{
func newOptionsCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "options",
Short: "Print list of extended options",
Long: `
@ -35,7 +36,9 @@ Exit status is 1 if there was any error.
}
},
}
return cmd
}
func init() {
cmdRoot.AddCommand(optionsCmd)
cmdRoot.AddCommand(newOptionsCommand())
}

View File

@ -19,7 +19,10 @@ import (
"github.com/spf13/pflag"
)
var cmdPrune = &cobra.Command{
func newPruneCommand() *cobra.Command {
var opts PruneOptions
cmd := &cobra.Command{
Use: "prune [flags]",
Short: "Remove unneeded data from the repository",
Long: `
@ -40,10 +43,18 @@ Exit status is 12 if the password is incorrect.
RunE: func(cmd *cobra.Command, _ []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runPrune(cmd.Context(), pruneOptions, globalOptions, term)
return runPrune(cmd.Context(), opts, globalOptions, term)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newPruneCommand())
}
// PruneOptions collects all options for the cleanup command.
type PruneOptions struct {
DryRun bool
@ -76,13 +87,6 @@ func (opts *PruneOptions) AddLimitedFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.RepackUncompressed, "repack-uncompressed", false, "repack all uncompressed data")
}
var pruneOptions PruneOptions
func init() {
cmdRoot.AddCommand(cmdPrune)
pruneOptions.AddFlags(cmdPrune.Flags())
}
func verifyPruneOptions(opts *PruneOptions) error {
opts.MaxRepackBytes = math.MaxUint64
if len(opts.MaxRepackSize) > 0 {

View File

@ -11,7 +11,8 @@ import (
"golang.org/x/sync/errgroup"
)
var cmdRecover = &cobra.Command{
func newRecoverCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "recover [flags]",
Short: "Recover data from the repository not referenced by snapshots",
Long: `
@ -34,9 +35,11 @@ Exit status is 12 if the password is incorrect.
return runRecover(cmd.Context(), globalOptions)
},
}
return cmd
}
func init() {
cmdRoot.AddCommand(cmdRecover)
cmdRoot.AddCommand(newRecoverCommand())
}
func runRecover(ctx context.Context, gopts GlobalOptions) error {

View File

@ -9,7 +9,10 @@ import (
"github.com/spf13/pflag"
)
var cmdRepairIndex = &cobra.Command{
func newRepairIndexCommand() *cobra.Command {
var opts RepairIndexOptions
cmd := &cobra.Command{
Use: "index [flags]",
Short: "Build a new index",
Long: `
@ -29,17 +32,16 @@ Exit status is 12 if the password is incorrect.
RunE: func(cmd *cobra.Command, _ []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runRebuildIndex(cmd.Context(), repairIndexOptions, globalOptions, term)
return runRebuildIndex(cmd.Context(), opts, globalOptions, term)
},
}
var cmdRebuildIndex = &cobra.Command{
Use: "rebuild-index [flags]",
Short: cmdRepairIndex.Short,
Long: cmdRepairIndex.Long,
Deprecated: `Use "repair index" instead`,
DisableAutoGenTag: true,
RunE: cmdRepairIndex.RunE,
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRepair.AddCommand(newRepairIndexCommand())
}
// RepairIndexOptions collects all options for the repair index command.
@ -51,14 +53,32 @@ func (opts *RepairIndexOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.ReadAllPacks, "read-all-packs", false, "read all pack files to generate new index from scratch")
}
var repairIndexOptions RepairIndexOptions
func newRebuildIndexCommand() *cobra.Command {
var opts RepairIndexOptions
replacement := newRepairIndexCommand()
cmd := &cobra.Command{
Use: "rebuild-index [flags]",
Short: replacement.Short,
Long: replacement.Long,
Deprecated: `Use "repair index" instead`,
DisableAutoGenTag: true,
// must create a new instance of the run function as it captures opts
// by reference
RunE: func(cmd *cobra.Command, _ []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runRebuildIndex(cmd.Context(), opts, globalOptions, term)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRepair.AddCommand(cmdRepairIndex)
repairIndexOptions.AddFlags(cmdRepairIndex.Flags())
// add alias for old name
cmdRoot.AddCommand(cmdRebuildIndex)
repairIndexOptions.AddFlags(cmdRebuildIndex.Flags())
cmdRoot.AddCommand(newRebuildIndexCommand())
}
func runRebuildIndex(ctx context.Context, opts RepairIndexOptions, gopts GlobalOptions, term *termstatus.Terminal) error {

View File

@ -13,7 +13,8 @@ import (
"github.com/spf13/cobra"
)
var cmdRepairPacks = &cobra.Command{
func newRepairPacksCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "packs [packIDs...]",
Short: "Salvage damaged pack files",
Long: `
@ -36,9 +37,11 @@ Exit status is 12 if the password is incorrect.
return runRepairPacks(cmd.Context(), globalOptions, term, args)
},
}
return cmd
}
func init() {
cmdRepair.AddCommand(cmdRepairPacks)
cmdRepair.AddCommand(newRepairPacksCommand())
}
func runRepairPacks(ctx context.Context, gopts GlobalOptions, term *termstatus.Terminal, args []string) error {

View File

@ -11,7 +11,10 @@ import (
"github.com/spf13/pflag"
)
var cmdRepairSnapshots = &cobra.Command{
func newRepairSnapshotsCommand() *cobra.Command {
var opts RepairOptions
cmd := &cobra.Command{
Use: "snapshots [flags] [snapshot ID] [...]",
Short: "Repair snapshots",
Long: `
@ -46,10 +49,18 @@ Exit status is 12 if the password is incorrect.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runRepairSnapshots(cmd.Context(), globalOptions, repairSnapshotOptions, args)
return runRepairSnapshots(cmd.Context(), globalOptions, opts, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRepair.AddCommand(newRepairSnapshotsCommand())
}
// RepairOptions collects all options for the repair command.
type RepairOptions struct {
DryRun bool
@ -65,13 +76,6 @@ func (opts *RepairOptions) AddFlags(f *pflag.FlagSet) {
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
}
var repairSnapshotOptions RepairOptions
func init() {
cmdRepair.AddCommand(cmdRepairSnapshots)
repairSnapshotOptions.AddFlags(cmdRepairSnapshots.Flags())
}
func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOptions, args []string) error {
ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, opts.DryRun)
if err != nil {

View File

@ -18,7 +18,10 @@ import (
"github.com/spf13/pflag"
)
var cmdRestore = &cobra.Command{
func newRestoreCommand() *cobra.Command {
var opts RestoreOptions
cmd := &cobra.Command{
Use: "restore [flags] snapshotID",
Short: "Extract the data from a snapshot",
Long: `
@ -45,10 +48,18 @@ Exit status is 12 if the password is incorrect.
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runRestore(cmd.Context(), restoreOptions, globalOptions, term, args)
return runRestore(cmd.Context(), opts, globalOptions, term, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newRestoreCommand())
}
// RestoreOptions collects all options for the restore command.
type RestoreOptions struct {
filter.ExcludePatternOptions
@ -81,13 +92,6 @@ func (opts *RestoreOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.Delete, "delete", false, "delete files from target directory if they do not exist in snapshot. Use '--dry-run -vv' to check what would be deleted")
}
var restoreOptions RestoreOptions
func init() {
cmdRoot.AddCommand(cmdRestore)
restoreOptions.AddFlags(cmdRestore.Flags())
}
func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions,
term *termstatus.Terminal, args []string) error {

View File

@ -16,7 +16,10 @@ import (
"github.com/restic/restic/internal/walker"
)
var cmdRewrite = &cobra.Command{
func newRewriteCommand() *cobra.Command {
var opts RewriteOptions
cmd := &cobra.Command{
Use: "rewrite [flags] [snapshotID ...]",
Short: "Rewrite snapshots to exclude unwanted files",
Long: `
@ -55,10 +58,18 @@ Exit status is 12 if the password is incorrect.
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runRewrite(cmd.Context(), rewriteOptions, globalOptions, args)
return runRewrite(cmd.Context(), opts, globalOptions, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newRewriteCommand())
}
type snapshotMetadata struct {
Hostname string
Time *time.Time
@ -111,13 +122,6 @@ func (opts *RewriteOptions) AddFlags(f *pflag.FlagSet) {
opts.ExcludePatternOptions.Add(f)
}
var rewriteOptions RewriteOptions
func init() {
cmdRoot.AddCommand(cmdRewrite)
rewriteOptions.AddFlags(cmdRewrite.Flags())
}
// rewriteFilterFunc returns the filtered tree ID or an error. If a snapshot summary is returned, the snapshot will
// be updated accordingly.
type rewriteFilterFunc func(ctx context.Context, sn *restic.Snapshot) (restic.ID, *restic.SnapshotSummary, error)

View File

@ -13,7 +13,10 @@ import (
"github.com/spf13/pflag"
)
var cmdSelfUpdate = &cobra.Command{
func newSelfUpdateCommand() *cobra.Command {
var opts SelfUpdateOptions
cmd := &cobra.Command{
Use: "self-update [flags]",
Short: "Update the restic binary",
Long: `
@ -33,10 +36,18 @@ Exit status is 12 if the password is incorrect.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runSelfUpdate(cmd.Context(), selfUpdateOptions, globalOptions, args)
return runSelfUpdate(cmd.Context(), opts, globalOptions, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newSelfUpdateCommand())
}
// SelfUpdateOptions collects all options for the update-restic command.
type SelfUpdateOptions struct {
Output string
@ -46,13 +57,6 @@ func (opts *SelfUpdateOptions) AddFlags(f *pflag.FlagSet) {
f.StringVar(&opts.Output, "output", "", "Save the downloaded file as `filename` (default: running binary itself)")
}
var selfUpdateOptions SelfUpdateOptions
func init() {
cmdRoot.AddCommand(cmdSelfUpdate)
selfUpdateOptions.AddFlags(cmdSelfUpdate.Flags())
}
func runSelfUpdate(ctx context.Context, opts SelfUpdateOptions, gopts GlobalOptions, args []string) error {
if opts.Output == "" {
file, err := os.Executable()

View File

@ -15,7 +15,10 @@ import (
"github.com/spf13/pflag"
)
var cmdSnapshots = &cobra.Command{
func newSnapshotsCommand() *cobra.Command {
var opts SnapshotOptions
cmd := &cobra.Command{
Use: "snapshots [flags] [snapshotID ...]",
Short: "List all snapshots",
Long: `
@ -33,10 +36,18 @@ Exit status is 12 if the password is incorrect.
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runSnapshots(cmd.Context(), snapshotOptions, globalOptions, args)
return runSnapshots(cmd.Context(), opts, globalOptions, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newSnapshotsCommand())
}
// SnapshotOptions bundles all options for the snapshots command.
type SnapshotOptions struct {
restic.SnapshotFilter
@ -59,13 +70,6 @@ func (opts *SnapshotOptions) AddFlags(f *pflag.FlagSet) {
f.VarP(&opts.GroupBy, "group-by", "g", "`group` snapshots by host, paths and/or tags, separated by comma")
}
var snapshotOptions SnapshotOptions
func init() {
cmdRoot.AddCommand(cmdSnapshots)
snapshotOptions.AddFlags(cmdSnapshots.Flags())
}
func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts GlobalOptions, args []string) error {
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, gopts.NoLock)
if err != nil {

View File

@ -21,7 +21,10 @@ import (
"github.com/spf13/pflag"
)
var cmdStats = &cobra.Command{
func newStatsCommand() *cobra.Command {
var opts StatsOptions
cmd := &cobra.Command{
Use: "stats [flags] [snapshot ID] [...]",
Short: "Scan the repository and show basic statistics",
Long: `
@ -59,10 +62,21 @@ Exit status is 12 if the password is incorrect.
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runStats(cmd.Context(), statsOptions, globalOptions, args)
return runStats(cmd.Context(), opts, globalOptions, args)
},
}
opts.AddFlags(cmd.Flags())
must(cmd.RegisterFlagCompletionFunc("mode", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{countModeRestoreSize, countModeUniqueFilesByContents, countModeBlobsPerFile, countModeRawData}, cobra.ShellCompDirectiveDefault
}))
return cmd
}
func init() {
cmdRoot.AddCommand(newStatsCommand())
}
// StatsOptions collects all options for the stats command.
type StatsOptions struct {
// the mode of counting to perform (see consts for available modes)
@ -76,22 +90,12 @@ func (opts *StatsOptions) AddFlags(f *pflag.FlagSet) {
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
}
var statsOptions StatsOptions
func must(err error) {
if err != nil {
panic(fmt.Sprintf("error during setup: %v", err))
}
}
func init() {
cmdRoot.AddCommand(cmdStats)
statsOptions.AddFlags(cmdStats.Flags())
must(cmdStats.RegisterFlagCompletionFunc("mode", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{countModeRestoreSize, countModeUniqueFilesByContents, countModeBlobsPerFile, countModeRawData}, cobra.ShellCompDirectiveDefault
}))
}
func runStats(ctx context.Context, opts StatsOptions, gopts GlobalOptions, args []string) error {
err := verifyStatsInput(opts)
if err != nil {

View File

@ -14,7 +14,10 @@ import (
"github.com/restic/restic/internal/ui/termstatus"
)
var cmdTag = &cobra.Command{
func newTagCommand() *cobra.Command {
var opts TagOptions
cmd := &cobra.Command{
Use: "tag [flags] [snapshotID ...]",
Short: "Modify tags on snapshots",
Long: `
@ -39,10 +42,18 @@ Exit status is 12 if the password is incorrect.
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runTag(cmd.Context(), tagOptions, globalOptions, term, args)
return runTag(cmd.Context(), opts, globalOptions, term, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newTagCommand())
}
// TagOptions bundles all options for the 'tag' command.
type TagOptions struct {
restic.SnapshotFilter
@ -58,13 +69,6 @@ func (opts *TagOptions) AddFlags(f *pflag.FlagSet) {
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
}
var tagOptions TagOptions
func init() {
cmdRoot.AddCommand(cmdTag)
tagOptions.AddFlags(cmdTag.Flags())
}
type changedSnapshot struct {
MessageType string `json:"message_type"` // changed
OldSnapshotID restic.ID `json:"old_snapshot_id"`

View File

@ -8,7 +8,10 @@ import (
"github.com/spf13/pflag"
)
var unlockCmd = &cobra.Command{
func newUnlockCommand() *cobra.Command {
var opts UnlockOptions
cmd := &cobra.Command{
Use: "unlock",
Short: "Remove locks other processes created",
Long: `
@ -23,9 +26,16 @@ Exit status is 1 if there was any error.
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error {
return runUnlock(cmd.Context(), unlockOptions, globalOptions)
return runUnlock(cmd.Context(), opts, globalOptions)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newUnlockCommand())
}
// UnlockOptions collects all options for the unlock command.
type UnlockOptions struct {
@ -36,13 +46,6 @@ func (opts *UnlockOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.RemoveAll, "remove-all", false, "remove all locks, even non-stale ones")
}
var unlockOptions UnlockOptions
func init() {
cmdRoot.AddCommand(unlockCmd)
unlockOptions.AddFlags(unlockCmd.Flags())
}
func runUnlock(ctx context.Context, opts UnlockOptions, gopts GlobalOptions) error {
repo, err := OpenRepository(ctx, gopts)
if err != nil {

View File

@ -8,7 +8,8 @@ import (
"github.com/spf13/cobra"
)
var versionCmd = &cobra.Command{
func newVersionCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "version",
Short: "Print version information",
Long: `
@ -52,7 +53,9 @@ Exit status is 1 if there was any error.
},
}
return cmd
}
func init() {
cmdRoot.AddCommand(versionCmd)
cmdRoot.AddCommand(newVersionCommand())
}