Compare commits

..

20 Commits

Author SHA1 Message Date
db7c4042d0 chore: go mod vendor 2025-11-09 11:52:00 +01:00
ed1a66dc5f chore: publish next tag 0.12.0-beta
Some checks failed
continuous-integration/drone/tag Build is failing
continuous-integration/drone/push Build is passing
2025-11-09 11:46:38 +01:00
bb93e4266a fix: show domain with https (clickable)
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See #643
2025-11-09 10:56:21 +01:00
a2cc70b2f5 test: reinstate debug
Some checks failed
continuous-integration/drone/push Build is failing
2025-11-09 10:43:09 +01:00
ce1aa3d870 test: ensure recipe sync is robust
All checks were successful
continuous-integration/drone/push Build is passing
2025-11-09 10:41:32 +01:00
d75700c8a9 test: ensure env vars updated
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
See #723
2025-11-09 09:32:52 +01:00
0ccc4aae72 chore: remove --debug 2025-11-09 09:32:42 +01:00
ec22d5d51d test: remove old tests
All checks were successful
continuous-integration/drone/push Build is passing
See #716
2025-11-05 09:52:28 +01:00
ab42584d05 docs: update var name 2025-11-05 09:52:28 +01:00
f
40eb6e9a18 fix: shorter hyphen
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-11-04 11:49:44 -03:00
35eb9d4a89 chore: update translation files
All checks were successful
continuous-integration/drone/push Build is passing
Updated by "Update PO files to match POT (msgmerge)" add-on in Weblate.

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/
2025-11-04 14:34:39 +00:00
08cc63d523 chore: make i18n
All checks were successful
continuous-integration/drone/push Build is passing
2025-11-04 15:34:27 +01:00
797b8d899b chore: update translation files
All checks were successful
continuous-integration/drone/push Build is passing
Updated by "Update PO files to match POT (msgmerge)" add-on in Weblate.

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/
2025-11-04 13:53:26 +00:00
fb786306b5 chore: make i18n
All checks were successful
continuous-integration/drone/push Build is passing
2025-11-04 14:53:15 +01:00
c3a2048eba fix: throw away unknown version
Some checks failed
continuous-integration/drone/push Build is failing
See #715
2025-11-04 13:52:49 +00:00
1bdc11ba62 fix no-input app deployment when no tty is present
Some checks failed
continuous-integration/drone/push Build is failing
2025-11-04 13:52:28 +00:00
cc8703310c chore: update translation files
All checks were successful
continuous-integration/drone/push Build is passing
Updated by "Update PO files to match POT (msgmerge)" add-on in Weblate.

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/
2025-11-04 07:35:14 +00:00
fcd5bd863d chore: make i18n
Some checks failed
continuous-integration/drone/push Build is failing
2025-11-04 08:35:04 +01:00
e6af2da9dd refactor: named note, merge if clause 2025-11-04 08:34:49 +01:00
4b688825e0 feat: create docker context when server folder does exist
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2025-11-03 17:29:04 +01:00
24 changed files with 531 additions and 1363 deletions

View File

