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

Add piped option #21

Merged
merged 1 commit into from
Jul 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 48 additions & 3 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os/exec"
"path/filepath"
"regexp"
"sort"
"strings"
"sync"
"time"
Expand All @@ -33,6 +34,7 @@ var (
failList []string
mutex sync.Mutex
envExcludeTags []string // store for LEFTHOOK_EXCLUDE=tag,tag
isPipeBroken bool
)

const (
Expand All @@ -53,6 +55,7 @@ const (
subStagedFiles string = "{staged_files}"
runnerWrapPattern string = "{cmd}"
tagsConfigKey string = "tags"
pipedConfigKey string = "piped"
excludeTagsConfigKey string = "exclude_tags"
execMode os.FileMode = 0751
)
Expand Down Expand Up @@ -96,6 +99,11 @@ func RunCmdExecutor(args []string, fs afero.Fs) error {
startTime := time.Now()
log.Println(au.Cyan("RUNNING HOOKS GROUP:"), au.Bold(hooksGroup))

if isPipedAndParallel(hooksGroup) {
log.Println(au.Brown("Config error! Conflicted options 'piped' and 'parallel'. Remove one of this option from hook group."))
return errors.New("Piped and Parallel options in conflict.")
}

sourcePath := filepath.Join(getSourceDir(), hooksGroup)
executables, err := afero.ReadDir(fs, sourcePath)
if err == nil && len(executables) > 0 {
Expand Down Expand Up @@ -125,7 +133,7 @@ func RunCmdExecutor(args []string, fs afero.Fs) error {
commands := getCommands(hooksGroup)
if len(commands) != 0 {

for commandName := range commands {
for _, commandName := range commands {
wg.Add(1)
if getParallel(hooksGroup) {
go executeCommand(hooksGroup, commandName, &wg)
Expand All @@ -148,6 +156,12 @@ func RunCmdExecutor(args []string, fs afero.Fs) error {
func executeCommand(hooksGroup, commandName string, wg *sync.WaitGroup) {
defer wg.Done()

if getPiped(hooksGroup) && isPipeBroken {
log.Println(au.Cyan("\n EXECUTE >"), au.Bold(commandName))
log.Println(au.Brown("(SKIP BY BROKEN PIPE)"))
return
}

files, _ := context.AllFiles()
runner := getRunner(hooksGroup, commandsConfigKey, commandName)

Expand Down Expand Up @@ -176,6 +190,7 @@ func executeCommand(hooksGroup, commandName string, wg *sync.WaitGroup) {
log.Println(au.Cyan("\n EXECUTE >"), au.Bold(commandName))
if err != nil {
failList = append(failList, commandName)
setPipeBroken()
log.Println(err)
return
}
Expand All @@ -199,13 +214,20 @@ func executeCommand(hooksGroup, commandName string, wg *sync.WaitGroup) {
okList = append(okList, commandName)
} else {
failList = append(failList, commandName)
setPipeBroken()
}
}

func executeScript(hooksGroup, source string, executable os.FileInfo, wg *sync.WaitGroup, gitArgs []string) {
defer wg.Done()
executableName := executable.Name()

if getPiped(hooksGroup) && isPipeBroken {
log.Println(au.Cyan("\n EXECUTE >"), au.Bold(executableName))
log.Println(au.Brown("(SKIP BY BROKEN PIPE)"))
return
}

pathToExecutable := filepath.Join(source, executableName)

if err := isExecutable(executable); err != nil {
Expand Down Expand Up @@ -240,6 +262,7 @@ func executeScript(hooksGroup, source string, executable os.FileInfo, wg *sync.W
failList = append(failList, executableName)
log.Println(err)
log.Println(au.Brown("TIP: Command start failed. Checkout `runner:` option for this script"))
setPipeBroken()
return
}
if isSkipScript(hooksGroup, executableName) {
Expand All @@ -257,6 +280,7 @@ func executeScript(hooksGroup, source string, executable os.FileInfo, wg *sync.W
okList = append(okList, executableName)
} else {
failList = append(failList, executableName)
setPipeBroken()
}
}

Expand Down Expand Up @@ -332,9 +356,17 @@ func isSkipEmptyCommmand(hooksGroup, executableName string) bool {
return true
}

func getCommands(hooksGroup string) map[string]interface{} {
func getCommands(hooksGroup string) []string {
key := strings.Join([]string{hooksGroup, commandsConfigKey}, ".")
return viper.GetStringMap(key)
commands := viper.GetStringMap(key)

keys := make([]string, 0, len(commands))
for k := range commands {
keys = append(keys, k)
}
sort.Strings(keys)

return keys
}

func getCommandIncludeRegexp(hooksGroup, executableName string) string {
Expand Down Expand Up @@ -398,6 +430,19 @@ func getParallel(hooksGroup string) bool {
return viper.GetBool(key)
}

func getPiped(hooksGroup string) bool {
key := strings.Join([]string{hooksGroup, pipedConfigKey}, ".")
return viper.GetBool(key)
}

func isPipedAndParallel(hooksGroup string) bool {
return getParallel(hooksGroup) && getPiped(hooksGroup)
}

func setPipeBroken() {
isPipeBroken = true
}

func FilterGlob(vs []string, matcher string) []string {
if matcher == "" {
return vs
Expand Down
73 changes: 60 additions & 13 deletions cmd/run_windows.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
// On windows we can`t use pty, so we cant sync output in parallel mode

package cmd

import (
"errors"
"fmt"
// "io" // win specific
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"sort"
"strings"
"sync"
"time"
Expand All @@ -16,6 +20,7 @@ import (

arrop "github.com/adam-hanna/arrayOperations"
"github.com/gobwas/glob"
// "github.com/kr/pty" //win specific
"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand All @@ -29,6 +34,7 @@ var (
failList []string
mutex sync.Mutex
envExcludeTags []string // store for LEFTHOOK_EXCLUDE=tag,tag
isPipeBroken bool
)

const (
Expand All @@ -49,6 +55,7 @@ const (
subStagedFiles string = "{staged_files}"
runnerWrapPattern string = "{cmd}"
tagsConfigKey string = "tags"
pipedConfigKey string = "piped"
excludeTagsConfigKey string = "exclude_tags"
execMode os.FileMode = 0751
)
Expand Down Expand Up @@ -92,6 +99,11 @@ func RunCmdExecutor(args []string, fs afero.Fs) error {
startTime := time.Now()
log.Println(au.Cyan("RUNNING HOOKS GROUP:"), au.Bold(hooksGroup))

if isPipedAndParallel(hooksGroup) {
log.Println(au.Brown("Config error! Conflicted options 'piped' and 'parallel'. Remove one of this option from hook group."))
return errors.New("Piped and Parallel options in conflict.")
}

sourcePath := filepath.Join(getSourceDir(), hooksGroup)
executables, err := afero.ReadDir(fs, sourcePath)
if err == nil && len(executables) > 0 {
Expand Down Expand Up @@ -121,7 +133,7 @@ func RunCmdExecutor(args []string, fs afero.Fs) error {
commands := getCommands(hooksGroup)
if len(commands) != 0 {

for commandName := range commands {
for _, commandName := range commands {
wg.Add(1)
if getParallel(hooksGroup) {
go executeCommand(hooksGroup, commandName, &wg)
Expand All @@ -144,6 +156,12 @@ func RunCmdExecutor(args []string, fs afero.Fs) error {
func executeCommand(hooksGroup, commandName string, wg *sync.WaitGroup) {
defer wg.Done()

if getPiped(hooksGroup) && isPipeBroken {
log.Println(au.Cyan("\n EXECUTE >"), au.Bold(commandName))
log.Println(au.Brown("(SKIP BY BROKEN PIPE)"))
return
}

files, _ := context.AllFiles()
runner := getRunner(hooksGroup, commandsConfigKey, commandName)

Expand All @@ -163,18 +181,18 @@ func executeCommand(hooksGroup, commandName string, wg *sync.WaitGroup) {

runnerArg := strings.Split(runner, " ")
command := exec.Command(runnerArg[0], runnerArg[1:]...)
command.Stdout = os.Stdout
command.Stderr = os.Stderr
command.Stdin = os.Stdin
command.Stdout = os.Stdout // win specific
command.Stderr = os.Stderr // win specific

err := command.Start()

err := command.Start() // ptyOut, err := pty.Start(command) // win specific
mutex.Lock()
defer mutex.Unlock()

log.Println(au.Cyan("\n EXECUTE >"), au.Bold(commandName))
if err != nil {
failList = append(failList, commandName)
setPipeBroken()
log.Println(err)
return
}
Expand All @@ -191,18 +209,25 @@ func executeCommand(hooksGroup, commandName string, wg *sync.WaitGroup) {
log.Println(au.Brown("(SKIP. NO FILES FOR INSPECTING)"))
return
}

// io.Copy(os.Stdout, ptyOut) // win specific
if command.Wait() == nil {
okList = append(okList, commandName)
} else {
failList = append(failList, commandName)
setPipeBroken()
}
}

func executeScript(hooksGroup, source string, executable os.FileInfo, wg *sync.WaitGroup, gitArgs []string) {
defer wg.Done()
executableName := executable.Name()

if getPiped(hooksGroup) && isPipeBroken {
log.Println(au.Cyan("\n EXECUTE >"), au.Bold(executableName))
log.Println(au.Brown("(SKIP BY BROKEN PIPE)"))
return
}

pathToExecutable := filepath.Join(source, executableName)

if err := isExecutable(executable); err != nil {
Expand All @@ -218,12 +243,11 @@ func executeScript(hooksGroup, source string, executable os.FileInfo, wg *sync.W

command = exec.Command(runnerArg[0], runnerArg[1:]...)
}
command.Stdout = os.Stdout
command.Stderr = os.Stderr
command.Stdin = os.Stdin
command.Stdout = os.Stdout // win specific
command.Stderr = os.Stderr // win specific

err := command.Start()

err := command.Start() // ptyOut, err := pty.Start(command) // win specific
mutex.Lock()
defer mutex.Unlock()

Expand All @@ -240,6 +264,7 @@ func executeScript(hooksGroup, source string, executable os.FileInfo, wg *sync.W
failList = append(failList, executableName)
log.Println(err)
log.Println(au.Brown("TIP: Command start failed. Checkout `runner:` option for this script"))
setPipeBroken()
return
}
if isSkipScript(hooksGroup, executableName) {
Expand All @@ -250,11 +275,12 @@ func executeScript(hooksGroup, source string, executable os.FileInfo, wg *sync.W
log.Println(au.Brown("(SKIP BY TAGS)"))
return
}

// io.Copy(os.Stdout, ptyOut) // win specific
if command.Wait() == nil {
okList = append(okList, executableName)
} else {
failList = append(failList, executableName)
setPipeBroken()
}
}

Expand Down Expand Up @@ -330,9 +356,17 @@ func isSkipEmptyCommmand(hooksGroup, executableName string) bool {
return true
}

func getCommands(hooksGroup string) map[string]interface{} {
func getCommands(hooksGroup string) []string {
key := strings.Join([]string{hooksGroup, commandsConfigKey}, ".")
return viper.GetStringMap(key)
commands := viper.GetStringMap(key)

keys := make([]string, 0, len(commands))
for k := range commands {
keys = append(keys, k)
}
sort.Strings(keys)

return keys
}

func getCommandIncludeRegexp(hooksGroup, executableName string) string {
Expand Down Expand Up @@ -396,6 +430,19 @@ func getParallel(hooksGroup string) bool {
return viper.GetBool(key)
}

func getPiped(hooksGroup string) bool {
key := strings.Join([]string{hooksGroup, pipedConfigKey}, ".")
return viper.GetBool(key)
}

func isPipedAndParallel(hooksGroup string) bool {
return getParallel(hooksGroup) && getPiped(hooksGroup)
}

func setPipeBroken() {
isPipeBroken = true
}

func FilterGlob(vs []string, matcher string) []string {
if matcher == "" {
return vs
Expand Down
14 changes: 14 additions & 0 deletions docs/full_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,20 @@ pre-push:
- frontend
```

## Piped option
If any command in the sequence fails, the other will not be executed.
```yml
database:
piped: true
commands:
1_create:
run: rake db:create
2_migrate:
run: rake db:migrate
3_seed:
run: rake db:seed
```

## Referencing commands from lefthook.yml

If you have the following config
Expand Down
2 changes: 1 addition & 1 deletion examples/complete/.lefthook-local/pre-commit/hello.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package main
import "fmt"

func main() {
fmt.Println("hello world")
fmt.Println("hello world")
}
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ github.com/spf13/afero v1.2.1 h1:qgMbHoJbPbw579P+1zVY+6n4nIFuIchaIjzZ/I/Yq8M=
github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.1 h1:5+8j8FTpnFV4nEImW/ofkzEt8VoOiLXxdYIDsB73T38=
github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
Expand Down