summaryrefslogtreecommitdiffstats
path: root/pkg/commands/oscommands
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2021-04-10 16:01:46 +1000
committerJesse Duffield <jessedduffield@gmail.com>2021-04-11 17:07:49 +1000
commitbb918b579a3073d786fda83a2bc11c602e442aa0 (patch)
treefe126f12a88db9a9e119d0b172c0902bc0f49244 /pkg/commands/oscommands
parente145090046e1693e2100cb0a0ab40a271705f4b0 (diff)
start adding support for logging of commands
Diffstat (limited to 'pkg/commands/oscommands')
-rw-r--r--pkg/commands/oscommands/exec_live_default.go1
-rw-r--r--pkg/commands/oscommands/os.go128
2 files changed, 103 insertions, 26 deletions
diff --git a/pkg/commands/oscommands/exec_live_default.go b/pkg/commands/oscommands/exec_live_default.go
index 6f51ecedc..124792840 100644
--- a/pkg/commands/oscommands/exec_live_default.go
+++ b/pkg/commands/oscommands/exec_live_default.go
@@ -20,6 +20,7 @@ import (
// NOTE: If the return data is empty it won't written anything to stdin
func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error {
c.Log.WithField("command", command).Info("RunCommand")
+ c.LogCommand(command, true)
cmd := c.ExecutableFromString(command)
cmd.Env = append(cmd.Env, "LANG=en_US.UTF-8", "LC_ALL=en_US.UTF-8")
diff --git a/pkg/commands/oscommands/os.go b/pkg/commands/oscommands/os.go
index 5b9b4dd91..2c85cfc5a 100644
--- a/pkg/commands/oscommands/os.go
+++ b/pkg/commands/oscommands/os.go
@@ -40,7 +40,44 @@ type OSCommand struct {
Command func(string, ...string) *exec.Cmd
BeforeExecuteCmd func(*exec.Cmd)
Getenv func(string) string
- onRunCommand func(string)
+
+ // callback to run before running a command, i.e. for the purposes of logging
+ onRunCommand func(CmdLogEntry)
+
+ // something like 'Staging File': allows us to group cmd logs under a single title
+ CmdLogSpan string
+
+ removeFile func(string) error
+}
+
+// TODO: make these fields private
+type CmdLogEntry struct {
+ // e.g. 'git commit -m "haha"'
+ cmdStr string
+ // Span is something like 'Staging File'. Multiple commands can be grouped under the same
+ // span
+ span string
+
+ // sometimes our command is direct like 'git commit', and sometimes it's a
+ // command to remove a file but through Go's standard library rather than the
+ // command line
+ commandLine bool
+}
+
+func (e CmdLogEntry) GetCmdStr() string {
+ return e.cmdStr
+}
+
+func (e CmdLogEntry) GetSpan() string {
+ return e.span
+}
+
+func (e CmdLogEntry) GetCommandLine() bool {
+ return e.commandLine
+}
+
+func NewCmdLogEntry(cmdStr string, span string, commandLine bool) CmdLogEntry {
+ return CmdLogEntry{cmdStr: cmdStr, span: span, commandLine: commandLine}
}
// NewOSCommand os command runner
@@ -52,10 +89,30 @@ func NewOSCommand(log *logrus.Entry, config config.AppConfigurer) *OSCommand {
Command: secureexec.Command,
BeforeExecuteCmd: func(*exec.Cmd) {},
Getenv: os.Getenv,
+ removeFile: os.RemoveAll,
+ }
+}
+
+func (c *OSCommand) WithSpan(span string) *OSCommand {
+ newOSCommand := &OSCommand{}
+ *newOSCommand = *c
+ newOSCommand.CmdLogSpan = span
+ return newOSCommand
+}
+
+func (c *OSCommand) LogExecCmd(cmd *exec.Cmd) {
+ c.LogCommand(strings.Join(cmd.Args, " "), true)
+}
+
+func (c *OSCommand) LogCommand(cmdStr string, commandLine bool) {
+ c.Log.WithField("command", cmdStr).Info("RunCommand")
+
+ if c.onRunCommand != nil && c.CmdLogSpan != "" {
+ c.onRunCommand(NewCmdLogEntry(cmdStr, c.CmdLogSpan, commandLine))
}
}
-func (c *OSCommand) SetOnRunCommand(f func(string)) {
+func (c *OSCommand) SetOnRunCommand(f func(CmdLogEntry)) {
c.onRunCommand = f
}
@@ -65,6 +122,11 @@ func (c *OSCommand) SetCommand(cmd func(string, ...string) *exec.Cmd) {
c.Command = cmd
}
+// To be used for testing only
+func (c *OSCommand) SetRemoveFile(f func(string) error) {
+ c.removeFile = f
+}
+
func (c *OSCommand) SetBeforeExecuteCmd(cmd func(*exec.Cmd)) {
c.BeforeExecuteCmd = cmd
}
@@ -74,10 +136,7 @@ type RunCommandOptions struct {
}
func (c *OSCommand) RunCommandWithOutputWithOptions(command string, options RunCommandOptions) (string, error) {
- c.Log.WithField("command", command).Info("RunCommand")
- if c.onRunCommand != nil {
- c.onRunCommand(command)
- }
+ c.LogCommand(command, true)
cmd := c.ExecutableFromString(command)
cmd.Env = append(cmd.Env, "GIT_TERMINAL_PROMPT=0") // prevents git from prompting us for input which would freeze the program
@@ -102,11 +161,8 @@ func (c *OSCommand) RunCommandWithOutput(formatString string, formatArgs ...inte
if formatArgs != nil {
command = fmt.Sprintf(formatString, formatArgs...)
}
- c.Log.WithField("command", command).Info("RunCommand")
- if c.onRunCommand != nil {
- c.onRunCommand(command)
- }
cmd := c.ExecutableFromString(command)
+ c.LogExecCmd(cmd)
output, err := sanitisedCommandOutput(cmd.CombinedOutput())
if err != nil {
c.Log.WithField("command", command).Error(output)
@@ -114,20 +170,9 @@ func (c *OSCommand) RunCommandWithOutput(formatString string, formatArgs ...inte
return output, err
}
-func (c *OSCommand) CatFile(filename string) (string, error) {
- arr := append(c.Platform.CatCmd, filename)
- cmdStr := strings.Join(arr, " ")
- c.Log.WithField("command", cmdStr).Info("Cat")
- cmd := c.Command(arr[0], arr[1:]...)
- output, err := sanitisedCommandOutput(cmd.CombinedOutput())
- if err != nil {
- c.Log.WithField("command", cmdStr).Error(output)
- }
- return output, err
-}
-
// RunExecutableWithOutput runs an executable file and returns its output
func (c *OSCommand) RunExecutableWithOutput(cmd *exec.Cmd) (string, error) {
+ c.LogExecCmd(cmd)
c.BeforeExecuteCmd(cmd)
return sanitisedCommandOutput(cmd.CombinedOutput())
}
@@ -165,6 +210,18 @@ func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string)
return RunCommandWithOutputLiveWrapper(c, command, output)
}
+func (c *OSCommand) CatFile(filename string) (string, error) {
+ arr := append(c.Platform.CatCmd, filename)
+ cmdStr := strings.Join(arr, " ")
+ c.Log.WithField("command", cmdStr).Info("Cat")
+ cmd := c.Command(arr[0], arr[1:]...)
+ output, err := sanitisedCommandOutput(cmd.CombinedOutput())
+ if err != nil {
+ c.Log.WithField("command", cmdStr).Error(output)
+ }
+ return output, err
+}
+
// DetectUnamePass detect a username / password / passphrase question in a command
// promptUserForCredential is a function that gets executed when this function detect you need to fillin a password or passphrase
// The promptUserForCredential argument will be "username", "password" or "passphrase" and expects the user's password/passphrase or username back
@@ -201,9 +258,9 @@ func (c *OSCommand) RunCommand(formatString string, formatArgs ...interface{}) e
// RunShellCommand runs shell commands i.e. 'sh -c <command>'. Good for when you
// need access to the shell
func (c *OSCommand) RunShellCommand(command string) error {
- c.Log.WithField("command", command).Info("RunShellCommand")
-
cmd := c.Command(c.Platform.Shell, c.Platform.ShellArg, command)
+ c.LogExecCmd(cmd)
+
_, err := sanitisedCommandOutput(cmd.CombinedOutput())
return err
@@ -236,6 +293,7 @@ func sanitisedCommandOutput(output []byte, err error) (string, error) {
// OpenFile opens a file with the given
func (c *OSCommand) OpenFile(filename string) error {
+ c.LogCommand(fmt.Sprintf("Opening file '%s'", filename), false)
commandTemplate := c.Config.GetUserConfig().OS.OpenCommand
templateValues := map[string]string{
"filename": c.Quote(filename),
@@ -248,6 +306,7 @@ func (c *OSCommand) OpenFile(filename string) error {
// OpenLink opens a file with the given
func (c *OSCommand) OpenLink(link string) error {
+ c.LogCommand(fmt.Sprintf("Opening link '%s'", link), false)
commandTemplate := c.Config.GetUserConfig().OS.OpenLinkCommand
templateValues := map[string]string{
"link": c.Quote(link),
@@ -265,6 +324,7 @@ func (c *OSCommand) PrepareSubProcess(cmdName string, commandArgs ...string) *ex
if cmd != nil {
cmd.Env = append(os.Environ(), "GIT_OPTIONAL_LOCKS=0")
}
+ c.LogExecCmd(cmd)
return cmd
}
@@ -290,6 +350,7 @@ func (c *OSCommand) Quote(message string) string {
// AppendLineToFile adds a new line in file
func (c *OSCommand) AppendLineToFile(filename, line string) error {
+ c.LogCommand(fmt.Sprintf("Appending '%s' to file '%s'", line, filename), false)
f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return utils.WrapError(err)
@@ -310,6 +371,7 @@ func (c *OSCommand) CreateTempFile(filename, content string) (string, error) {
c.Log.Error(err)
return "", utils.WrapError(err)
}
+ c.LogCommand(fmt.Sprintf("Creating temp file '%s'", tmpfile.Name()), false)
if _, err := tmpfile.WriteString(content); err != nil {
c.Log.Error(err)
@@ -325,6 +387,7 @@ func (c *OSCommand) CreateTempFile(filename, content string) (string, error) {
// CreateFileWithContent creates a file with the given content
func (c *OSCommand) CreateFileWithContent(path string, content string) error {
+ c.LogCommand(fmt.Sprintf("Creating file '%s'", path), false)
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
c.Log.Error(err)
return err
@@ -340,6 +403,7 @@ func (c *OSCommand) CreateFileWithContent(path string, content string) error {
// Remove removes a file or directory at the specified path
func (c *OSCommand) Remove(filename string) error {
+ c.LogCommand(fmt.Sprintf("Removing '%s'", filename), false)
err := os.RemoveAll(filename)
return utils.WrapError(err)
}
@@ -360,6 +424,7 @@ func (c *OSCommand) FileExists(path string) (bool, error) {
// before running it
func (c *OSCommand) RunPreparedCommand(cmd *exec.Cmd) error {
c.BeforeExecuteCmd(cmd)
+ c.LogExecCmd(cmd)
out, err := cmd.CombinedOutput()
outString := string(out)
c.Log.Info(outString)
@@ -383,12 +448,16 @@ func (c *OSCommand) GetLazygitPath() string {
// PipeCommands runs a heap of commands and pipes their inputs/outputs together like A | B | C
func (c *OSCommand) PipeCommands(commandStrings ...string) error {
-
cmds := make([]*exec.Cmd, len(commandStrings))
-
+ logCmdStr := ""
for i, str := range commandStrings {
+ if i > 0 {
+ logCmdStr += " | "
+ }
+ logCmdStr += str
cmds[i] = c.ExecutableFromString(str)
}
+ c.LogCommand(logCmdStr, true)
for i := 0; i < len(cmds)-1; i++ {
stdout, err := cmds[i].StdoutPipe()
@@ -479,5 +548,12 @@ func RunLineOutputCmd(cmd *exec.Cmd, onLine func(line string) (bool, error)) err
}
func (c *OSCommand) CopyToClipboard(str string) error {
+ c.LogCommand(fmt.Sprintf("Copying '%s' to clipboard", utils.TruncateWithEllipsis(str, 40)), false)
return clipboard.WriteAll(str)
}
+
+func (c *OSCommand) RemoveFile(path string) error {
+ c.LogCommand(fmt.Sprintf("Deleting path '%s'", path), false)
+
+ return c.removeFile(path)
+}