extract GlobalOptions into internal/global package

Rough steps:
```
mv cmd/restic/global* cmd/restic/secondary_repo* internal/global/
sed -i "s/package main/package global/" internal/global/*.go
Rename "GlobalOptions" to "Options" in internal/global/
Replace everywhere " GlobalOptions" -> " global.Options"
Replace everywhere "\*GlobalOptions" -> " *global.Options"
Make SecondaryRepoOptions public
Make create public
Make version public
```
This commit is contained in:
Michael Eischer 2025-09-28 22:04:48 +02:00
parent 2c677d8db4
commit a816b827cf
74 changed files with 407 additions and 333 deletions

View file

@ -24,6 +24,7 @@ import (
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/filter" "github.com/restic/restic/internal/filter"
"github.com/restic/restic/internal/fs" "github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/textfile" "github.com/restic/restic/internal/textfile"
@ -31,7 +32,7 @@ import (
"github.com/restic/restic/internal/ui/backup" "github.com/restic/restic/internal/ui/backup"
) )
func newBackupCommand(globalOptions *GlobalOptions) *cobra.Command { func newBackupCommand(globalOptions *global.Options) *cobra.Command {
var opts BackupOptions var opts BackupOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -274,7 +275,7 @@ func readFilenamesRaw(r io.Reader) (names []string, err error) {
} }
// Check returns an error when an invalid combination of options was set. // Check returns an error when an invalid combination of options was set.
func (opts BackupOptions) Check(gopts GlobalOptions, args []string) error { func (opts BackupOptions) Check(gopts global.Options, args []string) error {
if gopts.Password == "" && !gopts.InsecureNoPassword { if gopts.Password == "" && !gopts.InsecureNoPassword {
if opts.Stdin { if opts.Stdin {
return errors.Fatal("cannot read both password and data from stdin") return errors.Fatal("cannot read both password and data from stdin")
@ -475,7 +476,7 @@ func findParentSnapshot(ctx context.Context, repo restic.ListerLoaderUnpacked, o
return sn, err 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 vsscfg fs.VSSConfig
var err error var err error
@ -509,7 +510,7 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter
timeStamp := time.Now() timeStamp := time.Now()
backupStart := timeStamp backupStart := timeStamp
if opts.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 { if err != nil {
return errors.Fatalf("error in time option: %v", err) return errors.Fatalf("error in time option: %v", err)
} }
@ -668,7 +669,7 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter
Time: timeStamp, Time: timeStamp,
Hostname: opts.Host, Hostname: opts.Host,
ParentSnapshot: parentSnapshot, ParentSnapshot: parentSnapshot,
ProgramVersion: "restic " + version, ProgramVersion: "restic " + global.Version,
SkipIfUnchanged: opts.SkipIfUnchanged, SkipIfUnchanged: opts.SkipIfUnchanged,
} }

View file

@ -12,12 +12,13 @@ import (
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/fs" "github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
func testRunBackupAssumeFailure(t testing.TB, dir string, target []string, opts BackupOptions, 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 GlobalOptions) error { return withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
t.Logf("backing up %v in %v", target, dir) t.Logf("backing up %v in %v", target, dir)
if dir != "" { if dir != "" {
cleanup := rtest.Chdir(t, dir) cleanup := rtest.Chdir(t, dir)
@ -29,7 +30,7 @@ func testRunBackupAssumeFailure(t testing.TB, dir string, target []string, opts
}) })
} }
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) err := testRunBackupAssumeFailure(t, dir, target, opts, gopts)
rtest.Assert(t, err == nil, "Error while backing up: %v", err) rtest.Assert(t, err == nil, "Error while backing up: %v", err)
} }
@ -512,7 +513,7 @@ func TestBackupProgramVersion(t *testing.T) {
if newest == nil { if newest == nil {
t.Fatal("expected a backup, got nil") t.Fatal("expected a backup, got nil")
} }
resticVersion := "restic " + version resticVersion := "restic " + global.Version
rtest.Assert(t, newest.ProgramVersion == resticVersion, rtest.Assert(t, newest.ProgramVersion == resticVersion,
"expected %v, got %v", resticVersion, newest.ProgramVersion) "expected %v, got %v", resticVersion, newest.ProgramVersion)
} }

View file

@ -10,13 +10,14 @@ import (
"github.com/restic/restic/internal/backend/cache" "github.com/restic/restic/internal/backend/cache"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/table" "github.com/restic/restic/internal/ui/table"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newCacheCommand(globalOptions *GlobalOptions) *cobra.Command { func newCacheCommand(globalOptions *global.Options) *cobra.Command {
var opts CacheOptions var opts CacheOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -55,7 +56,7 @@ func (opts *CacheOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.NoSize, "no-size", false, "do not output the size of the cache directories") 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 { func runCache(opts CacheOptions, gopts global.Options, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
if len(args) > 0 { if len(args) > 0 {

View file

@ -9,6 +9,7 @@ import (
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
@ -16,7 +17,7 @@ import (
var catAllowedCmds = []string{"config", "index", "snapshot", "key", "masterkey", "lock", "pack", "blob", "tree"} 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{ cmd := &cobra.Command{
Use: "cat [flags] [masterkey|config|pack ID|blob ID|snapshot ID|index ID|key ID|lock ID|tree snapshot:subfolder]", 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", Short: "Print internal objects to stdout",
@ -65,7 +66,7 @@ func validateCatArgs(args []string) error {
return nil return nil
} }
func runCat(ctx context.Context, gopts GlobalOptions, args []string, term ui.Terminal) error { func runCat(ctx context.Context, gopts global.Options, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term)
if err := validateCatArgs(args); err != nil { if err := validateCatArgs(args); err != nil {

View file

@ -16,13 +16,14 @@ import (
"github.com/restic/restic/internal/backend/cache" "github.com/restic/restic/internal/backend/cache"
"github.com/restic/restic/internal/checker" "github.com/restic/restic/internal/checker"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/progress" "github.com/restic/restic/internal/ui/progress"
) )
func newCheckCommand(globalOptions *GlobalOptions) *cobra.Command { func newCheckCommand(globalOptions *global.Options) *cobra.Command {
var opts CheckOptions var opts CheckOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "check [flags]", Use: "check [flags]",
@ -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 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 // - 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 // - 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() {} cleanup = func() {}
if opts.WithCache { if opts.WithCache {
// use the default cache, no setup needed // use the default cache, no setup needed
@ -217,7 +218,7 @@ func prepareCheckCache(opts CheckOptions, gopts *GlobalOptions, printer progress
return cleanup 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"} summary := checkSummary{MessageType: "summary"}
if len(args) != 0 { 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") return summary, errors.Fatal("the check command expects no arguments, only options - please see `restic help check` for usage and flags")

View file

@ -4,10 +4,11 @@ import (
"context" "context"
"testing" "testing"
"github.com/restic/restic/internal/global"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
func testRunCheck(t testing.TB, gopts GlobalOptions) { func testRunCheck(t testing.TB, gopts global.Options) {
t.Helper() t.Helper()
output, err := testRunCheckOutput(t, gopts, true) output, err := testRunCheckOutput(t, gopts, true)
if err != nil { if err != nil {
@ -16,14 +17,14 @@ func testRunCheck(t testing.TB, gopts GlobalOptions) {
} }
} }
func testRunCheckMustFail(t testing.TB, gopts GlobalOptions) { func testRunCheckMustFail(t testing.TB, gopts global.Options) {
t.Helper() t.Helper()
_, err := testRunCheckOutput(t, gopts, false) _, err := testRunCheckOutput(t, gopts, false)
rtest.Assert(t, err != nil, "expected non nil error after check of damaged repository") 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) { func testRunCheckOutput(t testing.TB, gopts global.Options, checkUnused bool) (string, error) {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
opts := CheckOptions{ opts := CheckOptions{
ReadData: true, ReadData: true,
CheckUnused: checkUnused, CheckUnused: checkUnused,

View file

@ -9,6 +9,7 @@ import (
"testing" "testing"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
"github.com/restic/restic/internal/ui/progress" "github.com/restic/restic/internal/ui/progress"
@ -202,7 +203,7 @@ func TestPrepareCheckCache(t *testing.T) {
err := os.Remove(tmpDirBase) err := os.Remove(tmpDirBase)
rtest.OK(t, err) rtest.OK(t, err)
} }
gopts := GlobalOptions{CacheDir: tmpDirBase} gopts := global.Options{CacheDir: tmpDirBase}
cleanup := prepareCheckCache(testCase.opts, &gopts, &progress.NoopPrinter{}) cleanup := prepareCheckCache(testCase.opts, &gopts, &progress.NoopPrinter{})
files, err := os.ReadDir(tmpDirBase) files, err := os.ReadDir(tmpDirBase)
rtest.OK(t, err) rtest.OK(t, err)
@ -232,7 +233,7 @@ func TestPrepareCheckCache(t *testing.T) {
} }
func TestPrepareDefaultCheckCache(t *testing.T) { func TestPrepareDefaultCheckCache(t *testing.T) {
gopts := GlobalOptions{CacheDir: ""} gopts := global.Options{CacheDir: ""}
cleanup := prepareCheckCache(CheckOptions{}, &gopts, &progress.NoopPrinter{}) cleanup := prepareCheckCache(CheckOptions{}, &gopts, &progress.NoopPrinter{})
_, err := os.ReadDir(gopts.CacheDir) _, err := os.ReadDir(gopts.CacheDir)
rtest.OK(t, err) rtest.OK(t, err)

View file

@ -7,6 +7,7 @@ import (
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
@ -17,7 +18,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newCopyCommand(globalOptions *GlobalOptions) *cobra.Command { func newCopyCommand(globalOptions *global.Options) *cobra.Command {
var opts CopyOptions var opts CopyOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "copy [flags] [snapshotID ...]", Use: "copy [flags] [snapshotID ...]",
@ -60,18 +61,18 @@ Exit status is 12 if the password is incorrect.
// CopyOptions bundles all options for the copy command. // CopyOptions bundles all options for the copy command.
type CopyOptions struct { type CopyOptions struct {
secondaryRepoOptions global.SecondaryRepoOptions
data.SnapshotFilter data.SnapshotFilter
} }
func (opts *CopyOptions) AddFlags(f *pflag.FlagSet) { 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) initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
} }
func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { func runCopy(ctx context.Context, opts CopyOptions, gopts global.Options, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
secondaryGopts, isFromRepo, err := fillSecondaryGlobalOpts(ctx, opts.secondaryRepoOptions, gopts, "destination") secondaryGopts, isFromRepo, err := opts.SecondaryRepoOptions.FillGlobalOpts(ctx, gopts, "destination")
if err != nil { if err != nil {
return err return err
} }

View file

@ -6,23 +6,24 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/restic/restic/internal/global"
rtest "github.com/restic/restic/internal/test" 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 := srcGopts
gopts.Repo = dstGopts.Repo gopts.Repo = dstGopts.Repo
gopts.Password = dstGopts.Password gopts.Password = dstGopts.Password
gopts.InsecureNoPassword = dstGopts.InsecureNoPassword gopts.InsecureNoPassword = dstGopts.InsecureNoPassword
copyOpts := CopyOptions{ copyOpts := CopyOptions{
secondaryRepoOptions: secondaryRepoOptions{ SecondaryRepoOptions: global.SecondaryRepoOptions{
Repo: srcGopts.Repo, Repo: srcGopts.Repo,
password: srcGopts.Password, Password: srcGopts.Password,
InsecureNoPassword: srcGopts.InsecureNoPassword, InsecureNoPassword: srcGopts.InsecureNoPassword,
}, },
} }
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runCopy(context.TODO(), copyOpts, gopts, nil, gopts.Term) return runCopy(context.TODO(), copyOpts, gopts, nil, gopts.Term)
})) }))
} }

View file

@ -24,6 +24,7 @@ import (
"github.com/restic/restic/internal/crypto" "github.com/restic/restic/internal/crypto"
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/repository/index" "github.com/restic/restic/internal/repository/index"
"github.com/restic/restic/internal/repository/pack" "github.com/restic/restic/internal/repository/pack"
@ -32,13 +33,13 @@ import (
"github.com/restic/restic/internal/ui/progress" "github.com/restic/restic/internal/ui/progress"
) )
func registerDebugCommand(cmd *cobra.Command, globalOptions *GlobalOptions) { func registerDebugCommand(cmd *cobra.Command, globalOptions *global.Options) {
cmd.AddCommand( cmd.AddCommand(
newDebugCommand(globalOptions), newDebugCommand(globalOptions),
) )
} }
func newDebugCommand(globalOptions *GlobalOptions) *cobra.Command { func newDebugCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "debug", Use: "debug",
Short: "Debug commands", Short: "Debug commands",
@ -50,7 +51,7 @@ func newDebugCommand(globalOptions *GlobalOptions) *cobra.Command {
return cmd return cmd
} }
func newDebugDumpCommand(globalOptions *GlobalOptions) *cobra.Command { func newDebugDumpCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "dump [indexes|snapshots|all|packs]", Use: "dump [indexes|snapshots|all|packs]",
Short: "Dump data structures", Short: "Dump data structures",
@ -75,7 +76,7 @@ Exit status is 12 if the password is incorrect.
return cmd return cmd
} }
func newDebugExamineCommand(globalOptions *GlobalOptions) *cobra.Command { func newDebugExamineCommand(globalOptions *global.Options) *cobra.Command {
var opts DebugExamineOptions var opts DebugExamineOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -184,7 +185,7 @@ func dumpIndexes(ctx context.Context, repo restic.ListerLoaderUnpacked, wr io.Wr
}) })
} }
func runDebugDump(ctx context.Context, gopts GlobalOptions, args []string, term ui.Terminal) error { func runDebugDump(ctx context.Context, gopts global.Options, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
if len(args) != 1 { if len(args) != 1 {
@ -455,7 +456,7 @@ func storePlainBlob(id restic.ID, prefix string, plain []byte, printer progress.
return nil return nil
} }
func runDebugExamine(ctx context.Context, gopts GlobalOptions, opts DebugExamineOptions, args []string, term ui.Terminal) error { func runDebugExamine(ctx context.Context, gopts global.Options, opts DebugExamineOptions, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
if opts.ExtractPack && gopts.NoLock { if opts.ExtractPack && gopts.NoLock {

View file

@ -2,8 +2,11 @@
package main 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 // No commands to register in non-debug mode
} }

View file

@ -10,13 +10,14 @@ import (
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newDiffCommand(globalOptions *GlobalOptions) *cobra.Command { func newDiffCommand(globalOptions *global.Options) *cobra.Command {
var opts DiffOptions var opts DiffOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -361,7 +362,7 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref
return ctx.Err() 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 { if len(args) != 2 {
return errors.Fatalf("specify two snapshot IDs") return errors.Fatalf("specify two snapshot IDs")
} }

View file

@ -11,11 +11,12 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/restic/restic/internal/global"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
func testRunDiffOutput(t testing.TB, gopts GlobalOptions, firstSnapshotID string, secondSnapshotID string) (string, 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 GlobalOptions) error { buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
opts := DiffOptions{ opts := DiffOptions{
ShowMetadata: false, ShowMetadata: false,
} }

View file

@ -11,6 +11,7 @@ import (
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/dump" "github.com/restic/restic/internal/dump"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
@ -18,7 +19,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newDumpCommand(globalOptions *GlobalOptions) *cobra.Command { func newDumpCommand(globalOptions *global.Options) *cobra.Command {
var opts DumpOptions var opts DumpOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "dump [flags] snapshotID file", Use: "dump [flags] snapshotID file",
@ -127,7 +128,7 @@ func printFromTree(ctx context.Context, tree *data.Tree, repo restic.BlobLoader,
return fmt.Errorf("path %q not found in snapshot", item) 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 { if len(args) != 2 {
return errors.Fatal("no file and no snapshot ID specified") return errors.Fatal("no file and no snapshot ID specified")
} }

View file

@ -3,12 +3,13 @@ package main
import ( import (
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/feature" "github.com/restic/restic/internal/feature"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/ui/table" "github.com/restic/restic/internal/ui/table"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func newFeaturesCommand(globalOptions *GlobalOptions) *cobra.Command { func newFeaturesCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "features", Use: "features",
Short: "Print list of feature flags", Short: "Print list of feature flags",

View file

@ -16,12 +16,13 @@ import (
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/filter" "github.com/restic/restic/internal/filter"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/walker" "github.com/restic/restic/internal/walker"
) )
func newFindCommand(globalOptions *GlobalOptions) *cobra.Command { func newFindCommand(globalOptions *global.Options) *cobra.Command {
var opts FindOptions var opts FindOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -187,7 +188,7 @@ func (s *statefulOutput) PrintPatternNormal(path string, node *data.Node) {
s.printer.P("") s.printer.P("")
} }
s.oldsn = s.newsn 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)) 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 { } else {
s.printer.S(" ... path %s", nodepath) 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) { func (s *statefulOutput) PrintObject(kind, id, nodepath, treeID string, sn *data.Snapshot) {
@ -579,7 +580,7 @@ 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 { if len(args) == 0 {
return errors.Fatal("wrong number of arguments") return errors.Fatal("wrong number of arguments")
} }

View file

@ -7,11 +7,12 @@ import (
"testing" "testing"
"time" "time"
"github.com/restic/restic/internal/global"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
func testRunFind(t testing.TB, wantJSON bool, opts FindOptions, gopts GlobalOptions, pattern string) []byte { 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 GlobalOptions) error { buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
gopts.JSON = wantJSON gopts.JSON = wantJSON
return runFind(ctx, opts, gopts, []string{pattern}, gopts.Term) return runFind(ctx, opts, gopts, []string{pattern}, gopts.Term)

View file

@ -9,13 +9,14 @@ import (
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newForgetCommand(globalOptions *GlobalOptions) *cobra.Command { func newForgetCommand(globalOptions *global.Options) *cobra.Command {
var opts ForgetOptions var opts ForgetOptions
var pruneOpts PruneOptions var pruneOpts PruneOptions
@ -173,7 +174,7 @@ func verifyForgetOptions(opts *ForgetOptions) error {
return nil 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) err := verifyForgetOptions(&opts)
if err != nil { if err != nil {
return err return err

View file

@ -7,19 +7,20 @@ import (
"testing" "testing"
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/global"
rtest "github.com/restic/restic/internal/test" 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{ pruneOpts := PruneOptions{
MaxUnused: "5%", MaxUnused: "5%",
} }
return withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { return withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runForget(context.TODO(), opts, pruneOpts, gopts, gopts.Term, args) 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...)) rtest.OK(t, testRunForgetMayFail(t, gopts, opts, args...))
} }

View file

@ -6,6 +6,7 @@ import (
"time" "time"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/progress" "github.com/restic/restic/internal/ui/progress"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -13,7 +14,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newGenerateCommand(globalOptions *GlobalOptions) *cobra.Command { func newGenerateCommand(globalOptions *global.Options) *cobra.Command {
var opts generateOptions var opts generateOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -72,7 +73,7 @@ func writeManpages(root *cobra.Command, dir string, printer progress.Printer) er
return doc.GenManTree(root, header, dir) 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) printer.PT("writing %s completion file to %v", shell, filename)
var outWriter io.Writer var outWriter io.Writer
if filename != "-" { if filename != "-" {
@ -110,13 +111,13 @@ func checkStdoutForSingleShell(opts generateOptions) error {
return nil 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 { if len(args) > 0 {
return errors.Fatal("the generate command expects no arguments, only options - please see `restic help generate` for usage and flags") 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) printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term)
cmdRoot := newRootCommand(&GlobalOptions{}) cmdRoot := newRootCommand(&global.Options{})
if opts.ManDir != "" { if opts.ManDir != "" {
err := writeManpages(cmdRoot, opts.ManDir, printer) err := writeManpages(cmdRoot, opts.ManDir, printer)

View file

@ -5,11 +5,12 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/restic/restic/internal/global"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
func testRunGenerate(t testing.TB, gopts GlobalOptions, opts generateOptions) ([]byte, error) { func testRunGenerate(t testing.TB, gopts global.Options, opts generateOptions) ([]byte, error) {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runGenerate(opts, gopts, []string{}, gopts.Term) return runGenerate(opts, gopts, []string{}, gopts.Term)
}) })
return buf.Bytes(), err return buf.Bytes(), err
@ -28,14 +29,14 @@ func TestGenerateStdout(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { 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.OK(t, err)
rtest.Assert(t, strings.Contains(string(output), "# "+tc.name+" completion for restic"), "has no expected completion header") 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) { 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") rtest.Assert(t, err != nil, "generate shell completions to stdout for two shells fails")
}) })
} }

View file

@ -8,6 +8,7 @@ import (
"github.com/restic/chunker" "github.com/restic/chunker"
"github.com/restic/restic/internal/backend/location" "github.com/restic/restic/internal/backend/location"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
@ -17,7 +18,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newInitCommand(globalOptions *GlobalOptions) *cobra.Command { func newInitCommand(globalOptions *global.Options) *cobra.Command {
var opts InitOptions var opts InitOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -44,18 +45,18 @@ Exit status is 1 if there was any error.
// InitOptions bundles all options for the init command. // InitOptions bundles all options for the init command.
type InitOptions struct { type InitOptions struct {
secondaryRepoOptions global.SecondaryRepoOptions
CopyChunkerParameters bool CopyChunkerParameters bool
RepositoryVersion string RepositoryVersion string
} }
func (opts *InitOptions) AddFlags(f *pflag.FlagSet) { 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.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'") 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 { if len(args) > 0 {
return errors.Fatal("the init command expects no arguments, only options - please see `restic help init` for usage and flags") return errors.Fatal("the init command expects no arguments, only options - please see `restic help init` for usage and flags")
} }
@ -85,19 +86,19 @@ func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args []
return err return err
} }
gopts.Repo, err = ReadRepo(gopts) gopts.Repo, err = global.ReadRepo(gopts)
if err != nil { if err != nil {
return err return err
} }
gopts.Password, err = ReadPasswordTwice(ctx, gopts, gopts.Password, err = global.ReadPasswordTwice(ctx, gopts,
"enter password for new repository: ", "enter password for new repository: ",
"enter password again: ") "enter password again: ")
if err != nil { if err != nil {
return err return err
} }
be, err := create(ctx, gopts.Repo, gopts, gopts.Extended, printer) be, err := global.Create(ctx, gopts.Repo, gopts, gopts.Extended, printer)
if err != nil { if err != nil {
return errors.Fatalf("create repository at %s failed: %v", location.StripPassword(gopts.Backends, gopts.Repo), err) return errors.Fatalf("create repository at %s failed: %v", location.StripPassword(gopts.Backends, gopts.Repo), err)
} }
@ -137,14 +138,14 @@ func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args []
return nil 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 { if opts.CopyChunkerParameters {
otherGopts, _, err := fillSecondaryGlobalOpts(ctx, opts.secondaryRepoOptions, gopts, "secondary") otherGopts, _, err := opts.SecondaryRepoOptions.FillGlobalOpts(ctx, gopts, "secondary")
if err != nil { if err != nil {
return nil, err return nil, err
} }
otherRepo, err := OpenRepository(ctx, otherGopts, printer) otherRepo, err := global.OpenRepository(ctx, otherGopts, printer)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -6,18 +6,19 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
"github.com/restic/restic/internal/ui/progress" "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) repository.TestUseLowSecurityKDFParameters(t)
restic.TestDisableCheckPolynomial(t) restic.TestDisableCheckPolynomial(t)
restic.TestSetLockTimeout(t, 0) restic.TestSetLockTimeout(t, 0)
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runInit(ctx, InitOptions{}, gopts, nil, gopts.Term) return runInit(ctx, InitOptions{}, gopts, nil, gopts.Term)
}) })
rtest.OK(t, err) rtest.OK(t, err)
@ -38,32 +39,32 @@ func TestInitCopyChunkerParams(t *testing.T) {
testRunInit(t, env2.gopts) testRunInit(t, env2.gopts)
initOpts := InitOptions{ initOpts := InitOptions{
secondaryRepoOptions: secondaryRepoOptions{ SecondaryRepoOptions: global.SecondaryRepoOptions{
Repo: env2.gopts.Repo, Repo: env2.gopts.Repo,
password: env2.gopts.Password, Password: env2.gopts.Password,
}, },
} }
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 runInit(ctx, initOpts, gopts, nil, gopts.Term) return runInit(ctx, initOpts, gopts, nil, gopts.Term)
}) })
rtest.Assert(t, err != nil, "expected invalid init options to fail") rtest.Assert(t, err != nil, "expected invalid init options to fail")
initOpts.CopyChunkerParameters = true initOpts.CopyChunkerParameters = true
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 runInit(ctx, initOpts, gopts, nil, gopts.Term) return runInit(ctx, initOpts, gopts, nil, gopts.Term)
}) })
rtest.OK(t, err) rtest.OK(t, err)
var repo *repository.Repository var repo *repository.Repository
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 {
repo, err = OpenRepository(ctx, gopts, &progress.NoopPrinter{}) repo, err = global.OpenRepository(ctx, gopts, &progress.NoopPrinter{})
return err return err
}) })
rtest.OK(t, err) rtest.OK(t, err)
var otherRepo *repository.Repository var otherRepo *repository.Repository
err = withTermStatus(t, env2.gopts, func(ctx context.Context, gopts GlobalOptions) error { err = withTermStatus(t, env2.gopts, func(ctx context.Context, gopts global.Options) error {
otherRepo, err = OpenRepository(ctx, gopts, &progress.NoopPrinter{}) otherRepo, err = global.OpenRepository(ctx, gopts, &progress.NoopPrinter{})
return err return err
}) })
rtest.OK(t, err) rtest.OK(t, err)

View file

@ -1,10 +1,11 @@
package main package main
import ( import (
"github.com/restic/restic/internal/global"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func newKeyCommand(globalOptions *GlobalOptions) *cobra.Command { func newKeyCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "key", Use: "key",
Short: "Manage keys (passwords)", Short: "Manage keys (passwords)",

View file

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/progress" "github.com/restic/restic/internal/ui/progress"
@ -12,7 +13,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newKeyAddCommand(globalOptions *GlobalOptions) *cobra.Command { func newKeyAddCommand(globalOptions *global.Options) *cobra.Command {
var opts KeyAddOptions var opts KeyAddOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -54,7 +55,7 @@ func (opts *KeyAddOptions) Add(flags *pflag.FlagSet) {
flags.StringVarP(&opts.Hostname, "host", "", "", "the hostname for new key") 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 { 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") return fmt.Errorf("the key add command expects no arguments, only options - please see `restic help key add` for usage and flags")
} }
@ -69,7 +70,7 @@ func runKeyAdd(ctx context.Context, gopts GlobalOptions, opts KeyAddOptions, arg
return addKey(ctx, repo, gopts, opts, printer) 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) pw, err := getNewPassword(ctx, gopts, opts.NewPasswordFile, opts.InsecureNoPassword)
if err != nil { if err != nil {
return err 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. // testKeyNewPassword is used to set a new password during integration testing.
var testKeyNewPassword string 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 != "" { if testKeyNewPassword != "" {
return testKeyNewPassword, nil return testKeyNewPassword, nil
} }
@ -106,7 +107,7 @@ func getNewPassword(ctx context.Context, gopts GlobalOptions, newPasswordFile st
} }
if newPasswordFile != "" { if newPasswordFile != "" {
password, err := loadPasswordFromFile(newPasswordFile) password, err := global.LoadPasswordFromFile(newPasswordFile)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -123,7 +124,7 @@ func getNewPassword(ctx context.Context, gopts GlobalOptions, newPasswordFile st
// empty passwords are already handled above // empty passwords are already handled above
newopts.InsecureNoPassword = false newopts.InsecureNoPassword = false
return ReadPasswordTwice(ctx, newopts, return global.ReadPasswordTwice(ctx, newopts,
"enter new password: ", "enter new password: ",
"enter password again: ") "enter password again: ")
} }

View file

@ -10,13 +10,14 @@ import (
"testing" "testing"
"github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
"github.com/restic/restic/internal/ui/progress" "github.com/restic/restic/internal/ui/progress"
) )
func testRunKeyListOtherIDs(t testing.TB, gopts GlobalOptions) []string { func testRunKeyListOtherIDs(t testing.TB, gopts global.Options) []string {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyList(ctx, gopts, []string{}, gopts.Term) return runKeyList(ctx, gopts, []string{}, gopts.Term)
}) })
rtest.OK(t, err) rtest.OK(t, err)
@ -34,26 +35,26 @@ func testRunKeyListOtherIDs(t testing.TB, gopts GlobalOptions) []string {
return IDs return IDs
} }
func testRunKeyAddNewKey(t testing.TB, newPassword string, gopts GlobalOptions) { func testRunKeyAddNewKey(t testing.TB, newPassword string, gopts global.Options) {
testKeyNewPassword = newPassword testKeyNewPassword = newPassword
defer func() { defer func() {
testKeyNewPassword = "" testKeyNewPassword = ""
}() }()
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{}, []string{}, gopts.Term) return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{}, gopts.Term)
}) })
rtest.OK(t, err) rtest.OK(t, err)
} }
func testRunKeyAddNewKeyUserHost(t testing.TB, gopts GlobalOptions) { func testRunKeyAddNewKeyUserHost(t testing.TB, gopts global.Options) {
testKeyNewPassword = "john's geheimnis" testKeyNewPassword = "john's geheimnis"
defer func() { defer func() {
testKeyNewPassword = "" testKeyNewPassword = ""
}() }()
t.Log("adding key for john@example.com") 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{ return runKeyAdd(ctx, gopts, KeyAddOptions{
Username: "john", Username: "john",
Hostname: "example.com", Hostname: "example.com",
@ -61,8 +62,8 @@ func testRunKeyAddNewKeyUserHost(t testing.TB, gopts GlobalOptions) {
}) })
rtest.OK(t, err) rtest.OK(t, err)
_ = withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { _ = withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
repo, err := OpenRepository(ctx, gopts, &progress.NoopPrinter{}) repo, err := global.OpenRepository(ctx, gopts, &progress.NoopPrinter{})
rtest.OK(t, err) rtest.OK(t, err)
key, err := repository.SearchKey(ctx, repo, testKeyNewPassword, 2, "") key, err := repository.SearchKey(ctx, repo, testKeyNewPassword, 2, "")
rtest.OK(t, err) rtest.OK(t, err)
@ -73,22 +74,22 @@ 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 testKeyNewPassword = newPassword
defer func() { defer func() {
testKeyNewPassword = "" testKeyNewPassword = ""
}() }()
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{}, gopts.Term) return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{}, gopts.Term)
}) })
rtest.OK(t, err) 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) t.Logf("remove %d keys: %q\n", len(IDs), IDs)
for _, id := range IDs { for _, id := range IDs {
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyRemove(ctx, gopts, []string{id}, gopts.Term) return runKeyRemove(ctx, gopts, []string{id}, gopts.Term)
}) })
rtest.OK(t, err) rtest.OK(t, err)
@ -121,7 +122,7 @@ func TestKeyAddRemove(t *testing.T) {
env.gopts.Password = passwordList[len(passwordList)-1] env.gopts.Password = passwordList[len(passwordList)-1]
t.Logf("testing access with last password %q\n", env.gopts.Password) t.Logf("testing access with last password %q\n", env.gopts.Password)
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 runKeyList(ctx, gopts, []string{}, gopts.Term) return runKeyList(ctx, gopts, []string{}, gopts.Term)
}) })
rtest.OK(t, err) rtest.OK(t, err)
@ -135,7 +136,7 @@ func TestKeyAddInvalid(t *testing.T) {
defer cleanup() defer cleanup()
testRunInit(t, env.gopts) 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{ return runKeyAdd(ctx, gopts, KeyAddOptions{
NewPasswordFile: "some-file", NewPasswordFile: "some-file",
InsecureNoPassword: true, InsecureNoPassword: true,
@ -146,7 +147,7 @@ func TestKeyAddInvalid(t *testing.T) {
pwfile := filepath.Join(t.TempDir(), "pwfile") pwfile := filepath.Join(t.TempDir(), "pwfile")
rtest.OK(t, os.WriteFile(pwfile, []byte{}, 0o666)) 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{ return runKeyAdd(ctx, gopts, KeyAddOptions{
NewPasswordFile: pwfile, NewPasswordFile: pwfile,
}, []string{}, gopts.Term) }, []string{}, gopts.Term)
@ -161,7 +162,7 @@ func TestKeyAddEmpty(t *testing.T) {
defer cleanup() defer cleanup()
testRunInit(t, env.gopts) 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{ return runKeyAdd(ctx, gopts, KeyAddOptions{
InsecureNoPassword: true, InsecureNoPassword: true,
}, []string{}, gopts.Term) }, []string{}, gopts.Term)
@ -196,20 +197,20 @@ func TestKeyProblems(t *testing.T) {
testKeyNewPassword = "" testKeyNewPassword = ""
}() }()
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 runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{}, gopts.Term) return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{}, gopts.Term)
}) })
t.Log(err) t.Log(err)
rtest.Assert(t, err != nil, "expected passwd change to fail") rtest.Assert(t, err != nil, "expected passwd change to fail")
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{}, []string{}, gopts.Term) return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{}, gopts.Term)
}) })
t.Log(err) t.Log(err)
rtest.Assert(t, err != nil, "expected key adding to fail") rtest.Assert(t, err != nil, "expected key adding to fail")
t.Logf("testing access with initial password %q\n", env.gopts.Password) t.Logf("testing access with initial password %q\n", env.gopts.Password)
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 runKeyList(ctx, gopts, []string{}, gopts.Term) return runKeyList(ctx, gopts, []string{}, gopts.Term)
}) })
rtest.OK(t, err) rtest.OK(t, err)
@ -225,31 +226,31 @@ func TestKeyCommandInvalidArguments(t *testing.T) {
return &emptySaveBackend{r}, nil return &emptySaveBackend{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 {
return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{"johndoe"}, gopts.Term) return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{"johndoe"}, gopts.Term)
}) })
t.Log(err) t.Log(err)
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key add: %v", 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 { err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{"johndoe"}, gopts.Term) return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{"johndoe"}, gopts.Term)
}) })
t.Log(err) t.Log(err)
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key passwd: %v", 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 { err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyList(ctx, gopts, []string{"johndoe"}, gopts.Term) return runKeyList(ctx, gopts, []string{"johndoe"}, gopts.Term)
}) })
t.Log(err) t.Log(err)
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key list: %v", 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 { err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyRemove(ctx, gopts, []string{}, gopts.Term) return runKeyRemove(ctx, gopts, []string{}, gopts.Term)
}) })
t.Log(err) t.Log(err)
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "one argument"), "unexpected error for key remove: %v", 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 { err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyRemove(ctx, gopts, []string{"john", "doe"}, gopts.Term) return runKeyRemove(ctx, gopts, []string{"john", "doe"}, gopts.Term)
}) })
t.Log(err) t.Log(err)

