@@ -19,8 +19,11 @@ package libgit2
1919import (
2020 "context"
2121 "fmt"
22+ "sort"
23+ "time"
2224
23- "github.com/blang/semver/v4"
25+ "github.com/Masterminds/semver/v3"
26+ "github.com/fluxcd/pkg/version"
2427 git2go "github.com/libgit2/git2go/v31"
2528
2629 "github.com/fluxcd/pkg/gitutil"
@@ -168,7 +171,7 @@ type CheckoutSemVer struct {
168171}
169172
170173func (c * CheckoutSemVer ) Checkout (ctx context.Context , path , url string , auth * git.Auth ) (git.Commit , string , error ) {
171- rng , err := semver .ParseRange (c .semVer )
174+ verConstraint , err := semver .NewConstraint (c .semVer )
172175 if err != nil {
173176 return nil , "" , fmt .Errorf ("semver parse range error: %w" , err )
174177 }
@@ -186,28 +189,61 @@ func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth *g
186189 return nil , "" , fmt .Errorf ("unable to clone '%s', error: %w" , url , err )
187190 }
188191
189- repoTags , err := repo .Tags .List ()
190- if err != nil {
191- return nil , "" , fmt .Errorf ("git list tags error: %w" , err )
192- }
192+ tags := make (map [string ]string )
193+ tagTimestamps := make (map [string ]time.Time )
194+ if err := repo .Tags .Foreach (func (name string , id * git2go.Oid ) error {
195+ tag , err := repo .LookupTag (id )
196+ if err != nil {
197+ return nil
198+ }
193199
194- svTags := make (map [string ]string )
195- var svers []semver.Version
196- for _ , tag := range repoTags {
197- v , _ := semver .ParseTolerant (tag )
198- if rng (v ) {
199- svers = append (svers , v )
200- svTags [v .String ()] = tag
200+ commit , err := tag .Peel (git2go .ObjectCommit )
201+ if err != nil {
202+ return fmt .Errorf ("can't get commit for tag %s: %w" , name , err )
201203 }
204+ c , err := commit .AsCommit ()
205+ if err != nil {
206+ return err
207+ }
208+ tagTimestamps [tag .Name ()] = c .Committer ().When
209+ tags [tag .Name ()] = name
210+ return nil
211+ }); err != nil {
212+ return nil , "" , err
202213 }
203214
204- if len (svers ) == 0 {
215+ var matchedVersions semver.Collection
216+ for tag , _ := range tags {
217+ v , err := version .ParseVersion (tag )
218+ if err != nil {
219+ continue
220+ }
221+ if ! verConstraint .Check (v ) {
222+ continue
223+ }
224+ matchedVersions = append (matchedVersions , v )
225+ }
226+ if len (matchedVersions ) == 0 {
205227 return nil , "" , fmt .Errorf ("no match found for semver: %s" , c .semVer )
206228 }
207229
208- semver .Sort (svers )
209- v := svers [len (svers )- 1 ]
210- t := svTags [v .String ()]
230+ // Sort versions
231+ sort .SliceStable (matchedVersions , func (i , j int ) bool {
232+ left := matchedVersions [i ]
233+ right := matchedVersions [j ]
234+
235+ if ! left .Equal (right ) {
236+ return left .LessThan (right )
237+ }
238+
239+ // Having tag target timestamps at our disposal, we further try to sort
240+ // versions into a chronological order. This is especially important for
241+ // versions that differ only by build metadata, because it is not considered
242+ // a part of the comparable version in Semver
243+ return tagTimestamps [left .String ()].Before (tagTimestamps [right .String ()])
244+ })
245+ v := matchedVersions [len (matchedVersions )- 1 ]
246+ t := v .Original ()
211247
212248 ref , err := repo .References .Dwim (t )
213249 if err != nil {
0 commit comments