forked from toolshed/abra
		
	@ -93,7 +93,7 @@ var AppNewCommand = &cobra.Command{
 | 
				
			|||||||
		var recipeVersions recipePkg.RecipeVersions
 | 
							var recipeVersions recipePkg.RecipeVersions
 | 
				
			||||||
		if recipeVersion == "" {
 | 
							if recipeVersion == "" {
 | 
				
			||||||
			var err error
 | 
								var err error
 | 
				
			||||||
			recipeVersions, err = recipe.GetRecipeVersions()
 | 
								recipeVersions, _, err = recipe.GetRecipeVersions()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Fatal(err)
 | 
									log.Fatal(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
 | 
						"slices"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"coopcloud.tech/abra/cli/internal"
 | 
						"coopcloud.tech/abra/cli/internal"
 | 
				
			||||||
	"coopcloud.tech/abra/pkg/autocomplete"
 | 
						"coopcloud.tech/abra/pkg/autocomplete"
 | 
				
			||||||
@ -61,52 +62,48 @@ keys configured on your account.`,
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		repos, err := recipe.ReadReposMetadata()
 | 
							repos, err := recipe.ReadReposMetadata(internal.Debug)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Fatal(err)
 | 
								log.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var barLength int
 | 
							barLength := len(repos)
 | 
				
			||||||
		var logMsg string
 | 
					 | 
				
			||||||
		if recipeName != "" {
 | 
							if recipeName != "" {
 | 
				
			||||||
			barLength = 1
 | 
								barLength = 1
 | 
				
			||||||
			logMsg = fmt.Sprintf("ensuring %v recipe is cloned & up-to-date", barLength)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			barLength = len(repos)
 | 
					 | 
				
			||||||
			logMsg = fmt.Sprintf("ensuring %v recipes are cloned & up-to-date, this could take some time...", barLength)
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if !skipUpdates {
 | 
							if !skipUpdates {
 | 
				
			||||||
			log.Warn(logMsg)
 | 
								if err := recipe.UpdateRepositories(repos, recipeName, internal.Debug); err != nil {
 | 
				
			||||||
			if err := recipe.UpdateRepositories(repos, recipeName); err != nil {
 | 
					 | 
				
			||||||
				log.Fatal(err)
 | 
									log.Fatal(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var warnings []string
 | 
				
			||||||
		catl := make(recipe.RecipeCatalogue)
 | 
							catl := make(recipe.RecipeCatalogue)
 | 
				
			||||||
		catlBar := formatter.CreateProgressbar(barLength, "generating catalogue metadata...")
 | 
							catlBar := formatter.CreateProgressbar(barLength, "collecting catalogue metadata")
 | 
				
			||||||
		for _, recipeMeta := range repos {
 | 
							for _, recipeMeta := range repos {
 | 
				
			||||||
			if recipeName != "" && recipeName != recipeMeta.Name {
 | 
								if recipeName != "" && recipeName != recipeMeta.Name {
 | 
				
			||||||
				catlBar.Add(1)
 | 
									if !internal.Debug {
 | 
				
			||||||
				continue
 | 
										catlBar.Add(1)
 | 
				
			||||||
			}
 | 
									}
 | 
				
			||||||
 | 
					 | 
				
			||||||
			// NOTE(d1): the "example" recipe is a temporary special case
 | 
					 | 
				
			||||||
			// https://git.coopcloud.tech/toolshed/organising/issues/666
 | 
					 | 
				
			||||||
			if recipeMeta.Name == "example" {
 | 
					 | 
				
			||||||
				catlBar.Add(1)
 | 
					 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			r := recipe.Get(recipeMeta.Name)
 | 
								r := recipe.Get(recipeMeta.Name)
 | 
				
			||||||
			versions, err := r.GetRecipeVersions()
 | 
								versions, warnMsgs, err := r.GetRecipeVersions()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Warn(err)
 | 
									warnings = append(warnings, err.Error())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if len(warnMsgs) > 0 {
 | 
				
			||||||
 | 
									warnings = append(warnings, warnMsgs...)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			features, category, err := recipe.GetRecipeFeaturesAndCategory(r)
 | 
								features, category, warnMsgs, err := recipe.GetRecipeFeaturesAndCategory(r)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Warn(err)
 | 
									warnings = append(warnings, err.Error())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if len(warnMsgs) > 0 {
 | 
				
			||||||
 | 
									warnings = append(warnings, warnMsgs...)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			catl[recipeMeta.Name] = recipe.RecipeMeta{
 | 
								catl[recipeMeta.Name] = recipe.RecipeMeta{
 | 
				
			||||||
@ -122,7 +119,24 @@ keys configured on your account.`,
 | 
				
			|||||||
				Features:      features,
 | 
									Features:      features,
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			catlBar.Add(1)
 | 
								if !internal.Debug {
 | 
				
			||||||
 | 
									catlBar.Add(1)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err := catlBar.Close(); err != nil {
 | 
				
			||||||
 | 
								log.Fatal(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var uniqueWarnings []string
 | 
				
			||||||
 | 
							for _, w := range warnings {
 | 
				
			||||||
 | 
								if !slices.Contains(uniqueWarnings, w) {
 | 
				
			||||||
 | 
									uniqueWarnings = append(uniqueWarnings, w)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for _, warnMsg := range uniqueWarnings {
 | 
				
			||||||
 | 
								log.Warn(warnMsg)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		recipesJSON, err := json.MarshalIndent(catl, "", "    ")
 | 
							recipesJSON, err := json.MarshalIndent(catl, "", "    ")
 | 
				
			||||||
@ -152,7 +166,7 @@ keys configured on your account.`,
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log.Infof("generated new recipe catalogue in %s", config.RECIPES_JSON)
 | 
							log.Infof("generated recipe catalogue: %s", config.RECIPES_JSON)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		cataloguePath := path.Join(config.ABRA_DIR, "catalogue")
 | 
							cataloguePath := path.Join(config.ABRA_DIR, "catalogue")
 | 
				
			||||||
		if publishChanges {
 | 
							if publishChanges {
 | 
				
			||||||
 | 
				
			|||||||
@ -37,10 +37,13 @@ var RecipeVersionCommand = &cobra.Command{
 | 
				
			|||||||
		if !ok {
 | 
							if !ok {
 | 
				
			||||||
			warnMessages = append(warnMessages, "retrieved versions from local recipe repository")
 | 
								warnMessages = append(warnMessages, "retrieved versions from local recipe repository")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			recipeVersions, err := recipe.GetRecipeVersions()
 | 
								recipeVersions, warnMsg, err := recipe.GetRecipeVersions()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				warnMessages = append(warnMessages, err.Error())
 | 
									warnMessages = append(warnMessages, err.Error())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								if len(warnMsg) > 0 {
 | 
				
			||||||
 | 
									warnMessages = append(warnMessages, warnMsg...)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			recipeMeta = recipePkg.RecipeMeta{Versions: recipeVersions}
 | 
								recipeMeta = recipePkg.RecipeMeta{Versions: recipeVersions}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
				
			|||||||
@ -16,13 +16,12 @@ import (
 | 
				
			|||||||
func EnsureCatalogue() error {
 | 
					func EnsureCatalogue() error {
 | 
				
			||||||
	catalogueDir := path.Join(config.ABRA_DIR, "catalogue")
 | 
						catalogueDir := path.Join(config.ABRA_DIR, "catalogue")
 | 
				
			||||||
	if _, err := os.Stat(catalogueDir); err != nil && os.IsNotExist(err) {
 | 
						if _, err := os.Stat(catalogueDir); err != nil && os.IsNotExist(err) {
 | 
				
			||||||
		log.Warnf("local recipe catalogue is missing, retrieving now")
 | 
							log.Debugf("catalogue is missing, retrieving now")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, config.CATALOGUE_JSON_REPO_NAME)
 | 
							url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, config.CATALOGUE_JSON_REPO_NAME)
 | 
				
			||||||
		if err := gitPkg.Clone(catalogueDir, url); err != nil {
 | 
							if err := gitPkg.Clone(catalogueDir, url); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
		log.Debugf("cloned catalogue repository to %s", catalogueDir)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
 | 
				
			|||||||
@ -217,7 +217,6 @@ func CreateProgressbar(length int, title string) *progressbar.ProgressBar {
 | 
				
			|||||||
		progressbar.OptionClearOnFinish(),
 | 
							progressbar.OptionClearOnFinish(),
 | 
				
			||||||
		progressbar.OptionSetPredictTime(false),
 | 
							progressbar.OptionSetPredictTime(false),
 | 
				
			||||||
		progressbar.OptionShowCount(),
 | 
							progressbar.OptionShowCount(),
 | 
				
			||||||
		progressbar.OptionFullWidth(),
 | 
					 | 
				
			||||||
		progressbar.OptionSetDescription(title),
 | 
							progressbar.OptionSetDescription(title),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,7 @@
 | 
				
			|||||||
package git
 | 
					package git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"coopcloud.tech/abra/pkg/log"
 | 
						"coopcloud.tech/abra/pkg/log"
 | 
				
			||||||
@ -11,10 +9,23 @@ import (
 | 
				
			|||||||
	"github.com/go-git/go-git/v5/plumbing"
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// gitCloneIgnoreErr checks whether we can ignore a git clone error or not.
 | 
				
			||||||
 | 
					func gitCloneIgnoreErr(err error) bool {
 | 
				
			||||||
 | 
						if strings.Contains(err.Error(), "authentication required") {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if strings.Contains(err.Error(), "remote repository is empty") {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Clone runs a git clone which accounts for different default branches.
 | 
					// Clone runs a git clone which accounts for different default branches.
 | 
				
			||||||
func Clone(dir, url string) error {
 | 
					func Clone(dir, url string) error {
 | 
				
			||||||
	if _, err := os.Stat(dir); os.IsNotExist(err) {
 | 
						if _, err := os.Stat(dir); os.IsNotExist(err) {
 | 
				
			||||||
		log.Debugf("%s does not exist, attempting git clone of %s", dir, url)
 | 
							log.Debugf("git clone: %s", dir, url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_, err := git.PlainClone(dir, false, &git.CloneOptions{
 | 
							_, err := git.PlainClone(dir, false, &git.CloneOptions{
 | 
				
			||||||
			URL:           url,
 | 
								URL:           url,
 | 
				
			||||||
@ -23,6 +34,11 @@ func Clone(dir, url string) error {
 | 
				
			|||||||
			SingleBranch:  true,
 | 
								SingleBranch:  true,
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err != nil && gitCloneIgnoreErr(err) {
 | 
				
			||||||
 | 
								log.Debugf("git clone: %s cloned successfully", dir)
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Debug("git clone: main branch failed, attempting master branch")
 | 
								log.Debug("git clone: main branch failed, attempting master branch")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -32,12 +48,13 @@ func Clone(dir, url string) error {
 | 
				
			|||||||
				ReferenceName: plumbing.ReferenceName("refs/heads/master"),
 | 
									ReferenceName: plumbing.ReferenceName("refs/heads/master"),
 | 
				
			||||||
				SingleBranch:  true,
 | 
									SingleBranch:  true,
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				if strings.Contains(err.Error(), "authentication required") {
 | 
					 | 
				
			||||||
					name := filepath.Base(dir)
 | 
					 | 
				
			||||||
					return fmt.Errorf("unable to clone %s, does %s exist?", name, url)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err != nil && gitCloneIgnoreErr(err) {
 | 
				
			||||||
 | 
									log.Debugf("git clone: %s cloned successfully", dir)
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
				
			|||||||
@ -409,7 +409,7 @@ func LintHasPublishedVersion(recipe recipe.Recipe) (bool, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LintMetadataFilledIn(r recipe.Recipe) (bool, error) {
 | 
					func LintMetadataFilledIn(r recipe.Recipe) (bool, error) {
 | 
				
			||||||
	features, category, err := recipe.GetRecipeFeaturesAndCategory(r)
 | 
						features, category, _, err := recipe.GetRecipeFeaturesAndCategory(r)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return false, err
 | 
							return false, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,7 @@ package recipe
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"slices"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"coopcloud.tech/abra/pkg/formatter"
 | 
						"coopcloud.tech/abra/pkg/formatter"
 | 
				
			||||||
@ -350,23 +351,26 @@ func (r Recipe) Tags() ([]string, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetRecipeVersions retrieves all recipe versions.
 | 
					// GetRecipeVersions retrieves all recipe versions.
 | 
				
			||||||
func (r Recipe) GetRecipeVersions() (RecipeVersions, error) {
 | 
					func (r Recipe) GetRecipeVersions() (RecipeVersions, []string, error) {
 | 
				
			||||||
 | 
						var warnMsg []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	versions := RecipeVersions{}
 | 
						versions := RecipeVersions{}
 | 
				
			||||||
	log.Debugf("attempting to open git repository in %s", r.Dir)
 | 
					
 | 
				
			||||||
 | 
						log.Debugf("git: opening repository in %s", r.Dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	repo, err := git.PlainOpen(r.Dir)
 | 
						repo, err := git.PlainOpen(r.Dir)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return versions, err
 | 
							return versions, warnMsg, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	worktree, err := repo.Worktree()
 | 
						worktree, err := repo.Worktree()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return versions, err
 | 
							return versions, warnMsg, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gitTags, err := repo.Tags()
 | 
						gitTags, err := repo.Tags()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return versions, err
 | 
							return versions, warnMsg, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := gitTags.ForEach(func(ref *plumbing.Reference) (err error) {
 | 
						if err := gitTags.ForEach(func(ref *plumbing.Reference) (err error) {
 | 
				
			||||||
@ -384,7 +388,7 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, error) {
 | 
				
			|||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log.Debugf("successfully checked out %s in %s", ref.Name(), r.Dir)
 | 
							log.Debugf("git checkout: %s in %s", ref.Name(), r.Dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		config, err := r.GetComposeConfig(nil)
 | 
							config, err := r.GetComposeConfig(nil)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
@ -408,7 +412,7 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, error) {
 | 
				
			|||||||
			case reference.NamedTagged:
 | 
								case reference.NamedTagged:
 | 
				
			||||||
				tag = img.(reference.NamedTagged).Tag()
 | 
									tag = img.(reference.NamedTagged).Tag()
 | 
				
			||||||
			case reference.Named:
 | 
								case reference.Named:
 | 
				
			||||||
				log.Warnf("%s service is missing image tag?", path)
 | 
									warnMsg = append(warnMsg, fmt.Sprintf("%s service is missing image tag?", path))
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -422,19 +426,26 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}); err != nil {
 | 
						}); err != nil {
 | 
				
			||||||
		return versions, err
 | 
							return versions, warnMsg, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err = gitPkg.CheckoutDefaultBranch(repo, r.Dir)
 | 
						_, err = gitPkg.CheckoutDefaultBranch(repo, r.Dir)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return versions, err
 | 
							return versions, warnMsg, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sortRecipeVersions(versions)
 | 
						sortRecipeVersions(versions)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.Debugf("collected %s for %s", versions, r.Dir)
 | 
						log.Debugf("collected %s for %s", versions, r.Dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return versions, nil
 | 
						var uniqueWarnings []string
 | 
				
			||||||
 | 
						for _, w := range warnMsg {
 | 
				
			||||||
 | 
							if !slices.Contains(uniqueWarnings, w) {
 | 
				
			||||||
 | 
								uniqueWarnings = append(uniqueWarnings, w)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return versions, uniqueWarnings, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Head retrieves latest HEAD metadata.
 | 
					// Head retrieves latest HEAD metadata.
 | 
				
			||||||
 | 
				
			|||||||
@ -233,16 +233,18 @@ func GetRecipesLocal() ([]string, error) {
 | 
				
			|||||||
	return recipes, nil
 | 
						return recipes, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetRecipeFeaturesAndCategory(r Recipe) (Features, string, error) {
 | 
					func GetRecipeFeaturesAndCategory(r Recipe) (Features, string, []string, error) {
 | 
				
			||||||
	feat := Features{}
 | 
						var (
 | 
				
			||||||
 | 
							category string
 | 
				
			||||||
 | 
							warnMsgs []string
 | 
				
			||||||
 | 
							feat     = Features{}
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var category string
 | 
						log.Debugf("%s: attempt recipe metadata parse", r.ReadmePath)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	log.Debugf("attempting to open %s for recipe metadata parsing", r.ReadmePath)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	readmeFS, err := ioutil.ReadFile(r.ReadmePath)
 | 
						readmeFS, err := ioutil.ReadFile(r.ReadmePath)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return feat, category, err
 | 
							return feat, category, warnMsgs, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	readmeMetadata, err := GetStringInBetween( // Find text between delimiters
 | 
						readmeMetadata, err := GetStringInBetween( // Find text between delimiters
 | 
				
			||||||
@ -251,7 +253,7 @@ func GetRecipeFeaturesAndCategory(r Recipe) (Features, string, error) {
 | 
				
			|||||||
		"<!-- metadata -->", "<!-- endmetadata -->",
 | 
							"<!-- metadata -->", "<!-- endmetadata -->",
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return feat, category, err
 | 
							return feat, category, warnMsgs, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	readmeLines := strings.Split( // Array item from lines
 | 
						readmeLines := strings.Split( // Array item from lines
 | 
				
			||||||
@ -295,20 +297,25 @@ func GetRecipeFeaturesAndCategory(r Recipe) (Features, string, error) {
 | 
				
			|||||||
			)
 | 
								)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if strings.Contains(val, "**Image**") {
 | 
							if strings.Contains(val, "**Image**") {
 | 
				
			||||||
			imageMetadata, err := GetImageMetadata(strings.TrimSpace(
 | 
								imageMetadata, warnings, err := GetImageMetadata(strings.TrimSpace(
 | 
				
			||||||
				strings.TrimPrefix(val, "* **Image**:"),
 | 
									strings.TrimPrefix(val, "* **Image**:"),
 | 
				
			||||||
			), r.Name)
 | 
								), r.Name)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								if len(warnings) > 0 {
 | 
				
			||||||
 | 
									warnMsgs = append(warnMsgs, warnings...)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			feat.Image = imageMetadata
 | 
								feat.Image = imageMetadata
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return feat, category, nil
 | 
						return feat, category, warnMsgs, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetImageMetadata(imageRowString, recipeName string) (Image, error) {
 | 
					func GetImageMetadata(imageRowString, recipeName string) (Image, []string, error) {
 | 
				
			||||||
 | 
						var warnMsgs []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	img := Image{}
 | 
						img := Image{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	imgFields := strings.Split(imageRowString, ",")
 | 
						imgFields := strings.Split(imageRowString, ",")
 | 
				
			||||||
@ -319,11 +326,18 @@ func GetImageMetadata(imageRowString, recipeName string) (Image, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if len(imgFields) < 3 {
 | 
						if len(imgFields) < 3 {
 | 
				
			||||||
		if imageRowString != "" {
 | 
							if imageRowString != "" {
 | 
				
			||||||
			log.Warnf("%s image meta has incorrect format: %s", recipeName, imageRowString)
 | 
								warnMsgs = append(
 | 
				
			||||||
 | 
									warnMsgs,
 | 
				
			||||||
 | 
									fmt.Sprintf("%s: image meta has incorrect format: %s", recipeName, imageRowString),
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			log.Warnf("%s image meta is empty?", recipeName)
 | 
								warnMsgs = append(
 | 
				
			||||||
 | 
									warnMsgs,
 | 
				
			||||||
 | 
									fmt.Sprintf("%s: image meta is empty?", recipeName),
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return img, nil
 | 
					
 | 
				
			||||||
 | 
							return img, warnMsgs, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	img.Rating = imgFields[1]
 | 
						img.Rating = imgFields[1]
 | 
				
			||||||
@ -333,17 +347,17 @@ func GetImageMetadata(imageRowString, recipeName string) (Image, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	imageName, err := GetStringInBetween(recipeName, imgString, "[", "]")
 | 
						imageName, err := GetStringInBetween(recipeName, imgString, "[", "]")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal(err)
 | 
							return img, warnMsgs, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	img.Image = strings.ReplaceAll(imageName, "`", "")
 | 
						img.Image = strings.ReplaceAll(imageName, "`", "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	imageURL, err := GetStringInBetween(recipeName, imgString, "(", ")")
 | 
						imageURL, err := GetStringInBetween(recipeName, imgString, "(", ")")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal(err)
 | 
							return img, warnMsgs, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	img.URL = imageURL
 | 
						img.URL = imageURL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return img, nil
 | 
						return img, warnMsgs, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetStringInBetween returns empty string if no start or end string found
 | 
					// GetStringInBetween returns empty string if no start or end string found
 | 
				
			||||||
@ -534,11 +548,11 @@ type InternalTracker struct {
 | 
				
			|||||||
type RepoCatalogue map[string]RepoMeta
 | 
					type RepoCatalogue map[string]RepoMeta
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ReadReposMetadata retrieves coop-cloud/... repo metadata from Gitea.
 | 
					// ReadReposMetadata retrieves coop-cloud/... repo metadata from Gitea.
 | 
				
			||||||
func ReadReposMetadata() (RepoCatalogue, error) {
 | 
					func ReadReposMetadata(debug bool) (RepoCatalogue, error) {
 | 
				
			||||||
	reposMeta := make(RepoCatalogue)
 | 
						reposMeta := make(RepoCatalogue)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pageIdx := 1
 | 
						pageIdx := 1
 | 
				
			||||||
	bar := formatter.CreateProgressbar(-1, "retrieving recipe repos list from git.coopcloud.tech...")
 | 
						bar := formatter.CreateProgressbar(-1, "collecting recipe listing")
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		var reposList []RepoMeta
 | 
							var reposList []RepoMeta
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -551,19 +565,32 @@ func ReadReposMetadata() (RepoCatalogue, error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if len(reposList) == 0 {
 | 
							if len(reposList) == 0 {
 | 
				
			||||||
			bar.Add(1)
 | 
								if !debug {
 | 
				
			||||||
 | 
									bar.Add(1)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for idx, repo := range reposList {
 | 
							for idx, repo := range reposList {
 | 
				
			||||||
 | 
								// NOTE(d1): the "example" recipe is a temporary special case
 | 
				
			||||||
 | 
								// https://git.coopcloud.tech/toolshed/organising/issues/666
 | 
				
			||||||
 | 
								if repo.Name == "example" {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			reposMeta[repo.Name] = reposList[idx]
 | 
								reposMeta[repo.Name] = reposList[idx]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		pageIdx++
 | 
							pageIdx++
 | 
				
			||||||
		bar.Add(1)
 | 
					
 | 
				
			||||||
 | 
							if !debug {
 | 
				
			||||||
 | 
								bar.Add(1)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fmt.Println() // newline for spinner
 | 
						if err := bar.Close(); err != nil {
 | 
				
			||||||
 | 
							return reposMeta, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return reposMeta, nil
 | 
						return reposMeta, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -625,7 +652,7 @@ func GetRecipeCatalogueVersions(recipeName string, catl RecipeCatalogue) ([]stri
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UpdateRepositories clones and updates all recipe repositories locally.
 | 
					// UpdateRepositories clones and updates all recipe repositories locally.
 | 
				
			||||||
func UpdateRepositories(repos RepoCatalogue, recipeName string) error {
 | 
					func UpdateRepositories(repos RepoCatalogue, recipeName string, debug bool) error {
 | 
				
			||||||
	var barLength int
 | 
						var barLength int
 | 
				
			||||||
	if recipeName != "" {
 | 
						if recipeName != "" {
 | 
				
			||||||
		barLength = 1
 | 
							barLength = 1
 | 
				
			||||||
@ -633,9 +660,9 @@ func UpdateRepositories(repos RepoCatalogue, recipeName string) error {
 | 
				
			|||||||
		barLength = len(repos)
 | 
							barLength = len(repos)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cloneLimiter := limit.New(10)
 | 
						cloneLimiter := limit.New(3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	retrieveBar := formatter.CreateProgressbar(barLength, "ensuring recipes are cloned & up-to-date...")
 | 
						retrieveBar := formatter.CreateProgressbar(barLength, "retrieving recipes")
 | 
				
			||||||
	ch := make(chan string, barLength)
 | 
						ch := make(chan string, barLength)
 | 
				
			||||||
	for _, repoMeta := range repos {
 | 
						for _, repoMeta := range repos {
 | 
				
			||||||
		go func(rm RepoMeta) {
 | 
							go func(rm RepoMeta) {
 | 
				
			||||||
@ -644,7 +671,9 @@ func UpdateRepositories(repos RepoCatalogue, recipeName string) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			if recipeName != "" && recipeName != rm.Name {
 | 
								if recipeName != "" && recipeName != rm.Name {
 | 
				
			||||||
				ch <- rm.Name
 | 
									ch <- rm.Name
 | 
				
			||||||
				retrieveBar.Add(1)
 | 
									if !debug {
 | 
				
			||||||
 | 
										retrieveBar.Add(1)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -653,7 +682,9 @@ func UpdateRepositories(repos RepoCatalogue, recipeName string) error {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ch <- rm.Name
 | 
								ch <- rm.Name
 | 
				
			||||||
			retrieveBar.Add(1)
 | 
								if !debug {
 | 
				
			||||||
 | 
									retrieveBar.Add(1)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}(repoMeta)
 | 
							}(repoMeta)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -661,6 +692,10 @@ func UpdateRepositories(repos RepoCatalogue, recipeName string) error {
 | 
				
			|||||||
		<-ch // wait for everything
 | 
							<-ch // wait for everything
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := retrieveBar.Close(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -6,9 +6,13 @@ setup(){
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# bats test_tags=slow
 | 
					# bats test_tags=slow
 | 
				
			||||||
@test "generate entire catalogue" {
 | 
					@test "generate catalogue" {
 | 
				
			||||||
  run $ABRA catalogue generate
 | 
					  run $ABRA catalogue generate
 | 
				
			||||||
  assert_success
 | 
					  assert_success
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for d in $(ls $ABRA_DIR/recipes); do 
 | 
				
			||||||
 | 
					    assert_exists "$ABRA_DIR/recipes/$d/.git"
 | 
				
			||||||
 | 
					  done
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@test "error if unstaged changes" {
 | 
					@test "error if unstaged changes" {
 | 
				
			||||||
@ -41,4 +45,5 @@ setup(){
 | 
				
			|||||||
@test "generate only specific recipe" {
 | 
					@test "generate only specific recipe" {
 | 
				
			||||||
  run $ABRA catalogue generate gitea
 | 
					  run $ABRA catalogue generate gitea
 | 
				
			||||||
  assert_success
 | 
					  assert_success
 | 
				
			||||||
 | 
					  assert_exists "$ABRA_DIR/recipes/gitea/.git"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user