654 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			654 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package app
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"sort"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 
 | |
| 	"coopcloud.tech/abra/cli/internal"
 | |
| 	appPkg "coopcloud.tech/abra/pkg/app"
 | |
| 	"coopcloud.tech/abra/pkg/autocomplete"
 | |
| 	"coopcloud.tech/abra/pkg/client"
 | |
| 	"coopcloud.tech/abra/pkg/formatter"
 | |
| 	"coopcloud.tech/abra/pkg/i18n"
 | |
| 	"coopcloud.tech/abra/pkg/log"
 | |
| 	"coopcloud.tech/abra/pkg/secret"
 | |
| 	"github.com/AlecAivazis/survey/v2"
 | |
| 	"github.com/docker/docker/api/types"
 | |
| 	dockerClient "github.com/docker/docker/client"
 | |
| 	"github.com/spf13/cobra"
 | |
| )
 | |
| 
 | |
| // translators: `abra app secret generate` aliases. use a comma separated list of aliases with
 | |
| // no spaces in between
 | |
| var appSecretGenerateAliases = i18n.G("g")
 | |
| 
 | |
| var AppSecretGenerateCommand = &cobra.Command{
 | |
| 	// translators: `app secret generate` command
 | |
| 	Use:     i18n.G("generate <domain> [[secret] [version] | --all] [flags]"),
 | |
| 	Aliases: strings.Split(appSecretGenerateAliases, ","),
 | |
| 	// translators: Short description for `app secret generate` command
 | |
| 	Short: i18n.G("Generate secrets"),
 | |
| 	Args:  cobra.RangeArgs(1, 3),
 | |
| 	ValidArgsFunction: func(
 | |
| 		cmd *cobra.Command,
 | |
| 		args []string,
 | |
| 		toComplete string,
 | |
| 	) ([]string, cobra.ShellCompDirective) {
 | |
| 		switch l := len(args); l {
 | |
| 		case 0:
 | |
| 			return autocomplete.AppNameComplete()
 | |
| 		case 1:
 | |
| 			app, err := appPkg.Get(args[0])
 | |
| 			if err != nil {
 | |
| 				return []string{i18n.G("autocomplete failed: %s", err)}, cobra.ShellCompDirectiveError
 | |
| 			}
 | |
| 			return autocomplete.SecretComplete(app.Recipe.Name)
 | |
| 		default:
 | |
| 			return nil, cobra.ShellCompDirectiveDefault
 | |
| 		}
 | |
| 	},
 | |
| 	Run: func(cmd *cobra.Command, args []string) {
 | |
| 		app := internal.ValidateApp(args)
 | |
| 
 | |
| 		if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		if len(args) <= 2 && !generateAllSecrets {
 | |
| 			log.Fatal(i18n.G("missing arguments [secret]/[version] or '--all'"))
 | |
| 		}
 | |
| 
 | |
| 		if len(args) > 2 && generateAllSecrets {
 | |
| 			log.Fatal(i18n.G("cannot use '[secret] [version]' and '--all' together"))
 | |
| 		}
 | |
| 
 | |
| 		composeFiles, err := app.Recipe.GetComposeFiles(app.Env)
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		secrets, err := secret.ReadSecretsConfig(app.Path, composeFiles, app.StackName())
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		if !generateAllSecrets {
 | |
| 			secretName := args[1]
 | |
| 			secretVersion := args[2]
 | |
| 			s, ok := secrets[secretName]
 | |
| 			if !ok {
 | |
| 				log.Fatal(i18n.G("%s doesn't exist in the env config?", secretName))
 | |
| 			}
 | |
| 			s.Version = secretVersion
 | |
| 			secrets = map[string]secret.Secret{
 | |
| 				secretName: s,
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		cl, err := client.New(app.Server)
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		secretVals, err := secret.GenerateSecrets(cl, secrets, app.Server)
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		if storeInPass {
 | |
| 			for name, data := range secretVals {
 | |
| 				if err := secret.PassInsertSecret(data, name, app.Name, app.Server); err != nil {
 | |
| 					log.Fatal(err)
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if len(secretVals) == 0 {
 | |
| 			log.Warn(i18n.G("no secrets generated"))
 | |
| 			os.Exit(1)
 | |
| 		}
 | |
| 
 | |
| 		headers := []string{i18n.G("NAME"), i18n.G("VALUE")}
 | |
| 		table, err := formatter.CreateTable()
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		table.Headers(headers...)
 | |
| 
 | |
| 		var rows [][]string
 | |
| 		for name, val := range secretVals {
 | |
| 			row := []string{name, val}
 | |
| 			rows = append(rows, row)
 | |
| 			table.Row(row...)
 | |
| 		}
 | |
| 
 | |
| 		if internal.MachineReadable {
 | |
| 			out, err := formatter.ToJSON(headers, rows)
 | |
| 			if err != nil {
 | |
| 				log.Fatal(i18n.G("unable to render to JSON: %s", err))
 | |
| 			}
 | |
| 			fmt.Println(out)
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		if err := formatter.PrintTable(table); err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		log.Warn(i18n.G(
 | |
| 			"generated secrets %s shown again, please take note of them %s",
 | |
| 			formatter.BoldStyle.Render(i18n.G("NOT")),
 | |
| 			formatter.BoldStyle.Render(i18n.G("NOW")),
 | |
| 		))
 | |
| 	},
 | |
| }
 | |
| 
 | |
| // translators: `abra app secret insert` aliases. use a comma separated list of aliases with
 | |
| // no spaces in between
 | |
| var appSecretInsertAliases = i18n.G("i")
 | |
| 
 | |
| var AppSecretInsertCommand = &cobra.Command{
 | |
| 	// translators: `app secret insert` command
 | |
| 	Use:     i18n.G("insert <domain> <secret> <version> [<data>] [flags]"),
 | |
| 	Aliases: strings.Split(appSecretInsertAliases, ","),
 | |
| 	// translators: Short description for `app secret insert` command
 | |
| 	Short: i18n.G("Insert secret"),
 | |
| 	Long: i18n.G(`This command inserts a secret into an app environment.
 | |
| 
 | |
| Arbitrary secret insertion is not supported. Secrets that are inserted must
 | |
| match those configured in the recipe beforehand.
 | |
| 
 | |
| This can be useful when you want to manually generate secrets for an app
 | |
| environment. Typically, you can let Abra generate them for you on app creation
 | |
| (see "abra app new --secrets/-S" for more).`),
 | |
| 	Example: i18n.G(`  # insert regular secret
 | |
|   abra app secret insert 1312.net my_secret v1 mySuperSecret
 | |
| 
 | |
|   # insert secret as file
 | |
|   abra app secret insert 1312.net my_secret v1 secret.txt -f
 | |
| 
 | |
|   # insert secret from stdin
 | |
|   echo "mmySuperSecret" | abra app secret insert 1312.net my_secret v1`),
 | |
| 	Args: cobra.MinimumNArgs(3),
 | |
| 	ValidArgsFunction: func(
 | |
| 		cmd *cobra.Command,
 | |
| 		args []string,
 | |
| 		toComplete string,
 | |
| 	) ([]string, cobra.ShellCompDirective) {
 | |
| 		switch l := len(args); l {
 | |
| 		case 0:
 | |
| 			return autocomplete.AppNameComplete()
 | |
| 		case 1:
 | |
| 			app, err := appPkg.Get(args[0])
 | |
| 			if err != nil {
 | |
| 				return []string{i18n.G("autocomplete failed: %s", err)}, cobra.ShellCompDirectiveError
 | |
| 			}
 | |
| 			return autocomplete.SecretComplete(app.Recipe.Name)
 | |
| 		default:
 | |
| 			return nil, cobra.ShellCompDirectiveDefault
 | |
| 		}
 | |
| 	},
 | |
| 	Run: func(cmd *cobra.Command, args []string) {
 | |
| 		app := internal.ValidateApp(args)
 | |
| 
 | |
| 		if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		cl, err := client.New(app.Server)
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		name := args[1]
 | |
| 		version := args[2]
 | |
| 		data, err := readSecretData(args)
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		composeFiles, err := app.Recipe.GetComposeFiles(app.Env)
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		secrets, err := secret.ReadSecretsConfig(app.Path, composeFiles, app.StackName())
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		var isRecipeSecret bool
 | |
| 		for secretName := range secrets {
 | |
| 			if secretName == name {
 | |
| 				isRecipeSecret = true
 | |
| 			}
 | |
| 		}
 | |
| 		if !isRecipeSecret {
 | |
| 			log.Fatal(i18n.G("no secret %s available for recipe %s?", name, app.Recipe.Name))
 | |
| 		}
 | |
| 
 | |
| 		if insertFromFile {
 | |
| 			raw, err := os.ReadFile(data)
 | |
| 			if err != nil {
 | |
| 				log.Fatal(i18n.G("reading secret from file: %s", err))
 | |
| 			}
 | |
| 			data = string(raw)
 | |
| 		}
 | |
| 
 | |
| 		if trimInput {
 | |
| 			data = strings.TrimSpace(data)
 | |
| 		}
 | |
| 
 | |
| 		secretName := fmt.Sprintf("%s_%s_%s", app.StackName(), name, version)
 | |
| 		if err := client.StoreSecret(cl, secretName, data); err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		log.Info(i18n.G("%s successfully stored on server", secretName))
 | |
| 
 | |
| 		if storeInPass {
 | |
| 			if err := secret.PassInsertSecret(data, name, app.Name, app.Server); err != nil {
 | |
| 				log.Fatal(err)
 | |
| 			}
 | |
| 		}
 | |
| 	},
 | |
| }
 | |
| 
 | |
| func readSecretData(args []string) (string, error) {
 | |
| 	if len(args) == 4 {
 | |
| 		return args[3], nil
 | |
| 	}
 | |
| 
 | |
| 	if len(args) != 3 {
 | |
| 		return "", errors.New(i18n.G("need 3 or 4 arguments"))
 | |
| 	}
 | |
| 	// First check if data is provided by stdin
 | |
| 	fi, err := os.Stdin.Stat()
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	if fi.Mode()&os.ModeNamedPipe != 0 {
 | |
| 		// Can't insert from stdin and read from file
 | |
| 		if insertFromFile {
 | |
| 			return "", errors.New(i18n.G("can not insert from file and read from stdin"))
 | |
| 		}
 | |
| 
 | |
| 		log.Debug(i18n.G("reading secret data from stdin"))
 | |
| 		bytes, err := io.ReadAll(os.Stdin)
 | |
| 		if err != nil {
 | |
| 			return "", errors.New(i18n.G("reading data from stdin: %s", err))
 | |
| 		}
 | |
| 
 | |
| 		return string(bytes), nil
 | |
| 	}
 | |
| 	if internal.NoInput {
 | |
| 		return "", errors.New(i18n.G("must provide <data> argument if --no-input is passed"))
 | |
| 	}
 | |
| 
 | |
| 	log.Debug(i18n.G("secret data not provided on command-line or stdin, prompting"))
 | |
| 	var prompt survey.Prompt
 | |
| 	if !insertFromFile {
 | |
| 		prompt = &survey.Password{
 | |
| 			Message: i18n.G("specify secret value"),
 | |
| 		}
 | |
| 	} else {
 | |
| 		prompt = &survey.Input{
 | |
| 			Message: i18n.G("specify secret file"),
 | |
| 		}
 | |
| 	}
 | |
| 	var data string
 | |
| 	if err := survey.AskOne(prompt, &data); err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	return data, nil
 | |
| }
 | |
| 
 | |
| // secretRm removes a secret.
 | |
| func secretRm(cl *dockerClient.Client, app appPkg.App, secretName, parsed string) error {
 | |
| 	if err := cl.SecretRemove(context.Background(), secretName); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	log.Info(i18n.G("deleted %s successfully from server", secretName))
 | |
| 
 | |
| 	if removeFromPass {
 | |
| 		if err := secret.PassRmSecret(parsed, app.StackName(), app.Server); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		log.Info(i18n.G("deleted %s successfully from local pass store", secretName))
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // translators: `abra app secret remove` aliases. use a comma separated list of aliases with
 | |
| // no spaces in between
 | |
| var appSecretRemoveAliases = i18n.G("rm")
 | |
| 
 | |
| var AppSecretRmCommand = &cobra.Command{
 | |
| 	// translators: `app secret remove` command
 | |
| 	Use:     i18n.G("remove <domain> [[secret] | --all] [flags]"),
 | |
| 	Aliases: strings.Split(appSecretRemoveAliases, ","),
 | |
| 	// translators: Short description for `app secret remove` command
 | |
| 	Short: i18n.G("Remove a secret"),
 | |
| 	Long: i18n.G(`This command removes a secret from an app environment.
 | |
| 
 | |
| Arbitrary secret removal is not supported. Secrets that are removed must
 | |
| match those configured in the recipe beforehand.`),
 | |
| 	Example: i18n.G("  abra app secret rm 1312.net oauth_key"),
 | |
| 	Args:    cobra.RangeArgs(1, 2),
 | |
| 	ValidArgsFunction: func(
 | |
| 		cmd *cobra.Command,
 | |
| 		args []string,
 | |
| 		toComplete string,
 | |
| 	) ([]string, cobra.ShellCompDirective) {
 | |
| 		switch l := len(args); l {
 | |
| 		case 0:
 | |
| 			return autocomplete.AppNameComplete()
 | |
| 		case 1:
 | |
| 			if !rmAllSecrets {
 | |
| 				app, err := appPkg.Get(args[0])
 | |
| 				if err != nil {
 | |
| 					return []string{i18n.G("autocomplete failed: %s", err)}, cobra.ShellCompDirectiveError
 | |
| 				}
 | |
| 				return autocomplete.SecretComplete(app.Recipe.Name)
 | |
| 			}
 | |
| 			return nil, cobra.ShellCompDirectiveDefault
 | |
| 		default:
 | |
| 			return nil, cobra.ShellCompDirectiveError
 | |
| 		}
 | |
| 	},
 | |
| 	Run: func(cmd *cobra.Command, args []string) {
 | |
| 		app := internal.ValidateApp(args)
 | |
| 
 | |
| 		if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		composeFiles, err := app.Recipe.GetComposeFiles(app.Env)
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		secrets, err := secret.ReadSecretsConfig(app.Path, composeFiles, app.StackName())
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		if len(args) == 2 && rmAllSecrets {
 | |
| 			log.Fatal(i18n.G("cannot use [secret] and --all/-a together"))
 | |
| 		}
 | |
| 
 | |
| 		if len(args) != 2 && !rmAllSecrets {
 | |
| 			log.Fatal(i18n.G("no secret(s) specified?"))
 | |
| 		}
 | |
| 
 | |
| 		cl, err := client.New(app.Server)
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		filters, err := app.Filters(false, false)
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		secretList, err := cl.SecretList(context.Background(), types.SecretListOptions{Filters: filters})
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		remoteSecretNames := make(map[string]bool)
 | |
| 		for _, cont := range secretList {
 | |
| 			remoteSecretNames[cont.Spec.Annotations.Name] = true
 | |
| 		}
 | |
| 
 | |
| 		var secretToRm string
 | |
| 		if len(args) == 2 {
 | |
| 			secretToRm = args[1]
 | |
| 		}
 | |
| 
 | |
| 		match := false
 | |
| 		for secretName, val := range secrets {
 | |
| 			secretRemoteName := fmt.Sprintf("%s_%s_%s", app.StackName(), secretName, val.Version)
 | |
| 			if _, ok := remoteSecretNames[secretRemoteName]; ok {
 | |
| 				if secretToRm != "" {
 | |
| 					if secretName == secretToRm {
 | |
| 						if err := secretRm(cl, app, secretRemoteName, secretName); err != nil {
 | |
| 							log.Fatal(err)
 | |
| 						}
 | |
| 
 | |
| 						return
 | |
| 					}
 | |
| 				} else {
 | |
| 					match = true
 | |
| 
 | |
| 					if err := secretRm(cl, app, secretRemoteName, secretName); err != nil {
 | |
| 						log.Fatal(err)
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if !match && secretToRm != "" {
 | |
| 			log.Fatal(i18n.G("%s doesn't exist on server?", secretToRm))
 | |
| 		}
 | |
| 
 | |
| 		if !match {
 | |
| 			log.Fatal(i18n.G("no secrets to remove?"))
 | |
| 		}
 | |
| 	},
 | |
| }
 | |
| 
 | |
| // translators: `abra app secret ls` aliases. use a comma separated list of aliases with
 | |
| // no spaces in between
 | |
| var appSecretLsAliases = i18n.G("ls")
 | |
| 
 | |
| var AppSecretLsCommand = &cobra.Command{
 | |
| 	// translators: `app secret list` command
 | |
| 	Use:     i18n.G("list <domain>"),
 | |
| 	Aliases: strings.Split(appSecretLsAliases, ","),
 | |
| 	// translators: Short description for `app secret list` command
 | |
| 	Short: i18n.G("List all secrets"),
 | |
| 	Args:  cobra.MinimumNArgs(1),
 | |
| 	ValidArgsFunction: func(
 | |
| 		cmd *cobra.Command,
 | |
| 		args []string,
 | |
| 		toComplete string,
 | |
| 	) ([]string, cobra.ShellCompDirective) {
 | |
| 		return autocomplete.AppNameComplete()
 | |
| 	},
 | |
| 	Run: func(cmd *cobra.Command, args []string) {
 | |
| 		app := internal.ValidateApp(args)
 | |
| 
 | |
| 		if err := app.Recipe.Ensure(internal.GetEnsureContext()); err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		cl, err := client.New(app.Server)
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		headers := []string{i18n.G("NAME"), i18n.G("VERSION"), i18n.G("GENERATED NAME"), i18n.G("CREATED ON SERVER")}
 | |
| 		table, err := formatter.CreateTable()
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		table.Headers(headers...)
 | |
| 
 | |
| 		secStats, err := secret.PollSecretsStatus(cl, app)
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		// Sort secrets to ensure reproducible output
 | |
| 		sort.Slice(secStats, func(i, j int) bool {
 | |
| 			return secStats[i].LocalName < secStats[j].LocalName
 | |
| 		})
 | |
| 		var rows [][]string
 | |
| 		for _, secStat := range secStats {
 | |
| 			row := []string{
 | |
| 				secStat.LocalName,
 | |
| 				secStat.Version,
 | |
| 				secStat.RemoteName,
 | |
| 				strconv.FormatBool(secStat.CreatedOnRemote),
 | |
| 			}
 | |
| 
 | |
| 			rows = append(rows, row)
 | |
| 			table.Row(row...)
 | |
| 		}
 | |
| 
 | |
| 		if len(rows) > 0 {
 | |
| 			if internal.MachineReadable {
 | |
| 				out, err := formatter.ToJSON(headers, rows)
 | |
| 				if err != nil {
 | |
| 					log.Fatal(i18n.G("unable to render to JSON: %s", err))
 | |
| 				}
 | |
| 				fmt.Println(out)
 | |
| 				return
 | |
| 			}
 | |
| 
 | |
| 			if err := formatter.PrintTable(table); err != nil {
 | |
| 				log.Fatal(err)
 | |
| 			}
 | |
| 
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		log.Warn(i18n.G("no secrets stored for %s", app.Name))
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var AppSecretCommand = &cobra.Command{
 | |
| 	// translators: `app secret` command group
 | |
| 	Use:     i18n.G("secret [cmd] [args] [flags]"),
 | |
| 	Aliases: []string{i18n.G("s")},
 | |
| 	// translators: Short description for `app secret` command group
 | |
| 	Short: i18n.G("Manage app secrets"),
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	storeInPass        bool
 | |
| 	insertFromFile     bool
 | |
| 	trimInput          bool
 | |
| 	rmAllSecrets       bool
 | |
| 	generateAllSecrets bool
 | |
| 	removeFromPass     bool
 | |
| )
 | |
| 
 | |
| func init() {
 | |
| 	AppSecretGenerateCommand.Flags().BoolVarP(
 | |
| 		&internal.MachineReadable,
 | |
| 		i18n.G("machine"),
 | |
| 		i18n.G("m"),
 | |
| 		false,
 | |
| 		i18n.G("print machine-readable output"),
 | |
| 	)
 | |
| 
 | |
| 	AppSecretGenerateCommand.Flags().BoolVarP(
 | |
| 		&storeInPass,
 | |
| 		i18n.G("pass"),
 | |
| 		i18n.G("p"),
 | |
| 		false,
 | |
| 		i18n.G("store generated secrets in a local pass store"),
 | |
| 	)
 | |
| 
 | |
| 	AppSecretGenerateCommand.Flags().BoolVarP(
 | |
| 		&internal.Chaos,
 | |
| 		i18n.G("chaos"),
 | |
| 		i18n.G("C"),
 | |
| 		false,
 | |
| 		i18n.G("ignore uncommitted recipes changes"),
 | |
| 	)
 | |
| 
 | |
| 	AppSecretGenerateCommand.Flags().BoolVarP(
 | |
| 		&generateAllSecrets,
 | |
| 		i18n.G("all"),
 | |
| 		i18n.G("a"),
 | |
| 		false,
 | |
| 		i18n.G("generate all secrets"),
 | |
| 	)
 | |
| 
 | |
| 	AppSecretInsertCommand.Flags().BoolVarP(
 | |
| 		&storeInPass,
 | |
| 		i18n.G("pass"),
 | |
| 		i18n.G("p"),
 | |
| 		false,
 | |
| 		i18n.G("store generated secrets in a local pass store"),
 | |
| 	)
 | |
| 
 | |
| 	AppSecretInsertCommand.Flags().BoolVarP(
 | |
| 		&insertFromFile,
 | |
| 		i18n.G("file"),
 | |
| 		i18n.G("f"),
 | |
| 		false,
 | |
| 		i18n.G("treat input as a file"),
 | |
| 	)
 | |
| 
 | |
| 	AppSecretInsertCommand.Flags().BoolVarP(
 | |
| 		&trimInput,
 | |
| 		i18n.G("trim"),
 | |
| 		i18n.G("t"),
 | |
| 		false,
 | |
| 		i18n.G("trim input"),
 | |
| 	)
 | |
| 
 | |
| 	AppSecretInsertCommand.Flags().BoolVarP(
 | |
| 		&internal.Chaos,
 | |
| 		i18n.G("chaos"),
 | |
| 		i18n.G("C"),
 | |
| 		false,
 | |
| 		i18n.G("ignore uncommitted recipes changes"),
 | |
| 	)
 | |
| 
 | |
| 	AppSecretRmCommand.Flags().BoolVarP(
 | |
| 		&rmAllSecrets,
 | |
| 		i18n.G("all"),
 | |
| 		i18n.G("a"),
 | |
| 		false,
 | |
| 		i18n.G("remove all secrets"),
 | |
| 	)
 | |
| 
 | |
| 	AppSecretRmCommand.Flags().BoolVarP(
 | |
| 		&removeFromPass,
 | |
| 		i18n.G("pass"),
 | |
| 		i18n.G("p"),
 | |
| 		false,
 | |
| 		i18n.G("remove generated secrets from a local pass store"),
 | |
| 	)
 | |
| 
 | |
| 	AppSecretRmCommand.Flags().BoolVarP(
 | |
| 		&internal.Chaos,
 | |
| 		i18n.G("chaos"),
 | |
| 		i18n.G("C"),
 | |
| 		false,
 | |
| 		i18n.G("ignore uncommitted recipes changes"),
 | |
| 	)
 | |
| 
 | |
| 	AppSecretLsCommand.Flags().BoolVarP(
 | |
| 		&internal.Chaos,
 | |
| 		i18n.G("chaos"),
 | |
| 		i18n.G("C"),
 | |
| 		false,
 | |
| 		i18n.G("ignore uncommitted recipes changes"),
 | |
| 	)
 | |
| 
 | |
| 	AppSecretLsCommand.Flags().BoolVarP(
 | |
| 		&internal.MachineReadable,
 | |
| 		i18n.G("machine"),
 | |
| 		i18n.G("m"),
 | |
| 		false,
 | |
| 		i18n.G("print machine-readable output"),
 | |
| 	)
 | |
| }
 |