Skip to content

Commit

Permalink
feat: support .lefthook.yml and .lefthook-local.yml (#520)
Browse files Browse the repository at this point in the history
  • Loading branch information
hyperupcall authored Jul 17, 2023
1 parent c134f31 commit 27426fa
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 18 deletions.
2 changes: 1 addition & 1 deletion cmd/uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func newUninstallCmd(opts *lefthook.Options) *cobra.Command {

uninstallCmd.Flags().BoolVarP(
&args.RemoveConfig, "remove-configs", "c", false,
"remove lefthook.yml and lefthook-local.yml",
"remove lefthook.yml, lefthook-local.yml, .lefthook.yml, and .lefthook-local.yml",
)

return &uninstallCmd
Expand Down
48 changes: 39 additions & 9 deletions internal/config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,8 @@ func (err NotFoundError) Error() string {

// Loads configs from the given directory with extensions.
func Load(fs afero.Fs, repo *git.Repository) (*Config, error) {
global, err := read(fs, repo.RootPath, "lefthook")
global, err := readOne(fs, repo.RootPath, []string{"lefthook", ".lefthook"})
if err != nil {
var notFoundErr viper.ConfigFileNotFoundError
if ok := errors.As(err, &notFoundErr); ok {
return nil, NotFoundError{err.Error()}
}

return nil, err
}

Expand Down Expand Up @@ -81,9 +76,28 @@ func read(fs afero.Fs, path string, name string) (*viper.Viper, error) {
return v, nil
}

// mergeAll merges remotes and extends from .lefthook and .lefthook-local.
func readOne(fs afero.Fs, path string, names []string) (*viper.Viper, error) {
for _, name := range names {
v, err := read(fs, path, name)
if err != nil {
var notFoundErr viper.ConfigFileNotFoundError
if ok := errors.As(err, &notFoundErr); ok {
continue
} else {
return nil, err
}
}

return v, nil
}

return nil, NotFoundError{fmt.Sprintf("No config files with names %q could not be found in \"%s\"", names, path)}
}

// mergeAll merges (.lefthook or lefthook) and (extended config) and (remote)
// and (.lefthook-local or .lefthook-local) configs.
func mergeAll(fs afero.Fs, repo *git.Repository) (*viper.Viper, error) {
extends, err := read(fs, repo.RootPath, "lefthook")
extends, err := readOne(fs, repo.RootPath, []string{"lefthook", ".lefthook"})
if err != nil {
return nil, err
}
Expand All @@ -96,7 +110,7 @@ func mergeAll(fs afero.Fs, repo *git.Repository) (*viper.Viper, error) {
return nil, err
}

if err := merge("lefthook-local", "", extends); err == nil {
if err := mergeOne([]string{"lefthook-local", ".lefthook-local"}, "", extends); err == nil {
if err = extend(extends, repo.RootPath); err != nil {
return nil, err
}
Expand Down Expand Up @@ -173,6 +187,22 @@ func merge(name, path string, v *viper.Viper) error {
return nil
}

func mergeOne(names []string, path string, v *viper.Viper) error {
for _, name := range names {
err := merge(name, path, v)
if err == nil {
break
} else {
var notFoundErr viper.ConfigFileNotFoundError
if ok := errors.As(err, &notFoundErr); !ok {
return err
}
}
}

return nil
}

func unmarshalConfigs(base, extra *viper.Viper, c *Config) error {
c.Hooks = make(map[string]*Hook)

Expand Down
210 changes: 203 additions & 7 deletions internal/config/load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,93 @@ func TestLoad(t *testing.T) {
name string
global, local, remote string
remoteConfigPath string
extends map[string]string
otherFiles map[string]string
result *Config
}{
{
name: "with global, dot",
otherFiles: map[string]string{
".lefthook.yml": `
pre-commit:
commands:
tests:
run: yarn test
`,
},
result: &Config{
SourceDir: DefaultSourceDir,
SourceDirLocal: DefaultSourceDirLocal,
Colors: nil,
Hooks: map[string]*Hook{
"pre-commit": {
Parallel: false,
Commands: map[string]*Command{
"tests": {
Run: "yarn test",
},
},
},
},
},
},
{
name: "with global, nodot",
otherFiles: map[string]string{
"lefthook.yml": `
pre-commit:
commands:
tests:
run: yarn test
`,
},
result: &Config{
SourceDir: DefaultSourceDir,
SourceDirLocal: DefaultSourceDirLocal,
Colors: nil,
Hooks: map[string]*Hook{
"pre-commit": {
Parallel: false,
Commands: map[string]*Command{
"tests": {
Run: "yarn test",
},
},
},
},
},
},
{
name: "with global, nodot has priority",
otherFiles: map[string]string{
".lefthook.yml": `
pre-commit:
commands:
tests:
run: yarn test1
`,
"lefthook.yml": `
pre-commit:
commands:
tests:
run: yarn test2
`,
},
result: &Config{
SourceDir: DefaultSourceDir,
SourceDirLocal: DefaultSourceDirLocal,
Colors: nil,
Hooks: map[string]*Hook{
"pre-commit": {
Parallel: false,
Commands: map[string]*Command{
"tests": {
Run: "yarn test2",
},
},
},
},
},
},
{
name: "simple",
global: `
Expand Down Expand Up @@ -144,6 +228,114 @@ pre-push:
},
},
},
{
name: "with overrides, dot",
otherFiles: map[string]string{
".lefthook.yml": `
pre-push:
scripts:
"global-extend":
runner: bash
`,
".lefthook-local.yml": `
pre-push:
scripts:
"local-extend":
runner: bash
`,
},
result: &Config{
SourceDir: DefaultSourceDir,
SourceDirLocal: DefaultSourceDirLocal,
Colors: nil,
Hooks: map[string]*Hook{
"pre-push": {
Scripts: map[string]*Script{
"global-extend": {
Runner: "bash",
},
"local-extend": {
Runner: "bash",
},
},
},
},
},
},
{
name: "with overrides, dot, nodot",
otherFiles: map[string]string{
"lefthook.yml": `
pre-push:
scripts:
"global-extend":
runner: bash
`,
".lefthook-local.yml": `
pre-push:
scripts:
"local-extend":
runner: bash
`,
},
result: &Config{
SourceDir: DefaultSourceDir,
SourceDirLocal: DefaultSourceDirLocal,
Colors: nil,
Hooks: map[string]*Hook{
"pre-push": {
Scripts: map[string]*Script{
"global-extend": {
Runner: "bash",
},
"local-extend": {
Runner: "bash",
},
},
},
},
},
},
{
name: "with overrides, nodot has priority",
otherFiles: map[string]string{
"lefthook.yml": `
pre-push:
scripts:
"global-extend":
runner: bash
`,
".lefthook-local.yml": `
pre-push:
scripts:
"local-extend":
runner: bash1
`,
"lefthook-local.yml": `
pre-push:
scripts:
"local-extend":
runner: bash2
`,
},
result: &Config{
SourceDir: DefaultSourceDir,
SourceDirLocal: DefaultSourceDirLocal,
Colors: nil,
Hooks: map[string]*Hook{
"pre-push": {
Scripts: map[string]*Script{
"global-extend": {
Runner: "bash",
},
"local-extend": {
Runner: "bash2",
},
},
},
},
},
},
{
name: "with extra hooks",
global: `
Expand Down Expand Up @@ -356,7 +548,7 @@ pre-push:
run: echo remote
`,
remoteConfigPath: filepath.Join(root, ".git", "info", "lefthook-remotes", "lefthook", "examples", "config.yml"),
extends: map[string]string{
otherFiles: map[string]string{
"global-extend.yml": `
pre-push:
scripts:
Expand Down Expand Up @@ -422,12 +614,16 @@ pre-push:
}

t.Run(fmt.Sprintf("%d: %s", i, tt.name), func(t *testing.T) {
if err := fs.WriteFile(filepath.Join(root, "lefthook.yml"), []byte(tt.global), 0o644); err != nil {
t.Errorf("unexpected error: %s", err)
if tt.global != "" {
if err := fs.WriteFile(filepath.Join(root, "lefthook.yml"), []byte(tt.global), 0o644); err != nil {
t.Errorf("unexpected error: %s", err)
}
}

if err := fs.WriteFile(filepath.Join(root, "lefthook-local.yml"), []byte(tt.local), 0o644); err != nil {
t.Errorf("unexpected error: %s", err)
if tt.local != "" {
if err := fs.WriteFile(filepath.Join(root, "lefthook-local.yml"), []byte(tt.local), 0o644); err != nil {
t.Errorf("unexpected error: %s", err)
}
}

if len(tt.remoteConfigPath) > 0 {
Expand All @@ -440,7 +636,7 @@ pre-push:
}
}

for name, content := range tt.extends {
for name, content := range tt.otherFiles {
path := filepath.Join(
root,
filepath.Join(strings.Split(name, "/")...),
Expand Down
2 changes: 1 addition & 1 deletion internal/lefthook/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const (

var (
lefthookChecksumRegexp = regexp.MustCompile(`(\w+)\s+(\d+)`)
configGlob = glob.MustCompile("lefthook.{yml,yaml,json,toml}")
configGlob = glob.MustCompile("{.,}lefthook.{yml,yaml,json,toml}")
errNoConfig = fmt.Errorf("no lefthook config found")
)

Expand Down
2 changes: 2 additions & 0 deletions internal/lefthook/uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ func (l *Lefthook) Uninstall(args *UninstallArgs) error {

if args.RemoveConfig {
for _, glob := range []string{
".lefthook.y*ml",
"lefthook.y*ml",
".lefthook-local.y*ml",
"lefthook-local.y*ml",
} {
l.removeFile(filepath.Join(l.repo.RootPath, glob))
Expand Down

0 comments on commit 27426fa

Please sign in to comment.