mirror of
https://github.com/restic/restic.git
synced 2025-12-12 10:52:08 +00:00
deduplicate termstatus setup
This commit is contained in:
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
@@ -10,27 +11,27 @@ import (
|
||||
"github.com/restic/restic/internal/debug"
|
||||
)
|
||||
|
||||
func createGlobalContext() context.Context {
|
||||
func createGlobalContext(stderr io.Writer) context.Context {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
ch := make(chan os.Signal, 1)
|
||||
go cleanupHandler(ch, cancel)
|
||||
go cleanupHandler(ch, cancel, stderr)
|
||||
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
// cleanupHandler handles the SIGINT and SIGTERM signals.
|
||||
func cleanupHandler(c <-chan os.Signal, cancel context.CancelFunc) {
|
||||
func cleanupHandler(c <-chan os.Signal, cancel context.CancelFunc, stderr io.Writer) {
|
||||
s := <-c
|
||||
debug.Log("signal %v received, cleaning up", s)
|
||||
// ignore error as there's no good way to handle it
|
||||
_, _ = fmt.Fprintf(os.Stderr, "\rsignal %v received, cleaning up \n", s)
|
||||
_, _ = fmt.Fprintf(stderr, "\rsignal %v received, cleaning up \n", s)
|
||||
|
||||
if val, _ := os.LookupEnv("RESTIC_DEBUG_STACKTRACE_SIGINT"); val != "" {
|
||||
_, _ = os.Stderr.WriteString("\n--- STACKTRACE START ---\n\n")
|
||||
_, _ = os.Stderr.WriteString(debug.DumpStacktrace())
|
||||
_, _ = os.Stderr.WriteString("\n--- STACKTRACE END ---\n")
|
||||
_, _ = stderr.Write([]byte("\n--- STACKTRACE START ---\n\n"))
|
||||
_, _ = stderr.Write([]byte(debug.DumpStacktrace()))
|
||||
_, _ = stderr.Write([]byte("\n--- STACKTRACE END ---\n"))
|
||||
}
|
||||
|
||||
cancel()
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
"github.com/restic/restic/internal/ui/backup"
|
||||
)
|
||||
|
||||
func newBackupCommand() *cobra.Command {
|
||||
func newBackupCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts BackupOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -63,9 +63,7 @@ Exit status is 12 if the password is incorrect.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runBackup(cmd.Context(), opts, globalOptions, term, args)
|
||||
return runBackup(cmd.Context(), opts, *globalOptions, globalOptions.term, args)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,10 @@ import (
|
||||
"github.com/restic/restic/internal/fs"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func testRunBackupAssumeFailure(t testing.TB, dir string, target []string, opts BackupOptions, gopts GlobalOptions) error {
|
||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
t.Logf("backing up %v in %v", target, dir)
|
||||
if dir != "" {
|
||||
cleanup := rtest.Chdir(t, dir)
|
||||
@@ -25,7 +24,7 @@ func testRunBackupAssumeFailure(t testing.TB, dir string, target []string, opts
|
||||
}
|
||||
|
||||
opts.GroupBy = restic.SnapshotGroupByOptions{Host: true, Path: true}
|
||||
return runBackup(ctx, opts, gopts, term, target)
|
||||
return runBackup(ctx, opts, gopts, gopts.term, target)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newCacheCommand() *cobra.Command {
|
||||
func newCacheCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts CacheOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -34,9 +34,7 @@ Exit status is 1 if there was any error.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runCache(opts, globalOptions, args, term)
|
||||
return runCache(opts, *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
var catAllowedCmds = []string{"config", "index", "snapshot", "key", "masterkey", "lock", "pack", "blob", "tree"}
|
||||
|
||||
func newCatCommand() *cobra.Command {
|
||||
func newCatCommand(globalOptions *GlobalOptions) *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",
|
||||
@@ -34,9 +34,7 @@ Exit status is 12 if the password is incorrect.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runCat(cmd.Context(), globalOptions, args, term)
|
||||
return runCat(cmd.Context(), *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
ValidArgs: catAllowedCmds,
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"github.com/restic/restic/internal/ui/progress"
|
||||
)
|
||||
|
||||
func newCheckCommand() *cobra.Command {
|
||||
func newCheckCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts CheckOptions
|
||||
cmd := &cobra.Command{
|
||||
Use: "check [flags]",
|
||||
@@ -46,14 +46,12 @@ Exit status is 12 if the password is incorrect.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
summary, err := runCheck(cmd.Context(), opts, globalOptions, args, term)
|
||||
summary, err := runCheck(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||
if globalOptions.JSON {
|
||||
if err != nil && summary.NumErrors == 0 {
|
||||
summary.NumErrors = 1
|
||||
}
|
||||
term.Print(ui.ToJSONString(summary))
|
||||
globalOptions.term.Print(ui.ToJSONString(summary))
|
||||
}
|
||||
return err
|
||||
},
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"testing"
|
||||
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func testRunCheck(t testing.TB, gopts GlobalOptions) {
|
||||
@@ -25,12 +24,12 @@ func testRunCheckMustFail(t testing.TB, gopts GlobalOptions) {
|
||||
|
||||
func testRunCheckOutput(gopts GlobalOptions, checkUnused bool) (string, error) {
|
||||
buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error {
|
||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
opts := CheckOptions{
|
||||
ReadData: true,
|
||||
CheckUnused: checkUnused,
|
||||
}
|
||||
_, err := runCheck(context.TODO(), opts, gopts, nil, term)
|
||||
_, err := runCheck(context.TODO(), opts, gopts, nil, gopts.term)
|
||||
return err
|
||||
})
|
||||
})
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newCopyCommand() *cobra.Command {
|
||||
func newCopyCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts CopyOptions
|
||||
cmd := &cobra.Command{
|
||||
Use: "copy [flags] [snapshotID ...]",
|
||||
@@ -48,9 +48,7 @@ Exit status is 12 if the password is incorrect.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runCopy(cmd.Context(), opts, globalOptions, args, term)
|
||||
return runCopy(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"testing"
|
||||
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func testRunCopy(t testing.TB, srcGopts GlobalOptions, dstGopts GlobalOptions) {
|
||||
@@ -23,8 +22,8 @@ func testRunCopy(t testing.TB, srcGopts GlobalOptions, dstGopts GlobalOptions) {
|
||||
},
|
||||
}
|
||||
|
||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runCopy(context.TODO(), copyOpts, gopts, nil, term)
|
||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runCopy(context.TODO(), copyOpts, gopts, nil, gopts.term)
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -31,25 +31,25 @@ import (
|
||||
"github.com/restic/restic/internal/ui/progress"
|
||||
)
|
||||
|
||||
func registerDebugCommand(cmd *cobra.Command) {
|
||||
func registerDebugCommand(cmd *cobra.Command, globalOptions *GlobalOptions) {
|
||||
cmd.AddCommand(
|
||||
newDebugCommand(),
|
||||
newDebugCommand(globalOptions),
|
||||
)
|
||||
}
|
||||
|
||||
func newDebugCommand() *cobra.Command {
|
||||
func newDebugCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "debug",
|
||||
Short: "Debug commands",
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
cmd.AddCommand(newDebugDumpCommand())
|
||||
cmd.AddCommand(newDebugExamineCommand())
|
||||
cmd.AddCommand(newDebugDumpCommand(globalOptions))
|
||||
cmd.AddCommand(newDebugExamineCommand(globalOptions))
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newDebugDumpCommand() *cobra.Command {
|
||||
func newDebugDumpCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "dump [indexes|snapshots|all|packs]",
|
||||
Short: "Dump data structures",
|
||||
@@ -68,15 +68,13 @@ Exit status is 12 if the password is incorrect.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runDebugDump(cmd.Context(), globalOptions, args, term)
|
||||
return runDebugDump(cmd.Context(), *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newDebugExamineCommand() *cobra.Command {
|
||||
func newDebugExamineCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts DebugExamineOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -84,9 +82,7 @@ func newDebugExamineCommand() *cobra.Command {
|
||||
Short: "Examine a pack file",
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runDebugExamine(cmd.Context(), globalOptions, opts, args, term)
|
||||
return runDebugExamine(cmd.Context(), *globalOptions, opts, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,6 @@ package main
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
func registerDebugCommand(_ *cobra.Command) {
|
||||
func registerDebugCommand(_ *cobra.Command, _ *GlobalOptions) {
|
||||
// No commands to register in non-debug mode
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newDiffCommand() *cobra.Command {
|
||||
func newDiffCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts DiffOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -52,9 +52,7 @@ Exit status is 12 if the password is incorrect.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runDiff(cmd.Context(), opts, globalOptions, args, term)
|
||||
return runDiff(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"testing"
|
||||
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func testRunDiffOutput(gopts GlobalOptions, firstSnapshotID string, secondSnapshotID string) (string, error) {
|
||||
@@ -20,8 +19,8 @@ func testRunDiffOutput(gopts GlobalOptions, firstSnapshotID string, secondSnapsh
|
||||
opts := DiffOptions{
|
||||
ShowMetadata: false,
|
||||
}
|
||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runDiff(ctx, opts, gopts, []string{firstSnapshotID, secondSnapshotID}, term)
|
||||
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runDiff(ctx, opts, gopts, []string{firstSnapshotID, secondSnapshotID}, gopts.term)
|
||||
})
|
||||
})
|
||||
return buf.String(), err
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newDumpCommand() *cobra.Command {
|
||||
func newDumpCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts DumpOptions
|
||||
cmd := &cobra.Command{
|
||||
Use: "dump [flags] snapshotID file",
|
||||
@@ -47,9 +47,7 @@ Exit status is 12 if the password is incorrect.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runDump(cmd.Context(), opts, globalOptions, args, term)
|
||||
return runDump(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newFeaturesCommand() *cobra.Command {
|
||||
func newFeaturesCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "features",
|
||||
Short: "Print list of feature flags",
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"github.com/restic/restic/internal/walker"
|
||||
)
|
||||
|
||||
func newFindCommand() *cobra.Command {
|
||||
func newFindCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts FindOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -51,9 +51,7 @@ Exit status is 12 if the password is incorrect.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runFind(cmd.Context(), opts, globalOptions, args, term)
|
||||
return runFind(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -8,15 +8,14 @@ import (
|
||||
"time"
|
||||
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func testRunFind(t testing.TB, wantJSON bool, opts FindOptions, gopts GlobalOptions, pattern string) []byte {
|
||||
buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error {
|
||||
gopts.JSON = wantJSON
|
||||
|
||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runFind(ctx, opts, gopts, []string{pattern}, term)
|
||||
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runFind(ctx, opts, gopts, []string{pattern}, gopts.term)
|
||||
})
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newForgetCommand() *cobra.Command {
|
||||
func newForgetCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts ForgetOptions
|
||||
var pruneOpts PruneOptions
|
||||
|
||||
@@ -49,9 +49,7 @@ Exit status is 12 if the password is incorrect.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runForget(cmd.Context(), opts, pruneOpts, globalOptions, term, args)
|
||||
return runForget(cmd.Context(), opts, pruneOpts, *globalOptions, globalOptions.term, args)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -8,15 +8,14 @@ import (
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func testRunForgetMayFail(gopts GlobalOptions, opts ForgetOptions, args ...string) error {
|
||||
pruneOpts := PruneOptions{
|
||||
MaxUnused: "5%",
|
||||
}
|
||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runForget(context.TODO(), opts, pruneOpts, gopts, term, args)
|
||||
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runForget(context.TODO(), opts, pruneOpts, gopts, gopts.term, args)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newGenerateCommand() *cobra.Command {
|
||||
func newGenerateCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts generateOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -31,9 +31,7 @@ Exit status is 1 if there was any error.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runGenerate(opts, globalOptions, args, term)
|
||||
return runGenerate(opts, *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
opts.AddFlags(cmd.Flags())
|
||||
@@ -118,7 +116,7 @@ func runGenerate(opts generateOptions, gopts GlobalOptions, args []string, term
|
||||
}
|
||||
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term)
|
||||
cmdRoot := newRootCommand()
|
||||
cmdRoot := newRootCommand(&GlobalOptions{})
|
||||
|
||||
if opts.ManDir != "" {
|
||||
err := writeManpages(cmdRoot, opts.ManDir, printer)
|
||||
|
||||
@@ -6,13 +6,12 @@ import (
|
||||
"testing"
|
||||
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func testRunGenerate(gopts GlobalOptions, opts generateOptions) ([]byte, error) {
|
||||
buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error {
|
||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runGenerate(opts, gopts, []string{}, term)
|
||||
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runGenerate(opts, gopts, []string{}, gopts.term)
|
||||
})
|
||||
})
|
||||
return buf.Bytes(), err
|
||||
@@ -31,14 +30,14 @@ func TestGenerateStdout(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
output, err := testRunGenerate(globalOptions, tc.opts)
|
||||
output, err := testRunGenerate(GlobalOptions{}, 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(globalOptions, generateOptions{BashCompletionFile: "-", FishCompletionFile: "-"})
|
||||
_, err := testRunGenerate(GlobalOptions{}, generateOptions{BashCompletionFile: "-", FishCompletionFile: "-"})
|
||||
rtest.Assert(t, err != nil, "generate shell completions to stdout for two shells fails")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newInitCommand() *cobra.Command {
|
||||
func newInitCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts InitOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -35,9 +35,7 @@ Exit status is 1 if there was any error.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runInit(cmd.Context(), opts, globalOptions, args, term)
|
||||
return runInit(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
opts.AddFlags(cmd.Flags())
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"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"
|
||||
"github.com/restic/restic/internal/ui/progress"
|
||||
)
|
||||
|
||||
@@ -18,8 +17,8 @@ func testRunInit(t testing.TB, opts GlobalOptions) {
|
||||
restic.TestDisableCheckPolynomial(t)
|
||||
restic.TestSetLockTimeout(t, 0)
|
||||
|
||||
err := withTermStatus(opts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runInit(ctx, InitOptions{}, opts, nil, term)
|
||||
err := withTermStatus(opts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runInit(ctx, InitOptions{}, opts, nil, gopts.term)
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
t.Logf("repository initialized at %v", opts.Repo)
|
||||
@@ -44,14 +43,14 @@ func TestInitCopyChunkerParams(t *testing.T) {
|
||||
password: env2.gopts.password,
|
||||
},
|
||||
}
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runInit(ctx, initOpts, env.gopts, nil, term)
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) 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(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runInit(ctx, initOpts, env.gopts, nil, term)
|
||||
err = withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runInit(ctx, initOpts, gopts, nil, gopts.term)
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newKeyCommand() *cobra.Command {
|
||||
func newKeyCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "key",
|
||||
Short: "Manage keys (passwords)",
|
||||
@@ -17,10 +17,10 @@ per repository.
|
||||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
newKeyAddCommand(),
|
||||
newKeyListCommand(),
|
||||
newKeyPasswdCommand(),
|
||||
newKeyRemoveCommand(),
|
||||
newKeyAddCommand(globalOptions),
|
||||
newKeyListCommand(globalOptions),
|
||||
newKeyPasswdCommand(globalOptions),
|
||||
newKeyRemoveCommand(globalOptions),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newKeyAddCommand() *cobra.Command {
|
||||
func newKeyAddCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts KeyAddOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -32,9 +32,7 @@ Exit status is 12 if the password is incorrect.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runKeyAdd(cmd.Context(), globalOptions, opts, args, term)
|
||||
return runKeyAdd(cmd.Context(), *globalOptions, opts, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -12,14 +12,13 @@ import (
|
||||
"github.com/restic/restic/internal/backend"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
"github.com/restic/restic/internal/ui/progress"
|
||||
)
|
||||
|
||||
func testRunKeyListOtherIDs(t testing.TB, gopts GlobalOptions) []string {
|
||||
buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error {
|
||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runKeyList(ctx, gopts, []string{}, term)
|
||||
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runKeyList(ctx, gopts, []string{}, gopts.term)
|
||||
})
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
@@ -43,8 +42,8 @@ func testRunKeyAddNewKey(t testing.TB, newPassword string, gopts GlobalOptions)
|
||||
testKeyNewPassword = ""
|
||||
}()
|
||||
|
||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{}, term)
|
||||
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{}, gopts.term)
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
}
|
||||
@@ -56,11 +55,11 @@ func testRunKeyAddNewKeyUserHost(t testing.TB, gopts GlobalOptions) {
|
||||
}()
|
||||
|
||||
t.Log("adding key for john@example.com")
|
||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runKeyAdd(ctx, gopts, KeyAddOptions{
|
||||
Username: "john",
|
||||
Hostname: "example.com",
|
||||
}, []string{}, term)
|
||||
}, []string{}, gopts.term)
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
|
||||
@@ -79,8 +78,8 @@ func testRunKeyPasswd(t testing.TB, newPassword string, gopts GlobalOptions) {
|
||||
testKeyNewPassword = ""
|
||||
}()
|
||||
|
||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{}, term)
|
||||
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{}, gopts.term)
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
}
|
||||
@@ -88,8 +87,8 @@ func testRunKeyPasswd(t testing.TB, newPassword string, gopts GlobalOptions) {
|
||||
func testRunKeyRemove(t testing.TB, gopts GlobalOptions, IDs []string) {
|
||||
t.Logf("remove %d keys: %q\n", len(IDs), IDs)
|
||||
for _, id := range IDs {
|
||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runKeyRemove(ctx, gopts, []string{id}, term)
|
||||
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runKeyRemove(ctx, gopts, []string{id}, gopts.term)
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
}
|
||||
@@ -121,8 +120,8 @@ func TestKeyAddRemove(t *testing.T) {
|
||||
|
||||
env.gopts.password = passwordList[len(passwordList)-1]
|
||||
t.Logf("testing access with last password %q\n", env.gopts.password)
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runKeyList(ctx, env.gopts, []string{}, term)
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runKeyList(ctx, gopts, []string{}, gopts.term)
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
testRunCheck(t, env.gopts)
|
||||
@@ -135,21 +134,21 @@ func TestKeyAddInvalid(t *testing.T) {
|
||||
defer cleanup()
|
||||
testRunInit(t, env.gopts)
|
||||
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runKeyAdd(ctx, env.gopts, KeyAddOptions{
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runKeyAdd(ctx, gopts, KeyAddOptions{
|
||||
NewPasswordFile: "some-file",
|
||||
InsecureNoPassword: true,
|
||||
}, []string{}, 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(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runKeyAdd(ctx, env.gopts, KeyAddOptions{
|
||||
err = withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runKeyAdd(ctx, gopts, KeyAddOptions{
|
||||
NewPasswordFile: pwfile,
|
||||
}, []string{}, 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)
|
||||
}
|
||||
@@ -161,10 +160,10 @@ func TestKeyAddEmpty(t *testing.T) {
|
||||
defer cleanup()
|
||||
testRunInit(t, env.gopts)
|
||||
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runKeyAdd(ctx, env.gopts, KeyAddOptions{
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runKeyAdd(ctx, gopts, KeyAddOptions{
|
||||
InsecureNoPassword: true,
|
||||
}, []string{}, term)
|
||||
}, []string{}, gopts.term)
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
|
||||
@@ -196,21 +195,21 @@ func TestKeyProblems(t *testing.T) {
|
||||
testKeyNewPassword = ""
|
||||
}()
|
||||
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runKeyPasswd(ctx, env.gopts, KeyPasswdOptions{}, []string{}, term)
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{}, gopts.term)
|
||||
})
|
||||
t.Log(err)
|
||||
rtest.Assert(t, err != nil, "expected passwd change to fail")
|
||||
|
||||
err = withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runKeyAdd(ctx, env.gopts, KeyAddOptions{}, []string{}, term)
|
||||
err = withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) 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(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runKeyList(ctx, env.gopts, []string{}, term)
|
||||
err = withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runKeyList(ctx, gopts, []string{}, gopts.term)
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
testRunCheck(t, env.gopts)
|
||||
@@ -225,32 +224,32 @@ func TestKeyCommandInvalidArguments(t *testing.T) {
|
||||
return &emptySaveBackend{r}, nil
|
||||
}
|
||||
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runKeyAdd(ctx, env.gopts, KeyAddOptions{}, []string{"johndoe"}, term)
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) 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(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runKeyPasswd(ctx, env.gopts, KeyPasswdOptions{}, []string{"johndoe"}, term)
|
||||
err = withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) 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(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runKeyList(ctx, env.gopts, []string{"johndoe"}, term)
|
||||
err = withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) 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(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runKeyRemove(ctx, env.gopts, []string{}, term)
|
||||
err = withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) 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(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runKeyRemove(ctx, env.gopts, []string{"john", "doe"}, term)
|
||||
err = withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) 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)
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newKeyListCommand() *cobra.Command {
|
||||
func newKeyListCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List keys (passwords)",
|
||||
@@ -34,9 +34,7 @@ Exit status is 12 if the password is incorrect.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runKeyList(cmd.Context(), globalOptions, args, term)
|
||||
return runKeyList(cmd.Context(), *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newKeyPasswdCommand() *cobra.Command {
|
||||
func newKeyPasswdCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts KeyPasswdOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -33,9 +33,7 @@ Exit status is 12 if the password is incorrect.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runKeyPasswd(cmd.Context(), globalOptions, opts, args, term)
|
||||
return runKeyPasswd(cmd.Context(), *globalOptions, opts, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newKeyRemoveCommand() *cobra.Command {
|
||||
func newKeyRemoveCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "remove [ID]",
|
||||
Short: "Remove key ID (password) from the repository.",
|
||||
@@ -31,9 +31,7 @@ Exit status is 12 if the password is incorrect.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runKeyRemove(cmd.Context(), globalOptions, args, term)
|
||||
return runKeyRemove(cmd.Context(), *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newListCommand() *cobra.Command {
|
||||
func newListCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var listAllowedArgs = []string{"blobs", "packs", "index", "snapshots", "keys", "locks"}
|
||||
var listAllowedArgsUseString = strings.Join(listAllowedArgs, "|")
|
||||
|
||||
@@ -34,9 +34,7 @@ Exit status is 12 if the password is incorrect.
|
||||
DisableAutoGenTag: true,
|
||||
GroupID: cmdGroupDefault,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runList(cmd.Context(), globalOptions, args, term)
|
||||
return runList(cmd.Context(), *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
ValidArgs: listAllowedArgs,
|
||||
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
|
||||
|
||||
@@ -8,13 +8,12 @@ import (
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func testRunList(t testing.TB, opts GlobalOptions, tpe string) restic.IDs {
|
||||
buf, err := withCaptureStdout(opts, func(opts GlobalOptions) error {
|
||||
return withTermStatus(opts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runList(ctx, opts, []string{tpe}, term)
|
||||
return withTermStatus(opts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runList(ctx, opts, []string{tpe}, gopts.term)
|
||||
})
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"github.com/restic/restic/internal/walker"
|
||||
)
|
||||
|
||||
func newLsCommand() *cobra.Command {
|
||||
func newLsCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts LsOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -60,9 +60,7 @@ Exit status is 12 if the password is incorrect.
|
||||
DisableAutoGenTag: true,
|
||||
GroupID: cmdGroupDefault,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runLs(cmd.Context(), opts, globalOptions, args, term)
|
||||
return runLs(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
opts.AddFlags(cmd.Flags())
|
||||
|
||||
@@ -10,14 +10,13 @@ import (
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func testRunLsWithOpts(t testing.TB, gopts GlobalOptions, opts LsOptions, args []string) []byte {
|
||||
buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error {
|
||||
gopts.Quiet = true
|
||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runLs(context.TODO(), opts, gopts, args, term)
|
||||
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runLs(context.TODO(), opts, gopts, args, gopts.term)
|
||||
})
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newMigrateCommand() *cobra.Command {
|
||||
func newMigrateCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts MigrateOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -35,9 +35,7 @@ Exit status is 12 if the password is incorrect.
|
||||
DisableAutoGenTag: true,
|
||||
GroupID: cmdGroupDefault,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runMigrate(cmd.Context(), opts, globalOptions, args, term)
|
||||
return runMigrate(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@ import (
|
||||
"github.com/anacrolix/fuse/fs"
|
||||
)
|
||||
|
||||
func registerMountCommand(cmdRoot *cobra.Command) {
|
||||
cmdRoot.AddCommand(newMountCommand())
|
||||
func registerMountCommand(cmdRoot *cobra.Command, globalOptions *GlobalOptions) {
|
||||
cmdRoot.AddCommand(newMountCommand(globalOptions))
|
||||
}
|
||||
|
||||
func newMountCommand() *cobra.Command {
|
||||
func newMountCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts MountOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -82,9 +82,7 @@ Exit status is 12 if the password is incorrect.
|
||||
DisableAutoGenTag: true,
|
||||
GroupID: cmdGroupDefault,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runMount(cmd.Context(), opts, globalOptions, args, term)
|
||||
return runMount(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,6 @@ package main
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
func registerMountCommand(_ *cobra.Command) {
|
||||
func registerMountCommand(_ *cobra.Command, _ *GlobalOptions) {
|
||||
// Mount command not supported on these platforms
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/restic/restic/internal/debug"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -62,8 +61,8 @@ func testRunMount(t testing.TB, gopts GlobalOptions, dir string, wg *sync.WaitGr
|
||||
opts := MountOptions{
|
||||
TimeTemplate: time.RFC3339,
|
||||
}
|
||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runMount(context.TODO(), opts, gopts, []string{dir}, term)
|
||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runMount(context.TODO(), opts, gopts, []string{dir}, gopts.term)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -128,8 +127,8 @@ func checkSnapshots(t testing.TB, gopts GlobalOptions, mountpoint string, snapsh
|
||||
}
|
||||
}
|
||||
|
||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term)
|
||||
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||
_, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newOptionsCommand() *cobra.Command {
|
||||
func newOptionsCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "options",
|
||||
Short: "Print list of extended options",
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newPruneCommand() *cobra.Command {
|
||||
func newPruneCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts PruneOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -40,9 +40,7 @@ Exit status is 12 if the password is incorrect.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runPrune(cmd.Context(), opts, globalOptions, term)
|
||||
return runPrune(cmd.Context(), opts, *globalOptions, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/restic/restic/internal/backend"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func testRunPrune(t testing.TB, gopts GlobalOptions, opts PruneOptions) {
|
||||
@@ -29,8 +28,8 @@ func testRunPruneOutput(gopts GlobalOptions, opts PruneOptions) error {
|
||||
defer func() {
|
||||
gopts.backendTestHook = oldHook
|
||||
}()
|
||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runPrune(context.TODO(), opts, gopts, term)
|
||||
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runPrune(context.TODO(), opts, gopts, gopts.term)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -99,8 +98,8 @@ func testRunForgetJSON(t testing.TB, gopts GlobalOptions, args ...string) {
|
||||
pruneOpts := PruneOptions{
|
||||
MaxUnused: "5%",
|
||||
}
|
||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runForget(context.TODO(), opts, pruneOpts, gopts, term, args)
|
||||
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runForget(context.TODO(), opts, pruneOpts, gopts, gopts.term, args)
|
||||
})
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
@@ -122,8 +121,8 @@ func testPrune(t *testing.T, pruneOpts PruneOptions, checkOpts CheckOptions) {
|
||||
|
||||
createPrunableRepo(t, env)
|
||||
testRunPrune(t, env.gopts, pruneOpts)
|
||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
_, err := runCheck(context.TODO(), checkOpts, env.gopts, nil, term)
|
||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
_, err := runCheck(context.TODO(), checkOpts, gopts, nil, gopts.term)
|
||||
return err
|
||||
}))
|
||||
}
|
||||
@@ -158,8 +157,8 @@ func TestPruneWithDamagedRepository(t *testing.T) {
|
||||
env.gopts.backendTestHook = oldHook
|
||||
}()
|
||||
// prune should fail
|
||||
rtest.Equals(t, repository.ErrPacksMissing, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runPrune(context.TODO(), pruneDefaultOptions, env.gopts, term)
|
||||
rtest.Equals(t, repository.ErrPacksMissing, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runPrune(context.TODO(), pruneDefaultOptions, gopts, gopts.term)
|
||||
}), "prune should have reported index not complete error")
|
||||
}
|
||||
|
||||
@@ -231,8 +230,8 @@ func testEdgeCaseRepo(t *testing.T, tarfile string, optionsCheck CheckOptions, o
|
||||
if checkOK {
|
||||
testRunCheck(t, env.gopts)
|
||||
} else {
|
||||
rtest.Assert(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
_, err := runCheck(context.TODO(), optionsCheck, env.gopts, nil, term)
|
||||
rtest.Assert(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
_, err := runCheck(context.TODO(), optionsCheck, gopts, nil, gopts.term)
|
||||
return err
|
||||
}) != nil,
|
||||
"check should have reported an error")
|
||||
@@ -242,8 +241,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(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runPrune(context.TODO(), optionsPrune, env.gopts, term)
|
||||
rtest.Assert(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runPrune(context.TODO(), optionsPrune, gopts, gopts.term)
|
||||
}) != nil,
|
||||
"prune should have reported an error")
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func newRecoverCommand() *cobra.Command {
|
||||
func newRecoverCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "recover [flags]",
|
||||
Short: "Recover data from the repository not referenced by snapshots",
|
||||
@@ -35,9 +35,7 @@ Exit status is 12 if the password is incorrect.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runRecover(cmd.Context(), globalOptions, term)
|
||||
return runRecover(cmd.Context(), *globalOptions, globalOptions.term)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
|
||||
@@ -5,12 +5,11 @@ import (
|
||||
"testing"
|
||||
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func testRunRecover(t testing.TB, gopts GlobalOptions) {
|
||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runRecover(context.TODO(), gopts, term)
|
||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runRecover(context.TODO(), gopts, gopts.term)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -33,7 +32,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(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runCat(context.TODO(), env.gopts, []string{"tree", ids[0].String() + ":" + sn.Tree.Str()}, term)
|
||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runCat(context.TODO(), gopts, []string{"tree", ids[0].String() + ":" + sn.Tree.Str()}, gopts.term)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newRepairCommand() *cobra.Command {
|
||||
func newRepairCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "repair",
|
||||
Short: "Repair the repository",
|
||||
@@ -13,9 +13,9 @@ func newRepairCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
newRepairIndexCommand(),
|
||||
newRepairPacksCommand(),
|
||||
newRepairSnapshotsCommand(),
|
||||
newRepairIndexCommand(globalOptions),
|
||||
newRepairPacksCommand(globalOptions),
|
||||
newRepairSnapshotsCommand(globalOptions),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newRepairIndexCommand() *cobra.Command {
|
||||
func newRepairIndexCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts RepairIndexOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -30,9 +30,7 @@ Exit status is 12 if the password is incorrect.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runRebuildIndex(cmd.Context(), opts, globalOptions, term)
|
||||
return runRebuildIndex(cmd.Context(), opts, *globalOptions, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -49,10 +47,10 @@ 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() *cobra.Command {
|
||||
func newRebuildIndexCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts RepairIndexOptions
|
||||
|
||||
replacement := newRepairIndexCommand()
|
||||
replacement := newRepairIndexCommand(globalOptions)
|
||||
cmd := &cobra.Command{
|
||||
Use: "rebuild-index [flags]",
|
||||
Short: replacement.Short,
|
||||
@@ -62,9 +60,7 @@ func newRebuildIndexCommand() *cobra.Command {
|
||||
// must create a new instance of the run function as it captures opts
|
||||
// by reference
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runRebuildIndex(cmd.Context(), opts, globalOptions, term)
|
||||
return runRebuildIndex(cmd.Context(), opts, *globalOptions, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -13,15 +13,12 @@ import (
|
||||
"github.com/restic/restic/internal/repository/index"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func testRunRebuildIndex(t testing.TB, gopts GlobalOptions) {
|
||||
rtest.OK(t, withRestoreGlobalOptions(func() error {
|
||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
gopts.stdout = io.Discard
|
||||
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, term)
|
||||
})
|
||||
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.term)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -128,14 +125,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)
|
||||
|
||||
err := withRestoreGlobalOptions(func() error {
|
||||
env.gopts.backendTestHook = func(r backend.Backend) (backend.Backend, error) {
|
||||
return &appendOnlyBackend{r}, nil
|
||||
}
|
||||
return withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
env.gopts.stdout = io.Discard
|
||||
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, env.gopts, term)
|
||||
})
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
gopts.stdout = io.Discard
|
||||
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.term)
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newRepairPacksCommand() *cobra.Command {
|
||||
func newRepairPacksCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "packs [packIDs...]",
|
||||
Short: "Salvage damaged pack files",
|
||||
@@ -32,9 +32,7 @@ Exit status is 12 if the password is incorrect.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runRepairPacks(cmd.Context(), globalOptions, term, args)
|
||||
return runRepairPacks(cmd.Context(), *globalOptions, globalOptions.term, args)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newRepairSnapshotsCommand() *cobra.Command {
|
||||
func newRepairSnapshotsCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts RepairOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -50,9 +50,7 @@ Exit status is 12 if the password is incorrect.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runRepairSnapshots(cmd.Context(), globalOptions, opts, args, term)
|
||||
return runRepairSnapshots(cmd.Context(), *globalOptions, opts, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func testRunRepairSnapshot(t testing.TB, gopts GlobalOptions, forget bool) {
|
||||
@@ -20,8 +19,8 @@ func testRunRepairSnapshot(t testing.TB, gopts GlobalOptions, forget bool) {
|
||||
Forget: forget,
|
||||
}
|
||||
|
||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runRepairSnapshots(context.TODO(), gopts, opts, nil, term)
|
||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runRepairSnapshots(context.TODO(), gopts, opts, nil, gopts.term)
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newRestoreCommand() *cobra.Command {
|
||||
func newRestoreCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts RestoreOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -46,9 +46,7 @@ Exit status is 12 if the password is incorrect.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runRestore(cmd.Context(), opts, globalOptions, term, args)
|
||||
return runRestore(cmd.Context(), opts, *globalOptions, globalOptions.term, args)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func testRunRestore(t testing.TB, opts GlobalOptions, dir string, snapshotID string) {
|
||||
@@ -31,8 +30,8 @@ func testRunRestoreExcludes(t testing.TB, gopts GlobalOptions, dir string, snaps
|
||||
}
|
||||
|
||||
func testRunRestoreAssumeFailure(snapshotID string, opts RestoreOptions, gopts GlobalOptions) error {
|
||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runRestore(ctx, opts, gopts, term, []string{snapshotID})
|
||||
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runRestore(ctx, opts, gopts, gopts.term, []string{snapshotID})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -337,11 +336,8 @@ func TestRestoreWithPermissionFailure(t *testing.T) {
|
||||
|
||||
snapshots := testListSnapshots(t, env.gopts, 1)
|
||||
|
||||
_ = withRestoreGlobalOptions(func() error {
|
||||
env.gopts.stderr = io.Discard
|
||||
testRunRestore(t, env.gopts, filepath.Join(env.base, "restore"), snapshots[0].String())
|
||||
return nil
|
||||
})
|
||||
|
||||
// make sure that all files have been restored, regardless of any
|
||||
// permission errors
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"github.com/restic/restic/internal/walker"
|
||||
)
|
||||
|
||||
func newRewriteCommand() *cobra.Command {
|
||||
func newRewriteCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts RewriteOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -60,9 +60,7 @@ Exit status is 12 if the password is incorrect.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runRewrite(cmd.Context(), opts, globalOptions, args, term)
|
||||
return runRewrite(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ func testRunRewriteExclude(t testing.TB, gopts GlobalOptions, excludes []string,
|
||||
Metadata: metadata,
|
||||
}
|
||||
|
||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runRewrite(context.TODO(), opts, gopts, nil, term)
|
||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runRewrite(context.TODO(), opts, gopts, nil, gopts.term)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -41,9 +41,9 @@ func getSnapshot(t testing.TB, snapshotID restic.ID, env *testEnvironment) *rest
|
||||
t.Helper()
|
||||
|
||||
var snapshots []*restic.Snapshot
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
printer := newTerminalProgressPrinter(env.gopts.JSON, env.gopts.verbosity, term)
|
||||
ctx, repo, unlock, err := openWithReadLock(ctx, env.gopts, false, printer)
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
||||
rtest.OK(t, err)
|
||||
defer unlock()
|
||||
|
||||
@@ -117,9 +117,9 @@ func testRewriteMetadata(t *testing.T, metadata snapshotMetadataArgs) {
|
||||
testRunRewriteExclude(t, env.gopts, []string{}, true, metadata)
|
||||
|
||||
var snapshots []*restic.Snapshot
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
printer := newTerminalProgressPrinter(env.gopts.JSON, env.gopts.verbosity, term)
|
||||
ctx, repo, unlock, err := openWithReadLock(ctx, env.gopts, false, printer)
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
||||
rtest.OK(t, err)
|
||||
defer unlock()
|
||||
|
||||
@@ -157,17 +157,17 @@ func TestRewriteSnaphotSummary(t *testing.T) {
|
||||
defer cleanup()
|
||||
createBasicRewriteRepo(t, env)
|
||||
|
||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runRewrite(context.TODO(), RewriteOptions{SnapshotSummary: true}, env.gopts, []string{}, term)
|
||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) 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 *restic.SnapshotSummary
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
printer := newTerminalProgressPrinter(env.gopts.JSON, env.gopts.verbosity, term)
|
||||
_, repo, unlock, err := openWithExclusiveLock(ctx, env.gopts, false, printer)
|
||||
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||
_, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
|
||||
rtest.OK(t, err)
|
||||
defer unlock()
|
||||
|
||||
@@ -182,8 +182,8 @@ func TestRewriteSnaphotSummary(t *testing.T) {
|
||||
rtest.OK(t, err)
|
||||
|
||||
// rewrite snapshot and lookup ID of new snapshot
|
||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runRewrite(context.TODO(), RewriteOptions{SnapshotSummary: true}, env.gopts, []string{}, term)
|
||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) 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]
|
||||
|
||||
@@ -14,13 +14,13 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func registerSelfUpdateCommand(cmd *cobra.Command) {
|
||||
func registerSelfUpdateCommand(cmd *cobra.Command, globalOptions *GlobalOptions) {
|
||||
cmd.AddCommand(
|
||||
newSelfUpdateCommand(),
|
||||
newSelfUpdateCommand(globalOptions),
|
||||
)
|
||||
}
|
||||
|
||||
func newSelfUpdateCommand() *cobra.Command {
|
||||
func newSelfUpdateCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts SelfUpdateOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -43,9 +43,7 @@ Exit status is 12 if the password is incorrect.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runSelfUpdate(cmd.Context(), opts, globalOptions, args, term)
|
||||
return runSelfUpdate(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,6 @@ package main
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
func registerSelfUpdateCommand(_ *cobra.Command) {
|
||||
func registerSelfUpdateCommand(_ *cobra.Command, _ *GlobalOptions) {
|
||||
// No commands to register in non-selfupdate mode
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newSnapshotsCommand() *cobra.Command {
|
||||
func newSnapshotsCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts SnapshotOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -36,9 +36,7 @@ Exit status is 12 if the password is incorrect.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runSnapshots(cmd.Context(), opts, globalOptions, args, term)
|
||||
return runSnapshots(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func testRunSnapshots(t testing.TB, gopts GlobalOptions) (newest *Snapshot, snapmap map[restic.ID]Snapshot) {
|
||||
@@ -15,8 +14,8 @@ func testRunSnapshots(t testing.TB, gopts GlobalOptions) (newest *Snapshot, snap
|
||||
gopts.JSON = true
|
||||
|
||||
opts := SnapshotOptions{}
|
||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runSnapshots(ctx, opts, gopts, []string{}, term)
|
||||
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runSnapshots(ctx, opts, gopts, []string{}, gopts.term)
|
||||
})
|
||||
})
|
||||
rtest.OK(t, err)
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newStatsCommand() *cobra.Command {
|
||||
func newStatsCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts StatsOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -63,9 +63,7 @@ Exit status is 12 if the password is incorrect.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runStats(cmd.Context(), opts, globalOptions, args, term)
|
||||
return runStats(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func newTagCommand() *cobra.Command {
|
||||
func newTagCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts TagOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -39,9 +39,7 @@ Exit status is 12 if the password is incorrect.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runTag(cmd.Context(), opts, globalOptions, term, args)
|
||||
return runTag(cmd.Context(), opts, *globalOptions, globalOptions.term, args)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -6,12 +6,11 @@ import (
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func testRunTag(t testing.TB, opts TagOptions, gopts GlobalOptions) {
|
||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runTag(context.TODO(), opts, gopts, term, []string{})
|
||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runTag(context.TODO(), opts, gopts, gopts.term, []string{})
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func newUnlockCommand() *cobra.Command {
|
||||
func newUnlockCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
var opts UnlockOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -27,9 +27,7 @@ Exit status is 1 if there was any error.
|
||||
GroupID: cmdGroupDefault,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
return runUnlock(cmd.Context(), opts, globalOptions, term)
|
||||
return runUnlock(cmd.Context(), opts, *globalOptions, globalOptions.term)
|
||||
},
|
||||
}
|
||||
opts.AddFlags(cmd.Flags())
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newVersionCommand() *cobra.Command {
|
||||
func newVersionCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print version information",
|
||||
@@ -23,9 +23,7 @@ Exit status is 1 if there was any error.
|
||||
`,
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
term, cancel := setupTermstatus()
|
||||
defer cancel()
|
||||
printer := newTerminalProgressPrinter(globalOptions.JSON, globalOptions.verbosity, term)
|
||||
printer := newTerminalProgressPrinter(globalOptions.JSON, globalOptions.verbosity, globalOptions.term)
|
||||
|
||||
if globalOptions.JSON {
|
||||
type jsonVersion struct {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
// 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().Commands() {
|
||||
for _, cmd := range newRootCommand(&GlobalOptions{}).Commands() {
|
||||
t.Run(cmd.Name(), func(t *testing.T) {
|
||||
cmd.Flags().SetOutput(io.Discard)
|
||||
err := cmd.ParseFlags([]string{"--help"})
|
||||
|
||||
@@ -34,6 +34,7 @@ import (
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/terminal"
|
||||
"github.com/restic/restic/internal/textfile"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
"github.com/restic/restic/internal/ui/progress"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
@@ -77,6 +78,7 @@ type GlobalOptions struct {
|
||||
password string
|
||||
stdout io.Writer
|
||||
stderr io.Writer
|
||||
term ui.Terminal
|
||||
|
||||
backends *location.Registry
|
||||
backendTestHook, backendInnerTestHook backendWrapper
|
||||
@@ -177,12 +179,6 @@ func (opts *GlobalOptions) PreRun(needsPassword bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var globalOptions = GlobalOptions{
|
||||
stdout: os.Stdout,
|
||||
stderr: os.Stderr,
|
||||
backends: collectBackends(),
|
||||
}
|
||||
|
||||
func collectBackends() *location.Registry {
|
||||
backends := location.NewRegistry()
|
||||
backends.Register(azure.NewFactory())
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"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"
|
||||
"github.com/restic/restic/internal/ui/termstatus"
|
||||
)
|
||||
|
||||
@@ -222,12 +221,9 @@ func withTestEnvironment(t testing.TB) (env *testEnvironment, cleanup func()) {
|
||||
// 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 },
|
||||
// start with default set of backends
|
||||
backends: globalOptions.backends,
|
||||
backends: collectBackends(),
|
||||
}
|
||||
|
||||
// always overwrite global options
|
||||
globalOptions = env.gopts
|
||||
|
||||
cleanup = func() {
|
||||
if !rtest.TestCleanupTempDirs {
|
||||
t.Logf("leaving temporary directory %v used for test", tempdir)
|
||||
@@ -248,8 +244,8 @@ func testSetupBackupData(t testing.TB, env *testEnvironment) string {
|
||||
|
||||
func listPacks(gopts GlobalOptions, t *testing.T) restic.IDSet {
|
||||
var packs restic.IDSet
|
||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term)
|
||||
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||
ctx, r, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
||||
rtest.OK(t, err)
|
||||
defer unlock()
|
||||
@@ -267,8 +263,8 @@ func listPacks(gopts GlobalOptions, t *testing.T) restic.IDSet {
|
||||
|
||||
func listTreePacks(gopts GlobalOptions, t *testing.T) restic.IDSet {
|
||||
var treePacks restic.IDSet
|
||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term)
|
||||
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||
ctx, r, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
||||
rtest.OK(t, err)
|
||||
defer unlock()
|
||||
@@ -298,8 +294,8 @@ func captureBackend(gopts *GlobalOptions) func() backend.Backend {
|
||||
|
||||
func removePacks(gopts GlobalOptions, t testing.TB, remove restic.IDSet) {
|
||||
be := captureBackend(&gopts)
|
||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term)
|
||||
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||
ctx, _, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
|
||||
rtest.OK(t, err)
|
||||
defer unlock()
|
||||
@@ -314,8 +310,8 @@ func removePacks(gopts GlobalOptions, t testing.TB, remove restic.IDSet) {
|
||||
|
||||
func removePacksExcept(gopts GlobalOptions, t testing.TB, keep restic.IDSet, removeTreePacks bool) {
|
||||
be := captureBackend(&gopts)
|
||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term)
|
||||
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||
ctx, r, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
|
||||
rtest.OK(t, err)
|
||||
defer unlock()
|
||||
@@ -375,8 +371,8 @@ func lastSnapshot(old, new map[string]struct{}) (map[string]struct{}, string) {
|
||||
|
||||
func testLoadSnapshot(t testing.TB, gopts GlobalOptions, id restic.ID) *restic.Snapshot {
|
||||
var snapshot *restic.Snapshot
|
||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term)
|
||||
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||
_, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
||||
rtest.OK(t, err)
|
||||
defer unlock()
|
||||
@@ -420,30 +416,19 @@ func testFileSize(filename string, size int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func withRestoreGlobalOptions(inner func() error) error {
|
||||
gopts := globalOptions
|
||||
defer func() {
|
||||
globalOptions = gopts
|
||||
}()
|
||||
return inner()
|
||||
}
|
||||
|
||||
func withCaptureStdout(gopts GlobalOptions, inner func(gopts GlobalOptions) error) (*bytes.Buffer, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
err := withRestoreGlobalOptions(func() error {
|
||||
globalOptions.stdout = buf
|
||||
gopts.stdout = buf
|
||||
return inner(gopts)
|
||||
})
|
||||
|
||||
err := inner(gopts)
|
||||
return buf, err
|
||||
}
|
||||
|
||||
func withTermStatus(gopts GlobalOptions, callback func(ctx context.Context, term ui.Terminal) error) error {
|
||||
func withTermStatus(gopts GlobalOptions, callback func(ctx context.Context, gopts GlobalOptions) error) error {
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
var wg sync.WaitGroup
|
||||
|
||||
term := termstatus.New(gopts.stdout, gopts.stderr, gopts.Quiet)
|
||||
gopts.term = term
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
@@ -453,5 +438,5 @@ func withTermStatus(gopts GlobalOptions, callback func(ctx context.Context, term
|
||||
defer wg.Wait()
|
||||
defer cancel()
|
||||
|
||||
return callback(ctx, term)
|
||||
return callback(ctx, gopts)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
)
|
||||
|
||||
func TestCheckRestoreNoLock(t *testing.T) {
|
||||
@@ -87,15 +86,15 @@ func TestListOnce(t *testing.T) {
|
||||
|
||||
createPrunableRepo(t, env)
|
||||
testRunPrune(t, env.gopts, pruneOpts)
|
||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
_, err := runCheck(context.TODO(), checkOpts, env.gopts, nil, term)
|
||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
_, err := runCheck(context.TODO(), checkOpts, gopts, nil, gopts.term)
|
||||
return err
|
||||
}))
|
||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, env.gopts, term)
|
||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.term)
|
||||
}))
|
||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
return runRebuildIndex(context.TODO(), RepairIndexOptions{ReadAllPacks: true}, env.gopts, term)
|
||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
return runRebuildIndex(context.TODO(), RepairIndexOptions{ReadAllPacks: true}, gopts, gopts.term)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -162,9 +161,9 @@ func TestFindListOnce(t *testing.T) {
|
||||
thirdSnapshot := restic.NewIDSet(testListSnapshots(t, env.gopts, 3)...)
|
||||
|
||||
var snapshotIDs restic.IDSet
|
||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||
printer := newTerminalProgressPrinter(env.gopts.JSON, env.gopts.verbosity, term)
|
||||
ctx, repo, unlock, err := openWithReadLock(ctx, env.gopts, false, printer)
|
||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
||||
rtest.OK(t, err)
|
||||
defer unlock()
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/restic/restic/internal/feature"
|
||||
"github.com/restic/restic/internal/repository"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/ui/termstatus"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -31,7 +32,7 @@ var ErrOK = errors.New("ok")
|
||||
var cmdGroupDefault = "default"
|
||||
var cmdGroupAdvanced = "advanced"
|
||||
|
||||
func newRootCommand() *cobra.Command {
|
||||
func newRootCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "restic",
|
||||
Short: "Backup and restore files",
|
||||
@@ -66,40 +67,41 @@ The full documentation can be found at https://restic.readthedocs.io/ .
|
||||
// Use our "generate" command instead of the cobra provided "completion" command
|
||||
cmd.CompletionOptions.DisableDefaultCmd = true
|
||||
|
||||
// globalOptions is passed to commands by reference to allow PersistentPreRunE to modify it
|
||||
cmd.AddCommand(
|
||||
newBackupCommand(),
|
||||
newCacheCommand(),
|
||||
newCatCommand(),
|
||||
newCheckCommand(),
|
||||
newCopyCommand(),
|
||||
newDiffCommand(),
|
||||
newDumpCommand(),
|
||||
newFeaturesCommand(),
|
||||
newFindCommand(),
|
||||
newForgetCommand(),
|
||||
newGenerateCommand(),
|
||||
newInitCommand(),
|
||||
newKeyCommand(),
|
||||
newListCommand(),
|
||||
newLsCommand(),
|
||||
newMigrateCommand(),
|
||||
newOptionsCommand(),
|
||||
newPruneCommand(),
|
||||
newRebuildIndexCommand(),
|
||||
newRecoverCommand(),
|
||||
newRepairCommand(),
|
||||
newRestoreCommand(),
|
||||
newRewriteCommand(),
|
||||
newSnapshotsCommand(),
|
||||
newStatsCommand(),
|
||||
newTagCommand(),
|
||||
newUnlockCommand(),
|
||||
newVersionCommand(),
|
||||
newBackupCommand(globalOptions),
|
||||
newCacheCommand(globalOptions),
|
||||
newCatCommand(globalOptions),
|
||||
newCheckCommand(globalOptions),
|
||||
newCopyCommand(globalOptions),
|
||||
newDiffCommand(globalOptions),
|
||||
newDumpCommand(globalOptions),
|
||||
newFeaturesCommand(globalOptions),
|
||||
newFindCommand(globalOptions),
|
||||
newForgetCommand(globalOptions),
|
||||
newGenerateCommand(globalOptions),
|
||||
newInitCommand(globalOptions),
|
||||
newKeyCommand(globalOptions),
|
||||
newListCommand(globalOptions),
|
||||
newLsCommand(globalOptions),
|
||||
newMigrateCommand(globalOptions),
|
||||
newOptionsCommand(globalOptions),
|
||||
newPruneCommand(globalOptions),
|
||||
newRebuildIndexCommand(globalOptions),
|
||||
newRecoverCommand(globalOptions),
|
||||
newRepairCommand(globalOptions),
|
||||
newRestoreCommand(globalOptions),
|
||||
newRewriteCommand(globalOptions),
|
||||
newSnapshotsCommand(globalOptions),
|
||||
newStatsCommand(globalOptions),
|
||||
newTagCommand(globalOptions),
|
||||
newUnlockCommand(globalOptions),
|
||||
newVersionCommand(globalOptions),
|
||||
)
|
||||
|
||||
registerDebugCommand(cmd)
|
||||
registerMountCommand(cmd)
|
||||
registerSelfUpdateCommand(cmd)
|
||||
registerDebugCommand(cmd, globalOptions)
|
||||
registerMountCommand(cmd, globalOptions)
|
||||
registerSelfUpdateCommand(cmd, globalOptions)
|
||||
registerProfiling(cmd, os.Stderr)
|
||||
|
||||
return cmd
|
||||
@@ -125,7 +127,7 @@ func tweakGoGC() {
|
||||
}
|
||||
}
|
||||
|
||||
func printExitError(code int, message string) {
|
||||
func printExitError(globalOptions GlobalOptions, code int, message string) {
|
||||
if globalOptions.JSON {
|
||||
type jsonExitError struct {
|
||||
MessageType string `json:"message_type"` // exit_error
|
||||
@@ -139,7 +141,7 @@ func printExitError(code int, message string) {
|
||||
Message: message,
|
||||
}
|
||||
|
||||
err := json.NewEncoder(globalOptions.stderr).Encode(jsonS)
|
||||
err := json.NewEncoder(os.Stderr).Encode(jsonS)
|
||||
if err != nil {
|
||||
// ignore error as there's no good way to handle it
|
||||
_, _ = fmt.Fprintf(os.Stderr, "JSON encode failed: %v\n", err)
|
||||
@@ -147,7 +149,7 @@ func printExitError(code int, message string) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
_, _ = fmt.Fprintf(globalOptions.stderr, "%v\n", message)
|
||||
_, _ = fmt.Fprintf(os.Stderr, "%v\n", message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,9 +172,18 @@ func main() {
|
||||
debug.Log("restic %s compiled with %v on %v/%v",
|
||||
version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||
|
||||
ctx := createGlobalContext()
|
||||
err = newRootCommand().ExecuteContext(ctx)
|
||||
|
||||
globalOptions := GlobalOptions{
|
||||
stdout: os.Stdout,
|
||||
stderr: os.Stderr,
|
||||
backends: collectBackends(),
|
||||
}
|
||||
func() {
|
||||
term, cancel := termstatus.Setup(os.Stdout, os.Stderr, globalOptions.Quiet)
|
||||
defer cancel()
|
||||
globalOptions.stdout, globalOptions.stderr = termstatus.WrapStdio(term)
|
||||
globalOptions.term = term
|
||||
ctx := createGlobalContext(os.Stderr)
|
||||
err = newRootCommand(&globalOptions).ExecuteContext(ctx)
|
||||
switch err {
|
||||
case nil:
|
||||
err = ctx.Err()
|
||||
@@ -180,6 +191,7 @@ func main() {
|
||||
// ErrOK overwrites context cancellation errors
|
||||
err = nil
|
||||
}
|
||||
}()
|
||||
|
||||
var exitMessage string
|
||||
switch {
|
||||
@@ -224,7 +236,7 @@ func main() {
|
||||
}
|
||||
|
||||
if exitCode != 0 {
|
||||
printExitError(exitCode, exitMessage)
|
||||
printExitError(globalOptions, exitCode, exitMessage)
|
||||
}
|
||||
Exit(exitCode)
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/restic/restic/internal/ui/termstatus"
|
||||
)
|
||||
|
||||
// setupTermstatus creates a new termstatus and reroutes globalOptions.{stdout,stderr} to it
|
||||
// The returned function must be called to shut down the termstatus,
|
||||
//
|
||||
// Expected usage:
|
||||
// ```
|
||||
// term, cancel := setupTermstatus()
|
||||
// defer cancel()
|
||||
// // do stuff
|
||||
// ```
|
||||
func setupTermstatus() (*termstatus.Terminal, func()) {
|
||||
var wg sync.WaitGroup
|
||||
// only shutdown once cancel is called to ensure that no output is lost
|
||||
cancelCtx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
term := termstatus.New(globalOptions.stdout, globalOptions.stderr, globalOptions.Quiet)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
term.Run(cancelCtx)
|
||||
}()
|
||||
|
||||
// use the termstatus for stdout/stderr
|
||||
prevStdout, prevStderr := globalOptions.stdout, globalOptions.stderr
|
||||
globalOptions.stdout, globalOptions.stderr = termstatus.WrapStdio(term)
|
||||
|
||||
return term, func() {
|
||||
// shutdown termstatus
|
||||
globalOptions.stdout, globalOptions.stderr = prevStdout, prevStderr
|
||||
cancel()
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/restic/restic/internal/terminal"
|
||||
"github.com/restic/restic/internal/ui"
|
||||
@@ -46,6 +47,34 @@ type fder interface {
|
||||
Fd() uintptr
|
||||
}
|
||||
|
||||
// Setup creates a new termstatus.
|
||||
// The returned function must be called to shut down the termstatus,
|
||||
//
|
||||
// Expected usage:
|
||||
// ```
|
||||
// term, cancel := termstatus.Setup(os.stdout, os.stderr, false)
|
||||
// defer cancel()
|
||||
// // do stuff
|
||||
// ```
|
||||
func Setup(stdout, stderr io.Writer, quiet bool) (*Terminal, func()) {
|
||||
var wg sync.WaitGroup
|
||||
// only shutdown once cancel is called to ensure that no output is lost
|
||||
cancelCtx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
term := New(stdout, stderr, quiet)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
term.Run(cancelCtx)
|
||||
}()
|
||||
|
||||
return term, func() {
|
||||
// shutdown termstatus
|
||||
cancel()
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
// New returns a new Terminal for wr. A goroutine is started to update the
|
||||
// terminal. It is terminated when ctx is cancelled. When wr is redirected to
|
||||
// a file (e.g. via shell output redirection) or is just an io.Writer (not the
|
||||
|
||||
Reference in New Issue
Block a user