Skip to content

Commit

Permalink
feat: allow providing a list of globs (#937)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrexox authored Jan 21, 2025
1 parent 8cc927f commit f692bb5
Show file tree
Hide file tree
Showing 14 changed files with 112 additions and 51 deletions.
25 changes: 19 additions & 6 deletions docs/mdbook/configuration/glob.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,25 @@ You can set a glob to filter files for your command. This is only used if you us
# lefthook.yml

pre-commit:
commands:
lint:
glob: "*.{js,ts,jsx,tsx}"
jobs:
- name: lint
run: yarn eslint {staged_files}
glob: "*.{js,ts,jsx,tsx}"
```
> **Note:** from lefthook version `1.10.10` you can also provide a list of globs:
>
> ```yml
> # lefthook.yml
>
> pre-commit:
> jobs:
> - run: yarn lint {staged_files}
> glob:
> - "*.ts"
> - "*.js"
> ```

**Notes**

For patterns that you can use see [this](https://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm) reference. We use [glob](https://github.com/gobwas/glob) library.
Expand All @@ -24,8 +37,8 @@ If you've specified `glob` but don't have a files template in [`run`](./run.md)
# lefthook.yml
pre-commit:
commands:
lint:
glob: "*.js"
jobs:
- name: lint
run: npm run lint # skipped if no .js files staged
glob: "*.js"
```
2 changes: 1 addition & 1 deletion internal/config/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type Command struct {

FileTypes []string `json:"file_types,omitempty" koanf:"file_types" mapstructure:"file_types" toml:"file_types,omitempty" yaml:"file_types,omitempty"`

Glob string `json:"glob,omitempty" mapstructure:"glob" toml:"glob,omitempty" yaml:",omitempty"`
Glob []string `json:"glob,omitempty" jsonschema:"oneof_type=string;array" mapstructure:"glob" toml:"glob,omitempty" yaml:",omitempty"`
Root string `json:"root,omitempty" mapstructure:"root" toml:"root,omitempty" yaml:",omitempty"`
Exclude interface{} `json:"exclude,omitempty" jsonschema:"oneof_type=string;array" mapstructure:"exclude" toml:"exclude,omitempty" yaml:",omitempty"`

Expand Down
8 changes: 4 additions & 4 deletions internal/config/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ type Job struct {
Script string `json:"script,omitempty" jsonschema:"oneof_required=Run a script" mapstructure:"script" toml:"script,omitempty" yaml:",omitempty"`
Runner string `json:"runner,omitempty" mapstructure:"runner" toml:"runner,omitempty" yaml:",omitempty"`

Glob string `json:"glob,omitempty" mapstructure:"glob" toml:"glob,omitempty" yaml:",omitempty"`
Root string `json:"root,omitempty" mapstructure:"root" toml:"root,omitempty" yaml:",omitempty"`
Files string `json:"files,omitempty" mapstructure:"files" toml:"files,omitempty" yaml:",omitempty"`
FailText string `json:"fail_text,omitempty" koanf:"fail_text" mapstructure:"fail_text" toml:"fail_text,omitempty" yaml:"fail_text,omitempty"`
Glob []string `json:"glob,omitempty" jsonschema:"oneof_type=string;array" mapstructure:"glob" toml:"glob,omitempty" yaml:",omitempty"`
Root string `json:"root,omitempty" mapstructure:"root" toml:"root,omitempty" yaml:",omitempty"`
Files string `json:"files,omitempty" mapstructure:"files" toml:"files,omitempty" yaml:",omitempty"`
FailText string `json:"fail_text,omitempty" koanf:"fail_text" mapstructure:"fail_text" toml:"fail_text,omitempty" yaml:"fail_text,omitempty"`

Tags []string `json:"tags,omitempty" mapstructure:"tags" toml:"tags,omitempty" yaml:",omitempty"`
FileTypes []string `json:"file_types,omitempty" koanf:"file_types" mapstructure:"file_types" toml:"file_types,omitempty" yaml:"file_types,omitempty"`
Expand Down
6 changes: 3 additions & 3 deletions internal/config/load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ pre-push:
Tags: []string{"backend", "test"},
},
"lint": {
Glob: "*.rb",
Glob: []string{"*.rb"},
Run: "docker exec -it ruby:2.7 bundle exec rubocop",
Tags: []string{"backend", "linter"},
},
Expand Down Expand Up @@ -649,7 +649,7 @@ pre-commit:
"global-lint": {
Run: "bundle exec rubocop",
Tags: []string{"backend", "linter"},
Glob: "*.rb",
Glob: []string{"*.rb"},
},
"global-other": {
Run: "bundle exec rubocop",
Expand Down Expand Up @@ -783,7 +783,7 @@ pre-commit:
Jobs: []*Job{
{
Name: "group 1",
Glob: "*.rb",
Glob: []string{"*.rb"},
Group: &Group{
Parallel: true,
Jobs: []*Job{
Expand Down
30 changes: 22 additions & 8 deletions internal/lefthook/runner/filters/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const (
)

type Params struct {
Glob string
Glob []string
Root string
FileTypes []string
Exclude interface{}
Expand All @@ -52,19 +52,33 @@ func Apply(fs afero.Fs, files []string, params Params) []string {
return files
}

func byGlob(vs []string, matcher string) []string {
if matcher == "" {
func byGlob(vs []string, matchers []string) []string {
if len(matchers) == 0 {
return vs
}

g := glob.MustCompile(strings.ToLower(matcher))

var hasNonEmpty bool
vsf := make([]string, 0)
for _, v := range vs {
if res := g.Match(strings.ToLower(v)); res {
vsf = append(vsf, v)
for _, matcher := range matchers {
if len(matcher) == 0 {
continue
}

hasNonEmpty = true

g := glob.MustCompile(strings.ToLower(matcher))

for _, v := range vs {
if res := g.Match(strings.ToLower(v)); res {
vsf = append(vsf, v)
}
}
}

if !hasNonEmpty {
return vs
}

return vsf
}

Expand Down
19 changes: 12 additions & 7 deletions internal/lefthook/runner/filters/filters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,31 +28,31 @@ func slicesEqual(a, b []string) bool {
func TestByGlob(t *testing.T) {
for i, tt := range [...]struct {
source, result []string
glob string
glob []string
}{
{
source: []string{"folder/subfolder/0.rb", "1.txt", "2.RB", "3.rbs"},
glob: "",
glob: []string{},
result: []string{"folder/subfolder/0.rb", "1.txt", "2.RB", "3.rbs"},
},
{
source: []string{"folder/subfolder/0.rb", "1.txt", "2.RB", "3.rbs"},
glob: "*.rb",
glob: []string{"*.rb"},
result: []string{"folder/subfolder/0.rb", "2.RB"},
},
{
source: []string{"folder/subfolder/0.rb", "1.rbs"},
glob: "**/*.rb",
glob: []string{"**/*.rb"},
result: []string{"folder/subfolder/0.rb"},
},
{
source: []string{"folder/0.rb", "1.rBs", "2.rbv"},
glob: "*.rb?",
glob: []string{"*.rb?"},
result: []string{"1.rBs", "2.rbv"},
},
{
source: []string{"f.a", "f.b", "f.c", "f.cn"},
glob: "*.{a,b,cn}",
glob: []string{"*.{a,b,cn}"},
result: []string{"f.a", "f.b", "f.cn"},
},
} {
Expand All @@ -68,7 +68,7 @@ func TestByGlob(t *testing.T) {
func TestByExclude(t *testing.T) {
for i, tt := range [...]struct {
source, result []string
exclude string
exclude interface{}
}{
{
source: []string{"folder/subfolder/0.rb", "1.txt", "2.RB", "3.rb"},
Expand All @@ -95,6 +95,11 @@ func TestByExclude(t *testing.T) {
exclude: ".*\\.(a|b|cn)$",
result: []string{"f.c"},
},
{
source: []string{"f.a", "f.b", "f.c", "f.cn"},
exclude: []interface{}{"*.a", "*.b", "*.cn"},
result: []string{"f.c"},
},
} {
t.Run(fmt.Sprintf("%d:", i), func(t *testing.T) {
res := byExclude(tt.source, tt.exclude)
Expand Down
2 changes: 1 addition & 1 deletion internal/lefthook/runner/jobs/jobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ type Params struct {
Root string
Runner string
Script string
Glob string
Files string
FileTypes []string
Tags []string
Glob []string
Templates map[string]string
Exclude interface{}
Only interface{}
Expand Down
6 changes: 3 additions & 3 deletions internal/lefthook/runner/run_jobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ var (
type domain struct {
failed *atomic.Bool

glob string
glob []string
root string
exclude interface{}
onlyJobs []string
Expand Down Expand Up @@ -95,7 +95,7 @@ func (r *Runner) runJob(ctx context.Context, domain *domain, id string, job *con

if job.Group != nil {
inheritedDomain := *domain
inheritedDomain.glob = first(job.Glob, domain.glob)
inheritedDomain.glob = slices.Concat(inheritedDomain.glob, job.Glob)
inheritedDomain.root = first(job.Root, domain.root)
switch list := job.Exclude.(type) {
case []interface{}:
Expand Down Expand Up @@ -131,7 +131,7 @@ func (r *Runner) runSingleJob(ctx context.Context, domain *domain, id string, jo
name := job.PrintableName(id)

root := first(job.Root, domain.root)
glob := first(job.Glob, domain.glob)
glob := slices.Concat(domain.glob, job.Glob)
exclude := join(job.Exclude, domain.exclude)
executionJob, err := jobs.New(name, &jobs.Params{
Repo: r.Repo,
Expand Down
12 changes: 6 additions & 6 deletions internal/lefthook/runner/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -618,12 +618,12 @@ func TestRunAll(t *testing.T) {
"ok": {
Run: "success",
StageFixed: true,
Glob: "*.md",
Glob: []string{"*.md"},
},
"fail": {
Run: "fail",
StageFixed: true,
Glob: "*.txt",
Glob: []string{"*.txt"},
},
},
},
Expand All @@ -648,12 +648,12 @@ func TestRunAll(t *testing.T) {
"ok": {
Run: "success",
StageFixed: true,
Glob: "*.md",
Glob: []string{"*.md"},
},
"fail": {
Run: "fail",
StageFixed: true,
Glob: "*.sh",
Glob: []string{"*.sh"},
},
},
},
Expand Down Expand Up @@ -703,12 +703,12 @@ func TestRunAll(t *testing.T) {
"ok": {
Run: "success",
StageFixed: true,
Glob: "*.md",
Glob: []string{"*.md"},
},
"fail": {
Run: "fail",
StageFixed: true,
Glob: "*.sh",
Glob: []string{"*.sh"},
},
},
},
Expand Down
26 changes: 23 additions & 3 deletions schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,17 @@
"type": "array"
},
"glob": {
"type": "string"
"oneOf": [
{
"type": "string"
},
{
"type": "array"
}
],
"items": {
"type": "string"
}
},
"root": {
"type": "string"
Expand Down Expand Up @@ -214,7 +224,17 @@
"type": "string"
},
"glob": {
"type": "string"
"oneOf": [
{
"type": "string"
},
{
"type": "array"
}
],
"items": {
"type": "string"
}
},
"root": {
"type": "string"
Expand Down Expand Up @@ -392,7 +412,7 @@
"type": "object"
}
},
"$comment": "Last updated on 2025.01.16.",
"$comment": "Last updated on 2025.01.21.",
"properties": {
"min_version": {
"type": "string",
Expand Down
9 changes: 6 additions & 3 deletions testdata/dump.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ pre-commit:
test:
run: yarn test
skip: merge
glob: '*.js'
glob:
- '*.js'
scripts:
my-script.sh:
runner: bash
Expand Down Expand Up @@ -95,7 +96,9 @@ pre-commit:
"test": {
"run": "yarn test",
"skip": "merge",
"glob": "*.js"
"glob": [
"*.js"
]
}
},
"scripts": {
Expand Down Expand Up @@ -130,7 +133,7 @@ interactive = true
[pre-commit.commands.test]
run = 'yarn test'
skip = 'merge'
glob = '*.js'
glob = ['*.js']

[pre-commit.scripts]
[pre-commit.scripts.'my-script.sh']
Expand Down
6 changes: 4 additions & 2 deletions testdata/job_merging.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ extends:
pre-commit:
jobs:
- name: group
glob: '*.rb'
glob:
- '*.rb'
group:
jobs:
- name: child
Expand All @@ -115,7 +116,8 @@ pre-commit:
- run: 3 no-name
- name: echo
run: echo 2
glob: "3"
glob:
- "3"
tags:
- backend
skip: true
Expand Down
3 changes: 2 additions & 1 deletion testdata/many_extends_levels.txt
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,5 @@ pre-commit:
skip: true
tags:
- backend
glob: "3"
glob:
- "3"
Loading

0 comments on commit f692bb5

Please sign in to comment.