diff --git a/monitor/commands/attach.go b/monitor/commands/attach.go index bf2d9232..07cb5acf 100644 --- a/monitor/commands/attach.go +++ b/monitor/commands/attach.go @@ -20,7 +20,17 @@ func NewAttachCmd(m types.Monitor, stdout io.WriteCloser) types.Command { } func (cm *AttachCmd) Info() types.CommandInfo { - return types.CommandInfo{HelpMessage: "attach to a buildx server or a process in the container"} + return types.CommandInfo{ + Name: "attach", + HelpMessage: "attach to a buildx server or a process in the container", + HelpMessageLong: ` +Usage: + attach ID + +ID is for a session (visible via list command) or a process (visible via ps command). +If you attached to a process, use Ctrl-c-a for switching the monitor to that process's STDIO. +`, + } } func (cm *AttachCmd) Exec(ctx context.Context, args []string) error { diff --git a/monitor/commands/disconnect.go b/monitor/commands/disconnect.go index f55f8ccf..f358eda3 100644 --- a/monitor/commands/disconnect.go +++ b/monitor/commands/disconnect.go @@ -2,6 +2,7 @@ package commands import ( "context" + "fmt" "github.com/docker/buildx/monitor/types" "github.com/pkg/errors" @@ -16,7 +17,16 @@ func NewDisconnectCmd(m types.Monitor) types.Command { } func (cm *DisconnectCmd) Info() types.CommandInfo { - return types.CommandInfo{HelpMessage: "disconnect a client from a buildx server. Specific session ID can be specified an arg"} + return types.CommandInfo{ + Name: "disconnect", + HelpMessage: "disconnect a client from a buildx server. Specific session ID can be specified an arg", + HelpMessageLong: fmt.Sprintf(` +Usage: + disconnect [ID] + +ID is for a session (visible via list command). Default is %q. +`, cm.m.AttachedSessionID()), + } } func (cm *DisconnectCmd) Exec(ctx context.Context, args []string) error { diff --git a/monitor/commands/exec.go b/monitor/commands/exec.go index 1e44cf97..3d274d4d 100644 --- a/monitor/commands/exec.go +++ b/monitor/commands/exec.go @@ -22,7 +22,16 @@ func NewExecCmd(m types.Monitor, invokeConfig controllerapi.InvokeConfig, stdout } func (cm *ExecCmd) Info() types.CommandInfo { - return types.CommandInfo{HelpMessage: "execute a process in the interactive container"} + return types.CommandInfo{ + Name: "exec", + HelpMessage: "execute a process in the interactive container", + HelpMessageLong: ` +Usage: + exec COMMAND [ARG...] + +COMMAND and ARG... will be executed in the container. +`, + } } func (cm *ExecCmd) Exec(ctx context.Context, args []string) error { diff --git a/monitor/commands/kill.go b/monitor/commands/kill.go index be3665eb..2cb9cb5f 100644 --- a/monitor/commands/kill.go +++ b/monitor/commands/kill.go @@ -16,7 +16,16 @@ func NewKillCmd(m types.Monitor) types.Command { } func (cm *KillCmd) Info() types.CommandInfo { - return types.CommandInfo{HelpMessage: "kill buildx server"} + return types.CommandInfo{ + Name: "kill", + HelpMessage: "kill buildx server", + HelpMessageLong: ` +Usage: + kill + +Kills the currently connecting buildx server process. +`, + } } func (cm *KillCmd) Exec(ctx context.Context, args []string) error { diff --git a/monitor/commands/list.go b/monitor/commands/list.go index 5a6bfb07..29b9f597 100644 --- a/monitor/commands/list.go +++ b/monitor/commands/list.go @@ -21,7 +21,14 @@ func NewListCmd(m types.Monitor, stdout io.WriteCloser) types.Command { } func (cm *ListCmd) Info() types.CommandInfo { - return types.CommandInfo{HelpMessage: "list buildx sessions"} + return types.CommandInfo{ + Name: "list", + HelpMessage: "list buildx sessions", + HelpMessageLong: ` +Usage: + list +`, + } } func (cm *ListCmd) Exec(ctx context.Context, args []string) error { diff --git a/monitor/commands/ps.go b/monitor/commands/ps.go index 28b3bc22..b5e30c3e 100644 --- a/monitor/commands/ps.go +++ b/monitor/commands/ps.go @@ -20,7 +20,14 @@ func NewPsCmd(m types.Monitor, stdout io.WriteCloser) types.Command { } func (cm *PsCmd) Info() types.CommandInfo { - return types.CommandInfo{HelpMessage: `list processes invoked by "exec". Use "attach" to attach IO to that process`} + return types.CommandInfo{ + Name: "ps", + HelpMessage: `list processes invoked by "exec". Use "attach" to attach IO to that process`, + HelpMessageLong: ` +Usage: + ps +`, + } } func (cm *PsCmd) Exec(ctx context.Context, args []string) error { diff --git a/monitor/commands/reload.go b/monitor/commands/reload.go index 11dd4589..b5938026 100644 --- a/monitor/commands/reload.go +++ b/monitor/commands/reload.go @@ -27,7 +27,14 @@ func NewReloadCmd(m types.Monitor, stdout io.WriteCloser, progress *progress.Pri } func (cm *ReloadCmd) Info() types.CommandInfo { - return types.CommandInfo{HelpMessage: "reloads the context and build it"} + return types.CommandInfo{ + Name: "reload", + HelpMessage: "reloads the context and build it", + HelpMessageLong: ` +Usage: + reload +`, + } } func (cm *ReloadCmd) Exec(ctx context.Context, args []string) error { diff --git a/monitor/commands/rollback.go b/monitor/commands/rollback.go index 9fca04a4..8633a6e8 100644 --- a/monitor/commands/rollback.go +++ b/monitor/commands/rollback.go @@ -21,7 +21,19 @@ func NewRollbackCmd(m types.Monitor, invokeConfig controllerapi.InvokeConfig, st } func (cm *RollbackCmd) Info() types.CommandInfo { - return types.CommandInfo{HelpMessage: "re-runs the interactive container with initial rootfs contents"} + return types.CommandInfo{ + Name: "rollback", + HelpMessage: "re-runs the interactive container with the step's rootfs contents", + HelpMessageLong: ` +Usage: + rollback [FLAGS] [COMMAND] [ARG...] + +Flags: + --init Run the container with the initial rootfs of that step. + +COMMAND and ARG... will be executed in the container. +`, + } } func (cm *RollbackCmd) Exec(ctx context.Context, args []string) error { diff --git a/monitor/monitor.go b/monitor/monitor.go index 78666e0e..0763bfb6 100644 --- a/monitor/monitor.go +++ b/monitor/monitor.go @@ -85,18 +85,22 @@ func RunMonitor(ctx context.Context, curRef string, options *controllerapi.Build id := m.Rollback(ctx, invokeConfig) fmt.Fprintf(stdout, "Interactive container was restarted with process %q. Press Ctrl-a-c to switch to the new container\n", id) - registeredCommands := map[string]types.Command{ - "reload": commands.NewReloadCmd(m, stdout, progress, options, invokeConfig), - "rollback": commands.NewRollbackCmd(m, invokeConfig, stdout), - "list": commands.NewListCmd(m, stdout), - "disconnect": commands.NewDisconnectCmd(m), - "kill": commands.NewKillCmd(m), - "attach": commands.NewAttachCmd(m, stdout), - "exec": commands.NewExecCmd(m, invokeConfig, stdout), - "ps": commands.NewPsCmd(m, stdout), + availableCommands := []types.Command{ + commands.NewReloadCmd(m, stdout, progress, options, invokeConfig), + commands.NewRollbackCmd(m, invokeConfig, stdout), + commands.NewListCmd(m, stdout), + commands.NewDisconnectCmd(m), + commands.NewKillCmd(m), + commands.NewAttachCmd(m, stdout), + commands.NewExecCmd(m, invokeConfig, stdout), + commands.NewPsCmd(m, stdout), + } + registeredCommands := make(map[string]types.Command) + for _, c := range availableCommands { + registeredCommands[c.Info().Name] = c } additionalHelpMessages := map[string]string{ - "help": "shows this message", + "help": "shows this message. Optionally pass a command name as an argument to print the detailed usage.", "exit": "exits monitor", } @@ -141,6 +145,10 @@ func RunMonitor(ctx context.Context, curRef string, options *controllerapi.Build case "exit": return case "help": + if len(args) >= 2 { + printHelpMessageOfCommand(stdout, args[1], registeredCommands, additionalHelpMessages) + continue + } printHelpMessage(stdout, registeredCommands, additionalHelpMessages) continue default: @@ -171,6 +179,21 @@ func RunMonitor(ctx context.Context, curRef string, options *controllerapi.Build } } +func printHelpMessageOfCommand(out io.Writer, name string, registeredCommands map[string]types.Command, additional map[string]string) { + var target types.Command + if c, ok := registeredCommands[name]; ok { + target = c + } else { + fmt.Fprintf(out, "monitor: no help message for %q\n", name) + printHelpMessage(out, registeredCommands, additional) + return + } + fmt.Fprintln(out, target.Info().HelpMessage) + if h := target.Info().HelpMessageLong; h != "" { + fmt.Fprintln(out, h) + } +} + func printHelpMessage(out io.Writer, registeredCommands map[string]types.Command, additional map[string]string) { var names []string for name := range registeredCommands { diff --git a/monitor/types/types.go b/monitor/types/types.go index 9efbcc15..7c81e716 100644 --- a/monitor/types/types.go +++ b/monitor/types/types.go @@ -38,9 +38,14 @@ type Monitor interface { // CommandInfo is information about a command. type CommandInfo struct { + // Name is the name of the command. + Name string - // HelpMessage is the message printed to the console when "help" command is invoked. + // HelpMessage is one-line message printed to the console when "help" command is invoked. HelpMessage string + + // HelpMessageLong is a detailed message printed to the console when "help" command prints this command's information. + HelpMessageLong string } // Command represents a command for debugging.