forked from toolshed/abra
		
	Compare commits
	
		
			21 Commits
		
	
	
		
			0.1.3-alph
			...
			0.1.4-alph
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e840328e44 | |||
| 6f43778691 | |||
| 9783563fa6 | |||
| 1392afc015 | |||
| 886009975d | |||
| b1147cd136 | |||
| 95a9013658 | |||
| bd1bf3b0d6 | |||
| 7b349732ac | |||
| a8ce64a9db | |||
| 96aa74a977 | |||
| 700f022790 | |||
| d188327b17 | |||
| fdd46a4d98 | |||
| e00920643e | |||
| 754fe81e01 | |||
| bece2e8351 | |||
| 
						
						
							
						
						e47d7029d7
	
				 | 
					
					
						|||
| 
						
						
							
						
						31edbbd32e
	
				 | 
					
					
						|||
| 
						
						
							
						
						0a1c73bf00
	
				 | 
					
					
						|||
| a74a8bc21b | 
@ -89,6 +89,7 @@ For developers, while using this `-alpha` format, the `y` part is the "major" ve
 | 
			
		||||
- Make a new tag (e.g. `git tag 0.y.z-alpha`)
 | 
			
		||||
- Push the new tag (e.g. `git push && git push --tags`)
 | 
			
		||||