View file

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"sync" "sync"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
@ -14,7 +15,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func newKeyListCommand(globalOptions *GlobalOptions) *cobra.Command { func newKeyListCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "list", Use: "list",
Short: "List keys (passwords)", Short: "List keys (passwords)",
@ -40,7 +41,7 @@ Exit status is 12 if the password is incorrect.
return cmd 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 { 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") return fmt.Errorf("the key list command expects no arguments, only options - please see `restic help key list` for usage and flags")
} }
@ -55,7 +56,7 @@ func runKeyList(ctx context.Context, gopts GlobalOptions, args []string, term ui
return listKeys(ctx, repo, gopts, printer) 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 { type keyInfo struct {
Current bool `json:"current"` Current bool `json:"current"`
ID string `json:"id"` ID string `json:"id"`
@ -81,7 +82,7 @@ func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions
ShortID: id.Str(), ShortID: id.Str(),
UserName: k.Username, UserName: k.Username,
HostName: k.Hostname, HostName: k.Hostname,
Created: k.Created.Local().Format(TimeFormat), Created: k.Created.Local().Format(global.TimeFormat),
} }
m.Lock() m.Lock()

View file

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/progress" "github.com/restic/restic/internal/ui/progress"
@ -12,7 +13,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newKeyPasswdCommand(globalOptions *GlobalOptions) *cobra.Command { func newKeyPasswdCommand(globalOptions *global.Options) *cobra.Command {
var opts KeyPasswdOptions var opts KeyPasswdOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -49,7 +50,7 @@ func (opts *KeyPasswdOptions) AddFlags(flags *pflag.FlagSet) {
opts.KeyAddOptions.Add(flags) 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 { 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") return fmt.Errorf("the key passwd command expects no arguments, only options - please see `restic help key passwd` for usage and flags")
} }
@ -64,7 +65,7 @@ func runKeyPasswd(ctx context.Context, gopts GlobalOptions, opts KeyPasswdOption
return changePassword(ctx, repo, gopts, opts, printer) 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) pw, err := getNewPassword(ctx, gopts, opts.NewPasswordFile, opts.InsecureNoPassword)
if err != nil { if err != nil {
return err return err

View file

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
@ -12,7 +13,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func newKeyRemoveCommand(globalOptions *GlobalOptions) *cobra.Command { func newKeyRemoveCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "remove [ID]", Use: "remove [ID]",
Short: "Remove key ID (password) from the repository.", Short: "Remove key ID (password) from the repository.",
@ -37,7 +38,7 @@ Exit status is 12 if the password is incorrect.
return cmd 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 { if len(args) != 1 {
return fmt.Errorf("key remove expects one argument as the key id") return fmt.Errorf("key remove expects one argument as the key id")
} }

View file

@ -5,6 +5,7 @@ import (
"strings" "strings"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository/index" "github.com/restic/restic/internal/repository/index"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
@ -12,7 +13,7 @@ import (
"github.com/spf13/cobra" "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 listAllowedArgs = []string{"blobs", "packs", "index", "snapshots", "keys", "locks"}
var listAllowedArgsUseString = strings.Join(listAllowedArgs, "|") var listAllowedArgsUseString = strings.Join(listAllowedArgs, "|")
@ -42,7 +43,7 @@ Exit status is 12 if the password is incorrect.
return cmd return cmd
} }
func runList(ctx context.Context, gopts GlobalOptions, args []string, term ui.Terminal) error { func runList(ctx context.Context, gopts global.Options, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
if len(args) != 1 { if len(args) != 1 {

View file

@ -8,13 +8,14 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
) )
func testRunList(t testing.TB, gopts GlobalOptions, tpe string) restic.IDs { func testRunList(t testing.TB, gopts global.Options, tpe string) restic.IDs {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runList(ctx, gopts, []string{tpe}, gopts.Term) return runList(ctx, gopts, []string{tpe}, gopts.Term)
}) })
rtest.OK(t, err) rtest.OK(t, err)
@ -50,7 +51,7 @@ func parseIDsFromReader(t testing.TB, rd io.Reader) restic.IDs {
return 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() t.Helper()
snapshotIDs := testRunList(t, gopts, "snapshots") snapshotIDs := testRunList(t, gopts, "snapshots")
rtest.Assert(t, len(snapshotIDs) == expected, "expected %v snapshot, got %v", expected, snapshotIDs) rtest.Assert(t, len(snapshotIDs) == expected, "expected %v snapshot, got %v", expected, snapshotIDs)
@ -58,8 +59,8 @@ func testListSnapshots(t testing.TB, gopts GlobalOptions, expected int) restic.I
} }
// extract blob set from repository index // extract blob set from repository index
func testListBlobs(t testing.TB, gopts GlobalOptions) (blobSetFromIndex restic.IDSet) { func testListBlobs(t testing.TB, gopts global.Options) (blobSetFromIndex restic.IDSet) {
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
_, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) _, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err) rtest.OK(t, err)

View file

@ -18,12 +18,13 @@ import (
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/fs" "github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/walker" "github.com/restic/restic/internal/walker"
) )
func newLsCommand(globalOptions *GlobalOptions) *cobra.Command { func newLsCommand(globalOptions *global.Options) *cobra.Command {
var opts LsOptions var opts LsOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -303,7 +304,7 @@ type toSortOutput struct {
node *data.Node node *data.Node
} }
func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { func runLs(ctx context.Context, opts LsOptions, gopts global.Options, args []string, term ui.Terminal) error {
termPrinter := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) termPrinter := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term)
if len(args) == 0 { if len(args) == 0 {

View file

@ -9,12 +9,13 @@ import (
"testing" "testing"
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
func testRunLsWithOpts(t testing.TB, gopts GlobalOptions, opts LsOptions, args []string) []byte { func testRunLsWithOpts(t testing.TB, gopts global.Options, opts LsOptions, args []string) []byte {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
gopts.Quiet = true gopts.Quiet = true
return runLs(context.TODO(), opts, gopts, args, gopts.Term) return runLs(context.TODO(), opts, gopts, args, gopts.Term)
}) })
@ -22,7 +23,7 @@ func testRunLsWithOpts(t testing.TB, gopts GlobalOptions, opts LsOptions, args [
return buf.Bytes() 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}) out := testRunLsWithOpts(t, gopts, LsOptions{}, []string{snapshotID})
return strings.Split(string(out), "\n") return strings.Split(string(out), "\n")
} }

View file

@ -3,6 +3,7 @@ package main
import ( import (
"context" "context"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/migrations" "github.com/restic/restic/internal/migrations"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
@ -12,7 +13,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newMigrateCommand(globalOptions *GlobalOptions) *cobra.Command { func newMigrateCommand(globalOptions *global.Options) *cobra.Command {
var opts MigrateOptions var opts MigrateOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -75,7 +76,7 @@ func checkMigrations(ctx context.Context, repo restic.Repository, printer progre
return nil 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 var firsterr error
for _, name := range args { for _, name := range args {
found := false found := false
@ -133,7 +134,7 @@ func applyMigrations(ctx context.Context, opts MigrateOptions, gopts GlobalOptio
return firsterr return firsterr
} }
func runMigrate(ctx context.Context, opts MigrateOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { func runMigrate(ctx context.Context, opts MigrateOptions, gopts global.Options, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)

View file

@ -16,6 +16,7 @@ import (
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/fuse" "github.com/restic/restic/internal/fuse"
@ -24,11 +25,11 @@ import (
"github.com/anacrolix/fuse/fs" "github.com/anacrolix/fuse/fs"
) )
func registerMountCommand(cmdRoot *cobra.Command, globalOptions *GlobalOptions) { func registerMountCommand(cmdRoot *cobra.Command, globalOptions *global.Options) {
cmdRoot.AddCommand(newMountCommand(globalOptions)) cmdRoot.AddCommand(newMountCommand(globalOptions))
} }
func newMountCommand(globalOptions *GlobalOptions) *cobra.Command { func newMountCommand(globalOptions *global.Options) *cobra.Command {
var opts MountOptions var opts MountOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -114,7 +115,7 @@ func (opts *MountOptions) AddFlags(f *pflag.FlagSet) {
_ = f.MarkDeprecated("snapshot-template", "use --time-template") _ = f.MarkDeprecated("snapshot-template", "use --time-template")
} }
func runMount(ctx context.Context, opts MountOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { func runMount(ctx context.Context, opts MountOptions, gopts global.Options, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
if opts.TimeTemplate == "" { if opts.TimeTemplate == "" {

View file

@ -3,8 +3,11 @@
package main 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 // Mount command not supported on these platforms
} }

View file

@ -15,6 +15,7 @@ import (
systemFuse "github.com/anacrolix/fuse" systemFuse "github.com/anacrolix/fuse"
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
@ -58,12 +59,12 @@ func waitForMount(t testing.TB, dir string) {
t.Errorf("subdir %q of dir %s never appeared", mountTestSubdir, dir) 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() defer wg.Done()
opts := MountOptions{ opts := MountOptions{
TimeTemplate: time.RFC3339, TimeTemplate: time.RFC3339,
} }
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runMount(context.TODO(), opts, gopts, []string{dir}, gopts.Term) return runMount(context.TODO(), opts, gopts, []string{dir}, gopts.Term)
})) }))
} }
@ -91,7 +92,7 @@ func listSnapshots(t testing.TB, dir string) []string {
return names 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) t.Logf("checking for %d snapshots: %v", len(snapshotIDs), snapshotIDs)
var wg sync.WaitGroup var wg sync.WaitGroup
@ -129,7 +130,7 @@ func checkSnapshots(t testing.TB, gopts GlobalOptions, mountpoint string, snapsh
} }
} }
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
_, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) _, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
if err != nil { if err != nil {

View file

@ -3,12 +3,13 @@ package main
import ( import (
"fmt" "fmt"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/options" "github.com/restic/restic/internal/options"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func newOptionsCommand(globalOptions *GlobalOptions) *cobra.Command { func newOptionsCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "options", Use: "options",
Short: "Print list of extended options", Short: "Print list of extended options",

View file

@ -10,6 +10,7 @@ import (
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
@ -19,7 +20,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newPruneCommand(globalOptions *GlobalOptions) *cobra.Command { func newPruneCommand(globalOptions *global.Options) *cobra.Command {
var opts PruneOptions var opts PruneOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -153,7 +154,7 @@ func verifyPruneOptions(opts *PruneOptions) error {
return nil 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) err := verifyPruneOptions(&opts)
if err != nil { if err != nil {
return err return err

View file

@ -7,28 +7,29 @@ import (
"testing" "testing"
"github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
rtest "github.com/restic/restic/internal/test" 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() t.Helper()
rtest.OK(t, testRunPruneOutput(t, gopts, opts)) 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() t.Helper()
err := testRunPruneOutput(t, gopts, opts) err := testRunPruneOutput(t, gopts, opts)
rtest.Assert(t, err != nil, "expected non nil error") rtest.Assert(t, err != nil, "expected non nil error")
} }
func testRunPruneOutput(t testing.TB, gopts GlobalOptions, opts PruneOptions) error { func testRunPruneOutput(t testing.TB, gopts global.Options, opts PruneOptions) error {
oldHook := gopts.BackendTestHook oldHook := gopts.BackendTestHook
gopts.BackendTestHook = func(r backend.Backend) (backend.Backend, error) { return newListOnceBackend(r), nil } gopts.BackendTestHook = func(r backend.Backend) (backend.Backend, error) { return newListOnceBackend(r), nil }
defer func() { defer func() {
gopts.BackendTestHook = oldHook gopts.BackendTestHook = oldHook
}() }()
return withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { return withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runPrune(context.TODO(), opts, gopts, gopts.Term) 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()) testRunForget(t, env.gopts, ForgetOptions{}, firstSnapshot.String())
} }
func testRunForgetJSON(t testing.TB, gopts GlobalOptions, args ...string) { func testRunForgetJSON(t testing.TB, gopts global.Options, args ...string) {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
gopts.JSON = true gopts.JSON = true
opts := ForgetOptions{ opts := ForgetOptions{
DryRun: true, DryRun: true,
@ -119,7 +120,7 @@ func testPrune(t *testing.T, pruneOpts PruneOptions, checkOpts CheckOptions) {
createPrunableRepo(t, env) createPrunableRepo(t, env)
testRunPrune(t, env.gopts, pruneOpts) testRunPrune(t, env.gopts, pruneOpts)
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
_, err := runCheck(context.TODO(), checkOpts, gopts, nil, gopts.Term) _, err := runCheck(context.TODO(), checkOpts, gopts, nil, gopts.Term)
return err return err
})) }))
@ -155,7 +156,7 @@ func TestPruneWithDamagedRepository(t *testing.T) {
env.gopts.BackendTestHook = oldHook env.gopts.BackendTestHook = oldHook
}() }()
// prune should fail // prune should fail
rtest.Equals(t, repository.ErrPacksMissing, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { 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) return runPrune(context.TODO(), pruneDefaultOptions, gopts, gopts.Term)
}), "prune should have reported index not complete error") }), "prune should have reported index not complete error")
} }
@ -228,7 +229,7 @@ func testEdgeCaseRepo(t *testing.T, tarfile string, optionsCheck CheckOptions, o
if checkOK { if checkOK {
testRunCheck(t, env.gopts) testRunCheck(t, env.gopts)
} else { } else {
rtest.Assert(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { rtest.Assert(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
_, err := runCheck(context.TODO(), optionsCheck, gopts, nil, gopts.Term) _, err := runCheck(context.TODO(), optionsCheck, gopts, nil, gopts.Term)
return err return err
}) != nil, }) != nil,
@ -239,7 +240,7 @@ func testEdgeCaseRepo(t *testing.T, tarfile string, optionsCheck CheckOptions, o
testRunPrune(t, env.gopts, optionsPrune) testRunPrune(t, env.gopts, optionsPrune)
testRunCheck(t, env.gopts) testRunCheck(t, env.gopts)
} else { } else {
rtest.Assert(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { rtest.Assert(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runPrune(context.TODO(), optionsPrune, gopts, gopts.Term) return runPrune(context.TODO(), optionsPrune, gopts, gopts.Term)
}) != nil, }) != nil,
"prune should have reported an error") "prune should have reported an error")

View file

@ -7,6 +7,7 @@ import (
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
@ -15,7 +16,7 @@ import (
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
func newRecoverCommand(globalOptions *GlobalOptions) *cobra.Command { func newRecoverCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "recover [flags]", Use: "recover [flags]",
Short: "Recover data from the repository not referenced by snapshots", Short: "Recover data from the repository not referenced by snapshots",
@ -42,7 +43,7 @@ Exit status is 12 if the password is incorrect.
return cmd 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() hostname, err := os.Hostname()
if err != nil { if err != nil {
return err return err

View file

@ -4,11 +4,12 @@ import (
"context" "context"
"testing" "testing"
"github.com/restic/restic/internal/global"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
func testRunRecover(t testing.TB, gopts GlobalOptions) { func testRunRecover(t testing.TB, gopts global.Options) {
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runRecover(context.TODO(), gopts, gopts.Term) return runRecover(context.TODO(), gopts, gopts.Term)
})) }))
} }
@ -32,7 +33,7 @@ func TestRecover(t *testing.T) {
ids = testListSnapshots(t, env.gopts, 1) ids = testListSnapshots(t, env.gopts, 1)
testRunCheck(t, env.gopts) testRunCheck(t, env.gopts)
// check that the root tree is included in the snapshot // check that the root tree is included in the snapshot
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { 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) return runCat(context.TODO(), gopts, []string{"tree", ids[0].String() + ":" + sn.Tree.Str()}, gopts.Term)
})) }))
} }

