refactor!: cobra migrate

This commit is contained in:
2024-12-26 17:53:25 +01:00
parent 0df2b15c33
commit 671e1ca276
76 changed files with 12042 additions and 2545 deletions

View File

@ -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",
)
}