@ -260,6 +260,7 @@ checkout as-is. Recipe commit hashes are also supported as values for
app.Name,
app.Server,
internal.DontWaitConverge,
internal.NoInput,
f,
); err != nil {
log.Fatal(err)

View File

@ -128,6 +128,7 @@ Pass "--all-services/-a" to restart all services.`),
AppName: app.Name,
ServerName: app.Server,
Filters: f,
NoInput: internal.NoInput,
NoLog: true,
Quiet: true,
}

View File

@ -246,6 +246,7 @@ beforehand. See "abra app backup" for more.`),
stackName,
app.Server,
internal.DontWaitConverge,
internal.NoInput,
f,
); err != nil {
log.Fatal(err)

View File

@ -282,6 +282,7 @@ beforehand. See "abra app backup" for more.`),
stackName,
app.Server,
internal.DontWaitConverge,
internal.NoInput,
f,
); err != nil {
log.Fatal(err)

View File

@ -64,7 +64,7 @@ func DeployOverview(
server = "local"
}
domain := app.Domain
domain := fmt.Sprintf("https://%s", app.Domain)
if domain == "" {
domain = config.MISSING_DEFAULT
}

2
go.mod
View File

@ -8,7 +8,6 @@ require (
coopcloud.tech/tagcmp v0.0.0-20250818180036-0ec1b205b5ca
git.coopcloud.tech/toolshed/godotenv v1.5.2-0.20250103171850-4d0ca41daa5c
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/charmbracelet/bubbles v0.21.0
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/lipgloss v1.1.0
github.com/charmbracelet/log v0.4.2
@ -42,6 +41,7 @@ require (
github.com/charmbracelet/colorprofile v0.3.2 // indirect
github.com/charmbracelet/x/ansi v0.10.2 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect

2
go.sum
View File

@ -133,8 +133,6 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
github.com/charmbracelet/colorprofile v0.3.2 h1:9J27WdztfJQVAQKX2WOlSSRB+5gaKqqITmrvb1uTIiI=

View File

@ -633,6 +633,11 @@ func (a App) WipeRecipeVersion() error {
// WriteRecipeVersion writes the recipe version to the app .env file.
func (a App) WriteRecipeVersion(version string, dryRun bool) error {
if version == config.UNKNOWN_DEFAULT {
log.Debug(i18n.G("version is unknown, skipping env write"))
return nil
}
file, err := os.Open(a.Path)
if err != nil {
return err

View File

@ -224,3 +224,16 @@ func TestWriteRecipeVersionOverwrite(t *testing.T) {
assert.Equal(t, "foo", app.Recipe.EnvVersion)
}
func TestWriteRecipeVersionUnknown(t *testing.T) {
app, err := appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
if err != nil {
t.Fatal(err)
}
if err := app.WriteRecipeVersion(config.UNKNOWN_DEFAULT, false); err != nil {
t.Fatal(err)
}
assert.NotEqual(t, config.UNKNOWN_DEFAULT, app.Recipe.EnvVersion)
}

View File

@ -37,18 +37,27 @@ func WithTimeout(timeout int) Opt {
// New initiates a new Docker client. New client connections are validated so
// that we ensure connections via SSH to the daemon can succeed. It takes into
// account that you may only want the local client and not communicate via SSH.
// For this use-case, please pass "default" as the contextName.
// For this use-case, please pass "default" as the serverName.
func New(serverName string, opts ...Opt) (*client.Client, error) {
var clientOpts []client.Opt
ctx, err := GetContext(serverName)
if err != nil {
serverDir := path.Join(config.SERVERS_DIR, serverName)
if _, err := os.Stat(serverDir); err == nil {
return nil, errors.New(i18n.G("server missing context, run \"abra server add %s\"?", serverName))
if _, err := os.Stat(serverDir); err != nil {
return nil, errors.New(i18n.G("server missing, run \"abra server add %s\"?", serverName))
}
return nil, errors.New(i18n.G("unknown server, run \"abra server add %s\"?", serverName))
// NOTE(p4u1): when the docker context does not exist but the server folder
// is there, let's create a new docker context.
if err = CreateContext(serverName); err != nil {
return nil, errors.New(i18n.G("server missing context, context creation failed: %s", err))
}
ctx, err = GetContext(serverName)
if err != nil {
return nil, errors.New(i18n.G("server missing context, run \"abra server add %s\"?", serverName))
}
}
ctxEndpoint, err := contextPkg.GetContextEndpoint(ctx)

View File

@ -7,7 +7,7 @@
msgid ""
msgstr "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: EMAIL\n"
"POT-Creation-Date: 2025-11-02 14:13+0100\n"
"POT-Creation-Date: 2025-11-04 15:34+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -274,12 +274,12 @@ msgstr ""
msgid "%s has been detected as not deployed"
msgstr ""
#: ./cli/app/restart.go:139
#: ./cli/app/restart.go:140
#, c-format
msgid "%s has been scaled to 0"
msgstr ""
#: ./cli/app/restart.go:150
#: ./cli/app/restart.go:151
#, c-format
msgid "%s has been scaled to 1"
msgstr ""
@ -339,17 +339,17 @@ msgstr ""
msgid "%s is missing the TYPE env var?"
msgstr ""
#: ./cli/app/rollback.go:308 ./cli/app/rollback.go:312
#: ./cli/app/rollback.go:309 ./cli/app/rollback.go:313
#, c-format
msgid "%s is not a downgrade for %s?"
msgstr ""
#: ./cli/app/upgrade.go:428 ./cli/app/upgrade.go:432
#: ./cli/app/upgrade.go:429 ./cli/app/upgrade.go:433
#, c-format
msgid "%s is not an upgrade for %s?"
msgstr ""
#: ./cli/app/env.go:146 ./cli/app/logs.go:65 ./cli/app/ps.go:62 ./cli/app/restart.go:100 ./cli/app/services.go:55 ./cli/app/undeploy.go:66 ./cli/app/upgrade.go:449
#: ./cli/app/env.go:146 ./cli/app/logs.go:65 ./cli/app/ps.go:62 ./cli/app/restart.go:100 ./cli/app/services.go:55 ./cli/app/undeploy.go:66 ./cli/app/upgrade.go:450
#, c-format
msgid "%s is not deployed?"
msgstr ""
@ -424,7 +424,7 @@ msgstr ""
msgid "%s service is missing image tag?"
msgstr ""
#: ./cli/app/restart.go:151
#: ./cli/app/restart.go:152
#, c-format
msgid "%s service successfully restarted"
msgstr ""
@ -459,7 +459,7 @@ msgstr ""
msgid "%s/example.git"
msgstr ""
#: ./pkg/upstream/stack/stack.go:602
#: ./pkg/upstream/stack/stack.go:613
#, c-format
msgid "%s: %s"
msgstr ""
@ -469,7 +469,7 @@ msgstr ""
msgid "%s: %s (new)"
msgstr ""
#: ./pkg/ui/deploy.go:406
#: ./pkg/ui/deploy.go:344
#, c-format
msgid "%s: %s (retries: %v, healthcheck: %s)"
msgstr ""
@ -549,12 +549,12 @@ msgstr ""
msgid "%s: waiting %d seconds before next retry"
msgstr ""
#: ./cli/app/upgrade.go:423
#: ./cli/app/upgrade.go:424
#, c-format
msgid "'%s' is not a known version"
msgstr ""
#: ./cli/app/rollback.go:303 ./cli/app/upgrade.go:418
#: ./cli/app/rollback.go:304 ./cli/app/upgrade.go:419
#, c-format
msgid "'%s' is not a known version for %s"
msgstr ""
@ -621,7 +621,7 @@ msgstr ""
msgid "Both local recipe and live deployment labels are shown."
msgstr ""
#: ./cli/app/backup.go:319 ./cli/app/backup.go:335 ./cli/app/check.go:95 ./cli/app/cmd.go:285 ./cli/app/cp.go:385 ./cli/app/deploy.go:395 ./cli/app/labels.go:143 ./cli/app/new.go:397 ./cli/app/ps.go:213 ./cli/app/restart.go:162 ./cli/app/restore.go:138 ./cli/app/secret.go:569 ./cli/app/secret.go:609 ./cli/app/secret.go:633 ./cli/app/secret.go:641 ./cli/catalogue/catalogue.go:318 ./cli/recipe/lint.go:137
#: ./cli/app/backup.go:319 ./cli/app/backup.go:335 ./cli/app/check.go:95 ./cli/app/cmd.go:285 ./cli/app/cp.go:385 ./cli/app/deploy.go:396 ./cli/app/labels.go:143 ./cli/app/new.go:397 ./cli/app/ps.go:213 ./cli/app/restart.go:163 ./cli/app/restore.go:138 ./cli/app/secret.go:569 ./cli/app/secret.go:609 ./cli/app/secret.go:633 ./cli/app/secret.go:641 ./cli/catalogue/catalogue.go:318 ./cli/recipe/lint.go:137
msgid "C"
msgstr ""
@ -762,7 +762,7 @@ msgid "Creates a new app from a default recipe.\n"
"on your $PATH."
msgstr ""
#: ./cli/app/deploy.go:411 ./cli/app/new.go:373 ./cli/app/rollback.go:360 ./cli/app/upgrade.go:469
#: ./cli/app/deploy.go:412 ./cli/app/new.go:373 ./cli/app/rollback.go:361 ./cli/app/upgrade.go:470
msgid "D"
msgstr ""
@ -1469,7 +1469,7 @@ msgid "To load completions:\n"
" # and source this file from your PowerShell profile."
msgstr ""
#: ./cli/app/deploy.go:435 ./cli/app/rollback.go:376 ./cli/app/upgrade.go:493
#: ./cli/app/deploy.go:436 ./cli/app/rollback.go:377 ./cli/app/upgrade.go:494
msgid "U"
msgstr ""
@ -1659,7 +1659,7 @@ msgid "\n"
"lint %s: %s"
msgstr ""
#: ./pkg/ui/deploy.go:132
#: ./pkg/ui/deploy.go:121
#, c-format
msgid "^%s"
msgstr ""
@ -1676,7 +1676,7 @@ msgctxt "app backup list"
msgid "a"
msgstr ""
#: ./cli/app/restart.go:169
#: ./cli/app/restart.go:170
msgctxt "app restart"
msgid "a"
msgstr ""
@ -1785,7 +1785,7 @@ msgstr ""
msgid "all tasks reached terminal state"
msgstr ""
#: ./cli/app/restart.go:168
#: ./cli/app/restart.go:169
msgid "all-services"
msgstr ""
@ -1844,7 +1844,7 @@ msgstr ""
msgid "attempting to run %s"
msgstr ""
#: ./cli/app/deploy.go:272 ./cli/app/upgrade.go:295
#: ./cli/app/deploy.go:273 ./cli/app/upgrade.go:296
#, c-format
msgid "attempting to run post deploy commands, saw: %s"
msgstr ""
@ -1854,7 +1854,7 @@ msgstr ""
msgid "attempting to scale %s to 0"
msgstr ""
#: ./cli/app/restart.go:140
#: ./cli/app/restart.go:141
#, c-format
msgid "attempting to scale %s to 1"
msgstr ""
@ -1924,7 +1924,7 @@ msgstr ""
#. no spaces in between
#. translators: `abra app cp` aliases. use a comma separated list of aliases with
#. no spaces in between
#: ./cli/app/backup.go:148 ./cli/app/cp.go:30 ./cli/app/deploy.go:419 ./cli/app/rollback.go:368 ./cli/app/upgrade.go:477
#: ./cli/app/backup.go:148 ./cli/app/cp.go:30 ./cli/app/deploy.go:420 ./cli/app/rollback.go:369 ./cli/app/upgrade.go:478
msgid "c"
msgstr ""
@ -1965,7 +1965,7 @@ msgstr ""
msgid "cannot find app with name %s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:657
#: ./pkg/upstream/stack/stack.go:668
#, c-format
msgid "cannot get label %s for %s"
msgstr ""
@ -1980,7 +1980,7 @@ msgstr ""
msgid "cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?"
msgstr ""
#: ./cli/app/deploy.go:369
#: ./cli/app/deploy.go:370
#, c-format
msgid "cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?\n"
" to return to a regular release, specify a release tag, commit SHA or use \"--latest\""
@ -1999,7 +1999,7 @@ msgstr ""
msgid "cannot use '[secret] [version]' and '--all' together"
msgstr ""
#: ./cli/app/deploy.go:311
#: ./cli/app/deploy.go:312
msgid "cannot use --chaos and --latest together"
msgstr ""
@ -2023,11 +2023,11 @@ msgstr ""
msgid "cannot use [service] and --all-services/-a together"
msgstr ""
#: ./cli/app/deploy.go:303 ./cli/app/new.go:76
#: ./cli/app/deploy.go:304 ./cli/app/new.go:76
msgid "cannot use [version] and --chaos together"
msgstr ""
#: ./cli/app/deploy.go:307
#: ./cli/app/deploy.go:308
msgid "cannot use [version] and --latest together"
msgstr ""
@ -2059,7 +2059,7 @@ msgstr ""
msgid "cfg"
msgstr ""
#: ./cli/app/backup.go:318 ./cli/app/backup.go:334 ./cli/app/check.go:94 ./cli/app/cmd.go:284 ./cli/app/cp.go:384 ./cli/app/deploy.go:394 ./cli/app/labels.go:142 ./cli/app/new.go:396 ./cli/app/ps.go:212 ./cli/app/restart.go:161 ./cli/app/restore.go:137 ./cli/app/secret.go:568 ./cli/app/secret.go:608 ./cli/app/secret.go:632 ./cli/app/secret.go:640 ./cli/catalogue/catalogue.go:317 ./cli/recipe/lint.go:136
#: ./cli/app/backup.go:318 ./cli/app/backup.go:334 ./cli/app/check.go:94 ./cli/app/cmd.go:284 ./cli/app/cp.go:384 ./cli/app/deploy.go:395 ./cli/app/labels.go:142 ./cli/app/new.go:396 ./cli/app/ps.go:212 ./cli/app/restart.go:162 ./cli/app/restore.go:137 ./cli/app/secret.go:568 ./cli/app/secret.go:608 ./cli/app/secret.go:632 ./cli/app/secret.go:640 ./cli/catalogue/catalogue.go:317 ./cli/recipe/lint.go:136
msgid "chaos"
msgstr ""
@ -2068,7 +2068,7 @@ msgstr ""
msgid "check <domain> [flags]"
msgstr ""
#: ./cli/app/deploy.go:94 ./cli/app/undeploy.go:58 ./cli/app/upgrade.go:441
#: ./cli/app/deploy.go:94 ./cli/app/undeploy.go:58 ./cli/app/upgrade.go:442
#, c-format
msgid "checking whether %s is already deployed"
msgstr ""
@ -2291,7 +2291,7 @@ msgstr ""
msgid "create remote directory: %s"
msgstr ""
#: ./pkg/client/client.go:102
#: ./pkg/client/client.go:111
#, c-format
msgid "created client for %s"
msgstr ""
@ -2311,7 +2311,7 @@ msgstr ""
msgid "created the %s context"
msgstr ""
#: ./pkg/upstream/stack/stack.go:520
#: ./pkg/upstream/stack/stack.go:524
#, c-format
msgid "creating %s"
msgstr ""
@ -2326,12 +2326,12 @@ msgstr ""
msgid "creating context with domain %s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:422
#: ./pkg/upstream/stack/stack.go:426
#, c-format
msgid "creating network %s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:369
#: ./pkg/upstream/stack/stack.go:373
#, c-format
msgid "creating secret %s"
msgstr ""
@ -2350,7 +2350,7 @@ msgstr ""
msgid "critical errors present in %s config"
msgstr ""
#: ./cli/app/rollback.go:298
#: ./cli/app/rollback.go:299
#, c-format
msgid "current deployment '%s' is not a known version for %s"
msgstr ""
@ -2390,11 +2390,11 @@ msgstr ""
msgid "deploy <domain> [version] [flags]"
msgstr ""
#: ./pkg/upstream/stack/stack.go:593
#: ./pkg/upstream/stack/stack.go:604
msgid "deploy failed 🛑"
msgstr ""
#: ./pkg/upstream/stack/stack.go:597
#: ./pkg/upstream/stack/stack.go:608
msgid "deploy in progress 🟠"
msgstr ""
@ -2402,15 +2402,15 @@ msgstr ""
msgid "deploy labels stanza present"
msgstr ""
#: ./cli/app/deploy.go:429
#: ./cli/app/deploy.go:430
msgid "deploy latest recipe version"
msgstr ""
#: ./pkg/upstream/stack/stack.go:637
#: ./pkg/upstream/stack/stack.go:648
msgid "deploy succeeded 🟢"
msgstr ""
#: ./pkg/upstream/stack/stack.go:595
#: ./pkg/upstream/stack/stack.go:606
msgid "deploy timed out 🟠"
msgstr ""
@ -2504,11 +2504,11 @@ msgstr ""
msgid "dirty: %v, "
msgstr ""
#: ./cli/app/deploy.go:421 ./cli/app/rollback.go:370 ./cli/app/upgrade.go:479
#: ./cli/app/deploy.go:422 ./cli/app/rollback.go:371 ./cli/app/upgrade.go:480
msgid "disable converge logic checks"
msgstr ""
#: ./cli/app/deploy.go:413 ./cli/app/rollback.go:362 ./cli/app/upgrade.go:471
#: ./cli/app/deploy.go:414 ./cli/app/rollback.go:363 ./cli/app/upgrade.go:472
msgid "disable public DNS checks"
msgstr ""
@ -2662,7 +2662,7 @@ msgstr ""
msgid "env file for %s has issues: %s"
msgstr ""
#: ./pkg/ui/deploy.go:94
#: ./pkg/ui/deploy.go:83
#, c-format
msgid "err: %v, "
msgstr ""
@ -2726,7 +2726,7 @@ msgstr ""
#. translators: `abra recipe fetch` aliases. use a comma separated list of aliases
#. with no spaces in between
#: ./cli/app/deploy.go:403 ./cli/app/env.go:325 ./cli/app/remove.go:163 ./cli/app/rollback.go:352 ./cli/app/secret.go:593 ./cli/app/upgrade.go:461 ./cli/app/volume.go:217 ./cli/recipe/fetch.go:20 ./cli/recipe/fetch.go:138
#: ./cli/app/deploy.go:404 ./cli/app/env.go:325 ./cli/app/remove.go:163 ./cli/app/rollback.go:353 ./cli/app/secret.go:593 ./cli/app/upgrade.go:462 ./cli/app/volume.go:217 ./cli/recipe/fetch.go:20 ./cli/recipe/fetch.go:138
msgid "f"
msgstr ""
@ -2760,22 +2760,22 @@ msgstr ""
msgid "failed to copy %s from local machine to %s: output:%s err:%s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:531
#: ./pkg/upstream/stack/stack.go:535
#, c-format
msgid "failed to create %s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:393
#: ./pkg/upstream/stack/stack.go:397
#, c-format
msgid "failed to create config %s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:424
#: ./pkg/upstream/stack/stack.go:428
#, c-format
msgid "failed to create network %s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:371
#: ./pkg/upstream/stack/stack.go:375
#, c-format
msgid "failed to create secret %s"
msgstr ""
@ -2872,7 +2872,7 @@ msgstr ""
msgid "failed to retrieve latest commit for %s: %s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:468
#: ./pkg/upstream/stack/stack.go:472
#, c-format
msgid "failed to retrieve registry auth for image %s: %s"
msgstr ""
@ -2892,17 +2892,17 @@ msgstr ""
msgid "failed to tag release: %s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:508
#: ./pkg/upstream/stack/stack.go:512
#, c-format
msgid "failed to update %s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:387
#: ./pkg/upstream/stack/stack.go:391
#, c-format
msgid "failed to update config %s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:365
#: ./pkg/upstream/stack/stack.go:369
#, c-format
msgid "failed to update secret %s"
msgstr ""
@ -2957,7 +2957,7 @@ msgstr ""
msgid "final merged env values for %s are: %s"
msgstr ""
#: ./cli/app/deploy.go:402 ./cli/app/env.go:324 ./cli/app/remove.go:162 ./cli/app/rollback.go:351 ./cli/app/upgrade.go:460 ./cli/app/volume.go:216 ./cli/recipe/fetch.go:137
#: ./cli/app/deploy.go:403 ./cli/app/env.go:324 ./cli/app/remove.go:162 ./cli/app/rollback.go:352 ./cli/app/upgrade.go:461 ./cli/app/volume.go:216 ./cli/recipe/fetch.go:137
msgid "force"
msgstr ""
@ -3166,12 +3166,12 @@ msgstr ""
msgid "i"
msgstr ""
#: ./pkg/ui/deploy.go:95
#: ./pkg/ui/deploy.go:84
#, c-format
msgid "id: %s, "
msgstr ""
#: ./cli/app/backup.go:321 ./cli/app/backup.go:337 ./cli/app/check.go:97 ./cli/app/cmd.go:287 ./cli/app/cp.go:387 ./cli/app/deploy.go:397 ./cli/app/labels.go:145 ./cli/app/new.go:399 ./cli/app/ps.go:215 ./cli/app/restart.go:164 ./cli/app/restore.go:140 ./cli/app/secret.go:571 ./cli/app/secret.go:611 ./cli/app/secret.go:635 ./cli/app/secret.go:643 ./cli/catalogue/catalogue.go:320 ./cli/recipe/lint.go:139
#: ./cli/app/backup.go:321 ./cli/app/backup.go:337 ./cli/app/check.go:97 ./cli/app/cmd.go:287 ./cli/app/cp.go:387 ./cli/app/deploy.go:398 ./cli/app/labels.go:145 ./cli/app/new.go:399 ./cli/app/ps.go:215 ./cli/app/restart.go:165 ./cli/app/restore.go:140 ./cli/app/secret.go:571 ./cli/app/secret.go:611 ./cli/app/secret.go:635 ./cli/app/secret.go:643 ./cli/catalogue/catalogue.go:320 ./cli/recipe/lint.go:139
msgid "ignore uncommitted recipes changes"
msgstr ""
@ -3282,7 +3282,7 @@ msgstr ""
msgid "initialised new git repo in %s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:206
#: ./pkg/upstream/stack/stack.go:207
msgid "initialising deployment"
msgstr ""
@ -3346,7 +3346,7 @@ msgstr ""
msgid "invalid npipe source, source cannot be empty"
msgstr ""
#: ./pkg/upstream/stack/stack.go:239
#: ./pkg/upstream/stack/stack.go:241
#, c-format
msgid "invalid option %s for flag --resolve-image"
msgstr ""
@ -3369,7 +3369,7 @@ msgstr ""
#. no spaces in between
#. translators: `abra recipe lint` aliases. use a comma separated list of
#. aliases with no spaces in between
#: ./cli/app/cmd.go:261 ./cli/app/deploy.go:427 ./cli/app/logs.go:20 ./cli/recipe/lint.go:17 ./cli/server/add.go:207
#: ./cli/app/cmd.go:261 ./cli/app/deploy.go:428 ./cli/app/logs.go:20 ./cli/recipe/lint.go:17 ./cli/server/add.go:207
msgid "l"
msgstr ""
@ -3384,7 +3384,7 @@ msgstr ""
msgid "labels <domain> [flags]"
msgstr ""
#: ./cli/app/deploy.go:426 ./cli/app/list.go:182
#: ./cli/app/deploy.go:427 ./cli/app/list.go:182
msgid "latest"
msgstr ""
@ -3473,12 +3473,12 @@ msgstr ""
msgid "logs <domain> [service] [flags]"
msgstr ""
#: ./pkg/upstream/stack/stack.go:628
#: ./pkg/upstream/stack/stack.go:639
#, c-format
msgid "logs: %s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:630
#: ./pkg/upstream/stack/stack.go:641
msgid "logs: no log output received from deployment"
msgstr ""
@ -3605,7 +3605,7 @@ msgstr ""
msgid "name a servce 'app'"
msgstr ""
#: ./pkg/ui/deploy.go:96
#: ./pkg/ui/deploy.go:85
#, c-format
msgid "name: %s, "
msgstr ""
@ -3614,12 +3614,12 @@ msgstr ""
msgid "need 3 or 4 arguments"
msgstr ""
#: ./pkg/upstream/stack/stack.go:348
#: ./pkg/upstream/stack/stack.go:352
#, c-format
msgid "network %q is declared as external, but could not be found. You need to create a swarm-scoped network before the stack is deployed, which you can do by running this on the server: docker network create -d overlay proxy"
msgstr ""
#: ./pkg/upstream/stack/stack.go:352
#: ./pkg/upstream/stack/stack.go:356
#, c-format
msgid "network %q is declared as external, but it is not in the right scope: %q instead of \"swarm\""
msgstr ""
@ -3842,11 +3842,11 @@ msgstr ""
msgid "no volumes to remove"
msgstr ""
#: ./cli/app/deploy.go:418 ./cli/app/rollback.go:367 ./cli/app/upgrade.go:476
#: ./cli/app/deploy.go:419 ./cli/app/rollback.go:368 ./cli/app/upgrade.go:477
msgid "no-converge-checks"
msgstr ""
#: ./cli/app/deploy.go:410 ./cli/app/rollback.go:359 ./cli/app/upgrade.go:468
#: ./cli/app/deploy.go:411 ./cli/app/rollback.go:360 ./cli/app/upgrade.go:469
msgid "no-domain-checks"
msgstr ""
@ -3902,7 +3902,7 @@ msgstr ""
msgid "only show errors"
msgstr ""
#: ./cli/app/upgrade.go:487
#: ./cli/app/upgrade.go:488
msgid "only show release notes"
msgstr ""
@ -3933,22 +3933,22 @@ msgstr ""
msgid "parsed following command arguments: %s"
msgstr ""
#: ./cli/app/upgrade.go:344
#: ./cli/app/upgrade.go:345
#, c-format
msgid "parsing chosen upgrade version failed: %s"
msgstr ""
#: ./cli/app/upgrade.go:388
#: ./cli/app/upgrade.go:389
#, c-format
msgid "parsing deployed version failed: %s"
msgstr ""
#: ./cli/app/upgrade.go:349
#: ./cli/app/upgrade.go:350
#, c-format
msgid "parsing deployment version failed: %s"
msgstr ""
#: ./cli/app/upgrade.go:355 ./cli/app/upgrade.go:394
#: ./cli/app/upgrade.go:356 ./cli/app/upgrade.go:395
#, c-format
msgid "parsing recipe version failed: %s"
msgstr ""
@ -3973,7 +3973,7 @@ msgstr ""
msgid "pattern"
msgstr ""
#: ./cli/app/deploy.go:405 ./cli/app/env.go:327 ./cli/app/remove.go:165 ./cli/app/rollback.go:354 ./cli/app/upgrade.go:463 ./cli/app/volume.go:219
#: ./cli/app/deploy.go:406 ./cli/app/env.go:327 ./cli/app/remove.go:165 ./cli/app/rollback.go:355 ./cli/app/upgrade.go:464 ./cli/app/volume.go:219
msgid "perform action without further prompt"
msgstr ""
@ -3988,27 +3988,27 @@ msgstr ""
msgid "please fix your synced label for %s and re-run this command"
msgstr ""
#: ./cli/app/rollback.go:266
#: ./cli/app/rollback.go:267
#, c-format
msgid "please select a downgrade (version: %s):"
msgstr ""
#: ./cli/app/rollback.go:271
#: ./cli/app/rollback.go:272
#, c-format
msgid "please select a downgrade (version: %s, chaos: %s):"
msgstr ""
#: ./cli/app/upgrade.go:311
#: ./cli/app/upgrade.go:312
#, c-format
msgid "please select an upgrade (version: %s):"
msgstr ""
#: ./cli/app/upgrade.go:316
#: ./cli/app/upgrade.go:317
#, c-format
msgid "please select an upgrade (version: %s, chaos: %s):"
msgstr ""
#: ./pkg/upstream/stack/stack.go:576
#: ./pkg/upstream/stack/stack.go:587
msgid "polling deployment status"
msgstr ""
@ -4094,7 +4094,7 @@ msgstr ""
#. with no spaces in between
#. translators: `abra recipe` aliases. use a comma separated list of aliases
#. with no spaces in between
#: ./cli/app/backup.go:327 ./cli/app/list.go:300 ./cli/app/move.go:350 ./cli/app/run.go:23 ./cli/app/upgrade.go:485 ./cli/catalogue/catalogue.go:302 ./cli/recipe/recipe.go:12 ./cli/recipe/release.go:649 ./cli/recipe/sync.go:272
#: ./cli/app/backup.go:327 ./cli/app/list.go:300 ./cli/app/move.go:350 ./cli/app/run.go:23 ./cli/app/upgrade.go:486 ./cli/catalogue/catalogue.go:302 ./cli/recipe/recipe.go:12 ./cli/recipe/release.go:649 ./cli/recipe/sync.go:272
msgid "r"
msgstr ""
@ -4147,7 +4147,7 @@ msgstr ""
msgid "read v:%s k: %s"
msgstr ""
#: ./pkg/ui/deploy.go:97
#: ./pkg/ui/deploy.go:86
#, c-format
msgid "reader: %v, "
msgstr ""
@ -4210,7 +4210,7 @@ msgstr ""
msgid "release <recipe> [version] [flags]"
msgstr ""
#: ./cli/app/upgrade.go:484
#: ./cli/app/upgrade.go:485
msgid "releasenotes"
msgstr ""
@ -4376,7 +4376,7 @@ msgstr ""
msgid "restart <domain> [[service] | --all-services] [flags]"
msgstr ""
#: ./cli/app/restart.go:171
#: ./cli/app/restart.go:172
msgid "restart all services"
msgstr ""
@ -4441,7 +4441,7 @@ msgstr ""
msgid "retrieved versions from local recipe repository"
msgstr ""
#: ./pkg/upstream/stack/stack.go:464
#: ./pkg/upstream/stack/stack.go:468
#, c-format
msgid "retrieving docker auth token: failed create docker cli: %s"
msgstr ""
@ -4475,7 +4475,7 @@ msgstr ""
msgid "rollback <domain> [version] [flags]"
msgstr ""
#: ./pkg/ui/deploy.go:398
#: ./pkg/ui/deploy.go:336
msgid "rolled back"
msgstr ""
@ -4514,7 +4514,7 @@ msgstr ""
msgid "run command locally"
msgstr ""
#: ./cli/app/deploy.go:270 ./cli/app/upgrade.go:292
#: ./cli/app/deploy.go:271 ./cli/app/upgrade.go:293
#, c-format
msgid "run the following post-deploy commands: %s"
msgstr ""
@ -4599,12 +4599,12 @@ msgstr ""
msgid "secret not found: %s"
msgstr ""
#: ./cli/app/deploy.go:339
#: ./cli/app/deploy.go:340
#, c-format
msgid "secret not generated: %s"
msgstr ""
#: ./cli/app/deploy.go:337
#: ./cli/app/deploy.go:338
#, c-format
msgid "secret not inserted (#generate=false): %s"
msgstr ""
@ -4651,11 +4651,21 @@ msgstr ""
msgid "server doesn't exist?"
msgstr ""
#: ./pkg/client/client.go:48
#: ./pkg/client/client.go:54
#, c-format
msgid "server missing context, context creation failed: %s"
msgstr ""
#: ./pkg/client/client.go:59
#, c-format
msgid "server missing context, run \"abra server add %s\"?"
msgstr ""
#: ./pkg/client/client.go:48
#, c-format
msgid "server missing, run \"abra server add %s\"?"
msgstr ""
#: ./cli/server/add.go:148
#, c-format
msgid "serverAdd: cleanUp: %s is not empty, aborting cleanup"
@ -4728,7 +4738,7 @@ msgstr ""
msgid "severity"
msgstr ""
#: ./cli/app/deploy.go:437 ./cli/app/rollback.go:378 ./cli/app/upgrade.go:495
#: ./cli/app/deploy.go:438 ./cli/app/rollback.go:379 ./cli/app/upgrade.go:496
msgid "show all configs & images, including unchanged ones"
msgstr ""
@ -4752,7 +4762,7 @@ msgstr ""
msgid "show debug messages"
msgstr ""
#: ./cli/app/deploy.go:434 ./cli/app/rollback.go:375 ./cli/app/upgrade.go:492
#: ./cli/app/deploy.go:435 ./cli/app/rollback.go:376 ./cli/app/upgrade.go:493
msgid "show-unchanged"
msgstr ""
@ -4796,7 +4806,7 @@ msgstr ""
msgid "skipping as requested, undeploy still in progress 🟠"
msgstr ""
#: ./pkg/upstream/stack/stack.go:306
#: ./pkg/upstream/stack/stack.go:309
msgid "skipping converge logic checks"
msgstr ""
@ -4818,12 +4828,12 @@ msgstr ""
msgid "skipping secret (because it already exists) on %s: %s"
msgstr ""
#: ./pkg/app/app.go:692
#: ./pkg/app/app.go:697
#, c-format
msgid "skipping version %s write as already exists in %s.env"
msgstr ""
#: ./pkg/app/app.go:686
#: ./pkg/app/app.go:691
#, c-format
msgid "skipping writing version %s because dry run"
msgstr ""
@ -4890,7 +4900,7 @@ msgstr ""
msgid "status"
msgstr ""
#: ./pkg/ui/deploy.go:99
#: ./pkg/ui/deploy.go:88
#, c-format
msgid "status: %s}"
msgstr ""
@ -4912,7 +4922,7 @@ msgstr ""
msgid "stripped %s to %s for parsing"
msgstr ""
#: ./pkg/ui/deploy.go:395
#: ./pkg/ui/deploy.go:333
msgid "succeeded"
msgstr ""
@ -4931,12 +4941,12 @@ msgstr ""
msgid "successfully created %s"
msgstr ""
#: ./pkg/client/client.go:111
#: ./pkg/client/client.go:120
#, c-format
msgid "swarm mode not enabled on %s?"
msgstr ""
#: ./pkg/client/client.go:114
#: ./pkg/client/client.go:123
msgid "swarm mode not enabled on local server?"
msgstr ""
@ -5012,7 +5022,7 @@ msgstr ""
msgid "timeout label: %s"
msgstr ""
#: ./pkg/upstream/stack/remove.go:29 ./pkg/upstream/stack/stack.go:209
#: ./pkg/upstream/stack/remove.go:29 ./pkg/upstream/stack/stack.go:210
#, c-format
msgid "timeout: set to %d second(s)"
msgstr ""
@ -5435,11 +5445,6 @@ msgstr ""
msgid "unknown server %s, run \"abra server add %s\"?"
msgstr ""
#: ./pkg/client/client.go:51
#, c-format
msgid "unknown server, run \"abra server add %s\"?"
msgstr ""
#: ./cli/app/cp.go:259
#, c-format
msgid "untar: %s"
@ -5451,7 +5456,7 @@ msgstr ""
msgid "up"
msgstr ""
#: ./pkg/upstream/stack/stack.go:473
#: ./pkg/upstream/stack/stack.go:477
#, c-format
msgid "updating %s"
msgstr ""
@ -5563,7 +5568,7 @@ msgstr ""
msgid "version"
msgstr ""
#: ./pkg/app/app.go:690
#: ./pkg/app/app.go:695
#, c-format
msgid "version %s saved to %s.env"
msgstr ""
@ -5582,6 +5587,10 @@ msgstr ""
msgid "version for abra"
msgstr ""
#: ./pkg/app/app.go:637
msgid "version is unknown, skipping env write"
msgstr ""
#: ./pkg/recipe/recipe.go:130
#, c-format
msgid "version seems invalid: %s"
@ -5592,27 +5601,27 @@ msgstr ""
msgid "version wiped from %s.env"
msgstr ""
#: ./cli/app/deploy.go:353
#: ./cli/app/deploy.go:354
#, c-format
msgid "version: taking chaos version: %s"
msgstr ""
#: ./cli/app/deploy.go:379
#: ./cli/app/deploy.go:380
#, c-format
msgid "version: taking deployed version: %s"
msgstr ""
#: ./cli/app/deploy.go:384
#: ./cli/app/deploy.go:385
#, c-format
msgid "version: taking new recipe version: %s"
msgstr ""
#: ./cli/app/deploy.go:373
#: ./cli/app/deploy.go:374
#, c-format
msgid "version: taking version from .env file: %s"
msgstr ""
#: ./cli/app/deploy.go:359
#: ./cli/app/deploy.go:360
#, c-format
msgid "version: taking version from cli arg: %s"
msgstr ""
@ -5672,22 +5681,22 @@ msgstr ""
msgid "volumes pruned: %d; space reclaimed: %s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:614
#: ./pkg/upstream/stack/stack.go:625
#, c-format
msgid "waitOnServices: error creating log dir: %s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:619
#: ./pkg/upstream/stack/stack.go:630
#, c-format
msgid "waitOnServices: error opening file: %s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:585
#: ./pkg/upstream/stack/stack.go:596
#, c-format
msgid "waitOnServices: error running TUI: %s"
msgstr ""
#: ./pkg/upstream/stack/stack.go:625
#: ./pkg/upstream/stack/stack.go:636
#, c-format
msgid "waitOnServices: writeFile: %s"
msgstr ""
@ -5730,12 +5739,12 @@ msgstr ""
msgid "wire up healthchecks"
msgstr ""
#: ./pkg/ui/deploy.go:98
#: ./pkg/ui/deploy.go:87
#, c-format
msgid "writer: %v, "
msgstr ""
#: ./cli/app/deploy.go:277 ./cli/app/new.go:241 ./cli/app/rollback.go:255 ./cli/app/undeploy.go:120 ./cli/app/upgrade.go:300
#: ./cli/app/deploy.go:278 ./cli/app/new.go:241 ./cli/app/rollback.go:256 ./cli/app/undeploy.go:120 ./cli/app/upgrade.go:301
#, c-format
msgid "writing recipe version failed: %s"
msgstr ""
@ -5764,7 +5773,7 @@ msgstr ""
msgid "z"
msgstr ""
#: ./pkg/ui/deploy.go:93
#: ./pkg/ui/deploy.go:82
#, c-format
msgid "{decoder: %v, "
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,6 @@ import (
"coopcloud.tech/abra/pkg/formatter"
"coopcloud.tech/abra/pkg/i18n"
"coopcloud.tech/abra/pkg/logs"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/docker/cli/cli/command/service/progress"
containerTypes "github.com/docker/docker/api/types/container"
@ -42,12 +41,6 @@ type ServiceMeta struct {
ID string
}
const (
statusMode = iota
logsMode = iota
errorsMode = iota
)
type Model struct {
appName string
cl *dockerClient.Client
@ -56,10 +49,6 @@ type Model struct {
timeout time.Duration
width int
filters filters.Args
mode int
logsViewport viewport.Model
logsViewportReady bool
Streams *[]stream
Logs *[]string
@ -247,10 +236,7 @@ func deployTimeout(m Model) tea.Msg {
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var (
cmd tea.Cmd
cmds []tea.Cmd
)
var cmds []tea.Cmd
switch msg := msg.(type) {
case tea.KeyMsg:
@ -258,25 +244,11 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case "ctrl+c", "q":
m.Quit = true
return m, tea.Quit
case "s":
m.mode = statusMode
case "l":
m.mode = logsMode
case "e":
m.mode = errorsMode
}
case tea.WindowSizeMsg:
m.width = msg.Width
if !m.logsViewportReady {
m.logsViewport = viewport.New(msg.Width, 20)
m.logsViewportReady = true
} else {
m.logsViewport.Width = msg.Width
m.logsViewport.Height = 20
}
case progressCompleteMsg:
if msg.failed {
m.Failed = true
@ -284,9 +256,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.count += 1
// if m.complete() {
// return m, tea.Quit
// }
if m.complete() {
return m, tea.Quit
}
case timeoutMsg:
m.TimedOut = true
@ -346,46 +318,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
)
}
m.logsViewport, cmd = m.logsViewport.Update(msg)
cmds = append(cmds, cmd)
return m, tea.Batch(cmds...)
}
func (m Model) View() string {
body := strings.Builder{}
body.WriteString("menu: [s]tatus [l]ogs [e]rrors\n")
var res string
switch {
case m.mode == statusMode:
res = statusView(m)
case m.mode == logsMode:
res = logsView(m)
}
return body.String() + res
}
func logsView(m Model) string {
body := strings.Builder{}
m.logsViewport.SetContent(strings.Join(*m.Logs, "\n"))
m.logsViewport.GotoBottom()
body.WriteString(m.logsViewport.View())
return body.String()
}
func errorsView(m Model) string {
body := strings.Builder{}
body.WriteString("ERRORS COMING SOON")
return body.String()
}
func statusView(m Model) string {
body := strings.Builder{}
for _, stream := range *m.Streams {
split := strings.Split(stream.Name, "_")
short := split[len(split)-1]

View File

@ -201,6 +201,7 @@ func RunDeploy(
appName string,
serverName string,
dontWait bool,
noInput bool,
filters filters.Args,
) error {
log.Info(i18n.G("initialising deployment"))
@ -226,6 +227,7 @@ func RunDeploy(
appName,
serverName,
dontWait,
noInput,
filters,
)
}
@ -248,6 +250,7 @@ func deployCompose(
appName string,
serverName string,
dontWait bool,
noInput bool,
filters filters.Args,
) error {
namespace := convert.NewNamespace(opts.Namespace)
@ -311,6 +314,7 @@ func deployCompose(
Services: serviceIDs,
AppName: appName,
ServerName: serverName,
NoInput: noInput,
Filters: filters,
}
@ -561,6 +565,7 @@ func timestamp() string {
type WaitOpts struct {
AppName string
Filters filters.Args
NoInput bool
NoLog bool
Quiet bool
ServerName string
@ -570,7 +575,13 @@ type WaitOpts struct {
func WaitOnServices(ctx context.Context, cl *dockerClient.Client, opts WaitOpts) error {
timeout := time.Duration(WaitTimeout) * time.Second
model := ui.DeployInitialModel(ctx, cl, opts.Services, opts.AppName, timeout, opts.Filters)
tui := tea.NewProgram(model)
var tui *tea.Program
if opts.NoInput {
tui = tea.NewProgram(model, tea.WithoutRenderer(), tea.WithInput(nil))
} else {
tui = tea.NewProgram(model)
}
if !opts.Quiet {
log.Info(i18n.G("polling deployment status"))

View File

@ -1,8 +1,8 @@
#!/usr/bin/env bash
ABRA_VERSION="0.11.0-beta"
ABRA_VERSION="0.12.0-beta"
ABRA_RELEASE_URL="https://git.coopcloud.tech/api/v1/repos/toolshed/abra/releases/tags/$ABRA_VERSION"
RC_VERSION="0.11.0-beta"
RC_VERSION="0.12.0-beta"
RC_VERSION_URL="https://git.coopcloud.tech/api/v1/repos/toolshed/abra/releases/tags/$RC_VERSION"
for arg in "$@"; do
@ -14,15 +14,15 @@ done
function show_banner {
echo ""
echo " ____ ____ _ _ "
echo " / ___|___ ___ _ __ / ___| | ___ _ _ __| |"
echo " | | / _ \ _____ / _ \| '_ \ | | | |/ _ \| | | |/ _' |"
echo " | |__| (_) |_____| (_) | |_) | | |___| | (_) | |_| | (_| |"
echo " \____\___/ \___/| .__/ \____|_|\___/ \__,_|\__,_|"
echo " |_|"
echo " ____ ____ _ _ "
echo " / ___|___ ___ _ __ / ___| | ___ _ _ __| |"
echo " | | / _ \ ___ / _ \| '_ \ | | | |/ _ \| | | |/ _' |"
echo " | |__| (_) |___| (_) | |_) | | |___| | (_) | |_| | (_| |"
echo " \____\___/ \___/| .__/ \____|_|\___/ \__,_|\__,_|"
echo " |_|"
echo ""
echo ""
echo " === Public interest infrastructure === "
echo " === Public interest infrastructure === "
echo ""
echo ""
}

View File

@ -543,7 +543,7 @@ teardown(){
# bats test_tags=slow
@test "ignore timeout when not present in env" {
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --no-converge-checks --debug
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --no-converge-checks
assert_success
refute_output --partial "timeout: set to"
}
@ -554,6 +554,7 @@ teardown(){
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
assert_success
# NOTE(d1}: --debug required
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --no-converge-checks --debug
assert_success
assert_output --partial "timeout: set to 120"
@ -579,16 +580,25 @@ teardown(){
}
# bats test_tags=slow
@test "manually created server without context bails gracefully" {
run mkdir -p "$ABRA_DIR/servers/default2"
@test "re-deploy updates existing env vars" {
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input
assert_success
assert_exists "$ABRA_DIR/servers/default2"
run cp "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" "$ABRA_DIR/servers/default2/$TEST_APP_DOMAIN_2.env"
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
assert_success
assert_exists "$ABRA_DIR/servers/default2/$TEST_APP_DOMAIN_2.env"
assert_output --partial "WITH_COMMENT=foo"
run $ABRA app deploy "$TEST_APP_DOMAIN_2" --no-input --no-converge-checks
assert_failure
assert_output --partial "server missing context"
run sed -i 's/WITH_COMMENT=foo/WITH_COMMENT=bar/g' \
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
assert_success
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --force
assert_success
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
assert_success
refute_output --partial "WITH_COMMENT=foo"
assert_output --partial "WITH_COMMENT=bar"
}

View File

@ -68,6 +68,13 @@ teardown(){
assert_success
}
@test "domain shown with https" {
run $ABRA app deploy "$TEST_APP_DOMAIN" \
--no-input --no-converge-checks
assert_success
assert_output --partial "https://$TEST_DOMAIN"
}
# bats test_tags=slow
@test "show changed config version on re-deploy" {
run $ABRA app deploy "$TEST_APP_DOMAIN" \

View File

@ -160,23 +160,6 @@ teardown(){
assert_not_exists "$ABRA_DIR/servers/foo.com"
}
@test "list with status skips unknown servers" {
if [[ ! -d "$ABRA_DIR/servers/foo" ]]; then
run mkdir -p "$ABRA_DIR/servers/foo"
assert_success
assert_exists "$ABRA_DIR/servers/foo"
run cp "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env" \
"$ABRA_DIR/servers/foo/$TEST_APP_DOMAIN.env"
assert_success
assert_exists "$ABRA_DIR/servers/foo/$TEST_APP_DOMAIN.env"
fi
run $ABRA app ls --status
assert_success
assert_output --partial "server missing context"
}
# bats test_tags=slow
@test "list does not fail if missing .env" {
_deploy_app

View File

@ -19,6 +19,10 @@ setup(){
teardown(){
_reset_recipe
_reset_tags
if [[ -d "$ABRA_DIR/recipes/foobar" ]]; then
run rm -rf "$ABRA_DIR/recipes/foobar"
assert_success
fi
}
@test "validate recipe argument" {
@ -126,3 +130,71 @@ teardown(){
assert_line --index 0 --partial 'synced label'
refute_line --index 1 --partial 'synced label'
}
@test "sync with no tags or previous release" {
_remove_tags
run $ABRA recipe upgrade "$TEST_RECIPE" --no-input --patch
assert_success
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" diff
assert_success
assert_output --partial 'image: nginx:1.21.6'
# NOTE(d1): ensure the latest tag is the one we expect
_remove_tags
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" tag \
-a "0.3.0+1.21.0" -m "fake: 0.3.0+1.21.0"
assert_success
run $ABRA recipe sync "$TEST_RECIPE" --no-input --patch
assert_success
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" diff
assert_success
assert_output --regexp 'coop-cloud\.\$\{STACK_NAME\}\.version=0\.3\.1\+1\.2.*'
}
@test "sync recipe without input fails with prompt" {
run $ABRA recipe new foobar
assert_success
assert_exists "$ABRA_DIR/recipes/foobar"
run $ABRA recipe sync foobar --no-input --patch
assert_failure
assert_output --partial "input required for initial version"
}
@test "sync new recipe: development release" {
run $ABRA recipe new foobar
assert_success
assert_exists "$ABRA_DIR/recipes/foobar"
run bash -c "echo 0.1.0 | $ABRA recipe sync foobar --patch"
assert_success
assert_output --regexp 'coop-cloud\.\$\{STACK_NAME\}\.version=0\.1\.0\+1\.2.*'
}
@test "sync new recipe: public release" {
run $ABRA recipe new foobar
assert_success
assert_exists "$ABRA_DIR/recipes/foobar"
run bash -c "echo 1.0.0 | $ABRA recipe sync foobar --patch"
assert_success
assert_output --regexp 'coop-cloud\.\$\{STACK_NAME\}\.version=1\.0\.0\+1\.2.*'
}
@test "sync newly created recipe with no version label" {
run $ABRA recipe new foobar
assert_success
assert_exists "$ABRA_DIR/recipes/foobar"
run sed -i 's/- "coop-cloud.${STACK_NAME}.version="/#- "coop-cloud.${STACK_NAME}.version="/g' \
"$ABRA_DIR/recipes/foobar/compose.yml"
assert_success
run bash -c "echo 0.1.0 | $ABRA recipe sync foobar --patch"
assert_failure
assert_output --partial "automagic insertion not supported yet"
}

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020-2023 Charmbracelet, Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,140 +0,0 @@
// Package key provides some types and functions for generating user-definable
// keymappings useful in Bubble Tea components. There are a few different ways
// you can define a keymapping with this package. Here's one example:
//
// type KeyMap struct {
// Up key.Binding
// Down key.Binding
// }
//
// var DefaultKeyMap = KeyMap{
// Up: key.NewBinding(
// key.WithKeys("k", "up"), // actual keybindings
// key.WithHelp("↑/k", "move up"), // corresponding help text
// ),
// Down: key.NewBinding(
// key.WithKeys("j", "down"),
// key.WithHelp("↓/j", "move down"),
// ),
// }
//
// func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// switch msg := msg.(type) {
// case tea.KeyMsg:
// switch {
// case key.Matches(msg, DefaultKeyMap.Up):
// // The user pressed up
// case key.Matches(msg, DefaultKeyMap.Down):
// // The user pressed down
// }
// }
//
// // ...
// }
//
// The help information, which is not used in the example above, can be used
// to render help text for keystrokes in your views.
package key
import "fmt"
// Binding describes a set of keybindings and, optionally, their associated
// help text.
type Binding struct {
keys []string
help Help
disabled bool
}
// BindingOpt is an initialization option for a keybinding. It's used as an
// argument to NewBinding.
type BindingOpt func(*Binding)
// NewBinding returns a new keybinding from a set of BindingOpt options.
func NewBinding(opts ...BindingOpt) Binding {
b := &Binding{}
for _, opt := range opts {
opt(b)
}
return *b
}
// WithKeys initializes a keybinding with the given keystrokes.
func WithKeys(keys ...string) BindingOpt {
return func(b *Binding) {
b.keys = keys
}
}
// WithHelp initializes a keybinding with the given help text.
func WithHelp(key, desc string) BindingOpt {
return func(b *Binding) {
b.help = Help{Key: key, Desc: desc}
}
}
// WithDisabled initializes a disabled keybinding.
func WithDisabled() BindingOpt {
return func(b *Binding) {
b.disabled = true
}
}
// SetKeys sets the keys for the keybinding.
func (b *Binding) SetKeys(keys ...string) {
b.keys = keys
}
// Keys returns the keys for the keybinding.
func (b Binding) Keys() []string {
return b.keys
}
// SetHelp sets the help text for the keybinding.
func (b *Binding) SetHelp(key, desc string) {
b.help = Help{Key: key, Desc: desc}
}
// Help returns the Help information for the keybinding.
func (b Binding) Help() Help {
return b.help
}
// Enabled returns whether or not the keybinding is enabled. Disabled
// keybindings won't be activated and won't show up in help. Keybindings are
// enabled by default.
func (b Binding) Enabled() bool {
return !b.disabled && b.keys != nil
}
// SetEnabled enables or disables the keybinding.
func (b *Binding) SetEnabled(v bool) {
b.disabled = !v
}
// Unbind removes the keys and help from this binding, effectively nullifying
// it. This is a step beyond disabling it, since applications can enable
// or disable key bindings based on application state.
func (b *Binding) Unbind() {
b.keys = nil
b.help = Help{}
}
// Help is help information for a given keybinding.
type Help struct {
Key string
Desc string
}
// Matches checks if the given key matches the given bindings.
func Matches[Key fmt.Stringer](k Key, b ...Binding) bool {
keys := k.String()
for _, binding := range b {
for _, v := range binding.keys {
if keys == v && binding.Enabled() {
return true
}
}
}
return false
}

View File

@ -1,60 +0,0 @@
// Package viewport provides a component for rendering a viewport in a Bubble
// Tea.
package viewport
import "github.com/charmbracelet/bubbles/key"
const spacebar = " "
// KeyMap defines the keybindings for the viewport. Note that you don't
// necessary need to use keybindings at all; the viewport can be controlled
// programmatically with methods like Model.LineDown(1). See the GoDocs for
// details.
type KeyMap struct {
PageDown key.Binding
PageUp key.Binding
HalfPageUp key.Binding
HalfPageDown key.Binding
Down key.Binding
Up key.Binding
Left key.Binding
Right key.Binding
}
// DefaultKeyMap returns a set of pager-like default keybindings.
func DefaultKeyMap() KeyMap {
return KeyMap{
PageDown: key.NewBinding(
key.WithKeys("pgdown", spacebar, "f"),
key.WithHelp("f/pgdn", "page down"),
),
PageUp: key.NewBinding(
key.WithKeys("pgup", "b"),
key.WithHelp("b/pgup", "page up"),
),
HalfPageUp: key.NewBinding(
key.WithKeys("u", "ctrl+u"),
key.WithHelp("u", "½ page up"),
),
HalfPageDown: key.NewBinding(
key.WithKeys("d", "ctrl+d"),
key.WithHelp("d", "½ page down"),
),
Up: key.NewBinding(
key.WithKeys("up", "k"),
key.WithHelp("↑/k", "up"),
),
Down: key.NewBinding(
key.WithKeys("down", "j"),
key.WithHelp("↓/j", "down"),
),
Left: key.NewBinding(
key.WithKeys("left", "h"),
key.WithHelp("←/h", "move left"),
),
Right: key.NewBinding(
key.WithKeys("right", "l"),
key.WithHelp("→/l", "move right"),
),
}
}

View File

@ -1,544 +0,0 @@
package viewport
import (
"math"
"strings"
"github.com/charmbracelet/bubbles/key"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/x/ansi"
)
// New returns a new model with the given width and height as well as default
// key mappings.
func New(width, height int) (m Model) {
m.Width = width
m.Height = height
m.setInitialValues()
return m
}
// Model is the Bubble Tea model for this viewport element.
type Model struct {
Width int
Height int
KeyMap KeyMap
// Whether or not to respond to the mouse. The mouse must be enabled in
// Bubble Tea for this to work. For details, see the Bubble Tea docs.
MouseWheelEnabled bool
// The number of lines the mouse wheel will scroll. By default, this is 3.
MouseWheelDelta int
// YOffset is the vertical scroll position.
YOffset int
// xOffset is the horizontal scroll position.
xOffset int
// horizontalStep is the number of columns we move left or right during a
// default horizontal scroll.
horizontalStep int
// YPosition is the position of the viewport in relation to the terminal
// window. It's used in high performance rendering only.
YPosition int
// Style applies a lipgloss style to the viewport. Realistically, it's most
// useful for setting borders, margins and padding.
Style lipgloss.Style
// HighPerformanceRendering bypasses the normal Bubble Tea renderer to
// provide higher performance rendering. Most of the time the normal Bubble
// Tea rendering methods will suffice, but if you're passing content with
// a lot of ANSI escape codes you may see improved rendering in certain
// terminals with this enabled.
//
// This should only be used in program occupying the entire terminal,
// which is usually via the alternate screen buffer.
//
// Deprecated: high performance rendering is now deprecated in Bubble Tea.
HighPerformanceRendering bool
initialized bool
lines []string
longestLineWidth int
}
func (m *Model) setInitialValues() {
m.KeyMap = DefaultKeyMap()
m.MouseWheelEnabled = true
m.MouseWheelDelta = 3
m.initialized = true
}
// Init exists to satisfy the tea.Model interface for composability purposes.
func (m Model) Init() tea.Cmd {
return nil
}
// AtTop returns whether or not the viewport is at the very top position.
func (m Model) AtTop() bool {
return m.YOffset <= 0
}
// AtBottom returns whether or not the viewport is at or past the very bottom
// position.
func (m Model) AtBottom() bool {
return m.YOffset >= m.maxYOffset()
}
// PastBottom returns whether or not the viewport is scrolled beyond the last
// line. This can happen when adjusting the viewport height.
func (m Model) PastBottom() bool {
return m.YOffset > m.maxYOffset()
}
// ScrollPercent returns the amount scrolled as a float between 0 and 1.
func (m Model) ScrollPercent() float64 {
if m.Height >= len(m.lines) {
return 1.0
}
y := float64(m.YOffset)
h := float64(m.Height)
t := float64(len(m.lines))
v := y / (t - h)
return math.Max(0.0, math.Min(1.0, v))
}
// HorizontalScrollPercent returns the amount horizontally scrolled as a float
// between 0 and 1.
func (m Model) HorizontalScrollPercent() float64 {
if m.xOffset >= m.longestLineWidth-m.Width {
return 1.0
}
y := float64(m.xOffset)
h := float64(m.Width)
t := float64(m.longestLineWidth)
v := y / (t - h)
return math.Max(0.0, math.Min(1.0, v))
}
// SetContent set the pager's text content.
func (m *Model) SetContent(s string) {
s = strings.ReplaceAll(s, "\r\n", "\n") // normalize line endings
m.lines = strings.Split(s, "\n")
m.longestLineWidth = findLongestLineWidth(m.lines)
if m.YOffset > len(m.lines)-1 {
m.GotoBottom()
}
}
// maxYOffset returns the maximum possible value of the y-offset based on the
// viewport's content and set height.
func (m Model) maxYOffset() int {
return max(0, len(m.lines)-m.Height+m.Style.GetVerticalFrameSize())
}
// visibleLines returns the lines that should currently be visible in the
// viewport.
func (m Model) visibleLines() (lines []string) {
h := m.Height - m.Style.GetVerticalFrameSize()
w := m.Width - m.Style.GetHorizontalFrameSize()
if len(m.lines) > 0 {
top := max(0, m.YOffset)
bottom := clamp(m.YOffset+h, top, len(m.lines))
lines = m.lines[top:bottom]
}
if (m.xOffset == 0 && m.longestLineWidth <= w) || w == 0 {
return lines
}
cutLines := make([]string, len(lines))
for i := range lines {
cutLines[i] = ansi.Cut(lines[i], m.xOffset, m.xOffset+w)
}
return cutLines
}
// scrollArea returns the scrollable boundaries for high performance rendering.
//
// Deprecated: high performance rendering is deprecated in Bubble Tea.
func (m Model) scrollArea() (top, bottom int) {
top = max(0, m.YPosition)
bottom = max(top, top+m.Height)
if top > 0 && bottom > top {
bottom--
}
return top, bottom
}
// SetYOffset sets the Y offset.
func (m *Model) SetYOffset(n int) {
m.YOffset = clamp(n, 0, m.maxYOffset())
}
// ViewDown moves the view down by the number of lines in the viewport.
// Basically, "page down".
//
// Deprecated: use [Model.PageDown] instead.
func (m *Model) ViewDown() []string {
return m.PageDown()
}
// PageDown moves the view down by the number of lines in the viewport.
func (m *Model) PageDown() []string {
if m.AtBottom() {
return nil
}
return m.ScrollDown(m.Height)
}
// ViewUp moves the view up by one height of the viewport.
// Basically, "page up".
//
// Deprecated: use [Model.PageUp] instead.
func (m *Model) ViewUp() []string {
return m.PageUp()
}
// PageUp moves the view up by one height of the viewport.
func (m *Model) PageUp() []string {
if m.AtTop() {
return nil
}
return m.ScrollUp(m.Height)
}
// HalfViewDown moves the view down by half the height of the viewport.
//
// Deprecated: use [Model.HalfPageDown] instead.
func (m *Model) HalfViewDown() (lines []string) {
return m.HalfPageDown()
}
// HalfPageDown moves the view down by half the height of the viewport.
func (m *Model) HalfPageDown() (lines []string) {
if m.AtBottom() {
return nil
}
return m.ScrollDown(m.Height / 2) //nolint:mnd
}
// HalfViewUp moves the view up by half the height of the viewport.
//
// Deprecated: use [Model.HalfPageUp] instead.
func (m *Model) HalfViewUp() (lines []string) {
return m.HalfPageUp()
}
// HalfPageUp moves the view up by half the height of the viewport.
func (m *Model) HalfPageUp() (lines []string) {
if m.AtTop() {
return nil
}
return m.ScrollUp(m.Height / 2) //nolint:mnd
}
// LineDown moves the view down by the given number of lines.
//
// Deprecated: use [Model.ScrollDown] instead.
func (m *Model) LineDown(n int) (lines []string) {
return m.ScrollDown(n)
}
// ScrollDown moves the view down by the given number of lines.
func (m *Model) ScrollDown(n int) (lines []string) {
if m.AtBottom() || n == 0 || len(m.lines) == 0 {
return nil
}
// Make sure the number of lines by which we're going to scroll isn't
// greater than the number of lines we actually have left before we reach
// the bottom.
m.SetYOffset(m.YOffset + n)
// Gather lines to send off for performance scrolling.
//
// XXX: high performance rendering is deprecated in Bubble Tea.
bottom := clamp(m.YOffset+m.Height, 0, len(m.lines))
top := clamp(m.YOffset+m.Height-n, 0, bottom)
return m.lines[top:bottom]
}
// LineUp moves the view down by the given number of lines. Returns the new
// lines to show.
//
// Deprecated: use [Model.ScrollUp] instead.
func (m *Model) LineUp(n int) (lines []string) {
return m.ScrollUp(n)
}
// ScrollUp moves the view down by the given number of lines. Returns the new
// lines to show.
func (m *Model) ScrollUp(n int) (lines []string) {
if m.AtTop() || n == 0 || len(m.lines) == 0 {
return nil
}
// Make sure the number of lines by which we're going to scroll isn't
// greater than the number of lines we are from the top.
m.SetYOffset(m.YOffset - n)
// Gather lines to send off for performance scrolling.
//
// XXX: high performance rendering is deprecated in Bubble Tea.
top := max(0, m.YOffset)
bottom := clamp(m.YOffset+n, 0, m.maxYOffset())
return m.lines[top:bottom]
}
// SetHorizontalStep sets the default amount of columns to scroll left or right
// with the default viewport key map.
//
// If set to 0 or less, horizontal scrolling is disabled.
//
// On v1, horizontal scrolling is disabled by default.
func (m *Model) SetHorizontalStep(n int) {
m.horizontalStep = max(n, 0)
}
// SetXOffset sets the X offset.
func (m *Model) SetXOffset(n int) {
m.xOffset = clamp(n, 0, m.longestLineWidth-m.Width)
}
// ScrollLeft moves the viewport to the left by the given number of columns.
func (m *Model) ScrollLeft(n int) {
m.SetXOffset(m.xOffset - n)
}
// ScrollRight moves viewport to the right by the given number of columns.
func (m *Model) ScrollRight(n int) {
m.SetXOffset(m.xOffset + n)
}
// TotalLineCount returns the total number of lines (both hidden and visible) within the viewport.
func (m Model) TotalLineCount() int {
return len(m.lines)
}
// VisibleLineCount returns the number of the visible lines within the viewport.
func (m Model) VisibleLineCount() int {
return len(m.visibleLines())
}
// GotoTop sets the viewport to the top position.
func (m *Model) GotoTop() (lines []string) {
if m.AtTop() {
return nil
}
m.SetYOffset(0)
return m.visibleLines()
}
// GotoBottom sets the viewport to the bottom position.
func (m *Model) GotoBottom() (lines []string) {
m.SetYOffset(m.maxYOffset())
return m.visibleLines()
}
// Sync tells the renderer where the viewport will be located and requests
// a render of the current state of the viewport. It should be called for the
// first render and after a window resize.
//
// For high performance rendering only.
//
// Deprecated: high performance rendering is deprecated in Bubble Tea.
func Sync(m Model) tea.Cmd {
if len(m.lines) == 0 {
return nil
}
top, bottom := m.scrollArea()
return tea.SyncScrollArea(m.visibleLines(), top, bottom)
}
// ViewDown is a high performance command that moves the viewport up by a given
// number of lines. Use Model.ViewDown to get the lines that should be rendered.
// For example:
//
// lines := model.ViewDown(1)
// cmd := ViewDown(m, lines)
//
// Deprecated: high performance rendering is deprecated in Bubble Tea.
func ViewDown(m Model, lines []string) tea.Cmd {
if len(lines) == 0 {
return nil
}
top, bottom := m.scrollArea()
// XXX: high performance rendering is deprecated in Bubble Tea. In a v2 we
// won't need to return a command here.
return tea.ScrollDown(lines, top, bottom)
}
// ViewUp is a high performance command the moves the viewport down by a given
// number of lines height. Use Model.ViewUp to get the lines that should be
// rendered.
//
// Deprecated: high performance rendering is deprecated in Bubble Tea.
func ViewUp(m Model, lines []string) tea.Cmd {
if len(lines) == 0 {
return nil
}
top, bottom := m.scrollArea()
// XXX: high performance rendering is deprecated in Bubble Tea. In a v2 we
// won't need to return a command here.
return tea.ScrollUp(lines, top, bottom)
}
// Update handles standard message-based viewport updates.
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
var cmd tea.Cmd
m, cmd = m.updateAsModel(msg)
return m, cmd
}
// Author's note: this method has been broken out to make it easier to
// potentially transition Update to satisfy tea.Model.
func (m Model) updateAsModel(msg tea.Msg) (Model, tea.Cmd) {
if !m.initialized {
m.setInitialValues()
}
var cmd tea.Cmd
switch msg := msg.(type) {
case tea.KeyMsg:
switch {
case key.Matches(msg, m.KeyMap.PageDown):
lines := m.PageDown()
if m.HighPerformanceRendering {
cmd = ViewDown(m, lines)
}
case key.Matches(msg, m.KeyMap.PageUp):
lines := m.PageUp()
if m.HighPerformanceRendering {
cmd = ViewUp(m, lines)
}
case key.Matches(msg, m.KeyMap.HalfPageDown):
lines := m.HalfPageDown()
if m.HighPerformanceRendering {
cmd = ViewDown(m, lines)
}
case key.Matches(msg, m.KeyMap.HalfPageUp):
lines := m.HalfPageUp()
if m.HighPerformanceRendering {
cmd = ViewUp(m, lines)
}
case key.Matches(msg, m.KeyMap.Down):
lines := m.ScrollDown(1)
if m.HighPerformanceRendering {
cmd = ViewDown(m, lines)
}
case key.Matches(msg, m.KeyMap.Up):
lines := m.ScrollUp(1)
if m.HighPerformanceRendering {
cmd = ViewUp(m, lines)
}
case key.Matches(msg, m.KeyMap.Left):
m.ScrollLeft(m.horizontalStep)
case key.Matches(msg, m.KeyMap.Right):
m.ScrollRight(m.horizontalStep)
}
case tea.MouseMsg:
if !m.MouseWheelEnabled || msg.Action != tea.MouseActionPress {
break
}
switch msg.Button { //nolint:exhaustive
case tea.MouseButtonWheelUp:
if msg.Shift {
// Note that not every terminal emulator sends the shift event for mouse actions by default (looking at you Konsole)
m.ScrollLeft(m.horizontalStep)
} else {
lines := m.ScrollUp(m.MouseWheelDelta)
if m.HighPerformanceRendering {
cmd = ViewUp(m, lines)
}
}
case tea.MouseButtonWheelDown:
if msg.Shift {
m.ScrollRight(m.horizontalStep)
} else {
lines := m.ScrollDown(m.MouseWheelDelta)
if m.HighPerformanceRendering {
cmd = ViewDown(m, lines)
}
}
// Note that not every terminal emulator sends the horizontal wheel events by default (looking at you Konsole)
case tea.MouseButtonWheelLeft:
m.ScrollLeft(m.horizontalStep)
case tea.MouseButtonWheelRight:
m.ScrollRight(m.horizontalStep)
}
}
return m, cmd
}
// View renders the viewport into a string.
func (m Model) View() string {
if m.HighPerformanceRendering {
// Just send newlines since we're going to be rendering the actual
// content separately. We still need to send something that equals the
// height of this view so that the Bubble Tea standard renderer can
// position anything below this view properly.
return strings.Repeat("\n", max(0, m.Height-1))
}
w, h := m.Width, m.Height
if sw := m.Style.GetWidth(); sw != 0 {
w = min(w, sw)
}
if sh := m.Style.GetHeight(); sh != 0 {
h = min(h, sh)
}
contentWidth := w - m.Style.GetHorizontalFrameSize()
contentHeight := h - m.Style.GetVerticalFrameSize()
contents := lipgloss.NewStyle().
Width(contentWidth). // pad to width.
Height(contentHeight). // pad to height.
MaxHeight(contentHeight). // truncate height if taller.
MaxWidth(contentWidth). // truncate width if wider.
Render(strings.Join(m.visibleLines(), "\n"))
return m.Style.
UnsetWidth().UnsetHeight(). // Style size already applied in contents.
Render(contents)
}
func clamp(v, low, high int) int {
if high < low {
low, high = high, low
}
return min(high, max(low, v))
}
func findLongestLineWidth(lines []string) int {
w := 0
for _, l := range lines {
if ww := ansi.StringWidth(l); ww > w {
w = ww
}
}
return w
}

6
vendor/modules.txt vendored
View File

@ -65,10 +65,6 @@ github.com/cenkalti/backoff/v5
# github.com/cespare/xxhash/v2 v2.3.0
## explicit; go 1.11
github.com/cespare/xxhash/v2
# github.com/charmbracelet/bubbles v0.21.0
## explicit; go 1.23.0
github.com/charmbracelet/bubbles/key
github.com/charmbracelet/bubbles/viewport
# github.com/charmbracelet/bubbletea v1.3.10
## explicit; go 1.24.0
github.com/charmbracelet/bubbletea
@ -89,6 +85,8 @@ github.com/charmbracelet/x/ansi/parser
# github.com/charmbracelet/x/cellbuf v0.0.13
## explicit; go 1.18
github.com/charmbracelet/x/cellbuf
# github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91
## explicit; go 1.19
# github.com/charmbracelet/x/term v0.2.1
## explicit; go 1.18
github.com/charmbracelet/x/term