diff --git a/cmd/incus/main.go b/cmd/incus/main.go index ce0fda48d8c..bc435fe0672 100644 --- a/cmd/incus/main.go +++ b/cmd/incus/main.go @@ -92,14 +92,7 @@ func aliases() []string { return aliases } -func main() { - // Process aliases - err := execIfAliases() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(1) - } - +func createApp() (*cobra.Command, *cmdGlobal) { // Setup the parser app := &cobra.Command{} app.Use = "incus" @@ -306,8 +299,14 @@ Custom commands can be defined through aliases, use "incus alias" to control tho app.Flags().BoolVar(&globalCmd.flagHelpAll, "all", false, i18n.G("Show less common commands")) help.Flags().BoolVar(&globalCmd.flagHelpAll, "all", false, i18n.G("Show less common commands")) - // Deal with --all flag and --sub-commands flag - err = app.ParseFlags(os.Args[1:]) + return app, &globalCmd +} + +func main() { + app, globalCmd := createApp() + + // Deal with --all and --sub-commands flags as well as process aliases. + err := app.ParseFlags(os.Args[1:]) if err == nil { if globalCmd.flagHelpAll { // Show all commands @@ -325,6 +324,13 @@ Custom commands can be defined through aliases, use "incus alias" to control tho } } + // Process aliases + err = execIfAliases(app) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + // Run the main command and handle errors err = app.Execute() if err != nil { diff --git a/cmd/incus/main_aliases.go b/cmd/incus/main_aliases.go index 64f29acebfd..d02bb9179da 100644 --- a/cmd/incus/main_aliases.go +++ b/cmd/incus/main_aliases.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/kballard/go-shellquote" + "github.com/spf13/cobra" "github.com/lxc/incus/v6/internal/i18n" config "github.com/lxc/incus/v6/shared/cliconfig" @@ -76,26 +77,36 @@ func findAlias(aliases map[string]string, origArgs []string) ([]string, []string return aliasKey, aliasValue, foundAlias } -func expandAlias(conf *config.Config, args []string) ([]string, bool, error) { - var completion = false - var completionFrament string - var newArgs []string - var origArgs []string +func expandAlias(conf *config.Config, args []string, app *cobra.Command) ([]string, bool, error) { + fset := app.Flags() - for _, arg := range args[1:] { - if !strings.HasPrefix(arg, "-") { - break - } + nargs := fset.NArg() + firstArgIndex := 1 + firstPosArgIndex := 0 + if fset.Arg(0) == "__complete" { + nargs-- + firstArgIndex++ + firstPosArgIndex++ + } - newArgs = append(newArgs, arg) + if nargs == 0 { + return nil, false, nil } - origArgs = append([]string{args[0]}, args[len(newArgs)+1:]...) + lastFlagIndex := slices.Index(args, fset.Arg(firstPosArgIndex)) + + // newArgs contains all the flags before the first positional argument + newArgs := args[firstArgIndex:lastFlagIndex] + + // origArgs contains everything except the flags in newArgs + origArgs := slices.Concat(args[:firstArgIndex], args[lastFlagIndex:]) // strip out completion subcommand and fragment from end + completion := false + completionFragment := "" if len(origArgs) >= 3 && origArgs[1] == "__complete" { completion = true - completionFrament = origArgs[len(origArgs)-1] + completionFragment = origArgs[len(origArgs)-1] origArgs = append(origArgs[:1], origArgs[2:len(origArgs)-1]...) } @@ -189,7 +200,7 @@ func expandAlias(conf *config.Config, args []string) ([]string, bool, error) { // add back in completion if it was stripped before if completion { newArgs = append([]string{newArgs[0], "__complete"}, newArgs[1:]...) - newArgs = append(newArgs, completionFrament) + newArgs = append(newArgs, completionFragment) } // Add the rest of the arguments only if @ARGS@ wasn't used. @@ -200,9 +211,7 @@ func expandAlias(conf *config.Config, args []string) ([]string, bool, error) { return newArgs, true, nil } -func execIfAliases() error { - args := os.Args - +func execIfAliases(app *cobra.Command) error { // Avoid loops if os.Getenv("INCUS_ALIASES") == "1" { return nil @@ -214,7 +223,7 @@ func execIfAliases() error { } // Expand the aliases - newArgs, expanded, err := expandAlias(conf, args) + newArgs, expanded, err := expandAlias(conf, os.Args, app) if err != nil { return err } else if !expanded { diff --git a/cmd/incus/main_test.go b/cmd/incus/main_test.go index ff6eb2ca2ca..29bfc24b18a 100644 --- a/cmd/incus/main_test.go +++ b/cmd/incus/main_test.go @@ -84,12 +84,22 @@ func TestExpandAliases(t *testing.T) { input: []string{"incus", "snapshots", "with", "recursion", "c1", "2"}, expected: []string{"incus", "query", "/1.0/instances/c1/snapshots?recursion=2"}, }, + { + input: []string{"incus", "--project", "default", "fizz", "c1", "buzz"}, + expected: []string{"incus", "--project", "default", "exec", "c1", "--", "echo", "buzz"}, + }, + { + input: []string{"incus", "--project=default", "fizz", "c1", "buzz"}, + expected: []string{"incus", "--project=default", "exec", "c1", "--", "echo", "buzz"}, + }, } conf := &config.Config{Aliases: aliases} for _, tc := range testcases { - result, expanded, err := expandAlias(conf, tc.input) + app, _ := createApp() + _ = app.ParseFlags(tc.input[1:]) + result, expanded, err := expandAlias(conf, tc.input, app) if tc.expectErr { assert.Error(t, err) continue