View file

@ -1,10 +1,11 @@
package main package main
import ( import (
"github.com/restic/restic/internal/global"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func newRepairCommand(globalOptions *GlobalOptions) *cobra.Command { func newRepairCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "repair", Use: "repair",
Short: "Repair the repository", Short: "Repair the repository",

View file

@ -3,13 +3,14 @@ package main
import ( import (
"context" "context"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newRepairIndexCommand(globalOptions *GlobalOptions) *cobra.Command { func newRepairIndexCommand(globalOptions *global.Options) *cobra.Command {
var opts RepairIndexOptions var opts RepairIndexOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -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") 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 var opts RepairIndexOptions
replacement := newRepairIndexCommand(globalOptions) replacement := newRepairIndexCommand(globalOptions)
@ -68,7 +69,7 @@ func newRebuildIndexCommand(globalOptions *GlobalOptions) *cobra.Command {
return cmd return cmd
} }
func runRebuildIndex(ctx context.Context, opts RepairIndexOptions, gopts GlobalOptions, term ui.Terminal) error { func runRebuildIndex(ctx context.Context, opts RepairIndexOptions, gopts global.Options, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)

View file

@ -10,19 +10,20 @@ import (
"github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository/index" "github.com/restic/restic/internal/repository/index"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
func testRunRebuildIndex(t testing.TB, gopts GlobalOptions) { func testRunRebuildIndex(t testing.TB, gopts global.Options) {
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
gopts.Quiet = true 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) env, cleanup := withTestEnvironment(t)
defer cleanup() defer cleanup()
@ -128,7 +129,7 @@ func TestRebuildIndexFailsOnAppendOnly(t *testing.T) {
env.gopts.BackendTestHook = func(r backend.Backend) (backend.Backend, error) { env.gopts.BackendTestHook = func(r backend.Backend) (backend.Backend, error) {
return &appendOnlyBackend{r}, nil 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 gopts.Quiet = true
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.Term) return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.Term)
}) })

View file

@ -7,13 +7,14 @@ import (
"os" "os"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func newRepairPacksCommand(globalOptions *GlobalOptions) *cobra.Command { func newRepairPacksCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "packs [packIDs...]", Use: "packs [packIDs...]",
Short: "Salvage damaged pack files", Short: "Salvage damaged pack files",
@ -38,7 +39,7 @@ Exit status is 12 if the password is incorrect.
return cmd 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() ids := restic.NewIDSet()
for _, arg := range args { for _, arg := range args {
id, err := restic.ParseID(arg) id, err := restic.ParseID(arg)

View file

@ -5,6 +5,7 @@ import (
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/walker" "github.com/restic/restic/internal/walker"
@ -13,7 +14,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newRepairSnapshotsCommand(globalOptions *GlobalOptions) *cobra.Command { func newRepairSnapshotsCommand(globalOptions *global.Options) *cobra.Command {
var opts RepairOptions var opts RepairOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -75,7 +76,7 @@ func (opts *RepairOptions) AddFlags(f *pflag.FlagSet) {
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
} }
func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOptions, args []string, term ui.Terminal) error { func runRepairSnapshots(ctx context.Context, gopts global.Options, opts RepairOptions, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term) printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, opts.DryRun, printer) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, opts.DryRun, printer)

View file

@ -10,16 +10,17 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" 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{ opts := RepairOptions{
Forget: forget, Forget: forget,
} }
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runRepairSnapshots(context.TODO(), gopts, opts, nil, gopts.Term) return runRepairSnapshots(context.TODO(), gopts, opts, nil, gopts.Term)
})) }))
} }

