Create root command via function

This commit is contained in:
Michael Eischer 2025-02-07 18:28:56 +01:00
parent aa9cdf93cf
commit 412d6d9ec5
43 changed files with 163 additions and 215 deletions

View File

@ -74,10 +74,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newBackupCommand())
}
// BackupOptions bundles all options for the backup command.
type BackupOptions struct {
filter.ExcludePatternOptions

View File

@ -42,10 +42,6 @@ Exit status is 1 if there was any error.
return cmd
}
func init() {
cmdRoot.AddCommand(newCacheCommand())
}
// CacheOptions bundles all options for the snapshots command.
type CacheOptions struct {
Cleanup bool

View File

@ -40,10 +40,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newCatCommand())
}
func validateCatArgs(args []string) error {
if len(args) < 1 {
return errors.Fatal("type not specified")

View File

@ -67,10 +67,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newCheckCommand())
}
// CheckOptions bundles all options for the 'check' command.
type CheckOptions struct {
ReadData bool

View File

@ -54,10 +54,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newCopyCommand())
}
// CopyOptions bundles all options for the copy command.
type CopyOptions struct {
secondaryRepoOptions

View File

@ -29,6 +29,12 @@ import (
"github.com/restic/restic/internal/restic"
)
func registerDebugCommand(cmd *cobra.Command) {
cmd.AddCommand(
newDebugCommand(),
)
}
func newDebugCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "debug",
@ -41,10 +47,6 @@ func newDebugCommand() *cobra.Command {
return cmd
}
func init() {
cmdRoot.AddCommand(newDebugCommand())
}
func newDebugDumpCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "dump [indexes|snapshots|all|packs]",

View File

@ -0,0 +1,9 @@
//go:build !debug
package main
import "github.com/spf13/cobra"
func registerDebugCommand(_ *cobra.Command) {
// No commands to register in non-debug mode
}

View File

@ -60,10 +60,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newDiffCommand())
}
// DiffOptions collects all options for the diff command.
type DiffOptions struct {
ShowMetadata bool

View File

@ -54,10 +54,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newDumpCommand())
}
// DumpOptions collects all options for the dump command.
type DumpOptions struct {
restic.SnapshotFilter

View File

@ -57,7 +57,3 @@ Exit status is 1 if there was any error.
return cmd
}
func init() {
cmdRoot.AddCommand(newFeaturesCommand())
}

View File

@ -56,10 +56,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newFindCommand())
}
// FindOptions bundles all options for the find command.
type FindOptions struct {
Oldest string

View File

@ -59,10 +59,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newForgetCommand())
}
type ForgetPolicyCount int
var ErrNegativePolicyCount = errors.New("negative values not allowed, use 'unlimited' instead")

View File

