github.com/azunymous/cdx@v0.0.0-20201122180449-fbb46cc4d252/vcs/gogit/repo.go (about) 1 // Package gogit interacts with git and git tags via the gogit git implementation 2 package gogit 3 4 import ( 5 "github.com/azunymous/cdx/parse" 6 "github.com/blang/semver/v4" 7 "github.com/go-git/go-git/v5" 8 "github.com/go-git/go-git/v5/plumbing" 9 "github.com/sirupsen/logrus" 10 "regexp" 11 "sort" 12 ) 13 14 // Repo is a VCS repository that can be manipulated 15 type Repo struct { 16 gitRepo *git.Repository 17 log logrus.StdLogger 18 } 19 20 // NewRepo returns a new repository, search recursively upwards for a git repository 21 func NewRepo() (*Repo, error) { 22 gr, err := git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true}) 23 24 if err != nil { 25 return nil, err 26 } 27 return &Repo{gitRepo: gr, log: logrus.New()}, nil 28 } 29 30 // TagsForHead returns sorted version tags at HEAD 31 // If no promotion stage is provided, only unpromoted tags are returned. 32 // Only the first provided promotion stage is used for filtering. 33 func (r *Repo) TagsForHead(module string, stage ...string) ([]string, error) { 34 suffix := "" 35 if len(stage) > 0 && stage[0] != "" { 36 suffix = "\\+" + stage[0] 37 } 38 39 current, err := r.gitRepo.ResolveRevision("HEAD") 40 if err != nil { 41 return nil, err 42 } 43 tags, err := r.gitRepo.Tags() 44 if err != nil { 45 return nil, err 46 } 47 48 regex, err := regexp.Compile("^" + module + "-[0-9]+\\.[0-9]+\\.[0-9]+" + suffix + "$") 49 if err != nil { 50 return nil, err 51 } 52 53 var t []string 54 _ = tags.ForEach(func(reference *plumbing.Reference) error { 55 if (reference.Hash() == *current && regex.MatchString(reference.Name().Short())) || r.annotatedMatch(reference, current, regex) { 56 t = append(t, reference.Name().Short()) 57 } 58 return nil 59 }) 60 sort.Strings(t) 61 return t, nil 62 } 63 64 // TagsForModule returns sorted all semantic version tags for a module and a promotion stage. 65 // If no promotion stage is provided, only unpromoted tags are returned. 66 // Only the first provided promotion stage is used for filtering. 67 func (r *Repo) TagsForModule(module string, stage ...string) ([]string, error) { 68 suffix := "" 69 if len(stage) > 0 && stage[0] != "" { 70 suffix = "\\+" + stage[0] 71 } 72 73 tags, err := r.gitRepo.Tags() 74 if err != nil { 75 return nil, err 76 } 77 78 var t []string 79 regex, err := regexp.Compile("^" + module + "-[0-9]+\\.[0-9]+\\.[0-9]+" + suffix + "$") 80 if err != nil { 81 return nil, err 82 } 83 _ = tags.ForEach(func(reference *plumbing.Reference) error { 84 if regex.MatchString(reference.Name().Short()) { 85 t = append(t, reference.Name().Short()) 86 } 87 return nil 88 }) 89 sortSemanticVers(t) 90 return t, nil 91 } 92 93 func sortSemanticVers(t []string) { 94 sort.Slice(t, func(i, j int) bool { 95 iVer := semver.MustParse(parse.Version(t[i])) 96 jVer := semver.MustParse(parse.Version(t[j])) 97 return iVer.LE(jVer) 98 }) 99 } 100 101 func (r *Repo) annotatedMatch(reference *plumbing.Reference, hash *plumbing.Hash, regex *regexp.Regexp) bool { 102 obj, err := r.gitRepo.TagObject(reference.Hash()) 103 if err != nil { 104 return false 105 } 106 commit, err := obj.Commit() 107 if err != nil { 108 return false 109 } 110 if commit.Hash == *hash && regex.MatchString(obj.Name) { 111 return true 112 } 113 return false 114 }