Documentation ¶
Overview ¶
Package cmdsync has logic for synchronizing multiple shell comands. Commands can depend on the completion or readiness of other commands where readiness can be determined by the output matching some regular expression.
Index ¶
- type Group
- type ShellCmd
- type ShellCmdOption
- func CmdDir(dir string) ShellCmdOption
- func Color(c color.Color) ShellCmdOption
- func DependsOn(cmdNames ...string) ShellCmdOption
- func Environment(envMap map[string]string) ShellCmdOption
- func Name(name string) ShellCmdOption
- func ReadyPattern(pattern string) ShellCmdOption
- func SilenceOutput() ShellCmdOption
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Group ¶
type Group struct {
// contains filtered or unexported fields
}
Group manages scheduling concurrent ShellCmds
func NewGroup ¶
NewGroup makes a new Group it can be optionally initialized with commands or they can be added later via AddCommands
func (*Group) AddCommands ¶
AddCommands will add ShellCmds to the commands slice It will return an error if called after Group.Run()
func (*Group) Run ¶
Run will run all of the group's ShellCmds and block until they have all finished running or an interrupt signal is sent (ctrl + c). Internally it relays the first interrupt signal to all underlying ShellCmds. Additional interrupt commands will return to normal behavior.
It checks for each ShellCmd's prerequisites before starting. See ShellCmd for details on ready regexp.
The returned error is the first error returned from any of the Group's ShellCmds, if any.
Example ¶
package main import ( "log" "github.com/alexchao26/oneterminal/cmdsync" ) func main() { cmd1, _ := cmdsync.NewShellCmd("bash", "echo logging into vault && sleep 0.5 && echo logged in", cmdsync.Name("setup"), // a regexp pattern that must match the command's outputs for it to be deemed "ready", and // for its dependents to start executing cmdsync.ReadyPattern("logged in"), ) cmd2, _ := cmdsync.NewShellCmd("bash", "echo starting some api...", cmdsync.Name("second"), // will not start until the "setup" command is "ready" cmdsync.DependsOn("setup"), ) cmd3, _ := cmdsync.NewShellCmd("bash", "echo sweep sweep", cmdsync.Name("cleanup"), // will happen last cmdsync.DependsOn("second"), ) group := cmdsync.NewGroup(cmd1, cmd2, cmd3) err := group.Run() if err != nil { log.Fatal(err) } }
Output: setup | logging into vault setup | logged in second | starting some api... cleanup | sweep sweep
func (*Group) RunContext ¶ added in v0.4.0
RunContext is the same as Run but does not setup singal notifying internally. This means callers can only interrupt the Group's ShellCmds by cancelling the context.
To cancel the context via an interrupt signal from the terminal (ctrl + c), use signal.NotifyContext.
ctx, done := signal.NotifyContext(context.Background(), os.Interrupt) // ensure done() is called to restore normal SIGINT behavior go func() { <- ctx.Done() done() }() err := group.Run(ctx) // handle error
Example (Notify) ¶
package main import ( "context" "log" "os" "os/signal" "github.com/alexchao26/oneterminal/cmdsync" ) func main() { ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) // RunContext might hang even when interrupted, so reset the signal behavior // in a goroutine to ensure subsequent interrupt signals behave "normally". go func() { <-ctx.Done() stop() }() cmd1, _ := cmdsync.NewShellCmd("bash", "echo potatoes", cmdsync.Name("first")) cmd2, _ := cmdsync.NewShellCmd("bash", "echo are", cmdsync.Name("second"), cmdsync.DependsOn("first")) cmd3, _ := cmdsync.NewShellCmd("bash", "echo great", cmdsync.Name("third"), cmdsync.DependsOn("second")) group := cmdsync.NewGroup(cmd1, cmd2, cmd3) err := group.RunContext(ctx) if err != nil { log.Fatal(err) } }
Output: first | potatoes second | are third | great
func (*Group) SendInterrupts ¶
func (g *Group) SendInterrupts()
SendInterrupts relays an interrupt signal to all underlying commands
type ShellCmd ¶ added in v0.4.0
type ShellCmd struct {
// contains filtered or unexported fields
}
ShellCmd is a wrapper around exec.Cmd that eases syncing to other ShellCmd's via Group.
Its implementation calls the shell directly (through zsh/bash)
ShellCmd can indicate that the underlying process has reached a "ready state" by
- Its stdout/stderr outputs matching a given regexp.
- Its underlying process completing/exiting with a non-zero code.
An interrupt signal can be sent to the underlying process via Interrupt().
func NewShellCmd ¶ added in v0.4.0
func NewShellCmd(shell, command string, options ...ShellCmdOption) (*ShellCmd, error)
NewShellCmd defaults to using zsh. bash and sh are also supported
func (*ShellCmd) IsReady ¶ added in v0.4.0
IsReady is a simple getter for the ready state of a monitored command
func (*ShellCmd) Run ¶ added in v0.4.0
Run the underlying command. This function blocks until the command exits
Example (Options) ¶
package main import ( "log" "github.com/alexchao26/oneterminal/cmdsync" ) func main() { cmd, err := cmdsync.NewShellCmd("bash", "echo potato && echo loves $FAV_FOOD", cmdsync.Name("monkey"), cmdsync.Environment(map[string]string{ "FAV_FOOD": "cheeseburgers", }), ) if err != nil { log.Fatal(err) } err = cmd.Run() if err != nil { log.Fatal(err) } }
Output: monkey | potato monkey | loves cheeseburgers
Example (Simple) ¶
package main import ( "log" "github.com/alexchao26/oneterminal/cmdsync" ) func main() { cmd, err := cmdsync.NewShellCmd("bash", "echo hello potato") if err != nil { log.Fatal(err) } err = cmd.Run() if err != nil { log.Fatal(err) } }
Output: hello potato
func (*ShellCmd) RunContext ¶ added in v0.4.0
RunContext is the same as Run but cancels if the ctx cancels
func (*ShellCmd) Write ¶ added in v0.4.0
Write implements io.Writer, so that ShellCmd itself can be used for exec.ShellCmd.Stdout and Stderr Write "intercepts" writes to Stdout/Stderr to check if the outputs match a regexp and determines if a command has reached its "ready state" the ready state is used by Orchestrator to coordinate dependent commands
type ShellCmdOption ¶ added in v0.4.0
func CmdDir ¶
func CmdDir(dir string) ShellCmdOption
CmdDir is a functional option that modifies the Dir property of the underlying exec.ShellCmd which is the directory to execute the Command from
func Color ¶ added in v0.4.0
func Color(c color.Color) ShellCmdOption
Color is a functional option that sets the ansiColor for the outputs
func DependsOn ¶
func DependsOn(cmdNames ...string) ShellCmdOption
DependsOn is a functional option that sets a slice of dependencies for this command. The dependencies are names of commands that need to have completed or reached a ready state prior to this command starting.
Note that there is no validation that the cmdNames are valid/match other ShellCmd's configs (because it would cause a circular dependency). Some, but not all possible config errors are checked at runtime.
func Environment ¶
func Environment(envMap map[string]string) ShellCmdOption
Environment is a functional option that adds export commands to the start of a command. This is a bit of a hacky workaround to maintain exec.ShellCmd's default environment, while being able to set additional variables
func Name ¶ added in v0.4.0
func Name(name string) ShellCmdOption
Name is a functional option that sets a monitored command's name, which is used to prefix each line written to Stdout
func ReadyPattern ¶
func ReadyPattern(pattern string) ShellCmdOption
ReadyPattern is a functional option that takes in a pattern string that must compile into a valid regexp and sets it to monitored command's readyPattern field
func SilenceOutput ¶
func SilenceOutput() ShellCmdOption
SilenceOutput sets the command's Stdout and Stderr to nil so no output will be seen in the terminal