diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 0000000..2f3c953 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,6 @@ +tasks: + - init: | + curl -L https://github.com/linuxsuren/http-downloader/releases/latest/download/hd-linux-amd64.tar.gz | tar xzv + git checkout README.md + sudo install hd /usr/bin + hd install upx diff --git a/controllers/releaser_controller.go b/controllers/releaser_controller.go index 48aa459..654ede5 100644 --- a/controllers/releaser_controller.go +++ b/controllers/releaser_controller.go @@ -29,7 +29,6 @@ import ( "sigs.k8s.io/yaml" "time" - "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -41,7 +40,6 @@ import ( type ReleaserReconciler struct { logger logr.Logger client.Client - Scheme *runtime.Scheme GitCacheDir string gitUser string @@ -169,18 +167,41 @@ func (r *ReleaserReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -func (r *ReleaserReconciler) markAsDone(secret *v1.Secret, releaser *devopsv1alpha1.Releaser) (err error) { - gitOps := releaser.Spec.GitOps - if gitOps == nil || !gitOps.Enable { - releaser.Spec.Phase = devopsv1alpha1.PhaseDone - if err = r.Update(context.TODO(), releaser); err == nil { - nextReleaser := releaser.DeepCopy() - bumpReleaser(nextReleaser) +func (r *ReleaserReconciler) bumpResource(releaser *devopsv1alpha1.Releaser) (err error) { + ctx := context.Background() + releaser.Spec.Phase = devopsv1alpha1.PhaseDone + if err = r.Update(context.TODO(), releaser); err == nil { + nextReleaser := releaser.DeepCopy() + isPre := bumpReleaser(nextReleaser, true) + + if err = r.Create(ctx, nextReleaser); err != nil { + err = fmt.Errorf("failed to create next releaser: %s, error: %v", nextReleaser.GetName(), err) + } - if err = r.Create(context.TODO(), nextReleaser); err != nil { - err = fmt.Errorf("failed to create next releaser: %s, error: %v", nextReleaser.GetName(), err) + if err == nil && isPre { + nextReleaserWithoutPre := releaser.DeepCopy() + bumpReleaser(nextReleaserWithoutPre, false) + + err = r.Get(ctx, types.NamespacedName{ + Namespace: nextReleaserWithoutPre.Namespace, + Name: nextReleaserWithoutPre.Name, + }, &devopsv1alpha1.Releaser{}) + if err != nil { + if client.IgnoreNotFound(err) == nil { + if err = r.Create(ctx, nextReleaserWithoutPre); err != nil { + err = fmt.Errorf("failed to create next releaser: %s, error: %v", nextReleaserWithoutPre.GetName(), err) + } + } } } + } + return +} + +func (r *ReleaserReconciler) markAsDone(secret *v1.Secret, releaser *devopsv1alpha1.Releaser) (err error) { + gitOps := releaser.Spec.GitOps + if gitOps == nil || !gitOps.Enable { + err = r.bumpResource(releaser) return } @@ -219,7 +240,6 @@ func (r *ReleaserReconciler) markAsDone(secret *v1.Secret, releaser *devopsv1alp copiedReleaser.ObjectMeta.ResourceVersion = "" data, _ = yaml.Marshal(copiedReleaser) - r.logger.Info("start to commit phase to be done", "name", releaser.Name) if err = saveAndPush(gitRepo, r.gitUser, currentReleaserPath, data, secret, fmt.Sprintf("release %s", releaser.Name)); err != nil { @@ -229,13 +249,28 @@ func (r *ReleaserReconciler) markAsDone(secret *v1.Secret, releaser *devopsv1alp r.logger.Info("start to create next release file") var bumpFilename string - if data, bumpFilename, err = bumpReleaserAsData(data); err != nil { + var isPre bool + if data, bumpFilename, isPre, err = bumpReleaserAsData(data, true); err != nil { err = fmt.Errorf("failed to bump releaser: %s, error: %v", currentReleaserPath, err) } else { bumpFilePath := path.Join(path.Dir(currentReleaserPath), bumpFilename) err = saveAndPush(gitRepo, r.gitUser, bumpFilePath, data, secret, fmt.Sprintf("prepare the next release of %s", releaser.Name)) } + + // try to prepare the next version which is not a preRlease + if err == nil && isPre { + if data, bumpFilename, _, err = bumpReleaserAsData(data, false); err != nil { + err = fmt.Errorf("failed to bump releaser: %s, error: %v", currentReleaserPath, err) + } else { + bumpFilePath := path.Join(path.Dir(currentReleaserPath), bumpFilename) + if ok, _ := PathExists(bumpFilePath); !ok { + err = saveAndPush(gitRepo, r.gitUser, bumpFilePath, data, secret, + fmt.Sprintf("prepare the next release of %s", releaser.Name)) + } + } + + } return } diff --git a/controllers/releaser_controller_test.go b/controllers/releaser_controller_test.go new file mode 100644 index 0000000..5afb271 --- /dev/null +++ b/controllers/releaser_controller_test.go @@ -0,0 +1,93 @@ +package controllers + +import ( + "context" + "fmt" + "github.com/go-logr/logr" + devopsv1alpha1 "github.com/kubesphere-sigs/ks-releaser/api/v1alpha1" + "github.com/stretchr/testify/assert" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "testing" +) + +func TestReleaserReconciler_bumpResource(t *testing.T) { + schema, err := devopsv1alpha1.SchemeBuilder.Register().Build() + assert.Nil(t, err) + + defaultReleaser := &devopsv1alpha1.Releaser{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "fake", + Name: "fake-v3.3.0-alpha.0", + ResourceVersion: "123", + }, + Spec: devopsv1alpha1.ReleaserSpec{ + Version: "v3.3.0-alpha.0", + }, + } + + type fields struct { + logger logr.Logger + Client client.Client + GitCacheDir string + gitUser string + } + type args struct { + releaser *devopsv1alpha1.Releaser + } + tests := []struct { + name string + fields fields + args args + wantErr assert.ErrorAssertionFunc + verify func(t *testing.T, Client client.Client) + }{{ + name: "pre-release version", + fields: fields{ + Client: fake.NewFakeClientWithScheme(schema, defaultReleaser.DeepCopy()), + }, + args: args{ + releaser: defaultReleaser.DeepCopy(), + }, + wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { + return false + }, + verify: func(t *testing.T, Client client.Client) { + item := &devopsv1alpha1.Releaser{} + err := Client.Get(context.Background(), types.NamespacedName{ + Namespace: "fake", + Name: "fake-v3.3.0-alpha.0", + }, item) + assert.Nil(t, err) + assert.Equal(t, devopsv1alpha1.PhaseDone, item.Spec.Phase) + + err = Client.Get(context.Background(), types.NamespacedName{ + Namespace: "fake", + Name: "fake-v3.3.0-alpha.1", + }, item) + assert.Nil(t, err) + assert.Equal(t, devopsv1alpha1.PhaseDraft, item.Spec.Phase) + + err = Client.Get(context.Background(), types.NamespacedName{ + Namespace: "fake", + Name: "fake-v3.3.0", + }, item) + assert.Nil(t, err) + assert.Equal(t, devopsv1alpha1.PhaseDraft, item.Spec.Phase) + }, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &ReleaserReconciler{ + logger: tt.fields.logger, + Client: tt.fields.Client, + GitCacheDir: tt.fields.GitCacheDir, + gitUser: tt.fields.gitUser, + } + tt.wantErr(t, r.bumpResource(tt.args.releaser), fmt.Sprintf("bumpResource(%v)", tt.args.releaser)) + tt.verify(t, tt.fields.Client) + }) + } +} diff --git a/controllers/version.go b/controllers/version.go index b58b251..fe0871d 100644 --- a/controllers/version.go +++ b/controllers/version.go @@ -15,7 +15,7 @@ func isPreRelease(versionStr string) bool { return false } -func bumpVersion(versionStr string) (nextVersion string, err error) { +func bumpVersionTo(versionStr string, remainPre bool) (nextVersion string, isPre bool, err error) { nextVersion = versionStr // keep using the old version if there's any problem happened var version semver.Version @@ -25,12 +25,17 @@ func bumpVersion(versionStr string) (nextVersion string, err error) { } if preVersionCount := len(version.Pre); preVersionCount > 0 { - for i := preVersionCount - 1; i >= 0; i-- { - preVersion := &version.Pre[i] - if preVersion.IsNumeric() { - preVersion.VersionNum += 1 - break + isPre = true + if remainPre { + for i := preVersionCount - 1; i >= 0; i-- { + preVersion := &version.Pre[i] + if preVersion.IsNumeric() { + preVersion.VersionNum += 1 + break + } } + } else { + version.Pre = nil } } else { version.Patch += 1 @@ -43,9 +48,16 @@ func bumpVersion(versionStr string) (nextVersion string, err error) { return } -func bumpReleaser(releaser *devopsv1alpha1.Releaser) { +func bumpVersion(versionStr string) (nextVersion string, isPre bool, err error) { + nextVersion, isPre, err = bumpVersionTo(versionStr, true) + return +} + +func bumpReleaser(releaser *devopsv1alpha1.Releaser, remainPre bool) (isPre bool) { + var nextVersion string + currentVersion := releaser.Spec.Version - nextVersion, _ := bumpVersion(currentVersion) + nextVersion, isPre, _ = bumpVersionTo(currentVersion, remainPre) if strings.HasSuffix(releaser.Name, currentVersion) { nameWithoutVersion := strings.ReplaceAll(releaser.Name, currentVersion, "") releaser.Name = nameWithoutVersion + nextVersion @@ -62,7 +74,7 @@ func bumpReleaser(releaser *devopsv1alpha1.Releaser) { for i, _ := range releaser.Spec.Repositories { repo := &releaser.Spec.Repositories[i] - repo.Version, _ = bumpVersion(repo.Version) + repo.Version, _, _ = bumpVersionTo(repo.Version, remainPre) } // remove status @@ -70,10 +82,10 @@ func bumpReleaser(releaser *devopsv1alpha1.Releaser) { return } -func bumpReleaserAsData(data []byte) (result []byte, filename string, err error) { +func bumpReleaserAsData(data []byte, remainPre bool) (result []byte, filename string, isPre bool, err error) { targetReleaser := &devopsv1alpha1.Releaser{} if err = yaml.Unmarshal(data, targetReleaser); err == nil { - bumpReleaser(targetReleaser) + isPre = bumpReleaser(targetReleaser, remainPre) filename = targetReleaser.Name + ".yaml" result, err = yaml.Marshal(targetReleaser) } diff --git a/controllers/version_test.go b/controllers/version_test.go index 7f72c5c..498f2a1 100644 --- a/controllers/version_test.go +++ b/controllers/version_test.go @@ -67,7 +67,7 @@ func TestVersionBump(t *testing.T) { for i, _ := range testCases { caseItem := testCases[i] - nextVersion, err := bumpVersion(caseItem.arg.version) + nextVersion, _, err := bumpVersion(caseItem.arg.version) if caseItem.wantErr { assert.NotNil(t, err, fmt.Sprintf("test failed with case[%d]", i)) } @@ -137,7 +137,7 @@ func Test_bumpReleaser(t *testing.T) { }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - bumpReleaser(tt.args.releaser) + bumpReleaser(tt.args.releaser, true) if !reflect.DeepEqual(tt.args.releaser, tt.wantResult) { t.Errorf("bumpReleaser() gotResult = %v, want %v", tt.args.releaser, tt.wantResult) } @@ -180,7 +180,7 @@ status: {} }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - gotResult, _, err := bumpReleaserAsData([]byte(tt.args.data)) + gotResult, _, _, err := bumpReleaserAsData([]byte(tt.args.data), true) if (err != nil) != tt.wantErr { t.Errorf("bumpReleaserAsData() error = %v, wantErr %v", err, tt.wantErr) return @@ -239,3 +239,39 @@ func Test_isPreRelease(t *testing.T) { }) } } + +func Test_bumpVersionTo(t *testing.T) { + type testCase struct { + name string + arg struct { + version string + remainPre bool + } + wantErr bool + wantVersion string + } + + testCases := []testCase{{ + name: "version with rc tail", + arg: struct { + version string + remainPre bool + }{ + version: "v1.2.0-rc.0", + remainPre: false, + }, + wantErr: false, + wantVersion: "v1.2.0", + }} + + for i, _ := range testCases { + caseItem := testCases[i] + + nextVersion, _, err := bumpVersionTo(caseItem.arg.version, caseItem.arg.remainPre) + if caseItem.wantErr { + assert.NotNil(t, err, fmt.Sprintf("test failed with case[%d]", i)) + } + + assert.Equal(t, caseItem.wantVersion, nextVersion, fmt.Sprintf("test failed with case[%d]", i)) + } +} diff --git a/main.go b/main.go index ef19e1f..20a8ec4 100644 --- a/main.go +++ b/main.go @@ -80,7 +80,6 @@ func main() { if err = (&controllers.ReleaserReconciler{ Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), GitCacheDir: "tmp", }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Releaser")