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

Display executed commands via Verbose Logging #111

Merged
merged 8 commits into from
Nov 10, 2017
Merged
8 changes: 4 additions & 4 deletions commands/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ func (cmd *Dashboard) LaunchDashboard(machine Machine) error {
util.ForceStreamCommand(exec.Command("docker", args...))

if util.IsMac() {
exec.Command("open", "http://dashboard.outrigger.vm").Run()
util.Command("open", "http://dashboard.outrigger.vm").Run()
} else if util.IsWindows() {
exec.Command("start", "http://dashboard.outrigger.vm").Run()
util.Command("start", "http://dashboard.outrigger.vm").Run()
} else {
cmd.out.Info.Println("Outrigger Dashboard is now available at http://dashboard.outrigger.vm")
}
Expand All @@ -86,6 +86,6 @@ func (cmd *Dashboard) LaunchDashboard(machine Machine) error {

// StopDashboard stops and removes the dashboard container
func (cmd *Dashboard) StopDashboard() {
exec.Command("docker", "stop", dashboardContainerName).Run()
exec.Command("docker", "rm", dashboardContainerName).Run()
util.Command("docker", "stop", dashboardContainerName).Run()
util.Command("docker", "rm", dashboardContainerName).Run()
}
71 changes: 56 additions & 15 deletions util/shell_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,33 @@ package util

import (
"io/ioutil"
"fmt"
"os"
"os/exec"
"strings"
"syscall"

"github.com/fatih/color"
)

const defaultFailedCode = 1

type Executor struct {
cmd *exec.Cmd
}

// StreamCommand sets up the output streams (and colors) to stream command output if verbose is configured
func StreamCommand(cmd *exec.Cmd) error {
return RunCommand(cmd, false)
return Executor{cmd}.Execute(false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You know, we could make this and ForceStreamCommand even easier to read on the caller side if we just take the command and args here instead of the exec.Cmd and call exec.Command just like the Executor.Execute

}

// ForceStreamCommand sets up the output streams (and colors) to stream command output regardless of verbosity
func ForceStreamCommand(cmd *exec.Cmd) error {
return RunCommand(cmd, true)
return Executor{cmd}.Execute(true)
}

// RunCommand executes the provided command, it also can sspecify if the output should be forced to print to the console
func RunCommand(cmd *exec.Cmd, forceOutput bool) error {
cmd.Stderr = os.Stderr
if Logger().IsVerbose || forceOutput {
cmd.Stdout = os.Stdout
} else {
cmd.Stdout = ioutil.Discard
}

color.Set(color.FgCyan)
err := cmd.Run()
color.Unset()
return err
func Command(path string, arg ...string) Executor {
return Executor{exec.Command(path, arg...)}
}

// PassthruCommand is similar to ForceStreamCommand in that it will issue all output
Expand Down Expand Up @@ -69,3 +64,49 @@ func PassthruCommand(cmd *exec.Cmd) (exitCode int) {

return
}

// RunCommand executes the provided command, it also can sspecify if the output should be forced to print to the console
func (x Executor) Execute(forceOutput bool) error {
x.cmd.Stderr = os.Stderr
if Logger().IsVerbose || forceOutput {
x.cmd.Stdout = os.Stdout
} else {
x.cmd.Stdout = ioutil.Discard
}

x.Log("Executing")
color.Set(color.FgCyan)
err := x.Run()
color.Unset()
return err
}

// Run runs a command via exec.Run() without modification or output of the underlying command.
func (x Executor) Run() error {
return x.cmd.Run()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to log all commands that are run? Or only commands that force/stream?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably all commands, but I wanted to lay out the initial API for it more flexibly so the refactoring can go either way.

}

// Log verbosely logs the command.
func (x Executor) Log(tag string) {
color.Set(color.FgYellow)
Logger().Verbose.Printf("%s: %s", tag, x.ToString())
color.Unset()
}

// CmdToString converts a Command to a human-readable string with key context details.
func (x Executor) ToString() string {
context := ""
if x.cmd.Dir != "" {
context = fmt.Sprintf("(WD: %s", x.cmd.Dir)
}
if x.cmd.Env != nil {
env := strings.Join(x.cmd.Env, " ")
if context == "" {
context = fmt.Sprintf("(Env: %s", env)
} else {
context = fmt.Sprintf("%s, Env: %s)", context, env)
}
}

return fmt.Sprintf("%s %s %s", x.cmd.Path, strings.Join(x.cmd.Args[1:], " "), context)
}