abra/cli/recipe/sync.go

186 lines
4.8 KiB
Go

package recipe
import (
"fmt"
"path"
"strconv"
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/tagcmp"
"github.com/AlecAivazis/survey/v2"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)
var recipeSyncCommand = &cli.Command{
Name: "sync",
Usage: "Ensure recipe version labels are up-to-date",
Aliases: []string{"s"},
ArgsUsage: "<recipe> [<version>]",
Flags: []cli.Flag{
internal.DryFlag,
internal.MajorFlag,
internal.MinorFlag,
internal.PatchFlag,
},
Description: `
This command will generate labels for the main recipe service (i.e. by
convention, the service named "app") which corresponds to the following format:
coop-cloud.${STACK_NAME}.version=<version>
The <version> is determined by the recipe maintainer and is specified on the
command-line. The <recipe> configuration will be updated on the local file
system.
You may invoke this command in "wizard" mode and be prompted for input:
abra recipe sync
`,
Action: func(c *cli.Context) error {
recipe := internal.ValidateRecipeWithPrompt(c)
mainApp := internal.GetMainApp(recipe)
imagesTmp, err := getImageVersions(recipe)
if err != nil {
logrus.Fatal(err)
}
mainAppVersion := imagesTmp[mainApp]
tags, err := recipe.Tags()
if err != nil {
logrus.Fatal(err)
}
nextTag := c.Args().Get(1)
if len(tags) == 0 && nextTag == "" {
logrus.Warnf("no tags found for %s", recipe.Name)
var chosenVersion string
edPrompt := &survey.Select{
Message: "which version do you want to begin with?",
Options: []string{"0.1.0", "1.0.0"},
}
if err := survey.AskOne(edPrompt, &chosenVersion); err != nil {
logrus.Fatal(err)
}
nextTag = fmt.Sprintf("%s+%s", chosenVersion, mainAppVersion)
}
if nextTag == "" && (!internal.Major && !internal.Minor && !internal.Patch) {
if err := internal.PromptBumpType(""); err != nil {
logrus.Fatal(err)
}
}
if nextTag == "" {
recipeDir := path.Join(config.APPS_DIR, recipe.Name)
repo, err := git.PlainOpen(recipeDir)
if err != nil {
logrus.Fatal(err)
}
var lastGitTag tagcmp.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 {
return err
}
tagcmpTag, err := tagcmp.Parse(obj.Name)
if err != nil {
return err
}
if (lastGitTag == tagcmp.Tag{}) {
lastGitTag = tagcmpTag
} else if tagcmpTag.IsGreaterThan(lastGitTag) {
lastGitTag = tagcmpTag
}
return nil
}); err != nil {
logrus.Fatal(err)
}
// bumpType is used to decide what part of the tag should be incremented
bumpType := btoi(internal.Major)*4 + btoi(internal.Minor)*2 + btoi(internal.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.")
}
}
newTag := lastGitTag
if bumpType > 0 {
if internal.Patch {
now, err := strconv.Atoi(newTag.Patch)
if err != nil {
logrus.Fatal(err)
}
newTag.Patch = strconv.Itoa(now + 1)
} else if internal.Minor {
now, err := strconv.Atoi(newTag.Minor)
if err != nil {
logrus.Fatal(err)
}
newTag.Patch = "0"
newTag.Minor = strconv.Itoa(now + 1)
} else if internal.Major {
now, err := strconv.Atoi(newTag.Major)
if err != nil {
logrus.Fatal(err)
}
newTag.Patch = "0"
newTag.Minor = "0"
newTag.Major = strconv.Itoa(now + 1)
}
}
newTag.Metadata = mainAppVersion
logrus.Debugf("choosing %s as new version for %s", newTag.String(), recipe.Name)
nextTag = newTag.String()
}
if _, err := tagcmp.Parse(nextTag); err != nil {
logrus.Fatalf("invalid version %s specified", nextTag)
}
mainService := "app"
var services []string
hasAppService := false
for _, service := range recipe.Config.Services {
services = append(services, service.Name)
if service.Name == "app" {
hasAppService = true
logrus.Debugf("detected app service in %s", recipe.Name)
}
}
if !hasAppService {
logrus.Fatalf("%s has no main 'app' service?", recipe.Name)
}
logrus.Debugf("selecting %s as the service to sync version label", mainService)
label := fmt.Sprintf("coop-cloud.${STACK_NAME}.version=%s", nextTag)
if !internal.Dry {
if err := recipe.UpdateLabel(mainService, label); err != nil {
logrus.Fatal(err)
}
logrus.Infof("synced label '%s' to service '%s'", label, mainService)
} else {
logrus.Infof("dry run only: NOT syncing label %s for recipe %s", nextTag, recipe.Name)
}
return nil
},
BashComplete: autocomplete.RecipeNameComplete,
}