refactor: recipe validation
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			This commit is contained in:
		@ -9,14 +9,11 @@ import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"coopcloud.tech/abra/config"
 | 
			
		||||
	"coopcloud.tech/abra/web"
 | 
			
		||||
	"github.com/go-git/go-git/v5"
 | 
			
		||||
	"github.com/go-git/go-git/v5/plumbing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RecipeCatalogueURL is the only current recipe catalogue available.
 | 
			
		||||
@ -67,62 +64,6 @@ type Recipe struct {
 | 
			
		||||
	Website       string                          `json:"website"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EnsureExists checks whether a recipe has been cloned locally or not.
 | 
			
		||||
func (r Recipe) EnsureExists() error {
 | 
			
		||||
	recipeDir := path.Join(config.ABRA_DIR, "apps", strings.ToLower(r.Name))
 | 
			
		||||
 | 
			
		||||
	if _, err := os.Stat(recipeDir); os.IsNotExist(err) {
 | 
			
		||||
		url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, r.Name)
 | 
			
		||||
		_, err := git.PlainClone(recipeDir, false, &git.CloneOptions{URL: url, Tags: git.AllTags})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EnsureVersion checks whether a specific version exists for a recipe.
 | 
			
		||||
func (r Recipe) EnsureVersion(version string) error {
 | 
			
		||||
	recipeDir := path.Join(config.ABRA_DIR, "apps", strings.ToLower(r.Name))
 | 
			
		||||
 | 
			
		||||
	repo, err := git.PlainOpen(recipeDir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tags, err := repo.Tags()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var tagRef plumbing.ReferenceName
 | 
			
		||||
	if err := tags.ForEach(func(ref *plumbing.Reference) (err error) {
 | 
			
		||||
		if ref.Name().Short() == version {
 | 
			
		||||
			tagRef = ref.Name()
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tagRef.String() == "" {
 | 
			
		||||
		return fmt.Errorf("%s is not available?", version)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	worktree, err := repo.Worktree()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	opts := &git.CheckoutOptions{Branch: tagRef, Keep: true}
 | 
			
		||||
	if err := worktree.Checkout(opts); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LatestVersion returns the latest version of a recipe.
 | 
			
		||||
func (r Recipe) LatestVersion() string {
 | 
			
		||||
	var latestVersion string
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@ import (
 | 
			
		||||
	abraFormatter "coopcloud.tech/abra/cli/formatter"
 | 
			
		||||
	"coopcloud.tech/abra/cli/internal"
 | 
			
		||||
	"coopcloud.tech/abra/config"
 | 
			
		||||
	"coopcloud.tech/abra/recipe"
 | 
			
		||||
	"coopcloud.tech/abra/secret"
 | 
			
		||||
	"github.com/AlecAivazis/survey/v2"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
@ -87,15 +88,15 @@ func getRecipe(recipeName string) (catalogue.Recipe, error) {
 | 
			
		||||
		return catalogue.Recipe{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	recipe, ok := catl[recipeName]
 | 
			
		||||
	rec, ok := catl[recipeName]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return catalogue.Recipe{}, fmt.Errorf("recipe '%s' does not exist?", recipeName)
 | 
			
		||||
	}
 | 
			
		||||
	if err := recipe.EnsureExists(); err != nil {
 | 
			
		||||
	if err := recipe.EnsureExists(rec.Name); err != nil {
 | 
			
		||||
		return catalogue.Recipe{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return recipe, nil
 | 
			
		||||
	return rec, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ensureDomainFlag checks if the domain flag was used. if not, asks the user for it/
 | 
			
		||||
@ -180,12 +181,12 @@ func action(c *cli.Context) error {
 | 
			
		||||
		logrus.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	recipe, err := getRecipe(recipeName)
 | 
			
		||||
	rec, err := getRecipe(recipeName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logrus.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	latestVersion := recipe.LatestVersion()
 | 
			
		||||
	latestVersion := rec.LatestVersion()
 | 
			
		||||
	if err := recipe.EnsureVersion(latestVersion); err != nil {
 | 
			
		||||
		logrus.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -4,10 +4,6 @@ import (
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Flags
 | 
			
		||||
 | 
			
		||||
// AppName stores the variable from AppNameFlag
 | 
			
		||||
 | 
			
		||||
// Secrets stores the variable from SecretsFlag
 | 
			
		||||
var Secrets bool
 | 
			
		||||
 | 
			
		||||
@ -43,8 +39,10 @@ var ContextFlag = &cli.StringFlag{
 | 
			
		||||
	Destination: &Context,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Force force functionality without asking.
 | 
			
		||||
var Force bool
 | 
			
		||||
 | 
			
		||||
// ForceFlag turns on/off force functionality.
 | 
			
		||||
var ForceFlag = &cli.BoolFlag{
 | 
			
		||||
	Name:        "force",
 | 
			
		||||
	Value:       false,
 | 
			
		||||
 | 
			
		||||
@ -7,10 +7,10 @@ import (
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ShowSubcommandHelpAndError exits the program on error, logs the error to the terminal, and shows the help command.
 | 
			
		||||
// ShowSubcommandHelpAndError exits the program on error, logs the error to the
 | 
			
		||||
// terminal, and shows the help command.
 | 
			
		||||
func ShowSubcommandHelpAndError(c *cli.Context, err interface{}) {
 | 
			
		||||
	if err2 := cli.ShowSubcommandHelp(c); err2 != nil {
 | 
			
		||||
		// go-critic wants me to check this error but if this throws an error while we throw an error that would be annoying
 | 
			
		||||
		logrus.Error(err2)
 | 
			
		||||
	}
 | 
			
		||||
	logrus.Error(err)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								cli/internal/validate.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								cli/internal/validate.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
package internal
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
 | 
			
		||||
	"coopcloud.tech/abra/recipe"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ValidateRecipeArg ensures the recipe arg is valid.
 | 
			
		||||
func ValidateRecipeArg(c *cli.Context) string {
 | 
			
		||||
	recipeName := c.Args().First()
 | 
			
		||||
 | 
			
		||||
	if recipeName == "" {
 | 
			
		||||
		ShowSubcommandHelpAndError(c, errors.New("no recipe provided"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := recipe.EnsureExists(recipeName); err != nil {
 | 
			
		||||
		logrus.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return recipeName
 | 
			
		||||
}
 | 
			
		||||
@ -56,10 +56,7 @@ var recipeVersionCommand = &cli.Command{
 | 
			
		||||
	Aliases:   []string{"v"},
 | 
			
		||||
	ArgsUsage: "<recipe>",
 | 
			
		||||
	Action: func(c *cli.Context) error {
 | 
			
		||||
		recipe := c.Args().First()
 | 
			
		||||
		if recipe == "" {
 | 
			
		||||
			internal.ShowSubcommandHelpAndError(c, errors.New("no recipe provided"))
 | 
			
		||||
		}
 | 
			
		||||
		recipe := internal.ValidateRecipeArg(c)
 | 
			
		||||
 | 
			
		||||
		catalogue, err := catalogue.ReadRecipeCatalogue()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
@ -173,10 +170,7 @@ This is step 1 of upgrading a recipe. Step 2 is running "abra recipe sync
 | 
			
		||||
`,
 | 
			
		||||
	ArgsUsage: "<recipe>",
 | 
			
		||||
	Action: func(c *cli.Context) error {
 | 
			
		||||
		recipe := c.Args().First()
 | 
			
		||||
		if recipe == "" {
 | 
			
		||||
			internal.ShowSubcommandHelpAndError(c, errors.New("no recipe provided"))
 | 
			
		||||
		}
 | 
			
		||||
		recipe := internal.ValidateRecipeArg(c)
 | 
			
		||||
 | 
			
		||||
		appFiles, err := config.LoadAppFiles("")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
@ -304,10 +298,7 @@ the versioning metadata of up-and-running containers are.
 | 
			
		||||
`,
 | 
			
		||||
	ArgsUsage: "<recipe>",
 | 
			
		||||
	Action: func(c *cli.Context) error {
 | 
			
		||||
		recipe := c.Args().First()
 | 
			
		||||
		if recipe == "" {
 | 
			
		||||
			internal.ShowSubcommandHelpAndError(c, errors.New("no recipe provided"))
 | 
			
		||||
		}
 | 
			
		||||
		recipe := internal.ValidateRecipeArg(c)
 | 
			
		||||
 | 
			
		||||
		appFiles, err := config.LoadAppFiles("")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
@ -363,10 +354,7 @@ var recipeLintCommand = &cli.Command{
 | 
			
		||||
	Aliases:   []string{"l"},
 | 
			
		||||
	ArgsUsage: "<recipe>",
 | 
			
		||||
	Action: func(c *cli.Context) error {
 | 
			
		||||
		recipe := c.Args().First()
 | 
			
		||||
		if recipe == "" {
 | 
			
		||||
			internal.ShowSubcommandHelpAndError(c, errors.New("no recipe provided"))
 | 
			
		||||
		}
 | 
			
		||||
		recipe := internal.ValidateRecipeArg(c)
 | 
			
		||||
 | 
			
		||||
		pattern := fmt.Sprintf("%s/%s/compose**yml", config.APPS_DIR, recipe)
 | 
			
		||||
		composeFiles, err := filepath.Glob(pattern)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										68
									
								
								recipe/recipe.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								recipe/recipe.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,68 @@
 | 
			
		||||
package recipe
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"coopcloud.tech/abra/config"
 | 
			
		||||
	"github.com/go-git/go-git/v5"
 | 
			
		||||
	"github.com/go-git/go-git/v5/plumbing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// EnsureExists checks whether a recipe has been cloned locally or not.
 | 
			
		||||
func EnsureExists(recipe string) error {
 | 
			
		||||
	recipeDir := path.Join(config.ABRA_DIR, "apps", strings.ToLower(recipe))
 | 
			
		||||
 | 
			
		||||
	if _, err := os.Stat(recipeDir); os.IsNotExist(err) {
 | 
			
		||||
		url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, recipe)
 | 
			
		||||
		_, err := git.PlainClone(recipeDir, false, &git.CloneOptions{URL: url, Tags: git.AllTags})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EnsureVersion checks whether a specific version exists for a recipe.
 | 
			
		||||
func EnsureVersion(version string) error {
 | 
			
		||||
	recipeDir := path.Join(config.ABRA_DIR, "apps", strings.ToLower(version))
 | 
			
		||||
 | 
			
		||||
	repo, err := git.PlainOpen(recipeDir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tags, err := repo.Tags()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var tagRef plumbing.ReferenceName
 | 
			
		||||
	if err := tags.ForEach(func(ref *plumbing.Reference) (err error) {
 | 
			
		||||
		if ref.Name().Short() == version {
 | 
			
		||||
			tagRef = ref.Name()
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tagRef.String() == "" {
 | 
			
		||||
		return fmt.Errorf("%s is not available?", version)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	worktree, err := repo.Worktree()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	opts := &git.CheckoutOptions{Branch: tagRef, Keep: true}
 | 
			
		||||
	if err := worktree.Checkout(opts); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user