View file

@ -9,6 +9,7 @@ import (
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/filter" "github.com/restic/restic/internal/filter"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restorer" "github.com/restic/restic/internal/restorer"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/progress" "github.com/restic/restic/internal/ui/progress"
@ -18,7 +19,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newRestoreCommand(globalOptions *GlobalOptions) *cobra.Command { func newRestoreCommand(globalOptions *global.Options) *cobra.Command {
var opts RestoreOptions var opts RestoreOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -87,7 +88,7 @@ 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") 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 { term ui.Terminal, args []string) error {
var printer restoreui.ProgressPrinter var printer restoreui.ProgressPrinter

View file

@ -12,15 +12,16 @@ import (
"time" "time"
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" 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) 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{ opts := RestoreOptions{
Target: dir, Target: dir,
} }
@ -29,13 +30,13 @@ func testRunRestoreExcludes(t testing.TB, gopts GlobalOptions, dir string, snaps
rtest.OK(t, testRunRestoreAssumeFailure(t, snapshotID, opts, gopts)) rtest.OK(t, testRunRestoreAssumeFailure(t, snapshotID, opts, gopts))
} }
func testRunRestoreAssumeFailure(t testing.TB, snapshotID string, opts RestoreOptions, gopts GlobalOptions) error { func testRunRestoreAssumeFailure(t testing.TB, snapshotID string, opts RestoreOptions, gopts global.Options) error {
return withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { return withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runRestore(ctx, opts, gopts, gopts.Term, []string{snapshotID}) 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{ opts := RestoreOptions{
Target: dir, Target: dir,
SnapshotFilter: data.SnapshotFilter{ 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)) 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{ opts := RestoreOptions{
Target: dir, 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)) 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{ opts := RestoreOptions{
Target: dir, Target: dir,
} }
@ -65,7 +66,7 @@ func testRunRestoreIncludesFromFile(t testing.TB, gopts GlobalOptions, dir strin
rtest.OK(t, testRunRestoreAssumeFailure(t, snapshotID.String(), opts, gopts)) 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{ opts := RestoreOptions{
Target: dir, Target: dir,
} }

View file

@ -12,6 +12,7 @@ import (
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/filter" "github.com/restic/restic/internal/filter"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
@ -19,7 +20,7 @@ import (
"github.com/restic/restic/internal/walker" "github.com/restic/restic/internal/walker"
) )
func newRewriteCommand(globalOptions *GlobalOptions) *cobra.Command { func newRewriteCommand(globalOptions *global.Options) *cobra.Command {
var opts RewriteOptions var opts RewriteOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -91,7 +92,7 @@ func (sma snapshotMetadataArgs) convert() (*snapshotMetadata, error) {
var timeStamp *time.Time var timeStamp *time.Time
if sma.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 { if err != nil {
return nil, errors.Fatalf("error in time option: %v", err) return nil, errors.Fatalf("error in time option: %v", err)
} }
@ -291,7 +292,7 @@ func filterAndReplaceSnapshot(ctx context.Context, repo restic.Repository, sn *d
return true, nil 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() { if !opts.SnapshotSummary && opts.ExcludePatternOptions.Empty() && opts.Metadata.empty() {
return errors.Fatal("Nothing to do: no excludes provided and no new metadata provided") return errors.Fatal("Nothing to do: no excludes provided and no new metadata provided")
} }

View file

@ -7,12 +7,13 @@ import (
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/filter" "github.com/restic/restic/internal/filter"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
"github.com/restic/restic/internal/ui" "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{ opts := RewriteOptions{
ExcludePatternOptions: filter.ExcludePatternOptions{ ExcludePatternOptions: filter.ExcludePatternOptions{
Excludes: excludes, Excludes: excludes,
@ -21,7 +22,7 @@ func testRunRewriteExclude(t testing.TB, gopts GlobalOptions, excludes []string,
Metadata: metadata, Metadata: metadata,
} }
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runRewrite(context.TODO(), opts, gopts, nil, gopts.Term) return runRewrite(context.TODO(), opts, gopts, nil, gopts.Term)
})) }))
} }
@ -42,7 +43,7 @@ func getSnapshot(t testing.TB, snapshotID restic.ID, env *testEnvironment) *data
t.Helper() t.Helper()
var snapshots []*data.Snapshot var snapshots []*data.Snapshot
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 {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err) rtest.OK(t, err)
@ -118,7 +119,7 @@ func testRewriteMetadata(t *testing.T, metadata snapshotMetadataArgs) {
testRunRewriteExclude(t, env.gopts, []string{}, true, metadata) testRunRewriteExclude(t, env.gopts, []string{}, true, metadata)
var snapshots []*data.Snapshot var snapshots []*data.Snapshot
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 {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err) rtest.OK(t, err)
@ -132,7 +133,7 @@ func testRewriteMetadata(t *testing.T, metadata snapshotMetadataArgs) {
newSnapshot := snapshots[0] newSnapshot := snapshots[0]
if metadata.Time != "" { 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 != "" { if metadata.Hostname != "" {
@ -158,7 +159,7 @@ func TestRewriteSnaphotSummary(t *testing.T) {
defer cleanup() defer cleanup()
createBasicRewriteRepo(t, env) createBasicRewriteRepo(t, env)
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { 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) return runRewrite(context.TODO(), RewriteOptions{SnapshotSummary: true}, gopts, []string{}, gopts.Term)
})) }))
// no new snapshot should be created as the snapshot already has a summary // no new snapshot should be created as the snapshot already has a summary
@ -166,7 +167,7 @@ func TestRewriteSnaphotSummary(t *testing.T) {
// replace snapshot by one without a summary // replace snapshot by one without a summary
var oldSummary *data.SnapshotSummary var oldSummary *data.SnapshotSummary
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 {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
_, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) _, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
rtest.OK(t, err) rtest.OK(t, err)
@ -183,7 +184,7 @@ func TestRewriteSnaphotSummary(t *testing.T) {
rtest.OK(t, err) rtest.OK(t, err)
// rewrite snapshot and lookup ID of new snapshot // rewrite snapshot and lookup ID of new snapshot
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { 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) return runRewrite(context.TODO(), RewriteOptions{SnapshotSummary: true}, gopts, []string{}, gopts.Term)
})) }))
newSnapshots := testListSnapshots(t, env.gopts, 2) newSnapshots := testListSnapshots(t, env.gopts, 2)

