feat: tag recipes with abra #99

Merged
decentral1se merged 5 commits from knoflook/abra:recipe-release into main 2021-09-29 12:39:36 +00:00
Showing only changes of commit c0f92ca13d - Show all commits

View File

@ -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",
knoflook marked this conversation as resolved Outdated

This block is all done in internal.ValidateRecipe(c) already.

This block is all done in `internal.ValidateRecipe(c)` already.
Value: false,
Aliases: []string{"mi", "y"},
Destination: &Minor,
}
var Patch bool
var PatchFlag = &cli.BoolFlag{
Name: "patch",
Value: false,
Aliases: []string{"p", "z"},
knoflook marked this conversation as resolved Outdated

The thing is, most versions are not strictly semver so we'd only be able to cover a tiny amount of the recipes we have. But we've already got tagcmp which can handle discovering what is a patch/minor/major upgrade and these formats are supported. So then we just need to check if it IsParsable?

The thing is, most versions are not strictly semver so we'd only be able to cover a tiny amount of the recipes we have. But we've already got `tagcmp` which can handle discovering what is a patch/minor/major upgrade and [these formats](https://git.coopcloud.tech/coop-cloud/tagcmp#types-of-versions-supported) are supported. So then we just need to check if it [`IsParsable`](https://git.coopcloud.tech/coop-cloud/tagcmp/src/branch/main/godoc.md#user-content-func-isparsable)?
Destination: &Patch,
}
var recipeReleaseCommand = &cli.Command{
Name: "release",
Usage: "tag a recipe",
@ -31,25 +56,15 @@ var recipeReleaseCommand = &cli.Command{
ArgsUsage: "<recipe> [<tag>]",
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)
knoflook marked this conversation as resolved Outdated

(from recipe.EnsureVersion)

repo, err := git.PlainOpen(directory)
if err != nil {
    return err
}

tags, err := repo.Tags()
if err != nil {
    return nil
}
(from `recipe.EnsureVersion`) ```golang repo, err := git.PlainOpen(directory) if err != nil { return err } tags, err := repo.Tags() if err != nil { return nil } ```
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
}