diff --git a/cmd/restic/cmd_backup.go b/cmd/restic/cmd_backup.go index d0f7f849f..48587aeda 100644 --- a/cmd/restic/cmd_backup.go +++ b/cmd/restic/cmd_backup.go @@ -24,6 +24,7 @@ import ( "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/filter" "github.com/restic/restic/internal/fs" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/textfile" @@ -31,7 +32,7 @@ import ( "github.com/restic/restic/internal/ui/backup" ) -func newBackupCommand(globalOptions *GlobalOptions) *cobra.Command { +func newBackupCommand(globalOptions *global.Options) *cobra.Command { var opts BackupOptions cmd := &cobra.Command{ @@ -64,7 +65,7 @@ Exit status is 12 if the password is incorrect. GroupID: cmdGroupDefault, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - return runBackup(cmd.Context(), opts, *globalOptions, globalOptions.term, args) + return runBackup(cmd.Context(), opts, *globalOptions, globalOptions.Term, args) }, } @@ -274,8 +275,8 @@ func readFilenamesRaw(r io.Reader) (names []string, err error) { } // Check returns an error when an invalid combination of options was set. -func (opts BackupOptions) Check(gopts GlobalOptions, args []string) error { - if gopts.password == "" && !gopts.InsecureNoPassword { +func (opts BackupOptions) Check(gopts global.Options, args []string) error { + if gopts.Password == "" && !gopts.InsecureNoPassword { if opts.Stdin { return errors.Fatal("cannot read both password and data from stdin") } @@ -475,18 +476,18 @@ func findParentSnapshot(ctx context.Context, repo restic.ListerLoaderUnpacked, o return sn, err } -func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, term ui.Terminal, args []string) error { +func runBackup(ctx context.Context, opts BackupOptions, gopts global.Options, term ui.Terminal, args []string) error { var vsscfg fs.VSSConfig var err error var printer backup.ProgressPrinter if gopts.JSON { - printer = backup.NewJSONProgress(term, gopts.verbosity) + printer = backup.NewJSONProgress(term, gopts.Verbosity) } else { - printer = backup.NewTextProgress(term, gopts.verbosity) + printer = backup.NewTextProgress(term, gopts.Verbosity) } if runtime.GOOS == "windows" { - if vsscfg, err = fs.ParseVSSConfig(gopts.extended); err != nil { + if vsscfg, err = fs.ParseVSSConfig(gopts.Extended); err != nil { return err } } @@ -509,13 +510,13 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter timeStamp := time.Now() backupStart := timeStamp if opts.TimeStamp != "" { - timeStamp, err = time.ParseInLocation(TimeFormat, opts.TimeStamp, time.Local) + timeStamp, err = time.ParseInLocation(global.TimeFormat, opts.TimeStamp, time.Local) if err != nil { return errors.Fatalf("error in time option: %v", err) } } - if gopts.verbosity >= 2 && !gopts.JSON { + if gopts.Verbosity >= 2 && !gopts.JSON { printer.P("open repository") } @@ -668,7 +669,7 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter Time: timeStamp, Hostname: opts.Host, ParentSnapshot: parentSnapshot, - ProgramVersion: "restic " + version, + ProgramVersion: "restic " + global.Version, SkipIfUnchanged: opts.SkipIfUnchanged, } diff --git a/cmd/restic/cmd_backup_integration_test.go b/cmd/restic/cmd_backup_integration_test.go index d8b766d8c..840ef65b9 100644 --- a/cmd/restic/cmd_backup_integration_test.go +++ b/cmd/restic/cmd_backup_integration_test.go @@ -12,12 +12,13 @@ import ( "github.com/restic/restic/internal/data" "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/fs" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) -func testRunBackupAssumeFailure(t testing.TB, dir string, target []string, opts BackupOptions, gopts GlobalOptions) error { - return withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { +func testRunBackupAssumeFailure(t testing.TB, dir string, target []string, opts BackupOptions, gopts global.Options) error { + return withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { t.Logf("backing up %v in %v", target, dir) if dir != "" { cleanup := rtest.Chdir(t, dir) @@ -25,11 +26,11 @@ func testRunBackupAssumeFailure(t testing.TB, dir string, target []string, opts } opts.GroupBy = data.SnapshotGroupByOptions{Host: true, Path: true} - return runBackup(ctx, opts, gopts, gopts.term, target) + return runBackup(ctx, opts, gopts, gopts.Term, target) }) } -func testRunBackup(t testing.TB, dir string, target []string, opts BackupOptions, gopts GlobalOptions) { +func testRunBackup(t testing.TB, dir string, target []string, opts BackupOptions, gopts global.Options) { err := testRunBackupAssumeFailure(t, dir, target, opts, gopts) rtest.Assert(t, err == nil, "Error while backing up: %v", err) } @@ -512,7 +513,7 @@ func TestBackupProgramVersion(t *testing.T) { if newest == nil { t.Fatal("expected a backup, got nil") } - resticVersion := "restic " + version + resticVersion := "restic " + global.Version rtest.Assert(t, newest.ProgramVersion == resticVersion, "expected %v, got %v", resticVersion, newest.ProgramVersion) } @@ -706,7 +707,7 @@ func TestBackupEmptyPassword(t *testing.T) { env, cleanup := withTestEnvironment(t) defer cleanup() - env.gopts.password = "" + env.gopts.Password = "" env.gopts.InsecureNoPassword = true testSetupBackupData(t, env) diff --git a/cmd/restic/cmd_cache.go b/cmd/restic/cmd_cache.go index da6537fcb..7a571ae85 100644 --- a/cmd/restic/cmd_cache.go +++ b/cmd/restic/cmd_cache.go @@ -10,13 +10,14 @@ import ( "github.com/restic/restic/internal/backend/cache" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui/table" "github.com/spf13/cobra" "github.com/spf13/pflag" ) -func newCacheCommand(globalOptions *GlobalOptions) *cobra.Command { +func newCacheCommand(globalOptions *global.Options) *cobra.Command { var opts CacheOptions cmd := &cobra.Command{ @@ -34,7 +35,7 @@ Exit status is 1 if there was any error. GroupID: cmdGroupDefault, DisableAutoGenTag: true, RunE: func(_ *cobra.Command, args []string) error { - return runCache(opts, *globalOptions, args, globalOptions.term) + return runCache(opts, *globalOptions, args, globalOptions.Term) }, } @@ -55,8 +56,8 @@ func (opts *CacheOptions) AddFlags(f *pflag.FlagSet) { f.BoolVar(&opts.NoSize, "no-size", false, "do not output the size of the cache directories") } -func runCache(opts CacheOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { - printer := ui.NewProgressPrinter(false, gopts.verbosity, term) +func runCache(opts CacheOptions, gopts global.Options, args []string, term ui.Terminal) error { + printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) if len(args) > 0 { return errors.Fatal("the cache command expects no arguments, only options - please see `restic help cache` for usage and flags") @@ -161,7 +162,7 @@ func runCache(opts CacheOptions, gopts GlobalOptions, args []string, term ui.Ter }) } - _ = tab.Write(gopts.term.OutputWriter()) + _ = tab.Write(gopts.Term.OutputWriter()) printer.S("%d cache dirs in %s", len(dirs), cachedir) return nil diff --git a/cmd/restic/cmd_cat.go b/cmd/restic/cmd_cat.go index 1b5c44830..0d64dbd87 100644 --- a/cmd/restic/cmd_cat.go +++ b/cmd/restic/cmd_cat.go @@ -9,6 +9,7 @@ import ( "github.com/restic/restic/internal/data" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" @@ -16,7 +17,7 @@ import ( var catAllowedCmds = []string{"config", "index", "snapshot", "key", "masterkey", "lock", "pack", "blob", "tree"} -func newCatCommand(globalOptions *GlobalOptions) *cobra.Command { +func newCatCommand(globalOptions *global.Options) *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", @@ -35,7 +36,7 @@ Exit status is 12 if the password is incorrect. GroupID: cmdGroupDefault, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - return runCat(cmd.Context(), *globalOptions, args, globalOptions.term) + return runCat(cmd.Context(), *globalOptions, args, globalOptions.Term) }, ValidArgs: catAllowedCmds, } @@ -65,8 +66,8 @@ func validateCatArgs(args []string) error { return nil } -func runCat(ctx context.Context, gopts GlobalOptions, args []string, term ui.Terminal) error { - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) +func runCat(ctx context.Context, gopts global.Options, args []string, term ui.Terminal) error { + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) if err := validateCatArgs(args); err != nil { return err diff --git a/cmd/restic/cmd_check.go b/cmd/restic/cmd_check.go index 834648126..f93006f14 100644 --- a/cmd/restic/cmd_check.go +++ b/cmd/restic/cmd_check.go @@ -16,13 +16,14 @@ import ( "github.com/restic/restic/internal/backend/cache" "github.com/restic/restic/internal/checker" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui/progress" ) -func newCheckCommand(globalOptions *GlobalOptions) *cobra.Command { +func newCheckCommand(globalOptions *global.Options) *cobra.Command { var opts CheckOptions cmd := &cobra.Command{ Use: "check [flags]", @@ -46,12 +47,12 @@ Exit status is 12 if the password is incorrect. GroupID: cmdGroupDefault, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - summary, err := runCheck(cmd.Context(), opts, *globalOptions, args, globalOptions.term) + summary, err := runCheck(cmd.Context(), opts, *globalOptions, args, globalOptions.Term) if globalOptions.JSON { if err != nil && summary.NumErrors == 0 { summary.NumErrors = 1 } - globalOptions.term.Print(ui.ToJSONString(summary)) + globalOptions.Term.Print(ui.ToJSONString(summary)) } return err }, @@ -170,7 +171,7 @@ func parsePercentage(s string) (float64, error) { // - if the user explicitly requested --no-cache, we don't use any cache // - if the user provides --cache-dir, we use a cache in a temporary sub-directory of the specified directory and the sub-directory is deleted after the check // - by default, we use a cache in a temporary directory that is deleted after the check -func prepareCheckCache(opts CheckOptions, gopts *GlobalOptions, printer progress.Printer) (cleanup func()) { +func prepareCheckCache(opts CheckOptions, gopts *global.Options, printer progress.Printer) (cleanup func()) { cleanup = func() {} if opts.WithCache { // use the default cache, no setup needed @@ -217,7 +218,7 @@ func prepareCheckCache(opts CheckOptions, gopts *GlobalOptions, printer progress return cleanup } -func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args []string, term ui.Terminal) (checkSummary, error) { +func runCheck(ctx context.Context, opts CheckOptions, gopts global.Options, args []string, term ui.Terminal) (checkSummary, error) { summary := checkSummary{MessageType: "summary"} if len(args) != 0 { return summary, errors.Fatal("the check command expects no arguments, only options - please see `restic help check` for usage and flags") @@ -225,7 +226,7 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args var printer progress.Printer if !gopts.JSON { - printer = ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer = ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) } else { printer = newJSONErrorPrinter(term) } diff --git a/cmd/restic/cmd_check_integration_test.go b/cmd/restic/cmd_check_integration_test.go index c28a3ed4f..c4580100e 100644 --- a/cmd/restic/cmd_check_integration_test.go +++ b/cmd/restic/cmd_check_integration_test.go @@ -4,10 +4,11 @@ import ( "context" "testing" + "github.com/restic/restic/internal/global" rtest "github.com/restic/restic/internal/test" ) -func testRunCheck(t testing.TB, gopts GlobalOptions) { +func testRunCheck(t testing.TB, gopts global.Options) { t.Helper() output, err := testRunCheckOutput(t, gopts, true) if err != nil { @@ -16,19 +17,19 @@ func testRunCheck(t testing.TB, gopts GlobalOptions) { } } -func testRunCheckMustFail(t testing.TB, gopts GlobalOptions) { +func testRunCheckMustFail(t testing.TB, gopts global.Options) { t.Helper() _, err := testRunCheckOutput(t, gopts, false) rtest.Assert(t, err != nil, "expected non nil error after check of damaged repository") } -func testRunCheckOutput(t testing.TB, gopts GlobalOptions, checkUnused bool) (string, error) { - buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { +func testRunCheckOutput(t testing.TB, gopts global.Options, checkUnused bool) (string, error) { + buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error { opts := CheckOptions{ ReadData: true, CheckUnused: checkUnused, } - _, err := runCheck(context.TODO(), opts, gopts, nil, gopts.term) + _, err := runCheck(context.TODO(), opts, gopts, nil, gopts.Term) return err }) return buf.String(), err diff --git a/cmd/restic/cmd_check_test.go b/cmd/restic/cmd_check_test.go index 18d607a14..58b50dae3 100644 --- a/cmd/restic/cmd_check_test.go +++ b/cmd/restic/cmd_check_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" "github.com/restic/restic/internal/ui/progress" @@ -202,7 +203,7 @@ func TestPrepareCheckCache(t *testing.T) { err := os.Remove(tmpDirBase) rtest.OK(t, err) } - gopts := GlobalOptions{CacheDir: tmpDirBase} + gopts := global.Options{CacheDir: tmpDirBase} cleanup := prepareCheckCache(testCase.opts, &gopts, &progress.NoopPrinter{}) files, err := os.ReadDir(tmpDirBase) rtest.OK(t, err) @@ -232,7 +233,7 @@ func TestPrepareCheckCache(t *testing.T) { } func TestPrepareDefaultCheckCache(t *testing.T) { - gopts := GlobalOptions{CacheDir: ""} + gopts := global.Options{CacheDir: ""} cleanup := prepareCheckCache(CheckOptions{}, &gopts, &progress.NoopPrinter{}) _, err := os.ReadDir(gopts.CacheDir) rtest.OK(t, err) diff --git a/cmd/restic/cmd_copy.go b/cmd/restic/cmd_copy.go index 210e68e05..7b9eccad2 100644 --- a/cmd/restic/cmd_copy.go +++ b/cmd/restic/cmd_copy.go @@ -7,6 +7,7 @@ import ( "github.com/restic/restic/internal/data" "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" @@ -17,7 +18,7 @@ import ( "github.com/spf13/pflag" ) -func newCopyCommand(globalOptions *GlobalOptions) *cobra.Command { +func newCopyCommand(globalOptions *global.Options) *cobra.Command { var opts CopyOptions cmd := &cobra.Command{ Use: "copy [flags] [snapshotID ...]", @@ -50,7 +51,7 @@ Exit status is 12 if the password is incorrect. DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { finalizeSnapshotFilter(&opts.SnapshotFilter) - return runCopy(cmd.Context(), opts, *globalOptions, args, globalOptions.term) + return runCopy(cmd.Context(), opts, *globalOptions, args, globalOptions.Term) }, } @@ -60,18 +61,18 @@ Exit status is 12 if the password is incorrect. // CopyOptions bundles all options for the copy command. type CopyOptions struct { - secondaryRepoOptions + global.SecondaryRepoOptions data.SnapshotFilter } func (opts *CopyOptions) AddFlags(f *pflag.FlagSet) { - opts.secondaryRepoOptions.AddFlags(f, "destination", "to copy snapshots from") + opts.SecondaryRepoOptions.AddFlags(f, "destination", "to copy snapshots from") initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) } -func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { - printer := ui.NewProgressPrinter(false, gopts.verbosity, term) - secondaryGopts, isFromRepo, err := fillSecondaryGlobalOpts(ctx, opts.secondaryRepoOptions, gopts, "destination") +func runCopy(ctx context.Context, opts CopyOptions, gopts global.Options, args []string, term ui.Terminal) error { + printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) + secondaryGopts, isFromRepo, err := opts.SecondaryRepoOptions.FillGlobalOpts(ctx, gopts, "destination") if err != nil { return err } diff --git a/cmd/restic/cmd_copy_integration_test.go b/cmd/restic/cmd_copy_integration_test.go index 73bc038dd..c35e960ff 100644 --- a/cmd/restic/cmd_copy_integration_test.go +++ b/cmd/restic/cmd_copy_integration_test.go @@ -6,24 +6,25 @@ import ( "path/filepath" "testing" + "github.com/restic/restic/internal/global" rtest "github.com/restic/restic/internal/test" ) -func testRunCopy(t testing.TB, srcGopts GlobalOptions, dstGopts GlobalOptions) { +func testRunCopy(t testing.TB, srcGopts global.Options, dstGopts global.Options) { gopts := srcGopts gopts.Repo = dstGopts.Repo - gopts.password = dstGopts.password + gopts.Password = dstGopts.Password gopts.InsecureNoPassword = dstGopts.InsecureNoPassword copyOpts := CopyOptions{ - secondaryRepoOptions: secondaryRepoOptions{ + SecondaryRepoOptions: global.SecondaryRepoOptions{ Repo: srcGopts.Repo, - password: srcGopts.password, + Password: srcGopts.Password, InsecureNoPassword: srcGopts.InsecureNoPassword, }, } - rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runCopy(context.TODO(), copyOpts, gopts, nil, gopts.term) + rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + return runCopy(context.TODO(), copyOpts, gopts, nil, gopts.Term) })) } @@ -144,7 +145,7 @@ func TestCopyToEmptyPassword(t *testing.T) { defer cleanup() env2, cleanup2 := withTestEnvironment(t) defer cleanup2() - env2.gopts.password = "" + env2.gopts.Password = "" env2.gopts.InsecureNoPassword = true testSetupBackupData(t, env) diff --git a/cmd/restic/cmd_debug.go b/cmd/restic/cmd_debug.go index b5d4d646c..f9ede9df1 100644 --- a/cmd/restic/cmd_debug.go +++ b/cmd/restic/cmd_debug.go @@ -24,6 +24,7 @@ import ( "github.com/restic/restic/internal/crypto" "github.com/restic/restic/internal/data" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository/index" "github.com/restic/restic/internal/repository/pack" @@ -32,13 +33,13 @@ import ( "github.com/restic/restic/internal/ui/progress" ) -func registerDebugCommand(cmd *cobra.Command, globalOptions *GlobalOptions) { +func registerDebugCommand(cmd *cobra.Command, globalOptions *global.Options) { cmd.AddCommand( newDebugCommand(globalOptions), ) } -func newDebugCommand(globalOptions *GlobalOptions) *cobra.Command { +func newDebugCommand(globalOptions *global.Options) *cobra.Command { cmd := &cobra.Command{ Use: "debug", Short: "Debug commands", @@ -50,7 +51,7 @@ func newDebugCommand(globalOptions *GlobalOptions) *cobra.Command { return cmd } -func newDebugDumpCommand(globalOptions *GlobalOptions) *cobra.Command { +func newDebugDumpCommand(globalOptions *global.Options) *cobra.Command { cmd := &cobra.Command{ Use: "dump [indexes|snapshots|all|packs]", Short: "Dump data structures", @@ -69,13 +70,13 @@ Exit status is 12 if the password is incorrect. `, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - return runDebugDump(cmd.Context(), *globalOptions, args, globalOptions.term) + return runDebugDump(cmd.Context(), *globalOptions, args, globalOptions.Term) }, } return cmd } -func newDebugExamineCommand(globalOptions *GlobalOptions) *cobra.Command { +func newDebugExamineCommand(globalOptions *global.Options) *cobra.Command { var opts DebugExamineOptions cmd := &cobra.Command{ @@ -83,7 +84,7 @@ func newDebugExamineCommand(globalOptions *GlobalOptions) *cobra.Command { Short: "Examine a pack file", DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - return runDebugExamine(cmd.Context(), *globalOptions, opts, args, globalOptions.term) + return runDebugExamine(cmd.Context(), *globalOptions, opts, args, globalOptions.Term) }, } @@ -184,8 +185,8 @@ func dumpIndexes(ctx context.Context, repo restic.ListerLoaderUnpacked, wr io.Wr }) } -func runDebugDump(ctx context.Context, gopts GlobalOptions, args []string, term ui.Terminal) error { - printer := ui.NewProgressPrinter(false, gopts.verbosity, term) +func runDebugDump(ctx context.Context, gopts global.Options, args []string, term ui.Terminal) error { + printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) if len(args) != 1 { return errors.Fatal("type not specified") @@ -201,20 +202,20 @@ func runDebugDump(ctx context.Context, gopts GlobalOptions, args []string, term switch tpe { case "indexes": - return dumpIndexes(ctx, repo, gopts.term.OutputWriter(), printer) + return dumpIndexes(ctx, repo, gopts.Term.OutputWriter(), printer) case "snapshots": - return debugPrintSnapshots(ctx, repo, gopts.term.OutputWriter()) + return debugPrintSnapshots(ctx, repo, gopts.Term.OutputWriter()) case "packs": - return printPacks(ctx, repo, gopts.term.OutputWriter(), printer) + return printPacks(ctx, repo, gopts.Term.OutputWriter(), printer) case "all": printer.S("snapshots:") - err := debugPrintSnapshots(ctx, repo, gopts.term.OutputWriter()) + err := debugPrintSnapshots(ctx, repo, gopts.Term.OutputWriter()) if err != nil { return err } printer.S("indexes:") - err = dumpIndexes(ctx, repo, gopts.term.OutputWriter(), printer) + err = dumpIndexes(ctx, repo, gopts.Term.OutputWriter(), printer) if err != nil { return err } @@ -455,8 +456,8 @@ func storePlainBlob(id restic.ID, prefix string, plain []byte, printer progress. return nil } -func runDebugExamine(ctx context.Context, gopts GlobalOptions, opts DebugExamineOptions, args []string, term ui.Terminal) error { - printer := ui.NewProgressPrinter(false, gopts.verbosity, term) +func runDebugExamine(ctx context.Context, gopts global.Options, opts DebugExamineOptions, args []string, term ui.Terminal) error { + printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) if opts.ExtractPack && gopts.NoLock { return fmt.Errorf("--extract-pack and --no-lock are mutually exclusive") diff --git a/cmd/restic/cmd_debug_disabled.go b/cmd/restic/cmd_debug_disabled.go index ba794925c..8d670bf01 100644 --- a/cmd/restic/cmd_debug_disabled.go +++ b/cmd/restic/cmd_debug_disabled.go @@ -2,8 +2,11 @@ package main -import "github.com/spf13/cobra" +import ( + "github.com/restic/restic/internal/global" + "github.com/spf13/cobra" +) -func registerDebugCommand(_ *cobra.Command, _ *GlobalOptions) { +func registerDebugCommand(_ *cobra.Command, _ *global.Options) { // No commands to register in non-debug mode } diff --git a/cmd/restic/cmd_diff.go b/cmd/restic/cmd_diff.go index 908d536e6..3bd8e37b8 100644 --- a/cmd/restic/cmd_diff.go +++ b/cmd/restic/cmd_diff.go @@ -10,13 +10,14 @@ import ( "github.com/restic/restic/internal/data" "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" "github.com/spf13/cobra" "github.com/spf13/pflag" ) -func newDiffCommand(globalOptions *GlobalOptions) *cobra.Command { +func newDiffCommand(globalOptions *global.Options) *cobra.Command { var opts DiffOptions cmd := &cobra.Command{ @@ -53,7 +54,7 @@ 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(), opts, *globalOptions, args, globalOptions.term) + return runDiff(cmd.Context(), opts, *globalOptions, args, globalOptions.Term) }, } @@ -361,12 +362,12 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref return ctx.Err() } -func runDiff(ctx context.Context, opts DiffOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { +func runDiff(ctx context.Context, opts DiffOptions, gopts global.Options, args []string, term ui.Terminal) error { if len(args) != 2 { return errors.Fatalf("specify two snapshot IDs") } - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, gopts.NoLock, printer) if err != nil { @@ -424,7 +425,7 @@ func runDiff(ctx context.Context, opts DiffOptions, gopts GlobalOptions, args [] } if gopts.JSON { - enc := json.NewEncoder(gopts.term.OutputWriter()) + enc := json.NewEncoder(gopts.Term.OutputWriter()) c.printChange = func(change *Change) { err := enc.Encode(change) if err != nil { @@ -458,7 +459,7 @@ func runDiff(ctx context.Context, opts DiffOptions, gopts GlobalOptions, args [] updateBlobs(repo, stats.BlobsAfter.Sub(both).Sub(stats.BlobsCommon), &stats.Added, printer.E) if gopts.JSON { - err := json.NewEncoder(gopts.term.OutputWriter()).Encode(stats) + err := json.NewEncoder(gopts.Term.OutputWriter()).Encode(stats) if err != nil { printer.E("JSON encode failed: %v", err) } diff --git a/cmd/restic/cmd_diff_integration_test.go b/cmd/restic/cmd_diff_integration_test.go index bf62f1e19..dbf78a0d3 100644 --- a/cmd/restic/cmd_diff_integration_test.go +++ b/cmd/restic/cmd_diff_integration_test.go @@ -11,15 +11,16 @@ import ( "strings" "testing" + "github.com/restic/restic/internal/global" rtest "github.com/restic/restic/internal/test" ) -func testRunDiffOutput(t testing.TB, gopts GlobalOptions, firstSnapshotID string, secondSnapshotID string) (string, error) { - buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { +func testRunDiffOutput(t testing.TB, gopts global.Options, firstSnapshotID string, secondSnapshotID string) (string, error) { + buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error { opts := DiffOptions{ ShowMetadata: false, } - return runDiff(ctx, opts, gopts, []string{firstSnapshotID, secondSnapshotID}, gopts.term) + return runDiff(ctx, opts, gopts, []string{firstSnapshotID, secondSnapshotID}, gopts.Term) }) return buf.String(), err } diff --git a/cmd/restic/cmd_dump.go b/cmd/restic/cmd_dump.go index c538b9153..da45cc303 100644 --- a/cmd/restic/cmd_dump.go +++ b/cmd/restic/cmd_dump.go @@ -11,6 +11,7 @@ import ( "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/dump" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" @@ -18,7 +19,7 @@ import ( "github.com/spf13/pflag" ) -func newDumpCommand(globalOptions *GlobalOptions) *cobra.Command { +func newDumpCommand(globalOptions *global.Options) *cobra.Command { var opts DumpOptions cmd := &cobra.Command{ Use: "dump [flags] snapshotID file", @@ -49,7 +50,7 @@ Exit status is 12 if the password is incorrect. DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { finalizeSnapshotFilter(&opts.SnapshotFilter) - return runDump(cmd.Context(), opts, *globalOptions, args, globalOptions.term) + return runDump(cmd.Context(), opts, *globalOptions, args, globalOptions.Term) }, } @@ -127,12 +128,12 @@ func printFromTree(ctx context.Context, tree *data.Tree, repo restic.BlobLoader, return fmt.Errorf("path %q not found in snapshot", item) } -func runDump(ctx context.Context, opts DumpOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { +func runDump(ctx context.Context, opts DumpOptions, gopts global.Options, args []string, term ui.Terminal) error { if len(args) != 2 { return errors.Fatal("no file and no snapshot ID specified") } - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) switch opts.Archive { case "tar", "zip": diff --git a/cmd/restic/cmd_features.go b/cmd/restic/cmd_features.go index ec3b93db5..3545bf151 100644 --- a/cmd/restic/cmd_features.go +++ b/cmd/restic/cmd_features.go @@ -3,12 +3,13 @@ package main import ( "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/feature" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/ui/table" "github.com/spf13/cobra" ) -func newFeaturesCommand(globalOptions *GlobalOptions) *cobra.Command { +func newFeaturesCommand(globalOptions *global.Options) *cobra.Command { cmd := &cobra.Command{ Use: "features", Short: "Print list of feature flags", @@ -37,7 +38,7 @@ Exit status is 1 if there was any error. return errors.Fatal("the feature command expects no arguments") } - globalOptions.term.Print("All Feature Flags:\n") + globalOptions.Term.Print("All Feature Flags:\n") flags := feature.Flag.List() tab := table.New() @@ -49,7 +50,7 @@ Exit status is 1 if there was any error. for _, flag := range flags { tab.AddRow(flag) } - return tab.Write(globalOptions.term.OutputWriter()) + return tab.Write(globalOptions.Term.OutputWriter()) }, } diff --git a/cmd/restic/cmd_find.go b/cmd/restic/cmd_find.go index f8f124f9a..0ae7e315d 100644 --- a/cmd/restic/cmd_find.go +++ b/cmd/restic/cmd_find.go @@ -16,12 +16,13 @@ import ( "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/filter" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/walker" ) -func newFindCommand(globalOptions *GlobalOptions) *cobra.Command { +func newFindCommand(globalOptions *global.Options) *cobra.Command { var opts FindOptions cmd := &cobra.Command{ @@ -53,7 +54,7 @@ Exit status is 12 if the password is incorrect. DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { finalizeSnapshotFilter(&opts.SnapshotFilter) - return runFind(cmd.Context(), opts, *globalOptions, args, globalOptions.term) + return runFind(cmd.Context(), opts, *globalOptions, args, globalOptions.Term) }, } @@ -187,7 +188,7 @@ func (s *statefulOutput) PrintPatternNormal(path string, node *data.Node) { s.printer.P("") } s.oldsn = s.newsn - s.printer.P("Found matching entries in snapshot %s from %s", s.oldsn.ID().Str(), s.oldsn.Time.Local().Format(TimeFormat)) + s.printer.P("Found matching entries in snapshot %s from %s", s.oldsn.ID().Str(), s.oldsn.Time.Local().Format(global.TimeFormat)) } s.printer.S(formatNode(path, node, s.ListLong, s.HumanReadable)) } @@ -240,7 +241,7 @@ func (s *statefulOutput) PrintObjectNormal(kind, id, nodepath, treeID string, sn } else { s.printer.S(" ... path %s", nodepath) } - s.printer.S(" ... in snapshot %s (%s)", sn.ID().Str(), sn.Time.Local().Format(TimeFormat)) + s.printer.S(" ... in snapshot %s (%s)", sn.ID().Str(), sn.Time.Local().Format(global.TimeFormat)) } func (s *statefulOutput) PrintObject(kind, id, nodepath, treeID string, sn *data.Snapshot) { @@ -579,12 +580,12 @@ func (f *Finder) findObjectsPacks() { } } -func runFind(ctx context.Context, opts FindOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { +func runFind(ctx context.Context, opts FindOptions, gopts global.Options, args []string, term ui.Terminal) error { if len(args) == 0 { return errors.Fatal("wrong number of arguments") } - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) var err error pat := findPattern{pattern: args} diff --git a/cmd/restic/cmd_find_integration_test.go b/cmd/restic/cmd_find_integration_test.go index f49d34127..360620952 100644 --- a/cmd/restic/cmd_find_integration_test.go +++ b/cmd/restic/cmd_find_integration_test.go @@ -7,14 +7,15 @@ import ( "testing" "time" + "github.com/restic/restic/internal/global" rtest "github.com/restic/restic/internal/test" ) -func testRunFind(t testing.TB, wantJSON bool, opts FindOptions, gopts GlobalOptions, pattern string) []byte { - buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { +func testRunFind(t testing.TB, wantJSON bool, opts FindOptions, gopts global.Options, pattern string) []byte { + buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error { gopts.JSON = wantJSON - return runFind(ctx, opts, gopts, []string{pattern}, gopts.term) + return runFind(ctx, opts, gopts, []string{pattern}, gopts.Term) }) rtest.OK(t, err) return buf.Bytes() diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go index bc5fd8d11..976db4d0d 100644 --- a/cmd/restic/cmd_forget.go +++ b/cmd/restic/cmd_forget.go @@ -9,13 +9,14 @@ import ( "github.com/restic/restic/internal/data" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" "github.com/spf13/cobra" "github.com/spf13/pflag" ) -func newForgetCommand(globalOptions *GlobalOptions) *cobra.Command { +func newForgetCommand(globalOptions *global.Options) *cobra.Command { var opts ForgetOptions var pruneOpts PruneOptions @@ -51,7 +52,7 @@ Exit status is 12 if the password is incorrect. DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { finalizeSnapshotFilter(&opts.SnapshotFilter) - return runForget(cmd.Context(), opts, pruneOpts, *globalOptions, globalOptions.term, args) + return runForget(cmd.Context(), opts, pruneOpts, *globalOptions, globalOptions.Term, args) }, } @@ -173,7 +174,7 @@ func verifyForgetOptions(opts *ForgetOptions) error { return nil } -func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOptions, gopts GlobalOptions, term ui.Terminal, args []string) error { +func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOptions, gopts global.Options, term ui.Terminal, args []string) error { err := verifyForgetOptions(&opts) if err != nil { return err @@ -188,7 +189,7 @@ func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOption return errors.Fatal("--no-lock is only applicable in combination with --dry-run for forget command") } - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, opts.DryRun && gopts.NoLock, printer) if err != nil { return err @@ -253,7 +254,7 @@ func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOption } if gopts.Verbose >= 1 && !gopts.JSON { - err = PrintSnapshotGroupHeader(gopts.term.OutputWriter(), k) + err = PrintSnapshotGroupHeader(gopts.Term.OutputWriter(), k) if err != nil { return err } @@ -276,7 +277,7 @@ func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOption } if len(keep) != 0 && !gopts.Quiet && !gopts.JSON { printer.P("keep %d snapshots:\n", len(keep)) - if err := PrintSnapshots(gopts.term.OutputWriter(), keep, reasons, opts.Compact); err != nil { + if err := PrintSnapshots(gopts.Term.OutputWriter(), keep, reasons, opts.Compact); err != nil { return err } printer.P("\n") @@ -285,7 +286,7 @@ func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOption if len(remove) != 0 && !gopts.Quiet && !gopts.JSON { printer.P("remove %d snapshots:\n", len(remove)) - if err := PrintSnapshots(gopts.term.OutputWriter(), remove, nil, opts.Compact); err != nil { + if err := PrintSnapshots(gopts.Term.OutputWriter(), remove, nil, opts.Compact); err != nil { return err } printer.P("\n") @@ -330,7 +331,7 @@ func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOption } if gopts.JSON && len(jsonGroups) > 0 { - err = printJSONForget(gopts.term.OutputWriter(), jsonGroups) + err = printJSONForget(gopts.Term.OutputWriter(), jsonGroups) if err != nil { return err } diff --git a/cmd/restic/cmd_forget_integration_test.go b/cmd/restic/cmd_forget_integration_test.go index 49f7ed96b..93a178481 100644 --- a/cmd/restic/cmd_forget_integration_test.go +++ b/cmd/restic/cmd_forget_integration_test.go @@ -7,19 +7,20 @@ import ( "testing" "github.com/restic/restic/internal/data" + "github.com/restic/restic/internal/global" rtest "github.com/restic/restic/internal/test" ) -func testRunForgetMayFail(t testing.TB, gopts GlobalOptions, opts ForgetOptions, args ...string) error { +func testRunForgetMayFail(t testing.TB, gopts global.Options, opts ForgetOptions, args ...string) error { pruneOpts := PruneOptions{ MaxUnused: "5%", } - return withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runForget(context.TODO(), opts, pruneOpts, gopts, gopts.term, args) + return withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + return runForget(context.TODO(), opts, pruneOpts, gopts, gopts.Term, args) }) } -func testRunForget(t testing.TB, gopts GlobalOptions, opts ForgetOptions, args ...string) { +func testRunForget(t testing.TB, gopts global.Options, opts ForgetOptions, args ...string) { rtest.OK(t, testRunForgetMayFail(t, gopts, opts, args...)) } diff --git a/cmd/restic/cmd_generate.go b/cmd/restic/cmd_generate.go index 580e704dc..6263804da 100644 --- a/cmd/restic/cmd_generate.go +++ b/cmd/restic/cmd_generate.go @@ -6,6 +6,7 @@ import ( "time" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui/progress" "github.com/spf13/cobra" @@ -13,7 +14,7 @@ import ( "github.com/spf13/pflag" ) -func newGenerateCommand(globalOptions *GlobalOptions) *cobra.Command { +func newGenerateCommand(globalOptions *global.Options) *cobra.Command { var opts generateOptions cmd := &cobra.Command{ @@ -31,7 +32,7 @@ Exit status is 1 if there was any error. `, DisableAutoGenTag: true, RunE: func(_ *cobra.Command, args []string) error { - return runGenerate(opts, *globalOptions, args, globalOptions.term) + return runGenerate(opts, *globalOptions, args, globalOptions.Term) }, } opts.AddFlags(cmd.Flags()) @@ -72,7 +73,7 @@ func writeManpages(root *cobra.Command, dir string, printer progress.Printer) er return doc.GenManTree(root, header, dir) } -func writeCompletion(filename string, shell string, generate func(w io.Writer) error, printer progress.Printer, gopts GlobalOptions) (err error) { +func writeCompletion(filename string, shell string, generate func(w io.Writer) error, printer progress.Printer, gopts global.Options) (err error) { printer.PT("writing %s completion file to %v", shell, filename) var outWriter io.Writer if filename != "-" { @@ -84,7 +85,7 @@ func writeCompletion(filename string, shell string, generate func(w io.Writer) e defer func() { err = outFile.Close() }() outWriter = outFile } else { - outWriter = gopts.term.OutputWriter() + outWriter = gopts.Term.OutputWriter() } err = generate(outWriter) @@ -110,13 +111,13 @@ func checkStdoutForSingleShell(opts generateOptions) error { return nil } -func runGenerate(opts generateOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { +func runGenerate(opts generateOptions, gopts global.Options, args []string, term ui.Terminal) error { if len(args) > 0 { return errors.Fatal("the generate command expects no arguments, only options - please see `restic help generate` for usage and flags") } - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) - cmdRoot := newRootCommand(&GlobalOptions{}) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) + cmdRoot := newRootCommand(&global.Options{}) if opts.ManDir != "" { err := writeManpages(cmdRoot, opts.ManDir, printer) diff --git a/cmd/restic/cmd_generate_integration_test.go b/cmd/restic/cmd_generate_integration_test.go index 97248d370..9ff1cfae8 100644 --- a/cmd/restic/cmd_generate_integration_test.go +++ b/cmd/restic/cmd_generate_integration_test.go @@ -5,12 +5,13 @@ import ( "strings" "testing" + "github.com/restic/restic/internal/global" rtest "github.com/restic/restic/internal/test" ) -func testRunGenerate(t testing.TB, gopts GlobalOptions, opts generateOptions) ([]byte, error) { - buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runGenerate(opts, gopts, []string{}, gopts.term) +func testRunGenerate(t testing.TB, gopts global.Options, opts generateOptions) ([]byte, error) { + buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error { + return runGenerate(opts, gopts, []string{}, gopts.Term) }) return buf.Bytes(), err } @@ -28,14 +29,14 @@ func TestGenerateStdout(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - output, err := testRunGenerate(t, GlobalOptions{}, tc.opts) + output, err := testRunGenerate(t, global.Options{}, tc.opts) rtest.OK(t, err) rtest.Assert(t, strings.Contains(string(output), "# "+tc.name+" completion for restic"), "has no expected completion header") }) } t.Run("Generate shell completions to stdout for two shells", func(t *testing.T) { - _, err := testRunGenerate(t, GlobalOptions{}, generateOptions{BashCompletionFile: "-", FishCompletionFile: "-"}) + _, err := testRunGenerate(t, global.Options{}, generateOptions{BashCompletionFile: "-", FishCompletionFile: "-"}) rtest.Assert(t, err != nil, "generate shell completions to stdout for two shells fails") }) } diff --git a/cmd/restic/cmd_init.go b/cmd/restic/cmd_init.go index c13b69e87..50d8810d8 100644 --- a/cmd/restic/cmd_init.go +++ b/cmd/restic/cmd_init.go @@ -8,7 +8,7 @@ import ( "github.com/restic/chunker" "github.com/restic/restic/internal/backend/location" "github.com/restic/restic/internal/errors" - "github.com/restic/restic/internal/repository" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui/progress" @@ -17,7 +17,7 @@ import ( "github.com/spf13/pflag" ) -func newInitCommand(globalOptions *GlobalOptions) *cobra.Command { +func newInitCommand(globalOptions *global.Options) *cobra.Command { var opts InitOptions cmd := &cobra.Command{ @@ -35,7 +35,7 @@ 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(), opts, *globalOptions, args, globalOptions.term) + return runInit(cmd.Context(), opts, *globalOptions, args, globalOptions.Term) }, } opts.AddFlags(cmd.Flags()) @@ -44,23 +44,23 @@ Exit status is 1 if there was any error. // InitOptions bundles all options for the init command. type InitOptions struct { - secondaryRepoOptions + global.SecondaryRepoOptions CopyChunkerParameters bool RepositoryVersion string } func (opts *InitOptions) AddFlags(f *pflag.FlagSet) { - opts.secondaryRepoOptions.AddFlags(f, "secondary", "to copy chunker parameters from") + opts.SecondaryRepoOptions.AddFlags(f, "secondary", "to copy chunker parameters from") f.BoolVar(&opts.CopyChunkerParameters, "copy-chunker-params", false, "copy chunker parameters from the secondary repository (useful with the copy command)") f.StringVar(&opts.RepositoryVersion, "repository-version", "stable", "repository format version to use, allowed values are a format version, 'latest' and 'stable'") } -func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { +func runInit(ctx context.Context, opts InitOptions, gopts global.Options, args []string, term ui.Terminal) 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") } - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) var version uint switch opts.RepositoryVersion { @@ -76,47 +76,18 @@ func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args [] version = uint(v) } - if version < restic.MinRepoVersion || version > restic.MaxRepoVersion { - return errors.Fatalf("only repository versions between %v and %v are allowed", restic.MinRepoVersion, restic.MaxRepoVersion) - } - chunkerPolynomial, err := maybeReadChunkerPolynomial(ctx, opts, gopts, printer) if err != nil { return err } - gopts.Repo, err = ReadRepo(gopts) - if err != nil { - return err - } - - gopts.password, err = ReadPasswordTwice(ctx, gopts, - "enter password for new repository: ", - "enter password again: ") - if err != nil { - return err - } - - be, err := create(ctx, gopts.Repo, gopts, gopts.extended, printer) - if err != nil { - return errors.Fatalf("create repository at %s failed: %v", location.StripPassword(gopts.backends, gopts.Repo), err) - } - - s, err := repository.New(be, repository.Options{ - Compression: gopts.Compression, - PackSize: gopts.PackSize * 1024 * 1024, - }) + s, err := global.CreateRepository(ctx, gopts, version, chunkerPolynomial, printer) if err != nil { return errors.Fatalf("%s", err) } - err = s.Init(ctx, version, gopts.password, chunkerPolynomial) - if err != nil { - return errors.Fatalf("create key in repository at %s failed: %v", location.StripPassword(gopts.backends, gopts.Repo), err) - } - if !gopts.JSON { - printer.P("created restic repository %v at %s", s.Config().ID[:10], location.StripPassword(gopts.backends, gopts.Repo)) + printer.P("created restic repository %v at %s", s.Config().ID[:10], location.StripPassword(gopts.Backends, gopts.Repo)) if opts.CopyChunkerParameters && chunkerPolynomial != nil { printer.P(" with chunker parameters copied from secondary repository") } @@ -129,22 +100,22 @@ func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args [] status := initSuccess{ MessageType: "initialized", ID: s.Config().ID, - Repository: location.StripPassword(gopts.backends, gopts.Repo), + Repository: location.StripPassword(gopts.Backends, gopts.Repo), } - return json.NewEncoder(gopts.term.OutputWriter()).Encode(status) + return json.NewEncoder(gopts.Term.OutputWriter()).Encode(status) } return nil } -func maybeReadChunkerPolynomial(ctx context.Context, opts InitOptions, gopts GlobalOptions, printer progress.Printer) (*chunker.Pol, error) { +func maybeReadChunkerPolynomial(ctx context.Context, opts InitOptions, gopts global.Options, printer progress.Printer) (*chunker.Pol, error) { if opts.CopyChunkerParameters { - otherGopts, _, err := fillSecondaryGlobalOpts(ctx, opts.secondaryRepoOptions, gopts, "secondary") + otherGopts, _, err := opts.SecondaryRepoOptions.FillGlobalOpts(ctx, gopts, "secondary") if err != nil { return nil, err } - otherRepo, err := OpenRepository(ctx, otherGopts, printer) + otherRepo, err := global.OpenRepository(ctx, otherGopts, printer) if err != nil { return nil, err } diff --git a/cmd/restic/cmd_init_integration_test.go b/cmd/restic/cmd_init_integration_test.go index 174b17360..ef08eabe5 100644 --- a/cmd/restic/cmd_init_integration_test.go +++ b/cmd/restic/cmd_init_integration_test.go @@ -6,19 +6,20 @@ import ( "path/filepath" "testing" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" "github.com/restic/restic/internal/ui/progress" ) -func testRunInit(t testing.TB, gopts GlobalOptions) { +func testRunInit(t testing.TB, gopts global.Options) { repository.TestUseLowSecurityKDFParameters(t) restic.TestDisableCheckPolynomial(t) restic.TestSetLockTimeout(t, 0) - err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runInit(ctx, InitOptions{}, gopts, nil, gopts.term) + err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + return runInit(ctx, InitOptions{}, gopts, nil, gopts.Term) }) rtest.OK(t, err) t.Logf("repository initialized at %v", gopts.Repo) @@ -38,32 +39,32 @@ func TestInitCopyChunkerParams(t *testing.T) { testRunInit(t, env2.gopts) initOpts := InitOptions{ - secondaryRepoOptions: secondaryRepoOptions{ + SecondaryRepoOptions: global.SecondaryRepoOptions{ Repo: env2.gopts.Repo, - password: env2.gopts.password, + Password: env2.gopts.Password, }, } - err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runInit(ctx, initOpts, gopts, nil, gopts.term) + err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runInit(ctx, initOpts, gopts, nil, gopts.Term) }) rtest.Assert(t, err != nil, "expected invalid init options to fail") initOpts.CopyChunkerParameters = true - err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runInit(ctx, initOpts, gopts, nil, gopts.term) + err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runInit(ctx, initOpts, gopts, nil, gopts.Term) }) rtest.OK(t, err) var repo *repository.Repository - err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - repo, err = OpenRepository(ctx, gopts, &progress.NoopPrinter{}) + err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + repo, err = global.OpenRepository(ctx, gopts, &progress.NoopPrinter{}) return err }) rtest.OK(t, err) var otherRepo *repository.Repository - err = withTermStatus(t, env2.gopts, func(ctx context.Context, gopts GlobalOptions) error { - otherRepo, err = OpenRepository(ctx, gopts, &progress.NoopPrinter{}) + err = withTermStatus(t, env2.gopts, func(ctx context.Context, gopts global.Options) error { + otherRepo, err = global.OpenRepository(ctx, gopts, &progress.NoopPrinter{}) return err }) rtest.OK(t, err) diff --git a/cmd/restic/cmd_key.go b/cmd/restic/cmd_key.go index 508b428e0..9d79db6d4 100644 --- a/cmd/restic/cmd_key.go +++ b/cmd/restic/cmd_key.go @@ -1,10 +1,11 @@ package main import ( + "github.com/restic/restic/internal/global" "github.com/spf13/cobra" ) -func newKeyCommand(globalOptions *GlobalOptions) *cobra.Command { +func newKeyCommand(globalOptions *global.Options) *cobra.Command { cmd := &cobra.Command{ Use: "key", Short: "Manage keys (passwords)", diff --git a/cmd/restic/cmd_key_add.go b/cmd/restic/cmd_key_add.go index 455969d6e..5df3819ac 100644 --- a/cmd/restic/cmd_key_add.go +++ b/cmd/restic/cmd_key_add.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui/progress" @@ -12,7 +13,7 @@ import ( "github.com/spf13/pflag" ) -func newKeyAddCommand(globalOptions *GlobalOptions) *cobra.Command { +func newKeyAddCommand(globalOptions *global.Options) *cobra.Command { var opts KeyAddOptions cmd := &cobra.Command{ @@ -32,7 +33,7 @@ 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, globalOptions.term) + return runKeyAdd(cmd.Context(), *globalOptions, opts, args, globalOptions.Term) }, } @@ -54,12 +55,12 @@ func (opts *KeyAddOptions) Add(flags *pflag.FlagSet) { flags.StringVarP(&opts.Hostname, "host", "", "", "the hostname for new key") } -func runKeyAdd(ctx context.Context, gopts GlobalOptions, opts KeyAddOptions, args []string, term ui.Terminal) error { +func runKeyAdd(ctx context.Context, gopts global.Options, opts KeyAddOptions, args []string, term ui.Terminal) error { if len(args) > 0 { return fmt.Errorf("the key add command expects no arguments, only options - please see `restic help key add` for usage and flags") } - printer := ui.NewProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) ctx, repo, unlock, err := openWithAppendLock(ctx, gopts, false, printer) if err != nil { return err @@ -69,7 +70,7 @@ func runKeyAdd(ctx context.Context, gopts GlobalOptions, opts KeyAddOptions, arg return addKey(ctx, repo, gopts, opts, printer) } -func addKey(ctx context.Context, repo *repository.Repository, gopts GlobalOptions, opts KeyAddOptions, printer progress.Printer) error { +func addKey(ctx context.Context, repo *repository.Repository, gopts global.Options, opts KeyAddOptions, printer progress.Printer) error { pw, err := getNewPassword(ctx, gopts, opts.NewPasswordFile, opts.InsecureNoPassword) if err != nil { return err @@ -93,7 +94,7 @@ func addKey(ctx context.Context, repo *repository.Repository, gopts GlobalOption // testKeyNewPassword is used to set a new password during integration testing. var testKeyNewPassword string -func getNewPassword(ctx context.Context, gopts GlobalOptions, newPasswordFile string, insecureNoPassword bool) (string, error) { +func getNewPassword(ctx context.Context, gopts global.Options, newPasswordFile string, insecureNoPassword bool) (string, error) { if testKeyNewPassword != "" { return testKeyNewPassword, nil } @@ -106,7 +107,7 @@ func getNewPassword(ctx context.Context, gopts GlobalOptions, newPasswordFile st } if newPasswordFile != "" { - password, err := loadPasswordFromFile(newPasswordFile) + password, err := global.LoadPasswordFromFile(newPasswordFile) if err != nil { return "", err } @@ -119,11 +120,11 @@ func getNewPassword(ctx context.Context, gopts GlobalOptions, newPasswordFile st // Since we already have an open repository, temporary remove the password // to prompt the user for the passwd. newopts := gopts - newopts.password = "" + newopts.Password = "" // empty passwords are already handled above newopts.InsecureNoPassword = false - return ReadPasswordTwice(ctx, newopts, + return global.ReadPasswordTwice(ctx, newopts, "enter new password: ", "enter password again: ") } diff --git a/cmd/restic/cmd_key_integration_test.go b/cmd/restic/cmd_key_integration_test.go index 3a127946a..000094632 100644 --- a/cmd/restic/cmd_key_integration_test.go +++ b/cmd/restic/cmd_key_integration_test.go @@ -10,14 +10,15 @@ import ( "testing" "github.com/restic/restic/internal/backend" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" rtest "github.com/restic/restic/internal/test" "github.com/restic/restic/internal/ui/progress" ) -func testRunKeyListOtherIDs(t testing.TB, gopts GlobalOptions) []string { - buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runKeyList(ctx, gopts, []string{}, gopts.term) +func testRunKeyListOtherIDs(t testing.TB, gopts global.Options) []string { + buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error { + return runKeyList(ctx, gopts, []string{}, gopts.Term) }) rtest.OK(t, err) @@ -34,35 +35,35 @@ func testRunKeyListOtherIDs(t testing.TB, gopts GlobalOptions) []string { return IDs } -func testRunKeyAddNewKey(t testing.TB, newPassword string, gopts GlobalOptions) { +func testRunKeyAddNewKey(t testing.TB, newPassword string, gopts global.Options) { testKeyNewPassword = newPassword defer func() { testKeyNewPassword = "" }() - err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{}, gopts.term) + err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{}, gopts.Term) }) rtest.OK(t, err) } -func testRunKeyAddNewKeyUserHost(t testing.TB, gopts GlobalOptions) { +func testRunKeyAddNewKeyUserHost(t testing.TB, gopts global.Options) { testKeyNewPassword = "john's geheimnis" defer func() { testKeyNewPassword = "" }() t.Log("adding key for john@example.com") - err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { + err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { return runKeyAdd(ctx, gopts, KeyAddOptions{ Username: "john", Hostname: "example.com", - }, []string{}, gopts.term) + }, []string{}, gopts.Term) }) rtest.OK(t, err) - _ = withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - repo, err := OpenRepository(ctx, gopts, &progress.NoopPrinter{}) + _ = withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + repo, err := global.OpenRepository(ctx, gopts, &progress.NoopPrinter{}) rtest.OK(t, err) key, err := repository.SearchKey(ctx, repo, testKeyNewPassword, 2, "") rtest.OK(t, err) @@ -73,23 +74,23 @@ func testRunKeyAddNewKeyUserHost(t testing.TB, gopts GlobalOptions) { }) } -func testRunKeyPasswd(t testing.TB, newPassword string, gopts GlobalOptions) { +func testRunKeyPasswd(t testing.TB, newPassword string, gopts global.Options) { testKeyNewPassword = newPassword defer func() { testKeyNewPassword = "" }() - err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{}, gopts.term) + err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{}, gopts.Term) }) rtest.OK(t, err) } -func testRunKeyRemove(t testing.TB, gopts GlobalOptions, IDs []string) { +func testRunKeyRemove(t testing.TB, gopts global.Options, IDs []string) { t.Logf("remove %d keys: %q\n", len(IDs), IDs) for _, id := range IDs { - err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runKeyRemove(ctx, gopts, []string{id}, gopts.term) + err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + return runKeyRemove(ctx, gopts, []string{id}, gopts.Term) }) rtest.OK(t, err) } @@ -103,26 +104,26 @@ func TestKeyAddRemove(t *testing.T) { env, cleanup := withTestEnvironment(t) // must list keys more than once - env.gopts.backendTestHook = nil + env.gopts.BackendTestHook = nil defer cleanup() testRunInit(t, env.gopts) testRunKeyPasswd(t, "geheim2", env.gopts) - env.gopts.password = "geheim2" - t.Logf("changed password to %q", env.gopts.password) + env.gopts.Password = "geheim2" + t.Logf("changed password to %q", env.gopts.Password) for _, newPassword := range passwordList { testRunKeyAddNewKey(t, newPassword, env.gopts) t.Logf("added new password %q", newPassword) - env.gopts.password = newPassword + env.gopts.Password = newPassword testRunKeyRemove(t, env.gopts, testRunKeyListOtherIDs(t, env.gopts)) } - env.gopts.password = passwordList[len(passwordList)-1] - t.Logf("testing access with last password %q\n", env.gopts.password) - err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runKeyList(ctx, gopts, []string{}, gopts.term) + env.gopts.Password = passwordList[len(passwordList)-1] + t.Logf("testing access with last password %q\n", env.gopts.Password) + err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runKeyList(ctx, gopts, []string{}, gopts.Term) }) rtest.OK(t, err) testRunCheck(t, env.gopts) @@ -135,21 +136,21 @@ func TestKeyAddInvalid(t *testing.T) { defer cleanup() testRunInit(t, env.gopts) - err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { + err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { return runKeyAdd(ctx, gopts, KeyAddOptions{ NewPasswordFile: "some-file", InsecureNoPassword: true, - }, []string{}, gopts.term) + }, []string{}, gopts.Term) }) rtest.Assert(t, strings.Contains(err.Error(), "only either"), "unexpected error message, got %q", err) pwfile := filepath.Join(t.TempDir(), "pwfile") rtest.OK(t, os.WriteFile(pwfile, []byte{}, 0o666)) - err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { + err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { return runKeyAdd(ctx, gopts, KeyAddOptions{ NewPasswordFile: pwfile, - }, []string{}, gopts.term) + }, []string{}, gopts.Term) }) rtest.Assert(t, strings.Contains(err.Error(), "an empty password is not allowed by default"), "unexpected error message, got %q", err) } @@ -157,18 +158,18 @@ func TestKeyAddInvalid(t *testing.T) { func TestKeyAddEmpty(t *testing.T) { env, cleanup := withTestEnvironment(t) // must list keys more than once - env.gopts.backendTestHook = nil + env.gopts.BackendTestHook = nil defer cleanup() testRunInit(t, env.gopts) - err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { + err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { return runKeyAdd(ctx, gopts, KeyAddOptions{ InsecureNoPassword: true, - }, []string{}, gopts.term) + }, []string{}, gopts.Term) }) rtest.OK(t, err) - env.gopts.password = "" + env.gopts.Password = "" env.gopts.InsecureNoPassword = true testRunCheck(t, env.gopts) @@ -187,7 +188,7 @@ func TestKeyProblems(t *testing.T) { defer cleanup() testRunInit(t, env.gopts) - env.gopts.backendTestHook = func(r backend.Backend) (backend.Backend, error) { + env.gopts.BackendTestHook = func(r backend.Backend) (backend.Backend, error) { return &emptySaveBackend{r}, nil } @@ -196,21 +197,21 @@ func TestKeyProblems(t *testing.T) { testKeyNewPassword = "" }() - err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{}, gopts.term) + err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{}, gopts.Term) }) t.Log(err) rtest.Assert(t, err != nil, "expected passwd change to fail") - err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{}, gopts.term) + err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{}, gopts.Term) }) t.Log(err) rtest.Assert(t, err != nil, "expected key adding to fail") - t.Logf("testing access with initial password %q\n", env.gopts.password) - err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runKeyList(ctx, gopts, []string{}, gopts.term) + t.Logf("testing access with initial password %q\n", env.gopts.Password) + err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runKeyList(ctx, gopts, []string{}, gopts.Term) }) rtest.OK(t, err) testRunCheck(t, env.gopts) @@ -221,36 +222,36 @@ func TestKeyCommandInvalidArguments(t *testing.T) { defer cleanup() testRunInit(t, env.gopts) - env.gopts.backendTestHook = func(r backend.Backend) (backend.Backend, error) { + env.gopts.BackendTestHook = func(r backend.Backend) (backend.Backend, error) { return &emptySaveBackend{r}, nil } - err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{"johndoe"}, gopts.term) + err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{"johndoe"}, gopts.Term) }) t.Log(err) rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key add: %v", err) - err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{"johndoe"}, gopts.term) + err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{"johndoe"}, gopts.Term) }) t.Log(err) rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key passwd: %v", err) - err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runKeyList(ctx, gopts, []string{"johndoe"}, gopts.term) + err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runKeyList(ctx, gopts, []string{"johndoe"}, gopts.Term) }) t.Log(err) rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key list: %v", err) - err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runKeyRemove(ctx, gopts, []string{}, gopts.term) + err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runKeyRemove(ctx, gopts, []string{}, gopts.Term) }) t.Log(err) rtest.Assert(t, err != nil && strings.Contains(err.Error(), "one argument"), "unexpected error for key remove: %v", err) - err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runKeyRemove(ctx, gopts, []string{"john", "doe"}, gopts.term) + err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runKeyRemove(ctx, gopts, []string{"john", "doe"}, gopts.Term) }) t.Log(err) rtest.Assert(t, err != nil && strings.Contains(err.Error(), "one argument"), "unexpected error for key remove: %v", err) diff --git a/cmd/restic/cmd_key_list.go b/cmd/restic/cmd_key_list.go index 91c14f6c0..73527ae48 100644 --- a/cmd/restic/cmd_key_list.go +++ b/cmd/restic/cmd_key_list.go @@ -6,6 +6,7 @@ import ( "fmt" "sync" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" @@ -14,7 +15,7 @@ import ( "github.com/spf13/cobra" ) -func newKeyListCommand(globalOptions *GlobalOptions) *cobra.Command { +func newKeyListCommand(globalOptions *global.Options) *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "List keys (passwords)", @@ -34,18 +35,18 @@ Exit status is 12 if the password is incorrect. `, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - return runKeyList(cmd.Context(), *globalOptions, args, globalOptions.term) + return runKeyList(cmd.Context(), *globalOptions, args, globalOptions.Term) }, } return cmd } -func runKeyList(ctx context.Context, gopts GlobalOptions, args []string, term ui.Terminal) error { +func runKeyList(ctx context.Context, gopts global.Options, args []string, term ui.Terminal) error { if len(args) > 0 { return fmt.Errorf("the key list command expects no arguments, only options - please see `restic help key list` for usage and flags") } - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, gopts.NoLock, printer) if err != nil { return err @@ -55,7 +56,7 @@ func runKeyList(ctx context.Context, gopts GlobalOptions, args []string, term ui return listKeys(ctx, repo, gopts, printer) } -func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions, printer progress.Printer) error { +func listKeys(ctx context.Context, s *repository.Repository, gopts global.Options, printer progress.Printer) error { type keyInfo struct { Current bool `json:"current"` ID string `json:"id"` @@ -81,7 +82,7 @@ func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions ShortID: id.Str(), UserName: k.Username, HostName: k.Hostname, - Created: k.Created.Local().Format(TimeFormat), + Created: k.Created.Local().Format(global.TimeFormat), } m.Lock() @@ -95,7 +96,7 @@ func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions } if gopts.JSON { - return json.NewEncoder(gopts.term.OutputWriter()).Encode(keys) + return json.NewEncoder(gopts.Term.OutputWriter()).Encode(keys) } tab := table.New() @@ -108,5 +109,5 @@ func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions tab.AddRow(key) } - return tab.Write(gopts.term.OutputWriter()) + return tab.Write(gopts.Term.OutputWriter()) } diff --git a/cmd/restic/cmd_key_passwd.go b/cmd/restic/cmd_key_passwd.go index 98cdfc43f..fad1361da 100644 --- a/cmd/restic/cmd_key_passwd.go +++ b/cmd/restic/cmd_key_passwd.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui/progress" @@ -12,7 +13,7 @@ import ( "github.com/spf13/pflag" ) -func newKeyPasswdCommand(globalOptions *GlobalOptions) *cobra.Command { +func newKeyPasswdCommand(globalOptions *global.Options) *cobra.Command { var opts KeyPasswdOptions cmd := &cobra.Command{ @@ -33,7 +34,7 @@ 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, globalOptions.term) + return runKeyPasswd(cmd.Context(), *globalOptions, opts, args, globalOptions.Term) }, } @@ -49,12 +50,12 @@ func (opts *KeyPasswdOptions) AddFlags(flags *pflag.FlagSet) { opts.KeyAddOptions.Add(flags) } -func runKeyPasswd(ctx context.Context, gopts GlobalOptions, opts KeyPasswdOptions, args []string, term ui.Terminal) error { +func runKeyPasswd(ctx context.Context, gopts global.Options, opts KeyPasswdOptions, args []string, term ui.Terminal) error { if len(args) > 0 { return fmt.Errorf("the key passwd command expects no arguments, only options - please see `restic help key passwd` for usage and flags") } - printer := ui.NewProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) if err != nil { return err @@ -64,7 +65,7 @@ func runKeyPasswd(ctx context.Context, gopts GlobalOptions, opts KeyPasswdOption return changePassword(ctx, repo, gopts, opts, printer) } -func changePassword(ctx context.Context, repo *repository.Repository, gopts GlobalOptions, opts KeyPasswdOptions, printer progress.Printer) error { +func changePassword(ctx context.Context, repo *repository.Repository, gopts global.Options, opts KeyPasswdOptions, printer progress.Printer) error { pw, err := getNewPassword(ctx, gopts, opts.NewPasswordFile, opts.InsecureNoPassword) if err != nil { return err diff --git a/cmd/restic/cmd_key_remove.go b/cmd/restic/cmd_key_remove.go index 6ab034bd9..90ab6b7fe 100644 --- a/cmd/restic/cmd_key_remove.go +++ b/cmd/restic/cmd_key_remove.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" @@ -12,7 +13,7 @@ import ( "github.com/spf13/cobra" ) -func newKeyRemoveCommand(globalOptions *GlobalOptions) *cobra.Command { +func newKeyRemoveCommand(globalOptions *global.Options) *cobra.Command { cmd := &cobra.Command{ Use: "remove [ID]", Short: "Remove key ID (password) from the repository.", @@ -31,18 +32,18 @@ Exit status is 12 if the password is incorrect. `, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - return runKeyRemove(cmd.Context(), *globalOptions, args, globalOptions.term) + return runKeyRemove(cmd.Context(), *globalOptions, args, globalOptions.Term) }, } return cmd } -func runKeyRemove(ctx context.Context, gopts GlobalOptions, args []string, term ui.Terminal) error { +func runKeyRemove(ctx context.Context, gopts global.Options, args []string, term ui.Terminal) error { if len(args) != 1 { return fmt.Errorf("key remove expects one argument as the key id") } - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) if err != nil { return err diff --git a/cmd/restic/cmd_list.go b/cmd/restic/cmd_list.go index 385cc82f6..d7c263abb 100644 --- a/cmd/restic/cmd_list.go +++ b/cmd/restic/cmd_list.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository/index" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" @@ -12,7 +13,7 @@ import ( "github.com/spf13/cobra" ) -func newListCommand(globalOptions *GlobalOptions) *cobra.Command { +func newListCommand(globalOptions *global.Options) *cobra.Command { var listAllowedArgs = []string{"blobs", "packs", "index", "snapshots", "keys", "locks"} var listAllowedArgsUseString = strings.Join(listAllowedArgs, "|") @@ -34,7 +35,7 @@ Exit status is 12 if the password is incorrect. DisableAutoGenTag: true, GroupID: cmdGroupDefault, RunE: func(cmd *cobra.Command, args []string) error { - return runList(cmd.Context(), *globalOptions, args, globalOptions.term) + return runList(cmd.Context(), *globalOptions, args, globalOptions.Term) }, ValidArgs: listAllowedArgs, Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), @@ -42,8 +43,8 @@ Exit status is 12 if the password is incorrect. return cmd } -func runList(ctx context.Context, gopts GlobalOptions, args []string, term ui.Terminal) error { - printer := ui.NewProgressPrinter(false, gopts.verbosity, term) +func runList(ctx context.Context, gopts global.Options, args []string, term ui.Terminal) error { + printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) if len(args) != 1 { return errors.Fatal("type not specified") diff --git a/cmd/restic/cmd_list_integration_test.go b/cmd/restic/cmd_list_integration_test.go index b8b528945..655d6da27 100644 --- a/cmd/restic/cmd_list_integration_test.go +++ b/cmd/restic/cmd_list_integration_test.go @@ -8,14 +8,15 @@ import ( "strings" "testing" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" "github.com/restic/restic/internal/ui" ) -func testRunList(t testing.TB, gopts GlobalOptions, tpe string) restic.IDs { - buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runList(ctx, gopts, []string{tpe}, gopts.term) +func testRunList(t testing.TB, gopts global.Options, tpe string) restic.IDs { + buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error { + return runList(ctx, gopts, []string{tpe}, gopts.Term) }) rtest.OK(t, err) return parseIDsFromReader(t, buf) @@ -50,7 +51,7 @@ func parseIDsFromReader(t testing.TB, rd io.Reader) restic.IDs { return IDs } -func testListSnapshots(t testing.TB, gopts GlobalOptions, expected int) restic.IDs { +func testListSnapshots(t testing.TB, gopts global.Options, expected int) restic.IDs { t.Helper() snapshotIDs := testRunList(t, gopts, "snapshots") rtest.Assert(t, len(snapshotIDs) == expected, "expected %v snapshot, got %v", expected, snapshotIDs) @@ -58,9 +59,9 @@ func testListSnapshots(t testing.TB, gopts GlobalOptions, expected int) restic.I } // extract blob set from repository index -func testListBlobs(t testing.TB, gopts GlobalOptions) (blobSetFromIndex restic.IDSet) { - err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) +func testListBlobs(t testing.TB, gopts global.Options) (blobSetFromIndex restic.IDSet) { + err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) _, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() diff --git a/cmd/restic/cmd_ls.go b/cmd/restic/cmd_ls.go index 2bdf2bfa3..8e45c50a1 100644 --- a/cmd/restic/cmd_ls.go +++ b/cmd/restic/cmd_ls.go @@ -18,12 +18,13 @@ import ( "github.com/restic/restic/internal/data" "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/fs" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/walker" ) -func newLsCommand(globalOptions *GlobalOptions) *cobra.Command { +func newLsCommand(globalOptions *global.Options) *cobra.Command { var opts LsOptions cmd := &cobra.Command{ @@ -62,7 +63,7 @@ Exit status is 12 if the password is incorrect. GroupID: cmdGroupDefault, RunE: func(cmd *cobra.Command, args []string) error { finalizeSnapshotFilter(&opts.SnapshotFilter) - return runLs(cmd.Context(), opts, *globalOptions, args, globalOptions.term) + return runLs(cmd.Context(), opts, *globalOptions, args, globalOptions.Term) }, } opts.AddFlags(cmd.Flags()) @@ -303,8 +304,8 @@ type toSortOutput struct { node *data.Node } -func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { - termPrinter := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) +func runLs(ctx context.Context, opts LsOptions, gopts global.Options, args []string, term ui.Terminal) error { + termPrinter := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) if len(args) == 0 { return errors.Fatal("no snapshot ID specified, specify snapshot ID or use special ID 'latest'") @@ -383,11 +384,11 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri if gopts.JSON { printer = &jsonLsPrinter{ - enc: json.NewEncoder(gopts.term.OutputWriter()), + enc: json.NewEncoder(gopts.Term.OutputWriter()), } } else if opts.Ncdu { printer = &ncduLsPrinter{ - out: gopts.term.OutputWriter(), + out: gopts.Term.OutputWriter(), } } else { printer = &textLsPrinter{ diff --git a/cmd/restic/cmd_ls_integration_test.go b/cmd/restic/cmd_ls_integration_test.go index 1f16fec10..d05a89d86 100644 --- a/cmd/restic/cmd_ls_integration_test.go +++ b/cmd/restic/cmd_ls_integration_test.go @@ -9,20 +9,21 @@ import ( "testing" "github.com/restic/restic/internal/data" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) -func testRunLsWithOpts(t testing.TB, gopts GlobalOptions, opts LsOptions, args []string) []byte { - buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { +func testRunLsWithOpts(t testing.TB, gopts global.Options, opts LsOptions, args []string) []byte { + buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error { gopts.Quiet = true - return runLs(context.TODO(), opts, gopts, args, gopts.term) + return runLs(context.TODO(), opts, gopts, args, gopts.Term) }) rtest.OK(t, err) return buf.Bytes() } -func testRunLs(t testing.TB, gopts GlobalOptions, snapshotID string) []string { +func testRunLs(t testing.TB, gopts global.Options, snapshotID string) []string { out := testRunLsWithOpts(t, gopts, LsOptions{}, []string{snapshotID}) return strings.Split(string(out), "\n") } diff --git a/cmd/restic/cmd_migrate.go b/cmd/restic/cmd_migrate.go index bacc7f24a..8ad3541eb 100644 --- a/cmd/restic/cmd_migrate.go +++ b/cmd/restic/cmd_migrate.go @@ -3,6 +3,7 @@ package main import ( "context" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/migrations" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" @@ -12,7 +13,7 @@ import ( "github.com/spf13/pflag" ) -func newMigrateCommand(globalOptions *GlobalOptions) *cobra.Command { +func newMigrateCommand(globalOptions *global.Options) *cobra.Command { var opts MigrateOptions cmd := &cobra.Command{ @@ -35,7 +36,7 @@ Exit status is 12 if the password is incorrect. DisableAutoGenTag: true, GroupID: cmdGroupDefault, RunE: func(cmd *cobra.Command, args []string) error { - return runMigrate(cmd.Context(), opts, *globalOptions, args, globalOptions.term) + return runMigrate(cmd.Context(), opts, *globalOptions, args, globalOptions.Term) }, } @@ -75,7 +76,7 @@ func checkMigrations(ctx context.Context, repo restic.Repository, printer progre return nil } -func applyMigrations(ctx context.Context, opts MigrateOptions, gopts GlobalOptions, repo restic.Repository, args []string, term ui.Terminal, printer progress.Printer) error { +func applyMigrations(ctx context.Context, opts MigrateOptions, gopts global.Options, repo restic.Repository, args []string, term ui.Terminal, printer progress.Printer) error { var firsterr error for _, name := range args { found := false @@ -133,8 +134,8 @@ func applyMigrations(ctx context.Context, opts MigrateOptions, gopts GlobalOptio return firsterr } -func runMigrate(ctx context.Context, opts MigrateOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { - printer := ui.NewProgressPrinter(false, gopts.verbosity, term) +func runMigrate(ctx context.Context, opts MigrateOptions, gopts global.Options, args []string, term ui.Terminal) error { + printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) if err != nil { diff --git a/cmd/restic/cmd_mount.go b/cmd/restic/cmd_mount.go index 9bcf1295c..a05f9c6b5 100644 --- a/cmd/restic/cmd_mount.go +++ b/cmd/restic/cmd_mount.go @@ -16,6 +16,7 @@ import ( "github.com/restic/restic/internal/data" "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/fuse" @@ -24,11 +25,11 @@ import ( "github.com/anacrolix/fuse/fs" ) -func registerMountCommand(cmdRoot *cobra.Command, globalOptions *GlobalOptions) { +func registerMountCommand(cmdRoot *cobra.Command, globalOptions *global.Options) { cmdRoot.AddCommand(newMountCommand(globalOptions)) } -func newMountCommand(globalOptions *GlobalOptions) *cobra.Command { +func newMountCommand(globalOptions *global.Options) *cobra.Command { var opts MountOptions cmd := &cobra.Command{ @@ -83,7 +84,7 @@ Exit status is 12 if the password is incorrect. GroupID: cmdGroupDefault, RunE: func(cmd *cobra.Command, args []string) error { finalizeSnapshotFilter(&opts.SnapshotFilter) - return runMount(cmd.Context(), opts, *globalOptions, args, globalOptions.term) + return runMount(cmd.Context(), opts, *globalOptions, args, globalOptions.Term) }, } @@ -114,8 +115,8 @@ func (opts *MountOptions) AddFlags(f *pflag.FlagSet) { _ = f.MarkDeprecated("snapshot-template", "use --time-template") } -func runMount(ctx context.Context, opts MountOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { - printer := ui.NewProgressPrinter(false, gopts.verbosity, term) +func runMount(ctx context.Context, opts MountOptions, gopts global.Options, args []string, term ui.Terminal) error { + printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) if opts.TimeTemplate == "" { return errors.Fatal("time template string cannot be empty") diff --git a/cmd/restic/cmd_mount_disabled.go b/cmd/restic/cmd_mount_disabled.go index 57a44940f..4d45cbb6f 100644 --- a/cmd/restic/cmd_mount_disabled.go +++ b/cmd/restic/cmd_mount_disabled.go @@ -3,8 +3,11 @@ package main -import "github.com/spf13/cobra" +import ( + "github.com/restic/restic/internal/global" + "github.com/spf13/cobra" +) -func registerMountCommand(_ *cobra.Command, _ *GlobalOptions) { +func registerMountCommand(_ *cobra.Command, _ *global.Options) { // Mount command not supported on these platforms } diff --git a/cmd/restic/cmd_mount_integration_test.go b/cmd/restic/cmd_mount_integration_test.go index d4747c721..197b21d52 100644 --- a/cmd/restic/cmd_mount_integration_test.go +++ b/cmd/restic/cmd_mount_integration_test.go @@ -15,6 +15,7 @@ import ( systemFuse "github.com/anacrolix/fuse" "github.com/restic/restic/internal/data" "github.com/restic/restic/internal/debug" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" "github.com/restic/restic/internal/ui" @@ -58,13 +59,13 @@ func waitForMount(t testing.TB, dir string) { t.Errorf("subdir %q of dir %s never appeared", mountTestSubdir, dir) } -func testRunMount(t testing.TB, gopts GlobalOptions, dir string, wg *sync.WaitGroup) { +func testRunMount(t testing.TB, gopts global.Options, dir string, wg *sync.WaitGroup) { defer wg.Done() opts := MountOptions{ TimeTemplate: time.RFC3339, } - rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runMount(context.TODO(), opts, gopts, []string{dir}, gopts.term) + rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + return runMount(context.TODO(), opts, gopts, []string{dir}, gopts.Term) })) } @@ -91,7 +92,7 @@ func listSnapshots(t testing.TB, dir string) []string { return names } -func checkSnapshots(t testing.TB, gopts GlobalOptions, mountpoint string, snapshotIDs restic.IDs, expectedSnapshotsInFuseDir int) { +func checkSnapshots(t testing.TB, gopts global.Options, mountpoint string, snapshotIDs restic.IDs, expectedSnapshotsInFuseDir int) { t.Logf("checking for %d snapshots: %v", len(snapshotIDs), snapshotIDs) var wg sync.WaitGroup @@ -129,8 +130,8 @@ func checkSnapshots(t testing.TB, gopts GlobalOptions, mountpoint string, snapsh } } - err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) _, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) if err != nil { return err @@ -177,7 +178,7 @@ func TestMount(t *testing.T) { env, cleanup := withTestEnvironment(t) // must list snapshots more than once - env.gopts.backendTestHook = nil + env.gopts.BackendTestHook = nil defer cleanup() testRunInit(t, env.gopts) @@ -224,7 +225,7 @@ func TestMountSameTimestamps(t *testing.T) { env, cleanup := withTestEnvironment(t) // must list snapshots more than once - env.gopts.backendTestHook = nil + env.gopts.BackendTestHook = nil defer cleanup() rtest.SetupTarTestFixture(t, env.base, filepath.Join("testdata", "repo-same-timestamps.tar.gz")) diff --git a/cmd/restic/cmd_options.go b/cmd/restic/cmd_options.go index 801beb5cd..c6ddb648c 100644 --- a/cmd/restic/cmd_options.go +++ b/cmd/restic/cmd_options.go @@ -3,12 +3,13 @@ package main import ( "fmt" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/options" "github.com/spf13/cobra" ) -func newOptionsCommand(globalOptions *GlobalOptions) *cobra.Command { +func newOptionsCommand(globalOptions *global.Options) *cobra.Command { cmd := &cobra.Command{ Use: "options", Short: "Print list of extended options", @@ -24,7 +25,7 @@ Exit status is 1 if there was any error. GroupID: cmdGroupAdvanced, DisableAutoGenTag: true, Run: func(_ *cobra.Command, _ []string) { - globalOptions.term.Print("All Extended Options:") + globalOptions.Term.Print("All Extended Options:") var maxLen int for _, opt := range options.List() { if l := len(opt.Namespace + "." + opt.Name); l > maxLen { @@ -32,7 +33,7 @@ Exit status is 1 if there was any error. } } for _, opt := range options.List() { - globalOptions.term.Print(fmt.Sprintf(" %*s %s", -maxLen, opt.Namespace+"."+opt.Name, opt.Text)) + globalOptions.Term.Print(fmt.Sprintf(" %*s %s", -maxLen, opt.Namespace+"."+opt.Name, opt.Text)) } }, } diff --git a/cmd/restic/cmd_prune.go b/cmd/restic/cmd_prune.go index 778fce207..f6f5b0557 100644 --- a/cmd/restic/cmd_prune.go +++ b/cmd/restic/cmd_prune.go @@ -10,6 +10,7 @@ import ( "github.com/restic/restic/internal/data" "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" @@ -19,7 +20,7 @@ import ( "github.com/spf13/pflag" ) -func newPruneCommand(globalOptions *GlobalOptions) *cobra.Command { +func newPruneCommand(globalOptions *global.Options) *cobra.Command { var opts PruneOptions cmd := &cobra.Command{ @@ -41,7 +42,7 @@ Exit status is 12 if the password is incorrect. GroupID: cmdGroupDefault, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, _ []string) error { - return runPrune(cmd.Context(), opts, *globalOptions, globalOptions.term) + return runPrune(cmd.Context(), opts, *globalOptions, globalOptions.Term) }, } @@ -153,7 +154,7 @@ func verifyPruneOptions(opts *PruneOptions) error { return nil } -func runPrune(ctx context.Context, opts PruneOptions, gopts GlobalOptions, term ui.Terminal) error { +func runPrune(ctx context.Context, opts PruneOptions, gopts global.Options, term ui.Terminal) error { err := verifyPruneOptions(&opts) if err != nil { return err @@ -167,7 +168,7 @@ func runPrune(ctx context.Context, opts PruneOptions, gopts GlobalOptions, term return errors.Fatal("--no-lock is only applicable in combination with --dry-run for prune command") } - printer := ui.NewProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, opts.DryRun && gopts.NoLock, printer) if err != nil { return err diff --git a/cmd/restic/cmd_prune_integration_test.go b/cmd/restic/cmd_prune_integration_test.go index 70e66e383..1061966b3 100644 --- a/cmd/restic/cmd_prune_integration_test.go +++ b/cmd/restic/cmd_prune_integration_test.go @@ -7,29 +7,30 @@ import ( "testing" "github.com/restic/restic/internal/backend" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" rtest "github.com/restic/restic/internal/test" ) -func testRunPrune(t testing.TB, gopts GlobalOptions, opts PruneOptions) { +func testRunPrune(t testing.TB, gopts global.Options, opts PruneOptions) { t.Helper() rtest.OK(t, testRunPruneOutput(t, gopts, opts)) } -func testRunPruneMustFail(t testing.TB, gopts GlobalOptions, opts PruneOptions) { +func testRunPruneMustFail(t testing.TB, gopts global.Options, opts PruneOptions) { t.Helper() err := testRunPruneOutput(t, gopts, opts) rtest.Assert(t, err != nil, "expected non nil error") } -func testRunPruneOutput(t testing.TB, gopts GlobalOptions, opts PruneOptions) error { - oldHook := gopts.backendTestHook - gopts.backendTestHook = func(r backend.Backend) (backend.Backend, error) { return newListOnceBackend(r), nil } +func testRunPruneOutput(t testing.TB, gopts global.Options, opts PruneOptions) error { + oldHook := gopts.BackendTestHook + gopts.BackendTestHook = func(r backend.Backend) (backend.Backend, error) { return newListOnceBackend(r), nil } defer func() { - gopts.backendTestHook = oldHook + gopts.BackendTestHook = oldHook }() - return withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runPrune(context.TODO(), opts, gopts, gopts.term) + return withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + return runPrune(context.TODO(), opts, gopts, gopts.Term) }) } @@ -88,8 +89,8 @@ func createPrunableRepo(t *testing.T, env *testEnvironment) { testRunForget(t, env.gopts, ForgetOptions{}, firstSnapshot.String()) } -func testRunForgetJSON(t testing.TB, gopts GlobalOptions, args ...string) { - buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { +func testRunForgetJSON(t testing.TB, gopts global.Options, args ...string) { + buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error { gopts.JSON = true opts := ForgetOptions{ DryRun: true, @@ -98,7 +99,7 @@ func testRunForgetJSON(t testing.TB, gopts GlobalOptions, args ...string) { pruneOpts := PruneOptions{ MaxUnused: "5%", } - return runForget(context.TODO(), opts, pruneOpts, gopts, gopts.term, args) + return runForget(context.TODO(), opts, pruneOpts, gopts, gopts.Term, args) }) rtest.OK(t, err) @@ -119,8 +120,8 @@ func testPrune(t *testing.T, pruneOpts PruneOptions, checkOpts CheckOptions) { createPrunableRepo(t, env) testRunPrune(t, env.gopts, pruneOpts) - rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - _, err := runCheck(context.TODO(), checkOpts, gopts, nil, gopts.term) + rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + _, err := runCheck(context.TODO(), checkOpts, gopts, nil, gopts.Term) return err })) } @@ -149,14 +150,14 @@ func TestPruneWithDamagedRepository(t *testing.T) { testListSnapshots(t, env.gopts, 1) removePacksExcept(env.gopts, t, oldPacks, false) - oldHook := env.gopts.backendTestHook - env.gopts.backendTestHook = func(r backend.Backend) (backend.Backend, error) { return newListOnceBackend(r), nil } + oldHook := env.gopts.BackendTestHook + env.gopts.BackendTestHook = func(r backend.Backend) (backend.Backend, error) { return newListOnceBackend(r), nil } defer func() { - env.gopts.backendTestHook = oldHook + env.gopts.BackendTestHook = oldHook }() // prune should fail - rtest.Equals(t, repository.ErrPacksMissing, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runPrune(context.TODO(), pruneDefaultOptions, gopts, gopts.term) + rtest.Equals(t, repository.ErrPacksMissing, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runPrune(context.TODO(), pruneDefaultOptions, gopts, gopts.Term) }), "prune should have reported index not complete error") } @@ -228,8 +229,8 @@ func testEdgeCaseRepo(t *testing.T, tarfile string, optionsCheck CheckOptions, o if checkOK { testRunCheck(t, env.gopts) } else { - rtest.Assert(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - _, err := runCheck(context.TODO(), optionsCheck, gopts, nil, gopts.term) + rtest.Assert(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + _, err := runCheck(context.TODO(), optionsCheck, gopts, nil, gopts.Term) return err }) != nil, "check should have reported an error") @@ -239,8 +240,8 @@ func testEdgeCaseRepo(t *testing.T, tarfile string, optionsCheck CheckOptions, o testRunPrune(t, env.gopts, optionsPrune) testRunCheck(t, env.gopts) } else { - rtest.Assert(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runPrune(context.TODO(), optionsPrune, gopts, gopts.term) + rtest.Assert(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runPrune(context.TODO(), optionsPrune, gopts, gopts.Term) }) != nil, "prune should have reported an error") } diff --git a/cmd/restic/cmd_recover.go b/cmd/restic/cmd_recover.go index 6d70961c0..3dda6214f 100644 --- a/cmd/restic/cmd_recover.go +++ b/cmd/restic/cmd_recover.go @@ -7,6 +7,7 @@ import ( "github.com/restic/restic/internal/data" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" @@ -15,7 +16,7 @@ import ( "golang.org/x/sync/errgroup" ) -func newRecoverCommand(globalOptions *GlobalOptions) *cobra.Command { +func newRecoverCommand(globalOptions *global.Options) *cobra.Command { cmd := &cobra.Command{ Use: "recover [flags]", Short: "Recover data from the repository not referenced by snapshots", @@ -36,19 +37,19 @@ Exit status is 12 if the password is incorrect. GroupID: cmdGroupDefault, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, _ []string) error { - return runRecover(cmd.Context(), *globalOptions, globalOptions.term) + return runRecover(cmd.Context(), *globalOptions, globalOptions.Term) }, } return cmd } -func runRecover(ctx context.Context, gopts GlobalOptions, term ui.Terminal) error { +func runRecover(ctx context.Context, gopts global.Options, term ui.Terminal) error { hostname, err := os.Hostname() if err != nil { return err } - printer := ui.NewProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) if err != nil { return err diff --git a/cmd/restic/cmd_recover_integration_test.go b/cmd/restic/cmd_recover_integration_test.go index cbcd2b019..4668c5ea5 100644 --- a/cmd/restic/cmd_recover_integration_test.go +++ b/cmd/restic/cmd_recover_integration_test.go @@ -4,19 +4,20 @@ import ( "context" "testing" + "github.com/restic/restic/internal/global" rtest "github.com/restic/restic/internal/test" ) -func testRunRecover(t testing.TB, gopts GlobalOptions) { - rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runRecover(context.TODO(), gopts, gopts.term) +func testRunRecover(t testing.TB, gopts global.Options) { + rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + return runRecover(context.TODO(), gopts, gopts.Term) })) } func TestRecover(t *testing.T) { env, cleanup := withTestEnvironment(t) // must list index more than once - env.gopts.backendTestHook = nil + env.gopts.BackendTestHook = nil defer cleanup() testSetupBackupData(t, env) @@ -32,7 +33,7 @@ func TestRecover(t *testing.T) { ids = testListSnapshots(t, env.gopts, 1) testRunCheck(t, env.gopts) // check that the root tree is included in the snapshot - rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runCat(context.TODO(), gopts, []string{"tree", ids[0].String() + ":" + sn.Tree.Str()}, gopts.term) + rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runCat(context.TODO(), gopts, []string{"tree", ids[0].String() + ":" + sn.Tree.Str()}, gopts.Term) })) } diff --git a/cmd/restic/cmd_repair.go b/cmd/restic/cmd_repair.go index c6b5a212b..6181e7dfe 100644 --- a/cmd/restic/cmd_repair.go +++ b/cmd/restic/cmd_repair.go @@ -1,10 +1,11 @@ package main import ( + "github.com/restic/restic/internal/global" "github.com/spf13/cobra" ) -func newRepairCommand(globalOptions *GlobalOptions) *cobra.Command { +func newRepairCommand(globalOptions *global.Options) *cobra.Command { cmd := &cobra.Command{ Use: "repair", Short: "Repair the repository", diff --git a/cmd/restic/cmd_repair_index.go b/cmd/restic/cmd_repair_index.go index bff6af46e..32e38fc7b 100644 --- a/cmd/restic/cmd_repair_index.go +++ b/cmd/restic/cmd_repair_index.go @@ -3,13 +3,14 @@ package main import ( "context" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/ui" "github.com/spf13/cobra" "github.com/spf13/pflag" ) -func newRepairIndexCommand(globalOptions *GlobalOptions) *cobra.Command { +func newRepairIndexCommand(globalOptions *global.Options) *cobra.Command { var opts RepairIndexOptions cmd := &cobra.Command{ @@ -30,7 +31,7 @@ Exit status is 12 if the password is incorrect. `, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, _ []string) error { - return runRebuildIndex(cmd.Context(), opts, *globalOptions, globalOptions.term) + return runRebuildIndex(cmd.Context(), opts, *globalOptions, globalOptions.Term) }, } @@ -47,7 +48,7 @@ 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") } -func newRebuildIndexCommand(globalOptions *GlobalOptions) *cobra.Command { +func newRebuildIndexCommand(globalOptions *global.Options) *cobra.Command { var opts RepairIndexOptions replacement := newRepairIndexCommand(globalOptions) @@ -60,7 +61,7 @@ func newRebuildIndexCommand(globalOptions *GlobalOptions) *cobra.Command { // must create a new instance of the run function as it captures opts // by reference RunE: func(cmd *cobra.Command, _ []string) error { - return runRebuildIndex(cmd.Context(), opts, *globalOptions, globalOptions.term) + return runRebuildIndex(cmd.Context(), opts, *globalOptions, globalOptions.Term) }, } @@ -68,8 +69,8 @@ func newRebuildIndexCommand(globalOptions *GlobalOptions) *cobra.Command { return cmd } -func runRebuildIndex(ctx context.Context, opts RepairIndexOptions, gopts GlobalOptions, term ui.Terminal) error { - printer := ui.NewProgressPrinter(false, gopts.verbosity, term) +func runRebuildIndex(ctx context.Context, opts RepairIndexOptions, gopts global.Options, term ui.Terminal) error { + printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) if err != nil { diff --git a/cmd/restic/cmd_repair_index_integration_test.go b/cmd/restic/cmd_repair_index_integration_test.go index 7b8488889..0a56f4b7a 100644 --- a/cmd/restic/cmd_repair_index_integration_test.go +++ b/cmd/restic/cmd_repair_index_integration_test.go @@ -10,19 +10,20 @@ import ( "github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository/index" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) -func testRunRebuildIndex(t testing.TB, gopts GlobalOptions) { - rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { +func testRunRebuildIndex(t testing.TB, gopts global.Options) { + rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { gopts.Quiet = true - return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.term) + return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.Term) })) } -func testRebuildIndex(t *testing.T, backendTestHook backendWrapper) { +func testRebuildIndex(t *testing.T, backendTestHook global.BackendWrapper) { env, cleanup := withTestEnvironment(t) defer cleanup() @@ -42,10 +43,10 @@ func testRebuildIndex(t *testing.T, backendTestHook backendWrapper) { t.Fatalf("did not find hint for repair index command") } - env.gopts.backendTestHook = backendTestHook + env.gopts.BackendTestHook = backendTestHook testRunRebuildIndex(t, env.gopts) - env.gopts.backendTestHook = nil + env.gopts.BackendTestHook = nil out, err = testRunCheckOutput(t, env.gopts, false) if len(out) != 0 { t.Fatalf("expected no output from the checker, got: %v", out) @@ -125,12 +126,12 @@ func TestRebuildIndexFailsOnAppendOnly(t *testing.T) { datafile := filepath.Join("..", "..", "internal", "checker", "testdata", "duplicate-packs-in-index-test-repo.tar.gz") rtest.SetupTarTestFixture(t, env.base, datafile) - env.gopts.backendTestHook = func(r backend.Backend) (backend.Backend, error) { + env.gopts.BackendTestHook = func(r backend.Backend) (backend.Backend, error) { return &appendOnlyBackend{r}, nil } - err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { + err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { gopts.Quiet = true - return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.term) + return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.Term) }) if err == nil { diff --git a/cmd/restic/cmd_repair_packs.go b/cmd/restic/cmd_repair_packs.go index b8e68a65e..f0f4cccbf 100644 --- a/cmd/restic/cmd_repair_packs.go +++ b/cmd/restic/cmd_repair_packs.go @@ -7,13 +7,14 @@ import ( "os" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" "github.com/spf13/cobra" ) -func newRepairPacksCommand(globalOptions *GlobalOptions) *cobra.Command { +func newRepairPacksCommand(globalOptions *global.Options) *cobra.Command { cmd := &cobra.Command{ Use: "packs [packIDs...]", Short: "Salvage damaged pack files", @@ -32,13 +33,13 @@ Exit status is 12 if the password is incorrect. `, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - return runRepairPacks(cmd.Context(), *globalOptions, globalOptions.term, args) + return runRepairPacks(cmd.Context(), *globalOptions, globalOptions.Term, args) }, } return cmd } -func runRepairPacks(ctx context.Context, gopts GlobalOptions, term ui.Terminal, args []string) error { +func runRepairPacks(ctx context.Context, gopts global.Options, term ui.Terminal, args []string) error { ids := restic.NewIDSet() for _, arg := range args { id, err := restic.ParseID(arg) @@ -51,7 +52,7 @@ func runRepairPacks(ctx context.Context, gopts GlobalOptions, term ui.Terminal, return errors.Fatal("no ids specified") } - printer := ui.NewProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) if err != nil { diff --git a/cmd/restic/cmd_repair_snapshots.go b/cmd/restic/cmd_repair_snapshots.go index 0a702e596..e7547bdba 100644 --- a/cmd/restic/cmd_repair_snapshots.go +++ b/cmd/restic/cmd_repair_snapshots.go @@ -5,6 +5,7 @@ import ( "github.com/restic/restic/internal/data" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/walker" @@ -13,7 +14,7 @@ import ( "github.com/spf13/pflag" ) -func newRepairSnapshotsCommand(globalOptions *GlobalOptions) *cobra.Command { +func newRepairSnapshotsCommand(globalOptions *global.Options) *cobra.Command { var opts RepairOptions cmd := &cobra.Command{ @@ -52,7 +53,7 @@ Exit status is 12 if the password is incorrect. DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { finalizeSnapshotFilter(&opts.SnapshotFilter) - return runRepairSnapshots(cmd.Context(), *globalOptions, opts, args, globalOptions.term) + return runRepairSnapshots(cmd.Context(), *globalOptions, opts, args, globalOptions.Term) }, } @@ -75,8 +76,8 @@ func (opts *RepairOptions) AddFlags(f *pflag.FlagSet) { initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) } -func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOptions, args []string, term ui.Terminal) error { - printer := ui.NewProgressPrinter(false, gopts.verbosity, term) +func runRepairSnapshots(ctx context.Context, gopts global.Options, opts RepairOptions, args []string, term ui.Terminal) error { + printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, opts.DryRun, printer) if err != nil { diff --git a/cmd/restic/cmd_repair_snapshots_integration_test.go b/cmd/restic/cmd_repair_snapshots_integration_test.go index 942f03d0b..a4d6bbcaf 100644 --- a/cmd/restic/cmd_repair_snapshots_integration_test.go +++ b/cmd/restic/cmd_repair_snapshots_integration_test.go @@ -10,17 +10,18 @@ import ( "reflect" "testing" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) -func testRunRepairSnapshot(t testing.TB, gopts GlobalOptions, forget bool) { +func testRunRepairSnapshot(t testing.TB, gopts global.Options, forget bool) { opts := RepairOptions{ Forget: forget, } - rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runRepairSnapshots(context.TODO(), gopts, opts, nil, gopts.term) + rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + return runRepairSnapshots(context.TODO(), gopts, opts, nil, gopts.Term) })) } diff --git a/cmd/restic/cmd_restore.go b/cmd/restic/cmd_restore.go index 407e2ab32..6024c8b73 100644 --- a/cmd/restic/cmd_restore.go +++ b/cmd/restic/cmd_restore.go @@ -9,6 +9,7 @@ import ( "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/filter" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restorer" "github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui/progress" @@ -18,7 +19,7 @@ import ( "github.com/spf13/pflag" ) -func newRestoreCommand(globalOptions *GlobalOptions) *cobra.Command { +func newRestoreCommand(globalOptions *global.Options) *cobra.Command { var opts RestoreOptions cmd := &cobra.Command{ @@ -47,7 +48,7 @@ Exit status is 12 if the password is incorrect. DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { finalizeSnapshotFilter(&opts.SnapshotFilter) - return runRestore(cmd.Context(), opts, *globalOptions, globalOptions.term, args) + return runRestore(cmd.Context(), opts, *globalOptions, globalOptions.Term, args) }, } @@ -87,14 +88,14 @@ 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") } -func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions, +func runRestore(ctx context.Context, opts RestoreOptions, gopts global.Options, term ui.Terminal, args []string) error { var printer restoreui.ProgressPrinter if gopts.JSON { - printer = restoreui.NewJSONProgress(term, gopts.verbosity) + printer = restoreui.NewJSONProgress(term, gopts.Verbosity) } else { - printer = restoreui.NewTextProgress(term, gopts.verbosity) + printer = restoreui.NewTextProgress(term, gopts.Verbosity) } excludePatternFns, err := opts.ExcludePatternOptions.CollectPatterns(printer.E) diff --git a/cmd/restic/cmd_restore_integration_test.go b/cmd/restic/cmd_restore_integration_test.go index e7d3e8184..47d611d8b 100644 --- a/cmd/restic/cmd_restore_integration_test.go +++ b/cmd/restic/cmd_restore_integration_test.go @@ -12,15 +12,16 @@ import ( "time" "github.com/restic/restic/internal/data" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) -func testRunRestore(t testing.TB, gopts GlobalOptions, dir string, snapshotID string) { +func testRunRestore(t testing.TB, gopts global.Options, dir string, snapshotID string) { testRunRestoreExcludes(t, gopts, dir, snapshotID, nil) } -func testRunRestoreExcludes(t testing.TB, gopts GlobalOptions, dir string, snapshotID string, excludes []string) { +func testRunRestoreExcludes(t testing.TB, gopts global.Options, dir string, snapshotID string, excludes []string) { opts := RestoreOptions{ Target: dir, } @@ -29,13 +30,13 @@ func testRunRestoreExcludes(t testing.TB, gopts GlobalOptions, dir string, snaps rtest.OK(t, testRunRestoreAssumeFailure(t, snapshotID, opts, gopts)) } -func testRunRestoreAssumeFailure(t testing.TB, snapshotID string, opts RestoreOptions, gopts GlobalOptions) error { - return withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runRestore(ctx, opts, gopts, gopts.term, []string{snapshotID}) +func testRunRestoreAssumeFailure(t testing.TB, snapshotID string, opts RestoreOptions, gopts global.Options) error { + return withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + return runRestore(ctx, opts, gopts, gopts.Term, []string{snapshotID}) }) } -func testRunRestoreLatest(t testing.TB, gopts GlobalOptions, dir string, paths []string, hosts []string) { +func testRunRestoreLatest(t testing.TB, gopts global.Options, dir string, paths []string, hosts []string) { opts := RestoreOptions{ Target: dir, SnapshotFilter: data.SnapshotFilter{ @@ -47,7 +48,7 @@ func testRunRestoreLatest(t testing.TB, gopts GlobalOptions, dir string, paths [ rtest.OK(t, testRunRestoreAssumeFailure(t, "latest", opts, gopts)) } -func testRunRestoreIncludes(t testing.TB, gopts GlobalOptions, dir string, snapshotID restic.ID, includes []string) { +func testRunRestoreIncludes(t testing.TB, gopts global.Options, dir string, snapshotID restic.ID, includes []string) { opts := RestoreOptions{ Target: dir, } @@ -56,7 +57,7 @@ func testRunRestoreIncludes(t testing.TB, gopts GlobalOptions, dir string, snaps rtest.OK(t, testRunRestoreAssumeFailure(t, snapshotID.String(), opts, gopts)) } -func testRunRestoreIncludesFromFile(t testing.TB, gopts GlobalOptions, dir string, snapshotID restic.ID, includesFile string) { +func testRunRestoreIncludesFromFile(t testing.TB, gopts global.Options, dir string, snapshotID restic.ID, includesFile string) { opts := RestoreOptions{ Target: dir, } @@ -65,7 +66,7 @@ func testRunRestoreIncludesFromFile(t testing.TB, gopts GlobalOptions, dir strin rtest.OK(t, testRunRestoreAssumeFailure(t, snapshotID.String(), opts, gopts)) } -func testRunRestoreExcludesFromFile(t testing.TB, gopts GlobalOptions, dir string, snapshotID restic.ID, excludesFile string) { +func testRunRestoreExcludesFromFile(t testing.TB, gopts global.Options, dir string, snapshotID restic.ID, excludesFile string) { opts := RestoreOptions{ Target: dir, } diff --git a/cmd/restic/cmd_rewrite.go b/cmd/restic/cmd_rewrite.go index 9d502b483..470b89024 100644 --- a/cmd/restic/cmd_rewrite.go +++ b/cmd/restic/cmd_rewrite.go @@ -12,6 +12,7 @@ import ( "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/filter" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" @@ -19,7 +20,7 @@ import ( "github.com/restic/restic/internal/walker" ) -func newRewriteCommand(globalOptions *GlobalOptions) *cobra.Command { +func newRewriteCommand(globalOptions *global.Options) *cobra.Command { var opts RewriteOptions cmd := &cobra.Command{ @@ -62,7 +63,7 @@ Exit status is 12 if the password is incorrect. DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { finalizeSnapshotFilter(&opts.SnapshotFilter) - return runRewrite(cmd.Context(), opts, *globalOptions, args, globalOptions.term) + return runRewrite(cmd.Context(), opts, *globalOptions, args, globalOptions.Term) }, } @@ -91,7 +92,7 @@ func (sma snapshotMetadataArgs) convert() (*snapshotMetadata, error) { var timeStamp *time.Time if sma.Time != "" { - t, err := time.ParseInLocation(TimeFormat, sma.Time, time.Local) + t, err := time.ParseInLocation(global.TimeFormat, sma.Time, time.Local) if err != nil { return nil, errors.Fatalf("error in time option: %v", err) } @@ -291,12 +292,12 @@ func filterAndReplaceSnapshot(ctx context.Context, repo restic.Repository, sn *d return true, nil } -func runRewrite(ctx context.Context, opts RewriteOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { +func runRewrite(ctx context.Context, opts RewriteOptions, gopts global.Options, args []string, term ui.Terminal) error { if !opts.SnapshotSummary && opts.ExcludePatternOptions.Empty() && opts.Metadata.empty() { return errors.Fatal("Nothing to do: no excludes provided and no new metadata provided") } - printer := ui.NewProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) var ( repo *repository.Repository diff --git a/cmd/restic/cmd_rewrite_integration_test.go b/cmd/restic/cmd_rewrite_integration_test.go index 1a0dd0abe..35f5f4d01 100644 --- a/cmd/restic/cmd_rewrite_integration_test.go +++ b/cmd/restic/cmd_rewrite_integration_test.go @@ -7,12 +7,13 @@ import ( "github.com/restic/restic/internal/data" "github.com/restic/restic/internal/filter" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" "github.com/restic/restic/internal/ui" ) -func testRunRewriteExclude(t testing.TB, gopts GlobalOptions, excludes []string, forget bool, metadata snapshotMetadataArgs) { +func testRunRewriteExclude(t testing.TB, gopts global.Options, excludes []string, forget bool, metadata snapshotMetadataArgs) { opts := RewriteOptions{ ExcludePatternOptions: filter.ExcludePatternOptions{ Excludes: excludes, @@ -21,8 +22,8 @@ func testRunRewriteExclude(t testing.TB, gopts GlobalOptions, excludes []string, Metadata: metadata, } - rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runRewrite(context.TODO(), opts, gopts, nil, gopts.term) + rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + return runRewrite(context.TODO(), opts, gopts, nil, gopts.Term) })) } @@ -42,8 +43,8 @@ func getSnapshot(t testing.TB, snapshotID restic.ID, env *testEnvironment) *data t.Helper() var snapshots []*data.Snapshot - err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() @@ -118,8 +119,8 @@ func testRewriteMetadata(t *testing.T, metadata snapshotMetadataArgs) { testRunRewriteExclude(t, env.gopts, []string{}, true, metadata) var snapshots []*data.Snapshot - err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() @@ -132,7 +133,7 @@ func testRewriteMetadata(t *testing.T, metadata snapshotMetadataArgs) { newSnapshot := snapshots[0] if metadata.Time != "" { - rtest.Assert(t, newSnapshot.Time.Format(TimeFormat) == metadata.Time, "New snapshot should have time %s", metadata.Time) + rtest.Assert(t, newSnapshot.Time.Format(global.TimeFormat) == metadata.Time, "New snapshot should have time %s", metadata.Time) } if metadata.Hostname != "" { @@ -158,16 +159,16 @@ func TestRewriteSnaphotSummary(t *testing.T) { defer cleanup() createBasicRewriteRepo(t, env) - rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runRewrite(context.TODO(), RewriteOptions{SnapshotSummary: true}, gopts, []string{}, gopts.term) + rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runRewrite(context.TODO(), RewriteOptions{SnapshotSummary: true}, gopts, []string{}, gopts.Term) })) // no new snapshot should be created as the snapshot already has a summary snapshots := testListSnapshots(t, env.gopts, 1) // replace snapshot by one without a summary var oldSummary *data.SnapshotSummary - err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) _, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() @@ -183,8 +184,8 @@ func TestRewriteSnaphotSummary(t *testing.T) { rtest.OK(t, err) // rewrite snapshot and lookup ID of new snapshot - rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runRewrite(context.TODO(), RewriteOptions{SnapshotSummary: true}, gopts, []string{}, gopts.term) + rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runRewrite(context.TODO(), RewriteOptions{SnapshotSummary: true}, gopts, []string{}, gopts.Term) })) newSnapshots := testListSnapshots(t, env.gopts, 2) newSnapshot := restic.NewIDSet(newSnapshots...).Sub(restic.NewIDSet(snapshots...)).List()[0] diff --git a/cmd/restic/cmd_self_update.go b/cmd/restic/cmd_self_update.go index e8cd64d48..5b684b22e 100644 --- a/cmd/restic/cmd_self_update.go +++ b/cmd/restic/cmd_self_update.go @@ -8,19 +8,20 @@ import ( "path/filepath" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/selfupdate" "github.com/restic/restic/internal/ui" "github.com/spf13/cobra" "github.com/spf13/pflag" ) -func registerSelfUpdateCommand(cmd *cobra.Command, globalOptions *GlobalOptions) { +func registerSelfUpdateCommand(cmd *cobra.Command, globalOptions *global.Options) { cmd.AddCommand( newSelfUpdateCommand(globalOptions), ) } -func newSelfUpdateCommand(globalOptions *GlobalOptions) *cobra.Command { +func newSelfUpdateCommand(globalOptions *global.Options) *cobra.Command { var opts SelfUpdateOptions cmd := &cobra.Command{ @@ -43,7 +44,7 @@ Exit status is 12 if the password is incorrect. `, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - return runSelfUpdate(cmd.Context(), opts, *globalOptions, args, globalOptions.term) + return runSelfUpdate(cmd.Context(), opts, *globalOptions, args, globalOptions.Term) }, } @@ -60,7 +61,7 @@ func (opts *SelfUpdateOptions) AddFlags(f *pflag.FlagSet) { f.StringVar(&opts.Output, "output", "", "Save the downloaded file as `filename` (default: running binary itself)") } -func runSelfUpdate(ctx context.Context, opts SelfUpdateOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { +func runSelfUpdate(ctx context.Context, opts SelfUpdateOptions, gopts global.Options, args []string, term ui.Terminal) error { if opts.Output == "" { file, err := os.Executable() if err != nil { @@ -86,15 +87,15 @@ func runSelfUpdate(ctx context.Context, opts SelfUpdateOptions, gopts GlobalOpti } } - printer := ui.NewProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) printer.P("writing restic to %v", opts.Output) - v, err := selfupdate.DownloadLatestStableRelease(ctx, opts.Output, version, printer.P) + v, err := selfupdate.DownloadLatestStableRelease(ctx, opts.Output, global.Version, printer.P) if err != nil { return errors.Fatalf("unable to update restic: %v", err) } - if v != version { + if v != global.Version { printer.S("successfully updated restic to version %v", v) } diff --git a/cmd/restic/cmd_self_update_disabled.go b/cmd/restic/cmd_self_update_disabled.go index bca03335f..b8d4c89c0 100644 --- a/cmd/restic/cmd_self_update_disabled.go +++ b/cmd/restic/cmd_self_update_disabled.go @@ -2,8 +2,11 @@ package main -import "github.com/spf13/cobra" +import ( + "github.com/restic/restic/internal/global" + "github.com/spf13/cobra" +) -func registerSelfUpdateCommand(_ *cobra.Command, _ *GlobalOptions) { +func registerSelfUpdateCommand(_ *cobra.Command, _ *global.Options) { // No commands to register in non-selfupdate mode } diff --git a/cmd/restic/cmd_snapshots.go b/cmd/restic/cmd_snapshots.go index 1f9d46801..f37481b29 100644 --- a/cmd/restic/cmd_snapshots.go +++ b/cmd/restic/cmd_snapshots.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/restic/restic/internal/data" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui/table" @@ -16,7 +17,7 @@ import ( "github.com/spf13/pflag" ) -func newSnapshotsCommand(globalOptions *GlobalOptions) *cobra.Command { +func newSnapshotsCommand(globalOptions *global.Options) *cobra.Command { var opts SnapshotOptions cmd := &cobra.Command{ @@ -38,7 +39,7 @@ Exit status is 12 if the password is incorrect. DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { finalizeSnapshotFilter(&opts.SnapshotFilter) - return runSnapshots(cmd.Context(), opts, *globalOptions, args, globalOptions.term) + return runSnapshots(cmd.Context(), opts, *globalOptions, args, globalOptions.Term) }, } @@ -68,8 +69,8 @@ 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") } -func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) +func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts global.Options, args []string, term ui.Terminal) error { + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, gopts.NoLock, printer) if err != nil { return err @@ -105,7 +106,7 @@ func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts GlobalOptions } if gopts.JSON { - err := printSnapshotGroupJSON(gopts.term.OutputWriter(), snapshotGroups, grouped) + err := printSnapshotGroupJSON(gopts.Term.OutputWriter(), snapshotGroups, grouped) if err != nil { printer.E("error printing snapshots: %v", err) } @@ -118,12 +119,12 @@ func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts GlobalOptions } if grouped { - err := PrintSnapshotGroupHeader(gopts.term.OutputWriter(), k) + err := PrintSnapshotGroupHeader(gopts.Term.OutputWriter(), k) if err != nil { return err } } - err := PrintSnapshots(gopts.term.OutputWriter(), list, nil, opts.Compact) + err := PrintSnapshots(gopts.Term.OutputWriter(), list, nil, opts.Compact) if err != nil { return err } @@ -242,7 +243,7 @@ func PrintSnapshots(stdout io.Writer, list data.Snapshots, reasons []data.KeepRe for _, sn := range list { data := snapshot{ ID: sn.ID().Str(), - Timestamp: sn.Time.Local().Format(TimeFormat), + Timestamp: sn.Time.Local().Format(global.TimeFormat), Hostname: sn.Hostname, Tags: sn.Tags, Paths: sn.Paths, diff --git a/cmd/restic/cmd_snapshots_integration_test.go b/cmd/restic/cmd_snapshots_integration_test.go index d962ddfc8..6b6f10ea6 100644 --- a/cmd/restic/cmd_snapshots_integration_test.go +++ b/cmd/restic/cmd_snapshots_integration_test.go @@ -5,16 +5,17 @@ import ( "encoding/json" "testing" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) -func testRunSnapshots(t testing.TB, gopts GlobalOptions) (newest *Snapshot, snapmap map[restic.ID]Snapshot) { - buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { +func testRunSnapshots(t testing.TB, gopts global.Options) (newest *Snapshot, snapmap map[restic.ID]Snapshot) { + buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error { gopts.JSON = true opts := SnapshotOptions{} - return runSnapshots(ctx, opts, gopts, []string{}, gopts.term) + return runSnapshots(ctx, opts, gopts, []string{}, gopts.Term) }) rtest.OK(t, err) diff --git a/cmd/restic/cmd_stats.go b/cmd/restic/cmd_stats.go index db4dc686e..2b7a74b94 100644 --- a/cmd/restic/cmd_stats.go +++ b/cmd/restic/cmd_stats.go @@ -11,6 +11,7 @@ import ( "github.com/restic/chunker" "github.com/restic/restic/internal/crypto" "github.com/restic/restic/internal/data" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restorer" @@ -23,7 +24,7 @@ import ( "github.com/spf13/pflag" ) -func newStatsCommand(globalOptions *GlobalOptions) *cobra.Command { +func newStatsCommand(globalOptions *global.Options) *cobra.Command { var opts StatsOptions cmd := &cobra.Command{ @@ -65,7 +66,7 @@ Exit status is 12 if the password is incorrect. DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { finalizeSnapshotFilter(&opts.SnapshotFilter) - return runStats(cmd.Context(), opts, *globalOptions, args, globalOptions.term) + return runStats(cmd.Context(), opts, *globalOptions, args, globalOptions.Term) }, } @@ -95,13 +96,13 @@ func must(err error) { } } -func runStats(ctx context.Context, opts StatsOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { +func runStats(ctx context.Context, opts StatsOptions, gopts global.Options, args []string, term ui.Terminal) error { err := verifyStatsInput(opts) if err != nil { return err } - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, gopts.NoLock, printer) if err != nil { @@ -170,7 +171,7 @@ func runStats(ctx context.Context, opts StatsOptions, gopts GlobalOptions, args } if gopts.JSON { - err = json.NewEncoder(gopts.term.OutputWriter()).Encode(stats) + err = json.NewEncoder(gopts.Term.OutputWriter()).Encode(stats) if err != nil { return fmt.Errorf("encoding output: %v", err) } diff --git a/cmd/restic/cmd_tag.go b/cmd/restic/cmd_tag.go index 6009fb9fd..65df7c292 100644 --- a/cmd/restic/cmd_tag.go +++ b/cmd/restic/cmd_tag.go @@ -9,12 +9,13 @@ import ( "github.com/restic/restic/internal/data" "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" ) -func newTagCommand(globalOptions *GlobalOptions) *cobra.Command { +func newTagCommand(globalOptions *global.Options) *cobra.Command { var opts TagOptions cmd := &cobra.Command{ @@ -41,8 +42,7 @@ Exit status is 12 if the password is incorrect. DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { finalizeSnapshotFilter(&opts.SnapshotFilter) - finalizeSnapshotFilter(&opts.SnapshotFilter) - return runTag(cmd.Context(), opts, *globalOptions, globalOptions.term, args) + return runTag(cmd.Context(), opts, *globalOptions, globalOptions.Term, args) }, } @@ -119,8 +119,8 @@ func changeTags(ctx context.Context, repo *repository.Repository, sn *data.Snaps return changed, nil } -func runTag(ctx context.Context, opts TagOptions, gopts GlobalOptions, term ui.Terminal, args []string) error { - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) +func runTag(ctx context.Context, opts TagOptions, gopts global.Options, term ui.Terminal, args []string) error { + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) if len(opts.SetTags) == 0 && len(opts.AddTags) == 0 && len(opts.RemoveTags) == 0 { return errors.Fatal("nothing to do!") diff --git a/cmd/restic/cmd_tag_integration_test.go b/cmd/restic/cmd_tag_integration_test.go index 58664fcab..e7a6f09cb 100644 --- a/cmd/restic/cmd_tag_integration_test.go +++ b/cmd/restic/cmd_tag_integration_test.go @@ -5,12 +5,13 @@ import ( "testing" "github.com/restic/restic/internal/data" + "github.com/restic/restic/internal/global" rtest "github.com/restic/restic/internal/test" ) -func testRunTag(t testing.TB, opts TagOptions, gopts GlobalOptions) { - rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runTag(context.TODO(), opts, gopts, gopts.term, []string{}) +func testRunTag(t testing.TB, opts TagOptions, gopts global.Options) { + rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + return runTag(context.TODO(), opts, gopts, gopts.Term, []string{}) })) } diff --git a/cmd/restic/cmd_unlock.go b/cmd/restic/cmd_unlock.go index 91ce9105a..c06fc8614 100644 --- a/cmd/restic/cmd_unlock.go +++ b/cmd/restic/cmd_unlock.go @@ -3,13 +3,14 @@ package main import ( "context" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/ui" "github.com/spf13/cobra" "github.com/spf13/pflag" ) -func newUnlockCommand(globalOptions *GlobalOptions) *cobra.Command { +func newUnlockCommand(globalOptions *global.Options) *cobra.Command { var opts UnlockOptions cmd := &cobra.Command{ @@ -27,7 +28,7 @@ Exit status is 1 if there was any error. GroupID: cmdGroupDefault, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, _ []string) error { - return runUnlock(cmd.Context(), opts, *globalOptions, globalOptions.term) + return runUnlock(cmd.Context(), opts, *globalOptions, globalOptions.Term) }, } opts.AddFlags(cmd.Flags()) @@ -43,9 +44,9 @@ func (opts *UnlockOptions) AddFlags(f *pflag.FlagSet) { f.BoolVar(&opts.RemoveAll, "remove-all", false, "remove all locks, even non-stale ones") } -func runUnlock(ctx context.Context, opts UnlockOptions, gopts GlobalOptions, term ui.Terminal) error { - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) - repo, err := OpenRepository(ctx, gopts, printer) +func runUnlock(ctx context.Context, opts UnlockOptions, gopts global.Options, term ui.Terminal) error { + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) + repo, err := global.OpenRepository(ctx, gopts, printer) if err != nil { return err } diff --git a/cmd/restic/cmd_version.go b/cmd/restic/cmd_version.go index f2aa47eb2..1232ea779 100644 --- a/cmd/restic/cmd_version.go +++ b/cmd/restic/cmd_version.go @@ -4,11 +4,12 @@ import ( "encoding/json" "runtime" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/ui" "github.com/spf13/cobra" ) -func newVersionCommand(globalOptions *GlobalOptions) *cobra.Command { +func newVersionCommand(globalOptions *global.Options) *cobra.Command { cmd := &cobra.Command{ Use: "version", Short: "Print version information", @@ -24,7 +25,7 @@ Exit status is 1 if there was any error. `, DisableAutoGenTag: true, Run: func(_ *cobra.Command, _ []string) { - printer := ui.NewProgressPrinter(globalOptions.JSON, globalOptions.verbosity, globalOptions.term) + printer := ui.NewProgressPrinter(globalOptions.JSON, globalOptions.Verbosity, globalOptions.Term) if globalOptions.JSON { type jsonVersion struct { @@ -37,20 +38,20 @@ Exit status is 1 if there was any error. jsonS := jsonVersion{ MessageType: "version", - Version: version, + Version: global.Version, GoVersion: runtime.Version(), GoOS: runtime.GOOS, GoArch: runtime.GOARCH, } - err := json.NewEncoder(globalOptions.term.OutputWriter()).Encode(jsonS) + err := json.NewEncoder(globalOptions.Term.OutputWriter()).Encode(jsonS) if err != nil { printer.E("JSON encode failed: %v\n", err) return } } else { printer.S("restic %s compiled with %v on %v/%v\n", - version, runtime.Version(), runtime.GOOS, runtime.GOARCH) + global.Version, runtime.Version(), runtime.GOOS, runtime.GOARCH) } }, } diff --git a/cmd/restic/flags_test.go b/cmd/restic/flags_test.go index aa177c45c..8f3da31ec 100644 --- a/cmd/restic/flags_test.go +++ b/cmd/restic/flags_test.go @@ -3,12 +3,14 @@ package main import ( "io" "testing" + + "github.com/restic/restic/internal/global" ) // TestFlags checks for double defined flags, the commands will panic on // ParseFlags() when a shorthand flag is defined twice. func TestFlags(t *testing.T) { - for _, cmd := range newRootCommand(&GlobalOptions{}).Commands() { + for _, cmd := range newRootCommand(&global.Options{}).Commands() { t.Run(cmd.Name(), func(t *testing.T) { cmd.Flags().SetOutput(io.Discard) err := cmd.ParseFlags([]string{"--help"}) diff --git a/cmd/restic/format.go b/cmd/restic/format.go index 213d76aeb..7d2f0c807 100644 --- a/cmd/restic/format.go +++ b/cmd/restic/format.go @@ -5,6 +5,7 @@ import ( "os" "github.com/restic/restic/internal/data" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/ui" ) @@ -43,6 +44,6 @@ func formatNode(path string, n *data.Node, long bool, human bool) string { return fmt.Sprintf("%s %5d %5d %s %s %s%s", mode|n.Mode, n.UID, n.GID, size, - n.ModTime.Local().Format(TimeFormat), path, + n.ModTime.Local().Format(global.TimeFormat), path, target) } diff --git a/cmd/restic/integration_helpers_test.go b/cmd/restic/integration_helpers_test.go index 2a8fec61a..b9a2db4c5 100644 --- a/cmd/restic/integration_helpers_test.go +++ b/cmd/restic/integration_helpers_test.go @@ -14,9 +14,11 @@ import ( "testing" "github.com/restic/restic/internal/backend" + "github.com/restic/restic/internal/backend/all" "github.com/restic/restic/internal/backend/retry" "github.com/restic/restic/internal/data" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/options" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" @@ -168,7 +170,7 @@ func dirStats(t testing.TB, dir string) (stat dirStat) { type testEnvironment struct { base, cache, repo, mountpoint, testdata string - gopts GlobalOptions + gopts global.Options } type logOutputter struct { @@ -208,17 +210,17 @@ func withTestEnvironment(t testing.TB) (env *testEnvironment, cleanup func()) { rtest.OK(t, os.MkdirAll(env.cache, 0700)) rtest.OK(t, os.MkdirAll(env.repo, 0700)) - env.gopts = GlobalOptions{ + env.gopts = global.Options{ Repo: env.repo, Quiet: true, CacheDir: env.cache, - password: rtest.TestPassword, - extended: make(options.Options), + Password: rtest.TestPassword, + Extended: make(options.Options), // replace this hook with "nil" if listing a filetype more than once is necessary - backendTestHook: func(r backend.Backend) (backend.Backend, error) { return newOrderedListOnceBackend(r), nil }, + BackendTestHook: func(r backend.Backend) (backend.Backend, error) { return newOrderedListOnceBackend(r), nil }, // start with default set of backends - backends: collectBackends(), + Backends: all.Backends(), } cleanup = func() { @@ -239,10 +241,10 @@ func testSetupBackupData(t testing.TB, env *testEnvironment) string { return datafile } -func listPacks(gopts GlobalOptions, t *testing.T) restic.IDSet { +func listPacks(gopts global.Options, t *testing.T) restic.IDSet { var packs restic.IDSet - err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) ctx, r, unlock, err := openWithReadLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() @@ -258,10 +260,10 @@ func listPacks(gopts GlobalOptions, t *testing.T) restic.IDSet { return packs } -func listTreePacks(gopts GlobalOptions, t *testing.T) restic.IDSet { +func listTreePacks(gopts global.Options, t *testing.T) restic.IDSet { var treePacks restic.IDSet - err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) ctx, r, unlock, err := openWithReadLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() @@ -278,9 +280,9 @@ func listTreePacks(gopts GlobalOptions, t *testing.T) restic.IDSet { return treePacks } -func captureBackend(gopts *GlobalOptions) func() backend.Backend { +func captureBackend(gopts *global.Options) func() backend.Backend { var be backend.Backend - gopts.backendTestHook = func(r backend.Backend) (backend.Backend, error) { + gopts.BackendTestHook = func(r backend.Backend) (backend.Backend, error) { be = r return r, nil } @@ -289,10 +291,10 @@ func captureBackend(gopts *GlobalOptions) func() backend.Backend { } } -func removePacks(gopts GlobalOptions, t testing.TB, remove restic.IDSet) { +func removePacks(gopts global.Options, t testing.TB, remove restic.IDSet) { be := captureBackend(&gopts) - err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) ctx, _, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() @@ -305,10 +307,10 @@ func removePacks(gopts GlobalOptions, t testing.TB, remove restic.IDSet) { rtest.OK(t, err) } -func removePacksExcept(gopts GlobalOptions, t testing.TB, keep restic.IDSet, removeTreePacks bool) { +func removePacksExcept(gopts global.Options, t testing.TB, keep restic.IDSet, removeTreePacks bool) { be := captureBackend(&gopts) - err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) ctx, r, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() @@ -344,7 +346,7 @@ func includes(haystack []string, needle string) bool { return false } -func loadSnapshotMap(t testing.TB, gopts GlobalOptions) map[string]struct{} { +func loadSnapshotMap(t testing.TB, gopts global.Options) map[string]struct{} { snapshotIDs := testRunList(t, gopts, "snapshots") m := make(map[string]struct{}) @@ -366,10 +368,10 @@ func lastSnapshot(old, new map[string]struct{}) (map[string]struct{}, string) { return old, "" } -func testLoadSnapshot(t testing.TB, gopts GlobalOptions, id restic.ID) *data.Snapshot { +func testLoadSnapshot(t testing.TB, gopts global.Options, id restic.ID) *data.Snapshot { var snapshot *data.Snapshot - err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error { + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) _, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() @@ -413,25 +415,25 @@ func testFileSize(filename string, size int64) error { return nil } -func withCaptureStdout(t testing.TB, gopts GlobalOptions, callback func(ctx context.Context, gopts GlobalOptions) error) (*bytes.Buffer, error) { +func withCaptureStdout(t testing.TB, gopts global.Options, callback func(ctx context.Context, gopts global.Options) error) (*bytes.Buffer, error) { buf := bytes.NewBuffer(nil) err := withTermStatusRaw(os.Stdin, buf, &logOutputter{t: t}, gopts, callback) return buf, err } -func withTermStatus(t testing.TB, gopts GlobalOptions, callback func(ctx context.Context, gopts GlobalOptions) error) error { +func withTermStatus(t testing.TB, gopts global.Options, callback func(ctx context.Context, gopts global.Options) error) error { // stdout and stderr are written to by printer functions etc. That is the written data // usually consists of one or multiple lines and therefore can be handled well // by t.Log. return withTermStatusRaw(os.Stdin, &logOutputter{t: t}, &logOutputter{t: t}, gopts, callback) } -func withTermStatusRaw(stdin io.ReadCloser, stdout, stderr io.Writer, gopts GlobalOptions, callback func(ctx context.Context, gopts GlobalOptions) error) error { +func withTermStatusRaw(stdin io.ReadCloser, stdout, stderr io.Writer, gopts global.Options, callback func(ctx context.Context, gopts global.Options) error) error { ctx, cancel := context.WithCancel(context.TODO()) var wg sync.WaitGroup term := termstatus.New(stdin, stdout, stderr, gopts.Quiet) - gopts.term = term + gopts.Term = term wg.Add(1) go func() { defer wg.Done() diff --git a/cmd/restic/integration_test.go b/cmd/restic/integration_test.go index b1cef1ad4..8c7ac6ccd 100644 --- a/cmd/restic/integration_test.go +++ b/cmd/restic/integration_test.go @@ -11,6 +11,7 @@ import ( "github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/data" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" "github.com/restic/restic/internal/ui" @@ -80,7 +81,7 @@ func TestListOnce(t *testing.T) { env, cleanup := withTestEnvironment(t) defer cleanup() - env.gopts.backendTestHook = func(r backend.Backend) (backend.Backend, error) { + env.gopts.BackendTestHook = func(r backend.Backend) (backend.Backend, error) { return newOrderedListOnceBackend(r), nil } pruneOpts := PruneOptions{MaxUnused: "0"} @@ -88,15 +89,15 @@ func TestListOnce(t *testing.T) { createPrunableRepo(t, env) testRunPrune(t, env.gopts, pruneOpts) - rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - _, err := runCheck(context.TODO(), checkOpts, gopts, nil, gopts.term) + rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + _, err := runCheck(context.TODO(), checkOpts, gopts, nil, gopts.Term) return err })) - rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.term) + rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.Term) })) - rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - return runRebuildIndex(context.TODO(), RepairIndexOptions{ReadAllPacks: true}, gopts, gopts.term) + rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + return runRebuildIndex(context.TODO(), RepairIndexOptions{ReadAllPacks: true}, gopts, gopts.Term) })) } @@ -129,7 +130,7 @@ func TestBackendLoadWriteTo(t *testing.T) { defer cleanup() // setup backend which only works if it's WriteTo method is correctly propagated upwards - env.gopts.backendInnerTestHook = func(r backend.Backend) (backend.Backend, error) { + env.gopts.BackendInnerTestHook = func(r backend.Backend) (backend.Backend, error) { return &onlyLoadWithWriteToBackend{Backend: r}, nil } @@ -149,7 +150,7 @@ func TestFindListOnce(t *testing.T) { env, cleanup := withTestEnvironment(t) defer cleanup() - env.gopts.backendTestHook = func(r backend.Backend) (backend.Backend, error) { + env.gopts.BackendTestHook = func(r backend.Backend) (backend.Backend, error) { return newOrderedListOnceBackend(r), nil } @@ -163,8 +164,8 @@ func TestFindListOnce(t *testing.T) { thirdSnapshot := restic.NewIDSet(testListSnapshots(t, env.gopts, 3)...) var snapshotIDs restic.IDSet - rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error { + printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() @@ -214,7 +215,7 @@ func TestBackendRetryConfig(t *testing.T) { var wrappedBackend *failConfigOnceBackend // cause config loading to fail once - env.gopts.backendInnerTestHook = func(r backend.Backend) (backend.Backend, error) { + env.gopts.BackendInnerTestHook = func(r backend.Backend) (backend.Backend, error) { wrappedBackend = &failConfigOnceBackend{Backend: r} return wrappedBackend, nil } diff --git a/cmd/restic/lock.go b/cmd/restic/lock.go index eb95c4432..149f0e309 100644 --- a/cmd/restic/lock.go +++ b/cmd/restic/lock.go @@ -3,12 +3,13 @@ package main import ( "context" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/ui/progress" ) -func internalOpenWithLocked(ctx context.Context, gopts GlobalOptions, dryRun bool, exclusive bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) { - repo, err := OpenRepository(ctx, gopts, printer) +func internalOpenWithLocked(ctx context.Context, gopts global.Options, dryRun bool, exclusive bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) { + repo, err := global.OpenRepository(ctx, gopts, printer) if err != nil { return nil, nil, nil, err } @@ -34,16 +35,16 @@ func internalOpenWithLocked(ctx context.Context, gopts GlobalOptions, dryRun boo return ctx, repo, unlock, nil } -func openWithReadLock(ctx context.Context, gopts GlobalOptions, noLock bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) { +func openWithReadLock(ctx context.Context, gopts global.Options, noLock bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) { // TODO enforce read-only operations once the locking code has moved to the repository return internalOpenWithLocked(ctx, gopts, noLock, false, printer) } -func openWithAppendLock(ctx context.Context, gopts GlobalOptions, dryRun bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) { +func openWithAppendLock(ctx context.Context, gopts global.Options, dryRun bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) { // TODO enforce non-exclusive operations once the locking code has moved to the repository return internalOpenWithLocked(ctx, gopts, dryRun, false, printer) } -func openWithExclusiveLock(ctx context.Context, gopts GlobalOptions, dryRun bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) { +func openWithExclusiveLock(ctx context.Context, gopts global.Options, dryRun bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) { return internalOpenWithLocked(ctx, gopts, dryRun, true, printer) } diff --git a/cmd/restic/main.go b/cmd/restic/main.go index 43e1f0b7a..3307d2787 100644 --- a/cmd/restic/main.go +++ b/cmd/restic/main.go @@ -14,9 +14,11 @@ import ( "github.com/spf13/cobra" "go.uber.org/automaxprocs/maxprocs" + "github.com/restic/restic/internal/backend/all" "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/feature" + "github.com/restic/restic/internal/global" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui/termstatus" @@ -32,7 +34,7 @@ var ErrOK = errors.New("ok") var cmdGroupDefault = "default" var cmdGroupAdvanced = "advanced" -func newRootCommand(globalOptions *GlobalOptions) *cobra.Command { +func newRootCommand(globalOptions *global.Options) *cobra.Command { cmd := &cobra.Command{ Use: "restic", Short: "Backup and restore files", @@ -102,7 +104,7 @@ The full documentation can be found at https://restic.readthedocs.io/ . registerDebugCommand(cmd, globalOptions) registerMountCommand(cmd, globalOptions) registerSelfUpdateCommand(cmd, globalOptions) - registerProfiling(cmd, os.Stderr) + global.RegisterProfiling(cmd, os.Stderr) return cmd } @@ -127,7 +129,7 @@ func tweakGoGC() { } } -func printExitError(globalOptions GlobalOptions, code int, message string) { +func printExitError(globalOptions global.Options, code int, message string) { if globalOptions.JSON { type jsonExitError struct { MessageType string `json:"message_type"` // exit_error @@ -170,15 +172,15 @@ func main() { debug.Log("main %#v", os.Args) debug.Log("restic %s compiled with %v on %v/%v", - version, runtime.Version(), runtime.GOOS, runtime.GOARCH) + global.Version, runtime.Version(), runtime.GOOS, runtime.GOARCH) - globalOptions := GlobalOptions{ - backends: collectBackends(), + globalOptions := global.Options{ + Backends: all.Backends(), } func() { term, cancel := termstatus.Setup(os.Stdin, os.Stdout, os.Stderr, globalOptions.Quiet) defer cancel() - globalOptions.term = term + globalOptions.Term = term ctx := createGlobalContext(os.Stderr) err = newRootCommand(&globalOptions).ExecuteContext(ctx) switch err { @@ -220,7 +222,7 @@ func main() { exitCode = 3 case errors.Is(err, ErrFailedToRemoveOneOrMoreSnapshots): exitCode = 3 - case errors.Is(err, ErrNoRepository): + case errors.Is(err, global.ErrNoRepository): exitCode = 10 case restic.IsAlreadyLocked(err): exitCode = 11 diff --git a/helpers/prepare-release/main.go b/helpers/prepare-release/main.go index 607d16936..1bb042969 100644 --- a/helpers/prepare-release/main.go +++ b/helpers/prepare-release/main.go @@ -308,9 +308,9 @@ func generateFiles() { } } -var versionPattern = `var version = ".*"` +var versionPattern = `const Version = ".*"` -const versionCodeFile = "cmd/restic/global.go" +const versionCodeFile = "internal/global/global.go" func updateVersion() { err := os.WriteFile("VERSION", []byte(opts.Version+"\n"), 0644) @@ -318,7 +318,7 @@ func updateVersion() { die("unable to write version to file: %v", err) } - newVersion := fmt.Sprintf("var version = %q", opts.Version) + newVersion := fmt.Sprintf("const Version = %q", opts.Version) replace(versionCodeFile, versionPattern, newVersion) if len(uncommittedChanges("VERSION")) > 0 || len(uncommittedChanges(versionCodeFile)) > 0 { diff --git a/internal/backend/all/all.go b/internal/backend/all/all.go new file mode 100644 index 000000000..c71acb00d --- /dev/null +++ b/internal/backend/all/all.go @@ -0,0 +1,28 @@ +package all + +import ( + "github.com/restic/restic/internal/backend/azure" + "github.com/restic/restic/internal/backend/b2" + "github.com/restic/restic/internal/backend/gs" + "github.com/restic/restic/internal/backend/local" + "github.com/restic/restic/internal/backend/location" + "github.com/restic/restic/internal/backend/rclone" + "github.com/restic/restic/internal/backend/rest" + "github.com/restic/restic/internal/backend/s3" + "github.com/restic/restic/internal/backend/sftp" + "github.com/restic/restic/internal/backend/swift" +) + +func Backends() *location.Registry { + backends := location.NewRegistry() + backends.Register(azure.NewFactory()) + backends.Register(b2.NewFactory()) + backends.Register(gs.NewFactory()) + backends.Register(local.NewFactory()) + backends.Register(rclone.NewFactory()) + backends.Register(rest.NewFactory()) + backends.Register(s3.NewFactory()) + backends.Register(sftp.NewFactory()) + backends.Register(swift.NewFactory()) + return backends +} diff --git a/cmd/restic/global.go b/internal/global/global.go similarity index 65% rename from cmd/restic/global.go rename to internal/global/global.go index cd17dccc7..c65338b92 100644 --- a/cmd/restic/global.go +++ b/internal/global/global.go @@ -1,8 +1,9 @@ -package main +package global import ( "context" "fmt" + "net/http" "os" "os/exec" "path/filepath" @@ -10,22 +11,14 @@ import ( "strings" "time" + "github.com/restic/chunker" "github.com/restic/restic/internal/backend" - "github.com/restic/restic/internal/backend/azure" - "github.com/restic/restic/internal/backend/b2" "github.com/restic/restic/internal/backend/cache" - "github.com/restic/restic/internal/backend/gs" "github.com/restic/restic/internal/backend/limiter" - "github.com/restic/restic/internal/backend/local" "github.com/restic/restic/internal/backend/location" "github.com/restic/restic/internal/backend/logger" - "github.com/restic/restic/internal/backend/rclone" - "github.com/restic/restic/internal/backend/rest" "github.com/restic/restic/internal/backend/retry" - "github.com/restic/restic/internal/backend/s3" "github.com/restic/restic/internal/backend/sema" - "github.com/restic/restic/internal/backend/sftp" - "github.com/restic/restic/internal/backend/swift" "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/options" "github.com/restic/restic/internal/repository" @@ -42,15 +35,15 @@ import ( // to a missing backend storage location or config file var ErrNoRepository = errors.New("repository does not exist") -var version = "0.18.1-dev (compiled manually)" +const Version = "0.18.1-dev (compiled manually)" // TimeFormat is the format used for all timestamps printed by restic. const TimeFormat = "2006-01-02 15:04:05" -type backendWrapper func(r backend.Backend) (backend.Backend, error) +type BackendWrapper func(r backend.Backend) (backend.Backend, error) -// GlobalOptions hold all global options for restic. -type GlobalOptions struct { +// Options hold all global options for restic. +type Options struct { Repo string RepositoryFile string PasswordFile string @@ -72,25 +65,25 @@ type GlobalOptions struct { backend.TransportOptions limiter.Limits - password string - term ui.Terminal + Password string + Term ui.Terminal - backends *location.Registry - backendTestHook, backendInnerTestHook backendWrapper + Backends *location.Registry + BackendTestHook, BackendInnerTestHook BackendWrapper - // verbosity is set as follows: + // Verbosity is set as follows: // 0 means: don't print any messages except errors, this is used when --quiet is specified // 1 is the default: print essential messages // 2 means: print more messages, report minor things, this is used when --verbose is specified // 3 means: print very detailed debug messages, this is used when --verbose=2 is specified - verbosity uint + Verbosity uint Options []string - extended options.Options + Extended options.Options } -func (opts *GlobalOptions) AddFlags(f *pflag.FlagSet) { +func (opts *Options) AddFlags(f *pflag.FlagSet) { f.StringVarP(&opts.Repo, "repo", "r", "", "`repository` to backup to or restore from (default: $RESTIC_REPOSITORY)") f.StringVarP(&opts.RepositoryFile, "repository-file", "", "", "`file` to read the repository location from (default: $RESTIC_REPOSITORY_FILE)") f.StringVarP(&opts.PasswordFile, "password-file", "p", "", "`file` to read the repository password from (default: $RESTIC_PASSWORD_FILE)") @@ -141,20 +134,20 @@ func (opts *GlobalOptions) AddFlags(f *pflag.FlagSet) { } } -func (opts *GlobalOptions) PreRun(needsPassword bool) error { +func (opts *Options) PreRun(needsPassword bool) error { // set verbosity, default is one - opts.verbosity = 1 + opts.Verbosity = 1 if opts.Quiet && opts.Verbose > 0 { return errors.Fatal("--quiet and --verbose cannot be specified at the same time") } switch { case opts.Verbose >= 2: - opts.verbosity = 3 + opts.Verbosity = 3 case opts.Verbose > 0: - opts.verbosity = 2 + opts.Verbosity = 2 case opts.Quiet: - opts.verbosity = 0 + opts.Verbosity = 0 } // parse extended options @@ -162,7 +155,7 @@ func (opts *GlobalOptions) PreRun(needsPassword bool) error { if err != nil { return err } - opts.extended = extendedOpts + opts.Extended = extendedOpts if !needsPassword { return nil } @@ -170,26 +163,12 @@ func (opts *GlobalOptions) PreRun(needsPassword bool) error { if err != nil { return errors.Fatalf("Resolving password failed: %v", err) } - opts.password = pwd + opts.Password = pwd return nil } -func collectBackends() *location.Registry { - backends := location.NewRegistry() - backends.Register(azure.NewFactory()) - backends.Register(b2.NewFactory()) - backends.Register(gs.NewFactory()) - backends.Register(local.NewFactory()) - backends.Register(rclone.NewFactory()) - backends.Register(rest.NewFactory()) - backends.Register(s3.NewFactory()) - backends.Register(sftp.NewFactory()) - backends.Register(swift.NewFactory()) - return backends -} - // resolvePassword determines the password to be used for opening the repository. -func resolvePassword(opts *GlobalOptions, envStr string) (string, error) { +func resolvePassword(opts *Options, envStr string) (string, error) { if opts.PasswordFile != "" && opts.PasswordCommand != "" { return "", errors.Fatalf("Password file and command are mutually exclusive options") } @@ -207,7 +186,7 @@ func resolvePassword(opts *GlobalOptions, envStr string) (string, error) { return strings.TrimSpace(string(output)), nil } if opts.PasswordFile != "" { - return loadPasswordFromFile(opts.PasswordFile) + return LoadPasswordFromFile(opts.PasswordFile) } if pwd := os.Getenv(envStr); pwd != "" { @@ -217,9 +196,9 @@ func resolvePassword(opts *GlobalOptions, envStr string) (string, error) { return "", nil } -// loadPasswordFromFile loads a password from a file while stripping a BOM and +// LoadPasswordFromFile loads a password from a file while stripping a BOM and // converting the password to UTF-8. -func loadPasswordFromFile(pwdFile string) (string, error) { +func LoadPasswordFromFile(pwdFile string) (string, error) { s, err := textfile.Read(pwdFile) if errors.Is(err, os.ErrNotExist) { return "", errors.Fatalf("%s does not exist", pwdFile) @@ -227,22 +206,22 @@ func loadPasswordFromFile(pwdFile string) (string, error) { return strings.TrimSpace(string(s)), errors.Wrap(err, "Readfile") } -// ReadPassword reads the password from a password file, the environment +// readPassword reads the password from a password file, the environment // variable RESTIC_PASSWORD or prompts the user. If the context is canceled, // the function leaks the password reading goroutine. -func ReadPassword(ctx context.Context, gopts GlobalOptions, prompt string) (string, error) { +func readPassword(ctx context.Context, gopts Options, prompt string) (string, error) { if gopts.InsecureNoPassword { - if gopts.password != "" { + if gopts.Password != "" { return "", errors.Fatal("--insecure-no-password must not be specified together with providing a password via a cli option or environment variable") } return "", nil } - if gopts.password != "" { - return gopts.password, nil + if gopts.Password != "" { + return gopts.Password, nil } - password, err := gopts.term.ReadPassword(ctx, prompt) + password, err := gopts.Term.ReadPassword(ctx, prompt) if err != nil { return "", fmt.Errorf("unable to read password: %w", err) } @@ -257,13 +236,13 @@ func ReadPassword(ctx context.Context, gopts GlobalOptions, prompt string) (stri // ReadPasswordTwice calls ReadPassword two times and returns an error when the // passwords don't match. If the context is canceled, the function leaks the // password reading goroutine. -func ReadPasswordTwice(ctx context.Context, gopts GlobalOptions, prompt1, prompt2 string) (string, error) { - pw1, err := ReadPassword(ctx, gopts, prompt1) +func ReadPasswordTwice(ctx context.Context, gopts Options, prompt1, prompt2 string) (string, error) { + pw1, err := readPassword(ctx, gopts, prompt1) if err != nil { return "", err } - if gopts.term.InputIsTerminal() { - pw2, err := ReadPassword(ctx, gopts, prompt2) + if gopts.Term.InputIsTerminal() { + pw2, err := readPassword(ctx, gopts, prompt2) if err != nil { return "", err } @@ -276,7 +255,7 @@ func ReadPasswordTwice(ctx context.Context, gopts GlobalOptions, prompt1, prompt return pw1, nil } -func ReadRepo(gopts GlobalOptions) (string, error) { +func readRepo(gopts Options) (string, error) { if gopts.Repo == "" && gopts.RepositoryFile == "" { return "", errors.Fatal("Please specify repository location (-r or --repository-file)") } @@ -304,17 +283,65 @@ func ReadRepo(gopts GlobalOptions) (string, error) { const maxKeys = 20 // OpenRepository reads the password and opens the repository. -func OpenRepository(ctx context.Context, gopts GlobalOptions, printer progress.Printer) (*repository.Repository, error) { - repo, err := ReadRepo(gopts) +func OpenRepository(ctx context.Context, gopts Options, printer progress.Printer) (*repository.Repository, error) { + repo, err := readRepo(gopts) if err != nil { return nil, err } - be, err := open(ctx, repo, gopts, gopts.extended, printer) + be, err := innerOpenBackend(ctx, repo, gopts, gopts.Extended, false, printer) if err != nil { return nil, err } + err = hasRepositoryConfig(ctx, be, repo, gopts) + if err != nil { + return nil, err + } + + s, err := createRepositoryInstance(be, gopts) + if err != nil { + return nil, err + } + + err = decryptRepository(ctx, s, &gopts, printer) + if err != nil { + return nil, err + } + + printRepositoryInfo(s, gopts, printer) + + if gopts.NoCache { + return s, nil + } + + err = setupCache(s, gopts, printer) + if err != nil { + return nil, err + } + return s, nil +} + +// hasRepositoryConfig checks if the repository config file exists and is not empty. +func hasRepositoryConfig(ctx context.Context, be backend.Backend, repo string, gopts Options) error { + fi, err := be.Stat(ctx, backend.Handle{Type: restic.ConfigFile}) + if be.IsNotExist(err) { + //nolint:staticcheck // capitalized error string is intentional + return fmt.Errorf("Fatal: %w: unable to open config file: %v\nIs there a repository at the following location?\n%v", ErrNoRepository, err, location.StripPassword(gopts.Backends, repo)) + } + if err != nil { + return errors.Fatalf("unable to open config file: %v\n%v", err, location.StripPassword(gopts.Backends, repo)) + } + + if fi.Size == 0 { + return errors.New("config file has zero size, invalid repository?") + } + + return nil +} + +// createRepositoryInstance creates a new repository instance with the given options. +func createRepositoryInstance(be backend.Backend, gopts Options) (*repository.Repository, error) { s, err := repository.New(be, repository.Options{ Compression: gopts.Compression, PackSize: gopts.PackSize * 1024 * 1024, @@ -323,38 +350,48 @@ func OpenRepository(ctx context.Context, gopts GlobalOptions, printer progress.P if err != nil { return nil, errors.Fatalf("%s", err) } + return s, nil +} +// decryptRepository handles password reading and decrypts the repository. +func decryptRepository(ctx context.Context, s *repository.Repository, gopts *Options, printer progress.Printer) error { passwordTriesLeft := 1 - if gopts.term.InputIsTerminal() && gopts.password == "" && !gopts.InsecureNoPassword { + if gopts.Term.InputIsTerminal() && gopts.Password == "" && !gopts.InsecureNoPassword { passwordTriesLeft = 3 } + var err error for ; passwordTriesLeft > 0; passwordTriesLeft-- { - gopts.password, err = ReadPassword(ctx, gopts, "enter password for repository: ") + gopts.Password, err = readPassword(ctx, *gopts, "enter password for repository: ") if ctx.Err() != nil { - return nil, ctx.Err() + return ctx.Err() } if err != nil && passwordTriesLeft > 1 { - gopts.password = "" + gopts.Password = "" printer.E("%s. Try again", err) } if err != nil { continue } - err = s.SearchKey(ctx, gopts.password, maxKeys, gopts.KeyHint) + err = s.SearchKey(ctx, gopts.Password, maxKeys, gopts.KeyHint) if err != nil && passwordTriesLeft > 1 { - gopts.password = "" + gopts.Password = "" printer.E("%s. Try again", err) } } if err != nil { if errors.IsFatal(err) || errors.Is(err, repository.ErrNoKeyFound) { - return nil, err + return err } - return nil, errors.Fatalf("%s", err) + return errors.Fatalf("%s", err) } + return nil +} + +// printRepositoryInfo displays the repository ID, version and compression level. +func printRepositoryInfo(s *repository.Repository, gopts Options, printer progress.Printer) { id := s.Config().ID if len(id) > 8 { id = id[:8] @@ -364,15 +401,14 @@ func OpenRepository(ctx context.Context, gopts GlobalOptions, printer progress.P extra = ", compression level " + gopts.Compression.String() } printer.PT("repository %v opened (version %v%s)", id, s.Config().Version, extra) +} - if gopts.NoCache { - return s, nil - } - +// setupCache creates a new cache and removes old cache directories if instructed to do so. +func setupCache(s *repository.Repository, gopts Options, printer progress.Printer) error { c, err := cache.New(s.Config().ID, gopts.CacheDir) if err != nil { printer.E("unable to open cache: %v", err) - return s, nil + return err } if c.Created { @@ -389,7 +425,7 @@ func OpenRepository(ctx context.Context, gopts GlobalOptions, printer progress.P // nothing more to do if no old cache dirs could be found if len(oldCacheDirs) == 0 { - return s, nil + return nil } // cleanup old cache dirs if instructed to do so @@ -406,11 +442,78 @@ func OpenRepository(ctx context.Context, gopts GlobalOptions, printer progress.P printer.PT("found %d old cache directories in %v, run `restic cache --cleanup` to remove them", len(oldCacheDirs), c.Base) } + return nil +} + +// CreateRepository a repository with the given version and chunker polynomial. +func CreateRepository(ctx context.Context, gopts Options, version uint, chunkerPolynomial *chunker.Pol, printer progress.Printer) (*repository.Repository, error) { + if version < restic.MinRepoVersion || version > restic.MaxRepoVersion { + return nil, errors.Fatalf("only repository versions between %v and %v are allowed", restic.MinRepoVersion, restic.MaxRepoVersion) + } + + repo, err := readRepo(gopts) + if err != nil { + return nil, err + } + + gopts.Password, err = ReadPasswordTwice(ctx, gopts, + "enter password for new repository: ", + "enter password again: ") + if err != nil { + return nil, err + } + + be, err := innerOpenBackend(ctx, repo, gopts, gopts.Extended, true, printer) + if err != nil { + return nil, errors.Fatalf("create repository at %s failed: %v", location.StripPassword(gopts.Backends, repo), err) + } + + s, err := createRepositoryInstance(be, gopts) + if err != nil { + return nil, err + } + + err = s.Init(ctx, version, gopts.Password, chunkerPolynomial) + if err != nil { + return nil, errors.Fatalf("create key in repository at %s failed: %v", location.StripPassword(gopts.Backends, repo), err) + } return s, nil } -func parseConfig(loc location.Location, opts options.Options) (interface{}, error) { +func innerOpenBackend(ctx context.Context, s string, gopts Options, opts options.Options, create bool, printer progress.Printer) (backend.Backend, error) { + debug.Log("parsing location %v", location.StripPassword(gopts.Backends, s)) + + scheme, cfg, err := parseConfig(gopts.Backends, s, opts) + if err != nil { + return nil, err + } + + rt, lim, err := setupTransport(gopts) + if err != nil { + return nil, err + } + + be, err := createOrOpenBackend(ctx, scheme, cfg, rt, lim, gopts, s, create, printer) + if err != nil { + return nil, err + } + + be, err = wrapBackend(be, gopts, printer) + if err != nil { + return nil, err + } + + return be, nil +} + +// parseConfig parses the repository location and extended options and returns the scheme and configuration. +func parseConfig(backends *location.Registry, s string, opts options.Options) (string, interface{}, error) { + loc, err := location.Parse(backends, s) + if err != nil { + return "", nil, errors.Fatalf("parsing repository location failed: %v", err) + } + cfg := loc.Config if cfg, ok := cfg.(backend.ApplyEnvironmenter); ok { cfg.ApplyEnvironment("") @@ -419,40 +522,36 @@ func parseConfig(loc location.Location, opts options.Options) (interface{}, erro // only apply options for a particular backend here opts = opts.Extract(loc.Scheme) if err := opts.Apply(loc.Scheme, cfg); err != nil { - return nil, err + return "", nil, err } debug.Log("opening %v repository at %#v", loc.Scheme, cfg) - return cfg, nil + return loc.Scheme, cfg, nil } -func innerOpen(ctx context.Context, s string, gopts GlobalOptions, opts options.Options, create bool, printer progress.Printer) (backend.Backend, error) { - debug.Log("parsing location %v", location.StripPassword(gopts.backends, s)) - loc, err := location.Parse(gopts.backends, s) - if err != nil { - return nil, errors.Fatalf("parsing repository location failed: %v", err) - } - - cfg, err := parseConfig(loc, opts) - if err != nil { - return nil, err - } - +// setupTransport creates and configures the transport with rate limiting. +func setupTransport(gopts Options) (http.RoundTripper, limiter.Limiter, error) { rt, err := backend.Transport(gopts.TransportOptions) if err != nil { - return nil, errors.Fatalf("%s", err) + return nil, nil, errors.Fatalf("%s", err) } // wrap the transport so that the throughput via HTTP is limited lim := limiter.NewStaticLimiter(gopts.Limits) rt = lim.Transport(rt) - factory := gopts.backends.Lookup(loc.Scheme) + return rt, lim, nil +} + +// createOrOpenBackend creates or opens a backend using the appropriate factory method. +func createOrOpenBackend(ctx context.Context, scheme string, cfg interface{}, rt http.RoundTripper, lim limiter.Limiter, gopts Options, s string, create bool, printer progress.Printer) (backend.Backend, error) { + factory := gopts.Backends.Lookup(scheme) if factory == nil { - return nil, errors.Fatalf("invalid backend: %q", loc.Scheme) + return nil, errors.Fatalf("invalid backend: %q", scheme) } var be backend.Backend + var err error if create { be, err = factory.Create(ctx, cfg, rt, lim, printer.E) } else { @@ -461,22 +560,28 @@ func innerOpen(ctx context.Context, s string, gopts GlobalOptions, opts options. if errors.Is(err, backend.ErrNoRepository) { //nolint:staticcheck // capitalized error string is intentional - return nil, fmt.Errorf("Fatal: %w at %v: %v", ErrNoRepository, location.StripPassword(gopts.backends, s), err) + return nil, fmt.Errorf("Fatal: %w at %v: %v", ErrNoRepository, location.StripPassword(gopts.Backends, s), err) } if err != nil { if create { // init already wraps the error message return nil, err } - return nil, errors.Fatalf("unable to open repository at %v: %v", location.StripPassword(gopts.backends, s), err) + return nil, errors.Fatalf("unable to open repository at %v: %v", location.StripPassword(gopts.Backends, s), err) } + return be, nil +} + +// wrapBackend applies debug logging, test hooks, and retry wrapper to the backend. +func wrapBackend(be backend.Backend, gopts Options, printer progress.Printer) (backend.Backend, error) { // wrap with debug logging and connection limiting be = logger.New(sema.NewBackend(be)) // wrap backend if a test specified an inner hook - if gopts.backendInnerTestHook != nil { - be, err = gopts.backendInnerTestHook(be) + if gopts.BackendInnerTestHook != nil { + var err error + be, err = gopts.BackendInnerTestHook(be) if err != nil { return nil, err } @@ -495,8 +600,9 @@ func innerOpen(ctx context.Context, s string, gopts GlobalOptions, opts options. be = retry.New(be, 15*time.Minute, report, success) // wrap backend if a test specified a hook - if gopts.backendTestHook != nil { - be, err = gopts.backendTestHook(be) + if gopts.BackendTestHook != nil { + var err error + be, err = gopts.BackendTestHook(be) if err != nil { return nil, err } @@ -504,32 +610,3 @@ func innerOpen(ctx context.Context, s string, gopts GlobalOptions, opts options. return be, nil } - -// Open the backend specified by a location config. -func open(ctx context.Context, s string, gopts GlobalOptions, opts options.Options, printer progress.Printer) (backend.Backend, error) { - be, err := innerOpen(ctx, s, gopts, opts, false, printer) - if err != nil { - return nil, err - } - - // check if config is there - fi, err := be.Stat(ctx, backend.Handle{Type: restic.ConfigFile}) - if be.IsNotExist(err) { - //nolint:staticcheck // capitalized error string is intentional - return nil, fmt.Errorf("Fatal: %w: unable to open config file: %v\nIs there a repository at the following location?\n%v", ErrNoRepository, err, location.StripPassword(gopts.backends, s)) - } - if err != nil { - return nil, errors.Fatalf("unable to open config file: %v\nIs there a repository at the following location?\n%v", err, location.StripPassword(gopts.backends, s)) - } - - if fi.Size == 0 { - return nil, errors.New("config file has zero size, invalid repository?") - } - - return be, nil -} - -// Create the backend specified by URI. -func create(ctx context.Context, s string, gopts GlobalOptions, opts options.Options, printer progress.Printer) (backend.Backend, error) { - return innerOpen(ctx, s, gopts, opts, true, printer) -} diff --git a/cmd/restic/global_debug.go b/internal/global/global_debug.go similarity index 93% rename from cmd/restic/global_debug.go rename to internal/global/global_debug.go index ff1ac3a57..be414ff80 100644 --- a/cmd/restic/global_debug.go +++ b/internal/global/global_debug.go @@ -1,7 +1,7 @@ //go:build debug || profile // +build debug profile -package main +package global import ( "fmt" @@ -17,8 +17,8 @@ import ( "github.com/pkg/profile" ) -func registerProfiling(cmd *cobra.Command, stderr io.Writer) { - var profiler profiler +func RegisterProfiling(cmd *cobra.Command, stderr io.Writer) { + var profiler Profiler origPreRun := cmd.PersistentPreRunE cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { @@ -40,7 +40,7 @@ func registerProfiling(cmd *cobra.Command, stderr io.Writer) { profiler.opts.AddFlags(cmd.PersistentFlags()) } -type profiler struct { +type Profiler struct { opts ProfileOptions stop interface { Stop() @@ -73,7 +73,7 @@ func (t fakeTestingTB) Logf(msg string, args ...interface{}) { fmt.Fprintf(t.stderr, msg, args...) } -func (p *profiler) Start(profileOpts ProfileOptions, stderr io.Writer) error { +func (p *Profiler) Start(profileOpts ProfileOptions, stderr io.Writer) error { if profileOpts.listen != "" { fmt.Fprintf(stderr, "running profile HTTP server on %v\n", profileOpts.listen) go func() { @@ -119,7 +119,7 @@ func (p *profiler) Start(profileOpts ProfileOptions, stderr io.Writer) error { return nil } -func (p *profiler) Stop() { +func (p *Profiler) Stop() { if p.stop != nil { p.stop.Stop() } diff --git a/cmd/restic/global_release.go b/internal/global/global_release.go similarity index 66% rename from cmd/restic/global_release.go rename to internal/global/global_release.go index 1e1e2147c..f0c21ac60 100644 --- a/cmd/restic/global_release.go +++ b/internal/global/global_release.go @@ -1,7 +1,7 @@ //go:build !debug && !profile // +build !debug,!profile -package main +package global import ( "io" @@ -9,6 +9,6 @@ import ( "github.com/spf13/cobra" ) -func registerProfiling(_ *cobra.Command, _ io.Writer) { +func RegisterProfiling(_ *cobra.Command, _ io.Writer) { // No profiling in release mode } diff --git a/cmd/restic/global_test.go b/internal/global/global_test.go similarity index 72% rename from cmd/restic/global_test.go rename to internal/global/global_test.go index e8def7b94..7d5ead722 100644 --- a/cmd/restic/global_test.go +++ b/internal/global/global_test.go @@ -1,4 +1,4 @@ -package main +package global import ( "context" @@ -14,9 +14,9 @@ func TestReadRepo(t *testing.T) { tempDir := rtest.TempDir(t) // test --repo option - var gopts GlobalOptions + var gopts Options gopts.Repo = tempDir - repo, err := ReadRepo(gopts) + repo, err := readRepo(gopts) rtest.OK(t, err) rtest.Equals(t, tempDir, repo) @@ -25,27 +25,27 @@ func TestReadRepo(t *testing.T) { err = os.WriteFile(foo, []byte(tempDir+"\n"), 0666) rtest.OK(t, err) - var gopts2 GlobalOptions + var gopts2 Options gopts2.RepositoryFile = foo - repo, err = ReadRepo(gopts2) + repo, err = readRepo(gopts2) rtest.OK(t, err) rtest.Equals(t, tempDir, repo) - var gopts3 GlobalOptions + var gopts3 Options gopts3.RepositoryFile = foo + "-invalid" - _, err = ReadRepo(gopts3) + _, err = readRepo(gopts3) if err == nil { t.Fatal("must not read repository path from invalid file path") } } func TestReadEmptyPassword(t *testing.T) { - opts := GlobalOptions{InsecureNoPassword: true} - password, err := ReadPassword(context.TODO(), opts, "test") + opts := Options{InsecureNoPassword: true} + password, err := readPassword(context.TODO(), opts, "test") rtest.OK(t, err) rtest.Equals(t, "", password, "got unexpected password") - opts.password = "invalid" - _, err = ReadPassword(context.TODO(), opts, "test") + opts.Password = "invalid" + _, err = readPassword(context.TODO(), opts, "test") rtest.Assert(t, strings.Contains(err.Error(), "must not be specified together with providing a password via a cli option or environment variable"), "unexpected error message, got %v", err) } diff --git a/cmd/restic/secondary_repo.go b/internal/global/secondary_repo.go similarity index 80% rename from cmd/restic/secondary_repo.go rename to internal/global/secondary_repo.go index db4c93bad..1224495df 100644 --- a/cmd/restic/secondary_repo.go +++ b/internal/global/secondary_repo.go @@ -1,4 +1,4 @@ -package main +package global import ( "context" @@ -8,8 +8,8 @@ import ( "github.com/spf13/pflag" ) -type secondaryRepoOptions struct { - password string +type SecondaryRepoOptions struct { + Password string // from-repo options Repo string RepositoryFile string @@ -25,7 +25,7 @@ type secondaryRepoOptions struct { LegacyKeyHint string } -func (opts *secondaryRepoOptions) AddFlags(f *pflag.FlagSet, repoPrefix string, repoUsage string) { +func (opts *SecondaryRepoOptions) AddFlags(f *pflag.FlagSet, repoPrefix string, repoUsage string) { f.StringVarP(&opts.LegacyRepo, "repo2", "", "", repoPrefix+" `repository` "+repoUsage+" (default: $RESTIC_REPOSITORY2)") f.StringVarP(&opts.LegacyRepositoryFile, "repository-file2", "", "", "`file` from which to read the "+repoPrefix+" repository location "+repoUsage+" (default: $RESTIC_REPOSITORY_FILE2)") f.StringVarP(&opts.LegacyPasswordFile, "password-file2", "", "", "`file` to read the "+repoPrefix+" repository password from (default: $RESTIC_PASSWORD_FILE2)") @@ -59,9 +59,9 @@ func (opts *secondaryRepoOptions) AddFlags(f *pflag.FlagSet, repoPrefix string, opts.PasswordCommand = os.Getenv("RESTIC_FROM_PASSWORD_COMMAND") } -func fillSecondaryGlobalOpts(ctx context.Context, opts secondaryRepoOptions, gopts GlobalOptions, repoPrefix string) (GlobalOptions, bool, error) { +func (opts *SecondaryRepoOptions) FillGlobalOpts(ctx context.Context, gopts Options, repoPrefix string) (Options, bool, error) { if opts.Repo == "" && opts.RepositoryFile == "" && opts.LegacyRepo == "" && opts.LegacyRepositoryFile == "" { - return GlobalOptions{}, false, errors.Fatal("Please specify a source repository location (--from-repo or --from-repository-file)") + return Options{}, false, errors.Fatal("Please specify a source repository location (--from-repo or --from-repository-file)") } hasFromRepo := opts.Repo != "" || opts.RepositoryFile != "" || opts.PasswordFile != "" || @@ -70,7 +70,7 @@ func fillSecondaryGlobalOpts(ctx context.Context, opts secondaryRepoOptions, gop opts.LegacyKeyHint != "" || opts.LegacyPasswordCommand != "" if hasFromRepo && hasRepo2 { - return GlobalOptions{}, false, errors.Fatal("Option groups repo2 and from-repo are mutually exclusive, please specify only one") + return Options{}, false, errors.Fatal("Option groups repo2 and from-repo are mutually exclusive, please specify only one") } var err error @@ -79,7 +79,7 @@ func fillSecondaryGlobalOpts(ctx context.Context, opts secondaryRepoOptions, gop if hasFromRepo { if opts.Repo != "" && opts.RepositoryFile != "" { - return GlobalOptions{}, false, errors.Fatal("Options --from-repo and --from-repository-file are mutually exclusive, please specify only one") + return Options{}, false, errors.Fatal("Options --from-repo and --from-repository-file are mutually exclusive, please specify only one") } dstGopts.Repo = opts.Repo @@ -93,7 +93,7 @@ func fillSecondaryGlobalOpts(ctx context.Context, opts secondaryRepoOptions, gop repoPrefix = "source" } else { if opts.LegacyRepo != "" && opts.LegacyRepositoryFile != "" { - return GlobalOptions{}, false, errors.Fatal("Options --repo2 and --repository-file2 are mutually exclusive, please specify only one") + return Options{}, false, errors.Fatal("Options --repo2 and --repository-file2 are mutually exclusive, please specify only one") } dstGopts.Repo = opts.LegacyRepo @@ -107,17 +107,17 @@ func fillSecondaryGlobalOpts(ctx context.Context, opts secondaryRepoOptions, gop pwdEnv = "RESTIC_PASSWORD2" } - if opts.password != "" { - dstGopts.password = opts.password + if opts.Password != "" { + dstGopts.Password = opts.Password } else { - dstGopts.password, err = resolvePassword(&dstGopts, pwdEnv) + dstGopts.Password, err = resolvePassword(&dstGopts, pwdEnv) if err != nil { - return GlobalOptions{}, false, err + return Options{}, false, err } } - dstGopts.password, err = ReadPassword(ctx, dstGopts, "enter password for "+repoPrefix+" repository: ") + dstGopts.Password, err = readPassword(ctx, dstGopts, "enter password for "+repoPrefix+" repository: ") if err != nil { - return GlobalOptions{}, false, err + return Options{}, false, err } return dstGopts, hasFromRepo, nil } diff --git a/cmd/restic/secondary_repo_test.go b/internal/global/secondary_repo_test.go similarity index 76% rename from cmd/restic/secondary_repo_test.go rename to internal/global/secondary_repo_test.go index c6837ada4..0ae94aef6 100644 --- a/cmd/restic/secondary_repo_test.go +++ b/internal/global/secondary_repo_test.go @@ -1,4 +1,4 @@ -package main +package global import ( "context" @@ -13,8 +13,8 @@ import ( func TestFillSecondaryGlobalOpts(t *testing.T) { //secondaryRepoTestCase defines a struct for test cases type secondaryRepoTestCase struct { - Opts secondaryRepoOptions - DstGOpts GlobalOptions + Opts SecondaryRepoOptions + DstGOpts Options FromRepo bool } @@ -22,74 +22,74 @@ func TestFillSecondaryGlobalOpts(t *testing.T) { var validSecondaryRepoTestCases = []secondaryRepoTestCase{ { // Test if Repo and Password are parsed correctly. - Opts: secondaryRepoOptions{ + Opts: SecondaryRepoOptions{ Repo: "backupDst", - password: "secretDst", + Password: "secretDst", }, - DstGOpts: GlobalOptions{ + DstGOpts: Options{ Repo: "backupDst", - password: "secretDst", + Password: "secretDst", }, FromRepo: true, }, { // Test if RepositoryFile and PasswordFile are parsed correctly. - Opts: secondaryRepoOptions{ + Opts: SecondaryRepoOptions{ RepositoryFile: "backupDst", PasswordFile: "passwordFileDst", }, - DstGOpts: GlobalOptions{ + DstGOpts: Options{ RepositoryFile: "backupDst", - password: "secretDst", + Password: "secretDst", PasswordFile: "passwordFileDst", }, FromRepo: true, }, { // Test if RepositoryFile and PasswordCommand are parsed correctly. - Opts: secondaryRepoOptions{ + Opts: SecondaryRepoOptions{ RepositoryFile: "backupDst", PasswordCommand: "echo secretDst", }, - DstGOpts: GlobalOptions{ + DstGOpts: Options{ RepositoryFile: "backupDst", - password: "secretDst", + Password: "secretDst", PasswordCommand: "echo secretDst", }, FromRepo: true, }, { // Test if LegacyRepo and Password are parsed correctly. - Opts: secondaryRepoOptions{ + Opts: SecondaryRepoOptions{ LegacyRepo: "backupDst", - password: "secretDst", + Password: "secretDst", }, - DstGOpts: GlobalOptions{ + DstGOpts: Options{ Repo: "backupDst", - password: "secretDst", + Password: "secretDst", }, }, { // Test if LegacyRepositoryFile and LegacyPasswordFile are parsed correctly. - Opts: secondaryRepoOptions{ + Opts: SecondaryRepoOptions{ LegacyRepositoryFile: "backupDst", LegacyPasswordFile: "passwordFileDst", }, - DstGOpts: GlobalOptions{ + DstGOpts: Options{ RepositoryFile: "backupDst", - password: "secretDst", + Password: "secretDst", PasswordFile: "passwordFileDst", }, }, { // Test if LegacyRepositoryFile and LegacyPasswordCommand are parsed correctly. - Opts: secondaryRepoOptions{ + Opts: SecondaryRepoOptions{ LegacyRepositoryFile: "backupDst", LegacyPasswordCommand: "echo secretDst", }, - DstGOpts: GlobalOptions{ + DstGOpts: Options{ RepositoryFile: "backupDst", - password: "secretDst", + Password: "secretDst", PasswordCommand: "echo secretDst", }, }, @@ -99,18 +99,18 @@ func TestFillSecondaryGlobalOpts(t *testing.T) { var invalidSecondaryRepoTestCases = []secondaryRepoTestCase{ { // Test must fail on no repo given. - Opts: secondaryRepoOptions{}, + Opts: SecondaryRepoOptions{}, }, { // Test must fail as Repo and RepositoryFile are both given - Opts: secondaryRepoOptions{ + Opts: SecondaryRepoOptions{ Repo: "backupDst", RepositoryFile: "backupDst", }, }, { // Test must fail as PasswordFile and PasswordCommand are both given - Opts: secondaryRepoOptions{ + Opts: SecondaryRepoOptions{ Repo: "backupDst", PasswordFile: "passwordFileDst", PasswordCommand: "notEmpty", @@ -118,28 +118,28 @@ func TestFillSecondaryGlobalOpts(t *testing.T) { }, { // Test must fail as PasswordFile does not exist - Opts: secondaryRepoOptions{ + Opts: SecondaryRepoOptions{ Repo: "backupDst", PasswordFile: "NonExistingFile", }, }, { // Test must fail as PasswordCommand does not exist - Opts: secondaryRepoOptions{ + Opts: SecondaryRepoOptions{ Repo: "backupDst", PasswordCommand: "notEmpty", }, }, { // Test must fail as current and legacy options are mixed - Opts: secondaryRepoOptions{ + Opts: SecondaryRepoOptions{ Repo: "backupDst", LegacyRepo: "backupDst", }, }, { // Test must fail as current and legacy options are mixed - Opts: secondaryRepoOptions{ + Opts: SecondaryRepoOptions{ Repo: "backupDst", LegacyPasswordCommand: "notEmpty", }, @@ -147,10 +147,10 @@ func TestFillSecondaryGlobalOpts(t *testing.T) { } //gOpts defines the Global options used in the secondary repository tests - var gOpts = GlobalOptions{ + var gOpts = Options{ Repo: "backupSrc", RepositoryFile: "backupSrc", - password: "secretSrc", + Password: "secretSrc", PasswordFile: "passwordFileSrc", } @@ -165,7 +165,7 @@ func TestFillSecondaryGlobalOpts(t *testing.T) { // Test all valid cases for _, testCase := range validSecondaryRepoTestCases { - DstGOpts, isFromRepo, err := fillSecondaryGlobalOpts(context.TODO(), testCase.Opts, gOpts, "destination") + DstGOpts, isFromRepo, err := testCase.Opts.FillGlobalOpts(context.TODO(), gOpts, "destination") rtest.OK(t, err) rtest.Equals(t, DstGOpts, testCase.DstGOpts) rtest.Equals(t, isFromRepo, testCase.FromRepo) @@ -173,7 +173,7 @@ func TestFillSecondaryGlobalOpts(t *testing.T) { // Test all invalid cases for _, testCase := range invalidSecondaryRepoTestCases { - _, _, err := fillSecondaryGlobalOpts(context.TODO(), testCase.Opts, gOpts, "destination") + _, _, err := testCase.Opts.FillGlobalOpts(context.TODO(), gOpts, "destination") rtest.Assert(t, err != nil, "Expected error, but function did not return an error") } }