Compare commits
4 Commits
main
...
hk-autocom
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e7b74cb6d | ||
|
|
78f2dbdccb | ||
|
|
84f4400635 | ||
|
|
bfea3dea34 |
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Added
|
||||
- Added export support for emails in exchange backups as `.eml` files
|
||||
- More colorful and informational cli output
|
||||
- CLI completions for corso commands and flags (bash, zsh, fish, powershell)
|
||||
|
||||
### Changed
|
||||
- Change file extension of messages export to json to match the content
|
||||
@ -19,6 +20,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Automatically re-run a full delta query on incremental if the prior backup is found to have malformed prior-state information.
|
||||
- Retry drive item permission downloads during long-running backups after the jwt token expires and refreshes.
|
||||
|
||||
### Known issues
|
||||
- CLI completions cannot autocomplete multiple values for flags
|
||||
|
||||
## [v0.15.0] (beta) - 2023-10-31
|
||||
|
||||
### Added
|
||||
|
||||
@ -75,7 +75,7 @@ func backupCmd() *cobra.Command {
|
||||
Short: "Backup your service data",
|
||||
Long: `Backup the data stored in one of your M365 services.`,
|
||||
RunE: handleBackupCmd,
|
||||
Args: cobra.NoArgs,
|
||||
Args: utils.SubcommandsRequiredWithSuggestions,
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,7 +94,7 @@ func createCmd() *cobra.Command {
|
||||
Use: createCommand,
|
||||
Short: "Backup an M365 Service",
|
||||
RunE: handleCreateCmd,
|
||||
Args: cobra.NoArgs,
|
||||
Args: utils.SubcommandsRequiredWithSuggestions,
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +113,7 @@ func listCmd() *cobra.Command {
|
||||
Use: listCommand,
|
||||
Short: "List the history of backups",
|
||||
RunE: handleListCmd,
|
||||
Args: cobra.NoArgs,
|
||||
Args: utils.SubcommandsRequiredWithSuggestions,
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ func detailsCmd() *cobra.Command {
|
||||
Use: detailsCommand,
|
||||
Short: "Shows the details of a backup",
|
||||
RunE: handleDetailsCmd,
|
||||
Args: cobra.NoArgs,
|
||||
Args: utils.SubcommandsRequiredWithSuggestions,
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ func deleteCmd() *cobra.Command {
|
||||
Use: deleteCommand,
|
||||
Short: "Deletes a backup",
|
||||
RunE: handleDeleteCmd,
|
||||
Args: cobra.NoArgs,
|
||||
Args: utils.SubcommandsRequiredWithSuggestions,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -77,7 +77,7 @@ func addExchangeCommands(cmd *cobra.Command) *cobra.Command {
|
||||
|
||||
// Flags addition ordering should follow the order we want them to appear in help and docs:
|
||||
// More generic (ex: --user) and more frequently used flags take precedence.
|
||||
flags.AddMailBoxFlag(c)
|
||||
flags.AddMailBoxFlag(c, utils.MailboxCompletionFunc(path.ExchangeService))
|
||||
flags.AddDataFlag(c, []string{dataEmail, dataContacts, dataEvents}, false)
|
||||
flags.AddFetchParallelismFlag(c)
|
||||
flags.AddDisableDeltaFlag(c)
|
||||
@ -90,7 +90,7 @@ func addExchangeCommands(cmd *cobra.Command) *cobra.Command {
|
||||
c, fs = utils.AddCommand(cmd, exchangeListCmd())
|
||||
fs.SortFlags = false
|
||||
|
||||
flags.AddBackupIDFlag(c, false)
|
||||
flags.AddBackupIDFlag(c, false, utils.BackupIDCompletionFunc(path.ExchangeService))
|
||||
flags.AddAllBackupListFlags(c)
|
||||
|
||||
case detailsCommand:
|
||||
@ -104,7 +104,7 @@ func addExchangeCommands(cmd *cobra.Command) *cobra.Command {
|
||||
|
||||
// Flags addition ordering should follow the order we want them to appear in help and docs:
|
||||
// More generic (ex: --user) and more frequently used flags take precedence.
|
||||
flags.AddBackupIDFlag(c, true)
|
||||
flags.AddBackupIDFlag(c, true, utils.BackupIDCompletionFunc(path.ExchangeService))
|
||||
flags.AddExchangeDetailsAndRestoreFlags(c, false)
|
||||
|
||||
case deleteCommand:
|
||||
@ -115,7 +115,7 @@ func addExchangeCommands(cmd *cobra.Command) *cobra.Command {
|
||||
c.Example = exchangeServiceCommandDeleteExamples
|
||||
|
||||
flags.AddMultipleBackupIDsFlag(c, false)
|
||||
flags.AddBackupIDFlag(c, false)
|
||||
flags.AddBackupIDFlag(c, false, utils.BackupIDCompletionFunc(path.ExchangeService))
|
||||
}
|
||||
|
||||
return c
|
||||
|
||||
@ -70,7 +70,7 @@ func addGroupsCommands(cmd *cobra.Command) *cobra.Command {
|
||||
c.Example = groupsServiceCommandCreateExamples
|
||||
|
||||
// Flags addition ordering should follow the order we want them to appear in help and docs:
|
||||
flags.AddGroupFlag(c)
|
||||
flags.AddGroupFlag(c, utils.GroupsCompletionFunc())
|
||||
flags.AddDataFlag(c, []string{flags.DataLibraries, flags.DataMessages}, false)
|
||||
flags.AddFetchParallelismFlag(c)
|
||||
flags.AddDisableDeltaFlag(c)
|
||||
@ -80,7 +80,7 @@ func addGroupsCommands(cmd *cobra.Command) *cobra.Command {
|
||||
c, fs = utils.AddCommand(cmd, groupsListCmd(), utils.MarkPreviewCommand())
|
||||
fs.SortFlags = false
|
||||
|
||||
flags.AddBackupIDFlag(c, false)
|
||||
flags.AddBackupIDFlag(c, false, utils.BackupIDCompletionFunc(path.GroupsService))
|
||||
flags.AddAllBackupListFlags(c)
|
||||
|
||||
case detailsCommand:
|
||||
@ -94,7 +94,7 @@ func addGroupsCommands(cmd *cobra.Command) *cobra.Command {
|
||||
|
||||
// Flags addition ordering should follow the order we want them to appear in help and docs:
|
||||
// More generic (ex: --user) and more frequently used flags take precedence.
|
||||
flags.AddBackupIDFlag(c, true)
|
||||
flags.AddBackupIDFlag(c, true, utils.BackupIDCompletionFunc(path.GroupsService))
|
||||
flags.AddGroupDetailsAndRestoreFlags(c)
|
||||
flags.AddSharePointDetailsAndRestoreFlags(c)
|
||||
|
||||
@ -106,7 +106,7 @@ func addGroupsCommands(cmd *cobra.Command) *cobra.Command {
|
||||
c.Example = groupsServiceCommandDeleteExamples
|
||||
|
||||
flags.AddMultipleBackupIDsFlag(c, false)
|
||||
flags.AddBackupIDFlag(c, false)
|
||||
flags.AddBackupIDFlag(c, false, utils.BackupIDCompletionFunc(path.GroupsService))
|
||||
}
|
||||
|
||||
return c
|
||||
|
||||
@ -65,14 +65,14 @@ func addOneDriveCommands(cmd *cobra.Command) *cobra.Command {
|
||||
c.Use = c.Use + " " + oneDriveServiceCommandCreateUseSuffix
|
||||
c.Example = oneDriveServiceCommandCreateExamples
|
||||
|
||||
flags.AddUserFlag(c)
|
||||
flags.AddUserFlag(c, utils.UsersCompletionFunc(path.OneDriveService))
|
||||
flags.AddGenericBackupFlags(c)
|
||||
|
||||
case listCommand:
|
||||
c, fs = utils.AddCommand(cmd, oneDriveListCmd())
|
||||
fs.SortFlags = false
|
||||
|
||||
flags.AddBackupIDFlag(c, false)
|
||||
flags.AddBackupIDFlag(c, false, utils.BackupIDCompletionFunc(path.OneDriveService))
|
||||
flags.AddAllBackupListFlags(c)
|
||||
|
||||
case detailsCommand:
|
||||
@ -83,7 +83,7 @@ func addOneDriveCommands(cmd *cobra.Command) *cobra.Command {
|
||||
c.Example = oneDriveServiceCommandDetailsExamples
|
||||
|
||||
flags.AddSkipReduceFlag(c)
|
||||
flags.AddBackupIDFlag(c, true)
|
||||
flags.AddBackupIDFlag(c, true, utils.BackupIDCompletionFunc(path.OneDriveService))
|
||||
flags.AddOneDriveDetailsAndRestoreFlags(c)
|
||||
|
||||
case deleteCommand:
|
||||
@ -94,7 +94,7 @@ func addOneDriveCommands(cmd *cobra.Command) *cobra.Command {
|
||||
c.Example = oneDriveServiceCommandDeleteExamples
|
||||
|
||||
flags.AddMultipleBackupIDsFlag(c, false)
|
||||
flags.AddBackupIDFlag(c, false)
|
||||
flags.AddBackupIDFlag(c, false, utils.BackupIDCompletionFunc(path.OneDriveService))
|
||||
}
|
||||
|
||||
return c
|
||||
|
||||
@ -76,7 +76,7 @@ func addSharePointCommands(cmd *cobra.Command) *cobra.Command {
|
||||
c.Use = c.Use + " " + sharePointServiceCommandCreateUseSuffix
|
||||
c.Example = sharePointServiceCommandCreateExamples
|
||||
|
||||
flags.AddSiteFlag(c, true)
|
||||
flags.AddSiteFlag(c, true, utils.SitesCompletionFunc())
|
||||
flags.AddSiteIDFlag(c, true)
|
||||
flags.AddDataFlag(c, []string{flags.DataLibraries}, true)
|
||||
flags.AddGenericBackupFlags(c)
|
||||
@ -85,7 +85,7 @@ func addSharePointCommands(cmd *cobra.Command) *cobra.Command {
|
||||
c, fs = utils.AddCommand(cmd, sharePointListCmd())
|
||||
fs.SortFlags = false
|
||||
|
||||
flags.AddBackupIDFlag(c, false)
|
||||
flags.AddBackupIDFlag(c, false, utils.BackupIDCompletionFunc(path.SharePointService))
|
||||
flags.AddAllBackupListFlags(c)
|
||||
|
||||
case detailsCommand:
|
||||
@ -96,7 +96,7 @@ func addSharePointCommands(cmd *cobra.Command) *cobra.Command {
|
||||
c.Example = sharePointServiceCommandDetailsExamples
|
||||
|
||||
flags.AddSkipReduceFlag(c)
|
||||
flags.AddBackupIDFlag(c, true)
|
||||
flags.AddBackupIDFlag(c, true, utils.BackupIDCompletionFunc(path.SharePointService))
|
||||
flags.AddSharePointDetailsAndRestoreFlags(c)
|
||||
|
||||
case deleteCommand:
|
||||
@ -107,7 +107,7 @@ func addSharePointCommands(cmd *cobra.Command) *cobra.Command {
|
||||
c.Example = sharePointServiceCommandDeleteExamples
|
||||
|
||||
flags.AddMultipleBackupIDsFlag(c, false)
|
||||
flags.AddBackupIDFlag(c, false)
|
||||
flags.AddBackupIDFlag(c, false, utils.BackupIDCompletionFunc(path.SharePointService))
|
||||
}
|
||||
|
||||
return c
|
||||
|
||||
@ -2,6 +2,7 @@ package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
@ -54,7 +55,19 @@ func preRun(cc *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
avoidTheseCommands := []string{
|
||||
"corso", "env", "help", "backup", "details", "list", "restore", "export", "delete", "repo", "init", "connect",
|
||||
"corso",
|
||||
"env",
|
||||
"help",
|
||||
"backup",
|
||||
"details",
|
||||
"list",
|
||||
"restore",
|
||||
"export",
|
||||
"delete",
|
||||
"repo",
|
||||
"init",
|
||||
"connect",
|
||||
"completion [bash|zsh|fish|powershell]",
|
||||
}
|
||||
|
||||
if len(logger.ResolvedLogFile) > 0 && !slices.Contains(avoidTheseCommands, cc.Use) {
|
||||
@ -121,6 +134,7 @@ func BuildCommandTree(cmd *cobra.Command) {
|
||||
cmd.SetUsageTemplate(indentExamplesTemplate(corsoCmd.UsageTemplate()))
|
||||
|
||||
cmd.CompletionOptions.DisableDefaultCmd = true
|
||||
cmd.SuggestionsMinimumDistance = 2 // default
|
||||
|
||||
repo.AddCommands(cmd)
|
||||
backup.AddCommands(cmd)
|
||||
@ -128,6 +142,53 @@ func BuildCommandTree(cmd *cobra.Command) {
|
||||
export.AddCommands(cmd)
|
||||
debug.AddCommands(cmd)
|
||||
help.AddCommands(cmd)
|
||||
AddCompletion(cmd)
|
||||
}
|
||||
|
||||
// We are not using the default completion command as it will be
|
||||
// harder to control it, for example skipping printing "Logging to file"
|
||||
// message
|
||||
func AddCompletion(cmd *cobra.Command) {
|
||||
longMessage := `Generate shell completion script for Corso.
|
||||
|
||||
These need to be hooked into your shell to enable completions for Corso.
|
||||
|
||||
For bash, add the following line to your ` + "`~/.bashrc` \n" + // two spaces for markdown and \n for go
|
||||
"`eval \"$(corso completion bash)\"` \n" + `
|
||||
|
||||
For zsh, add the following line to your ` + "`~/.zshrc` \n" +
|
||||
"`eval \"$(corso completion zsh)\"` \n" + `
|
||||
|
||||
For fish, add the following line to your ` + "`~/.config/fish/config.fish` \n" +
|
||||
"`corso completion fish | source` \n" + `
|
||||
|
||||
For powershell, add the following to your ` + "`$PROFILE` \n" +
|
||||
"`Invoke-Expression \"$(corso completion powershell)\"`"
|
||||
completion := &cobra.Command{
|
||||
Use: "completion [bash|zsh|fish|powershell]",
|
||||
Short: "Generate completion script",
|
||||
Long: longMessage,
|
||||
DisableFlagsInUseLine: true,
|
||||
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
|
||||
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
|
||||
Hidden: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
switch args[0] {
|
||||
case "bash":
|
||||
return cmd.Root().GenBashCompletion(os.Stdout)
|
||||
case "zsh":
|
||||
return cmd.Root().GenZshCompletion(os.Stdout)
|
||||
case "fish":
|
||||
return cmd.Root().GenFishCompletion(os.Stdout, true)
|
||||
case "powershell":
|
||||
return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
|
||||
}
|
||||
|
||||
return fmt.Errorf("unknown shell type %q", args[0])
|
||||
},
|
||||
}
|
||||
|
||||
cmd.AddCommand(completion)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/cli/flags"
|
||||
"github.com/alcionai/corso/src/cli/utils"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
"github.com/alcionai/corso/src/pkg/selectors"
|
||||
)
|
||||
|
||||
@ -26,7 +27,7 @@ func addExchangeCommands(cmd *cobra.Command) *cobra.Command {
|
||||
// More generic (ex: --user) and more frequently used flags take precedence.
|
||||
fs.SortFlags = false
|
||||
|
||||
flags.AddBackupIDFlag(c, true)
|
||||
flags.AddBackupIDFlag(c, true, utils.BackupIDCompletionFunc(path.ExchangeService))
|
||||
}
|
||||
|
||||
return c
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/cli/flags"
|
||||
"github.com/alcionai/corso/src/cli/utils"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
"github.com/alcionai/corso/src/pkg/selectors"
|
||||
)
|
||||
|
||||
@ -26,7 +27,7 @@ func addGroupsCommands(cmd *cobra.Command) *cobra.Command {
|
||||
// More generic (ex: --user) and more frequently used flags take precedence.
|
||||
fs.SortFlags = false
|
||||
|
||||
flags.AddBackupIDFlag(c, true)
|
||||
flags.AddBackupIDFlag(c, true, utils.BackupIDCompletionFunc(path.GroupsService))
|
||||
}
|
||||
|
||||
return c
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/cli/flags"
|
||||
"github.com/alcionai/corso/src/cli/utils"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
"github.com/alcionai/corso/src/pkg/selectors"
|
||||
)
|
||||
|
||||
@ -26,7 +27,7 @@ func addOneDriveCommands(cmd *cobra.Command) *cobra.Command {
|
||||
// More generic (ex: --user) and more frequently used flags take precedence.
|
||||
fs.SortFlags = false
|
||||
|
||||
flags.AddBackupIDFlag(c, true)
|
||||
flags.AddBackupIDFlag(c, true, utils.BackupIDCompletionFunc(path.OneDriveService))
|
||||
}
|
||||
|
||||
return c
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/cli/flags"
|
||||
"github.com/alcionai/corso/src/cli/utils"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
"github.com/alcionai/corso/src/pkg/selectors"
|
||||
)
|
||||
|
||||
@ -26,7 +27,7 @@ func addSharePointCommands(cmd *cobra.Command) *cobra.Command {
|
||||
// More generic (ex: --user) and more frequently used flags take precedence.
|
||||
fs.SortFlags = false
|
||||
|
||||
flags.AddBackupIDFlag(c, true)
|
||||
flags.AddBackupIDFlag(c, true, utils.BackupIDCompletionFunc(path.SharePointService))
|
||||
}
|
||||
|
||||
return c
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/cli/flags"
|
||||
"github.com/alcionai/corso/src/cli/utils"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
|
||||
// called by export.go to map subcommands to provider-specific handling.
|
||||
@ -26,7 +27,7 @@ func addExchangeCommands(cmd *cobra.Command) *cobra.Command {
|
||||
// More generic (ex: --user) and more frequently used flags take precedence.
|
||||
fs.SortFlags = false
|
||||
|
||||
flags.AddBackupIDFlag(c, true)
|
||||
flags.AddBackupIDFlag(c, true, utils.BackupIDCompletionFunc(path.ExchangeService))
|
||||
flags.AddExchangeDetailsAndRestoreFlags(c, true)
|
||||
flags.AddExportConfigFlags(c)
|
||||
flags.AddFailFastFlag(c)
|
||||
|
||||
@ -49,7 +49,7 @@ func exportCmd() *cobra.Command {
|
||||
Short: "Export your service data",
|
||||
Long: `Export the data stored in one of your M365 services.`,
|
||||
RunE: handleExportCmd,
|
||||
Args: cobra.NoArgs,
|
||||
Args: utils.SubcommandsRequiredWithSuggestions,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"github.com/alcionai/corso/src/cli/flags"
|
||||
"github.com/alcionai/corso/src/cli/utils"
|
||||
"github.com/alcionai/corso/src/pkg/control"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
|
||||
// called by export.go to map subcommands to provider-specific handling.
|
||||
@ -27,8 +28,8 @@ func addGroupsCommands(cmd *cobra.Command) *cobra.Command {
|
||||
// More generic (ex: --user) and more frequently used flags take precedence.
|
||||
fs.SortFlags = false
|
||||
|
||||
flags.AddBackupIDFlag(c, true)
|
||||
flags.AddSiteFlag(c, false)
|
||||
flags.AddBackupIDFlag(c, true, utils.BackupIDCompletionFunc(path.GroupsService))
|
||||
flags.AddSiteFlag(c, false, utils.SitesCompletionFunc())
|
||||
flags.AddSiteIDFlag(c, false)
|
||||
flags.AddSharePointDetailsAndRestoreFlags(c)
|
||||
flags.AddGroupDetailsAndRestoreFlags(c)
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/cli/flags"
|
||||
"github.com/alcionai/corso/src/cli/utils"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
|
||||
// called by export.go to map subcommands to provider-specific handling.
|
||||
@ -26,7 +27,7 @@ func addOneDriveCommands(cmd *cobra.Command) *cobra.Command {
|
||||
// More generic (ex: --user) and more frequently used flags take precedence.
|
||||
fs.SortFlags = false
|
||||
|
||||
flags.AddBackupIDFlag(c, true)
|
||||
flags.AddBackupIDFlag(c, true, utils.BackupIDCompletionFunc(path.OneDriveService))
|
||||
flags.AddOneDriveDetailsAndRestoreFlags(c)
|
||||
flags.AddExportConfigFlags(c)
|
||||
flags.AddFailFastFlag(c)
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/cli/flags"
|
||||
"github.com/alcionai/corso/src/cli/utils"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
|
||||
// called by export.go to map subcommands to provider-specific handling.
|
||||
@ -26,7 +27,7 @@ func addSharePointCommands(cmd *cobra.Command) *cobra.Command {
|
||||
// More generic (ex: --user) and more frequently used flags take precedence.
|
||||
fs.SortFlags = false
|
||||
|
||||
flags.AddBackupIDFlag(c, true)
|
||||
flags.AddBackupIDFlag(c, true, utils.BackupIDCompletionFunc(path.SharePointService))
|
||||
flags.AddSharePointDetailsAndRestoreFlags(c)
|
||||
flags.AddExportConfigFlags(c)
|
||||
flags.AddFailFastFlag(c)
|
||||
|
||||
@ -68,9 +68,14 @@ func AddGroupDetailsAndRestoreFlags(cmd *cobra.Command) {
|
||||
// Mail is most accurate, MailNickame is accurate and shorter, but the end user
|
||||
// may not see either one visibly.
|
||||
// https://learn.microsoft.com/en-us/graph/api/group-list?view=graph-rest-1.0&tabs=http
|
||||
func AddGroupFlag(cmd *cobra.Command) {
|
||||
func AddGroupFlag(
|
||||
cmd *cobra.Command,
|
||||
completionFunc func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective),
|
||||
) {
|
||||
cmd.Flags().StringSliceVar(
|
||||
&GroupFV,
|
||||
GroupFN, nil,
|
||||
"Backup data by group; accepts '"+Wildcard+"' to select all groups.")
|
||||
|
||||
cobra.CheckErr(cmd.RegisterFlagCompletionFunc(GroupFN, completionFunc))
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var CategoryDataFV []string
|
||||
@ -39,4 +40,36 @@ func AddDataFlag(cmd *cobra.Command, allowed []string, hide bool) {
|
||||
if hide {
|
||||
cobra.CheckErr(fs.MarkHidden(CategoryDataFN))
|
||||
}
|
||||
|
||||
// TODO(meain): This is a hacky way to get it to autocomplete multiple items
|
||||
cobra.CheckErr(cmd.RegisterFlagCompletionFunc(
|
||||
CategoryDataFN,
|
||||
func(
|
||||
cmd *cobra.Command,
|
||||
args []string,
|
||||
toComplete string,
|
||||
) ([]string, cobra.ShellCompDirective) {
|
||||
added := strings.Split(toComplete, ",")
|
||||
last := added[len(added)-1]
|
||||
added = added[:len(added)-1]
|
||||
|
||||
if slices.Contains(added, last) {
|
||||
added = append(added, last)
|
||||
last = ""
|
||||
}
|
||||
|
||||
pending := make([]string, 0, len(allowed)-len(added))
|
||||
for _, a := range allowed {
|
||||
if !slices.Contains(added, a) && strings.HasPrefix(a, last) {
|
||||
pending = append(pending, a)
|
||||
}
|
||||
}
|
||||
|
||||
completions := []string{}
|
||||
for _, p := range pending {
|
||||
completions = append(completions, strings.Join(append(added, p), ","))
|
||||
}
|
||||
|
||||
return completions, cobra.ShellCompDirectiveNoSpace
|
||||
}))
|
||||
}
|
||||
|
||||
@ -22,16 +22,24 @@ var (
|
||||
)
|
||||
|
||||
// AddUserFlag adds the --user flag.
|
||||
func AddUserFlag(cmd *cobra.Command) {
|
||||
func AddUserFlag(
|
||||
cmd *cobra.Command,
|
||||
completionFunc func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective),
|
||||
) {
|
||||
cmd.Flags().StringSliceVar(
|
||||
&UserFV,
|
||||
UserFN, nil,
|
||||
"Backup a specific user's data; accepts '"+Wildcard+"' to select all users.")
|
||||
cobra.CheckErr(cmd.MarkFlagRequired(UserFN))
|
||||
|
||||
cobra.CheckErr(cmd.RegisterFlagCompletionFunc(UserFN, completionFunc))
|
||||
}
|
||||
|
||||
// AddMailBoxFlag adds the --user and --mailbox flag.
|
||||
func AddMailBoxFlag(cmd *cobra.Command) {
|
||||
func AddMailBoxFlag(
|
||||
cmd *cobra.Command,
|
||||
completionFunc func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective),
|
||||
) {
|
||||
flags := cmd.Flags()
|
||||
|
||||
flags.StringSliceVar(
|
||||
@ -41,10 +49,22 @@ func AddMailBoxFlag(cmd *cobra.Command) {
|
||||
|
||||
cobra.CheckErr(flags.MarkDeprecated(UserFN, fmt.Sprintf("use --%s instead", MailBoxFN)))
|
||||
|
||||
cobra.CheckErr(cmd.RegisterFlagCompletionFunc(UserFN,
|
||||
func(
|
||||
cmd *cobra.Command,
|
||||
args []string,
|
||||
toComplete string,
|
||||
) ([]string, cobra.ShellCompDirective) {
|
||||
message := fmt.Sprintf("This flag is deprecated, Use --%s instead", MailBoxFN)
|
||||
return cobra.AppendActiveHelp(nil, message), cobra.ShellCompDirectiveNoFileComp
|
||||
}))
|
||||
|
||||
flags.StringSliceVar(
|
||||
&UserFV,
|
||||
MailBoxFN, nil,
|
||||
"Backup a specific mailbox's data; accepts '"+Wildcard+"' to select all mailbox.")
|
||||
|
||||
cobra.CheckErr(cmd.RegisterFlagCompletionFunc(MailBoxFN, completionFunc))
|
||||
}
|
||||
|
||||
// AddAzureCredsFlags adds M365 cred flags
|
||||
|
||||
@ -41,8 +41,13 @@ func AddMultipleBackupIDsFlag(cmd *cobra.Command, require bool) {
|
||||
}
|
||||
|
||||
// AddBackupIDFlag adds the --backup flag.
|
||||
func AddBackupIDFlag(cmd *cobra.Command, require bool) {
|
||||
func AddBackupIDFlag(
|
||||
cmd *cobra.Command,
|
||||
require bool,
|
||||
completionFunc func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective),
|
||||
) {
|
||||
cmd.Flags().StringVar(&BackupIDFV, BackupFN, "", "ID of the backup to retrieve.")
|
||||
cobra.CheckErr(cmd.RegisterFlagCompletionFunc(BackupFN, completionFunc))
|
||||
|
||||
if require {
|
||||
cobra.CheckErr(cmd.MarkFlagRequired(BackupFN))
|
||||
|
||||
@ -95,6 +95,7 @@ func AddSharePointDetailsAndRestoreFlags(cmd *cobra.Command) {
|
||||
// AddSiteIDFlag adds the --site-id flag, which accepts site ID values.
|
||||
// This flag is hidden, since we expect users to prefer the --site url
|
||||
// and do not want to encourage confusion.
|
||||
// TODO(meain): --site is the primary one, but it would be useful to have comepletion for this as well
|
||||
func AddSiteIDFlag(cmd *cobra.Command, multiple bool) {
|
||||
fs := cmd.Flags()
|
||||
|
||||
@ -112,11 +113,17 @@ func AddSiteIDFlag(cmd *cobra.Command, multiple bool) {
|
||||
}
|
||||
|
||||
// AddSiteFlag adds the --site flag, which accepts webURL values.
|
||||
func AddSiteFlag(cmd *cobra.Command, multiple bool) {
|
||||
func AddSiteFlag(
|
||||
cmd *cobra.Command,
|
||||
multiple bool,
|
||||
completionFunc func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective),
|
||||
) {
|
||||
message := "Web URL of the site to operate on"
|
||||
if multiple {
|
||||
message += "; accepts '" + Wildcard + "' to select all sites."
|
||||
}
|
||||
|
||||
cmd.Flags().StringSliceVar(&WebURLFV, SiteFN, nil, message)
|
||||
|
||||
cobra.CheckErr(cmd.RegisterFlagCompletionFunc(SiteFN, completionFunc))
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
. "github.com/alcionai/corso/src/cli/print"
|
||||
"github.com/alcionai/corso/src/cli/utils"
|
||||
)
|
||||
|
||||
// AddCommands attaches all `corso env * *` commands to the parent.
|
||||
@ -19,7 +20,7 @@ func envCmd() *cobra.Command {
|
||||
Short: "env var guide",
|
||||
Long: `A guide to using environment variables in Corso.`,
|
||||
RunE: handleEnvCmd,
|
||||
Args: cobra.NoArgs,
|
||||
Args: utils.SubcommandsRequiredWithSuggestions,
|
||||
}
|
||||
envCmd.SetHelpFunc(envGuide)
|
||||
|
||||
|
||||
@ -77,7 +77,7 @@ func repoCmd() *cobra.Command {
|
||||
Short: "Manage your repositories",
|
||||
Long: `Initialize, configure, connect and update to your account backup repositories`,
|
||||
RunE: handleRepoCmd,
|
||||
Args: cobra.NoArgs,
|
||||
Args: utils.SubcommandsRequiredWithSuggestions,
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ func initCmd() *cobra.Command {
|
||||
Short: "Initialize a repository.",
|
||||
Long: `Create a new repository to store your backups.`,
|
||||
RunE: handleInitCmd,
|
||||
Args: cobra.NoArgs,
|
||||
Args: utils.SubcommandsRequiredWithSuggestions,
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,7 +112,7 @@ func connectCmd() *cobra.Command {
|
||||
Short: "Connect to a repository.",
|
||||
Long: `Connect to an existing repository.`,
|
||||
RunE: handleConnectCmd,
|
||||
Args: cobra.NoArgs,
|
||||
Args: utils.SubcommandsRequiredWithSuggestions,
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,7 +127,7 @@ func maintenanceCmd() *cobra.Command {
|
||||
Short: "Run maintenance on an existing repository",
|
||||
Long: `Run maintenance on an existing repository to optimize performance and storage use`,
|
||||
RunE: handleMaintenanceCmd,
|
||||
Args: cobra.NoArgs,
|
||||
Args: utils.SubcommandsRequiredWithSuggestions,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/cli/flags"
|
||||
"github.com/alcionai/corso/src/cli/utils"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
|
||||
// called by restore.go to map subcommands to provider-specific handling.
|
||||
@ -26,7 +27,7 @@ func addExchangeCommands(cmd *cobra.Command) *cobra.Command {
|
||||
// general flags
|
||||
fs.SortFlags = false
|
||||
|
||||
flags.AddBackupIDFlag(c, true)
|
||||
flags.AddBackupIDFlag(c, true, utils.BackupIDCompletionFunc(path.ExchangeService))
|
||||
flags.AddExchangeDetailsAndRestoreFlags(c, false)
|
||||
flags.AddRestoreConfigFlags(c, true)
|
||||
flags.AddFailFastFlag(c)
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"github.com/alcionai/corso/src/cli/flags"
|
||||
"github.com/alcionai/corso/src/cli/utils"
|
||||
"github.com/alcionai/corso/src/internal/common/dttm"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
|
||||
// called by restore.go to map subcommands to provider-specific handling.
|
||||
@ -26,8 +27,8 @@ func addGroupsCommands(cmd *cobra.Command) *cobra.Command {
|
||||
// More generic (ex: --user) and more frequently used flags take precedence.
|
||||
fs.SortFlags = false
|
||||
|
||||
flags.AddBackupIDFlag(c, true)
|
||||
flags.AddSiteFlag(c, false)
|
||||
flags.AddBackupIDFlag(c, true, utils.BackupIDCompletionFunc(path.GroupsService))
|
||||
flags.AddSiteFlag(c, false, utils.SitesCompletionFunc())
|
||||
flags.AddSiteIDFlag(c, false)
|
||||
flags.AddNoPermissionsFlag(c)
|
||||
flags.AddSharePointDetailsAndRestoreFlags(c)
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"github.com/alcionai/corso/src/cli/flags"
|
||||
"github.com/alcionai/corso/src/cli/utils"
|
||||
"github.com/alcionai/corso/src/internal/common/dttm"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
|
||||
// called by restore.go to map subcommands to provider-specific handling.
|
||||
@ -26,7 +27,7 @@ func addOneDriveCommands(cmd *cobra.Command) *cobra.Command {
|
||||
// More generic (ex: --user) and more frequently used flags take precedence.
|
||||
fs.SortFlags = false
|
||||
|
||||
flags.AddBackupIDFlag(c, true)
|
||||
flags.AddBackupIDFlag(c, true, utils.BackupIDCompletionFunc(path.OneDriveService))
|
||||
flags.AddOneDriveDetailsAndRestoreFlags(c)
|
||||
flags.AddNoPermissionsFlag(c)
|
||||
flags.AddRestoreConfigFlags(c, true)
|
||||
|
||||
@ -74,7 +74,7 @@ func restoreCmd() *cobra.Command {
|
||||
Short: "Restore your service data",
|
||||
Long: `Restore the data stored in one of your M365 services.`,
|
||||
RunE: handleRestoreCmd,
|
||||
Args: cobra.NoArgs,
|
||||
Args: utils.SubcommandsRequiredWithSuggestions,
|
||||
Example: restoreCommandExamples,
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"github.com/alcionai/corso/src/cli/flags"
|
||||
"github.com/alcionai/corso/src/cli/utils"
|
||||
"github.com/alcionai/corso/src/internal/common/dttm"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
|
||||
// called by restore.go to map subcommands to provider-specific handling.
|
||||
@ -26,7 +27,7 @@ func addSharePointCommands(cmd *cobra.Command) *cobra.Command {
|
||||
// More generic (ex: --site) and more frequently used flags take precedence.
|
||||
fs.SortFlags = false
|
||||
|
||||
flags.AddBackupIDFlag(c, true)
|
||||
flags.AddBackupIDFlag(c, true, utils.BackupIDCompletionFunc(path.SharePointService))
|
||||
flags.AddSharePointDetailsAndRestoreFlags(c)
|
||||
flags.AddNoPermissionsFlag(c)
|
||||
flags.AddRestoreConfigFlags(c, true)
|
||||
|
||||
194
src/cli/utils/completions.go
Normal file
194
src/cli/utils/completions.go
Normal file
@ -0,0 +1,194 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/alcionai/corso/src/pkg/backup"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
"github.com/alcionai/corso/src/pkg/services/m365"
|
||||
"github.com/alcionai/corso/src/pkg/store"
|
||||
)
|
||||
|
||||
func GetBackups(ctx context.Context, cmd *cobra.Command, service path.ServiceType) ([]*backup.Backup, error) {
|
||||
r, _, err := GetAccountAndConnect(ctx, cmd, service)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer CloseRepo(ctx, r)
|
||||
|
||||
return r.BackupsByTag(ctx, store.Service(service))
|
||||
}
|
||||
|
||||
type completionFunc func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective)
|
||||
|
||||
func BackupIDCompletionFunc(service path.ServiceType) completionFunc {
|
||||
return func(
|
||||
cmd *cobra.Command,
|
||||
args []string,
|
||||
toComplete string,
|
||||
) ([]string, cobra.ShellCompDirective) {
|
||||
bs, err := GetBackups(cmd.Context(), cmd, service)
|
||||
if err != nil {
|
||||
return cobra.AppendActiveHelp(
|
||||
nil,
|
||||
fmt.Sprintf("Unable to fetch %s backups", service)), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
if len(bs) == 0 {
|
||||
return cobra.AppendActiveHelp(
|
||||
nil,
|
||||
fmt.Sprintf("No %s backups found", service)), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
backups := make([]string, len(bs))
|
||||
for _, b := range bs {
|
||||
backups = append(backups, fmt.Sprintf("%s\tCreated at %s", b.GetID(), b.CreationTime))
|
||||
}
|
||||
|
||||
return cobra.AppendActiveHelp(backups, "Choose backup ID to use"), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
}
|
||||
|
||||
func UsersCompletionFunc(service path.ServiceType) completionFunc {
|
||||
return func(
|
||||
cmd *cobra.Command,
|
||||
args []string,
|
||||
toComplete string,
|
||||
) ([]string, cobra.ShellCompDirective) {
|
||||
ctx := cmd.Context()
|
||||
|
||||
r, acct, err := AccountConnectAndWriteRepoConfig(ctx, cmd, service)
|
||||
if err != nil {
|
||||
return cobra.AppendActiveHelp(
|
||||
nil,
|
||||
fmt.Sprintf("Unable to fetch %s users", service)), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
ins, err := UsersMap(ctx, *acct, Control(), r.Counter(), fault.New(true))
|
||||
if err != nil {
|
||||
return cobra.AppendActiveHelp(
|
||||
nil,
|
||||
fmt.Sprintf("Unable to fetch %s users", service)), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
if len(ins.IDs()) == 0 {
|
||||
return cobra.AppendActiveHelp(
|
||||
nil,
|
||||
fmt.Sprintf("No %s users found", service)), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
backups := make([]string, len(ins.IDs()))
|
||||
|
||||
for _, u := range ins.IDs() {
|
||||
name, _ := ins.NameOf(u)
|
||||
backups = append(backups, fmt.Sprintf("%s\t%s", u, name))
|
||||
}
|
||||
|
||||
return cobra.AppendActiveHelp(backups, "Choose user ID to use"), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
}
|
||||
|
||||
func MailboxCompletionFunc(service path.ServiceType) completionFunc {
|
||||
return func(
|
||||
cmd *cobra.Command,
|
||||
args []string,
|
||||
toComplete string,
|
||||
) ([]string, cobra.ShellCompDirective) {
|
||||
ctx := cmd.Context()
|
||||
|
||||
r, acct, err := AccountConnectAndWriteRepoConfig(ctx, cmd, service)
|
||||
if err != nil {
|
||||
return cobra.AppendActiveHelp(
|
||||
nil,
|
||||
fmt.Sprintf("Unable to fetch %s mailboxes", service)), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
ins, err := UsersMap(ctx, *acct, Control(), r.Counter(), fault.New(true))
|
||||
if err != nil {
|
||||
return cobra.AppendActiveHelp(
|
||||
nil,
|
||||
fmt.Sprintf("Unable to fetch %s mailboxes", service)), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
if len(ins.IDs()) == 0 {
|
||||
return cobra.AppendActiveHelp(
|
||||
nil,
|
||||
fmt.Sprintf("No %s mailboxes found", service)), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
backups := ins.Names()
|
||||
|
||||
return cobra.AppendActiveHelp(backups, "Choose mailbox to use"), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
}
|
||||
|
||||
func GroupsCompletionFunc() completionFunc {
|
||||
return func(
|
||||
cmd *cobra.Command,
|
||||
args []string,
|
||||
toComplete string,
|
||||
) ([]string, cobra.ShellCompDirective) {
|
||||
ctx := cmd.Context()
|
||||
|
||||
_, acct, err := AccountConnectAndWriteRepoConfig(ctx, cmd, path.GroupsService)
|
||||
if err != nil {
|
||||
return cobra.AppendActiveHelp(nil, "Unable to fetch Groups"), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
ins, err := m365.GroupsMap(ctx, *acct, fault.New(true))
|
||||
if err != nil {
|
||||
return cobra.AppendActiveHelp(nil, "Unable to fetch Groups"), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
if len(ins.IDs()) == 0 {
|
||||
return cobra.AppendActiveHelp(nil, "No Groups found"), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
backups := make([]string, len(ins.IDs()))
|
||||
|
||||
for _, u := range ins.IDs() {
|
||||
name, _ := ins.NameOf(u)
|
||||
backups = append(backups, fmt.Sprintf("%s\t%s", u, name))
|
||||
}
|
||||
|
||||
return cobra.AppendActiveHelp(backups, "Choose Group to use"), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
}
|
||||
|
||||
func SitesCompletionFunc() completionFunc {
|
||||
return func(
|
||||
cmd *cobra.Command,
|
||||
args []string,
|
||||
toComplete string,
|
||||
) ([]string, cobra.ShellCompDirective) {
|
||||
ctx := cmd.Context()
|
||||
|
||||
_, acct, err := AccountConnectAndWriteRepoConfig(ctx, cmd, path.SharePointService)
|
||||
if err != nil {
|
||||
return cobra.AppendActiveHelp(nil, "Unable to fetch Sites"), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
ins, err := m365.SitesMap(ctx, *acct, fault.New(true))
|
||||
if err != nil {
|
||||
return cobra.AppendActiveHelp(nil, "Unable to fetch Sites"), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
if len(ins.IDs()) == 0 {
|
||||
return cobra.AppendActiveHelp(nil, "No Sites found"), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
backups := make([]string, len(ins.IDs()))
|
||||
|
||||
for _, u := range ins.IDs() {
|
||||
name, _ := ins.NameOf(u)
|
||||
backups = append(backups, fmt.Sprintf("%s\t%s", u, name))
|
||||
}
|
||||
|
||||
return cobra.AppendActiveHelp(backups, "Choose Site to use"), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
}
|
||||
115
src/cli/utils/suggestions.go
Normal file
115
src/cli/utils/suggestions.go
Normal file
@ -0,0 +1,115 @@
|
||||
package utils
|
||||
|
||||
// The code in this file is mostly lifted out of cobra itself. It as
|
||||
// of now has some issue with how it handles completions for
|
||||
// subcommands and only autocompletes for top level commands.
|
||||
// https://github.com/spf13/cobra/issues/981#issuecomment-547003669
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// SubcommandsRequiredWithSuggestions will ensure we have a subcommand provided by the user and augments it with
|
||||
// suggestion for commands, alias and help on root command.
|
||||
func SubcommandsRequiredWithSuggestions(cmd *cobra.Command, args []string) error {
|
||||
requireMsg := "%s requires a valid subcommand"
|
||||
// This will be triggered if cobra didn't find any subcommands.
|
||||
// Find some suggestions.
|
||||
var suggestions []string
|
||||
|
||||
if len(args) != 0 && !cmd.DisableSuggestions {
|
||||
typedName := args[0]
|
||||
|
||||
if cmd.SuggestionsMinimumDistance <= 0 {
|
||||
cmd.SuggestionsMinimumDistance = 2
|
||||
}
|
||||
// subcommand suggestions
|
||||
suggestions = cmd.SuggestionsFor(args[0])
|
||||
|
||||
// subcommand alias suggestions (with distance, not exact)
|
||||
for _, c := range cmd.Commands() {
|
||||
if c.IsAvailableCommand() {
|
||||
for _, alias := range c.Aliases {
|
||||
levenshteinDistance := levenshteinDistance(typedName, alias, true)
|
||||
suggestByLevenshtein := levenshteinDistance <= cmd.SuggestionsMinimumDistance
|
||||
suggestByPrefix := strings.HasPrefix(strings.ToLower(alias), strings.ToLower(typedName))
|
||||
|
||||
if suggestByLevenshtein || suggestByPrefix {
|
||||
suggestions = append(suggestions, alias)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// help for root command
|
||||
if !cmd.HasParent() {
|
||||
help := "help"
|
||||
levenshteinDistance := levenshteinDistance(typedName, help, true)
|
||||
suggestByLevenshtein := levenshteinDistance <= cmd.SuggestionsMinimumDistance
|
||||
suggestByPrefix := strings.HasPrefix(strings.ToLower(help), strings.ToLower(typedName))
|
||||
|
||||
if suggestByLevenshtein || suggestByPrefix {
|
||||
suggestions = append(suggestions, help)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var suggestionsMsg string
|
||||
if len(suggestions) > 0 {
|
||||
suggestionsMsg += "\n\nDid you mean this?\n"
|
||||
for _, s := range suggestions {
|
||||
suggestionsMsg += fmt.Sprintf("\t%v\n", s)
|
||||
}
|
||||
}
|
||||
|
||||
if len(suggestionsMsg) > 0 {
|
||||
requireMsg = fmt.Sprintf("%s. %s", requireMsg, suggestionsMsg)
|
||||
}
|
||||
|
||||
return fmt.Errorf(requireMsg, cmd.Name())
|
||||
}
|
||||
|
||||
// levenshteinDistance compares two strings and returns the levenshtein distance between them.
|
||||
func levenshteinDistance(s, t string, ignoreCase bool) int {
|
||||
if ignoreCase {
|
||||
s = strings.ToLower(s)
|
||||
t = strings.ToLower(t)
|
||||
}
|
||||
|
||||
d := make([][]int, len(s)+1)
|
||||
for i := range d {
|
||||
d[i] = make([]int, len(t)+1)
|
||||
}
|
||||
|
||||
for i := range d {
|
||||
d[i][0] = i
|
||||
}
|
||||
|
||||
for j := range d[0] {
|
||||
d[0][j] = j
|
||||
}
|
||||
|
||||
for j := 1; j <= len(t); j++ {
|
||||
for i := 1; i <= len(s); i++ {
|
||||
if s[i-1] == t[j-1] {
|
||||
d[i][j] = d[i-1][j-1]
|
||||
} else {
|
||||
min := d[i-1][j]
|
||||
if d[i][j-1] < min {
|
||||
min = d[i][j-1]
|
||||
}
|
||||
|
||||
if d[i-1][j-1] < min {
|
||||
min = d[i-1][j-1]
|
||||
}
|
||||
|
||||
d[i][j] = min + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return d[len(s)][len(t)]
|
||||
}
|
||||
@ -89,7 +89,8 @@ func fatal(err error) {
|
||||
// Adapted from https://github.com/spf13/cobra/blob/main/doc/md_docs.go for Corso specific formatting
|
||||
func genMarkdownCorso(cmd *cobra.Command, dir string) error {
|
||||
for _, c := range cmd.Commands() {
|
||||
if !isAvailableCommand(c) || c.IsAdditionalHelpTopicCommand() {
|
||||
if c.Use != "completion [bash|zsh|fish|powershell]" &&
|
||||
(!isAvailableCommand(c) || c.IsAdditionalHelpTopicCommand()) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@ -101,6 +101,16 @@ func AddLoggingFlags(cmd *cobra.Command) {
|
||||
|
||||
addFlags(fs, "corso-<timestamp>.log")
|
||||
|
||||
cobra.CheckErr(cmd.RegisterFlagCompletionFunc(LogLevelFN, cobra.FixedCompletions(
|
||||
[]string{string(LLDebug), string(LLInfo), string(LLWarn), string(LLError), string(LLDisabled)},
|
||||
cobra.ShellCompDirectiveNoFileComp)))
|
||||
cobra.CheckErr(cmd.RegisterFlagCompletionFunc(LogFormatFN, cobra.FixedCompletions(
|
||||
[]string{string(LFText), string(LFJSON)},
|
||||
cobra.ShellCompDirectiveNoFileComp)))
|
||||
cobra.CheckErr(cmd.RegisterFlagCompletionFunc(MaskSensitiveDataFN, cobra.FixedCompletions(
|
||||
[]string{string(PIIPlainText), string(PIIMask), string(PIIHash)},
|
||||
cobra.ShellCompDirectiveNoFileComp)))
|
||||
|
||||
//nolint:errcheck
|
||||
fs.MarkHidden(ReadableLogsFN)
|
||||
}
|
||||
|
||||
@ -55,8 +55,8 @@ _validatemdgen: # in case we have a different architecture
|
||||
|
||||
${MDGEN_BINARY}: $(shell find ${CORSO_LOCAL_PATH}/src -type f -name *.go) $(shell find ${CORSO_LOCAL_PATH}/src -type d )
|
||||
@echo 'Re-building Corso CLI docs auto-gen tooling...'
|
||||
$(GOC) go mod download
|
||||
$(GOC) go build -o ${MDGEN_BINARY} ${MDGEN_SRC}
|
||||
$(GOC) go mod download
|
||||
$(GOC) go build -o ${MDGEN_BINARY} ${MDGEN_SRC}
|
||||
|
||||
clean:
|
||||
$(WEBC) rm -rf docs/cli build node_modules
|
||||
|
||||
@ -35,3 +35,5 @@ Below is a list of known Corso issues and limitations:
|
||||
* Groups and Teams support is available in an early-access status, and may be subject to breaking changes.
|
||||
|
||||
* Restoring the data into a different Group from the one it was backed up from isn't currently supported
|
||||
|
||||
* CLI completions can't autocomplete multiple values for flags
|
||||
|
||||
@ -54,7 +54,8 @@ const sidebars = {
|
||||
'cli/corso-repo-connect-filesystem',
|
||||
'cli/corso-repo-maintenance',
|
||||
'cli/corso-repo-update-passphrase',
|
||||
'cli/corso-env']
|
||||
'cli/corso-env',
|
||||
'cli/corso-completion']
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
|
||||
@ -60,3 +60,4 @@ subtrees
|
||||
anonymized
|
||||
unreferenced
|
||||
hostname
|
||||
zsh
|
||||
Loading…
x
Reference in New Issue
Block a user