- Wait until the build finishes on [build.coopcloud.tech](https://build.coopcloud.tech/coop-cloud/abra)
 | 
			
		||||
- Deploy the new installer script (e.g. `cd ./scripts/installer && make`)
 | 
			
		||||
- Check the release worked, (e.g. `abra upgrade; abra version`)
 | 
			
		||||
 | 
			
		||||
## Fork maintenance
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
package app
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
@ -75,7 +74,6 @@ var appCpCommand = &cli.Command{
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ctx := context.Background()
 | 
			
		||||
		cl, err := client.New(app.Server)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
@ -83,7 +81,7 @@ var appCpCommand = &cli.Command{
 | 
			
		||||
 | 
			
		||||
		filters := filters.NewArgs()
 | 
			
		||||
		filters.Add("name", fmt.Sprintf("%s_%s", appEnv.StackName(), service))
 | 
			
		||||
		containers, err := cl.ContainerList(ctx, types.ContainerListOptions{Filters: filters})
 | 
			
		||||
		containers, err := cl.ContainerList(c.Context, types.ContainerListOptions{Filters: filters})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
@ -107,11 +105,11 @@ var appCpCommand = &cli.Command{
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			copyOpts := types.CopyToContainerOptions{AllowOverwriteDirWithFile: false, CopyUIDGID: false}
 | 
			
		||||
			if err := cl.CopyToContainer(ctx, container.ID, dstPath, content, copyOpts); err != nil {
 | 
			
		||||
			if err := cl.CopyToContainer(c.Context, container.ID, dstPath, content, copyOpts); err != nil {
 | 
			
		||||
				logrus.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			content, _, err := cl.CopyFromContainer(ctx, container.ID, srcPath)
 | 
			
		||||
			content, _, err := cl.CopyFromContainer(c.Context, container.ID, srcPath)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				logrus.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
package app
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
@ -18,12 +17,11 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// stackLogs lists logs for all stack services
 | 
			
		||||
func stackLogs(stackName string, client *dockerClient.Client) {
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
func stackLogs(c *cli.Context, stackName string, client *dockerClient.Client) {
 | 
			
		||||
	filters := filters.NewArgs()
 | 
			
		||||
	filters.Add("name", stackName)
 | 
			
		||||
	serviceOpts := types.ServiceListOptions{Filters: filters}
 | 
			
		||||
	services, err := client.ServiceList(ctx, serviceOpts)
 | 
			
		||||
	services, err := client.ServiceList(c.Context, serviceOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logrus.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
@ -40,7 +38,7 @@ func stackLogs(stackName string, client *dockerClient.Client) {
 | 
			
		||||
				Tail:       "20",
 | 
			
		||||
				Timestamps: true,
 | 
			
		||||
			}
 | 
			
		||||
			logs, err := client.ServiceLogs(ctx, s, logOpts)
 | 
			
		||||
			logs, err := client.ServiceLogs(c.Context, s, logOpts)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				logrus.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
@ -65,7 +63,6 @@ var appLogsCommand = &cli.Command{
 | 
			
		||||
	Action: func(c *cli.Context) error {
 | 
			
		||||
		app := internal.ValidateApp(c)
 | 
			
		||||
 | 
			
		||||
		ctx := context.Background()
 | 
			
		||||
		cl, err := client.New(app.Server)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
@ -74,7 +71,7 @@ var appLogsCommand = &cli.Command{
 | 
			
		||||
		serviceName := c.Args().Get(1)
 | 
			
		||||
		if serviceName == "" {
 | 
			
		||||
			logrus.Debug("tailing logs for all app services")
 | 
			
		||||
			stackLogs(app.StackName(), cl)
 | 
			
		||||
			stackLogs(c, app.StackName(), cl)
 | 
			
		||||
		}
 | 
			
		||||
		logrus.Debugf("tailing logs for '%s'", serviceName)
 | 
			
		||||
 | 
			
		||||
@ -82,7 +79,7 @@ var appLogsCommand = &cli.Command{
 | 
			
		||||
		filters := filters.NewArgs()
 | 
			
		||||
		filters.Add("name", service)
 | 
			
		||||
		serviceOpts := types.ServiceListOptions{Filters: filters}
 | 
			
		||||
		services, err := cl.ServiceList(ctx, serviceOpts)
 | 
			
		||||
		services, err := cl.ServiceList(c.Context, serviceOpts)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
@ -98,7 +95,7 @@ var appLogsCommand = &cli.Command{
 | 
			
		||||
			Tail:       "20",
 | 
			
		||||
			Timestamps: true,
 | 
			
		||||
		}
 | 
			
		||||
		logs, err := cl.ServiceLogs(ctx, services[0].ID, logOpts)
 | 
			
		||||
		logs, err := cl.ServiceLogs(c.Context, services[0].ID, logOpts)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,6 @@ import (
 | 
			
		||||
	"coopcloud.tech/abra/cli/internal"
 | 
			
		||||
	"coopcloud.tech/abra/pkg/catalogue"
 | 
			
		||||
	"coopcloud.tech/abra/pkg/config"
 | 
			
		||||
	recipePkg "coopcloud.tech/abra/pkg/recipe"
 | 
			
		||||
	"coopcloud.tech/abra/pkg/secret"
 | 
			
		||||
	"github.com/AlecAivazis/survey/v2"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
@ -86,7 +85,7 @@ var appNewCommand = &cli.Command{
 | 
			
		||||
		if c.NArg() > 0 {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		for name, _ := range catl {
 | 
			
		||||
		for name := range catl {
 | 
			
		||||
			fmt.Println(name)
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
@ -172,16 +171,6 @@ func action(c *cli.Context) error {
 | 
			
		||||
		logrus.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	recipeMeta, err := catalogue.GetRecipeMeta(recipe.Name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logrus.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	latestVersion := recipeMeta.LatestVersion()
 | 
			
		||||
	if err := recipePkg.EnsureVersion(recipe.Name, latestVersion); err != nil {
 | 
			
		||||
		logrus.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := ensureServerFlag(); err != nil {
 | 
			
		||||
		logrus.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
@ -200,7 +189,7 @@ func action(c *cli.Context) error {
 | 
			
		||||
	}
 | 
			
		||||
	logrus.Debugf("'%s' sanitised as '%s' for new app", newAppName, sanitisedAppName)
 | 
			
		||||
 | 
			
		||||
	if err := config.CopyAppEnvSample(recipe.Name, newAppName, newAppServer); err != nil {
 | 
			
		||||
	if err := config.TemplateAppEnvSample(recipe.Name, newAppName, newAppServer, domain, recipe.Name); err != nil {
 | 
			
		||||
		logrus.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -224,7 +213,18 @@ func action(c *cli.Context) error {
 | 
			
		||||
	tableCol := []string{"Name", "Domain", "Type", "Server"}
 | 
			
		||||
	table := abraFormatter.CreateTable(tableCol)
 | 
			
		||||
	table.Append([]string{sanitisedAppName, domain, recipe.Name, newAppServer})
 | 
			
		||||
	defer table.Render()
 | 
			
		||||
 | 
			
		||||
	fmt.Println("")
 | 
			
		||||
	fmt.Println(fmt.Sprintf("New '%s' created! Here is your new app overview:", recipe.Name))
 | 
			
		||||
	fmt.Println("")
 | 
			
		||||
	table.Render()
 | 
			
		||||
	fmt.Println("")
 | 
			
		||||
	fmt.Println("You can configure this app by running the following:")
 | 
			
		||||
	fmt.Println(fmt.Sprintf("\n    abra app config %s", sanitisedAppName))
 | 
			
		||||
	fmt.Println("")
 | 
			
		||||
	fmt.Println("You can deploy this app by running the following:")
 | 
			
		||||
	fmt.Println(fmt.Sprintf("\n    abra app deploy %s", sanitisedAppName))
 | 
			
		||||
	fmt.Println("")
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
package app
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
@ -23,7 +22,6 @@ var appPsCommand = &cli.Command{
 | 
			
		||||
	Action: func(c *cli.Context) error {
 | 
			
		||||
		app := internal.ValidateApp(c)
 | 
			
		||||
 | 
			
		||||
		ctx := context.Background()
 | 
			
		||||
		cl, err := client.New(app.Server)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
@ -32,7 +30,7 @@ var appPsCommand = &cli.Command{
 | 
			
		||||
		filters := filters.NewArgs()
 | 
			
		||||
		filters.Add("name", app.StackName())
 | 
			
		||||
 | 
			
		||||
		containers, err := cl.ContainerList(ctx, types.ContainerListOptions{Filters: filters})
 | 
			
		||||
		containers, err := cl.ContainerList(c.Context, types.ContainerListOptions{Filters: filters})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
package app
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
@ -54,7 +53,6 @@ var appRemoveCommand = &cli.Command{
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ctx := context.Background()
 | 
			
		||||
		cl, err := client.New(app.Server)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
@ -72,7 +70,7 @@ var appRemoveCommand = &cli.Command{
 | 
			
		||||
 | 
			
		||||
		fs := filters.NewArgs()
 | 
			
		||||
		fs.Add("name", app.Name)
 | 
			
		||||
		secretList, err := cl.SecretList(ctx, types.SecretListOptions{Filters: fs})
 | 
			
		||||
		secretList, err := cl.SecretList(c.Context, types.SecretListOptions{Filters: fs})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
@ -99,7 +97,7 @@ var appRemoveCommand = &cli.Command{
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, name := range secretNamesToRemove {
 | 
			
		||||
				err := cl.SecretRemove(ctx, secrets[name])
 | 
			
		||||
				err := cl.SecretRemove(c.Context, secrets[name])
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					logrus.Fatal(err)
 | 
			
		||||
				}
 | 
			
		||||
@ -109,7 +107,7 @@ var appRemoveCommand = &cli.Command{
 | 
			
		||||
			logrus.Info("no secrets to remove")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		volumeListOKBody, err := cl.VolumeList(ctx, fs)
 | 
			
		||||
		volumeListOKBody, err := cl.VolumeList(c.Context, fs)
 | 
			
		||||
		volumeList := volumeListOKBody.Volumes
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
@ -134,7 +132,7 @@ var appRemoveCommand = &cli.Command{
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				for _, vol := range removeVols {
 | 
			
		||||
					err := cl.VolumeRemove(ctx, vol, internal.Force) // last argument is for force removing
 | 
			
		||||
					err := cl.VolumeRemove(c.Context, vol, internal.Force) // last argument is for force removing
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						logrus.Fatal(err)
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
package app
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
@ -50,7 +49,6 @@ var appRunCommand = &cli.Command{
 | 
			
		||||
			internal.ShowSubcommandHelpAndError(c, errors.New("no <args> provided?"))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ctx := context.Background()
 | 
			
		||||
		cl, err := client.New(app.Server)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
@ -60,7 +58,7 @@ var appRunCommand = &cli.Command{
 | 
			
		||||
		filters := filters.NewArgs()
 | 
			
		||||
		filters.Add("name", fmt.Sprintf("%s_%s", app.StackName(), serviceName))
 | 
			
		||||
 | 
			
		||||
		containers, err := cl.ContainerList(ctx, types.ContainerListOptions{Filters: filters})
 | 
			
		||||
		containers, err := cl.ContainerList(c.Context, types.ContainerListOptions{Filters: filters})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
package app
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
@ -144,7 +143,6 @@ var appSecretRmCommand = &cli.Command{
 | 
			
		||||
			internal.ShowSubcommandHelpAndError(c, errors.New("no secret(s) specified?"))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ctx := context.Background()
 | 
			
		||||
		cl, err := client.New(app.Server)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
@ -152,7 +150,7 @@ var appSecretRmCommand = &cli.Command{
 | 
			
		||||
 | 
			
		||||
		filters := filters.NewArgs()
 | 
			
		||||
		filters.Add("name", app.StackName())
 | 
			
		||||
		secretList, err := cl.SecretList(ctx, types.SecretListOptions{Filters: filters})
 | 
			
		||||
		secretList, err := cl.SecretList(c.Context, types.SecretListOptions{Filters: filters})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
@ -162,7 +160,7 @@ var appSecretRmCommand = &cli.Command{
 | 
			
		||||
			secretName := cont.Spec.Annotations.Name
 | 
			
		||||
			parsed := secret.ParseGeneratedSecretName(secretName, app)
 | 
			
		||||
			if allSecrets {
 | 
			
		||||
				if err := cl.SecretRemove(ctx, secretName); err != nil {
 | 
			
		||||
				if err := cl.SecretRemove(c.Context, secretName); err != nil {
 | 
			
		||||
					logrus.Fatal(err)
 | 
			
		||||
				}
 | 
			
		||||
				if internal.Pass {
 | 
			
		||||
@ -172,7 +170,7 @@ var appSecretRmCommand = &cli.Command{
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if parsed == secretToRm {
 | 
			
		||||
					if err := cl.SecretRemove(ctx, secretName); err != nil {
 | 
			
		||||
					if err := cl.SecretRemove(c.Context, secretName); err != nil {
 | 
			
		||||
						logrus.Fatal(err)
 | 
			
		||||
					}
 | 
			
		||||
					if internal.Pass {
 | 
			
		||||
@ -199,7 +197,6 @@ var appSecretLsCommand = &cli.Command{
 | 
			
		||||
		tableCol := []string{"Name", "Version", "Generated Name", "Created On Server"}
 | 
			
		||||
		table := abraFormatter.CreateTable(tableCol)
 | 
			
		||||
 | 
			
		||||
		ctx := context.Background()
 | 
			
		||||
		cl, err := client.New(app.Server)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
@ -207,7 +204,7 @@ var appSecretLsCommand = &cli.Command{
 | 
			
		||||
 | 
			
		||||
		filters := filters.NewArgs()
 | 
			
		||||
		filters.Add("name", app.StackName())
 | 
			
		||||
		secretList, err := cl.SecretList(ctx, types.SecretListOptions{Filters: filters})
 | 
			
		||||
		secretList, err := cl.SecretList(c.Context, types.SecretListOptions{Filters: filters})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
package app
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"coopcloud.tech/abra/cli/internal"
 | 
			
		||||
@ -24,14 +23,13 @@ volumes as eligiblef or pruning once undeployed.
 | 
			
		||||
	Action: func(c *cli.Context) error {
 | 
			
		||||
		app := internal.ValidateApp(c)
 | 
			
		||||
 | 
			
		||||
		ctx := context.Background()
 | 
			
		||||
		cl, err := client.New(app.Server)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rmOpts := stack.Remove{Namespaces: []string{app.StackName()}}
 | 
			
		||||
		if err := stack.RunRemove(ctx, cl, rmOpts); err != nil {
 | 
			
		||||
		if err := stack.RunRemove(c.Context, cl, rmOpts); err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
package app
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	abraFormatter "coopcloud.tech/abra/cli/formatter"
 | 
			
		||||
@ -20,8 +19,7 @@ var appVolumeListCommand = &cli.Command{
 | 
			
		||||
	Action: func(c *cli.Context) error {
 | 
			
		||||
		app := internal.ValidateApp(c)
 | 
			
		||||
 | 
			
		||||
		ctx := context.Background()
 | 
			
		||||
		volumeList, err := client.GetVolumes(ctx, app.Server, app.Name)
 | 
			
		||||
		volumeList, err := client.GetVolumes(c.Context, app.Server, app.Name)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
@ -53,8 +51,7 @@ var appVolumeRemoveCommand = &cli.Command{
 | 
			
		||||
	Action: func(c *cli.Context) error {
 | 
			
		||||
		app := internal.ValidateApp(c)
 | 
			
		||||
 | 
			
		||||
		ctx := context.Background()
 | 
			
		||||
		volumeList, err := client.GetVolumes(ctx, app.Server, app.Name)
 | 
			
		||||
		volumeList, err := client.GetVolumes(c.Context, app.Server, app.Name)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
@ -74,7 +71,7 @@ var appVolumeRemoveCommand = &cli.Command{
 | 
			
		||||
			volumesToRemove = volumeNames
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		err = client.RemoveVolumes(ctx, app.Server, volumesToRemove, internal.Force)
 | 
			
		||||
		err = client.RemoveVolumes(c.Context, app.Server, volumesToRemove, internal.Force)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,11 @@
 | 
			
		||||
package catalogue
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"coopcloud.tech/abra/cli/formatter"
 | 
			
		||||
	"coopcloud.tech/abra/pkg/catalogue"
 | 
			
		||||
	"coopcloud.tech/abra/pkg/config"
 | 
			
		||||
	"coopcloud.tech/abra/pkg/git"
 | 
			
		||||
@ -11,30 +13,119 @@ import (
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CatalogueSkipList is all the repos that are not recipes.
 | 
			
		||||
var CatalogueSkipList = map[string]bool{
 | 
			
		||||
	"abra":                  true,
 | 
			
		||||
	"abra-bash":             true,
 | 
			
		||||
	"abra-apps":             true,
 | 
			
		||||
	"abra-aur":              true,
 | 
			
		||||
	"abra-capsul":           true,
 | 
			
		||||
	"abra-gandi":            true,
 | 
			
		||||
	"abra-hetzner":          true,
 | 
			
		||||
	"apps":                  true,
 | 
			
		||||
	"aur-abra-git":          true,
 | 
			
		||||
	"auto-apps-json":        true,
 | 
			
		||||
	"auto-mirror":           true,
 | 
			
		||||
	"backup-bot":            true,
 | 
			
		||||
	"coopcloud.tech":        true,
 | 
			
		||||
	"coturn":                true,
 | 
			
		||||
	"docker-cp-deploy":      true,
 | 
			
		||||
	"docker-dind-bats-kcov": true,
 | 
			
		||||
	"docs.coopcloud.tech":   true,
 | 
			
		||||
	"example":               true,
 | 
			
		||||
	"gardening":             true,
 | 
			
		||||
	"go-abra":               true,
 | 
			
		||||
	"organising":            true,
 | 
			
		||||
	"pyabra":                true,
 | 
			
		||||
	"radicle-seed-node":     true,
 | 
			
		||||
	"stack-ssh-deploy":      true,
 | 
			
		||||
	"swarm-cronjob":         true,
 | 
			
		||||
	"tagcmp":                true,
 | 
			
		||||
	"tyop":                  true,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var catalogueGenerateCommand = &cli.Command{
 | 
			
		||||
	Name:         "generate",
 | 
			
		||||
	Aliases:      []string{"g"},
 | 
			
		||||
	Usage:        "Generate a new copy of the catalogue",
 | 
			
		||||
	ArgsUsage:    "[<recipe>]",
 | 
			
		||||
	BashComplete: func(c *cli.Context) {},
 | 
			
		||||
	Action: func(c *cli.Context) error {
 | 
			
		||||
		catl, err := catalogue.ReadRecipeCatalogue()
 | 
			
		||||
		recipeName := c.Args().First()
 | 
			
		||||
 | 
			
		||||
		repos, err := catalogue.ReadReposMetadata()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for recipeName, recipeMeta := range catl {
 | 
			
		||||
			recipeDir := path.Join(config.ABRA_DIR, "apps", strings.ToLower(recipeName))
 | 
			
		||||
			if err := git.Clone(recipeDir, recipeMeta.Repository); err != nil {
 | 
			
		||||
				logrus.Fatal(err)
 | 
			
		||||
		logrus.Debugf("ensuring '%v' recipe(s) are locally present and up-to-date", len(repos))
 | 
			
		||||
 | 
			
		||||
		bar := formatter.CreateProgressbar(len(repos), "retrieving recipes...")
 | 
			
		||||
		ch := make(chan string, len(repos))
 | 
			
		||||
		for _, repoMeta := range repos {
 | 
			
		||||
			go func(rm catalogue.RepoMeta) {
 | 
			
		||||
				if recipeName != "" && recipeName != rm.Name {
 | 
			
		||||
					ch <- rm.Name
 | 
			
		||||
					bar.Add(1)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				if _, exists := CatalogueSkipList[rm.Name]; exists {
 | 
			
		||||
					ch <- rm.Name
 | 
			
		||||
					bar.Add(1)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				recipeDir := path.Join(config.ABRA_DIR, "apps", rm.Name)
 | 
			
		||||
 | 
			
		||||
				if err := git.Clone(recipeDir, rm.SSHURL); err != nil {
 | 
			
		||||
					logrus.Fatal(err)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if err := git.EnsureUpToDate(recipeDir); err != nil {
 | 
			
		||||
					logrus.Fatal(err)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				ch <- rm.Name
 | 
			
		||||
				bar.Add(1)
 | 
			
		||||
			}(repoMeta)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for range repos {
 | 
			
		||||
			<-ch // wait for everything
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		catl := make(catalogue.RecipeCatalogue)
 | 
			
		||||
		for _, recipeMeta := range repos {
 | 
			
		||||
			if recipeName != "" && recipeName != recipeMeta.Name {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if _, exists := CatalogueSkipList[recipeMeta.Name]; exists {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if err := git.EnsureUpToDate(recipeDir); err != nil {
 | 
			
		||||
				logrus.Fatal(err)
 | 
			
		||||
			catl[recipeMeta.Name] = catalogue.RecipeMeta{
 | 
			
		||||
				Name:          recipeMeta.Name,
 | 
			
		||||
				Repository:    recipeMeta.CloneURL,
 | 
			
		||||
				Icon:          recipeMeta.AvatarURL,
 | 
			
		||||
				DefaultBranch: recipeMeta.DefaultBranch,
 | 
			
		||||
				Description:   recipeMeta.Description,
 | 
			
		||||
				Website:       recipeMeta.Website,
 | 
			
		||||
				// Versions:      ..., // FIXME: once the new versions work goes down
 | 
			
		||||
				// Category:      ..., // FIXME: once we sort out the machine-readable catalogue interface
 | 
			
		||||
				// Features:      ..., // FIXME: once we figure out the machine-readable catalogue interface
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// for reach app, build the recipemeta from parsing
 | 
			
		||||
		// spit out a JSON file
 | 
			
		||||
		recipesJSON, err := json.MarshalIndent(catl, "", "    ")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := ioutil.WriteFile(config.APPS_JSON, recipesJSON, 0644); err != nil {
 | 
			
		||||
			logrus.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		logrus.Debugf("generated new recipe catalogue in '%s'", config.APPS_JSON)
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
@ -64,7 +64,7 @@ func RunApp(version, commit string) {
 | 
			
		||||
			DebugFlag,
 | 
			
		||||
		},
 | 
			
		||||
		Authors: []*cli.Author{
 | 
			
		||||
			&cli.Author{
 | 
			
		||||
			{
 | 
			
		||||
				Name:  "Autonomic Co-op",
 | 
			
		||||
				Email: "helo@autonomic.zone",
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@ import (
 | 
			
		||||
	"github.com/docker/cli/cli/command/formatter"
 | 
			
		||||
	"github.com/docker/go-units"
 | 
			
		||||
	"github.com/olekukonko/tablewriter"
 | 
			
		||||
	"github.com/schollz/progressbar/v3"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func ShortenID(str string) string {
 | 
			
		||||
@ -37,3 +38,15 @@ func CreateTable(columns []string) *tablewriter.Table {
 | 
			
		||||
	table.SetHeader(columns)
 | 
			
		||||
	return table
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateProgressbar generates a progress bar
 | 
			
		||||
func CreateProgressbar(length int, title string) *progressbar.ProgressBar {
 | 
			
		||||
	return progressbar.NewOptions(
 | 
			
		||||
		length,
 | 
			
		||||
		progressbar.OptionClearOnFinish(),
 | 
			
		||||
		progressbar.OptionSetPredictTime(false),
 | 
			
		||||
		progressbar.OptionShowCount(),
 | 
			
		||||
		progressbar.OptionFullWidth(),
 | 
			
		||||
		progressbar.OptionSetDescription(title),
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -99,7 +99,7 @@ var recipeLintCommand = &cli.Command{
 | 
			
		||||
		if c.NArg() > 0 {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		for name, _ := range catl {
 | 
			
		||||
		for name := range catl {
 | 
			
		||||
			fmt.Println(name)
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@ package server
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"os/user"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"coopcloud.tech/abra/cli/internal"
 | 
			
		||||
	"coopcloud.tech/abra/pkg/client"
 | 
			
		||||
@ -23,7 +24,7 @@ can use the <user> and <port> arguments to adjust this.
 | 
			
		||||
 | 
			
		||||
For example:
 | 
			
		||||
 | 
			
		||||
    abra server add varia.zone 12345 glodemodem
 | 
			
		||||
    abra server add varia.zone glodemodem 12345
 | 
			
		||||
 | 
			
		||||
Abra will construct the following SSH connection string then:
 | 
			
		||||
 | 
			
		||||
@ -79,7 +80,11 @@ All communication between Abra and the server will use this SSH connection.
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, err := cl.Info(ctx); err != nil {
 | 
			
		||||
			logrus.Fatalf("unable to make a connection to '%s'?", domainName)
 | 
			
		||||
			if strings.Contains(err.Error(), "command not found") {
 | 
			
		||||
				logrus.Fatalf("docker is not installed on '%s'?", domainName)
 | 
			
		||||
			} else {
 | 
			
		||||
				logrus.Fatalf("unable to make a connection to '%s'?", domainName)
 | 
			
		||||
			}
 | 
			
		||||
			logrus.Debug(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										13
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								go.mod
									
									
									
									
									
								
							@ -16,6 +16,7 @@ require (
 | 
			
		||||
	github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
 | 
			
		||||
	github.com/olekukonko/tablewriter v0.0.5
 | 
			
		||||
	github.com/pkg/errors v0.9.1
 | 
			
		||||
	github.com/schollz/progressbar/v3 v3.8.3
 | 
			
		||||
	github.com/schultz-is/passgen v1.0.1
 | 
			
		||||
	github.com/sirupsen/logrus v1.8.1
 | 
			
		||||
	github.com/urfave/cli/v2 v2.3.0
 | 
			
		||||
@ -53,11 +54,12 @@ require (
 | 
			
		||||
	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
 | 
			
		||||
	github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
 | 
			
		||||
	github.com/mattn/go-colorable v0.1.2 // indirect
 | 
			
		||||
	github.com/mattn/go-isatty v0.0.8 // indirect
 | 
			
		||||
	github.com/mattn/go-runewidth v0.0.9 // indirect
 | 
			
		||||
	github.com/mattn/go-isatty v0.0.14 // indirect
 | 
			
		||||
	github.com/mattn/go-runewidth v0.0.13 // indirect
 | 
			
		||||
	github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
 | 
			
		||||
	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
 | 
			
		||||
	github.com/miekg/pkcs11 v1.0.3 // indirect
 | 
			
		||||
	github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
 | 
			
		||||
	github.com/mitchellh/go-homedir v1.1.0 // indirect
 | 
			
		||||
	github.com/mitchellh/mapstructure v1.1.2 // indirect
 | 
			
		||||
	github.com/moby/sys/mount v0.2.0 // indirect
 | 
			
		||||
@ -70,6 +72,7 @@ require (
 | 
			
		||||
	github.com/prometheus/client_model v0.2.0 // indirect
 | 
			
		||||
	github.com/prometheus/common v0.26.0 // indirect
 | 
			
		||||
	github.com/prometheus/procfs v0.6.0 // indirect
 | 
			
		||||
	github.com/rivo/uniseg v0.2.0 // indirect
 | 
			
		||||
	github.com/russross/blackfriday/v2 v2.0.1 // indirect
 | 
			
		||||
	github.com/sergi/go-diff v1.1.0 // indirect
 | 
			
		||||
	github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
 | 
			
		||||
@ -81,10 +84,10 @@ require (
 | 
			
		||||
	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
 | 
			
		||||
	github.com/xeipuuv/gojsonschema v1.2.0 // indirect
 | 
			
		||||
	go.opencensus.io v0.22.3 // indirect
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
 | 
			
		||||
	golang.org/x/net v0.0.0-20210326060303-6b1517762897 // indirect
 | 
			
		||||
	golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
 | 
			
		||||
	golang.org/x/term v0.0.0-20210503060354-a79de5458b56 // indirect
 | 
			
		||||
	golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 // indirect
 | 
			
		||||
	golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
 | 
			
		||||
	golang.org/x/text v0.3.4 // indirect
 | 
			
		||||
	google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a // indirect
 | 
			
		||||
	google.golang.org/grpc v1.33.2 // indirect
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										23
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								go.sum
									
									
									
									
									
								
							@ -479,6 +479,7 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
 | 
			
		||||
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
 | 
			
		||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 | 
			
		||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
 | 
			
		||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
 | 
			
		||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
 | 
			
		||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
 | 
			
		||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
 | 
			
		||||
@ -518,11 +519,13 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
 | 
			
		||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 | 
			
		||||
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 | 
			
		||||
@ -534,6 +537,8 @@ github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WT
 | 
			
		||||
github.com/miekg/pkcs11 v1.0.3 h1:iMwmD7I5225wv84WxIG/bmxz9AXjWvTWIbM/TYHvWtw=
 | 
			
		||||
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
 | 
			
		||||
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
 | 
			
		||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
 | 
			
		||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
			
		||||
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 | 
			
		||||
@ -662,12 +667,16 @@ github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
 | 
			
		||||
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
 | 
			
		||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
 | 
			
		||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 | 
			
		||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
 | 
			
		||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 | 
			
		||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
			
		||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
 | 
			
		||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 | 
			
		||||
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
 | 
			
		||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 | 
			
		||||
github.com/schollz/progressbar/v3 v3.8.3 h1:FnLGl3ewlDUP+YdSwveXBaXs053Mem/du+wr7XSYKl8=
 | 
			
		||||
github.com/schollz/progressbar/v3 v3.8.3/go.mod h1:pWnVCjSBZsT2X3nx9HfRdnCDrpbevliMeoEVhStwHko=
 | 
			
		||||
github.com/schultz-is/passgen v1.0.1 h1:wUINzqW1Xmmy3yREHR6YTj+83VlFYjj2DIDMHzIi5TQ=
 | 
			
		||||
github.com/schultz-is/passgen v1.0.1/go.mod h1:NnqzT2aSfvyheNQvBtlLUa0YlPFLDj60Jw2DZVwqiJk=
 | 
			
		||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
 | 
			
		||||
@ -798,8 +807,9 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh
 | 
			
		||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
			
		||||
@ -947,13 +957,16 @@ golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7w
 | 
			
		||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 h1:xrCZDmdtoloIiooiA9q0OQb9r8HejIHYoHGhGCe1pGg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 | 
			
		||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
			
		||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
 | 
			
		||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
 | 
			
		||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
 | 
			
		||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
			
		||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
 | 
			
		||||
@ -21,6 +21,9 @@ import (
 | 
			
		||||
// RecipeCatalogueURL is the only current recipe catalogue available.
 | 
			
		||||
const RecipeCatalogueURL = "https://apps.coopcloud.tech"
 | 
			
		||||
 | 
			
		||||
// ReposMetadataURL is the recipe repository metadata
 | 
			
		||||
const ReposMetadataURL = "https://git.coopcloud.tech/api/v1/orgs/coop-cloud/repos"
 | 
			
		||||
 | 
			
		||||
// image represents a recipe container image.
 | 
			
		||||
type image struct {
 | 
			
		||||
	Image  string `json:"image"`
 | 
			
		||||
@ -255,3 +258,110 @@ func GetRecipeMeta(recipeName string) (RecipeMeta, error) {
 | 
			
		||||
 | 
			
		||||
	return recipeMeta, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RepoMeta is a single recipe repo metadata.
 | 
			
		||||
type RepoMeta struct {
 | 
			
		||||
	ID                        int `json:"id"`
 | 
			
		||||
	Owner                     Owner
 | 
			
		||||
	Name                      string      `json:"name"`
 | 
			
		||||
	FullName                  string      `json:"full_name"`
 | 
			
		||||
	Description               string      `json:"description"`
 | 
			
		||||
	Empty                     bool        `json:"empty"`
 | 
			
		||||
	Private                   bool        `json:"private"`
 | 
			
		||||
	Fork                      bool        `json:"fork"`
 | 
			
		||||
	Template                  bool        `json:"template"`
 | 
			
		||||
	Parent                    interface{} `json:"parent"`
 | 
			
		||||
	Mirror                    bool        `json:"mirror"`
 | 
			
		||||
	Size                      int         `json:"size"`
 | 
			
		||||
	HTMLURL                   string      `json:"html_url"`
 | 
			
		||||
	SSHURL                    string      `json:"ssh_url"`
 | 
			
		||||
	CloneURL                  string      `json:"clone_url"`
 | 
			
		||||
	OriginalURL               string      `json:"original_url"`
 | 
			
		||||
	Website                   string      `json:"website"`
 | 
			
		||||
	StarsCount                int         `json:"stars_count"`
 | 
			
		||||
	ForksCount                int         `json:"forks_count"`
 | 
			
		||||
	WatchersCount             int         `json:"watchers_count"`
 | 
			
		||||
	OpenIssuesCount           int         `json:"open_issues_count"`
 | 
			
		||||
	OpenPRCount               int         `json:"open_pr_counter"`
 | 
			
		||||
	ReleaseCounter            int         `json:"release_counter"`
 | 
			
		||||
	DefaultBranch             string      `json:"default_branch"`
 | 
			
		||||
	Archived                  bool        `json:"archived"`
 | 
			
		||||
	CreatedAt                 string      `json:"created_at"`
 | 
			
		||||
	UpdatedAt                 string      `json:"updated_at"`
 | 
			
		||||
	Permissions               Permissions
 | 
			
		||||
	HasIssues                 bool `json:"has_issues"`
 | 
			
		||||
	InternalTracker           InternalTracker
 | 
			
		||||
	HasWiki                   bool   `json:"has_wiki"`
 | 
			
		||||
	HasPullRequests           bool   `json:"has_pull_requests"`
 | 
			
		||||
	HasProjects               bool   `json:"has_projects"`
 | 
			
		||||
	IgnoreWhitespaceConflicts bool   `json:"ignore_whitespace_conflicts"`
 | 
			
		||||
	AllowMergeCommits         bool   `json:"allow_merge_commits"`
 | 
			
		||||
	AllowRebase               bool   `json:"allow_rebase"`
 | 
			
		||||
	AllowRebaseExplicit       bool   `json:"allow_rebase_explicit"`
 | 
			
		||||
	AllowSquashMerge          bool   `json:"allow_squash_merge"`
 | 
			
		||||
	AvatarURL                 string `json:"avatar_url"`
 | 
			
		||||
	Internal                  bool   `json:"internal"`
 | 
			
		||||
	MirrorInterval            string `json:"mirror_interval"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Owner is the repo organisation owner metadata.
 | 
			
		||||
type Owner struct {
 | 
			
		||||
	ID         int    `json:"id"`
 | 
			
		||||
	Login      string `json:"login"`
 | 
			
		||||
	FullName   string `json:"full_name"`
 | 
			
		||||
	Email      string `json:"email"`
 | 
			
		||||
	AvatarURL  string `json:"avatar_url"`
 | 
			
		||||
	Language   string `json:"language"`
 | 
			
		||||
	IsAdmin    bool   `json:"is_admin"`
 | 
			
		||||
	LastLogin  string `json:"last_login"`
 | 
			
		||||
	Created    string `json:"created"`
 | 
			
		||||
	Restricted bool   `json:"restricted"`
 | 
			
		||||
	Username   string `json:"username"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Permissions is perms metadata for a repo.
 | 
			
		||||
type Permissions struct {
 | 
			
		||||
	Admin bool `json:"admin"`
 | 
			
		||||
	Push  bool `json:"push"`
 | 
			
		||||
	Pull  bool `json:"pull"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InternalTracker is issue tracker metadata for a repo.
 | 
			
		||||
type InternalTracker struct {
 | 
			
		||||
	EnableTimeTracker                bool `json:"enable_time_tracker"`
 | 
			
		||||
	AllowOnlyContributorsToTrackTime bool `json:"allow_only_contributors_to_track_time"`
 | 
			
		||||
	EnableIssuesDependencies         bool `json:"enable_issue_dependencies"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RepoCatalogue represents all the recipe repo metadata.
 | 
			
		||||
type RepoCatalogue map[string]RepoMeta
 | 
			
		||||
 | 
			
		||||
// ReadReposMetadata retrieves coop-cloud/... repo metadata from Gitea.
 | 
			
		||||
func ReadReposMetadata() (RepoCatalogue, error) {
 | 
			
		||||
	reposMeta := make(RepoCatalogue)
 | 
			
		||||
 | 
			
		||||
	pageIdx := 1
 | 
			
		||||
	for {
 | 
			
		||||
		var reposList []RepoMeta
 | 
			
		||||
 | 
			
		||||
		pagedURL := fmt.Sprintf("%s?page=%v", ReposMetadataURL, pageIdx)
 | 
			
		||||
 | 
			
		||||
		logrus.Debugf("fetching repo metadata from '%s'", pagedURL)
 | 
			
		||||
 | 
			
		||||
		if err := web.ReadJSON(pagedURL, &reposList); err != nil {
 | 
			
		||||
			return reposMeta, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(reposList) == 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for idx, repo := range reposList {
 | 
			
		||||
			reposMeta[repo.Name] = reposList[idx]
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pageIdx++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return reposMeta, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@ import (
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"coopcloud.tech/abra/cli/formatter"
 | 
			
		||||
	"coopcloud.tech/abra/pkg/client/convert"
 | 
			
		||||
	loader "coopcloud.tech/abra/pkg/client/stack"
 | 
			
		||||
	stack "coopcloud.tech/abra/pkg/client/stack"
 | 
			
		||||
@ -243,8 +244,8 @@ func GetAppNames() ([]string, error) {
 | 
			
		||||
	return appNames, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CopyAppEnvSample copies the example env file for the app into the users env files
 | 
			
		||||
func CopyAppEnvSample(appType, appName, server string) error {
 | 
			
		||||
// TemplateAppEnvSample copies the example env file for the app into the users env files
 | 
			
		||||
func TemplateAppEnvSample(appType, appName, server, domain, recipe string) error {
 | 
			
		||||
	envSamplePath := path.Join(ABRA_DIR, "apps", appType, ".env.sample")
 | 
			
		||||
	envSample, err := ioutil.ReadFile(envSamplePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@ -256,6 +257,9 @@ func CopyAppEnvSample(appType, appName, server string) error {
 | 
			
		||||
		return fmt.Errorf("%s already exists?", appEnvPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	envSample = []byte(strings.Replace(string(envSample), fmt.Sprintf("%s.example.com", recipe), domain, -1))
 | 
			
		||||
	envSample = []byte(strings.Replace(string(envSample), "example.com", domain, -1))
 | 
			
		||||
 | 
			
		||||
	err = ioutil.WriteFile(appEnvPath, envSample, 0755)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
@ -275,14 +279,22 @@ func SanitiseAppName(name string) string {
 | 
			
		||||
func GetAppStatuses(appFiles AppFiles) (map[string]string, error) {
 | 
			
		||||
	statuses := map[string]string{}
 | 
			
		||||
 | 
			
		||||
	servers, err := GetServers()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return statuses, err
 | 
			
		||||
	var unique []string
 | 
			
		||||
	servers := make(map[string]struct{})
 | 
			
		||||
	for _, appFile := range appFiles {
 | 
			
		||||
		if _, ok := servers[appFile.Server]; !ok {
 | 
			
		||||
			servers[appFile.Server] = struct{}{}
 | 
			
		||||
			unique = append(unique, appFile.Server)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bar := formatter.CreateProgressbar(len(servers), "querying remote servers...")
 | 
			
		||||
	ch := make(chan stack.StackStatus, len(servers))
 | 
			
		||||
	for _, server := range servers {
 | 
			
		||||
		go func(s string) { ch <- stack.GetAllDeployedServices(s) }(server)
 | 
			
		||||
	for server := range servers {
 | 
			
		||||
		go func(s string) {
 | 
			
		||||
			ch <- stack.GetAllDeployedServices(s)
 | 
			
		||||
			bar.Add(1)
 | 
			
		||||
		}(server)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for range servers {
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@ func Clone(dir, url string) error {
 | 
			
		||||
		logrus.Debugf("'%s' does not exist, attempting to git clone from '%s'", dir, url)
 | 
			
		||||
		_, err := git.PlainClone(dir, false, &git.CloneOptions{URL: url, Tags: git.AllTags})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Debugf("cloning from default branch failed, attempting from main branch")
 | 
			
		||||
			logrus.Debugf("cloning '%s' default branch failed, attempting from main branch", url)
 | 
			
		||||
			_, err := git.PlainClone(dir, false, &git.CloneOptions{
 | 
			
		||||
				URL:           url,
 | 
			
		||||
				Tags:          git.AllTags,
 | 
			
		||||
@ -45,12 +45,13 @@ func EnsureUpToDate(dir string) error {
 | 
			
		||||
	branch := "master"
 | 
			
		||||
	if _, err := repo.Branch("master"); err != nil {
 | 
			
		||||
		if _, err := repo.Branch("main"); err != nil {
 | 
			
		||||
			logrus.Debugf("failed to select branch in '%s'", dir)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		branch = "main"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logrus.Debugf("choosing '%s' as main git branch for in '%s'", branch, dir)
 | 
			
		||||
	logrus.Debugf("choosing '%s' as main git branch in '%s'", branch, dir)
 | 
			
		||||
 | 
			
		||||
	worktree, err := repo.Worktree()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@ -65,10 +66,11 @@ func EnsureUpToDate(dir string) error {
 | 
			
		||||
		Branch: plumbing.ReferenceName(refName),
 | 
			
		||||
	}
 | 
			
		||||
	if err := worktree.Checkout(checkOutOpts); err != nil {
 | 
			
		||||
		logrus.Debugf("failed to check out '%s' in '%s'", refName, dir)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logrus.Debugf("successfully checked out '%s'", branch)
 | 
			
		||||
	logrus.Debugf("successfully checked out '%s' in '%s'", branch, dir)
 | 
			
		||||
 | 
			
		||||
	remote, err := repo.Remote("origin")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 | 
			
		||||
@ -6,4 +6,4 @@ Register-ArgumentCompleter -Native -CommandName $name -ScriptBlock {
 | 
			
		||||
         Invoke-Expression $other | ForEach-Object {
 | 
			
		||||
            [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
 | 
			
		||||
         }
 | 
			
		||||
 }
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
ABRA_VERSION="0.1.3-alpha"
 | 
			
		||||
ABRA_VERSION="0.1.4-alpha"
 | 
			
		||||
ABRA_RELEASE_URL="https://git.coopcloud.tech/api/v1/repos/coop-cloud/abra/releases/tags/$ABRA_VERSION"
 | 
			
		||||
 | 
			
		||||
function show_banner {
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user