diff --git a/cli.go b/cli.go index 4f89b055ae..2a50b44ee6 100644 --- a/cli.go +++ b/cli.go @@ -33,7 +33,11 @@ func (cli *Cli) Run(args []string) (err error) { if ctx.Command == nil { return ErrHelp } - ctx.Args, ctx.App, err = parseArgs(ctx.Command, args[2:]) + if ctx.Command.VariableArgs { + ctx.Args, ctx.App, err = parseVarArgs(ctx.Command, args[2:]) + } else { + ctx.Args, ctx.App, err = parseArgs(ctx.Command, args[2:]) + } if err != nil { return err } @@ -70,13 +74,33 @@ func (cli *Cli) ParseCmd(cmd string) (topic *Topic, command *Command) { return topic, cli.Commands.ByTopicAndCommand(tc[0], "") } +func parseVarArgs(command *Command, args []string) (result []string, appName string, err error) { + result = args + parseFlags := true + for i := 0; i < len(args); i++ { + switch { + case parseFlags && (args[i] == "help" || args[i] == "--help" || args[i] == "-h"): + return nil, "", ErrHelp + case args[i] == "--": + parseFlags = false + case args[i] == "-a" || args[i] == "--app": + i++ + if len(args) == i { + return nil, "", errors.New("Must specify app name") + } + appName = args[i] + } + } + return result, appName, nil +} + func parseArgs(command *Command, args []string) (result map[string]string, appName string, err error) { result = map[string]string{} numArgs := 0 parseFlags := true for i := 0; i < len(args); i++ { switch { - case args[i] == "help" || args[i] == "--help" || args[i] == "-h": + case parseFlags && (args[i] == "help" || args[i] == "--help" || args[i] == "-h"): return nil, "", ErrHelp case args[i] == "--": parseFlags = false diff --git a/command.go b/command.go index d0c2cba814..e37e75381c 100644 --- a/command.go +++ b/command.go @@ -11,19 +11,20 @@ import ( // For example, in the command `heroku apps:create` the command would be `create`. // They must have a Topic name that links to a real topic's name. type Command struct { - Topic string `json:"topic"` - Command string `json:"command,omitempty"` - Plugin string `json:"plugin"` - Usage string `json:"usage"` - Description string `json:"description"` - Help string `json:"help"` - FullHelp string `json:"fullHelp"` - Hidden bool `json:"hidden"` - NeedsApp bool `json:"needsApp"` - NeedsAuth bool `json:"needsAuth"` - Args []Arg `json:"args"` - Flags []Flag `json:"flags"` - Run func(ctx *Context) `json:"-"` + Topic string `json:"topic"` + Command string `json:"command,omitempty"` + Plugin string `json:"plugin"` + Usage string `json:"usage"` + Description string `json:"description"` + Help string `json:"help"` + FullHelp string `json:"fullHelp"` + Hidden bool `json:"hidden"` + NeedsApp bool `json:"needsApp"` + NeedsAuth bool `json:"needsAuth"` + VariableArgs bool `json:"variableArgs"` + Args []Arg `json:"args"` + Flags []Flag `json:"flags"` + Run func(ctx *Context) `json:"-"` } func (c *Command) String() string { @@ -143,8 +144,7 @@ var commandsListCmd = &Command{ Description: "list all commands", Flags: []Flag{{Name: "json"}}, Run: func(ctx *Context) { - if ctx.Args["json"] != "True" { - // TODO: remove this and make json default + if ctx.Args.(map[string]string)["json"] != "True" { for _, command := range cli.Commands { if command.Command == "" { Printf("%s\n", command.Topic) diff --git a/context.go b/context.go index b33741c071..1f4f76edc9 100644 --- a/context.go +++ b/context.go @@ -4,12 +4,12 @@ package main // It contains information about the user's command arguments // as well as Heroku information like the auth token and app name (if requested). type Context struct { - Topic *Topic `json:"topic"` - Command *Command `json:"command"` - App string `json:"app"` - Args map[string]string `json:"args"` - Cwd string `json:"cwd"` - HerokuDir string `json:"herokuDir"` + Topic *Topic `json:"topic"` + Command *Command `json:"command"` + App string `json:"app"` + Args interface{} `json:"args"` + Cwd string `json:"cwd"` + HerokuDir string `json:"herokuDir"` Auth struct { Username string `json:"username"` Password string `json:"password"` diff --git a/plugins.go b/plugins.go index b8b6d249ee..62746de146 100644 --- a/plugins.go +++ b/plugins.go @@ -68,7 +68,7 @@ var pluginsInstallCmd = &Command{ $ heroku plugins:install dickeyxxx/heroku-production-status`, Run: func(ctx *Context) { - name := ctx.Args["name"] + name := ctx.Args.(map[string]string)["name"] if len(name) == 0 { Errln("Must specify a plugin name") return @@ -99,7 +99,7 @@ var pluginsUninstallCmd = &Command{ $ heroku plugins:uninstall heroku-production-status`, Run: func(ctx *Context) { - name := ctx.Args["name"] + name := ctx.Args.(map[string]string)["name"] Errf("Uninstalling plugin %s... ", name) if err := node.RemovePackage(name); err != nil { panic(err) diff --git a/update.go b/update.go index c42de8eba2..eb919f7f51 100644 --- a/update.go +++ b/update.go @@ -27,7 +27,7 @@ var updateCmd = &Command{ Description: "updates heroku-cli", Args: []Arg{{Name: "channel", Optional: true}}, Run: func(ctx *Context) { - channel := ctx.Args["channel"] + channel := ctx.Args.(map[string]string)["channel"] if channel == "" { channel = "master" }