Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cli: public commands API #232

Merged
merged 20 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 49 additions & 67 deletions internals/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,52 +63,34 @@ var optionsData options
// ErrExtraArgs is returned if extra arguments to a command are found
var ErrExtraArgs = fmt.Errorf("too many arguments for command")

// cmdInfo holds information needed to call parser.AddCommand(...).
type cmdInfo struct {
name, shortHelp, longHelp string
builder func() flags.Commander
hidden bool
optDescs map[string]string
argDescs []argDesc
alias string
extra func(*flags.Command)
// CmdInfo holds information needed to call parser.AddCommand(...).
type CmdInfo struct {
anpep marked this conversation as resolved.
Show resolved Hide resolved
Name, ShortHelp, LongHelp string
Builder func() flags.Commander
Hidden bool
OptDescs map[string]string
ArgDescs []argDesc
anpep marked this conversation as resolved.
Show resolved Hide resolved
Alias string
Extra func(*flags.Command)
}

// commands holds information about all non-debug commands.
var commands []*cmdInfo
var commands []*CmdInfo

// debugCommands holds information about all debug commands.
var debugCommands []*cmdInfo
var debugCommands []*CmdInfo

// AddCommand replaces parser.addCommand() in a way that is compatible with
// re-constructing a pristine parser.
func AddCommand(name, shortHelp, longHelp string, builder func() flags.Commander, optDescs map[string]string, argDescs []argDesc) *cmdInfo {
info := &cmdInfo{
name: name,
shortHelp: shortHelp,
longHelp: longHelp,
builder: builder,
optDescs: optDescs,
argDescs: argDescs,
}
func AddCommand(info *CmdInfo) {
anpep marked this conversation as resolved.
Show resolved Hide resolved
commands = append(commands, info)
anpep marked this conversation as resolved.
Show resolved Hide resolved
return info
}

// addDebugCommand replaces parser.addCommand() in a way that is
// compatible with re-constructing a pristine parser. It is meant for
// adding debug commands.
func addDebugCommand(name, shortHelp, longHelp string, builder func() flags.Commander, optDescs map[string]string, argDescs []argDesc) *cmdInfo {
anpep marked this conversation as resolved.
Show resolved Hide resolved
info := &cmdInfo{
name: name,
shortHelp: shortHelp,
longHelp: longHelp,
builder: builder,
optDescs: optDescs,
argDescs: argDescs,
}
func addDebugCommand(info *CmdInfo) {
debugCommands = append(debugCommands, info)
return info
}

type parserSetter interface {
Expand Down Expand Up @@ -192,59 +174,59 @@ func Parser(cli *client.Client) *flags.Parser {

// Add all regular commands
for _, c := range commands {
obj := c.builder()
obj := c.Builder()
if x, ok := obj.(clientSetter); ok {
x.setClient(cli)
}
if x, ok := obj.(parserSetter); ok {
x.setParser(parser)
}

cmd, err := parser.AddCommand(c.name, c.shortHelp, strings.TrimSpace(c.longHelp), obj)
cmd, err := parser.AddCommand(c.Name, c.ShortHelp, strings.TrimSpace(c.LongHelp), obj)
if err != nil {
logger.Panicf("cannot add command %q: %v", c.name, err)
logger.Panicf("cannot add command %q: %v", c.Name, err)
}
cmd.Hidden = c.hidden
if c.alias != "" {
cmd.Aliases = append(cmd.Aliases, c.alias)
cmd.Hidden = c.Hidden
if c.Alias != "" {
cmd.Aliases = append(cmd.Aliases, c.Alias)
}

opts := cmd.Options()
if c.optDescs != nil && len(opts) != len(c.optDescs) {
logger.Panicf("wrong number of option descriptions for %s: expected %d, got %d", c.name, len(opts), len(c.optDescs))
if c.OptDescs != nil && len(opts) != len(c.OptDescs) {
logger.Panicf("wrong number of option descriptions for %s: expected %d, got %d", c.Name, len(opts), len(c.OptDescs))
}
for _, opt := range opts {
name := opt.LongName
if name == "" {
name = string(opt.ShortName)
}
desc, ok := c.optDescs[name]
if !(c.optDescs == nil || ok) {
logger.Panicf("%s missing description for %s", c.name, name)
desc, ok := c.OptDescs[name]
if !(c.OptDescs == nil || ok) {
logger.Panicf("%s missing description for %s", c.Name, name)
}
lintDesc(c.name, name, desc, opt.Description)
lintDesc(c.Name, name, desc, opt.Description)
if desc != "" {
opt.Description = desc
}
}

args := cmd.Args()
if c.argDescs != nil && len(args) != len(c.argDescs) {
logger.Panicf("wrong number of argument descriptions for %s: expected %d, got %d", c.name, len(args), len(c.argDescs))
if c.ArgDescs != nil && len(args) != len(c.ArgDescs) {
logger.Panicf("wrong number of argument descriptions for %s: expected %d, got %d", c.Name, len(args), len(c.ArgDescs))
}
for i, arg := range args {
name, desc := arg.Name, ""
if c.argDescs != nil {
name = c.argDescs[i].name
desc = c.argDescs[i].desc
if c.ArgDescs != nil {
name = c.ArgDescs[i].name
desc = c.ArgDescs[i].desc
}
lintArg(c.name, name, desc, arg.Description)
lintArg(c.Name, name, desc, arg.Description)
name = fixupArg(name)
arg.Name = name
arg.Description = desc
}
if c.extra != nil {
c.extra(cmd)
if c.Extra != nil {
c.Extra(cmd)
}
}
// Add the debug command
Expand All @@ -255,45 +237,45 @@ func Parser(cli *client.Client) *flags.Parser {
}
// Add all the sub-commands of the debug command
for _, c := range debugCommands {
anpep marked this conversation as resolved.
Show resolved Hide resolved
obj := c.builder()
obj := c.Builder()
if x, ok := obj.(clientSetter); ok {
x.setClient(cli)
}
cmd, err := debugCommand.AddCommand(c.name, c.shortHelp, strings.TrimSpace(c.longHelp), obj)
cmd, err := debugCommand.AddCommand(c.Name, c.ShortHelp, strings.TrimSpace(c.LongHelp), obj)
if err != nil {
logger.Panicf("cannot add debug command %q: %v", c.name, err)
logger.Panicf("cannot add debug command %q: %v", c.Name, err)
}
cmd.Hidden = c.hidden
cmd.Hidden = c.Hidden
opts := cmd.Options()
if c.optDescs != nil && len(opts) != len(c.optDescs) {
logger.Panicf("wrong number of option descriptions for %s: expected %d, got %d", c.name, len(opts), len(c.optDescs))
if c.OptDescs != nil && len(opts) != len(c.OptDescs) {
logger.Panicf("wrong number of option descriptions for %s: expected %d, got %d", c.Name, len(opts), len(c.OptDescs))
}
for _, opt := range opts {
name := opt.LongName
if name == "" {
name = string(opt.ShortName)
}
desc, ok := c.optDescs[name]
if !(c.optDescs == nil || ok) {
logger.Panicf("%s missing description for %s", c.name, name)
desc, ok := c.OptDescs[name]
if !(c.OptDescs == nil || ok) {
logger.Panicf("%s missing description for %s", c.Name, name)
}
lintDesc(c.name, name, desc, opt.Description)
lintDesc(c.Name, name, desc, opt.Description)
if desc != "" {
opt.Description = desc
}
}

anpep marked this conversation as resolved.
Show resolved Hide resolved
args := cmd.Args()
if c.argDescs != nil && len(args) != len(c.argDescs) {
logger.Panicf("wrong number of argument descriptions for %s: expected %d, got %d", c.name, len(args), len(c.argDescs))
if c.ArgDescs != nil && len(args) != len(c.ArgDescs) {
logger.Panicf("wrong number of argument descriptions for %s: expected %d, got %d", c.Name, len(args), len(c.ArgDescs))
}
for i, arg := range args {
name, desc := arg.Name, ""
if c.argDescs != nil {
name = c.argDescs[i].name
desc = c.argDescs[i].desc
if c.ArgDescs != nil {
name = c.ArgDescs[i].name
desc = c.ArgDescs[i].desc
}
lintArg(c.name, name, desc, arg.Description)
lintArg(c.Name, name, desc, arg.Description)
name = fixupArg(name)
arg.Name = name
arg.Description = desc
Expand Down
8 changes: 7 additions & 1 deletion internals/cli/cmd_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,11 @@ func (cmd *cmdAdd) Execute(args []string) error {
}

func init() {
AddCommand("add", shortAddHelp, longAddHelp, func() flags.Commander { return &cmdAdd{} }, addDescs, nil)
AddCommand(&CmdInfo{
anpep marked this conversation as resolved.
Show resolved Hide resolved
Name: "add",
ShortHelp: shortAddHelp,
LongHelp: longAddHelp,
Builder: func() flags.Commander { return &cmdAdd{} },
OptDescs: addDescs,
})
}
8 changes: 7 additions & 1 deletion internals/cli/cmd_autostart.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ type cmdAutoStart struct {
}

func init() {
AddCommand("autostart", shortAutoStartHelp, longAutoStartHelp, func() flags.Commander { return &cmdAutoStart{} }, waitDescs, nil)
AddCommand(&CmdInfo{
Name: "autostart",
ShortHelp: shortAutoStartHelp,
LongHelp: longAutoStartHelp,
Builder: func() flags.Commander { return &cmdAutoStart{} },
OptDescs: waitDescs,
})
}

func (cmd cmdAutoStart) Execute(args []string) error {
Expand Down
21 changes: 15 additions & 6 deletions internals/cli/cmd_changes.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,21 @@ type cmdTasks struct {
}

func init() {
AddCommand("changes", shortChangesHelp, longChangesHelp,
func() flags.Commander { return &cmdChanges{} }, timeDescs, nil)
AddCommand("tasks", shortTasksHelp, longTasksHelp,
func() flags.Commander { return &cmdTasks{} },
merge(changeIDMixinOptDesc, timeDescs),
changeIDMixinArgDesc)
AddCommand(&CmdInfo{
Name: "changes",
ShortHelp: shortChangesHelp,
LongHelp: longChangesHelp,
Builder: func() flags.Commander { return &cmdChanges{} },
OptDescs: timeDescs,
})
AddCommand(&CmdInfo{
Name: "tasks",
ShortHelp: shortTasksHelp,
LongHelp: longTasksHelp,
Builder: func() flags.Commander { return &cmdTasks{} },
OptDescs: merge(changeIDMixinOptDesc, timeDescs),
ArgDescs: changeIDMixinArgDesc,
})
}

type changesByTime []*client.Change
Expand Down
8 changes: 7 additions & 1 deletion internals/cli/cmd_checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,11 @@ func (cmd *cmdChecks) Execute(args []string) error {
}

func init() {
AddCommand("checks", shortChecksHelp, longChecksHelp, func() flags.Commander { return &cmdChecks{} }, checksDescs, nil)
AddCommand(&CmdInfo{
Name: "checks",
ShortHelp: shortChecksHelp,
LongHelp: longChecksHelp,
Builder: func() flags.Commander { return &cmdChecks{} },
OptDescs: checksDescs,
})
}
15 changes: 11 additions & 4 deletions internals/cli/cmd_enter.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,17 @@ func init() {
for k, v := range sharedRunEnterOptsHelp {
optsHelp[k] = v
}
cmdInfo := AddCommand("enter", shortEnterHelp, longEnterHelp, func() flags.Commander { return &cmdEnter{} }, optsHelp, nil)
cmdInfo.extra = func(cmd *flags.Command) {
cmd.PassAfterNonOption = true
}
AddCommand(&CmdInfo{
Name: "enter",
ShortHelp: shortEnterHelp,
LongHelp: longEnterHelp,
Builder: func() flags.Commander { return &cmdEnter{} },
OptDescs: optsHelp,

Extra: func(cmd *flags.Command) {
cmd.PassAfterNonOption = true
},
})
}

func commandEnterFlags(commander flags.Commander) (enterFlags enterFlags, supported bool) {
Expand Down
14 changes: 10 additions & 4 deletions internals/cli/cmd_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,14 @@ func execControlHandler(process *client.ExecProcess, terminal bool, stop <-chan
}

func init() {
info := AddCommand("exec", shortExecHelp, longExecHelp, func() flags.Commander { return &cmdExec{} }, execDescs, nil)
info.extra = func(cmd *flags.Command) {
cmd.PassAfterNonOption = true
}
AddCommand(&CmdInfo{
Name: "exec",
ShortHelp: shortExecHelp,
LongHelp: longExecHelp,
Builder: func() flags.Commander { return &cmdExec{} },
OptDescs: execDescs,
Extra: func(cmd *flags.Command) {
cmd.PassAfterNonOption = true
},
})
}
28 changes: 14 additions & 14 deletions internals/cli/cmd_help.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,16 @@ type cmdHelp struct {
}

func init() {
AddCommand("help", shortHelpHelp, longHelpHelp, func() flags.Commander { return &cmdHelp{} },
map[string]string{
AddCommand(&CmdInfo{
Name: "help",
ShortHelp: shortHelpHelp,
LongHelp: longHelpHelp,
Builder: func() flags.Commander { return &cmdHelp{} },
OptDescs: map[string]string{
"all": "Show a short summary of all commands",
"man": "Generate the manpage",
}, nil)
},
})
}

func (cmd *cmdHelp) setParser(parser *flags.Parser) {
Expand Down Expand Up @@ -164,8 +169,8 @@ type HelpCategory struct {
Commands []string
}

// helpCategories helps us by grouping commands
var helpCategories = []HelpCategory{{
// HelpCategories helps us by grouping commands
var HelpCategories = []HelpCategory{{
anpep marked this conversation as resolved.
Show resolved Hide resolved
Label: "Run",
Description: "run pebble",
Commands: []string{"run", "help", "version"},
Expand All @@ -191,11 +196,6 @@ var helpCategories = []HelpCategory{{
Commands: []string{"warnings", "okay"},
}}

// AddHelpCategory appends an existing help category to the Pebble help manual.
func AddHelpCategory(category HelpCategory) {
helpCategories = append(helpCategories, category)
}

var (
longPebbleDescription = strings.TrimSpace(`
Pebble lets you control services and perform management actions on
Expand Down Expand Up @@ -234,12 +234,12 @@ func printShortHelp() {
printHelpHeader()
fmt.Fprintln(Stdout)
maxLen := 0
for _, categ := range helpCategories {
for _, categ := range HelpCategories {
if l := utf8.RuneCountInString(categ.Label); l > maxLen {
maxLen = l
}
}
for _, categ := range helpCategories {
for _, categ := range HelpCategories {
fmt.Fprintf(Stdout, "%*s: %s\n", maxLen+2, categ.Label, strings.Join(categ.Commands, ", "))
}
printHelpFooter()
Expand All @@ -249,7 +249,7 @@ func printShortHelp() {
func printLongHelp(parser *flags.Parser) {
printHelpHeader()
maxLen := 0
for _, categ := range helpCategories {
for _, categ := range HelpCategories {
for _, command := range categ.Commands {
if l := len(command); l > maxLen {
maxLen = l
Expand All @@ -264,7 +264,7 @@ func printLongHelp(parser *flags.Parser) {
cmdLookup[cmd.Name] = cmd
}

for _, categ := range helpCategories {
for _, categ := range HelpCategories {
fmt.Fprintln(Stdout)
fmt.Fprintf(Stdout, " %s (%s):\n", categ.Label, categ.Description)
for _, name := range categ.Commands {
Expand Down
Loading