View file

@ -8,19 +8,20 @@ import (
"path/filepath" "path/filepath"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/selfupdate" "github.com/restic/restic/internal/selfupdate"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func registerSelfUpdateCommand(cmd *cobra.Command, globalOptions *GlobalOptions) { func registerSelfUpdateCommand(cmd *cobra.Command, globalOptions *global.Options) {
cmd.AddCommand( cmd.AddCommand(
newSelfUpdateCommand(globalOptions), newSelfUpdateCommand(globalOptions),
) )
} }
func newSelfUpdateCommand(globalOptions *GlobalOptions) *cobra.Command { func newSelfUpdateCommand(globalOptions *global.Options) *cobra.Command {
var opts SelfUpdateOptions var opts SelfUpdateOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -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)") 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 == "" { if opts.Output == "" {
file, err := os.Executable() file, err := os.Executable()
if err != nil { if err != nil {
@ -89,12 +90,12 @@ 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) 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 { if err != nil {
return errors.Fatalf("unable to update restic: %v", err) 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) printer.S("successfully updated restic to version %v", v)
} }

View file

@ -2,8 +2,11 @@
package main 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 // No commands to register in non-selfupdate mode
} }

View file

@ -9,6 +9,7 @@ import (
"strings" "strings"
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/table" "github.com/restic/restic/internal/ui/table"
@ -16,7 +17,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newSnapshotsCommand(globalOptions *GlobalOptions) *cobra.Command { func newSnapshotsCommand(globalOptions *global.Options) *cobra.Command {
var opts SnapshotOptions var opts SnapshotOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -68,7 +69,7 @@ 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") 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 { func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts global.Options, args []string, term ui.Terminal) error {
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) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, gopts.NoLock, printer)
if err != nil { if err != nil {
@ -242,7 +243,7 @@ func PrintSnapshots(stdout io.Writer, list data.Snapshots, reasons []data.KeepRe
for _, sn := range list { for _, sn := range list {
data := snapshot{ data := snapshot{
ID: sn.ID().Str(), ID: sn.ID().Str(),
Timestamp: sn.Time.Local().Format(TimeFormat), Timestamp: sn.Time.Local().Format(global.TimeFormat),
Hostname: sn.Hostname, Hostname: sn.Hostname,
Tags: sn.Tags, Tags: sn.Tags,
Paths: sn.Paths, Paths: sn.Paths,

View file

@ -5,12 +5,13 @@ import (
"encoding/json" "encoding/json"
"testing" "testing"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
func testRunSnapshots(t testing.TB, gopts GlobalOptions) (newest *Snapshot, snapmap map[restic.ID]Snapshot) { 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 GlobalOptions) error { buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
gopts.JSON = true gopts.JSON = true
opts := SnapshotOptions{} opts := SnapshotOptions{}

View file

@ -11,6 +11,7 @@ import (
"github.com/restic/chunker" "github.com/restic/chunker"
"github.com/restic/restic/internal/crypto" "github.com/restic/restic/internal/crypto"
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/restorer" "github.com/restic/restic/internal/restorer"
@ -23,7 +24,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newStatsCommand(globalOptions *GlobalOptions) *cobra.Command { func newStatsCommand(globalOptions *global.Options) *cobra.Command {
var opts StatsOptions var opts StatsOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -95,7 +96,7 @@ 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) err := verifyStatsInput(opts)
if err != nil { if err != nil {
return err return err

View file

@ -9,12 +9,13 @@ import (
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
) )
func newTagCommand(globalOptions *GlobalOptions) *cobra.Command { func newTagCommand(globalOptions *global.Options) *cobra.Command {
var opts TagOptions var opts TagOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -118,7 +119,7 @@ func changeTags(ctx context.Context, repo *repository.Repository, sn *data.Snaps
return changed, nil return changed, nil
} }
func runTag(ctx context.Context, opts TagOptions, gopts GlobalOptions, term ui.Terminal, args []string) error { func runTag(ctx context.Context, opts TagOptions, gopts global.Options, term ui.Terminal, args []string) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term)
if len(opts.SetTags) == 0 && len(opts.AddTags) == 0 && len(opts.RemoveTags) == 0 { if len(opts.SetTags) == 0 && len(opts.AddTags) == 0 && len(opts.RemoveTags) == 0 {

View file

@ -5,11 +5,12 @@ import (
"testing" "testing"
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/global"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
) )
func testRunTag(t testing.TB, opts TagOptions, gopts GlobalOptions) { func testRunTag(t testing.TB, opts TagOptions, gopts global.Options) {
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runTag(context.TODO(), opts, gopts, gopts.Term, []string{}) return runTag(context.TODO(), opts, gopts, gopts.Term, []string{})
})) }))
} }

View file

@ -3,13 +3,14 @@ package main
import ( import (
"context" "context"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func newUnlockCommand(globalOptions *GlobalOptions) *cobra.Command { func newUnlockCommand(globalOptions *global.Options) *cobra.Command {
var opts UnlockOptions var opts UnlockOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -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") 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 { func runUnlock(ctx context.Context, opts UnlockOptions, gopts global.Options, term ui.Terminal) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term)
repo, err := OpenRepository(ctx, gopts, printer) repo, err := global.OpenRepository(ctx, gopts, printer)
if err != nil { if err != nil {
return err return err
} }

View file

@ -4,11 +4,12 @@ import (
"encoding/json" "encoding/json"
"runtime" "runtime"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func newVersionCommand(globalOptions *GlobalOptions) *cobra.Command { func newVersionCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "version", Use: "version",
Short: "Print version information", Short: "Print version information",
@ -37,7 +38,7 @@ Exit status is 1 if there was any error.
jsonS := jsonVersion{ jsonS := jsonVersion{
MessageType: "version", MessageType: "version",
Version: version, Version: global.Version,
GoVersion: runtime.Version(), GoVersion: runtime.Version(),
GoOS: runtime.GOOS, GoOS: runtime.GOOS,
GoArch: runtime.GOARCH, GoArch: runtime.GOARCH,
@ -50,7 +51,7 @@ Exit status is 1 if there was any error.
} }
} else { } else {
printer.S("restic %s compiled with %v on %v/%v\n", 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)
} }
}, },
} }

View file

@ -3,12 +3,14 @@ package main
import ( import (
"io" "io"
"testing" "testing"
"github.com/restic/restic/internal/global"
) )
// TestFlags checks for double defined flags, the commands will panic on // TestFlags checks for double defined flags, the commands will panic on
// ParseFlags() when a shorthand flag is defined twice. // ParseFlags() when a shorthand flag is defined twice.
func TestFlags(t *testing.T) { 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) { t.Run(cmd.Name(), func(t *testing.T) {
cmd.Flags().SetOutput(io.Discard) cmd.Flags().SetOutput(io.Discard)
err := cmd.ParseFlags([]string{"--help"}) err := cmd.ParseFlags([]string{"--help"})

View file

@ -5,6 +5,7 @@ import (
"os" "os"
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/ui" "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", return fmt.Sprintf("%s %5d %5d %s %s %s%s",
mode|n.Mode, n.UID, n.GID, size, mode|n.Mode, n.UID, n.GID, size,
n.ModTime.Local().Format(TimeFormat), path, n.ModTime.Local().Format(global.TimeFormat), path,
target) target)
} }

View file

@ -18,6 +18,7 @@ import (
"github.com/restic/restic/internal/backend/retry" "github.com/restic/restic/internal/backend/retry"
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/options" "github.com/restic/restic/internal/options"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
@ -169,7 +170,7 @@ func dirStats(t testing.TB, dir string) (stat dirStat) {
type testEnvironment struct { type testEnvironment struct {
base, cache, repo, mountpoint, testdata string base, cache, repo, mountpoint, testdata string
gopts GlobalOptions gopts global.Options
} }
type logOutputter struct { type logOutputter struct {
@ -209,7 +210,7 @@ func withTestEnvironment(t testing.TB) (env *testEnvironment, cleanup func()) {
rtest.OK(t, os.MkdirAll(env.cache, 0700)) rtest.OK(t, os.MkdirAll(env.cache, 0700))
rtest.OK(t, os.MkdirAll(env.repo, 0700)) rtest.OK(t, os.MkdirAll(env.repo, 0700))
env.gopts = GlobalOptions{ env.gopts = global.Options{
Repo: env.repo, Repo: env.repo,
Quiet: true, Quiet: true,
CacheDir: env.cache, CacheDir: env.cache,
@ -240,9 +241,9 @@ func testSetupBackupData(t testing.TB, env *testEnvironment) string {
return datafile 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 var packs restic.IDSet
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
ctx, r, unlock, err := openWithReadLock(ctx, gopts, false, printer) ctx, r, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err) rtest.OK(t, err)
@ -259,9 +260,9 @@ func listPacks(gopts GlobalOptions, t *testing.T) restic.IDSet {
return packs 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 var treePacks restic.IDSet
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
ctx, r, unlock, err := openWithReadLock(ctx, gopts, false, printer) ctx, r, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err) rtest.OK(t, err)
@ -279,7 +280,7 @@ func listTreePacks(gopts GlobalOptions, t *testing.T) restic.IDSet {
return treePacks return treePacks
} }
func captureBackend(gopts *GlobalOptions) func() backend.Backend { func captureBackend(gopts *global.Options) func() backend.Backend {
var be 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 be = r
@ -290,9 +291,9 @@ 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) be := captureBackend(&gopts)
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
ctx, _, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) ctx, _, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
rtest.OK(t, err) rtest.OK(t, err)
@ -306,9 +307,9 @@ func removePacks(gopts GlobalOptions, t testing.TB, remove restic.IDSet) {
rtest.OK(t, err) 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) be := captureBackend(&gopts)
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
ctx, r, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) ctx, r, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
rtest.OK(t, err) rtest.OK(t, err)
@ -345,7 +346,7 @@ func includes(haystack []string, needle string) bool {
return false 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") snapshotIDs := testRunList(t, gopts, "snapshots")
m := make(map[string]struct{}) m := make(map[string]struct{})
@ -367,9 +368,9 @@ func lastSnapshot(old, new map[string]struct{}) (map[string]struct{}, string) {
return old, "" 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 var snapshot *data.Snapshot
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error { err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
_, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) _, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err) rtest.OK(t, err)
@ -414,20 +415,20 @@ func testFileSize(filename string, size int64) error {
return nil 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) buf := bytes.NewBuffer(nil)
err := withTermStatusRaw(os.Stdin, buf, &logOutputter{t: t}, gopts, callback) err := withTermStatusRaw(os.Stdin, buf, &logOutputter{t: t}, gopts, callback)
return buf, err 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 // 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 // usually consists of one or multiple lines and therefore can be handled well
// by t.Log. // by t.Log.
return withTermStatusRaw(os.Stdin, &logOutputter{t: t}, &logOutputter{t: t}, gopts, callback) 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()) ctx, cancel := context.WithCancel(context.TODO())
var wg sync.WaitGroup var wg sync.WaitGroup

View file

@ -11,6 +11,7 @@ import (
"github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/data" "github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
"github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui"
@ -88,14 +89,14 @@ func TestListOnce(t *testing.T) {
createPrunableRepo(t, env) createPrunableRepo(t, env)
testRunPrune(t, env.gopts, pruneOpts) testRunPrune(t, env.gopts, pruneOpts)
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
_, err := runCheck(context.TODO(), checkOpts, gopts, nil, gopts.Term) _, err := runCheck(context.TODO(), checkOpts, gopts, nil, gopts.Term)
return err return err
})) }))
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.Term) return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.Term)
})) }))
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { 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) return runRebuildIndex(context.TODO(), RepairIndexOptions{ReadAllPacks: true}, gopts, gopts.Term)
})) }))
} }
@ -163,7 +164,7 @@ func TestFindListOnce(t *testing.T) {
thirdSnapshot := restic.NewIDSet(testListSnapshots(t, env.gopts, 3)...) thirdSnapshot := restic.NewIDSet(testListSnapshots(t, env.gopts, 3)...)
var snapshotIDs restic.IDSet var snapshotIDs restic.IDSet
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error { rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term) printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err) rtest.OK(t, err)

