Commit 85ec9f4218
85ec9f42188b756a08648c1db06a12f5b8a223d2
parent: 568741bf32
Verified · cmc
cmc <hello@cleberg.net> · 2026-09-12 04:58 UTC
web: refs and the release form order tags by version
Ref #182
internal/gitutil/versions.go
added
+50
| @@ -0,0 +1,50 @@ |
| 1 | package gitutil |
| 2 | |
| 3 | import ( |
| 4 | "sort" |
| 5 | "strconv" |
| 6 | "strings" |
| 7 | ) |
| 8 | |
| 9 | // version parses "v1.2.3" or "1.2" into numeric parts. Anything else is |
| 10 | // not a version. |
| 11 | func version(name string) ([]int, bool) { |
| 12 | s := strings.TrimPrefix(name, "v") |
| 13 | if s == "" { |
| 14 | return nil, false |
| 15 | } |
| 16 | var parts []int |
| 17 | for _, p := range strings.Split(s, ".") { |
| 18 | n, err := strconv.Atoi(p) |
| 19 | if err != nil || n < 0 { |
| 20 | return nil, false |
| 21 | } |
| 22 | parts = append(parts, n) |
| 23 | } |
| 24 | return parts, true |
| 25 | } |
| 26 | |
| 27 | func versionLess(a, b []int) bool { |
| 28 | for i := 0; i < len(a) && i < len(b); i++ { |
| 29 | if a[i] != b[i] { |
| 30 | return a[i] < b[i] |
| 31 | } |
| 32 | } |
| 33 | return len(a) < len(b) |
| 34 | } |
| 35 | |
| 36 | // SortVersions orders refs newest version first. Names that are not |
| 37 | // versions follow, by name. |
| 38 | func SortVersions(refs []Ref) { |
| 39 | sort.SliceStable(refs, func(i, j int) bool { |
| 40 | vi, oki := version(refs[i].Name) |
| 41 | vj, okj := version(refs[j].Name) |
| 42 | switch { |
| 43 | case oki && okj: |
| 44 | return versionLess(vj, vi) |
| 45 | case oki != okj: |
| 46 | return oki |
| 47 | } |
| 48 | return refs[i].Name < refs[j].Name |
| 49 | }) |
| 50 | } |
internal/gitutil/versions_test.go
added
+18
| @@ -0,0 +1,18 @@ |
| 1 | package gitutil |
| 2 | |
| 3 | import "testing" |
| 4 | |
| 5 | func TestSortVersionsNewestFirst(t *testing.T) { |
| 6 | refs := []Ref{{Name: "v1.2.0"}, {Name: "v1.10.0"}, {Name: "nightly"}, {Name: "v1.2.1"}, {Name: "v0.9"}, {Name: "beta"}, {Name: "2.0.0"}} |
| 7 | SortVersions(refs) |
| 8 | var got []string |
| 9 | for _, r := range refs { |
| 10 | got = append(got, r.Name) |
| 11 | } |
| 12 | want := []string{"2.0.0", "v1.10.0", "v1.2.1", "v1.2.0", "v0.9", "beta", "nightly"} |
| 13 | for i := range want { |
| 14 | if i >= len(got) || got[i] != want[i] { |
| 15 | t.Fatalf("order %v, want %v", got, want) |
| 16 | } |
| 17 | } |
| 18 | } |
internal/httpd/web.go
+2
| @@ -639,6 +639,7 @@ func (s *Server) releases(w http.ResponseWriter, r *http.Request) { |
| 639 | 639 | } |
| 640 | 640 | var freeTags []string |
| 641 | 641 | if tags, err := gitutil.Refs(p.Dir, "tags"); err == nil { |
| 642 | gitutil.SortVersions(tags) |
| 642 | 643 | for _, tg := range tags { |
| 643 | 644 | if !released[tg.Name] { |
| 644 | 645 | freeTags = append(freeTags, tg.Name) |
| @@ -1963,6 +1964,7 @@ func (s *Server) refs(w http.ResponseWriter, r *http.Request) { |
| 1963 | 1964 | p.Tab = "refs" |
| 1964 | 1965 | branches, _ := gitutil.Refs(p.Dir, "heads") |
| 1965 | 1966 | tags, _ := gitutil.Refs(p.Dir, "tags") |
| 1967 | gitutil.SortVersions(tags) |
| 1966 | 1968 | s.render(w, "refs.html", struct { |
| 1967 | 1969 | repoPage |
| 1968 | 1970 | Branches, Tags []gitutil.Ref |