refactor!: simplifying publish logic
continuous-integration/drone/push Build is passing Details

This commit is contained in:
decentral1se 2021-12-27 19:56:27 +01:00
parent eb1b6be4c5
commit 0aa37fcee8
Signed by: decentral1se
GPG Key ID: 03789458B3D0C410
10 changed files with 137 additions and 168 deletions

View File

@ -13,7 +13,6 @@ import (
gitPkg "coopcloud.tech/abra/pkg/git"
"coopcloud.tech/abra/pkg/limit"
"coopcloud.tech/abra/pkg/recipe"
"github.com/AlecAivazis/survey/v2"
"github.com/go-git/go-git/v5"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
@ -59,11 +58,9 @@ var CatalogueSkipList = map[string]bool{
var catalogueGenerateCommand = &cli.Command{
Name: "generate",
Aliases: []string{"g"},
Usage: "Generate a new copy of the recipe catalogue",
Usage: "Generate the recipe catalogue",
Flags: []cli.Flag{
internal.PushFlag,
internal.CommitFlag,
internal.CommitMessageFlag,
internal.PublishFlag,
internal.DryFlag,
internal.SkipUpdatesFlag,
internal.RegistryUsernameFlag,
@ -81,14 +78,13 @@ metadata and produces a recipes JSON file.
It is possible to generate new metadata for a single recipe by passing
<recipe>. The existing local catalogue will be updated, not overwritten.
A new catalogue copy can be published to the recipes repository by passing the
"--commit" and "--push" flags. The recipes repository is available here:
https://git.coopcloud.tech/coop-cloud/recipes
It is quite easy to get rate limited by Docker Hub when running this command.
If you have a Hub account you can have Abra log you in to avoid this. Pass
"--username" and "--password".
"--user" and "--pass".
Push your new release git.coopcloud.tech with "-p/--publish". This requires
that you have permission to git push to these repositories and have your SSH
keys configured on your account.
`,
ArgsUsage: "[<recipe>]",
Action: func(c *cli.Context) error {
@ -197,37 +193,43 @@ If you have a Hub account you can have Abra log you in to avoid this. Pass
logrus.Infof("generated new recipe catalogue in %s", config.RECIPES_JSON)
if internal.Commit {
if internal.CommitMessage == "" && !internal.NoInput {
prompt := &survey.Input{
Message: "commit message",
Default: fmt.Sprintf("chore: publish catalogue changes"),
}
if err := survey.AskOne(prompt, &internal.CommitMessage); err != nil {
logrus.Fatal(err)
}
}
if internal.Publish {
cataloguePath := path.Join(config.ABRA_DIR, "catalogue")
if err := gitPkg.Commit(cataloguePath, "**.json", internal.CommitMessage, internal.Dry); err != nil {
isClean, err := gitPkg.IsClean(cataloguePath)
if err != nil {
logrus.Fatal(err)
}
if internal.Push {
repo, err := git.PlainOpen(cataloguePath)
if err != nil {
logrus.Fatal(err)
}
sshURL := fmt.Sprintf(config.SSH_URL_TEMPLATE, "recipes")
if err := gitPkg.CreateRemote(repo, "origin-ssh", sshURL, internal.Dry); err != nil {
logrus.Fatal(err)
}
if err := gitPkg.Push(cataloguePath, "origin-ssh", false, internal.Dry); err != nil {
logrus.Fatal(err)
}
if isClean {
logrus.Fatalf("no changes discovered in %s, nothing to publish?", cataloguePath)
}
msg := "chore: publish new catalogue release changes"
if err := gitPkg.Commit(cataloguePath, "**.json", msg, internal.Dry); err != nil {
logrus.Fatal(err)
}
repo, err := git.PlainOpen(cataloguePath)
if err != nil {
logrus.Fatal(err)
}
sshURL := fmt.Sprintf(config.SSH_URL_TEMPLATE, "recipes")
if err := gitPkg.CreateRemote(repo, "origin-ssh", sshURL, internal.Dry); err != nil {
logrus.Fatal(err)
}
if err := gitPkg.Push(cataloguePath, "origin-ssh", false, internal.Dry); err != nil {
logrus.Fatal(err)
}
}
if internal.Dry {
logrus.Info("dry run: no changes published")
} else {
url := fmt.Sprintf("%s/recipes", config.REPOS_BASE_URL)
logrus.Infof("new changes published: %s", url)
}
return nil
@ -281,7 +283,7 @@ func updateRepositories(repos recipe.RepoCatalogue, recipeName string) error {
logrus.Fatal(err)
}
isClean, err := gitPkg.IsClean(rm.Name)
isClean, err := gitPkg.IsClean(recipeDir)
if err != nil {
logrus.Fatal(err)
}

View File

@ -306,7 +306,7 @@ var PatchFlag = &cli.BoolFlag{
Name: "patch",
Usage: "Increase the patch part of the version",
Value: false,
Aliases: []string{"p", "z"},
Aliases: []string{"pa", "z"},
Destination: &Patch,
}
@ -319,38 +319,13 @@ var DryFlag = &cli.BoolFlag{
Destination: &Dry,
}
var Push bool
var PushFlag = &cli.BoolFlag{
Name: "push",
Usage: "Git push changes",
var Publish bool
var PublishFlag = &cli.BoolFlag{
Name: "publish",
Usage: "Publish changes to git.coopcloud.tech",
Value: false,
Aliases: []string{"P"},
Destination: &Push,
}
var CommitMessage string
var CommitMessageFlag = &cli.StringFlag{
Name: "commit-message",
Usage: "Commit message (implies --commit)",
Aliases: []string{"cm"},
Destination: &CommitMessage,
}
var Commit bool
var CommitFlag = &cli.BoolFlag{
Name: "commit",
Usage: "Commit new changes",
Value: false,
Aliases: []string{"c"},
Destination: &Commit,
}
var TagMessage string
var TagMessageFlag = &cli.StringFlag{
Name: "tag-comment",
Usage: "Description for release tag",
Aliases: []string{"t", "tm"},
Destination: &TagMessage,
Aliases: []string{"p"},
Destination: &Publish,
}
var Domain string
@ -439,14 +414,14 @@ var SkipUpdatesFlag = &cli.BoolFlag{
Name: "skip-updates",
Aliases: []string{"s"},
Value: false,
Usage: "Skip updating git repositories",
Usage: "Skip updating recipe repositories",
Destination: &SkipUpdates,
}
var RegistryUsername string
var RegistryUsernameFlag = &cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Aliases: []string{"user"},
Value: "",
Usage: "Registry username",
EnvVars: []string{"REGISTRY_USERNAME"},
@ -456,7 +431,7 @@ var RegistryUsernameFlag = &cli.StringFlag{
var RegistryPassword string
var RegistryPasswordFlag = &cli.StringFlag{
Name: "password",
Aliases: []string{"p"},
Aliases: []string{"pass"},
Value: "",
Usage: "Registry password",
EnvVars: []string{"REGISTRY_PASSWORD"},

View File

@ -6,6 +6,7 @@ import (
"coopcloud.tech/abra/pkg/recipe"
"github.com/AlecAivazis/survey/v2"
"github.com/docker/distribution/reference"
"github.com/sirupsen/logrus"
)
@ -64,20 +65,29 @@ func SetBumpType(bumpType string) {
}
}
// GetMainApp retrieves the main 'app' image name
func GetMainApp(recipe recipe.Recipe) string {
var app string
// GetMainAppImage retrieves the main 'app' image name
func GetMainAppImage(recipe recipe.Recipe) (string, error) {
var path string
for _, service := range recipe.Config.Services {
name := service.Name
if name == "app" {
app = strings.Split(service.Image, ":")[0]
if service.Name == "app" {
img, err := reference.ParseNormalizedNamed(service.Image)
if err != nil {
return "", err
}
path = reference.Path(img)
if strings.Contains(path, "library") {
path = strings.Split(path, "/")[1]
}
return path, nil
}
}
if app == "" {
logrus.Fatalf("%s has no main 'app' service?", recipe.Name)
if path == "" {
return path, fmt.Errorf("%s has no main 'app' service?", recipe.Name)
}
return app
return path, nil
}

View File

@ -47,20 +47,16 @@ Abra does its best to read the "a.b.c" version scheme and communicate what
action needs to be taken when performing different operations such as an update
or a rollback of an app.
You may invoke this command in "wizard" mode and be prompted for input:
abra recipe release gitea
Publish your new release git.coopcloud.tech with "-p/--publish". This requires
that you have permission to git push to these repositories and have your SSH
keys configured on your account.
`,
Flags: []cli.Flag{
internal.DryFlag,
internal.MajorFlag,
internal.MinorFlag,
internal.PatchFlag,
internal.PushFlag,
internal.CommitFlag,
internal.CommitMessageFlag,
internal.TagMessageFlag,
internal.PublishFlag,
},
BashComplete: autocomplete.RecipeNameComplete,
Action: func(c *cli.Context) error {
@ -71,7 +67,11 @@ You may invoke this command in "wizard" mode and be prompted for input:
logrus.Fatal(err)
}
mainApp := internal.GetMainApp(recipe)
mainApp, err := internal.GetMainAppImage(recipe)
if err != nil {
logrus.Fatal(err)
}
mainAppVersion := imagesTmp[mainApp]
if mainAppVersion == "" {
logrus.Fatalf("main app service version for %s is empty?", recipe.Name)
@ -201,14 +201,14 @@ func createReleaseFromTag(recipe recipe.Recipe, tagString, mainAppVersion string
tag.MissingPatch = false
}
if err := commitRelease(recipe); err != nil {
logrus.Fatal(err)
}
if tagString == "" {
tagString = fmt.Sprintf("%s+%s", tag.String(), mainAppVersion)
}
if err := commitRelease(recipe, tagString); err != nil {
logrus.Fatal(err)
}
if err := tagRelease(tagString, repo); err != nil {
logrus.Fatal(err)
}
@ -230,50 +230,30 @@ func btoi(b bool) int {
}
// getTagCreateOptions constructs git tag create options
func getTagCreateOptions() (git.CreateTagOptions, error) {
if internal.TagMessage == "" && !internal.NoInput {
prompt := &survey.Input{
Message: "git tag message?",
Default: "chore: publish new release",
}
if err := survey.AskOne(prompt, &internal.TagMessage); err != nil {
return git.CreateTagOptions{}, err
}
}
return git.CreateTagOptions{Message: internal.TagMessage}, nil
func getTagCreateOptions(tag string) (git.CreateTagOptions, error) {
msg := fmt.Sprintf("chore: publish %s release", tag)
return git.CreateTagOptions{Message: msg}, nil
}
func commitRelease(recipe recipe.Recipe) error {
func commitRelease(recipe recipe.Recipe, tag string) error {
if internal.Dry {
logrus.Info("dry run: no changed committed")
logrus.Debugf("dry run: no changes committed")
return nil
}
if !internal.Commit && !internal.NoInput {
prompt := &survey.Confirm{
Message: "git commit changes?",
}
if err := survey.AskOne(prompt, &internal.Commit); err != nil {
return err
}
isClean, err := gitPkg.IsClean(recipe.Dir())
if err != nil {
return err
}
if internal.CommitMessage == "" && !internal.NoInput && internal.Commit {
prompt := &survey.Input{
Message: "git commit message?",
Default: "chore: publish new version",
}
if err := survey.AskOne(prompt, &internal.CommitMessage); err != nil {
return err
}
if isClean {
return fmt.Errorf("no changes discovered in %s, nothing to publish?", recipe.Dir())
}
if internal.Commit {
if internal.Publish {
msg := fmt.Sprintf("chore: publish %s release", tag)
repoPath := path.Join(config.RECIPES_DIR, recipe.Name)
if err := gitPkg.Commit(repoPath, "compose.**yml", internal.CommitMessage, internal.Dry); err != nil {
if err := gitPkg.Commit(repoPath, "compose.**yml", msg, internal.Dry); err != nil {
return err
}
}
@ -283,7 +263,7 @@ func commitRelease(recipe recipe.Recipe) error {
func tagRelease(tagString string, repo *git.Repository) error {
if internal.Dry {
logrus.Infof("dry run: no git tag created (%s)", tagString)
logrus.Debugf("dry run: no git tag created (%s)", tagString)
return nil
}
@ -292,7 +272,7 @@ func tagRelease(tagString string, repo *git.Repository) error {
return err
}
createTagOptions, err := getTagCreateOptions()
createTagOptions, err := getTagCreateOptions(tagString)
if err != nil {
return err
}
@ -303,33 +283,36 @@ func tagRelease(tagString string, repo *git.Repository) error {
}
hash := abraFormatter.SmallSHA(head.Hash().String())
logrus.Info(fmt.Sprintf("created tag %s at %s", tagString, hash))
logrus.Debugf(fmt.Sprintf("created tag %s at %s", tagString, hash))
return nil
}
func pushRelease(recipe recipe.Recipe) error {
if internal.Dry {
logrus.Info("dry run: no changes pushed")
logrus.Info("dry run: no changes published")
return nil
}
if !internal.Push && !internal.NoInput {
if !internal.Publish && !internal.NoInput {
prompt := &survey.Confirm{
Message: "git push changes?",
Message: "publish new release?",
}
if err := survey.AskOne(prompt, &internal.Push); err != nil {
if err := survey.AskOne(prompt, &internal.Publish); err != nil {
return err
}
}
if internal.Push {
if internal.Publish {
if err := recipe.Push(internal.Dry); err != nil {
return err
}
}
url := fmt.Sprintf("%s/%s/tags", config.REPOS_BASE_URL, recipe.Name)
logrus.Infof("new release published: %s", url)
return nil
}
@ -392,13 +375,13 @@ func createReleaseFromPreviousTag(tagString, mainAppVersion string, recipe recip
newTag.Major = strconv.Itoa(now + 1)
}
if err := commitRelease(recipe); err != nil {
logrus.Fatal(err)
}
newTag.Metadata = mainAppVersion
newTagString := newTag.String()
if err := commitRelease(recipe, newTagString); err != nil {
logrus.Fatal(err)
}
if err := tagRelease(newTagString, repo); err != nil {
logrus.Fatal(err)
}
@ -422,7 +405,7 @@ func cleanUpTag(tag, recipeName string) error {
return err
}
logrus.Warnf("removed freshly created tag %s", tag)
logrus.Debugf("removed freshly created tag %s", tag)
return nil
}

View File

@ -18,7 +18,7 @@ import (
var recipeSyncCommand = &cli.Command{
Name: "sync",
Usage: "Ensure recipe version labels are up-to-date",
Usage: "Sync recipe version label",
Aliases: []string{"s"},
ArgsUsage: "<recipe> [<version>]",
Flags: []cli.Flag{
@ -29,23 +29,21 @@ var recipeSyncCommand = &cli.Command{
},
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:
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
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)
mainApp := internal.GetMainApp(recipe)
mainApp, err := internal.GetMainAppImage(recipe)
if err != nil {
logrus.Fatal(err)
}
imagesTmp, err := getImageVersions(recipe)
if err != nil {

View File

@ -9,6 +9,7 @@ import (
"strings"
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
recipePkg "coopcloud.tech/abra/pkg/recipe"
@ -42,7 +43,8 @@ You may invoke this command in "wizard" mode and be prompted for input:
abra recipe upgrade
`,
ArgsUsage: "<recipe>",
BashComplete: autocomplete.RecipeNameComplete,
ArgsUsage: "<recipe>",
Flags: []cli.Flag{
internal.PatchFlag,
internal.MinorFlag,

View File

@ -47,9 +47,9 @@ func Commit(repoPath, glob, commitMessage string, dryRun bool) error {
if err != nil {
return err
}
logrus.Info("changes commited")
logrus.Debug("git changes commited")
} else {
logrus.Info("dry run: no changes commited")
logrus.Debug("dry run: no changes commited")
}
return nil

View File

@ -9,7 +9,7 @@ import (
// Push pushes the latest changes & optionally tags to the default remote
func Push(repoDir string, remote string, tags bool, dryRun bool) error {
if dryRun {
logrus.Infof("dry run: no git changes pushed in %s", repoDir)
logrus.Debugf("dry run: no git changes pushed in %s", repoDir)
return nil
}
@ -27,7 +27,7 @@ func Push(repoDir string, remote string, tags bool, dryRun bool) error {
return err
}
logrus.Info("git changes pushed")
logrus.Debugf("git changes pushed")
if tags {
opts.RefSpecs = append(opts.RefSpecs, config.RefSpec("+refs/tags/*:refs/tags/*"))
@ -36,7 +36,7 @@ func Push(repoDir string, remote string, tags bool, dryRun bool) error {
return err
}
logrus.Info("git tags pushed")
logrus.Debugf("git tags pushed")
}
return nil

View File

@ -34,10 +34,8 @@ func GetRecipeHead(recipeName string) (*plumbing.Reference, error) {
}
// IsClean checks if a repo has unstaged changes
func IsClean(recipeName string) (bool, error) {
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
repo, err := git.PlainOpen(recipeDir)
func IsClean(repoPath string) (bool, error) {
repo, err := git.PlainOpen(repoPath)
if err != nil {
return false, err
}
@ -62,9 +60,9 @@ func IsClean(recipeName string) (bool, error) {
}
if status.String() != "" {
logrus.Debugf("discovered git status for %s repository: %s", recipeName, status.String())
logrus.Debugf("discovered git status in %s: %s", repoPath, status.String())
} else {
logrus.Debugf("discovered clean git status for %s repository", recipeName)
logrus.Debugf("discovered clean git status in %s", repoPath)
}
return status.IsClean(), nil

View File

@ -260,7 +260,7 @@ func EnsureExists(recipeName string) error {
func EnsureVersion(recipeName, version string) error {
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
isClean, err := gitPkg.IsClean(recipeName)
isClean, err := gitPkg.IsClean(recipeDir)
if err != nil {
return err
}
@ -325,7 +325,7 @@ func EnsureVersion(recipeName, version string) error {
func EnsureLatest(recipeName string) error {
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
isClean, err := gitPkg.IsClean(recipeName)
isClean, err := gitPkg.IsClean(recipeDir)
if err != nil {
return err
}
@ -380,7 +380,8 @@ func ChaosVersion(recipeName string) (string, error) {
version = head.String()[:8]
isClean, err := gitPkg.IsClean(recipeName)
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
isClean, err := gitPkg.IsClean(recipeDir)
if err != nil {
return version, err
}
@ -556,7 +557,7 @@ func GetStringInBetween(recipeName, str, start, end string) (result string, err
func EnsureUpToDate(recipeName string) error {
recipeDir := path.Join(config.RECIPES_DIR, recipeName)
isClean, err := gitPkg.IsClean(recipeName)
isClean, err := gitPkg.IsClean(recipeDir)
if err != nil {
return err
}