Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add minimum git version support warning #886

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions internal/git/repository.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package git

import (
"errors"
"fmt"
"os"
"path/filepath"
Expand All @@ -10,17 +11,20 @@ import (
"github.com/spf13/afero"

"github.com/evilmartians/lefthook/internal/log"
"github.com/evilmartians/lefthook/internal/version"
)

const (
minGitVersion = "2.31.0"
stashMessage = "lefthook auto backup"
unstagedPatchName = "lefthook-unstaged.patch"
infoDirMode = 0o775
minStatusLen = 3
)

var (
headBranchRegexp = regexp.MustCompile(`HEAD -> (?P<name>.*)$`)
reHeadBranch = regexp.MustCompile(`HEAD -> (?P<name>.*)$`)
reVersion = regexp.MustCompile(`\d+\.\d+\.\d+`)
cmdPushFilesBase = []string{"git", "diff", "--name-only", "HEAD", "@{push}"}
cmdPushFilesHead = []string{"git", "diff", "--name-only", "HEAD"}
cmdStagedFiles = []string{"git", "diff", "--name-only", "--cached", "--diff-filter=ACMR"}
Expand All @@ -36,6 +40,7 @@ var (
cmdRemotes = []string{"git", "branch", "--remotes"}
cmdHideUnstaged = []string{"git", "checkout", "--force", "--"}
cmdEmptyTreeSHA = []string{"git", "hash-object", "-t", "tree", "/dev/null"}
cmdGitVersion = []string{"git", "version"}
)

// Repository represents a git repository.
Expand All @@ -53,6 +58,18 @@ type Repository struct {

// NewRepository returns a Repository or an error, if git repository it not initialized.
func NewRepository(fs afero.Fs, git *CommandExecutor) (*Repository, error) {
gitVersionOut, err := git.Cmd(cmdGitVersion)
if err == nil {
gitVersion := reVersion.FindString(gitVersionOut)
if err = version.Check(minGitVersion, gitVersion); err != nil {
log.Debugf("[lefthook] version check warning: %s %s", gitVersion, err)

if errors.Is(err, version.ErrUncoveredVersion) {
log.Warn("Git version is too old. Minimum supported version is " + minGitVersion)
}
}
}

rootPath, err := git.Cmd(cmdRootPath)
if err != nil {
return nil, err
Expand Down Expand Up @@ -127,12 +144,12 @@ func (r *Repository) PushFiles() ([]string, error) {
}

for _, branch := range branches {
if !headBranchRegexp.MatchString(branch) {
if !reHeadBranch.MatchString(branch) {
continue
}

matches := headBranchRegexp.FindStringSubmatch(branch)
r.headBranch = matches[headBranchRegexp.SubexpIndex("name")]
matches := reHeadBranch.FindStringSubmatch(branch)
r.headBranch = matches[reHeadBranch.SubexpIndex("name")]
break
}
}
Expand Down
70 changes: 42 additions & 28 deletions internal/version/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ var (
`^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?$`,
)

errIncorrectVersion = errors.New("format of 'min_version' setting is incorrect")
ErrInvalidVersion = errors.New("invalid version format")
ErrUncoveredVersion = errors.New("version is lower than required")

errInvalidMinVersion = errors.New("format of 'min_version' setting is incorrect")
)

func Version(verbose bool) string {
Expand All @@ -29,47 +32,58 @@ func Version(verbose bool) string {
return version
}

// CheckCovered returns true if given version is less or equal than current
// and false otherwise.
func CheckCovered(targetVersion string) error {
if len(targetVersion) == 0 {
return nil
}

if !versionRegexp.MatchString(targetVersion) {
return errIncorrectVersion
}

major, minor, patch, err := parseVersion(version)
if err != nil {
return err
func Check(wanted, given string) error {
if !versionRegexp.MatchString(given) {
return ErrInvalidVersion
}

tMajor, tMinor, tPatch, err := parseVersion(targetVersion)
major, minor, patch, err := parseVersion(given)
if err != nil {
return err
return ErrInvalidVersion
}

execPath, err := os.Executable()
wantMajor, wantMinor, wantPatch, err := parseVersion(wanted)
if err != nil {
execPath = "<unknown>"
return ErrInvalidVersion
}
errUncovered := fmt.Errorf("required lefthook version (%s) is higher than current (%s) at %s", targetVersion, version, execPath)

switch {
case major > tMajor:
case major > wantMajor:
return nil
case major < tMajor:
return errUncovered
case minor > tMinor:
case major < wantMajor:
return ErrUncoveredVersion
case minor > wantMinor:
return nil
case minor < tMinor:
return errUncovered
case patch >= tPatch:
case minor < wantMinor:
return ErrUncoveredVersion
case patch >= wantPatch:
return nil
default:
return errUncovered
return ErrUncoveredVersion
}
}

// CheckCovered returns true if given version is less or equal than current
// and false otherwise.
func CheckCovered(targetVersion string) error {
if len(targetVersion) == 0 {
return nil
}

err := Check(targetVersion, version)

if errors.Is(err, ErrUncoveredVersion) {
execPath, oserr := os.Executable()
if oserr != nil {
execPath = "<unknown>"
}

return fmt.Errorf("required lefthook version (%s) is higher than current (%s) at %s", targetVersion, version, execPath)
} else if errors.Is(err, ErrInvalidVersion) {
return errInvalidMinVersion
}

return err
}

// parseVersion parses the version string of "1.2.3", "1.2", or just "1" and
Expand Down
Loading