package cli import ( "fmt" "os" "path" "sort" "text/template" "coopcloud.tech/abra/catalogue" "coopcloud.tech/abra/config" "github.com/go-git/go-git/v5" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) var recipeListCommand = &cli.Command{ Name: "list", Usage: "List all available recipes", Aliases: []string{"ls"}, Action: func(c *cli.Context) error { catl, err := catalogue.ReadAppsCatalogue() if err != nil { logrus.Fatal(err.Error()) } apps := catl.Flatten() sort.Sort(catalogue.ByAppName(apps)) tableCol := []string{"Name", "Category", "Status"} table := createTable(tableCol) for _, app := range apps { status := fmt.Sprintf("%v", app.Features.Status) tableRow := []string{app.Name, app.Category, status} table.Append(tableRow) } table.Render() return nil }, } var recipeVersionCommand = &cli.Command{ Name: "versions", Usage: "List available versions for ", ArgsUsage: "", Action: func(c *cli.Context) error { recipe := c.Args().First() if recipe == "" { cli.ShowSubcommandHelp(c) return nil } catalogue, err := catalogue.ReadAppsCatalogue() if err != nil { logrus.Fatal(err) return nil } if app, ok := catalogue[recipe]; ok { tableCol := []string{"Version", "Service", "Image", "Digest"} table := createTable(tableCol) for version := range app.Versions { for service := range app.Versions[version] { meta := app.Versions[version][service] table.Append([]string{version, service, meta.Image, meta.Digest}) } } table.SetAutoMergeCells(true) table.Render() return nil } logrus.Fatalf("'%s' recipe doesn't exist?", recipe) return nil }, } var recipeCreateCommand = &cli.Command{ Name: "create", Usage: "Create a new recipe", ArgsUsage: "", Action: func(c *cli.Context) error { recipe := c.Args().First() if recipe == "" { cli.ShowSubcommandHelp(c) return nil } directory := path.Join(config.APPS_DIR, recipe) if _, err := os.Stat(directory); !os.IsNotExist(err) { logrus.Fatalf("'%s' recipe directory already exists?", directory) return nil } url := fmt.Sprintf("%s/example.git", config.REPOS_BASE_URL) _, err := git.PlainClone(directory, false, &git.CloneOptions{URL: url}) if err != nil { logrus.Fatal(err) return nil } gitRepo := path.Join(config.APPS_DIR, recipe, ".git") if err := os.RemoveAll(gitRepo); err != nil { logrus.Fatal(err) return nil } toParse := []string{ path.Join(config.APPS_DIR, recipe, "README.md"), path.Join(config.APPS_DIR, recipe, ".env.sample"), path.Join(config.APPS_DIR, recipe, ".drone.yml"), } for _, path := range toParse { file, err := os.OpenFile(path, os.O_RDWR, 0755) if err != nil { logrus.Fatal(err) return nil } tpl, err := template.ParseFiles(path) if err != nil { logrus.Fatal(err) return nil } // TODO: ask for description and probably other things so that the // template repository is more "ready" to go than the current best-guess // mode of templating if err := tpl.Execute(file, struct { Name string Description string }{recipe, "TODO"}); err != nil { logrus.Fatal(err) return nil } } fmt.Printf( "New recipe '%s' created in %s, happy hacking!", recipe, path.Join(config.APPS_DIR, recipe), ) return nil }, } var RecipeCommand = &cli.Command{ Name: "recipe", Usage: "Manage app recipes", Description: ` A recipe is a blueprint for a Co-op Cloud app. It is made up of two things: - A libre software app (e.g. Nextcloud, Wordpress, Mastodon) - A package configuration which describes how to deploy and maintain it Recipes are developed, maintained and extended by the Co-op Cloud volunteer-run community. Each recipe has a "level" which is intended as a way to quickly show how reliable this app is to deploy and maintain in its current state. `, Subcommands: []*cli.Command{ recipeListCommand, recipeVersionCommand, recipeCreateCommand, }, }