View file

@ -3,12 +3,13 @@ package main
import ( import (
"context" "context"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/ui/progress" "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) { func internalOpenWithLocked(ctx context.Context, gopts global.Options, dryRun bool, exclusive bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) {
repo, err := OpenRepository(ctx, gopts, printer) repo, err := global.OpenRepository(ctx, gopts, printer)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
@ -34,16 +35,16 @@ func internalOpenWithLocked(ctx context.Context, gopts GlobalOptions, dryRun boo
return ctx, repo, unlock, nil 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 // TODO enforce read-only operations once the locking code has moved to the repository
return internalOpenWithLocked(ctx, gopts, noLock, false, printer) 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 // TODO enforce non-exclusive operations once the locking code has moved to the repository
return internalOpenWithLocked(ctx, gopts, dryRun, false, printer) 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) return internalOpenWithLocked(ctx, gopts, dryRun, true, printer)
} }

View file

@ -18,6 +18,7 @@ import (
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/feature" "github.com/restic/restic/internal/feature"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui/termstatus" "github.com/restic/restic/internal/ui/termstatus"
@ -33,7 +34,7 @@ var ErrOK = errors.New("ok")
var cmdGroupDefault = "default" var cmdGroupDefault = "default"
var cmdGroupAdvanced = "advanced" var cmdGroupAdvanced = "advanced"
func newRootCommand(globalOptions *GlobalOptions) *cobra.Command { func newRootCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "restic", Use: "restic",
Short: "Backup and restore files", Short: "Backup and restore files",
@ -103,7 +104,7 @@ The full documentation can be found at https://restic.readthedocs.io/ .
registerDebugCommand(cmd, globalOptions) registerDebugCommand(cmd, globalOptions)
registerMountCommand(cmd, globalOptions) registerMountCommand(cmd, globalOptions)
registerSelfUpdateCommand(cmd, globalOptions) registerSelfUpdateCommand(cmd, globalOptions)
registerProfiling(cmd, os.Stderr) global.RegisterProfiling(cmd, os.Stderr)
return cmd return cmd
} }
@ -128,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 { if globalOptions.JSON {
type jsonExitError struct { type jsonExitError struct {
MessageType string `json:"message_type"` // exit_error MessageType string `json:"message_type"` // exit_error
@ -171,9 +172,9 @@ func main() {
debug.Log("main %#v", os.Args) debug.Log("main %#v", os.Args)
debug.Log("restic %s compiled with %v on %v/%v", 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{ globalOptions := global.Options{
Backends: all.Backends(), Backends: all.Backends(),
} }
func() { func() {
@ -221,7 +222,7 @@ func main() {
exitCode = 3 exitCode = 3
case errors.Is(err, ErrFailedToRemoveOneOrMoreSnapshots): case errors.Is(err, ErrFailedToRemoveOneOrMoreSnapshots):
exitCode = 3 exitCode = 3
case errors.Is(err, ErrNoRepository): case errors.Is(err, global.ErrNoRepository):
exitCode = 10 exitCode = 10
case restic.IsAlreadyLocked(err): case restic.IsAlreadyLocked(err):
exitCode = 11 exitCode = 11

View file

@ -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() { func updateVersion() {
err := os.WriteFile("VERSION", []byte(opts.Version+"\n"), 0644) err := os.WriteFile("VERSION", []byte(opts.Version+"\n"), 0644)
@ -318,7 +318,7 @@ func updateVersion() {
die("unable to write version to file: %v", err) 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) replace(versionCodeFile, versionPattern, newVersion)
if len(uncommittedChanges("VERSION")) > 0 || len(uncommittedChanges(versionCodeFile)) > 0 { if len(uncommittedChanges("VERSION")) > 0 || len(uncommittedChanges(versionCodeFile)) > 0 {

View file

@ -1,4 +1,4 @@
package main package global
import ( import (
"context" "context"
@ -33,15 +33,15 @@ import (
// to a missing backend storage location or config file // to a missing backend storage location or config file
var ErrNoRepository = errors.New("repository does not exist") 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. // TimeFormat is the format used for all timestamps printed by restic.
const TimeFormat = "2006-01-02 15:04:05" 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. // Options hold all global options for restic.
type GlobalOptions struct { type Options struct {
Repo string Repo string
RepositoryFile string RepositoryFile string
PasswordFile string PasswordFile string
@ -81,7 +81,7 @@ type GlobalOptions struct {
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.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.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)") f.StringVarP(&opts.PasswordFile, "password-file", "p", "", "`file` to read the repository password from (default: $RESTIC_PASSWORD_FILE)")
@ -132,7 +132,7 @@ 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 // set verbosity, default is one
opts.Verbosity = 1 opts.Verbosity = 1
if opts.Quiet && opts.Verbose > 0 { if opts.Quiet && opts.Verbose > 0 {
@ -166,7 +166,7 @@ func (opts *GlobalOptions) PreRun(needsPassword bool) error {
} }
// resolvePassword determines the password to be used for opening the repository. // 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 != "" { if opts.PasswordFile != "" && opts.PasswordCommand != "" {
return "", errors.Fatalf("Password file and command are mutually exclusive options") return "", errors.Fatalf("Password file and command are mutually exclusive options")
} }
@ -184,7 +184,7 @@ func resolvePassword(opts *GlobalOptions, envStr string) (string, error) {
return strings.TrimSpace(string(output)), nil return strings.TrimSpace(string(output)), nil
} }
if opts.PasswordFile != "" { if opts.PasswordFile != "" {
return loadPasswordFromFile(opts.PasswordFile) return LoadPasswordFromFile(opts.PasswordFile)
} }
if pwd := os.Getenv(envStr); pwd != "" { if pwd := os.Getenv(envStr); pwd != "" {
@ -194,9 +194,9 @@ func resolvePassword(opts *GlobalOptions, envStr string) (string, error) {
return "", nil 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. // converting the password to UTF-8.
func loadPasswordFromFile(pwdFile string) (string, error) { func LoadPasswordFromFile(pwdFile string) (string, error) {
s, err := textfile.Read(pwdFile) s, err := textfile.Read(pwdFile)
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
return "", errors.Fatalf("%s does not exist", pwdFile) return "", errors.Fatalf("%s does not exist", pwdFile)
@ -207,7 +207,7 @@ func loadPasswordFromFile(pwdFile string) (string, error) {
// 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, // variable RESTIC_PASSWORD or prompts the user. If the context is canceled,
// the function leaks the password reading goroutine. // 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.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 "", errors.Fatal("--insecure-no-password must not be specified together with providing a password via a cli option or environment variable")
@ -234,7 +234,7 @@ func ReadPassword(ctx context.Context, gopts GlobalOptions, prompt string) (stri
// ReadPasswordTwice calls ReadPassword two times and returns an error when the // ReadPasswordTwice calls ReadPassword two times and returns an error when the
// passwords don't match. If the context is canceled, the function leaks the // passwords don't match. If the context is canceled, the function leaks the
// password reading goroutine. // password reading goroutine.
func ReadPasswordTwice(ctx context.Context, gopts GlobalOptions, prompt1, prompt2 string) (string, error) { func ReadPasswordTwice(ctx context.Context, gopts Options, prompt1, prompt2 string) (string, error) {
pw1, err := ReadPassword(ctx, gopts, prompt1) pw1, err := ReadPassword(ctx, gopts, prompt1)
if err != nil { if err != nil {
return "", err return "", err
@ -253,7 +253,7 @@ func ReadPasswordTwice(ctx context.Context, gopts GlobalOptions, prompt1, prompt
return pw1, nil return pw1, nil
} }
func ReadRepo(gopts GlobalOptions) (string, error) { func ReadRepo(gopts Options) (string, error) {
if gopts.Repo == "" && gopts.RepositoryFile == "" { if gopts.Repo == "" && gopts.RepositoryFile == "" {
return "", errors.Fatal("Please specify repository location (-r or --repository-file)") return "", errors.Fatal("Please specify repository location (-r or --repository-file)")
} }
@ -281,7 +281,7 @@ func ReadRepo(gopts GlobalOptions) (string, error) {
const maxKeys = 20 const maxKeys = 20
// OpenRepository reads the password and opens the repository. // OpenRepository reads the password and opens the repository.
func OpenRepository(ctx context.Context, gopts GlobalOptions, printer progress.Printer) (*repository.Repository, error) { func OpenRepository(ctx context.Context, gopts Options, printer progress.Printer) (*repository.Repository, error) {
repo, err := ReadRepo(gopts) repo, err := ReadRepo(gopts)
if err != nil { if err != nil {
return nil, err return nil, err
@ -403,7 +403,7 @@ func parseConfig(loc location.Location, opts options.Options) (interface{}, erro
return cfg, nil return cfg, nil
} }
func innerOpen(ctx context.Context, s string, gopts GlobalOptions, opts options.Options, create bool, printer progress.Printer) (backend.Backend, error) { func innerOpen(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)) debug.Log("parsing location %v", location.StripPassword(gopts.Backends, s))
loc, err := location.Parse(gopts.Backends, s) loc, err := location.Parse(gopts.Backends, s)
if err != nil { if err != nil {
@ -483,7 +483,7 @@ func innerOpen(ctx context.Context, s string, gopts GlobalOptions, opts options.
} }
// Open the backend specified by a location config. // 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) { func open(ctx context.Context, s string, gopts Options, opts options.Options, printer progress.Printer) (backend.Backend, error) {
be, err := innerOpen(ctx, s, gopts, opts, false, printer) be, err := innerOpen(ctx, s, gopts, opts, false, printer)
if err != nil { if err != nil {
return nil, err return nil, err
@ -507,6 +507,6 @@ func open(ctx context.Context, s string, gopts GlobalOptions, opts options.Optio
} }
// Create the backend specified by URI. // Create the backend specified by URI.
func create(ctx context.Context, s string, gopts GlobalOptions, opts options.Options, printer progress.Printer) (backend.Backend, error) { func Create(ctx context.Context, s string, gopts Options, opts options.Options, printer progress.Printer) (backend.Backend, error) {
return innerOpen(ctx, s, gopts, opts, true, printer) return innerOpen(ctx, s, gopts, opts, true, printer)
} }

View file

@ -1,7 +1,7 @@
//go:build debug || profile //go:build debug || profile
// +build debug profile // +build debug profile
package main package global
import ( import (
"fmt" "fmt"
@ -17,8 +17,8 @@ import (
"github.com/pkg/profile" "github.com/pkg/profile"
) )
func registerProfiling(cmd *cobra.Command, stderr io.Writer) { func RegisterProfiling(cmd *cobra.Command, stderr io.Writer) {
var profiler profiler var profiler Profiler
origPreRun := cmd.PersistentPreRunE origPreRun := cmd.PersistentPreRunE
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { 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()) profiler.opts.AddFlags(cmd.PersistentFlags())
} }
type profiler struct { type Profiler struct {
opts ProfileOptions opts ProfileOptions
stop interface { stop interface {
Stop() Stop()
@ -73,7 +73,7 @@ func (t fakeTestingTB) Logf(msg string, args ...interface{}) {
fmt.Fprintf(t.stderr, msg, args...) 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 != "" { if profileOpts.listen != "" {
fmt.Fprintf(stderr, "running profile HTTP server on %v\n", profileOpts.listen) fmt.Fprintf(stderr, "running profile HTTP server on %v\n", profileOpts.listen)
go func() { go func() {
@ -119,7 +119,7 @@ func (p *profiler) Start(profileOpts ProfileOptions, stderr io.Writer) error {
return nil return nil
} }
func (p *profiler) Stop() { func (p *Profiler) Stop() {
if p.stop != nil { if p.stop != nil {
p.stop.Stop() p.stop.Stop()
} }

View file

@ -1,7 +1,7 @@
//go:build !debug && !profile //go:build !debug && !profile
// +build !debug,!profile // +build !debug,!profile
package main package global
import ( import (
"io" "io"
@ -9,6 +9,6 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func registerProfiling(_ *cobra.Command, _ io.Writer) { func RegisterProfiling(_ *cobra.Command, _ io.Writer) {
// No profiling in release mode // No profiling in release mode
} }

View file

@ -1,4 +1,4 @@
package main package global
import ( import (
"context" "context"
@ -14,7 +14,7 @@ func TestReadRepo(t *testing.T) {
tempDir := rtest.TempDir(t) tempDir := rtest.TempDir(t)
// test --repo option // test --repo option
var gopts GlobalOptions var gopts Options
gopts.Repo = tempDir gopts.Repo = tempDir
repo, err := ReadRepo(gopts) repo, err := ReadRepo(gopts)
rtest.OK(t, err) rtest.OK(t, err)
@ -25,13 +25,13 @@ func TestReadRepo(t *testing.T) {
err = os.WriteFile(foo, []byte(tempDir+"\n"), 0666) err = os.WriteFile(foo, []byte(tempDir+"\n"), 0666)
rtest.OK(t, err) rtest.OK(t, err)
var gopts2 GlobalOptions var gopts2 Options
gopts2.RepositoryFile = foo gopts2.RepositoryFile = foo
repo, err = ReadRepo(gopts2) repo, err = ReadRepo(gopts2)
rtest.OK(t, err) rtest.OK(t, err)
rtest.Equals(t, tempDir, repo) rtest.Equals(t, tempDir, repo)
var gopts3 GlobalOptions var gopts3 Options
gopts3.RepositoryFile = foo + "-invalid" gopts3.RepositoryFile = foo + "-invalid"
_, err = ReadRepo(gopts3) _, err = ReadRepo(gopts3)
if err == nil { if err == nil {
@ -40,7 +40,7 @@ func TestReadRepo(t *testing.T) {
} }
func TestReadEmptyPassword(t *testing.T) { func TestReadEmptyPassword(t *testing.T) {
opts := GlobalOptions{InsecureNoPassword: true} opts := Options{InsecureNoPassword: true}
password, err := ReadPassword(context.TODO(), opts, "test") password, err := ReadPassword(context.TODO(), opts, "test")
rtest.OK(t, err) rtest.OK(t, err)
rtest.Equals(t, "", password, "got unexpected password") rtest.Equals(t, "", password, "got unexpected password")

View file

@ -1,4 +1,4 @@
package main package global
import ( import (
"context" "context"
@ -8,8 +8,8 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
type secondaryRepoOptions struct { type SecondaryRepoOptions struct {
password string Password string
// from-repo options // from-repo options
Repo string Repo string
RepositoryFile string RepositoryFile string
@ -25,7 +25,7 @@ type secondaryRepoOptions struct {
LegacyKeyHint string 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.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.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)") 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") 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 == "" { 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 != "" || hasFromRepo := opts.Repo != "" || opts.RepositoryFile != "" || opts.PasswordFile != "" ||
@ -70,7 +70,7 @@ func fillSecondaryGlobalOpts(ctx context.Context, opts secondaryRepoOptions, gop
opts.LegacyKeyHint != "" || opts.LegacyPasswordCommand != "" opts.LegacyKeyHint != "" || opts.LegacyPasswordCommand != ""
if hasFromRepo && hasRepo2 { 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 var err error
@ -79,7 +79,7 @@ func fillSecondaryGlobalOpts(ctx context.Context, opts secondaryRepoOptions, gop
if hasFromRepo { if hasFromRepo {
if opts.Repo != "" && opts.RepositoryFile != "" { 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 dstGopts.Repo = opts.Repo
@ -93,7 +93,7 @@ func fillSecondaryGlobalOpts(ctx context.Context, opts secondaryRepoOptions, gop
repoPrefix = "source" repoPrefix = "source"
} else { } else {
if opts.LegacyRepo != "" && opts.LegacyRepositoryFile != "" { 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 dstGopts.Repo = opts.LegacyRepo
@ -107,17 +107,17 @@ func fillSecondaryGlobalOpts(ctx context.Context, opts secondaryRepoOptions, gop
pwdEnv = "RESTIC_PASSWORD2" pwdEnv = "RESTIC_PASSWORD2"
} }
if opts.password != "" { if opts.Password != "" {
dstGopts.Password = opts.password dstGopts.Password = opts.Password
} else { } else {
dstGopts.Password, err = resolvePassword(&dstGopts, pwdEnv) dstGopts.Password, err = resolvePassword(&dstGopts, pwdEnv)
if err != nil { 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 { if err != nil {
return GlobalOptions{}, false, err return Options{}, false, err
} }
return dstGopts, hasFromRepo, nil return dstGopts, hasFromRepo, nil
} }

View file

@ -1,4 +1,4 @@
package main package global
import ( import (
"context" "context"
@ -13,8 +13,8 @@ import (
func TestFillSecondaryGlobalOpts(t *testing.T) { func TestFillSecondaryGlobalOpts(t *testing.T) {
//secondaryRepoTestCase defines a struct for test cases //secondaryRepoTestCase defines a struct for test cases
type secondaryRepoTestCase struct { type secondaryRepoTestCase struct {
Opts secondaryRepoOptions Opts SecondaryRepoOptions
DstGOpts GlobalOptions DstGOpts Options
FromRepo bool FromRepo bool
} }
@ -22,11 +22,11 @@ func TestFillSecondaryGlobalOpts(t *testing.T) {
var validSecondaryRepoTestCases = []secondaryRepoTestCase{ var validSecondaryRepoTestCases = []secondaryRepoTestCase{
{ {
// Test if Repo and Password are parsed correctly. // Test if Repo and Password are parsed correctly.
Opts: secondaryRepoOptions{ Opts: SecondaryRepoOptions{
Repo: "backupDst", Repo: "backupDst",
password: "secretDst", Password: "secretDst",
}, },
DstGOpts: GlobalOptions{ DstGOpts: Options{
Repo: "backupDst", Repo: "backupDst",
Password: "secretDst", Password: "secretDst",
}, },
@ -34,11 +34,11 @@ func TestFillSecondaryGlobalOpts(t *testing.T) {
}, },
{ {
// Test if RepositoryFile and PasswordFile are parsed correctly. // Test if RepositoryFile and PasswordFile are parsed correctly.
Opts: secondaryRepoOptions{ Opts: SecondaryRepoOptions{
RepositoryFile: "backupDst", RepositoryFile: "backupDst",
PasswordFile: "passwordFileDst", PasswordFile: "passwordFileDst",
}, },
DstGOpts: GlobalOptions{ DstGOpts: Options{
RepositoryFile: "backupDst", RepositoryFile: "backupDst",
Password: "secretDst", Password: "secretDst",
PasswordFile: "passwordFileDst", PasswordFile: "passwordFileDst",
@ -47,11 +47,11 @@ func TestFillSecondaryGlobalOpts(t *testing.T) {
}, },
{ {
// Test if RepositoryFile and PasswordCommand are parsed correctly. // Test if RepositoryFile and PasswordCommand are parsed correctly.
Opts: secondaryRepoOptions{ Opts: SecondaryRepoOptions{
RepositoryFile: "backupDst", RepositoryFile: "backupDst",
PasswordCommand: "echo secretDst", PasswordCommand: "echo secretDst",
}, },
DstGOpts: GlobalOptions{ DstGOpts: Options{
RepositoryFile: "backupDst", RepositoryFile: "backupDst",
Password: "secretDst", Password: "secretDst",
PasswordCommand: "echo secretDst", PasswordCommand: "echo secretDst",
@ -60,22 +60,22 @@ func TestFillSecondaryGlobalOpts(t *testing.T) {
}, },
{ {
// Test if LegacyRepo and Password are parsed correctly. // Test if LegacyRepo and Password are parsed correctly.
Opts: secondaryRepoOptions{ Opts: SecondaryRepoOptions{
LegacyRepo: "backupDst", LegacyRepo: "backupDst",
password: "secretDst", Password: "secretDst",
}, },
DstGOpts: GlobalOptions{ DstGOpts: Options{
Repo: "backupDst", Repo: "backupDst",
Password: "secretDst", Password: "secretDst",
}, },
}, },
{ {
// Test if LegacyRepositoryFile and LegacyPasswordFile are parsed correctly. // Test if LegacyRepositoryFile and LegacyPasswordFile are parsed correctly.
Opts: secondaryRepoOptions{ Opts: SecondaryRepoOptions{
LegacyRepositoryFile: "backupDst", LegacyRepositoryFile: "backupDst",
LegacyPasswordFile: "passwordFileDst", LegacyPasswordFile: "passwordFileDst",
}, },
DstGOpts: GlobalOptions{ DstGOpts: Options{
RepositoryFile: "backupDst", RepositoryFile: "backupDst",
Password: "secretDst", Password: "secretDst",
PasswordFile: "passwordFileDst", PasswordFile: "passwordFileDst",
@ -83,11 +83,11 @@ func TestFillSecondaryGlobalOpts(t *testing.T) {
}, },
{ {
// Test if LegacyRepositoryFile and LegacyPasswordCommand are parsed correctly. // Test if LegacyRepositoryFile and LegacyPasswordCommand are parsed correctly.
Opts: secondaryRepoOptions{ Opts: SecondaryRepoOptions{
LegacyRepositoryFile: "backupDst", LegacyRepositoryFile: "backupDst",
LegacyPasswordCommand: "echo secretDst", LegacyPasswordCommand: "echo secretDst",
}, },
DstGOpts: GlobalOptions{ DstGOpts: Options{
RepositoryFile: "backupDst", RepositoryFile: "backupDst",
Password: "secretDst", Password: "secretDst",
PasswordCommand: "echo secretDst", PasswordCommand: "echo secretDst",
@ -99,18 +99,18 @@ func TestFillSecondaryGlobalOpts(t *testing.T) {
var invalidSecondaryRepoTestCases = []secondaryRepoTestCase{ var invalidSecondaryRepoTestCases = []secondaryRepoTestCase{
{ {
// Test must fail on no repo given. // Test must fail on no repo given.
Opts: secondaryRepoOptions{}, Opts: SecondaryRepoOptions{},
}, },
{ {
// Test must fail as Repo and RepositoryFile are both given // Test must fail as Repo and RepositoryFile are both given
Opts: secondaryRepoOptions{ Opts: SecondaryRepoOptions{
Repo: "backupDst", Repo: "backupDst",
RepositoryFile: "backupDst", RepositoryFile: "backupDst",
}, },
}, },
{ {
// Test must fail as PasswordFile and PasswordCommand are both given // Test must fail as PasswordFile and PasswordCommand are both given
Opts: secondaryRepoOptions{ Opts: SecondaryRepoOptions{
Repo: "backupDst", Repo: "backupDst",
PasswordFile: "passwordFileDst", PasswordFile: "passwordFileDst",
PasswordCommand: "notEmpty", PasswordCommand: "notEmpty",
@ -118,28 +118,28 @@ func TestFillSecondaryGlobalOpts(t *testing.T) {
}, },
{ {
// Test must fail as PasswordFile does not exist // Test must fail as PasswordFile does not exist
Opts: secondaryRepoOptions{ Opts: SecondaryRepoOptions{
Repo: "backupDst", Repo: "backupDst",
PasswordFile: "NonExistingFile", PasswordFile: "NonExistingFile",
}, },
}, },
{ {
// Test must fail as PasswordCommand does not exist // Test must fail as PasswordCommand does not exist
Opts: secondaryRepoOptions{ Opts: SecondaryRepoOptions{
Repo: "backupDst", Repo: "backupDst",
PasswordCommand: "notEmpty", PasswordCommand: "notEmpty",
}, },
}, },
{ {
// Test must fail as current and legacy options are mixed // Test must fail as current and legacy options are mixed
Opts: secondaryRepoOptions{ Opts: SecondaryRepoOptions{
Repo: "backupDst", Repo: "backupDst",
LegacyRepo: "backupDst", LegacyRepo: "backupDst",
}, },
}, },
{ {
// Test must fail as current and legacy options are mixed // Test must fail as current and legacy options are mixed
Opts: secondaryRepoOptions{ Opts: SecondaryRepoOptions{
Repo: "backupDst", Repo: "backupDst",
LegacyPasswordCommand: "notEmpty", LegacyPasswordCommand: "notEmpty",
}, },
@ -147,7 +147,7 @@ func TestFillSecondaryGlobalOpts(t *testing.T) {
} }
//gOpts defines the Global options used in the secondary repository tests //gOpts defines the Global options used in the secondary repository tests
var gOpts = GlobalOptions{ var gOpts = Options{
Repo: "backupSrc", Repo: "backupSrc",
RepositoryFile: "backupSrc", RepositoryFile: "backupSrc",
Password: "secretSrc", Password: "secretSrc",
@ -165,7 +165,7 @@ func TestFillSecondaryGlobalOpts(t *testing.T) {
// Test all valid cases // Test all valid cases
for _, testCase := range validSecondaryRepoTestCases { 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.OK(t, err)
rtest.Equals(t, DstGOpts, testCase.DstGOpts) rtest.Equals(t, DstGOpts, testCase.DstGOpts)
rtest.Equals(t, isFromRepo, testCase.FromRepo) rtest.Equals(t, isFromRepo, testCase.FromRepo)
@ -173,7 +173,7 @@ func TestFillSecondaryGlobalOpts(t *testing.T) {
// Test all invalid cases // Test all invalid cases
for _, testCase := range invalidSecondaryRepoTestCases { 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") rtest.Assert(t, err != nil, "Expected error, but function did not return an error")
} }
} }