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

feat: add jobs option #861

Merged
merged 12 commits into from
Dec 19, 2024
7 changes: 7 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ linters-settings:
rules:
- name: unused-parameter
disabled: true
unused:
field-writes-are-uses: false
post-statements-are-reads: true
exported-fields-are-used: false
parameters-are-used: true
local-variables-are-used: false
generated-is-used: false

issues:
exclude:
Expand Down
22 changes: 22 additions & 0 deletions docs/mdbook/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,28 @@
- [`exclude_tags`](./configuration/exclude_tags.md)
- [`skip`](./configuration/skip.md)
- [`only`](./configuration/only.md)
- [`jobs`](./configuration/jobs.md)
- [`name`](./configuration/name.md)
- [`run`](./configuration/run.md)
- [`script`](./configuration/script.md)
- [`runner`](./configuration/runner.md)
- [`group`](./configuration/group.md)
- [`parallel`](./configuration/parallel.md)
- [`piped`](./configuration/piped.md)
- [`jobs`](./configuration/jobs.md)
- [`skip`](./configuration/skip.md)
- [`only`](./configuration/only.md)
- [`tags`](./configuration/tags.md)
- [`glob`](./configuration/glob.md)
- [`files`](./configuration/files.md)
- [`file_types`](./configuration/file_types.md)
- [`env`](./configuration/env.md)
- [`root`](./configuration/root.md)
- [`exclude`](./configuration/exclude.md)
- [`fail_text`](./configuration/fail_text.md)
- [`stage_fixed`](./configuration/stage_fixed.md)
- [`interactive`](./configuration/interactive.md)
- [`use_stdin`](./configuration/use_stdin.md)
- [`commands`](./configuration/Commands.md)
- [`run`](./configuration/run.md)
- [`skip`](./configuration/skip.md)
Expand Down
28 changes: 14 additions & 14 deletions docs/mdbook/configuration/Scripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,20 @@ To add a script for a `pre-commit` hook:
runner: bash
```

**Example**
### Script options

- [`runner`](./runner.md)
- [`skip`](./skip.md)
- [`only`](./only.md)
- [`tags`](./tags.md)
- [`env`](./env.md)
- [`fail_text`](./fail_text.md)
- [`stage_fixed`](./stage_fixed.md)
- [`interactive`](./interactive.md)
- [`use_stdin`](./use_stdin.md)
- [`priority`](./priority.md)

### Example

Let's create a bash script to check commit templates `.lefthook/commit-msg/template_checker`:

Expand All @@ -43,16 +56,3 @@ commit-msg:
```

When you try to commit `git commit -m "bad commit text"` script `template_checker` will be executed. Since commit text doesn't match the described pattern the commit process will be interrupted.

### Script options

- [`runner`](./runner.md)
- [`skip`](./skip.md)
- [`only`](./only.md)
- [`tags`](./tags.md)
- [`env`](./env.md)
- [`fail_text`](./fail_text.md)
- [`stage_fixed`](./stage_fixed.md)
- [`interactive`](./interactive.md)
- [`use_stdin`](./use_stdin.md)
- [`priority`](./priority.md)
30 changes: 30 additions & 0 deletions docs/mdbook/configuration/group.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
## `group`

Specifies a group of jobs and option to run them with.

- [`parallel`](./parallel.md)
- [`piped`](./piped.md)
- [`jobs`](./jobs.md)

### Example

```yml
# lefthook.yml

pre-commit:
jobs:
- group:
parallel: true
jobs:
- run: echo hello from a group
```

> **Note:** To make a group mergeable with settings defined in local config or extends you have to specify the name of the job group belongs to:
> ```yml
> pre-commit:
> jobs:
> - name: a name of a group
> group:
> jobs:
> - run: echo from a group job
> ```
72 changes: 72 additions & 0 deletions docs/mdbook/configuration/jobs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
## `jobs`

Job can either be a command or a script. Configuring `jobs` is more flexible than configuring `commands` and `scripts`, although all options are supported now.

```yml
# lefthook.yml

pre-commit:
jobs:
- run: yarn lint
- run: yarn test
```

This is how jobs configuration differ from commands and scripts:

- Jobs have optional names. Lefthook merges named jobs across [extends](./extends.md) and [local configs](../examples/lefthook-local.md). Unnamed jobs get appended in the definition order.
- Jobs can have groups of other jobs. For groups you can specify [`parallel`](./parallel.md) or [`piped`](./piped.md) flow for a bunch of jobs. Also [`glob`](./glob.md) and [`root`](./root.md) parameters of a group apply to all its jobs (even nested).

