forked from toolshed/abra
Merge remote-tracking branch 'upstream/main' into deploy-relax-force
This commit is contained in:
commit
5a4dac7e76
@ -46,7 +46,10 @@ ${FOO:<default>} syntax). "check" does not confirm or deny this for you.`,
|
|||||||
}
|
}
|
||||||
|
|
||||||
table.
|
table.
|
||||||
Headers("RECIPE ENV SAMPLE", "APP ENV").
|
Headers(
|
||||||
|
fmt.Sprintf("%s .env.sample", app.Recipe.Name),
|
||||||
|
fmt.Sprintf("%s.env", app.Name),
|
||||||
|
).
|
||||||
StyleFunc(func(row, col int) lipgloss.Style {
|
StyleFunc(func(row, col int) lipgloss.Style {
|
||||||
switch {
|
switch {
|
||||||
case col == 1:
|
case col == 1:
|
||||||
@ -71,7 +74,9 @@ ${FOO:<default>} syntax). "check" does not confirm or deny this for you.`,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(table)
|
if err := formatter.PrintTable(table); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,8 +54,8 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
|||||||
case 1:
|
case 1:
|
||||||
app, err := appPkg.Get(args[0])
|
app, err := appPkg.Get(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("autocomplete failed: %s", err)
|
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
|
||||||
return nil, cobra.ShellCompDirectiveDefault
|
return []string{errMsg}, cobra.ShellCompDirectiveError
|
||||||
}
|
}
|
||||||
return autocomplete.RecipeVersionComplete(app.Recipe.Name)
|
return autocomplete.RecipeVersionComplete(app.Recipe.Name)
|
||||||
default:
|
default:
|
||||||
@ -112,19 +112,19 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
|||||||
// is because we need to deal with GetComposeFiles under the hood and these
|
// is because we need to deal with GetComposeFiles under the hood and these
|
||||||
// files change from version to version which therefore affects which
|
// files change from version to version which therefore affects which
|
||||||
// secrets might be generated
|
// secrets might be generated
|
||||||
version := deployMeta.Version
|
toDeployVersion := deployMeta.Version
|
||||||
if specificVersion != "" {
|
if specificVersion != "" {
|
||||||
version = specificVersion
|
toDeployVersion = specificVersion
|
||||||
log.Debugf("choosing %s as version to deploy", version)
|
log.Debugf("choosing %s as version to deploy", toDeployVersion)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
isChaosCommit, err = app.Recipe.EnsureVersion(version)
|
isChaosCommit, err = app.Recipe.EnsureVersion(toDeployVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isChaosCommit {
|
if isChaosCommit {
|
||||||
log.Debugf("assuming '%s' is a chaos commit", version)
|
log.Debugf("assuming '%s' is a chaos commit", toDeployVersion)
|
||||||
internal.Chaos = true
|
internal.Chaos = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,9 +147,9 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(versions) > 0 && !internal.Chaos {
|
if len(versions) > 0 && !internal.Chaos {
|
||||||
version = versions[len(versions)-1]
|
toDeployVersion = versions[len(versions)-1]
|
||||||
log.Debugf("choosing %s as version to deploy", version)
|
log.Debugf("choosing %s as version to deploy", toDeployVersion)
|
||||||
if _, err := app.Recipe.EnsureVersion(version); err != nil {
|
if _, err := app.Recipe.EnsureVersion(toDeployVersion); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -157,25 +157,22 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
version = formatter.SmallSHA(head.String())
|
toDeployVersion = formatter.SmallSHA(head.String())
|
||||||
warnMessages = append(warnMessages, fmt.Sprintf("no versions detected, using latest commit"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chaosVersion := config.CHAOS_DEFAULT
|
toDeployChaosVersion := config.CHAOS_DEFAULT
|
||||||
if internal.Chaos {
|
if internal.Chaos {
|
||||||
warnMessages = append(warnMessages, "chaos mode engaged")
|
|
||||||
|
|
||||||
if isChaosCommit {
|
if isChaosCommit {
|
||||||
chaosVersion = specificVersion
|
toDeployChaosVersion = specificVersion
|
||||||
versionLabelLocal, err := app.Recipe.GetVersionLabelLocal()
|
versionLabelLocal, err := app.Recipe.GetVersionLabelLocal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
version = versionLabelLocal
|
toDeployVersion = versionLabelLocal
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
chaosVersion, err = app.Recipe.ChaosVersion()
|
toDeployChaosVersion, err = app.Recipe.ChaosVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -189,11 +186,11 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
if appStatus.Chaos && !internal.Chaos {
|
if appStatus.Chaos && !internal.Chaos {
|
||||||
log.Fatalf("%s is deployed from a chaos version. Are you sure the local changes are in the current version (%s)?", app.Name, version)
|
log.Fatalf("%s is deployed from a chaos version. Are you sure the local changes are in the current version (%s)?", app.Name, toDeployVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
if version != "" && appStatus.Version != "" {
|
if toDeployVersion != "" && appStatus.Version != "" {
|
||||||
localVersion, err := tagcmp.Parse(version)
|
localVersion, err := tagcmp.Parse(toDeployVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -203,7 +200,7 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if localVersion.IsLessThan(remoteVersion) {
|
if localVersion.IsLessThan(remoteVersion) {
|
||||||
log.Fatalf("%s is deployed at %s. Are you sure you want to downgrade to %s?", app.Name, appStatus.Version, version)
|
log.Fatalf("%s is deployed at %s. Are you sure you want to downgrade to %s?", app.Name, appStatus.Version, toDeployVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,7 +234,7 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
|||||||
appPkg.ExposeAllEnv(stackName, compose, app.Env)
|
appPkg.ExposeAllEnv(stackName, compose, app.Env)
|
||||||
appPkg.SetRecipeLabel(compose, stackName, app.Recipe.Name)
|
appPkg.SetRecipeLabel(compose, stackName, app.Recipe.Name)
|
||||||
appPkg.SetChaosLabel(compose, stackName, internal.Chaos)
|
appPkg.SetChaosLabel(compose, stackName, internal.Chaos)
|
||||||
appPkg.SetChaosVersionLabel(compose, stackName, chaosVersion)
|
appPkg.SetChaosVersionLabel(compose, stackName, toDeployChaosVersion)
|
||||||
appPkg.SetUpdateLabel(compose, stackName, app.Env)
|
appPkg.SetUpdateLabel(compose, stackName, app.Env)
|
||||||
|
|
||||||
envVars, err := appPkg.CheckEnv(app)
|
envVars, err := appPkg.CheckEnv(app)
|
||||||
@ -260,13 +257,24 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
warnMessages = append(warnMessages, "skipping domain checks as no DOMAIN=... configured for app")
|
log.Debug("skipping domain checks as no DOMAIN=... configured for app")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
warnMessages = append(warnMessages, "skipping domain checks as requested")
|
log.Debug("skipping domain checks as requested")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := internal.DeployOverview(app, warnMessages, version, chaosVersion); err != nil {
|
deployedVersion := config.NO_VERSION_DEFAULT
|
||||||
|
if deployMeta.IsDeployed {
|
||||||
|
deployedVersion = deployMeta.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := internal.DeployOverview(
|
||||||
|
app,
|
||||||
|
warnMessages,
|
||||||
|
deployedVersion,
|
||||||
|
deployMeta.ChaosVersion,
|
||||||
|
toDeployVersion,
|
||||||
|
toDeployChaosVersion); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,9 +296,9 @@ Please note, "upgrade"/"rollback" do not support chaos operations.`,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Recipe.Version = version
|
app.Recipe.Version = toDeployVersion
|
||||||
if chaosVersion != config.CHAOS_DEFAULT {
|
if toDeployChaosVersion != config.CHAOS_DEFAULT {
|
||||||
app.Recipe.Version = chaosVersion
|
app.Recipe.Version = toDeployChaosVersion
|
||||||
}
|
}
|
||||||
log.Debugf("choosing %s as version to save to env file", app.Recipe.Version)
|
log.Debugf("choosing %s as version to save to env file", app.Recipe.Version)
|
||||||
if err := app.WriteRecipeVersion(app.Recipe.Version, false); err != nil {
|
if err := app.WriteRecipeVersion(app.Recipe.Version, false); err != nil {
|
||||||
|
@ -173,7 +173,7 @@ Use "--status/-S" flag to query all servers for the live deployment status.`,
|
|||||||
stats.LatestCount++
|
stats.LatestCount++
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newUpdates = internal.ReverseStringList(newUpdates)
|
newUpdates = internal.SortVersionsDesc(newUpdates)
|
||||||
appStats.Upgrade = strings.Join(newUpdates, "\n")
|
appStats.Upgrade = strings.Join(newUpdates, "\n")
|
||||||
stats.UpgradeCount++
|
stats.UpgradeCount++
|
||||||
}
|
}
|
||||||
@ -208,7 +208,7 @@ Use "--status/-S" flag to query all servers for the live deployment status.`,
|
|||||||
|
|
||||||
serverStat := allStats[app.Server]
|
serverStat := allStats[app.Server]
|
||||||
|
|
||||||
headers := []string{"RECIPE", "DOMAIN"}
|
headers := []string{"RECIPE", "DOMAIN", "SERVER"}
|
||||||
if status {
|
if status {
|
||||||
headers = append(headers, []string{
|
headers = append(headers, []string{
|
||||||
"STATUS",
|
"STATUS",
|
||||||
@ -228,7 +228,7 @@ Use "--status/-S" flag to query all servers for the live deployment status.`,
|
|||||||
|
|
||||||
var rows [][]string
|
var rows [][]string
|
||||||
for _, appStat := range serverStat.Apps {
|
for _, appStat := range serverStat.Apps {
|
||||||
row := []string{appStat.Recipe, appStat.Domain}
|
row := []string{appStat.Recipe, appStat.Domain, appStat.Server}
|
||||||
if status {
|
if status {
|
||||||
chaosStatus := appStat.Chaos
|
chaosStatus := appStat.Chaos
|
||||||
if chaosStatus != "unknown" {
|
if chaosStatus != "unknown" {
|
||||||
@ -256,20 +256,8 @@ Use "--status/-S" flag to query all servers for the live deployment status.`,
|
|||||||
table.Rows(rows...)
|
table.Rows(rows...)
|
||||||
|
|
||||||
if len(rows) > 0 {
|
if len(rows) > 0 {
|
||||||
fmt.Println(table)
|
if err := formatter.PrintTable(table); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
if status {
|
|
||||||
fmt.Println(fmt.Sprintf(
|
|
||||||
"SERVER: %s | TOTAL APPS: %v | VERSIONED: %v | UNVERSIONED: %v | LATEST : %v | UPGRADE: %v",
|
|
||||||
app.Server,
|
|
||||||
serverStat.AppCount,
|
|
||||||
serverStat.VersionCount,
|
|
||||||
serverStat.UnversionedCount,
|
|
||||||
serverStat.LatestCount,
|
|
||||||
serverStat.UpgradeCount,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
log.Infof("SERVER: %s TOTAL APPS: %v", app.Server, serverStat.AppCount)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(allStats) > 1 && len(rows) > 0 {
|
if len(allStats) > 1 && len(rows) > 0 {
|
||||||
@ -279,12 +267,6 @@ Use "--status/-S" flag to query all servers for the live deployment status.`,
|
|||||||
|
|
||||||
alreadySeen[app.Server] = true
|
alreadySeen[app.Server] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(allStats) > 1 {
|
|
||||||
totalServers := formatter.BoldStyle.Render("TOTAL SERVERS")
|
|
||||||
totalApps := formatter.BoldStyle.Render("TOTAL APPS")
|
|
||||||
log.Infof("%s: %v | %s: %v ", totalServers, totalServersCount, totalApps, totalAppsCount)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
@ -37,8 +38,8 @@ var AppLogsCommand = &cobra.Command{
|
|||||||
case 1:
|
case 1:
|
||||||
app, err := appPkg.Get(args[0])
|
app, err := appPkg.Get(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("autocomplete failed: %s", err)
|
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
|
||||||
return nil, cobra.ShellCompDirectiveDefault
|
return []string{errMsg}, cobra.ShellCompDirectiveError
|
||||||
}
|
}
|
||||||
return autocomplete.ServiceNameComplete(app.Name)
|
return autocomplete.ServiceNameComplete(app.Name)
|
||||||
default:
|
default:
|
||||||
|
105
cli/app/new.go
105
cli/app/new.go
@ -64,56 +64,56 @@ var AppNewCommand = &cobra.Command{
|
|||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
recipe := internal.ValidateRecipe(args, cmd.Name())
|
recipe := internal.ValidateRecipe(args, cmd.Name())
|
||||||
|
|
||||||
|
if len(args) == 2 && internal.Chaos {
|
||||||
|
log.Fatal("cannot use [version] and --chaos together")
|
||||||
|
}
|
||||||
|
|
||||||
var recipeVersion string
|
var recipeVersion string
|
||||||
if !internal.Chaos {
|
if len(args) == 2 {
|
||||||
if err := recipe.EnsureIsClean(); err != nil {
|
recipeVersion = args[1]
|
||||||
log.Fatal(err)
|
}
|
||||||
}
|
|
||||||
|
chaosVersion := config.CHAOS_DEFAULT
|
||||||
|
if internal.Chaos {
|
||||||
|
recipeVersion = chaosVersion
|
||||||
|
|
||||||
if !internal.Offline {
|
if !internal.Offline {
|
||||||
if err := recipe.EnsureUpToDate(); err != nil {
|
if err := recipe.EnsureUpToDate(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(args) == 2 {
|
if !internal.Chaos {
|
||||||
recipeVersion = args[1]
|
if err := recipe.EnsureIsClean(); err != nil {
|
||||||
}
|
log.Fatal(err)
|
||||||
|
|
||||||
if recipeVersion == "" {
|
|
||||||
recipeVersions, err := recipe.GetRecipeVersions()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(recipeVersions) > 0 {
|
|
||||||
latest := recipeVersions[len(recipeVersions)-1]
|
|
||||||
for tag := range latest {
|
|
||||||
recipeVersion = tag
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := recipe.EnsureVersion(recipeVersion); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := recipe.EnsureLatest(); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if _, err := recipe.EnsureVersion(recipeVersion); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if internal.Chaos && recipeVersion == "" {
|
var recipeVersions recipePkg.RecipeVersions
|
||||||
|
if recipeVersion == "" {
|
||||||
var err error
|
var err error
|
||||||
recipeVersion, err = recipe.ChaosVersion()
|
recipeVersions, err = recipe.GetRecipeVersions()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(recipeVersions) > 0 {
|
||||||
|
latest := recipeVersions[len(recipeVersions)-1]
|
||||||
|
for tag := range latest {
|
||||||
|
recipeVersion = tag
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := recipe.EnsureVersion(recipeVersion); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := recipe.EnsureLatest(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := ensureServerFlag(); err != nil {
|
if err := ensureServerFlag(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -187,37 +187,20 @@ var AppNewCommand = &cobra.Command{
|
|||||||
newAppServer = "local"
|
newAppServer = "local"
|
||||||
}
|
}
|
||||||
|
|
||||||
table, err := formatter.CreateTable()
|
log.Infof("%s created successfully (version: %s, chaos: %s)", appDomain, recipeVersion, chaosVersion)
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
headers := []string{"SERVER", "DOMAIN", "RECIPE", "VERSION"}
|
|
||||||
table.Headers(headers...)
|
|
||||||
|
|
||||||
table.Row(newAppServer, appDomain, recipe.Name, recipeVersion)
|
|
||||||
|
|
||||||
log.Infof("new app '%s' created 🌞", recipe.Name)
|
|
||||||
|
|
||||||
fmt.Println("")
|
|
||||||
fmt.Println(table)
|
|
||||||
fmt.Println("")
|
|
||||||
|
|
||||||
fmt.Println("Configure this app:")
|
|
||||||
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", appDomain))
|
|
||||||
|
|
||||||
if len(appSecrets) > 0 {
|
if len(appSecrets) > 0 {
|
||||||
fmt.Println("")
|
rows := [][]string{}
|
||||||
fmt.Println("Generated secrets:")
|
for k, v := range appSecrets {
|
||||||
fmt.Println("")
|
rows = append(rows, []string{k, v})
|
||||||
fmt.Println(secretsTable)
|
}
|
||||||
|
|
||||||
|
overview := formatter.CreateOverview("SECRETS OVERVIEW", rows)
|
||||||
|
|
||||||
|
fmt.Println(overview)
|
||||||
|
|
||||||
log.Warnf(
|
log.Warnf(
|
||||||
"generated secrets %s shown again, please take note of them %s",
|
"secrets are %s shown again, please save them %s",
|
||||||
formatter.BoldStyle.Render("NOT"),
|
formatter.BoldStyle.Render("NOT"),
|
||||||
formatter.BoldStyle.Render("NOW"),
|
formatter.BoldStyle.Render("NOW"),
|
||||||
)
|
)
|
||||||
|
@ -128,24 +128,35 @@ func showPSOutput(app appPkg.App, cl *dockerClient.Client, deployedVersion, chao
|
|||||||
|
|
||||||
allContainerStats[containerStats["service"]] = containerStats
|
allContainerStats[containerStats["service"]] = containerStats
|
||||||
|
|
||||||
|
// NOTE(d1): don't clobber these variables for --machine output
|
||||||
|
dVersion := deployedVersion
|
||||||
|
cVersion := chaosVersion
|
||||||
|
|
||||||
|
if containerStats["service"] != "app" {
|
||||||
|
// NOTE(d1): don't repeat info which only relevant for the "app" service
|
||||||
|
dVersion = ""
|
||||||
|
cVersion = ""
|
||||||
|
}
|
||||||
|
|
||||||
row := []string{
|
row := []string{
|
||||||
containerStats["service"],
|
containerStats["service"],
|
||||||
containerStats["image"],
|
containerStats["image"],
|
||||||
containerStats["created"],
|
dVersion,
|
||||||
|
cVersion,
|
||||||
containerStats["status"],
|
containerStats["status"],
|
||||||
containerStats["state"],
|
|
||||||
containerStats["ports"],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rows = append(rows, row)
|
rows = append(rows, row)
|
||||||
}
|
}
|
||||||
|
|
||||||
if internal.MachineReadable {
|
if internal.MachineReadable {
|
||||||
jsonstring, err := json.Marshal(allContainerStats)
|
rendered, err := json.Marshal(allContainerStats)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("unable to convert to JSON: %s", err)
|
log.Fatal("unable to convert to JSON: %s", err)
|
||||||
}
|
}
|
||||||
fmt.Println(string(jsonstring))
|
|
||||||
|
fmt.Println(string(rendered))
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,19 +168,18 @@ func showPSOutput(app appPkg.App, cl *dockerClient.Client, deployedVersion, chao
|
|||||||
headers := []string{
|
headers := []string{
|
||||||
"SERVICE",
|
"SERVICE",
|
||||||
"IMAGE",
|
"IMAGE",
|
||||||
"CREATED",
|
"VERSION",
|
||||||
|
"CHAOS",
|
||||||
"STATUS",
|
"STATUS",
|
||||||
"STATE",
|
|
||||||
"PORTS",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
table.
|
table.
|
||||||
Headers(headers...).
|
Headers(headers...).
|
||||||
Rows(rows...)
|
Rows(rows...)
|
||||||
|
|
||||||
fmt.Println(table)
|
if err := formatter.PrintTable(table); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
log.Infof("VERSION: %s CHAOS: %s", deployedVersion, chaosVersion)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -46,8 +46,8 @@ beforehand.`,
|
|||||||
case 1:
|
case 1:
|
||||||
app, err := appPkg.Get(args[0])
|
app, err := appPkg.Get(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("autocomplete failed: %s", err)
|
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
|
||||||
return nil, cobra.ShellCompDirectiveDefault
|
return []string{errMsg}, cobra.ShellCompDirectiveError
|
||||||
}
|
}
|
||||||
return autocomplete.RecipeVersionComplete(app.Recipe.Name)
|
return autocomplete.RecipeVersionComplete(app.Recipe.Name)
|
||||||
default:
|
default:
|
||||||
@ -167,7 +167,7 @@ beforehand.`,
|
|||||||
|
|
||||||
prompt := &survey.Select{
|
prompt := &survey.Select{
|
||||||
Message: msg,
|
Message: msg,
|
||||||
Options: internal.ReverseStringList(availableDowngrades),
|
Options: internal.SortVersionsDesc(availableDowngrades),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := survey.AskOne(prompt, &chosenDowngrade); err != nil {
|
if err := survey.AskOne(prompt, &chosenDowngrade); err != nil {
|
||||||
|
@ -34,8 +34,8 @@ var AppSecretGenerateCommand = &cobra.Command{
|
|||||||
case 1:
|
case 1:
|
||||||
app, err := appPkg.Get(args[0])
|
app, err := appPkg.Get(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("autocomplete failed: %s", err)
|
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
|
||||||
return nil, cobra.ShellCompDirectiveDefault
|
return []string{errMsg}, cobra.ShellCompDirectiveError
|
||||||
}
|
}
|
||||||
return autocomplete.SecretComplete(app.Recipe.Name)
|
return autocomplete.SecretComplete(app.Recipe.Name)
|
||||||
default:
|
default:
|
||||||
@ -127,7 +127,9 @@ var AppSecretGenerateCommand = &cobra.Command{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(table)
|
if err := formatter.PrintTable(table); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
log.Warnf(
|
log.Warnf(
|
||||||
"generated secrets %s shown again, please take note of them %s",
|
"generated secrets %s shown again, please take note of them %s",
|
||||||
@ -157,8 +159,8 @@ environment. Typically, you can let Abra generate them for you on app creation
|
|||||||
case 1:
|
case 1:
|
||||||
app, err := appPkg.Get(args[0])
|
app, err := appPkg.Get(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("autocomplete failed: %s", err)
|
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
|
||||||
return nil, cobra.ShellCompDirectiveDefault
|
return []string{errMsg}, cobra.ShellCompDirectiveError
|
||||||
}
|
}
|
||||||
return autocomplete.SecretComplete(app.Recipe.Name)
|
return autocomplete.SecretComplete(app.Recipe.Name)
|
||||||
default:
|
default:
|
||||||
@ -243,8 +245,8 @@ var AppSecretRmCommand = &cobra.Command{
|
|||||||
if !rmAllSecrets {
|
if !rmAllSecrets {
|
||||||
app, err := appPkg.Get(args[0])
|
app, err := appPkg.Get(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("autocomplete failed: %s", err)
|
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
|
||||||
return nil, cobra.ShellCompDirectiveDefault
|
return []string{errMsg}, cobra.ShellCompDirectiveError
|
||||||
}
|
}
|
||||||
return autocomplete.SecretComplete(app.Recipe.Name)
|
return autocomplete.SecretComplete(app.Recipe.Name)
|
||||||
}
|
}
|
||||||
@ -394,7 +396,10 @@ var AppSecretLsCommand = &cobra.Command{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(table)
|
if err := formatter.PrintTable(table); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ var AppServicesCommand = &cobra.Command{
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
headers := []string{"SERVICE (SHORT)", "SERVICE (LONG)", "IMAGE"}
|
headers := []string{"SERVICE (SHORT)", "SERVICE (LONG)"}
|
||||||
table.Headers(headers...)
|
table.Headers(headers...)
|
||||||
|
|
||||||
var rows [][]string
|
var rows [][]string
|
||||||
@ -80,7 +80,6 @@ var AppServicesCommand = &cobra.Command{
|
|||||||
row := []string{
|
row := []string{
|
||||||
serviceShortName,
|
serviceShortName,
|
||||||
serviceLongName,
|
serviceLongName,
|
||||||
formatter.RemoveSha(container.Image),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rows = append(rows, row)
|
rows = append(rows, row)
|
||||||
@ -89,7 +88,9 @@ var AppServicesCommand = &cobra.Command{
|
|||||||
table.Rows(rows...)
|
table.Rows(rows...)
|
||||||
|
|
||||||
if len(rows) > 0 {
|
if len(rows) > 0 {
|
||||||
fmt.Println(table)
|
if err := formatter.PrintTable(table); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ var AppUndeployCommand = &cobra.Command{
|
|||||||
Use: "undeploy <app> [flags]",
|
Use: "undeploy <app> [flags]",
|
||||||
Aliases: []string{"un"},
|
Aliases: []string{"un"},
|
||||||
Short: "Undeploy an app",
|
Short: "Undeploy an app",
|
||||||
Long: `This does not destroy any of the application data.
|
Long: `This does not destroy any application data.
|
||||||
|
|
||||||
However, you should remain vigilant, as your swarm installation will consider
|
However, you should remain vigilant, as your swarm installation will consider
|
||||||
any previously attached volumes as eligible for pruning once undeployed.
|
any previously attached volumes as eligible for pruning once undeployed.
|
||||||
@ -59,7 +59,10 @@ Passing "--prune/-p" does not remove those volumes.`,
|
|||||||
chaosVersion = deployMeta.ChaosVersion
|
chaosVersion = deployMeta.ChaosVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := internal.DeployOverview(app, []string{}, deployMeta.Version, chaosVersion); err != nil {
|
if err := internal.UndeployOverview(
|
||||||
|
app,
|
||||||
|
deployMeta.Version,
|
||||||
|
chaosVersion); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,6 +79,11 @@ Passing "--prune/-p" does not remove those volumes.`,
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debugf("choosing %s as version to save to env file", deployMeta.Version)
|
||||||
|
if err := app.WriteRecipeVersion(deployMeta.Version, false); err != nil {
|
||||||
|
log.Fatalf("writing undeployed recipe version in env file: %s", err)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,8 +40,8 @@ beforehand.`,
|
|||||||
case 1:
|
case 1:
|
||||||
app, err := appPkg.Get(args[0])
|
app, err := appPkg.Get(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("autocomplete failed: %s", err)
|
errMsg := fmt.Sprintf("autocomplete failed: %s", err)
|
||||||
return nil, cobra.ShellCompDirectiveDefault
|
return []string{errMsg}, cobra.ShellCompDirectiveError
|
||||||
}
|
}
|
||||||
return autocomplete.RecipeVersionComplete(app.Recipe.Name)
|
return autocomplete.RecipeVersionComplete(app.Recipe.Name)
|
||||||
default:
|
default:
|
||||||
@ -159,7 +159,7 @@ beforehand.`,
|
|||||||
|
|
||||||
prompt := &survey.Select{
|
prompt := &survey.Select{
|
||||||
Message: msg,
|
Message: msg,
|
||||||
Options: internal.ReverseStringList(availableUpgrades),
|
Options: internal.SortVersionsDesc(availableUpgrades),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := survey.AskOne(prompt, &chosenUpgrade); err != nil {
|
if err := survey.AskOne(prompt, &chosenUpgrade); err != nil {
|
||||||
@ -250,7 +250,6 @@ beforehand.`,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if showReleaseNotes {
|
if showReleaseNotes {
|
||||||
fmt.Println()
|
|
||||||
fmt.Print(releaseNotes)
|
fmt.Print(releaseNotes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
@ -43,7 +42,7 @@ var AppVolumeListCommand = &cobra.Command{
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
headers := []string{"name", "created", "mounted"}
|
headers := []string{"NAME", "ON SERVER"}
|
||||||
|
|
||||||
table, err := formatter.CreateTable()
|
table, err := formatter.CreateTable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -54,14 +53,16 @@ var AppVolumeListCommand = &cobra.Command{
|
|||||||
|
|
||||||
var rows [][]string
|
var rows [][]string
|
||||||
for _, volume := range volumes {
|
for _, volume := range volumes {
|
||||||
row := []string{volume.Name, volume.CreatedAt, volume.Mountpoint}
|
row := []string{volume.Name, volume.Mountpoint}
|
||||||
rows = append(rows, row)
|
rows = append(rows, row)
|
||||||
}
|
}
|
||||||
|
|
||||||
table.Rows(rows...)
|
table.Rows(rows...)
|
||||||
|
|
||||||
if len(rows) > 0 {
|
if len(rows) > 0 {
|
||||||
fmt.Println(table)
|
if err := formatter.PrintTable(table); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,10 +3,14 @@ package internal
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
appPkg "coopcloud.tech/abra/pkg/app"
|
appPkg "coopcloud.tech/abra/pkg/app"
|
||||||
|
"coopcloud.tech/abra/pkg/config"
|
||||||
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
"coopcloud.tech/abra/pkg/log"
|
"coopcloud.tech/abra/pkg/log"
|
||||||
|
"coopcloud.tech/tagcmp"
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
dockerClient "github.com/docker/docker/client"
|
dockerClient "github.com/docker/docker/client"
|
||||||
@ -20,7 +24,8 @@ var borderStyle = lipgloss.NewStyle().
|
|||||||
|
|
||||||
var headerStyle = lipgloss.NewStyle().
|
var headerStyle = lipgloss.NewStyle().
|
||||||
Underline(true).
|
Underline(true).
|
||||||
Bold(true)
|
Bold(true).
|
||||||
|
PaddingBottom(1)
|
||||||
|
|
||||||
var leftStyle = lipgloss.NewStyle().
|
var leftStyle = lipgloss.NewStyle().
|
||||||
Bold(true)
|
Bold(true)
|
||||||
@ -37,13 +42,13 @@ func NewVersionOverview(
|
|||||||
app appPkg.App,
|
app appPkg.App,
|
||||||
warnMessages []string,
|
warnMessages []string,
|
||||||
kind,
|
kind,
|
||||||
currentVersion,
|
deployedVersion,
|
||||||
chaosVersion,
|
deployedChaosVersion,
|
||||||
newVersion,
|
toDeployVersion,
|
||||||
releaseNotes string) error {
|
releaseNotes string) error {
|
||||||
deployConfig := "compose.yml"
|
deployConfig := "compose.yml"
|
||||||
if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
|
if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
|
||||||
deployConfig = strings.Join(strings.Split(composeFiles, ":"), "\n")
|
deployConfig = composeFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
server := app.Server
|
server := app.Server
|
||||||
@ -51,32 +56,35 @@ func NewVersionOverview(
|
|||||||
server = "local"
|
server = "local"
|
||||||
}
|
}
|
||||||
|
|
||||||
body := strings.Builder{}
|
domain := app.Domain
|
||||||
body.WriteString(
|
if domain == "" {
|
||||||
borderStyle.Render(
|
domain = config.NO_DOMAIN_DEFAULT
|
||||||
lipgloss.JoinVertical(
|
}
|
||||||
lipgloss.Center,
|
|
||||||
headerStyle.Render(fmt.Sprintf("%s OVERVIEW", strings.ToUpper(kind))),
|
|
||||||
lipgloss.JoinVertical(
|
|
||||||
lipgloss.Left,
|
|
||||||
horizontal(leftStyle.Render("SERVER"), " ", rightStyle.Render(server)),
|
|
||||||
horizontal(leftStyle.Render("DOMAIN"), " ", rightStyle.Render(app.Domain)),
|
|
||||||
horizontal(leftStyle.Render("RECIPE"), " ", rightStyle.Render(app.Recipe.Name)),
|
|
||||||
horizontal(leftStyle.Render("CONFIG"), " ", rightStyle.Render(deployConfig)),
|
|
||||||
horizontal(leftStyle.Render("VERSION"), " ", rightStyle.Render(currentVersion)),
|
|
||||||
horizontal(leftStyle.Render("CHAOS"), " ", rightStyle.Render(chaosVersion)),
|
|
||||||
horizontal(leftStyle.Render("DEPLOY"), " ", rightStyle.Padding(0).Render(newVersion)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
fmt.Println(body.String())
|
|
||||||
|
|
||||||
if releaseNotes != "" && newVersion != "" {
|
rows := [][]string{
|
||||||
fmt.Println()
|
[]string{"APP", domain},
|
||||||
|
[]string{"RECIPE", app.Recipe.Name},
|
||||||
|
[]string{"SERVER", server},
|
||||||
|
[]string{"DEPLOYED", deployedVersion},
|
||||||
|
[]string{"CURRENT CHAOS ", deployedChaosVersion},
|
||||||
|
[]string{fmt.Sprintf("TO %s", strings.ToUpper(kind)), toDeployVersion},
|
||||||
|
[]string{"CONFIG", deployConfig},
|
||||||
|
}
|
||||||
|
|
||||||
|
overview := formatter.CreateOverview(
|
||||||
|
fmt.Sprintf("%s OVERVIEW", strings.ToUpper(kind)),
|
||||||
|
rows,
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(overview)
|
||||||
|
|
||||||
|
if releaseNotes != "" && toDeployVersion != "" {
|
||||||
fmt.Print(releaseNotes)
|
fmt.Print(releaseNotes)
|
||||||
} else {
|
} else {
|
||||||
warnMessages = append(warnMessages, fmt.Sprintf("no release notes available for %s", newVersion))
|
warnMessages = append(
|
||||||
|
warnMessages,
|
||||||
|
fmt.Sprintf("no release notes available for %s", toDeployVersion),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, msg := range warnMessages {
|
for _, msg := range warnMessages {
|
||||||
@ -101,10 +109,16 @@ func NewVersionOverview(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeployOverview shows a deployment overview
|
// DeployOverview shows a deployment overview
|
||||||
func DeployOverview(app appPkg.App, warnMessages []string, version, chaosVersion string) error {
|
func DeployOverview(
|
||||||
|
app appPkg.App,
|
||||||
|
warnMessages []string,
|
||||||
|
deployedVersion string,
|
||||||
|
deployedChaosVersion string,
|
||||||
|
toDeployVersion,
|
||||||
|
toDeployChaosVersion string) error {
|
||||||
deployConfig := "compose.yml"
|
deployConfig := "compose.yml"
|
||||||
if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
|
if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
|
||||||
deployConfig = strings.Join(strings.Split(composeFiles, ":"), "\n")
|
deployConfig = composeFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
server := app.Server
|
server := app.Server
|
||||||
@ -112,25 +126,25 @@ func DeployOverview(app appPkg.App, warnMessages []string, version, chaosVersion
|
|||||||
server = "local"
|
server = "local"
|
||||||
}
|
}
|
||||||
|
|
||||||
body := strings.Builder{}
|
domain := app.Domain
|
||||||
body.WriteString(
|
if domain == "" {
|
||||||
borderStyle.Render(
|
domain = config.NO_DOMAIN_DEFAULT
|
||||||
lipgloss.JoinVertical(
|
}
|
||||||
lipgloss.Center,
|
|
||||||
headerStyle.Render("DEPLOY OVERVIEW"),
|
rows := [][]string{
|
||||||
lipgloss.JoinVertical(
|
[]string{"APP", domain},
|
||||||
lipgloss.Left,
|
[]string{"RECIPE", app.Recipe.Name},
|
||||||
horizontal(leftStyle.Render("SERVER"), " ", rightStyle.Render(server)),
|
[]string{"SERVER", server},
|
||||||
horizontal(leftStyle.Render("DOMAIN"), " ", rightStyle.Render(app.Domain)),
|
[]string{"DEPLOYED", deployedVersion},
|
||||||
horizontal(leftStyle.Render("RECIPE"), " ", rightStyle.Render(app.Recipe.Name)),
|
[]string{"CURRENT CHAOS ", deployedChaosVersion},
|
||||||
horizontal(leftStyle.Render("CONFIG"), " ", rightStyle.Render(deployConfig)),
|
[]string{"TO DEPLOY", toDeployVersion},
|
||||||
horizontal(leftStyle.Render("VERSION"), " ", rightStyle.Render(version)),
|
[]string{"NEW CHAOS", toDeployChaosVersion},
|
||||||
horizontal(leftStyle.Render("CHAOS"), " ", rightStyle.Padding(0).Render(chaosVersion)),
|
[]string{"CONFIG", deployConfig},
|
||||||
),
|
}
|
||||||
),
|
|
||||||
),
|
overview := formatter.CreateOverview("DEPLOY OVERVIEW", rows)
|
||||||
)
|
|
||||||
fmt.Println(body.String())
|
fmt.Println(overview)
|
||||||
|
|
||||||
for _, msg := range warnMessages {
|
for _, msg := range warnMessages {
|
||||||
log.Warn(msg)
|
log.Warn(msg)
|
||||||
@ -153,6 +167,56 @@ func DeployOverview(app appPkg.App, warnMessages []string, version, chaosVersion
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UndeployOverview shows an undeployment overview
|
||||||
|
func UndeployOverview(
|
||||||
|
app appPkg.App,
|
||||||
|
version,
|
||||||
|
chaosVersion string) error {
|
||||||
|
deployConfig := "compose.yml"
|
||||||
|
if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
|
||||||
|
deployConfig = composeFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
server := app.Server
|
||||||
|
if app.Server == "default" {
|
||||||
|
server = "local"
|
||||||
|
}
|
||||||
|
|
||||||
|
domain := app.Domain
|
||||||
|
if domain == "" {
|
||||||
|
domain = config.NO_DOMAIN_DEFAULT
|
||||||
|
}
|
||||||
|
|
||||||
|
rows := [][]string{
|
||||||
|
[]string{"APP", domain},
|
||||||
|
[]string{"RECIPE", app.Recipe.Name},
|
||||||
|
[]string{"SERVER", server},
|
||||||
|
[]string{"DEPLOYED", version},
|
||||||
|
[]string{"CHAOS", chaosVersion},
|
||||||
|
[]string{"CONFIG", deployConfig},
|
||||||
|
}
|
||||||
|
|
||||||
|
overview := formatter.CreateOverview("UNDEPLOY OVERVIEW", rows)
|
||||||
|
|
||||||
|
fmt.Println(overview)
|
||||||
|
|
||||||
|
if NoInput {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
response := false
|
||||||
|
prompt := &survey.Confirm{Message: "proceed?"}
|
||||||
|
if err := survey.AskOne(prompt, &response); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !response {
|
||||||
|
log.Fatal("undeploy cancelled")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// PostCmds parses a string of commands and executes them inside of the respective services
|
// PostCmds parses a string of commands and executes them inside of the respective services
|
||||||
// the commands string must have the following format:
|
// the commands string must have the following format:
|
||||||
// "<service> <command> <arguments>|<service> <command> <arguments>|... "
|
// "<service> <command> <arguments>|<service> <command> <arguments>|... "
|
||||||
@ -210,3 +274,22 @@ func PostCmds(cl *dockerClient.Client, app appPkg.App, commands string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SortVersionsDesc sorts versions in descending order.
|
||||||
|
func SortVersionsDesc(versions []string) []string {
|
||||||
|
var tags []tagcmp.Tag
|
||||||
|
|
||||||
|
for _, v := range versions {
|
||||||
|
parsed, _ := tagcmp.Parse(v) // skips unsupported tags
|
||||||
|
tags = append(tags, parsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(tagcmp.ByTagDesc(tags))
|
||||||
|
|
||||||
|
var desc []string
|
||||||
|
for _, t := range tags {
|
||||||
|
desc = append(desc, t.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc
|
||||||
|
}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
package internal
|
|
||||||
|
|
||||||
// ReverseStringList reverses a list of a strings. Roll on Go generics.
|
|
||||||
func ReverseStringList(strings []string) []string {
|
|
||||||
for i, j := 0, len(strings)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
strings[i], strings[j] = strings[j], strings[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings
|
|
||||||
}
|
|
@ -10,10 +10,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var RecipeFetchCommand = &cobra.Command{
|
var RecipeFetchCommand = &cobra.Command{
|
||||||
Use: "fetch [recipe] [flags]",
|
Use: "fetch [recipe | --all] [flags]",
|
||||||
Aliases: []string{"f"},
|
Aliases: []string{"f"},
|
||||||
Short: "Fetch recipe(s)",
|
Short: "Clone recipe(s) locally",
|
||||||
Long: "Retrieves all recipes if no [recipe] argument is passed.",
|
|
||||||
Args: cobra.RangeArgs(0, 1),
|
Args: cobra.RangeArgs(0, 1),
|
||||||
ValidArgsFunction: func(
|
ValidArgsFunction: func(
|
||||||
cmd *cobra.Command,
|
cmd *cobra.Command,
|
||||||
@ -27,6 +26,14 @@ var RecipeFetchCommand = &cobra.Command{
|
|||||||
recipeName = args[0]
|
recipeName = args[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if recipeName == "" && !fetchAllRecipes {
|
||||||
|
log.Fatal("missing [recipe] or --all/-a")
|
||||||
|
}
|
||||||
|
|
||||||
|
if recipeName != "" && fetchAllRecipes {
|
||||||
|
log.Fatal("cannot use [recipe] and --all/-a together")
|
||||||
|
}
|
||||||
|
|
||||||
if recipeName != "" {
|
if recipeName != "" {
|
||||||
r := internal.ValidateRecipe(args, cmd.Name())
|
r := internal.ValidateRecipe(args, cmd.Name())
|
||||||
if err := r.Ensure(false, false); err != nil {
|
if err := r.Ensure(false, false); err != nil {
|
||||||
@ -50,3 +57,17 @@ var RecipeFetchCommand = &cobra.Command{
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
fetchAllRecipes bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RecipeFetchCommand.Flags().BoolVarP(
|
||||||
|
&fetchAllRecipes,
|
||||||
|
"all",
|
||||||
|
"a",
|
||||||
|
false,
|
||||||
|
"fetch all recipes",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package recipe
|
package recipe
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/pkg/autocomplete"
|
"coopcloud.tech/abra/pkg/autocomplete"
|
||||||
"coopcloud.tech/abra/pkg/formatter"
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
@ -104,7 +102,9 @@ var RecipeLintCommand = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(rows) > 0 {
|
if len(rows) > 0 {
|
||||||
fmt.Println(table)
|
if err := formatter.PrintTable(table); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
for _, warnMsg := range warnMessages {
|
for _, warnMsg := range warnMessages {
|
||||||
log.Warn(warnMsg)
|
log.Warn(warnMsg)
|
||||||
|
@ -79,8 +79,9 @@ var RecipeListCommand = &cobra.Command{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(table)
|
if err := formatter.PrintTable(table); err != nil {
|
||||||
log.Infof("total recipes: %v", len(rows))
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -252,7 +252,14 @@ func getTagCreateOptions(tag string) (git.CreateTagOptions, error) {
|
|||||||
// addReleaseNotes checks if the release/next release note exists and moves the
|
// addReleaseNotes checks if the release/next release note exists and moves the
|
||||||
// file to release/<tag>.
|
// file to release/<tag>.
|
||||||
func addReleaseNotes(recipe recipe.Recipe, tag string) error {
|
func addReleaseNotes(recipe recipe.Recipe, tag string) error {
|
||||||
tagReleaseNotePath := path.Join(recipe.Dir, "release", tag)
|
releaseDir := path.Join(recipe.Dir, "release")
|
||||||
|
if _, err := os.Stat(releaseDir); errors.Is(err, os.ErrNotExist) {
|
||||||
|
if err := os.Mkdir(releaseDir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tagReleaseNotePath := path.Join(releaseDir, tag)
|
||||||
if _, err := os.Stat(tagReleaseNotePath); err == nil {
|
if _, err := os.Stat(tagReleaseNotePath); err == nil {
|
||||||
// Release note for current tag already exist exists.
|
// Release note for current tag already exist exists.
|
||||||
return nil
|
return nil
|
||||||
@ -260,7 +267,7 @@ func addReleaseNotes(recipe recipe.Recipe, tag string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
nextReleaseNotePath := path.Join(recipe.Dir, "release", "next")
|
nextReleaseNotePath := path.Join(releaseDir, "next")
|
||||||
if _, err := os.Stat(nextReleaseNotePath); err == nil {
|
if _, err := os.Stat(nextReleaseNotePath); err == nil {
|
||||||
// release/next note exists. Move it to release/<tag>
|
// release/next note exists. Move it to release/<tag>
|
||||||
if internal.Dry {
|
if internal.Dry {
|
||||||
|
@ -55,15 +55,32 @@ var RecipeVersionCommand = &cobra.Command{
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
table.Headers("SERVICE", "NAME", "TAG")
|
table.Headers("SERVICE", "IMAGE", "TAG", "VERSION")
|
||||||
|
|
||||||
for version, meta := range recipeMeta.Versions[i] {
|
for version, meta := range recipeMeta.Versions[i] {
|
||||||
var allRows [][]string
|
var allRows [][]string
|
||||||
var rows [][]string
|
var rows [][]string
|
||||||
|
|
||||||
for service, serviceMeta := range meta {
|
for service, serviceMeta := range meta {
|
||||||
rows = append(rows, []string{service, serviceMeta.Image, serviceMeta.Tag})
|
recipeVersion := version
|
||||||
allRows = append(allRows, []string{version, service, serviceMeta.Image, serviceMeta.Tag})
|
if service != "app" {
|
||||||
|
recipeVersion = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
rows = append(rows, []string{
|
||||||
|
service,
|
||||||
|
serviceMeta.Image,
|
||||||
|
serviceMeta.Tag,
|
||||||
|
recipeVersion,
|
||||||
|
})
|
||||||
|
|
||||||
|
allRows = append(allRows, []string{
|
||||||
|
version,
|
||||||
|
service,
|
||||||
|
serviceMeta.Image,
|
||||||
|
serviceMeta.Tag,
|
||||||
|
recipeVersion,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(rows, sortServiceByName(rows))
|
sort.Slice(rows, sortServiceByName(rows))
|
||||||
@ -71,9 +88,9 @@ var RecipeVersionCommand = &cobra.Command{
|
|||||||
table.Rows(rows...)
|
table.Rows(rows...)
|
||||||
|
|
||||||
if !internal.MachineReadable {
|
if !internal.MachineReadable {
|
||||||
fmt.Println(table)
|
if err := formatter.PrintTable(table); err != nil {
|
||||||
log.Infof("VERSION: %s", version)
|
log.Fatal(err)
|
||||||
fmt.Println()
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,11 +117,7 @@ var RecipeVersionCommand = &cobra.Command{
|
|||||||
|
|
||||||
func sortServiceByName(versions [][]string) func(i, j int) bool {
|
func sortServiceByName(versions [][]string) func(i, j int) bool {
|
||||||
return func(i, j int) bool {
|
return func(i, j int) bool {
|
||||||
// NOTE(d1): corresponds to the `tableCol` definition below
|
return versions[i][0] < versions[j][0]
|
||||||
if versions[i][1] == "app" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return versions[i][1] < versions[j][1]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,10 +88,6 @@ developer machine. The domain is then set to "default".`,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := dns.EnsureIPv4(name); err != nil {
|
|
||||||
log.Warn(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := createServerDir(name)
|
_, err := createServerDir(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@ -100,21 +96,28 @@ developer machine. The domain is then set to "default".`,
|
|||||||
created, err := newContext(name)
|
created, err := newContext(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cleanUp(name)
|
cleanUp(name)
|
||||||
log.Fatal(err)
|
log.Fatalf("unable to create local context: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("attempting to create client for %s", name)
|
log.Debugf("attempting to create client for %s", name)
|
||||||
|
|
||||||
if _, err := client.New(name, timeout); err != nil {
|
if _, err := client.New(name, timeout); err != nil {
|
||||||
cleanUp(name)
|
cleanUp(name)
|
||||||
log.Fatal(sshPkg.Fatal(name, err))
|
log.Debugf("ssh %s error: %s", name, sshPkg.Fatal(name, err))
|
||||||
|
log.Fatalf("can't ssh to %s, make sure \"ssh %s\" works", name, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if created {
|
if created {
|
||||||
log.Infof("%s successfully added", name)
|
log.Infof("%s successfully added", name)
|
||||||
} else {
|
|
||||||
log.Warnf("%s already exists", name)
|
if _, err := dns.EnsureIPv4(name); err != nil {
|
||||||
|
log.Warnf("unable to resolve IPv4 for %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Warnf("%s already exists", name)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,9 @@ var ServerListCommand = &cobra.Command{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(table)
|
if err := formatter.PrintTable(table); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -634,7 +634,7 @@ func (a App) WriteRecipeVersion(version string, dryRun bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !skipped {
|
if !skipped {
|
||||||
log.Infof("version %s saved to %s.env", version, a.Domain)
|
log.Debugf("version %s saved to %s.env", version, a.Domain)
|
||||||
} else {
|
} else {
|
||||||
log.Debugf("skipping version %s write as already exists in %s.env", version, a.Domain)
|
log.Debugf("skipping version %s write as already exists in %s.env", version, a.Domain)
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,26 @@
|
|||||||
package autocomplete
|
package autocomplete
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"coopcloud.tech/abra/pkg/app"
|
"coopcloud.tech/abra/pkg/app"
|
||||||
appPkg "coopcloud.tech/abra/pkg/app"
|
appPkg "coopcloud.tech/abra/pkg/app"
|
||||||
"coopcloud.tech/abra/pkg/log"
|
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AppNameComplete copletes app names.
|
// AppNameComplete copletes app names.
|
||||||
func AppNameComplete() ([]string, cobra.ShellCompDirective) {
|
func AppNameComplete() ([]string, cobra.ShellCompDirective) {
|
||||||
appNames, err := app.GetAppNames()
|
appFiles, err := app.LoadAppFiles("")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("autocomplete failed: %s", err)
|
err := fmt.Sprintf("autocomplete failed: %s", err)
|
||||||
return nil, cobra.ShellCompDirectiveError
|
return []string{err}, cobra.ShellCompDirectiveError
|
||||||
|
}
|
||||||
|
|
||||||
|
var appNames []string
|
||||||
|
for appName := range appFiles {
|
||||||
|
appNames = append(appNames, appName)
|
||||||
}
|
}
|
||||||
|
|
||||||
return appNames, cobra.ShellCompDirectiveDefault
|
return appNames, cobra.ShellCompDirectiveDefault
|
||||||
@ -24,8 +29,8 @@ func AppNameComplete() ([]string, cobra.ShellCompDirective) {
|
|||||||
func ServiceNameComplete(appName string) ([]string, cobra.ShellCompDirective) {
|
func ServiceNameComplete(appName string) ([]string, cobra.ShellCompDirective) {
|
||||||
serviceNames, err := app.GetAppServiceNames(appName)
|
serviceNames, err := app.GetAppServiceNames(appName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("autocomplete failed: %s", err)
|
err := fmt.Sprintf("autocomplete failed: %s", err)
|
||||||
return nil, cobra.ShellCompDirectiveError
|
return []string{err}, cobra.ShellCompDirectiveError
|
||||||
}
|
}
|
||||||
|
|
||||||
return serviceNames, cobra.ShellCompDirectiveDefault
|
return serviceNames, cobra.ShellCompDirectiveDefault
|
||||||
@ -35,8 +40,8 @@ func ServiceNameComplete(appName string) ([]string, cobra.ShellCompDirective) {
|
|||||||
func RecipeNameComplete() ([]string, cobra.ShellCompDirective) {
|
func RecipeNameComplete() ([]string, cobra.ShellCompDirective) {
|
||||||
catl, err := recipe.ReadRecipeCatalogue(false)
|
catl, err := recipe.ReadRecipeCatalogue(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("autocomplete failed: %s", err)
|
err := fmt.Sprintf("autocomplete failed: %s", err)
|
||||||
return nil, cobra.ShellCompDirectiveError
|
return []string{err}, cobra.ShellCompDirectiveError
|
||||||
}
|
}
|
||||||
|
|
||||||
var recipeNames []string
|
var recipeNames []string
|
||||||
@ -51,8 +56,8 @@ func RecipeNameComplete() ([]string, cobra.ShellCompDirective) {
|
|||||||
func RecipeVersionComplete(recipeName string) ([]string, cobra.ShellCompDirective) {
|
func RecipeVersionComplete(recipeName string) ([]string, cobra.ShellCompDirective) {
|
||||||
catl, err := recipe.ReadRecipeCatalogue(false)
|
catl, err := recipe.ReadRecipeCatalogue(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("autocomplete failed: %s", err)
|
err := fmt.Sprintf("autocomplete failed: %s", err)
|
||||||
return nil, cobra.ShellCompDirectiveError
|
return []string{err}, cobra.ShellCompDirectiveError
|
||||||
}
|
}
|
||||||
|
|
||||||
var recipeVersions []string
|
var recipeVersions []string
|
||||||
@ -69,8 +74,8 @@ func RecipeVersionComplete(recipeName string) ([]string, cobra.ShellCompDirectiv
|
|||||||
func ServerNameComplete() ([]string, cobra.ShellCompDirective) {
|
func ServerNameComplete() ([]string, cobra.ShellCompDirective) {
|
||||||
files, err := app.LoadAppFiles("")
|
files, err := app.LoadAppFiles("")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("autocomplete failed: %s", err)
|
err := fmt.Sprintf("autocomplete failed: %s", err)
|
||||||
return nil, cobra.ShellCompDirectiveError
|
return []string{err}, cobra.ShellCompDirectiveError
|
||||||
}
|
}
|
||||||
|
|
||||||
var serverNames []string
|
var serverNames []string
|
||||||
@ -85,14 +90,14 @@ func ServerNameComplete() ([]string, cobra.ShellCompDirective) {
|
|||||||
func CommandNameComplete(appName string) ([]string, cobra.ShellCompDirective) {
|
func CommandNameComplete(appName string) ([]string, cobra.ShellCompDirective) {
|
||||||
app, err := app.Get(appName)
|
app, err := app.Get(appName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("autocomplete failed: %s", err)
|
err := fmt.Sprintf("autocomplete failed: %s", err)
|
||||||
return nil, cobra.ShellCompDirectiveError
|
return []string{err}, cobra.ShellCompDirectiveError
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdNames, err := appPkg.ReadAbraShCmdNames(app.Recipe.AbraShPath)
|
cmdNames, err := appPkg.ReadAbraShCmdNames(app.Recipe.AbraShPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("autocomplete failed: %s", err)
|
err := fmt.Sprintf("autocomplete failed: %s", err)
|
||||||
return nil, cobra.ShellCompDirectiveError
|
return []string{err}, cobra.ShellCompDirectiveError
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Strings(cmdNames)
|
sort.Strings(cmdNames)
|
||||||
@ -106,8 +111,8 @@ func SecretComplete(recipeName string) ([]string, cobra.ShellCompDirective) {
|
|||||||
|
|
||||||
config, err := r.GetComposeConfig(nil)
|
config, err := r.GetComposeConfig(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("autocomplete failed: %s", err)
|
err := fmt.Sprintf("autocomplete failed: %s", err)
|
||||||
return nil, cobra.ShellCompDirectiveError
|
return []string{err}, cobra.ShellCompDirectiveError
|
||||||
}
|
}
|
||||||
|
|
||||||
var secretNames []string
|
var secretNames []string
|
||||||
|
@ -107,5 +107,13 @@ var (
|
|||||||
REPOS_BASE_URL = "https://git.coopcloud.tech/coop-cloud"
|
REPOS_BASE_URL = "https://git.coopcloud.tech/coop-cloud"
|
||||||
CATALOGUE_JSON_REPO_NAME = "recipes-catalogue-json"
|
CATALOGUE_JSON_REPO_NAME = "recipes-catalogue-json"
|
||||||
SSH_URL_TEMPLATE = "ssh://git@git.coopcloud.tech:2222/coop-cloud/%s.git"
|
SSH_URL_TEMPLATE = "ssh://git@git.coopcloud.tech:2222/coop-cloud/%s.git"
|
||||||
CHAOS_DEFAULT = "false"
|
|
||||||
|
// NOTE(d1): please note, this was done purely out of laziness on our part
|
||||||
|
// AFAICR. it's easy to punt the value into the label because that is what is
|
||||||
|
// expects. it's not particularly useful in terms of UI/UX but hey, nobody
|
||||||
|
// complained yet!
|
||||||
|
CHAOS_DEFAULT = "false"
|
||||||
|
|
||||||
|
NO_DOMAIN_DEFAULT = "N/A"
|
||||||
|
NO_VERSION_DEFAULT = "N/A"
|
||||||
)
|
)
|
||||||
|
@ -9,12 +9,11 @@ import (
|
|||||||
func EnsureIPv4(domainName string) (string, error) {
|
func EnsureIPv4(domainName string) (string, error) {
|
||||||
ipv4, err := net.ResolveIPAddr("ip4", domainName)
|
ipv4, err := net.ResolveIPAddr("ip4", domainName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to resolve ipv4 address for %s, %s", domainName, err)
|
return "", fmt.Errorf("%s: unable to resolve IPv4 address: %s", domainName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE(d1): e.g. when there is only an ipv6 record available
|
|
||||||
if ipv4 == nil {
|
if ipv4 == nil {
|
||||||
return "", fmt.Errorf("unable to resolve ipv4 address for %s", domainName)
|
return "", fmt.Errorf("%s: no IPv4 available", domainName)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ipv4.String(), nil
|
return ipv4.String(), nil
|
||||||
|
@ -43,33 +43,122 @@ func HumanDuration(timestamp int64) string {
|
|||||||
|
|
||||||
// CreateTable prepares a table layout for output.
|
// CreateTable prepares a table layout for output.
|
||||||
func CreateTable() (*table.Table, error) {
|
func CreateTable() (*table.Table, error) {
|
||||||
|
var (
|
||||||
|
renderer = lipgloss.NewRenderer(os.Stdout)
|
||||||
|
headerStyle = renderer.NewStyle().Bold(true).Align(lipgloss.Center)
|
||||||
|
cellStyle = renderer.NewStyle().Padding(0, 1)
|
||||||
|
borderStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("63"))
|
||||||
|
)
|
||||||
|
|
||||||
table := table.New().
|
table := table.New().
|
||||||
Border(lipgloss.ThickBorder()).
|
Border(lipgloss.ThickBorder()).
|
||||||
BorderStyle(
|
BorderStyle(borderStyle).
|
||||||
lipgloss.NewStyle().
|
StyleFunc(func(row, col int) lipgloss.Style {
|
||||||
Foreground(lipgloss.Color("63")),
|
var style lipgloss.Style
|
||||||
)
|
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case row == table.HeaderRow:
|
||||||
|
return headerStyle
|
||||||
|
default:
|
||||||
|
style = cellStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
return style
|
||||||
|
})
|
||||||
|
|
||||||
|
return table, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintTable(t *table.Table) error {
|
||||||
if isAbraCI, ok := os.LookupEnv("ABRA_CI"); ok && isAbraCI == "1" {
|
if isAbraCI, ok := os.LookupEnv("ABRA_CI"); ok && isAbraCI == "1" {
|
||||||
// NOTE(d1): no width limits for CI testing since we test against outputs
|
// NOTE(d1): no width limits for CI testing since we test against outputs
|
||||||
log.Debug("detected ABRA_CI=1")
|
log.Debug("detected ABRA_CI=1")
|
||||||
return table, nil
|
fmt.Println(t)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tWidth, _ := lipgloss.Size(t.String())
|
||||||
|
|
||||||
width, _, err := term.GetSize(0)
|
width, _, err := term.GetSize(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if width-10 < 79 {
|
if tWidth > width {
|
||||||
// NOTE(d1): maintain standard minimum width
|
t.Width(width - 10)
|
||||||
table.Width(79)
|
|
||||||
} else {
|
|
||||||
// NOTE(d1): tests show that this produces stable border drawing
|
|
||||||
table.Width(width - 10)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return table, nil
|
fmt.Println(t)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// horizontal is a JoinHorizontal helper function.
|
||||||
|
func horizontal(left, mid, right string) string {
|
||||||
|
return lipgloss.JoinHorizontal(lipgloss.Right, left, mid, right)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateOverview(header string, rows [][]string) string {
|
||||||
|
var borderStyle = lipgloss.NewStyle().
|
||||||
|
BorderStyle(lipgloss.ThickBorder()).
|
||||||
|
Padding(0, 1, 0, 1).
|
||||||
|
MaxWidth(79).
|
||||||
|
BorderForeground(lipgloss.Color("63"))
|
||||||
|
|
||||||
|
var headerStyle = lipgloss.NewStyle().
|
||||||
|
Underline(true).
|
||||||
|
Bold(true).
|
||||||
|
PaddingBottom(1)
|
||||||
|
|
||||||
|
var leftStyle = lipgloss.NewStyle().
|
||||||
|
Bold(true)
|
||||||
|
|
||||||
|
var rightStyle = lipgloss.NewStyle()
|
||||||
|
|
||||||
|
var longest int
|
||||||
|
for _, row := range rows {
|
||||||
|
if len(row[0]) > longest {
|
||||||
|
longest = len(row[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var renderedRows []string
|
||||||
|
for _, row := range rows {
|
||||||
|
if len(row) > 2 {
|
||||||
|
panic("CreateOverview: only accepts rows of len == 2")
|
||||||
|
}
|
||||||
|
|
||||||
|
lenOffset := 4
|
||||||
|
if len(row[0]) < longest {
|
||||||
|
lenOffset += longest - len(row[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := ""
|
||||||
|
for range lenOffset {
|
||||||
|
offset = offset + " "
|
||||||
|
}
|
||||||
|
|
||||||
|
renderedRows = append(
|
||||||
|
renderedRows,
|
||||||
|
horizontal(leftStyle.Render(row[0]), offset, rightStyle.Render(row[1])),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
body := strings.Builder{}
|
||||||
|
body.WriteString(
|
||||||
|
borderStyle.Render(
|
||||||
|
lipgloss.JoinVertical(
|
||||||
|
lipgloss.Center,
|
||||||
|
headerStyle.Render(header),
|
||||||
|
lipgloss.JoinVertical(
|
||||||
|
lipgloss.Left,
|
||||||
|
renderedRows...,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return body.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToJSON converts a lipgloss.Table to JSON representation. It's not a robust
|
// ToJSON converts a lipgloss.Table to JSON representation. It's not a robust
|
||||||
|
@ -137,6 +137,13 @@ var LintRules = map[string][]LintRule{
|
|||||||
HowToResolve: "name a servce 'app'",
|
HowToResolve: "name a servce 'app'",
|
||||||
Function: LintAppService,
|
Function: LintAppService,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Ref: "R015",
|
||||||
|
Level: "error",
|
||||||
|
Description: "deploy labels stanza present",
|
||||||
|
HowToResolve: "include \"deploy: labels: ...\" stanza",
|
||||||
|
Function: LintDeployLabelsPresent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Ref: "R010",
|
Ref: "R010",
|
||||||
Level: "error",
|
Level: "error",
|
||||||
@ -269,6 +276,21 @@ func LintTraefikEnabled(recipe recipe.Recipe) (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LintDeployLabelsPresent(recipe recipe.Recipe) (bool, error) {
|
||||||
|
config, err := recipe.GetComposeConfig(nil)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, service := range config.Services {
|
||||||
|
if service.Name == "app" && service.Deploy.Labels != nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
func LintHealthchecks(recipe recipe.Recipe) (bool, error) {
|
func LintHealthchecks(recipe recipe.Recipe) (bool, error) {
|
||||||
config, err := recipe.GetComposeConfig(nil)
|
config, err := recipe.GetComposeConfig(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
|
|
||||||
"coopcloud.tech/abra/pkg/envfile"
|
"coopcloud.tech/abra/pkg/envfile"
|
||||||
|
"coopcloud.tech/abra/pkg/formatter"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r Recipe) SampleEnv() (map[string]string, error) {
|
func (r Recipe) SampleEnv() (map[string]string, error) {
|
||||||
@ -29,7 +30,10 @@ func (r Recipe) GetReleaseNotes(version string) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
withTitle := fmt.Sprintf("%s release notes:\n%s", version, string(releaseNotes))
|
|
||||||
|
title := formatter.BoldStyle.Render(fmt.Sprintf("%s release notes:", version))
|
||||||
|
withTitle := fmt.Sprintf("%s\n%s\n", title, releaseNotes)
|
||||||
|
|
||||||
return withTitle, nil
|
return withTitle, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +247,7 @@ func (r Recipe) ChaosVersion() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !isClean {
|
if !isClean {
|
||||||
version = fmt.Sprintf("%s + unstaged changes", version)
|
version = fmt.Sprintf("%s+U", version)
|
||||||
}
|
}
|
||||||
|
|
||||||
return version, nil
|
return version, nil
|
||||||
|
@ -65,7 +65,7 @@ teardown(){
|
|||||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||||
--chaos --no-input --no-converge-checks
|
--chaos --no-input --no-converge-checks
|
||||||
assert_success
|
assert_success
|
||||||
assert_output --partial 'chaos'
|
assert_output --partial 'NEW CHAOS'
|
||||||
}
|
}
|
||||||
|
|
||||||
# bats test_tags=slow
|
# bats test_tags=slow
|
||||||
@ -111,8 +111,6 @@ teardown(){
|
|||||||
--no-input --no-converge-checks --offline
|
--no-input --no-converge-checks --offline
|
||||||
assert_success
|
assert_success
|
||||||
assert_output --partial "$latestCommit"
|
assert_output --partial "$latestCommit"
|
||||||
assert_output --partial 'using latest commit'
|
|
||||||
refute_output --partial 'chaos'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# bats test_tags=slow
|
# bats test_tags=slow
|
||||||
@ -130,7 +128,7 @@ teardown(){
|
|||||||
--no-input --no-converge-checks --chaos
|
--no-input --no-converge-checks --chaos
|
||||||
assert_success
|
assert_success
|
||||||
assert_output --partial "${wantHash:0:8}"
|
assert_output --partial "${wantHash:0:8}"
|
||||||
assert_output --partial 'chaos'
|
assert_output --partial 'NEW CHAOS'
|
||||||
}
|
}
|
||||||
|
|
||||||
# bats test_tags=slow
|
# bats test_tags=slow
|
||||||
@ -172,12 +170,10 @@ teardown(){
|
|||||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||||
--no-input --no-converge-checks --force
|
--no-input --no-converge-checks --force
|
||||||
assert_success
|
assert_success
|
||||||
assert_output --partial 'already deployed'
|
|
||||||
|
|
||||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||||
--no-input --no-converge-checks --chaos
|
--no-input --no-converge-checks --chaos
|
||||||
assert_success
|
assert_success
|
||||||
assert_output --partial 'already deployed'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# bats test_tags=slow
|
# bats test_tags=slow
|
||||||
@ -228,7 +224,6 @@ teardown(){
|
|||||||
|
|
||||||
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --no-converge-checks
|
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --no-converge-checks
|
||||||
assert_success
|
assert_success
|
||||||
assert_output --partial 'no DOMAIN=... configured for app'
|
|
||||||
|
|
||||||
run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input
|
run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input
|
||||||
assert_success
|
assert_success
|
||||||
@ -262,7 +257,6 @@ teardown(){
|
|||||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
||||||
--no-input --no-converge-checks --no-domain-checks
|
--no-input --no-converge-checks --no-domain-checks
|
||||||
assert_success
|
assert_success
|
||||||
assert_output --partial 'skipping domain checks as requested'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "error if specific version does not exist" {
|
@test "error if specific version does not exist" {
|
||||||
|
@ -60,11 +60,6 @@ teardown(){
|
|||||||
assert_output --partial "$TEST_SERVER"
|
assert_output --partial "$TEST_SERVER"
|
||||||
assert_output --partial "foo.com"
|
assert_output --partial "foo.com"
|
||||||
|
|
||||||
run $ABRA app ls --server foo.com
|
|
||||||
assert_success
|
|
||||||
refute_output --partial "SERVER: $TEST_SERVER"
|
|
||||||
assert_output --partial "SERVER: foo.com"
|
|
||||||
|
|
||||||
run rm -rf "$ABRA_DIR/servers/foo.com"
|
run rm -rf "$ABRA_DIR/servers/foo.com"
|
||||||
assert_success
|
assert_success
|
||||||
assert_not_exists "$ABRA_DIR/servers/foo.com"
|
assert_not_exists "$ABRA_DIR/servers/foo.com"
|
||||||
@ -94,33 +89,6 @@ teardown(){
|
|||||||
assert_output --partial "foo-recipe"
|
assert_output --partial "foo-recipe"
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "server stats are correct" {
|
|
||||||
run $ABRA app ls
|
|
||||||
assert_success
|
|
||||||
assert_output --partial "SERVER: $TEST_SERVER"
|
|
||||||
assert_output --partial "TOTAL APPS: 1"
|
|
||||||
|
|
||||||
run mkdir -p "$ABRA_DIR/servers/foo.com"
|
|
||||||
assert_success
|
|
||||||
assert_exists "$ABRA_DIR/servers/foo.com"
|
|
||||||
|
|
||||||
run cp \
|
|
||||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" \
|
|
||||||
"$ABRA_DIR/servers/foo.com/app.foo.com.env"
|
|
||||||
assert_exists "$ABRA_DIR/servers/foo.com/app.foo.com.env"
|
|
||||||
|
|
||||||
run $ABRA app ls
|
|
||||||
assert_success
|
|
||||||
assert_output --partial "$TEST_SERVER"
|
|
||||||
assert_output --partial "foo.com"
|
|
||||||
assert_output --partial "TOTAL SERVERS: 2"
|
|
||||||
assert_output --partial "TOTAL APPS: 2"
|
|
||||||
|
|
||||||
run rm -rf "$ABRA_DIR/servers/foo.com"
|
|
||||||
assert_success
|
|
||||||
assert_not_exists "$ABRA_DIR/servers/foo.com"
|
|
||||||
}
|
|
||||||
|
|
||||||
@test "output is machine readable" {
|
@test "output is machine readable" {
|
||||||
run $ABRA app ls --machine
|
run $ABRA app ls --machine
|
||||||
|
|
||||||
|
@ -173,8 +173,6 @@ teardown(){
|
|||||||
--domain "$TEST_APP_DOMAIN" \
|
--domain "$TEST_APP_DOMAIN" \
|
||||||
--secrets
|
--secrets
|
||||||
assert_success
|
assert_success
|
||||||
assert_output --partial 'generated secrets'
|
|
||||||
assert_output --partial 'test_pass_one'
|
|
||||||
assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
|
|
||||||
run $ABRA app secret ls "$TEST_APP_DOMAIN"
|
run $ABRA app secret ls "$TEST_APP_DOMAIN"
|
||||||
|
@ -45,5 +45,4 @@ teardown(){
|
|||||||
|
|
||||||
sanitisedDomainName="${TEST_APP_DOMAIN//./_}"
|
sanitisedDomainName="${TEST_APP_DOMAIN//./_}"
|
||||||
assert_output --partial "$sanitisedDomainName_app"
|
assert_output --partial "$sanitisedDomainName_app"
|
||||||
assert_output --partial "nginx"
|
|
||||||
}
|
}
|
||||||
|
@ -107,10 +107,10 @@ teardown(){
|
|||||||
|
|
||||||
# bats test_tags=slow
|
# bats test_tags=slow
|
||||||
@test "undeploy chaos deployment" {
|
@test "undeploy chaos deployment" {
|
||||||
run $ABRA app deploy "$TEST_APP_DOMAIN" \
|
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --chaos
|
||||||
--no-input --no-converge-checks --chaos
|
|
||||||
|
|
||||||
_undeploy_app
|
run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input
|
||||||
|
assert_success
|
||||||
|
|
||||||
# NOTE(d1): ensure chaos undeploy
|
# NOTE(d1): ensure chaos undeploy
|
||||||
assert_output --partial ${_get_current_hash:0:8}
|
assert_output --partial ${_get_current_hash:0:8}
|
||||||
|
60
tests/integration/app_undeploy_env_version.bats
Normal file
60
tests/integration/app_undeploy_env_version.bats
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
setup_file(){
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
_add_server
|
||||||
|
_new_app
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown_file(){
|
||||||
|
_rm_app
|
||||||
|
_rm_server
|
||||||
|
_reset_recipe
|
||||||
|
}
|
||||||
|
|
||||||
|
setup(){
|
||||||
|
load "$PWD/tests/integration/helpers/common"
|
||||||
|
_common_setup
|
||||||
|
_ensure_catalogue
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown(){
|
||||||
|
_reset_recipe
|
||||||
|
_undeploy_app
|
||||||
|
_reset_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "undeploy version written to env" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" \
|
||||||
|
--no-input --no-converge-checks
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run grep -q "TYPE=$TEST_RECIPE:0.1.0+1.20.0" \
|
||||||
|
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run sed -i "s/TYPE=$TEST_RECIPE:.*/TYPE=$TEST_RECIPE/g" \
|
||||||
|
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run grep -q "TYPE=$TEST_RECIPE:0.1.0+1.20.0" \
|
||||||
|
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
|
assert_success
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats test_tags=slow
|
||||||
|
@test "chaos commit written to env" {
|
||||||
|
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --chaos
|
||||||
|
|
||||||
|
run $ABRA app undeploy "$TEST_APP_DOMAIN" --no-input
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run grep -q "TYPE=$TEST_RECIPE:${_get_current_hash:0:8}" \
|
||||||
|
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||||
|
assert_success
|
||||||
|
}
|
@ -15,12 +15,14 @@ teardown_file(){
|
|||||||
setup(){
|
setup(){
|
||||||
load "$PWD/tests/integration/helpers/common"
|
load "$PWD/tests/integration/helpers/common"
|
||||||
_common_setup
|
_common_setup
|
||||||
|
_ensure_catalogue
|
||||||
}
|
}
|
||||||
|
|
||||||
teardown(){
|
teardown(){
|
||||||
_reset_recipe
|
_reset_recipe
|
||||||
_reset_app
|
_reset_app
|
||||||
_undeploy_app
|
_undeploy_app
|
||||||
|
_reset_app # NOTE(d1): _undeploy_app writes version
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "validate app argument" {
|
@test "validate app argument" {
|
||||||
|
@ -11,7 +11,7 @@ setup() {
|
|||||||
assert_success
|
assert_success
|
||||||
assert_not_exists "$ABRA_DIR/recipes/matrix-synapse"
|
assert_not_exists "$ABRA_DIR/recipes/matrix-synapse"
|
||||||
|
|
||||||
run $ABRA recipe fetch
|
run $ABRA recipe fetch --all
|
||||||
assert_success
|
assert_success
|
||||||
assert_exists "$ABRA_DIR/recipes/matrix-synapse"
|
assert_exists "$ABRA_DIR/recipes/matrix-synapse"
|
||||||
}
|
}
|
||||||
@ -25,3 +25,13 @@ setup() {
|
|||||||
assert_success
|
assert_success
|
||||||
assert_exists "$ABRA_DIR/recipes/matrix-synapse"
|
assert_exists "$ABRA_DIR/recipes/matrix-synapse"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "error if missing args/flags" {
|
||||||
|
run $ABRA recipe fetch
|
||||||
|
assert_failure
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "error if single recipe and --all" {
|
||||||
|
run $ABRA recipe fetch matrix-synapse --all
|
||||||
|
assert_failure
|
||||||
|
}
|
||||||
|
@ -5,13 +5,6 @@ setup() {
|
|||||||
_common_setup
|
_common_setup
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "recipe list" {
|
|
||||||
run $ABRA recipe list
|
|
||||||
assert_success
|
|
||||||
NUM_RECIPES=$(jq length "$ABRA_DIR/catalogue/recipes.json")
|
|
||||||
assert_output --partial "total recipes: $NUM_RECIPES"
|
|
||||||
}
|
|
||||||
|
|
||||||
@test "recipe list with pattern" {
|
@test "recipe list with pattern" {
|
||||||
run $ABRA recipe list --pattern cloud
|
run $ABRA recipe list --pattern cloud
|
||||||
assert_success
|
assert_success
|
||||||
|
@ -48,11 +48,9 @@ teardown(){
|
|||||||
--server "$TEST_SERVER" \
|
--server "$TEST_SERVER" \
|
||||||
--domain "foobar.$TEST_SERVER"
|
--domain "foobar.$TEST_SERVER"
|
||||||
assert_success
|
assert_success
|
||||||
assert_output --partial "new app 'foobar' created"
|
|
||||||
|
|
||||||
run $ABRA app deploy "foobar.$TEST_SERVER" --no-input
|
run $ABRA app deploy "foobar.$TEST_SERVER" --no-input
|
||||||
assert_success
|
assert_success
|
||||||
assert_output --partial 'using latest commit'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "create new recipe with git credentials" {
|
@test "create new recipe with git credentials" {
|
||||||
|
@ -32,13 +32,3 @@ setup() {
|
|||||||
assert_success
|
assert_success
|
||||||
assert_output "$latestVersion"
|
assert_output "$latestVersion"
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "app is first service listed" {
|
|
||||||
run bash -c '$ABRA recipe versions gitea --machine | jq -r ".[0].service" | uniq'
|
|
||||||
assert_success
|
|
||||||
assert_output 'app'
|
|
||||||
|
|
||||||
run bash -c '$ABRA recipe versions gitea --machine | jq -r ".[1].service" | uniq'
|
|
||||||
assert_success
|
|
||||||
assert_output 'db'
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user