All checks were successful
continuous-integration/drone/push Build is passing
See #627
323 lines
8.3 KiB
Go
323 lines
8.3 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"slices"
|
|
"strings"
|
|
|
|
"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/i18n"
|
|
"coopcloud.tech/abra/pkg/log"
|
|
"coopcloud.tech/abra/pkg/recipe"
|
|
"github.com/go-git/go-git/v5"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// translators: `abra catalogue sync` aliases. use a comma separated list of aliases with
|
|
// no spaces in between
|
|
var appCatalogueSyncAliases = i18n.G("s")
|
|
|
|
var CatalogueSyncCommand = &cobra.Command{
|
|
// translators: `catalogue sync` command
|
|
Use: i18n.G("sync [flags]"),
|
|
Aliases: strings.Split(appCatalogueSyncAliases, ","),
|
|
// translators: Short description for `catalogue sync` command
|
|
Short: i18n.G("Sync recipe catalogue for latest changes"),
|
|
Args: cobra.NoArgs,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
if err := catalogue.EnsureCatalogue(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if err := catalogue.EnsureUpToDate(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
log.Info(i18n.G("catalogue successfully synced"))
|
|
},
|
|
}
|
|
|
|
// translators: `abra catalogue` aliases. use a comma separated list of aliases with
|
|
// no spaces in between
|
|
var appCatalogueAliases = i18n.G("g")
|
|
|
|
var CatalogueGenerateCommand = &cobra.Command{
|
|
// translators: `catalogue generate` command
|
|
Use: i18n.G("generate [recipe] [flags]"),
|
|
Aliases: strings.Split(appCatalogueAliases, ","),
|
|
// translators: Short description for `catalogue generate` command
|
|
Short: i18n.G("Generate the recipe catalogue"),
|
|
Long: i18n.G(`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.
|
|
|
|
Publish 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. Enable ssh-agent and make sure to add
|
|
your private key and enter your passphrase beforehand.
|
|
|
|
eval ` + "`ssh-agent`" + `
|
|
ssh-add ~/.ssh/<my-ssh-private-key-for-git-coopcloud-tech>`),
|
|
Example: ` # publish catalogue
|
|
eval ` + "`ssh-agent`" + `
|
|
ssh-add ~/.ssh/id_ed25519
|
|
abra catalogue generate -p`,
|
|
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 os.Getenv("SSH_AUTH_SOCK") == "" {
|
|
log.Warn(i18n.G("ssh: SSH_AUTH_SOCK missing, --publish/-p will fail. see \"abra catalogue generate --help\""))
|
|
}
|
|
|
|
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, i18n.G("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.Info(i18n.G("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.Fatal(i18n.G("no changes discovered in %s, nothing to publish?", cataloguePath))
|
|
}
|
|
}
|
|
|
|
msg := i18n.G("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.Info(i18n.G("new changes published: %s", url))
|
|
}
|
|
|
|
if internal.Dry {
|
|
log.Info(i18n.G("dry run: no changes published"))
|
|
}
|
|
},
|
|
}
|
|
|
|
// CatalogueCommand defines the `abra catalogue` command and sub-commands.
|
|
var CatalogueCommand = &cobra.Command{
|
|
// translators: `catalogue` command group
|
|
Use: i18n.G("catalogue [cmd] [args] [flags]"),
|
|
// translators: Short description for `catalogue` command group
|
|
Short: i18n.G("Manage the recipe catalogue"),
|
|
Aliases: []string{"c"},
|
|
}
|
|
|
|
var (
|
|
publishChanges bool
|
|
skipUpdates bool
|
|
)
|
|
|
|
func init() {
|
|
CatalogueGenerateCommand.Flags().BoolVarP(
|
|
&publishChanges,
|
|
i18n.G("publish"),
|
|
i18n.G("p"),
|
|
false,
|
|
i18n.G("publish changes to git.coopcloud.tech"),
|
|
)
|
|
|
|
CatalogueGenerateCommand.Flags().BoolVarP(
|
|
&internal.Dry,
|
|
i18n.G("dry-run"),
|
|
i18n.G("r"),
|
|
false,
|
|
i18n.G("report changes that would be made"),
|
|
)
|
|
|
|
CatalogueGenerateCommand.Flags().BoolVarP(
|
|
&skipUpdates,
|
|
i18n.G("skip-updates"),
|
|
i18n.G("s"),
|
|
false,
|
|
i18n.G("skip updating recipe repositories"),
|
|
)
|
|
|
|
CatalogueGenerateCommand.Flags().BoolVarP(
|
|
&internal.Chaos,
|
|
i18n.G("chaos"),
|
|
i18n.G("C"),
|
|
false,
|
|
i18n.G("ignore uncommitted recipes changes"),
|
|
)
|
|
}
|