From 1e6837bdce262143c6c793820fd36a2f17bd362e Mon Sep 17 00:00:00 2001 From: nicholasSUSE Date: Wed, 3 Sep 2025 21:42:57 -0300 Subject: [PATCH 1/5] adding new argument (NewChart) - used for auto-bump to specifyc we are adding a previous-non-existing chart - refactoring InitDependencies --- main.go | 27 ++++++++++++++++++++------- pkg/lifecycle/lifecycle.go | 12 +++++++++++- pkg/validate/validate.go | 2 +- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/main.go b/main.go index 640fd422..241c8a52 100644 --- a/main.go +++ b/main.go @@ -76,6 +76,8 @@ const ( defaultPrimeUserEnvironmentVariable = "PRIME_USER" defaultPrimePasswordEnvironmentVariable = "PRIME_PASSWORD" defaultPrimeURLEnvironmentVariable = "PRIME_URL" + // New Chart Options for Autobump + defaultNewChartVariable = "NEW_CHART" ) var ( @@ -133,6 +135,8 @@ var ( PrimePassword string // PrimeURL of SUSE Prime registry PrimeURL string + // NewChart boolean option for creating a net-new chart with auto-bump + NewChart bool ) func init() { @@ -383,6 +387,15 @@ func main() { Destination: &OverrideVersion, EnvVar: defaultOverrideVersionEnvironmentVariable, } + newChartFlag := cli.BoolFlag{ + Name: "new-chart", + Usage: `Usage: + -new-chart= + `, + Required: false, + Destination: &NewChart, + EnvVar: defaultNewChartVariable, + } // Commands app.Commands = []cli.Command{ @@ -547,7 +560,7 @@ func main() { Usage: `Generate a new chart bump PR.`, Action: chartBump, Before: setupCache, - Flags: []cli.Flag{packageFlag, branchFlag, overrideVersionFlag, multiRCFlag}, + Flags: []cli.Flag{packageFlag, branchFlag, overrideVersionFlag, multiRCFlag, newChartFlag}, }, } @@ -979,7 +992,7 @@ func lifecycleStatus(c *cli.Context) { getRepoRoot() rootFs := filesystem.GetFilesystem(RepoRoot) - lifeCycleDep, err := lifecycle.InitDependencies(ctx, RepoRoot, rootFs, c.String("branch-version"), CurrentChart) + lifeCycleDep, err := lifecycle.InitDependencies(ctx, rootFs, RepoRoot, c.String("branch-version"), CurrentChart, false) if err != nil { logger.Fatal(ctx, fmt.Errorf("encountered error while initializing dependencies: %w", err).Error()) } @@ -1005,7 +1018,7 @@ func autoForwardPort(c *cli.Context) { getRepoRoot() rootFs := filesystem.GetFilesystem(RepoRoot) - lifeCycleDep, err := lifecycle.InitDependencies(ctx, RepoRoot, rootFs, c.String("branch-version"), CurrentChart) + lifeCycleDep, err := lifecycle.InitDependencies(ctx, rootFs, RepoRoot, c.String("branch-version"), CurrentChart, false) if err != nil { logger.Fatal(ctx, fmt.Errorf("encountered error while initializing dependencies: %w", err).Error()) } @@ -1042,7 +1055,7 @@ func release(c *cli.Context) { getRepoRoot() rootFs := filesystem.GetFilesystem(RepoRoot) - dependencies, err := lifecycle.InitDependencies(ctx, RepoRoot, rootFs, c.String("branch-version"), CurrentChart) + dependencies, err := lifecycle.InitDependencies(ctx, rootFs, RepoRoot, c.String("branch-version"), CurrentChart, false) if err != nil { logger.Fatal(ctx, fmt.Errorf("encountered error while initializing dependencies: %w", err).Error()) } @@ -1101,7 +1114,7 @@ func validateRelease(c *cli.Context) { logger.Fatal(ctx, "branch must be in the format release-v2.x") } - dependencies, err := lifecycle.InitDependencies(ctx, RepoRoot, rootFs, strings.TrimPrefix(Branch, "release-v"), "") + dependencies, err := lifecycle.InitDependencies(ctx, rootFs, RepoRoot, strings.TrimPrefix(Branch, "release-v"), "", false) if err != nil { logger.Fatal(ctx, fmt.Errorf("encountered error while initializing dependencies: %w", err).Error()) } @@ -1149,12 +1162,12 @@ func chartBump(c *cli.Context) { ChartsScriptOptionsFile = path.ConfigurationYamlFile chartsScriptOptions := parseScriptOptions(ctx) - bump, err := auto.SetupBump(ctx, RepoRoot, CurrentPackage, Branch, chartsScriptOptions) + bump, err := auto.SetupBump(ctx, RepoRoot, CurrentPackage, Branch, chartsScriptOptions, NewChart) if err != nil { logger.Fatal(ctx, fmt.Errorf("failed to setup: %w", err).Error()) } - if err := bump.BumpChart(ctx, OverrideVersion, MultiRC); err != nil { + if err := bump.BumpChart(ctx, OverrideVersion, MultiRC, NewChart); err != nil { logger.Fatal(ctx, fmt.Errorf("failed to bump: %w", err).Error()) } } diff --git a/pkg/lifecycle/lifecycle.go b/pkg/lifecycle/lifecycle.go index 2c468260..6d7dc9a2 100644 --- a/pkg/lifecycle/lifecycle.go +++ b/pkg/lifecycle/lifecycle.go @@ -39,7 +39,11 @@ type WalkDirFunc func(ctx context.Context, fs billy.Filesystem, dirPath string, // InitDependencies will check the filesystem, branch version, // git status, initialize the Dependencies struct and populate it. // If anything fails the operation will be aborted. -func InitDependencies(ctx context.Context, repoRoot string, rootFs billy.Filesystem, branchVersion string, currentChart string) (*Dependencies, error) { +func InitDependencies(ctx context.Context, rootFs billy.Filesystem, repoRoot, branchVersion, currentChart string, newChart bool) (*Dependencies, error) { + if newChart && currentChart == "" { + return nil, errors.New("can't create a new empty chart") + } + var err error workDir := repoRoot @@ -76,6 +80,12 @@ func InitDependencies(ctx context.Context, repoRoot string, rootFs billy.Filesys return nil, err } + if newChart { + dep.AssetsVersionsMap = make(map[string][]Asset) + dep.AssetsVersionsMap[currentChart] = []Asset{} + return dep, nil + } + // Get the absolute path of the Helm index file and assets versions map to apply rules helmIndexPath := filesystem.GetAbsPath(dep.RootFs, path.RepositoryHelmIndexFile) dep.AssetsVersionsMap, err = getAssetsMapFromIndex(helmIndexPath, currentChart) diff --git a/pkg/validate/validate.go b/pkg/validate/validate.go index 43057adf..8a656f2d 100644 --- a/pkg/validate/validate.go +++ b/pkg/validate/validate.go @@ -75,7 +75,7 @@ func CompareGeneratedAssets(ctx context.Context, repoRoot string, repoFs billy.F } // Initialize lifecycle package for validating with assets lifecycle rules - lifeCycleDep, err := lifecycle.InitDependencies(ctx, repoRoot, repoFs, lifecycle.ExtractBranchVersion(branch), "") + lifeCycleDep, err := lifecycle.InitDependencies(ctx, repoFs, repoRoot, lifecycle.ExtractBranchVersion(branch), "", false) if err != nil { logger.Log(ctx, slog.LevelError, "failed to initialize lifecycle dependencies", logger.Err(err)) return response, err From f7579bcb74f9019a4ec25927560de0b700f52fc6 Mon Sep 17 00:00:00 2001 From: nicholasSUSE Date: Wed, 3 Sep 2025 21:45:27 -0300 Subject: [PATCH 2/5] implementing net new version calculation it is based on the current branch and version rules. - get the app version from the upstream chart repository. - calculate the first version of the charts repo (105, 106, etc...) skip the other calculations. --- pkg/auto/versioning.go | 77 +++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/pkg/auto/versioning.go b/pkg/auto/versioning.go index ca63cbef..17bfbe3f 100644 --- a/pkg/auto/versioning.go +++ b/pkg/auto/versioning.go @@ -4,6 +4,7 @@ import ( "context" "errors" "log/slog" + "strconv" "strings" "github.com/blang/semver" @@ -53,32 +54,41 @@ func (v *version) updateTxt() { // if the chart had a patch bump, it will increment the patch version for the repoPrefixVersion // if the chart had a minor or major bump, it will increment the minor version for the repoPrefixVersion // the major repoPrefixVersion is only bumped when Rancher version is bumped. -func (b *Bump) calculateNextVersion(ctx context.Context, versionOverride string) error { +func (b *Bump) calculateNextVersion(ctx context.Context, versionOverride string, newChart bool) error { logger.Log(ctx, slog.LevelInfo, "calculate next version") - // load versions and parse the repository prefix versions from them - if err := b.loadVersions(); err != nil { - return err - } + if newChart { + if err := b.netNewVersion(); err != nil { + return err + } + } else { + // load versions and parse the repository prefix versions from them + if err := b.loadVersions(); err != nil { + return err + } - // check and parse the versions before building the new version - if err := b.applyVersionRules(versionOverride); err != nil { - return err - } + // check and parse the versions before building the new version + if err := b.applyVersionRules(versionOverride); err != nil { + return err + } - logger.Log(ctx, slog.LevelInfo, "checking current RC's") - currentRCs, err := getCurrentRCsFromIndex(b.assetsVersionsMap[b.target.main], b.versions.toReleaseRepoPrefix.txt) - if err != nil { - logger.Log(ctx, slog.LevelError, "failed checking RC's", logger.Err(err), slog.String("version", b.target.main)) - return err - } - if currentRCs != nil && len(currentRCs) > 0 { - b.versions.currentRCs = currentRCs - logger.Log(ctx, slog.LevelWarn, "RCs present", slog.Any("amount", len(currentRCs))) - for _, rc := range currentRCs { - rcVersion := rc.repoPrefix.txt + "+up" + rc.appVersion.txt - logger.Log(ctx, slog.LevelDebug, "", slog.String("version", rcVersion)) + logger.Log(ctx, slog.LevelInfo, "checking current RC's") + currentRCs, err := getCurrentRCsFromIndex(b.assetsVersionsMap[b.target.main], b.versions.toReleaseRepoPrefix.txt) + if err != nil { + logger.Log(ctx, slog.LevelError, "failed checking RC's", logger.Err(err), slog.String("version", b.target.main)) + return err } + if currentRCs != nil && len(currentRCs) > 0 { + b.versions.currentRCs = currentRCs + logger.Log(ctx, slog.LevelWarn, "RCs present", slog.Any("amount", len(currentRCs))) + for _, rc := range currentRCs { + rcVersion := rc.repoPrefix.txt + "+up" + rc.appVersion.txt + logger.Log(ctx, slog.LevelDebug, "", slog.String("version", rcVersion)) + } + } + + logger.Log(ctx, slog.LevelDebug, "", slog.String("latestVersion", b.versions.latest.txt)) + logger.Log(ctx, slog.LevelDebug, "", slog.String("latestRepoVersion", b.versions.latestRepoPrefix.txt)) } // build: toRelease full version @@ -87,14 +97,35 @@ func (b *Bump) calculateNextVersion(ctx context.Context, versionOverride string) b.releaseYaml.ChartVersion = targetVersion b.Pkg.AutoGeneratedBumpVersion = &targetSemver - logger.Log(ctx, slog.LevelDebug, "", slog.String("latestVersion", b.versions.latest.txt)) - logger.Log(ctx, slog.LevelDebug, "", slog.String("latestRepoVersion", b.versions.latestRepoPrefix.txt)) logger.Log(ctx, slog.LevelDebug, "", slog.String("toReleaseVersion", b.versions.toRelease.txt)) logger.Log(ctx, slog.LevelDebug, "", slog.String("toReleaseRepoVersion", b.versions.toReleaseRepoPrefix.txt)) logger.Log(ctx, slog.LevelInfo, "calculated bump", slog.String("version", b.Pkg.AutoGeneratedBumpVersion.String())) return nil } +// netNewVersion will calculate the first version for a previous non-existing chart +func (b *Bump) netNewVersion() error { + b.versions = &versions{ + toRelease: &version{}, + toReleaseRepoPrefix: &version{}, + } + + b.versions.toRelease.txt = b.Pkg.Chart.GetUpstreamVersion() + if b.versions.toRelease.txt == "" { + return errChartUpstreamVersion + } + if err := b.versions.toRelease.updateSemver(); err != nil { + return err + } + + b.versions.toReleaseRepoPrefix.txt = strconv.Itoa(b.versionRules.MaxVersion-1) + ".0.0" + if err := b.versions.toReleaseRepoPrefix.updateSemver(); err != nil { + return err + } + + return nil +} + // loadVersions will load the latest version from the index.yaml and the version to release from the chart owner upstream repository // rules: // - latest version might not contain a repoPrefixVersion From adf3172ea801789709f62de104f348ad24984dab Mon Sep 17 00:00:00 2001 From: nicholasSUSE Date: Wed, 3 Sep 2025 21:46:56 -0300 Subject: [PATCH 3/5] update hard-coded chart names without this we don't accept new chart names in the auto-chart-bumps --- pkg/auto/chart_bump.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/auto/chart_bump.go b/pkg/auto/chart_bump.go index 8d421062..670b036c 100644 --- a/pkg/auto/chart_bump.go +++ b/pkg/auto/chart_bump.go @@ -51,6 +51,7 @@ var ChartTargetsMap = map[string][]string{ "sriov": {"sriov", "sriov-crd"}, "system-upgrade-controller": {"system-upgrade-controller"}, "ui-plugin-operator": {"ui-plugin-operator", "ui-plugin-operator-crd"}, + "rancher-turtles": {"rancher-turtles"}, } // Bump represents the chart bump process for a single chart From 2602de2fbe30994aa5d8f7cf3f154d6042b925a9 Mon Sep 17 00:00:00 2001 From: nicholasSUSE Date: Wed, 3 Sep 2025 21:47:46 -0300 Subject: [PATCH 4/5] implementing the auto chart bump for a net new chart --- pkg/auto/chart_bump.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pkg/auto/chart_bump.go b/pkg/auto/chart_bump.go index 670b036c..b8b46e23 100644 --- a/pkg/auto/chart_bump.go +++ b/pkg/auto/chart_bump.go @@ -125,7 +125,7 @@ var ( */ // SetupBump will load and parse all related information to the chart that should be bumped. -func SetupBump(ctx context.Context, repoRoot, targetPackage, targetBranch string, chScriptOpts *options.ChartsScriptOptions) (*Bump, error) { +func SetupBump(ctx context.Context, repoRoot, targetPackage, targetBranch string, chScriptOpts *options.ChartsScriptOptions, newChart bool) (*Bump, error) { logger.Log(ctx, slog.LevelInfo, "setup auto-chart-bump") bump := &Bump{ @@ -145,7 +145,7 @@ func SetupBump(ctx context.Context, repoRoot, targetPackage, targetBranch string } //Initialize the lifecycle dependencies because of the versioning rules and the index.yaml mapping. - dependencies, err := lifecycle.InitDependencies(ctx, repoRoot, filesystem.GetFilesystem(repoRoot), branch, bump.target.main) + dependencies, err := lifecycle.InitDependencies(ctx, filesystem.GetFilesystem(repoRoot), repoRoot, branch, bump.target.main, newChart) if err != nil { err = fmt.Errorf("failure at SetupBump: %w ", err) return bump, err @@ -211,9 +211,12 @@ func SetupBump(ctx context.Context, repoRoot, targetPackage, targetBranch string slog.Bool("DoNotRelease", bump.Pkg.DoNotRelease), slog.Bool("Auto", bump.Pkg.Auto), ), - slog.String("last version", bump.assetsVersionsMap[bump.target.main][0].Version), )) + if !newChart { + logger.Log(ctx, slog.LevelInfo, "", slog.String("last version", bump.assetsVersionsMap[bump.target.main][0].Version)) + } + return bump, nil } @@ -320,7 +323,7 @@ func checkUpstreamOptions(options *options.UpstreamOptions) error { // BumpChart will execute a similar approach as the defined development workflow for chartowners. // The main difference is that between the steps: (make prepare and make patch) we will calculate the next version to release. -func (b *Bump) BumpChart(ctx context.Context, versionOverride string, multiRCs bool) error { +func (b *Bump) BumpChart(ctx context.Context, versionOverride string, multiRCs, newChart bool) error { logger.Log(ctx, slog.LevelInfo, "start auto-chart-bump") if err := b.prepare(ctx); err != nil { @@ -328,7 +331,7 @@ func (b *Bump) BumpChart(ctx context.Context, versionOverride string, multiRCs b } // Calculate the next version to release - if err := b.calculateNextVersion(ctx, versionOverride); err != nil { + if err := b.calculateNextVersion(ctx, versionOverride, newChart); err != nil { return err } @@ -349,7 +352,7 @@ func (b *Bump) BumpChart(ctx context.Context, versionOverride string, multiRCs b } // check if should remove previous RCs versions - if !multiRCs { + if !multiRCs && !newChart { logger.Log(ctx, slog.LevelWarn, "removing existing RC's") if err := b.checkMultiRC(ctx); err != nil { return err From 49c3c9eff4a2b1e832e7cf7da155c7c3af94ad1e Mon Sep 17 00:00:00 2001 From: nicholasSUSE Date: Wed, 3 Sep 2025 22:41:14 -0300 Subject: [PATCH 5/5] updating unit-tests --- pkg/auto/versioning_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/auto/versioning_test.go b/pkg/auto/versioning_test.go index 0fe40d90..cac20ebb 100644 --- a/pkg/auto/versioning_test.go +++ b/pkg/auto/versioning_test.go @@ -402,7 +402,7 @@ func Test_calculateNextVersion(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - err := tc.input.b.calculateNextVersion(context.Background(), tc.input.versionOverride) + err := tc.input.b.calculateNextVersion(context.Background(), tc.input.versionOverride, false) assertError(t, err, tc.expected.err) if tc.expected.err == nil { assert.Equal(t, tc.expected.b.releaseYaml.ChartVersion, tc.input.b.releaseYaml.ChartVersion)