diff --git a/docs/configuration.md b/docs/configuration.md index b39e7df9..c715b555 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,19 +1,20 @@ # Configure lefthook.yml - [Top level options](#top-level-options) + - [`assert_lefthook_installed`](#assert_lefthook_installed) - [`colors`](#colors) - [`yellow`](#colors) - [`green`](#colors) - [`cyan`](#colors) - [`gray`](#colors) - [`red`](#colors) - - [`no_tty`](#no_tty) - [`extends`](#extends) - [`min_version`](#min_version) + - [`no_tty`](#no_tty) + - [`rc`](#rc) - [`skip_output`](#skip_output) - [`source_dir`](#source_dir) - [`source_dir_local`](#source_dir_local) - - [`rc`](#rc) - [`remote` (Beta :test_tube:)](#remote) - [`git_url`](#git_url) - [`ref`](#ref) @@ -59,6 +60,12 @@ These options are not related to git hooks, and they only control lefthook behavior. +### `assert_lefthook_installed` + +**Default: `false`** + +When set to `true`, fail (with exit status 1) if `lefthook` executable can't be found in $PATH, under node_modules/, as a Ruby gem, or other supported method. This makes sure git hook won't omit `lefthook` rules if `lefthook` ever was installed. + ### `colors` **Default: `true`** diff --git a/internal/config/config.go b/internal/config/config.go index 52fc5c8b..7aea9cf7 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -15,15 +15,16 @@ import ( const dumpIndent = 2 type Config struct { - MinVersion string `mapstructure:"min_version,omitempty"` - SourceDir string `mapstructure:"source_dir"` - SourceDirLocal string `mapstructure:"source_dir_local"` - Rc string `mapstructure:"rc,omitempty"` - SkipOutput []string `mapstructure:"skip_output,omitempty"` - Extends []string `mapstructure:"extends,omitempty"` - NoTTY bool `mapstructure:"no_tty,omitempty"` - Colors interface{} `mapstructure:"colors,omitempty"` - Remote *Remote `mapstructure:"remote,omitempty" ` + MinVersion string `mapstructure:"min_version,omitempty"` + SourceDir string `mapstructure:"source_dir"` + SourceDirLocal string `mapstructure:"source_dir_local"` + Rc string `mapstructure:"rc,omitempty"` + SkipOutput []string `mapstructure:"skip_output,omitempty"` + Extends []string `mapstructure:"extends,omitempty"` + NoTTY bool `mapstructure:"no_tty,omitempty"` + AssertLefthookInstalled bool `mapstructure:"assert_lefthook_installed,omitempty"` + Colors interface{} `mapstructure:"colors,omitempty"` + Remote *Remote `mapstructure:"remote,omitempty"` Hooks map[string]*Hook `mapstructure:"-"` } diff --git a/internal/lefthook/add.go b/internal/lefthook/add.go index adbe1d9e..d77e8464 100644 --- a/internal/lefthook/add.go +++ b/internal/lefthook/add.go @@ -39,7 +39,7 @@ func (l *Lefthook) Add(args *AddArgs) error { return err } - err = l.addHook(args.Hook, "") + err = l.addHook(args.Hook, "", false) if err != nil { return err } diff --git a/internal/lefthook/install.go b/internal/lefthook/install.go index c2a0896a..540e1b9c 100644 --- a/internal/lefthook/install.go +++ b/internal/lefthook/install.go @@ -135,12 +135,12 @@ func (l *Lefthook) createHooksIfNeeded(cfg *config.Config, force bool) error { return err } - if err = l.addHook(hook, cfg.Rc); err != nil { + if err = l.addHook(hook, cfg.Rc, cfg.AssertLefthookInstalled); err != nil { return err } } - if err = l.addHook(config.GhostHookName, cfg.Rc); err != nil { + if err = l.addHook(config.GhostHookName, cfg.Rc, cfg.AssertLefthookInstalled); err != nil { return nil } diff --git a/internal/lefthook/lefthook.go b/internal/lefthook/lefthook.go index 5adcb142..75f78342 100644 --- a/internal/lefthook/lefthook.go +++ b/internal/lefthook/lefthook.go @@ -121,9 +121,9 @@ func (l *Lefthook) cleanHook(hook string, force bool) error { } // Creates a hook file using hook template. -func (l *Lefthook) addHook(hook, rc string) error { +func (l *Lefthook) addHook(hook, rc string, assertLefthookInstalled bool) error { hookPath := filepath.Join(l.repo.HooksPath, hook) return afero.WriteFile( - l.Fs, hookPath, templates.Hook(hook, rc), hookFileMode, + l.Fs, hookPath, templates.Hook(hook, rc, assertLefthookInstalled), hookFileMode, ) } diff --git a/internal/templates/hook.tmpl b/internal/templates/hook.tmpl index 6432a74e..af750cd5 100644 --- a/internal/templates/hook.tmpl +++ b/internal/templates/hook.tmpl @@ -48,6 +48,12 @@ call_lefthook() npx @evilmartians/lefthook "$@" else echo "Can't find lefthook in PATH" + {{- if .AssertLefthookInstalled}} + echo "ERROR: Operation is aborted due to lefthook settings." + echo "Make sure lefthook is available in your environment and re-try." + echo "To skip these checks use --no-verify git argument or set LEFTHOOK=0 env variable." + exit 1 + {{- end}} fi } diff --git a/internal/templates/templates.go b/internal/templates/templates.go index 0a1112db..8b1da93e 100644 --- a/internal/templates/templates.go +++ b/internal/templates/templates.go @@ -14,18 +14,20 @@ const checksumFormat = "%s %d\n" var templatesFS embed.FS type hookTmplData struct { - HookName string - Extension string - Rc string + HookName string + Extension string + Rc string + AssertLefthookInstalled bool } -func Hook(hookName, rc string) []byte { +func Hook(hookName, rc string, assertLefthookInstalled bool) []byte { buf := &bytes.Buffer{} t := template.Must(template.ParseFS(templatesFS, "hook.tmpl")) err := t.Execute(buf, hookTmplData{ - HookName: hookName, - Extension: getExtension(), - Rc: rc, + HookName: hookName, + Extension: getExtension(), + Rc: rc, + AssertLefthookInstalled: assertLefthookInstalled, }) if err != nil { panic(err)