@ -36,10 +36,6 @@ Exit status is 1 if there was any error.
return cmd
}
func init() {
cmdRoot.AddCommand(newGenerateCommand())
}
type generateOptions struct {
ManDir string
BashCompletionFile string
@ -56,7 +52,7 @@ func (opts *generateOptions) AddFlags(f *pflag.FlagSet) {
f.StringVar(&opts.PowerShellCompletionFile, "powershell-completion", "", "write powershell completion `file` (`-` for stdout)")
}
func writeManpages(dir string) error {
func writeManpages(root *cobra.Command, dir string) error {
// use a fixed date for the man pages so that generating them is deterministic
date, err := time.Parse("Jan 2006", "Jan 2017")
if err != nil {
@ -71,7 +67,7 @@ func writeManpages(dir string) error {
}
Verbosef("writing man pages to directory %v\n", dir)
return doc.GenManTree(cmdRoot, header, dir)
return doc.GenManTree(root, header, dir)
}
func writeCompletion(filename string, shell string, generate func(w io.Writer) error) (err error) {
@ -119,8 +115,10 @@ func runGenerate(opts generateOptions, args []string) error {
return errors.Fatal("the generate command expects no arguments, only options - please see `restic help generate` for usage and flags")
}
cmdRoot := newRootCommand()
if opts.ManDir != "" {
err := writeManpages(opts.ManDir)
err := writeManpages(cmdRoot, opts.ManDir)
if err != nil {
return err
}

View File

@ -40,10 +40,6 @@ Exit status is 1 if there was any error.
return cmd
}
func init() {
cmdRoot.AddCommand(newInitCommand())
}
// InitOptions bundles all options for the init command.
type InitOptions struct {
secondaryRepoOptions

View File

@ -4,17 +4,23 @@ import (
"github.com/spf13/cobra"
)
var cmdKey = &cobra.Command{
Use: "key",
Short: "Manage keys (passwords)",
Long: `
func newKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "key",
Short: "Manage keys (passwords)",
Long: `
The "key" command allows you to set multiple access keys or passwords
per repository.
`,
DisableAutoGenTag: true,
GroupID: cmdGroupDefault,
}
DisableAutoGenTag: true,
GroupID: cmdGroupDefault,
}
func init() {
cmdRoot.AddCommand(cmdKey)
cmd.AddCommand(
newKeyAddCommand(),
newKeyListCommand(),
newKeyPasswdCommand(),
newKeyRemoveCommand(),
)
return cmd
}

View File

@ -52,10 +52,6 @@ func (opts *KeyAddOptions) Add(flags *pflag.FlagSet) {
flags.StringVarP(&opts.Hostname, "host", "", "", "the hostname for new key")
}
func init() {
cmdKey.AddCommand(newKeyAddCommand())
}
func runKeyAdd(ctx context.Context, gopts GlobalOptions, opts KeyAddOptions, args []string) error {
if len(args) > 0 {
return fmt.Errorf("the key add command expects no arguments, only options - please see `restic help key add` for usage and flags")

View File

@ -38,10 +38,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdKey.AddCommand(newKeyListCommand())
}
func runKeyList(ctx context.Context, gopts GlobalOptions, args []string) error {
if len(args) > 0 {
return fmt.Errorf("the key list command expects no arguments, only options - please see `restic help key list` for usage and flags")

View File

@ -39,10 +39,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdKey.AddCommand(newKeyPasswdCommand())
}
type KeyPasswdOptions struct {
KeyAddOptions
}

View File

@ -35,10 +35,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdKey.AddCommand(newKeyRemoveCommand())
}
func runKeyRemove(ctx context.Context, gopts GlobalOptions, args []string) error {
if len(args) != 1 {
return fmt.Errorf("key remove expects one argument as the key id")

View File

@ -41,10 +41,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newListCommand())
}
func runList(ctx context.Context, gopts GlobalOptions, args []string) error {
if len(args) != 1 {
return errors.Fatal("type not specified")

View File

@ -66,10 +66,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newLsCommand())
}
// LsOptions collects all options for the ls command.
type LsOptions struct {
ListLong bool

View File

@ -45,10 +45,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newMigrateCommand())
}
// MigrateOptions bundles all options for the 'check' command.
type MigrateOptions struct {
Force bool

View File

@ -22,6 +22,10 @@ import (
"github.com/anacrolix/fuse/fs"
)
func registerMountCommand(cmdRoot *cobra.Command) {
cmdRoot.AddCommand(newMountCommand())
}
func newMountCommand() *cobra.Command {
var opts MountOptions
@ -84,10 +88,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newMountCommand())
}
// MountOptions collects all options for the mount command.
type MountOptions struct {
OwnerRoot bool

View File

@ -0,0 +1,10 @@
//go:build !darwin && !freebsd && !linux
// +build !darwin,!freebsd,!linux
package main
import "github.com/spf13/cobra"
func registerMountCommand(_ *cobra.Command) {
// Mount command not supported on these platforms
}

View File

@ -38,7 +38,3 @@ Exit status is 1 if there was any error.
}
return cmd
}
func init() {
cmdRoot.AddCommand(newOptionsCommand())
}

View File

@ -51,10 +51,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newPruneCommand())
}
// PruneOptions collects all options for the cleanup command.
type PruneOptions struct {
DryRun bool

View File

@ -38,10 +38,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newRecoverCommand())
}
func runRecover(ctx context.Context, gopts GlobalOptions) error {
hostname, err := os.Hostname()
if err != nil {

View File

@ -4,13 +4,18 @@ import (
"github.com/spf13/cobra"
)
var cmdRepair = &cobra.Command{
Use: "repair",
Short: "Repair the repository",
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
}
func newRepairCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "repair",
Short: "Repair the repository",
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
}
func init() {
cmdRoot.AddCommand(cmdRepair)
cmd.AddCommand(
newRepairIndexCommand(),
newRepairPacksCommand(),
newRepairSnapshotsCommand(),
)
return cmd
}

View File

@ -40,10 +40,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRepair.AddCommand(newRepairIndexCommand())
}
// RepairIndexOptions collects all options for the repair index command.
type RepairIndexOptions struct {
ReadAllPacks bool
@ -76,11 +72,6 @@ func newRebuildIndexCommand() *cobra.Command {
return cmd
}
func init() {
// add alias for old name
cmdRoot.AddCommand(newRebuildIndexCommand())
}
func runRebuildIndex(ctx context.Context, opts RepairIndexOptions, gopts GlobalOptions, term *termstatus.Terminal) error {
ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false)
if err != nil {

View File

@ -40,10 +40,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRepair.AddCommand(newRepairPacksCommand())
}
func runRepairPacks(ctx context.Context, gopts GlobalOptions, term *termstatus.Terminal, args []string) error {
ids := restic.NewIDSet()
for _, arg := range args {

View File

@ -57,10 +57,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRepair.AddCommand(newRepairSnapshotsCommand())
}
// RepairOptions collects all options for the repair command.
type RepairOptions struct {
DryRun bool

View File

@ -56,10 +56,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newRestoreCommand())
}
// RestoreOptions collects all options for the restore command.
type RestoreOptions struct {
filter.ExcludePatternOptions

View File

@ -66,10 +66,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newRewriteCommand())
}
type snapshotMetadata struct {
Hostname string
Time *time.Time

View File

@ -13,6 +13,12 @@ import (
"github.com/spf13/pflag"
)
func registerSelfUpdateCommand(cmd *cobra.Command) {
cmd.AddCommand(
newSelfUpdateCommand(),
)
}
func newSelfUpdateCommand() *cobra.Command {
var opts SelfUpdateOptions
@ -44,10 +50,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newSelfUpdateCommand())
}
// SelfUpdateOptions collects all options for the update-restic command.
type SelfUpdateOptions struct {
Output string

View File

@ -0,0 +1,9 @@
//go:build !selfupdate
package main
import "github.com/spf13/cobra"
func registerSelfUpdateCommand(_ *cobra.Command) {
// No commands to register in non-selfupdate mode
}

View File

@ -44,10 +44,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newSnapshotsCommand())
}
// SnapshotOptions bundles all options for the snapshots command.
type SnapshotOptions struct {
restic.SnapshotFilter

View File

@ -73,10 +73,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newStatsCommand())
}
// StatsOptions collects all options for the stats command.
type StatsOptions struct {
// the mode of counting to perform (see consts for available modes)

View File

@ -50,10 +50,6 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func init() {
cmdRoot.AddCommand(newTagCommand())
}
// TagOptions bundles all options for the 'tag' command.
type TagOptions struct {
restic.SnapshotFilter

View File

@ -33,10 +33,6 @@ Exit status is 1 if there was any error.
return cmd
}
func init() {
cmdRoot.AddCommand(newUnlockCommand())
}
// UnlockOptions collects all options for the unlock command.
type UnlockOptions struct {
RemoveAll bool

View File

@ -55,7 +55,3 @@ Exit status is 1 if there was any error.
}
return cmd
}
func init() {
cmdRoot.AddCommand(newVersionCommand())
}

View File

@ -8,7 +8,7 @@ import (
// TestFlags checks for double defined flags, the commands will panic on
// ParseFlags() when a shorthand flag is defined twice.
func TestFlags(t *testing.T) {
for _, cmd := range cmdRoot.Commands() {
for _, cmd := range newRootCommand().Commands() {
t.Run(cmd.Name(), func(t *testing.T) {
cmd.Flags().SetOutput(io.Discard)
err := cmd.ParseFlags([]string{"--help"})

View File

@ -5,6 +5,6 @@ package main
import "github.com/spf13/cobra"
func registerProfiling(cmd *cobra.Command) {
func registerProfiling(_ *cobra.Command) {
// No profiling in release mode
}

View File

@ -29,60 +29,60 @@ func init() {
var ErrOK = errors.New("ok")
// cmdRoot is the base command when no other command has been specified.
var cmdRoot = &cobra.Command{
Use: "restic",
Short: "Backup and restore files",
Long: `
var cmdGroupDefault = "default"
var cmdGroupAdvanced = "advanced"
func newRootCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "restic",
Short: "Backup and restore files",
Long: `
restic is a backup program which allows saving multiple revisions of files and
directories in an encrypted repository stored on different backends.
The full documentation can be found at https://restic.readthedocs.io/ .
`,
SilenceErrors: true,
SilenceUsage: true,
DisableAutoGenTag: true,
SilenceErrors: true,
SilenceUsage: true,
DisableAutoGenTag: true,
PersistentPreRunE: func(c *cobra.Command, _ []string) error {
// set verbosity, default is one
globalOptions.verbosity = 1
if globalOptions.Quiet && globalOptions.Verbose > 0 {
return errors.Fatal("--quiet and --verbose cannot be specified at the same time")
}
PersistentPreRunE: func(c *cobra.Command, _ []string) error {
// set verbosity, default is one
globalOptions.verbosity = 1
if globalOptions.Quiet && globalOptions.Verbose > 0 {
return errors.Fatal("--quiet and --verbose cannot be specified at the same time")
}
switch {
case globalOptions.Verbose >= 2:
globalOptions.verbosity = 3
case globalOptions.Verbose > 0:
globalOptions.verbosity = 2
case globalOptions.Quiet:
globalOptions.verbosity = 0
}
switch {
case globalOptions.Verbose >= 2:
globalOptions.verbosity = 3
case globalOptions.Verbose > 0:
globalOptions.verbosity = 2
case globalOptions.Quiet:
globalOptions.verbosity = 0
}
// parse extended options
opts, err := options.Parse(globalOptions.Options)
if err != nil {
return err
}
globalOptions.extended = opts
if !needsPassword(c.Name()) {
return nil
}
pwd, err := resolvePassword(globalOptions, "RESTIC_PASSWORD")
if err != nil {
fmt.Fprintf(os.Stderr, "Resolving password failed: %v\n", err)
Exit(1)
}
globalOptions.password = pwd
// parse extended options
opts, err := options.Parse(globalOptions.Options)
if err != nil {
return err
}
globalOptions.extended = opts
if !needsPassword(c.Name()) {
return nil
}
pwd, err := resolvePassword(globalOptions, "RESTIC_PASSWORD")
if err != nil {
fmt.Fprintf(os.Stderr, "Resolving password failed: %v\n", err)
Exit(1)
}
globalOptions.password = pwd
return nil
},
}
},
}
var cmdGroupDefault = "default"
var cmdGroupAdvanced = "advanced"
func init() {
cmdRoot.AddGroup(
cmd.AddGroup(
&cobra.Group{
ID: cmdGroupDefault,
Title: "Available Commands:",
@ -93,12 +93,48 @@ func init() {
},
)
globalOptions.AddFlags(cmdRoot.PersistentFlags())
globalOptions.AddFlags(cmd.PersistentFlags())
// Use our "generate" command instead of the cobra provided "completion" command
cmdRoot.CompletionOptions.DisableDefaultCmd = true
cmd.CompletionOptions.DisableDefaultCmd = true
registerProfiling(cmdRoot)
cmd.AddCommand(
newBackupCommand(),
newCacheCommand(),
newCatCommand(),
newCheckCommand(),
newCopyCommand(),
newDiffCommand(),
newDumpCommand(),
newFeaturesCommand(),
newFindCommand(),
newForgetCommand(),
newGenerateCommand(),
newInitCommand(),
newKeyCommand(),
newListCommand(),
newLsCommand(),
newMigrateCommand(),
newOptionsCommand(),
newPruneCommand(),
newRebuildIndexCommand(),
newRecoverCommand(),
newRepairCommand(),
newRestoreCommand(),
newRewriteCommand(),
newSnapshotsCommand(),
newStatsCommand(),
newTagCommand(),
newUnlockCommand(),
newVersionCommand(),
)
registerDebugCommand(cmd)
registerMountCommand(cmd)
registerSelfUpdateCommand(cmd)
registerProfiling(cmd)
return cmd
}
// Distinguish commands that need the password from those that work without,
@ -165,7 +201,7 @@ func main() {
version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
ctx := createGlobalContext()
err = cmdRoot.ExecuteContext(ctx)
err = newRootCommand().ExecuteContext(ctx)
if err == nil {
err = ctx.Err()