### Job options

- [`name`](./name.md)
- [`run`](./run.md)
- [`script`](./script.md)
- [`runner`](./runner.md)
- [`group`](./group.md)
- [`parallel`](./parallel.md)
- [`piped`](./piped.md)
- [`jobs`](./jobs.md)
- [`skip`](./skip.md)
- [`only`](./only.md)
- [`tags`](./tags.md)
- [`glob`](./glob.md)
- [`files`](./files.md)
- [`file_types`](./file_types.md)
- [`env`](./env.md)
- [`root`](./root.md)
- [`exclude`](./exclude.md)
- [`fail_text`](./fail_text.md)
- [`stage_fixed`](./stage_fixed.md)
- [`interactive`](./interactive.md)
- [`use_stdin`](./use_stdin.md)

### Example

> **Note:** Currently only `root` and `glob` options are applied to group jobs. Other options must be set for each job separately. If you find this inconvenient, please submit a [feature request](https://github.com/evilmartians/lefthook/issues/new?assignees=&labels=feature+request&projects=&template=feature_request.md).

A simple configuration with one piped group which executes in parallel with other jobs.

```yml
# lefthook.yml

pre-commit:
parallel: true
jobs:
- name: migrate
root: backend/
glob: "db/migrations/*"
group:
piped: true
jobs:
- run: bundle install
- run: rails db:migrate
- run: yarn lint --fix {staged_files}
root: frontend/
stage_fixed: true
- run: bundle exec rubocop
root: backend/
- run: golangci-lint
root: proxy/
- script: verify.sh
runner: bash
```
14 changes: 14 additions & 0 deletions docs/mdbook/configuration/name.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## `name`

Name of a job. Will be printed in summary. If specified, the jobs can be merged with a jobs of the same name in a [local config](../examples/lefthook-local.md) or [extends](./extends.md).

### Example

```yml
# lefthook.yml

pre-commit:
jobs:
- name: lint and fix
run: yarn run eslint --fix {staged_files}
```
20 changes: 20 additions & 0 deletions docs/mdbook/configuration/script.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## `script`

Name of a script to execute. The rules are the same as for [`scripts`](./Scripts.md)

### Example

```yml
# lefthook.yml

pre-commit:
jobs:
- script: linter.sh
runner: bash
```

```bash
# .lefthook/pre-commit/linter.sh

echo "Everything is OK"
```
30 changes: 3 additions & 27 deletions docs/mdbook/examples/lefthook-local.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,9 @@

pre-commit:
commands:
lint-frontend:
run: yarn lint
glob: ".{ts,tsx}"
lint-backend:
lint:
run: bundle exec rubocop {staged_files}
glob: "*.rb"
test-frontend:
run: yarn test
glob: "*.tsx"
test-backend:
run: bundle exec rspec
glob: "spec/*"
check-typos:
run: typos {staged_files}
check-links:
run: lychee {staged_files}
```
Expand All @@ -35,10 +24,8 @@ pre-commit:
pre-commit:
parallel: true # run all commands concurrently
commands:
lint-backend:
lint:
run: docker-compose run backend {cmd} # wrap the original command with docker-compose
test-backend:
run: docker-compose run backend {cmd}
check-links:
skip: true # skip checking links

Expand All @@ -59,23 +46,12 @@ post-merge:
pre-commit:
parallel: true
commands:
lint-frontend:
run: yarn lint
glob: "*.{ts,tsx}"
lint-backend:
lint:
run: docker-compose run backend bundle exec rubocop {staged_files}
glob: "*.rb"
test-frontend:
run: yarn test
glob: "*.tsx"
test-backend:
run: docker-compose run backend bundle exec rspec
glob: "spec/*"
check-links:
run: lychee {staged_files}
skip: true
check-typos:
run: typos {staged_files}

post-merge:
files: "git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD"
Expand Down
18 changes: 1 addition & 17 deletions internal/config/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@ package config

import (
"errors"

"github.com/evilmartians/lefthook/internal/git"
"github.com/evilmartians/lefthook/internal/system"
)

var errFilesIncompatible = errors.New("One of your runners contains incompatible file types")
var ErrFilesIncompatible = errors.New("One of your runners contains incompatible file types")

type Command struct {
Run string `json:"run" mapstructure:"run" toml:"run" yaml:"run"`
Expand All @@ -31,19 +28,6 @@ type Command struct {
StageFixed bool `json:"stage_fixed,omitempty" koanf:"stage_fixed" mapstructure:"stage_fixed" toml:"stage_fixed,omitempty" yaml:"stage_fixed,omitempty"`
}

func (c Command) Validate() error {
if !isRunnerFilesCompatible(c.Run) {
return errFilesIncompatible
}

return nil
}

func (c Command) DoSkip(state func() git.State) bool {
skipChecker := NewSkipChecker(system.Cmd)
return skipChecker.check(state, c.Skip, c.Only)
}

func (c Command) ExecutionPriority() int {
return c.Priority
}
6 changes: 0 additions & 6 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import (
"github.com/mitchellh/mapstructure"
toml "github.com/pelletier/go-toml/v2"
"gopkg.in/yaml.v3"

"github.com/evilmartians/lefthook/internal/version"
)

type DumpFormat int
Expand Down Expand Up @@ -46,10 +44,6 @@ type Config struct {
Hooks map[string]*Hook `mapstructure:"-"`
}

func (c *Config) Validate() error {
return version.CheckCovered(c.MinVersion)
}

func (c *Config) Md5() (checksum string, err error) {
configBytes := new(bytes.Buffer)

Expand Down
7 changes: 2 additions & 5 deletions internal/config/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ const (
SubPushFiles string = "{push_files}"
)

func isRunnerFilesCompatible(runner string) bool {
if strings.Contains(runner, SubStagedFiles) && strings.Contains(runner, SubPushFiles) {
return false
}
return true
func IsRunFilesCompatible(run string) bool {
return !(strings.Contains(run, SubStagedFiles) && strings.Contains(run, SubPushFiles))
}
20 changes: 5 additions & 15 deletions internal/config/hook.go
Original file line number Diff line number Diff line change
@@ -1,38 +1,28 @@
package config

import (
"errors"

"github.com/evilmartians/lefthook/internal/git"
"github.com/evilmartians/lefthook/internal/system"
)

const CMD = "{cmd}"

var errPipedAndParallelSet = errors.New("conflicting options 'piped' and 'parallel' are set to 'true', remove one of this option from hook group")

type Hook struct {
Commands map[string]*Command `json:"commands,omitempty" mapstructure:"-" toml:"commands,omitempty" yaml:",omitempty"`
Scripts map[string]*Script `json:"scripts,omitempty" mapstructure:"-" toml:"scripts,omitempty" yaml:",omitempty"`

Files string `json:"files,omitempty" mapstructure:"files" toml:"files,omitempty" yaml:",omitempty"`
Parallel bool `json:"parallel,omitempty" mapstructure:"parallel" toml:"parallel,omitempty" yaml:",omitempty"`
Piped bool `json:"piped,omitempty" mapstructure:"piped" toml:"piped,omitempty" yaml:",omitempty"`
Follow bool `json:"follow,omitempty" mapstructure:"follow" toml:"follow,omitempty" yaml:",omitempty"`
Files string `json:"files,omitempty" mapstructure:"files" toml:"files,omitempty" yaml:",omitempty"`
ExcludeTags []string `json:"exclude_tags,omitempty" koanf:"exclude_tags" mapstructure:"exclude_tags" toml:"exclude_tags,omitempty" yaml:"exclude_tags,omitempty"`
Skip interface{} `json:"skip,omitempty" mapstructure:"skip" toml:"skip,omitempty,inline" yaml:",omitempty"`
Only interface{} `json:"only,omitempty" mapstructure:"only" toml:"only,omitempty,inline" yaml:",omitempty"`
}

func (h *Hook) Validate() error {
if h.Parallel && h.Piped {
return errPipedAndParallelSet
}
Jobs []*Job `json:"jobs,omitempty" mapstructure:"jobs" toml:"jobs,omitempty" yaml:",omitempty"`

return nil
Commands map[string]*Command `json:"commands,omitempty" mapstructure:"-" toml:"commands,omitempty" yaml:",omitempty"`
Scripts map[string]*Script `json:"scripts,omitempty" mapstructure:"-" toml:"scripts,omitempty" yaml:",omitempty"`
}

func (h *Hook) DoSkip(state func() git.State) bool {
skipChecker := NewSkipChecker(system.Cmd)
return skipChecker.check(state, h.Skip, h.Only)
return skipChecker.Check(state, h.Skip, h.Only)
}
Loading
Loading