Skip to content

Commit

Permalink
feat: add lefthook option for custom path or command (#927)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrexox authored Jan 14, 2025
1 parent ab93bf1 commit 9e02365
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 4 deletions.
1 change: 1 addition & 0 deletions docs/mdbook/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
- [`colors`](./configuration/colors.md)
- [`no_tty`](./configuration/no_tty.md)
- [`extends`](./configuration/extends.md)
- [`lefthook`](./configuration/lefthook.md)
- [`min_version`](./configuration/min_version.md)
- [`output`](./configuration/output.md)
- [`skip_output`](./configuration/skip_output.md)
Expand Down
1 change: 1 addition & 0 deletions docs/mdbook/configuration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Lefthook also merges an extra config with the name `lefthook-local`. All support
- [`colors`](./colors.md)
- [`no_tty`](./no_tty.md)
- [`extends`](./extends.md)
- [`lefthook`](./lefthook.md)
- [`min_version`](./min_version.md)
- [`output`](./output.md)
- [`skip_output`](./skip_output.md)
Expand Down
62 changes: 62 additions & 0 deletions docs/mdbook/configuration/lefthook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
## `lefthook`

**Default:** `null`

Provide a full path to lefthook executable or a command to run lefthook. Bourne shell (`sh`) syntax is supported.

> **Important:** This option does not merge from `remotes` or `extends` for security reasons. But it gets merged from lefthook local config if specified.
There are three reasons you may want to specify `lefthook`:

1. You want to force using specific lefthook version from your dependencies (e.g. npm package)
1. You use OnP loader for your JS/TS project, and your `package.json` with lefthook dependency locates in a subfolder
1. You want to make sure you use concrete lefthook executable path and want to defined it in `lefthook-local.yml`

### Examples

#### Specify lefthook executable

```yml
# lefthook.yml

lefthook: /usr/bin/lefthook

pre-commit:
jobs:
- run: yarn lint
```
#### Specify a command to run lefthook
```yml
# lefthook.yml

lefthook: |
cd project-with-lefthook
pnpm lefthook
pre-commit:
jobs:
- run: yarn lint
root: project-with-lefthook
```
#### Force using a version from Rubygems
```yml
# lefthook.yml

lefthook: bundle exec lefthook

pre-commit:
jobs:
- run: bundle exec rubocop {staged_files}
```
#### Enable debug logs
```yml
# lefthook-local.yml

lefthook: lefthook --verbose
```
2 changes: 2 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const (
type Config struct {
MinVersion string `json:"min_version,omitempty" jsonschema:"description=Specify a minimum version for the lefthook binary" koanf:"min_version" mapstructure:"min_version,omitempty"`

Lefthook string `json:"lefthook,omitempty" jsonschema:"description=Lefthook executable path or command" mapstructure:"lefthook,omitempty"`

SourceDir string `json:"source_dir,omitempty" jsonschema:"default=.lefthook/,description=Change a directory for script files. Directory for script files contains folders with git hook names which contain script files." koanf:"source_dir" mapstructure:"source_dir,omitempty"`

SourceDirLocal string `json:"source_dir_local,omitempty" jsonschema:"default=.lefthook-local/,description=Change a directory for local script files (not stored in VCS)" koanf:"source_dir_local" mapstructure:"source_dir_local,omitempty"`
Expand Down
3 changes: 3 additions & 0 deletions internal/config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ func LoadKoanf(filesystem afero.Fs, repo *git.Repository) (*koanf.Koanf, *koanf.
return nil, nil, err
}

// Don't allow to set `lefthook` field from a remote config
secondary.Delete("lefthook")

// Load optional local config (e.g. lefthook-local.yml)
var noLocal bool
if err := loadOne(secondary, filesystem, repo.RootPath, localConfigNames); err != nil {
Expand Down
1 change: 1 addition & 0 deletions internal/lefthook/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ func (l *Lefthook) createHooksIfNeeded(cfg *config.Config, checkHashSum, force b
Rc: cfg.Rc,
AssertLefthookInstalled: cfg.AssertLefthookInstalled,
Roots: roots,
LefthookExe: cfg.Lefthook,
}
if err = l.addHook(hook, templateArgs); err != nil {
return fmt.Errorf("could not add the hook: %w", err)
Expand Down
11 changes: 8 additions & 3 deletions internal/templates/hook.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,20 @@ call_lefthook()
if test -n "$LEFTHOOK_BIN"
then
"$LEFTHOOK_BIN" "$@"
{{ if .LefthookExe -}}
elif test -n "{{ .LefthookExe }}"
then
{{ .LefthookExe }} "$@"
{{ end -}}
elif lefthook{{.Extension}} -h >/dev/null 2>&1
then
lefthook{{.Extension}} "$@"
{{if .Extension -}}
{{ if .Extension -}}
{{/* Check if lefthook.bat exists. Ruby bundler creates such a wrapper */ -}}
elif lefthook.bat -h >/dev/null 2>&1
then
lefthook.bat "$@"
{{end -}}
{{ end -}}
else
dir="$(git rev-parse --show-toplevel)"
osArch=$(uname | tr '[:upper:]' '[:lower:]')
Expand Down Expand Up @@ -57,7 +62,7 @@ call_lefthook()
elif test -f "$dir/{{.}}/node_modules/lefthook/bin/index.js"
then
"$dir/{{.}}/node_modules/lefthook/bin/index.js" "$@"
{{end}}
{{ end }}
elif bundle exec lefthook -h >/dev/null 2>&1
then
bundle exec lefthook "$@"
Expand Down
4 changes: 4 additions & 0 deletions internal/templates/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"embed"
"fmt"
"runtime"
"strings"
"text/template"
)

Expand All @@ -15,13 +16,15 @@ var templatesFS embed.FS

type Args struct {
Rc string
LefthookExe string
AssertLefthookInstalled bool
Roots []string
}

type hookTmplData struct {
HookName string
Extension string
LefthookExe string
Rc string
Roots []string
AssertLefthookInstalled bool
Expand All @@ -36,6 +39,7 @@ func Hook(hookName string, args Args) []byte {
Rc: args.Rc,
AssertLefthookInstalled: args.AssertLefthookInstalled,
Roots: args.Roots,
LefthookExe: strings.ReplaceAll(strings.TrimSpace(args.LefthookExe), "\n", ";"),
})
if err != nil {
panic(err)
Expand Down
6 changes: 5 additions & 1 deletion schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -392,12 +392,16 @@
"type": "object"
}
},
"$comment": "Last updated on 2025.01.10.",
"$comment": "Last updated on 2025.01.14.",
"properties": {
"min_version": {
"type": "string",
"description": "Specify a minimum version for the lefthook binary"
},
"lefthook": {
"type": "string",
"description": "Lefthook executable path or command"
},
"source_dir": {
"type": "string",
"description": "Change a directory for script files. Directory for script files contains folders with git hook names which contain script files.",
Expand Down
26 changes: 26 additions & 0 deletions testdata/lefthook_option.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
exec git init
exec lefthook install
exec git config user.email "[email protected]"
exec git config user.name "Your Name"
exec git add -A
exec git commit -m 'must show debug logs'
stderr 'injected'
stdout '[lefthook]'

-- lefthook.yml --
lefthook: |
echo 'injected'
lefthook -v

output:
- execution
pre-commit:
jobs:
- run: echo {all_files}
glob: "*.txt"

-- file.txt --
sometext

-- file.js --
somecode

0 comments on commit 9e02365

Please sign in to comment.