2021-09-05 20:33:07 +00:00
|
|
|
package recipe
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2021-11-06 22:40:22 +00:00
|
|
|
"path"
|
|
|
|
"strconv"
|
2021-09-05 20:33:07 +00:00
|
|
|
|
|
|
|
"coopcloud.tech/abra/cli/internal"
|
2021-12-11 23:17:39 +00:00
|
|
|
"coopcloud.tech/abra/pkg/autocomplete"
|
2021-11-06 22:40:22 +00:00
|
|
|
"coopcloud.tech/abra/pkg/config"
|
|
|
|
"coopcloud.tech/tagcmp"
|
2021-10-05 08:28:09 +00:00
|
|
|
"github.com/AlecAivazis/survey/v2"
|
2021-11-06 22:40:22 +00:00
|
|
|
"github.com/go-git/go-git/v5"
|
|
|
|
"github.com/go-git/go-git/v5/plumbing"
|
2021-09-05 20:33:07 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"github.com/urfave/cli/v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
var recipeSyncCommand = &cli.Command{
|
2021-10-08 07:51:47 +00:00
|
|
|
Name: "sync",
|
|
|
|
Usage: "Ensure recipe version labels are up-to-date",
|
|
|
|
Aliases: []string{"s"},
|
2021-11-06 21:38:29 +00:00
|
|
|
ArgsUsage: "<recipe> [<version>]",
|
2021-11-06 22:40:22 +00:00
|
|
|
Flags: []cli.Flag{
|
|
|
|
internal.DryFlag,
|
|
|
|
internal.MajorFlag,
|
|
|
|
internal.MinorFlag,
|
|
|
|
internal.PatchFlag,
|
|
|
|
},
|
2021-09-05 20:33:07 +00:00
|
|
|
Description: `
|
2021-10-08 07:51:47 +00:00
|
|
|
This command will generate labels for the main recipe service (i.e. by
|
2021-11-06 22:40:22 +00:00
|
|
|
convention, the service named "app") which corresponds to the following format:
|
2021-09-05 20:33:07 +00:00
|
|
|
|
2021-10-08 07:51:47 +00:00
|
|
|
coop-cloud.${STACK_NAME}.version=<version>
|
2021-09-05 20:33:07 +00:00
|
|
|
|
2021-10-08 07:51:47 +00:00
|
|
|
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.
|
2021-11-06 22:40:22 +00:00
|
|
|
|
|
|
|
You may invoke this command in "wizard" mode and be prompted for input:
|
|
|
|
|
2021-11-09 17:05:53 +00:00
|
|
|
abra recipe sync
|
2021-11-06 22:40:22 +00:00
|
|
|
|
2021-09-05 20:33:07 +00:00
|
|
|
`,
|
2021-11-06 22:40:22 +00:00
|
|
|
Action: func(c *cli.Context) error {
|
|
|
|
recipe := internal.ValidateRecipeWithPrompt(c)
|
|
|
|
|
|
|
|
mainApp := internal.GetMainApp(recipe)
|
|
|
|
|
|
|
|
imagesTmp, err := getImageVersions(recipe)
|
2021-10-05 08:28:09 +00:00
|
|
|
if err != nil {
|
2021-11-06 22:40:22 +00:00
|
|
|
logrus.Fatal(err)
|
2021-10-05 08:28:09 +00:00
|
|
|
}
|
2021-12-21 00:48:21 +00:00
|
|
|
|
2021-11-06 22:40:22 +00:00
|
|
|
mainAppVersion := imagesTmp[mainApp]
|
|
|
|
|
|
|
|
tags, err := recipe.Tags()
|
|
|
|
if err != nil {
|
|
|
|
logrus.Fatal(err)
|
2021-10-05 08:28:09 +00:00
|
|
|
}
|
2021-11-06 22:40:22 +00:00
|
|
|
|
|
|
|
nextTag := c.Args().Get(1)
|
|
|
|
if len(tags) == 0 && nextTag == "" {
|
2021-12-19 23:59:48 +00:00
|
|
|
logrus.Warnf("no git tags found for %s", recipe.Name)
|
|
|
|
fmt.Println(fmt.Sprintf(`
|
|
|
|
The following options are two types of initial version that you can pick for
|
|
|
|
the first published version of %s that will be in the recipe catalogue. This
|
|
|
|
follows the semver convention (more on semver.org), here is a short cheatsheet
|
|
|
|
|
|
|
|
0.1.0 -> development release, still hacking
|
|
|
|
1.0.0 -> public release, assumed to be working
|
|
|
|
|
|
|
|
In other words, if you want people to be able alpha test your current config
|
|
|
|
for %s but don't think it is quite ready and reliable, go with 0.1.0 and people
|
|
|
|
will know that things are likely to change.
|
|
|
|
|
|
|
|
`, recipe.Name, recipe.Name))
|
2021-11-06 22:40:22 +00:00
|
|
|
var chosenVersion string
|
|
|
|
edPrompt := &survey.Select{
|
|
|
|
Message: "which version do you want to begin with?",
|
|
|
|
Options: []string{"0.1.0", "1.0.0"},
|
|
|
|
}
|
2021-12-21 00:48:21 +00:00
|
|
|
|
2021-11-06 22:40:22 +00:00
|
|
|
if err := survey.AskOne(edPrompt, &chosenVersion); err != nil {
|
|
|
|
logrus.Fatal(err)
|
|
|
|
}
|
2021-12-21 00:48:21 +00:00
|
|
|
|
2021-11-06 22:40:22 +00:00
|
|
|
nextTag = fmt.Sprintf("%s+%s", chosenVersion, mainAppVersion)
|
2021-10-05 08:28:09 +00:00
|
|
|
}
|
2021-11-06 22:40:22 +00:00
|
|
|
|
|
|
|
if nextTag == "" && (!internal.Major && !internal.Minor && !internal.Patch) {
|
|
|
|
if err := internal.PromptBumpType(""); err != nil {
|
|
|
|
logrus.Fatal(err)
|
|
|
|
}
|
2021-10-08 07:51:47 +00:00
|
|
|
}
|
|
|
|
|
2021-11-06 22:40:22 +00:00
|
|
|
if nextTag == "" {
|
|
|
|
recipeDir := path.Join(config.APPS_DIR, recipe.Name)
|
|
|
|
repo, err := git.PlainOpen(recipeDir)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Fatal(err)
|
|
|
|
}
|
2021-12-21 00:48:21 +00:00
|
|
|
|
2021-11-06 22:40:22 +00:00
|
|
|
var lastGitTag tagcmp.Tag
|
|
|
|
iter, err := repo.Tags()
|
|
|
|
if err != nil {
|
|
|
|
logrus.Fatal(err)
|
|
|
|
}
|
2021-12-21 00:48:21 +00:00
|
|
|
|
2021-11-06 22:40:22 +00:00
|
|
|
if err := iter.ForEach(func(ref *plumbing.Reference) error {
|
|
|
|
obj, err := repo.TagObject(ref.Hash())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-12-21 00:48:21 +00:00
|
|
|
|
2021-11-06 22:40:22 +00:00
|
|
|
tagcmpTag, err := tagcmp.Parse(obj.Name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-12-21 00:48:21 +00:00
|
|
|
|
2021-11-06 22:40:22 +00:00
|
|
|
if (lastGitTag == tagcmp.Tag{}) {
|
|
|
|
lastGitTag = tagcmpTag
|
|
|
|
} else if tagcmpTag.IsGreaterThan(lastGitTag) {
|
|
|
|
lastGitTag = tagcmpTag
|
|
|
|
}
|
2021-12-21 00:48:21 +00:00
|
|
|
|
2021-11-06 22:40:22 +00:00
|
|
|
return nil
|
|
|
|
}); err != nil {
|
|
|
|
logrus.Fatal(err)
|
|
|
|
}
|
2021-09-05 20:33:07 +00:00
|
|
|
|
2021-11-06 22:40:22 +00:00
|
|
|
// 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 {
|
2021-12-21 00:48:21 +00:00
|
|
|
logrus.Fatal("you can only use one version flag: --major, --minor or --patch")
|
2021-11-06 22:40:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
newTag := lastGitTag
|
|
|
|
if bumpType > 0 {
|
|
|
|
if internal.Patch {
|
|
|
|
now, err := strconv.Atoi(newTag.Patch)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Fatal(err)
|
|
|
|
}
|
2021-12-21 00:48:21 +00:00
|
|
|
|
2021-11-06 22:40:22 +00:00
|
|
|
newTag.Patch = strconv.Itoa(now + 1)
|
|
|
|
} else if internal.Minor {
|
|
|
|
now, err := strconv.Atoi(newTag.Minor)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Fatal(err)
|
|
|
|
}
|
2021-12-21 00:48:21 +00:00
|
|
|
|
2021-11-06 22:40:22 +00:00
|
|
|
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)
|
|
|
|
}
|
2021-12-21 00:48:21 +00:00
|
|
|
|
2021-11-06 22:40:22 +00:00
|
|
|
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)
|
|
|
|
}
|
2021-10-05 08:28:09 +00:00
|
|
|
|
2021-10-08 07:51:47 +00:00
|
|
|
mainService := "app"
|
|
|
|
label := fmt.Sprintf("coop-cloud.${STACK_NAME}.version=%s", nextTag)
|
2021-11-06 22:40:22 +00:00
|
|
|
if !internal.Dry {
|
2021-12-21 00:48:37 +00:00
|
|
|
if err := recipe.UpdateLabel("compose.y*ml", mainService, label); err != nil {
|
2021-11-06 22:40:22 +00:00
|
|
|
logrus.Fatal(err)
|
|
|
|
}
|
|
|
|
} else {
|
2021-12-20 00:05:51 +00:00
|
|
|
logrus.Infof("dry run: not syncing label %s for recipe %s", nextTag, recipe.Name)
|
2021-10-05 08:28:09 +00:00
|
|
|
}
|
|
|
|
|
2021-09-05 20:33:07 +00:00
|
|
|
return nil
|
|
|
|
},
|
2021-12-11 23:17:39 +00:00
|
|
|
BashComplete: autocomplete.RecipeNameComplete,
|
2021-09-05 20:33:07 +00:00
|
|
|
}
|