From 7032ec06267fcc5323ab7ad1fc84131120ec11cb Mon Sep 17 00:00:00 2001 From: Valentin Kiselev Date: Thu, 20 Jun 2024 10:55:52 +0300 Subject: [PATCH] fix: apply local extends only if they are present --- internal/config/load.go | 26 +++++++++++----- internal/config/load_test.go | 58 ++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/internal/config/load.go b/internal/config/load.go index 7c1f060d..9ce9f2e0 100644 --- a/internal/config/load.go +++ b/internal/config/load.go @@ -5,6 +5,7 @@ import ( "fmt" "path/filepath" "regexp" + "slices" "strings" "github.com/spf13/afero" @@ -20,7 +21,10 @@ const ( DefaultSourceDirLocal = ".lefthook-local" ) -var hookKeyRegexp = regexp.MustCompile(`^(?P[^.]+)\.(scripts|commands)`) +var ( + hookKeyRegexp = regexp.MustCompile(`^(?P[^.]+)\.(scripts|commands)`) + localConfigNames = []string{"lefthook-local", ".lefthook-local"} +) // NotFoundError wraps viper.ConfigFileNotFoundError for lefthook. type NotFoundError struct { @@ -109,13 +113,21 @@ func mergeAll(fs afero.Fs, repo *git.Repository) (*viper.Viper, error) { return nil, err } + // Save global extends to compare them after merging local config + globalExtends := extends.GetStringSlice("extends") + if err := mergeRemotes(fs, repo, extends); err != nil { return nil, err } - if err := mergeOne([]string{"lefthook-local", ".lefthook-local"}, "", extends); err == nil { - if err = extend(extends, repo.RootPath); err != nil { - return nil, err + //nolint:nestif + if err := mergeLocal(extends); err == nil { + // Local extends need to be re-applied only if they have different settings + localExtends := extends.GetStringSlice("extends") + if !slices.Equal(globalExtends, localExtends) { + if err = extend(extends, repo.RootPath); err != nil { + return nil, err + } } } else { var notFoundErr viper.ConfigFileNotFoundError @@ -216,9 +228,9 @@ func merge(name, path string, v *viper.Viper) error { return v.MergeInConfig() } -func mergeOne(names []string, path string, v *viper.Viper) error { - for _, name := range names { - err := merge(name, path, v) +func mergeLocal(v *viper.Viper) error { + for _, name := range localConfigNames { + err := merge(name, "", v) if err == nil { break } diff --git a/internal/config/load_test.go b/internal/config/load_test.go index 76f0aa43..af54394e 100644 --- a/internal/config/load_test.go +++ b/internal/config/load_test.go @@ -612,6 +612,64 @@ pre-push: }, }, }, + { + name: "with extends and local", + global: ` +extends: + - global-extend.yml +pre-commit: + parallel: true + exclude_tags: [linter] + commands: + global-lint: + run: bundle exec rubocop + glob: "*.rb" + tags: [backend, linter] + global-other: + run: bundle exec rubocop + tags: [other] +`, + local: ` +pre-commit: + exclude_tags: [backend] +`, + otherFiles: map[string]string{ + "global-extend.yml": ` +pre-commit: + exclude_tags: [test] + commands: + extended-tests: + run: bundle exec rspec + tags: [backend, test] +`, + }, + result: &Config{ + SourceDir: DefaultSourceDir, + SourceDirLocal: DefaultSourceDirLocal, + Extends: []string{"global-extend.yml"}, + Hooks: map[string]*Hook{ + "pre-commit": { + Parallel: true, + ExcludeTags: []string{"backend"}, + Commands: map[string]*Command{ + "global-lint": { + Run: "bundle exec rubocop", + Tags: []string{"backend", "linter"}, + Glob: "*.rb", + }, + "global-other": { + Run: "bundle exec rubocop", + Tags: []string{"other"}, + }, + "extended-tests": { + Run: "bundle exec rspec", + Tags: []string{"backend", "test"}, + }, + }, + }, + }, + }, + }, } { fs := afero.Afero{Fs: afero.NewMemMapFs()} repo := &git.Repository{