forked from toolshed/abra
		
	
		
			
				
	
	
		
			200 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			200 lines
		
	
	
		
			5.3 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"
 | 
						|
)
 | 
						|
 | 
						|
var recipeSyncCommand = cli.Command{
 | 
						|
	Name:      "sync",
 | 
						|
	Aliases:   []string{"s"},
 | 
						|
	Usage:     "Sync recipe version label",
 | 
						|
	ArgsUsage: "<recipe> [<version>]",
 | 
						|
	Flags: []cli.Flag{
 | 
						|
		internal.DebugFlag,
 | 
						|
		internal.NoInputFlag,
 | 
						|
		internal.DryFlag,
 | 
						|
		internal.MajorFlag,
 | 
						|
		internal.MinorFlag,
 | 
						|
		internal.PatchFlag,
 | 
						|
	},
 | 
						|
	Before: internal.SubCommandBefore,
 | 
						|
	Description: `
 | 
						|
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>
 | 
						|
 | 
						|
Where <version> can be specifed on the command-line or Abra can attempt to
 | 
						|
auto-generate it for you. The <recipe> configuration will be updated on the
 | 
						|
local file system.
 | 
						|
`,
 | 
						|
	Action: func(c *cli.Context) error {
 | 
						|
		recipe := internal.ValidateRecipeWithPrompt(c, false)
 | 
						|
 | 
						|
		mainApp, err := internal.GetMainAppImage(recipe)
 | 
						|
		if err != nil {
 | 
						|
			logrus.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		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 git tags found for %s", recipe.Name)
 | 
						|
			fmt.Println(fmt.Sprintf(`
 | 
						|
The following options are two types of initial semantic version that you can
 | 
						|
pick for %s that will be published in the recipe catalogue. This follows the
 | 
						|
semver convention (more on https://semver.org), here is a short cheatsheet
 | 
						|
 | 
						|
    0.1.0: development release, still hacking. when you make a major upgrade
 | 
						|
           you increment the "y" part (i.e. 0.1.0 -> 0.2.0) and only move to
 | 
						|
           using the "x" part when things are stable.
 | 
						|
 | 
						|
    1.0.0: public release, assumed to be working. you already have a stable
 | 
						|
           and reliable deployment of this app and feel relatively confident
 | 
						|
           about it.
 | 
						|
 | 
						|
If you want people to be able alpha test your current config for %s but don't
 | 
						|
think it is quite reliable, go with 0.1.0 and people will know that things are
 | 
						|
likely to change.
 | 
						|
 | 
						|
`, recipe.Name, 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) {
 | 
						|
			latestRelease := tags[len(tags)-1]
 | 
						|
			if err := internal.PromptBumpType("", latestRelease); err != nil {
 | 
						|
				logrus.Fatal(err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if nextTag == "" {
 | 
						|
			recipeDir := path.Join(config.RECIPES_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 version flag: --major, --minor or --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"
 | 
						|
		label := fmt.Sprintf("coop-cloud.${STACK_NAME}.version=%s", nextTag)
 | 
						|
		if !internal.Dry {
 | 
						|
			if err := recipe.UpdateLabel("compose.y*ml", mainService, label); err != nil {
 | 
						|
				logrus.Fatal(err)
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			logrus.Infof("dry run: not syncing label %s for recipe %s", nextTag, recipe.Name)
 | 
						|
		}
 | 
						|
 | 
						|
		return nil
 | 
						|
	},
 | 
						|
	BashComplete: autocomplete.RecipeNameComplete,
 | 
						|
}
 |