Skip to content

Commit ab7e461

Browse files
authored
Merge pull request #251 from nicholasSUSE/fix-rc-ordering
Improve version ordering, fixing RCs
2 parents 9b6c149 + b7a3985 commit ab7e461

File tree

5 files changed

+187
-52
lines changed

5 files changed

+187
-52
lines changed

main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ func main() {
390390
newChartFlag := cli.BoolFlag{
391391
Name: "new-chart",
392392
Usage: `Usage:
393-
-new-chart=<false or true>
393+
--new-chart=<false or true>
394394
`,
395395
Required: false,
396396
Destination: &NewChart,
@@ -1149,6 +1149,7 @@ func chartBump(c *cli.Context) {
11491149
logger.Log(ctx, slog.LevelInfo, "", slog.String("branch", Branch))
11501150
logger.Log(ctx, slog.LevelInfo, "", slog.String("overrideVersion", OverrideVersion))
11511151
logger.Log(ctx, slog.LevelInfo, "", slog.Bool("multi-RC", MultiRC))
1152+
logger.Log(ctx, slog.LevelInfo, "", slog.Bool("new-chart", NewChart))
11521153

11531154
if CurrentPackage == "" || Branch == "" || OverrideVersion == "" {
11541155
logger.Fatal(ctx, fmt.Sprintf("must provide values for CurrentPackage[%s], Branch[%s], and OverrideVersion[%s]",

pkg/helm/helm.go

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ import (
66
"fmt"
77
"log/slog"
88
"os"
9+
"sort"
10+
"strconv"
11+
"strings"
912

13+
"github.com/Masterminds/semver"
1014
"github.com/go-git/go-billy/v5"
1115
"github.com/rancher/charts-build-scripts/pkg/filesystem"
1216
"github.com/rancher/charts-build-scripts/pkg/logger"
@@ -41,8 +45,8 @@ func CreateOrUpdateHelmIndex(ctx context.Context, rootFs billy.Filesystem) error
4145
}
4246

4347
// Sort entries to ensure consistent ordering
44-
helmIndexFile.SortEntries()
45-
newHelmIndexFile.SortEntries()
48+
SortVersions(helmIndexFile)
49+
SortVersions(newHelmIndexFile)
4650

4751
// Update index
4852
helmIndexFile, upToDate := UpdateIndex(ctx, helmIndexFile, newHelmIndexFile)
@@ -128,3 +132,85 @@ func OpenIndexYaml(ctx context.Context, rootFs billy.Filesystem) (*helmRepo.Inde
128132

129133
return helmRepo.LoadIndexFile(helmIndexFilePath)
130134
}
135+
136+
// SortVersions sorts chart versions with custom RC handling
137+
func SortVersions(index *helmRepo.IndexFile) {
138+
for _, versions := range index.Entries {
139+
sort.Slice(versions, func(i, j int) bool {
140+
return compareVersions(versions[i].Version, versions[j].Version)
141+
})
142+
}
143+
}
144+
145+
// compareVersions compares two version strings for sorting
146+
// Returns true if versionA should come before versionB (descending order)
147+
func compareVersions(versionA, versionB string) bool {
148+
// Parse both versions
149+
baseA, rcA, isRCA := parseVersionWithRC(versionA)
150+
baseB, rcB, isRCB := parseVersionWithRC(versionB)
151+
152+
// Parse base versions using semver
153+
semverA, errA := semver.NewVersion(baseA)
154+
semverB, errB := semver.NewVersion(baseB)
155+
156+
if errA != nil {
157+
return false // push invalid to end
158+
}
159+
if errB != nil {
160+
return true // push invalid to end
161+
}
162+
163+
// If base versions are different, use semver comparison (descending)
164+
if !semverA.Equal(semverB) {
165+
return semverA.GreaterThan(semverB)
166+
}
167+
168+
// Same base version - handle RC logic
169+
// Stable (non-RC) should come first
170+
if !isRCA && isRCB {
171+
return true // A is stable, B is RC - A comes first
172+
}
173+
if isRCA && !isRCB {
174+
return false // A is RC, B is stable - B comes first
175+
}
176+
177+
// Both are RCs - higher RC number comes first (descending)
178+
if isRCA && isRCB {
179+
return rcA > rcB
180+
}
181+
182+
// Both are stable with same base version - they're equal
183+
return false
184+
}
185+
186+
// parseVersionWithRC extracts the base version and RC number from a version string
187+
// Example: "108.0.0+up0.9.0-rc.1" returns ("108.0.0+up0.9.0", 1, true)
188+
func parseVersionWithRC(version string) (baseVersion string, rcNumber int, isRC bool) {
189+
// Split by '+' to separate version from build metadata
190+
parts := strings.Split(version, "+")
191+
if len(parts) != 2 {
192+
return version, 0, false
193+
}
194+
195+
baseVersionNum := parts[0]
196+
buildMetadata := parts[1]
197+
198+
// Check if build metadata contains RC
199+
if !strings.Contains(buildMetadata, "-rc.") {
200+
return version, 0, false
201+
}
202+
203+
// Extract RC number
204+
rcParts := strings.Split(buildMetadata, "-rc.")
205+
if len(rcParts) != 2 {
206+
return version, 0, false
207+
}
208+
209+
rcNum, err := strconv.Atoi(rcParts[1])
210+
if err != nil {
211+
return version, 0, false
212+
}
213+
214+
// Return base version with the non-RC part of build metadata
215+
return baseVersionNum + "+" + rcParts[0], rcNum, true
216+
}

pkg/helm/helm_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package helm
2+
3+
import (
4+
"testing"
5+
6+
helmRepo "helm.sh/helm/v3/pkg/repo"
7+
"helm.sh/helm/v3/pkg/chart"
8+
)
9+
10+
func TestSortVersions(t *testing.T) {
11+
tests := []struct {
12+
name string
13+
input helmRepo.ChartVersions
14+
expected []string // expected order of versions
15+
}{
16+
{
17+
name: "stable versions only - should sort descending",
18+
input: helmRepo.ChartVersions{
19+
{Metadata: &chart.Metadata{Version: "108.0.0+up0.9.0"}},
20+
{Metadata: &chart.Metadata{Version: "108.0.2+up0.9.2"}},
21+
{Metadata: &chart.Metadata{Version: "108.0.1+up0.9.1"}},
22+
},
23+
expected: []string{
24+
"108.0.2+up0.9.2",
25+
"108.0.1+up0.9.1",
26+
"108.0.0+up0.9.0",
27+
},
28+
},
29+
{
30+
name: "stable + RCs with same base - stable first, then RCs descending",
31+
input: helmRepo.ChartVersions{
32+
{Metadata: &chart.Metadata{Version: "108.0.0+up0.9.0-rc.1"}},
33+
{Metadata: &chart.Metadata{Version: "108.0.0+up0.9.0"}},
34+
{Metadata: &chart.Metadata{Version: "108.0.0+up0.9.0-rc.2"}},
35+
},
36+
expected: []string{
37+
"108.0.0+up0.9.0",
38+
"108.0.0+up0.9.0-rc.2",
39+
"108.0.0+up0.9.0-rc.1",
40+
},
41+
},
42+
{
43+
name: "RCs only - should sort descending by RC number",
44+
input: helmRepo.ChartVersions{
45+
{Metadata: &chart.Metadata{Version: "108.0.0+up0.9.0-rc.1"}},
46+
{Metadata: &chart.Metadata{Version: "108.0.0+up0.9.0-rc.3"}},
47+
{Metadata: &chart.Metadata{Version: "108.0.0+up0.9.0-rc.2"}},
48+
},
49+
expected: []string{
50+
"108.0.0+up0.9.0-rc.3",
51+
"108.0.0+up0.9.0-rc.2",
52+
"108.0.0+up0.9.0-rc.1",
53+
},
54+
},
55+
{
56+
name: "mixed base versions with RCs - sort by semver first",
57+
input: helmRepo.ChartVersions{
58+
{Metadata: &chart.Metadata{Version: "108.0.0+up0.9.0-rc.1"}},
59+
{Metadata: &chart.Metadata{Version: "109.0.0+up0.10.0"}},
60+
{Metadata: &chart.Metadata{Version: "108.0.0+up0.9.0"}},
61+
{Metadata: &chart.Metadata{Version: "109.0.0+up0.10.0-rc.1"}},
62+
},
63+
expected: []string{
64+
"109.0.0+up0.10.0",
65+
"109.0.0+up0.10.0-rc.1",
66+
"108.0.0+up0.9.0",
67+
"108.0.0+up0.9.0-rc.1",
68+
},
69+
},
70+
}
71+
72+
for _, tt := range tests {
73+
t.Run(tt.name, func(t *testing.T) {
74+
// Create mock IndexFile
75+
index := &helmRepo.IndexFile{
76+
Entries: map[string]helmRepo.ChartVersions{
77+
"test-chart": tt.input,
78+
},
79+
}
80+
81+
// Run the sort function
82+
SortVersions(index)
83+
84+
// Check the order
85+
sorted := index.Entries["test-chart"]
86+
if len(sorted) != len(tt.expected) {
87+
t.Fatalf("expected %d versions, got %d", len(tt.expected), len(sorted))
88+
}
89+
90+
for i, expectedVersion := range tt.expected {
91+
if sorted[i].Version != expectedVersion {
92+
t.Errorf("at index %d: expected %s, got %s", i, expectedVersion, sorted[i].Version)
93+
}
94+
}
95+
})
96+
}
97+
}

pkg/lifecycle/parser.go

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@ package lifecycle
33
import (
44
"context"
55
"fmt"
6-
"sort"
76
"strings"
87

9-
"github.com/Masterminds/semver"
108
"github.com/go-git/go-billy/v5"
119
helmRepo "helm.sh/helm/v3/pkg/repo"
1210
)
@@ -103,19 +101,3 @@ func (ld *Dependencies) populateAssetsVersionsPath(ctx context.Context) error {
103101
// Now fileNames slice contains the names of all files in the directories
104102
return nil
105103
}
106-
107-
// sortAssetsVersions will convert to semver and
108-
// sort the assets for each key in the assetsVersionsMap
109-
func (ld *Dependencies) sortAssetsVersions() {
110-
// Iterate over the map and sort the assets for each key
111-
for k, assets := range ld.AssetsVersionsMap {
112-
sort.Slice(assets, func(i, j int) bool {
113-
vi, _ := semver.NewVersion(assets[i].Version)
114-
vj, _ := semver.NewVersion(assets[j].Version)
115-
return vi.LessThan(vj)
116-
})
117-
ld.AssetsVersionsMap[k] = assets
118-
}
119-
120-
return
121-
}

pkg/lifecycle/parser_test.go

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -106,34 +106,3 @@ func Test_populateAssetsVersionsPath(t *testing.T) {
106106
assert.Error(t, err, "populateAssetsVersionsPath should have returned an error")
107107
})
108108
}
109-
110-
func Test_sortAssetsVersions(t *testing.T) {
111-
// Arrange
112-
dependency := &Dependencies{
113-
AssetsVersionsMap: map[string][]Asset{
114-
"chart1": {
115-
{Version: "1.0.0"},
116-
{Version: "0.1.0"},
117-
{Version: "0.0.1"},
118-
},
119-
"chart2": {
120-
{Version: "2.0.0"},
121-
{Version: "1.1.0"},
122-
{Version: "1.0.1"},
123-
},
124-
},
125-
}
126-
127-
// Act
128-
dependency.sortAssetsVersions()
129-
130-
// Assertions
131-
assert.Equal(t, len(dependency.AssetsVersionsMap), 2, "Expected 2 charts in the assetsVersionsMap")
132-
assert.Equal(t, dependency.AssetsVersionsMap["chart1"][0].Version, "0.0.1", "Expected 0.0.1")
133-
assert.Equal(t, dependency.AssetsVersionsMap["chart1"][1].Version, "0.1.0", "Expected 0.1.0")
134-
assert.Equal(t, dependency.AssetsVersionsMap["chart1"][2].Version, "1.0.0", "Expected 1.0.0")
135-
136-
assert.Equal(t, dependency.AssetsVersionsMap["chart2"][0].Version, "1.0.1", "Expected 1.0.1")
137-
assert.Equal(t, dependency.AssetsVersionsMap["chart2"][1].Version, "1.1.0", "Expected 1.1.0")
138-
assert.Equal(t, dependency.AssetsVersionsMap["chart2"][2].Version, "2.0.0", "Expected 2.0.0")
139-
}

0 commit comments

Comments
 (0)