Skip to content

Commit

Permalink
feat: add only option (#478)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrexox authored May 11, 2023
1 parent 173b204 commit 1bd921f
Show file tree
Hide file tree
Showing 11 changed files with 310 additions and 38 deletions.
46 changes: 46 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- [`config`](#config)
- [Hook](#git-hook)
- [`skip`](#skip)
- [`only`](#only)
- [`files`](#files-global)
- [`parallel`](#parallel)
- [`piped`](#piped)
Expand All @@ -25,6 +26,7 @@
- [Command](#command)
- [`run`](#run)
- [`skip`](#skip)
- [`only`](#only)
- [`tags`](#tags)
- [`glob`](#glob)
- [`files`](#files)
Expand All @@ -37,6 +39,7 @@
- [Script](#script)
- [`runner`](#runner)
- [`skip`](#skip)
- [`only`](#only)
- [`tags`](#tags)
- [`env`](#env)
- [`fail_text`](#fail_text)
Expand Down Expand Up @@ -718,6 +721,49 @@ pre-commit:
skip: true
```

### `only`

You can force a command, script, or the whole hook to execute only in certain conditions. This option acts like the opposite of [`skip`](#skip). It accepts the same values but skips execution only if the condition is not satisfied.

**Note**

`skip` option takes precedence over `only` option, so if you have conflicting conditions the execution will be skipped.

**Example**

Execute a hook only for `dev/*` branches.

```yml
# lefthook.yml
pre-commit:
only:
- ref: dev/*
commands:
lint:
run: yarn lint
test:
run: yarn test
```

When rebasing execute quick linter but skip usual linter and tests.

```yml
# lefthook.yml
pre-commit:
commands:
lint:
skip: rebase
run: yarn lint
test:
skip: rebase
run: yarn test
lint-on-rebase:
only: rebase
run: yarn lint-quickly
```

### `tags`

You can specify tags for commands and scripts. This is useful for [excluding](#exclude_tags). You can specify more than one tag using comma or space.
Expand Down
6 changes: 2 additions & 4 deletions internal/config/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type Command struct {
Run string `mapstructure:"run"`

Skip interface{} `mapstructure:"skip"`
Only interface{} `mapstructure:"only"`
Tags []string `mapstructure:"tags"`
Glob string `mapstructure:"glob"`
Files string `mapstructure:"files"`
Expand All @@ -37,10 +38,7 @@ func (c Command) Validate() error {
}

func (c Command) DoSkip(gitState git.State) bool {
if value := c.Skip; value != nil {
return isSkip(gitState, value)
}
return false
return doSkip(gitState, c.Skip, c.Only)
}

type commandRunReplace struct {
Expand Down
6 changes: 2 additions & 4 deletions internal/config/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type Hook struct {
Piped bool `mapstructure:"piped"`
ExcludeTags []string `mapstructure:"exclude_tags"`
Skip interface{} `mapstructure:"skip"`
Only interface{} `mapstructure:"only"`
Follow bool `mapstructure:"follow"`
}

Expand All @@ -42,10 +43,7 @@ func (h *Hook) Validate() error {
}

func (h *Hook) DoSkip(gitState git.State) bool {
if value := h.Skip; value != nil {
return isSkip(gitState, value)
}
return false
return doSkip(gitState, h.Skip, h.Only)
}

func unmarshalHooks(base, extra *viper.Viper) (*Hook, error) {
Expand Down
18 changes: 11 additions & 7 deletions internal/config/load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ pre-commit:
run: docker exec -it ruby:2.7 {cmd}
scripts:
"format.sh":
skip: true
only: true
pre-push:
commands:
Expand Down Expand Up @@ -128,7 +128,7 @@ pre-push:
},
Scripts: map[string]*Script{
"format.sh": {
Skip: true,
Only: true,
Runner: "bash",
},
},
Expand Down Expand Up @@ -265,7 +265,7 @@ remote:
config: examples/custom.yml
pre-commit:
skip:
only:
- ref: main
commands:
global:
Expand All @@ -277,11 +277,14 @@ pre-commit:
pre-commit:
commands:
lint:
run: yarn lint
skip:
only:
- merge
- rebase
run: yarn lint
scripts:
"test.sh":
skip:
- merge
runner: bash
`,
remoteConfigPath: filepath.Join(root, ".git", "info", "lefthook-remotes", "lefthook", "examples", "custom.yml"),
Expand All @@ -296,11 +299,11 @@ pre-commit:
},
Hooks: map[string]*Hook{
"pre-commit": {
Skip: []interface{}{map[string]interface{}{"ref": "main"}},
Only: []interface{}{map[string]interface{}{"ref": "main"}},
Commands: map[string]*Command{
"lint": {
Run: "yarn lint",
Skip: []interface{}{"merge"},
Only: []interface{}{"merge", "rebase"},
},
"global": {
Run: "echo 'Global!'",
Expand All @@ -309,6 +312,7 @@ pre-commit:
Scripts: map[string]*Script{
"test.sh": {
Runner: "bash",
Skip: []interface{}{"merge"},
},
},
},
Expand Down
6 changes: 2 additions & 4 deletions internal/config/script.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type Script struct {
Runner string `mapstructure:"runner"`

Skip interface{} `mapstructure:"skip"`
Only interface{} `mapstructure:"only"`
Tags []string `mapstructure:"tags"`
Env map[string]string `mapstructure:"env"`

Expand All @@ -22,10 +23,7 @@ type Script struct {
}

func (s Script) DoSkip(gitState git.State) bool {
if value := s.Skip; value != nil {
return isSkip(gitState, value)
}
return false
return doSkip(gitState, s.Skip, s.Only)
}

type scriptRunnerReplace struct {
Expand Down
16 changes: 15 additions & 1 deletion internal/config/skip.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,21 @@ import (
"github.com/evilmartians/lefthook/internal/git"
)

func isSkip(gitState git.State, value interface{}) bool {
func doSkip(gitState git.State, skip, only interface{}) bool {
if skip != nil {
if matches(gitState, skip) {
return true
}
}

if only != nil {
return !matches(gitState, only)
}

return false
}

func matches(gitState git.State, value interface{}) bool {
switch typedValue := value.(type) {
case bool:
return typedValue
Expand Down
68 changes: 53 additions & 15 deletions internal/config/skip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,76 +6,114 @@ import (
"github.com/evilmartians/lefthook/internal/git"
)

func TestIsSkip(t *testing.T) {
func TestDoSkip(t *testing.T) {
for _, tt := range [...]struct {
name string
skip interface{}
state git.State
skipped bool
name string
state git.State
skip, only interface{}
skipped bool
}{
{
name: "when true",
skip: true,
state: git.State{},
skip: true,
skipped: true,
},
{
name: "when false",
skip: false,
state: git.State{},
skip: false,
skipped: false,
},
{
name: "when merge",
skip: "merge",
state: git.State{Step: "merge"},
skip: "merge",
skipped: true,
},
{
name: "when rebase (but want merge)",
skip: "merge",
state: git.State{Step: "rebase"},
skip: "merge",
skipped: false,
},
{
name: "when rebase",
skip: []interface{}{"rebase"},
state: git.State{Step: "rebase"},
skip: []interface{}{"rebase"},
skipped: true,
},
{
name: "when rebase (but want merge)",
skip: []interface{}{"merge"},
state: git.State{Step: "rebase"},
skip: []interface{}{"merge"},
skipped: false,
},
{
name: "when branch",
skip: []interface{}{map[string]interface{}{"ref": "feat/skipme"}},
state: git.State{Branch: "feat/skipme"},
skip: []interface{}{map[string]interface{}{"ref": "feat/skipme"}},
skipped: true,
},
{
name: "when branch doesn't match",
skip: []interface{}{map[string]interface{}{"ref": "feat/skipme"}},
state: git.State{Branch: "feat/important"},
skip: []interface{}{map[string]interface{}{"ref": "feat/skipme"}},
skipped: false,
},
{
name: "when branch glob",
skip: []interface{}{map[string]interface{}{"ref": "feat/*"}},
state: git.State{Branch: "feat/important"},
skip: []interface{}{map[string]interface{}{"ref": "feat/*"}},
skipped: true,
},
{
name: "when branch glob doesn't match",
state: git.State{Branch: "feat"},
skip: []interface{}{map[string]interface{}{"ref": "feat/*"}},
skipped: false,
},
{
name: "when only specified",
state: git.State{Branch: "feat"},
only: []interface{}{map[string]interface{}{"ref": "feat"}},
skipped: false,
},
{
name: "when only branch doesn't match",
state: git.State{Branch: "dev"},
only: []interface{}{map[string]interface{}{"ref": "feat"}},
skipped: true,
},
{
name: "when only branch with glob",
state: git.State{Branch: "feat/important"},
only: []interface{}{map[string]interface{}{"ref": "feat/*"}},
skipped: false,
},
{
name: "when only merge",
state: git.State{Step: "merge"},
only: []interface{}{"merge"},
skipped: false,
},
{
name: "when only and skip",
state: git.State{Step: "rebase"},
skip: []interface{}{map[string]interface{}{"ref": "feat/*"}},
only: "rebase",
skipped: false,
},
{
name: "when only and skip applies skip",
state: git.State{Step: "rebase"},
skip: []interface{}{"rebase"},
only: "rebase",
skipped: true,
},
} {
t.Run(tt.name, func(t *testing.T) {
if isSkip(tt.state, tt.skip) != tt.skipped {
if doSkip(tt.state, tt.skip, tt.only) != tt.skipped {
t.Errorf("Expected: %v, Was %v", tt.skipped, !tt.skipped)
}
})
Expand Down
2 changes: 1 addition & 1 deletion internal/lefthook/runner/prepare_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type commandArgs struct {
}

func (r *Runner) prepareCommand(name string, command *config.Command) (*commandArgs, error) {
if command.Skip != nil && command.DoSkip(r.Repo.State()) {
if command.DoSkip(r.Repo.State()) {
return nil, errors.New("settings")
}

Expand Down
2 changes: 1 addition & 1 deletion internal/lefthook/runner/prepare_script.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

func (r *Runner) prepareScript(script *config.Script, path string, file os.FileInfo) ([]string, error) {
if script.Skip != nil && script.DoSkip(r.Repo.State()) {
if script.DoSkip(r.Repo.State()) {
return nil, errors.New("settings")
}

Expand Down
2 changes: 1 addition & 1 deletion internal/lefthook/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (r *Runner) RunAll(sourceDirs []string) {
log.Error(err)
}

if r.Hook.Skip != nil && r.Hook.DoSkip(r.Repo.State()) {
if r.Hook.DoSkip(r.Repo.State()) {
r.logSkip(r.HookName, "hook setting")
return
}
Expand Down
Loading

0 comments on commit 1bd921f

Please sign in to comment.