From c0f92ca13d5325d74d31139a1c3624a3f7dc6df0 Mon Sep 17 00:00:00 2001 From: knoflook Date: Thu, 23 Sep 2021 18:27:19 +0200 Subject: [PATCH] feat: support --major/-x --minor/-y --patch/-z for tag calculation --- cli/recipe/release.go | 168 ++++++++++++++++++++++++++++++------------ 1 file changed, 121 insertions(+), 47 deletions(-) diff --git a/cli/recipe/release.go b/cli/recipe/release.go index d675ca41..6569d635 100644 --- a/cli/recipe/release.go +++ b/cli/recipe/release.go @@ -2,7 +2,6 @@ package recipe import ( "fmt" - "os" "path" "strconv" "strings" @@ -10,6 +9,7 @@ import ( "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/recipe" + "coopcloud.tech/tagcmp" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" @@ -21,9 +21,34 @@ var Dry bool var DryFlag = &cli.BoolFlag{ Name: "dry-run", Value: false, + Aliases: []string{"d"}, Destination: &Dry, } +var Major bool +var MajorFlag = &cli.BoolFlag{ + Name: "major", + Value: false, + Aliases: []string{"ma", "x"}, + Destination: &Major, +} + +var Minor bool +var MinorFlag = &cli.BoolFlag{ + Name: "minor", + Value: false, + Aliases: []string{"mi", "y"}, + Destination: &Minor, +} + +var Patch bool +var PatchFlag = &cli.BoolFlag{ + Name: "patch", + Value: false, + Aliases: []string{"p", "z"}, + Destination: &Patch, +} + var recipeReleaseCommand = &cli.Command{ Name: "release", Usage: "tag a recipe", @@ -31,25 +56,15 @@ var recipeReleaseCommand = &cli.Command{ ArgsUsage: " []", Flags: []cli.Flag{ DryFlag, + PatchFlag, + MinorFlag, + MajorFlag, }, Action: func(c *cli.Context) error { recipe := internal.ValidateRecipe(c) - directory := path.Join(config.APPS_DIR, recipe.Name) - if _, err := os.Stat(directory); os.IsNotExist(err) { - logrus.Fatalf("recipe doesn't exist at path %s.", directory) - return nil - } - - images := getImageVersions(recipe) - tag := c.Args().Get(1) - if tag == "" { - for name, version := range images { - if !isSemver(version) { - logrus.Fatal(fmt.Sprintf("app %s: version number %s is not in the format x.y.z (where x,y,z are integers) - unable to generate tags, please specify a tag yourself.", name, version)) - } - } - } + tagstring := c.Args().Get(1) + imagesTmp := getImageVersions(recipe) repo, err := git.PlainOpen(directory) if err != nil { @@ -59,42 +74,107 @@ var recipeReleaseCommand = &cli.Command{ if err != nil { logrus.Fatal(err) } - // get the latest tag with its hash, name etc - if tag == "" { - var lastTag *object.Tag - iter, err := repo.Tags() + + if tagstring != "" { + repo, err := git.PlainOpen(directory) if err != nil { logrus.Fatal(err) } - // TODO: This is some magic and I have no idea what's going on but it does the job. Re-write if this looks stupid to you. Copied from the docs /knoflook - if err := iter.ForEach(func(ref *plumbing.Reference) error { - obj, err := repo.TagObject(ref.Hash()) - switch err { - case nil: - lastTag = obj - break - case plumbing.ErrObjectNotFound: - logrus.Fatal(err) - default: - return err - } - return nil - }); err != nil { + head, err := repo.Head() + if err != nil { logrus.Fatal(err) } + if Dry { + logrus.Info(fmt.Sprintf("Dry run only. NOT creating tag %s at %s", tagstring, head.Hash())) + return nil + } + + repo.CreateTag(tagstring, head.Hash(), nil) /* &git.CreateTagOptions{ + Message: tag, + })*/ + logrus.Info(fmt.Sprintf("Created tag %s at %s.", tagstring, head.Hash())) + + return nil + } + + // bumpType is used to decide what part of the tag should be incremented + bumpType := btoi(Major)*4 + btoi(Minor)*2 + btoi(Patch) + if bumpType != 0 { + // a bitwise check if the number is a power of 2 + if (bumpType & (bumpType - 1)) != 0 { + logrus.Fatal("you can only use one of: --major, --minor, --patch.") + } + } + + // get the latest tag with its hash, name etc + var lastGitTag *object.Tag + iter, err := repo.Tags() + if err != nil { + logrus.Fatal(err) + } + if err := iter.ForEach(func(ref *plumbing.Reference) error { + obj, err := repo.TagObject(ref.Hash()) + if err == nil { + lastGitTag = obj + return nil + } + return err + + }); err != nil { + logrus.Fatal(err) + } + + newTag, err := tagcmp.Parse(lastGitTag.Name) + if err != nil { + logrus.Fatal(err) + } + + var newTagString string + if bumpType > 0 { + if Patch { + now, err := strconv.Atoi(newTag.Patch) + if err != nil { + logrus.Fatal(err) + } + newTag.Patch = strconv.Itoa(now + 1) + } else if Minor { + now, err := strconv.Atoi(newTag.Minor) + if err != nil { + logrus.Fatal(err) + } + newTag.Minor = strconv.Itoa(now + 1) + } else if Major { + now, err := strconv.Atoi(newTag.Major) + if err != nil { + logrus.Fatal(err) + } + newTag.Major = strconv.Itoa(now + 1) + } + newTagString = newTag.String() + } else { + // calculate the new tag + var images = make(map[string]tagcmp.Tag) + for name, version := range imagesTmp { + t, err := tagcmp.Parse(version) + if err != nil { + logrus.Fatal(err) + } + images[name] = t + } } if Dry { - logrus.Info(fmt.Sprintf("Dry run only. NOT creating tag %s at %s", tag, head.Hash())) + logrus.Info(fmt.Sprintf("Dry run only. NOT creating tag %s at %s", newTagString, head.Hash())) return nil } - repo.CreateTag(tag, head.Hash(), nil) /* &git.CreateTagOptions{ + repo.CreateTag(newTagString, head.Hash(), nil) /* &git.CreateTagOptions{ Message: tag, })*/ - logrus.Info(fmt.Sprintf("Created tag %s at %s.", tag, head.Hash())) + logrus.Info(fmt.Sprintf("Created tag %s at %s.", newTagString, head.Hash())) return nil + }, } @@ -109,15 +189,9 @@ func getImageVersions(recipe recipe.Recipe) map[string]string { return services } -func isSemver(ver string) bool { - numbers := strings.Split(ver, ".") - if len(numbers) > 3 { - return false +func btoi(b bool) int { + if b { + return 1 } - for _, part := range numbers { - if _, err := strconv.Atoi(part); err != nil { - return false - } - } - return true + return 0 }