* vendor: switch from "mcuadros/go-version" to "hashicorp/go-version" * Adapt P1 * simplify * fix lint * adapt * fix lint & rm old code * no deadlock * rm RWMutex and check GoVersion only 1-time * Copyright header Co-authored-by: techknowlogick <techknowlogick@gitea.io>tags/v1.13.0-rc1
@@ -51,6 +51,7 @@ require ( | |||
github.com/google/uuid v1.1.1 | |||
github.com/gorilla/context v1.1.1 | |||
github.com/hashicorp/go-retryablehttp v0.6.6 // indirect | |||
github.com/hashicorp/go-version v1.2.1 | |||
github.com/huandu/xstrings v1.3.0 | |||
github.com/issue9/assert v1.3.2 // indirect | |||
github.com/issue9/identicon v1.0.1 | |||
@@ -65,7 +66,6 @@ require ( | |||
github.com/markbates/goth v1.61.2 | |||
github.com/mattn/go-isatty v0.0.12 | |||
github.com/mattn/go-sqlite3 v1.14.0 | |||
github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75 | |||
github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81 | |||
github.com/mgechev/revive v1.0.2 | |||
github.com/mholt/archiver/v3 v3.3.0 | |||
@@ -482,6 +482,8 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX | |||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= | |||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= | |||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= | |||
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= | |||
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= | |||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= | |||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | |||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | |||
@@ -659,8 +661,6 @@ github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/ | |||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= | |||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= | |||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= | |||
github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75 h1:Pijfgr7ZuvX7QIQiEwLdRVr3RoMG+i0SbBO1Qu+7yVk= | |||
github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo= | |||
github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81 h1:QASJXOGm2RZ5Ardbc86qNFvby9AqkLDibfChMtAg5QM= | |||
github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= | |||
github.com/mgechev/revive v1.0.2 h1:v0NxxQ7fSFz/u1NQydPo6EGdq7va0J1BtsZmae6kzUg= | |||
@@ -1,3 +1,4 @@ | |||
// Copyright 2020 The Gitea Authors. All rights reserved. | |||
// Copyright 2015 The Gogs Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
@@ -11,12 +12,10 @@ import ( | |||
"io" | |||
"os" | |||
"os/exec" | |||
"runtime" | |||
"strings" | |||
"time" | |||
"code.gitea.io/gitea/modules/process" | |||
"github.com/mcuadros/go-version" | |||
) | |||
var ( | |||
@@ -132,7 +131,7 @@ func (c *Command) RunInDirTimeoutEnvFullPipelineFunc(env []string, timeout time. | |||
} | |||
// TODO: verify if this is still needed in golang 1.15 | |||
if version.Compare(runtime.Version(), "go1.15", "<") { | |||
if goVersionLessThan115 { | |||
cmd.Env = append(cmd.Env, "GODEBUG=asyncpreemptoff=1") | |||
} | |||
cmd.Dir = dir | |||
@@ -21,7 +21,6 @@ import ( | |||
"strings" | |||
"github.com/go-git/go-git/v5/plumbing/object" | |||
"github.com/mcuadros/go-version" | |||
) | |||
// Commit represents a git commit. | |||
@@ -470,7 +469,7 @@ func (c *Commit) GetSubModule(entryname string) (*SubModule, error) { | |||
// GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only') | |||
func (c *Commit) GetBranchName() (string, error) { | |||
binVersion, err := BinVersion() | |||
err := LoadGitVersion() | |||
if err != nil { | |||
return "", fmt.Errorf("Git version missing: %v", err) | |||
} | |||
@@ -478,7 +477,7 @@ func (c *Commit) GetBranchName() (string, error) { | |||
args := []string{ | |||
"name-rev", | |||
} | |||
if version.Compare(binVersion, "2.13.0", ">=") { | |||
if CheckGitVersionConstraint(">= 2.13.0") == nil { | |||
args = append(args, "--exclude", "refs/tags/*") | |||
} | |||
args = append(args, "--name-only", "--no-undefined", c.ID.String()) | |||
@@ -15,14 +15,9 @@ import ( | |||
"code.gitea.io/gitea/modules/process" | |||
"github.com/mcuadros/go-version" | |||
"github.com/hashicorp/go-version" | |||
) | |||
// Version return this package's current version | |||
func Version() string { | |||
return "0.4.2" | |||
} | |||
var ( | |||
// Debug enables verbose logging on everything. | |||
// This should be false in case Gogs starts in SSH mode. | |||
@@ -39,7 +34,10 @@ var ( | |||
// DefaultContext is the default context to run git commands in | |||
DefaultContext = context.Background() | |||
gitVersion string | |||
gitVersion *version.Version | |||
// will be checked on Init | |||
goVersionLessThan115 = true | |||
) | |||
func log(format string, args ...interface{}) { | |||
@@ -55,31 +53,43 @@ func log(format string, args ...interface{}) { | |||
} | |||
} | |||
// BinVersion returns current Git version from shell. | |||
func BinVersion() (string, error) { | |||
if len(gitVersion) > 0 { | |||
return gitVersion, nil | |||
// LocalVersion returns current Git version from shell. | |||
func LocalVersion() (*version.Version, error) { | |||
if err := LoadGitVersion(); err != nil { | |||
return nil, err | |||
} | |||
return gitVersion, nil | |||
} | |||
// LoadGitVersion returns current Git version from shell. | |||
func LoadGitVersion() error { | |||
// doesn't need RWMutex because its exec by Init() | |||
if gitVersion != nil { | |||
return nil | |||
} | |||
stdout, err := NewCommand("version").Run() | |||
if err != nil { | |||
return "", err | |||
return err | |||
} | |||
fields := strings.Fields(stdout) | |||
if len(fields) < 3 { | |||
return "", fmt.Errorf("not enough output: %s", stdout) | |||
return fmt.Errorf("not enough output: %s", stdout) | |||
} | |||
var versionString string | |||
// Handle special case on Windows. | |||
i := strings.Index(fields[2], "windows") | |||
if i >= 1 { | |||
gitVersion = fields[2][:i-1] | |||
return gitVersion, nil | |||
versionString = fields[2][:i-1] | |||
} else { | |||
versionString = fields[2] | |||
} | |||
gitVersion = fields[2] | |||
return gitVersion, nil | |||
gitVersion, err = version.NewVersion(versionString) | |||
return err | |||
} | |||
// SetExecutablePath changes the path of git executable and checks the file permission and version. | |||
@@ -94,11 +104,17 @@ func SetExecutablePath(path string) error { | |||
} | |||
GitExecutable = absPath | |||
gitVersion, err := BinVersion() | |||
err = LoadGitVersion() | |||
if err != nil { | |||
return fmt.Errorf("Git version missing: %v", err) | |||
} | |||
if version.Compare(gitVersion, GitVersionRequired, "<") { | |||
versionRequired, err := version.NewVersion(GitVersionRequired) | |||
if err != nil { | |||
return err | |||
} | |||
if gitVersion.LessThan(versionRequired) { | |||
return fmt.Errorf("Git version not supported. Requires version > %v", GitVersionRequired) | |||
} | |||
@@ -108,6 +124,20 @@ func SetExecutablePath(path string) error { | |||
// Init initializes git module | |||
func Init(ctx context.Context) error { | |||
DefaultContext = ctx | |||
// Save current git version on init to gitVersion otherwise it would require an RWMutex | |||
if err := LoadGitVersion(); err != nil { | |||
return err | |||
} | |||
// Save if the go version used to compile gitea is greater or equal 1.15 | |||
runtimeVersion, err := version.NewVersion(strings.TrimPrefix(runtime.Version(), "go")) | |||
if err != nil { | |||
return err | |||
} | |||
version115, _ := version.NewVersion("1.15") | |||
goVersionLessThan115 = runtimeVersion.LessThan(version115) | |||
// Git requires setting user.name and user.email in order to commit changes - if they're not set just add some defaults | |||
for configKey, defaultValue := range map[string]string{"user.name": "Gitea", "user.email": "gitea@fake.local"} { | |||
if err := checkAndSetConfig(configKey, defaultValue, false); err != nil { | |||
@@ -120,13 +150,13 @@ func Init(ctx context.Context) error { | |||
return err | |||
} | |||
if version.Compare(gitVersion, "2.10", ">=") { | |||
if CheckGitVersionConstraint(">= 2.10") == nil { | |||
if err := checkAndSetConfig("receive.advertisePushOptions", "true", true); err != nil { | |||
return err | |||
} | |||
} | |||
if version.Compare(gitVersion, "2.18", ">=") { | |||
if CheckGitVersionConstraint(">= 2.18") == nil { | |||
if err := checkAndSetConfig("core.commitGraph", "true", true); err != nil { | |||
return err | |||
} | |||
@@ -143,6 +173,21 @@ func Init(ctx context.Context) error { | |||
return nil | |||
} | |||
// CheckGitVersionConstraint check version constrain against local installed git version | |||
func CheckGitVersionConstraint(constraint string) error { | |||
if err := LoadGitVersion(); err != nil { | |||
return err | |||
} | |||
check, err := version.NewConstraint(constraint) | |||
if err != nil { | |||
return err | |||
} | |||
if !check.Check(gitVersion) { | |||
return fmt.Errorf("installed git binary %s does not satisfy version constraint %s", gitVersion.Original(), constraint) | |||
} | |||
return nil | |||
} | |||
func checkAndSetConfig(key, defaultValue string, forceToDefault bool) error { | |||
stdout, stderr, err := process.GetManager().Exec("git.Init(get setting)", GitExecutable, "config", "--get", key) | |||
if err != nil { | |||
@@ -7,8 +7,6 @@ package git | |||
import ( | |||
"bytes" | |||
"fmt" | |||
"github.com/mcuadros/go-version" | |||
) | |||
// CheckAttributeOpts represents the possible options to CheckAttribute | |||
@@ -21,7 +19,7 @@ type CheckAttributeOpts struct { | |||
// CheckAttribute return the Blame object of file | |||
func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[string]string, error) { | |||
binVersion, err := BinVersion() | |||
err := LoadGitVersion() | |||
if err != nil { | |||
return nil, fmt.Errorf("Git version missing: %v", err) | |||
} | |||
@@ -42,7 +40,7 @@ func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[ | |||
} | |||
// git check-attr --cached first appears in git 1.7.8 | |||
if opts.CachedOnly && version.Compare(binVersion, "1.7.8", ">=") { | |||
if opts.CachedOnly && CheckGitVersionConstraint(">= 1.7.8") == nil { | |||
cmdArgs = append(cmdArgs, "--cached") | |||
} | |||
@@ -14,7 +14,6 @@ import ( | |||
"github.com/go-git/go-git/v5/plumbing" | |||
"github.com/go-git/go-git/v5/plumbing/object" | |||
"github.com/mcuadros/go-version" | |||
) | |||
// GetRefCommitID returns the last commit ID string of given reference (branch or tag). | |||
@@ -470,7 +469,7 @@ func (repo *Repository) getCommitsBeforeLimit(id SHA1, num int) (*list.List, err | |||
} | |||
func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error) { | |||
if version.Compare(gitVersion, "2.7.0", ">=") { | |||
if CheckGitVersionConstraint(">= 2.7.0") == nil { | |||
stdout, err := NewCommand("for-each-ref", "--count="+strconv.Itoa(limit), "--format=%(refname:strip=2)", "--contains", commit.ID.String(), BranchPrefix).RunInDir(repo.Path) | |||
if err != nil { | |||
return nil, err | |||
@@ -10,7 +10,6 @@ import ( | |||
"strings" | |||
"github.com/go-git/go-git/v5/plumbing" | |||
"github.com/mcuadros/go-version" | |||
) | |||
// TagPrefix tags prefix path on the repository | |||
@@ -239,8 +238,6 @@ func (repo *Repository) GetTags() ([]string, error) { | |||
return nil | |||
}) | |||
version.Sort(tagNames) | |||
// Reverse order | |||
for i := 0; i < len(tagNames)/2; i++ { | |||
j := len(tagNames) - i - 1 | |||
@@ -11,8 +11,6 @@ import ( | |||
"os" | |||
"strings" | |||
"time" | |||
"github.com/mcuadros/go-version" | |||
) | |||
func (repo *Repository) getTree(id SHA1) (*Tree, error) { | |||
@@ -65,7 +63,7 @@ type CommitTreeOpts struct { | |||
// CommitTree creates a commit from a given tree id for the user with provided message | |||
func (repo *Repository) CommitTree(sig *Signature, tree *Tree, opts CommitTreeOpts) (SHA1, error) { | |||
binVersion, err := BinVersion() | |||
err := LoadGitVersion() | |||
if err != nil { | |||
return SHA1{}, err | |||
} | |||
@@ -91,11 +89,11 @@ func (repo *Repository) CommitTree(sig *Signature, tree *Tree, opts CommitTreeOp | |||
_, _ = messageBytes.WriteString(opts.Message) | |||
_, _ = messageBytes.WriteString("\n") | |||
if version.Compare(binVersion, "1.7.9", ">=") && (opts.KeyID != "" || opts.AlwaysSign) { | |||
if CheckGitVersionConstraint(">= 1.7.9") == nil && (opts.KeyID != "" || opts.AlwaysSign) { | |||
cmd.AddArguments(fmt.Sprintf("-S%s", opts.KeyID)) | |||
} | |||
if version.Compare(binVersion, "2.0.0", ">=") && opts.NoGPGSign { | |||
if CheckGitVersionConstraint(">= 2.0.0") == nil && opts.NoGPGSign { | |||
cmd.AddArguments("--no-gpg-sign") | |||
} | |||
@@ -19,8 +19,6 @@ import ( | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/services/gitdiff" | |||
"github.com/mcuadros/go-version" | |||
) | |||
// TemporaryUploadRepository is a type to wrap our upload repositories as a shallow clone | |||
@@ -196,7 +194,7 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *models | |||
authorSig := author.NewGitSig() | |||
committerSig := committer.NewGitSig() | |||
binVersion, err := git.BinVersion() | |||
err := git.LoadGitVersion() | |||
if err != nil { | |||
return "", fmt.Errorf("Unable to get git version: %v", err) | |||
} | |||
@@ -218,11 +216,11 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *models | |||
args := []string{"commit-tree", treeHash, "-p", "HEAD"} | |||
// Determine if we should sign | |||
if version.Compare(binVersion, "1.7.9", ">=") { | |||
if git.CheckGitVersionConstraint(">= 1.7.9") == nil { | |||
sign, keyID, _ := t.repo.SignCRUDAction(author, t.basePath, "HEAD") | |||
if sign { | |||
args = append(args, "-S"+keyID) | |||
} else if version.Compare(binVersion, "2.0.0", ">=") { | |||
} else if git.CheckGitVersionConstraint(">= 2.0.0") == nil { | |||
args = append(args, "--no-gpg-sign") | |||
} | |||
} | |||
@@ -309,7 +307,7 @@ func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) { | |||
// CheckAttribute checks the given attribute of the provided files | |||
func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...string) (map[string]map[string]string, error) { | |||
binVersion, err := git.BinVersion() | |||
err := git.LoadGitVersion() | |||
if err != nil { | |||
log.Error("Error retrieving git version: %v", err) | |||
return nil, err | |||
@@ -321,7 +319,7 @@ func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...str | |||
cmdArgs := []string{"check-attr", "-z", attribute} | |||
// git check-attr --cached first appears in git 1.7.8 | |||
if version.Compare(binVersion, "1.7.8", ">=") { | |||
if git.CheckGitVersionConstraint(">= 1.7.8") == nil { | |||
cmdArgs = append(cmdArgs, "--cached") | |||
} | |||
cmdArgs = append(cmdArgs, "--") | |||
@@ -19,7 +19,6 @@ import ( | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/modules/util" | |||
"github.com/mcuadros/go-version" | |||
"github.com/unknwon/com" | |||
) | |||
@@ -122,7 +121,7 @@ func initRepoCommit(tmpPath string, repo *models.Repository, u *models.User, def | |||
return fmt.Errorf("git add --all: %v", err) | |||
} | |||
binVersion, err := git.BinVersion() | |||
err = git.LoadGitVersion() | |||
if err != nil { | |||
return fmt.Errorf("Unable to get git version: %v", err) | |||
} | |||
@@ -132,11 +131,11 @@ func initRepoCommit(tmpPath string, repo *models.Repository, u *models.User, def | |||
"-m", "Initial commit", | |||
} | |||
if version.Compare(binVersion, "1.7.9", ">=") { | |||
if git.CheckGitVersionConstraint(">= 1.7.9") == nil { | |||
sign, keyID, _ := models.SignInitialCommit(tmpPath, u) | |||
if sign { | |||
args = append(args, "-S"+keyID) | |||
} else if version.Compare(binVersion, "2.0.0", ">=") { | |||
} else if git.CheckGitVersionConstraint(">= 2.0.0") == nil { | |||
args = append(args, "--no-gpg-sign") | |||
} | |||
} | |||
@@ -9,8 +9,6 @@ import ( | |||
"code.gitea.io/gitea/modules/git" | |||
"code.gitea.io/gitea/modules/log" | |||
version "github.com/mcuadros/go-version" | |||
) | |||
var ( | |||
@@ -71,20 +69,20 @@ func newGit() { | |||
} | |||
git.DefaultCommandExecutionTimeout = time.Duration(Git.Timeout.Default) * time.Second | |||
binVersion, err := git.BinVersion() | |||
version, err := git.LocalVersion() | |||
if err != nil { | |||
log.Fatal("Error retrieving git version: %v", err) | |||
} | |||
if version.Compare(binVersion, "2.9", ">=") { | |||
if git.CheckGitVersionConstraint(">= 2.9") == nil { | |||
// Explicitly disable credential helper, otherwise Git credentials might leak | |||
git.GlobalCommandArgs = append(git.GlobalCommandArgs, "-c", "credential.helper=") | |||
} | |||
var format = "Git Version: %s" | |||
var args = []interface{}{binVersion} | |||
var args = []interface{}{version.Original()} | |||
// Since git wire protocol has been released from git v2.18 | |||
if Git.EnableAutoGitWireProtocol && version.Compare(binVersion, "2.18", ">=") { | |||
if Git.EnableAutoGitWireProtocol && git.CheckGitVersionConstraint(">= 2.18") == nil { | |||
git.GlobalCommandArgs = append(git.GlobalCommandArgs, "-c", "protocol.version=2") | |||
format += ", Wire Protocol %s Enabled" | |||
args = append(args, "Version 2") // for focus color | |||
@@ -28,7 +28,6 @@ import ( | |||
"code.gitea.io/gitea/modules/user" | |||
shellquote "github.com/kballard/go-shellquote" | |||
version "github.com/mcuadros/go-version" | |||
"github.com/unknwon/com" | |||
ini "gopkg.in/ini.v1" | |||
"strk.kbt.io/projects/go/libravatar" | |||
@@ -479,12 +478,12 @@ func CheckLFSVersion() { | |||
//Disable LFS client hooks if installed for the current OS user | |||
//Needs at least git v2.1.2 | |||
binVersion, err := git.BinVersion() | |||
err := git.LoadGitVersion() | |||
if err != nil { | |||
log.Fatal("Error retrieving git version: %v", err) | |||
} | |||
if !version.Compare(binVersion, "2.1.2", ">=") { | |||
if git.CheckGitVersionConstraint(">= 2.1.2") != nil { | |||
LFS.StartServer = false | |||
log.Error("LFS server support needs at least Git v2.1.2") | |||
} else { | |||
@@ -243,7 +243,9 @@ func Config(ctx *context.Context) { | |||
ctx.Data["DisableRouterLog"] = setting.DisableRouterLog | |||
ctx.Data["RunUser"] = setting.RunUser | |||
ctx.Data["RunMode"] = strings.Title(macaron.Env) | |||
ctx.Data["GitVersion"], _ = git.BinVersion() | |||
if version, err := git.LocalVersion(); err == nil { | |||
ctx.Data["GitVersion"] = version.Original() | |||
} | |||
ctx.Data["RepoRootPath"] = setting.RepoRootPath | |||
ctx.Data["CustomRootPath"] = setting.CustomPath | |||
ctx.Data["StaticRootPath"] = setting.StaticRootPath | |||
@@ -33,7 +33,6 @@ import ( | |||
gogit "github.com/go-git/go-git/v5" | |||
"github.com/go-git/go-git/v5/plumbing" | |||
"github.com/go-git/go-git/v5/plumbing/object" | |||
"github.com/mcuadros/go-version" | |||
"github.com/unknwon/com" | |||
) | |||
@@ -541,7 +540,7 @@ func LFSPointerFiles(ctx *context.Context) { | |||
return | |||
} | |||
ctx.Data["PageIsSettingsLFS"] = true | |||
binVersion, err := git.BinVersion() | |||
err := git.LoadGitVersion() | |||
if err != nil { | |||
log.Fatal("Error retrieving git version: %v", err) | |||
} | |||
@@ -586,7 +585,7 @@ func LFSPointerFiles(ctx *context.Context) { | |||
go createPointerResultsFromCatFileBatch(catFileBatchReader, &wg, pointerChan, ctx.Repo.Repository, ctx.User) | |||
go pipeline.CatFileBatch(shasToBatchReader, catFileBatchWriter, &wg, basePath) | |||
go pipeline.BlobsLessThan1024FromCatFileBatchCheck(catFileCheckReader, shasToBatchWriter, &wg) | |||
if !version.Compare(binVersion, "2.6.0", ">=") { | |||
if git.CheckGitVersionConstraint(">= 2.6.0") != nil { | |||
revListReader, revListWriter := io.Pipe() | |||
shasToCheckReader, shasToCheckWriter := io.Pipe() | |||
wg.Add(2) | |||
@@ -23,7 +23,6 @@ import ( | |||
"code.gitea.io/gitea/modules/timeutil" | |||
"code.gitea.io/gitea/modules/util" | |||
"github.com/mcuadros/go-version" | |||
"github.com/unknwon/com" | |||
) | |||
@@ -43,11 +42,11 @@ func readAddress(m *models.Mirror) { | |||
func remoteAddress(repoPath string) (string, error) { | |||
var cmd *git.Command | |||
binVersion, err := git.BinVersion() | |||
err := git.LoadGitVersion() | |||
if err != nil { | |||
return "", err | |||
} | |||
if version.Compare(binVersion, "2.7", ">=") { | |||
if git.CheckGitVersionConstraint(">= 2.7") == nil { | |||
cmd = git.NewCommand("remote", "get-url", "origin") | |||
} else { | |||
cmd = git.NewCommand("config", "--get", "remote.origin.url") | |||
@@ -25,8 +25,6 @@ import ( | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/modules/timeutil" | |||
issue_service "code.gitea.io/gitea/services/issue" | |||
"github.com/mcuadros/go-version" | |||
) | |||
// Merge merges pull request to base repository. | |||
@@ -113,9 +111,9 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor | |||
// rawMerge perform the merge operation without changing any pull information in database | |||
func rawMerge(pr *models.PullRequest, doer *models.User, mergeStyle models.MergeStyle, message string) (string, error) { | |||
binVersion, err := git.BinVersion() | |||
err := git.LoadGitVersion() | |||
if err != nil { | |||
log.Error("git.BinVersion: %v", err) | |||
log.Error("git.LoadGitVersion: %v", err) | |||
return "", fmt.Errorf("Unable to get git version: %v", err) | |||
} | |||
@@ -157,7 +155,7 @@ func rawMerge(pr *models.PullRequest, doer *models.User, mergeStyle models.Merge | |||
} | |||
var gitConfigCommand func() *git.Command | |||
if version.Compare(binVersion, "1.8.0", ">=") { | |||
if git.CheckGitVersionConstraint(">= 1.8.0") == nil { | |||
gitConfigCommand = func() *git.Command { | |||
return git.NewCommand("config", "--local") | |||
} | |||
@@ -213,11 +211,11 @@ func rawMerge(pr *models.PullRequest, doer *models.User, mergeStyle models.Merge | |||
// Determine if we should sign | |||
signArg := "" | |||
if version.Compare(binVersion, "1.7.9", ">=") { | |||
if git.CheckGitVersionConstraint(">= 1.7.9") == nil { | |||
sign, keyID, _ := pr.SignMerge(doer, tmpBasePath, "HEAD", trackingBranch) | |||
if sign { | |||
signArg = "-S" + keyID | |||
} else if version.Compare(binVersion, "2.0.0", ">=") { | |||
} else if git.CheckGitVersionConstraint(">= 2.0.0") == nil { | |||
signArg = "--no-gpg-sign" | |||
} | |||
} | |||
@@ -0,0 +1,354 @@ | |||
Mozilla Public License, version 2.0 | |||
1. Definitions | |||
1.1. “Contributor” | |||
means each individual or legal entity that creates, contributes to the | |||
creation of, or owns Covered Software. | |||
1.2. “Contributor Version” | |||
means the combination of the Contributions of others (if any) used by a | |||
Contributor and that particular Contributor’s Contribution. | |||
1.3. “Contribution” | |||
means Covered Software of a particular Contributor. | |||
1.4. “Covered Software” | |||
means Source Code Form to which the initial Contributor has attached the | |||
notice in Exhibit A, the Executable Form of such Source Code Form, and | |||
Modifications of such Source Code Form, in each case including portions | |||
thereof. | |||
1.5. “Incompatible With Secondary Licenses” | |||
means | |||
a. that the initial Contributor has attached the notice described in | |||
Exhibit B to the Covered Software; or | |||
b. that the Covered Software was made available under the terms of version | |||
1.1 or earlier of the License, but not also under the terms of a | |||
Secondary License. | |||
1.6. “Executable Form” | |||
means any form of the work other than Source Code Form. | |||
1.7. “Larger Work” | |||
means a work that combines Covered Software with other material, in a separate | |||
file or files, that is not Covered Software. | |||
1.8. “License” | |||
means this document. | |||
1.9. “Licensable” | |||
means having the right to grant, to the maximum extent possible, whether at the | |||
time of the initial grant or subsequently, any and all of the rights conveyed by | |||
this License. | |||
1.10. “Modifications” | |||
means any of the following: | |||
a. any file in Source Code Form that results from an addition to, deletion | |||
from, or modification of the contents of Covered Software; or | |||
b. any new file in Source Code Form that contains any Covered Software. | |||
1.11. “Patent Claims” of a Contributor | |||
means any patent claim(s), including without limitation, method, process, | |||
and apparatus claims, in any patent Licensable by such Contributor that | |||
would be infringed, but for the grant of the License, by the making, | |||
using, selling, offering for sale, having made, import, or transfer of | |||
either its Contributions or its Contributor Version. | |||
1.12. “Secondary License” | |||
means either the GNU General Public License, Version 2.0, the GNU Lesser | |||
General Public License, Version 2.1, the GNU Affero General Public | |||
License, Version 3.0, or any later versions of those licenses. | |||
1.13. “Source Code Form” | |||
means the form of the work preferred for making modifications. | |||
1.14. “You” (or “Your”) | |||
means an individual or a legal entity exercising rights under this | |||
License. For legal entities, “You” includes any entity that controls, is | |||
controlled by, or is under common control with You. For purposes of this | |||
definition, “control” means (a) the power, direct or indirect, to cause | |||
the direction or management of such entity, whether by contract or | |||
otherwise, or (b) ownership of more than fifty percent (50%) of the | |||
outstanding shares or beneficial ownership of such entity. | |||
2. License Grants and Conditions | |||
2.1. Grants | |||
Each Contributor hereby grants You a world-wide, royalty-free, | |||
non-exclusive license: | |||
a. under intellectual property rights (other than patent or trademark) | |||
Licensable by such Contributor to use, reproduce, make available, | |||
modify, display, perform, distribute, and otherwise exploit its | |||
Contributions, either on an unmodified basis, with Modifications, or as | |||
part of a Larger Work; and | |||
b. under Patent Claims of such Contributor to make, use, sell, offer for | |||
sale, have made, import, and otherwise transfer either its Contributions | |||
or its Contributor Version. | |||
2.2. Effective Date | |||
The licenses granted in Section 2.1 with respect to any Contribution become | |||
effective for each Contribution on the date the Contributor first distributes | |||
such Contribution. | |||
2.3. Limitations on Grant Scope | |||
The licenses granted in this Section 2 are the only rights granted under this | |||
License. No additional rights or licenses will be implied from the distribution | |||
or licensing of Covered Software under this License. Notwithstanding Section | |||
2.1(b) above, no patent license is granted by a Contributor: | |||
a. for any code that a Contributor has removed from Covered Software; or | |||
b. for infringements caused by: (i) Your and any other third party’s | |||
modifications of Covered Software, or (ii) the combination of its | |||
Contributions with other software (except as part of its Contributor | |||
Version); or | |||
c. under Patent Claims infringed by Covered Software in the absence of its | |||
Contributions. | |||
This License does not grant any rights in the trademarks, service marks, or | |||
logos of any Contributor (except as may be necessary to comply with the | |||
notice requirements in Section 3.4). | |||
2.4. Subsequent Licenses | |||
No Contributor makes additional grants as a result of Your choice to | |||
distribute the Covered Software under a subsequent version of this License | |||
(see Section 10.2) or under the terms of a Secondary License (if permitted | |||
under the terms of Section 3.3). | |||
2.5. Representation | |||
Each Contributor represents that the Contributor believes its Contributions | |||
are its original creation(s) or it has sufficient rights to grant the | |||
rights to its Contributions conveyed by this License. | |||
2.6. Fair Use | |||
This License is not intended to limit any rights You have under applicable | |||
copyright doctrines of fair use, fair dealing, or other equivalents. | |||
2.7. Conditions | |||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in | |||
Section 2.1. | |||
3. Responsibilities | |||
3.1. Distribution of Source Form | |||
All distribution of Covered Software in Source Code Form, including any | |||
Modifications that You create or to which You contribute, must be under the | |||
terms of this License. You must inform recipients that the Source Code Form | |||
of the Covered Software is governed by the terms of this License, and how | |||
they can obtain a copy of this License. You may not attempt to alter or | |||
restrict the recipients’ rights in the Source Code Form. | |||
3.2. Distribution of Executable Form | |||
If You distribute Covered Software in Executable Form then: | |||
a. such Covered Software must also be made available in Source Code Form, | |||
as described in Section 3.1, and You must inform recipients of the | |||
Executable Form how they can obtain a copy of such Source Code Form by | |||
reasonable means in a timely manner, at a charge no more than the cost | |||
of distribution to the recipient; and | |||
b. You may distribute such Executable Form under the terms of this License, | |||
or sublicense it under different terms, provided that the license for | |||
the Executable Form does not attempt to limit or alter the recipients’ | |||
rights in the Source Code Form under this License. | |||
3.3. Distribution of a Larger Work | |||
You may create and distribute a Larger Work under terms of Your choice, | |||
provided that You also comply with the requirements of this License for the | |||
Covered Software. If the Larger Work is a combination of Covered Software | |||
with a work governed by one or more Secondary Licenses, and the Covered | |||
Software is not Incompatible With Secondary Licenses, this License permits | |||
You to additionally distribute such Covered Software under the terms of | |||
such Secondary License(s), so that the recipient of the Larger Work may, at | |||
their option, further distribute the Covered Software under the terms of | |||
either this License or such Secondary License(s). | |||
3.4. Notices | |||
You may not remove or alter the substance of any license notices (including | |||
copyright notices, patent notices, disclaimers of warranty, or limitations | |||
of liability) contained within the Source Code Form of the Covered | |||
Software, except that You may alter any license notices to the extent | |||
required to remedy known factual inaccuracies. | |||
3.5. Application of Additional Terms | |||
You may choose to offer, and to charge a fee for, warranty, support, | |||
indemnity or liability obligations to one or more recipients of Covered | |||
Software. However, You may do so only on Your own behalf, and not on behalf | |||
of any Contributor. You must make it absolutely clear that any such | |||
warranty, support, indemnity, or liability obligation is offered by You | |||
alone, and You hereby agree to indemnify every Contributor for any | |||
liability incurred by such Contributor as a result of warranty, support, | |||
indemnity or liability terms You offer. You may include additional | |||
disclaimers of warranty and limitations of liability specific to any | |||
jurisdiction. | |||
4. Inability to Comply Due to Statute or Regulation | |||
If it is impossible for You to comply with any of the terms of this License | |||
with respect to some or all of the Covered Software due to statute, judicial | |||
order, or regulation then You must: (a) comply with the terms of this License | |||
to the maximum extent possible; and (b) describe the limitations and the code | |||
they affect. Such description must be placed in a text file included with all | |||
distributions of the Covered Software under this License. Except to the | |||
extent prohibited by statute or regulation, such description must be | |||
sufficiently detailed for a recipient of ordinary skill to be able to | |||
understand it. | |||
5. Termination | |||
5.1. The rights granted under this License will terminate automatically if You | |||
fail to comply with any of its terms. However, if You become compliant, | |||
then the rights granted under this License from a particular Contributor | |||
are reinstated (a) provisionally, unless and until such Contributor | |||
explicitly and finally terminates Your grants, and (b) on an ongoing basis, | |||
if such Contributor fails to notify You of the non-compliance by some | |||
reasonable means prior to 60 days after You have come back into compliance. | |||
Moreover, Your grants from a particular Contributor are reinstated on an | |||
ongoing basis if such Contributor notifies You of the non-compliance by | |||
some reasonable means, this is the first time You have received notice of | |||
non-compliance with this License from such Contributor, and You become | |||
compliant prior to 30 days after Your receipt of the notice. | |||
5.2. If You initiate litigation against any entity by asserting a patent | |||
infringement claim (excluding declaratory judgment actions, counter-claims, | |||
and cross-claims) alleging that a Contributor Version directly or | |||
indirectly infringes any patent, then the rights granted to You by any and | |||
all Contributors for the Covered Software under Section 2.1 of this License | |||
shall terminate. | |||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user | |||
license agreements (excluding distributors and resellers) which have been | |||
validly granted by You or Your distributors under this License prior to | |||
termination shall survive termination. | |||
6. Disclaimer of Warranty | |||
Covered Software is provided under this License on an “as is” basis, without | |||
warranty of any kind, either expressed, implied, or statutory, including, | |||
without limitation, warranties that the Covered Software is free of defects, | |||
merchantable, fit for a particular purpose or non-infringing. The entire | |||
risk as to the quality and performance of the Covered Software is with You. | |||
Should any Covered Software prove defective in any respect, You (not any | |||
Contributor) assume the cost of any necessary servicing, repair, or | |||
correction. This disclaimer of warranty constitutes an essential part of this | |||
License. No use of any Covered Software is authorized under this License | |||
except under this disclaimer. | |||
7. Limitation of Liability | |||
Under no circumstances and under no legal theory, whether tort (including | |||
negligence), contract, or otherwise, shall any Contributor, or anyone who | |||
distributes Covered Software as permitted above, be liable to You for any | |||
direct, indirect, special, incidental, or consequential damages of any | |||
character including, without limitation, damages for lost profits, loss of | |||
goodwill, work stoppage, computer failure or malfunction, or any and all | |||
other commercial damages or losses, even if such party shall have been | |||
informed of the possibility of such damages. This limitation of liability | |||
shall not apply to liability for death or personal injury resulting from such | |||
party’s negligence to the extent applicable law prohibits such limitation. | |||
Some jurisdictions do not allow the exclusion or limitation of incidental or | |||
consequential damages, so this exclusion and limitation may not apply to You. | |||
8. Litigation | |||
Any litigation relating to this License may be brought only in the courts of | |||
a jurisdiction where the defendant maintains its principal place of business | |||
and such litigation shall be governed by laws of that jurisdiction, without | |||
reference to its conflict-of-law provisions. Nothing in this Section shall | |||
prevent a party’s ability to bring cross-claims or counter-claims. | |||
9. Miscellaneous | |||
This License represents the complete agreement concerning the subject matter | |||
hereof. If any provision of this License is held to be unenforceable, such | |||
provision shall be reformed only to the extent necessary to make it | |||
enforceable. Any law or regulation which provides that the language of a | |||
contract shall be construed against the drafter shall not be used to construe | |||
this License against a Contributor. | |||
10. Versions of the License | |||
10.1. New Versions | |||
Mozilla Foundation is the license steward. Except as provided in Section | |||
10.3, no one other than the license steward has the right to modify or | |||
publish new versions of this License. Each version will be given a | |||
distinguishing version number. | |||
10.2. Effect of New Versions | |||
You may distribute the Covered Software under the terms of the version of | |||
the License under which You originally received the Covered Software, or | |||
under the terms of any subsequent version published by the license | |||
steward. | |||
10.3. Modified Versions | |||
If you create software not governed by this License, and you want to | |||
create a new license for such software, you may create and use a modified | |||
version of this License if you rename the license and remove any | |||
references to the name of the license steward (except to note that such | |||
modified license differs from this License). | |||
10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses | |||
If You choose to distribute Source Code Form that is Incompatible With | |||
Secondary Licenses under the terms of this version of the License, the | |||
notice described in Exhibit B of this License must be attached. | |||
Exhibit A - Source Code Form License Notice | |||
This Source Code Form is subject to the | |||
terms of the Mozilla Public License, v. | |||
2.0. If a copy of the MPL was not | |||
distributed with this file, You can | |||
obtain one at | |||
http://mozilla.org/MPL/2.0/. | |||
If it is not possible or desirable to put the notice in a particular file, then | |||
You may include the notice in a location (such as a LICENSE file in a relevant | |||
directory) where a recipient would be likely to look for such a notice. | |||
You may add additional accurate notices of copyright ownership. | |||
Exhibit B - “Incompatible With Secondary Licenses” Notice | |||
This Source Code Form is “Incompatible | |||
With Secondary Licenses”, as defined by | |||
the Mozilla Public License, v. 2.0. | |||
@@ -0,0 +1,66 @@ | |||
# Versioning Library for Go | |||
[](https://circleci.com/gh/hashicorp/go-version/tree/master) | |||
[](https://godoc.org/github.com/hashicorp/go-version) | |||
go-version is a library for parsing versions and version constraints, | |||
and verifying versions against a set of constraints. go-version | |||
can sort a collection of versions properly, handles prerelease/beta | |||
versions, can increment versions, etc. | |||
Versions used with go-version must follow [SemVer](http://semver.org/). | |||
## Installation and Usage | |||
Package documentation can be found on | |||
[GoDoc](http://godoc.org/github.com/hashicorp/go-version). | |||
Installation can be done with a normal `go get`: | |||
``` | |||
$ go get github.com/hashicorp/go-version | |||
``` | |||
#### Version Parsing and Comparison | |||
```go | |||
v1, err := version.NewVersion("1.2") | |||
v2, err := version.NewVersion("1.5+metadata") | |||
// Comparison example. There is also GreaterThan, Equal, and just | |||
// a simple Compare that returns an int allowing easy >=, <=, etc. | |||
if v1.LessThan(v2) { | |||
fmt.Printf("%s is less than %s", v1, v2) | |||
} | |||
``` | |||
#### Version Constraints | |||
```go | |||
v1, err := version.NewVersion("1.2") | |||
// Constraints example. | |||
constraints, err := version.NewConstraint(">= 1.0, < 1.4") | |||
if constraints.Check(v1) { | |||
fmt.Printf("%s satisfies constraints %s", v1, constraints) | |||
} | |||
``` | |||
#### Version Sorting | |||
```go | |||
versionsRaw := []string{"1.1", "0.7.1", "1.4-beta", "1.4", "2"} | |||
versions := make([]*version.Version, len(versionsRaw)) | |||
for i, raw := range versionsRaw { | |||
v, _ := version.NewVersion(raw) | |||
versions[i] = v | |||
} | |||
// After this, the versions are properly sorted | |||
sort.Sort(version.Collection(versions)) | |||
``` | |||
## Issues and Contributing | |||
If you find an issue with this library, please report an issue. If you'd | |||
like, we welcome any contributions. Fork this library and submit a pull | |||
request. |
@@ -0,0 +1,204 @@ | |||
package version | |||
import ( | |||
"fmt" | |||
"reflect" | |||
"regexp" | |||
"strings" | |||
) | |||
// Constraint represents a single constraint for a version, such as | |||
// ">= 1.0". | |||
type Constraint struct { | |||
f constraintFunc | |||
check *Version | |||
original string | |||
} | |||
// Constraints is a slice of constraints. We make a custom type so that | |||
// we can add methods to it. | |||
type Constraints []*Constraint | |||
type constraintFunc func(v, c *Version) bool | |||
var constraintOperators map[string]constraintFunc | |||
var constraintRegexp *regexp.Regexp | |||
func init() { | |||
constraintOperators = map[string]constraintFunc{ | |||
"": constraintEqual, | |||
"=": constraintEqual, | |||
"!=": constraintNotEqual, | |||
">": constraintGreaterThan, | |||
"<": constraintLessThan, | |||
">=": constraintGreaterThanEqual, | |||
"<=": constraintLessThanEqual, | |||
"~>": constraintPessimistic, | |||
} | |||
ops := make([]string, 0, len(constraintOperators)) | |||
for k := range constraintOperators { | |||
ops = append(ops, regexp.QuoteMeta(k)) | |||
} | |||
constraintRegexp = regexp.MustCompile(fmt.Sprintf( | |||
`^\s*(%s)\s*(%s)\s*$`, | |||
strings.Join(ops, "|"), | |||
VersionRegexpRaw)) | |||
} | |||
// NewConstraint will parse one or more constraints from the given | |||
// constraint string. The string must be a comma-separated list of | |||
// constraints. | |||
func NewConstraint(v string) (Constraints, error) { | |||
vs := strings.Split(v, ",") | |||
result := make([]*Constraint, len(vs)) | |||
for i, single := range vs { | |||
c, err := parseSingle(single) | |||
if err != nil { | |||
return nil, err | |||
} | |||
result[i] = c | |||
} | |||
return Constraints(result), nil | |||
} | |||
// Check tests if a version satisfies all the constraints. | |||
func (cs Constraints) Check(v *Version) bool { | |||
for _, c := range cs { | |||
if !c.Check(v) { | |||
return false | |||
} | |||
} | |||
return true | |||
} | |||
// Returns the string format of the constraints | |||
func (cs Constraints) String() string { | |||
csStr := make([]string, len(cs)) | |||
for i, c := range cs { | |||
csStr[i] = c.String() | |||
} | |||
return strings.Join(csStr, ",") | |||
} | |||
// Check tests if a constraint is validated by the given version. | |||
func (c *Constraint) Check(v *Version) bool { | |||
return c.f(v, c.check) | |||
} | |||
func (c *Constraint) String() string { | |||
return c.original | |||
} | |||
func parseSingle(v string) (*Constraint, error) { | |||
matches := constraintRegexp.FindStringSubmatch(v) | |||
if matches == nil { | |||
return nil, fmt.Errorf("Malformed constraint: %s", v) | |||
} | |||
check, err := NewVersion(matches[2]) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return &Constraint{ | |||
f: constraintOperators[matches[1]], | |||
check: check, | |||
original: v, | |||
}, nil | |||
} | |||
func prereleaseCheck(v, c *Version) bool { | |||
switch vPre, cPre := v.Prerelease() != "", c.Prerelease() != ""; { | |||
case cPre && vPre: | |||
// A constraint with a pre-release can only match a pre-release version | |||
// with the same base segments. | |||
return reflect.DeepEqual(c.Segments64(), v.Segments64()) | |||
case !cPre && vPre: | |||
// A constraint without a pre-release can only match a version without a | |||
// pre-release. | |||
return false | |||
case cPre && !vPre: | |||
// OK, except with the pessimistic operator | |||
case !cPre && !vPre: | |||
// OK | |||
} | |||
return true | |||
} | |||
//------------------------------------------------------------------- | |||
// Constraint functions | |||
//------------------------------------------------------------------- | |||
func constraintEqual(v, c *Version) bool { | |||
return v.Equal(c) | |||
} | |||
func constraintNotEqual(v, c *Version) bool { | |||
return !v.Equal(c) | |||
} | |||
func constraintGreaterThan(v, c *Version) bool { | |||
return prereleaseCheck(v, c) && v.Compare(c) == 1 | |||
} | |||
func constraintLessThan(v, c *Version) bool { | |||
return prereleaseCheck(v, c) && v.Compare(c) == -1 | |||
} | |||
func constraintGreaterThanEqual(v, c *Version) bool { | |||
return prereleaseCheck(v, c) && v.Compare(c) >= 0 | |||
} | |||
func constraintLessThanEqual(v, c *Version) bool { | |||
return prereleaseCheck(v, c) && v.Compare(c) <= 0 | |||
} | |||
func constraintPessimistic(v, c *Version) bool { | |||
// Using a pessimistic constraint with a pre-release, restricts versions to pre-releases | |||
if !prereleaseCheck(v, c) || (c.Prerelease() != "" && v.Prerelease() == "") { | |||
return false | |||
} | |||
// If the version being checked is naturally less than the constraint, then there | |||
// is no way for the version to be valid against the constraint | |||
if v.LessThan(c) { | |||
return false | |||
} | |||
// We'll use this more than once, so grab the length now so it's a little cleaner | |||
// to write the later checks | |||
cs := len(c.segments) | |||
// If the version being checked has less specificity than the constraint, then there | |||
// is no way for the version to be valid against the constraint | |||
if cs > len(v.segments) { | |||
return false | |||
} | |||
// Check the segments in the constraint against those in the version. If the version | |||
// being checked, at any point, does not have the same values in each index of the | |||
// constraints segments, then it cannot be valid against the constraint. | |||
for i := 0; i < c.si-1; i++ { | |||
if v.segments[i] != c.segments[i] { | |||
return false | |||
} | |||
} | |||
// Check the last part of the segment in the constraint. If the version segment at | |||
// this index is less than the constraints segment at this index, then it cannot | |||
// be valid against the constraint | |||
if c.segments[cs-1] > v.segments[cs-1] { | |||
return false | |||
} | |||
// If nothing has rejected the version by now, it's valid | |||
return true | |||
} |
@@ -0,0 +1 @@ | |||
module github.com/hashicorp/go-version |
@@ -0,0 +1,384 @@ | |||
package version | |||
import ( | |||
"bytes" | |||
"fmt" | |||
"reflect" | |||
"regexp" | |||
"strconv" | |||
"strings" | |||
) | |||
// The compiled regular expression used to test the validity of a version. | |||
var ( | |||
versionRegexp *regexp.Regexp | |||
semverRegexp *regexp.Regexp | |||
) | |||
// The raw regular expression string used for testing the validity | |||
// of a version. | |||
const ( | |||
VersionRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` + | |||
`(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-?([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` + | |||
`(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` + | |||
`?` | |||
// SemverRegexpRaw requires a separator between version and prerelease | |||
SemverRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` + | |||
`(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` + | |||
`(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` + | |||
`?` | |||
) | |||
// Version represents a single version. | |||
type Version struct { | |||
metadata string | |||
pre string | |||
segments []int64 | |||
si int | |||
original string | |||
} | |||
func init() { | |||
versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$") | |||
semverRegexp = regexp.MustCompile("^" + SemverRegexpRaw + "$") | |||
} | |||
// NewVersion parses the given version and returns a new | |||
// Version. | |||
func NewVersion(v string) (*Version, error) { | |||
return newVersion(v, versionRegexp) | |||
} | |||
// NewSemver parses the given version and returns a new | |||
// Version that adheres strictly to SemVer specs | |||
// https://semver.org/ | |||
func NewSemver(v string) (*Version, error) { | |||
return newVersion(v, semverRegexp) | |||
} | |||
func newVersion(v string, pattern *regexp.Regexp) (*Version, error) { | |||
matches := pattern.FindStringSubmatch(v) | |||
if matches == nil { | |||
return nil, fmt.Errorf("Malformed version: %s", v) | |||
} | |||
segmentsStr := strings.Split(matches[1], ".") | |||
segments := make([]int64, len(segmentsStr)) | |||
si := 0 | |||
for i, str := range segmentsStr { | |||
val, err := strconv.ParseInt(str, 10, 64) | |||
if err != nil { | |||
return nil, fmt.Errorf( | |||
"Error parsing version: %s", err) | |||
} | |||
segments[i] = int64(val) | |||
si++ | |||
} | |||
// Even though we could support more than three segments, if we | |||
// got less than three, pad it with 0s. This is to cover the basic | |||
// default usecase of semver, which is MAJOR.MINOR.PATCH at the minimum | |||
for i := len(segments); i < 3; i++ { | |||
segments = append(segments, 0) | |||
} | |||
pre := matches[7] | |||
if pre == "" { | |||
pre = matches[4] | |||
} | |||
return &Version{ | |||
metadata: matches[10], | |||
pre: pre, | |||
segments: segments, | |||
si: si, | |||
original: v, | |||
}, nil | |||
} | |||
// Must is a helper that wraps a call to a function returning (*Version, error) | |||
// and panics if error is non-nil. | |||
func Must(v *Version, err error) *Version { | |||
if err != nil { | |||
panic(err) | |||
} | |||
return v | |||
} | |||
// Compare compares this version to another version. This | |||
// returns -1, 0, or 1 if this version is smaller, equal, | |||
// or larger than the other version, respectively. | |||
// | |||
// If you want boolean results, use the LessThan, Equal, | |||
// GreaterThan, GreaterThanOrEqual or LessThanOrEqual methods. | |||
func (v *Version) Compare(other *Version) int { | |||
// A quick, efficient equality check | |||
if v.String() == other.String() { | |||
return 0 | |||
} | |||
segmentsSelf := v.Segments64() | |||
segmentsOther := other.Segments64() | |||
// If the segments are the same, we must compare on prerelease info | |||
if reflect.DeepEqual(segmentsSelf, segmentsOther) { | |||
preSelf := v.Prerelease() | |||
preOther := other.Prerelease() | |||
if preSelf == "" && preOther == "" { | |||
return 0 | |||
} | |||
if preSelf == "" { | |||
return 1 | |||
} | |||
if preOther == "" { | |||
return -1 | |||
} | |||
return comparePrereleases(preSelf, preOther) | |||
} | |||
// Get the highest specificity (hS), or if they're equal, just use segmentSelf length | |||
lenSelf := len(segmentsSelf) | |||
lenOther := len(segmentsOther) | |||
hS := lenSelf | |||
if lenSelf < lenOther { | |||
hS = lenOther | |||
} | |||
// Compare the segments | |||
// Because a constraint could have more/less specificity than the version it's | |||
// checking, we need to account for a lopsided or jagged comparison | |||
for i := 0; i < hS; i++ { | |||
if i > lenSelf-1 { | |||
// This means Self had the lower specificity | |||
// Check to see if the remaining segments in Other are all zeros | |||
if !allZero(segmentsOther[i:]) { | |||
// if not, it means that Other has to be greater than Self | |||
return -1 | |||
} | |||
break | |||
} else if i > lenOther-1 { | |||
// this means Other had the lower specificity | |||
// Check to see if the remaining segments in Self are all zeros - | |||
if !allZero(segmentsSelf[i:]) { | |||
//if not, it means that Self has to be greater than Other | |||
return 1 | |||
} | |||
break | |||
} | |||
lhs := segmentsSelf[i] | |||
rhs := segmentsOther[i] | |||
if lhs == rhs { | |||
continue | |||
} else if lhs < rhs { | |||
return -1 | |||
} | |||
// Otherwis, rhs was > lhs, they're not equal | |||
return 1 | |||
} | |||
// if we got this far, they're equal | |||
return 0 | |||
} | |||
func allZero(segs []int64) bool { | |||
for _, s := range segs { | |||
if s != 0 { | |||
return false | |||
} | |||
} | |||
return true | |||
} | |||
func comparePart(preSelf string, preOther string) int { | |||
if preSelf == preOther { | |||
return 0 | |||
} | |||
var selfInt int64 | |||
selfNumeric := true | |||
selfInt, err := strconv.ParseInt(preSelf, 10, 64) | |||
if err != nil { | |||
selfNumeric = false | |||
} | |||
var otherInt int64 | |||
otherNumeric := true | |||
otherInt, err = strconv.ParseInt(preOther, 10, 64) | |||
if err != nil { | |||
otherNumeric = false | |||
} | |||
// if a part is empty, we use the other to decide | |||
if preSelf == "" { | |||
if otherNumeric { | |||
return -1 | |||
} | |||
return 1 | |||
} | |||
if preOther == "" { | |||
if selfNumeric { | |||
return 1 | |||
} | |||
return -1 | |||
} | |||
if selfNumeric && !otherNumeric { | |||
return -1 | |||
} else if !selfNumeric && otherNumeric { | |||
return 1 | |||
} else if !selfNumeric && !otherNumeric && preSelf > preOther { | |||
return 1 | |||
} else if selfInt > otherInt { | |||
return 1 | |||
} | |||
return -1 | |||
} | |||
func comparePrereleases(v string, other string) int { | |||
// the same pre release! | |||
if v == other { | |||
return 0 | |||
} | |||
// split both pre releases for analyse their parts | |||
selfPreReleaseMeta := strings.Split(v, ".") | |||
otherPreReleaseMeta := strings.Split(other, ".") | |||
selfPreReleaseLen := len(selfPreReleaseMeta) | |||
otherPreReleaseLen := len(otherPreReleaseMeta) | |||
biggestLen := otherPreReleaseLen | |||
if selfPreReleaseLen > otherPreReleaseLen { | |||
biggestLen = selfPreReleaseLen | |||
} | |||
// loop for parts to find the first difference | |||
for i := 0; i < biggestLen; i = i + 1 { | |||
partSelfPre := "" | |||
if i < selfPreReleaseLen { | |||
partSelfPre = selfPreReleaseMeta[i] | |||
} | |||
partOtherPre := "" | |||
if i < otherPreReleaseLen { | |||
partOtherPre = otherPreReleaseMeta[i] | |||
} | |||
compare := comparePart(partSelfPre, partOtherPre) | |||
// if parts are equals, continue the loop | |||
if compare != 0 { | |||
return compare | |||
} | |||
} | |||
return 0 | |||
} | |||
// Equal tests if two versions are equal. | |||
func (v *Version) Equal(o *Version) bool { | |||
if v == nil || o == nil { | |||
return v == o | |||
} | |||
return v.Compare(o) == 0 | |||
} | |||
// GreaterThan tests if this version is greater than another version. | |||
func (v *Version) GreaterThan(o *Version) bool { | |||
return v.Compare(o) > 0 | |||
} | |||
// GreaterThanOrEqual tests if this version is greater than or equal to another version. | |||
func (v *Version) GreaterThanOrEqual(o *Version) bool { | |||
return v.Compare(o) >= 0 | |||
} | |||
// LessThan tests if this version is less than another version. | |||
func (v *Version) LessThan(o *Version) bool { | |||
return v.Compare(o) < 0 | |||
} | |||
// LessThanOrEqual tests if this version is less than or equal to another version. | |||
func (v *Version) LessThanOrEqual(o *Version) bool { | |||
return v.Compare(o) <= 0 | |||
} | |||
// Metadata returns any metadata that was part of the version | |||
// string. | |||
// | |||
// Metadata is anything that comes after the "+" in the version. | |||
// For example, with "1.2.3+beta", the metadata is "beta". | |||
func (v *Version) Metadata() string { | |||
return v.metadata | |||
} | |||
// Prerelease returns any prerelease data that is part of the version, | |||
// or blank if there is no prerelease data. | |||
// | |||
// Prerelease information is anything that comes after the "-" in the | |||
// version (but before any metadata). For example, with "1.2.3-beta", | |||
// the prerelease information is "beta". | |||
func (v *Version) Prerelease() string { | |||
return v.pre | |||
} | |||
// Segments returns the numeric segments of the version as a slice of ints. | |||
// | |||
// This excludes any metadata or pre-release information. For example, | |||
// for a version "1.2.3-beta", segments will return a slice of | |||
// 1, 2, 3. | |||
func (v *Version) Segments() []int { | |||
segmentSlice := make([]int, len(v.segments)) | |||
for i, v := range v.segments { | |||
segmentSlice[i] = int(v) | |||
} | |||
return segmentSlice | |||
} | |||
// Segments64 returns the numeric segments of the version as a slice of int64s. | |||
// | |||
// This excludes any metadata or pre-release information. For example, | |||
// for a version "1.2.3-beta", segments will return a slice of | |||
// 1, 2, 3. | |||
func (v *Version) Segments64() []int64 { | |||
result := make([]int64, len(v.segments)) | |||
copy(result, v.segments) | |||
return result | |||
} | |||
// String returns the full version string included pre-release | |||
// and metadata information. | |||
// | |||
// This value is rebuilt according to the parsed segments and other | |||
// information. Therefore, ambiguities in the version string such as | |||
// prefixed zeroes (1.04.0 => 1.4.0), `v` prefix (v1.0.0 => 1.0.0), and | |||
// missing parts (1.0 => 1.0.0) will be made into a canonicalized form | |||
// as shown in the parenthesized examples. | |||
func (v *Version) String() string { | |||
var buf bytes.Buffer | |||
fmtParts := make([]string, len(v.segments)) | |||
for i, s := range v.segments { | |||
// We can ignore err here since we've pre-parsed the values in segments | |||
str := strconv.FormatInt(s, 10) | |||
fmtParts[i] = str | |||
} | |||
fmt.Fprintf(&buf, strings.Join(fmtParts, ".")) | |||
if v.pre != "" { | |||
fmt.Fprintf(&buf, "-%s", v.pre) | |||
} | |||
if v.metadata != "" { | |||
fmt.Fprintf(&buf, "+%s", v.metadata) | |||
} | |||
return buf.String() | |||
} | |||
// Original returns the original parsed version as-is, including any | |||
// potential whitespace, `v` prefix, etc. | |||
func (v *Version) Original() string { | |||
return v.original | |||
} |
@@ -0,0 +1,17 @@ | |||
package version | |||
// Collection is a type that implements the sort.Interface interface | |||
// so that versions can be sorted. | |||
type Collection []*Version | |||
func (v Collection) Len() int { | |||
return len(v) | |||
} | |||
func (v Collection) Less(i, j int) bool { | |||
return v[i].LessThan(v[j]) | |||
} | |||
func (v Collection) Swap(i, j int) { | |||
v[i], v[j] = v[j], v[i] | |||
} |
@@ -1,22 +0,0 @@ | |||
# Compiled Object files, Static and Dynamic libs (Shared Objects) | |||
*.o | |||
*.a | |||
*.so | |||
# Folders | |||
_obj | |||
_test | |||
# Architecture specific extensions/prefixes | |||
*.[568vq] | |||
[568vq].out | |||
*.cgo1.go | |||
*.cgo2.c | |||
_cgo_defun.c | |||
_cgo_gotypes.go | |||
_cgo_export.* | |||
_testmain.go | |||
*.exe |
@@ -1 +0,0 @@ | |||
language: go |
@@ -1,19 +0,0 @@ | |||
Copyright (c) 2013 Máximo Cuadros | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is furnished | |||
to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
THE SOFTWARE. |
@@ -1,80 +0,0 @@ | |||
go-version [](https://travis-ci.org/mcuadros/go-version) [](http://godoc.org/github.com/mcuadros/go-version) | |||
============================== | |||
Version normalizer and comparison library for go, heavy based on PHP version_compare function and Version comparsion libs from [Composer](https://github.com/composer/composer) PHP project | |||
Installation | |||
------------ | |||
The recommended way to install go-version | |||
``` | |||
go get github.com/mcuadros/go-version | |||
``` | |||
Examples | |||
-------- | |||
How import the package | |||
```go | |||
import ( | |||
"github.com/mcuadros/go-version" | |||
) | |||
``` | |||
`version.Normalize()`: Normalizes a version string to be able to perform comparisons on it | |||
```go | |||
version.Normalize("10.4.13-b") | |||
//Returns: 10.4.13.0-beta | |||
``` | |||
`version.CompareSimple()`: Compares two normalizated version number strings | |||
```go | |||
version.CompareSimple("1.2", "1.0.1") | |||
//Returns: 1 | |||
version.CompareSimple("1.0rc1", "1.0") | |||
//Returns: -1 | |||
``` | |||
`version.Compare()`: Compares two normalizated version number strings, for a particular relationship | |||
```go | |||
version.Compare("1.0-dev", "1.0", "<") | |||
//Returns: true | |||
version.Compare("1.0rc1", "1.0", ">=") | |||
//Returns: false | |||
version.Compare("2.3.4", "v3.1.2", "<") | |||
//Returns: true | |||
``` | |||
`version.ConstrainGroup.Match()`: Match a given version againts a group of constrains, read about constraint string format at [Composer documentation](http://getcomposer.org/doc/01-basic-usage.md#package-versions) | |||
```go | |||
c := version.NewConstrainGroupFromString(">2.0,<=3.0") | |||
c.Match("2.5.0beta") | |||
//Returns: true | |||
c := version.NewConstrainGroupFromString("~1.2.3") | |||
c.Match("1.2.3.5") | |||
//Returns: true | |||
``` | |||
`version.Sort()`: Sorts a string slice of version number strings using version.CompareSimple() | |||
```go | |||
version.Sort([]string{"1.10-dev", "1.0rc1", "1.0", "1.0-dev"}) | |||
//Returns []string{"1.0-dev", "1.0rc1", "1.0", "1.10-dev"} | |||
``` | |||
License | |||
------- | |||
MIT, see [LICENSE](LICENSE) |
@@ -1,173 +0,0 @@ | |||
package version | |||
import ( | |||
"regexp" | |||
"strconv" | |||
"strings" | |||
) | |||
var regexpSigns = regexp.MustCompile(`[_\-+]`) | |||
var regexpDotBeforeDigit = regexp.MustCompile(`([^.\d]+)`) | |||
var regexpMultipleDots = regexp.MustCompile(`\.{2,}`) | |||
var specialForms = map[string]int{ | |||
"SNAPSHOT": -7, | |||
"snapshot": -7, | |||
"dev": -6, | |||
"alpha": -5, | |||
"a": -5, | |||
"beta": -4, | |||
"b": -4, | |||
"RC": -3, | |||
"rc": -3, | |||
"#": -2, | |||
"p": 1, | |||
"pl": 1, | |||
} | |||
var unknownForm int = -7 | |||
// Compares two version number strings, for a particular relationship | |||
// | |||
// Usage | |||
// version.Compare("2.3.4", "v3.1.2", "<") | |||
// Returns: true | |||
// | |||
// version.Compare("1.0rc1", "1.0", ">=") | |||
// Returns: false | |||
func Compare(version1, version2, operator string) bool { | |||
version1N := Normalize(version1) | |||
version2N := Normalize(version2) | |||
return CompareNormalized(version1N, version2N, operator) | |||
} | |||
// Compares two normalizated version number strings, for a particular relationship | |||
// | |||
// The function first replaces _, - and + with a dot . in the version strings | |||
// and also inserts dots . before and after any non number so that for example | |||
// '4.3.2RC1' becomes '4.3.2.RC.1'. | |||
// | |||
// Then it splits the results like if you were using Split(version, '.'). | |||
// Then it compares the parts starting from left to right. If a part contains | |||
// special version strings these are handled in the following order: any string | |||
// not found in this list: | |||
// < dev < alpha = a < beta = b < RC = rc < # < pl = p. | |||
// | |||
// Usage | |||
// version.CompareNormalized("1.0-dev", "1.0", "<") | |||
// Returns: true | |||
// | |||
// version.CompareNormalized("1.0rc1", "1.0", ">=") | |||
// Returns: false | |||
// | |||
// version.CompareNormalized("1.0", "1.0b1", "ge") | |||
// Returns: true | |||
func CompareNormalized(version1, version2, operator string) bool { | |||
compare := CompareSimple(version1, version2) | |||
switch { | |||
case operator == ">" || operator == "gt": | |||
return compare > 0 | |||
case operator == ">=" || operator == "ge": | |||
return compare >= 0 | |||
case operator == "<=" || operator == "le": | |||
return compare <= 0 | |||
case operator == "==" || operator == "=" || operator == "eq": | |||
return compare == 0 | |||
case operator == "<>" || operator == "!=" || operator == "ne": | |||
return compare != 0 | |||
case operator == "" || operator == "<" || operator == "lt": | |||
return compare < 0 | |||
} | |||
return false | |||
} | |||
// Compares two normalizated version number strings | |||
// | |||
// Just the same of CompareVersion but return a int result, 0 if both version | |||
// are equal, 1 if the right side is bigger and -1 if the right side is lower | |||
// | |||
// Usage | |||
// version.CompareSimple("1.2", "1.0.1") | |||
// Returns: 1 | |||
// | |||
// version.CompareSimple("1.0rc1", "1.0") | |||
// Returns: -1 | |||
func CompareSimple(version1, version2 string) int { | |||
var x, r, l int = 0, 0, 0 | |||
v1, v2 := prepVersion(version1), prepVersion(version2) | |||
len1, len2 := len(v1), len(v2) | |||
if len1 > len2 { | |||
x = len1 | |||
} else { | |||
x = len2 | |||
} | |||
for i := 0; i < x; i++ { | |||
if i < len1 && i < len2 { | |||
if v1[i] == v2[i] { | |||
continue | |||
} | |||
} | |||
r = 0 | |||
if i < len1 { | |||
r = numVersion(v1[i]) | |||
} | |||
l = 0 | |||
if i < len2 { | |||
l = numVersion(v2[i]) | |||
} | |||
if r < l { | |||
return -1 | |||
} else if r > l { | |||
return 1 | |||
} | |||
} | |||
return 0 | |||
} | |||
func prepVersion(version string) []string { | |||
if len(version) == 0 { | |||
return []string{""} | |||
} | |||
version = regexpSigns.ReplaceAllString(version, ".") | |||
version = regexpDotBeforeDigit.ReplaceAllString(version, ".$1.") | |||
version = regexpMultipleDots.ReplaceAllString(version, ".") | |||
return strings.Split(version, ".") | |||
} | |||
func numVersion(value string) int { | |||
if value == "" { | |||
return 0 | |||
} | |||
if number, err := strconv.Atoi(value); err == nil { | |||
return number | |||
} | |||
if special, ok := specialForms[value]; ok { | |||
return special | |||
} | |||
return unknownForm | |||
} | |||
func ValidSimpleVersionFormat(value string) bool { | |||
normalized := Normalize(value) | |||
for _, component := range prepVersion(normalized) { | |||
if numVersion(component) == unknownForm { | |||
return false | |||
} | |||
} | |||
return true | |||
} |
@@ -1,49 +0,0 @@ | |||
package version | |||
import ( | |||
"strings" | |||
) | |||
type Constraint struct { | |||
operator string | |||
version string | |||
} | |||
// Return a new Constrain and sets operator and version to compare | |||
func NewConstrain(operator, version string) *Constraint { | |||
constraint := new(Constraint) | |||
constraint.SetOperator(operator) | |||
constraint.SetVersion(version) | |||
return constraint | |||
} | |||
// Sets operator to compare | |||
func (self *Constraint) SetOperator(operator string) { | |||
self.operator = operator | |||
} | |||
// Get operator to compare | |||
func (self *Constraint) GetOperator() string { | |||
return self.operator | |||
} | |||
// Sets version to compare | |||
func (self *Constraint) SetVersion(version string) { | |||
self.version = version | |||
} | |||
// Get version to compare | |||
func (self *Constraint) GetVersion() string { | |||
return self.version | |||
} | |||
// Match a given version againts the constraint | |||
func (self *Constraint) Match(version string) bool { | |||
return Compare(version, self.version, self.operator) | |||
} | |||
// Return a string representation | |||
func (self *Constraint) String() string { | |||
return strings.Trim(self.operator+" "+self.version, " ") | |||
} |
@@ -1,6 +0,0 @@ | |||
/* | |||
Version normalizer and comparison library for go, heavy based on PHP | |||
version_compare function and Version comparsion libs from Composer | |||
(https://github.com/composer/composer) PHP project | |||
*/ | |||
package version |
@@ -1,300 +0,0 @@ | |||
package version | |||
import ( | |||
"regexp" | |||
"strconv" | |||
"strings" | |||
"sync" | |||
) | |||
type ConstraintGroup struct { | |||
constraints []*Constraint | |||
} | |||
// Return a new NewConstrainGroup | |||
func NewConstrainGroup() *ConstraintGroup { | |||
group := new(ConstraintGroup) | |||
return group | |||
} | |||
// Return a new NewConstrainGroup and create the constraints based on a string | |||
// | |||
// Version constraints can be specified in a few different ways: | |||
// | |||
// Exact version: You can specify the exact version of a package, for | |||
// example 1.0.2. | |||
// | |||
// Range: By using comparison operators you can specify ranges of valid versions. | |||
// Valid operators are >, >=, <, <=, !=. An example range would be >=1.0. You can | |||
// define multiple ranges, separated by a comma: >=1.0,<2.0. | |||
// | |||
// Wildcard: You can specify a pattern with a * wildcard. 1.0.* is the equivalent | |||
// of >=1.0,<1.1. | |||
// | |||
// Next Significant Release (Tilde Operator): The ~ operator is best explained by | |||
// example: ~1.2 is equivalent to >=1.2,<2.0, while ~1.2.3 is equivalent to | |||
// >=1.2.3,<1.3. As you can see it is mostly useful for projects respecting | |||
// semantic versioning. A common usage would be to mark the minimum minor | |||
// version you depend on, like ~1.2 (which allows anything up to, but not | |||
// including, 2.0). Since in theory there should be no backwards compatibility | |||
// breaks until 2.0, that works well. Another way of looking at it is that | |||
// using ~ specifies a minimum version, but allows the last digit specified | |||
// to go up. | |||
// | |||
// By default only stable releases are taken into consideration. If you would like | |||
// to also get RC, beta, alpha or dev versions of your dependencies you can do so | |||
// using stability flags. To change that for all packages instead of doing per | |||
// dependency you can also use the minimum-stability setting. | |||
// | |||
// From: http://getcomposer.org/doc/01-basic-usage.md#package-versions | |||
func NewConstrainGroupFromString(name string) *ConstraintGroup { | |||
group := new(ConstraintGroup) | |||
group.fromString(name) | |||
return group | |||
} | |||
// Adds a Contraint to the group | |||
func (self *ConstraintGroup) AddConstraint(constraint ...*Constraint) { | |||
if self.constraints == nil { | |||
self.constraints = make([]*Constraint, 0) | |||
} | |||
self.constraints = append(self.constraints, constraint...) | |||
} | |||
// Return all the constraints | |||
func (self *ConstraintGroup) GetConstraints() []*Constraint { | |||
return self.constraints | |||
} | |||
// Match a given version againts the group | |||
// | |||
// Usage | |||
// c := version.NewConstrainGroupFromString(">2.0,<=3.0") | |||
// c.Match("2.5.0beta") | |||
// Returns: true | |||
// | |||
// c := version.NewConstrainGroupFromString("~1.2.3") | |||
// c.Match("1.2.3.5") | |||
// Returns: true | |||
func (self *ConstraintGroup) Match(version string) bool { | |||
for _, constraint := range self.constraints { | |||
if constraint.Match(version) == false { | |||
return false | |||
} | |||
} | |||
return true | |||
} | |||
func (self *ConstraintGroup) fromString(constraint string) bool { | |||
result := RegFind(`(?i)^([^,\s]*?)@(stable|RC|beta|alpha|dev)$`, constraint) | |||
if result != nil { | |||
constraint = result[1] | |||
if constraint == "" { | |||
constraint = "*" | |||
} | |||
} | |||
result = RegFind(`(?i)^(dev-[^,\s@]+?|[^,\s@]+?\.x-dev)#.+$`, constraint) | |||
if result != nil { | |||
if result[1] != "" { | |||
constraint = result[1] | |||
} | |||
} | |||
constraints := RegSplit(`\s*,\s*`, strings.Trim(constraint, " ")) | |||
if len(constraints) > 1 { | |||
for _, part := range constraints { | |||
self.AddConstraint(self.parseConstraint(part)...) | |||
} | |||
return true | |||
} | |||
self.AddConstraint(self.parseConstraint(constraints[0])...) | |||
return true | |||
} | |||
func (self *ConstraintGroup) parseConstraint(constraint string) []*Constraint { | |||
stabilityModifier := "" | |||
result := RegFind(`(?i)^([^,\s]+?)@(stable|RC|beta|alpha|dev)$`, constraint) | |||
if result != nil { | |||
constraint = result[1] | |||
if result[2] != "stable" { | |||
stabilityModifier = result[2] | |||
} | |||
} | |||
result = RegFind(`^[x*](\.[x*])*$`, constraint) | |||
if result != nil { | |||
return make([]*Constraint, 0) | |||
} | |||
highVersion := "" | |||
lowVersion := "" | |||
result = RegFind(`(?i)^~(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?`+modifierRegex+`?$`, constraint) | |||
if result != nil { | |||
if len(result) > 4 && result[4] != "" { | |||
last, _ := strconv.Atoi(result[3]) | |||
highVersion = result[1] + "." + result[2] + "." + strconv.Itoa(last+1) + ".0-dev" | |||
lowVersion = result[1] + "." + result[2] + "." + result[3] + "." + result[4] | |||
} else if len(result) > 3 && result[3] != "" { | |||
last, _ := strconv.Atoi(result[2]) | |||
highVersion = result[1] + "." + strconv.Itoa(last+1) + ".0.0-dev" | |||
lowVersion = result[1] + "." + result[2] + "." + result[3] + ".0" | |||
} else { | |||
last, _ := strconv.Atoi(result[1]) | |||
highVersion = strconv.Itoa(last+1) + ".0.0.0-dev" | |||
if len(result) > 2 && result[2] != "" { | |||
lowVersion = result[1] + "." + result[2] + ".0.0" | |||
} else { | |||
lowVersion = result[1] + ".0.0.0" | |||
} | |||
} | |||
if len(result) > 5 && result[5] != "" { | |||
lowVersion = lowVersion + "-" + expandStability(result[5]) | |||
} | |||
if len(result) > 6 && result[6] != "" { | |||
lowVersion = lowVersion + result[6] | |||
} | |||
if len(result) > 7 && result[7] != "" { | |||
lowVersion = lowVersion + "-dev" | |||
} | |||
return []*Constraint{ | |||
{">=", lowVersion}, | |||
{"<", highVersion}, | |||
} | |||
} | |||
result = RegFind(`^(\d+)(?:\.(\d+))?(?:\.(\d+))?\.[x*]$`, constraint) | |||
if result != nil { | |||
if len(result) > 3 && result[3] != "" { | |||
highVersion = result[1] + "." + result[2] + "." + result[3] + ".9999999" | |||
if result[3] == "0" { | |||
last, _ := strconv.Atoi(result[2]) | |||
lowVersion = result[1] + "." + strconv.Itoa(last-1) + ".9999999.9999999" | |||
} else { | |||
last, _ := strconv.Atoi(result[3]) | |||
lowVersion = result[1] + "." + result[2] + "." + strconv.Itoa(last-1) + ".9999999" | |||
} | |||
} else if len(result) > 2 && result[2] != "" { | |||
highVersion = result[1] + "." + result[2] + ".9999999.9999999" | |||
if result[2] == "0" { | |||
last, _ := strconv.Atoi(result[1]) | |||
lowVersion = strconv.Itoa(last-1) + ".9999999.9999999.9999999" | |||
} else { | |||
last, _ := strconv.Atoi(result[2]) | |||
lowVersion = result[1] + "." + strconv.Itoa(last-1) + ".9999999.9999999" | |||
} | |||
} else { | |||
highVersion = result[1] + ".9999999.9999999.9999999" | |||
if result[1] == "0" { | |||
return []*Constraint{{"<", highVersion}} | |||
} else { | |||
last, _ := strconv.Atoi(result[1]) | |||
lowVersion = strconv.Itoa(last-1) + ".9999999.9999999.9999999" | |||
} | |||
} | |||
return []*Constraint{ | |||
{">", lowVersion}, | |||
{"<", highVersion}, | |||
} | |||
} | |||
// match operators constraints | |||
result = RegFind(`^(<>|!=|>=?|<=?|==?)?\s*(.*)`, constraint) | |||
if result != nil { | |||
version := Normalize(result[2]) | |||
if stabilityModifier != "" && parseStability(version) == "stable" { | |||
version = version + "-" + stabilityModifier | |||
} else if result[1] == "<" { | |||
match := RegFind(`(?i)-stable$`, result[2]) | |||
if match == nil { | |||
version = version + "-dev" | |||
} | |||
} | |||
if len(result) > 1 && result[1] != "" { | |||
return []*Constraint{{result[1], version}} | |||
} else { | |||
return []*Constraint{{"=", version}} | |||
} | |||
} | |||
return []*Constraint{{constraint, stabilityModifier}} | |||
} | |||
// PCRegMap : PreCompiled Regex Map | |||
type PCRegMap struct { | |||
sync.RWMutex | |||
m map[string]*regexp.Regexp | |||
} | |||
// MustCompile : to replace regexp.MustCompile in RegFind. | |||
func (p *PCRegMap) MustCompile(pattern string) *regexp.Regexp { | |||
p.RLock() | |||
ret, exist := p.m[pattern] | |||
p.RUnlock() | |||
if exist { | |||
return ret | |||
} | |||
ret = regexp.MustCompile(pattern) | |||
p.Lock() | |||
p.m[pattern] = ret | |||
p.Unlock() | |||
return ret | |||
} | |||
var ( | |||
regexpCache *PCRegMap | |||
) | |||
func init() { | |||
regexpCache = new(PCRegMap) | |||
regexpCache.m = make(map[string]*regexp.Regexp) | |||
} | |||
func RegFind(pattern, subject string) []string { | |||
reg := regexpCache.MustCompile(pattern) | |||
matched := reg.FindAllStringSubmatch(subject, -1) | |||
if matched != nil { | |||
return matched[0] | |||
} | |||
return nil | |||
} | |||
func RegSplit(pattern, subject string) []string { | |||
reg := regexp.MustCompile(pattern) | |||
indexes := reg.FindAllStringIndex(subject, -1) | |||
laststart := 0 | |||
result := make([]string, len(indexes)+1) | |||
for i, element := range indexes { | |||
result[i] = subject[laststart:element[0]] | |||
laststart = element[1] | |||
} | |||
result[len(indexes)] = subject[laststart:len(subject)] | |||
return result | |||
} |
@@ -1,116 +0,0 @@ | |||
package version | |||
import ( | |||
"regexp" | |||
"strings" | |||
) | |||
var modifierRegex = `[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)(?:[.-]?(\d+))?)?([.-]?dev)?` | |||
var regexpMasterLikeBranches = regexp.MustCompile(`^(?:dev-)?(?:master|trunk|default)$`) | |||
var regexpBranchNormalize = regexp.MustCompile(`(?i)^v?(\d+)(\.(?:\d+|[x*]))?(\.(?:\d+|[x*]))?(\.(?:\d+|[x*]))?$`) | |||
// Normalizes a version string to be able to perform comparisons on it | |||
// | |||
// Example: | |||
// version.Normalize("10.4.13-b") | |||
// Returns: 10.4.13.0-beta | |||
// | |||
func Normalize(version string) string { | |||
// ignore aliases and just assume the alias is required instead of the source | |||
result := RegFind(`^([^,\s]+) +as +([^,\s]+)$`, version) | |||
if result != nil { | |||
version = result[1] | |||
} | |||
// match master-like branches | |||
if regexpMasterLikeBranches.MatchString(strings.ToLower(version)) { | |||
return "9999999-dev" | |||
} | |||
if strings.HasPrefix(strings.ToLower(version), "dev-") { | |||
return "dev-" + version[4:len(version)] | |||
} | |||
index := 0 | |||
// match classical versioning | |||
result = RegFind(`(?i)^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?`+modifierRegex+`$`, version) | |||
if result != nil { | |||
version = "" | |||
for _, val := range result[1:5] { | |||
if val != "" { | |||
version = version + val | |||
} else { | |||
version = version + ".0" | |||
} | |||
} | |||
index = 5 | |||
} else { | |||
// match date-based versioning | |||
result = RegFind(`(?i)^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)`+modifierRegex+`$`, version) | |||
if result != nil { | |||
version = regexp.MustCompile(`\D`).ReplaceAllString(result[1], "-") | |||
index = 2 | |||
} | |||
} | |||
if index != 0 { | |||
if result[index] != "" { | |||
if result[index] == "stable" { | |||
return version | |||
} | |||
version = version + "-" + expandStability(result[index]) | |||
if result[index+1] != "" { | |||
version = version + result[index+1] | |||
} | |||
} | |||
if result[index+2] != "" { | |||
version = version + "-dev" | |||
} | |||
return version | |||
} | |||
result = RegFind(`(?i)(.*?)[.-]?dev$`, version) | |||
if result != nil { | |||
return normalizeBranch(result[1]) | |||
} | |||
return version | |||
} | |||
func normalizeBranch(name string) string { | |||
name = strings.Trim(name, " ") | |||
if name == "master" || name == "trunk" || name == "default" { | |||
return Normalize(name) | |||
} | |||
replace := strings.NewReplacer("*", "9999999", "x", "9999999") | |||
matched := regexpBranchNormalize.FindAllStringSubmatch(name, -1) | |||
if matched != nil { | |||
name = "" | |||
for _, val := range matched[0][1:5] { | |||
if val != "" { | |||
name = name + replace.Replace(val) | |||
} else { | |||
name = name + ".9999999" | |||
} | |||
} | |||
return name + "-dev" | |||
} | |||
if strings.HasSuffix(strings.ToLower(name), "-dev") { | |||
return name | |||
} | |||
return "dev-" + name | |||
} |
@@ -1,36 +0,0 @@ | |||
package version | |||
import ( | |||
"sort" | |||
) | |||
// Sorts a string slice of version number strings using version.CompareSimple() | |||
// | |||
// Example: | |||
// version.Sort([]string{"1.10-dev", "1.0rc1", "1.0", "1.0-dev"}) | |||
// Returns []string{"1.0-dev", "1.0rc1", "1.0", "1.10-dev"} | |||
// | |||
func Sort(versionStrings []string) { | |||
versions := versionSlice(versionStrings) | |||
sort.Sort(versions) | |||
} | |||
type versionSlice []string | |||
func (s versionSlice) Len() int { | |||
return len(s) | |||
} | |||
func (s versionSlice) Less(i, j int) bool { | |||
cmp := CompareSimple(Normalize(s[i]), Normalize(s[j])) | |||
if cmp == 0 { | |||
return s[i] < s[j] | |||
} | |||
return cmp < 0 | |||
} | |||
func (s versionSlice) Swap(i, j int) { | |||
tmp := s[j] | |||
s[j] = s[i] | |||
s[i] = tmp | |||
} |
@@ -1,83 +0,0 @@ | |||
package version | |||
import ( | |||
"regexp" | |||
"strings" | |||
) | |||
const ( | |||
Development = iota | |||
Alpha | |||
Beta | |||
RC | |||
Stable | |||
) | |||
func expandStability(stability string) string { | |||
stability = strings.ToLower(stability) | |||
switch stability { | |||
case "a": | |||
return "alpha" | |||
case "b": | |||
return "beta" | |||
case "p": | |||
return "patch" | |||
case "pl": | |||
return "patch" | |||
case "rc": | |||
return "RC" | |||
} | |||
return stability | |||
} | |||
func parseStability(version string) string { | |||
version = regexp.MustCompile(`(?i)#.+$`).ReplaceAllString(version, " ") | |||
version = strings.ToLower(version) | |||
if strings.HasPrefix(version, "dev-") || strings.HasSuffix(version, "-dev") { | |||
return "dev" | |||
} | |||
result := RegFind(`(?i)^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?`+modifierRegex+`$`, version) | |||
if result != nil { | |||
if len(result) > 3 { | |||
return "dev" | |||
} | |||
} | |||
if result[1] != "" { | |||
if "beta" == result[1] || "b" == result[1] { | |||
return "beta" | |||
} | |||
if "alpha" == result[1] || "a" == result[1] { | |||
return "alpha" | |||
} | |||
if "rc" == result[1] { | |||
return "RC" | |||
} | |||
} | |||
return "stable" | |||
} | |||
func GetStability(version string) int { | |||
result := RegFind(`(?i)(stable|RC|beta|alpha|dev)`, Normalize(version)) | |||
if len(result) == 0 { | |||
return Stable | |||
} | |||
switch result[1] { | |||
case "dev": | |||
return Development | |||
case "alpha": | |||
return Alpha | |||
case "beta": | |||
return Beta | |||
case "RC": | |||
return RC | |||
} | |||
return Stable | |||
} |
@@ -423,6 +423,9 @@ github.com/hashicorp/go-cleanhttp | |||
# github.com/hashicorp/go-retryablehttp v0.6.6 | |||
## explicit | |||
github.com/hashicorp/go-retryablehttp | |||
# github.com/hashicorp/go-version v1.2.1 | |||
## explicit | |||
github.com/hashicorp/go-version | |||
# github.com/hashicorp/hcl v1.0.0 | |||
github.com/hashicorp/hcl | |||
github.com/hashicorp/hcl/hcl/ast | |||
@@ -547,9 +550,6 @@ github.com/mattn/go-runewidth | |||
github.com/mattn/go-sqlite3 | |||
# github.com/matttproud/golang_protobuf_extensions v1.0.1 | |||
github.com/matttproud/golang_protobuf_extensions/pbutil | |||
# github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75 | |||
## explicit | |||
github.com/mcuadros/go-version | |||
# github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81 | |||
## explicit | |||
github.com/mgechev/dots | |||