forked from toolshed/abra
		
	refactor(recipe): load load compoes config where its used
This commit is contained in:
		@ -58,13 +58,8 @@ recipes.
 | 
				
			|||||||
			log.Fatal("cannot use <version> and --chaos together")
 | 
								log.Fatal("cannot use <version> and --chaos together")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		r2 := recipe.Get2(app.Recipe)
 | 
							r := recipe.Get2(app.Recipe)
 | 
				
			||||||
		if err := r2.Ensure(internal.Chaos, internal.Offline); err != nil {
 | 
							if err := r.Ensure(internal.Chaos, internal.Offline); err != nil {
 | 
				
			||||||
			log.Fatal(err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		r, err := recipe.Get(app.Recipe, internal.Offline)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Fatal(err)
 | 
								log.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -92,7 +87,7 @@ recipes.
 | 
				
			|||||||
		if specificVersion != "" {
 | 
							if specificVersion != "" {
 | 
				
			||||||
			version = specificVersion
 | 
								version = specificVersion
 | 
				
			||||||
			log.Debugf("choosing %s as version to deploy", version)
 | 
								log.Debugf("choosing %s as version to deploy", version)
 | 
				
			||||||
			if err := r2.EnsureVersion(version); err != nil {
 | 
								if err := r.EnsureVersion(version); err != nil {
 | 
				
			||||||
				log.Fatal(err)
 | 
									log.Fatal(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -128,7 +123,7 @@ recipes.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			if len(versions) == 0 && !internal.Chaos {
 | 
								if len(versions) == 0 && !internal.Chaos {
 | 
				
			||||||
				log.Warn("no published versions in catalogue, trying local recipe repository")
 | 
									log.Warn("no published versions in catalogue, trying local recipe repository")
 | 
				
			||||||
				recipeVersions, err := recipe.GetRecipeVersions(app.Recipe, internal.Offline)
 | 
									recipeVersions, err := r.GetRecipeVersions(internal.Offline)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					log.Warn(err)
 | 
										log.Warn(err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@ -142,7 +137,7 @@ recipes.
 | 
				
			|||||||
			if len(versions) > 0 && !internal.Chaos {
 | 
								if len(versions) > 0 && !internal.Chaos {
 | 
				
			||||||
				version = versions[len(versions)-1]
 | 
									version = versions[len(versions)-1]
 | 
				
			||||||
				log.Debugf("choosing %s as version to deploy", version)
 | 
									log.Debugf("choosing %s as version to deploy", version)
 | 
				
			||||||
				if err := r2.EnsureVersion(version); err != nil {
 | 
									if err := r.EnsureVersion(version); err != nil {
 | 
				
			||||||
					log.Fatal(err)
 | 
										log.Fatal(err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
@ -158,7 +153,7 @@ recipes.
 | 
				
			|||||||
		if internal.Chaos {
 | 
							if internal.Chaos {
 | 
				
			||||||
			log.Warnf("chaos mode engaged")
 | 
								log.Warnf("chaos mode engaged")
 | 
				
			||||||
			var err error
 | 
								var err error
 | 
				
			||||||
			version, err = r2.ChaosVersion()
 | 
								version, err = r.ChaosVersion()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Fatal(err)
 | 
									log.Fatal(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -173,7 +168,7 @@ recipes.
 | 
				
			|||||||
			app.Env[k] = v
 | 
								app.Env[k] = v
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		composeFiles, err := r2.GetComposeFiles(app.Env)
 | 
							composeFiles, err := r.GetComposeFiles(app.Env)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Fatal(err)
 | 
								log.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
				
			|||||||
@ -80,7 +80,7 @@ var appNewCommand = cli.Command{
 | 
				
			|||||||
			if c.Args().Get(1) == "" {
 | 
								if c.Args().Get(1) == "" {
 | 
				
			||||||
				var version string
 | 
									var version string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				recipeVersions, err := recipePkg.GetRecipeVersions(recipe.Name, internal.Offline)
 | 
									recipeVersions, err := r.GetRecipeVersions(internal.Offline)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					log.Fatal(err)
 | 
										log.Fatal(err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
				
			|||||||
@ -58,13 +58,8 @@ recipes.
 | 
				
			|||||||
			log.Fatal("cannot use <version> and --chaos together")
 | 
								log.Fatal("cannot use <version> and --chaos together")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		r2 := recipe.Get2(app.Recipe)
 | 
							r := recipe.Get2(app.Recipe)
 | 
				
			||||||
		if err := r2.Ensure(internal.Chaos, internal.Offline); err != nil {
 | 
							if err := r.Ensure(internal.Chaos, internal.Offline); err != nil {
 | 
				
			||||||
			log.Fatal(err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		r, err := recipe.Get(app.Recipe, internal.Offline)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Fatal(err)
 | 
								log.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -100,7 +95,7 @@ recipes.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if len(versions) == 0 && !internal.Chaos {
 | 
							if len(versions) == 0 && !internal.Chaos {
 | 
				
			||||||
			log.Warn("no published versions in catalogue, trying local recipe repository")
 | 
								log.Warn("no published versions in catalogue, trying local recipe repository")
 | 
				
			||||||
			recipeVersions, err := recipe.GetRecipeVersions(app.Recipe, internal.Offline)
 | 
								recipeVersions, err := r.GetRecipeVersions(internal.Offline)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Warn(err)
 | 
									log.Warn(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -170,7 +165,7 @@ recipes.
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if !internal.Chaos {
 | 
							if !internal.Chaos {
 | 
				
			||||||
			if err := r2.EnsureVersion(chosenDowngrade); err != nil {
 | 
								if err := r.EnsureVersion(chosenDowngrade); err != nil {
 | 
				
			||||||
				log.Fatal(err)
 | 
									log.Fatal(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -178,7 +173,7 @@ recipes.
 | 
				
			|||||||
		if internal.Chaos {
 | 
							if internal.Chaos {
 | 
				
			||||||
			log.Warn("chaos mode engaged")
 | 
								log.Warn("chaos mode engaged")
 | 
				
			||||||
			var err error
 | 
								var err error
 | 
				
			||||||
			chosenDowngrade, err = r2.ChaosVersion()
 | 
								chosenDowngrade, err = r.ChaosVersion()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Fatal(err)
 | 
									log.Fatal(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -193,7 +188,7 @@ recipes.
 | 
				
			|||||||
			app.Env[k] = v
 | 
								app.Env[k] = v
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		composeFiles, err := r2.GetComposeFiles(app.Env)
 | 
							composeFiles, err := r.GetComposeFiles(app.Env)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Fatal(err)
 | 
								log.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
				
			|||||||
@ -69,12 +69,8 @@ recipes.
 | 
				
			|||||||
			log.Fatal(err)
 | 
								log.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		recipe, err := recipePkg.Get(app.Recipe, internal.Offline)
 | 
							r2 := recipePkg.Get2(app.Recipe)
 | 
				
			||||||
		if err != nil {
 | 
							if err := lint.LintForErrors(r2); err != nil {
 | 
				
			||||||
			log.Fatal(err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if err := lint.LintForErrors(recipe); err != nil {
 | 
					 | 
				
			||||||
			log.Fatal(err)
 | 
								log.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -106,7 +102,7 @@ recipes.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if len(versions) == 0 && !internal.Chaos {
 | 
							if len(versions) == 0 && !internal.Chaos {
 | 
				
			||||||
			log.Warn("no published versions in catalogue, trying local recipe repository")
 | 
								log.Warn("no published versions in catalogue, trying local recipe repository")
 | 
				
			||||||
			recipeVersions, err := recipePkg.GetRecipeVersions(app.Recipe, internal.Offline)
 | 
								recipeVersions, err := r2.GetRecipeVersions(internal.Offline)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Warn(err)
 | 
									log.Warn(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
				
			|||||||
@ -99,7 +99,7 @@ keys configured on your account.
 | 
				
			|||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			versions, err := recipe.GetRecipeVersions(recipeMeta.Name, internal.Offline)
 | 
								versions, err := r.GetRecipeVersions(internal.Offline)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Warn(err)
 | 
									log.Warn(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
				
			|||||||
@ -85,10 +85,14 @@ func SetBumpType(bumpType string) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetMainAppImage retrieves the main 'app' image name
 | 
					// GetMainAppImage retrieves the main 'app' image name
 | 
				
			||||||
func GetMainAppImage(recipe recipe.Recipe) (string, error) {
 | 
					func GetMainAppImage(recipe recipe.Recipe2) (string, error) {
 | 
				
			||||||
	var path string
 | 
						var path string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, service := range recipe.Config.Services {
 | 
						config, err := recipe.GetComposeConfig(nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, service := range config.Services {
 | 
				
			||||||
		if service.Name == "app" {
 | 
							if service.Name == "app" {
 | 
				
			||||||
			img, err := reference.ParseNormalizedNamed(service.Image)
 | 
								img, err := reference.ParseNormalizedNamed(service.Image)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
 | 
				
			|||||||
@ -47,7 +47,7 @@ var recipeLintCommand = cli.Command{
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				skipped := false
 | 
									skipped := false
 | 
				
			||||||
				if rule.Skip(recipe) {
 | 
									if rule.Skip(r) {
 | 
				
			||||||
					skipped = true
 | 
										skipped = true
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -58,7 +58,7 @@ var recipeLintCommand = cli.Command{
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
				satisfied := false
 | 
									satisfied := false
 | 
				
			||||||
				if !skipped {
 | 
									if !skipped {
 | 
				
			||||||
					ok, err := rule.Function(recipe)
 | 
										ok, err := rule.Function(r)
 | 
				
			||||||
					if err != nil {
 | 
										if err != nil {
 | 
				
			||||||
						log.Warn(err)
 | 
											log.Warn(err)
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
				
			|||||||
@ -65,12 +65,12 @@ your SSH keys configured on your account.
 | 
				
			|||||||
		recipe := internal.ValidateRecipe(c)
 | 
							recipe := internal.ValidateRecipe(c)
 | 
				
			||||||
		r := recipePkg.Get2(recipe.Name)
 | 
							r := recipePkg.Get2(recipe.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		imagesTmp, err := getImageVersions(recipe)
 | 
							imagesTmp, err := getImageVersions(r)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Fatal(err)
 | 
								log.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		mainApp, err := internal.GetMainAppImage(recipe)
 | 
							mainApp, err := internal.GetMainAppImage(r)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Fatal(err)
 | 
								log.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -104,7 +104,7 @@ your SSH keys configured on your account.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if tagString == "" && (!internal.Major && !internal.Minor && !internal.Patch) {
 | 
							if tagString == "" && (!internal.Major && !internal.Minor && !internal.Patch) {
 | 
				
			||||||
			var err error
 | 
								var err error
 | 
				
			||||||
			tagString, err = getLabelVersion(recipe, false)
 | 
								tagString, err = getLabelVersion(r, false)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Fatal(err)
 | 
									log.Fatal(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -143,11 +143,15 @@ your SSH keys configured on your account.
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getImageVersions retrieves image versions for a recipe
 | 
					// getImageVersions retrieves image versions for a recipe
 | 
				
			||||||
func getImageVersions(recipe recipe.Recipe) (map[string]string, error) {
 | 
					func getImageVersions(recipe recipe.Recipe2) (map[string]string, error) {
 | 
				
			||||||
	services := make(map[string]string)
 | 
						services := make(map[string]string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config, err := recipe.GetComposeConfig(nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	missingTag := false
 | 
						missingTag := false
 | 
				
			||||||
	for _, service := range recipe.Config.Services {
 | 
						for _, service := range config.Services {
 | 
				
			||||||
		if service.Image == "" {
 | 
							if service.Image == "" {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -526,8 +530,8 @@ func cleanUpTag(tag, recipeName string) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getLabelVersion(recipe recipe.Recipe, prompt bool) (string, error) {
 | 
					func getLabelVersion(recipe recipe.Recipe2, prompt bool) (string, error) {
 | 
				
			||||||
	initTag, err := recipePkg.GetVersionLabelLocal(recipe)
 | 
						initTag, err := recipe.GetVersionLabelLocal()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -47,12 +47,12 @@ local file system.
 | 
				
			|||||||
		recipe := internal.ValidateRecipe(c)
 | 
							recipe := internal.ValidateRecipe(c)
 | 
				
			||||||
		r := recipePkg.Get2(recipe.Name)
 | 
							r := recipePkg.Get2(recipe.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		mainApp, err := internal.GetMainAppImage(recipe)
 | 
							mainApp, err := internal.GetMainAppImage(r)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Fatal(err)
 | 
								log.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		imagesTmp, err := getImageVersions(recipe)
 | 
							imagesTmp, err := getImageVersions(r)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Fatal(err)
 | 
								log.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
				
			|||||||
@ -130,7 +130,12 @@ You may invoke this command in "wizard" mode and be prompted for input:
 | 
				
			|||||||
			log.Debugf("did not find versions file for %s", recipe.Name)
 | 
								log.Debugf("did not find versions file for %s", recipe.Name)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for _, service := range recipe.Config.Services {
 | 
							config, err := r.GetComposeConfig(nil)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Fatal(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for _, service := range config.Services {
 | 
				
			||||||
			img, err := reference.ParseNormalizedNamed(service.Image)
 | 
								img, err := reference.ParseNormalizedNamed(service.Image)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Fatal(err)
 | 
									log.Fatal(err)
 | 
				
			||||||
 | 
				
			|||||||
@ -331,9 +331,7 @@ func processRecipeRepoVersion(r recipe.Recipe2, version string) error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if r, err := recipe.Get(r.Name, internal.Offline); err != nil {
 | 
						if err := lint.LintForErrors(r); err != nil {
 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	} else if err := lint.LintForErrors(r); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -19,13 +19,13 @@ import (
 | 
				
			|||||||
var Warn = "warn"
 | 
					var Warn = "warn"
 | 
				
			||||||
var Critical = "critical"
 | 
					var Critical = "critical"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type LintFunction func(recipe.Recipe) (bool, error)
 | 
					type LintFunction func(recipe.Recipe2) (bool, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SkipFunction determines whether the LintFunction is run or not. It should
 | 
					// SkipFunction determines whether the LintFunction is run or not. It should
 | 
				
			||||||
// not take the lint rule level into account because some rules are always an
 | 
					// not take the lint rule level into account because some rules are always an
 | 
				
			||||||
// error but may depend on some additional context of the recipe configuration.
 | 
					// error but may depend on some additional context of the recipe configuration.
 | 
				
			||||||
// This function aims to cover those additional cases.
 | 
					// This function aims to cover those additional cases.
 | 
				
			||||||
type SkipFunction func(recipe.Recipe) (bool, error)
 | 
					type SkipFunction func(recipe.Recipe2) (bool, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LintRule is a linting rule which helps a recipe maintainer avoid common
 | 
					// LintRule is a linting rule which helps a recipe maintainer avoid common
 | 
				
			||||||
// problems in their recipe configurations. We aim to highlight things that
 | 
					// problems in their recipe configurations. We aim to highlight things that
 | 
				
			||||||
@ -42,7 +42,7 @@ type LintRule struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Skip implements the SkipFunction for the lint rule.
 | 
					// Skip implements the SkipFunction for the lint rule.
 | 
				
			||||||
func (l LintRule) Skip(recipe recipe.Recipe) bool {
 | 
					func (l LintRule) Skip(recipe recipe.Recipe2) bool {
 | 
				
			||||||
	if l.SkipCondition != nil {
 | 
						if l.SkipCondition != nil {
 | 
				
			||||||
		ok, err := l.SkipCondition(recipe)
 | 
							ok, err := l.SkipCondition(recipe)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
@ -173,7 +173,7 @@ var LintRules = map[string][]LintRule{
 | 
				
			|||||||
// LintForErrors lints specifically for errors and not other levels. This is
 | 
					// LintForErrors lints specifically for errors and not other levels. This is
 | 
				
			||||||
// used in code paths such as "app deploy" to avoid nasty surprises but not for
 | 
					// used in code paths such as "app deploy" to avoid nasty surprises but not for
 | 
				
			||||||
// the typical linting commands, which do handle other levels.
 | 
					// the typical linting commands, which do handle other levels.
 | 
				
			||||||
func LintForErrors(recipe recipe.Recipe) error {
 | 
					func LintForErrors(recipe recipe.Recipe2) error {
 | 
				
			||||||
	log.Debugf("linting for critical errors in %s configs", recipe.Name)
 | 
						log.Debugf("linting for critical errors in %s configs", recipe.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for level := range LintRules {
 | 
						for level := range LintRules {
 | 
				
			||||||
@ -201,15 +201,19 @@ func LintForErrors(recipe recipe.Recipe) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LintComposeVersion(recipe recipe.Recipe) (bool, error) {
 | 
					func LintComposeVersion(recipe recipe.Recipe2) (bool, error) {
 | 
				
			||||||
	if recipe.Config.Version == "3.8" {
 | 
						config, err := recipe.GetComposeConfig(nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if config.Version == "3.8" {
 | 
				
			||||||
		return true, nil
 | 
							return true, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LintEnvConfigPresent(r recipe.Recipe) (bool, error) {
 | 
					func LintEnvConfigPresent(r recipe.Recipe2) (bool, error) {
 | 
				
			||||||
	r2 := recipe.Get2(r.Name)
 | 
						r2 := recipe.Get2(r.Name)
 | 
				
			||||||
	if _, err := os.Stat(r2.SampleEnvPath); !os.IsNotExist(err) {
 | 
						if _, err := os.Stat(r2.SampleEnvPath); !os.IsNotExist(err) {
 | 
				
			||||||
		return true, nil
 | 
							return true, nil
 | 
				
			||||||
@ -218,8 +222,12 @@ func LintEnvConfigPresent(r recipe.Recipe) (bool, error) {
 | 
				
			|||||||
	return false, nil
 | 
						return false, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LintAppService(recipe recipe.Recipe) (bool, error) {
 | 
					func LintAppService(recipe recipe.Recipe2) (bool, error) {
 | 
				
			||||||
	for _, service := range recipe.Config.Services {
 | 
						config, err := recipe.GetComposeConfig(nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, service := range config.Services {
 | 
				
			||||||
		if service.Name == "app" {
 | 
							if service.Name == "app" {
 | 
				
			||||||
			return true, nil
 | 
								return true, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -232,7 +240,7 @@ func LintAppService(recipe recipe.Recipe) (bool, error) {
 | 
				
			|||||||
// confirms that there is no "DOMAIN=..." in the .env.sample configuration of
 | 
					// confirms that there is no "DOMAIN=..." in the .env.sample configuration of
 | 
				
			||||||
// the recipe. This typically means that no domain is required to deploy and
 | 
					// the recipe. This typically means that no domain is required to deploy and
 | 
				
			||||||
// therefore no matching traefik deploy label will be present.
 | 
					// therefore no matching traefik deploy label will be present.
 | 
				
			||||||
func LintTraefikEnabledSkipCondition(r recipe.Recipe) (bool, error) {
 | 
					func LintTraefikEnabledSkipCondition(r recipe.Recipe2) (bool, error) {
 | 
				
			||||||
	r2 := recipe.Get2(r.Name)
 | 
						r2 := recipe.Get2(r.Name)
 | 
				
			||||||
	sampleEnv, err := r2.SampleEnv()
 | 
						sampleEnv, err := r2.SampleEnv()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@ -246,8 +254,12 @@ func LintTraefikEnabledSkipCondition(r recipe.Recipe) (bool, error) {
 | 
				
			|||||||
	return false, nil
 | 
						return false, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LintTraefikEnabled(recipe recipe.Recipe) (bool, error) {
 | 
					func LintTraefikEnabled(recipe recipe.Recipe2) (bool, error) {
 | 
				
			||||||
	for _, service := range recipe.Config.Services {
 | 
						config, err := recipe.GetComposeConfig(nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, service := range config.Services {
 | 
				
			||||||
		for label := range service.Deploy.Labels {
 | 
							for label := range service.Deploy.Labels {
 | 
				
			||||||
			if label == "traefik.enable" {
 | 
								if label == "traefik.enable" {
 | 
				
			||||||
				if service.Deploy.Labels[label] == "true" {
 | 
									if service.Deploy.Labels[label] == "true" {
 | 
				
			||||||
@ -260,8 +272,12 @@ func LintTraefikEnabled(recipe recipe.Recipe) (bool, error) {
 | 
				
			|||||||
	return false, nil
 | 
						return false, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LintHealthchecks(recipe recipe.Recipe) (bool, error) {
 | 
					func LintHealthchecks(recipe recipe.Recipe2) (bool, error) {
 | 
				
			||||||
	for _, service := range recipe.Config.Services {
 | 
						config, err := recipe.GetComposeConfig(nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, service := range config.Services {
 | 
				
			||||||
		if service.HealthCheck == nil {
 | 
							if service.HealthCheck == nil {
 | 
				
			||||||
			return false, nil
 | 
								return false, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -270,8 +286,12 @@ func LintHealthchecks(recipe recipe.Recipe) (bool, error) {
 | 
				
			|||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LintAllImagesTagged(recipe recipe.Recipe) (bool, error) {
 | 
					func LintAllImagesTagged(recipe recipe.Recipe2) (bool, error) {
 | 
				
			||||||
	for _, service := range recipe.Config.Services {
 | 
						config, err := recipe.GetComposeConfig(nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, service := range config.Services {
 | 
				
			||||||
		img, err := reference.ParseNormalizedNamed(service.Image)
 | 
							img, err := reference.ParseNormalizedNamed(service.Image)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return false, err
 | 
								return false, err
 | 
				
			||||||
@ -284,8 +304,12 @@ func LintAllImagesTagged(recipe recipe.Recipe) (bool, error) {
 | 
				
			|||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LintNoUnstableTags(recipe recipe.Recipe) (bool, error) {
 | 
					func LintNoUnstableTags(recipe recipe.Recipe2) (bool, error) {
 | 
				
			||||||
	for _, service := range recipe.Config.Services {
 | 
						config, err := recipe.GetComposeConfig(nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, service := range config.Services {
 | 
				
			||||||
		img, err := reference.ParseNormalizedNamed(service.Image)
 | 
							img, err := reference.ParseNormalizedNamed(service.Image)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return false, err
 | 
								return false, err
 | 
				
			||||||
@ -307,8 +331,12 @@ func LintNoUnstableTags(recipe recipe.Recipe) (bool, error) {
 | 
				
			|||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LintSemverLikeTags(recipe recipe.Recipe) (bool, error) {
 | 
					func LintSemverLikeTags(recipe recipe.Recipe2) (bool, error) {
 | 
				
			||||||
	for _, service := range recipe.Config.Services {
 | 
						config, err := recipe.GetComposeConfig(nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, service := range config.Services {
 | 
				
			||||||
		img, err := reference.ParseNormalizedNamed(service.Image)
 | 
							img, err := reference.ParseNormalizedNamed(service.Image)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return false, err
 | 
								return false, err
 | 
				
			||||||
@ -330,8 +358,12 @@ func LintSemverLikeTags(recipe recipe.Recipe) (bool, error) {
 | 
				
			|||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LintImagePresent(recipe recipe.Recipe) (bool, error) {
 | 
					func LintImagePresent(recipe recipe.Recipe2) (bool, error) {
 | 
				
			||||||
	for _, service := range recipe.Config.Services {
 | 
						config, err := recipe.GetComposeConfig(nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, service := range config.Services {
 | 
				
			||||||
		if service.Image == "" {
 | 
							if service.Image == "" {
 | 
				
			||||||
			return false, nil
 | 
								return false, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -339,7 +371,7 @@ func LintImagePresent(recipe recipe.Recipe) (bool, error) {
 | 
				
			|||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LintHasPublishedVersion(recipe recipe.Recipe) (bool, error) {
 | 
					func LintHasPublishedVersion(recipe recipe.Recipe2) (bool, error) {
 | 
				
			||||||
	catl, err := recipePkg.ReadRecipeCatalogue(false)
 | 
						catl, err := recipePkg.ReadRecipeCatalogue(false)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal(err)
 | 
							log.Fatal(err)
 | 
				
			||||||
@ -357,7 +389,7 @@ func LintHasPublishedVersion(recipe recipe.Recipe) (bool, error) {
 | 
				
			|||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LintMetadataFilledIn(r recipe.Recipe) (bool, error) {
 | 
					func LintMetadataFilledIn(r recipe.Recipe2) (bool, error) {
 | 
				
			||||||
	r2 := recipe.Get2(r.Name)
 | 
						r2 := recipe.Get2(r.Name)
 | 
				
			||||||
	features, category, err := recipe.GetRecipeFeaturesAndCategory(r2)
 | 
						features, category, err := recipe.GetRecipeFeaturesAndCategory(r2)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@ -379,10 +411,14 @@ func LintMetadataFilledIn(r recipe.Recipe) (bool, error) {
 | 
				
			|||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LintAbraShVendors(recipe recipe.Recipe) (bool, error) {
 | 
					func LintAbraShVendors(recipe recipe.Recipe2) (bool, error) {
 | 
				
			||||||
	for _, service := range recipe.Config.Services {
 | 
						config, err := recipe.GetComposeConfig(nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, service := range config.Services {
 | 
				
			||||||
		if len(service.Configs) > 0 {
 | 
							if len(service.Configs) > 0 {
 | 
				
			||||||
			abraSh := path.Join(config.RECIPES_DIR, recipe.Name, "abra.sh")
 | 
								abraSh := path.Join(recipe.Dir, "abra.sh")
 | 
				
			||||||
			if _, err := os.Stat(abraSh); err != nil {
 | 
								if _, err := os.Stat(abraSh); err != nil {
 | 
				
			||||||
				if os.IsNotExist(err) {
 | 
									if os.IsNotExist(err) {
 | 
				
			||||||
					return false, err
 | 
										return false, err
 | 
				
			||||||
@ -394,7 +430,7 @@ func LintAbraShVendors(recipe recipe.Recipe) (bool, error) {
 | 
				
			|||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LintHasRecipeRepo(recipe recipe.Recipe) (bool, error) {
 | 
					func LintHasRecipeRepo(recipe recipe.Recipe2) (bool, error) {
 | 
				
			||||||
	url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, recipe.Name)
 | 
						url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, recipe.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res, err := http.Get(url)
 | 
						res, err := http.Get(url)
 | 
				
			||||||
@ -409,8 +445,12 @@ func LintHasRecipeRepo(recipe recipe.Recipe) (bool, error) {
 | 
				
			|||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LintSecretLengths(recipe recipe.Recipe) (bool, error) {
 | 
					func LintSecretLengths(recipe recipe.Recipe2) (bool, error) {
 | 
				
			||||||
	for name := range recipe.Config.Secrets {
 | 
						config, err := recipe.GetComposeConfig(nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for name := range config.Secrets {
 | 
				
			||||||
		if len(name) > 12 {
 | 
							if len(name) > 12 {
 | 
				
			||||||
			return false, fmt.Errorf("secret %s is longer than 12 characters", name)
 | 
								return false, fmt.Errorf("secret %s is longer than 12 characters", name)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -419,7 +459,7 @@ func LintSecretLengths(recipe recipe.Recipe) (bool, error) {
 | 
				
			|||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LintValidTags(recipe recipe.Recipe) (bool, error) {
 | 
					func LintValidTags(recipe recipe.Recipe2) (bool, error) {
 | 
				
			||||||
	recipeDir := path.Join(config.RECIPES_DIR, recipe.Name)
 | 
						recipeDir := path.Join(config.RECIPES_DIR, recipe.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	repo, err := git.PlainOpen(recipeDir)
 | 
						repo, err := git.PlainOpen(recipeDir)
 | 
				
			||||||
 | 
				
			|||||||
@ -59,6 +59,55 @@ func (r Recipe2) GetComposeFiles(appEnv map[string]string) ([]string, error) {
 | 
				
			|||||||
	return composeFiles, nil
 | 
						return composeFiles, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r Recipe2) GetComposeConfig(env map[string]string) (*composetypes.Config, error) {
 | 
				
			||||||
 | 
						pattern := fmt.Sprintf("%s/compose**yml", r.Dir)
 | 
				
			||||||
 | 
						composeFiles, err := filepath.Glob(pattern)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(composeFiles) == 0 {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("%s is missing a compose.yml or compose.*.yml file?", r.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if env == nil {
 | 
				
			||||||
 | 
							env, err = r.SampleEnv()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := stack.Deploy{Composefiles: composeFiles}
 | 
				
			||||||
 | 
						config, err := loader.LoadComposefile(opts, env)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return config, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetVersionLabelLocal retrieves the version label on the local recipe config
 | 
				
			||||||
 | 
					func (r Recipe2) GetVersionLabelLocal() (string, error) {
 | 
				
			||||||
 | 
						var label string
 | 
				
			||||||
 | 
						config, err := r.GetComposeConfig(nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, service := range config.Services {
 | 
				
			||||||
 | 
							for label, value := range service.Deploy.Labels {
 | 
				
			||||||
 | 
								if strings.HasPrefix(label, "coop-cloud") && strings.Contains(label, "version") {
 | 
				
			||||||
 | 
									return value, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if label == "" {
 | 
				
			||||||
 | 
							return label, fmt.Errorf("%s has no version label? try running \"abra recipe sync %s\" first?", r.Name, r.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return label, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UpdateTag updates an image tag in-place on file system local compose files.
 | 
					// UpdateTag updates an image tag in-place on file system local compose files.
 | 
				
			||||||
func (r Recipe2) UpdateTag(image, tag string) (bool, error) {
 | 
					func (r Recipe2) UpdateTag(image, tag string) (bool, error) {
 | 
				
			||||||
	fullPattern := fmt.Sprintf("%s/compose**yml", r.Dir)
 | 
						fullPattern := fmt.Sprintf("%s/compose**yml", r.Dir)
 | 
				
			||||||
@ -119,7 +168,7 @@ func (r Recipe2) UpdateTag(image, tag string) (bool, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
				log.Debugf("updating %s to %s in %s", old, new, compose.Filename)
 | 
									log.Debugf("updating %s to %s in %s", old, new, compose.Filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if err := os.WriteFile(compose.Filename, []byte(replacedBytes), 0764); err != nil {
 | 
									if err := os.WriteFile(compose.Filename, []byte(replacedBytes), 0o764); err != nil {
 | 
				
			||||||
					return false, err
 | 
										return false, err
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -185,7 +234,7 @@ func (r Recipe2) UpdateLabel(pattern, serviceName, label string) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
				log.Debugf("updating %s to %s in %s", old, label, compose.Filename)
 | 
									log.Debugf("updating %s to %s in %s", old, label, compose.Filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if err := ioutil.WriteFile(compose.Filename, []byte(replacedBytes), 0764); err != nil {
 | 
									if err := ioutil.WriteFile(compose.Filename, []byte(replacedBytes), 0o764); err != nil {
 | 
				
			||||||
					return err
 | 
										return err
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,7 @@ import (
 | 
				
			|||||||
	"coopcloud.tech/abra/pkg/formatter"
 | 
						"coopcloud.tech/abra/pkg/formatter"
 | 
				
			||||||
	gitPkg "coopcloud.tech/abra/pkg/git"
 | 
						gitPkg "coopcloud.tech/abra/pkg/git"
 | 
				
			||||||
	"coopcloud.tech/abra/pkg/log"
 | 
						"coopcloud.tech/abra/pkg/log"
 | 
				
			||||||
 | 
						"github.com/distribution/reference"
 | 
				
			||||||
	"github.com/go-git/go-git/v5"
 | 
						"github.com/go-git/go-git/v5"
 | 
				
			||||||
	"github.com/go-git/go-git/v5/plumbing"
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -288,3 +289,91 @@ func (r Recipe2) Tags() ([]string, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return tags, nil
 | 
						return tags, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetRecipeVersions retrieves all recipe versions.
 | 
				
			||||||
 | 
					func (r Recipe2) GetRecipeVersions(offline bool) (RecipeVersions, error) {
 | 
				
			||||||
 | 
						versions := RecipeVersions{}
 | 
				
			||||||
 | 
						log.Debugf("attempting to open git repository in %s", r.Dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repo, err := git.PlainOpen(r.Dir)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return versions, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						worktree, err := repo.Worktree()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return versions, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gitTags, err := repo.Tags()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return versions, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := gitTags.ForEach(func(ref *plumbing.Reference) (err error) {
 | 
				
			||||||
 | 
							tag := strings.TrimPrefix(string(ref.Name()), "refs/tags/")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							log.Debugf("processing %s for %s", tag, r.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							checkOutOpts := &git.CheckoutOptions{
 | 
				
			||||||
 | 
								Create: false,
 | 
				
			||||||
 | 
								Force:  true,
 | 
				
			||||||
 | 
								Branch: plumbing.ReferenceName(ref.Name()),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err := worktree.Checkout(checkOutOpts); err != nil {
 | 
				
			||||||
 | 
								log.Debugf("failed to check out %s in %s", tag, r.Dir)
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							log.Debugf("successfully checked out %s in %s", ref.Name(), r.Dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							config, err := r.GetComposeConfig(nil)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							versionMeta := make(map[string]ServiceMeta)
 | 
				
			||||||
 | 
							for _, service := range config.Services {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								img, err := reference.ParseNormalizedNamed(service.Image)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								path := reference.Path(img)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								path = formatter.StripTagMeta(path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var tag string
 | 
				
			||||||
 | 
								switch img.(type) {
 | 
				
			||||||
 | 
								case reference.NamedTagged:
 | 
				
			||||||
 | 
									tag = img.(reference.NamedTagged).Tag()
 | 
				
			||||||
 | 
								case reference.Named:
 | 
				
			||||||
 | 
									log.Warnf("%s service is missing image tag?", path)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								versionMeta[service.Name] = ServiceMeta{
 | 
				
			||||||
 | 
									Image: path,
 | 
				
			||||||
 | 
									Tag:   tag,
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							versions = append(versions, map[string]map[string]ServiceMeta{tag: versionMeta})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}); err != nil {
 | 
				
			||||||
 | 
							return versions, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = gitPkg.CheckoutDefaultBranch(repo, r.Dir)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return versions, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sortRecipeVersions(versions)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Debugf("collected %s for %s", versions, r.Dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return versions, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,6 @@ import (
 | 
				
			|||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"path/filepath"
 | 
					 | 
				
			||||||
	"slices"
 | 
						"slices"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
@ -18,14 +17,8 @@ import (
 | 
				
			|||||||
	gitPkg "coopcloud.tech/abra/pkg/git"
 | 
						gitPkg "coopcloud.tech/abra/pkg/git"
 | 
				
			||||||
	"coopcloud.tech/abra/pkg/limit"
 | 
						"coopcloud.tech/abra/pkg/limit"
 | 
				
			||||||
	"coopcloud.tech/abra/pkg/log"
 | 
						"coopcloud.tech/abra/pkg/log"
 | 
				
			||||||
	"coopcloud.tech/abra/pkg/upstream/stack"
 | 
					 | 
				
			||||||
	loader "coopcloud.tech/abra/pkg/upstream/stack"
 | 
					 | 
				
			||||||
	"coopcloud.tech/abra/pkg/web"
 | 
						"coopcloud.tech/abra/pkg/web"
 | 
				
			||||||
	"coopcloud.tech/tagcmp"
 | 
						"coopcloud.tech/tagcmp"
 | 
				
			||||||
	"github.com/distribution/reference"
 | 
					 | 
				
			||||||
	composetypes "github.com/docker/cli/cli/compose/types"
 | 
					 | 
				
			||||||
	"github.com/go-git/go-git/v5"
 | 
					 | 
				
			||||||
	"github.com/go-git/go-git/v5/plumbing"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RecipeCatalogueURL is the only current recipe catalogue available.
 | 
					// RecipeCatalogueURL is the only current recipe catalogue available.
 | 
				
			||||||
@ -131,9 +124,8 @@ type Features struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Recipe represents a recipe.
 | 
					// Recipe represents a recipe.
 | 
				
			||||||
type Recipe struct {
 | 
					type Recipe struct {
 | 
				
			||||||
	Name   string
 | 
						Name string
 | 
				
			||||||
	Config *composetypes.Config
 | 
						Meta RecipeMeta
 | 
				
			||||||
	Meta   RecipeMeta
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Get retrieves a recipe.
 | 
					// Get retrieves a recipe.
 | 
				
			||||||
@ -143,27 +135,6 @@ func Get(recipeName string, offline bool) (Recipe, error) {
 | 
				
			|||||||
		return Recipe{}, err
 | 
							return Recipe{}, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pattern := fmt.Sprintf("%s/%s/compose**yml", config.RECIPES_DIR, r.Name)
 | 
					 | 
				
			||||||
	composeFiles, err := filepath.Glob(pattern)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return Recipe{}, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(composeFiles) == 0 {
 | 
					 | 
				
			||||||
		return Recipe{}, fmt.Errorf("%s is missing a compose.yml or compose.*.yml file?", r.Name)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sampleEnv, err := r.SampleEnv()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return Recipe{}, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	opts := stack.Deploy{Composefiles: composeFiles}
 | 
					 | 
				
			||||||
	config, err := loader.LoadComposefile(opts, sampleEnv)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return Recipe{}, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	meta, err := GetRecipeMeta(r.Name, offline)
 | 
						meta, err := GetRecipeMeta(r.Name, offline)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		switch err.(type) {
 | 
							switch err.(type) {
 | 
				
			||||||
@ -175,9 +146,8 @@ func Get(recipeName string, offline bool) (Recipe, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return Recipe{
 | 
						return Recipe{
 | 
				
			||||||
		Name:   recipeName,
 | 
							Name: recipeName,
 | 
				
			||||||
		Config: config,
 | 
							Meta: meta,
 | 
				
			||||||
		Meta:   meta,
 | 
					 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -216,25 +186,6 @@ func GetRecipesLocal() ([]string, error) {
 | 
				
			|||||||
	return recipes, nil
 | 
						return recipes, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetVersionLabelLocal retrieves the version label on the local recipe config
 | 
					 | 
				
			||||||
func GetVersionLabelLocal(recipe Recipe) (string, error) {
 | 
					 | 
				
			||||||
	var label string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, service := range recipe.Config.Services {
 | 
					 | 
				
			||||||
		for label, value := range service.Deploy.Labels {
 | 
					 | 
				
			||||||
			if strings.HasPrefix(label, "coop-cloud") && strings.Contains(label, "version") {
 | 
					 | 
				
			||||||
				return value, nil
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if label == "" {
 | 
					 | 
				
			||||||
		return label, fmt.Errorf("%s has no version label? try running \"abra recipe sync %s\" first?", recipe.Name, recipe.Name)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return label, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func GetRecipeFeaturesAndCategory(r Recipe2) (Features, string, error) {
 | 
					func GetRecipeFeaturesAndCategory(r Recipe2) (Features, string, error) {
 | 
				
			||||||
	feat := Features{}
 | 
						feat := Features{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -579,96 +530,6 @@ func ReadReposMetadata() (RepoCatalogue, error) {
 | 
				
			|||||||
	return reposMeta, nil
 | 
						return reposMeta, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetRecipeVersions retrieves all recipe versions.
 | 
					 | 
				
			||||||
func GetRecipeVersions(recipeName string, offline bool) (RecipeVersions, error) {
 | 
					 | 
				
			||||||
	versions := RecipeVersions{}
 | 
					 | 
				
			||||||
	recipeDir := path.Join(config.RECIPES_DIR, recipeName)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	log.Debugf("attempting to open git repository in %s", recipeDir)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	repo, err := git.PlainOpen(recipeDir)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return versions, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	worktree, err := repo.Worktree()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return versions, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	gitTags, err := repo.Tags()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return versions, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err := gitTags.ForEach(func(ref *plumbing.Reference) (err error) {
 | 
					 | 
				
			||||||
		tag := strings.TrimPrefix(string(ref.Name()), "refs/tags/")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		log.Debugf("processing %s for %s", tag, recipeName)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		checkOutOpts := &git.CheckoutOptions{
 | 
					 | 
				
			||||||
			Create: false,
 | 
					 | 
				
			||||||
			Force:  true,
 | 
					 | 
				
			||||||
			Branch: plumbing.ReferenceName(ref.Name()),
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if err := worktree.Checkout(checkOutOpts); err != nil {
 | 
					 | 
				
			||||||
			log.Debugf("failed to check out %s in %s", tag, recipeDir)
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		log.Debugf("successfully checked out %s in %s", ref.Name(), recipeDir)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		recipe, err := Get(recipeName, offline)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		versionMeta := make(map[string]ServiceMeta)
 | 
					 | 
				
			||||||
		for _, service := range recipe.Config.Services {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			img, err := reference.ParseNormalizedNamed(service.Image)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			path := reference.Path(img)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			path = formatter.StripTagMeta(path)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			var tag string
 | 
					 | 
				
			||||||
			switch img.(type) {
 | 
					 | 
				
			||||||
			case reference.NamedTagged:
 | 
					 | 
				
			||||||
				tag = img.(reference.NamedTagged).Tag()
 | 
					 | 
				
			||||||
			case reference.Named:
 | 
					 | 
				
			||||||
				log.Warnf("%s service is missing image tag?", path)
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			versionMeta[service.Name] = ServiceMeta{
 | 
					 | 
				
			||||||
				Image: path,
 | 
					 | 
				
			||||||
				Tag:   tag,
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		versions = append(versions, map[string]map[string]ServiceMeta{tag: versionMeta})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}); err != nil {
 | 
					 | 
				
			||||||
		return versions, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, err = gitPkg.CheckoutDefaultBranch(repo, recipeDir)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return versions, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sortRecipeVersions(versions)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	log.Debugf("collected %s for %s", versions, recipeName)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return versions, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// sortRecipeVersions sorts the recipe semver versions
 | 
					// sortRecipeVersions sorts the recipe semver versions
 | 
				
			||||||
func sortRecipeVersions(versions RecipeVersions) {
 | 
					func sortRecipeVersions(versions RecipeVersions) {
 | 
				
			||||||
	sort.Slice(versions, func(i, j int) bool {
 | 
						sort.Slice(versions, func(i, j int) bool {
 | 
				
			||||||
 | 
				
			|||||||
@ -14,13 +14,10 @@ func TestGetVersionLabelLocalDoesNotUseTimeoutLabel(t *testing.T) {
 | 
				
			|||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r, err := Get("traefik", offline)
 | 
						r := Get2("traefik")
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i := 1; i < 1000; i++ {
 | 
						for i := 1; i < 1000; i++ {
 | 
				
			||||||
		label, err := GetVersionLabelLocal(r)
 | 
							label, err := r.GetVersionLabelLocal()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Fatal(err)
 | 
								t.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user