package main import ( "os" "fmt" "os/exec" "syscall" "path/filepath" "log" "flag" "bytes" "strings" "code.google.com/p/go.crypto/ssh/terminal" ) func main() { var parallel = flag.Bool("p", false, "Run hooks in parallel") var trace = flag.Bool("x", false, "Trace mode") flag.Parse() if len(os.Getenv("PLUGINHOOK_TRACE")) > 0 { *trace = true } pluginPath := os.Getenv("PLUGIN_PATH") if pluginPath == "" { log.Fatal("[ERROR] Unable to locate plugins: set $PLUGIN_PATH\n") os.Exit(1) } if flag.NArg() < 1 { log.Fatal("[ERROR] Hook name argument is required\n") os.Exit(1) } cmds := make([]exec.Cmd, 0) var matches, _ = filepath.Glob(fmt.Sprintf("%s/*/%s", pluginPath, flag.Arg(0))) for _, hook := range matches { cmd := exec.Command(hook, flag.Args()[1:]...) cmds = append(cmds, *cmd) } for i := len(cmds)-1; i >= 0; i-- { cmds[i].Stderr = os.Stderr if i == len(cmds)-1 { cmds[i].Stdout = os.Stdout } if i > 0 { if *parallel { stdout, err := cmds[i-1].StdoutPipe() if err != nil { log.Fatal(err) } cmds[i].Stdin = stdout } else { var buf bytes.Buffer cmds[i-1].Stdout = &buf cmds[i].Stdin = &buf } } if i == 0 && !terminal.IsTerminal(syscall.Stdin) { cmds[i].Stdin = os.Stdin } } if *parallel { done := make(chan bool, len(cmds)) for i := 0; i < len(cmds); i++ { go func(cmd exec.Cmd, i int) { if *trace { fmt.Fprintln(os.Stderr, "+", strings.Join(cmds[i].Args, " ")) } err := cmd.Run() if msg, ok := err.(*exec.ExitError); ok { // there is error code os.Exit(msg.Sys().(syscall.WaitStatus).ExitStatus()) } done <- true }(cmds[i], i) } for i := 0; i < len(cmds); i++ { <-done } } else { for i := 0; i < len(cmds); i++ { if *trace { fmt.Fprintln(os.Stderr, "+", strings.Join(cmds[i].Args, " ")) } err := cmds[i].Run() if msg, ok := err.(*exec.ExitError); ok { // there is error code os.Exit(msg.Sys().(syscall.WaitStatus).ExitStatus()) } } } }