forked from toolshed/abra
refactor!: cobra migrate
This commit is contained in:
186
cli/app/new.go
186
cli/app/new.go
@ -1,7 +1,6 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"coopcloud.tech/abra/cli/internal"
|
||||
@ -17,7 +16,7 @@ import (
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/charmbracelet/lipgloss/table"
|
||||
dockerClient "github.com/docker/docker/client"
|
||||
"github.com/urfave/cli/v3"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var appNewDescription = `Creates a new app from a default recipe.
|
||||
@ -26,12 +25,12 @@ This new app configuration is stored in your $ABRA_DIR directory under the
|
||||
appropriate server.
|
||||
|
||||
This command does not deploy your app for you. You will need to run "abra app
|
||||
deploy <domain>" to do so.
|
||||
deploy <app>" to do so.
|
||||
|
||||
You can see what recipes are available (i.e. values for the <recipe> argument)
|
||||
You can see what recipes are available (i.e. values for the [recipe] argument)
|
||||
by running "abra recipe ls".
|
||||
|
||||
Recipe commit hashes are supported values for "[<version>]".
|
||||
Recipe commit hashes are supported values for "[version]".
|
||||
|
||||
Passing the "--secrets/-S" flag will automatically generate secrets for your
|
||||
app and store them encrypted at rest on the chosen target server. These
|
||||
@ -42,32 +41,28 @@ You can use the "--pass/-P" to store these generated passwords locally in a
|
||||
pass store (see passwordstore.org for more). The pass command must be available
|
||||
on your $PATH.`
|
||||
|
||||
var appNewCommand = cli.Command{
|
||||
Name: "new",
|
||||
Aliases: []string{"n"},
|
||||
Usage: "Create a new app",
|
||||
UsageText: "abra app new [<recipe>] [<version>] [options]",
|
||||
Description: appNewDescription,
|
||||
Flags: []cli.Flag{
|
||||
internal.NewAppServerFlag,
|
||||
internal.DomainFlag,
|
||||
internal.PassFlag,
|
||||
internal.SecretsFlag,
|
||||
internal.ChaosFlag,
|
||||
},
|
||||
Before: internal.SubCommandBefore,
|
||||
HideHelp: true,
|
||||
ShellComplete: func(ctx context.Context, cmd *cli.Command) {
|
||||
args := cmd.Args()
|
||||
switch args.Len() {
|
||||
var AppNewCommand = &cobra.Command{
|
||||
Use: "new [recipe] [version] [flags]",
|
||||
Aliases: []string{"n"},
|
||||
Short: "Create a new app",
|
||||
Long: appNewDescription,
|
||||
Args: cobra.RangeArgs(0, 2),
|
||||
ValidArgsFunction: func(
|
||||
cmd *cobra.Command,
|
||||
args []string,
|
||||
toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
switch l := len(args); l {
|
||||
case 0:
|
||||
autocomplete.RecipeNameComplete(ctx, cmd)
|
||||
return autocomplete.RecipeNameComplete()
|
||||
case 1:
|
||||
autocomplete.RecipeVersionComplete(cmd.Args().Get(0))
|
||||
recipe := internal.ValidateRecipe(args, cmd.Name())
|
||||
return autocomplete.RecipeVersionComplete(recipe.Name)
|
||||
default:
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
},
|
||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||
recipe := internal.ValidateRecipe(cmd)
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
recipe := internal.ValidateRecipe(args, cmd.Name())
|
||||
|
||||
var version string
|
||||
if !internal.Chaos {
|
||||
@ -80,7 +75,12 @@ var appNewCommand = cli.Command{
|
||||
}
|
||||
}
|
||||
|
||||
if cmd.Args().Get(1) == "" {
|
||||
var recipeVersion string
|
||||
if len(args) == 2 {
|
||||
recipeVersion = args[1]
|
||||
}
|
||||
|
||||
if recipeVersion == "" {
|
||||
recipeVersions, err := recipe.GetRecipeVersions()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@ -101,8 +101,7 @@ var appNewCommand = cli.Command{
|
||||
}
|
||||
}
|
||||
} else {
|
||||
version = cmd.Args().Get(1)
|
||||
if _, err := recipe.EnsureVersion(version); err != nil {
|
||||
if _, err := recipe.EnsureVersion(recipeVersion); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@ -112,25 +111,25 @@ var appNewCommand = cli.Command{
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := ensureDomainFlag(recipe, internal.NewAppServer); err != nil {
|
||||
if err := ensureDomainFlag(recipe, newAppServer); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
sanitisedAppName := appPkg.SanitiseAppName(internal.Domain)
|
||||
log.Debugf("%s sanitised as %s for new app", internal.Domain, sanitisedAppName)
|
||||
sanitisedAppName := appPkg.SanitiseAppName(appDomain)
|
||||
log.Debugf("%s sanitised as %s for new app", appDomain, sanitisedAppName)
|
||||
|
||||
if err := appPkg.TemplateAppEnvSample(
|
||||
recipe,
|
||||
internal.Domain,
|
||||
internal.NewAppServer,
|
||||
internal.Domain,
|
||||
appDomain,
|
||||
newAppServer,
|
||||
appDomain,
|
||||
); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var secrets AppSecrets
|
||||
var appSecrets AppSecrets
|
||||
var secretsTable *table.Table
|
||||
if internal.Secrets {
|
||||
if generateSecrets {
|
||||
sampleEnv, err := recipe.SampleEnv()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@ -141,21 +140,25 @@ var appNewCommand = cli.Command{
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
secretsConfig, err := secret.ReadSecretsConfig(recipe.SampleEnvPath, composeFiles, appPkg.StackName(internal.Domain))
|
||||
secretsConfig, err := secret.ReadSecretsConfig(
|
||||
recipe.SampleEnvPath,
|
||||
composeFiles,
|
||||
appPkg.StackName(appDomain),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := promptForSecrets(recipe.Name, secretsConfig); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
cl, err := client.New(internal.NewAppServer)
|
||||
cl, err := client.New(newAppServer)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
secrets, err = createSecrets(cl, secretsConfig, sanitisedAppName)
|
||||
appSecrets, err = createSecrets(cl, secretsConfig, sanitisedAppName)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@ -168,13 +171,13 @@ var appNewCommand = cli.Command{
|
||||
headers := []string{"NAME", "VALUE"}
|
||||
secretsTable.Headers(headers...)
|
||||
|
||||
for name, val := range secrets {
|
||||
for name, val := range appSecrets {
|
||||
secretsTable.Row(name, val)
|
||||
}
|
||||
}
|
||||
|
||||
if internal.NewAppServer == "default" {
|
||||
internal.NewAppServer = "local"
|
||||
if newAppServer == "default" {
|
||||
newAppServer = "local"
|
||||
}
|
||||
|
||||
table, err := formatter.CreateTable()
|
||||
@ -185,7 +188,7 @@ var appNewCommand = cli.Command{
|
||||
headers := []string{"SERVER", "DOMAIN", "RECIPE", "VERSION"}
|
||||
table.Headers(headers...)
|
||||
|
||||
table.Row(internal.NewAppServer, internal.Domain, recipe.Name, version)
|
||||
table.Row(newAppServer, appDomain, recipe.Name, version)
|
||||
|
||||
log.Infof("new app '%s' created 🌞", recipe.Name)
|
||||
|
||||
@ -194,13 +197,13 @@ var appNewCommand = cli.Command{
|
||||
fmt.Println("")
|
||||
|
||||
fmt.Println("Configure this app:")
|
||||
fmt.Println(fmt.Sprintf("\n abra app config %s", internal.Domain))
|
||||
fmt.Println(fmt.Sprintf("\n abra app config %s", appDomain))
|
||||
|
||||
fmt.Println("")
|
||||
fmt.Println("Deploy this app:")
|
||||
fmt.Println(fmt.Sprintf("\n abra app deploy %s", internal.Domain))
|
||||
fmt.Println(fmt.Sprintf("\n abra app deploy %s", appDomain))
|
||||
|
||||
if len(secrets) > 0 {
|
||||
if len(appSecrets) > 0 {
|
||||
fmt.Println("")
|
||||
fmt.Println("Generated secrets:")
|
||||
fmt.Println("")
|
||||
@ -213,7 +216,7 @@ var appNewCommand = cli.Command{
|
||||
)
|
||||
}
|
||||
|
||||
app, err := app.Get(internal.Domain)
|
||||
app, err := app.Get(appDomain)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@ -222,8 +225,6 @@ var appNewCommand = cli.Command{
|
||||
if err := app.WriteRecipeVersion(version, false); err != nil {
|
||||
log.Fatalf("writing new recipe version in env file: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@ -238,19 +239,19 @@ func createSecrets(cl *dockerClient.Client, secretsConfig map[string]secret.Secr
|
||||
sanitisedAppName = sanitisedAppName[:config.MAX_SANITISED_APP_NAME_LENGTH]
|
||||
}
|
||||
|
||||
secrets, err := secret.GenerateSecrets(cl, secretsConfig, internal.NewAppServer)
|
||||
secrets, err := secret.GenerateSecrets(cl, secretsConfig, newAppServer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if internal.Pass {
|
||||
if saveInPass {
|
||||
for secretName := range secrets {
|
||||
secretValue := secrets[secretName]
|
||||
if err := secret.PassInsertSecret(
|
||||
secretValue,
|
||||
secretName,
|
||||
internal.Domain,
|
||||
internal.NewAppServer,
|
||||
appDomain,
|
||||
newAppServer,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -262,17 +263,17 @@ func createSecrets(cl *dockerClient.Client, secretsConfig map[string]secret.Secr
|
||||
|
||||
// ensureDomainFlag checks if the domain flag was used. if not, asks the user for it/
|
||||
func ensureDomainFlag(recipe recipePkg.Recipe, server string) error {
|
||||
if internal.Domain == "" && !internal.NoInput {
|
||||
if appDomain == "" && !internal.NoInput {
|
||||
prompt := &survey.Input{
|
||||
Message: "Specify app domain",
|
||||
Default: fmt.Sprintf("%s.%s", recipe.Name, server),
|
||||
}
|
||||
if err := survey.AskOne(prompt, &internal.Domain); err != nil {
|
||||
if err := survey.AskOne(prompt, &appDomain); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if internal.Domain == "" {
|
||||
if appDomain == "" {
|
||||
return fmt.Errorf("no domain provided")
|
||||
}
|
||||
|
||||
@ -286,11 +287,11 @@ func promptForSecrets(recipeName string, secretsConfig map[string]secret.Secret)
|
||||
return nil
|
||||
}
|
||||
|
||||
if !internal.Secrets && !internal.NoInput {
|
||||
if !generateSecrets && !internal.NoInput {
|
||||
prompt := &survey.Confirm{
|
||||
Message: "Generate app secrets?",
|
||||
}
|
||||
if err := survey.AskOne(prompt, &internal.Secrets); err != nil {
|
||||
if err := survey.AskOne(prompt, &generateSecrets); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -305,19 +306,76 @@ func ensureServerFlag() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if internal.NewAppServer == "" && !internal.NoInput {
|
||||
if newAppServer == "" && !internal.NoInput {
|
||||
prompt := &survey.Select{
|
||||
Message: "Select app server:",
|
||||
Options: servers,
|
||||
}
|
||||
if err := survey.AskOne(prompt, &internal.NewAppServer); err != nil {
|
||||
if err := survey.AskOne(prompt, &newAppServer); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if internal.NewAppServer == "" {
|
||||
if newAppServer == "" {
|
||||
return fmt.Errorf("no server provided")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
newAppServer string
|
||||
appDomain string
|
||||
saveInPass bool
|
||||
generateSecrets bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
AppNewCommand.Flags().StringVarP(
|
||||
&newAppServer,
|
||||
"server",
|
||||
"s",
|
||||
"",
|
||||
"specify server for new app",
|
||||
)
|
||||
|
||||
AppNewCommand.RegisterFlagCompletionFunc(
|
||||
"server",
|
||||
func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return autocomplete.ServerNameComplete()
|
||||
},
|
||||
)
|
||||
|
||||
AppNewCommand.Flags().StringVarP(
|
||||
&appDomain,
|
||||
"domain",
|
||||
"D",
|
||||
"",
|
||||
"domain name for app",
|
||||
)
|
||||
|
||||
AppNewCommand.Flags().BoolVarP(
|
||||
&saveInPass,
|
||||
"pass",
|
||||
"p",
|
||||
false,
|
||||
"store secrets in a local pass store",
|
||||
)
|
||||
|
||||
AppNewCommand.Flags().BoolVarP(
|
||||
&generateSecrets,
|
||||
"secrets",
|
||||
"S",
|
||||
false,
|
||||
"automatically generate secrets",
|
||||
)
|
||||
|
||||
AppNewCommand.Flags().BoolVarP(
|
||||
&internal.Chaos,
|
||||
"chaos",
|
||||
"C",
|
||||
false,
|
||||
"ignore uncommitted recipes changes",
|
||||
)
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user