forked from toolshed/abra
		
	
		
			
				
	
	
		
			276 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			276 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package catalogue
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"io/ioutil"
 | 
						|
	"path"
 | 
						|
	"slices"
 | 
						|
 | 
						|
	"coopcloud.tech/abra/cli/internal"
 | 
						|
	"coopcloud.tech/abra/pkg/autocomplete"
 | 
						|
	"coopcloud.tech/abra/pkg/catalogue"
 | 
						|
	"coopcloud.tech/abra/pkg/config"
 | 
						|
	"coopcloud.tech/abra/pkg/formatter"
 | 
						|
	gitPkg "coopcloud.tech/abra/pkg/git"
 | 
						|
	"coopcloud.tech/abra/pkg/log"
 | 
						|
	"coopcloud.tech/abra/pkg/recipe"
 | 
						|
	"github.com/go-git/go-git/v5"
 | 
						|
	"github.com/spf13/cobra"
 | 
						|
)
 | 
						|
 | 
						|
var CatalogueGenerateCommand = &cobra.Command{
 | 
						|
	Use:     "generate [recipe] [flags]",
 | 
						|
	Aliases: []string{"g"},
 | 
						|
	Short:   "Generate the recipe catalogue",
 | 
						|
	Long: `Generate a new copy of the recipe catalogue.
 | 
						|
 | 
						|
N.B. this command **will** wipe local unstaged changes from your local recipes
 | 
						|
if present. "--chaos/-C" on this command refers to the catalogue repository
 | 
						|
("$ABRA_DIR/catalogue") and not the recipes. Please take care not to lose your
 | 
						|
changes.
 | 
						|
 | 
						|
It is possible to generate new metadata for a single recipe by passing
 | 
						|
[recipe]. The existing local catalogue will be updated, not overwritten.
 | 
						|
 | 
						|
It is quite easy to get rate limited by Docker Hub when running this command.
 | 
						|
If you have a Hub account you can "docker login" and Abra will automatically
 | 
						|
use those details.
 | 
						|
 | 
						|
Push your new release to git.coopcloud.tech with "--publish/-p". This requires
 | 
						|
that you have permission to git push to these repositories and have your SSH
 | 
						|
keys configured on your account.`,
 | 
						|
	Args: cobra.RangeArgs(0, 1),
 | 
						|
	ValidArgsFunction: func(
 | 
						|
		cmd *cobra.Command,
 | 
						|
		args []string,
 | 
						|
		toComplete string) ([]string, cobra.ShellCompDirective) {
 | 
						|
		return autocomplete.RecipeNameComplete()
 | 
						|
	},
 | 
						|
	Run: func(cmd *cobra.Command, args []string) {
 | 
						|
		var recipeName string
 | 
						|
		if len(args) > 0 {
 | 
						|
			recipeName = args[0]
 | 
						|
		}
 | 
						|
 | 
						|
		if recipeName != "" {
 | 
						|
			internal.ValidateRecipe(args, cmd.Name())
 | 
						|
		}
 | 
						|
 | 
						|
		if err := catalogue.EnsureCatalogue(); err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		if !internal.Chaos {
 | 
						|
			if err := catalogue.EnsureIsClean(); err != nil {
 | 
						|
				log.Fatal(err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		repos, err := recipe.ReadReposMetadata(internal.Debug)
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		barLength := len(repos)
 | 
						|
		if recipeName != "" {
 | 
						|
			barLength = 1
 | 
						|
		}
 | 
						|
 | 
						|
		if !skipUpdates {
 | 
						|
			if err := recipe.UpdateRepositories(repos, recipeName, internal.Debug); err != nil {
 | 
						|
				log.Fatal(err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		var warnings []string
 | 
						|
		catl := make(recipe.RecipeCatalogue)
 | 
						|
		catlBar := formatter.CreateProgressbar(barLength, "collecting catalogue metadata")
 | 
						|
		for _, recipeMeta := range repos {
 | 
						|
			if recipeName != "" && recipeName != recipeMeta.Name {
 | 
						|
				if !internal.Debug {
 | 
						|
					catlBar.Add(1)
 | 
						|
				}
 | 
						|
				continue
 | 
						|
			}
 | 
						|
 | 
						|
			r := recipe.Get(recipeMeta.Name)
 | 
						|
			versions, warnMsgs, err := r.GetRecipeVersions()
 | 
						|
			if err != nil {
 | 
						|
				warnings = append(warnings, err.Error())
 | 
						|
			}
 | 
						|
			if len(warnMsgs) > 0 {
 | 
						|
				warnings = append(warnings, warnMsgs...)
 | 
						|
			}
 | 
						|
 | 
						|
			features, category, warnMsgs, err := recipe.GetRecipeFeaturesAndCategory(r)
 | 
						|
			if err != nil {
 | 
						|
				warnings = append(warnings, err.Error())
 | 
						|
			}
 | 
						|
			if len(warnMsgs) > 0 {
 | 
						|
				warnings = append(warnings, warnMsgs...)
 | 
						|
			}
 | 
						|
 | 
						|
			catl[recipeMeta.Name] = recipe.RecipeMeta{
 | 
						|
				Name:          recipeMeta.Name,
 | 
						|
				Repository:    recipeMeta.CloneURL,
 | 
						|
				SSHURL:        recipeMeta.SSHURL,
 | 
						|
				Icon:          recipeMeta.AvatarURL,
 | 
						|
				DefaultBranch: recipeMeta.DefaultBranch,
 | 
						|
				Description:   recipeMeta.Description,
 | 
						|
				Website:       recipeMeta.Website,
 | 
						|
				Versions:      versions,
 | 
						|
				Category:      category,
 | 
						|
				Features:      features,
 | 
						|
			}
 | 
						|
 | 
						|
			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, "", "    ")
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		if recipeName == "" {
 | 
						|
			if err := ioutil.WriteFile(config.RECIPES_JSON, recipesJSON, 0764); err != nil {
 | 
						|
				log.Fatal(err)
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			catlFS, err := recipe.ReadRecipeCatalogue(internal.Offline)
 | 
						|
			if err != nil {
 | 
						|
				log.Fatal(err)
 | 
						|
			}
 | 
						|
 | 
						|
			catlFS[recipeName] = catl[recipeName]
 | 
						|
 | 
						|
			updatedRecipesJSON, err := json.MarshalIndent(catlFS, "", "    ")
 | 
						|
			if err != nil {
 | 
						|
				log.Fatal(err)
 | 
						|
			}
 | 
						|
 | 
						|
			if err := ioutil.WriteFile(config.RECIPES_JSON, updatedRecipesJSON, 0764); err != nil {
 | 
						|
				log.Fatal(err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		log.Infof("generated recipe catalogue: %s", config.RECIPES_JSON)
 | 
						|
 | 
						|
		cataloguePath := path.Join(config.ABRA_DIR, "catalogue")
 | 
						|
		if publishChanges {
 | 
						|
 | 
						|
			isClean, err := gitPkg.IsClean(cataloguePath)
 | 
						|
			if err != nil {
 | 
						|
				log.Fatal(err)
 | 
						|
			}
 | 
						|
 | 
						|
			if isClean {
 | 
						|
				if !internal.Dry {
 | 
						|
					log.Fatalf("no changes discovered in %s, nothing to publish?", cataloguePath)
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			msg := "chore: publish new catalogue release changes"
 | 
						|
			if err := gitPkg.Commit(cataloguePath, msg, internal.Dry); err != nil {
 | 
						|
				log.Fatal(err)
 | 
						|
			}
 | 
						|
 | 
						|
			repo, err := git.PlainOpen(cataloguePath)
 | 
						|
			if err != nil {
 | 
						|
				log.Fatal(err)
 | 
						|
			}
 | 
						|
 | 
						|
			sshURL := fmt.Sprintf(config.TOOLSHED_SSH_URL_TEMPLATE, config.CATALOGUE_JSON_REPO_NAME)
 | 
						|
			if err := gitPkg.CreateRemote(repo, "origin-ssh", sshURL, internal.Dry); err != nil {
 | 
						|
				log.Fatal(err)
 | 
						|
			}
 | 
						|
 | 
						|
			if err := gitPkg.Push(cataloguePath, "origin-ssh", false, internal.Dry); err != nil {
 | 
						|
				log.Fatal(err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		repo, err := git.PlainOpen(cataloguePath)
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		head, err := repo.Head()
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		if !internal.Dry && publishChanges {
 | 
						|
			url := fmt.Sprintf("%s/%s/commit/%s", config.REPOS_BASE_URL, config.CATALOGUE_JSON_REPO_NAME, head.Hash())
 | 
						|
			log.Infof("new changes published: %s", url)
 | 
						|
		}
 | 
						|
 | 
						|
		if internal.Dry {
 | 
						|
			log.Info("dry run: no changes published")
 | 
						|
		}
 | 
						|
	},
 | 
						|
}
 | 
						|
 | 
						|
// CatalogueCommand defines the `abra catalogue` command and sub-commands.
 | 
						|
var CatalogueCommand = &cobra.Command{
 | 
						|
	Use:     "catalogue [cmd] [args] [flags]",
 | 
						|
	Short:   "Manage the recipe catalogue",
 | 
						|
	Aliases: []string{"c"},
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	publishChanges bool
 | 
						|
	skipUpdates    bool
 | 
						|
)
 | 
						|
 | 
						|
func init() {
 | 
						|
	CatalogueGenerateCommand.Flags().BoolVarP(
 | 
						|
		&publishChanges,
 | 
						|
		"publish",
 | 
						|
		"p",
 | 
						|
		false,
 | 
						|
		"publish changes to git.coopcloud.tech",
 | 
						|
	)
 | 
						|
 | 
						|
	CatalogueGenerateCommand.Flags().BoolVarP(
 | 
						|
		&internal.Dry,
 | 
						|
		"dry-run",
 | 
						|
		"r",
 | 
						|
		false,
 | 
						|
		"report changes that would be made",
 | 
						|
	)
 | 
						|
 | 
						|
	CatalogueGenerateCommand.Flags().BoolVarP(
 | 
						|
		&skipUpdates,
 | 
						|
		"skip-updates",
 | 
						|
		"s",
 | 
						|
		false,
 | 
						|
		"skip updating recipe repositories",
 | 
						|
	)
 | 
						|
 | 
						|
	CatalogueGenerateCommand.Flags().BoolVarP(
 | 
						|
		&internal.Chaos,
 | 
						|
		"chaos",
 | 
						|
		"C",
 | 
						|
		false,
 | 
						|
		"ignore uncommitted recipes changes",
 | 
						|
	)
 | 
						|
}
 |