feat: generate versions for catalogue also
Closes coop-cloud/organising#179.
This commit is contained in:
		| @ -2,6 +2,7 @@ package catalogue | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"path" | 	"path" | ||||||
|  |  | ||||||
| @ -45,11 +46,22 @@ var CatalogueSkipList = map[string]bool{ | |||||||
| } | } | ||||||
|  |  | ||||||
| var catalogueGenerateCommand = &cli.Command{ | var catalogueGenerateCommand = &cli.Command{ | ||||||
| 	Name:         "generate", | 	Name:      "generate", | ||||||
| 	Aliases:      []string{"g"}, | 	Aliases:   []string{"g"}, | ||||||
| 	Usage:        "Generate a new copy of the catalogue", | 	Usage:     "Generate a new copy of the catalogue", | ||||||
| 	ArgsUsage:    "[<recipe>]", | 	ArgsUsage: "[<recipe>]", | ||||||
| 	BashComplete: func(c *cli.Context) {}, | 	BashComplete: func(c *cli.Context) { | ||||||
|  | 		catl, err := catalogue.ReadRecipeCatalogue() | ||||||
|  | 		if err != nil { | ||||||
|  | 			logrus.Warn(err) | ||||||
|  | 		} | ||||||
|  | 		if c.NArg() > 0 { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		for name := range catl { | ||||||
|  | 			fmt.Println(name) | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
| 	Action: func(c *cli.Context) error { | 	Action: func(c *cli.Context) error { | ||||||
| 		recipeName := c.Args().First() | 		recipeName := c.Args().First() | ||||||
|  |  | ||||||
| @ -60,18 +72,18 @@ var catalogueGenerateCommand = &cli.Command{ | |||||||
|  |  | ||||||
| 		logrus.Debugf("ensuring '%v' recipe(s) are locally present and up-to-date", len(repos)) | 		logrus.Debugf("ensuring '%v' recipe(s) are locally present and up-to-date", len(repos)) | ||||||
|  |  | ||||||
| 		bar := formatter.CreateProgressbar(len(repos), "retrieving recipes...") | 		retrieveBar := formatter.CreateProgressbar(len(repos), "retrieving recipes...") | ||||||
| 		ch := make(chan string, len(repos)) | 		ch := make(chan string, len(repos)) | ||||||
| 		for _, repoMeta := range repos { | 		for _, repoMeta := range repos { | ||||||
| 			go func(rm catalogue.RepoMeta) { | 			go func(rm catalogue.RepoMeta) { | ||||||
| 				if recipeName != "" && recipeName != rm.Name { | 				if recipeName != "" && recipeName != rm.Name { | ||||||
| 					ch <- rm.Name | 					ch <- rm.Name | ||||||
| 					bar.Add(1) | 					retrieveBar.Add(1) | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| 				if _, exists := CatalogueSkipList[rm.Name]; exists { | 				if _, exists := CatalogueSkipList[rm.Name]; exists { | ||||||
| 					ch <- rm.Name | 					ch <- rm.Name | ||||||
| 					bar.Add(1) | 					retrieveBar.Add(1) | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| @ -86,7 +98,7 @@ var catalogueGenerateCommand = &cli.Command{ | |||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				ch <- rm.Name | 				ch <- rm.Name | ||||||
| 				bar.Add(1) | 				retrieveBar.Add(1) | ||||||
| 			}(repoMeta) | 			}(repoMeta) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @ -95,14 +107,23 @@ var catalogueGenerateCommand = &cli.Command{ | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		catl := make(catalogue.RecipeCatalogue) | 		catl := make(catalogue.RecipeCatalogue) | ||||||
|  | 		catlBar := formatter.CreateProgressbar(len(repos), "generating catalogue...") | ||||||
| 		for _, recipeMeta := range repos { | 		for _, recipeMeta := range repos { | ||||||
| 			if recipeName != "" && recipeName != recipeMeta.Name { | 			if recipeName != "" && recipeName != recipeMeta.Name { | ||||||
|  | 				catlBar.Add(1) | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if _, exists := CatalogueSkipList[recipeMeta.Name]; exists { | 			if _, exists := CatalogueSkipList[recipeMeta.Name]; exists { | ||||||
|  | 				catlBar.Add(1) | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			versions, err := catalogue.GetRecipeVersions(recipeMeta.Name) | ||||||
|  | 			if err != nil { | ||||||
|  | 				logrus.Fatal(err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			catl[recipeMeta.Name] = catalogue.RecipeMeta{ | 			catl[recipeMeta.Name] = catalogue.RecipeMeta{ | ||||||
| 				Name:          recipeMeta.Name, | 				Name:          recipeMeta.Name, | ||||||
| 				Repository:    recipeMeta.CloneURL, | 				Repository:    recipeMeta.CloneURL, | ||||||
| @ -110,10 +131,11 @@ var catalogueGenerateCommand = &cli.Command{ | |||||||
| 				DefaultBranch: recipeMeta.DefaultBranch, | 				DefaultBranch: recipeMeta.DefaultBranch, | ||||||
| 				Description:   recipeMeta.Description, | 				Description:   recipeMeta.Description, | ||||||
| 				Website:       recipeMeta.Website, | 				Website:       recipeMeta.Website, | ||||||
| 				// Versions:      ..., // FIXME: once the new versions work goes down | 				Versions:      versions, | ||||||
| 				// Category:      ..., // FIXME: once we sort out the machine-readable catalogue interface | 				// Category:      ..., // FIXME: once we sort out the machine-readable catalogue interface | ||||||
| 				// Features:      ..., // FIXME: once we figure out the machine-readable catalogue interface | 				// Features:      ..., // FIXME: once we figure out the machine-readable catalogue interface | ||||||
| 			} | 			} | ||||||
|  | 			catlBar.Add(1) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		recipesJSON, err := json.MarshalIndent(catl, "", "    ") | 		recipesJSON, err := json.MarshalIndent(catl, "", "    ") | ||||||
| @ -125,7 +147,7 @@ var catalogueGenerateCommand = &cli.Command{ | |||||||
| 			logrus.Fatal(err) | 			logrus.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		logrus.Debugf("generated new recipe catalogue in '%s'", config.APPS_JSON) | 		logrus.Infof("generated new recipe catalogue in '%s'", config.APPS_JSON) | ||||||
|  |  | ||||||
| 		return nil | 		return nil | ||||||
| 	}, | 	}, | ||||||
|  | |||||||
| @ -9,12 +9,17 @@ import ( | |||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"path" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	"coopcloud.tech/abra/pkg/client" | ||||||
| 	"coopcloud.tech/abra/pkg/config" | 	"coopcloud.tech/abra/pkg/config" | ||||||
| 	"coopcloud.tech/abra/pkg/recipe" | 	"coopcloud.tech/abra/pkg/recipe" | ||||||
| 	"coopcloud.tech/abra/pkg/web" | 	"coopcloud.tech/abra/pkg/web" | ||||||
|  | 	"github.com/docker/distribution/reference" | ||||||
|  | 	"github.com/go-git/go-git/v5" | ||||||
|  | 	"github.com/go-git/go-git/v5/plumbing" | ||||||
| 	"github.com/sirupsen/logrus" | 	"github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @ -56,17 +61,20 @@ type serviceMeta struct { | |||||||
| 	Tag    string `json:"tag"` | 	Tag    string `json:"tag"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // RecipeVersions are the versions associated with a recipe. | ||||||
|  | type RecipeVersions []map[tag]map[service]serviceMeta | ||||||
|  |  | ||||||
| // RecipeMeta represents metadata for a recipe in the abra catalogue. | // RecipeMeta represents metadata for a recipe in the abra catalogue. | ||||||
| type RecipeMeta struct { | type RecipeMeta struct { | ||||||
| 	Category      string                            `json:"category"` | 	Category      string         `json:"category"` | ||||||
| 	DefaultBranch string                            `json:"default_branch"` | 	DefaultBranch string         `json:"default_branch"` | ||||||
| 	Description   string                            `json:"description"` | 	Description   string         `json:"description"` | ||||||
| 	Features      features                          `json:"features"` | 	Features      features       `json:"features"` | ||||||
| 	Icon          string                            `json:"icon"` | 	Icon          string         `json:"icon"` | ||||||
| 	Name          string                            `json:"name"` | 	Name          string         `json:"name"` | ||||||
| 	Repository    string                            `json:"repository"` | 	Repository    string         `json:"repository"` | ||||||
| 	Versions      []map[tag]map[service]serviceMeta `json:"versions"` | 	Versions      RecipeVersions `json:"versions"` | ||||||
| 	Website       string                            `json:"website"` | 	Website       string         `json:"website"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // LatestVersion returns the latest version of a recipe. | // LatestVersion returns the latest version of a recipe. | ||||||
| @ -365,3 +373,110 @@ func ReadReposMetadata() (RepoCatalogue, error) { | |||||||
|  |  | ||||||
| 	return reposMeta, nil | 	return reposMeta, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // GetRecipeVersions retrieves all recipe versions. | ||||||
|  | func GetRecipeVersions(recipeName string) (RecipeVersions, error) { | ||||||
|  | 	versions := RecipeVersions{} | ||||||
|  |  | ||||||
|  | 	recipeDir := path.Join(config.ABRA_DIR, "apps", recipeName) | ||||||
|  |  | ||||||
|  | 	logrus.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 { | ||||||
|  | 		logrus.Fatal(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/") | ||||||
|  |  | ||||||
|  | 		logrus.Debugf("processing '%s' for '%s'", tag, recipeName) | ||||||
|  |  | ||||||
|  | 		checkOutOpts := &git.CheckoutOptions{ | ||||||
|  | 			Create: false, | ||||||
|  | 			Force:  true, | ||||||
|  | 			Keep:   false, | ||||||
|  | 			Branch: plumbing.ReferenceName(ref.Name()), | ||||||
|  | 		} | ||||||
|  | 		if err := worktree.Checkout(checkOutOpts); err != nil { | ||||||
|  | 			logrus.Debugf("failed to check out '%s' in '%s'", tag, recipeDir) | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		logrus.Debugf("successfully checked out '%s' in '%s'", ref.Name(), recipeDir) | ||||||
|  |  | ||||||
|  | 		recipe, err := recipe.Get(recipeName) | ||||||
|  | 		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) | ||||||
|  | 			if strings.Contains(path, "library") { | ||||||
|  | 				path = strings.Split(path, "/")[1] | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			digest, err := client.GetTagDigest(img) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			versionMeta[service.Name] = serviceMeta{ | ||||||
|  | 				Digest: digest, | ||||||
|  | 				Image:  path, | ||||||
|  | 				Tag:    img.(reference.NamedTagged).Tag(), | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			logrus.Debugf("collecting digest: '%s', image: '%s', tag: '%s'", digest, path, tag) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		versions = append(versions, map[string]map[string]serviceMeta{tag: versionMeta}) | ||||||
|  |  | ||||||
|  | 		return nil | ||||||
|  | 	}); err != nil { | ||||||
|  | 		return versions, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	branch := "master" | ||||||
|  | 	if _, err := repo.Branch("master"); err != nil { | ||||||
|  | 		if _, err := repo.Branch("main"); err != nil { | ||||||
|  | 			logrus.Debugf("failed to select branch in '%s'", recipeDir) | ||||||
|  | 			logrus.Fatal(err) | ||||||
|  | 		} | ||||||
|  | 		branch = "main" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	refName := fmt.Sprintf("refs/heads/%s", branch) | ||||||
|  | 	checkOutOpts := &git.CheckoutOptions{ | ||||||
|  | 		Create: false, | ||||||
|  | 		Force:  true, | ||||||
|  | 		Keep:   false, | ||||||
|  | 		Branch: plumbing.ReferenceName(refName), | ||||||
|  | 	} | ||||||
|  | 	if err := worktree.Checkout(checkOutOpts); err != nil { | ||||||
|  | 		logrus.Debugf("failed to check out '%s' in '%s'", branch, recipeDir) | ||||||
|  | 		logrus.Fatal(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	logrus.Debugf("switched back to '%s' in '%s'", branch, recipeDir) | ||||||
|  | 	logrus.Debugf("collected '%s' for '%s'", versions, recipeName) | ||||||
|  |  | ||||||
|  | 	return versions, nil | ||||||
|  | } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user