diff --git a/cli_test.go b/cli_test.go index 1bb6e78..ae7cfd4 100644 --- a/cli_test.go +++ b/cli_test.go @@ -45,7 +45,7 @@ func TestCliUsage(t *testing.T) { t.Fatal(err) } - gotUsage := stderr.Bytes() + gotUsage := stdout.Bytes() expectedUsage, err := os.ReadFile(goldenFilepath) if err != nil { diff --git a/config_file_test.go b/config_file_test.go index 7d261e5..82794dc 100644 --- a/config_file_test.go +++ b/config_file_test.go @@ -1,13 +1,38 @@ package main import ( + "fmt" "os" "path/filepath" "testing" + "github.com/urfave/cli/v2" "gopkg.in/yaml.v3" ) +// buildTestConfig creates a minimal cli.App with appFlags(), runs it with the +// given args, and returns the appConfig produced by buildConfig. +func buildTestConfig(args []string) (*appConfig, error) { + var result *appConfig + var buildErr error + + app := &cli.App{ + Name: "gitbackup", + Flags: appFlags(), + Action: func(cCtx *cli.Context) error { + result, buildErr = buildConfig(cCtx) + return buildErr + }, + } + + // urfave/cli expects the program name as args[0] + fullArgs := append([]string{"gitbackup"}, args...) + if err := app.Run(fullArgs); err != nil { + return nil, fmt.Errorf("app.Run: %w", err) + } + return result, buildErr +} + func TestHandleInitConfig(t *testing.T) { tmpDir := t.TempDir() configPath := filepath.Join(tmpDir, defaultConfigFile) @@ -127,8 +152,8 @@ func TestInitConfigWithConfigFile(t *testing.T) { os.Setenv("GITLAB_TOKEN", "testtoken") defer os.Unsetenv("GITLAB_TOKEN") - // initConfig with --config flag should use config file values - c, err := initConfig([]string{"-config", configPath}) + // buildTestConfig with --config flag should use config file values + c, err := buildTestConfig([]string{"-config", configPath}) if err != nil { t.Fatalf("Expected no error, got: %v", err) } @@ -160,7 +185,7 @@ func TestInitConfigCLIOverridesConfigFile(t *testing.T) { defer os.Unsetenv("GITLAB_TOKEN") // CLI flag overrides service to github - c, err := initConfig([]string{"-config", configPath, "-service", "github"}) + c, err := buildTestConfig([]string{"-config", configPath, "-service", "github"}) if err != nil { t.Fatalf("Expected no error, got: %v", err) } @@ -177,7 +202,7 @@ func TestInitConfigCLIOverridesConfigFile(t *testing.T) { func TestInitConfigNoConfigFile(t *testing.T) { // No config file — should behave exactly as before - c, err := initConfig([]string{"-service", "github"}) + c, err := buildTestConfig([]string{"-service", "github"}) if err != nil { t.Fatalf("Expected no error, got: %v", err) } diff --git a/go.mod b/go.mod index 3848994..9a4ff66 100644 --- a/go.mod +++ b/go.mod @@ -15,11 +15,16 @@ require ( golang.org/x/text v0.27.0 // indirect ) -require codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.2.0 +require ( + codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.2.0 + github.com/urfave/cli/v2 v2.27.7 + gopkg.in/yaml.v3 v3.0.1 +) require ( github.com/42wim/httpsig v1.2.3 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/danieljoos/wincred v1.2.2 // indirect github.com/davidmz/go-pageant v1.0.2 // indirect github.com/dvsekhvalnov/jose2go v1.8.0 // indirect @@ -35,10 +40,11 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mtibben/percent v0.2.1 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect golang.org/x/crypto v0.40.0 // indirect golang.org/x/net v0.42.0 // indirect golang.org/x/sys v0.34.0 // indirect golang.org/x/term v0.33.0 // indirect golang.org/x/time v0.12.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index a2c3aa2..e9b467c 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,8 @@ github.com/cli/browser v1.0.0/go.mod h1:IEWkHYbLjkhtjwwWlwTHW2lGxeS5gezEQBMLTwDH github.com/cli/oauth v1.2.2 h1:/qG/wok8jzu66tx7q+duGOIp4DT5P/ACXrdc33UoNUQ= github.com/cli/oauth v1.2.2/go.mod h1:qd/FX8ZBD6n1sVNQO3aIdRxeu5LGw9WhKnYhIIoC2A4= github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= +github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= +github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0= github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -74,14 +76,20 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= +github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= github.com/xanzy/go-gitlab v0.115.0 h1:6DmtItNcVe+At/liXSgfE/DZNZrGfalQmBRmOcJjOn8= github.com/xanzy/go-gitlab v0.115.0/go.mod h1:5XCDtM7AM6WMKmfDdOiEpyRWUqui2iS9ILfvCZ2gJ5M= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= diff --git a/main.go b/main.go index 0a567c0..f5cfdb3 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,11 @@ package main import ( - "flag" "fmt" "log" "os" + + "github.com/urfave/cli/v2" ) // MaxConcurrentClones is the upper limit of the maximum number of @@ -26,62 +27,66 @@ var knownServices = map[string]string{ "forgejo": "codeberg.org", } -// parseSubcommandFlags parses --config and --help flags for a subcommand. -func parseSubcommandFlags(name, description string, args []string) string { - var configPath string - fs := flag.NewFlagSet("gitbackup "+name, flag.ExitOnError) - fs.StringVar(&configPath, "config", "", "Path to config file (default: OS config directory)") - fs.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage: gitbackup %s [--config path]\n\n", name) - fmt.Fprintf(os.Stderr, "%s\n\n", description) - fs.PrintDefaults() - } - fs.Parse(args) - return configPath -} - func main() { - - // Handle subcommands before flag parsing - if len(os.Args) > 1 { - switch os.Args[1] { - case "init": - configPath := parseSubcommandFlags("init", "Create a default gitbackup.yml configuration file.", os.Args[2:]) - if err := handleInitConfig(configPath); err != nil { - log.Fatal(err) + app := &cli.App{ + Name: "gitbackup", + Usage: "Backup your Git repositories from GitHub, GitLab, Bitbucket, or Forgejo", + Flags: appFlags(), + Action: func(cCtx *cli.Context) error { + c, err := buildConfig(cCtx) + if err != nil { + return err } - return - case "validate": - configPath := parseSubcommandFlags("validate", "Validate the gitbackup.yml configuration file.", os.Args[2:]) - if err := handleValidateConfig(configPath); err != nil { - log.Fatal(err) + err = validateConfig(c) + if err != nil { + return err } - return - } - } - - c, err := initConfig(os.Args[1:]) - if err != nil { - log.Fatal(err) - } - err = validateConfig(c) - if err != nil { - log.Fatal(err) - } - client := newClient(c.service, c.gitHostURL) - var executionErr error + client := newClient(c.service, c.gitHostURL) - // TODO implement validation of options so that we don't - // allow multiple operations at one go - if c.githubListUserMigrations { - handleGithubListUserMigrations(client, c) - } else if c.githubCreateUserMigration { - handleGithubCreateUserMigration(client, c) - } else { - executionErr = handleGitRepositoryClone(client, c) + if c.githubListUserMigrations { + handleGithubListUserMigrations(client, c) + } else if c.githubCreateUserMigration { + handleGithubCreateUserMigration(client, c) + } else { + if err := handleGitRepositoryClone(client, c); err != nil { + return err + } + } + return nil + }, + Commands: []*cli.Command{ + { + Name: "init", + Usage: "Create a default gitbackup.yml configuration file", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config", + Usage: "Path to config file (default: OS config directory)", + }, + }, + Action: func(cCtx *cli.Context) error { + return handleInitConfig(cCtx.String("config")) + }, + }, + { + Name: "validate", + Usage: "Validate the gitbackup.yml configuration file", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config", + Usage: "Path to config file (default: OS config directory)", + }, + }, + Action: func(cCtx *cli.Context) error { + return handleValidateConfig(cCtx.String("config")) + }, + }, + }, } - if executionErr != nil { - log.Fatal(executionErr) + + if err := app.Run(os.Args); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + log.Fatal(err) } } diff --git a/options.go b/options.go index 8a21352..9d88c9b 100644 --- a/options.go +++ b/options.go @@ -2,83 +2,118 @@ package main import ( "errors" - "flag" "os" "strings" + + "github.com/urfave/cli/v2" ) -// initConfig initializes and parses command-line flags into an appConfig struct. -// If a config file exists at the default or specified location, it is loaded -// first and CLI flags override any values from the config file. -func initConfig(args []string) (*appConfig, error) { - - var githubNamespaceWhitelistString string - var configPath string - var flagConfig appConfig - - fs := flag.NewFlagSet("gitbackup", flag.ExitOnError) - - // Config file flag - fs.StringVar(&configPath, "config", "", "Path to config file (default: OS config directory)") - - // Generic flags - fs.StringVar(&flagConfig.service, "service", "", "Git Hosted Service Name (github/gitlab/bitbucket/forgejo)") - fs.StringVar(&flagConfig.gitHostURL, "githost.url", "", "DNS of the custom Git host") - fs.StringVar(&flagConfig.backupDir, "backupdir", "", "Backup directory") - fs.BoolVar(&flagConfig.ignorePrivate, "ignore-private", false, "Ignore private repositories/projects") - fs.BoolVar(&flagConfig.ignoreFork, "ignore-fork", false, "Ignore repositories which are forks") - fs.BoolVar(&flagConfig.useHTTPSClone, "use-https-clone", false, "Use HTTPS for cloning instead of SSH") - fs.BoolVar(&flagConfig.bare, "bare", false, "Clone bare repositories") - - // GitHub specific flags - fs.StringVar(&flagConfig.githubRepoType, "github.repoType", "all", "Repo types to backup (all, owner, member, starred)") - fs.StringVar( - &githubNamespaceWhitelistString, "github.namespaceWhitelist", - "", "Organizations/Users from where we should clone (separate each value by a comma: 'user1,org2')", - ) - fs.BoolVar(&flagConfig.githubCreateUserMigration, "github.createUserMigration", false, "Download user data") - fs.BoolVar( - &flagConfig.githubCreateUserMigrationRetry, "github.createUserMigrationRetry", true, - "Retry creating the GitHub user migration if we get an error", - ) - fs.IntVar( - &flagConfig.githubCreateUserMigrationRetryMax, "github.createUserMigrationRetryMax", - defaultMaxUserMigrationRetry, - "Number of retries to attempt for creating GitHub user migration", - ) - fs.BoolVar( - &flagConfig.githubListUserMigrations, - "github.listUserMigrations", - false, - "List available user migrations", - ) - fs.BoolVar( - &flagConfig.githubWaitForMigrationComplete, - "github.waitForUserMigration", - true, - "Wait for migration to complete", - ) - - // Gitlab specific flags - fs.StringVar( - &flagConfig.gitlabProjectVisibility, - "gitlab.projectVisibility", - "internal", - "Visibility level of Projects to clone (internal, public, private)", - ) - fs.StringVar( - &flagConfig.gitlabProjectMembershipType, - "gitlab.projectMembershipType", "all", - "Project type to clone (all, owner, member, starred)", - ) - - // Forgejo specific flags - fs.StringVar(&flagConfig.forgejoRepoType, "forgejo.repoType", "user", "Repo types to backup (user, starred)") - - err := fs.Parse(args) - if err != nil && !errors.Is(err, flag.ErrHelp) { - return nil, err +// appFlags returns the CLI flags for the backup command. +func appFlags() []cli.Flag { + return []cli.Flag{ + // Config file flag + &cli.StringFlag{ + Name: "config", + Usage: "Path to config file (default: OS config directory)", + }, + + // Generic flags + &cli.StringFlag{ + Name: "service", + Usage: "Git Hosted Service Name (github/gitlab/bitbucket/forgejo)", + }, + &cli.StringFlag{ + Name: "githost.url", + Usage: "DNS of the custom Git host", + }, + &cli.StringFlag{ + Name: "backupdir", + Usage: "Backup directory", + }, + &cli.BoolFlag{ + Name: "ignore-private", + Usage: "Ignore private repositories/projects", + }, + &cli.BoolFlag{ + Name: "ignore-fork", + Usage: "Ignore repositories which are forks", + }, + &cli.BoolFlag{ + Name: "use-https-clone", + Usage: "Use HTTPS for cloning instead of SSH", + }, + &cli.BoolFlag{ + Name: "bare", + Usage: "Clone bare repositories", + }, + + // GitHub specific flags + &cli.StringFlag{ + Name: "github.repoType", + Usage: "Repo types to backup (all, owner, member, starred)", + DefaultText: "all", + Value: "all", + }, + &cli.StringFlag{ + Name: "github.namespaceWhitelist", + Usage: "Organizations/Users from where we should clone (separate each value by a comma: 'user1,org2')", + }, + &cli.BoolFlag{ + Name: "github.createUserMigration", + Usage: "Download user data", + }, + &cli.BoolFlag{ + Name: "github.createUserMigrationRetry", + Usage: "Retry creating the GitHub user migration if we get an error", + DefaultText: "true", + Value: true, + }, + &cli.IntFlag{ + Name: "github.createUserMigrationRetryMax", + Usage: "Number of retries to attempt for creating GitHub user migration", + DefaultText: "5", + Value: defaultMaxUserMigrationRetry, + }, + &cli.BoolFlag{ + Name: "github.listUserMigrations", + Usage: "List available user migrations", + }, + &cli.BoolFlag{ + Name: "github.waitForUserMigration", + Usage: "Wait for migration to complete", + DefaultText: "true", + Value: true, + }, + + // Gitlab specific flags + &cli.StringFlag{ + Name: "gitlab.projectVisibility", + Usage: "Visibility level of Projects to clone (internal, public, private)", + DefaultText: "internal", + Value: "internal", + }, + &cli.StringFlag{ + Name: "gitlab.projectMembershipType", + Usage: "Project type to clone (all, owner, member, starred)", + DefaultText: "all", + Value: "all", + }, + + // Forgejo specific flags + &cli.StringFlag{ + Name: "forgejo.repoType", + Usage: "Repo types to backup (user, starred)", + DefaultText: "user", + Value: "user", + }, } +} + +// buildConfig builds an appConfig from the CLI context, respecting config file precedence. +// If a config file exists, its values are used as the base and only explicitly-set +// CLI flags override them. +func buildConfig(cCtx *cli.Context) (*appConfig, error) { + configPath := cCtx.String("config") // Try to load config file as the base configuration var c appConfig @@ -98,51 +133,74 @@ func initConfig(args []string) (*appConfig, error) { if configFileLoaded { // Only override config file values with flags that were explicitly set - fs.Visit(func(f *flag.Flag) { - switch f.Name { - case "service": - c.service = flagConfig.service - case "githost.url": - c.gitHostURL = flagConfig.gitHostURL - case "backupdir": - c.backupDir = flagConfig.backupDir - case "ignore-private": - c.ignorePrivate = flagConfig.ignorePrivate - case "ignore-fork": - c.ignoreFork = flagConfig.ignoreFork - case "use-https-clone": - c.useHTTPSClone = flagConfig.useHTTPSClone - case "bare": - c.bare = flagConfig.bare - case "github.repoType": - c.githubRepoType = flagConfig.githubRepoType - case "github.namespaceWhitelist": - // handled below - case "gitlab.projectVisibility": - c.gitlabProjectVisibility = flagConfig.gitlabProjectVisibility - case "gitlab.projectMembershipType": - c.gitlabProjectMembershipType = flagConfig.gitlabProjectMembershipType - case "forgejo.repoType": - c.forgejoRepoType = flagConfig.forgejoRepoType + if cCtx.IsSet("service") { + c.service = cCtx.String("service") + } + if cCtx.IsSet("githost.url") { + c.gitHostURL = cCtx.String("githost.url") + } + if cCtx.IsSet("backupdir") { + c.backupDir = cCtx.String("backupdir") + } + if cCtx.IsSet("ignore-private") { + c.ignorePrivate = cCtx.Bool("ignore-private") + } + if cCtx.IsSet("ignore-fork") { + c.ignoreFork = cCtx.Bool("ignore-fork") + } + if cCtx.IsSet("use-https-clone") { + c.useHTTPSClone = cCtx.Bool("use-https-clone") + } + if cCtx.IsSet("bare") { + c.bare = cCtx.Bool("bare") + } + if cCtx.IsSet("github.repoType") { + c.githubRepoType = cCtx.String("github.repoType") + } + if cCtx.IsSet("github.namespaceWhitelist") { + ns := cCtx.String("github.namespaceWhitelist") + if len(ns) > 0 { + c.githubNamespaceWhitelist = strings.Split(ns, ",") } - }) + } + if cCtx.IsSet("gitlab.projectVisibility") { + c.gitlabProjectVisibility = cCtx.String("gitlab.projectVisibility") + } + if cCtx.IsSet("gitlab.projectMembershipType") { + c.gitlabProjectMembershipType = cCtx.String("gitlab.projectMembershipType") + } + if cCtx.IsSet("forgejo.repoType") { + c.forgejoRepoType = cCtx.String("forgejo.repoType") + } // Migration flags are always from CLI (not in config file) - c.githubCreateUserMigration = flagConfig.githubCreateUserMigration - c.githubCreateUserMigrationRetry = flagConfig.githubCreateUserMigrationRetry - c.githubCreateUserMigrationRetryMax = flagConfig.githubCreateUserMigrationRetryMax - c.githubListUserMigrations = flagConfig.githubListUserMigrations - c.githubWaitForMigrationComplete = flagConfig.githubWaitForMigrationComplete - - // Parse namespace whitelist if explicitly set - if len(githubNamespaceWhitelistString) > 0 { - c.githubNamespaceWhitelist = strings.Split(githubNamespaceWhitelistString, ",") - } + c.githubCreateUserMigration = cCtx.Bool("github.createUserMigration") + c.githubCreateUserMigrationRetry = cCtx.Bool("github.createUserMigrationRetry") + c.githubCreateUserMigrationRetryMax = cCtx.Int("github.createUserMigrationRetryMax") + c.githubListUserMigrations = cCtx.Bool("github.listUserMigrations") + c.githubWaitForMigrationComplete = cCtx.Bool("github.waitForUserMigration") } else { - // No config file — use flags directly (original behavior) - c = flagConfig - if len(githubNamespaceWhitelistString) > 0 { - c.githubNamespaceWhitelist = strings.Split(githubNamespaceWhitelistString, ",") + // No config file — read all values from CLI context directly + c.service = cCtx.String("service") + c.gitHostURL = cCtx.String("githost.url") + c.backupDir = cCtx.String("backupdir") + c.ignorePrivate = cCtx.Bool("ignore-private") + c.ignoreFork = cCtx.Bool("ignore-fork") + c.useHTTPSClone = cCtx.Bool("use-https-clone") + c.bare = cCtx.Bool("bare") + c.githubRepoType = cCtx.String("github.repoType") + c.gitlabProjectVisibility = cCtx.String("gitlab.projectVisibility") + c.gitlabProjectMembershipType = cCtx.String("gitlab.projectMembershipType") + c.forgejoRepoType = cCtx.String("forgejo.repoType") + c.githubCreateUserMigration = cCtx.Bool("github.createUserMigration") + c.githubCreateUserMigrationRetry = cCtx.Bool("github.createUserMigrationRetry") + c.githubCreateUserMigrationRetryMax = cCtx.Int("github.createUserMigrationRetryMax") + c.githubListUserMigrations = cCtx.Bool("github.listUserMigrations") + c.githubWaitForMigrationComplete = cCtx.Bool("github.waitForUserMigration") + + ns := cCtx.String("github.namespaceWhitelist") + if len(ns) > 0 { + c.githubNamespaceWhitelist = strings.Split(ns, ",") } } diff --git a/testdata/TestCliUsage.golden b/testdata/TestCliUsage.golden index 43b3caa..1e1f3c7 100644 --- a/testdata/TestCliUsage.golden +++ b/testdata/TestCliUsage.golden @@ -1,37 +1,31 @@ -Usage of gitbackup: - -backupdir string - Backup directory - -bare - Clone bare repositories - -config string - Path to config file (default: OS config directory) - -forgejo.repoType string - Repo types to backup (user, starred) (default "user") - -githost.url string - DNS of the custom Git host - -github.createUserMigration - Download user data - -github.createUserMigrationRetry - Retry creating the GitHub user migration if we get an error (default true) - -github.createUserMigrationRetryMax int - Number of retries to attempt for creating GitHub user migration (default 5) - -github.listUserMigrations - List available user migrations - -github.namespaceWhitelist string - Organizations/Users from where we should clone (separate each value by a comma: 'user1,org2') - -github.repoType string - Repo types to backup (all, owner, member, starred) (default "all") - -github.waitForUserMigration - Wait for migration to complete (default true) - -gitlab.projectMembershipType string - Project type to clone (all, owner, member, starred) (default "all") - -gitlab.projectVisibility string - Visibility level of Projects to clone (internal, public, private) (default "internal") - -ignore-fork - Ignore repositories which are forks - -ignore-private - Ignore private repositories/projects - -service string - Git Hosted Service Name (github/gitlab/bitbucket/forgejo) - -use-https-clone - Use HTTPS for cloning instead of SSH +NAME: + gitbackup - Backup your Git repositories from GitHub, GitLab, Bitbucket, or Forgejo + +USAGE: + gitbackup [global options] command [command options] + +COMMANDS: + init Create a default gitbackup.yml configuration file + validate Validate the gitbackup.yml configuration file + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS: + --config value Path to config file (default: OS config directory) + --service value Git Hosted Service Name (github/gitlab/bitbucket/forgejo) + --githost.url value DNS of the custom Git host + --backupdir value Backup directory + --ignore-private Ignore private repositories/projects (default: false) + --ignore-fork Ignore repositories which are forks (default: false) + --use-https-clone Use HTTPS for cloning instead of SSH (default: false) + --bare Clone bare repositories (default: false) + --github.repoType value Repo types to backup (all, owner, member, starred) (default: all) + --github.namespaceWhitelist value Organizations/Users from where we should clone (separate each value by a comma: 'user1,org2') + --github.createUserMigration Download user data (default: false) + --github.createUserMigrationRetry Retry creating the GitHub user migration if we get an error (default: true) + --github.createUserMigrationRetryMax value Number of retries to attempt for creating GitHub user migration (default: 5) + --github.listUserMigrations List available user migrations (default: false) + --github.waitForUserMigration Wait for migration to complete (default: true) + --gitlab.projectVisibility value Visibility level of Projects to clone (internal, public, private) (default: internal) + --gitlab.projectMembershipType value Project type to clone (all, owner, member, starred) (default: all) + --forgejo.repoType value Repo types to backup (user, starred) (default: user) + --help, -h show help diff --git a/testdata/TestCliUsage.golden.windows b/testdata/TestCliUsage.golden.windows index 43b3caa..1e1f3c7 100644 --- a/testdata/TestCliUsage.golden.windows +++ b/testdata/TestCliUsage.golden.windows @@ -1,37 +1,31 @@ -Usage of gitbackup: - -backupdir string - Backup directory - -bare - Clone bare repositories - -config string - Path to config file (default: OS config directory) - -forgejo.repoType string - Repo types to backup (user, starred) (default "user") - -githost.url string - DNS of the custom Git host - -github.createUserMigration - Download user data - -github.createUserMigrationRetry - Retry creating the GitHub user migration if we get an error (default true) - -github.createUserMigrationRetryMax int - Number of retries to attempt for creating GitHub user migration (default 5) - -github.listUserMigrations - List available user migrations - -github.namespaceWhitelist string - Organizations/Users from where we should clone (separate each value by a comma: 'user1,org2') - -github.repoType string - Repo types to backup (all, owner, member, starred) (default "all") - -github.waitForUserMigration - Wait for migration to complete (default true) - -gitlab.projectMembershipType string - Project type to clone (all, owner, member, starred) (default "all") - -gitlab.projectVisibility string - Visibility level of Projects to clone (internal, public, private) (default "internal") - -ignore-fork - Ignore repositories which are forks - -ignore-private - Ignore private repositories/projects - -service string - Git Hosted Service Name (github/gitlab/bitbucket/forgejo) - -use-https-clone - Use HTTPS for cloning instead of SSH +NAME: + gitbackup - Backup your Git repositories from GitHub, GitLab, Bitbucket, or Forgejo + +USAGE: + gitbackup [global options] command [command options] + +COMMANDS: + init Create a default gitbackup.yml configuration file + validate Validate the gitbackup.yml configuration file + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS: + --config value Path to config file (default: OS config directory) + --service value Git Hosted Service Name (github/gitlab/bitbucket/forgejo) + --githost.url value DNS of the custom Git host + --backupdir value Backup directory + --ignore-private Ignore private repositories/projects (default: false) + --ignore-fork Ignore repositories which are forks (default: false) + --use-https-clone Use HTTPS for cloning instead of SSH (default: false) + --bare Clone bare repositories (default: false) + --github.repoType value Repo types to backup (all, owner, member, starred) (default: all) + --github.namespaceWhitelist value Organizations/Users from where we should clone (separate each value by a comma: 'user1,org2') + --github.createUserMigration Download user data (default: false) + --github.createUserMigrationRetry Retry creating the GitHub user migration if we get an error (default: true) + --github.createUserMigrationRetryMax value Number of retries to attempt for creating GitHub user migration (default: 5) + --github.listUserMigrations List available user migrations (default: false) + --github.waitForUserMigration Wait for migration to complete (default: true) + --gitlab.projectVisibility value Visibility level of Projects to clone (internal, public, private) (default: internal) + --gitlab.projectMembershipType value Project type to clone (all, owner, member, starred) (default: all) + --forgejo.repoType value Repo types to backup (user, starred) (default: user) + --help, -h show help