package precommit
import (
"os"
"path"
"path/filepath"
"runtime"
"strings"
)
var configNames = []string{".pre-commit-config.yaml", ".pre-commit-config.yml"}
func ConfigFile(root string) (string, bool) {
for _, name := range configNames {
p := filepath.Join(root, name)
if info, err := os.Stat(p); err == nil && !info.IsDir() {
return p, true
}
}
return "", false
}
func ToolVersion(r CommandRunner) (string, bool) {
out, err := r.RunStdout("", "pre-commit", "--version")
if err != nil {
return "", false
}
fields := strings.Fields(strings.TrimSpace(out))
if len(fields) >= 2 {
return fields[1], true
}
if len(fields) == 1 {
return "", true
}
return "", false
}
const hookSignature = "generated by pre-commit"
type HookType string
const (
HookTypePreCommit HookType = "pre-commit"
HookTypePrePush HookType = "pre-push"
)
var requiredHookTypes = []HookType{HookTypePreCommit, HookTypePrePush}
func HookInstalled(r CommandRunner, root string) bool {
return HookTypeInstalled(r, root, HookTypePreCommit)
}
func HookTypeInstalled(r CommandRunner, root string, hookType HookType) bool {
if !isRequiredHookType(hookType) {
return false
}
hook := hookPath(r, root, hookType)
info, err := os.Stat(hook)
if err != nil || info.IsDir() {
return false
}
if runtime.GOOS != "windows" && info.Mode().Perm()&0o100 == 0 {
return false
}
data, err := os.ReadFile(hook)
if err != nil {
return false
}
content := string(data)
return strings.Contains(content, hookSignature) &&
strings.Contains(content, "--hook-type="+string(hookType))
}
func isRequiredHookType(hookType HookType) bool {
for _, required := range requiredHookTypes {
if hookType == required {
return true
}
}
return false
}
func hookPath(r CommandRunner, root string, hookType HookType) string {
gitPath := path.Join("hooks", string(hookType))
out, err := r.Run(root, "git", "rev-parse", "--git-path", gitPath)
if err == nil {
if p := strings.TrimSpace(out); p != "" {
if !filepath.IsAbs(p) {
p = filepath.Join(root, p)
}
return filepath.Clean(p)
}
}
return filepath.Join(root, ".git", "hooks", string(hookType))
}