diff --git a/Makefile b/Makefile index d103ae4c..f7051d70 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,10 @@ GOVERSION := 1.24 LDFLAGS := "-X 'main.Commit=$(COMMIT)'" DIST_LDFLAGS := $(LDFLAGS)" -s -w" GCFLAGS := "all=-l -B" +DOMAIN := abra +POFILES := $(wildcard pkg/i18n/locales/*.po) +MOFILES := $(patsubst %.po,%.mo,$(POFILES)) +LINGUAS := $(basename $(POFILES)) export GOPRIVATE=coopcloud.tech @@ -59,3 +63,32 @@ loc: deps: @go get -t -u ./... + +.PHONY: i18n +i18n: update-pot update-pot-po-metadata update-po build-mo + +.PHONY: update-po +update-po: + @set -eu; \ + for lang in $(LINGUAS); do \ + msgmerge --backup=none -U $$lang.po pkg/i18n/locales/$(DOMAIN).pot; \ + done + +.PHONY: update-pot +update-pot: + @xgettext-go \ + -o pkg/i18n/locales/$(DOMAIN).pot \ + --keyword=i18n.G \ + --sort-output \ + $$(find . -name "*.go" -not -path "*vendor*") + +.PHONY: update-pot-po-metadata +update-pot-po-metadata: + @sed -i "s/charset=CHARSET/charset=UTF-8/g" pkg/i18n/locales/*.po* + +.PHONY: build-mo +build-mo: + @set -eu; \ + for lang in $(POFILES); do \ + msgfmt $$lang -o $$(echo $$lang | sed 's/.po/.mo/g') --statistics; \ + done diff --git a/README.md b/README.md index 5ae21bbe..112d0abb 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Build Status](https://build.coopcloud.tech/api/badges/toolshed/abra/status.svg?ref=refs/heads/main)](https://build.coopcloud.tech/toolshed/abra) [![Go Report Card](https://goreportcard.com/badge/git.coopcloud.tech/toolshed/abra)](https://goreportcard.com/report/git.coopcloud.tech/toolshed/abra) [![Go Reference](https://pkg.go.dev/badge/coopcloud.tech/abra.svg)](https://pkg.go.dev/coopcloud.tech/abra) +[![Translation status](https://translate.coopcloud.tech/widget/co-op-cloud/svg-badge.svg)](https://translate.coopcloud.tech/engage/co-op-cloud/) The Co-op Cloud utility belt 🎩🐇 diff --git a/cli/app/app.go b/cli/app/app.go index 259dadfc..59f31165 100644 --- a/cli/app/app.go +++ b/cli/app/app.go @@ -1,12 +1,12 @@ package app import ( - "github.com/leonelquinteros/gotext" + "coopcloud.tech/abra/pkg/i18n" "github.com/spf13/cobra" ) var AppCommand = &cobra.Command{ - Use: "app [cmd] [args] [flags]", - Aliases: []string{"a"}, - Short: gotext.Get("Manage apps"), + Use: i18n.G("app [cmd] [args] [flags]"), + Aliases: []string{i18n.G("a")}, + Short: i18n.G("Manage apps"), } diff --git a/cli/app/backup.go b/cli/app/backup.go index 83083172..13d70d7e 100644 --- a/cli/app/backup.go +++ b/cli/app/backup.go @@ -6,14 +6,15 @@ import ( "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/spf13/cobra" ) var AppBackupListCommand = &cobra.Command{ - Use: "list [flags]", - Aliases: []string{"ls"}, - Short: "List the contents of a snapshot", + Use: i18n.G("list [flags]"), + Aliases: []string{i18n.G("ls")}, + Short: i18n.G("List the contents of a snapshot"), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -40,17 +41,17 @@ var AppBackupListCommand = &cobra.Command{ } if snapshot != "" { - log.Debugf("including SNAPSHOT=%s in backupbot exec invocation", snapshot) + log.Debug(i18n.G("including SNAPSHOT=%s in backupbot exec invocation", snapshot)) execEnv = append(execEnv, fmt.Sprintf("SNAPSHOT=%s", snapshot)) } if showAllPaths { - log.Debugf("including SHOW_ALL=%v in backupbot exec invocation", showAllPaths) + log.Debug(i18n.G("including SHOW_ALL=%v in backupbot exec invocation", showAllPaths)) execEnv = append(execEnv, fmt.Sprintf("SHOW_ALL=%v", showAllPaths)) } if timestamps { - log.Debugf("including TIMESTAMPS=%v in backupbot exec invocation", timestamps) + log.Debug(i18n.G("including TIMESTAMPS=%v in backupbot exec invocation", timestamps)) execEnv = append(execEnv, fmt.Sprintf("TIMESTAMPS=%v", timestamps)) } @@ -61,13 +62,13 @@ var AppBackupListCommand = &cobra.Command{ } var AppBackupDownloadCommand = &cobra.Command{ - Use: "download [flags]", - Aliases: []string{"d"}, - Short: "Download a snapshot", - Long: `Downloads a backup.tar.gz to the current working directory. + Use: i18n.G("download [flags]"), + Aliases: []string{i18n.G("d")}, + Short: i18n.G("Download a snapshot"), + Long: i18n.G(`Downloads a backup.tar.gz to the current working directory. "--volumes/-v" includes data contained in volumes alongide paths specified in -"backupbot.backup.path" labels.`, +"backupbot.backup.path" labels.`), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -98,22 +99,22 @@ var AppBackupDownloadCommand = &cobra.Command{ } if snapshot != "" { - log.Debugf("including SNAPSHOT=%s in backupbot exec invocation", snapshot) + log.Debug(i18n.G("including SNAPSHOT=%s in backupbot exec invocation", snapshot)) execEnv = append(execEnv, fmt.Sprintf("SNAPSHOT=%s", snapshot)) } if includePath != "" { - log.Debugf("including INCLUDE_PATH=%s in backupbot exec invocation", includePath) + log.Debug(i18n.G("including INCLUDE_PATH=%s in backupbot exec invocation", includePath)) execEnv = append(execEnv, fmt.Sprintf("INCLUDE_PATH=%s", includePath)) } if includeSecrets { - log.Debugf("including SECRETS=%v in backupbot exec invocation", includeSecrets) + log.Debug(i18n.G("including SECRETS=%v in backupbot exec invocation", includeSecrets)) execEnv = append(execEnv, fmt.Sprintf("SECRETS=%v", includeSecrets)) } if includeVolumes { - log.Debugf("including VOLUMES=%v in backupbot exec invocation", includeVolumes) + log.Debug(i18n.G("including VOLUMES=%v in backupbot exec invocation", includeVolumes)) execEnv = append(execEnv, fmt.Sprintf("VOLUMES=%v", includeVolumes)) } @@ -130,9 +131,9 @@ var AppBackupDownloadCommand = &cobra.Command{ } var AppBackupCreateCommand = &cobra.Command{ - Use: "create [flags]", - Aliases: []string{"c"}, - Short: "Create a new snapshot", + Use: i18n.G("create [flags]"), + Aliases: []string{i18n.G("c")}, + Short: i18n.G("Create a new snapshot"), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -163,7 +164,7 @@ var AppBackupCreateCommand = &cobra.Command{ } if retries != "" { - log.Debugf("including RETRIES=%s in backupbot exec invocation", retries) + log.Debug(i18n.G("including RETRIES=%s in backupbot exec invocation", retries)) execEnv = append(execEnv, fmt.Sprintf("RETRIES=%s", retries)) } @@ -174,9 +175,9 @@ var AppBackupCreateCommand = &cobra.Command{ } var AppBackupSnapshotsCommand = &cobra.Command{ - Use: "snapshots [flags]", - Aliases: []string{"s"}, - Short: "List all snapshots", + Use: i18n.G("snapshots [flags]"), + Aliases: []string{i18n.G("s")}, + Short: i18n.G("List all snapshots"), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -209,9 +210,9 @@ var AppBackupSnapshotsCommand = &cobra.Command{ } var AppBackupCommand = &cobra.Command{ - Use: "backup [cmd] [args] [flags]", - Aliases: []string{"b"}, - Short: "Manage app backups", + Use: i18n.G("backup [cmd] [args] [flags]"), + Aliases: []string{i18n.G("b")}, + Short: i18n.G("Manage app backups"), } var ( @@ -227,81 +228,81 @@ var ( func init() { AppBackupListCommand.Flags().StringVarP( &snapshot, - "snapshot", - "s", + i18n.G("snapshot"), + i18n.G("s"), "", - "list specific snapshot", + i18n.G("list specific snapshot"), ) AppBackupListCommand.Flags().BoolVarP( &showAllPaths, - "all", - "a", + i18n.G("all"), + i18n.G("a"), false, - "show all paths", + i18n.G("show all paths"), ) AppBackupListCommand.Flags().BoolVarP( ×tamps, - "timestamps", - "t", + i18n.G("timestamps"), + i18n.G("t"), false, - "include timestamps", + i18n.G("include timestamps"), ) AppBackupDownloadCommand.Flags().StringVarP( &snapshot, - "snapshot", - "s", + i18n.G("snapshot"), + i18n.G("s"), "", - "list specific snapshot", + i18n.G("list specific snapshot"), ) AppBackupDownloadCommand.Flags().StringVarP( &includePath, - "path", - "p", + i18n.G("path"), + i18n.G("p"), "", - "volumes path", + i18n.G("volumes path"), ) AppBackupDownloadCommand.Flags().BoolVarP( &includeSecrets, - "secrets", - "S", + i18n.G("secrets"), + i18n.G("S"), false, - "include secrets", + i18n.G("include secrets"), ) AppBackupDownloadCommand.Flags().BoolVarP( &includeVolumes, - "volumes", - "v", + i18n.G("volumes"), + i18n.G("v"), false, - "include volumes", + i18n.G("include volumes"), ) AppBackupDownloadCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) AppBackupCreateCommand.Flags().StringVarP( &retries, - "retries", - "r", + i18n.G("retries"), + i18n.G("r"), "1", - "number of retry attempts", + i18n.G("number of retry attempts"), ) AppBackupCreateCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) } diff --git a/cli/app/check.go b/cli/app/check.go index dde31b93..fab92047 100644 --- a/cli/app/check.go +++ b/cli/app/check.go @@ -7,16 +7,17 @@ import ( appPkg "coopcloud.tech/abra/pkg/app" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/charmbracelet/lipgloss" "github.com/spf13/cobra" ) var AppCheckCommand = &cobra.Command{ - Use: "check [flags]", - Aliases: []string{"chk"}, - Short: "Ensure an app is well configured", - Long: `Compare env vars in both the app ".env" and recipe ".env.sample" file. + Use: i18n.G("check [flags]"), + Aliases: []string{i18n.G("chk")}, + Short: i18n.G("Ensure an app is well configured"), + Long: i18n.G(`Compare env vars in both the app ".env" and recipe ".env.sample" file. The goal is to ensure that recipe ".env.sample" env vars are defined in your app ".env" file. Only env var definitions in the ".env.sample" which are @@ -25,7 +26,7 @@ these env vars, then "check" will complain. Recipe maintainers may or may not provide defaults for env vars within their recipes regardless of commenting or not (e.g. through the use of -${FOO:} syntax). "check" does not confirm or deny this for you.`, +${FOO:} syntax). "check" does not confirm or deny this for you.`), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -83,9 +84,9 @@ ${FOO:} syntax). "check" does not confirm or deny this for you.`, func init() { AppCheckCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) } diff --git a/cli/app/cmd.go b/cli/app/cmd.go index a8e2e17f..00477fe0 100644 --- a/cli/app/cmd.go +++ b/cli/app/cmd.go @@ -13,15 +13,16 @@ import ( appPkg "coopcloud.tech/abra/pkg/app" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/spf13/cobra" ) var AppCmdCommand = &cobra.Command{ - Use: "command [service | --local] [[args] [flags] | [flags] -- [args]]", - Aliases: []string{"cmd"}, - Short: "Run app commands", - Long: `Run an app specific command. + Use: i18n.G("command [service | --local] [[args] [flags] | [flags] -- [args]]"), + Aliases: []string{i18n.G("cmd")}, + Short: i18n.G("Run app commands"), + Long: i18n.G(`Run an app specific command. These commands are bash functions, defined in the abra.sh of the recipe itself. They can be run within the context of a service (e.g. app) or locally on your @@ -30,24 +31,24 @@ work station by passing "--local/-l". N.B. If using the "--" style to pass arguments, flags (e.g. "--local/-l") must be passed *before* the "--". It is possible to pass arguments without the "--" as long as no dashes are present (i.e. "foo" works without "--", "-foo" -does not).`, - Example: ` # pass args/flags without "--" +does not).`), + Example: i18n.G(` # pass args/flags without "--" abra app cmd 1312.net app my_cmd_arg foo --user bar # pass args/flags with "--" abra app cmd 1312.net app my_cmd_args --user bar -- foo -vvv # drop the [service] arg if using "--local/-l" - abra app cmd 1312.net my_cmd --local`, + abra app cmd 1312.net my_cmd --local`), Args: func(cmd *cobra.Command, args []string) error { if local { if !(len(args) >= 2) { - return errors.New("requires at least 2 arguments with --local/-l") + return errors.New(i18n.G("requires at least 2 arguments with --local/-l")) } if slices.Contains(os.Args, "--") { if cmd.ArgsLenAtDash() > 2 { - return errors.New("accepts at most 2 args with --local/-l") + return errors.New(i18n.G("accepts at most 2 args with --local/-l")) } } @@ -63,7 +64,7 @@ does not).`, } if !(len(args) >= 3) { - return errors.New("requires at least 3 arguments") + return errors.New(i18n.G("requires at least 3 arguments")) } return nil @@ -97,14 +98,14 @@ does not).`, } if local && remoteUser != "" { - log.Fatal("cannot use --local & --user together") + log.Fatal(i18n.G("cannot use --local & --user together")) } hasCmdArgs, parsedCmdArgs := parseCmdArgs(args, local) if _, err := os.Stat(app.Recipe.AbraShPath); err != nil { if os.IsNotExist(err) { - log.Fatalf("%s does not exist for %s?", app.Recipe.AbraShPath, app.Name) + log.Fatal(i18n.G("%s does not exist for %s?", app.Recipe.AbraShPath, app.Name)) } log.Fatal(err) } @@ -115,7 +116,7 @@ does not).`, log.Fatal(err) } - log.Debugf("--local detected, running %s on local work station", cmdName) + log.Debug(i18n.G("--local detected, running %s on local work station", cmdName)) var exportEnv string for k, v := range app.Env { @@ -124,16 +125,16 @@ does not).`, var sourceAndExec string if hasCmdArgs { - log.Debugf("parsed following command arguments: %s", parsedCmdArgs) + log.Debug(i18n.G("parsed following command arguments: %s", parsedCmdArgs)) sourceAndExec = fmt.Sprintf("TARGET=local; APP_NAME=%s; STACK_NAME=%s; %s . %s; %s %s", app.Name, app.StackName(), exportEnv, app.Recipe.AbraShPath, cmdName, parsedCmdArgs) } else { - log.Debug("did not detect any command arguments") + log.Debug(i18n.G("did not detect any command arguments")) sourceAndExec = fmt.Sprintf("TARGET=local; APP_NAME=%s; STACK_NAME=%s; %s . %s; %s", app.Name, app.StackName(), exportEnv, app.Recipe.AbraShPath, cmdName) } shell := "/bin/bash" if _, err := os.Stat(shell); errors.Is(err, os.ErrNotExist) { - log.Debugf("%s does not exist locally, use /bin/sh as fallback", shell) + log.Debug(i18n.G("%s does not exist locally, use /bin/sh as fallback", shell)) shell = "/bin/sh" } cmd := exec.Command(shell, "-c", sourceAndExec) @@ -164,15 +165,15 @@ does not).`, } if !matchingServiceName { - log.Fatalf("no service %s for %s?", targetServiceName, app.Name) + log.Fatal(i18n.G("no service %s for %s?", targetServiceName, app.Name)) } - log.Debugf("running command %s within the context of %s_%s", cmdName, app.StackName(), targetServiceName) + log.Debug(i18n.G("running command %s within the context of %s_%s", cmdName, app.StackName(), targetServiceName)) if hasCmdArgs { - log.Debugf("parsed following command arguments: %s", parsedCmdArgs) + log.Debug(i18n.G("parsed following command arguments: %s", parsedCmdArgs)) } else { - log.Debug("did not detect any command arguments") + log.Debug(i18n.G("did not detect any command arguments")) } cl, err := client.New(app.Server) @@ -192,9 +193,9 @@ does not).`, } var AppCmdListCommand = &cobra.Command{ - Use: "list [flags]", - Aliases: []string{"ls"}, - Short: "List all available commands", + Use: i18n.G("list [flags]"), + Aliases: []string{i18n.G("ls")}, + Short: i18n.G("List all available commands"), Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { app := internal.ValidateApp(args) @@ -244,33 +245,33 @@ var ( func init() { AppCmdCommand.Flags().BoolVarP( &local, - "local", - "l", + i18n.G("local"), + i18n.G("l"), false, - "run command locally", + i18n.G("run command locally"), ) AppCmdCommand.Flags().StringVarP( &remoteUser, - "user", - "u", + i18n.G("user"), + i18n.G("u"), "", - "request remote user", + i18n.G("request remote user"), ) AppCmdCommand.Flags().BoolVarP( &disableTTY, - "tty", - "T", + i18n.G("tty"), + i18n.G("T"), false, - "disable remote TTY", + i18n.G("disable remote TTY"), ) AppCmdCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) } diff --git a/cli/app/config.go b/cli/app/config.go index 163f1afc..23028973 100644 --- a/cli/app/config.go +++ b/cli/app/config.go @@ -6,16 +6,17 @@ import ( appPkg "coopcloud.tech/abra/pkg/app" "coopcloud.tech/abra/pkg/autocomplete" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/AlecAivazis/survey/v2" "github.com/spf13/cobra" ) var AppConfigCommand = &cobra.Command{ - Use: "config [flags]", - Aliases: []string{"cfg"}, - Short: "Edit app config", - Example: " abra config 1312.net", + Use: i18n.G("config [flags]"), + Aliases: []string{i18n.G("cfg")}, + Short: i18n.G("Edit app config"), + Example: i18n.G(" abra config 1312.net"), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -32,13 +33,13 @@ var AppConfigCommand = &cobra.Command{ appName := args[0] appFile, exists := files[appName] if !exists { - log.Fatalf("cannot find app with name %s", appName) + log.Fatal(i18n.G("cannot find app with name %s", appName)) } ed, ok := os.LookupEnv("EDITOR") if !ok { edPrompt := &survey.Select{ - Message: "which editor do you wish to use?", + Message: i18n.G("which editor do you wish to use?"), Options: []string{"vi", "vim", "nvim", "nano", "pico", "emacs"}, } if err := survey.AskOne(edPrompt, &ed); err != nil { diff --git a/cli/app/cp.go b/cli/app/cp.go index 6d0b1130..e75a3f7a 100644 --- a/cli/app/cp.go +++ b/cli/app/cp.go @@ -3,7 +3,6 @@ package app import ( "context" "errors" - "fmt" "io" "os" "path" @@ -15,6 +14,7 @@ import ( "coopcloud.tech/abra/pkg/client" containerPkg "coopcloud.tech/abra/pkg/container" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/upstream/container" "github.com/docker/cli/cli/command" @@ -26,14 +26,14 @@ import ( ) var AppCpCommand = &cobra.Command{ - Use: "cp [flags]", - Aliases: []string{"c"}, - Short: "Copy files to/from a deployed app service", - Example: ` # copy myfile.txt to the root of the app service + Use: i18n.G("cp [flags]"), + Aliases: []string{i18n.G("c")}, + Short: i18n.G("Copy files to/from a deployed app service"), + Example: i18n.G(` # copy myfile.txt to the root of the app service abra app cp 1312.net myfile.txt app:/ # copy that file back to your current working directory locally - abra app cp 1312.net app:/myfile.txt ./`, + abra app cp 1312.net app:/myfile.txt ./`), Args: cobra.ExactArgs(3), ValidArgsFunction: func( cmd *cobra.Command, @@ -69,7 +69,7 @@ var AppCpCommand = &cobra.Command{ if err != nil { log.Fatal(err) } - log.Debugf("retrieved %s as target container on %s", formatter.ShortenID(container.ID), app.Server) + log.Debug(i18n.G("retrieved %s as target container on %s", formatter.ShortenID(container.ID), app.Server)) if toContainer { err = CopyToContainer(cl, container.ID, srcPath, dstPath) @@ -82,7 +82,7 @@ var AppCpCommand = &cobra.Command{ }, } -var errServiceMissing = errors.New("one of / arguments must take $SERVICE:$PATH form") +var errServiceMissing = errors.New(i18n.G("one of / arguments must take $SERVICE:$PATH form")) // parseSrcAndDst parses src and dest string. One of src or dst must be of the form $SERVICE:$PATH func parseSrcAndDst(src, dst string) (srcPath string, dstPath string, service string, toContainer bool, err error) { @@ -105,7 +105,7 @@ func parseSrcAndDst(src, dst string) (srcPath string, dstPath string, service st func CopyToContainer(cl *dockerClient.Client, containerID, srcPath, dstPath string) error { srcStat, err := os.Stat(srcPath) if err != nil { - return fmt.Errorf("local %s ", err) + return errors.New(i18n.G("local %s ", err)) } dstStat, err := cl.ContainerStatPath(context.Background(), containerID, dstPath) @@ -114,7 +114,7 @@ func CopyToContainer(cl *dockerClient.Client, containerID, srcPath, dstPath stri if errdefs.IsNotFound(err) { dstExists = false } else { - return fmt.Errorf("remote path: %s", err) + return errors.New(i18n.G("remote path: %s", err)) } } @@ -142,7 +142,7 @@ func CopyToContainer(cl *dockerClient.Client, containerID, srcPath, dstPath stri Detach: false, Tty: true, }); err != nil { - return fmt.Errorf("create remote directory: %s", err) + return errors.New(i18n.G("create remote directory: %s", err)) } case CopyModeFileToFile: // Remove the file component from the path, since docker can only copy @@ -161,7 +161,7 @@ func CopyToContainer(cl *dockerClient.Client, containerID, srcPath, dstPath stri return err } - log.Debugf("copy %s from local to %s on container", srcPath, dstPath) + log.Debug(i18n.G("copy %s from local to %s on container", srcPath, dstPath)) copyOpts := containertypes.CopyToContainerOptions{AllowOverwriteDirWithFile: false, CopyUIDGID: false} if err := cl.CopyToContainer(context.Background(), containerID, dstPath, content, copyOpts); err != nil { return err @@ -181,7 +181,7 @@ func CopyToContainer(cl *dockerClient.Client, containerID, srcPath, dstPath stri Detach: false, Tty: true, }); err != nil { - return fmt.Errorf("create remote directory: %s", err) + return errors.New(i18n.G("create remote directory: %s", err)) } } @@ -194,9 +194,9 @@ func CopyFromContainer(cl *dockerClient.Client, containerID, srcPath, dstPath st srcStat, err := cl.ContainerStatPath(context.Background(), containerID, srcPath) if err != nil { if errdefs.IsNotFound(err) { - return fmt.Errorf("remote: %s does not exist", srcPath) + return errors.New(i18n.G("remote: %s does not exist", srcPath)) } else { - return fmt.Errorf("remote path: %s", err) + return errors.New(i18n.G("remote path: %s", err)) } } @@ -207,7 +207,7 @@ func CopyFromContainer(cl *dockerClient.Client, containerID, srcPath, dstPath st if os.IsNotExist(err) { dstExists = false } else { - return fmt.Errorf("remote path: %s", err) + return errors.New(i18n.G("remote path: %s", err)) } } else { dstMode = dstStat.Mode() @@ -242,7 +242,7 @@ func CopyFromContainer(cl *dockerClient.Client, containerID, srcPath, dstPath st content, _, err := cl.CopyFromContainer(context.Background(), containerID, srcPath) if err != nil { - return fmt.Errorf("copy: %s", err) + return errors.New(i18n.G("copy: %s", err)) } defer content.Close() if err := archive.Untar(content, dstPath, &archive.TarOptions{ @@ -250,7 +250,7 @@ func CopyFromContainer(cl *dockerClient.Client, containerID, srcPath, dstPath st Compression: archive.Gzip, NoLchown: true, }); err != nil { - return fmt.Errorf("untar: %s", err) + return errors.New(i18n.G("untar: %s", err)) } if moveDstFile != "" { @@ -269,8 +269,8 @@ func CopyFromContainer(cl *dockerClient.Client, containerID, srcPath, dstPath st } var ( - ErrCopyDirToFile = fmt.Errorf("can't copy dir to file") - ErrDstDirNotExist = fmt.Errorf("destination directory does not exist") + ErrCopyDirToFile = errors.New(i18n.G("can't copy dir to file")) + ErrDstDirNotExist = errors.New(i18n.G("destination directory does not exist")) ) type CopyMode int @@ -375,9 +375,9 @@ func moveFile(sourcePath, destPath string) error { func init() { AppCpCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) } diff --git a/cli/app/deploy.go b/cli/app/deploy.go index 72e45a58..19cd8872 100644 --- a/cli/app/deploy.go +++ b/cli/app/deploy.go @@ -2,7 +2,7 @@ package app import ( "context" - "fmt" + "errors" "strings" "coopcloud.tech/abra/cli/internal" @@ -16,6 +16,7 @@ import ( "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/dns" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/lint" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/upstream/stack" @@ -24,15 +25,15 @@ import ( ) var AppDeployCommand = &cobra.Command{ - Use: "deploy [version] [flags]", - Aliases: []string{"d"}, - Short: "Deploy an app", - Long: `Deploy an app. + Use: i18n.G("deploy [version] [flags]"), + Aliases: []string{i18n.G("d")}, + Short: i18n.G("Deploy an app"), + Long: i18n.G(`Deploy an app. This command supports chaos operations. Use "--chaos/-C" to deploy your recipe checkout as-is. Recipe commit hashes are also supported as values for -"[version]". Please note, "upgrade"/"rollback" do not support chaos operations.`, - Example: ` # standard deployment +"[version]". Please note, "upgrade"/"rollback" do not support chaos operations.`), + Example: i18n.G(` # standard deployment abra app deploy 1312.net # chaos deployment @@ -42,7 +43,7 @@ checkout as-is. Recipe commit hashes are also supported as values for abra app deploy 1312.net 2.0.0+1.2.3 # deploy a specific git hash - abra app deploy 1312.net 886db76d`, + abra app deploy 1312.net 886db76d`), Args: cobra.RangeArgs(1, 2), ValidArgsFunction: func( cmd *cobra.Command, @@ -55,7 +56,7 @@ checkout as-is. Recipe commit hashes are also supported as values for case 1: app, err := appPkg.Get(args[0]) if err != nil { - errMsg := fmt.Sprintf("autocomplete failed: %s", err) + errMsg := i18n.G("autocomplete failed: %s", err) return []string{errMsg}, cobra.ShellCompDirectiveError } return autocomplete.RecipeVersionComplete(app.Recipe.Name) @@ -84,7 +85,7 @@ checkout as-is. Recipe commit hashes are also supported as values for log.Fatal(err) } - log.Debugf("checking whether %s is already deployed", app.StackName()) + log.Debug(i18n.G("checking whether %s is already deployed", app.StackName())) deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName()) if err != nil { @@ -92,18 +93,18 @@ checkout as-is. Recipe commit hashes are also supported as values for } if deployMeta.IsDeployed && !(internal.Force || internal.Chaos) { - log.Fatalf("%s is already deployed", app.Name) + log.Fatal(i18n.G("%s is already deployed", app.Name)) } toDeployVersion, err = getDeployVersion(args, deployMeta, app) if err != nil { - log.Fatal(fmt.Errorf("get deploy version: %s", err)) + log.Fatal(i18n.G("get deploy version: %s", err)) } if !internal.Chaos { _, err = app.Recipe.EnsureVersion(toDeployVersion) if err != nil { - log.Fatalf("ensure recipe: %s", err) + log.Fatal(i18n.G("ensure recipe: %s", err)) } } @@ -162,7 +163,7 @@ checkout as-is. Recipe commit hashes are also supported as values for for _, envVar := range envVars { if !envVar.Present { deployWarnMessages = append(deployWarnMessages, - fmt.Sprintf("%s missing from %s.env", envVar.Name, app.Domain), + i18n.G("%s missing from %s.env", envVar.Name, app.Domain), ) } } @@ -173,10 +174,10 @@ checkout as-is. Recipe commit hashes are also supported as values for log.Fatal(err) } } else { - log.Debug("skipping domain checks, no DOMAIN=... configured") + log.Debug(i18n.G("skipping domain checks, no DOMAIN=... configured")) } } else { - log.Debug("skipping domain checks") + log.Debug(i18n.G("skipping domain checks")) } deployedVersion := config.NO_VERSION_DEFAULT @@ -199,7 +200,7 @@ checkout as-is. Recipe commit hashes are also supported as values for log.Fatal(err) } - log.Debugf("set waiting timeout to %d second(s)", stack.WaitTimeout) + log.Debug(i18n.G("set waiting timeout to %d second(s)", stack.WaitTimeout)) serviceNames, err := appPkg.GetAppServiceNames(app.Name) if err != nil { @@ -225,14 +226,14 @@ checkout as-is. Recipe commit hashes are also supported as values for postDeployCmds, ok := app.Env["POST_DEPLOY_CMDS"] if ok && !internal.DontWaitConverge { - log.Debugf("run the following post-deploy commands: %s", postDeployCmds) + log.Debug(i18n.G("run the following post-deploy commands: %s", postDeployCmds)) if err := internal.PostCmds(cl, app, postDeployCmds); err != nil { - log.Fatalf("attempting to run post deploy commands, saw: %s", err) + log.Fatal(i18n.G("attempting to run post deploy commands, saw: %s", err)) } } if err := app.WriteRecipeVersion(toDeployVersion, false); err != nil { - log.Fatalf("writing recipe version failed: %s", err) + log.Fatal(i18n.G("writing recipe version failed: %s", err)) } }, } @@ -258,7 +259,7 @@ func getLatestVersionOrCommit(app app.App) (string, error) { // validateArgsAndFlags ensures compatible args/flags. func validateArgsAndFlags(args []string) error { if len(args) == 2 && args[1] != "" && internal.Chaos { - return fmt.Errorf("cannot use [version] and --chaos together") + return errors.New(i18n.G("cannot use [version] and --chaos together")) } return nil @@ -272,7 +273,7 @@ func validateSecrets(cl *dockerClient.Client, app app.App) error { for _, secStat := range secStats { if !secStat.CreatedOnRemote { - return fmt.Errorf("secret not generated: %s", secStat.LocalName) + return errors.New(i18n.G("secret not generated: %s", secStat.LocalName)) } } @@ -286,33 +287,33 @@ func getDeployVersion(cliArgs []string, deployMeta stack.DeployMeta, app app.App if err != nil { return "", err } - log.Debugf("version: taking chaos version: %s", v) + log.Debug(i18n.G("version: taking chaos version: %s", v)) return v, nil } // Check if the deploy version is set with a cli argument if len(cliArgs) == 2 && cliArgs[1] != "" { - log.Debugf("version: taking version from cli arg: %s", cliArgs[1]) + log.Debug(i18n.G("version: taking version from cli arg: %s", cliArgs[1])) return cliArgs[1], nil } // Check if the recipe has a version in the .env file if app.Recipe.EnvVersion != "" && !internal.IgnoreEnvVersion { if strings.HasSuffix(app.Recipe.EnvVersionRaw, "+U") { - return "", fmt.Errorf("version: can not redeploy chaos version %s", app.Recipe.EnvVersionRaw) + return "", errors.New(i18n.G("version: can not redeploy chaos version %s", app.Recipe.EnvVersionRaw)) } - log.Debugf("version: taking version from .env file: %s", app.Recipe.EnvVersion) + log.Debug(i18n.G("version: taking version from .env file: %s", app.Recipe.EnvVersion)) return app.Recipe.EnvVersion, nil } // Take deployed version if deployMeta.IsDeployed { - log.Debugf("version: taking deployed version: %s", deployMeta.Version) + log.Debug(i18n.G("version: taking deployed version: %s", deployMeta.Version)) return deployMeta.Version, nil } v, err := getLatestVersionOrCommit(app) - log.Debugf("version: taking new recipe version: %s", v) + log.Debug(i18n.G("version: taking new recipe version: %s", v)) if err != nil { return "", err } @@ -322,33 +323,33 @@ func getDeployVersion(cliArgs []string, deployMeta stack.DeployMeta, app app.App func init() { AppDeployCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) AppDeployCommand.Flags().BoolVarP( &internal.Force, - "force", - "f", + i18n.G("force"), + i18n.G("f"), false, - "perform action without further prompt", + i18n.G("perform action without further prompt"), ) AppDeployCommand.Flags().BoolVarP( &internal.NoDomainChecks, - "no-domain-checks", - "D", + i18n.G("no-domain-checks"), + i18n.G("D"), false, - "disable public DNS checks", + i18n.G("disable public DNS checks"), ) AppDeployCommand.Flags().BoolVarP( &internal.DontWaitConverge, - "no-converge-checks", - "c", + i18n.G("no-converge-checks"), + i18n.G("c"), false, - "disable converge logic checks", + i18n.G("disable converge logic checks"), ) } diff --git a/cli/app/env.go b/cli/app/env.go index 44f3e3fe..265885f1 100644 --- a/cli/app/env.go +++ b/cli/app/env.go @@ -7,14 +7,15 @@ import ( "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "github.com/spf13/cobra" ) var AppEnvCommand = &cobra.Command{ - Use: "env [flags]", - Aliases: []string{"e"}, - Short: "Show app .env values", - Example: " abra app env 1312.net", + Use: i18n.G("env [flags]"), + Aliases: []string{i18n.G("e")}, + Short: i18n.G("Show app .env values"), + Example: i18n.G(" abra app env 1312.net"), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -37,7 +38,7 @@ var AppEnvCommand = &cobra.Command{ rows = append(rows, []string{k, app.Env[k]}) } - overview := formatter.CreateOverview("ENV OVERVIEW", rows) + overview := formatter.CreateOverview(i18n.G("ENV OVERVIEW"), rows) fmt.Println(overview) }, } diff --git a/cli/app/labels.go b/cli/app/labels.go index 5201db61..43017377 100644 --- a/cli/app/labels.go +++ b/cli/app/labels.go @@ -9,6 +9,7 @@ import ( "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/upstream/convert" composetypes "github.com/docker/cli/cli/compose/types" @@ -19,11 +20,11 @@ import ( ) var AppLabelsCommand = &cobra.Command{ - Use: "labels [flags]", - Aliases: []string{"lb"}, - Short: "Show deployment labels", - Long: "Both local recipe and live deployment labels are shown.", - Example: " abra app labels 1312.net", + Use: i18n.G("labels [flags]"), + Aliases: []string{i18n.G("lb")}, + Short: i18n.G("Show deployment labels"), + Long: i18n.G("Both local recipe and live deployment labels are shown."), + Example: i18n.G(" abra app labels 1312.net"), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -49,7 +50,7 @@ var AppLabelsCommand = &cobra.Command{ } rows := [][]string{ - {"DEPLOYED LABELS", "---"}, + {i18n.G("DEPLOYED LABELS"), "---"}, } remoteLabelKeys := make([]string, 0, len(remoteLabels)) @@ -67,10 +68,10 @@ var AppLabelsCommand = &cobra.Command{ } if len(remoteLabelKeys) == 0 { - rows = append(rows, []string{"unknown"}) + rows = append(rows, []string{i18n.G("unknown")}) } - rows = append(rows, []string{"RECIPE LABELS", "---"}) + rows = append(rows, []string{i18n.G("RECIPE LABELS"), "---"}) config, err := app.Recipe.GetComposeConfig(app.Env) if err != nil { @@ -98,7 +99,7 @@ var AppLabelsCommand = &cobra.Command{ }) } - overview := formatter.CreateOverview("LABELS OVERVIEW", rows) + overview := formatter.CreateOverview(i18n.G("LABELS OVERVIEW"), rows) fmt.Println(overview) }, } @@ -131,9 +132,9 @@ func getLabels(cl *dockerClient.Client, stackName string) (map[string]string, er func init() { AppLabelsCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) } diff --git a/cli/app/list.go b/cli/app/list.go index 50e7ce5c..2451ed8b 100644 --- a/cli/app/list.go +++ b/cli/app/list.go @@ -11,6 +11,7 @@ import ( appPkg "coopcloud.tech/abra/pkg/app" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/tagcmp" "github.com/spf13/cobra" @@ -39,20 +40,20 @@ type serverStatus struct { } var AppListCommand = &cobra.Command{ - Use: "list [flags]", - Aliases: []string{"ls"}, - Short: "List all managed apps", - Long: `Generate a report of all managed apps. + Use: i18n.G("list [flags]"), + Aliases: []string{i18n.G("ls")}, + Short: i18n.G("List all managed apps"), + Long: i18n.G(`Generate a report of all managed apps. -Use "--status/-S" flag to query all servers for the live deployment status.`, - Example: ` # list apps of all servers without live status +Use "--status/-S" flag to query all servers for the live deployment status.`), + Example: i18n.G(` # list apps of all servers without live status abra app ls # list apps of a specific server with live status abra app ls -s 1312.net -S # list apps of all servers which match a specific recipe - abra app ls -r gitea`, + abra app ls -r gitea`), Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { appFiles, err := appPkg.LoadAppFiles(listAppServer) @@ -107,11 +108,11 @@ Use "--status/-S" flag to query all servers for the live deployment status.`, totalAppsCount++ if status { - status := "unknown" - version := "unknown" - chaos := "unknown" - chaosVersion := "unknown" - autoUpdate := "unknown" + status := i18n.G("unknown") + version := i18n.G("unknown") + chaos := i18n.G("unknown") + chaosVersion := i18n.G("unknown") + autoUpdate := i18n.G("unknown") if statusMeta, ok := statuses[app.StackName()]; ok { if currentVersion, exists := statusMeta["version"]; exists { if currentVersion != "" { @@ -144,12 +145,12 @@ Use "--status/-S" flag to query all servers for the live deployment status.`, var newUpdates []string if version != "unknown" && chaos == "false" { if err := app.Recipe.EnsureExists(); err != nil { - log.Fatalf("unable to clone %s: %s", app.Name, err) + log.Fatal(i18n.G("unable to clone %s: %s", app.Name, err)) } updates, err := app.Recipe.Tags() if err != nil { - log.Fatalf("unable to retrieve tags for %s: %s", app.Name, err) + log.Fatal(i18n.G("unable to retrieve tags for %s: %s", app.Name, err)) } parsedVersion, err := tagcmp.Parse(version) @@ -171,9 +172,9 @@ Use "--status/-S" flag to query all servers for the live deployment status.`, if len(newUpdates) == 0 { if version == "unknown" { - appStats.Upgrade = "unknown" + appStats.Upgrade = i18n.G("unknown") } else { - appStats.Upgrade = "latest" + appStats.Upgrade = i18n.G("latest") stats.LatestCount++ } } else { @@ -212,14 +213,15 @@ Use "--status/-S" flag to query all servers for the live deployment status.`, serverStat := allStats[app.Server] - headers := []string{"RECIPE", "DOMAIN", "SERVER"} + headers := []string{i18n.G("RECIPE"), i18n.G("DOMAIN"), i18n.G("SERVER")} if status { headers = append(headers, []string{ - "STATUS", - "CHAOS", - "VERSION", - "UPGRADE", - "AUTOUPDATE"}..., + i18n.G("STATUS"), + i18n.G("CHAOS"), + i18n.G("VERSION"), + i18n.G("UPGRADE"), + i18n.G("AUTOUPDATE"), + }..., ) } @@ -283,22 +285,22 @@ var ( func init() { AppListCommand.Flags().BoolVarP( &status, - "status", - "S", + i18n.G("status"), + i18n.G("S"), false, - "show app deployment status", + i18n.G("show app deployment status"), ) AppListCommand.Flags().StringVarP( &recipeFilter, - "recipe", - "r", + i18n.G("recipe"), + i18n.G("r"), "", - "show apps of a specific recipe", + i18n.G("show apps of a specific recipe"), ) AppListCommand.RegisterFlagCompletionFunc( - "recipe", + i18n.G("recipe"), func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return autocomplete.RecipeNameComplete() }, @@ -306,22 +308,22 @@ func init() { AppListCommand.Flags().BoolVarP( &internal.MachineReadable, - "machine", - "m", + i18n.G("machine"), + i18n.G("m"), false, - "print machine-readable output", + i18n.G("print machine-readable output"), ) AppListCommand.Flags().StringVarP( &listAppServer, - "server", - "s", + i18n.G("server"), + i18n.G("s"), "", - "show apps of a specific server", + i18n.G("show apps of a specific server"), ) AppListCommand.RegisterFlagCompletionFunc( - "server", + i18n.G("server"), func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return autocomplete.ServerNameComplete() }, diff --git a/cli/app/logs.go b/cli/app/logs.go index 741bfd1b..6bc6b400 100644 --- a/cli/app/logs.go +++ b/cli/app/logs.go @@ -2,12 +2,12 @@ package app import ( "context" - "fmt" "coopcloud.tech/abra/cli/internal" appPkg "coopcloud.tech/abra/pkg/app" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/logs" "coopcloud.tech/abra/pkg/upstream/stack" @@ -15,9 +15,9 @@ import ( ) var AppLogsCommand = &cobra.Command{ - Use: "logs [service] [flags]", - Aliases: []string{"l"}, - Short: "Tail app logs", + Use: i18n.G("logs [service] [flags]"), + Aliases: []string{i18n.G("l")}, + Short: i18n.G("Tail app logs"), Args: cobra.RangeArgs(1, 2), ValidArgsFunction: func( cmd *cobra.Command, @@ -29,8 +29,7 @@ var AppLogsCommand = &cobra.Command{ case 1: app, err := appPkg.Get(args[0]) if err != nil { - errMsg := fmt.Sprintf("autocomplete failed: %s", err) - return []string{errMsg}, cobra.ShellCompDirectiveError + return []string{i18n.G("autocomplete failed: %s", err)}, cobra.ShellCompDirectiveError } return autocomplete.ServiceNameComplete(app.Name) default: @@ -56,7 +55,7 @@ var AppLogsCommand = &cobra.Command{ } if !deployMeta.IsDeployed { - log.Fatalf("%s is not deployed?", app.Name) + log.Fatal(i18n.G("%s is not deployed?", app.Name)) } var serviceNames []string @@ -91,17 +90,17 @@ var ( func init() { AppLogsCommand.Flags().BoolVarP( &stdErr, - "stderr", - "s", + i18n.G("stderr"), + i18n.G("s"), false, - "only tail stderr", + i18n.G("only tail stderr"), ) AppLogsCommand.Flags().StringVarP( &sinceLogs, - "since", - "S", + i18n.G("since"), + i18n.G("S"), "", - "tail logs since YYYY-MM-DDTHH:MM:SSZ", + i18n.G("tail logs since YYYY-MM-DDTHH:MM:SSZ"), ) } diff --git a/cli/app/new.go b/cli/app/new.go index e022beed..778b260c 100644 --- a/cli/app/new.go +++ b/cli/app/new.go @@ -1,6 +1,7 @@ package app import ( + "errors" "fmt" "coopcloud.tech/abra/cli/internal" @@ -10,6 +11,7 @@ import ( "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" recipePkg "coopcloud.tech/abra/pkg/recipe" "coopcloud.tech/abra/pkg/secret" @@ -19,7 +21,7 @@ import ( "github.com/spf13/cobra" ) -var appNewDescription = `Creates a new app from a default recipe. +var appNewDescription = i18n.G(`Creates a new app from a default recipe. This new app configuration is stored in your $ABRA_DIR directory under the appropriate server. @@ -39,12 +41,12 @@ store them somewhere safe. You can use the "--pass/-P" to store these generated passwords locally in a pass store (see passwordstore.org for more). The pass command must be available -on your $PATH.` +on your $PATH.`) var AppNewCommand = &cobra.Command{ - Use: "new [recipe] [version] [flags]", - Aliases: []string{"n"}, - Short: "Create a new app", + Use: i18n.G("new [recipe] [version] [flags]"), + Aliases: []string{i18n.G("n")}, + Short: i18n.G("Create a new app"), Long: appNewDescription, Args: cobra.RangeArgs(0, 2), ValidArgsFunction: func( @@ -65,7 +67,7 @@ var AppNewCommand = &cobra.Command{ recipe := internal.ValidateRecipe(args, cmd.Name()) if len(args) == 2 && internal.Chaos { - log.Fatal("cannot use [version] and --chaos together") + log.Fatal(i18n.G("cannot use [version] and --chaos together")) } var recipeVersion string @@ -113,7 +115,7 @@ var AppNewCommand = &cobra.Command{ if recipeVersion == "" { head, err := recipe.Head() if err != nil { - log.Fatalf("failed to retrieve latest commit for %s: %s", recipe.Name, err) + log.Fatal(i18n.G("failed to retrieve latest commit for %s: %s", recipe.Name, err)) } recipeVersion = formatter.SmallSHA(head.String()) @@ -130,7 +132,7 @@ var AppNewCommand = &cobra.Command{ } sanitisedAppName := appPkg.SanitiseAppName(appDomain) - log.Debugf("%s sanitised as %s for new app", appDomain, sanitisedAppName) + log.Debug(i18n.G("%s sanitised as %s for new app", appDomain, sanitisedAppName)) if err := appPkg.TemplateAppEnvSample( recipe, @@ -182,7 +184,7 @@ var AppNewCommand = &cobra.Command{ log.Fatal(err) } - headers := []string{"NAME", "VALUE"} + headers := []string{i18n.G("NAME"), i18n.G("VALUE")} secretsTable.Headers(headers...) for name, val := range appSecrets { @@ -194,7 +196,7 @@ var AppNewCommand = &cobra.Command{ newAppServer = "local" } - log.Infof("%s created (version: %s)", appDomain, recipeVersion) + log.Info(i18n.G("%s created (version: %s)", appDomain, recipeVersion)) if len(appSecrets) > 0 { rows := [][]string{} @@ -202,15 +204,15 @@ var AppNewCommand = &cobra.Command{ rows = append(rows, []string{k, v}) } - overview := formatter.CreateOverview("SECRETS OVERVIEW", rows) + overview := formatter.CreateOverview(i18n.G("SECRETS OVERVIEW"), rows) fmt.Println(overview) - log.Warnf( + log.Warn(i18n.G( "secrets are %s shown again, please save them %s", formatter.BoldUnderlineStyle.Render("NOT"), formatter.BoldUnderlineStyle.Render("NOW"), - ) + )) } app, err := app.Get(appDomain) @@ -219,7 +221,7 @@ var AppNewCommand = &cobra.Command{ } if err := app.WriteRecipeVersion(recipeVersion, false); err != nil { - log.Fatalf("writing recipe version failed: %s", err) + log.Fatal(i18n.G("writing recipe version failed: %s", err)) } }, } @@ -231,7 +233,7 @@ type AppSecrets map[string]string func createSecrets(cl *dockerClient.Client, secretsConfig map[string]secret.Secret, sanitisedAppName string) (AppSecrets, error) { // NOTE(d1): trim to match app.StackName() implementation if len(sanitisedAppName) > config.MAX_SANITISED_APP_NAME_LENGTH { - log.Debugf("trimming %s to %s to avoid runtime limits", sanitisedAppName, sanitisedAppName[:config.MAX_SANITISED_APP_NAME_LENGTH]) + log.Debug(i18n.G("trimming %s to %s to avoid runtime limits", sanitisedAppName, sanitisedAppName[:config.MAX_SANITISED_APP_NAME_LENGTH])) sanitisedAppName = sanitisedAppName[:config.MAX_SANITISED_APP_NAME_LENGTH] } @@ -261,7 +263,7 @@ func createSecrets(cl *dockerClient.Client, secretsConfig map[string]secret.Secr func ensureDomainFlag(recipe recipePkg.Recipe, server string) error { if appDomain == "" && !internal.NoInput { prompt := &survey.Input{ - Message: "Specify app domain", + Message: i18n.G("Specify app domain"), Default: fmt.Sprintf("%s.%s", recipe.Name, server), } if err := survey.AskOne(prompt, &appDomain); err != nil { @@ -270,7 +272,7 @@ func ensureDomainFlag(recipe recipePkg.Recipe, server string) error { } if appDomain == "" { - return fmt.Errorf("no domain provided") + return errors.New(i18n.G("no domain provided")) } return nil @@ -279,13 +281,13 @@ func ensureDomainFlag(recipe recipePkg.Recipe, server string) error { // promptForSecrets asks if we should generate secrets for a new app. func promptForSecrets(recipeName string, secretsConfig map[string]secret.Secret) error { if len(secretsConfig) == 0 { - log.Debugf("%s has no secrets to generate, skipping...", recipeName) + log.Debug(i18n.G("%s has no secrets to generate, skipping...", recipeName)) return nil } if !generateSecrets && !internal.NoInput { prompt := &survey.Confirm{ - Message: "Generate app secrets?", + Message: i18n.G("Generate app secrets?"), } if err := survey.AskOne(prompt, &generateSecrets); err != nil { return err @@ -304,13 +306,13 @@ func ensureServerFlag() error { if len(servers) == 1 { newAppServer = servers[0] - log.Infof("single server detected, choosing %s automatically", newAppServer) + log.Info(i18n.G("single server detected, choosing %s automatically", newAppServer)) return nil } if newAppServer == "" && !internal.NoInput { prompt := &survey.Select{ - Message: "Select app server:", + Message: i18n.G("Select app server:"), Options: servers, } if err := survey.AskOne(prompt, &newAppServer); err != nil { @@ -319,7 +321,7 @@ func ensureServerFlag() error { } if newAppServer == "" { - return fmt.Errorf("no server provided") + return errors.New(i18n.G("no server provided")) } return nil @@ -335,14 +337,14 @@ var ( func init() { AppNewCommand.Flags().StringVarP( &newAppServer, - "server", - "s", + i18n.G("server"), + i18n.G("s"), "", - "specify server for new app", + i18n.G("specify server for new app"), ) AppNewCommand.RegisterFlagCompletionFunc( - "server", + i18n.G("server"), func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return autocomplete.ServerNameComplete() }, @@ -350,34 +352,34 @@ func init() { AppNewCommand.Flags().StringVarP( &appDomain, - "domain", - "D", + i18n.G("domain"), + i18n.G("D"), "", - "domain name for app", + i18n.G("domain name for app"), ) AppNewCommand.Flags().BoolVarP( &saveInPass, - "pass", - "p", + i18n.G("pass"), + i18n.G("p"), false, - "store secrets in a local pass store", + i18n.G("store secrets in a local pass store"), ) AppNewCommand.Flags().BoolVarP( &generateSecrets, - "secrets", - "S", + i18n.G("secrets"), + i18n.G("S"), false, - "automatically generate secrets", + i18n.G("automatically generate secrets"), ) AppNewCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) } diff --git a/cli/app/ps.go b/cli/app/ps.go index 029e0d06..1c6540c3 100644 --- a/cli/app/ps.go +++ b/cli/app/ps.go @@ -13,6 +13,7 @@ import ( "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" abraService "coopcloud.tech/abra/pkg/service" stack "coopcloud.tech/abra/pkg/upstream/stack" @@ -24,9 +25,9 @@ import ( ) var AppPsCommand = &cobra.Command{ - Use: "ps [flags]", - Aliases: []string{"p"}, - Short: "Check app deployment status", + Use: i18n.G("ps [flags]"), + Aliases: []string{i18n.G("p")}, + Short: i18n.G("Check app deployment status"), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -52,7 +53,7 @@ var AppPsCommand = &cobra.Command{ } if !deployMeta.IsDeployed { - log.Fatalf("%s is not deployed?", app.Name) + log.Fatal(i18n.G("%s is not deployed?", app.Name)) } chaosVersion := config.CHAOS_DEFAULT @@ -115,11 +116,11 @@ func showPSOutput(app appPkg.App, cl *dockerClient.Client, deployedVersion, chao "version": deployedVersion, "chaos": chaosVersion, "service": service.Name, - "image": "unknown", - "created": "unknown", - "status": "unknown", - "state": "unknown", - "ports": "unknown", + "image": i18n.G("unknown"), + "created": i18n.G("unknown"), + "status": i18n.G("unknown"), + "state": i18n.G("unknown"), + "ports": i18n.G("unknown"), } } else { container := containers[0] @@ -161,7 +162,7 @@ func showPSOutput(app appPkg.App, cl *dockerClient.Client, deployedVersion, chao if internal.MachineReadable { rendered, err := json.Marshal(allContainerStats) if err != nil { - log.Fatal("unable to convert to JSON: %s", err) + log.Fatal(i18n.G("unable to convert to JSON: %s", err)) } fmt.Println(string(rendered)) @@ -175,11 +176,11 @@ func showPSOutput(app appPkg.App, cl *dockerClient.Client, deployedVersion, chao } headers := []string{ - "SERVICE", - "STATUS", - "IMAGE", - "VERSION", - "CHAOS", + i18n.G("SERVICE"), + i18n.G("STATUS"), + i18n.G("IMAGE"), + i18n.G("VERSION"), + i18n.G("CHAOS"), } table. @@ -194,17 +195,17 @@ func showPSOutput(app appPkg.App, cl *dockerClient.Client, deployedVersion, chao func init() { AppPsCommand.Flags().BoolVarP( &internal.MachineReadable, - "machine", - "m", + i18n.G("machine"), + i18n.G("m"), false, - "print machine-readable output", + i18n.G("print machine-readable output"), ) AppPsCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) } diff --git a/cli/app/remove.go b/cli/app/remove.go index f65f8638..6d5695ec 100644 --- a/cli/app/remove.go +++ b/cli/app/remove.go @@ -2,12 +2,12 @@ package app import ( "context" - "fmt" "os" "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/upstream/stack" "github.com/AlecAivazis/survey/v2" @@ -16,10 +16,10 @@ import ( ) var AppRemoveCommand = &cobra.Command{ - Use: "remove [flags]", - Aliases: []string{"rm"}, - Short: "Remove all app data, locally and remotely", - Long: `Remove everything related to an app which is already undeployed. + Use: i18n.G("remove [flags]"), + Aliases: []string{i18n.G("rm")}, + Short: i18n.G("Remove all app data, locally and remotely"), + Long: i18n.G(`Remove everything related to an app which is already undeployed. By default, it will prompt for confirmation before proceeding. All secrets, volumes and the local app env file will be deleted. @@ -34,8 +34,8 @@ Please note, if you delete the local app env file without removing volumes and secrets first, Abra will *not* be able to help you remove them afterwards. To delete everything without prompt, use the "--force/-f" or the "--no-input/n" -flag.`, - Example: " abra app remove 1312.net", +flag.`), + Example: i18n.G(" abra app remove 1312.net"), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -47,16 +47,16 @@ flag.`, app := internal.ValidateApp(args) if !internal.Force && !internal.NoInput { - log.Warnf("ALERTA ALERTA: deleting %s data and config (local/remote)", app.Name) + log.Warn(i18n.G("ALERTA ALERTA: deleting %s data and config (local/remote)", app.Name)) response := false - prompt := &survey.Confirm{Message: "are you sure?"} + prompt := &survey.Confirm{Message: i18n.G("are you sure?")} if err := survey.AskOne(prompt, &response); err != nil { log.Fatal(err) } if !response { - log.Fatal("aborting as requested") + log.Fatal(i18n.G("aborting as requested")) } } @@ -70,7 +70,7 @@ flag.`, log.Fatal(err) } if deployMeta.IsDeployed { - log.Fatalf("%s is still deployed. Run \"abra app undeploy %s\"", app.Name, app.Name) + log.Fatal(i18n.G("%s is still deployed. Run \"abra app undeploy %s\"", app.Name, app.Name)) } fs, err := app.Filters(false, false) @@ -86,12 +86,12 @@ flag.`, if len(configNames) > 0 { if err := client.RemoveConfigs(cl, context.Background(), configNames, internal.Force); err != nil { - log.Fatalf("removing configs failed: %s", err) + log.Fatal(i18n.G("removing configs failed: %s", err)) } - log.Infof("%d config(s) removed successfully", len(configNames)) + log.Info(i18n.G("%d config(s) removed successfully", len(configNames))) } else { - log.Info("no configs to remove") + log.Info(i18n.G("no configs to remove")) } secretList, err := cl.SecretList(context.Background(), types.SecretListOptions{Filters: fs}) @@ -113,10 +113,10 @@ flag.`, if err != nil { log.Fatal(err) } - log.Info(fmt.Sprintf("secret: %s removed", name)) + log.Info(i18n.G("secret: %s removed", name)) } } else { - log.Info("no secrets to remove") + log.Info(i18n.G("no secrets to remove")) } fs, err = app.Filters(false, true) @@ -133,28 +133,28 @@ flag.`, if len(volumeNames) > 0 { err := client.RemoveVolumes(cl, context.Background(), volumeNames, internal.Force, 5) if err != nil { - log.Fatalf("removing volumes failed: %s", err) + log.Fatal(i18n.G("removing volumes failed: %s", err)) } - log.Infof("%d volume(s) removed successfully", len(volumeNames)) + log.Info(i18n.G("%d volume(s) removed successfully", len(volumeNames))) } else { - log.Info("no volumes to remove") + log.Info(i18n.G("no volumes to remove")) } if err = os.Remove(app.Path); err != nil { log.Fatal(err) } - log.Info(fmt.Sprintf("file: %s removed", app.Path)) + log.Info(i18n.G("file: %s removed", app.Path)) }, } func init() { AppRemoveCommand.Flags().BoolVarP( &internal.Force, - "force", - "f", + i18n.G("force"), + i18n.G("f"), false, - "perform action without further prompt", + i18n.G("perform action without further prompt"), ) } diff --git a/cli/app/restart.go b/cli/app/restart.go index 860c77e4..bb838492 100644 --- a/cli/app/restart.go +++ b/cli/app/restart.go @@ -8,6 +8,7 @@ import ( appPkg "coopcloud.tech/abra/pkg/app" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/ui" upstream "coopcloud.tech/abra/pkg/upstream/service" @@ -17,19 +18,19 @@ import ( ) var AppRestartCommand = &cobra.Command{ - Use: "restart [[service] | --all-services] [flags]", - Aliases: []string{"re"}, - Short: "Restart an app", - Long: `This command restarts services within a deployed app. + Use: i18n.G("restart [[service] | --all-services] [flags]"), + Aliases: []string{i18n.G("re")}, + Short: i18n.G("Restart an app"), + Long: i18n.G(`This command restarts services within a deployed app. Run "abra app ps " to see a list of service names. -Pass "--all-services/-a" to restart all services.`, - Example: ` # restart a single app service +Pass "--all-services/-a" to restart all services.`), + Example: i18n.G(` # restart a single app service abra app restart 1312.net app # restart all app services - abra app restart 1312.net -a`, + abra app restart 1312.net -a`), Args: cobra.RangeArgs(1, 2), ValidArgsFunction: func( cmd *cobra.Command, @@ -60,11 +61,11 @@ Pass "--all-services/-a" to restart all services.`, } if serviceName == "" && !allServices { - log.Fatal("missing [service]") + log.Fatal(i18n.G("missing [service]")) } if serviceName != "" && allServices { - log.Fatal("cannot use [service] and --all-services/-a together") + log.Fatal(i18n.G("cannot use [service] and --all-services/-a together")) } var serviceNames []string @@ -89,7 +90,7 @@ Pass "--all-services/-a" to restart all services.`, } if !deployMeta.IsDeployed { - log.Fatalf("%s is not deployed?", app.Name) + log.Fatal(i18n.G("%s is not deployed?", app.Name)) } for _, serviceName := range serviceNames { @@ -104,7 +105,7 @@ Pass "--all-services/-a" to restart all services.`, log.Fatal(err) } - log.Debugf("attempting to scale %s to 0", stackServiceName) + log.Debug(i18n.G("attempting to scale %s to 0", stackServiceName)) if err := upstream.RunServiceScale(context.Background(), cl, stackServiceName, 0); err != nil { log.Fatal(err) @@ -128,8 +129,8 @@ Pass "--all-services/-a" to restart all services.`, log.Fatal(err) } - log.Debugf("%s has been scaled to 0", stackServiceName) - log.Debugf("attempting to scale %s to 1", stackServiceName) + log.Debug(i18n.G("%s has been scaled to 0", stackServiceName)) + log.Debug(i18n.G("attempting to scale %s to 1", stackServiceName)) if err := upstream.RunServiceScale(context.Background(), cl, stackServiceName, 1); err != nil { log.Fatal(err) @@ -139,8 +140,8 @@ Pass "--all-services/-a" to restart all services.`, log.Fatal(err) } - log.Debugf("%s has been scaled to 1", stackServiceName) - log.Infof("%s service successfully restarted", serviceName) + log.Debug(i18n.G("%s has been scaled to 1", stackServiceName)) + log.Info(i18n.G("%s service successfully restarted", serviceName)) } }, } @@ -150,16 +151,16 @@ var allServices bool func init() { AppRestartCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) AppRestartCommand.Flags().BoolVarP( &allServices, - "all-services", - "a", + i18n.G("all-services"), + i18n.G("a"), false, - "restart all services", + i18n.G("restart all services"), ) } diff --git a/cli/app/restore.go b/cli/app/restore.go index 21b1dcc0..a51d9d21 100644 --- a/cli/app/restore.go +++ b/cli/app/restore.go @@ -7,17 +7,18 @@ import ( "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/spf13/cobra" ) var AppRestoreCommand = &cobra.Command{ - Use: "restore [flags]", - Aliases: []string{"rs"}, - Short: "Restore a snapshot", - Long: `Snapshots are restored while apps are deployed. + Use: i18n.G("restore [flags]"), + Aliases: []string{i18n.G("rs")}, + Short: i18n.G("Restore a snapshot"), + Long: i18n.G(`Snapshots are restored while apps are deployed. -Some restore scenarios may require service / app restarts.`, +Some restore scenarios may require service / app restarts.`), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -48,34 +49,34 @@ Some restore scenarios may require service / app restarts.`, } if snapshot != "" { - log.Debugf("including SNAPSHOT=%s in backupbot exec invocation", snapshot) + log.Debug(i18n.G("including SNAPSHOT=%s in backupbot exec invocation", snapshot)) execEnv = append(execEnv, fmt.Sprintf("SNAPSHOT=%s", snapshot)) } if targetPath != "" { - log.Debugf("including TARGET=%s in backupbot exec invocation", targetPath) + log.Debug(i18n.G("including TARGET=%s in backupbot exec invocation", targetPath)) execEnv = append(execEnv, fmt.Sprintf("TARGET=%s", targetPath)) } if internal.NoInput { - log.Debugf("including NONINTERACTIVE=%v in backupbot exec invocation", internal.NoInput) + log.Debug(i18n.G("including NONINTERACTIVE=%v in backupbot exec invocation", internal.NoInput)) execEnv = append(execEnv, fmt.Sprintf("NONINTERACTIVE=%v", internal.NoInput)) } if len(volumes) > 0 { allVolumes := strings.Join(volumes, ",") - log.Debugf("including VOLUMES=%s in backupbot exec invocation", allVolumes) + log.Debug(i18n.G("including VOLUMES=%s in backupbot exec invocation", allVolumes)) execEnv = append(execEnv, fmt.Sprintf("VOLUMES=%s", allVolumes)) } if len(services) > 0 { allServices := strings.Join(services, ",") - log.Debugf("including CONTAINER=%s in backupbot exec invocation", allServices) + log.Debug(i18n.G("including CONTAINER=%s in backupbot exec invocation", allServices)) execEnv = append(execEnv, fmt.Sprintf("CONTAINER=%s", allServices)) } if hooks { - log.Debugf("including NO_COMMANDS=%v in backupbot exec invocation", false) + log.Debug(i18n.G("including NO_COMMANDS=%v in backupbot exec invocation", false)) execEnv = append(execEnv, fmt.Sprintf("NO_COMMANDS=%v", false)) } @@ -95,41 +96,41 @@ var ( func init() { AppRestoreCommand.Flags().StringVarP( &targetPath, - "target", - "t", + i18n.G("target"), + i18n.G("t"), "/", - "target path", + i18n.G("target path"), ) AppRestoreCommand.Flags().StringArrayVarP( &services, - "services", - "s", + i18n.G("services"), + i18n.G("s"), []string{}, - "restore specific services", + i18n.G("restore specific services"), ) AppRestoreCommand.Flags().StringArrayVarP( &volumes, - "volumes", - "v", + i18n.G("volumes"), + i18n.G("v"), []string{}, - "restore specific volumes", + i18n.G("restore specific volumes"), ) AppRestoreCommand.Flags().BoolVarP( &hooks, - "hooks", - "H", + i18n.G("hooks"), + i18n.G("H"), false, - "enable pre/post-hook command execution", + i18n.G("enable pre/post-hook command execution"), ) AppRestoreCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) } diff --git a/cli/app/rollback.go b/cli/app/rollback.go index 7bdebf94..0218dd0a 100644 --- a/cli/app/rollback.go +++ b/cli/app/rollback.go @@ -1,7 +1,7 @@ package app import ( - "fmt" + "errors" "coopcloud.tech/abra/pkg/app" appPkg "coopcloud.tech/abra/pkg/app" @@ -9,6 +9,7 @@ import ( "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/envfile" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/lint" stack "coopcloud.tech/abra/pkg/upstream/stack" "coopcloud.tech/tagcmp" @@ -21,10 +22,10 @@ import ( ) var AppRollbackCommand = &cobra.Command{ - Use: "rollback [version] [flags]", - Aliases: []string{"rl"}, - Short: "Roll an app back to a previous version", - Long: `This command rolls an app back to a previous version. + Use: i18n.G("rollback [version] [flags]"), + Aliases: []string{i18n.G("rl")}, + Short: i18n.G("Roll an app back to a previous version"), + Long: i18n.G(`This command rolls an app back to a previous version. Unlike "abra app deploy", chaos operations are not supported here. Only recipe versions are supported values for "[version]". @@ -37,12 +38,12 @@ are available. The live deployment version is the "source of truth" in this case. The stored .env version is not consulted. A downgrade can be destructive, please ensure you have a copy of your app data -beforehand. See "abra app backup" for more.`, - Example: ` # standard rollback +beforehand. See "abra app backup" for more.`), + Example: i18n.G(` # standard rollback abra app rollback 1312.net # rollback to specific version - abra app rollback 1312.net 2.0.0+1.2.3`, + abra app rollback 1312.net 2.0.0+1.2.3`), Args: cobra.RangeArgs(1, 2), ValidArgsFunction: func( cmd *cobra.Command, @@ -54,8 +55,7 @@ beforehand. See "abra app backup" for more.`, case 1: app, err := appPkg.Get(args[0]) if err != nil { - errMsg := fmt.Sprintf("autocomplete failed: %s", err) - return []string{errMsg}, cobra.ShellCompDirectiveError + return []string{i18n.G("autocomplete failed: %s", err)}, cobra.ShellCompDirectiveError } return autocomplete.RecipeVersionComplete(app.Recipe.Name) default: @@ -117,7 +117,7 @@ beforehand. See "abra app backup" for more.`, } if !downgradeAvailable { - log.Info("no available downgrades") + log.Info(i18n.G("no available downgrades")) return } } @@ -139,10 +139,10 @@ beforehand. See "abra app backup" for more.`, } if chosenDowngrade == "" { - log.Fatal("unknown deployed version, unable to downgrade") + log.Fatal(i18n.G("unknown deployed version, unable to downgrade")) } - log.Debugf("choosing %s as version to rollback", chosenDowngrade) + log.Debug(i18n.G("choosing %s as version to rollback", chosenDowngrade)) if _, err := app.Recipe.EnsureVersion(chosenDowngrade); err != nil { log.Fatal(err) @@ -199,7 +199,7 @@ beforehand. See "abra app backup" for more.`, log.Fatal(err) } - log.Debugf("set waiting timeout to %d second(s)", stack.WaitTimeout) + log.Debug(i18n.G("set waiting timeout to %d second(s)", stack.WaitTimeout)) serviceNames, err := appPkg.GetAppServiceNames(app.Name) if err != nil { @@ -224,7 +224,7 @@ beforehand. See "abra app backup" for more.`, } if err := app.WriteRecipeVersion(chosenDowngrade, false); err != nil { - log.Fatalf("writing recipe version failed: %s", err) + log.Fatal(i18n.G("writing recipe version failed: %s", err)) } }, } @@ -235,12 +235,12 @@ func chooseDowngrade( deployMeta stack.DeployMeta, chosenDowngrade *string, ) error { - msg := fmt.Sprintf("please select a downgrade (version: %s):", deployMeta.Version) + msg := i18n.G("please select a downgrade (version: %s):", deployMeta.Version) if deployMeta.IsChaos { chaosVersion := formatter.BoldDirtyDefault(deployMeta.ChaosVersion) - msg = fmt.Sprintf( + msg = i18n.G( "please select a downgrade (version: %s, chaos: %s):", deployMeta.Version, chaosVersion, @@ -267,21 +267,21 @@ func validateDowngradeVersionArg( ) error { parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) if err != nil { - return fmt.Errorf("current deployment '%s' is not a known version for %s", deployMeta.Version, app.Recipe.Name) + return errors.New(i18n.G("current deployment '%s' is not a known version for %s", deployMeta.Version, app.Recipe.Name)) } parsedSpecificVersion, err := tagcmp.Parse(specificVersion) if err != nil { - return fmt.Errorf("'%s' is not a known version for %s", specificVersion, app.Recipe.Name) + return errors.New(i18n.G("'%s' is not a known version for %s", specificVersion, app.Recipe.Name)) } if parsedSpecificVersion.IsGreaterThan(parsedDeployedVersion) && !parsedSpecificVersion.Equals(parsedDeployedVersion) { - return fmt.Errorf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion) + return errors.New(i18n.G("%s is not a downgrade for %s?", deployMeta.Version, specificVersion)) } if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force { - return fmt.Errorf("%s is not a downgrade for %s?", deployMeta.Version, specificVersion) + return errors.New(i18n.G("%s is not a downgrade for %s?", deployMeta.Version, specificVersion)) } return nil @@ -320,24 +320,25 @@ func ensureDowngradesAvailable( func init() { AppRollbackCommand.Flags().BoolVarP( &internal.Force, - "force", - "f", + i18n.G("force"), + i18n.G("f"), false, - "perform action without further prompt", + i18n.G("perform action without further prompt"), ) AppRollbackCommand.Flags().BoolVarP( &internal.NoDomainChecks, - "no-domain-checks", - "D", + i18n.G("no-domain-checks"), + i18n.G("D"), false, - "disable public DNS checks", + i18n.G("disable public DNS checks"), ) AppRollbackCommand.Flags().BoolVarP( - &internal.DontWaitConverge, "no-converge-checks", - "c", + &internal.DontWaitConverge, + i18n.G("no-converge-checks"), + i18n.G("c"), false, - "disable converge logic checks", + i18n.G("disable converge logic checks"), ) } diff --git a/cli/app/run.go b/cli/app/run.go index 21c0f1ea..a0f4a1a9 100644 --- a/cli/app/run.go +++ b/cli/app/run.go @@ -8,6 +8,7 @@ import ( "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" containerPkg "coopcloud.tech/abra/pkg/container" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/upstream/container" "github.com/docker/cli/cli/command" @@ -17,17 +18,17 @@ import ( ) var AppRunCommand = &cobra.Command{ - Use: "run [[args] [flags] | [flags] -- [args]]", - Aliases: []string{"r"}, - Short: "Run a command inside a service container", - Example: ` # run with args/flags + Use: i18n.G("run [[args] [flags] | [flags] -- [args]]"), + Aliases: []string{i18n.G("r")}, + Short: i18n.G("Run a command inside a service container"), + Example: i18n.G(` # run with args/flags abra app run 1312.net app -- ls -lha # run without args/flags abra app run 1312.net app bash --user nobody # run with both kinds of args/flags - abra app run 1312.net app --user nobody -- ls -lha`, + abra app run 1312.net app --user nobody -- ls -lha`), Args: cobra.MinimumNArgs(3), ValidArgsFunction: func( cmd *cobra.Command, @@ -98,17 +99,17 @@ var ( func init() { AppRunCommand.Flags().BoolVarP(&noTTY, - "no-tty", - "t", + i18n.G("no-tty"), + i18n.G("t"), false, - "do not request a TTY", + i18n.G("do not request a TTY"), ) AppRunCommand.Flags().StringVarP( &runAsUser, - "user", - "u", + i18n.G("user"), + i18n.G("u"), "", - "run command as user", + i18n.G("run command as user"), ) } diff --git a/cli/app/secret.go b/cli/app/secret.go index 6b5da2a2..550714b2 100644 --- a/cli/app/secret.go +++ b/cli/app/secret.go @@ -12,6 +12,7 @@ import ( "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/secret" "github.com/docker/docker/api/types" @@ -20,9 +21,9 @@ import ( ) var AppSecretGenerateCommand = &cobra.Command{ - Use: "generate [[secret] [version] | --all] [flags]", - Aliases: []string{"g"}, - Short: "Generate secrets", + Use: i18n.G("generate [[secret] [version] | --all] [flags]"), + Aliases: []string{i18n.G("g")}, + Short: i18n.G("Generate secrets"), Args: cobra.RangeArgs(1, 3), ValidArgsFunction: func( cmd *cobra.Command, @@ -34,8 +35,7 @@ var AppSecretGenerateCommand = &cobra.Command{ case 1: app, err := appPkg.Get(args[0]) if err != nil { - errMsg := fmt.Sprintf("autocomplete failed: %s", err) - return []string{errMsg}, cobra.ShellCompDirectiveError + return []string{i18n.G("autocomplete failed: %s", err)}, cobra.ShellCompDirectiveError } return autocomplete.SecretComplete(app.Recipe.Name) default: @@ -50,11 +50,11 @@ var AppSecretGenerateCommand = &cobra.Command{ } if len(args) <= 2 && !generateAllSecrets { - log.Fatal("missing arguments [secret]/[version] or '--all'") + log.Fatal(i18n.G("missing arguments [secret]/[version] or '--all'")) } if len(args) > 2 && generateAllSecrets { - log.Fatal("cannot use '[secret] [version]' and '--all' together") + log.Fatal(i18n.G("cannot use '[secret] [version]' and '--all' together")) } composeFiles, err := app.Recipe.GetComposeFiles(app.Env) @@ -72,7 +72,7 @@ var AppSecretGenerateCommand = &cobra.Command{ secretVersion := args[2] s, ok := secrets[secretName] if !ok { - log.Fatalf("%s doesn't exist in the env config?", secretName) + log.Fatal(i18n.G("%s doesn't exist in the env config?", secretName)) } s.Version = secretVersion secrets = map[string]secret.Secret{ @@ -99,11 +99,11 @@ var AppSecretGenerateCommand = &cobra.Command{ } if len(secretVals) == 0 { - log.Warn("no secrets generated") + log.Warn(i18n.G("no secrets generated")) os.Exit(1) } - headers := []string{"NAME", "VALUE"} + headers := []string{i18n.G("NAME"), i18n.G("VALUE")} table, err := formatter.CreateTable() if err != nil { log.Fatal(err) @@ -121,7 +121,7 @@ var AppSecretGenerateCommand = &cobra.Command{ if internal.MachineReadable { out, err := formatter.ToJSON(headers, rows) if err != nil { - log.Fatal("unable to render to JSON: %s", err) + log.Fatal(i18n.G("unable to render to JSON: %s", err)) } fmt.Println(out) return @@ -131,31 +131,31 @@ var AppSecretGenerateCommand = &cobra.Command{ log.Fatal(err) } - log.Warnf( + log.Warn(i18n.G( "generated secrets %s shown again, please take note of them %s", - formatter.BoldStyle.Render("NOT"), - formatter.BoldStyle.Render("NOW"), - ) + formatter.BoldStyle.Render(i18n.G("NOT")), + formatter.BoldStyle.Render(i18n.G("NOW")), + )) }, } var AppSecretInsertCommand = &cobra.Command{ - Use: "insert [flags]", - Aliases: []string{"i"}, - Short: "Insert secret", - Long: `This command inserts a secret into an app environment. + Use: i18n.G("insert [flags]"), + Aliases: []string{i18n.G("i")}, + Short: i18n.G("Insert secret"), + Long: i18n.G(`This command inserts a secret into an app environment. Arbitrary secret insertion is not supported. Secrets that are inserted must match those configured in the recipe beforehand. This can be useful when you want to manually generate secrets for an app environment. Typically, you can let Abra generate them for you on app creation -(see "abra app new --secrets/-S" for more).`, - Example: ` # insert regular secret +(see "abra app new --secrets/-S" for more).`), + Example: i18n.G(` # insert regular secret abra app secret insert 1312.net my_secret v1 mySuperSecret # insert secret as file - abra app secret insert 1312.net my_secret v1 secret.txt -f`, + abra app secret insert 1312.net my_secret v1 secret.txt -f`), Args: cobra.MinimumNArgs(4), ValidArgsFunction: func( cmd *cobra.Command, @@ -167,8 +167,7 @@ environment. Typically, you can let Abra generate them for you on app creation case 1: app, err := appPkg.Get(args[0]) if err != nil { - errMsg := fmt.Sprintf("autocomplete failed: %s", err) - return []string{errMsg}, cobra.ShellCompDirectiveError + return []string{i18n.G("autocomplete failed: %s", err)}, cobra.ShellCompDirectiveError } return autocomplete.SecretComplete(app.Recipe.Name) default: @@ -208,13 +207,13 @@ environment. Typically, you can let Abra generate them for you on app creation } } if !isRecipeSecret { - log.Fatalf("no secret %s available for recipe %s?", name, app.Recipe.Name) + log.Fatal(i18n.G("no secret %s available for recipe %s?", name, app.Recipe.Name)) } if insertFromFile { raw, err := os.ReadFile(data) if err != nil { - log.Fatalf("reading secret from file: %s", err) + log.Fatal(i18n.G("reading secret from file: %s", err)) } data = string(raw) } @@ -228,7 +227,7 @@ environment. Typically, you can let Abra generate them for you on app creation log.Fatal(err) } - log.Infof("%s successfully stored on server", secretName) + log.Info(i18n.G("%s successfully stored on server", secretName)) if storeInPass { if err := secret.PassInsertSecret(data, name, app.Name, app.Server); err != nil { @@ -244,28 +243,28 @@ func secretRm(cl *dockerClient.Client, app appPkg.App, secretName, parsed string return err } - log.Infof("deleted %s successfully from server", secretName) + log.Info(i18n.G("deleted %s successfully from server", secretName)) if removeFromPass { if err := secret.PassRmSecret(parsed, app.StackName(), app.Server); err != nil { return err } - log.Infof("deleted %s successfully from local pass store", secretName) + log.Info(i18n.G("deleted %s successfully from local pass store", secretName)) } return nil } var AppSecretRmCommand = &cobra.Command{ - Use: "remove [[secret] | --all] [flags]", - Aliases: []string{"rm"}, - Short: "Remove a secret", - Long: `This command removes a secret from an app environment. + Use: i18n.G("remove [[secret] | --all] [flags]"), + Aliases: []string{i18n.G("rm")}, + Short: i18n.G("Remove a secret"), + Long: i18n.G(`This command removes a secret from an app environment. Arbitrary secret removal is not supported. Secrets that are removed must -match those configured in the recipe beforehand.`, - Example: " abra app secret rm 1312.net oauth_key", +match those configured in the recipe beforehand.`), + Example: i18n.G(" abra app secret rm 1312.net oauth_key"), Args: cobra.RangeArgs(1, 2), ValidArgsFunction: func( cmd *cobra.Command, @@ -278,8 +277,7 @@ match those configured in the recipe beforehand.`, if !rmAllSecrets { app, err := appPkg.Get(args[0]) if err != nil { - errMsg := fmt.Sprintf("autocomplete failed: %s", err) - return []string{errMsg}, cobra.ShellCompDirectiveError + return []string{i18n.G("autocomplete failed: %s", err)}, cobra.ShellCompDirectiveError } return autocomplete.SecretComplete(app.Recipe.Name) } @@ -306,11 +304,11 @@ match those configured in the recipe beforehand.`, } if len(args) == 2 && rmAllSecrets { - log.Fatal("cannot use [secret] and --all/-a together") + log.Fatal(i18n.G("cannot use [secret] and --all/-a together")) } if len(args) != 2 && !rmAllSecrets { - log.Fatal("no secret(s) specified?") + log.Fatal(i18n.G("no secret(s) specified?")) } cl, err := client.New(app.Server) @@ -361,19 +359,19 @@ match those configured in the recipe beforehand.`, } if !match && secretToRm != "" { - log.Fatalf("%s doesn't exist on server?", secretToRm) + log.Fatal(i18n.G("%s doesn't exist on server?", secretToRm)) } if !match { - log.Fatal("no secrets to remove?") + log.Fatal(i18n.G("no secrets to remove?")) } }, } var AppSecretLsCommand = &cobra.Command{ - Use: "list ", - Aliases: []string{"ls"}, - Short: "List all secrets", + Use: i18n.G("list "), + Aliases: []string{i18n.G("ls")}, + Short: i18n.G("List all secrets"), Args: cobra.MinimumNArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -393,7 +391,7 @@ var AppSecretLsCommand = &cobra.Command{ log.Fatal(err) } - headers := []string{"NAME", "VERSION", "GENERATED NAME", "CREATED ON SERVER"} + headers := []string{i18n.G("NAME"), i18n.G("VERSION"), i18n.G("GENERATED NAME"), i18n.G("CREATED ON SERVER")} table, err := formatter.CreateTable() if err != nil { log.Fatal(err) @@ -423,7 +421,7 @@ var AppSecretLsCommand = &cobra.Command{ if internal.MachineReadable { out, err := formatter.ToJSON(headers, rows) if err != nil { - log.Fatal("unable to render to JSON: %s", err) + log.Fatal(i18n.G("unable to render to JSON: %s", err)) } fmt.Println(out) return @@ -436,14 +434,14 @@ var AppSecretLsCommand = &cobra.Command{ return } - log.Warnf("no secrets stored for %s", app.Name) + log.Warn(i18n.G("no secrets stored for %s", app.Name)) }, } var AppSecretCommand = &cobra.Command{ - Use: "secret [cmd] [args] [flags]", - Aliases: []string{"s"}, - Short: "Manage app secrets", + Use: i18n.G("secret [cmd] [args] [flags]"), + Aliases: []string{i18n.G("s")}, + Short: i18n.G("Manage app secrets"), } var ( @@ -458,105 +456,105 @@ var ( func init() { AppSecretGenerateCommand.Flags().BoolVarP( &internal.MachineReadable, - "machine", - "m", + i18n.G("machine"), + i18n.G("m"), false, - "print machine-readable output", + i18n.G("print machine-readable output"), ) AppSecretGenerateCommand.Flags().BoolVarP( &storeInPass, - "pass", - "p", + i18n.G("pass"), + i18n.G("p"), false, - "store generated secrets in a local pass store", + i18n.G("store generated secrets in a local pass store"), ) AppSecretGenerateCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) AppSecretGenerateCommand.Flags().BoolVarP( &generateAllSecrets, - "all", - "a", + i18n.G("all"), + i18n.G("a"), false, - "generate all secrets", + i18n.G("generate all secrets"), ) AppSecretInsertCommand.Flags().BoolVarP( &storeInPass, - "pass", - "p", + i18n.G("pass"), + i18n.G("p"), false, - "store generated secrets in a local pass store", + i18n.G("store generated secrets in a local pass store"), ) AppSecretInsertCommand.Flags().BoolVarP( &insertFromFile, - "file", - "f", + i18n.G("file"), + i18n.G("f"), false, - "treat input as a file", + i18n.G("treat input as a file"), ) AppSecretInsertCommand.Flags().BoolVarP( &trimInput, - "trim", - "t", + i18n.G("trim"), + i18n.G("t"), false, - "trim input", + i18n.G("trim input"), ) AppSecretInsertCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) AppSecretRmCommand.Flags().BoolVarP( &rmAllSecrets, - "all", - "a", + i18n.G("all"), + i18n.G("a"), false, - "remove all secrets", + i18n.G("remove all secrets"), ) AppSecretRmCommand.Flags().BoolVarP( &removeFromPass, - "pass", - "p", + i18n.G("pass"), + i18n.G("p"), false, - "remove generated secrets from a local pass store", + i18n.G("remove generated secrets from a local pass store"), ) AppSecretRmCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) AppSecretLsCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) AppSecretLsCommand.Flags().BoolVarP( &internal.MachineReadable, - "machine", - "m", + i18n.G("machine"), + i18n.G("m"), false, - "print machine-readable output", + i18n.G("print machine-readable output"), ) } diff --git a/cli/app/services.go b/cli/app/services.go index a49eba9c..4843ea86 100644 --- a/cli/app/services.go +++ b/cli/app/services.go @@ -9,6 +9,7 @@ import ( "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/service" stack "coopcloud.tech/abra/pkg/upstream/stack" @@ -17,9 +18,9 @@ import ( ) var AppServicesCommand = &cobra.Command{ - Use: "services [flags]", - Aliases: []string{"sr"}, - Short: "Display all services of an app", + Use: i18n.G("services [flags]"), + Aliases: []string{i18n.G("sr")}, + Short: i18n.G("Display all services of an app"), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -45,7 +46,7 @@ var AppServicesCommand = &cobra.Command{ } if !deployMeta.IsDeployed { - log.Fatalf("%s is not deployed?", app.Name) + log.Fatal(i18n.G("%s is not deployed?", app.Name)) } filters, err := app.Filters(true, true) @@ -63,7 +64,7 @@ var AppServicesCommand = &cobra.Command{ log.Fatal(err) } - headers := []string{"SERVICE (SHORT)", "SERVICE (LONG)"} + headers := []string{i18n.G("SERVICE (SHORT)"), i18n.G("SERVICE (LONG)")} table.Headers(headers...) var rows [][]string diff --git a/cli/app/undeploy.go b/cli/app/undeploy.go index 12aba61c..ecb32365 100644 --- a/cli/app/undeploy.go +++ b/cli/app/undeploy.go @@ -10,6 +10,7 @@ import ( "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" stack "coopcloud.tech/abra/pkg/upstream/stack" "github.com/docker/docker/api/types/filters" @@ -18,15 +19,15 @@ import ( ) var AppUndeployCommand = &cobra.Command{ - Use: "undeploy [flags]", - Aliases: []string{"un"}, - Short: "Undeploy an app", - Long: `This does not destroy any application data. + Use: i18n.G("undeploy [flags]"), + Aliases: []string{i18n.G("un")}, + Short: i18n.G("Undeploy an app"), + Long: i18n.G(`This does not destroy any application data. However, you should remain vigilant, as your swarm installation will consider any previously attached volumes as eligible for pruning once undeployed. -Passing "--prune/-p" does not remove those volumes.`, +Passing "--prune/-p" does not remove those volumes.`), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -47,7 +48,7 @@ Passing "--prune/-p" does not remove those volumes.`, log.Fatal(err) } - log.Debugf("checking whether %s is already deployed", stackName) + log.Debug(i18n.G("checking whether %s is already deployed", stackName)) deployMeta, err := stack.IsDeployed(context.Background(), cl, stackName) if err != nil { @@ -55,7 +56,7 @@ Passing "--prune/-p" does not remove those volumes.`, } if !deployMeta.IsDeployed { - log.Fatalf("%s is not deployed?", app.Name) + log.Fatal(i18n.G("%s is not deployed?", app.Name)) } if err := internal.DeployOverview( @@ -84,7 +85,7 @@ Passing "--prune/-p" does not remove those volumes.`, log.Fatal(err) } - log.Info("initialising undeploy") + log.Info(i18n.G("initialising undeploy")) rmOpts := stack.Remove{ Namespaces: []string{stackName}, @@ -100,10 +101,10 @@ Passing "--prune/-p" does not remove those volumes.`, } } - log.Info("undeploy succeeded 🟢") + log.Info(i18n.G("undeploy succeeded 🟢")) if err := app.WriteRecipeVersion(deployMeta.Version, false); err != nil { - log.Fatalf("writing recipe version failed: %s", err) + log.Fatal(i18n.G("writing recipe version failed: %s", err)) } }, } @@ -124,14 +125,14 @@ func pruneApp(cl *dockerClient.Client, app appPkg.App) error { } cntSpaceReclaimed := formatter.ByteCountSI(cr.SpaceReclaimed) - log.Infof("containers pruned: %d; space reclaimed: %s", len(cr.ContainersDeleted), cntSpaceReclaimed) + log.Info(i18n.G("containers pruned: %d; space reclaimed: %s", len(cr.ContainersDeleted), cntSpaceReclaimed)) nr, err := cl.NetworksPrune(ctx, pruneFilters) if err != nil { return err } - log.Infof("networks pruned: %d", len(nr.NetworksDeleted)) + log.Info(i18n.G("networks pruned: %d", len(nr.NetworksDeleted))) ir, err := cl.ImagesPrune(ctx, pruneFilters) if err != nil { @@ -139,7 +140,7 @@ func pruneApp(cl *dockerClient.Client, app appPkg.App) error { } imgSpaceReclaimed := formatter.ByteCountSI(ir.SpaceReclaimed) - log.Infof("images pruned: %d; space reclaimed: %s", len(ir.ImagesDeleted), imgSpaceReclaimed) + log.Info(i18n.G("images pruned: %d; space reclaimed: %s", len(ir.ImagesDeleted), imgSpaceReclaimed)) return nil } @@ -151,9 +152,9 @@ var ( func init() { AppUndeployCommand.Flags().BoolVarP( &prune, - "prune", - "p", + i18n.G("prune"), + i18n.G("p"), false, - "prune unused containers, networks, and dangling images", + i18n.G("prune unused containers, networks, and dangling images"), ) } diff --git a/cli/app/upgrade.go b/cli/app/upgrade.go index 5e6ef87d..be574c45 100644 --- a/cli/app/upgrade.go +++ b/cli/app/upgrade.go @@ -2,6 +2,7 @@ package app import ( "context" + "errors" "fmt" "strings" @@ -13,6 +14,7 @@ import ( "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/envfile" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/lint" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/recipe" @@ -24,10 +26,10 @@ import ( ) var AppUpgradeCommand = &cobra.Command{ - Use: "upgrade [version] [flags]", - Aliases: []string{"up"}, - Short: "Upgrade an app", - Long: `Upgrade an app. + Use: i18n.G("upgrade [version] [flags]"), + Aliases: []string{i18n.G("up")}, + Short: i18n.G("Upgrade an app"), + Long: i18n.G(`Upgrade an app. Unlike "abra app deploy", chaos operations are not supported here. Only recipe versions are supported values for "[version]". @@ -40,7 +42,7 @@ are available. The live deployment version is the "source of truth" in this case. The stored .env version is not consulted. An upgrade can be destructive, please ensure you have a copy of your app data -beforehand. See "abra app backup" for more.`, +beforehand. See "abra app backup" for more.`), Args: cobra.RangeArgs(1, 2), ValidArgsFunction: func( cmd *cobra.Command, @@ -53,8 +55,7 @@ beforehand. See "abra app backup" for more.`, case 1: app, err := appPkg.Get(args[0]) if err != nil { - errMsg := fmt.Sprintf("autocomplete failed: %s", err) - return []string{errMsg}, cobra.ShellCompDirectiveError + return []string{i18n.G("autocomplete failed: %s", err)}, cobra.ShellCompDirectiveError } return autocomplete.RecipeVersionComplete(app.Recipe.Name) default: @@ -123,7 +124,7 @@ beforehand. See "abra app backup" for more.`, } if !upgradeAvailable { - log.Info("no available upgrades") + log.Info(i18n.G("no available upgrades")) return } } @@ -145,10 +146,10 @@ beforehand. See "abra app backup" for more.`, } if chosenUpgrade == "" { - log.Fatal("unknown deployed version, unable to upgrade") + log.Fatal(i18n.G("unknown deployed version, unable to upgrade")) } - log.Debugf("choosing %s as version to upgrade", chosenUpgrade) + log.Debug(i18n.G("choosing %s as version to upgrade", chosenUpgrade)) // Get the release notes before checking out the new version in the // recipe. This enables us to get release notes, that were added after @@ -204,7 +205,7 @@ beforehand. See "abra app backup" for more.`, for _, envVar := range envVars { if !envVar.Present { upgradeWarnMessages = append(upgradeWarnMessages, - fmt.Sprintf("%s missing from %s.env", envVar.Name, app.Domain), + i18n.G("%s missing from %s.env", envVar.Name, app.Domain), ) } } @@ -236,7 +237,7 @@ beforehand. See "abra app backup" for more.`, log.Fatal(err) } - log.Debugf("set waiting timeout to %d second(s)", stack.WaitTimeout) + log.Debug(i18n.G("set waiting timeout to %d second(s)", stack.WaitTimeout)) serviceNames, err := appPkg.GetAppServiceNames(app.Name) if err != nil { @@ -262,15 +263,15 @@ beforehand. See "abra app backup" for more.`, postDeployCmds, ok := app.Env["POST_UPGRADE_CMDS"] if ok && !internal.DontWaitConverge { - log.Debugf("run the following post-deploy commands: %s", postDeployCmds) + log.Debug(i18n.G("run the following post-deploy commands: %s", postDeployCmds)) if err := internal.PostCmds(cl, app, postDeployCmds); err != nil { - log.Fatalf("attempting to run post deploy commands, saw: %s", err) + log.Fatal(i18n.G("attempting to run post deploy commands, saw: %s", err)) } } if err := app.WriteRecipeVersion(chosenUpgrade, false); err != nil { - log.Fatalf("writing recipe version failed: %s", err) + log.Fatal(i18n.G("writing recipe version failed: %s", err)) } }, } @@ -281,12 +282,12 @@ func chooseUpgrade( deployMeta stack.DeployMeta, chosenUpgrade *string, ) error { - msg := fmt.Sprintf("please select an upgrade (version: %s):", deployMeta.Version) + msg := i18n.G("please select an upgrade (version: %s):", deployMeta.Version) if deployMeta.IsChaos { chaosVersion := formatter.BoldDirtyDefault(deployMeta.ChaosVersion) - msg = fmt.Sprintf( + msg = i18n.G( "please select an upgrade (version: %s, chaos: %s):", deployMeta.Version, chaosVersion, @@ -314,18 +315,18 @@ func getReleaseNotes( ) error { parsedChosenUpgrade, err := tagcmp.Parse(chosenUpgrade) if err != nil { - return fmt.Errorf("parsing chosen upgrade version failed: %s", err) + return errors.New(i18n.G("parsing chosen upgrade version failed: %s", err)) } parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) if err != nil { - return fmt.Errorf("parsing deployment version failed: %s", err) + return errors.New(i18n.G("parsing deployment version failed: %s", err)) } for _, version := range internal.SortVersionsDesc(versions) { parsedVersion, err := tagcmp.Parse(version) if err != nil { - return fmt.Errorf("parsing recipe version failed: %s", err) + return errors.New(i18n.G("parsing recipe version failed: %s", err)) } if parsedVersion.IsGreaterThan(parsedDeployedVersion) && @@ -358,13 +359,13 @@ func ensureUpgradesAvailable( ) (bool, error) { parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) if err != nil { - return false, fmt.Errorf("parsing deployed version failed: %s", err) + return false, errors.New(i18n.G("parsing deployed version failed: %s", err)) } for _, version := range versions { parsedVersion, err := tagcmp.Parse(version) if err != nil { - return false, fmt.Errorf("parsing recipe version failed: %s", err) + return false, errors.New(i18n.G("parsing recipe version failed: %s", err)) } if parsedVersion.IsGreaterThan(parsedDeployedVersion) && @@ -388,21 +389,21 @@ func validateUpgradeVersionArg( ) error { parsedSpecificVersion, err := tagcmp.Parse(specificVersion) if err != nil { - return fmt.Errorf("'%s' is not a known version for %s", specificVersion, app.Recipe.Name) + return errors.New(i18n.G("'%s' is not a known version for %s", specificVersion, app.Recipe.Name)) } parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) if err != nil { - return fmt.Errorf("'%s' is not a known version", deployMeta.Version) + return errors.New(i18n.G("'%s' is not a known version", deployMeta.Version)) } if parsedSpecificVersion.IsLessThan(parsedDeployedVersion) && !parsedSpecificVersion.Equals(parsedDeployedVersion) { - return fmt.Errorf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion) + return errors.New(i18n.G("%s is not an upgrade for %s?", deployMeta.Version, specificVersion)) } if parsedSpecificVersion.Equals(parsedDeployedVersion) && !internal.Force { - return fmt.Errorf("%s is not an upgrade for %s?", deployMeta.Version, specificVersion) + return errors.New(i18n.G("%s is not an upgrade for %s?", deployMeta.Version, specificVersion)) } return nil @@ -411,7 +412,7 @@ func validateUpgradeVersionArg( // ensureDeployed ensures the app is deployed and if so, returns deployment // meta info. func ensureDeployed(cl *dockerClient.Client, app app.App) (stack.DeployMeta, error) { - log.Debugf("checking whether %s is already deployed", app.StackName()) + log.Debug(i18n.G("checking whether %s is already deployed", app.StackName())) deployMeta, err := stack.IsDeployed(context.Background(), cl, app.StackName()) if err != nil { @@ -419,7 +420,7 @@ func ensureDeployed(cl *dockerClient.Client, app app.App) (stack.DeployMeta, err } if !deployMeta.IsDeployed { - return stack.DeployMeta{}, fmt.Errorf("%s is not deployed?", app.Name) + return stack.DeployMeta{}, errors.New(i18n.G("%s is not deployed?", app.Name)) } return deployMeta, nil @@ -430,32 +431,33 @@ var showReleaseNotes bool func init() { AppUpgradeCommand.Flags().BoolVarP( &internal.Force, - "force", - "f", + i18n.G("force"), + i18n.G("f"), false, - "perform action without further prompt", + i18n.G("perform action without further prompt"), ) AppUpgradeCommand.Flags().BoolVarP( &internal.NoDomainChecks, - "no-domain-checks", - "D", + i18n.G("no-domain-checks"), + i18n.G("D"), false, - "disable public DNS checks", + i18n.G("disable public DNS checks"), ) AppUpgradeCommand.Flags().BoolVarP( - &internal.DontWaitConverge, "no-converge-checks", - "c", + &internal.DontWaitConverge, + i18n.G("no-converge-checks"), + i18n.G("c"), false, - "disable converge logic checks", + i18n.G("disable converge logic checks"), ) AppUpgradeCommand.Flags().BoolVarP( &showReleaseNotes, - "releasenotes", - "r", + i18n.G("releasenotes"), + i18n.G("r"), false, - "only show release notes", + i18n.G("only show release notes"), ) } diff --git a/cli/app/volume.go b/cli/app/volume.go index a19ab65c..bde116e6 100644 --- a/cli/app/volume.go +++ b/cli/app/volume.go @@ -8,6 +8,7 @@ import ( "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/upstream/stack" "github.com/AlecAivazis/survey/v2" @@ -15,9 +16,9 @@ import ( ) var AppVolumeListCommand = &cobra.Command{ - Use: "list [flags]", - Aliases: []string{"ls"}, - Short: "List volumes associated with an app", + Use: i18n.G("list [flags]"), + Aliases: []string{i18n.G("ls")}, + Short: i18n.G("List volumes associated with an app"), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -43,7 +44,7 @@ var AppVolumeListCommand = &cobra.Command{ log.Fatal(err) } - headers := []string{"NAME", "ON SERVER"} + headers := []string{i18n.G("NAME"), i18n.G("ON SERVER")} table, err := formatter.CreateTable() if err != nil { @@ -67,14 +68,14 @@ var AppVolumeListCommand = &cobra.Command{ return } - log.Warnf("no volumes created for %s", app.Name) + log.Warn(i18n.G("no volumes created for %s", app.Name)) }, } var AppVolumeRemoveCommand = &cobra.Command{ - Use: "remove [volume] [flags]", - Short: "Remove volume(s) associated with an app", - Long: `Remove volumes associated with an app. + Use: i18n.G("remove [volume] [flags]"), + Short: i18n.G("Remove volume(s) associated with an app"), + Long: i18n.G(`Remove volumes associated with an app. The app in question must be undeployed before you try to remove volumes. See "abra app undeploy " for more. @@ -83,13 +84,13 @@ The command is interactive and will show a multiple select input which allows you to make a seclection. Use the "?" key to see more help on navigating this interface. -Passing "--force/-f" will select all volumes for removal. Be careful.`, - Example: ` # delete volumes interactively +Passing "--force/-f" will select all volumes for removal. Be careful.`), + Example: i18n.G(` # delete volumes interactively abra app volume rm 1312.net # delete specific volume - abra app volume rm 1312.net my_volume`, - Aliases: []string{"rm"}, + abra app volume rm 1312.net my_volume`), + Aliases: []string{i18n.G("rm")}, Args: cobra.MinimumNArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -116,7 +117,7 @@ Passing "--force/-f" will select all volumes for removal. Be careful.`, } if deployMeta.IsDeployed { - log.Fatalf("%s is still deployed. Run \"abra app undeploy %s\"", app.Name, app.Name) + log.Fatal(i18n.G("%s is still deployed. Run \"abra app undeploy %s\"", app.Name, app.Name)) } filters, err := app.Filters(false, true) @@ -141,15 +142,15 @@ Passing "--force/-f" will select all volumes for removal. Be careful.`, } if !exactMatch { - log.Fatalf("unable to remove volume: no volume with name '%s'?", volumeToDelete) + log.Fatal(i18n.G("unable to remove volume: no volume with name '%s'?", volumeToDelete)) } err := client.RemoveVolumes(cl, context.Background(), []string{fullVolumeToDeleteName}, internal.Force, 5) if err != nil { - log.Fatalf("removing volume %s failed: %s", volumeToDelete, err) + log.Fatal(i18n.G("removing volume %s failed: %s", volumeToDelete, err)) } - log.Infof("volume %s removed successfully", volumeToDelete) + log.Info(i18n.G("volume %s removed successfully", volumeToDelete)) return } @@ -157,8 +158,8 @@ Passing "--force/-f" will select all volumes for removal. Be careful.`, var volumesToRemove []string if !internal.Force && !internal.NoInput { volumesPrompt := &survey.MultiSelect{ - Message: "which volumes do you want to remove?", - Help: "'x' indicates selected, enter / return to confirm, ctrl-c to exit, vim mode is enabled", + Message: i18n.G("which volumes do you want to remove?"), + Help: i18n.G("'x' indicates selected, enter / return to confirm, ctrl-c to exit, vim mode is enabled"), VimMode: true, Options: volumeNames, Default: volumeNames, @@ -175,28 +176,28 @@ Passing "--force/-f" will select all volumes for removal. Be careful.`, if len(volumesToRemove) > 0 { err := client.RemoveVolumes(cl, context.Background(), volumesToRemove, internal.Force, 5) if err != nil { - log.Fatalf("removing volumes failed: %s", err) + log.Fatal(i18n.G("removing volumes failed: %s", err)) } - log.Infof("%d volumes removed successfully", len(volumesToRemove)) + log.Info(i18n.G("%d volumes removed successfully", len(volumesToRemove))) } else { - log.Info("no volumes removed") + log.Info(i18n.G("no volumes removed")) } }, } var AppVolumeCommand = &cobra.Command{ - Use: "volume [cmd] [args] [flags]", - Aliases: []string{"vl"}, - Short: "Manage app volumes", + Use: i18n.G("volume [cmd] [args] [flags]"), + Aliases: []string{i18n.G("vl")}, + Short: i18n.G("Manage app volumes"), } func init() { AppVolumeRemoveCommand.Flags().BoolVarP( &internal.Force, - "force", - "f", + i18n.G("force"), + i18n.G("f"), false, - "perform action without further prompt", + i18n.G("perform action without further prompt"), ) } diff --git a/cli/catalogue/catalogue.go b/cli/catalogue/catalogue.go index 9c7abd27..6998f1aa 100644 --- a/cli/catalogue/catalogue.go +++ b/cli/catalogue/catalogue.go @@ -13,6 +13,7 @@ import ( "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/formatter" gitPkg "coopcloud.tech/abra/pkg/git" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/recipe" "github.com/go-git/go-git/v5" @@ -20,10 +21,10 @@ import ( ) var CatalogueGenerateCommand = &cobra.Command{ - Use: "generate [recipe] [flags]", - Aliases: []string{"g"}, - Short: "Generate the recipe catalogue", - Long: `Generate a new copy of the recipe catalogue. + Use: i18n.G("generate [recipe] [flags]"), + Aliases: []string{i18n.G("g")}, + Short: i18n.G("Generate the recipe catalogue"), + Long: i18n.G(`Generate a new copy of the recipe catalogue. N.B. this command **will** wipe local unstaged changes from your local recipes if present. "--chaos/-C" on this command refers to the catalogue repository @@ -39,7 +40,7 @@ use those details. Push your new release to git.coopcloud.tech with "--publish/-p". This requires that you have permission to git push to these repositories and have your SSH -keys configured on your account.`, +keys configured on your account.`), Args: cobra.RangeArgs(0, 1), ValidArgsFunction: func( cmd *cobra.Command, @@ -85,7 +86,7 @@ keys configured on your account.`, var warnings []string catl := make(recipe.RecipeCatalogue) - catlBar := formatter.CreateProgressbar(barLength, "collecting catalogue metadata") + catlBar := formatter.CreateProgressbar(barLength, i18n.G("collecting catalogue metadata")) for _, recipeMeta := range repos { if recipeName != "" && recipeName != recipeMeta.Name { if !internal.Debug { @@ -171,7 +172,7 @@ keys configured on your account.`, } } - log.Infof("generated recipe catalogue: %s", config.RECIPES_JSON) + log.Info(i18n.G("generated recipe catalogue: %s", config.RECIPES_JSON)) cataloguePath := path.Join(config.ABRA_DIR, "catalogue") if publishChanges { @@ -183,11 +184,11 @@ keys configured on your account.`, if isClean { if !internal.Dry { - log.Fatalf("no changes discovered in %s, nothing to publish?", cataloguePath) + log.Fatal(i18n.G("no changes discovered in %s, nothing to publish?", cataloguePath)) } } - msg := "chore: publish new catalogue release changes" + msg := i18n.G("chore: publish new catalogue release changes") if err := gitPkg.Commit(cataloguePath, msg, internal.Dry); err != nil { log.Fatal(err) } @@ -219,19 +220,19 @@ keys configured on your account.`, if !internal.Dry && publishChanges { url := fmt.Sprintf("%s/%s/commit/%s", config.REPOS_BASE_URL, config.CATALOGUE_JSON_REPO_NAME, head.Hash()) - log.Infof("new changes published: %s", url) + log.Info(i18n.G("new changes published: %s", url)) } if internal.Dry { - log.Info("dry run: no changes published") + log.Info(i18n.G("dry run: no changes published")) } }, } // CatalogueCommand defines the `abra catalogue` command and sub-commands. var CatalogueCommand = &cobra.Command{ - Use: "catalogue [cmd] [args] [flags]", - Short: "Manage the recipe catalogue", + Use: i18n.G("catalogue [cmd] [args] [flags]"), + Short: i18n.G("Manage the recipe catalogue"), Aliases: []string{"c"}, } @@ -243,33 +244,33 @@ var ( func init() { CatalogueGenerateCommand.Flags().BoolVarP( &publishChanges, - "publish", - "p", + i18n.G("publish"), + i18n.G("p"), false, - "publish changes to git.coopcloud.tech", + i18n.G("publish changes to git.coopcloud.tech"), ) CatalogueGenerateCommand.Flags().BoolVarP( &internal.Dry, - "dry-run", - "r", + i18n.G("dry-run"), + i18n.G("r"), false, - "report changes that would be made", + i18n.G("report changes that would be made"), ) CatalogueGenerateCommand.Flags().BoolVarP( &skipUpdates, - "skip-updates", - "s", + i18n.G("skip-updates"), + i18n.G("s"), false, - "skip updating recipe repositories", + i18n.G("skip updating recipe repositories"), ) CatalogueGenerateCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) } diff --git a/cli/complete.go b/cli/complete.go index 7f520df9..3eeb2105 100644 --- a/cli/complete.go +++ b/cli/complete.go @@ -3,13 +3,14 @@ package cli import ( "os" + "coopcloud.tech/abra/pkg/i18n" "github.com/spf13/cobra" ) var AutocompleteCommand = &cobra.Command{ - Use: "autocomplete [bash|zsh|fish|powershell]", - Short: "Generate autocompletion script", - Long: `To load completions: + Use: i18n.G("autocomplete [bash|zsh|fish|powershell]"), + Short: i18n.G("Generate autocompletion script"), + Long: i18n.G(`To load completions: Bash: # Load autocompletion for the current Bash session @@ -43,7 +44,7 @@ PowerShell: # To load autocompletions for every new session, run: PS> abra autocomplete powershell > abra.ps1 - # and source this file from your PowerShell profile.`, + # and source this file from your PowerShell profile.`), DisableFlagsInUseLine: true, ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), diff --git a/cli/internal/backup.go b/cli/internal/backup.go index 7554f973..d4355ef0 100644 --- a/cli/internal/backup.go +++ b/cli/internal/backup.go @@ -2,11 +2,12 @@ package internal import ( "context" - "fmt" + "errors" "io" "coopcloud.tech/abra/pkg/config" containerPkg "coopcloud.tech/abra/pkg/container" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/service" "coopcloud.tech/abra/pkg/upstream/container" @@ -22,10 +23,10 @@ func RetrieveBackupBotContainer(cl *dockerClient.Client) (types.Container, error ctx := context.Background() chosenService, err := service.GetServiceByLabel(ctx, cl, config.BackupbotLabel, NoInput) if err != nil { - return types.Container{}, fmt.Errorf("no backupbot discovered, is it deployed?") + return types.Container{}, errors.New(i18n.G("no backupbot discovered, is it deployed?")) } - log.Debugf("retrieved %s as backup enabled service", chosenService.Spec.Name) + log.Debug(i18n.G("retrieved %s as backup enabled service", chosenService.Spec.Name)) filters := filters.NewArgs() filters.Add("name", chosenService.Spec.Name) @@ -58,7 +59,7 @@ func RunBackupCmdRemote( Tty: true, } - log.Debugf("running backup %s on %s with exec config %v", backupCmd, containerID, execBackupListOpts) + log.Debug(i18n.G("running backup %s on %s with exec config %v", backupCmd, containerID, execBackupListOpts)) // FIXME: avoid instantiating a new CLI dcli, err := command.NewDockerCli() diff --git a/cli/internal/command.go b/cli/internal/command.go index 854d82d0..7f9ea578 100644 --- a/cli/internal/command.go +++ b/cli/internal/command.go @@ -3,6 +3,7 @@ package internal import ( "bufio" "context" + "errors" "fmt" "io/ioutil" "os/exec" @@ -11,6 +12,7 @@ import ( appPkg "coopcloud.tech/abra/pkg/app" containerPkg "coopcloud.tech/abra/pkg/container" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/upstream/container" "github.com/docker/cli/cli/command" @@ -34,7 +36,7 @@ func RunCmdRemote( return err } - log.Debugf("retrieved %s as target container on %s", formatter.ShortenID(targetContainer.ID), app.Server) + log.Debug(i18n.G("retrieved %s as target container on %s", formatter.ShortenID(targetContainer.ID), app.Server)) toTarOpts := &archive.TarOptions{NoOverwriteDirNonDir: true, Compression: archive.Gzip} content, err := archive.TarWithOptions(abraSh, toTarOpts) @@ -65,7 +67,7 @@ func RunCmdRemote( } if _, err := container.RunExec(dcli, cl, targetContainer.ID, &execCreateOpts); err != nil { - log.Infof("%s does not exist for %s, use /bin/sh as fallback", shell, app.Name) + log.Info(i18n.G("%s does not exist for %s, use /bin/sh as fallback", shell, app.Name)) shell = "/bin/sh" } @@ -76,10 +78,10 @@ func RunCmdRemote( cmd = []string{shell, "-c", fmt.Sprintf("TARGET=%s; APP_NAME=%s; STACK_NAME=%s; . /tmp/abra.sh; %s", serviceName, app.Name, app.StackName(), cmdName)} } - log.Debugf("running command: %s", strings.Join(cmd, " ")) + log.Debug(i18n.G("running command: %s", strings.Join(cmd, " "))) if remoteUser != "" { - log.Debugf("running command with user %s", remoteUser) + log.Debug(i18n.G("running command with user %s", remoteUser)) execCreateOpts.User = remoteUser } @@ -88,7 +90,7 @@ func RunCmdRemote( execCreateOpts.Tty = true if disableTTY { execCreateOpts.Tty = false - log.Debugf("not requesting a remote TTY") + log.Debug(i18n.G("not requesting a remote TTY")) } if _, err := container.RunExec(dcli, cl, targetContainer.ID, &execCreateOpts); err != nil { @@ -105,7 +107,7 @@ func EnsureCommand(abraSh, recipeName, execCmd string) error { } if !strings.Contains(string(bytes), execCmd) { - return fmt.Errorf("%s doesn't have a %s function", recipeName, execCmd) + return errors.New(i18n.G("%s doesn't have a %s function", recipeName, execCmd)) } return nil diff --git a/cli/internal/deploy.go b/cli/internal/deploy.go index d687c49f..37406128 100644 --- a/cli/internal/deploy.go +++ b/cli/internal/deploy.go @@ -1,6 +1,7 @@ package internal import ( + "errors" "fmt" "os" "sort" @@ -9,6 +10,7 @@ import ( appPkg "coopcloud.tech/abra/pkg/app" "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/tagcmp" "github.com/AlecAivazis/survey/v2" @@ -70,18 +72,18 @@ func DeployOverview( } rows := [][]string{ - {"DOMAIN", domain}, - {"RECIPE", app.Recipe.Name}, - {"SERVER", server}, - {"CONFIG", deployConfig}, + {i18n.G("DOMAIN"), domain}, + {i18n.G("RECIPE"), app.Recipe.Name}, + {i18n.G("SERVER"), server}, + {i18n.G("CONFIG"), deployConfig}, {"", ""}, - {"CURRENT DEPLOYMENT", formatter.BoldDirtyDefault(deployedVersion)}, - {"ENV VERSION", formatter.BoldDirtyDefault(envVersion)}, - {"NEW DEPLOYMENT", formatter.BoldDirtyDefault(toDeployVersion)}, + {i18n.G("CURRENT DEPLOYMENT"), formatter.BoldDirtyDefault(deployedVersion)}, + {i18n.G("ENV VERSION"), formatter.BoldDirtyDefault(envVersion)}, + {i18n.G("NEW DEPLOYMENT"), formatter.BoldDirtyDefault(toDeployVersion)}, } deployType := getDeployType(deployedVersion, toDeployVersion) - overview := formatter.CreateOverview(fmt.Sprintf("%s OVERVIEW", deployType), rows) + overview := formatter.CreateOverview(i18n.G("%s OVERVIEW", deployType), rows) fmt.Println(overview) @@ -104,7 +106,7 @@ func DeployOverview( } if !response { - log.Fatal("deployment cancelled") + log.Fatal(i18n.G("deployment cancelled")) } return nil @@ -112,32 +114,32 @@ func DeployOverview( func getDeployType(currentVersion, newVersion string) string { if newVersion == config.NO_DOMAIN_DEFAULT { - return "UNDEPLOY" + return i18n.G("UNDEPLOY") } if strings.Contains(newVersion, "+U") { - return "CHAOS DEPLOY" + return i18n.G("CHAOS DEPLOY") } if strings.Contains(currentVersion, "+U") { - return "UNCHAOS DEPLOY" + return i18n.G("UNCHAOS DEPLOY") } if currentVersion == newVersion { - return "REDEPLOY" + return ("REDEPLOY") } if currentVersion == config.NO_VERSION_DEFAULT { - return "NEW DEPLOY" + return i18n.G("NEW DEPLOY") } currentParsed, err := tagcmp.Parse(currentVersion) if err != nil { - return "DEPLOY" + return i18n.G("DEPLOY") } newParsed, err := tagcmp.Parse(newVersion) if err != nil { - return "DEPLOY" + return i18n.G("DEPLOY") } if currentParsed.IsLessThan(newParsed) { - return "UPGRADE" + return i18n.G("UPGRADE") } - return "DOWNGRADE" + return i18n.G("DOWNGRADE") } // PostCmds parses a string of commands and executes them inside of the respective services @@ -146,7 +148,7 @@ func getDeployType(currentVersion, newVersion string) string { func PostCmds(cl *dockerClient.Client, app appPkg.App, commands string) error { if _, err := os.Stat(app.Recipe.AbraShPath); err != nil { if os.IsNotExist(err) { - return fmt.Errorf("%s does not exist for %s?", app.Recipe.AbraShPath, app.Name) + return errors.New(i18n.G("%s does not exist for %s?", app.Recipe.AbraShPath, app.Name)) } return err } @@ -154,7 +156,7 @@ func PostCmds(cl *dockerClient.Client, app appPkg.App, commands string) error { for _, command := range strings.Split(commands, "|") { commandParts := strings.Split(command, " ") if len(commandParts) < 2 { - return fmt.Errorf("not enough arguments: %s", command) + return errors.New(i18n.G("not enough arguments: %s", command)) } targetServiceName := commandParts[0] cmdName := commandParts[1] @@ -162,7 +164,7 @@ func PostCmds(cl *dockerClient.Client, app appPkg.App, commands string) error { if len(commandParts) > 2 { parsedCmdArgs = fmt.Sprintf("%s ", strings.Join(commandParts[2:], " ")) } - log.Infof("running post-command '%s %s' in container %s", cmdName, parsedCmdArgs, targetServiceName) + log.Info(i18n.G("running post-command '%s %s' in container %s", cmdName, parsedCmdArgs, targetServiceName)) if err := EnsureCommand(app.Recipe.AbraShPath, app.Recipe.Name, cmdName); err != nil { return err @@ -184,7 +186,7 @@ func PostCmds(cl *dockerClient.Client, app appPkg.App, commands string) error { return fmt.Errorf("no service %s for %s?", targetServiceName, app.Name) } - log.Debugf("running command %s %s within the context of %s_%s", cmdName, parsedCmdArgs, app.StackName(), targetServiceName) + log.Debug(i18n.G("running command %s %s within the context of %s_%s", cmdName, parsedCmdArgs, app.StackName(), targetServiceName)) requestTTY := true if err := RunCmdRemote( diff --git a/cli/internal/recipe.go b/cli/internal/recipe.go index 2e209ae1..1c253ef3 100644 --- a/cli/internal/recipe.go +++ b/cli/internal/recipe.go @@ -1,9 +1,11 @@ package internal import ( + "errors" "fmt" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/recipe" "github.com/AlecAivazis/survey/v2" @@ -13,7 +15,7 @@ import ( // PromptBumpType prompts for version bump type func PromptBumpType(tagString, latestRelease string) error { if (!Major && !Minor && !Patch) && tagString == "" { - fmt.Printf(` + fmt.Print(i18n.G(` You need to make a decision about what kind of an update this new recipe version is. If someone else performs this upgrade, do they have to do some migration work or take care of some breaking changes? This can be signaled in @@ -36,12 +38,12 @@ Here is a semver cheat sheet (more on https://semver.org): should also Just Work and is mostly to do with minor bug fixes and/or security patches. "nothing to worry about". -`, latestRelease) +`, latestRelease)) var chosenBumpType string prompt := &survey.Select{ Message: fmt.Sprintf("select recipe version increment type"), - Options: []string{"major", "minor", "patch"}, + Options: []string{i18n.G("major"), i18n.G("minor"), i18n.G("patch")}, } if err := survey.AskOne(prompt, &chosenBumpType); err != nil { @@ -59,13 +61,13 @@ func GetBumpType() string { var bumpType string if Major { - bumpType = "major" + bumpType = i18n.G("major") } else if Minor { - bumpType = "minor" + bumpType = i18n.G("minor") } else if Patch { - bumpType = "patch" + bumpType = i18n.G("patch") } else { - log.Fatal("no version bump type specififed?") + log.Fatal(i18n.G("no version bump type specififed?")) } return bumpType @@ -73,14 +75,14 @@ func GetBumpType() string { // SetBumpType figures out which bump type is specified func SetBumpType(bumpType string) { - if bumpType == "major" { + if bumpType == i18n.G("major") { Major = true - } else if bumpType == "minor" { + } else if bumpType == i18n.G("minor") { Minor = true - } else if bumpType == "patch" { + } else if bumpType == i18n.G("patch") { Patch = true } else { - log.Fatal("no version bump type specififed?") + log.Fatal(i18n.G("no version bump type specififed?")) } } @@ -107,7 +109,7 @@ func GetMainAppImage(recipe recipe.Recipe) (string, error) { } if path == "" { - return path, fmt.Errorf("%s has no main 'app' service?", recipe.Name) + return path, errors.New(i18n.G("%s has no main 'app' service?", recipe.Name)) } return path, nil diff --git a/cli/internal/validate.go b/cli/internal/validate.go index 9c067db4..f39b9d34 100644 --- a/cli/internal/validate.go +++ b/cli/internal/validate.go @@ -5,6 +5,7 @@ import ( "coopcloud.tech/abra/pkg/app" "coopcloud.tech/abra/pkg/config" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/recipe" "github.com/AlecAivazis/survey/v2" @@ -31,7 +32,7 @@ func ValidateRecipe(args []string, cmdName string) recipe.Recipe { localRecipes, err := recipe.GetRecipesLocal() if err != nil { - log.Debugf("can't read local recipes: %s", err) + log.Debug(i18n.G("can't read local recipes: %s", err)) } else { for _, recipeLocal := range localRecipes { if _, ok := knownRecipes[recipeLocal]; !ok { @@ -46,7 +47,7 @@ func ValidateRecipe(args []string, cmdName string) recipe.Recipe { if recipeName == "" && !NoInput { prompt := &survey.Select{ - Message: "Select recipe", + Message: i18n.G("Select recipe"), Options: recipes, } if err := survey.AskOne(prompt, &recipeName); err != nil { @@ -55,12 +56,12 @@ func ValidateRecipe(args []string, cmdName string) recipe.Recipe { } if recipeName == "" { - log.Fatal("no recipe name provided") + log.Fatal(i18n.G("no recipe name provided")) } if _, ok := knownRecipes[recipeName]; !ok { if !strings.Contains(recipeName, "/") { - log.Fatalf("no recipe '%s' exists?", recipeName) + log.Fatal(i18n.G("no recipe '%s' exists?", recipeName)) } } @@ -71,20 +72,20 @@ func ValidateRecipe(args []string, cmdName string) recipe.Recipe { _, err = chosenRecipe.GetComposeConfig(nil) if err != nil { - if cmdName == "generate" { + if cmdName == i18n.G("generate") { if strings.Contains(err.Error(), "missing a compose") { log.Fatal(err) } log.Warn(err) } else { if strings.Contains(err.Error(), "template_driver is not allowed") { - log.Warnf("ensure %s recipe compose.* files include \"version: '3.8'\"", recipeName) + log.Warn(i18n.G("ensure %s recipe compose.* files include \"version: '3.8'\"", recipeName)) } - log.Fatalf("unable to validate recipe: %s", err) + log.Fatal(i18n.G("unable to validate recipe: %s", err)) } } - log.Debugf("validated %s as recipe argument", recipeName) + log.Debug(i18n.G("validated %s as recipe argument", recipeName)) return chosenRecipe } @@ -92,7 +93,7 @@ func ValidateRecipe(args []string, cmdName string) recipe.Recipe { // ValidateApp ensures the app name arg is valid. func ValidateApp(args []string) app.App { if len(args) == 0 { - log.Fatal("no app provided") + log.Fatal(i18n.G("no app provided")) } appName := args[0] @@ -102,7 +103,7 @@ func ValidateApp(args []string) app.App { log.Fatal(err) } - log.Debugf("validated %s as app argument", appName) + log.Debug(i18n.G("validated %s as app argument", appName)) return app } @@ -116,8 +117,8 @@ func ValidateDomain(args []string) string { if domainName == "" && !NoInput { prompt := &survey.Input{ - Message: "Specify a domain name", - Default: "example.com", + Message: i18n.G("Specify a domain name"), + Default: "1312.net", } if err := survey.AskOne(prompt, &domainName); err != nil { log.Fatal(err) @@ -125,10 +126,10 @@ func ValidateDomain(args []string) string { } if domainName == "" { - log.Fatal("no domain provided") + log.Fatal(i18n.G("no domain provided")) } - log.Debugf("validated %s as domain argument", domainName) + log.Debug(i18n.G("validated %s as domain argument", domainName)) return domainName } @@ -147,7 +148,7 @@ func ValidateServer(args []string) string { if serverName == "" && !NoInput { prompt := &survey.Select{ - Message: "Specify a server name", + Message: i18n.G("Specify a server name"), Options: serverNames, } if err := survey.AskOne(prompt, &serverName); err != nil { @@ -163,14 +164,14 @@ func ValidateServer(args []string) string { } if serverName == "" { - log.Fatal("no server provided") + log.Fatal(i18n.G("no server provided")) } if !matched { - log.Fatal("server doesn't exist?") + log.Fatal(i18n.G("server doesn't exist?")) } - log.Debugf("validated %s as server argument", serverName) + log.Debug(i18n.G("validated %s as server argument", serverName)) return serverName } diff --git a/cli/recipe/diff.go b/cli/recipe/diff.go index b8bdfd67..990352c8 100644 --- a/cli/recipe/diff.go +++ b/cli/recipe/diff.go @@ -4,15 +4,16 @@ import ( "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/autocomplete" gitPkg "coopcloud.tech/abra/pkg/git" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/spf13/cobra" ) var RecipeDiffCommand = &cobra.Command{ - Use: "diff [flags]", - Aliases: []string{"d"}, - Short: "Show unstaged changes in recipe config", - Long: "This command requires /usr/bin/git.", + Use: i18n.G("diff [flags]"), + Aliases: []string{i18n.G("d")}, + Short: i18n.G("Show unstaged changes in recipe config"), + Long: i18n.G("This command requires /usr/bin/git."), Args: cobra.MinimumNArgs(1), ValidArgsFunction: func( cmd *cobra.Command, diff --git a/cli/recipe/fetch.go b/cli/recipe/fetch.go index a5c1a368..519130d3 100644 --- a/cli/recipe/fetch.go +++ b/cli/recipe/fetch.go @@ -6,6 +6,7 @@ import ( "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/recipe" "github.com/go-git/go-git/v5" @@ -14,19 +15,19 @@ import ( ) var RecipeFetchCommand = &cobra.Command{ - Use: "fetch [recipe | --all] [flags]", - Aliases: []string{"f"}, - Short: "Clone recipe(s) locally", - Long: `Using "--force/-f" Git syncs an existing recipe. It does not erase unstaged changes.`, + Use: i18n.G("fetch [recipe | --all] [flags]"), + Aliases: []string{i18n.G("f")}, + Short: i18n.G("Clone recipe(s) locally"), + Long: i18n.G(`Using "--force/-f" Git syncs an existing recipe. It does not erase unstaged changes.`), Args: cobra.RangeArgs(0, 1), - Example: ` # fetch from recipe catalogue + Example: i18n.G(` # fetch from recipe catalogue abra recipe fetch gitea # fetch from remote recipe abra recipe fetch git.foo.org/recipes/myrecipe # fetch with ssh remote for hacking - abra recipe fetch gitea --ssh`, + abra recipe fetch gitea --ssh`), ValidArgsFunction: func( cmd *cobra.Command, args []string, @@ -40,18 +41,18 @@ var RecipeFetchCommand = &cobra.Command{ } if recipeName == "" && !fetchAllRecipes { - log.Fatal("missing [recipe] or --all/-a") + log.Fatal(i18n.G("missing [recipe] or --all/-a")) } if recipeName != "" && fetchAllRecipes { - log.Fatal("cannot use [recipe] and --all/-a together") + log.Fatal(i18n.G("cannot use [recipe] and --all/-a together")) } if recipeName != "" { r := recipe.Get(recipeName) if _, err := os.Stat(r.Dir); !os.IsNotExist(err) { if !force { - log.Warnf("%s is already fetched", r.Name) + log.Warn(i18n.G("%s is already fetched", r.Name)) return } } @@ -60,24 +61,24 @@ var RecipeFetchCommand = &cobra.Command{ if sshRemote { if r.SSHURL == "" { - log.Warnf("unable to discover SSH remote for %s", r.Name) + log.Warn(i18n.G("unable to discover SSH remote for %s", r.Name)) return } repo, err := git.PlainOpen(r.Dir) if err != nil { - log.Fatalf("unable to open %s: %s", r.Dir, err) + log.Fatal(i18n.G("unable to open %s: %s", r.Dir, err)) } if err = repo.DeleteRemote("origin"); err != nil { - log.Fatalf("unable to remove default remote in %s: %s", r.Dir, err) + log.Fatal(i18n.G("unable to remove default remote in %s: %s", r.Dir, err)) } if _, err := repo.CreateRemote(&gitCfg.RemoteConfig{ Name: "origin", URLs: []string{r.SSHURL}, }); err != nil { - log.Fatalf("unable to set SSH remote in %s: %s", r.Dir, err) + log.Fatal(i18n.G("unable to set SSH remote in %s: %s", r.Dir, err)) } } @@ -89,7 +90,7 @@ var RecipeFetchCommand = &cobra.Command{ log.Fatal(err) } - catlBar := formatter.CreateProgressbar(len(catalogue), "fetching latest recipes...") + catlBar := formatter.CreateProgressbar(len(catalogue), i18n.G("fetching latest recipes...")) ensureCtx := internal.GetEnsureContext() for recipeName := range catalogue { r := recipe.Get(recipeName) @@ -110,25 +111,25 @@ var ( func init() { RecipeFetchCommand.Flags().BoolVarP( &fetchAllRecipes, - "all", - "a", + i18n.G("all"), + i18n.G("a"), false, - "fetch all recipes", + i18n.G("fetch all recipes"), ) RecipeFetchCommand.Flags().BoolVarP( &sshRemote, - "ssh", - "s", + i18n.G("ssh"), + i18n.G("s"), false, - "automatically set ssh remote", + i18n.G("automatically set ssh remote"), ) RecipeFetchCommand.Flags().BoolVarP( &force, - "force", - "f", + i18n.G("force"), + i18n.G("f"), false, - "force re-fetch", + i18n.G("force re-fetch"), ) } diff --git a/cli/recipe/lint.go b/cli/recipe/lint.go index d885fe04..55c4d523 100644 --- a/cli/recipe/lint.go +++ b/cli/recipe/lint.go @@ -4,15 +4,16 @@ import ( "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/lint" "coopcloud.tech/abra/pkg/log" "github.com/spf13/cobra" ) var RecipeLintCommand = &cobra.Command{ - Use: "lint [flags]", - Short: "Lint a recipe", - Aliases: []string{"l"}, + Use: i18n.G("lint [flags]"), + Short: i18n.G("Lint a recipe"), + Aliases: []string{i18n.G("l")}, Args: cobra.MinimumNArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -28,12 +29,12 @@ var RecipeLintCommand = &cobra.Command{ } headers := []string{ - "ref", - "rule", - "severity", - "satisfied", - "skipped", - "resolve", + i18n.G("ref"), + i18n.G("rule"), + i18n.G("severity"), + i18n.G("satisfied"), + i18n.G("skipped"), + i18n.G("resolve"), } table, err := formatter.CreateTable() @@ -49,7 +50,7 @@ var RecipeLintCommand = &cobra.Command{ for level := range lint.LintRules { for _, rule := range lint.LintRules[level] { if onlyError && rule.Level != "error" { - log.Debugf("skipping %s, does not have level \"error\"", rule.Ref) + log.Debug(i18n.G("skipping %s, does not have level \"error\"", rule.Ref)) continue } @@ -70,7 +71,7 @@ var RecipeLintCommand = &cobra.Command{ warnMessages = append(warnMessages, err.Error()) } - if !ok && rule.Level == "error" { + if !ok && rule.Level == i18n.G("error") { hasError = true } @@ -111,7 +112,7 @@ var RecipeLintCommand = &cobra.Command{ } if hasError { - log.Warnf("critical errors present in %s config", recipe.Name) + log.Warn(i18n.G("critical errors present in %s config", recipe.Name)) } } }, @@ -124,17 +125,17 @@ var ( func init() { RecipeLintCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) RecipeLintCommand.Flags().BoolVarP( &onlyError, - "error", - "e", + i18n.G("error"), + i18n.G("e"), false, - "only show errors", + i18n.G("only show errors"), ) } diff --git a/cli/recipe/list.go b/cli/recipe/list.go index e0b44c08..884dccb1 100644 --- a/cli/recipe/list.go +++ b/cli/recipe/list.go @@ -8,15 +8,16 @@ import ( "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/recipe" "github.com/spf13/cobra" ) var RecipeListCommand = &cobra.Command{ - Use: "list", - Short: "List recipes", - Aliases: []string{"ls"}, + Use: i18n.G("list"), + Short: i18n.G("List recipes"), + Aliases: []string{i18n.G("ls")}, Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { catl, err := recipe.ReadRecipeCatalogue(internal.Offline) @@ -33,14 +34,14 @@ var RecipeListCommand = &cobra.Command{ } headers := []string{ - "name", - "category", - "status", - "healthcheck", - "backups", - "email", - "tests", - "SSO", + i18n.G("name"), + i18n.G("category"), + i18n.G("status"), + i18n.G("healthcheck"), + i18n.G("backups"), + i18n.G("email"), + i18n.G("tests"), + i18n.G("SSO"), } table.Headers(headers...) @@ -73,7 +74,7 @@ var RecipeListCommand = &cobra.Command{ if internal.MachineReadable { out, err := formatter.ToJSON(headers, rows) if err != nil { - log.Fatal("unable to render to JSON: %s", err) + log.Fatal(i18n.G("unable to render to JSON: %s", err)) } fmt.Println(out) return @@ -93,17 +94,17 @@ var ( func init() { RecipeListCommand.Flags().BoolVarP( &internal.MachineReadable, - "machine", - "m", + i18n.G("machine"), + i18n.G("m"), false, - "print machine-readable output", + i18n.G("print machine-readable output"), ) RecipeListCommand.Flags().StringVarP( &pattern, - "pattern", - "p", + i18n.G("pattern"), + i18n.G("p"), "", - "filter by recipe", + i18n.G("filter by recipe"), ) } diff --git a/cli/recipe/new.go b/cli/recipe/new.go index a5380e39..3a7fe514 100644 --- a/cli/recipe/new.go +++ b/cli/recipe/new.go @@ -10,6 +10,7 @@ import ( "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/git" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/recipe" "github.com/spf13/cobra" @@ -30,10 +31,10 @@ type recipeMetadata struct { } var RecipeNewCommand = &cobra.Command{ - Use: "new [flags]", - Aliases: []string{"n"}, - Short: "Create a new recipe", - Long: `A community managed recipe template is used.`, + Use: i18n.G("new [flags]"), + Aliases: []string{i18n.G("n")}, + Short: i18n.G("Create a new recipe"), + Long: i18n.G(`A community managed recipe template is used.`), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -46,10 +47,10 @@ var RecipeNewCommand = &cobra.Command{ r := recipe.Get(recipeName) if _, err := os.Stat(r.Dir); !os.IsNotExist(err) { - log.Fatalf("%s recipe directory already exists?", r.Dir) + log.Fatal(i18n.G("%s recipe directory already exists?", r.Dir)) } - url := fmt.Sprintf("%s/example.git", config.REPOS_BASE_URL) + url := i18n.G("%s/example.git", config.REPOS_BASE_URL) if err := git.Clone(r.Dir, url); err != nil { log.Fatal(err) } @@ -58,7 +59,7 @@ var RecipeNewCommand = &cobra.Command{ if err := os.RemoveAll(gitRepo); err != nil { log.Fatal(err) } - log.Debugf("removed .git repo in %s", gitRepo) + log.Debug(i18n.G("removed .git repo in %s", gitRepo)) meta := newRecipeMeta(recipeName) @@ -82,8 +83,8 @@ var RecipeNewCommand = &cobra.Command{ log.Fatal(err) } - log.Infof("new recipe '%s' created: %s", recipeName, path.Join(r.Dir)) - log.Info("happy hacking 🎉") + log.Info(i18n.G("new recipe '%s' created: %s", recipeName, path.Join(r.Dir))) + log.Info(i18n.G("happy hacking 🎉")) }, } @@ -111,17 +112,17 @@ var ( func init() { RecipeNewCommand.Flags().StringVarP( &gitName, - "git-name", - "N", + i18n.G("git-name"), + i18n.G("N"), "", - "Git (user) name to do commits with", + i18n.G("Git (user) name to do commits with"), ) RecipeNewCommand.Flags().StringVarP( &gitEmail, - "git-email", - "e", + i18n.G("git-email"), + i18n.G("e"), "", - "Git email name to do commits with", + i18n.G("Git email name to do commits with"), ) } diff --git a/cli/recipe/recipe.go b/cli/recipe/recipe.go index 733ffec5..bcba8bbf 100644 --- a/cli/recipe/recipe.go +++ b/cli/recipe/recipe.go @@ -1,13 +1,16 @@ package recipe -import "github.com/spf13/cobra" +import ( + "coopcloud.tech/abra/pkg/i18n" + "github.com/spf13/cobra" +) // RecipeCommand defines all recipe related sub-commands. var RecipeCommand = &cobra.Command{ - Use: "recipe [cmd] [args] [flags]", - Aliases: []string{"r"}, - Short: "Manage recipes", - Long: `A recipe is a blueprint for an app. + Use: i18n.G("recipe [cmd] [args] [flags]"), + Aliases: []string{i18n.G("r")}, + Short: i18n.G("Manage recipes"), + Long: i18n.G(`A recipe is a blueprint for an app. It is a bunch of config files which describe how to deploy and maintain an app. Recipes are maintained by the Co-op Cloud community and you can use Abra to @@ -15,5 +18,5 @@ read them, deploy them and create apps for you. Anyone who uses a recipe can become a maintainer. Maintainers typically make sure the recipe is in good working order and the config upgraded in a timely -manner.`, +manner.`), } diff --git a/cli/recipe/release.go b/cli/recipe/release.go index d56b88c7..b4bda80a 100644 --- a/cli/recipe/release.go +++ b/cli/recipe/release.go @@ -12,6 +12,7 @@ import ( "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/formatter" gitPkg "coopcloud.tech/abra/pkg/git" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/recipe" "coopcloud.tech/tagcmp" @@ -22,10 +23,10 @@ import ( ) var RecipeReleaseCommand = &cobra.Command{ - Use: "release [version] [flags]", - Aliases: []string{"rl"}, - Short: "Release a new recipe version", - Long: `Create a new version of a recipe. + Use: i18n.G("release [version] [flags]"), + Aliases: []string{i18n.G("rl")}, + Short: i18n.G("Release a new recipe version"), + Long: i18n.G(`Create a new version of a recipe. These versions are then published on the Co-op Cloud recipe catalogue. These versions take the following form: @@ -44,7 +45,7 @@ major and therefore require intervention while doing the upgrade work. Publish your new release to git.coopcloud.tech with "--publish/-p". This requires that you have permission to git push to these repositories and have -your SSH keys configured on your account.`, +your SSH keys configured on your account.`), Args: cobra.RangeArgs(1, 2), ValidArgsFunction: func( cmd *cobra.Command, @@ -74,7 +75,7 @@ your SSH keys configured on your account.`, mainAppVersion := imagesTmp[mainApp] if mainAppVersion == "" { - log.Fatalf("main app service version for %s is empty?", recipe.Name) + log.Fatal(i18n.G("main app service version for %s is empty?", recipe.Name)) } var tagString string @@ -84,12 +85,12 @@ your SSH keys configured on your account.`, if tagString != "" { if _, err := tagcmp.Parse(tagString); err != nil { - log.Fatalf("cannot parse %s, invalid tag specified?", tagString) + log.Fatal(i18n.G("cannot parse %s, invalid tag specified?", tagString)) } } if (internal.Major || internal.Minor || internal.Patch) && tagString != "" { - log.Fatal("cannot specify tag and bump type at the same time") + log.Fatal(i18n.G("cannot specify tag and bump type at the same time")) } if tagString != "" { @@ -117,19 +118,19 @@ your SSH keys configured on your account.`, } if !isClean { - log.Infof("%s currently has these unstaged changes 👇", recipe.Name) + log.Info(i18n.G("%s currently has these unstaged changes 👇", recipe.Name)) if err := gitPkg.DiffUnstaged(recipe.Dir); err != nil { log.Fatal(err) } } if len(tags) > 0 { - log.Warnf("previous git tags detected, assuming this is a new semver release") + log.Warn(i18n.G("previous git tags detected, assuming this is a new semver release")) if err := createReleaseFromPreviousTag(tagString, mainAppVersion, recipe, tags); err != nil { log.Fatal(err) } } else { - log.Warnf("no tag specified and no previous tag available for %s, assuming this is the initial release", recipe.Name) + log.Warn(i18n.G("no tag specified and no previous tag available for %s, assuming this is the initial release", recipe.Name)) if err := createReleaseFromTag(recipe, tagString, mainAppVersion); err != nil { if cleanUpErr := cleanUpTag(recipe, tagString); err != nil { @@ -181,7 +182,7 @@ func getImageVersions(recipe recipe.Recipe) (map[string]string, error) { } if missingTag { - return services, fmt.Errorf("app service is missing image tag?") + return services, errors.New(i18n.G("app service is missing image tag?")) } return services, nil @@ -245,7 +246,7 @@ func btoi(b bool) int { // getTagCreateOptions constructs git tag create options func getTagCreateOptions(tag string) (git.CreateTagOptions, error) { - msg := fmt.Sprintf("chore: publish %s release", tag) + msg := i18n.G("chore: publish %s release", tag) return git.CreateTagOptions{Message: msg}, nil } @@ -273,13 +274,13 @@ func addReleaseNotes(recipe recipe.Recipe, tag string) error { if _, err := os.Stat(nextReleaseNotePath); err == nil { // release/next note exists. Move it to release/ if internal.Dry { - log.Debugf("dry run: move release note from 'next' to %s", tag) + log.Debug(i18n.G("dry run: move release note from 'next' to %s", tag)) return nil } if !internal.NoInput { prompt := &survey.Confirm{ - Message: "Use release note in release/next?", + Message: i18n.G("Use release note in release/next?"), } if err := survey.AskOne(prompt, &addNextAsReleaseNotes); err != nil { @@ -313,7 +314,7 @@ func addReleaseNotes(recipe recipe.Recipe, tag string) error { } prompt := &survey.Input{ - Message: "Release Note (leave empty for no release note)", + Message: i18n.G("Release Note (leave empty for no release note)"), } var releaseNote string @@ -338,7 +339,7 @@ func addReleaseNotes(recipe recipe.Recipe, tag string) error { func commitRelease(recipe recipe.Recipe, tag string) error { if internal.Dry { - log.Debugf("dry run: no changes committed") + log.Debug(i18n.G("dry run: no changes committed")) return nil } @@ -349,7 +350,7 @@ func commitRelease(recipe recipe.Recipe, tag string) error { if isClean { if !internal.Dry { - return fmt.Errorf("no changes discovered in %s, nothing to publish?", recipe.Dir) + return errors.New(i18n.G("no changes discovered in %s, nothing to publish?", recipe.Dir)) } } @@ -363,7 +364,7 @@ func commitRelease(recipe recipe.Recipe, tag string) error { func tagRelease(tagString string, repo *git.Repository) error { if internal.Dry { - log.Debugf("dry run: no git tag created (%s)", tagString) + log.Debug(i18n.G("dry run: no git tag created (%s)", tagString)) return nil } @@ -383,20 +384,20 @@ func tagRelease(tagString string, repo *git.Repository) error { } hash := formatter.SmallSHA(head.Hash().String()) - log.Debugf(fmt.Sprintf("created tag %s at %s", tagString, hash)) + log.Debug(i18n.G("created tag %s at %s", tagString, hash)) return nil } func pushRelease(recipe recipe.Recipe, tagString string) error { if internal.Dry { - log.Info("dry run: no changes published") + log.Info(i18n.G("dry run: no changes published")) return nil } if !publish && !internal.NoInput { prompt := &survey.Confirm{ - Message: "publish new release?", + Message: i18n.G("publish new release?"), } if err := survey.AskOne(prompt, &publish); err != nil { @@ -409,9 +410,9 @@ func pushRelease(recipe recipe.Recipe, tagString string) error { return err } url := fmt.Sprintf("%s/src/tag/%s", recipe.GitURL, tagString) - log.Infof("new release published: %s", url) + log.Info(i18n.G("new release published: %s", url)) } else { - log.Info("no -p/--publish passed, not publishing") + log.Info(i18n.G("no -p/--publish passed, not publishing")) } return nil @@ -426,7 +427,7 @@ func createReleaseFromPreviousTag(tagString, mainAppVersion string, recipe recip bumpType := btoi(internal.Major)*4 + btoi(internal.Minor)*2 + btoi(internal.Patch) if bumpType != 0 { if (bumpType & (bumpType - 1)) != 0 { - return fmt.Errorf("you can only use one of: --major, --minor, --patch") + return errors.New(i18n.G("you can only use one of: --major, --minor, --patch")) } } @@ -483,12 +484,12 @@ func createReleaseFromPreviousTag(tagString, mainAppVersion string, recipe recip } if lastGitTag.String() == tagString { - log.Fatalf("latest git tag (%s) and synced label (%s) are the same?", lastGitTag, tagString) + log.Fatal(i18n.G("latest git tag (%s) and synced label (%s) are the same?", lastGitTag, tagString)) } if !internal.NoInput { prompt := &survey.Confirm{ - Message: fmt.Sprintf("current: %s, new: %s, correct?", lastGitTag, tagString), + Message: i18n.G("current: %s, new: %s, correct?", lastGitTag, tagString), } var ok bool @@ -497,7 +498,7 @@ func createReleaseFromPreviousTag(tagString, mainAppVersion string, recipe recip } if !ok { - log.Fatal("exiting as requested") + log.Fatal(i18n.G("exiting as requested")) } } @@ -506,15 +507,15 @@ func createReleaseFromPreviousTag(tagString, mainAppVersion string, recipe recip } if err := commitRelease(recipe, tagString); err != nil { - log.Fatalf("failed to commit changes: %s", err.Error()) + log.Fatal(i18n.G("failed to commit changes: %s", err.Error())) } if err := tagRelease(tagString, repo); err != nil { - log.Fatalf("failed to tag release: %s", err.Error()) + log.Fatal(i18n.G("failed to tag release: %s", err.Error())) } if err := pushRelease(recipe, tagString); err != nil { - log.Fatalf("failed to publish new release: %s", err.Error()) + log.Fatal(i18n.G("failed to publish new release: %s", err.Error())) } return nil @@ -533,7 +534,7 @@ func cleanUpTag(recipe recipe.Recipe, tag string) error { } } - log.Debugf("removed freshly created tag %s", tag) + log.Debug(i18n.G("removed freshly created tag %s", tag)) return nil } @@ -545,20 +546,20 @@ func getLabelVersion(recipe recipe.Recipe, prompt bool) (string, error) { } if initTag == "" { - log.Fatalf("unable to read version for %s from synced label. Did you try running \"abra recipe sync %s\" already?", recipe.Name, recipe.Name) + log.Fatal(i18n.G("unable to read version for %s from synced label. Did you try running \"abra recipe sync %s\" already?", recipe.Name, recipe.Name)) } - log.Warnf("discovered %s as currently synced recipe label", initTag) + log.Warn(i18n.G("discovered %s as currently synced recipe label", initTag)) if prompt && !internal.NoInput { var response bool - prompt := &survey.Confirm{Message: fmt.Sprintf("use %s as the new version?", initTag)} + prompt := &survey.Confirm{Message: i18n.G("use %s as the new version?", initTag)} if err := survey.AskOne(prompt, &response); err != nil { return "", err } if !response { - return "", fmt.Errorf("please fix your synced label for %s and re-run this command", recipe.Name) + return "", errors.New(i18n.G("please fix your synced label for %s and re-run this command", recipe.Name)) } } @@ -572,42 +573,41 @@ var ( func init() { RecipeReleaseCommand.Flags().BoolVarP( &internal.Dry, - "dry-run", - "r", + i18n.G("dry-run"), + i18n.G("r"), false, - "report changes that would be made", + i18n.G("report changes that would be made"), ) RecipeReleaseCommand.Flags().BoolVarP( &internal.Major, - "major", - "x", + i18n.G("major"), + i18n.G("x"), false, - "increase the major part of the version", + i18n.G("increase the major part of the version"), ) RecipeReleaseCommand.Flags().BoolVarP( &internal.Minor, - "minor", - "y", + i18n.G("minor"), + i18n.G("y"), false, - "increase the minor part of the version", + i18n.G("increase the minor part of the version"), ) RecipeReleaseCommand.Flags().BoolVarP( &internal.Patch, - "patch", - "z", + i18n.G("patch"), + i18n.G("z"), false, - "increase the patch part of the version", + i18n.G("increase the patch part of the version"), ) RecipeReleaseCommand.Flags().BoolVarP( &publish, - "publish", - "p", + i18n.G("publish"), + i18n.G("p"), false, - "publish changes to git.coopcloud.tech", + i18n.G("publish changes to git.coopcloud.tech"), ) - } diff --git a/cli/recipe/reset.go b/cli/recipe/reset.go index 664fc467..0299513e 100644 --- a/cli/recipe/reset.go +++ b/cli/recipe/reset.go @@ -3,16 +3,17 @@ package recipe import ( "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/autocomplete" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/go-git/go-git/v5" "github.com/spf13/cobra" ) var RecipeResetCommand = &cobra.Command{ - Use: "reset [flags]", - Aliases: []string{"rs"}, - Short: "Remove all unstaged changes from recipe config", - Long: "WARNING: this will delete your changes. Be Careful.", + Use: i18n.G("reset [flags]"), + Aliases: []string{i18n.G("rs")}, + Short: i18n.G("Remove all unstaged changes from recipe config"), + Long: i18n.G("WARNING: this will delete your changes. Be Careful."), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, diff --git a/cli/recipe/sync.go b/cli/recipe/sync.go index 73bc55bf..cfa46436 100644 --- a/cli/recipe/sync.go +++ b/cli/recipe/sync.go @@ -7,6 +7,7 @@ import ( "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/autocomplete" gitPkg "coopcloud.tech/abra/pkg/git" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/tagcmp" "github.com/AlecAivazis/survey/v2" @@ -16,10 +17,10 @@ import ( ) var RecipeSyncCommand = &cobra.Command{ - Use: "sync [version] [flags]", - Aliases: []string{"s"}, - Short: "Sync recipe version label", - Long: `Generate labels for the main recipe service. + Use: i18n.G("sync [version] [flags]"), + Aliases: []string{i18n.G("s")}, + Short: i18n.G("Sync recipe version label"), + Long: i18n.G(`Generate labels for the main recipe service. By convention, the service named "app" using the following format: @@ -27,7 +28,7 @@ By convention, the service named "app" using the following format: Where [version] can be specifed on the command-line or Abra can attempt to auto-generate it for you. The configuration will be updated on the -local file system.`, +local file system.`), Args: cobra.RangeArgs(1, 2), ValidArgsFunction: func( cmd *cobra.Command, @@ -68,11 +69,11 @@ local file system.`, } if len(tags) == 0 && nextTag == "" { - log.Warnf("no git tags found for %s", recipe.Name) + log.Warn(i18n.G("no git tags found for %s", recipe.Name)) if internal.NoInput { - log.Fatalf("unable to continue, input required for initial version") + log.Fatal(i18n.G("unable to continue, input required for initial version")) } - fmt.Println(fmt.Sprintf(` + fmt.Println(i18n.G(` The following options are two types of initial semantic version that you can pick for %s that will be published in the recipe catalogue. This follows the semver convention (more on https://semver.org), here is a short cheatsheet @@ -92,7 +93,7 @@ likely to change. `, recipe.Name, recipe.Name)) var chosenVersion string edPrompt := &survey.Select{ - Message: "which version do you want to begin with?", + Message: i18n.G("which version do you want to begin with?"), Options: []string{"0.1.0", "1.0.0"}, } @@ -125,7 +126,7 @@ likely to change. if err := iter.ForEach(func(ref *plumbing.Reference) error { obj, err := repo.TagObject(ref.Hash()) if err != nil { - log.Fatal("Tag at commit ", ref.Hash(), " is unannotated or otherwise broken. Please fix it.") + log.Fatal(i18n.G("tag at commit %s is unannotated or otherwise broken", ref.Hash())) return err } @@ -150,7 +151,7 @@ likely to change. if bumpType != 0 { // a bitwise check if the number is a power of 2 if (bumpType & (bumpType - 1)) != 0 { - log.Fatal("you can only use one version flag: --major, --minor or --patch") + log.Fatal(i18n.G("you can only use one version flag: --major, --minor or --patch")) } } @@ -184,22 +185,22 @@ likely to change. } newTag.Metadata = mainAppVersion - log.Debugf("choosing %s as new version for %s", newTag.String(), recipe.Name) + log.Debug(i18n.G("choosing %s as new version for %s", newTag.String(), recipe.Name)) nextTag = newTag.String() } if _, err := tagcmp.Parse(nextTag); err != nil { - log.Fatalf("invalid version %s specified", nextTag) + log.Fatal(i18n.G("invalid version %s specified", nextTag)) } mainService := "app" - label := fmt.Sprintf("coop-cloud.${STACK_NAME}.version=%s", nextTag) + label := i18n.G("coop-cloud.${STACK_NAME}.version=%s", nextTag) if !internal.Dry { if err := recipe.UpdateLabel("compose.y*ml", mainService, label); err != nil { log.Fatal(err) } } else { - log.Infof("dry run: not syncing label %s for recipe %s", nextTag, recipe.Name) + log.Info(i18n.G("dry run: not syncing label %s for recipe %s", nextTag, recipe.Name)) } isClean, err := gitPkg.IsClean(recipe.Dir) @@ -207,7 +208,7 @@ likely to change. log.Fatal(err) } if !isClean { - log.Infof("%s currently has these unstaged changes 👇", recipe.Name) + log.Info(i18n.G("%s currently has these unstaged changes 👇", recipe.Name)) if err := gitPkg.DiffUnstaged(recipe.Dir); err != nil { log.Fatal(err) } @@ -218,33 +219,33 @@ likely to change. func init() { RecipeSyncCommand.Flags().BoolVarP( &internal.Dry, - "dry-run", - "r", + i18n.G("dry-run"), + i18n.G("r"), false, - "report changes that would be made", + i18n.G("report changes that would be made"), ) RecipeSyncCommand.Flags().BoolVarP( &internal.Major, - "major", - "x", + i18n.G("major"), + i18n.G("x"), false, - "increase the major part of the version", + i18n.G("increase the major part of the version"), ) RecipeSyncCommand.Flags().BoolVarP( &internal.Minor, - "minor", - "y", + i18n.G("minor"), + i18n.G("y"), false, - "increase the minor part of the version", + i18n.G("increase the minor part of the version"), ) RecipeSyncCommand.Flags().BoolVarP( &internal.Patch, - "patch", - "z", + i18n.G("patch"), + i18n.G("z"), false, - "increase the patch part of the version", + i18n.G("increase the patch part of the version"), ) } diff --git a/cli/recipe/upgrade.go b/cli/recipe/upgrade.go index 9eb262c8..834cc9d5 100644 --- a/cli/recipe/upgrade.go +++ b/cli/recipe/upgrade.go @@ -14,6 +14,7 @@ import ( "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/formatter" gitPkg "coopcloud.tech/abra/pkg/git" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" recipePkg "coopcloud.tech/abra/pkg/recipe" "coopcloud.tech/tagcmp" @@ -37,10 +38,10 @@ type anUpgrade struct { } var RecipeUpgradeCommand = &cobra.Command{ - Use: "upgrade [flags]", - Aliases: []string{"u"}, - Short: "Upgrade recipe image tags", - Long: `Upgrade a given configuration. + Use: i18n.G("upgrade [flags]"), + Aliases: []string{i18n.G("u")}, + Short: i18n.G("Upgrade recipe image tags"), + Long: i18n.G(`Upgrade a given configuration. It will update the relevant compose file tags on the local file system. @@ -52,7 +53,7 @@ The command is interactive and will show a select input which allows you to make a seclection. Use the "?" key to see more help on navigating this interface. -You may invoke this command in "wizard" mode and be prompted for input.`, +You may invoke this command in "wizard" mode and be prompted for input.`), Args: cobra.RangeArgs(0, 1), ValidArgsFunction: func( cmd *cobra.Command, @@ -71,7 +72,7 @@ You may invoke this command in "wizard" mode and be prompted for input.`, if bumpType != 0 { // a bitwise check if the number is a power of 2 if (bumpType & (bumpType - 1)) != 0 { - log.Fatal("you can only use one of: --major, --minor, --patch.") + log.Fatal(i18n.G("you can only use one of: --major, --minor, --patch.")) } } @@ -87,7 +88,7 @@ You may invoke this command in "wizard" mode and be prompted for input.`, versionsPath := path.Join(recipe.Dir, "versions") servicePins := make(map[string]imgPin) if _, err := os.Stat(versionsPath); err == nil { - log.Debugf("found versions file for %s", recipe.Name) + log.Debug(i18n.G("found versions file for %s", recipe.Name)) file, err := os.Open(versionsPath) if err != nil { log.Fatal(err) @@ -97,7 +98,7 @@ You may invoke this command in "wizard" mode and be prompted for input.`, line := scanner.Text() splitLine := strings.Split(line, " ") if splitLine[0] != "pin" || len(splitLine) != 3 { - log.Fatalf("malformed version pin specification: %s", line) + log.Fatal(i18n.G("malformed version pin specification: %s", line)) } pinSlice := strings.Split(splitLine[2], ":") pinTag, err := tagcmp.Parse(pinSlice[1]) @@ -115,7 +116,7 @@ You may invoke this command in "wizard" mode and be prompted for input.`, } versionsPresent = true } else { - log.Debugf("did not find versions file for %s", recipe.Name) + log.Debug(i18n.G("did not find versions file for %s", recipe.Name)) } config, err := recipe.GetComposeConfig(nil) @@ -135,26 +136,26 @@ You may invoke this command in "wizard" mode and be prompted for input.`, } image := reference.Path(img) - log.Debugf("retrieved %s from remote registry for %s", regVersions, image) + log.Debug(i18n.G("retrieved %s from remote registry for %s", regVersions, image)) image = formatter.StripTagMeta(image) switch img.(type) { case reference.NamedTagged: if !tagcmp.IsParsable(img.(reference.NamedTagged).Tag()) { - log.Debugf("%s not considered semver-like", img.(reference.NamedTagged).Tag()) + log.Debug(i18n.G("%s not considered semver-like", img.(reference.NamedTagged).Tag())) } default: - log.Warnf("unable to read tag for image %s, is it missing? skipping upgrade for %s", image, service.Name) + log.Warn(i18n.G("unable to read tag for image %s, is it missing? skipping upgrade for %s", image, service.Name)) continue } tag, err := tagcmp.Parse(img.(reference.NamedTagged).Tag()) if err != nil { - log.Warnf("unable to parse %s, error was: %s, skipping upgrade for %s", image, err.Error(), service.Name) + log.Warn(i18n.G("unable to parse %s, error was: %s, skipping upgrade for %s", image, err.Error(), service.Name)) continue } - log.Debugf("parsed %s for %s", tag, service.Name) + log.Debug(i18n.G("parsed %s for %s", tag, service.Name)) var compatible []tagcmp.Tag for _, regVersion := range regVersions { @@ -168,12 +169,12 @@ You may invoke this command in "wizard" mode and be prompted for input.`, } } - log.Debugf("detected potential upgradable tags %s for %s", compatible, service.Name) + log.Debug(i18n.G("detected potential upgradable tags %s for %s", compatible, service.Name)) sort.Sort(tagcmp.ByTagDesc(compatible)) if len(compatible) == 0 && !allTags { - log.Info(fmt.Sprintf("no new versions available for %s, assuming %s is the latest (use -a/--all-tags to see all anyway)", image, tag)) + log.Info(i18n.G("no new versions available for %s, assuming %s is the latest (use -a/--all-tags to see all anyway)", image, tag)) continue // skip on to the next tag and don't update any compose files } @@ -195,7 +196,7 @@ You may invoke this command in "wizard" mode and be prompted for input.`, } } - log.Debugf("detected compatible upgradable tags %s for %s", compatibleStrings, service.Name) + log.Debug(i18n.G("detected compatible upgradable tags %s for %s", compatibleStrings, service.Name)) var upgradeTag string _, ok := servicePins[service.Name] @@ -212,13 +213,13 @@ You may invoke this command in "wizard" mode and be prompted for input.`, } } if contains { - log.Infof("upgrading service %s from %s to %s (pinned tag: %s)", service.Name, tag.String(), upgradeTag, pinnedTagString) + log.Info(i18n.G("upgrading service %s from %s to %s (pinned tag: %s)", service.Name, tag.String(), upgradeTag, pinnedTagString)) } else { - log.Infof("service %s, image %s pinned to %s, no compatible upgrade found", service.Name, servicePins[service.Name].image, pinnedTagString) + log.Info(i18n.G("service %s, image %s pinned to %s, no compatible upgrade found", service.Name, servicePins[service.Name].image, pinnedTagString)) continue } } else { - log.Fatalf("service %s is at version %s, but pinned to %s, please correct your compose.yml file manually!", service.Name, tag.String(), pinnedTag.String()) + log.Fatal(i18n.G("service %s is at version %s, but pinned to %s, please correct your compose.yml file manually!", service.Name, tag.String(), pinnedTag.String())) continue } } else { @@ -235,17 +236,17 @@ You may invoke this command in "wizard" mode and be prompted for input.`, } } if upgradeTag == "" { - log.Warnf("not upgrading from %s to %s for %s, because the upgrade type is more serious than what user wants", tag.String(), compatible[0].String(), image) + log.Warn(i18n.G("not upgrading from %s to %s for %s, because the upgrade type is more serious than what user wants", tag.String(), compatible[0].String(), image)) continue } } else { - msg := fmt.Sprintf("upgrade to which tag? (service: %s, image: %s, tag: %s)", service.Name, image, tag) + msg := i18n.G("upgrade to which tag? (service: %s, image: %s, tag: %s)", service.Name, image, tag) if !tagcmp.IsParsable(img.(reference.NamedTagged).Tag()) || allTags { tag := img.(reference.NamedTagged).Tag() if !allTags { - log.Warn(fmt.Sprintf("unable to determine versioning semantics of %s, listing all tags", tag)) + log.Warn(i18n.G("unable to determine versioning semantics of %s, listing all tags", tag)) } - msg = fmt.Sprintf("upgrade to which tag? (service: %s, tag: %s)", service.Name, tag) + msg = i18n.G("upgrade to which tag? (service: %s, tag: %s)", service.Name, tag) compatibleStrings = []string{"skip"} for _, regVersion := range regVersions { compatibleStrings = append(compatibleStrings, regVersion) @@ -276,7 +277,7 @@ You may invoke this command in "wizard" mode and be prompted for input.`, } else { prompt := &survey.Select{ Message: msg, - Help: "enter / return to confirm, choose 'skip' to not upgrade this tag, vim mode is enabled", + Help: i18n.G("enter / return to confirm, choose 'skip' to not upgrade this tag, vim mode is enabled"), VimMode: true, Options: compatibleStrings, } @@ -292,11 +293,11 @@ You may invoke this command in "wizard" mode and be prompted for input.`, log.Fatal(err) } if ok { - log.Infof("tag upgraded from %s to %s for %s", tag.String(), upgradeTag, image) + log.Info(i18n.G("tag upgraded from %s to %s for %s", tag.String(), upgradeTag, image)) } } else { if !internal.NoInput { - log.Warnf("not upgrading %s, skipping as requested", image) + log.Warn(i18n.G("not upgrading %s, skipping as requested", image)) } } } @@ -314,7 +315,7 @@ You may invoke this command in "wizard" mode and be prompted for input.`, } for _, upgrade := range upgradeList { - log.Infof("can upgrade service: %s, image: %s, tag: %s ::", upgrade.Service, upgrade.Image, upgrade.Tag) + log.Info(i18n.G("can upgrade service: %s, image: %s, tag: %s ::", upgrade.Service, upgrade.Image, upgrade.Tag)) for _, utag := range upgrade.UpgradeTags { log.Infof(" %s", utag) } @@ -326,7 +327,7 @@ You may invoke this command in "wizard" mode and be prompted for input.`, log.Fatal(err) } if !isClean { - log.Infof("%s currently has these unstaged changes 👇", recipe.Name) + log.Info(i18n.G("%s currently has these unstaged changes 👇", recipe.Name)) if err := gitPkg.DiffUnstaged(recipe.Dir); err != nil { log.Fatal(err) } @@ -341,41 +342,41 @@ var ( func init() { RecipeUpgradeCommand.Flags().BoolVarP( &internal.Major, - "major", - "x", + i18n.G("major"), + i18n.G("x"), false, - "increase the major part of the version", + i18n.G("increase the major part of the version"), ) RecipeUpgradeCommand.Flags().BoolVarP( &internal.Minor, - "minor", - "y", + i18n.G("minor"), + i18n.G("y"), false, - "increase the minor part of the version", + i18n.G("increase the minor part of the version"), ) RecipeUpgradeCommand.Flags().BoolVarP( &internal.Patch, - "patch", - "z", + i18n.G("patch"), + i18n.G("z"), false, - "increase the patch part of the version", + i18n.G("increase the patch part of the version"), ) RecipeUpgradeCommand.Flags().BoolVarP( &internal.MachineReadable, - "machine", - "m", + i18n.G("machine"), + i18n.G("m"), false, - "print machine-readable output", + i18n.G("print machine-readable output"), ) RecipeUpgradeCommand.Flags().BoolVarP( &allTags, - "all-tags", - "a", + i18n.G("all-tags"), + i18n.G("a"), false, - "list all tags, not just upgrades", + i18n.G("list all tags, not just upgrades"), ) } diff --git a/cli/recipe/version.go b/cli/recipe/version.go index 0ae32bd8..62db7a59 100644 --- a/cli/recipe/version.go +++ b/cli/recipe/version.go @@ -7,15 +7,16 @@ import ( "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" recipePkg "coopcloud.tech/abra/pkg/recipe" "github.com/spf13/cobra" ) var RecipeVersionCommand = &cobra.Command{ - Use: "versions [flags]", - Aliases: []string{"v"}, - Short: "List recipe versions", + Use: i18n.G("versions [flags]"), + Aliases: []string{i18n.G("v")}, + Short: i18n.G("List recipe versions"), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -35,7 +36,7 @@ var RecipeVersionCommand = &cobra.Command{ recipeMeta, ok := catl[recipe.Name] if !ok { - warnMessages = append(warnMessages, "retrieved versions from local recipe repository") + warnMessages = append(warnMessages, i18n.G("retrieved versions from local recipe repository")) recipeVersions, warnMsg, err := recipe.GetRecipeVersions() if err != nil { @@ -49,7 +50,7 @@ var RecipeVersionCommand = &cobra.Command{ } if len(recipeMeta.Versions) == 0 { - log.Fatalf("%s has no published versions?", recipe.Name) + log.Fatal(i18n.G("%s has no published versions?", recipe.Name)) } for i := len(recipeMeta.Versions) - 1; i >= 0; i-- { @@ -58,7 +59,7 @@ var RecipeVersionCommand = &cobra.Command{ log.Fatal(err) } - table.Headers("SERVICE", "IMAGE", "TAG", "VERSION") + table.Headers(i18n.G("SERVICE"), i18n.G("IMAGE"), i18n.G("TAG"), i18n.G("VERSION")) for version, meta := range recipeMeta.Versions[i] { var allRows [][]string @@ -99,10 +100,10 @@ var RecipeVersionCommand = &cobra.Command{ if internal.MachineReadable { sort.Slice(allRows, sortServiceByName(allRows)) - headers := []string{"VERSION", "SERVICE", "NAME", "TAG"} + headers := []string{i18n.G("VERSION"), i18n.G("SERVICE"), i18n.G("NAME"), i18n.G("TAG")} out, err := formatter.ToJSON(headers, allRows) if err != nil { - log.Fatal("unable to render to JSON: %s", err) + log.Fatal(i18n.G("unable to render to JSON: %s", err)) } fmt.Println(out) continue @@ -127,9 +128,9 @@ func sortServiceByName(versions [][]string) func(i, j int) bool { func init() { RecipeVersionCommand.Flags().BoolVarP( &internal.MachineReadable, - "machine", - "m", + i18n.G("machine"), + i18n.G("m"), false, - "print machine-readable output", + i18n.G("print machine-readable output"), ) } diff --git a/cli/run.go b/cli/run.go index 13dd8aad..72d98088 100644 --- a/cli/run.go +++ b/cli/run.go @@ -1,6 +1,7 @@ package cli import ( + "errors" "fmt" "os" @@ -10,6 +11,8 @@ import ( "coopcloud.tech/abra/cli/recipe" "coopcloud.tech/abra/cli/server" "coopcloud.tech/abra/pkg/config" + "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" charmLog "github.com/charmbracelet/log" "github.com/spf13/cobra" @@ -18,19 +21,20 @@ import ( func Run(version, commit string) { rootCmd := &cobra.Command{ - Use: "abra [cmd] [args] [flags]", - Short: "The Co-op Cloud command-line utility belt 🎩🐇", + Use: i18n.G("abra [cmd] [args] [flags]"), + Short: i18n.G("The Co-op Cloud command-line utility belt 🎩🐇"), Version: fmt.Sprintf("%s-%s", version, commit[:7]), ValidArgs: []string{ - "app", - "autocomplete", - "catalogue", - "man", - "recipe", - "server", - "upgrade", + i18n.G("app"), + i18n.G("autocomplete"), + i18n.G("catalogue"), + i18n.G("man"), + i18n.G("recipe"), + i18n.G("server"), + i18n.G("upgrade"), }, - PersistentPreRun: func(cmd *cobra.Command, args []string) { + + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { dirs := []map[string]os.FileMode{ {config.ABRA_DIR: 0764}, {config.SERVERS_DIR: 0700}, @@ -42,7 +46,7 @@ func Run(version, commit string) { for path, perm := range dir { if err := os.Mkdir(path, perm); err != nil { if !os.IsExist(err) { - log.Fatal(err) + return errors.New(i18n.G("unable to create %s: %s", path, err)) } continue } @@ -62,24 +66,29 @@ func Run(version, commit string) { log.SetReportCaller(true) } - log.Debugf("abra version %s, commit %s", version, commit) + log.Debug(i18n.G( + "abra version: %s, commit: %s, lang: %s", + version, formatter.SmallSHA(commit), i18n.Locale, + )) + + return nil }, } rootCmd.CompletionOptions.DisableDefaultCmd = true manCommand := &cobra.Command{ - Use: "man [flags]", + Use: i18n.G("man [flags]"), Aliases: []string{"m"}, - Short: "Generate manpage", - Example: ` # generate the man pages into /usr/local/share/man/man1 + Short: i18n.G("Generate manpage"), + Example: i18n.G(` # generate the man pages into /usr/local/share/man/man1 abra_path=$(which abra) # pass abra absolute path to sudo below sudo $abra_path man sudo mandb # read the man pages man abra - man abra-app-deploy`, + man abra-app-deploy`), Run: func(cmd *cobra.Command, args []string) { header := &doc.GenManHeader{ Title: "ABRA", @@ -88,7 +97,7 @@ func Run(version, commit string) { manDir := "/usr/local/share/man/man1" if _, err := os.Stat(manDir); os.IsNotExist(err) { - log.Fatalf("unable to proceed, '%s' does not exist?") + log.Fatal(i18n.G("unable to proceed, %s does not exist?", manDir)) } err := doc.GenManTree(rootCmd, header, manDir) @@ -96,7 +105,7 @@ func Run(version, commit string) { log.Fatal(err) } - log.Info("don't forget to run 'sudo mandb'") + log.Info(i18n.G("don't forget to run 'sudo mandb'")) }, } @@ -105,7 +114,7 @@ func Run(version, commit string) { "debug", "d", false, - "show debug messages", + i18n.G("show debug messages"), ) rootCmd.PersistentFlags().BoolVarP( @@ -113,7 +122,7 @@ func Run(version, commit string) { "no-input", "n", false, - "toggle non-interactive mode", + i18n.G("toggle non-interactive mode"), ) rootCmd.PersistentFlags().BoolVarP( @@ -121,7 +130,7 @@ func Run(version, commit string) { "offline", "o", false, - "prefer offline & filesystem access", + i18n.G("prefer offline & filesystem access"), ) rootCmd.PersistentFlags().BoolVarP( @@ -129,7 +138,7 @@ func Run(version, commit string) { "ignore-env-version", "i", false, - "ignore .env version checkout", + i18n.G("ignore .env version checkout"), ) catalogue.CatalogueCommand.AddCommand( diff --git a/cli/server/add.go b/cli/server/add.go index 12ca137f..a366a010 100644 --- a/cli/server/add.go +++ b/cli/server/add.go @@ -10,6 +10,7 @@ import ( "coopcloud.tech/abra/pkg/config" contextPkg "coopcloud.tech/abra/pkg/context" "coopcloud.tech/abra/pkg/dns" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/server" sshPkg "coopcloud.tech/abra/pkg/ssh" @@ -17,10 +18,10 @@ import ( ) var ServerAddCommand = &cobra.Command{ - Use: "add [[server] | --local] [flags]", - Aliases: []string{"a"}, - Short: "Add a new server", - Long: `Add a new server to your configuration so that it can be managed by Abra. + Use: i18n.G("add [[server] | --local] [flags]"), + Aliases: []string{i18n.G("a")}, + Short: i18n.G("Add a new server"), + Long: i18n.G(`Add a new server to your configuration so that it can be managed by Abra. Abra relies on the standard SSH command-line and ~/.ssh/config for client connection details. You must configure an entry per-host in your ~/.ssh/config @@ -35,8 +36,8 @@ for each server: If "--local" is passed, then Abra assumes that the current local server is intended as the target server. This is useful when you want to have your entire Co-op Cloud config located on the server itself, and not on your local -developer machine. The domain is then set to "default".`, - Example: " abra server add 1312.net", +developer machine. The domain is then set to "default".`), + Example: i18n.G(" abra server add 1312.net"), Args: cobra.RangeArgs(0, 1), ValidArgsFunction: func( cmd *cobra.Command, @@ -49,11 +50,11 @@ developer machine. The domain is then set to "default".`, }, Run: func(cmd *cobra.Command, args []string) { if len(args) > 0 && local { - log.Fatal("cannot use [server] and --local together") + log.Fatal(i18n.G("cannot use [server] and --local together")) } if len(args) == 0 && !local { - log.Fatal("missing argument or --local/-l flag") + log.Fatal(i18n.G("missing argument or --local/-l flag")) } name := "default" @@ -72,7 +73,7 @@ developer machine. The domain is then set to "default".`, log.Fatal(err) } - log.Debugf("attempting to create client for %s", name) + log.Debug(i18n.G("attempting to create client for %s", name)) if _, err := client.New(name, timeout); err != nil { cleanUp(name) @@ -80,9 +81,9 @@ developer machine. The domain is then set to "default".`, } if created { - log.Info("local server successfully added") + log.Info(i18n.G("local server successfully added")) } else { - log.Warn("local server already exists") + log.Warn(i18n.G("local server already exists")) } return @@ -96,27 +97,27 @@ developer machine. The domain is then set to "default".`, created, err := newContext(name) if err != nil { cleanUp(name) - log.Fatalf("unable to create local context: %s", err) + log.Fatal(i18n.G("unable to create local context: %s", err)) } - log.Debugf("attempting to create client for %s", name) + log.Debug(i18n.G("attempting to create client for %s", name)) if _, err := client.New(name, timeout); err != nil { cleanUp(name) - log.Fatalf("ssh %s error: %s", name, sshPkg.Fatal(name, err)) + log.Fatal(i18n.G("ssh %s error: %s", name, sshPkg.Fatal(name, err))) } if created { - log.Infof("%s successfully added", name) + log.Info(i18n.G("%s successfully added", name)) if _, err := dns.EnsureIPv4(name); err != nil { - log.Warnf("unable to resolve IPv4 for %s", name) + log.Warn(i18n.G("unable to resolve IPv4 for %s", name)) } return } - log.Warnf("%s already exists", name) + log.Warn(i18n.G("%s already exists", name)) }, } @@ -124,7 +125,7 @@ developer machine. The domain is then set to "default".`, // "server add" attempt. func cleanUp(name string) { if name != "default" { - log.Debugf("serverAdd: cleanUp: cleaning up context for %s", name) + log.Debug(i18n.G("serverAdd: cleanUp: cleaning up context for %s", name)) if err := client.DeleteContext(name); err != nil { log.Fatal(err) } @@ -133,16 +134,16 @@ func cleanUp(name string) { serverDir := filepath.Join(config.SERVERS_DIR, name) files, err := config.GetAllFilesInDirectory(serverDir) if err != nil { - log.Fatalf("serverAdd: cleanUp: unable to list files in %s: %s", serverDir, err) + log.Fatal(i18n.G("serverAdd: cleanUp: unable to list files in %s: %s", serverDir, err)) } if len(files) > 0 { - log.Debugf("serverAdd: cleanUp: %s is not empty, aborting cleanup", serverDir) + log.Debug(i18n.G("serverAdd: cleanUp: %s is not empty, aborting cleanup", serverDir)) return } if err := os.RemoveAll(serverDir); err != nil { - log.Fatalf("serverAdd: cleanUp: failed to remove %s: %s", serverDir, err) + log.Fatal(i18n.G("serverAdd: cleanUp: failed to remove %s: %s", serverDir, err)) } } @@ -159,12 +160,12 @@ func newContext(name string) (bool, error) { for _, context := range contexts { if context.Name == name { - log.Debugf("context for %s already exists", name) + log.Debug(i18n.G("context for %s already exists", name)) return false, nil } } - log.Debugf("creating context with domain %s", name) + log.Debugf(i18n.G("creating context with domain %s", name)) if err := client.CreateContext(name); err != nil { return false, nil @@ -180,7 +181,7 @@ func createServerDir(name string) (bool, error) { return false, err } - log.Debugf("server dir for %s already created", name) + log.Debug(i18n.G("server dir for %s already created", name)) return false, nil } @@ -195,9 +196,9 @@ var ( func init() { ServerAddCommand.Flags().BoolVarP( &local, - "local", - "l", + i18n.G("local"), + i18n.G("l"), false, - "use local server", + i18n.G("use local server"), ) } diff --git a/cli/server/list.go b/cli/server/list.go index dd3fd211..b7df081f 100644 --- a/cli/server/list.go +++ b/cli/server/list.go @@ -8,15 +8,16 @@ import ( "coopcloud.tech/abra/pkg/config" contextPkg "coopcloud.tech/abra/pkg/context" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/docker/cli/cli/connhelper/ssh" "github.com/spf13/cobra" ) var ServerListCommand = &cobra.Command{ - Use: "list [flags]", - Aliases: []string{"ls"}, - Short: "List managed servers", + Use: i18n.G("list [flags]"), + Aliases: []string{i18n.G("ls")}, + Short: i18n.G("List managed servers"), Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { dockerContextStore := contextPkg.NewDefaultDockerContextStore() @@ -30,7 +31,7 @@ var ServerListCommand = &cobra.Command{ log.Fatal(err) } - headers := []string{"NAME", "HOST"} + headers := []string{i18n.G("NAME"), i18n.G("HOST")} table.Headers(headers...) serverNames, err := config.ReadServerNames() @@ -55,7 +56,7 @@ var ServerListCommand = &cobra.Command{ } if sp.Host == "" { - sp.Host = "unknown" + sp.Host = i18n.G("unknown") } row = []string{serverName, sp.Host} @@ -65,9 +66,9 @@ var ServerListCommand = &cobra.Command{ if len(row) == 0 { if serverName == "default" { - row = []string{serverName, "local"} + row = []string{serverName, i18n.G("local")} } else { - row = []string{serverName, "unknown"} + row = []string{serverName, i18n.G("unknown")} } rows = append(rows, row) } @@ -78,7 +79,7 @@ var ServerListCommand = &cobra.Command{ if internal.MachineReadable { out, err := formatter.ToJSON(headers, rows) if err != nil { - log.Fatal("unable to render to JSON: %s", err) + log.Fatal(i18n.G("unable to render to JSON: %s", err)) } fmt.Println(out) @@ -95,9 +96,9 @@ var ServerListCommand = &cobra.Command{ func init() { ServerListCommand.Flags().BoolVarP( &internal.MachineReadable, - "machine", - "m", + i18n.G("machine"), + i18n.G("m"), false, - "print machine-readable output", + i18n.G("print machine-readable output"), ) } diff --git a/cli/server/prune.go b/cli/server/prune.go index 7a4e5c25..1a097714 100644 --- a/cli/server/prune.go +++ b/cli/server/prune.go @@ -5,19 +5,20 @@ import ( "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/docker/docker/api/types/filters" "github.com/spf13/cobra" ) var ServerPruneCommand = &cobra.Command{ - Use: "prune [flags]", - Aliases: []string{"p"}, - Short: "Prune resources on a server", - Long: `Prunes unused containers, networks, and dangling images. + Use: i18n.G("prune [flags]"), + Aliases: []string{i18n.G("p")}, + Short: i18n.G("Prune resources on a server"), + Long: i18n.G(`Prunes unused containers, networks, and dangling images. Use "--volumes/-v" to remove volumes that are not associated with a deployed -app. This can result in unwanted data loss if not used carefully.`, +app. This can result in unwanted data loss if not used carefully.`), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -41,18 +42,18 @@ app. This can result in unwanted data loss if not used carefully.`, } cntSpaceReclaimed := formatter.ByteCountSI(cr.SpaceReclaimed) - log.Infof("containers pruned: %d; space reclaimed: %s", len(cr.ContainersDeleted), cntSpaceReclaimed) + log.Info(i18n.G("containers pruned: %d; space reclaimed: %s", len(cr.ContainersDeleted), cntSpaceReclaimed)) nr, err := cl.NetworksPrune(cmd.Context(), filterArgs) if err != nil { log.Fatal(err) } - log.Infof("networks pruned: %d", len(nr.NetworksDeleted)) + log.Info(i18n.G("networks pruned: %d", len(nr.NetworksDeleted))) pruneFilters := filters.NewArgs() if allFilter { - log.Debugf("removing all images, not only dangling ones") + log.Debug(i18n.G("removing all images, not only dangling ones")) pruneFilters.Add("dangling", "false") } @@ -62,7 +63,7 @@ app. This can result in unwanted data loss if not used carefully.`, } imgSpaceReclaimed := formatter.ByteCountSI(ir.SpaceReclaimed) - log.Infof("images pruned: %d; space reclaimed: %s", len(ir.ImagesDeleted), imgSpaceReclaimed) + log.Info(i18n.G("images pruned: %d; space reclaimed: %s", len(ir.ImagesDeleted), imgSpaceReclaimed)) if volumesFilter { vr, err := cl.VolumesPrune(cmd.Context(), filterArgs) @@ -71,7 +72,7 @@ app. This can result in unwanted data loss if not used carefully.`, } volSpaceReclaimed := formatter.ByteCountSI(vr.SpaceReclaimed) - log.Infof("volumes pruned: %d; space reclaimed: %s", len(vr.VolumesDeleted), volSpaceReclaimed) + log.Info(i18n.G("volumes pruned: %d; space reclaimed: %s", len(vr.VolumesDeleted), volSpaceReclaimed)) } return @@ -86,17 +87,17 @@ var ( func init() { ServerPruneCommand.Flags().BoolVarP( &allFilter, - "all", - "a", + i18n.G("all"), + i18n.G("a"), false, - "remove all unused images", + i18n.G("remove all unused images"), ) ServerPruneCommand.Flags().BoolVarP( &volumesFilter, - "volumes", - "v", + i18n.G("volumes"), + i18n.G("v"), false, - "remove volumes", + i18n.G("remove volumes"), ) } diff --git a/cli/server/remove.go b/cli/server/remove.go index 4e79643f..db49a980 100644 --- a/cli/server/remove.go +++ b/cli/server/remove.go @@ -8,19 +8,20 @@ import ( "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/config" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/spf13/cobra" ) var ServerRemoveCommand = &cobra.Command{ - Use: "remove [flags]", - Aliases: []string{"rm"}, - Short: "Remove a managed server", - Long: `Remove a managed server. + Use: i18n.G("remove [flags]"), + Aliases: []string{i18n.G("rm")}, + Short: i18n.G("Remove a managed server"), + Long: i18n.G(`Remove a managed server. Abra will remove the internal bookkeeping ($ABRA_DIR/servers/...) and underlying client connection context. This server will then be lost in time, -like tears in rain.`, +like tears in rain.`), Args: cobra.ExactArgs(1), ValidArgsFunction: func( cmd *cobra.Command, @@ -39,7 +40,7 @@ like tears in rain.`, log.Fatal(err) } - log.Infof("%s is now lost in time, like tears in rain", serverName) + log.Info(i18n.G("%s is now lost in time, like tears in rain", serverName)) return }, diff --git a/cli/server/server.go b/cli/server/server.go index a9876aba..868a439d 100644 --- a/cli/server/server.go +++ b/cli/server/server.go @@ -1,10 +1,13 @@ package server -import "github.com/spf13/cobra" +import ( + "coopcloud.tech/abra/pkg/i18n" + "github.com/spf13/cobra" +) // ServerCommand defines the `abra server` command and its subcommands var ServerCommand = &cobra.Command{ - Use: "server [cmd] [args] [flags]", - Aliases: []string{"s"}, - Short: "Manage servers", + Use: i18n.G("server [cmd] [args] [flags]"), + Aliases: []string{i18n.G("s")}, + Short: i18n.G("Manage servers"), } diff --git a/cli/updater/updater.go b/cli/updater/updater.go index e0979107..2c59cb48 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -2,6 +2,7 @@ package updater import ( "context" + "errors" "fmt" "os" "strconv" @@ -11,6 +12,7 @@ import ( appPkg "coopcloud.tech/abra/pkg/app" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/envfile" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/lint" "coopcloud.tech/abra/pkg/recipe" "coopcloud.tech/abra/pkg/upstream/convert" @@ -30,14 +32,14 @@ const SERVER = "localhost" // NotifyCommand checks for available upgrades. var NotifyCommand = &cobra.Command{ - Use: "notify [flags]", - Aliases: []string{"n"}, - Short: "Check for available upgrades", - Long: `Notify on new versions for deployed apps. + Use: i18n.G("notify [flags]"), + Aliases: []string{i18n.G("n")}, + Short: i18n.G("Check for available upgrades"), + Long: i18n.G(`Notify on new versions for deployed apps. If a new patch/minor version is available, a notification is printed. -Use "--major/-m" to include new major versions.`, +Use "--major/-m" to include new major versions.`), Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { cl, err := client.New("default") @@ -69,10 +71,10 @@ Use "--major/-m" to include new major versions.`, // UpgradeCommand upgrades apps. var UpgradeCommand = &cobra.Command{ - Use: "upgrade [[stack] [recipe] | --all] [flags]", - Aliases: []string{"u"}, - Short: "Upgrade apps", - Long: `Upgrade an app by specifying stack name and recipe. + Use: i18n.G("upgrade [[stack] [recipe] | --all] [flags]"), + Aliases: []string{i18n.G("u")}, + Short: i18n.G("Upgrade apps"), + Long: i18n.G(`Upgrade an app by specifying stack name and recipe. Use "--all" to upgrade every deployed app. @@ -83,7 +85,7 @@ available, the app is upgraded. To include major versions use the "--major/-m" flag. You probably don't want that as it will break things. Only apps that are not deployed with "--chaos/-C" are upgraded, to update chaos deployments use the "--chaos/-C" flag. Use it -with care.`, +with care.`), Args: cobra.RangeArgs(0, 2), // TODO(d1): complete stack/recipe // ValidArgsFunction: func( @@ -98,7 +100,7 @@ with care.`, } if !updateAll && len(args) != 2 { - log.Fatal("missing arguments or --all/-a flag") + log.Fatal(i18n.G("missing arguments or --all/-a flag")) } if !updateAll { @@ -150,7 +152,7 @@ func getLabel(cl *dockerclient.Client, stackName string, label string) (string, } } - log.Debugf("no %s label found for %s", label, stackName) + log.Debug(i18n.G("no %s label found for %s", label, stackName)) return "", nil } @@ -171,7 +173,7 @@ func getBoolLabel(cl *dockerclient.Client, stackName string, label string) (bool return value, nil } - log.Debugf("boolean label %s could not be found for %s, set default to false.", label, stackName) + log.Debug(i18n.G("boolean label %s could not be found for %s, set default to false.", label, stackName)) return false, nil } @@ -192,12 +194,12 @@ func getEnv(cl *dockerclient.Client, stackName string) (envfile.AppEnv, error) { for _, envString := range envList { splitString := strings.SplitN(envString, "=", 2) if len(splitString) != 2 { - log.Debugf("can't separate key from value: %s (this variable is probably unset)", envString) + log.Debug(i18n.G("can't separate key from value: %s (this variable is probably unset)", envString)) continue } k := splitString[0] v := splitString[1] - log.Debugf("for %s read env %s with value: %s from docker service", stackName, k, v) + log.Debugf(i18n.G("for %s read env %s with value: %s from docker service", stackName, k, v)) envMap[k] = v } } @@ -219,14 +221,14 @@ func getLatestUpgrade(cl *dockerclient.Client, stackName string, recipeName stri } if len(availableUpgrades) == 0 { - log.Debugf("no available upgrades for %s", stackName) + log.Debugf(i18n.G("no available upgrades for %s", stackName)) return "", nil } var chosenUpgrade string if len(availableUpgrades) > 0 { chosenUpgrade = availableUpgrades[len(availableUpgrades)-1] - log.Infof("%s (%s) can be upgraded from version %s to %s", stackName, recipeName, deployedVersion, chosenUpgrade) + log.Info(i18n.G("%s (%s) can be upgraded from version %s to %s", stackName, recipeName, deployedVersion, chosenUpgrade)) } return chosenUpgrade, nil @@ -234,7 +236,7 @@ func getLatestUpgrade(cl *dockerclient.Client, stackName string, recipeName stri // getDeployedVersion returns the currently deployed version of an app. func getDeployedVersion(cl *dockerclient.Client, stackName string, recipeName string) (string, error) { - log.Debugf("retrieve deployed version whether %s is already deployed", stackName) + log.Debug(i18n.G("retrieve deployed version whether %s is already deployed", stackName)) deployMeta, err := stack.IsDeployed(context.Background(), cl, stackName) if err != nil { @@ -242,11 +244,11 @@ func getDeployedVersion(cl *dockerclient.Client, stackName string, recipeName st } if !deployMeta.IsDeployed { - return "", fmt.Errorf("%s is not deployed?", stackName) + return "", errors.New(i18n.G("%s is not deployed?", stackName)) } if deployMeta.Version == "unknown" { - return "", fmt.Errorf("failed to determine deployed version of %s", stackName) + return "", errors.New(i18n.G("failed to determine deployed version of %s", stackName)) } return deployMeta.Version, nil @@ -268,7 +270,7 @@ func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName } if len(versions) == 0 { - log.Warnf("no published releases for %s in the recipe catalogue?", recipeName) + log.Warn(i18n.G("no published releases for %s in the recipe catalogue?", recipeName)) return nil, nil } @@ -294,7 +296,7 @@ func getAvailableUpgrades(cl *dockerclient.Client, stackName string, recipeName } } - log.Debugf("available updates for %s: %s", stackName, availableUpgrades) + log.Debug(i18n.G("available updates for %s: %s", stackName, availableUpgrades)) return availableUpgrades, nil } @@ -371,7 +373,7 @@ func createDeployConfig(r recipe.Recipe, stackName string, env envfile.AppEnv) ( // tryUpgrade performs the upgrade if all the requirements are fulfilled. func tryUpgrade(cl *dockerclient.Client, stackName, recipeName string) error { if recipeName == "" { - log.Debugf("don't update %s due to missing recipe name", stackName) + log.Debug(i18n.G("don't update %s due to missing recipe name", stackName)) return nil } @@ -381,7 +383,7 @@ func tryUpgrade(cl *dockerclient.Client, stackName, recipeName string) error { } if chaos && !internal.Chaos { - log.Debugf("don't update %s due to chaos deployment", stackName) + log.Debug(i18n.G("don't update %s due to chaos deployment", stackName)) return nil } @@ -391,7 +393,7 @@ func tryUpgrade(cl *dockerclient.Client, stackName, recipeName string) error { } if !updatesEnabled { - log.Debugf("don't update %s due to disabled auto updates or missing ENABLE_AUTO_UPDATE env", stackName) + log.Debug(i18n.G("don't update %s due to disabled auto updates or missing ENABLE_AUTO_UPDATE env", stackName)) return nil } @@ -401,7 +403,7 @@ func tryUpgrade(cl *dockerclient.Client, stackName, recipeName string) error { } if upgradeVersion == "" { - log.Debugf("don't update %s due to no new version", stackName) + log.Debug(i18n.G("don't update %s due to no new version", stackName)) return nil } @@ -439,7 +441,7 @@ func upgrade(cl *dockerclient.Client, stackName, recipeName, upgradeVersion stri return err } - log.Infof("upgrade %s (%s) to version %s", stackName, recipeName, upgradeVersion) + log.Info(i18n.G("upgrade %s (%s) to version %s", stackName, recipeName, upgradeVersion)) serviceNames, err := appPkg.GetAppServiceNames(app.Name) if err != nil { @@ -466,9 +468,9 @@ func upgrade(cl *dockerclient.Client, stackName, recipeName, upgradeVersion stri func newKadabraApp(version, commit string) *cobra.Command { rootCmd := &cobra.Command{ - Use: "kadabra [cmd] [flags]", + Use: i18n.G("kadabra [cmd] [flags]"), Version: fmt.Sprintf("%s-%s", version, commit[:7]), - Short: "The Co-op Cloud auto-updater 🤖 🚀", + Short: i18n.G("The Co-op Cloud auto-updater 🤖 🚀"), PersistentPreRun: func(cmd *cobra.Command, args []string) { log.Logger.SetStyles(charmLog.DefaultStyles()) charmLog.SetDefault(log.Logger) @@ -479,18 +481,24 @@ func newKadabraApp(version, commit string) *cobra.Command { log.SetReportCaller(true) } - log.Debugf("kadabra version %s, commit %s", version, commit) + log.Debug(i18n.G("kadabra version %s, commit %s", version, commit)) }, } rootCmd.PersistentFlags().BoolVarP( - &internal.Debug, "debug", "d", false, - "show debug messages", + &internal.Debug, + i18n.G("debug"), + i18n.G("d"), + false, + i18n.G("show debug messages"), ) rootCmd.PersistentFlags().BoolVarP( - &internal.NoInput, "no-input", "n", false, - "toggle non-interactive mode", + &internal.NoInput, + i18n.G("no-input"), + i18n.G("n"), + false, + i18n.G("toggle non-interactive mode"), ) rootCmd.AddCommand( @@ -526,25 +534,25 @@ func init() { UpgradeCommand.Flags().BoolVarP( &internal.Chaos, - "chaos", - "C", + i18n.G("chaos"), + i18n.G("C"), false, - "ignore uncommitted recipes changes", + i18n.G("ignore uncommitted recipes changes"), ) UpgradeCommand.Flags().BoolVarP( &includeMajorUpdates, - "major", - "m", + i18n.G("major"), + i18n.G("m"), false, - "check for major updates", + i18n.G("check for major updates"), ) UpgradeCommand.Flags().BoolVarP( &updateAll, - "all", - "a", + i18n.G("all"), + i18n.G("a"), false, - "update all deployed apps", + i18n.G("update all deployed apps"), ) } diff --git a/cli/upgrade.go b/cli/upgrade.go index aafa1310..e018c2b8 100644 --- a/cli/upgrade.go +++ b/cli/upgrade.go @@ -6,23 +6,24 @@ import ( "os/exec" "coopcloud.tech/abra/cli/internal" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/spf13/cobra" ) // UpgradeCommand upgrades abra in-place. var UpgradeCommand = &cobra.Command{ - Use: "upgrade [flags]", + Use: i18n.G("upgrade [flags]"), Aliases: []string{"u"}, - Short: "Upgrade abra", - Long: `Upgrade abra in-place with the latest stable or release candidate. + Short: i18n.G("Upgrade abra"), + Long: i18n.G(`Upgrade abra in-place with the latest stable or release candidate. By default, the latest stable release is downloaded. Use "--rc/-r" to install the latest release candidate. Please bear in mind that it may contain absolutely catastrophic deal-breaker bugs. Thank you very much -for the testing efforts 💗`, - Example: " abra upgrade --rc", +for the testing efforts 💗`), + Example: i18n.G(" abra upgrade --rc"), Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { mainURL := "https://install.abra.coopcloud.tech" @@ -33,7 +34,7 @@ for the testing efforts 💗`, c = exec.Command("bash", "-c", fmt.Sprintf("wget -q -O- %s | bash -s -- --rc", releaseCandidateURL)) } - log.Debugf("attempting to run %s", c) + log.Debugf(i18n.G("attempting to run %s", c)) if err := internal.RunCmd(c); err != nil { log.Fatal(err) @@ -51,6 +52,6 @@ func init() { "rc", "r", false, - "install release candidate (may contain bugs)", + i18n.G("install release candidate (may contain bugs)"), ) } diff --git a/go.mod b/go.mod index be78dc17..8af1e328 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,6 @@ require ( require ( dario.cat/mergo v1.0.2 // indirect - github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/BurntSushi/toml v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect @@ -52,14 +51,12 @@ require ( github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/distribution v2.8.3+incompatible // indirect - github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect github.com/docker/go-connections v0.6.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect @@ -85,11 +82,9 @@ require ( github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/miekg/pkcs11 v1.1.1 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/go-archive v0.1.0 // indirect github.com/moby/sys/atomicwriter v0.1.0 // indirect - github.com/moby/sys/mountinfo v0.7.2 // indirect github.com/moby/sys/user v0.4.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect github.com/morikuni/aec v1.0.0 // indirect @@ -100,7 +95,6 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/runc v1.1.13 // indirect github.com/opencontainers/runtime-spec v1.1.0 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect github.com/pjbgf/sha1cd v0.4.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect diff --git a/go.sum b/go.sum index ed1015b9..ceab1712 100644 --- a/go.sum +++ b/go.sum @@ -22,21 +22,13 @@ cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIA cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -coopcloud.tech/tagcmp v0.0.0-20230809071031-eb3e7758d4eb h1:Ws6WEwKXeaYEkfdkX6AqX1XLPuaCeyStEtxbmEJPllk= -coopcloud.tech/tagcmp v0.0.0-20230809071031-eb3e7758d4eb/go.mod h1:ESVm0wQKcbcFi06jItF3rI7enf4Jt2PvbkWpDDHk1DQ= -coopcloud.tech/tagcmp v0.0.0-20250427094623-9ea3bbbde8e5 h1:tphJCjFJw9fdjyKnbU0f7f3z5KtYE8VbUcAfu+oHKg8= -coopcloud.tech/tagcmp v0.0.0-20250427094623-9ea3bbbde8e5/go.mod h1:ESVm0wQKcbcFi06jItF3rI7enf4Jt2PvbkWpDDHk1DQ= coopcloud.tech/tagcmp v0.0.0-20250818180036-0ec1b205b5ca h1:gSD53tBAsbIGq4SnFfq+mEep6foekQ2a5ea7b38qkm0= coopcloud.tech/tagcmp v0.0.0-20250818180036-0ec1b205b5ca/go.mod h1:ESVm0wQKcbcFi06jItF3rI7enf4Jt2PvbkWpDDHk1DQ= -dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= -dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= git.coopcloud.tech/toolshed/godotenv v1.5.2-0.20250103171850-4d0ca41daa5c h1:oeKnUB79PKYD8D0/unYuu7MRcWryQQWOns8+JL+acrs= git.coopcloud.tech/toolshed/godotenv v1.5.2-0.20250103171850-4d0ca41daa5c/go.mod h1:fQuhwrpg6qb9NlFXKYi/LysWu1wxjraS8sxyW12CUF0= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= @@ -57,8 +49,6 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -89,8 +79,6 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0 github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= -github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -98,7 +86,6 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -133,11 +120,8 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dR github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0 h1:s7+5BfS4WFJoVF9pnB8kBk03S7pZXRdKamnV0FOl5Sc= github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= @@ -145,29 +129,18 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 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/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI= -github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo= github.com/charmbracelet/bubbletea v1.3.6 h1:VkHIxPJQeDt0aFJIsVxw8BQdh/F/L2KKZGsK6et5taU= github.com/charmbracelet/bubbletea v1.3.6/go.mod h1:oQD9VCRQFF8KplacJLo28/jofOI2ToOfGYeFgBBxHOc= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= -github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40= -github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0= github.com/charmbracelet/colorprofile v0.3.2 h1:9J27WdztfJQVAQKX2WOlSSRB+5gaKqqITmrvb1uTIiI= github.com/charmbracelet/colorprofile v0.3.2/go.mod h1:mTD5XzNeWHj8oqHb+S1bssQb7vIHbepiebQ2kPKVKbI= github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= -github.com/charmbracelet/log v0.4.1 h1:6AYnoHKADkghm/vt4neaNEXkxcXLSV2g1rdyFDOpTyk= -github.com/charmbracelet/log v0.4.1/go.mod h1:pXgyTsqsVu4N9hGdHmQ0xEA4RsXof402LX9ZgiITn2I= github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig= github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw= -github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= -github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ= github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k= @@ -191,10 +164,7 @@ github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJ github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e85keuznYcH5rqI438v41pKcBl4ZxQ= github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= -github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= -github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -316,7 +286,6 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -347,8 +316,6 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v28.0.1+incompatible h1:g0h5NQNda3/CxIsaZfH4Tyf6vpxFth7PYl3hgCPOKzs= -github.com/docker/cli v28.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v28.3.3+incompatible h1:fp9ZHAr1WWPGdIWBM1b3zLtgCF+83gRdVMTJsUeiyAo= github.com/docker/cli v28.3.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= @@ -357,8 +324,6 @@ github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4Kfc github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0= -github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= @@ -367,8 +332,6 @@ github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= @@ -413,8 +376,6 @@ github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= @@ -430,8 +391,6 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60= -github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k= github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM= github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -447,8 +406,6 @@ github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KE github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -464,11 +421,8 @@ github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8 github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-sql-driver/mysql v1.3.0 h1:pgwjLi/dvffoP9aabwkT3AKpXQM93QARkjFhDDqC1UE= github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= -github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= @@ -522,7 +476,6 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93 h1:jc2UWq7CbdszqeH6qu1ougXMIUBfSy8Pbh/anURYbGI= github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -575,13 +528,9 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -592,13 +541,10 @@ github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVH github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= -github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= @@ -615,9 +561,7 @@ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8 h1:CZkYfurY6KGhVtlalI4QwQ6T0Cu6iuY3e0x5RLu96WE= github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= -github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d h1:jRQLvyVGL+iVtDElaEIDdKwpPqUIZJfzkNLV34htpEc= github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -670,7 +614,6 @@ github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -710,8 +653,6 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= @@ -725,8 +666,6 @@ github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtl github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= -github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= @@ -734,8 +673,6 @@ github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiT github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0= github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8= github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= -github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= -github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= @@ -817,15 +754,10 @@ github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqi github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= -github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pjbgf/sha1cd v0.4.0 h1:NXzbL1RvjTUi6kgYZCX3fPwwl27Q1LJndxtUDVfJGRY= github.com/pjbgf/sha1cd v0.4.0/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -843,8 +775,6 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= -github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -852,8 +782,6 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1: github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= @@ -862,8 +790,6 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= -github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -877,8 +803,6 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= @@ -890,7 +814,6 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -901,8 +824,6 @@ github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8G github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -923,10 +844,8 @@ github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:s github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -935,7 +854,6 @@ github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHN github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -943,12 +861,10 @@ github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bd github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= -github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -1023,47 +939,27 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0 h1:zG8GlgXCJQd5BU98C0hZnBbElszTmUgCNCfYneaDL0A= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0/go.mod h1:hOfBCz8kv/wuq73Mx2H2QnWokh/kHZxkh6SNF2bdKtw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= -go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1090,8 +986,6 @@ golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1104,10 +998,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= -golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= -golang.org/x/exp v0.0.0-20250811191247-51f88131bc50 h1:3yiSh9fhy5/RhCSntf4Sy0Tnx50DmMpQ4MQdKKk4yg4= -golang.org/x/exp v0.0.0-20250811191247-51f88131bc50/go.mod h1:rT6SFzZ7oxADUDx58pcaKFTcZ+inxAa9fTrYx/uVYwg= golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 h1:SbTAbRFnd5kjQXbczszQ0hdk3ctwYf3qBNH9jIsGclE= golang.org/x/exp v0.0.0-20250813145105-42675adae3e6/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -1173,8 +1063,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= -golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1194,8 +1082,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1275,17 +1161,12 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1297,8 +1178,6 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1307,8 +1186,6 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= -golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1404,12 +1281,8 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 h1:IFnXJq3UPB3oBREOodn1v1aGQeZYQclEmvWRMN0PSsY= -google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:c8q6Z6OCqnfVIqUFJkCzKcrj8eCvUrz+K4KRzSTuANg= google.golang.org/genproto/googleapis/api v0.0.0-20250811230008-5f3141c8851a h1:DMCgtIAIQGZqJXMVzJF4MV8BlWoJh2ZuFiRdAleyr58= google.golang.org/genproto/googleapis/api v0.0.0-20250811230008-5f3141c8851a/go.mod h1:y2yVLIE/CSMCPXaHnSKXxu1spLPnglFLegmgdY23uuE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a h1:tPE/Kp+x9dMSwUm/uM0JKK0IfdiJkwAbSMSeZBXXJXc= google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -1431,8 +1304,6 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= -google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4= google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -1448,13 +1319,10 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/cenkalti/backoff.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII= gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1470,7 +1338,6 @@ gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKW gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1 h1:d4KQkxAaAiRY2h5Zqis161Pv91A37uZyJOx73duwUwM= gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -1491,7 +1358,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= diff --git a/locales/default.pot b/locales/default.pot deleted file mode 100644 index 39987c8b..00000000 --- a/locales/default.pot +++ /dev/null @@ -1,12 +0,0 @@ -msgid "" -msgstr "" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: \n" -"X-Generator: xgotext\n" - -#: app.go:11 -msgid "Manage apps" -msgstr "" \ No newline at end of file diff --git a/locales/es/LC_MESSAGES/default.po b/locales/es/LC_MESSAGES/default.po deleted file mode 100644 index a50fdda0..00000000 --- a/locales/es/LC_MESSAGES/default.po +++ /dev/null @@ -1,20 +0,0 @@ -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-08-04 14:15+0000\n" -"PO-Revision-Date: 2025-08-04 14:15+0000\n" -"Last-Translator: 3wordchant <3wc.coopcloud@doesthisthing.work>\n" -"Language-Team: Spanish \n" -"Language: es\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: ENCODING\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.12.2\n" - -#: app.go:11 -msgid "Manage apps" -msgstr "Gestionar aplicaciones" diff --git a/pkg/app/app.go b/pkg/app/app.go index f7b7f27f..cca0e1ca 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -2,6 +2,7 @@ package app import ( "bufio" + "errors" "fmt" "os" "path" @@ -13,6 +14,7 @@ import ( "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/envfile" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/recipe" "coopcloud.tech/abra/pkg/upstream/convert" "coopcloud.tech/abra/pkg/upstream/stack" @@ -36,7 +38,7 @@ func Get(appName string) (App, error) { return App{}, err } - log.Debugf("loaded app %s: %s", appName, app) + log.Debug(i18n.G("loaded app %s: %s", appName, app)) return app, nil } @@ -47,7 +49,7 @@ func Get(appName string) (App, error) { func GetApp(apps AppFiles, name AppName) (App, error) { appFile, exists := apps[name] if !exists { - return App{}, fmt.Errorf("cannot find app with name %s", name) + return App{}, errors.New(i18n.G("cannot find app with name %s", name)) } app, err := ReadAppEnvFile(appFile, name) @@ -136,7 +138,7 @@ func StackName(appName string) string { stackName := SanitiseAppName(appName) if len(stackName) > config.MAX_SANITISED_APP_NAME_LENGTH { - log.Debugf("trimming %s to %s to avoid runtime limits", stackName, stackName[:config.MAX_SANITISED_APP_NAME_LENGTH]) + log.Debug(i18n.G("trimming %s to %s to avoid runtime limits", stackName, stackName[:config.MAX_SANITISED_APP_NAME_LENGTH])) stackName = stackName[:config.MAX_SANITISED_APP_NAME_LENGTH] } @@ -243,12 +245,12 @@ func (a ByName) Less(i, j int) bool { func ReadAppEnvFile(appFile AppFile, name AppName) (App, error) { env, err := envfile.ReadEnv(appFile.Path) if err != nil { - return App{}, fmt.Errorf("env file for %s couldn't be read: %s", name, err.Error()) + return App{}, errors.New(i18n.G("env file for %s couldn't be read: %s", name, err.Error())) } app, err := NewApp(env, name, appFile) if err != nil { - return App{}, fmt.Errorf("env file for %s has issues: %s", name, err.Error()) + return App{}, errors.New(i18n.G("env file for %s has issues: %s", name, err.Error())) } return app, nil @@ -262,7 +264,7 @@ func NewApp(env envfile.AppEnv, name string, appFile AppFile) (App, error) { if !exists { recipeName, exists = env["TYPE"] if !exists { - return App{}, fmt.Errorf("%s is missing the TYPE env var?", name) + return App{}, errors.New(i18n.G("%s is missing the TYPE env var?", name)) } } @@ -290,13 +292,13 @@ func LoadAppFiles(servers ...string) (AppFiles, error) { } } - log.Debugf("collecting metadata from %v servers: %s", len(servers), strings.Join(servers, ", ")) + log.Debug(i18n.G("collecting metadata from %v servers: %s", len(servers), strings.Join(servers, ", "))) for _, server := range servers { serverDir := path.Join(config.SERVERS_DIR, server) files, err := config.GetAllFilesInDirectory(serverDir) if err != nil { - return appFiles, fmt.Errorf("server %s doesn't exist? Run \"abra server ls\" to check", server) + return appFiles, errors.New(i18n.G("server %s doesn't exist? Run \"abra server ls\" to check", server)) } for _, file := range files { @@ -375,7 +377,7 @@ func TemplateAppEnvSample(r recipe.Recipe, appName, server, domain string) error appEnvPath := path.Join(config.ABRA_DIR, "servers", server, fmt.Sprintf("%s.env", appName)) if _, err := os.Stat(appEnvPath); !os.IsNotExist(err) { - return fmt.Errorf("%s already exists?", appEnvPath) + return errors.New(i18n.G("%s already exists?", appEnvPath)) } err = os.WriteFile(appEnvPath, envSample, 0o664) @@ -395,7 +397,7 @@ func TemplateAppEnvSample(r recipe.Recipe, appName, server, domain string) error return err } - log.Debugf("copied & templated %s to %s", r.SampleEnvPath, appEnvPath) + log.Debug(i18n.G("copied & templated %s to %s", r.SampleEnvPath, appEnvPath)) return nil } @@ -419,7 +421,7 @@ func GetAppStatuses(apps []App, MachineReadable bool) (map[string]map[string]str var bar *progressbar.ProgressBar if !MachineReadable { - bar = formatter.CreateProgressbar(len(servers), "querying remote servers...") + bar = formatter.CreateProgressbar(len(servers), i18n.G("querying remote servers...")) } ch := make(chan stack.StackStatus, len(servers)) @@ -482,7 +484,7 @@ func GetAppStatuses(apps []App, MachineReadable bool) (map[string]map[string]str } } - log.Debugf("retrieved app statuses: %s", statuses) + log.Debug(i18n.G("retrieved app statuses: %s", statuses)) return statuses, nil } @@ -496,7 +498,7 @@ func GetAppComposeConfig(recipe string, opts stack.Deploy, appEnv envfile.AppEnv return &composetypes.Config{}, err } - log.Debugf("retrieved %s for %s", compose.Filename, recipe) + log.Debug(i18n.G("retrieved %s for %s", compose.Filename, recipe)) return compose, nil } @@ -505,13 +507,13 @@ func GetAppComposeConfig(recipe string, opts stack.Deploy, appEnv envfile.AppEnv func ExposeAllEnv(stackName string, compose *composetypes.Config, appEnv envfile.AppEnv) { for _, service := range compose.Services { if service.Name == "app" { - log.Debugf("adding env vars to %s service config", stackName) + log.Debug(i18n.G("adding env vars to %s service config", stackName)) for k, v := range appEnv { _, exists := service.Environment[k] if !exists { value := v service.Environment[k] = &value - log.Debugf("%s: %s: %s", stackName, k, value) + log.Debug(i18n.G("%s: %s: %s", stackName, k, value)) } } } @@ -572,9 +574,9 @@ func ReadAbraShCmdNames(abraSh string) ([]string, error) { } if len(cmdNames) > 0 { - log.Debugf("read %s from %s", strings.Join(cmdNames, " "), abraSh) + log.Debug(i18n.G("read %s from %s", strings.Join(cmdNames, " "), abraSh)) } else { - log.Debugf("read 0 command names from %s", abraSh) + log.Debug(i18n.G("read 0 command names from %s", abraSh)) } return cmdNames, nil @@ -617,7 +619,7 @@ func (a App) WipeRecipeVersion() error { log.Fatal(err) } - log.Debugf("version wiped from %s.env", a.Domain) + log.Debug(i18n.G("version wiped from %s.env", a.Domain)) return nil } @@ -674,13 +676,13 @@ func (a App) WriteRecipeVersion(version string, dryRun bool) error { log.Fatal(err) } } else { - log.Debugf("skipping writing version %s because dry run", version) + log.Debug(i18n.G("skipping writing version %s because dry run", version)) } if !skipped { - log.Debugf("version %s saved to %s.env", version, a.Domain) + log.Debug(i18n.G("version %s saved to %s.env", version, a.Domain)) } else { - log.Debugf("skipping version %s write as already exists in %s.env", version, a.Domain) + log.Debug(i18n.G("skipping version %s write as already exists in %s.env", version, a.Domain)) } return nil diff --git a/pkg/app/compose.go b/pkg/app/compose.go index 1bc7b461..dd5070b1 100644 --- a/pkg/app/compose.go +++ b/pkg/app/compose.go @@ -5,6 +5,7 @@ import ( "strconv" "coopcloud.tech/abra/pkg/envfile" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" composetypes "github.com/docker/cli/cli/compose/types" ) @@ -14,7 +15,7 @@ import ( func SetRecipeLabel(compose *composetypes.Config, stackName string, recipe string) { for _, service := range compose.Services { if service.Name == "app" { - log.Debugf("set recipe label 'coop-cloud.%s.recipe' to %s for %s", stackName, recipe, stackName) + log.Debug(i18n.G("set recipe label 'coop-cloud.%s.recipe' to %s for %s", stackName, recipe, stackName)) labelKey := fmt.Sprintf("coop-cloud.%s.recipe", stackName) service.Deploy.Labels[labelKey] = recipe } @@ -26,7 +27,7 @@ func SetRecipeLabel(compose *composetypes.Config, stackName string, recipe strin func SetChaosLabel(compose *composetypes.Config, stackName string, chaos bool) { for _, service := range compose.Services { if service.Name == "app" { - log.Debugf("set label 'coop-cloud.%s.chaos' to %v for %s", stackName, chaos, stackName) + log.Debug(i18n.G("set label 'coop-cloud.%s.chaos' to %v for %s", stackName, chaos, stackName)) labelKey := fmt.Sprintf("coop-cloud.%s.chaos", stackName) service.Deploy.Labels[labelKey] = strconv.FormatBool(chaos) } @@ -37,7 +38,7 @@ func SetChaosLabel(compose *composetypes.Config, stackName string, chaos bool) { func SetChaosVersionLabel(compose *composetypes.Config, stackName string, chaosVersion string) { for _, service := range compose.Services { if service.Name == "app" { - log.Debugf("set label 'coop-cloud.%s.chaos-version' to %v for %s", stackName, chaosVersion, stackName) + log.Debug(i18n.G("set label 'coop-cloud.%s.chaos-version' to %v for %s", stackName, chaosVersion, stackName)) labelKey := fmt.Sprintf("coop-cloud.%s.chaos-version", stackName) service.Deploy.Labels[labelKey] = chaosVersion } @@ -47,7 +48,7 @@ func SetChaosVersionLabel(compose *composetypes.Config, stackName string, chaosV func SetVersionLabel(compose *composetypes.Config, stackName string, version string) { for _, service := range compose.Services { if service.Name == "app" { - log.Debugf("set label 'coop-cloud.%s.version' to %v for %s", stackName, version, stackName) + log.Debug(i18n.G("set label 'coop-cloud.%s.version' to %v for %s", stackName, version, stackName)) labelKey := fmt.Sprintf("coop-cloud.%s.version", stackName) service.Deploy.Labels[labelKey] = version } @@ -64,7 +65,7 @@ func SetUpdateLabel(compose *composetypes.Config, stackName string, appEnv envfi if !exists { enable_auto_update = "false" } - log.Debugf("set label 'coop-cloud.%s.autoupdate' to %s for %s", stackName, enable_auto_update, stackName) + log.Debug(i18n.G("set label 'coop-cloud.%s.autoupdate' to %s for %s", stackName, enable_auto_update, stackName)) labelKey := fmt.Sprintf("coop-cloud.%s.autoupdate", stackName) service.Deploy.Labels[labelKey] = enable_auto_update } @@ -76,13 +77,13 @@ func GetLabel(compose *composetypes.Config, stackName string, label string) stri for _, service := range compose.Services { if service.Name == "app" { labelKey := fmt.Sprintf("coop-cloud.%s.%s", stackName, label) - log.Debugf("get label '%s'", labelKey) + log.Debug(i18n.G("get label '%s'", labelKey)) if labelValue, ok := service.Deploy.Labels[labelKey]; ok { return labelValue } } } - log.Debugf("no %s label found for %s", label, stackName) + log.Debug(i18n.G("no %s label found for %s", label, stackName)) return "" } @@ -91,7 +92,7 @@ func GetTimeoutFromLabel(compose *composetypes.Config, stackName string) (int, e timeout := 50 // Default Timeout var err error = nil if timeoutLabel := GetLabel(compose, stackName, "timeout"); timeoutLabel != "" { - log.Debugf("timeout label: %s", timeoutLabel) + log.Debug(i18n.G("timeout label: %s", timeoutLabel)) timeout, err = strconv.Atoi(timeoutLabel) } return timeout, err diff --git a/pkg/autocomplete/autocomplete.go b/pkg/autocomplete/autocomplete.go index aafea741..03986c63 100644 --- a/pkg/autocomplete/autocomplete.go +++ b/pkg/autocomplete/autocomplete.go @@ -1,11 +1,11 @@ package autocomplete import ( - "fmt" "sort" "coopcloud.tech/abra/pkg/app" appPkg "coopcloud.tech/abra/pkg/app" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/recipe" "github.com/spf13/cobra" ) @@ -14,7 +14,7 @@ import ( func AppNameComplete() ([]string, cobra.ShellCompDirective) { appFiles, err := app.LoadAppFiles("") if err != nil { - err := fmt.Sprintf("autocomplete failed: %s", err) + err := i18n.G("autocomplete failed: %s", err) return []string{err}, cobra.ShellCompDirectiveError } @@ -29,7 +29,7 @@ func AppNameComplete() ([]string, cobra.ShellCompDirective) { func ServiceNameComplete(appName string) ([]string, cobra.ShellCompDirective) { serviceNames, err := app.GetAppServiceNames(appName) if err != nil { - err := fmt.Sprintf("autocomplete failed: %s", err) + err := i18n.G("autocomplete failed: %s", err) return []string{err}, cobra.ShellCompDirectiveError } @@ -40,7 +40,7 @@ func ServiceNameComplete(appName string) ([]string, cobra.ShellCompDirective) { func RecipeNameComplete() ([]string, cobra.ShellCompDirective) { catl, err := recipe.ReadRecipeCatalogue(false) if err != nil { - err := fmt.Sprintf("autocomplete failed: %s", err) + err := i18n.G("autocomplete failed: %s", err) return []string{err}, cobra.ShellCompDirectiveError } @@ -56,7 +56,7 @@ func RecipeNameComplete() ([]string, cobra.ShellCompDirective) { func RecipeVersionComplete(recipeName string) ([]string, cobra.ShellCompDirective) { catl, err := recipe.ReadRecipeCatalogue(true) if err != nil { - err := fmt.Sprintf("autocomplete failed: %s", err) + err := i18n.G("autocomplete failed: %s", err) return []string{err}, cobra.ShellCompDirectiveError } @@ -74,7 +74,7 @@ func RecipeVersionComplete(recipeName string) ([]string, cobra.ShellCompDirectiv func ServerNameComplete() ([]string, cobra.ShellCompDirective) { files, err := app.LoadAppFiles("") if err != nil { - err := fmt.Sprintf("autocomplete failed: %s", err) + err := i18n.G("autocomplete failed: %s", err) return []string{err}, cobra.ShellCompDirectiveError } @@ -90,13 +90,13 @@ func ServerNameComplete() ([]string, cobra.ShellCompDirective) { func CommandNameComplete(appName string) ([]string, cobra.ShellCompDirective) { app, err := app.Get(appName) if err != nil { - err := fmt.Sprintf("autocomplete failed: %s", err) + err := i18n.G("autocomplete failed: %s", err) return []string{err}, cobra.ShellCompDirectiveError } cmdNames, err := appPkg.ReadAbraShCmdNames(app.Recipe.AbraShPath) if err != nil { - err := fmt.Sprintf("autocomplete failed: %s", err) + err := i18n.G("autocomplete failed: %s", err) return []string{err}, cobra.ShellCompDirectiveError } @@ -111,7 +111,7 @@ func SecretComplete(recipeName string) ([]string, cobra.ShellCompDirective) { config, err := r.GetComposeConfig(nil) if err != nil { - err := fmt.Sprintf("autocomplete failed: %s", err) + err := i18n.G("autocomplete failed: %s", err) return []string{err}, cobra.ShellCompDirectiveError } diff --git a/pkg/catalogue/catalogue.go b/pkg/catalogue/catalogue.go index 76859616..cc043e52 100644 --- a/pkg/catalogue/catalogue.go +++ b/pkg/catalogue/catalogue.go @@ -1,6 +1,7 @@ package catalogue import ( + "errors" "fmt" "os" "path" @@ -8,6 +9,7 @@ import ( "coopcloud.tech/abra/pkg/config" gitPkg "coopcloud.tech/abra/pkg/git" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/go-git/go-git/v5" ) @@ -16,7 +18,7 @@ import ( func EnsureCatalogue() error { catalogueDir := path.Join(config.ABRA_DIR, "catalogue") if _, err := os.Stat(catalogueDir); err != nil && os.IsNotExist(err) { - log.Debugf("catalogue is missing, retrieving now") + log.Debug(i18n.G("catalogue is missing, retrieving now")) url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, config.CATALOGUE_JSON_REPO_NAME) if err := gitPkg.Clone(catalogueDir, url); err != nil { @@ -35,8 +37,7 @@ func EnsureIsClean() error { } if !isClean { - msg := "%s has locally unstaged changes? please commit/remove your changes before proceeding" - return fmt.Errorf(msg, config.CATALOGUE_DIR) + return errors.New(i18n.G("%s has locally unstaged changes? please commit/remove your changes before proceeding", config.CATALOGUE_DIR)) } return nil @@ -55,8 +56,7 @@ func EnsureUpToDate() error { } if len(remotes) == 0 { - msg := "cannot ensure %s is up-to-date, no git remotes configured" - log.Debugf(msg, config.CATALOGUE_DIR) + log.Debug(i18n.G("cannot ensure %s is up-to-date, no git remotes configured", config.CATALOGUE_DIR)) return nil } @@ -81,7 +81,7 @@ func EnsureUpToDate() error { } } - log.Debugf("fetched latest git changes for %s", config.CATALOGUE_DIR) + log.Debug(i18n.G("fetched latest git changes for %s", config.CATALOGUE_DIR)) return nil } diff --git a/pkg/client/client.go b/pkg/client/client.go index 315a2732..b73a2a2c 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -4,12 +4,12 @@ package client import ( "context" "errors" - "fmt" "net/http" "os" "time" contextPkg "coopcloud.tech/abra/pkg/context" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" sshPkg "coopcloud.tech/abra/pkg/ssh" commandconnPkg "coopcloud.tech/abra/pkg/upstream/commandconn" @@ -41,7 +41,7 @@ func New(serverName string, opts ...Opt) (*client.Client, error) { if serverName != "default" { context, err := GetContext(serverName) if err != nil { - return nil, fmt.Errorf("unknown server, run \"abra server add %s\"?", serverName) + return nil, errors.New(i18n.G("unknown server, run \"abra server add %s\"?", serverName)) } ctxEndpoint, err := contextPkg.GetContextEndpoint(context) @@ -85,7 +85,7 @@ func New(serverName string, opts ...Opt) (*client.Client, error) { return nil, err } - log.Debugf("created client for %s", serverName) + log.Debug(i18n.G("created client for %s", serverName)) info, err := cl.Info(context.Background()) if err != nil { @@ -94,10 +94,10 @@ func New(serverName string, opts ...Opt) (*client.Client, error) { if info.Swarm.LocalNodeState == "inactive" { if serverName != "default" { - return cl, fmt.Errorf("swarm mode not enabled on %s?", serverName) + return cl, errors.New(i18n.G("swarm mode not enabled on %s?", serverName)) } - return cl, errors.New("swarm mode not enabled on local server?") + return cl, errors.New(i18n.G("swarm mode not enabled on local server?")) } return cl, nil diff --git a/pkg/client/configs.go b/pkg/client/configs.go index efe14ab4..2a2a0eeb 100644 --- a/pkg/client/configs.go +++ b/pkg/client/configs.go @@ -2,8 +2,9 @@ package client import ( "context" - "fmt" + "errors" + "coopcloud.tech/abra/pkg/i18n" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/client" @@ -31,7 +32,7 @@ func GetConfigNames(configs []swarm.Config) []string { func RemoveConfigs(cl *client.Client, ctx context.Context, configNames []string, force bool) error { for _, confName := range configNames { if err := cl.ConfigRemove(context.Background(), confName); err != nil { - return fmt.Errorf("conf %s: %s", confName, err) + return errors.New(i18n.G("conf %s: %s", confName, err)) } } return nil diff --git a/pkg/client/context.go b/pkg/client/context.go index 4408bda5..e1cc669d 100644 --- a/pkg/client/context.go +++ b/pkg/client/context.go @@ -5,6 +5,7 @@ import ( "fmt" "coopcloud.tech/abra/pkg/context" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" commandconnPkg "coopcloud.tech/abra/pkg/upstream/commandconn" dConfig "github.com/docker/cli/cli/config" @@ -22,7 +23,7 @@ func CreateContext(contextName string) error { return err } - log.Debugf("created the %s context", contextName) + log.Debug(i18n.G("created the %s context", contextName)) return nil } @@ -62,7 +63,7 @@ func createContext(name string, host string) error { func DeleteContext(name string) error { if name == "default" { - return errors.New("context 'default' cannot be removed") + return errors.New(i18n.G("context 'default' cannot be removed")) } if _, err := GetContext(name); err != nil { diff --git a/pkg/client/registry.go b/pkg/client/registry.go index 36d0ea73..47d57bdc 100644 --- a/pkg/client/registry.go +++ b/pkg/client/registry.go @@ -2,8 +2,10 @@ package client import ( "context" + "errors" "fmt" + "coopcloud.tech/abra/pkg/i18n" "github.com/containers/image/docker" "github.com/containers/image/types" "github.com/distribution/reference" @@ -15,7 +17,7 @@ func GetRegistryTags(img reference.Named) ([]string, error) { ref, err := docker.ParseReference(fmt.Sprintf("//%s", img)) if err != nil { - return tags, fmt.Errorf("failed to parse image %s, saw: %s", img, err.Error()) + return tags, errors.New(i18n.G("failed to parse image %s, saw: %s", img, err.Error())) } ctx := context.Background() diff --git a/pkg/client/volumes.go b/pkg/client/volumes.go index c7af7619..3fedf783 100644 --- a/pkg/client/volumes.go +++ b/pkg/client/volumes.go @@ -2,9 +2,10 @@ package client import ( "context" - "fmt" + "errors" "time" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/volume" @@ -37,7 +38,7 @@ func RemoveVolumes(cl *client.Client, ctx context.Context, volumeNames []string, return cl.VolumeRemove(context.Background(), volName, force) }) if err != nil { - return fmt.Errorf("volume %s: %s", volName, err) + return errors.New(i18n.G("volume %s: %s", volName, err)) } } return nil @@ -54,9 +55,9 @@ func retryFunc(retries int, fn func() error) error { } if i+1 < retries { sleep := time.Duration(i+1) * time.Duration(i+1) - log.Infof("%s: waiting %d seconds before next retry", err, sleep) + log.Info(i18n.G("%s: waiting %d seconds before next retry", err, sleep)) time.Sleep(sleep * time.Second) } } - return fmt.Errorf("%d retries failed", retries) + return errors.New(i18n.G("%d retries failed", retries)) } diff --git a/pkg/config/abra.go b/pkg/config/abra.go index e0a5d452..48781b00 100644 --- a/pkg/config/abra.go +++ b/pkg/config/abra.go @@ -5,6 +5,7 @@ import ( "path" "path/filepath" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "gopkg.in/yaml.v3" ) @@ -16,13 +17,13 @@ func LoadAbraConfig() Abra { wd, _ := os.Getwd() configFile := findAbraConfig(wd) if configFile == "" { - log.Debugf("no config file found") + log.Debug(i18n.G("no config file found")) return Abra{} } data, err := os.ReadFile(configFile) if err != nil { // Do nothing, when an error occurs - log.Debugf("error reading config file: %s", err) + log.Debug(i18n.G("error reading config file: %s", err)) return Abra{} } @@ -30,10 +31,10 @@ func LoadAbraConfig() Abra { err = yaml.Unmarshal(data, &config) if err != nil { // Do nothing, when an error occurs - log.Debugf("error loading config file: %s", err) + log.Debug(i18n.G("error loading config file: %s", err)) return Abra{} } - log.Debugf("config file loaded from: %s", configFile) + log.Debug(i18n.G("config file loaded from: %s", configFile)) config.configPath = filepath.Dir(configFile) return config } @@ -73,26 +74,24 @@ type Abra struct { // 3. use $HOME/.abra when above two options failed func (a Abra) GetAbraDir() string { if dir, exists := os.LookupEnv("ABRA_DIR"); exists && dir != "" { - log.Debug("read abra dir from $ABRA_DIR") + log.Debug(i18n.G("read abra dir from $ABRA_DIR")) return dir } if a.AbraDir != "" { - log.Debug("read abra dir from config file") + log.Debug(i18n.G("read abra dir from config file")) if path.IsAbs(a.AbraDir) { return a.AbraDir } // Make the path absolute return path.Join(a.configPath, a.AbraDir) } - log.Debug("using default abra dir") + log.Debug(i18n.G("using default abra dir")) return os.ExpandEnv("$HOME/.abra") } func (a Abra) GetServersDir() string { return path.Join(a.GetAbraDir(), "servers") } func (a Abra) GetRecipesDir() string { return path.Join(a.GetAbraDir(), "recipes") } func (a Abra) GetLogsDir() string { return path.Join(a.GetAbraDir(), "logs") } -func (a Abra) GetVendorDir() string { return path.Join(a.GetAbraDir(), "vendor") } -func (a Abra) GetBackupDir() string { return path.Join(a.GetAbraDir(), "backups") } func (a Abra) GetCatalogueDir() string { return path.Join(a.GetAbraDir(), "catalogue") } var config = LoadAbraConfig() @@ -102,8 +101,6 @@ var ( SERVERS_DIR = config.GetServersDir() RECIPES_DIR = config.GetRecipesDir() LOGS_DIR = config.GetLogsDir() - VENDOR_DIR = config.GetVendorDir() - BACKUP_DIR = config.GetBackupDir() CATALOGUE_DIR = config.GetCatalogueDir() RECIPES_JSON = path.Join(config.GetCatalogueDir(), "recipes.json") REPOS_BASE_URL = "https://git.coopcloud.tech/coop-cloud" diff --git a/pkg/config/env.go b/pkg/config/env.go index c07a1e98..5550bd60 100644 --- a/pkg/config/env.go +++ b/pkg/config/env.go @@ -1,7 +1,7 @@ package config import ( - "fmt" + "errors" "io/fs" "io/ioutil" "os" @@ -9,6 +9,7 @@ import ( "path/filepath" "strings" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" ) @@ -33,7 +34,7 @@ func GetServers() ([]string, error) { } } - log.Debugf("retrieved %v servers: %s", len(filtered), filtered) + log.Debug(i18n.G("retrieved %v servers: %s", len(filtered), filtered)) return filtered, nil } @@ -46,7 +47,7 @@ func ReadServerNames() ([]string, error) { return nil, err } - log.Debugf("read %s from %s", strings.Join(serverNames, ","), SERVERS_DIR) + log.Debug(i18n.G("read %s from %s", strings.Join(serverNames, ","), SERVERS_DIR)) return serverNames, nil } @@ -70,7 +71,7 @@ func GetAllFilesInDirectory(directory string) ([]fs.FileInfo, error) { realPath, err := filepath.EvalSymlinks(filePath) if err != nil { - log.Warnf("broken symlink in your abra config folders: %s", filePath) + log.Warn(i18n.G("broken symlink in your abra config folders: %s", filePath)) } else { realFile, err := os.Stat(realPath) if err != nil { @@ -94,7 +95,7 @@ func GetAllFoldersInDirectory(directory string) ([]string, error) { return nil, err } if len(files) == 0 { - return nil, fmt.Errorf("directory is empty: %s", directory) + return nil, errors.New(i18n.G("directory is empty: %s", directory)) } for _, file := range files { @@ -103,7 +104,7 @@ func GetAllFoldersInDirectory(directory string) ([]string, error) { filePath := path.Join(directory, file.Name()) realDir, err := filepath.EvalSymlinks(filePath) if err != nil { - log.Warnf("broken symlink in your abra config folders: %s", filePath) + log.Warn(i18n.G("broken symlink in your abra config folders: %s", filePath)) } else if stat, err := os.Stat(realDir); err == nil && stat.IsDir() { // path is a directory folders = append(folders, file.Name()) diff --git a/pkg/container/container.go b/pkg/container/container.go index 16316218..35fe1c67 100644 --- a/pkg/container/container.go +++ b/pkg/container/container.go @@ -2,10 +2,12 @@ package container import ( "context" + "errors" "fmt" "strings" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/AlecAivazis/survey/v2" "github.com/docker/docker/api/types" @@ -26,7 +28,7 @@ func GetContainer(c context.Context, cl *client.Client, filters filters.Args, no if len(containers) == 0 { filter := filters.Get("name")[0] - return types.Container{}, fmt.Errorf("no containers matching the %v filter found?", filter) + return types.Container{}, errors.New(i18n.G("no containers matching the %v filter found?", filter)) } if len(containers) > 1 { @@ -35,19 +37,19 @@ func GetContainer(c context.Context, cl *client.Client, filters filters.Args, no containerName := strings.Join(container.Names, " ") trimmed := strings.TrimPrefix(containerName, "/") created := formatter.HumanDuration(container.Created) - containersRaw = append(containersRaw, fmt.Sprintf("%s (created %v)", trimmed, created)) + containersRaw = append(containersRaw, i18n.G("%s (created %v)", trimmed, created)) } if noInput { - err := fmt.Errorf("expected 1 container but found %v: %s", len(containers), strings.Join(containersRaw, " ")) + err := errors.New(i18n.G("expected 1 container but found %v: %s", len(containers), strings.Join(containersRaw, " "))) return types.Container{}, err } - log.Warnf("ambiguous container list received, prompting for input") + log.Warnf(i18n.G("ambiguous container list received, prompting for input")) var response string prompt := &survey.Select{ - Message: "which container are you looking for?", + Message: i18n.G("which container are you looking for?"), Options: containersRaw, } @@ -64,7 +66,7 @@ func GetContainer(c context.Context, cl *client.Client, filters filters.Args, no } } - log.Fatal("failed to match chosen container") + log.Fatal(i18n.G("failed to match chosen container")) } return containers[0], nil @@ -79,5 +81,6 @@ func GetContainerFromStackAndService(cl *client.Client, stack, service string) ( if err != nil { return types.Container{}, err } + return container, nil } diff --git a/pkg/context/context.go b/pkg/context/context.go index 9842f23c..d36a76b2 100644 --- a/pkg/context/context.go +++ b/pkg/context/context.go @@ -3,6 +3,7 @@ package context import ( "errors" + "coopcloud.tech/abra/pkg/i18n" "github.com/docker/cli/cli/command" dConfig "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/context" @@ -30,7 +31,7 @@ func NewDefaultDockerContextStore() *command.ContextStoreWithDefault { func GetContextEndpoint(ctx contextStore.Metadata) (string, error) { endpointmeta, ok := ctx.Endpoints["docker"].(context.EndpointMetaBase) if !ok { - err := errors.New("context lacks Docker endpoint") + err := errors.New(i18n.G("context lacks Docker endpoint")) return "", err } return endpointmeta.Host, nil diff --git a/pkg/dns/dns.go b/pkg/dns/dns.go index 40497d66..145b8885 100644 --- a/pkg/dns/dns.go +++ b/pkg/dns/dns.go @@ -1,19 +1,21 @@ package dns import ( - "fmt" + "errors" "net" + + "coopcloud.tech/abra/pkg/i18n" ) // EnsureIPv4 ensures that an ipv4 address is set for a domain name func EnsureIPv4(domainName string) (string, error) { ipv4, err := net.ResolveIPAddr("ip4", domainName) if err != nil { - return "", fmt.Errorf("%s: unable to resolve IPv4 address: %s", domainName, err) + return "", errors.New(i18n.G("%s: unable to resolve IPv4 address: %s", domainName, err)) } if ipv4 == nil { - return "", fmt.Errorf("%s: no IPv4 available", domainName) + return "", errors.New(i18n.G("%s: no IPv4 available", domainName)) } return ipv4.String(), nil @@ -33,7 +35,7 @@ func EnsureDomainsResolveSameIPv4(domainName, server string) (string, error) { } if domainIPv4 == "" { - return ipv4, fmt.Errorf("cannot resolve ipv4 for %s?", domainName) + return ipv4, errors.New(i18n.G("cannot resolve ipv4 for %s?", domainName)) } serverIPv4, err := EnsureIPv4(server) @@ -42,12 +44,16 @@ func EnsureDomainsResolveSameIPv4(domainName, server string) (string, error) { } if serverIPv4 == "" { - return ipv4, fmt.Errorf("cannot resolve ipv4 for %s?", server) + return ipv4, errors.New(i18n.G("cannot resolve ipv4 for %s?", server)) } if domainIPv4 != serverIPv4 { - err := "app domain %s (%s) does not appear to resolve to app server %s (%s)?" - return ipv4, fmt.Errorf(err, domainName, domainIPv4, server, serverIPv4) + return ipv4, errors.New( + i18n.G( + "app domain %s (%s) does not appear to resolve to app server %s (%s)?", + domainName, domainIPv4, server, serverIPv4, + ), + ) } return ipv4, nil diff --git a/pkg/envfile/envfile.go b/pkg/envfile/envfile.go index 0e3bd80f..ea3fa495 100644 --- a/pkg/envfile/envfile.go +++ b/pkg/envfile/envfile.go @@ -2,11 +2,12 @@ package envfile import ( "bufio" - "fmt" + "errors" "os" "regexp" "strings" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "git.coopcloud.tech/toolshed/godotenv" ) @@ -38,7 +39,7 @@ func ReadEnvWithModifiers(filePath string) (AppEnv, AppModifiers, error) { return nil, mods, err } - log.Debugf("read %s from %s", envVars, filePath) + log.Debug(i18n.G("read %s from %s", envVars, filePath)) return envVars, mods, nil } @@ -69,16 +70,16 @@ func ReadAbraShEnvVars(abraSh string) (map[string]string, error) { envVarDef := splitVals[len(splitVals)-1] keyVal := strings.Split(envVarDef, "=") if len(keyVal) != 2 { - return envVars, fmt.Errorf("couldn't parse %s", txt) + return envVars, errors.New(i18n.G("couldn't parse %s", txt)) } envVars[keyVal[0]] = keyVal[1] } } if len(envVars) > 0 { - log.Debugf("read %s from %s", envVars, abraSh) + log.Debug(i18n.G("read %s from %s", envVars, abraSh)) } else { - log.Debugf("read 0 env var exports from %s", abraSh) + log.Debug(i18n.G("read 0 env var exports from %s", abraSh)) } return envVars, nil diff --git a/pkg/formatter/formatter.go b/pkg/formatter/formatter.go index 3c4d1400..567a2e6e 100644 --- a/pkg/formatter/formatter.go +++ b/pkg/formatter/formatter.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "coopcloud.tech/abra/pkg/i18n" "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/table" "github.com/docker/go-units" @@ -42,7 +43,7 @@ func RemoveSha(str string) string { func HumanDuration(timestamp int64) string { date := time.Unix(timestamp, 0) now := time.Now().UTC() - return units.HumanDuration(now.Sub(date)) + " ago" + return units.HumanDuration(now.Sub(date)) + i18n.G(" ago") } // CreateTable prepares a table layout for output. @@ -76,7 +77,7 @@ func CreateTable() (*table.Table, error) { func PrintTable(t *table.Table) error { if isAbraCI, ok := os.LookupEnv("ABRA_CI"); ok && isAbraCI == "1" { // NOTE(d1): no width limits for CI testing since we test against outputs - log.Debug("detected ABRA_CI=1") + log.Debug(i18n.G("detected ABRA_CI=1")) fmt.Println(t) return nil } @@ -130,7 +131,7 @@ func CreateOverview(header string, rows [][]string) string { } if len(row) > 2 { - panic("CreateOverview: only accepts rows of len == 2") + panic(i18n.G("CreateOverview: only accepts rows of len == 2")) } lenOffset := 4 @@ -234,7 +235,7 @@ func StripTagMeta(image string) string { } if originalImage != image { - log.Debugf("stripped %s to %s for parsing", originalImage, image) + log.Debug(i18n.G("stripped %s to %s for parsing", originalImage, image)) } return image diff --git a/pkg/git/add.go b/pkg/git/add.go index d5bc3d14..b21e7413 100644 --- a/pkg/git/add.go +++ b/pkg/git/add.go @@ -1,6 +1,7 @@ package git import ( + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/go-git/go-git/v5" ) @@ -18,7 +19,7 @@ func Add(repoPath, path string, dryRun bool) error { } if dryRun { - log.Debugf("dry run: adding %s", path) + log.Debug(i18n.G("dry run: adding %s", path)) } else { worktree.Add(path) } diff --git a/pkg/git/branch.go b/pkg/git/branch.go index 1125bd3a..f02eb55d 100644 --- a/pkg/git/branch.go +++ b/pkg/git/branch.go @@ -1,8 +1,10 @@ package git import ( + "errors" "fmt" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" @@ -63,7 +65,7 @@ func GetDefaultBranch(repo *git.Repository, repoPath string) (plumbing.Reference if !HasBranch(repo, "master") { if !HasBranch(repo, "main") { - return "", fmt.Errorf("failed to select default branch in %s", repoPath) + return "", errors.New(i18n.G("failed to select default branch in %s", repoPath)) } branch = "main" } @@ -90,11 +92,11 @@ func CheckoutDefaultBranch(repo *git.Repository, repoPath string) (plumbing.Refe } if err := worktree.Checkout(checkOutOpts); err != nil { - log.Debugf("failed to check out %s in %s", branch, repoPath) + log.Debug(i18n.G("failed to check out %s in %s", branch, repoPath)) return branch, err } - log.Debugf("successfully checked out %v in %s", branch, repoPath) + log.Debug(i18n.G("successfully checked out %v in %s", branch, repoPath)) return branch, nil } diff --git a/pkg/git/clone.go b/pkg/git/clone.go index 87145f54..89a0ab8a 100644 --- a/pkg/git/clone.go +++ b/pkg/git/clone.go @@ -2,11 +2,13 @@ package git import ( "context" + "errors" "fmt" "os" "os/signal" "strings" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" @@ -44,7 +46,7 @@ func Clone(dir, url string) error { go func() { if _, err := os.Stat(dir); os.IsNotExist(err) { - log.Debugf("git clone: %s", url) + log.Debug(i18n.G("git clone: %s", url)) _, err := git.PlainCloneContext(ctx, dir, false, &git.CloneOptions{ URL: url, @@ -54,16 +56,16 @@ func Clone(dir, url string) error { }) if err != nil && gitCloneIgnoreErr(err) { - log.Debugf("git clone: %s cloned successfully", dir) + log.Debug(i18n.G("git clone: %s cloned successfully", dir)) errCh <- nil } if err := ctx.Err(); err != nil { - errCh <- fmt.Errorf("git clone %s: cancelled due to interrupt", dir) + errCh <- errors.New(i18n.G("git clone %s: cancelled due to interrupt", dir)) } if err != nil { - log.Debug("git clone: main branch failed, attempting master branch") + log.Debug(i18n.G("git clone: main branch failed, attempting master branch")) _, err := git.PlainCloneContext(ctx, dir, false, &git.CloneOptions{ URL: url, @@ -73,7 +75,7 @@ func Clone(dir, url string) error { }) if err != nil && gitCloneIgnoreErr(err) { - log.Debugf("git clone: %s cloned successfully", dir) + log.Debug(i18n.G("git clone: %s cloned successfully", dir)) errCh <- nil } @@ -82,9 +84,9 @@ func Clone(dir, url string) error { } } - log.Debugf("git clone: %s cloned successfully", dir) + log.Debug(i18n.G("git clone: %s cloned successfully", dir)) } else { - log.Debugf("git clone: %s already exists", dir) + log.Debug(i18n.G("git clone: %s already exists", dir)) } errCh <- nil @@ -95,9 +97,9 @@ func Clone(dir, url string) error { cancelCtx() fmt.Println() // NOTE(d1): newline after ^C if err := os.RemoveAll(dir); err != nil { - return fmt.Errorf("unable to clean up git clone of %s: %s", dir, err) + return errors.New(i18n.G("unable to clean up git clone of %s: %s", dir, err)) } - return fmt.Errorf("git clone %s: cancelled due to interrupt", dir) + return errors.New(i18n.G("git clone %s: cancelled due to interrupt", dir)) case err := <-errCh: return err } diff --git a/pkg/git/commit.go b/pkg/git/commit.go index 7775e518..38a22024 100644 --- a/pkg/git/commit.go +++ b/pkg/git/commit.go @@ -1,8 +1,9 @@ package git import ( - "fmt" + "errors" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/go-git/go-git/v5" ) @@ -10,7 +11,7 @@ import ( // Commit runs a git commit func Commit(repoPath, commitMessage string, dryRun bool) error { if commitMessage == "" { - return fmt.Errorf("no commit message specified?") + return errors.New(i18n.G("no commit message specified?")) } commitRepo, err := git.PlainOpen(repoPath) @@ -38,9 +39,9 @@ func Commit(repoPath, commitMessage string, dryRun bool) error { if err != nil { return err } - log.Debug("git changes commited") + log.Debug(i18n.G("git changes commited")) } else { - log.Debug("dry run: no changes commited") + log.Debug(i18n.G("dry run: no changes commited")) } return nil diff --git a/pkg/git/common.go b/pkg/git/common.go index ff4eb7d7..e41f2124 100644 --- a/pkg/git/common.go +++ b/pkg/git/common.go @@ -1,14 +1,16 @@ package git import ( - "fmt" + "errors" "os" + + "coopcloud.tech/abra/pkg/i18n" ) // EnsureGitRepo ensures a git repo .git folder exists func EnsureGitRepo(repoPath string) error { if _, err := os.Stat(repoPath); os.IsNotExist(err) { - return fmt.Errorf("no .git directory in %s?", repoPath) + return errors.New(i18n.G("no .git directory in %s?", repoPath)) } return nil } diff --git a/pkg/git/diff.go b/pkg/git/diff.go index 6242d654..0a41bbef 100644 --- a/pkg/git/diff.go +++ b/pkg/git/diff.go @@ -4,6 +4,7 @@ import ( "fmt" "os/exec" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" ) @@ -26,7 +27,7 @@ func getGitDiffArgs(repoPath string) []string { // skips if it cannot find the command on the system. func DiffUnstaged(path string) error { if _, err := exec.LookPath("git"); err != nil { - log.Warnf("unable to locate git command, cannot output diff") + log.Warnf(i18n.G("unable to locate git command, cannot output diff")) return nil } diff --git a/pkg/git/init.go b/pkg/git/init.go index 5adf37b1..06acf237 100644 --- a/pkg/git/init.go +++ b/pkg/git/init.go @@ -1,8 +1,9 @@ package git import ( - "fmt" + "errors" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" @@ -13,28 +14,28 @@ import ( func Init(repoPath string, commit bool, gitName, gitEmail string) error { repo, err := git.PlainInit(repoPath, false) if err != nil { - return fmt.Errorf("git init: %s", err) + return errors.New(i18n.G("git init: %s", err)) } if err = SwitchToMain(repo); err != nil { - return fmt.Errorf("git branch rename: %s", err) + return errors.New(i18n.G("git branch rename: %s", err)) } - log.Debugf("initialised new git repo in %s", repoPath) + log.Debug(i18n.G("initialised new git repo in %s", repoPath)) if commit { commitRepo, err := git.PlainOpen(repoPath) if err != nil { - return fmt.Errorf("git open: %s", err) + return errors.New(i18n.G("git open: %s", err)) } commitWorktree, err := commitRepo.Worktree() if err != nil { - return fmt.Errorf("git worktree: %s", err) + return errors.New(i18n.G("git worktree: %s", err)) } if err := commitWorktree.AddWithOptions(&git.AddOptions{All: true}); err != nil { - return fmt.Errorf("git add: %s", err) + return errors.New(i18n.G("git add: %s", err)) } var author *object.Signature @@ -43,10 +44,10 @@ func Init(repoPath string, commit bool, gitName, gitEmail string) error { } if _, err = commitWorktree.Commit("init", &git.CommitOptions{Author: author}); err != nil { - return fmt.Errorf("git commit: %s", err) + return errors.New(i18n.G("git commit: %s", err)) } - log.Debugf("init committed all files for new git repo in %s", repoPath) + log.Debug(i18n.G("init committed all files for new git repo in %s", repoPath)) } return nil @@ -56,20 +57,20 @@ func Init(repoPath string, commit bool, gitName, gitEmail string) error { func SwitchToMain(repo *git.Repository) error { ref := plumbing.NewSymbolicReference(plumbing.HEAD, plumbing.ReferenceName("refs/heads/main")) if err := repo.Storer.SetReference(ref); err != nil { - return fmt.Errorf("set reference: %s", err) + return errors.New(i18n.G("set reference: %s", err)) } cfg, err := repo.Config() if err != nil { - return fmt.Errorf("repo config: %s", err) + return errors.New(i18n.G("repo config: %s", err)) } cfg.Init.DefaultBranch = "main" if err := repo.SetConfig(cfg); err != nil { - return fmt.Errorf("repo set config: %s", err) + return errors.New(i18n.G("repo set config: %s", err)) } - log.Debug("set 'main' as the default branch") + log.Debug(i18n.G("set 'main' as the default branch")) return nil } diff --git a/pkg/git/push.go b/pkg/git/push.go index 9d279e65..138e64e4 100644 --- a/pkg/git/push.go +++ b/pkg/git/push.go @@ -1,6 +1,7 @@ package git import ( + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" @@ -9,7 +10,7 @@ import ( // Push pushes the latest changes & optionally tags to the default remote func Push(repoDir string, remote string, tags bool, dryRun bool) error { if dryRun { - log.Debugf("dry run: no git changes pushed in %s", repoDir) + log.Debug(i18n.G("dry run: no git changes pushed in %s", repoDir)) return nil } @@ -27,7 +28,7 @@ func Push(repoDir string, remote string, tags bool, dryRun bool) error { return err } - log.Debugf("git changes pushed") + log.Debug(i18n.G("git changes pushed")) if tags { opts.RefSpecs = append(opts.RefSpecs, config.RefSpec("+refs/tags/*:refs/tags/*")) @@ -36,7 +37,7 @@ func Push(repoDir string, remote string, tags bool, dryRun bool) error { return err } - log.Debugf("git tags pushed") + log.Debug(i18n.G("git tags pushed")) } return nil diff --git a/pkg/git/read.go b/pkg/git/read.go index b2991357..0a54d4fc 100644 --- a/pkg/git/read.go +++ b/pkg/git/read.go @@ -2,13 +2,13 @@ package git import ( "errors" - "fmt" "io/ioutil" "os" "os/user" "path/filepath" "strings" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/go-git/go-git/v5" gitConfigPkg "github.com/go-git/go-git/v5/config" @@ -23,12 +23,12 @@ func IsClean(repoPath string) (bool, error) { return false, git.ErrRepositoryNotExists } - return false, fmt.Errorf("unable to open %s: %s", repoPath, err) + return false, errors.New(i18n.G("unable to open %s: %s", repoPath, err)) } worktree, err := repo.Worktree() if err != nil { - return false, fmt.Errorf("unable to open worktree of %s: %s", repoPath, err) + return false, errors.New(i18n.G("unable to open worktree of %s: %s", repoPath, err)) } patterns, err := GetExcludesFiles() @@ -42,14 +42,14 @@ func IsClean(repoPath string) (bool, error) { status, err := worktree.Status() if err != nil { - return false, fmt.Errorf("unable to query status of %s: %s", repoPath, err) + return false, errors.New(i18n.G("unable to query status of %s: %s", repoPath, err)) } if status.String() != "" { noNewline := strings.TrimSuffix(status.String(), "\n") - log.Debugf("git status: %s: %s", repoPath, noNewline) + log.Debug(i18n.G("git status: %s: %s", repoPath, noNewline)) } else { - log.Debugf("git status: %s: clean", repoPath) + log.Debug(i18n.G("git status: %s: clean", repoPath)) } return status.IsClean(), nil @@ -85,7 +85,7 @@ func parseGitConfig() (*gitConfigPkg.Config, error) { globalGitConfig := filepath.Join(usr.HomeDir, ".gitconfig") if _, err := os.Stat(globalGitConfig); err != nil { if os.IsNotExist(err) { - log.Debugf("no %s exists, not reading any global gitignore config", globalGitConfig) + log.Debug(i18n.G("no %s exists, not reading any global gitignore config", globalGitConfig)) return cfg, nil } return cfg, err @@ -127,7 +127,7 @@ func parseExcludesFile(excludesfile string) ([]gitignore.Pattern, error) { if _, err := os.Stat(excludesfile); err != nil { if os.IsNotExist(err) { - log.Debugf("no %s exists, skipping reading gitignore paths", excludesfile) + log.Debug(i18n.G("no %s exists, skipping reading gitignore paths", excludesfile)) return ps, nil } return ps, err @@ -146,7 +146,7 @@ func parseExcludesFile(excludesfile string) ([]gitignore.Pattern, error) { } } - log.Debugf("read global ignore paths: %s", strings.Join(pathsRaw, " ")) + log.Debug(i18n.G("read global ignore paths: %s", strings.Join(pathsRaw, " "))) return ps, nil } diff --git a/pkg/git/remote.go b/pkg/git/remote.go index 5d7d7af4..250e1891 100644 --- a/pkg/git/remote.go +++ b/pkg/git/remote.go @@ -3,6 +3,7 @@ package git import ( "strings" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" @@ -11,7 +12,7 @@ import ( // CreateRemote creates a new git remote in a repository func CreateRemote(repo *git.Repository, name, url string, dryRun bool) error { if dryRun { - log.Debugf("dry run: remote %s (%s) not created", name, url) + log.Debug(i18n.G("dry run: remote %s (%s) not created", name, url)) return nil } diff --git a/pkg/i18n/i18n.go b/pkg/i18n/i18n.go new file mode 100644 index 00000000..43fefbca --- /dev/null +++ b/pkg/i18n/i18n.go @@ -0,0 +1,62 @@ +package i18n + +import ( + "embed" + "fmt" + "os" + "path/filepath" + "slices" + "strings" + + "coopcloud.tech/abra/pkg/log" + "github.com/leonelquinteros/gotext" +) + +//go:embed locales/*.mo +var assetFS embed.FS + +var ( + DefaultLocale = "en" + Locale = DefaultLocale +) + +func LoadLocale() *gotext.Mo { + entries, err := assetFS.ReadDir("locales") + if err != nil { + log.Fatalf("i18n: unable to read embedded locales directory: %s", err) + } + + var linguas []string + for _, entry := range entries { + if filepath.Ext(entry.Name()) == ".mo" { + fname := entry.Name() + fnameWithoutExt := strings.TrimSuffix(fname, filepath.Ext(fname)) + linguas = append(linguas, fnameWithoutExt) + } + } + + locale := os.Getenv("LANG") + if locale != "" { + if slices.Contains(linguas, locale) { + Locale = locale + } else { + log.Debugf("unsupported language: %s (want: %s)", locale, strings.Join(linguas, " ")) + } + } + + if Locale == DefaultLocale { + return gotext.NewMo() + } + + b, err := assetFS.ReadFile(fmt.Sprintf("locales/%s.mo", Locale)) + if err != nil { + log.Fatalf("i18n: %s", err) + } + + mo := gotext.NewMo() + mo.Parse(b) + + return mo +} + +var G = LoadLocale().Get diff --git a/pkg/i18n/locales/abra.pot b/pkg/i18n/locales/abra.pot new file mode 100644 index 00000000..6dc0e33f --- /dev/null +++ b/pkg/i18n/locales/abra.pot @@ -0,0 +1,4677 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "Project-Id-Version: \n" + "Report-Msgid-Bugs-To: EMAIL\n" + "POT-Creation-Date: 2025-08-23 17:54+0200\n" + "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" + "Last-Translator: FULL NAME \n" + "Language-Team: LANGUAGE \n" + "Language: \n" + "MIME-Version: 1.0\n" + "Content-Type: text/plain; charset=UTF-8\n" + "Content-Transfer-Encoding: 8bit\n" + +#: ./cli/app/cp.go:32 +msgid " # copy myfile.txt to the root of the app service\n" + " abra app cp 1312.net myfile.txt app:/\n" + "\n" + " # copy that file back to your current working directory locally\n" + " abra app cp 1312.net app:/myfile.txt ./" +msgstr "" + +#: ./cli/app/volume.go:88 +msgid " # delete volumes interactively\n" + " abra app volume rm 1312.net\n" + "\n" + " # delete specific volume\n" + " abra app volume rm 1312.net my_volume" +msgstr "" + +#: ./cli/recipe/fetch.go:23 +msgid " # fetch from recipe catalogue\n" + " abra recipe fetch gitea\n" + "\n" + " # fetch from remote recipe\n" + " abra recipe fetch git.foo.org/recipes/myrecipe\n" + "\n" + " # fetch with ssh remote for hacking\n" + " abra recipe fetch gitea --ssh" +msgstr "" + +#: ./cli/run.go:84 +msgid " # generate the man pages into /usr/local/share/man/man1\n" + " abra_path=$(which abra) # pass abra absolute path to sudo below\n" + " sudo $abra_path man\n" + " sudo mandb\n" + "\n" + " # read the man pages\n" + " man abra\n" + " man abra-app-deploy" +msgstr "" + +#: ./cli/app/secret.go:154 +msgid " # insert regular secret\n" + " abra app secret insert 1312.net my_secret v1 mySuperSecret\n" + "\n" + " # insert secret as file\n" + " abra app secret insert 1312.net my_secret v1 secret.txt -f" +msgstr "" + +#: ./cli/app/list.go:49 +msgid " # list apps of all servers without live status\n" + " abra app ls\n" + "\n" + " # list apps of a specific server with live status\n" + " abra app ls -s 1312.net -S\n" + "\n" + " # list apps of all servers which match a specific recipe\n" + " abra app ls -r gitea" +msgstr "" + +#: ./cli/app/cmd.go:35 +msgid " # pass args/flags without \"--\"\n" + " abra app cmd 1312.net app my_cmd_arg foo --user bar\n" + "\n" + " # pass args/flags with \"--\"\n" + " abra app cmd 1312.net app my_cmd_args --user bar -- foo -vvv\n" + "\n" + " # drop the [service] arg if using \"--local/-l\"\n" + " abra app cmd 1312.net my_cmd --local" +msgstr "" + +#: ./cli/app/restart.go:29 +msgid " # restart a single app service\n" + " abra app restart 1312.net app\n" + "\n" + " # restart all app services\n" + " abra app restart 1312.net -a" +msgstr "" + +#: ./cli/app/run.go:24 +msgid " # run with args/flags\n" + " abra app run 1312.net app -- ls -lha\n" + "\n" + " # run without args/flags\n" + " abra app run 1312.net app bash --user nobody\n" + "\n" + " # run with both kinds of args/flags \n" + " abra app run 1312.net app --user nobody -- ls -lha" +msgstr "" + +#: ./cli/app/deploy.go:36 +msgid " # standard deployment\n" + " abra app deploy 1312.net\n" + "\n" + " # chaos deployment\n" + " abra app deploy 1312.net --chaos\n" + " \n" + " # deploy specific version\n" + " abra app deploy 1312.net 2.0.0+1.2.3\n" + "\n" + " # deploy a specific git hash\n" + " abra app deploy 1312.net 886db76d" +msgstr "" + +#: ./cli/app/env.go:18 +msgid " abra app env 1312.net" +msgstr "" + +#: ./cli/app/labels.go:27 +msgid " abra app labels 1312.net" +msgstr "" + +#: ./cli/app/remove.go:38 +msgid " abra app remove 1312.net" +msgstr "" + +#: ./cli/app/secret.go:267 +msgid " abra app secret rm 1312.net oauth_key" +msgstr "" + +#: ./cli/app/config.go:19 +msgid " abra config 1312.net" +msgstr "" + +#: ./cli/server/add.go:40 +msgid " abra server add 1312.net" +msgstr "" + +#: ./cli/upgrade.go:26 +msgid " abra upgrade --rc" +msgstr "" + +#: ./cli/app/rollback.go:42 +msgid " # standard rollback\n" + " abra app rollback 1312.net\n" + "\n" + " # rollback to specific version\n" + " abra app rollback 1312.net 2.0.0+1.2.3" +msgstr "" + +#: ./pkg/formatter/formatter.go:46 +msgid " ago" +msgstr "" + +#: ./cli/app/remove.go:92 +#, c-format +msgid "%d config(s) removed successfully" +msgstr "" + +#: ./pkg/client/volumes.go:62 +#, c-format +msgid "%d retries failed" +msgstr "" + +#: ./cli/app/remove.go:139 +#, c-format +msgid "%d volume(s) removed successfully" +msgstr "" + +#: ./cli/app/volume.go:182 +#, c-format +msgid "%d volumes removed successfully" +msgstr "" + +#: ./cli/updater/updater.go:231 +#, c-format +msgid "%s (%s) can be upgraded from version %s to %s" +msgstr "" + +#: ./pkg/recipe/git.go:191 +#, c-format +msgid "%s (%s) has locally unstaged changes?" +msgstr "" + +#: ./pkg/service/service.go:103 ./pkg/container/container.go:40 +#, c-format +msgid "%s (created %v)" +msgstr "" + +#: ./cli/internal/deploy.go:86 +#, c-format +msgid "%s OVERVIEW" +msgstr "" + +#: ./cli/server/add.go:120 ./pkg/secret/secret.go:223 ./pkg/secret/secret.go:243 ./pkg/server/server.go:21 +#, c-format +msgid "%s already exists" +msgstr "" + +#: ./pkg/app/app.go:380 +#, c-format +msgid "%s already exists?" +msgstr "" + +#: ./cli/app/new.go:199 +#, c-format +msgid "%s created (version: %s)" +msgstr "" + +#: ./cli/recipe/upgrade.go:330 ./cli/recipe/sync.go:211 ./cli/recipe/release.go:121 +#, c-format +msgid "%s currently has these unstaged changes 👇" +msgstr "" + +#: ./cli/internal/command.go:70 +#, c-format +msgid "%s does not exist for %s, use /bin/sh as fallback" +msgstr "" + +#: ./cli/app/cmd.go:108 ./cli/internal/deploy.go:151 +#, c-format +msgid "%s does not exist for %s?" +msgstr "" + +#: ./cli/app/cmd.go:137 +#, c-format +msgid "%s does not exist locally, use /bin/sh as fallback" +msgstr "" + +#: ./cli/app/secret.go:75 +#, c-format +msgid "%s doesn't exist in the env config?" +msgstr "" + +#: ./cli/app/secret.go:362 +#, c-format +msgid "%s doesn't exist on server?" +msgstr "" + +#: ./cli/internal/command.go:110 +#, c-format +msgid "%s doesn't have a %s function" +msgstr "" + +#: ./cli/app/restart.go:132 +#, c-format +msgid "%s has been scaled to 0" +msgstr "" + +#: ./cli/app/restart.go:143 +#, c-format +msgid "%s has been scaled to 1" +msgstr "" + +#: ./pkg/catalogue/catalogue.go:40 +#, c-format +msgid "%s has locally unstaged changes? please commit/remove your changes before proceeding" +msgstr "" + +#: ./cli/internal/recipe.go:112 +#, c-format +msgid "%s has no main 'app' service?" +msgstr "" + +#: ./cli/recipe/version.go:53 +#, c-format +msgid "%s has no published versions?" +msgstr "" + +#: ./cli/app/new.go:284 +#, c-format +msgid "%s has no secrets to generate, skipping..." +msgstr "" + +#: ./pkg/recipe/compose.go:107 +#, c-format +msgid "%s has no version label? try running \"abra recipe sync %s\" first?" +msgstr "" + +#: ./pkg/secret/pass.go:29 +#, c-format +msgid "%s inserted into pass store" +msgstr "" + +#: ./cli/app/deploy.go:96 +#, c-format +msgid "%s is already deployed" +msgstr "" + +#: ./cli/recipe/fetch.go:55 +#, c-format +msgid "%s is already fetched" +msgstr "" + +#: ./pkg/recipe/compose.go:233 +#, c-format +msgid "%s is already set, nothing to do?" +msgstr "" + +#: ./pkg/recipe/compose.go:72 +#, c-format +msgid "%s is missing a compose.yml or compose.*.yml file?" +msgstr "" + +#: ./pkg/app/app.go:267 +#, c-format +msgid "%s is missing the TYPE env var?" +msgstr "" + +#: ./cli/app/rollback.go:280 ./cli/app/rollback.go:284 +#, c-format +msgid "%s is not a downgrade for %s?" +msgstr "" + +#: ./cli/app/upgrade.go:402 ./cli/app/upgrade.go:406 +#, c-format +msgid "%s is not an upgrade for %s?" +msgstr "" + +#: ./cli/app/ps.go:56 ./cli/app/upgrade.go:423 ./cli/app/undeploy.go:59 ./cli/app/services.go:49 ./cli/app/restart.go:93 ./cli/app/logs.go:58 ./cli/updater/updater.go:247 +#, c-format +msgid "%s is not deployed?" +msgstr "" + +#: ./cli/server/remove.go:43 +#, c-format +msgid "%s is now lost in time, like tears in rain" +msgstr "" + +#: ./cli/app/remove.go:73 ./cli/app/volume.go:120 +#, c-format +msgid "%s is still deployed. Run \"abra app undeploy %s\"" +msgstr "" + +#: ./cli/app/upgrade.go:208 ./cli/app/deploy.go:166 +#, c-format +msgid "%s missing from %s.env" +msgstr "" + +#: ./cli/recipe/upgrade.go:145 +#, c-format +msgid "%s not considered semver-like" +msgstr "" + +#: ./pkg/secret/secret.go:130 +#, c-format +msgid "%s not enabled in recipe config, skipping" +msgstr "" + +#: ./cli/recipe/new.go:50 +#, c-format +msgid "%s recipe directory already exists?" +msgstr "" + +#: ./pkg/recipe/files.go:36 +#, c-format +msgid "%s release notes:" +msgstr "" + +#: ./pkg/secret/pass.go:51 +#, c-format +msgid "%s removed from pass store" +msgstr "" + +#: ./cli/app/new.go:135 +#, c-format +msgid "%s sanitised as %s for new app" +msgstr "" + +#: ./pkg/recipe/git.go:434 +#, c-format +msgid "%s service is missing image tag?" +msgstr "" + +#: ./cli/app/restart.go:144 +#, c-format +msgid "%s service successfully restarted" +msgstr "" + +#: ./cli/server/add.go:111 +#, c-format +msgid "%s successfully added" +msgstr "" + +#: ./cli/app/secret.go:230 +#, c-format +msgid "%s successfully stored on server" +msgstr "" + +#: ./cli/recipe/new.go:53 +#, c-format +msgid "%s/example.git" +msgstr "" + +#: ./pkg/ui/deploy.go:341 +#, c-format +msgid "%s: %s (retries: %v, healthcheck: %s)" +msgstr "" + +#: ./pkg/app/app.go:516 +#, c-format +msgid "%s: %s: %s" +msgstr "" + +#: ./pkg/recipe/recipe.go:237 +#, c-format +msgid "%s: attempt recipe metadata parse" +msgstr "" + +#: ./pkg/recipe/recipe.go:368 +#, c-format +msgid "%s: end marker %s not found" +msgstr "" + +#: ./pkg/recipe/recipe.go:325 +#, c-format +msgid "%s: image meta has incorrect format: %s" +msgstr "" + +#: ./pkg/recipe/recipe.go:330 +#, c-format +msgid "%s: image meta is empty?" +msgstr "" + +#: ./pkg/recipe/recipe.go:361 +#, c-format +msgid "%s: marker string %s not found" +msgstr "" + +#: ./pkg/dns/dns.go:18 +#, c-format +msgid "%s: no IPv4 available" +msgstr "" + +#: ./pkg/lint/recipe.go:52 +#, c-format +msgid "%s: skip condition: %s" +msgstr "" + +#: ./pkg/dns/dns.go:14 +#, c-format +msgid "%s: unable to resolve IPv4 address: %s" +msgstr "" + +#: ./pkg/client/volumes.go:58 +#, c-format +msgid "%s: waiting %d seconds before next retry" +msgstr "" + +#: ./cli/app/upgrade.go:397 +#, c-format +msgid "'%s' is not a known version" +msgstr "" + +#: ./cli/app/upgrade.go:392 ./cli/app/rollback.go:275 +#, c-format +msgid "'%s' is not a known version for %s" +msgstr "" + +#: ./cli/app/volume.go:162 +msgid "'x' indicates selected, enter / return to confirm, ctrl-c to exit, vim mode is enabled" +msgstr "" + +#: ./cli/app/cmd.go:119 +#, c-format +msgid "--local detected, running %s on local work station" +msgstr "" + +#: ./pkg/lint/recipe.go:133 +msgid ".env.sample provided" +msgstr "" + +#: ./cli/recipe/new.go:37 +msgid "A community managed recipe template is used." +msgstr "" + +#: ./cli/recipe/recipe.go:13 +msgid "A recipe is a blueprint for an app.\n" + "\n" + "It is a bunch of config files which describe how to deploy and maintain an app.\n" + "Recipes are maintained by the Co-op Cloud community and you can use Abra to\n" + "read them, deploy them and create apps for you.\n" + "\n" + "Anyone who uses a recipe can become a maintainer. Maintainers typically make\n" + "sure the recipe is in good working order and the config upgraded in a timely\n" + "manner." +msgstr "" + +#: ./cli/app/remove.go:50 +#, c-format +msgid "ALERTA ALERTA: deleting %s data and config (local/remote)" +msgstr "" + +#: ./cli/app/list.go:223 +msgid "AUTOUPDATE" +msgstr "" + +#: ./cli/server/add.go:23 +msgid "Add a new server" +msgstr "" + +#: ./cli/server/add.go:24 +msgid "Add a new server to your configuration so that it can be managed by Abra.\n" + "\n" + "Abra relies on the standard SSH command-line and ~/.ssh/config for client\n" + "connection details. You must configure an entry per-host in your ~/.ssh/config\n" + "for each server:\n" + "\n" + " Host 1312.net 1312\n" + " Hostname 1312.net\n" + " User antifa\n" + " Port 12345\n" + " IdentityFile ~/.ssh/antifa@somewhere\n" + "\n" + "If \"--local\" is passed, then Abra assumes that the current local server is\n" + "intended as the target server. This is useful when you want to have your entire\n" + "Co-op Cloud config located on the server itself, and not on your local\n" + "developer machine. The domain is then set to \"default\"." +msgstr "" + +#: ./cli/app/labels.go:26 +msgid "Both local recipe and live deployment labels are shown." +msgstr "" + +#: ./cli/app/secret.go:476 ./cli/app/secret.go:516 ./cli/app/secret.go:540 ./cli/app/secret.go:548 ./cli/app/restore.go:132 ./cli/app/ps.go:207 ./cli/app/cmd.go:273 ./cli/app/new.go:380 ./cli/app/check.go:88 ./cli/app/restart.go:155 ./cli/app/deploy.go:327 ./cli/app/cp.go:379 ./cli/app/backup.go:288 ./cli/app/backup.go:304 ./cli/app/labels.go:136 ./cli/catalogue/catalogue.go:272 ./cli/recipe/lint.go:129 ./cli/updater/updater.go:538 +msgid "C" +msgstr "" + +#: ./cli/app/ps.go:183 ./cli/app/list.go:220 +msgid "CHAOS" +msgstr "" + +#: ./cli/internal/deploy.go:120 +msgid "CHAOS DEPLOY" +msgstr "" + +#: ./pkg/recipe/compose.go:47 +#, c-format +msgid "COMPOSE_FILE (=\"%s\") parsing failed?" +msgstr "" + +#: ./pkg/recipe/compose.go:58 +#, c-format +msgid "COMPOSE_FILE detected (%s), loading %s" +msgstr "" + +#: ./pkg/recipe/compose.go:38 +#, c-format +msgid "COMPOSE_FILE detected, loading %s" +msgstr "" + +#: ./cli/internal/deploy.go:78 +msgid "CONFIG" +msgstr "" + +#: ./cli/app/secret.go:394 +msgid "CREATED ON SERVER" +msgstr "" + +#: ./cli/internal/deploy.go:80 +msgid "CURRENT DEPLOYMENT" +msgstr "" + +#: ./cli/app/ps.go:30 +msgid "Check app deployment status" +msgstr "" + +#: ./cli/updater/updater.go:37 +msgid "Check for available upgrades" +msgstr "" + +#: ./cli/recipe/fetch.go:20 +msgid "Clone recipe(s) locally" +msgstr "" + +#: ./cli/app/check.go:20 +msgid "Compare env vars in both the app \".env\" and recipe \".env.sample\" file.\n" + "\n" + "The goal is to ensure that recipe \".env.sample\" env vars are defined in your\n" + "app \".env\" file. Only env var definitions in the \".env.sample\" which are\n" + "uncommented, e.g. \"FOO=bar\" are checked. If an app \".env\" file does not include\n" + "these env vars, then \"check\" will complain.\n" + "\n" + "Recipe maintainers may or may not provide defaults for env vars within their\n" + "recipes regardless of commenting or not (e.g. through the use of\n" + "${FOO:} syntax). \"check\" does not confirm or deny this for you." +msgstr "" + +#: ./cli/app/cp.go:31 +msgid "Copy files to/from a deployed app service" +msgstr "" + +#: ./cli/app/new.go:49 +msgid "Create a new app" +msgstr "" + +#: ./cli/recipe/new.go:36 +msgid "Create a new recipe" +msgstr "" + +#: ./cli/app/backup.go:136 +msgid "Create a new snapshot" +msgstr "" + +#: ./cli/recipe/release.go:29 +msgid "Create a new version of a recipe.\n" + "\n" + "These versions are then published on the Co-op Cloud recipe catalogue. These\n" + "versions take the following form:\n" + "\n" + " a.b.c+x.y.z\n" + "\n" + "Where the \"a.b.c\" part is a semantic version determined by the maintainer. The\n" + "\"x.y.z\" part is the image tag of the recipe \"app\" service (the main container\n" + "which contains the software to be used, by naming convention).\n" + "\n" + "We maintain a semantic versioning scheme (\"a.b.c\") alongside the recipe\n" + "versioning scheme (\"x.y.z\") in order to maximise the chances that the nature of\n" + "recipe updates are properly communicated. I.e. developers of an app might\n" + "publish a minor version but that might lead to changes in the recipe which are\n" + "major and therefore require intervention while doing the upgrade work.\n" + "\n" + "Publish your new release to git.coopcloud.tech with \"--publish/-p\". This\n" + "requires that you have permission to git push to these repositories and have\n" + "your SSH keys configured on your account." +msgstr "" + +#: ./pkg/formatter/formatter.go:134 +msgid "CreateOverview: only accepts rows of len == 2" +msgstr "" + +#: ./cli/app/new.go:24 +msgid "Creates a new app from a default recipe.\n" + "\n" + "This new app configuration is stored in your $ABRA_DIR directory under the\n" + "appropriate server.\n" + "\n" + "This command does not deploy your app for you. You will need to run \"abra app\n" + "deploy \" to do so.\n" + "\n" + "You can see what recipes are available (i.e. values for the [recipe] argument)\n" + "by running \"abra recipe ls\".\n" + "\n" + "Recipe commit hashes are supported values for \"[version]\".\n" + "\n" + "Passing the \"--secrets/-S\" flag will automatically generate secrets for your\n" + "app and store them encrypted at rest on the chosen target server. These\n" + "generated secrets are only visible at generation time, so please take care to\n" + "store them somewhere safe.\n" + "\n" + "You can use the \"--pass/-P\" to store these generated passwords locally in a\n" + "pass store (see passwordstore.org for more). The pass command must be available\n" + "on your $PATH." +msgstr "" + +#: ./cli/app/upgrade.go:443 ./cli/app/new.go:356 ./cli/app/rollback.go:332 ./cli/app/deploy.go:343 +msgid "D" +msgstr "" + +#: ./cli/internal/deploy.go:133 ./cli/internal/deploy.go:137 +msgid "DEPLOY" +msgstr "" + +#: ./cli/app/labels.go:53 +msgid "DEPLOYED LABELS" +msgstr "" + +#: ./cli/app/list.go:216 ./cli/internal/deploy.go:75 +msgid "DOMAIN" +msgstr "" + +#: ./cli/internal/deploy.go:142 +msgid "DOWNGRADE" +msgstr "" + +#: ./cli/app/deploy.go:30 +msgid "Deploy an app" +msgstr "" + +#: ./cli/app/deploy.go:31 +msgid "Deploy an app.\n" + "\n" + "This command supports chaos operations. Use \"--chaos/-C\" to deploy your recipe\n" + "checkout as-is. Recipe commit hashes are also supported as values for\n" + "\"[version]\". Please note, \"upgrade\"/\"rollback\" do not support chaos operations." +msgstr "" + +#: ./cli/app/services.go:23 +msgid "Display all services of an app" +msgstr "" + +#: ./cli/app/backup.go:67 +msgid "Download a snapshot" +msgstr "" + +#: ./cli/app/backup.go:68 +msgid "Downloads a backup.tar.gz to the current working directory.\n" + "\n" + "\"--volumes/-v\" includes data contained in volumes alongide paths specified in\n" + "\"backupbot.backup.path\" labels." +msgstr "" + +#: ./cli/app/env.go:41 +msgid "ENV OVERVIEW" +msgstr "" + +#: ./cli/internal/deploy.go:81 +msgid "ENV VERSION" +msgstr "" + +#: ./cli/app/config.go:18 +msgid "Edit app config" +msgstr "" + +#: ./cli/app/check.go:19 +msgid "Ensure an app is well configured" +msgstr "" + +#: ./pkg/upstream/container/exec.go:113 +#, c-format +msgid "Error hijack: %s" +msgstr "" + +#: ./pkg/upstream/container/exec.go:108 +msgid "Error monitoring TTY size:" +msgstr "" + +#: ./cli/app/secret.go:394 +msgid "GENERATED NAME" +msgstr "" + +#: ./cli/catalogue/catalogue.go:27 +msgid "Generate a new copy of the recipe catalogue.\n" + "\n" + "N.B. this command **will** wipe local unstaged changes from your local recipes\n" + "if present. \"--chaos/-C\" on this command refers to the catalogue repository\n" + "(\"$ABRA_DIR/catalogue\") and not the recipes. Please take care not to lose your\n" + "changes.\n" + "\n" + "It is possible to generate new metadata for a single recipe by passing\n" + "[recipe]. The existing local catalogue will be updated, not overwritten.\n" + "\n" + "It is quite easy to get rate limited by Docker Hub when running this command.\n" + "If you have a Hub account you can \"docker login\" and Abra will automatically\n" + "use those details.\n" + "\n" + "Push your new release to git.coopcloud.tech with \"--publish/-p\". This requires\n" + "that you have permission to git push to these repositories and have your SSH\n" + "keys configured on your account." +msgstr "" + +#: ./cli/app/list.go:46 +msgid "Generate a report of all managed apps.\n" + "\n" + "Use \"--status/-S\" flag to query all servers for the live deployment status." +msgstr "" + +#: ./cli/app/new.go:290 +msgid "Generate app secrets?" +msgstr "" + +#: ./cli/complete.go:12 +msgid "Generate autocompletion script" +msgstr "" + +#: ./cli/recipe/sync.go:23 +msgid "Generate labels for the main recipe service.\n" + "\n" + "By convention, the service named \"app\" using the following format:\n" + "\n" + " coop-cloud.${STACK_NAME}.version=\n" + "\n" + "Where [version] can be specifed on the command-line or Abra can attempt to\n" + "auto-generate it for you. The configuration will be updated on the\n" + "local file system." +msgstr "" + +#: ./cli/run.go:83 +msgid "Generate manpage" +msgstr "" + +#: ./cli/app/secret.go:26 +msgid "Generate secrets" +msgstr "" + +#: ./cli/catalogue/catalogue.go:26 +msgid "Generate the recipe catalogue" +msgstr "" + +#: ./cli/recipe/new.go:118 +msgid "Git (user) name to do commits with" +msgstr "" + +#: ./cli/recipe/new.go:126 +msgid "Git email name to do commits with" +msgstr "" + +#: ./cli/app/restore.go:124 +msgid "H" +msgstr "" + +#: ./cli/server/list.go:34 +msgid "HOST" +msgstr "" + +#: ./cli/app/ps.go:181 ./cli/recipe/version.go:62 +msgid "IMAGE" +msgstr "" + +#: ./cli/app/secret.go:145 +msgid "Insert secret" +msgstr "" + +#: ./cli/app/labels.go:102 +msgid "LABELS OVERVIEW" +msgstr "" + +#: ./cli/recipe/lint.go:15 +msgid "Lint a recipe" +msgstr "" + +#: ./cli/app/cmd.go:198 +msgid "List all available commands" +msgstr "" + +#: ./cli/app/list.go:45 +msgid "List all managed apps" +msgstr "" + +#: ./cli/app/secret.go:374 +msgid "List all secrets" +msgstr "" + +#: ./cli/app/backup.go:180 +msgid "List all snapshots" +msgstr "" + +#: ./cli/server/list.go:20 +msgid "List managed servers" +msgstr "" + +#: ./cli/recipe/version.go:19 +msgid "List recipe versions" +msgstr "" + +#: ./cli/recipe/list.go:19 +msgid "List recipes" +msgstr "" + +#: ./cli/app/backup.go:17 +msgid "List the contents of a snapshot" +msgstr "" + +#: ./cli/app/volume.go:21 +msgid "List volumes associated with an app" +msgstr "" + +#: ./cli/app/backup.go:215 +msgid "Manage app backups" +msgstr "" + +#: ./cli/app/secret.go:444 +msgid "Manage app secrets" +msgstr "" + +#: ./cli/app/volume.go:192 +msgid "Manage app volumes" +msgstr "" + +#: ./cli/app/app.go:11 +msgid "Manage apps" +msgstr "" + +#: ./cli/recipe/recipe.go:12 +msgid "Manage recipes" +msgstr "" + +#: ./cli/server/server.go:12 +msgid "Manage servers" +msgstr "" + +#: ./cli/catalogue/catalogue.go:235 +msgid "Manage the recipe catalogue" +msgstr "" + +#: ./cli/recipe/new.go:116 +msgid "N" +msgstr "" + +#: ./cli/app/secret.go:106 ./cli/app/secret.go:394 ./cli/app/new.go:187 ./cli/app/volume.go:47 ./cli/server/list.go:34 ./cli/recipe/version.go:103 +msgid "NAME" +msgstr "" + +#: ./cli/internal/deploy.go:129 +msgid "NEW DEPLOY" +msgstr "" + +#: ./cli/internal/deploy.go:82 +msgid "NEW DEPLOYMENT" +msgstr "" + +#: ./cli/app/secret.go:136 +msgid "NOT" +msgstr "" + +#: ./cli/app/secret.go:137 +msgid "NOW" +msgstr "" + +#: ./cli/updater/updater.go:38 +msgid "Notify on new versions for deployed apps.\n" + "\n" + "If a new patch/minor version is available, a notification is printed.\n" + "\n" + "Use \"--major/-m\" to include new major versions." +msgstr "" + +#: ./cli/app/volume.go:47 +msgid "ON SERVER" +msgstr "" + +#: ./cli/server/prune.go:17 +msgid "Prune resources on a server" +msgstr "" + +#: ./cli/server/prune.go:18 +msgid "Prunes unused containers, networks, and dangling images.\n" + "\n" + "Use \"--volumes/-v\" to remove volumes that are not associated with a deployed\n" + "app. This can result in unwanted data loss if not used carefully." +msgstr "" + +#: ./pkg/lint/recipe.go:110 +msgid "README.md metadata filled in" +msgstr "" + +#: ./cli/app/list.go:216 ./cli/internal/deploy.go:76 +msgid "RECIPE" +msgstr "" + +#: ./cli/app/labels.go:74 +msgid "RECIPE LABELS" +msgstr "" + +#: ./cli/recipe/release.go:317 +msgid "Release Note (leave empty for no release note)" +msgstr "" + +#: ./cli/recipe/release.go:28 +msgid "Release a new recipe version" +msgstr "" + +#: ./cli/server/remove.go:19 +msgid "Remove a managed server" +msgstr "" + +#: ./cli/server/remove.go:20 +msgid "Remove a managed server.\n" + "\n" + "Abra will remove the internal bookkeeping ($ABRA_DIR/servers/...) and\n" + "underlying client connection context. This server will then be lost in time,\n" + "like tears in rain." +msgstr "" + +#: ./cli/app/secret.go:262 +msgid "Remove a secret" +msgstr "" + +#: ./cli/app/remove.go:21 +msgid "Remove all app data, locally and remotely" +msgstr "" + +#: ./cli/recipe/reset.go:15 +msgid "Remove all unstaged changes from recipe config" +msgstr "" + +#: ./cli/app/remove.go:22 +msgid "Remove everything related to an app which is already undeployed.\n" + "\n" + "By default, it will prompt for confirmation before proceeding. All secrets,\n" + "volumes and the local app env file will be deleted.\n" + "\n" + "Only run this command when you are sure you want to completely remove the app\n" + "and all associated app data. This is a destructive action, Be Careful!\n" + "\n" + "If you would like to delete specific volumes or secrets, please use removal\n" + "sub-commands under \"app volume\" and \"app secret\" instead.\n" + "\n" + "Please note, if you delete the local app env file without removing volumes and\n" + "secrets first, Abra will *not* be able to help you remove them afterwards.\n" + "\n" + "To delete everything without prompt, use the \"--force/-f\" or the \"--no-input/n\"\n" + "flag." +msgstr "" + +#: ./cli/app/volume.go:77 +msgid "Remove volume(s) associated with an app" +msgstr "" + +#: ./cli/app/volume.go:78 +msgid "Remove volumes associated with an app.\n" + "\n" + "The app in question must be undeployed before you try to remove volumes. See\n" + "\"abra app undeploy \" for more.\n" + "\n" + "The command is interactive and will show a multiple select input which allows\n" + "you to make a seclection. Use the \"?\" key to see more help on navigating this\n" + "interface.\n" + "\n" + "Passing \"--force/-f\" will select all volumes for removal. Be careful." +msgstr "" + +#: ./cli/app/restart.go:23 +msgid "Restart an app" +msgstr "" + +#: ./cli/app/restore.go:18 +msgid "Restore a snapshot" +msgstr "" + +#: ./cli/app/rollback.go:27 +msgid "Roll an app back to a previous version" +msgstr "" + +#: ./cli/app/run.go:23 +msgid "Run a command inside a service container" +msgstr "" + +#: ./cli/app/cmd.go:25 +msgid "Run an app specific command.\n" + "\n" + "These commands are bash functions, defined in the abra.sh of the recipe itself.\n" + "They can be run within the context of a service (e.g. app) or locally on your\n" + "work station by passing \"--local/-l\".\n" + "\n" + "N.B. If using the \"--\" style to pass arguments, flags (e.g. \"--local/-l\") must\n" + "be passed *before* the \"--\". It is possible to pass arguments without the \"--\"\n" + "as long as no dashes are present (i.e. \"foo\" works without \"--\", \"-foo\"\n" + "does not)." +msgstr "" + +#: ./cli/app/cmd.go:24 +msgid "Run app commands" +msgstr "" + +#: ./cli/app/new.go:372 ./cli/app/logs.go:102 ./cli/app/backup.go:272 ./cli/app/list.go:289 +msgid "S" +msgstr "" + +#: ./cli/app/new.go:207 +msgid "SECRETS OVERVIEW" +msgstr "" + +#: ./cli/app/list.go:216 ./cli/internal/deploy.go:77 +msgid "SERVER" +msgstr "" + +#: ./cli/app/ps.go:179 ./cli/recipe/version.go:62 ./cli/recipe/version.go:103 +msgid "SERVICE" +msgstr "" + +#: ./cli/app/services.go:67 +msgid "SERVICE (LONG)" +msgstr "" + +#: ./cli/app/services.go:67 +msgid "SERVICE (SHORT)" +msgstr "" + +#: ./pkg/ssh/ssh.go:16 +#, c-format +msgid "SSH host key verification failed for %s" +msgstr "" + +#: ./cli/recipe/list.go:44 +msgid "SSO" +msgstr "" + +#: ./cli/app/ps.go:180 ./cli/app/list.go:219 +msgid "STATUS" +msgstr "" + +#: ./cli/app/new.go:315 +msgid "Select app server:" +msgstr "" + +#: ./cli/internal/validate.go:50 +msgid "Select recipe" +msgstr "" + +#: ./cli/app/env.go:17 +msgid "Show app .env values" +msgstr "" + +#: ./cli/app/labels.go:25 +msgid "Show deployment labels" +msgstr "" + +#: ./cli/recipe/diff.go:15 +msgid "Show unstaged changes in recipe config" +msgstr "" + +#: ./cli/app/restore.go:19 +msgid "Snapshots are restored while apps are deployed.\n" + "\n" + "Some restore scenarios may require service / app restarts." +msgstr "" + +#: ./cli/internal/validate.go:120 +msgid "Specify a domain name" +msgstr "" + +#: ./cli/internal/validate.go:151 +msgid "Specify a server name" +msgstr "" + +#: ./cli/app/new.go:266 +msgid "Specify app domain" +msgstr "" + +#: ./cli/recipe/sync.go:22 +msgid "Sync recipe version label" +msgstr "" + +#: ./cli/app/cmd.go:265 +msgid "T" +msgstr "" + +#: ./cli/recipe/version.go:62 ./cli/recipe/version.go:103 +msgid "TAG" +msgstr "" + +#: ./cli/app/logs.go:20 +msgid "Tail app logs" +msgstr "" + +#: ./cli/updater/updater.go:473 +msgid "The Co-op Cloud auto-updater 🤖 🚀" +msgstr "" + +#: ./cli/run.go:25 +msgid "The Co-op Cloud command-line utility belt 🎩🐇" +msgstr "" + +#: ./cli/app/secret.go:146 +msgid "This command inserts a secret into an app environment.\n" + "\n" + "Arbitrary secret insertion is not supported. Secrets that are inserted must\n" + "match those configured in the recipe beforehand.\n" + "\n" + "This can be useful when you want to manually generate secrets for an app\n" + "environment. Typically, you can let Abra generate them for you on app creation\n" + "(see \"abra app new --secrets/-S\" for more)." +msgstr "" + +#: ./cli/app/secret.go:263 +msgid "This command removes a secret from an app environment.\n" + "\n" + "Arbitrary secret removal is not supported. Secrets that are removed must\n" + "match those configured in the recipe beforehand." +msgstr "" + +#: ./cli/recipe/diff.go:16 +msgid "This command requires /usr/bin/git." +msgstr "" + +#: ./cli/app/restart.go:24 +msgid "This command restarts services within a deployed app.\n" + "\n" + "Run \"abra app ps \" to see a list of service names.\n" + "\n" + "Pass \"--all-services/-a\" to restart all services." +msgstr "" + +#: ./cli/app/rollback.go:28 +msgid "This command rolls an app back to a previous version.\n" + "\n" + "Unlike \"abra app deploy\", chaos operations are not supported here. Only recipe\n" + "versions are supported values for \"[version]\".\n" + "\n" + "It is possible to \"--force/-f\" an downgrade if you want to re-deploy a specific\n" + "version.\n" + "\n" + "Only the deployed version is consulted when trying to determine what downgrades\n" + "are available. The live deployment version is the \"source of truth\" in this\n" + "case. The stored .env version is not consulted.\n" + "\n" + "A downgrade can be destructive, please ensure you have a copy of your app data\n" + "beforehand. See \"abra app backup\" for more." +msgstr "" + +#: ./cli/app/undeploy.go:25 +msgid "This does not destroy any application data.\n" + "\n" + "However, you should remain vigilant, as your swarm installation will consider\n" + "any previously attached volumes as eligible for pruning once undeployed.\n" + "\n" + "Passing \"--prune/-p\" does not remove those volumes." +msgstr "" + +#: ./cli/complete.go:13 +msgid "To load completions:\n" + "\n" + "Bash:\n" + " # Load autocompletion for the current Bash session\n" + " $ source <(abra autocomplete bash)\n" + "\n" + " # To load autocompletion for each session, execute once:\n" + " # Linux:\n" + " $ abra autocomplete bash | sudo tee /etc/bash_completion.d/abra\n" + " # macOS:\n" + " $ abra autocomplete bash | sudo tee $(brew --prefix)/etc/bash_completion.d/abra\n" + "\n" + "Zsh:\n" + " # If shell autocompletion is not already enabled in your environment,\n" + " # you will need to enable it. You can execute the following once:\n" + "\n" + " $ echo \"autoload -U compinit; compinit\" >> ~/.zshrc\n" + "\n" + " # To load autocompletions for each session, execute once:\n" + " $ abra autocomplete zsh > \"${fpath[1]}/_abra\"\n" + "\n" + " # You will need to start a new shell for this setup to take effect.\n" + "\n" + "fish:\n" + " $ abra autocomplete fish | source\n" + "\n" + " # To load autocompletions for each session, execute once:\n" + " $ abra autocomplete fish > ~/.config/fish/completions/abra.fish\n" + "\n" + "PowerShell:\n" + " PS> abra autocomplete powershell | Out-String | Invoke-Expression\n" + "\n" + " # To load autocompletions for every new session, run:\n" + " PS> abra autocomplete powershell > abra.ps1\n" + " # and source this file from your PowerShell profile." +msgstr "" + +#: ./cli/internal/deploy.go:123 +msgid "UNCHAOS DEPLOY" +msgstr "" + +#: ./cli/internal/deploy.go:117 +msgid "UNDEPLOY" +msgstr "" + +#: ./cli/app/list.go:222 ./cli/internal/deploy.go:140 +msgid "UPGRADE" +msgstr "" + +#: ./cli/app/undeploy.go:24 +msgid "Undeploy an app" +msgstr "" + +#: ./cli/recipe/upgrade.go:44 +msgid "Upgrade a given configuration.\n" + "\n" + "It will update the relevant compose file tags on the local file system.\n" + "\n" + "Some image tags cannot be parsed because they do not follow some sort of\n" + "semver-like convention. In this case, all possible tags will be listed and it\n" + "is up to the end-user to decide.\n" + "\n" + "The command is interactive and will show a select input which allows you to\n" + "make a seclection. Use the \"?\" key to see more help on navigating this\n" + "interface.\n" + "\n" + "You may invoke this command in \"wizard\" mode and be prompted for input." +msgstr "" + +#: ./cli/upgrade.go:18 +msgid "Upgrade abra" +msgstr "" + +#: ./cli/upgrade.go:19 +msgid "Upgrade abra in-place with the latest stable or release candidate.\n" + "\n" + "By default, the latest stable release is downloaded.\n" + "\n" + "Use \"--rc/-r\" to install the latest release candidate. Please bear in mind that\n" + "it may contain absolutely catastrophic deal-breaker bugs. Thank you very much\n" + "for the testing efforts 💗" +msgstr "" + +#: ./cli/app/upgrade.go:31 +msgid "Upgrade an app" +msgstr "" + +#: ./cli/updater/updater.go:77 +msgid "Upgrade an app by specifying stack name and recipe. \n" + "\n" + "Use \"--all\" to upgrade every deployed app.\n" + "\n" + "For each app with auto updates enabled, the deployed version is compared with\n" + "the current recipe catalogue version. If a new patch/minor version is\n" + "available, the app is upgraded.\n" + "\n" + "To include major versions use the \"--major/-m\" flag. You probably don't want\n" + "that as it will break things. Only apps that are not deployed with \"--chaos/-C\"\n" + "are upgraded, to update chaos deployments use the \"--chaos/-C\" flag. Use it\n" + "with care." +msgstr "" + +#: ./cli/app/upgrade.go:32 +msgid "Upgrade an app.\n" + "\n" + "Unlike \"abra app deploy\", chaos operations are not supported here. Only recipe\n" + "versions are supported values for \"[version]\".\n" + "\n" + "It is possible to \"--force/-f\" an upgrade if you want to re-deploy a specific\n" + "version.\n" + "\n" + "Only the deployed version is consulted when trying to determine what upgrades\n" + "are available. The live deployment version is the \"source of truth\" in this\n" + "case. The stored .env version is not consulted.\n" + "\n" + "An upgrade can be destructive, please ensure you have a copy of your app data\n" + "beforehand. See \"abra app backup\" for more." +msgstr "" + +#: ./cli/updater/updater.go:76 +msgid "Upgrade apps" +msgstr "" + +#: ./cli/recipe/upgrade.go:43 +msgid "Upgrade recipe image tags" +msgstr "" + +#: ./cli/recipe/release.go:283 +msgid "Use release note in release/next?" +msgstr "" + +#: ./cli/recipe/fetch.go:21 +msgid "Using \"--force/-f\" Git syncs an existing recipe. It does not erase unstaged changes." +msgstr "" + +#: ./cli/app/secret.go:106 ./cli/app/new.go:187 +msgid "VALUE" +msgstr "" + +#: ./cli/app/secret.go:394 ./cli/app/ps.go:182 ./cli/app/list.go:221 ./cli/recipe/version.go:62 ./cli/recipe/version.go:103 +msgid "VERSION" +msgstr "" + +#: ./cli/recipe/reset.go:16 +msgid "WARNING: this will delete your changes. Be Careful." +msgstr "" + +#: ./pkg/upstream/container/hijack.go:156 +msgid "[hijack] End of stdin" +msgstr "" + +#: ./pkg/upstream/container/hijack.go:132 +msgid "[hijack] end of stdout" +msgstr "" + +#: ./cli/recipe/sync.go:76 +#, c-format +msgid "\n" + "The following options are two types of initial semantic version that you can\n" + "pick for %s that will be published in the recipe catalogue. This follows the\n" + "semver convention (more on https://semver.org), here is a short cheatsheet\n" + "\n" + " 0.1.0: development release, still hacking. when you make a major upgrade\n" + " you increment the \"y\" part (i.e. 0.1.0 -> 0.2.0) and only move to\n" + " using the \"x\" part when things are stable.\n" + "\n" + " 1.0.0: public release, assumed to be working. you already have a stable\n" + " and reliable deployment of this app and feel relatively confident\n" + " about it.\n" + "\n" + "If you want people to be able alpha test your current config for %s but don't\n" + "think it is quite reliable, go with 0.1.0 and people will know that things are\n" + "likely to change.\n" + "\n" +msgstr "" + +#: ./cli/internal/recipe.go:18 +#, c-format +msgid "\n" + "You need to make a decision about what kind of an update this new recipe\n" + "version is. If someone else performs this upgrade, do they have to do some\n" + "migration work or take care of some breaking changes? This can be signaled in\n" + "the version you specify on the recipe deploy label and is called a semantic\n" + "version.\n" + "\n" + "The latest published version is %s.\n" + "\n" + "Here is a semver cheat sheet (more on https://semver.org):\n" + "\n" + " major: new features/bug fixes, backwards incompatible (e.g 1.0.0 -> 2.0.0).\n" + " the upgrade won't work without some preparation work and others need\n" + " to take care when performing it. \"it could go wrong\".\n" + "\n" + " minor: new features/bug fixes, backwards compatible (e.g. 0.1.0 -> 0.2.0).\n" + " the upgrade should Just Work and there are no breaking changes in\n" + " the app and the recipe config. \"it should go fine\".\n" + "\n" + " patch: bug fixes, backwards compatible (e.g. 0.0.1 -> 0.0.2). this upgrade\n" + " should also Just Work and is mostly to do with minor bug fixes\n" + " and/or security patches. \"nothing to worry about\".\n" + "\n" +msgstr "" + +#: ./pkg/lint/recipe.go:203 +#, c-format +msgid "\n" + "lint %s: %s" +msgstr "" + +#: ./pkg/ui/deploy.go:121 +#, c-format +msgid "^%s" +msgstr "" + +#: ./cli/app/secret.go:484 ./cli/app/secret.go:524 ./cli/app/restart.go:162 ./cli/app/app.go:10 ./cli/app/backup.go:240 ./cli/server/prune.go:91 ./cli/server/add.go:22 ./cli/recipe/upgrade.go:378 ./cli/recipe/fetch.go:115 ./cli/updater/updater.go:554 +msgid "a" +msgstr "" + +#: ./cli/app/remove.go:59 +msgid "aborting as requested" +msgstr "" + +#: ./cli/run.go:24 +msgid "abra [cmd] [args] [flags]" +msgstr "" + +#: ./cli/run.go:69 +#, c-format +msgid "abra version: %s, commit: %s, lang: %s" +msgstr "" + +#: ./pkg/recipe/recipe.go:208 +#, c-format +msgid "abra.sh: %s}" +msgstr "" + +#: ./cli/app/cmd.go:51 +msgid "accepts at most 2 args with --local/-l" +msgstr "" + +#: ./pkg/recipe/compose.go:249 +#, c-format +msgid "add '- \"%s\"' manually to the 'app' service in %s" +msgstr "" + +#: ./cli/server/add.go:21 +msgid "add [[server] | --local] [flags]" +msgstr "" + +#: ./pkg/app/app.go:510 +#, c-format +msgid "adding env vars to %s service config" +msgstr "" + +#: ./cli/app/secret.go:483 ./cli/app/secret.go:523 ./cli/app/backup.go:239 ./cli/server/prune.go:90 ./cli/recipe/fetch.go:114 ./cli/updater/updater.go:553 +msgid "all" +msgstr "" + +#: ./pkg/lint/recipe.go:82 +msgid "all images use a tag" +msgstr "" + +#: ./pkg/lint/recipe.go:162 +msgid "all services have images" +msgstr "" + +#: ./cli/app/restart.go:161 +msgid "all-services" +msgstr "" + +#: ./cli/recipe/upgrade.go:377 +msgid "all-tags" +msgstr "" + +#: ./pkg/container/container.go:48 +msgid "ambiguous container list received, prompting for input" +msgstr "" + +#: ./pkg/service/service.go:57 ./pkg/service/service.go:111 +msgid "ambiguous service list received, prompting for input" +msgstr "" + +#: ./cli/run.go:28 +msgid "app" +msgstr "" + +#: ./cli/app/app.go:9 +msgid "app [cmd] [args] [flags]" +msgstr "" + +#: ./pkg/dns/dns.go:52 +#, c-format +msgid "app domain %s (%s) does not appear to resolve to app server %s (%s)?" +msgstr "" + +#: ./cli/recipe/release.go:185 +msgid "app service is missing image tag?" +msgstr "" + +#: ./cli/app/remove.go:53 +msgid "are you sure?" +msgstr "" + +#: ./pkg/recipe/git.go:156 +#, c-format +msgid "attempting to checkout '%s' as chaos commit" +msgstr "" + +#: ./cli/server/add.go:76 ./cli/server/add.go:103 +#, c-format +msgid "attempting to create client for %s" +msgstr "" + +#: ./pkg/secret/secret.go:212 +#, c-format +msgid "attempting to generate and store %s on %s" +msgstr "" + +#: ./cli/upgrade.go:37 ./pkg/secret/pass.go:23 ./pkg/secret/pass.go:45 +#, c-format +msgid "attempting to run %s" +msgstr "" + +#: ./cli/app/upgrade.go:269 ./cli/app/deploy.go:231 +#, c-format +msgid "attempting to run post deploy commands, saw: %s" +msgstr "" + +#: ./cli/app/restart.go:108 +#, c-format +msgid "attempting to scale %s to 0" +msgstr "" + +#: ./cli/app/restart.go:133 +#, c-format +msgid "attempting to scale %s to 1" +msgstr "" + +#: ./cli/run.go:29 +msgid "autocomplete" +msgstr "" + +#: ./cli/complete.go:11 +msgid "autocomplete [bash|zsh|fish|powershell]" +msgstr "" + +#: ./cli/app/secret.go:38 ./cli/app/secret.go:170 ./cli/app/secret.go:280 ./cli/app/upgrade.go:58 ./cli/app/rollback.go:58 ./cli/app/deploy.go:59 ./cli/app/logs.go:32 ./pkg/autocomplete/autocomplete.go:17 ./pkg/autocomplete/autocomplete.go:32 ./pkg/autocomplete/autocomplete.go:43 ./pkg/autocomplete/autocomplete.go:59 ./pkg/autocomplete/autocomplete.go:77 ./pkg/autocomplete/autocomplete.go:93 ./pkg/autocomplete/autocomplete.go:99 ./pkg/autocomplete/autocomplete.go:114 +#, c-format +msgid "autocomplete failed: %s" +msgstr "" + +#: ./cli/app/new.go:374 +msgid "automatically generate secrets" +msgstr "" + +#: ./cli/recipe/fetch.go:125 +msgid "automatically set ssh remote" +msgstr "" + +#: ./cli/updater/updater.go:299 +#, c-format +msgid "available updates for %s: %s" +msgstr "" + +#: ./cli/app/backup.go:214 +msgid "b" +msgstr "" + +#: ./cli/app/backup.go:213 +msgid "backup [cmd] [args] [flags]" +msgstr "" + +#: ./cli/recipe/list.go:41 +msgid "backups" +msgstr "" + +#: ./pkg/web/web.go:45 +#, c-format +msgid "bad status: %s" +msgstr "" + +#: ./pkg/upstream/convert/volume.go:113 +msgid "bind options are incompatible with type tmpfs" +msgstr "" + +#: ./pkg/upstream/convert/volume.go:47 +msgid "bind options are incompatible with type volume" +msgstr "" + +#: ./cli/updater/updater.go:176 +#, c-format +msgid "boolean label %s could not be found for %s, set default to false." +msgstr "" + +#: ./pkg/config/env.go:74 ./pkg/config/env.go:107 +#, c-format +msgid "broken symlink in your abra config folders: %s" +msgstr "" + +#: ./cli/app/upgrade.go:451 ./cli/app/rollback.go:340 ./cli/app/deploy.go:351 ./cli/app/cp.go:30 ./cli/app/backup.go:135 +msgid "c" +msgstr "" + +#: ./pkg/recipe/git.go:52 +#, c-format +msgid "can not redeploy chaos version (%s) without --chaos" +msgstr "" + +#: ./cli/recipe/upgrade.go:318 +#, c-format +msgid "can upgrade service: %s, image: %s, tag: %s ::" +msgstr "" + +#: ./cli/app/cp.go:272 +msgid "can't copy dir to file" +msgstr "" + +#: ./cli/internal/validate.go:35 +#, c-format +msgid "can't read local recipes: %s" +msgstr "" + +#: ./cli/updater/updater.go:197 +#, c-format +msgid "can't separate key from value: %s (this variable is probably unset)" +msgstr "" + +#: ./pkg/catalogue/catalogue.go:59 ./pkg/recipe/git.go:245 +#, c-format +msgid "cannot ensure %s is up-to-date, no git remotes configured" +msgstr "" + +#: ./cli/app/config.go:36 ./pkg/app/app.go:52 +#, c-format +msgid "cannot find app with name %s" +msgstr "" + +#: ./cli/recipe/release.go:88 +#, c-format +msgid "cannot parse %s, invalid tag specified?" +msgstr "" + +#: ./pkg/dns/dns.go:38 ./pkg/dns/dns.go:47 +#, c-format +msgid "cannot resolve ipv4 for %s?" +msgstr "" + +#: ./cli/recipe/release.go:93 +msgid "cannot specify tag and bump type at the same time" +msgstr "" + +#: ./cli/app/secret.go:57 +msgid "cannot use '[secret] [version]' and '--all' together" +msgstr "" + +#: ./cli/app/cmd.go:101 +msgid "cannot use --local & --user together" +msgstr "" + +#: ./cli/recipe/fetch.go:48 +msgid "cannot use [recipe] and --all/-a together" +msgstr "" + +#: ./cli/app/secret.go:307 +msgid "cannot use [secret] and --all/-a together" +msgstr "" + +#: ./cli/server/add.go:53 +msgid "cannot use [server] and --local together" +msgstr "" + +#: ./cli/app/restart.go:68 +msgid "cannot use [service] and --all-services/-a together" +msgstr "" + +#: ./cli/app/new.go:70 ./cli/app/deploy.go:262 +msgid "cannot use [version] and --chaos together" +msgstr "" + +#: ./cli/run.go:30 +msgid "catalogue" +msgstr "" + +#: ./cli/catalogue/catalogue.go:234 +msgid "catalogue [cmd] [args] [flags]" +msgstr "" + +#: ./pkg/catalogue/catalogue.go:21 +msgid "catalogue is missing, retrieving now" +msgstr "" + +#: ./cli/recipe/list.go:38 +msgid "category" +msgstr "" + +#: ./cli/app/config.go:17 +msgid "cfg" +msgstr "" + +#: ./cli/app/secret.go:475 ./cli/app/secret.go:515 ./cli/app/secret.go:539 ./cli/app/secret.go:547 ./cli/app/restore.go:131 ./cli/app/ps.go:206 ./cli/app/cmd.go:272 ./cli/app/new.go:379 ./cli/app/check.go:87 ./cli/app/restart.go:154 ./cli/app/deploy.go:326 ./cli/app/cp.go:378 ./cli/app/backup.go:287 ./cli/app/backup.go:303 ./cli/app/labels.go:135 ./cli/catalogue/catalogue.go:271 ./cli/recipe/lint.go:128 ./cli/updater/updater.go:537 +msgid "chaos" +msgstr "" + +#: ./cli/app/check.go:17 +msgid "check [flags]" +msgstr "" + +#: ./cli/updater/updater.go:548 +msgid "check for major updates" +msgstr "" + +#: ./cli/app/upgrade.go:415 ./cli/app/undeploy.go:51 ./cli/app/deploy.go:88 +#, c-format +msgid "checking whether %s is already deployed" +msgstr "" + +#: ./cli/app/check.go:18 +msgid "chk" +msgstr "" + +#: ./pkg/recipe/recipe.go:74 +#, c-format +msgid "choosing %s as latest version of %s" +msgstr "" + +#: ./cli/recipe/sync.go:188 +#, c-format +msgid "choosing %s as new version for %s" +msgstr "" + +#: ./cli/app/rollback.go:145 +#, c-format +msgid "choosing %s as version to rollback" +msgstr "" + +#: ./cli/app/upgrade.go:152 +#, c-format +msgid "choosing %s as version to upgrade" +msgstr "" + +#: ./cli/recipe/release.go:249 +#, c-format +msgid "chore: publish %s release" +msgstr "" + +#: ./cli/catalogue/catalogue.go:191 +msgid "chore: publish new catalogue release changes" +msgstr "" + +#: ./cli/app/cmd.go:23 +msgid "cmd" +msgstr "" + +#: ./pkg/recipe/git.go:458 +#, c-format +msgid "collected %s for %s" +msgstr "" + +#: ./cli/catalogue/catalogue.go:89 +msgid "collecting catalogue metadata" +msgstr "" + +#: ./pkg/app/app.go:295 +#, c-format +msgid "collecting metadata from %v servers: %s" +msgstr "" + +#: ./pkg/recipe/recipe.go:549 +msgid "collecting recipe listing" +msgstr "" + +#: ./pkg/upstream/commandconn/commandconn.go:162 +#, c-format +msgid "command %v did not exit after %v: stderr=%q" +msgstr "" + +#: ./pkg/upstream/commandconn/commandconn.go:172 +#, c-format +msgid "command %v has exited with %v, please make sure the URL is valid, and Docker 18.09 or later is installed on the remote host: stderr=%s" +msgstr "" + +#: ./cli/app/cmd.go:22 +msgid "command [service | --local] [[args] [flags] | [flags] -- [args]]" +msgstr "" + +#: ./pkg/upstream/commandconn/commandconn.go:239 +#, c-format +msgid "commandConn.Close: CloseRead: %v" +msgstr "" + +#: ./pkg/upstream/commandconn/commandconn.go:65 +#, c-format +msgid "commandconn (%s):" +msgstr "" + +#: ./pkg/upstream/commandconn/commandconn.go:141 +msgid "commandconn: failed to wait" +msgstr "" + +#: ./pkg/upstream/commandconn/commandconn.go:49 +#, c-format +msgid "commandconn: starting %s with %v" +msgstr "" + +#: ./pkg/lint/recipe.go:68 +msgid "compose config has expected version" +msgstr "" + +#: ./pkg/recipe/recipe.go:205 +#, c-format +msgid "compose: %s, " +msgstr "" + +#: ./pkg/client/configs.go:35 +#, c-format +msgid "conf %s: %s" +msgstr "" + +#: ./cli/app/config.go:16 +msgid "config [flags]" +msgstr "" + +#: ./pkg/config/abra.go:37 +#, c-format +msgid "config file loaded from: %s" +msgstr "" + +#: ./pkg/upstream/convert/service.go:153 ./pkg/upstream/convert/service.go:168 +#, c-format +msgid "config not found: %s" +msgstr "" + +#: ./pkg/lint/recipe.go:169 +msgid "config version are vendored" +msgstr "" + +#: ./pkg/ssh/ssh.go:20 +#, c-format +msgid "connection timed out for %s" +msgstr "" + +#: ./pkg/recipe/compose.go:191 +#, c-format +msgid "considering %s config(s) for label update" +msgstr "" + +#: ./pkg/recipe/compose.go:123 +#, c-format +msgid "considering %s config(s) for tag update" +msgstr "" + +#: ./cli/app/undeploy.go:128 ./cli/server/prune.go:45 +#, c-format +msgid "containers pruned: %d; space reclaimed: %s" +msgstr "" + +#: ./pkg/client/context.go:66 +msgid "context 'default' cannot be removed" +msgstr "" + +#: ./cli/server/add.go:163 +#, c-format +msgid "context for %s already exists" +msgstr "" + +#: ./pkg/context/context.go:34 +msgid "context lacks Docker endpoint" +msgstr "" + +#: ./cli/recipe/sync.go:197 ./pkg/recipe/compose.go:229 +#, c-format +msgid "coop-cloud.${STACK_NAME}.version=%s" +msgstr "" + +#: ./pkg/app/app.go:400 +#, c-format +msgid "copied & templated %s to %s" +msgstr "" + +#: ./cli/app/cp.go:164 +#, c-format +msgid "copy %s from local to %s on container" +msgstr "" + +#: ./cli/app/cp.go:245 +#, c-format +msgid "copy: %s" +msgstr "" + +#: ./pkg/ssh/ssh.go:18 +#, c-format +msgid "could not resolve hostname for %s" +msgstr "" + +#: ./pkg/envfile/envfile.go:73 +#, c-format +msgid "couldn't parse %s" +msgstr "" + +#: ./pkg/upstream/container/hijack.go:172 +#, c-format +msgid "couldn't send EOF: %s" +msgstr "" + +#: ./cli/app/cp.go:29 +msgid "cp [flags]" +msgstr "" + +#: ./cli/app/backup.go:134 +msgid "create [flags]" +msgstr "" + +#: ./pkg/lint/recipe.go:134 +msgid "create an example .env.sample" +msgstr "" + +#: ./cli/app/cp.go:145 ./cli/app/cp.go:184 +#, c-format +msgid "create remote directory: %s" +msgstr "" + +#: ./pkg/client/client.go:88 +#, c-format +msgid "created client for %s" +msgstr "" + +#: ./cli/recipe/release.go:387 +#, c-format +msgid "created tag %s at %s" +msgstr "" + +#: ./pkg/client/context.go:26 +#, c-format +msgid "created the %s context" +msgstr "" + +#: ./cli/server/add.go:168 +#, c-format +msgid "creating context with domain %s" +msgstr "" + +#: ./pkg/lint/recipe.go:22 +msgid "critical" +msgstr "" + +#: ./cli/recipe/lint.go:115 +#, c-format +msgid "critical errors present in %s config" +msgstr "" + +#: ./cli/app/rollback.go:270 +#, c-format +msgid "current deployment '%s' is not a known version for %s" +msgstr "" + +#: ./cli/recipe/release.go:492 +#, c-format +msgid "current: %s, new: %s, correct?" +msgstr "" + +#: ./cli/app/deploy.go:29 ./cli/app/backup.go:66 ./cli/recipe/diff.go:14 ./cli/updater/updater.go:491 +msgid "d" +msgstr "" + +#: ./cli/updater/updater.go:490 +msgid "debug" +msgstr "" + +#: ./cli/app/secret.go:253 +#, c-format +msgid "deleted %s successfully from local pass store" +msgstr "" + +#: ./cli/app/secret.go:246 +#, c-format +msgid "deleted %s successfully from server" +msgstr "" + +#: ./cli/app/deploy.go:28 +msgid "deploy [version] [flags]" +msgstr "" + +#: ./pkg/lint/recipe.go:147 +msgid "deploy labels stanza present" +msgstr "" + +#: ./cli/internal/deploy.go:109 +msgid "deployment cancelled" +msgstr "" + +#: ./cli/app/cp.go:273 +msgid "destination directory does not exist" +msgstr "" + +#: ./pkg/recipe/git.go:367 +#, c-format +msgid "detected %s as tags for recipe %s" +msgstr "" + +#: ./pkg/formatter/formatter.go:80 +msgid "detected ABRA_CI=1" +msgstr "" + +#: ./cli/recipe/upgrade.go:199 +#, c-format +msgid "detected compatible upgradable tags %s for %s" +msgstr "" + +#: ./cli/recipe/upgrade.go:172 +#, c-format +msgid "detected potential upgradable tags %s for %s" +msgstr "" + +#: ./pkg/recipe/recipe.go:435 +#, c-format +msgid "detected versions %s for %s" +msgstr "" + +#: ./cli/app/cmd.go:131 ./cli/app/cmd.go:176 +msgid "did not detect any command arguments" +msgstr "" + +#: ./cli/recipe/upgrade.go:119 +#, c-format +msgid "did not find versions file for %s" +msgstr "" + +#: ./cli/recipe/diff.go:13 +msgid "diff [flags]" +msgstr "" + +#: ./pkg/recipe/recipe.go:202 +#, c-format +msgid "dir: %s, " +msgstr "" + +#: ./pkg/config/env.go:98 +#, c-format +msgid "directory is empty: %s" +msgstr "" + +#: ./pkg/recipe/recipe.go:201 +#, c-format +msgid "dirty: %v, " +msgstr "" + +#: ./cli/app/upgrade.go:453 ./cli/app/rollback.go:342 ./cli/app/deploy.go:353 +msgid "disable converge logic checks" +msgstr "" + +#: ./cli/app/upgrade.go:445 ./cli/app/rollback.go:334 ./cli/app/deploy.go:345 +msgid "disable public DNS checks" +msgstr "" + +#: ./cli/app/cmd.go:267 +msgid "disable remote TTY" +msgstr "" + +#: ./cli/recipe/release.go:552 +#, c-format +msgid "discovered %s as currently synced recipe label" +msgstr "" + +#: ./cli/app/run.go:105 +msgid "do not request a TTY" +msgstr "" + +#: ./pkg/ssh/ssh.go:26 +msgid "docker: is the daemon running / your user has docker permissions?" +msgstr "" + +#: ./cli/app/new.go:355 +msgid "domain" +msgstr "" + +#: ./cli/app/new.go:358 +msgid "domain name for app" +msgstr "" + +#: ./cli/run.go:108 +msgid "don't forget to run 'sudo mandb'" +msgstr "" + +#: ./cli/updater/updater.go:386 +#, c-format +msgid "don't update %s due to chaos deployment" +msgstr "" + +#: ./cli/updater/updater.go:396 +#, c-format +msgid "don't update %s due to disabled auto updates or missing ENABLE_AUTO_UPDATE env" +msgstr "" + +#: ./cli/updater/updater.go:376 +#, c-format +msgid "don't update %s due to missing recipe name" +msgstr "" + +#: ./cli/updater/updater.go:406 +#, c-format +msgid "don't update %s due to no new version" +msgstr "" + +#: ./cli/app/backup.go:65 +msgid "download [flags]" +msgstr "" + +#: ./pkg/git/add.go:22 +#, c-format +msgid "dry run: adding %s" +msgstr "" + +#: ./cli/recipe/release.go:277 +#, c-format +msgid "dry run: move release note from 'next' to %s" +msgstr "" + +#: ./pkg/git/commit.go:44 +msgid "dry run: no changes commited" +msgstr "" + +#: ./cli/recipe/release.go:342 +msgid "dry run: no changes committed" +msgstr "" + +#: ./cli/catalogue/catalogue.go:227 ./cli/recipe/release.go:394 +msgid "dry run: no changes published" +msgstr "" + +#: ./pkg/git/push.go:13 +#, c-format +msgid "dry run: no git changes pushed in %s" +msgstr "" + +#: ./cli/recipe/release.go:367 +#, c-format +msgid "dry run: no git tag created (%s)" +msgstr "" + +#: ./cli/recipe/sync.go:203 +#, c-format +msgid "dry run: not syncing label %s for recipe %s" +msgstr "" + +#: ./pkg/git/remote.go:15 +#, c-format +msgid "dry run: remote %s (%s) not created" +msgstr "" + +#: ./cli/catalogue/catalogue.go:255 ./cli/recipe/sync.go:222 ./cli/recipe/release.go:576 +msgid "dry-run" +msgstr "" + +#: ./pkg/upstream/convert/service.go:122 +#, c-format +msgid "duplicate config target for %s not allowed" +msgstr "" + +#: ./pkg/upstream/convert/service.go:43 +#, c-format +msgid "duplicate secret target for %s not allowed" +msgstr "" + +#: ./cli/app/env.go:16 ./cli/recipe/new.go:124 ./cli/recipe/lint.go:137 +msgid "e" +msgstr "" + +#: ./cli/recipe/list.go:42 +msgid "email" +msgstr "" + +#: ./cli/app/restore.go:126 +msgid "enable pre/post-hook command execution" +msgstr "" + +#: ./cli/internal/validate.go:82 +#, c-format +msgid "ensure %s recipe compose.* files include \"version: '3.8'\"" +msgstr "" + +#: ./pkg/lint/recipe.go:69 +msgid "ensure 'version: \"3.8\"' in compose configs" +msgstr "" + +#: ./pkg/lint/recipe.go:163 +msgid "ensure \"image: ...\" set on all services" +msgstr "" + +#: ./cli/app/deploy.go:107 +#, c-format +msgid "ensure recipe: %s" +msgstr "" + +#: ./pkg/recipe/git.go:50 +#, c-format +msgid "ensuring env version %s" +msgstr "" + +#: ./cli/recipe/upgrade.go:280 +msgid "enter / return to confirm, choose 'skip' to not upgrade this tag, vim mode is enabled" +msgstr "" + +#: ./cli/app/env.go:15 +msgid "env [flags]" +msgstr "" + +#: ./pkg/app/app.go:248 +#, c-format +msgid "env file for %s couldn't be read: %s" +msgstr "" + +#: ./pkg/app/app.go:253 +#, c-format +msgid "env file for %s has issues: %s" +msgstr "" + +#: ./pkg/ui/deploy.go:83 +#, c-format +msgid "err: %v, " +msgstr "" + +#: ./cli/recipe/lint.go:74 ./cli/recipe/lint.go:136 ./pkg/lint/recipe.go:132 ./pkg/lint/recipe.go:139 ./pkg/lint/recipe.go:146 ./pkg/lint/recipe.go:153 ./pkg/lint/recipe.go:161 ./pkg/lint/recipe.go:168 ./pkg/lint/recipe.go:175 +msgid "error" +msgstr "" + +#: ./pkg/config/abra.go:34 +#, c-format +msgid "error loading config file: %s" +msgstr "" + +#: ./pkg/config/abra.go:26 +#, c-format +msgid "error reading config file: %s" +msgstr "" + +#: ./pkg/upstream/container/hijack.go:135 +#, c-format +msgid "error receiveStdout: %s" +msgstr "" + +#: ./pkg/upstream/container/tty.go:39 +#, c-format +msgid "error resize: %s" +msgstr "" + +#: ./pkg/upstream/container/hijack.go:167 +#, c-format +msgid "error sendStdin: %s" +msgstr "" + +#: ./pkg/upstream/container/exec.go:43 +msgid "exec ID empty" +msgstr "" + +#: ./cli/recipe/release.go:501 +msgid "exiting as requested" +msgstr "" + +#: ./pkg/container/container.go:44 +#, c-format +msgid "expected 1 container but found %v: %s" +msgstr "" + +#: ./pkg/service/service.go:53 ./pkg/service/service.go:107 +#, c-format +msgid "expected 1 service but found %v: %s" +msgstr "" + +#: ./cli/app/secret.go:500 ./cli/app/upgrade.go:435 ./cli/app/rollback.go:324 ./cli/app/deploy.go:335 ./cli/app/remove.go:156 ./cli/app/volume.go:199 ./cli/recipe/fetch.go:19 ./cli/recipe/fetch.go:131 +msgid "f" +msgstr "" + +#: ./pkg/recipe/recipe.go:175 +#, c-format +msgid "failed to check git status of %s: %s" +msgstr "" + +#: ./pkg/recipe/git.go:225 ./pkg/recipe/git.go:406 ./pkg/git/branch.go:95 +#, c-format +msgid "failed to check out %s in %s" +msgstr "" + +#: ./cli/recipe/release.go:510 +#, c-format +msgid "failed to commit changes: %s" +msgstr "" + +#: ./cli/updater/updater.go:251 +#, c-format +msgid "failed to determine deployed version of %s" +msgstr "" + +#: ./pkg/container/container.go:69 +msgid "failed to match chosen container" +msgstr "" + +#: ./pkg/service/service.go:77 ./pkg/service/service.go:131 +msgid "failed to match chosen service" +msgstr "" + +#: ./pkg/client/registry.go:20 +#, c-format +msgid "failed to parse image %s, saw: %s" +msgstr "" + +#: ./cli/recipe/release.go:518 +#, c-format +msgid "failed to publish new release: %s" +msgstr "" + +#: ./pkg/upstream/container/tty.go:66 +msgid "failed to resize tty, using default size" +msgstr "" + +#: ./cli/app/new.go:118 +#, c-format +msgid "failed to retrieve latest commit for %s: %s" +msgstr "" + +#: ./pkg/git/branch.go:68 +#, c-format +msgid "failed to select default branch in %s" +msgstr "" + +#: ./cli/recipe/release.go:514 +#, c-format +msgid "failed to tag release: %s" +msgstr "" + +#: ./cli/recipe/fetch.go:18 +msgid "fetch [recipe | --all] [flags]" +msgstr "" + +#: ./cli/recipe/fetch.go:117 +msgid "fetch all recipes" +msgstr "" + +#: ./pkg/catalogue/catalogue.go:84 ./pkg/recipe/git.go:278 +#, c-format +msgid "fetched latest git changes for %s" +msgstr "" + +#: ./cli/recipe/fetch.go:93 +msgid "fetching latest recipes..." +msgstr "" + +#: ./pkg/recipe/recipe.go:555 +#, c-format +msgid "fetching repo metadata from %s" +msgstr "" + +#: ./cli/app/secret.go:499 +msgid "file" +msgstr "" + +#: ./cli/app/remove.go:148 +#, c-format +msgid "file: %s removed" +msgstr "" + +#: ./pkg/lint/recipe.go:111 +msgid "fill out all the metadata" +msgstr "" + +#: ./cli/recipe/list.go:108 +msgid "filter by recipe" +msgstr "" + +#: ./cli/updater/updater.go:202 +#, c-format +msgid "for %s read env %s with value: %s from docker service" +msgstr "" + +#: ./cli/app/upgrade.go:434 ./cli/app/rollback.go:323 ./cli/app/deploy.go:334 ./cli/app/remove.go:155 ./cli/app/volume.go:198 ./cli/recipe/fetch.go:130 +msgid "force" +msgstr "" + +#: ./cli/recipe/fetch.go:133 +msgid "force re-fetch" +msgstr "" + +#: ./cli/recipe/upgrade.go:91 +#, c-format +msgid "found versions file for %s" +msgstr "" + +#: ./cli/app/secret.go:25 ./cli/catalogue/catalogue.go:25 +msgid "g" +msgstr "" + +#: ./cli/internal/validate.go:75 +msgid "generate" +msgstr "" + +#: ./cli/app/secret.go:24 +msgid "generate [[secret] [version] | --all] [flags]" +msgstr "" + +#: ./cli/catalogue/catalogue.go:24 +msgid "generate [recipe] [flags]" +msgstr "" + +#: ./cli/app/secret.go:486 +msgid "generate all secrets" +msgstr "" + +#: ./pkg/secret/secret.go:62 ./pkg/secret/secret.go:80 +#, c-format +msgid "generated %s" +msgstr "" + +#: ./pkg/secret/secret.go:268 +#, c-format +msgid "generated and stored %v on %s" +msgstr "" + +#: ./cli/catalogue/catalogue.go:175 +#, c-format +msgid "generated recipe catalogue: %s" +msgstr "" + +#: ./cli/app/secret.go:134 +#, c-format +msgid "generated secrets %s shown again, please take note of them %s" +msgstr "" + +#: ./cli/app/deploy.go:101 +#, c-format +msgid "get deploy version: %s" +msgstr "" + +#: ./pkg/app/compose.go:80 +#, c-format +msgid "get label '%s'" +msgstr "" + +#: ./pkg/git/init.go:38 +#, c-format +msgid "git add: %s" +msgstr "" + +#: ./pkg/git/init.go:21 +#, c-format +msgid "git branch rename: %s" +msgstr "" + +#: ./pkg/git/commit.go:42 +msgid "git changes commited" +msgstr "" + +#: ./pkg/git/push.go:31 +msgid "git changes pushed" +msgstr "" + +#: ./pkg/recipe/git.go:410 +#, c-format +msgid "git checkout: %s in %s" +msgstr "" + +#: ./pkg/git/clone.go:64 ./pkg/git/clone.go:102 +#, c-format +msgid "git clone %s: cancelled due to interrupt" +msgstr "" + +#: ./pkg/git/clone.go:49 +#, c-format +msgid "git clone: %s" +msgstr "" + +#: ./pkg/git/clone.go:89 +#, c-format +msgid "git clone: %s already exists" +msgstr "" + +#: ./pkg/git/clone.go:59 ./pkg/git/clone.go:78 ./pkg/git/clone.go:87 +#, c-format +msgid "git clone: %s cloned successfully" +msgstr "" + +#: ./pkg/git/clone.go:68 +msgid "git clone: main branch failed, attempting master branch" +msgstr "" + +#: ./pkg/git/init.go:47 +#, c-format +msgid "git commit: %s" +msgstr "" + +#: ./pkg/git/init.go:17 +#, c-format +msgid "git init: %s" +msgstr "" + +#: ./pkg/git/init.go:29 +#, c-format +msgid "git open: %s" +msgstr "" + +#: ./pkg/git/read.go:50 +#, c-format +msgid "git status: %s: %s" +msgstr "" + +#: ./pkg/git/read.go:52 +#, c-format +msgid "git status: %s: clean" +msgstr "" + +#: ./pkg/git/push.go:40 +msgid "git tags pushed" +msgstr "" + +#: ./pkg/recipe/recipe.go:203 +#, c-format +msgid "git url: %s, " +msgstr "" + +#: ./pkg/git/init.go:34 +#, c-format +msgid "git worktree: %s" +msgstr "" + +#: ./cli/recipe/new.go:123 +msgid "git-email" +msgstr "" + +#: ./cli/recipe/new.go:115 +msgid "git-name" +msgstr "" + +#: ./pkg/lint/recipe.go:117 +msgid "git.coopcloud.tech repo exists" +msgstr "" + +#: ./pkg/recipe/git.go:378 +#, c-format +msgid "git: opening repository in %s" +msgstr "" + +#: ./cli/recipe/new.go:87 +msgid "happy hacking 🎉" +msgstr "" + +#: ./pkg/lint/recipe.go:103 +msgid "has published catalogue version" +msgstr "" + +#: ./cli/recipe/list.go:40 +msgid "healthcheck" +msgstr "" + +#: ./pkg/lint/recipe.go:75 +msgid "healthcheck enabled for all services" +msgstr "" + +#: ./cli/app/restore.go:123 +msgid "hooks" +msgstr "" + +#: ./cli/app/secret.go:144 +msgid "i" +msgstr "" + +#: ./pkg/ui/deploy.go:84 +#, c-format +msgid "id: %s, " +msgstr "" + +#: ./cli/run.go:141 +msgid "ignore .env version checkout" +msgstr "" + +#: ./cli/app/secret.go:478 ./cli/app/secret.go:518 ./cli/app/secret.go:542 ./cli/app/secret.go:550 ./cli/app/restore.go:134 ./cli/app/ps.go:209 ./cli/app/cmd.go:275 ./cli/app/new.go:382 ./cli/app/check.go:90 ./cli/app/restart.go:157 ./cli/app/deploy.go:329 ./cli/app/cp.go:381 ./cli/app/backup.go:290 ./cli/app/backup.go:306 ./cli/app/labels.go:138 ./cli/catalogue/catalogue.go:274 ./cli/recipe/lint.go:131 ./cli/updater/updater.go:540 +msgid "ignore uncommitted recipes changes" +msgstr "" + +#: ./cli/app/undeploy.go:143 ./cli/server/prune.go:66 +#, c-format +msgid "images pruned: %d; space reclaimed: %s" +msgstr "" + +#: ./pkg/lint/recipe.go:148 +msgid "include \"deploy: labels: ...\" stanza" +msgstr "" + +#: ./pkg/lint/recipe.go:155 +msgid "include \"traefik.enable=true\" deploy label" +msgstr "" + +#: ./cli/app/backup.go:274 +msgid "include secrets" +msgstr "" + +#: ./cli/app/backup.go:250 +msgid "include timestamps" +msgstr "" + +#: ./cli/app/backup.go:282 +msgid "include volumes" +msgstr "" + +#: ./cli/app/restore.go:74 +#, c-format +msgid "including CONTAINER=%s in backupbot exec invocation" +msgstr "" + +#: ./cli/app/backup.go:107 +#, c-format +msgid "including INCLUDE_PATH=%s in backupbot exec invocation" +msgstr "" + +#: ./cli/app/restore.go:62 +#, c-format +msgid "including NONINTERACTIVE=%v in backupbot exec invocation" +msgstr "" + +#: ./cli/app/restore.go:79 +#, c-format +msgid "including NO_COMMANDS=%v in backupbot exec invocation" +msgstr "" + +#: ./cli/app/backup.go:167 +#, c-format +msgid "including RETRIES=%s in backupbot exec invocation" +msgstr "" + +#: ./cli/app/backup.go:112 +#, c-format +msgid "including SECRETS=%v in backupbot exec invocation" +msgstr "" + +#: ./cli/app/backup.go:49 +#, c-format +msgid "including SHOW_ALL=%v in backupbot exec invocation" +msgstr "" + +#: ./cli/app/restore.go:52 ./cli/app/backup.go:44 ./cli/app/backup.go:102 +#, c-format +msgid "including SNAPSHOT=%s in backupbot exec invocation" +msgstr "" + +#: ./cli/app/restore.go:57 +#, c-format +msgid "including TARGET=%s in backupbot exec invocation" +msgstr "" + +#: ./cli/app/backup.go:54 +#, c-format +msgid "including TIMESTAMPS=%v in backupbot exec invocation" +msgstr "" + +#: ./cli/app/restore.go:68 +#, c-format +msgid "including VOLUMES=%s in backupbot exec invocation" +msgstr "" + +#: ./cli/app/backup.go:117 +#, c-format +msgid "including VOLUMES=%v in backupbot exec invocation" +msgstr "" + +#: ./cli/recipe/upgrade.go:348 ./cli/recipe/sync.go:233 ./cli/recipe/release.go:587 +msgid "increase the major part of the version" +msgstr "" + +#: ./cli/recipe/upgrade.go:356 ./cli/recipe/sync.go:241 ./cli/recipe/release.go:595 +msgid "increase the minor part of the version" +msgstr "" + +#: ./cli/recipe/upgrade.go:364 ./cli/recipe/sync.go:249 ./cli/recipe/release.go:603 +msgid "increase the patch part of the version" +msgstr "" + +#: ./pkg/git/init.go:50 +#, c-format +msgid "init committed all files for new git repo in %s" +msgstr "" + +#: ./pkg/git/init.go:24 +#, c-format +msgid "initialised new git repo in %s" +msgstr "" + +#: ./cli/app/undeploy.go:88 +msgid "initialising undeploy" +msgstr "" + +#: ./cli/app/secret.go:143 +msgid "insert [flags]" +msgstr "" + +#: ./cli/upgrade.go:55 +msgid "install release candidate (may contain bugs)" +msgstr "" + +#: ./pkg/upstream/convert/volume.go:90 +msgid "invalid bind source, source cannot be empty" +msgstr "" + +#: ./pkg/upstream/convert/service.go:813 +#, c-format +msgid "invalid credential spec: cannot specify both %s and %s" +msgstr "" + +#: ./pkg/upstream/convert/service.go:815 +#, c-format +msgid "invalid credential spec: cannot specify both %s, and %s" +msgstr "" + +#: ./pkg/upstream/convert/service.go:834 +#, c-format +msgid "invalid credential spec: spec specifies config %v, but no such config can be found" +msgstr "" + +#: ./pkg/upstream/container/hijack.go:100 +#, c-format +msgid "invalid detach escape keys, using default: %s" +msgstr "" + +#: ./pkg/lint/recipe.go:504 +msgid "invalid lightweight tag detected" +msgstr "" + +#: ./pkg/upstream/convert/volume.go:130 +msgid "invalid npipe source, source cannot be empty" +msgstr "" + +#: ./pkg/recipe/recipe.go:147 +#, c-format +msgid "invalid recipe: %s" +msgstr "" + +#: ./pkg/upstream/convert/volume.go:110 +msgid "invalid tmpfs source, source must be empty" +msgstr "" + +#: ./cli/recipe/sync.go:193 +#, c-format +msgid "invalid version %s specified" +msgstr "" + +#: ./cli/updater/updater.go:471 +msgid "kadabra [cmd] [flags]" +msgstr "" + +#: ./cli/updater/updater.go:484 +#, c-format +msgid "kadabra version %s, commit %s" +msgstr "" + +#: ./cli/app/cmd.go:249 ./cli/app/logs.go:19 ./cli/server/add.go:200 ./cli/recipe/lint.go:16 +msgid "l" +msgstr "" + +#: ./cli/app/labels.go:23 +msgid "labels [flags]" +msgstr "" + +#: ./cli/app/list.go:177 +msgid "latest" +msgstr "" + +#: ./cli/recipe/release.go:487 +#, c-format +msgid "latest git tag (%s) and synced label (%s) are the same?" +msgstr "" + +#: ./cli/app/labels.go:24 +msgid "lb" +msgstr "" + +#: ./cli/recipe/lint.go:14 +msgid "lint [flags]" +msgstr "" + +#: ./pkg/lint/recipe.go:187 +#, c-format +msgid "linting for critical errors in %s configs" +msgstr "" + +#: ./pkg/lint/recipe.go:215 +#, c-format +msgid "linting successful, %s is well configured" +msgstr "" + +#: ./cli/recipe/list.go:18 +msgid "list" +msgstr "" + +#: ./cli/app/secret.go:372 +msgid "list " +msgstr "" + +#: ./cli/app/cmd.go:196 ./cli/app/backup.go:15 ./cli/app/volume.go:19 +msgid "list [flags]" +msgstr "" + +#: ./cli/app/list.go:43 ./cli/server/list.go:18 +msgid "list [flags]" +msgstr "" + +#: ./cli/recipe/upgrade.go:380 +msgid "list all tags, not just upgrades" +msgstr "" + +#: ./cli/app/backup.go:234 ./cli/app/backup.go:258 +msgid "list specific snapshot" +msgstr "" + +#: ./pkg/app/app.go:41 +#, c-format +msgid "loaded app %s: %s" +msgstr "" + +#: ./cli/app/cmd.go:248 ./cli/server/add.go:199 ./cli/server/list.go:69 +msgid "local" +msgstr "" + +#: ./cli/app/cp.go:108 +#, c-format +msgid "local %s " +msgstr "" + +#: ./cli/server/add.go:86 +msgid "local server already exists" +msgstr "" + +#: ./cli/server/add.go:84 +msgid "local server successfully added" +msgstr "" + +#: ./cli/app/logs.go:18 +msgid "logs [service] [flags]" +msgstr "" + +#: ./pkg/lint/recipe.go:124 +msgid "long secret names" +msgstr "" + +#: ./cli/app/secret.go:373 ./cli/app/cmd.go:197 ./cli/app/backup.go:16 ./cli/app/volume.go:20 ./cli/app/list.go:44 ./cli/server/list.go:19 ./cli/recipe/list.go:20 +msgid "ls" +msgstr "" + +#: ./cli/app/secret.go:460 ./cli/app/secret.go:556 ./cli/app/ps.go:199 ./cli/app/list.go:312 ./cli/server/list.go:100 ./cli/recipe/upgrade.go:370 ./cli/recipe/version.go:132 ./cli/recipe/list.go:98 ./cli/updater/updater.go:546 +msgid "m" +msgstr "" + +#: ./cli/app/secret.go:459 ./cli/app/secret.go:555 ./cli/app/ps.go:198 ./cli/app/list.go:311 ./cli/server/list.go:99 ./cli/recipe/upgrade.go:369 ./cli/recipe/version.go:131 ./cli/recipe/list.go:97 +msgid "machine" +msgstr "" + +#: ./cli/recipe/release.go:78 +#, c-format +msgid "main app service version for %s is empty?" +msgstr "" + +#: ./cli/recipe/upgrade.go:345 ./cli/recipe/sync.go:230 ./cli/recipe/release.go:584 ./cli/updater/updater.go:545 ./cli/internal/recipe.go:46 ./cli/internal/recipe.go:64 ./cli/internal/recipe.go:78 +msgid "major" +msgstr "" + +#: ./cli/recipe/upgrade.go:101 +#, c-format +msgid "malformed version pin specification: %s" +msgstr "" + +#: ./cli/run.go:31 +msgid "man" +msgstr "" + +#: ./cli/run.go:81 +msgid "man [flags]" +msgstr "" + +#: ./cli/recipe/upgrade.go:353 ./cli/recipe/sync.go:238 ./cli/recipe/release.go:592 ./cli/internal/recipe.go:46 ./cli/internal/recipe.go:66 ./cli/internal/recipe.go:80 +msgid "minor" +msgstr "" + +#: ./cli/recipe/fetch.go:44 +msgid "missing [recipe] or --all/-a" +msgstr "" + +#: ./cli/app/restart.go:64 +msgid "missing [service]" +msgstr "" + +#: ./cli/server/add.go:57 +msgid "missing argument or --local/-l flag" +msgstr "" + +#: ./cli/app/secret.go:53 +msgid "missing arguments [secret]/[version] or '--all'" +msgstr "" + +#: ./cli/updater/updater.go:103 +msgid "missing arguments or --all/-a flag" +msgstr "" + +#: ./pkg/secret/secret.go:126 +#, c-format +msgid "missing version for secret? (%s)" +msgstr "" + +#: ./cli/app/new.go:48 ./cli/recipe/new.go:35 ./cli/updater/updater.go:36 ./cli/updater/updater.go:499 +msgid "n" +msgstr "" + +#: ./cli/recipe/list.go:37 +msgid "name" +msgstr "" + +#: ./pkg/lint/recipe.go:141 +msgid "name a servce 'app'" +msgstr "" + +#: ./pkg/ui/deploy.go:85 +#, c-format +msgid "name: %s, " +msgstr "" + +#: ./cli/app/undeploy.go:135 ./cli/server/prune.go:52 +#, c-format +msgid "networks pruned: %d" +msgstr "" + +#: ./cli/recipe/new.go:34 +msgid "new [flags]" +msgstr "" + +#: ./cli/app/new.go:47 +msgid "new [recipe] [version] [flags]" +msgstr "" + +#: ./cli/catalogue/catalogue.go:223 +#, c-format +msgid "new changes published: %s" +msgstr "" + +#: ./cli/recipe/new.go:86 +#, c-format +msgid "new recipe '%s' created: %s" +msgstr "" + +#: ./cli/recipe/release.go:413 +#, c-format +msgid "new release published: %s" +msgstr "" + +#: ./pkg/git/read.go:88 +#, c-format +msgid "no %s exists, not reading any global gitignore config" +msgstr "" + +#: ./pkg/git/read.go:130 +#, c-format +msgid "no %s exists, skipping reading gitignore paths" +msgstr "" + +#: ./cli/updater/updater.go:155 ./pkg/app/compose.go:86 +#, c-format +msgid "no %s label found for %s" +msgstr "" + +#: ./cli/recipe/release.go:415 +msgid "no -p/--publish passed, not publishing" +msgstr "" + +#: ./pkg/git/common.go:13 +#, c-format +msgid "no .git directory in %s?" +msgstr "" + +#: ./pkg/recipe/compose.go:29 +#, c-format +msgid "no COMPOSE_FILE detected, loading default: %s" +msgstr "" + +#: ./cli/internal/validate.go:96 +msgid "no app provided" +msgstr "" + +#: ./cli/app/rollback.go:120 +msgid "no available downgrades" +msgstr "" + +#: ./cli/app/upgrade.go:127 +msgid "no available upgrades" +msgstr "" + +#: ./cli/updater/updater.go:224 +#, c-format +msgid "no available upgrades for %s" +msgstr "" + +#: ./cli/internal/backup.go:26 +msgid "no backupbot discovered, is it deployed?" +msgstr "" + +#: ./cli/catalogue/catalogue.go:187 ./cli/recipe/release.go:353 +#, c-format +msgid "no changes discovered in %s, nothing to publish?" +msgstr "" + +#: ./pkg/git/commit.go:14 +msgid "no commit message specified?" +msgstr "" + +#: ./pkg/config/abra.go:20 +msgid "no config file found" +msgstr "" + +#: ./cli/app/remove.go:94 +msgid "no configs to remove" +msgstr "" + +#: ./pkg/container/container.go:31 +#, c-format +msgid "no containers matching the %v filter found?" +msgstr "" + +#: ./cli/app/new.go:275 ./cli/internal/validate.go:129 +msgid "no domain provided" +msgstr "" + +#: ./pkg/recipe/compose.go:248 +msgid "no existing label found, automagic insertion not supported yet" +msgstr "" + +#: ./cli/recipe/sync.go:72 +#, c-format +msgid "no git tags found for %s" +msgstr "" + +#: ./cli/recipe/upgrade.go:177 +#, c-format +msgid "no new versions available for %s, assuming %s is the latest (use -a/--all-tags to see all anyway)" +msgstr "" + +#: ./cli/updater/updater.go:273 +#, c-format +msgid "no published releases for %s in the recipe catalogue?" +msgstr "" + +#: ./cli/internal/validate.go:64 +#, c-format +msgid "no recipe '%s' exists?" +msgstr "" + +#: ./cli/internal/validate.go:59 +msgid "no recipe name provided" +msgstr "" + +#: ./cli/app/secret.go:210 +#, c-format +msgid "no secret %s available for recipe %s?" +msgstr "" + +#: ./cli/app/secret.go:311 +msgid "no secret(s) specified?" +msgstr "" + +#: ./cli/app/secret.go:102 +msgid "no secrets generated" +msgstr "" + +#: ./cli/app/secret.go:437 +#, c-format +msgid "no secrets stored for %s" +msgstr "" + +#: ./cli/app/remove.go:119 +msgid "no secrets to remove" +msgstr "" + +#: ./cli/app/secret.go:366 +msgid "no secrets to remove?" +msgstr "" + +#: ./cli/app/new.go:324 ./cli/internal/validate.go:167 +msgid "no server provided" +msgstr "" + +#: ./cli/app/cmd.go:168 +#, c-format +msgid "no service %s for %s?" +msgstr "" + +#: ./pkg/service/service.go:41 +#, c-format +msgid "no services deployed matching label '%s'?" +msgstr "" + +#: ./pkg/service/service.go:30 +msgid "no services deployed?" +msgstr "" + +#: ./pkg/service/service.go:95 +#, c-format +msgid "no services matching the %v filter found?" +msgstr "" + +#: ./cli/recipe/release.go:133 +#, c-format +msgid "no tag specified and no previous tag available for %s, assuming this is the initial release" +msgstr "" + +#: ./pkg/lint/recipe.go:89 +msgid "no unstable tags" +msgstr "" + +#: ./cli/internal/recipe.go:70 ./cli/internal/recipe.go:85 +msgid "no version bump type specififed?" +msgstr "" + +#: ./cli/app/volume.go:71 +#, c-format +msgid "no volumes created for %s" +msgstr "" + +#: ./cli/app/volume.go:184 +msgid "no volumes removed" +msgstr "" + +#: ./cli/app/remove.go:141 +msgid "no volumes to remove" +msgstr "" + +#: ./cli/app/upgrade.go:450 ./cli/app/rollback.go:339 ./cli/app/deploy.go:350 +msgid "no-converge-checks" +msgstr "" + +#: ./cli/app/upgrade.go:442 ./cli/app/rollback.go:331 ./cli/app/deploy.go:342 +msgid "no-domain-checks" +msgstr "" + +#: ./cli/updater/updater.go:498 +msgid "no-input" +msgstr "" + +#: ./cli/app/run.go:102 +msgid "no-tty" +msgstr "" + +#: ./cli/internal/deploy.go:159 +#, c-format +msgid "not enough arguments: %s" +msgstr "" + +#: ./pkg/secret/secret.go:119 +msgid "not generating app secrets, none enabled in recipe config" +msgstr "" + +#: ./cli/internal/command.go:93 +msgid "not requesting a remote TTY" +msgstr "" + +#: ./cli/recipe/upgrade.go:300 +#, c-format +msgid "not upgrading %s, skipping as requested" +msgstr "" + +#: ./cli/recipe/upgrade.go:239 +#, c-format +msgid "not upgrading from %s to %s for %s, because the upgrade type is more serious than what user wants" +msgstr "" + +#: ./cli/updater/updater.go:35 +msgid "notify [flags]" +msgstr "" + +#: ./cli/app/backup.go:298 +msgid "number of retry attempts" +msgstr "" + +#: ./cli/app/cp.go:85 +msgid "one of / arguments must take $SERVICE:$PATH form" +msgstr "" + +#: ./pkg/lint/recipe.go:140 +msgid "one service named 'app'" +msgstr "" + +#: ./pkg/lint/recipe.go:176 +msgid "only annotated tags used for recipe version" +msgstr "" + +#: ./cli/recipe/lint.go:139 +msgid "only show errors" +msgstr "" + +#: ./cli/app/upgrade.go:461 +msgid "only show release notes" +msgstr "" + +#: ./cli/app/logs.go:96 +msgid "only tail stderr" +msgstr "" + +#: ./cli/app/secret.go:468 ./cli/app/secret.go:492 ./cli/app/secret.go:532 ./cli/app/ps.go:29 ./cli/app/undeploy.go:156 ./cli/app/new.go:364 ./cli/app/backup.go:264 ./cli/catalogue/catalogue.go:248 ./cli/server/prune.go:16 ./cli/recipe/list.go:106 ./cli/recipe/release.go:609 +msgid "p" +msgstr "" + +#: ./cli/recipe/upgrade.go:158 +#, c-format +msgid "parsed %s for %s" +msgstr "" + +#: ./pkg/recipe/compose.go:159 +#, c-format +msgid "parsed %s from %s" +msgstr "" + +#: ./cli/app/cmd.go:128 ./cli/app/cmd.go:174 +#, c-format +msgid "parsed following command arguments: %s" +msgstr "" + +#: ./cli/app/upgrade.go:318 +#, c-format +msgid "parsing chosen upgrade version failed: %s" +msgstr "" + +#: ./cli/app/upgrade.go:362 +#, c-format +msgid "parsing deployed version failed: %s" +msgstr "" + +#: ./cli/app/upgrade.go:323 +#, c-format +msgid "parsing deployment version failed: %s" +msgstr "" + +#: ./cli/app/upgrade.go:329 ./cli/app/upgrade.go:368 +#, c-format +msgid "parsing recipe version failed: %s" +msgstr "" + +#: ./cli/app/secret.go:467 ./cli/app/secret.go:491 ./cli/app/secret.go:531 ./cli/app/new.go:363 +msgid "pass" +msgstr "" + +#: ./pkg/secret/pass.go:15 ./pkg/secret/pass.go:37 +msgid "pass command not found on $PATH, is it installed?" +msgstr "" + +#: ./cli/recipe/upgrade.go:361 ./cli/recipe/sync.go:246 ./cli/recipe/release.go:600 ./cli/internal/recipe.go:46 ./cli/internal/recipe.go:68 ./cli/internal/recipe.go:82 +msgid "patch" +msgstr "" + +#: ./cli/app/backup.go:263 +msgid "path" +msgstr "" + +#: ./cli/recipe/list.go:105 +msgid "pattern" +msgstr "" + +#: ./cli/app/upgrade.go:437 ./cli/app/rollback.go:326 ./cli/app/deploy.go:337 ./cli/app/remove.go:158 ./cli/app/volume.go:201 +msgid "perform action without further prompt" +msgstr "" + +#: ./cli/recipe/release.go:562 +#, c-format +msgid "please fix your synced label for %s and re-run this command" +msgstr "" + +#: ./cli/app/rollback.go:238 +#, c-format +msgid "please select a downgrade (version: %s):" +msgstr "" + +#: ./cli/app/rollback.go:243 +#, c-format +msgid "please select a downgrade (version: %s, chaos: %s):" +msgstr "" + +#: ./cli/app/upgrade.go:285 +#, c-format +msgid "please select an upgrade (version: %s):" +msgstr "" + +#: ./cli/app/upgrade.go:290 +#, c-format +msgid "please select an upgrade (version: %s, chaos: %s):" +msgstr "" + +#: ./cli/run.go:133 +msgid "prefer offline & filesystem access" +msgstr "" + +#: ./cli/recipe/release.go:128 +msgid "previous git tags detected, assuming this is a new semver release" +msgstr "" + +#: ./cli/app/secret.go:462 ./cli/app/secret.go:558 ./cli/app/ps.go:201 ./cli/app/list.go:314 ./cli/server/list.go:102 ./cli/recipe/upgrade.go:372 ./cli/recipe/version.go:134 ./cli/recipe/list.go:100 +msgid "print machine-readable output" +msgstr "" + +#: ./pkg/recipe/git.go:398 +#, c-format +msgid "processing %s for %s" +msgstr "" + +#: ./cli/app/undeploy.go:155 +msgid "prune" +msgstr "" + +#: ./cli/server/prune.go:15 +msgid "prune [flags]" +msgstr "" + +#: ./cli/app/undeploy.go:158 +msgid "prune unused containers, networks, and dangling images" +msgstr "" + +#: ./cli/app/ps.go:28 +msgid "ps [flags]" +msgstr "" + +#: ./cli/catalogue/catalogue.go:247 ./cli/recipe/release.go:608 +msgid "publish" +msgstr "" + +#: ./pkg/lint/recipe.go:104 +msgid "publish a recipe version to the catalogue" +msgstr "" + +#: ./cli/catalogue/catalogue.go:250 ./cli/recipe/release.go:611 +msgid "publish changes to git.coopcloud.tech" +msgstr "" + +#: ./cli/recipe/release.go:400 +msgid "publish new release?" +msgstr "" + +#: ./pkg/app/app.go:424 +msgid "querying remote servers..." +msgstr "" + +#: ./cli/app/run.go:22 ./cli/app/upgrade.go:459 ./cli/app/backup.go:296 ./cli/app/list.go:297 ./cli/catalogue/catalogue.go:256 ./cli/recipe/sync.go:223 ./cli/recipe/recipe.go:11 ./cli/recipe/release.go:577 +msgid "r" +msgstr "" + +#: ./cli/app/restart.go:22 +msgid "re" +msgstr "" + +#: ./pkg/recipe/git.go:151 +#, c-format +msgid "read %s as tags for recipe %s" +msgstr "" + +#: ./pkg/app/app.go:577 ./pkg/envfile/envfile.go:42 ./pkg/envfile/envfile.go:80 ./pkg/config/env.go:50 +#, c-format +msgid "read %s from %s" +msgstr "" + +#: ./pkg/app/app.go:579 +#, c-format +msgid "read 0 command names from %s" +msgstr "" + +#: ./pkg/envfile/envfile.go:82 +#, c-format +msgid "read 0 env var exports from %s" +msgstr "" + +#: ./pkg/config/abra.go:77 +msgid "read abra dir from $ABRA_DIR" +msgstr "" + +#: ./pkg/config/abra.go:81 +msgid "read abra dir from config file" +msgstr "" + +#: ./pkg/git/read.go:149 +#, c-format +msgid "read global ignore paths: %s" +msgstr "" + +#: ./pkg/recipe/recipe.go:406 +#, c-format +msgid "read recipe catalogue from file system cache in %s" +msgstr "" + +#: ./pkg/ui/deploy.go:86 +#, c-format +msgid "reader: %v, " +msgstr "" + +#: ./cli/app/secret.go:216 +#, c-format +msgid "reading secret from file: %s" +msgstr "" + +#: ./pkg/recipe/recipe.go:206 +#, c-format +msgid "readme: %s, " +msgstr "" + +#: ./cli/app/list.go:296 ./cli/app/list.go:303 ./cli/run.go:32 +msgid "recipe" +msgstr "" + +#: ./pkg/recipe/recipe.go:458 +#, c-format +msgid "recipe %s does not exist?" +msgstr "" + +#: ./pkg/lint/recipe.go:212 +#, c-format +msgid "recipe '%s' failed lint checks:\n" + "%s" +msgstr "" + +#: ./cli/recipe/recipe.go:10 +msgid "recipe [cmd] [args] [flags]" +msgstr "" + +#: ./pkg/recipe/recipe.go:462 +#, c-format +msgid "recipe metadata retrieved for %s" +msgstr "" + +#: ./pkg/lint/recipe.go:125 +msgid "reduce length of secret names to 12 chars" +msgstr "" + +#: ./cli/recipe/lint.go:32 +msgid "ref" +msgstr "" + +#: ./cli/recipe/release.go:26 +msgid "release [version] [flags]" +msgstr "" + +#: ./cli/app/upgrade.go:458 +msgid "releasenotes" +msgstr "" + +#: ./cli/app/cp.go:117 ./cli/app/cp.go:199 ./cli/app/cp.go:210 +#, c-format +msgid "remote path: %s" +msgstr "" + +#: ./cli/app/cp.go:197 +#, c-format +msgid "remote: %s does not exist" +msgstr "" + +#: ./cli/app/secret.go:260 +msgid "remove [[secret] | --all] [flags]" +msgstr "" + +#: ./cli/app/remove.go:19 +msgid "remove [flags]" +msgstr "" + +#: ./cli/app/volume.go:76 +msgid "remove [volume] [flags]" +msgstr "" + +#: ./cli/server/remove.go:17 +msgid "remove [flags]" +msgstr "" + +#: ./cli/app/secret.go:526 +msgid "remove all secrets" +msgstr "" + +#: ./cli/server/prune.go:93 +msgid "remove all unused images" +msgstr "" + +#: ./cli/app/secret.go:534 +msgid "remove generated secrets from a local pass store" +msgstr "" + +#: ./cli/server/prune.go:101 +msgid "remove volumes" +msgstr "" + +#: ./cli/recipe/new.go:62 +#, c-format +msgid "removed .git repo in %s" +msgstr "" + +#: ./pkg/recipe/recipe.go:138 +#, c-format +msgid "removed dirty suffix from .env version: %s -> %s" +msgstr "" + +#: ./cli/recipe/release.go:537 +#, c-format +msgid "removed freshly created tag %s" +msgstr "" + +#: ./cli/server/prune.go:56 +msgid "removing all images, not only dangling ones" +msgstr "" + +#: ./cli/app/remove.go:89 +#, c-format +msgid "removing configs failed: %s" +msgstr "" + +#: ./cli/app/volume.go:150 +#, c-format +msgid "removing volume %s failed: %s" +msgstr "" + +#: ./cli/app/remove.go:136 ./cli/app/volume.go:179 +#, c-format +msgid "removing volumes failed: %s" +msgstr "" + +#: ./pkg/lint/recipe.go:177 +msgid "replace lightweight tag with annotated tag" +msgstr "" + +#: ./pkg/upstream/convert/service.go:774 +msgid "replicas can only be used with replicated mode" +msgstr "" + +#: ./pkg/git/init.go:65 +#, c-format +msgid "repo config: %s" +msgstr "" + +#: ./pkg/git/init.go:70 +#, c-format +msgid "repo set config: %s" +msgstr "" + +#: ./cli/catalogue/catalogue.go:258 ./cli/recipe/sync.go:225 ./cli/recipe/release.go:579 +msgid "report changes that would be made" +msgstr "" + +#: ./cli/app/cmd.go:259 +msgid "request remote user" +msgstr "" + +#: ./cli/app/cmd.go:46 +msgid "requires at least 2 arguments with --local/-l" +msgstr "" + +#: ./cli/app/cmd.go:67 +msgid "requires at least 3 arguments" +msgstr "" + +#: ./cli/recipe/reset.go:13 +msgid "reset [flags]" +msgstr "" + +#: ./cli/recipe/lint.go:37 +msgid "resolve" +msgstr "" + +#: ./cli/app/restart.go:21 +msgid "restart [[service] | --all-services] [flags]" +msgstr "" + +#: ./cli/app/restart.go:164 +msgid "restart all services" +msgstr "" + +#: ./cli/app/restore.go:16 +msgid "restore [flags]" +msgstr "" + +#: ./cli/app/restore.go:110 +msgid "restore specific services" +msgstr "" + +#: ./cli/app/restore.go:118 +msgid "restore specific volumes" +msgstr "" + +#: ./cli/app/backup.go:295 +msgid "retries" +msgstr "" + +#: ./cli/updater/updater.go:239 +#, c-format +msgid "retrieve deployed version whether %s is already deployed" +msgstr "" + +#: ./cli/internal/backup.go:29 +#, c-format +msgid "retrieved %s as backup enabled service" +msgstr "" + +#: ./cli/app/cp.go:72 ./cli/internal/command.go:39 +#, c-format +msgid "retrieved %s as target container on %s" +msgstr "" + +#: ./pkg/recipe/compose.go:59 +#, c-format +msgid "retrieved %s configs for %s" +msgstr "" + +#: ./pkg/app/app.go:501 +#, c-format +msgid "retrieved %s for %s" +msgstr "" + +#: ./cli/recipe/upgrade.go:139 +#, c-format +msgid "retrieved %s from remote registry for %s" +msgstr "" + +#: ./pkg/config/env.go:37 +#, c-format +msgid "retrieved %v servers: %s" +msgstr "" + +#: ./pkg/app/app.go:487 +#, c-format +msgid "retrieved app statuses: %s" +msgstr "" + +#: ./cli/recipe/version.go:39 +msgid "retrieved versions from local recipe repository" +msgstr "" + +#: ./pkg/recipe/recipe.go:659 +msgid "retrieving recipes" +msgstr "" + +#: ./cli/app/rollback.go:26 ./cli/recipe/release.go:27 +msgid "rl" +msgstr "" + +#: ./cli/app/secret.go:261 ./cli/app/remove.go:20 ./cli/app/volume.go:93 ./cli/server/remove.go:18 +msgid "rm" +msgstr "" + +#: ./cli/app/rollback.go:25 +msgid "rollback [version] [flags]" +msgstr "" + +#: ./pkg/ui/deploy.go:333 +msgid "rolled back" +msgstr "" + +#: ./cli/app/restore.go:17 ./cli/recipe/reset.go:14 +msgid "rs" +msgstr "" + +#: ./cli/recipe/lint.go:33 +msgid "rule" +msgstr "" + +#: ./cli/app/run.go:21 +msgid "run [[args] [flags] | [flags] -- [args]]" +msgstr "" + +#: ./cli/app/run.go:113 +msgid "run command as user" +msgstr "" + +#: ./cli/app/cmd.go:251 +msgid "run command locally" +msgstr "" + +#: ./cli/app/upgrade.go:266 ./cli/app/deploy.go:229 +#, c-format +msgid "run the following post-deploy commands: %s" +msgstr "" + +#: ./cli/internal/backup.go:62 +#, c-format +msgid "running backup %s on %s with exec config %v" +msgstr "" + +#: ./cli/internal/deploy.go:189 +#, c-format +msgid "running command %s %s within the context of %s_%s" +msgstr "" + +#: ./cli/app/cmd.go:171 +#, c-format +msgid "running command %s within the context of %s_%s" +msgstr "" + +#: ./cli/internal/command.go:84 +#, c-format +msgid "running command with user %s" +msgstr "" + +#: ./cli/internal/command.go:81 +#, c-format +msgid "running command: %s" +msgstr "" + +#: ./cli/internal/deploy.go:167 +#, c-format +msgid "running post-command '%s %s' in container %s" +msgstr "" + +#: ./cli/app/secret.go:443 ./cli/app/restore.go:108 ./cli/app/new.go:341 ./cli/app/logs.go:94 ./cli/app/backup.go:179 ./cli/app/backup.go:232 ./cli/app/backup.go:256 ./cli/app/list.go:320 ./cli/catalogue/catalogue.go:264 ./cli/server/server.go:11 ./cli/recipe/fetch.go:123 ./cli/recipe/sync.go:21 +msgid "s" +msgstr "" + +#: ./pkg/recipe/recipe.go:207 +#, c-format +msgid "sample env: %s, " +msgstr "" + +#: ./cli/recipe/lint.go:35 +msgid "satisfied" +msgstr "" + +#: ./pkg/secret/secret.go:139 +#, c-format +msgid "secret %s is > %d chars when combined with %s" +msgstr "" + +#: ./pkg/lint/recipe.go:481 +#, c-format +msgid "secret %s is longer than 12 characters" +msgstr "" + +#: ./cli/app/secret.go:442 +msgid "secret [cmd] [args] [flags]" +msgstr "" + +#: ./pkg/upstream/convert/service.go:72 +#, c-format +msgid "secret not found: %s" +msgstr "" + +#: ./cli/app/deploy.go:276 +#, c-format +msgid "secret not generated: %s" +msgstr "" + +#: ./cli/app/remove.go:116 +#, c-format +msgid "secret: %s removed" +msgstr "" + +#: ./cli/app/new.go:371 ./cli/app/backup.go:271 +msgid "secrets" +msgstr "" + +#: ./cli/app/new.go:211 +#, c-format +msgid "secrets are %s shown again, please save them %s" +msgstr "" + +#: ./cli/app/new.go:340 ./cli/app/new.go:347 ./cli/app/list.go:319 ./cli/app/list.go:326 ./cli/run.go:33 +msgid "server" +msgstr "" + +#: ./pkg/app/app.go:301 +#, c-format +msgid "server %s doesn't exist? Run \"abra server ls\" to check" +msgstr "" + +#: ./cli/server/server.go:10 +msgid "server [cmd] [args] [flags]" +msgstr "" + +#: ./cli/server/add.go:184 +#, c-format +msgid "server dir for %s already created" +msgstr "" + +#: ./cli/internal/validate.go:171 +msgid "server doesn't exist?" +msgstr "" + +#: ./cli/server/add.go:141 +#, c-format +msgid "serverAdd: cleanUp: %s is not empty, aborting cleanup" +msgstr "" + +#: ./cli/server/add.go:128 +#, c-format +msgid "serverAdd: cleanUp: cleaning up context for %s" +msgstr "" + +#: ./cli/server/add.go:146 +#, c-format +msgid "serverAdd: cleanUp: failed to remove %s: %s" +msgstr "" + +#: ./cli/server/add.go:137 +#, c-format +msgid "serverAdd: cleanUp: unable to list files in %s: %s" +msgstr "" + +#: ./cli/recipe/upgrade.go:222 +#, c-format +msgid "service %s is at version %s, but pinned to %s, please correct your compose.yml file manually!" +msgstr "" + +#: ./cli/recipe/upgrade.go:218 +#, c-format +msgid "service %s, image %s pinned to %s, no compatible upgrade found" +msgstr "" + +#: ./cli/app/restore.go:107 +msgid "services" +msgstr "" + +#: ./cli/app/services.go:21 +msgid "services [flags]" +msgstr "" + +#: ./pkg/git/init.go:73 +msgid "set 'main' as the default branch" +msgstr "" + +#: ./pkg/app/compose.go:68 +#, c-format +msgid "set label 'coop-cloud.%s.autoupdate' to %s for %s" +msgstr "" + +#: ./pkg/app/compose.go:30 +#, c-format +msgid "set label 'coop-cloud.%s.chaos' to %v for %s" +msgstr "" + +#: ./pkg/app/compose.go:41 +#, c-format +msgid "set label 'coop-cloud.%s.chaos-version' to %v for %s" +msgstr "" + +#: ./pkg/app/compose.go:51 +#, c-format +msgid "set label 'coop-cloud.%s.version' to %v for %s" +msgstr "" + +#: ./pkg/app/compose.go:18 +#, c-format +msgid "set recipe label 'coop-cloud.%s.recipe' to %s for %s" +msgstr "" + +#: ./pkg/git/init.go:60 +#, c-format +msgid "set reference: %s" +msgstr "" + +#: ./cli/app/upgrade.go:240 ./cli/app/rollback.go:202 ./cli/app/deploy.go:203 +#, c-format +msgid "set waiting timeout to %d second(s)" +msgstr "" + +#: ./cli/recipe/lint.go:34 +msgid "severity" +msgstr "" + +#: ./cli/app/backup.go:242 +msgid "show all paths" +msgstr "" + +#: ./cli/app/list.go:291 +msgid "show app deployment status" +msgstr "" + +#: ./cli/app/list.go:299 +msgid "show apps of a specific recipe" +msgstr "" + +#: ./cli/app/list.go:322 +msgid "show apps of a specific server" +msgstr "" + +#: ./cli/run.go:117 ./cli/updater/updater.go:493 +msgid "show debug messages" +msgstr "" + +#: ./cli/app/logs.go:101 +msgid "since" +msgstr "" + +#: ./cli/app/new.go:309 +#, c-format +msgid "single server detected, choosing %s automatically" +msgstr "" + +#: ./cli/catalogue/catalogue.go:266 +msgid "skip updating recipe repositories" +msgstr "" + +#: ./cli/catalogue/catalogue.go:263 +msgid "skip-updates" +msgstr "" + +#: ./cli/recipe/lint.go:36 +msgid "skipped" +msgstr "" + +#: ./pkg/lint/recipe.go:55 +#, c-format +msgid "skipping %s based on skip condition" +msgstr "" + +#: ./cli/recipe/lint.go:53 +#, c-format +msgid "skipping %s, does not have level \"error\"" +msgstr "" + +#: ./cli/app/deploy.go:180 +msgid "skipping domain checks" +msgstr "" + +#: ./cli/app/deploy.go:177 +msgid "skipping domain checks, no DOMAIN=... configured" +msgstr "" + +#: ./pkg/secret/secret.go:207 +#, c-format +msgid "skipping generation of %s (generate=false)" +msgstr "" + +#: ./pkg/app/app.go:685 +#, c-format +msgid "skipping version %s write as already exists in %s.env" +msgstr "" + +#: ./pkg/app/app.go:679 +#, c-format +msgid "skipping writing version %s because dry run" +msgstr "" + +#: ./cli/app/backup.go:231 ./cli/app/backup.go:255 +msgid "snapshot" +msgstr "" + +#: ./cli/app/backup.go:178 +msgid "snapshots [flags]" +msgstr "" + +#: ./cli/app/new.go:343 +msgid "specify server for new app" +msgstr "" + +#: ./cli/app/services.go:22 +msgid "sr" +msgstr "" + +#: ./cli/recipe/fetch.go:122 +msgid "ssh" +msgstr "" + +#: ./cli/server/add.go:107 +#, c-format +msgid "ssh %s error: %s" +msgstr "" + +#: ./pkg/ssh/ssh.go:22 +#, c-format +msgid "ssh auth: permission denied for %s" +msgstr "" + +#: ./pkg/upstream/commandconn/connection.go:38 +msgid "ssh host connection is not valid" +msgstr "" + +#: ./pkg/recipe/recipe.go:204 +#, c-format +msgid "ssh url: %s, " +msgstr "" + +#: ./cli/app/list.go:288 ./cli/recipe/list.go:39 +msgid "status" +msgstr "" + +#: ./pkg/ui/deploy.go:88 +#, c-format +msgid "status: %s}" +msgstr "" + +#: ./cli/app/logs.go:93 +msgid "stderr" +msgstr "" + +#: ./cli/app/secret.go:470 ./cli/app/secret.go:494 +msgid "store generated secrets in a local pass store" +msgstr "" + +#: ./cli/app/new.go:366 +msgid "store secrets in a local pass store" +msgstr "" + +#: ./pkg/formatter/formatter.go:238 +#, c-format +msgid "stripped %s to %s for parsing" +msgstr "" + +#: ./pkg/ui/deploy.go:330 +msgid "succeeded" +msgstr "" + +#: ./pkg/recipe/git.go:178 +#, c-format +msgid "successfully checked %s out to %s in %s" +msgstr "" + +#: ./pkg/git/branch.go:99 +#, c-format +msgid "successfully checked out %v in %s" +msgstr "" + +#: ./pkg/server/server.go:25 +#, c-format +msgid "successfully created %s" +msgstr "" + +#: ./pkg/client/client.go:97 +#, c-format +msgid "swarm mode not enabled on %s?" +msgstr "" + +#: ./pkg/client/client.go:100 +msgid "swarm mode not enabled on local server?" +msgstr "" + +#: ./cli/recipe/sync.go:20 +msgid "sync [version] [flags]" +msgstr "" + +#: ./pkg/recipe/compose.go:243 +#, c-format +msgid "synced label %s to service %s" +msgstr "" + +#: ./cli/app/secret.go:508 ./cli/app/restore.go:100 ./cli/app/run.go:103 ./cli/app/backup.go:248 +msgid "t" +msgstr "" + +#: ./pkg/lint/recipe.go:90 +msgid "tag all images with stable tags" +msgstr "" + +#: ./cli/recipe/sync.go:129 +#, c-format +msgid "tag at commit %s is unannotated or otherwise broken" +msgstr "" + +#: ./cli/recipe/upgrade.go:296 +#, c-format +msgid "tag upgraded from %s to %s for %s" +msgstr "" + +#: ./pkg/lint/recipe.go:96 +msgid "tags use semver-like format" +msgstr "" + +#: ./cli/app/logs.go:104 +msgid "tail logs since YYYY-MM-DDTHH:MM:SSZ" +msgstr "" + +#: ./pkg/logs/logs.go:86 +#, c-format +msgid "tailLogs: unable to copy buffer: %s" +msgstr "" + +#: ./cli/app/restore.go:99 +msgid "target" +msgstr "" + +#: ./cli/app/restore.go:102 +msgid "target path" +msgstr "" + +#: ./pkg/upstream/convert/service.go:604 +msgid "test and disable can't be set at the same time" +msgstr "" + +#: ./cli/recipe/list.go:43 +msgid "tests" +msgstr "" + +#: ./pkg/app/compose.go:95 +#, c-format +msgid "timeout label: %s" +msgstr "" + +#: ./cli/app/backup.go:247 +msgid "timestamps" +msgstr "" + +#: ./pkg/upstream/convert/volume.go:96 +msgid "tmpfs options are incompatible with type bind" +msgstr "" + +#: ./pkg/upstream/convert/volume.go:136 +msgid "tmpfs options are incompatible with type npipe" +msgstr "" + +#: ./pkg/upstream/convert/volume.go:44 +msgid "tmpfs options are incompatible with type volume" +msgstr "" + +#: ./cli/run.go:125 ./cli/updater/updater.go:501 +msgid "toggle non-interactive mode" +msgstr "" + +#: ./pkg/lint/recipe.go:154 +msgid "traefik routing enabled" +msgstr "" + +#: ./cli/app/secret.go:502 +msgid "treat input as a file" +msgstr "" + +#: ./cli/app/secret.go:507 +msgid "trim" +msgstr "" + +#: ./cli/app/secret.go:510 +msgid "trim input" +msgstr "" + +#: ./cli/app/new.go:236 ./pkg/app/app.go:141 +#, c-format +msgid "trimming %s to %s to avoid runtime limits" +msgstr "" + +#: ./cli/app/cmd.go:264 +msgid "tty" +msgstr "" + +#: ./cli/app/run.go:111 ./cli/app/cmd.go:257 ./cli/recipe/upgrade.go:42 ./cli/updater/updater.go:75 +msgid "u" +msgstr "" + +#: ./cli/app/undeploy.go:23 +msgid "un" +msgstr "" + +#: ./pkg/recipe/git.go:187 +#, c-format +msgid "unable to check git clean status in %s: %s" +msgstr "" + +#: ./pkg/recipe/git.go:256 +#, c-format +msgid "unable to check out default branch in %s: %s" +msgstr "" + +#: ./pkg/git/clone.go:100 +#, c-format +msgid "unable to clean up git clone of %s: %s" +msgstr "" + +#: ./cli/app/list.go:148 +#, c-format +msgid "unable to clone %s: %s" +msgstr "" + +#: ./pkg/ssh/ssh.go:24 +#, c-format +msgid "unable to connect to %s, please check your SSH config" +msgstr "" + +#: ./cli/recipe/sync.go:74 +msgid "unable to continue, input required for initial version" +msgstr "" + +#: ./cli/app/ps.go:165 +#, c-format +msgid "unable to convert to JSON: %s" +msgstr "" + +#: ./cli/run.go:49 +#, c-format +msgid "unable to create %s: %s" +msgstr "" + +#: ./cli/server/add.go:100 +#, c-format +msgid "unable to create local context: %s" +msgstr "" + +#: ./cli/recipe/upgrade.go:247 +#, c-format +msgid "unable to determine versioning semantics of %s, listing all tags" +msgstr "" + +#: ./pkg/lint/recipe.go:261 ./pkg/recipe/files.go:17 +#, c-format +msgid "unable to discover .env.sample for %s" +msgstr "" + +#: ./cli/recipe/fetch.go:64 +#, c-format +msgid "unable to discover SSH remote for %s" +msgstr "" + +#: ./pkg/recipe/git.go:262 +#, c-format +msgid "unable to fetch tags in %s: %s" +msgstr "" + +#: ./pkg/recipe/git.go:274 +#, c-format +msgid "unable to git pull in %s: %s" +msgstr "" + +#: ./pkg/lint/recipe.go:496 +#, c-format +msgid "unable to list local tags for %s" +msgstr "" + +#: ./pkg/git/diff.go:30 +msgid "unable to locate git command, cannot output diff" +msgstr "" + +#: ./cli/recipe/fetch.go:70 ./pkg/lint/recipe.go:491 ./pkg/recipe/git.go:236 ./pkg/git/read.go:26 +#, c-format +msgid "unable to open %s: %s" +msgstr "" + +#: ./pkg/recipe/git.go:251 +#, c-format +msgid "unable to open git work tree in %s: %s" +msgstr "" + +#: ./pkg/git/read.go:31 +#, c-format +msgid "unable to open worktree of %s: %s" +msgstr "" + +#: ./cli/recipe/upgrade.go:154 +#, c-format +msgid "unable to parse %s, error was: %s, skipping upgrade for %s" +msgstr "" + +#: ./pkg/recipe/compose.go:153 +#, c-format +msgid "unable to parse %s, skipping" +msgstr "" + +#: ./cli/run.go:100 +#, c-format +msgid "unable to proceed, %s does not exist?" +msgstr "" + +#: ./pkg/git/read.go:45 +#, c-format +msgid "unable to query status of %s: %s" +msgstr "" + +#: ./pkg/recipe/git.go:241 +#, c-format +msgid "unable to read remotes in %s: %s" +msgstr "" + +#: ./cli/recipe/upgrade.go:148 +#, c-format +msgid "unable to read tag for image %s, is it missing? skipping upgrade for %s" +msgstr "" + +#: ./cli/recipe/release.go:549 +#, c-format +msgid "unable to read version for %s from synced label. Did you try running \"abra recipe sync %s\" already?" +msgstr "" + +#: ./cli/recipe/fetch.go:74 +#, c-format +msgid "unable to remove default remote in %s: %s" +msgstr "" + +#: ./cli/app/volume.go:145 +#, c-format +msgid "unable to remove volume: no volume with name '%s'?" +msgstr "" + +#: ./cli/app/secret.go:124 ./cli/app/secret.go:424 ./cli/server/list.go:82 ./cli/recipe/version.go:106 ./cli/recipe/list.go:77 +#, c-format +msgid "unable to render to JSON: %s" +msgstr "" + +#: ./pkg/recipe/git.go:160 +#, c-format +msgid "unable to resolve '%s': %s" +msgstr "" + +#: ./cli/server/add.go:114 +#, c-format +msgid "unable to resolve IPv4 for %s" +msgstr "" + +#: ./cli/app/list.go:153 +#, c-format +msgid "unable to retrieve tags for %s: %s" +msgstr "" + +#: ./pkg/upstream/container/hijack.go:82 +#, c-format +msgid "unable to set IO streams as raw terminal: %s" +msgstr "" + +#: ./cli/recipe/fetch.go:81 +#, c-format +msgid "unable to set SSH remote in %s: %s" +msgstr "" + +#: ./pkg/upstream/container/hijack.go:43 +#, c-format +msgid "unable to setup input stream: %s" +msgstr "" + +#: ./cli/internal/validate.go:84 +#, c-format +msgid "unable to validate recipe: %s" +msgstr "" + +#: ./pkg/upstream/convert/service.go:462 +#, c-format +msgid "undefined config %q" +msgstr "" + +#: ./pkg/upstream/convert/service.go:375 +#, c-format +msgid "undefined network %q" +msgstr "" + +#: ./pkg/upstream/convert/service.go:414 +#, c-format +msgid "undefined secret %q" +msgstr "" + +#: ./pkg/upstream/convert/volume.go:56 +#, c-format +msgid "undefined volume %q" +msgstr "" + +#: ./cli/app/undeploy.go:22 +msgid "undeploy [flags]" +msgstr "" + +#: ./cli/app/undeploy.go:104 +msgid "undeploy succeeded 🟢" +msgstr "" + +#: ./pkg/upstream/commandconn/commandconn.go:255 +#, c-format +msgid "unimplemented call: SetDeadline(%v)" +msgstr "" + +#: ./pkg/upstream/commandconn/commandconn.go:259 +#, c-format +msgid "unimplemented call: SetReadDeadline(%v)" +msgstr "" + +#: ./pkg/upstream/commandconn/commandconn.go:263 +#, c-format +msgid "unimplemented call: SetWriteDeadline(%v)" +msgstr "" + +#: ./cli/app/ps.go:119 ./cli/app/ps.go:120 ./cli/app/ps.go:121 ./cli/app/ps.go:122 ./cli/app/ps.go:123 ./cli/app/list.go:111 ./cli/app/list.go:112 ./cli/app/list.go:113 ./cli/app/list.go:114 ./cli/app/list.go:115 ./cli/app/list.go:175 ./cli/app/labels.go:71 ./cli/server/list.go:59 ./cli/server/list.go:71 +msgid "unknown" +msgstr "" + +#: ./cli/app/rollback.go:142 +msgid "unknown deployed version, unable to downgrade" +msgstr "" + +#: ./cli/app/upgrade.go:149 +msgid "unknown deployed version, unable to upgrade" +msgstr "" + +#: ./pkg/upstream/convert/service.go:780 +#, c-format +msgid "unknown mode: %s" +msgstr "" + +#: ./pkg/upstream/convert/service.go:652 +#, c-format +msgid "unknown restart policy: %s" +msgstr "" + +#: ./pkg/client/client.go:44 +#, c-format +msgid "unknown server, run \"abra server add %s\"?" +msgstr "" + +#: ./cli/app/cp.go:253 +#, c-format +msgid "untar: %s" +msgstr "" + +#: ./cli/app/upgrade.go:30 +msgid "up" +msgstr "" + +#: ./cli/updater/updater.go:556 +msgid "update all deployed apps" +msgstr "" + +#: ./pkg/recipe/compose.go:171 ./pkg/recipe/compose.go:237 +#, c-format +msgid "updating %s to %s in %s" +msgstr "" + +#: ./cli/run.go:34 +msgid "upgrade" +msgstr "" + +#: ./cli/updater/updater.go:444 +#, c-format +msgid "upgrade %s (%s) to version %s" +msgstr "" + +#: ./cli/app/upgrade.go:29 +msgid "upgrade [version] [flags]" +msgstr "" + +#: ./cli/recipe/upgrade.go:41 +msgid "upgrade [flags]" +msgstr "" + +#: ./cli/updater/updater.go:74 +msgid "upgrade [[stack] [recipe] | --all] [flags]" +msgstr "" + +#: ./cli/upgrade.go:16 +msgid "upgrade [flags]" +msgstr "" + +#: ./cli/recipe/upgrade.go:243 +#, c-format +msgid "upgrade to which tag? (service: %s, image: %s, tag: %s)" +msgstr "" + +#: ./cli/recipe/upgrade.go:249 +#, c-format +msgid "upgrade to which tag? (service: %s, tag: %s)" +msgstr "" + +#: ./cli/recipe/upgrade.go:216 +#, c-format +msgid "upgrading service %s from %s to %s (pinned tag: %s)" +msgstr "" + +#: ./pkg/lint/recipe.go:118 +msgid "upload your recipe to git.coopcloud.tech/coop-cloud/..." +msgstr "" + +#: ./cli/recipe/release.go:556 +#, c-format +msgid "use %s as the new version?" +msgstr "" + +#: ./pkg/lint/recipe.go:83 +msgid "use a tag for all images" +msgstr "" + +#: ./cli/server/add.go:202 +msgid "use local server" +msgstr "" + +#: ./pkg/lint/recipe.go:97 +msgid "use semver-like tags" +msgstr "" + +#: ./cli/app/run.go:110 ./cli/app/cmd.go:256 +msgid "user" +msgstr "" + +#: ./pkg/config/abra.go:88 +msgid "using default abra dir" +msgstr "" + +#: ./cli/app/restore.go:116 ./cli/app/backup.go:280 ./cli/server/prune.go:99 ./cli/recipe/version.go:18 +msgid "v" +msgstr "" + +#: ./cli/internal/validate.go:106 +#, c-format +msgid "validated %s as app argument" +msgstr "" + +#: ./cli/internal/validate.go:132 +#, c-format +msgid "validated %s as domain argument" +msgstr "" + +#: ./cli/internal/validate.go:88 +#, c-format +msgid "validated %s as recipe argument" +msgstr "" + +#: ./cli/internal/validate.go:174 +#, c-format +msgid "validated %s as server argument" +msgstr "" + +#: ./pkg/lint/recipe.go:170 +msgid "vendor config versions in an abra.sh" +msgstr "" + +#: ./pkg/app/app.go:683 +#, c-format +msgid "version %s saved to %s.env" +msgstr "" + +#: ./pkg/recipe/recipe.go:200 +#, c-format +msgid "version : %s, " +msgstr "" + +#: ./pkg/recipe/recipe.go:130 +#, c-format +msgid "version seems invalid: %s" +msgstr "" + +#: ./pkg/app/app.go:622 +#, c-format +msgid "version wiped from %s.env" +msgstr "" + +#: ./cli/app/deploy.go:303 +#, c-format +msgid "version: can not redeploy chaos version %s" +msgstr "" + +#: ./cli/app/deploy.go:290 +#, c-format +msgid "version: taking chaos version: %s" +msgstr "" + +#: ./cli/app/deploy.go:311 +#, c-format +msgid "version: taking deployed version: %s" +msgstr "" + +#: ./cli/app/deploy.go:316 +#, c-format +msgid "version: taking new recipe version: %s" +msgstr "" + +#: ./cli/app/deploy.go:305 +#, c-format +msgid "version: taking version from .env file: %s" +msgstr "" + +#: ./cli/app/deploy.go:296 +#, c-format +msgid "version: taking version from cli arg: %s" +msgstr "" + +#: ./cli/recipe/version.go:17 +msgid "versions [flags]" +msgstr "" + +#: ./cli/app/volume.go:191 +msgid "vl" +msgstr "" + +#: ./cli/app/volume.go:153 +#, c-format +msgid "volume %s removed successfully" +msgstr "" + +#: ./pkg/client/volumes.go:41 +#, c-format +msgid "volume %s: %s" +msgstr "" + +#: ./cli/app/volume.go:190 +msgid "volume [cmd] [args] [flags]" +msgstr "" + +#: ./pkg/upstream/convert/volume.go:93 +msgid "volume options are incompatible with type bind" +msgstr "" + +#: ./pkg/upstream/convert/volume.go:133 +msgid "volume options are incompatible with type npipe" +msgstr "" + +#: ./pkg/upstream/convert/volume.go:116 +msgid "volume options are incompatible with type tmpfs" +msgstr "" + +#: ./pkg/upstream/convert/volume.go:162 +msgid "volume type must be volume, bind, tmpfs or npipe" +msgstr "" + +#: ./cli/app/restore.go:115 ./cli/app/backup.go:279 ./cli/server/prune.go:98 +msgid "volumes" +msgstr "" + +#: ./cli/app/backup.go:266 +msgid "volumes path" +msgstr "" + +#: ./cli/server/prune.go:75 +#, c-format +msgid "volumes pruned: %d; space reclaimed: %s" +msgstr "" + +#: ./pkg/lint/recipe.go:21 ./pkg/lint/recipe.go:67 ./pkg/lint/recipe.go:74 ./pkg/lint/recipe.go:81 ./pkg/lint/recipe.go:88 ./pkg/lint/recipe.go:95 ./pkg/lint/recipe.go:102 ./pkg/lint/recipe.go:109 ./pkg/lint/recipe.go:116 ./pkg/lint/recipe.go:123 +msgid "warn" +msgstr "" + +#: ./pkg/container/container.go:52 +msgid "which container are you looking for?" +msgstr "" + +#: ./cli/app/config.go:42 +msgid "which editor do you wish to use?" +msgstr "" + +#: ./pkg/service/service.go:61 ./pkg/service/service.go:115 +msgid "which service are you looking for?" +msgstr "" + +#: ./cli/recipe/sync.go:96 +msgid "which version do you want to begin with?" +msgstr "" + +#: ./cli/app/volume.go:161 +msgid "which volumes do you want to remove?" +msgstr "" + +#: ./pkg/lint/recipe.go:76 +msgid "wire up healthchecks" +msgstr "" + +#: ./pkg/ui/deploy.go:87 +#, c-format +msgid "writer: %v, " +msgstr "" + +#: ./cli/app/upgrade.go:274 ./cli/app/undeploy.go:107 ./cli/app/new.go:224 ./cli/app/rollback.go:227 ./cli/app/deploy.go:236 +#, c-format +msgid "writing recipe version failed: %s" +msgstr "" + +#: ./cli/recipe/upgrade.go:346 ./cli/recipe/sync.go:231 ./cli/recipe/release.go:585 +msgid "x" +msgstr "" + +#: ./cli/recipe/upgrade.go:354 ./cli/recipe/sync.go:239 ./cli/recipe/release.go:593 +msgid "y" +msgstr "" + +#: ./cli/recipe/release.go:430 +msgid "you can only use one of: --major, --minor, --patch" +msgstr "" + +#: ./cli/recipe/upgrade.go:75 +msgid "you can only use one of: --major, --minor, --patch." +msgstr "" + +#: ./cli/recipe/sync.go:154 +msgid "you can only use one version flag: --major, --minor or --patch" +msgstr "" + +#: ./cli/recipe/upgrade.go:362 ./cli/recipe/sync.go:247 ./cli/recipe/release.go:601 +msgid "z" +msgstr "" + +#: ./pkg/ui/deploy.go:82 +#, c-format +msgid "{decoder: %v, " +msgstr "" + +#: ./pkg/recipe/recipe.go:199 +#, c-format +msgid "{name: %s, " +msgstr "" + diff --git a/pkg/i18n/locales/es.mo b/pkg/i18n/locales/es.mo new file mode 100644 index 00000000..064f0e63 Binary files /dev/null and b/pkg/i18n/locales/es.mo differ diff --git a/pkg/i18n/locales/es.po b/pkg/i18n/locales/es.po new file mode 100644 index 00000000..95f8ba1d --- /dev/null +++ b/pkg/i18n/locales/es.po @@ -0,0 +1,4903 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: EMAIL\n" +"POT-Creation-Date: 2025-08-23 17:54+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: cli/app/cp.go:32 +msgid "" +" # copy myfile.txt to the root of the app service\n" +" abra app cp 1312.net myfile.txt app:/\n" +"\n" +" # copy that file back to your current working directory locally\n" +" abra app cp 1312.net app:/myfile.txt ./" +msgstr "" + +#: cli/app/volume.go:88 +msgid "" +" # delete volumes interactively\n" +" abra app volume rm 1312.net\n" +"\n" +" # delete specific volume\n" +" abra app volume rm 1312.net my_volume" +msgstr "" + +#: cli/recipe/fetch.go:23 +msgid "" +" # fetch from recipe catalogue\n" +" abra recipe fetch gitea\n" +"\n" +" # fetch from remote recipe\n" +" abra recipe fetch git.foo.org/recipes/myrecipe\n" +"\n" +" # fetch with ssh remote for hacking\n" +" abra recipe fetch gitea --ssh" +msgstr "" + +#: cli/run.go:84 +msgid "" +" # generate the man pages into /usr/local/share/man/man1\n" +" abra_path=$(which abra) # pass abra absolute path to sudo below\n" +" sudo $abra_path man\n" +" sudo mandb\n" +"\n" +" # read the man pages\n" +" man abra\n" +" man abra-app-deploy" +msgstr "" + +#: cli/app/secret.go:154 +msgid "" +" # insert regular secret\n" +" abra app secret insert 1312.net my_secret v1 mySuperSecret\n" +"\n" +" # insert secret as file\n" +" abra app secret insert 1312.net my_secret v1 secret.txt -f" +msgstr "" + +#: cli/app/list.go:49 +msgid "" +" # list apps of all servers without live status\n" +" abra app ls\n" +"\n" +" # list apps of a specific server with live status\n" +" abra app ls -s 1312.net -S\n" +"\n" +" # list apps of all servers which match a specific recipe\n" +" abra app ls -r gitea" +msgstr "" + +#: cli/app/cmd.go:35 +msgid "" +" # pass args/flags without \"--\"\n" +" abra app cmd 1312.net app my_cmd_arg foo --user bar\n" +"\n" +" # pass args/flags with \"--\"\n" +" abra app cmd 1312.net app my_cmd_args --user bar -- foo -vvv\n" +"\n" +" # drop the [service] arg if using \"--local/-l\"\n" +" abra app cmd 1312.net my_cmd --local" +msgstr "" + +#: cli/app/restart.go:29 +msgid "" +" # restart a single app service\n" +" abra app restart 1312.net app\n" +"\n" +" # restart all app services\n" +" abra app restart 1312.net -a" +msgstr "" + +#: cli/app/run.go:24 +msgid "" +" # run with args/flags\n" +" abra app run 1312.net app -- ls -lha\n" +"\n" +" # run without args/flags\n" +" abra app run 1312.net app bash --user nobody\n" +"\n" +" # run with both kinds of args/flags \n" +" abra app run 1312.net app --user nobody -- ls -lha" +msgstr "" + +#: cli/app/deploy.go:36 +msgid "" +" # standard deployment\n" +" abra app deploy 1312.net\n" +"\n" +" # chaos deployment\n" +" abra app deploy 1312.net --chaos\n" +" \n" +" # deploy specific version\n" +" abra app deploy 1312.net 2.0.0+1.2.3\n" +"\n" +" # deploy a specific git hash\n" +" abra app deploy 1312.net 886db76d" +msgstr "" + +#: cli/app/env.go:18 +msgid " abra app env 1312.net" +msgstr "" + +#: cli/app/labels.go:27 +msgid " abra app labels 1312.net" +msgstr "" + +#: cli/app/remove.go:38 +msgid " abra app remove 1312.net" +msgstr "" + +#: cli/app/secret.go:267 +msgid " abra app secret rm 1312.net oauth_key" +msgstr "" + +#: cli/app/config.go:19 +msgid " abra config 1312.net" +msgstr "" + +#: cli/server/add.go:40 +msgid " abra server add 1312.net" +msgstr "" + +#: cli/upgrade.go:26 +msgid " abra upgrade --rc" +msgstr "" + +#: cli/app/rollback.go:42 +msgid "" +" # standard rollback\n" +" abra app rollback 1312.net\n" +"\n" +" # rollback to specific version\n" +" abra app rollback 1312.net 2.0.0+1.2.3" +msgstr "" + +#: pkg/formatter/formatter.go:46 +msgid " ago" +msgstr "" + +#: cli/app/remove.go:92 +#, c-format +msgid "%d config(s) removed successfully" +msgstr "" + +#: pkg/client/volumes.go:62 +#, c-format +msgid "%d retries failed" +msgstr "" + +#: cli/app/remove.go:139 +#, c-format +msgid "%d volume(s) removed successfully" +msgstr "" + +#: cli/app/volume.go:182 +#, c-format +msgid "%d volumes removed successfully" +msgstr "" + +#: cli/updater/updater.go:231 +#, c-format +msgid "%s (%s) can be upgraded from version %s to %s" +msgstr "" + +#: pkg/recipe/git.go:191 +#, c-format +msgid "%s (%s) has locally unstaged changes?" +msgstr "" + +#: pkg/service/service.go:103 pkg/container/container.go:40 +#, c-format +msgid "%s (created %v)" +msgstr "" + +#: cli/internal/deploy.go:86 +#, c-format +msgid "%s OVERVIEW" +msgstr "" + +#: cli/server/add.go:120 pkg/secret/secret.go:223 pkg/secret/secret.go:243 +#: pkg/server/server.go:21 +#, c-format +msgid "%s already exists" +msgstr "" + +#: pkg/app/app.go:380 +#, c-format +msgid "%s already exists?" +msgstr "" + +#: cli/app/new.go:199 +#, c-format +msgid "%s created (version: %s)" +msgstr "" + +#: cli/recipe/upgrade.go:330 cli/recipe/sync.go:211 cli/recipe/release.go:121 +#, c-format +msgid "%s currently has these unstaged changes 👇" +msgstr "" + +#: cli/internal/command.go:70 +#, c-format +msgid "%s does not exist for %s, use /bin/sh as fallback" +msgstr "" + +#: cli/app/cmd.go:108 cli/internal/deploy.go:151 +#, c-format +msgid "%s does not exist for %s?" +msgstr "" + +#: cli/app/cmd.go:137 +#, c-format +msgid "%s does not exist locally, use /bin/sh as fallback" +msgstr "" + +#: cli/app/secret.go:75 +#, c-format +msgid "%s doesn't exist in the env config?" +msgstr "" + +#: cli/app/secret.go:362 +#, c-format +msgid "%s doesn't exist on server?" +msgstr "" + +#: cli/internal/command.go:110 +#, c-format +msgid "%s doesn't have a %s function" +msgstr "" + +#: cli/app/restart.go:132 +#, c-format +msgid "%s has been scaled to 0" +msgstr "" + +#: cli/app/restart.go:143 +#, c-format +msgid "%s has been scaled to 1" +msgstr "" + +#: pkg/catalogue/catalogue.go:40 +#, c-format +msgid "" +"%s has locally unstaged changes? please commit/remove your changes before " +"proceeding" +msgstr "" + +#: cli/internal/recipe.go:112 +#, c-format +msgid "%s has no main 'app' service?" +msgstr "" + +#: cli/recipe/version.go:53 +#, c-format +msgid "%s has no published versions?" +msgstr "" + +#: cli/app/new.go:284 +#, c-format +msgid "%s has no secrets to generate, skipping..." +msgstr "" + +#: pkg/recipe/compose.go:107 +#, c-format +msgid "%s has no version label? try running \"abra recipe sync %s\" first?" +msgstr "" + +#: pkg/secret/pass.go:29 +#, c-format +msgid "%s inserted into pass store" +msgstr "" + +#: cli/app/deploy.go:96 +#, c-format +msgid "%s is already deployed" +msgstr "" + +#: cli/recipe/fetch.go:55 +#, c-format +msgid "%s is already fetched" +msgstr "" + +#: pkg/recipe/compose.go:233 +#, c-format +msgid "%s is already set, nothing to do?" +msgstr "" + +#: pkg/recipe/compose.go:72 +#, c-format +msgid "%s is missing a compose.yml or compose.*.yml file?" +msgstr "" + +#: pkg/app/app.go:267 +#, c-format +msgid "%s is missing the TYPE env var?" +msgstr "" + +#: cli/app/rollback.go:280 cli/app/rollback.go:284 +#, c-format +msgid "%s is not a downgrade for %s?" +msgstr "" + +#: cli/app/upgrade.go:402 cli/app/upgrade.go:406 +#, c-format +msgid "%s is not an upgrade for %s?" +msgstr "" + +#: cli/app/ps.go:56 cli/app/upgrade.go:423 cli/app/undeploy.go:59 +#: cli/app/services.go:49 cli/app/restart.go:93 cli/app/logs.go:58 +#: cli/updater/updater.go:247 +#, c-format +msgid "%s is not deployed?" +msgstr "" + +#: cli/server/remove.go:43 +#, c-format +msgid "%s is now lost in time, like tears in rain" +msgstr "" + +#: cli/app/remove.go:73 cli/app/volume.go:120 +#, c-format +msgid "%s is still deployed. Run \"abra app undeploy %s\"" +msgstr "" + +#: cli/app/upgrade.go:208 cli/app/deploy.go:166 +#, c-format +msgid "%s missing from %s.env" +msgstr "" + +#: cli/recipe/upgrade.go:145 +#, c-format +msgid "%s not considered semver-like" +msgstr "" + +#: pkg/secret/secret.go:130 +#, c-format +msgid "%s not enabled in recipe config, skipping" +msgstr "" + +#: cli/recipe/new.go:50 +#, c-format +msgid "%s recipe directory already exists?" +msgstr "" + +#: pkg/recipe/files.go:36 +#, c-format +msgid "%s release notes:" +msgstr "" + +#: pkg/secret/pass.go:51 +#, c-format +msgid "%s removed from pass store" +msgstr "" + +#: cli/app/new.go:135 +#, c-format +msgid "%s sanitised as %s for new app" +msgstr "" + +#: pkg/recipe/git.go:434 +#, c-format +msgid "%s service is missing image tag?" +msgstr "" + +#: cli/app/restart.go:144 +#, c-format +msgid "%s service successfully restarted" +msgstr "" + +#: cli/server/add.go:111 +#, c-format +msgid "%s successfully added" +msgstr "" + +#: cli/app/secret.go:230 +#, c-format +msgid "%s successfully stored on server" +msgstr "" + +#: cli/recipe/new.go:53 +#, c-format +msgid "%s/example.git" +msgstr "" + +#: pkg/ui/deploy.go:341 +#, c-format +msgid "%s: %s (retries: %v, healthcheck: %s)" +msgstr "" + +#: pkg/app/app.go:516 +#, c-format +msgid "%s: %s: %s" +msgstr "" + +#: pkg/recipe/recipe.go:237 +#, c-format +msgid "%s: attempt recipe metadata parse" +msgstr "" + +#: pkg/recipe/recipe.go:368 +#, c-format +msgid "%s: end marker %s not found" +msgstr "" + +#: pkg/recipe/recipe.go:325 +#, c-format +msgid "%s: image meta has incorrect format: %s" +msgstr "" + +#: pkg/recipe/recipe.go:330 +#, c-format +msgid "%s: image meta is empty?" +msgstr "" + +#: pkg/recipe/recipe.go:361 +#, c-format +msgid "%s: marker string %s not found" +msgstr "" + +#: pkg/dns/dns.go:18 +#, c-format +msgid "%s: no IPv4 available" +msgstr "" + +#: pkg/lint/recipe.go:52 +#, c-format +msgid "%s: skip condition: %s" +msgstr "" + +#: pkg/dns/dns.go:14 +#, c-format +msgid "%s: unable to resolve IPv4 address: %s" +msgstr "" + +#: pkg/client/volumes.go:58 +#, c-format +msgid "%s: waiting %d seconds before next retry" +msgstr "" + +#: cli/app/upgrade.go:397 +#, c-format +msgid "'%s' is not a known version" +msgstr "" + +#: cli/app/upgrade.go:392 cli/app/rollback.go:275 +#, c-format +msgid "'%s' is not a known version for %s" +msgstr "" + +#: cli/app/volume.go:162 +msgid "" +"'x' indicates selected, enter / return to confirm, ctrl-c to exit, vim mode " +"is enabled" +msgstr "" + +#: cli/app/cmd.go:119 +#, c-format +msgid "--local detected, running %s on local work station" +msgstr "" + +#: pkg/lint/recipe.go:133 +msgid ".env.sample provided" +msgstr "" + +#: cli/recipe/new.go:37 +msgid "A community managed recipe template is used." +msgstr "" + +#: cli/recipe/recipe.go:13 +msgid "" +"A recipe is a blueprint for an app.\n" +"\n" +"It is a bunch of config files which describe how to deploy and maintain an " +"app.\n" +"Recipes are maintained by the Co-op Cloud community and you can use Abra to\n" +"read them, deploy them and create apps for you.\n" +"\n" +"Anyone who uses a recipe can become a maintainer. Maintainers typically " +"make\n" +"sure the recipe is in good working order and the config upgraded in a " +"timely\n" +"manner." +msgstr "" + +#: cli/app/remove.go:50 +#, c-format +msgid "ALERTA ALERTA: deleting %s data and config (local/remote)" +msgstr "" + +#: cli/app/list.go:223 +msgid "AUTOUPDATE" +msgstr "" + +#: cli/server/add.go:23 +msgid "Add a new server" +msgstr "" + +#: cli/server/add.go:24 +msgid "" +"Add a new server to your configuration so that it can be managed by Abra.\n" +"\n" +"Abra relies on the standard SSH command-line and ~/.ssh/config for client\n" +"connection details. You must configure an entry per-host in your ~/.ssh/" +"config\n" +"for each server:\n" +"\n" +" Host 1312.net 1312\n" +" Hostname 1312.net\n" +" User antifa\n" +" Port 12345\n" +" IdentityFile ~/.ssh/antifa@somewhere\n" +"\n" +"If \"--local\" is passed, then Abra assumes that the current local server " +"is\n" +"intended as the target server. This is useful when you want to have your " +"entire\n" +"Co-op Cloud config located on the server itself, and not on your local\n" +"developer machine. The domain is then set to \"default\"." +msgstr "" + +#: cli/app/labels.go:26 +msgid "Both local recipe and live deployment labels are shown." +msgstr "" + +#: cli/app/secret.go:476 cli/app/secret.go:516 cli/app/secret.go:540 +#: cli/app/secret.go:548 cli/app/restore.go:132 cli/app/ps.go:207 +#: cli/app/cmd.go:273 cli/app/new.go:380 cli/app/check.go:88 +#: cli/app/restart.go:155 cli/app/deploy.go:327 cli/app/cp.go:379 +#: cli/app/backup.go:288 cli/app/backup.go:304 cli/app/labels.go:136 +#: cli/catalogue/catalogue.go:272 cli/recipe/lint.go:129 +#: cli/updater/updater.go:538 +msgid "C" +msgstr "" + +#: cli/app/ps.go:183 cli/app/list.go:220 +msgid "CHAOS" +msgstr "" + +#: cli/internal/deploy.go:120 +msgid "CHAOS DEPLOY" +msgstr "" + +#: pkg/recipe/compose.go:47 +#, c-format +msgid "COMPOSE_FILE (=\"%s\") parsing failed?" +msgstr "" + +#: pkg/recipe/compose.go:58 +#, c-format +msgid "COMPOSE_FILE detected (%s), loading %s" +msgstr "" + +#: pkg/recipe/compose.go:38 +#, c-format +msgid "COMPOSE_FILE detected, loading %s" +msgstr "" + +#: cli/internal/deploy.go:78 +msgid "CONFIG" +msgstr "" + +#: cli/app/secret.go:394 +msgid "CREATED ON SERVER" +msgstr "" + +#: cli/internal/deploy.go:80 +msgid "CURRENT DEPLOYMENT" +msgstr "" + +#: cli/app/ps.go:30 +msgid "Check app deployment status" +msgstr "" + +#: cli/updater/updater.go:37 +msgid "Check for available upgrades" +msgstr "" + +#: cli/recipe/fetch.go:20 +msgid "Clone recipe(s) locally" +msgstr "" + +#: cli/app/check.go:20 +msgid "" +"Compare env vars in both the app \".env\" and recipe \".env.sample\" file.\n" +"\n" +"The goal is to ensure that recipe \".env.sample\" env vars are defined in " +"your\n" +"app \".env\" file. Only env var definitions in the \".env.sample\" which " +"are\n" +"uncommented, e.g. \"FOO=bar\" are checked. If an app \".env\" file does not " +"include\n" +"these env vars, then \"check\" will complain.\n" +"\n" +"Recipe maintainers may or may not provide defaults for env vars within " +"their\n" +"recipes regardless of commenting or not (e.g. through the use of\n" +"${FOO:} syntax). \"check\" does not confirm or deny this for you." +msgstr "" + +#: cli/app/cp.go:31 +msgid "Copy files to/from a deployed app service" +msgstr "" + +#: cli/app/new.go:49 +msgid "Create a new app" +msgstr "" + +#: cli/recipe/new.go:36 +msgid "Create a new recipe" +msgstr "" + +#: cli/app/backup.go:136 +msgid "Create a new snapshot" +msgstr "" + +#: cli/recipe/release.go:29 +msgid "" +"Create a new version of a recipe.\n" +"\n" +"These versions are then published on the Co-op Cloud recipe catalogue. " +"These\n" +"versions take the following form:\n" +"\n" +" a.b.c+x.y.z\n" +"\n" +"Where the \"a.b.c\" part is a semantic version determined by the maintainer. " +"The\n" +"\"x.y.z\" part is the image tag of the recipe \"app\" service (the main " +"container\n" +"which contains the software to be used, by naming convention).\n" +"\n" +"We maintain a semantic versioning scheme (\"a.b.c\") alongside the recipe\n" +"versioning scheme (\"x.y.z\") in order to maximise the chances that the " +"nature of\n" +"recipe updates are properly communicated. I.e. developers of an app might\n" +"publish a minor version but that might lead to changes in the recipe which " +"are\n" +"major and therefore require intervention while doing the upgrade work.\n" +"\n" +"Publish your new release to git.coopcloud.tech with \"--publish/-p\". This\n" +"requires that you have permission to git push to these repositories and " +"have\n" +"your SSH keys configured on your account." +msgstr "" + +#: pkg/formatter/formatter.go:134 +msgid "CreateOverview: only accepts rows of len == 2" +msgstr "" + +#: cli/app/new.go:24 +msgid "" +"Creates a new app from a default recipe.\n" +"\n" +"This new app configuration is stored in your $ABRA_DIR directory under the\n" +"appropriate server.\n" +"\n" +"This command does not deploy your app for you. You will need to run \"abra " +"app\n" +"deploy \" to do so.\n" +"\n" +"You can see what recipes are available (i.e. values for the [recipe] " +"argument)\n" +"by running \"abra recipe ls\".\n" +"\n" +"Recipe commit hashes are supported values for \"[version]\".\n" +"\n" +"Passing the \"--secrets/-S\" flag will automatically generate secrets for " +"your\n" +"app and store them encrypted at rest on the chosen target server. These\n" +"generated secrets are only visible at generation time, so please take care " +"to\n" +"store them somewhere safe.\n" +"\n" +"You can use the \"--pass/-P\" to store these generated passwords locally in " +"a\n" +"pass store (see passwordstore.org for more). The pass command must be " +"available\n" +"on your $PATH." +msgstr "" + +#: cli/app/upgrade.go:443 cli/app/new.go:356 cli/app/rollback.go:332 +#: cli/app/deploy.go:343 +msgid "D" +msgstr "" + +#: cli/internal/deploy.go:133 cli/internal/deploy.go:137 +msgid "DEPLOY" +msgstr "" + +#: cli/app/labels.go:53 +msgid "DEPLOYED LABELS" +msgstr "" + +#: cli/app/list.go:216 cli/internal/deploy.go:75 +msgid "DOMAIN" +msgstr "" + +#: cli/internal/deploy.go:142 +msgid "DOWNGRADE" +msgstr "" + +#: cli/app/deploy.go:30 +msgid "Deploy an app" +msgstr "" + +#: cli/app/deploy.go:31 +msgid "" +"Deploy an app.\n" +"\n" +"This command supports chaos operations. Use \"--chaos/-C\" to deploy your " +"recipe\n" +"checkout as-is. Recipe commit hashes are also supported as values for\n" +"\"[version]\". Please note, \"upgrade\"/\"rollback\" do not support chaos " +"operations." +msgstr "" + +#: cli/app/services.go:23 +msgid "Display all services of an app" +msgstr "" + +#: cli/app/backup.go:67 +msgid "Download a snapshot" +msgstr "" + +#: cli/app/backup.go:68 +msgid "" +"Downloads a backup.tar.gz to the current working directory.\n" +"\n" +"\"--volumes/-v\" includes data contained in volumes alongide paths specified " +"in\n" +"\"backupbot.backup.path\" labels." +msgstr "" + +#: cli/app/env.go:41 +msgid "ENV OVERVIEW" +msgstr "" + +#: cli/internal/deploy.go:81 +msgid "ENV VERSION" +msgstr "" + +#: cli/app/config.go:18 +msgid "Edit app config" +msgstr "" + +#: cli/app/check.go:19 +msgid "Ensure an app is well configured" +msgstr "" + +#: pkg/upstream/container/exec.go:113 +#, c-format +msgid "Error hijack: %s" +msgstr "" + +#: pkg/upstream/container/exec.go:108 +msgid "Error monitoring TTY size:" +msgstr "" + +#: cli/app/secret.go:394 +msgid "GENERATED NAME" +msgstr "" + +#: cli/catalogue/catalogue.go:27 +msgid "" +"Generate a new copy of the recipe catalogue.\n" +"\n" +"N.B. this command **will** wipe local unstaged changes from your local " +"recipes\n" +"if present. \"--chaos/-C\" on this command refers to the catalogue " +"repository\n" +"(\"$ABRA_DIR/catalogue\") and not the recipes. Please take care not to lose " +"your\n" +"changes.\n" +"\n" +"It is possible to generate new metadata for a single recipe by passing\n" +"[recipe]. The existing local catalogue will be updated, not overwritten.\n" +"\n" +"It is quite easy to get rate limited by Docker Hub when running this " +"command.\n" +"If you have a Hub account you can \"docker login\" and Abra will " +"automatically\n" +"use those details.\n" +"\n" +"Push your new release to git.coopcloud.tech with \"--publish/-p\". This " +"requires\n" +"that you have permission to git push to these repositories and have your " +"SSH\n" +"keys configured on your account." +msgstr "" + +#: cli/app/list.go:46 +msgid "" +"Generate a report of all managed apps.\n" +"\n" +"Use \"--status/-S\" flag to query all servers for the live deployment status." +msgstr "" + +#: cli/app/new.go:290 +msgid "Generate app secrets?" +msgstr "" + +#: cli/complete.go:12 +msgid "Generate autocompletion script" +msgstr "" + +#: cli/recipe/sync.go:23 +msgid "" +"Generate labels for the main recipe service.\n" +"\n" +"By convention, the service named \"app\" using the following format:\n" +"\n" +" coop-cloud.${STACK_NAME}.version=\n" +"\n" +"Where [version] can be specifed on the command-line or Abra can attempt to\n" +"auto-generate it for you. The configuration will be updated on the\n" +"local file system." +msgstr "" + +#: cli/run.go:83 +msgid "Generate manpage" +msgstr "" + +#: cli/app/secret.go:26 +msgid "Generate secrets" +msgstr "" + +#: cli/catalogue/catalogue.go:26 +msgid "Generate the recipe catalogue" +msgstr "" + +#: cli/recipe/new.go:118 +msgid "Git (user) name to do commits with" +msgstr "" + +#: cli/recipe/new.go:126 +msgid "Git email name to do commits with" +msgstr "" + +#: cli/app/restore.go:124 +msgid "H" +msgstr "" + +#: cli/server/list.go:34 +msgid "HOST" +msgstr "" + +#: cli/app/ps.go:181 cli/recipe/version.go:62 +msgid "IMAGE" +msgstr "" + +#: cli/app/secret.go:145 +msgid "Insert secret" +msgstr "" + +#: cli/app/labels.go:102 +msgid "LABELS OVERVIEW" +msgstr "" + +#: cli/recipe/lint.go:15 +msgid "Lint a recipe" +msgstr "" + +#: cli/app/cmd.go:198 +msgid "List all available commands" +msgstr "" + +#: cli/app/list.go:45 +msgid "List all managed apps" +msgstr "" + +#: cli/app/secret.go:374 +msgid "List all secrets" +msgstr "" + +#: cli/app/backup.go:180 +msgid "List all snapshots" +msgstr "" + +#: cli/server/list.go:20 +msgid "List managed servers" +msgstr "" + +#: cli/recipe/version.go:19 +msgid "List recipe versions" +msgstr "" + +#: cli/recipe/list.go:19 +msgid "List recipes" +msgstr "" + +#: cli/app/backup.go:17 +msgid "List the contents of a snapshot" +msgstr "" + +#: cli/app/volume.go:21 +msgid "List volumes associated with an app" +msgstr "" + +#: cli/app/backup.go:215 +msgid "Manage app backups" +msgstr "" + +#: cli/app/secret.go:444 +msgid "Manage app secrets" +msgstr "" + +#: cli/app/volume.go:192 +msgid "Manage app volumes" +msgstr "" + +#: cli/app/app.go:11 +msgid "Manage apps" +msgstr "" + +#: cli/recipe/recipe.go:12 +msgid "Manage recipes" +msgstr "" + +#: cli/server/server.go:12 +msgid "Manage servers" +msgstr "" + +#: cli/catalogue/catalogue.go:235 +msgid "Manage the recipe catalogue" +msgstr "" + +#: cli/recipe/new.go:116 +msgid "N" +msgstr "" + +#: cli/app/secret.go:106 cli/app/secret.go:394 cli/app/new.go:187 +#: cli/app/volume.go:47 cli/server/list.go:34 cli/recipe/version.go:103 +msgid "NAME" +msgstr "" + +#: cli/internal/deploy.go:129 +msgid "NEW DEPLOY" +msgstr "" + +#: cli/internal/deploy.go:82 +msgid "NEW DEPLOYMENT" +msgstr "" + +#: cli/app/secret.go:136 +msgid "NOT" +msgstr "" + +#: cli/app/secret.go:137 +msgid "NOW" +msgstr "" + +#: cli/updater/updater.go:38 +msgid "" +"Notify on new versions for deployed apps.\n" +"\n" +"If a new patch/minor version is available, a notification is printed.\n" +"\n" +"Use \"--major/-m\" to include new major versions." +msgstr "" + +#: cli/app/volume.go:47 +msgid "ON SERVER" +msgstr "" + +#: cli/server/prune.go:17 +msgid "Prune resources on a server" +msgstr "" + +#: cli/server/prune.go:18 +msgid "" +"Prunes unused containers, networks, and dangling images.\n" +"\n" +"Use \"--volumes/-v\" to remove volumes that are not associated with a " +"deployed\n" +"app. This can result in unwanted data loss if not used carefully." +msgstr "" + +#: pkg/lint/recipe.go:110 +msgid "README.md metadata filled in" +msgstr "" + +#: cli/app/list.go:216 cli/internal/deploy.go:76 +msgid "RECIPE" +msgstr "" + +#: cli/app/labels.go:74 +msgid "RECIPE LABELS" +msgstr "" + +#: cli/recipe/release.go:317 +msgid "Release Note (leave empty for no release note)" +msgstr "" + +#: cli/recipe/release.go:28 +msgid "Release a new recipe version" +msgstr "" + +#: cli/server/remove.go:19 +msgid "Remove a managed server" +msgstr "" + +#: cli/server/remove.go:20 +msgid "" +"Remove a managed server.\n" +"\n" +"Abra will remove the internal bookkeeping ($ABRA_DIR/servers/...) and\n" +"underlying client connection context. This server will then be lost in " +"time,\n" +"like tears in rain." +msgstr "" + +#: cli/app/secret.go:262 +msgid "Remove a secret" +msgstr "" + +#: cli/app/remove.go:21 +msgid "Remove all app data, locally and remotely" +msgstr "" + +#: cli/recipe/reset.go:15 +msgid "Remove all unstaged changes from recipe config" +msgstr "" + +#: cli/app/remove.go:22 +msgid "" +"Remove everything related to an app which is already undeployed.\n" +"\n" +"By default, it will prompt for confirmation before proceeding. All secrets,\n" +"volumes and the local app env file will be deleted.\n" +"\n" +"Only run this command when you are sure you want to completely remove the " +"app\n" +"and all associated app data. This is a destructive action, Be Careful!\n" +"\n" +"If you would like to delete specific volumes or secrets, please use removal\n" +"sub-commands under \"app volume\" and \"app secret\" instead.\n" +"\n" +"Please note, if you delete the local app env file without removing volumes " +"and\n" +"secrets first, Abra will *not* be able to help you remove them afterwards.\n" +"\n" +"To delete everything without prompt, use the \"--force/-f\" or the \"--no-" +"input/n\"\n" +"flag." +msgstr "" + +#: cli/app/volume.go:77 +msgid "Remove volume(s) associated with an app" +msgstr "" + +#: cli/app/volume.go:78 +msgid "" +"Remove volumes associated with an app.\n" +"\n" +"The app in question must be undeployed before you try to remove volumes. " +"See\n" +"\"abra app undeploy \" for more.\n" +"\n" +"The command is interactive and will show a multiple select input which " +"allows\n" +"you to make a seclection. Use the \"?\" key to see more help on navigating " +"this\n" +"interface.\n" +"\n" +"Passing \"--force/-f\" will select all volumes for removal. Be careful." +msgstr "" + +#: cli/app/restart.go:23 +msgid "Restart an app" +msgstr "" + +#: cli/app/restore.go:18 +msgid "Restore a snapshot" +msgstr "" + +#: cli/app/rollback.go:27 +msgid "Roll an app back to a previous version" +msgstr "" + +#: cli/app/run.go:23 +msgid "Run a command inside a service container" +msgstr "" + +#: cli/app/cmd.go:25 +msgid "" +"Run an app specific command.\n" +"\n" +"These commands are bash functions, defined in the abra.sh of the recipe " +"itself.\n" +"They can be run within the context of a service (e.g. app) or locally on " +"your\n" +"work station by passing \"--local/-l\".\n" +"\n" +"N.B. If using the \"--\" style to pass arguments, flags (e.g. \"--local/-" +"l\") must\n" +"be passed *before* the \"--\". It is possible to pass arguments without the " +"\"--\"\n" +"as long as no dashes are present (i.e. \"foo\" works without \"--\", \"-" +"foo\"\n" +"does not)." +msgstr "" + +#: cli/app/cmd.go:24 +msgid "Run app commands" +msgstr "" + +#: cli/app/new.go:372 cli/app/logs.go:102 cli/app/backup.go:272 +#: cli/app/list.go:289 +msgid "S" +msgstr "" + +#: cli/app/new.go:207 +msgid "SECRETS OVERVIEW" +msgstr "" + +#: cli/app/list.go:216 cli/internal/deploy.go:77 +msgid "SERVER" +msgstr "" + +#: cli/app/ps.go:179 cli/recipe/version.go:62 cli/recipe/version.go:103 +msgid "SERVICE" +msgstr "" + +#: cli/app/services.go:67 +msgid "SERVICE (LONG)" +msgstr "" + +#: cli/app/services.go:67 +msgid "SERVICE (SHORT)" +msgstr "" + +#: pkg/ssh/ssh.go:16 +#, c-format +msgid "SSH host key verification failed for %s" +msgstr "" + +#: cli/recipe/list.go:44 +msgid "SSO" +msgstr "" + +#: cli/app/ps.go:180 cli/app/list.go:219 +msgid "STATUS" +msgstr "" + +#: cli/app/new.go:315 +msgid "Select app server:" +msgstr "" + +#: cli/internal/validate.go:50 +msgid "Select recipe" +msgstr "" + +#: cli/app/env.go:17 +msgid "Show app .env values" +msgstr "" + +#: cli/app/labels.go:25 +msgid "Show deployment labels" +msgstr "" + +#: cli/recipe/diff.go:15 +msgid "Show unstaged changes in recipe config" +msgstr "" + +#: cli/app/restore.go:19 +msgid "" +"Snapshots are restored while apps are deployed.\n" +"\n" +"Some restore scenarios may require service / app restarts." +msgstr "" + +#: cli/internal/validate.go:120 +msgid "Specify a domain name" +msgstr "" + +#: cli/internal/validate.go:151 +msgid "Specify a server name" +msgstr "" + +#: cli/app/new.go:266 +msgid "Specify app domain" +msgstr "" + +#: cli/recipe/sync.go:22 +msgid "Sync recipe version label" +msgstr "" + +#: cli/app/cmd.go:265 +msgid "T" +msgstr "" + +#: cli/recipe/version.go:62 cli/recipe/version.go:103 +msgid "TAG" +msgstr "" + +#: cli/app/logs.go:20 +msgid "Tail app logs" +msgstr "" + +#: cli/updater/updater.go:473 +msgid "The Co-op Cloud auto-updater 🤖 🚀" +msgstr "" + +#: cli/run.go:25 +msgid "The Co-op Cloud command-line utility belt 🎩🐇" +msgstr "" + +#: cli/app/secret.go:146 +msgid "" +"This command inserts a secret into an app environment.\n" +"\n" +"Arbitrary secret insertion is not supported. Secrets that are inserted must\n" +"match those configured in the recipe beforehand.\n" +"\n" +"This can be useful when you want to manually generate secrets for an app\n" +"environment. Typically, you can let Abra generate them for you on app " +"creation\n" +"(see \"abra app new --secrets/-S\" for more)." +msgstr "" + +#: cli/app/secret.go:263 +msgid "" +"This command removes a secret from an app environment.\n" +"\n" +"Arbitrary secret removal is not supported. Secrets that are removed must\n" +"match those configured in the recipe beforehand." +msgstr "" + +#: cli/recipe/diff.go:16 +msgid "This command requires /usr/bin/git." +msgstr "" + +#: cli/app/restart.go:24 +msgid "" +"This command restarts services within a deployed app.\n" +"\n" +"Run \"abra app ps \" to see a list of service names.\n" +"\n" +"Pass \"--all-services/-a\" to restart all services." +msgstr "" + +#: cli/app/rollback.go:28 +msgid "" +"This command rolls an app back to a previous version.\n" +"\n" +"Unlike \"abra app deploy\", chaos operations are not supported here. Only " +"recipe\n" +"versions are supported values for \"[version]\".\n" +"\n" +"It is possible to \"--force/-f\" an downgrade if you want to re-deploy a " +"specific\n" +"version.\n" +"\n" +"Only the deployed version is consulted when trying to determine what " +"downgrades\n" +"are available. The live deployment version is the \"source of truth\" in " +"this\n" +"case. The stored .env version is not consulted.\n" +"\n" +"A downgrade can be destructive, please ensure you have a copy of your app " +"data\n" +"beforehand. See \"abra app backup\" for more." +msgstr "" + +#: cli/app/undeploy.go:25 +msgid "" +"This does not destroy any application data.\n" +"\n" +"However, you should remain vigilant, as your swarm installation will " +"consider\n" +"any previously attached volumes as eligible for pruning once undeployed.\n" +"\n" +"Passing \"--prune/-p\" does not remove those volumes." +msgstr "" + +#: cli/complete.go:13 +msgid "" +"To load completions:\n" +"\n" +"Bash:\n" +" # Load autocompletion for the current Bash session\n" +" $ source <(abra autocomplete bash)\n" +"\n" +" # To load autocompletion for each session, execute once:\n" +" # Linux:\n" +" $ abra autocomplete bash | sudo tee /etc/bash_completion.d/abra\n" +" # macOS:\n" +" $ abra autocomplete bash | sudo tee $(brew --prefix)/etc/bash_completion.d/" +"abra\n" +"\n" +"Zsh:\n" +" # If shell autocompletion is not already enabled in your environment,\n" +" # you will need to enable it. You can execute the following once:\n" +"\n" +" $ echo \"autoload -U compinit; compinit\" >> ~/.zshrc\n" +"\n" +" # To load autocompletions for each session, execute once:\n" +" $ abra autocomplete zsh > \"${fpath[1]}/_abra\"\n" +"\n" +" # You will need to start a new shell for this setup to take effect.\n" +"\n" +"fish:\n" +" $ abra autocomplete fish | source\n" +"\n" +" # To load autocompletions for each session, execute once:\n" +" $ abra autocomplete fish > ~/.config/fish/completions/abra.fish\n" +"\n" +"PowerShell:\n" +" PS> abra autocomplete powershell | Out-String | Invoke-Expression\n" +"\n" +" # To load autocompletions for every new session, run:\n" +" PS> abra autocomplete powershell > abra.ps1\n" +" # and source this file from your PowerShell profile." +msgstr "" + +#: cli/internal/deploy.go:123 +msgid "UNCHAOS DEPLOY" +msgstr "" + +#: cli/internal/deploy.go:117 +msgid "UNDEPLOY" +msgstr "" + +#: cli/app/list.go:222 cli/internal/deploy.go:140 +msgid "UPGRADE" +msgstr "" + +#: cli/app/undeploy.go:24 +msgid "Undeploy an app" +msgstr "" + +#: cli/recipe/upgrade.go:44 +msgid "" +"Upgrade a given configuration.\n" +"\n" +"It will update the relevant compose file tags on the local file system.\n" +"\n" +"Some image tags cannot be parsed because they do not follow some sort of\n" +"semver-like convention. In this case, all possible tags will be listed and " +"it\n" +"is up to the end-user to decide.\n" +"\n" +"The command is interactive and will show a select input which allows you to\n" +"make a seclection. Use the \"?\" key to see more help on navigating this\n" +"interface.\n" +"\n" +"You may invoke this command in \"wizard\" mode and be prompted for input." +msgstr "" + +#: cli/upgrade.go:18 +msgid "Upgrade abra" +msgstr "" + +#: cli/upgrade.go:19 +msgid "" +"Upgrade abra in-place with the latest stable or release candidate.\n" +"\n" +"By default, the latest stable release is downloaded.\n" +"\n" +"Use \"--rc/-r\" to install the latest release candidate. Please bear in mind " +"that\n" +"it may contain absolutely catastrophic deal-breaker bugs. Thank you very " +"much\n" +"for the testing efforts 💗" +msgstr "" + +#: cli/app/upgrade.go:31 +msgid "Upgrade an app" +msgstr "" + +#: cli/updater/updater.go:77 +msgid "" +"Upgrade an app by specifying stack name and recipe. \n" +"\n" +"Use \"--all\" to upgrade every deployed app.\n" +"\n" +"For each app with auto updates enabled, the deployed version is compared " +"with\n" +"the current recipe catalogue version. If a new patch/minor version is\n" +"available, the app is upgraded.\n" +"\n" +"To include major versions use the \"--major/-m\" flag. You probably don't " +"want\n" +"that as it will break things. Only apps that are not deployed with \"--" +"chaos/-C\"\n" +"are upgraded, to update chaos deployments use the \"--chaos/-C\" flag. Use " +"it\n" +"with care." +msgstr "" + +#: cli/app/upgrade.go:32 +msgid "" +"Upgrade an app.\n" +"\n" +"Unlike \"abra app deploy\", chaos operations are not supported here. Only " +"recipe\n" +"versions are supported values for \"[version]\".\n" +"\n" +"It is possible to \"--force/-f\" an upgrade if you want to re-deploy a " +"specific\n" +"version.\n" +"\n" +"Only the deployed version is consulted when trying to determine what " +"upgrades\n" +"are available. The live deployment version is the \"source of truth\" in " +"this\n" +"case. The stored .env version is not consulted.\n" +"\n" +"An upgrade can be destructive, please ensure you have a copy of your app " +"data\n" +"beforehand. See \"abra app backup\" for more." +msgstr "" + +#: cli/updater/updater.go:76 +msgid "Upgrade apps" +msgstr "" + +#: cli/recipe/upgrade.go:43 +msgid "Upgrade recipe image tags" +msgstr "" + +#: cli/recipe/release.go:283 +msgid "Use release note in release/next?" +msgstr "" + +#: cli/recipe/fetch.go:21 +msgid "" +"Using \"--force/-f\" Git syncs an existing recipe. It does not erase " +"unstaged changes." +msgstr "" + +#: cli/app/secret.go:106 cli/app/new.go:187 +msgid "VALUE" +msgstr "" + +#: cli/app/secret.go:394 cli/app/ps.go:182 cli/app/list.go:221 +#: cli/recipe/version.go:62 cli/recipe/version.go:103 +msgid "VERSION" +msgstr "" + +#: cli/recipe/reset.go:16 +msgid "WARNING: this will delete your changes. Be Careful." +msgstr "" + +#: pkg/upstream/container/hijack.go:156 +msgid "[hijack] End of stdin" +msgstr "" + +#: pkg/upstream/container/hijack.go:132 +msgid "[hijack] end of stdout" +msgstr "" + +#: cli/recipe/sync.go:76 +#, c-format +msgid "" +"\n" +"The following options are two types of initial semantic version that you " +"can\n" +"pick for %s that will be published in the recipe catalogue. This follows " +"the\n" +"semver convention (more on https://semver.org), here is a short cheatsheet\n" +"\n" +" 0.1.0: development release, still hacking. when you make a major " +"upgrade\n" +" you increment the \"y\" part (i.e. 0.1.0 -> 0.2.0) and only move " +"to\n" +" using the \"x\" part when things are stable.\n" +"\n" +" 1.0.0: public release, assumed to be working. you already have a stable\n" +" and reliable deployment of this app and feel relatively " +"confident\n" +" about it.\n" +"\n" +"If you want people to be able alpha test your current config for %s but " +"don't\n" +"think it is quite reliable, go with 0.1.0 and people will know that things " +"are\n" +"likely to change.\n" +"\n" +msgstr "" + +#: cli/internal/recipe.go:18 +#, c-format +msgid "" +"\n" +"You need to make a decision about what kind of an update this new recipe\n" +"version is. If someone else performs this upgrade, do they have to do some\n" +"migration work or take care of some breaking changes? This can be signaled " +"in\n" +"the version you specify on the recipe deploy label and is called a semantic\n" +"version.\n" +"\n" +"The latest published version is %s.\n" +"\n" +"Here is a semver cheat sheet (more on https://semver.org):\n" +"\n" +" major: new features/bug fixes, backwards incompatible (e.g 1.0.0 -> " +"2.0.0).\n" +" the upgrade won't work without some preparation work and others " +"need\n" +" to take care when performing it. \"it could go wrong\".\n" +"\n" +" minor: new features/bug fixes, backwards compatible (e.g. 0.1.0 -> " +"0.2.0).\n" +" the upgrade should Just Work and there are no breaking changes " +"in\n" +" the app and the recipe config. \"it should go fine\".\n" +"\n" +" patch: bug fixes, backwards compatible (e.g. 0.0.1 -> 0.0.2). this " +"upgrade\n" +" should also Just Work and is mostly to do with minor bug fixes\n" +" and/or security patches. \"nothing to worry about\".\n" +"\n" +msgstr "" + +#: pkg/lint/recipe.go:203 +#, c-format +msgid "" +"\n" +"lint %s: %s" +msgstr "" + +#: pkg/ui/deploy.go:121 +#, c-format +msgid "^%s" +msgstr "" + +#: cli/app/secret.go:484 cli/app/secret.go:524 cli/app/restart.go:162 +#: cli/app/app.go:10 cli/app/backup.go:240 cli/server/prune.go:91 +#: cli/server/add.go:22 cli/recipe/upgrade.go:378 cli/recipe/fetch.go:115 +#: cli/updater/updater.go:554 +msgid "a" +msgstr "" + +#: cli/app/remove.go:59 +msgid "aborting as requested" +msgstr "" + +#: cli/run.go:24 +msgid "abra [cmd] [args] [flags]" +msgstr "" + +#: cli/run.go:69 +#, c-format +msgid "abra version: %s, commit: %s, lang: %s" +msgstr "" + +#: pkg/recipe/recipe.go:208 +#, c-format +msgid "abra.sh: %s}" +msgstr "" + +#: cli/app/cmd.go:51 +msgid "accepts at most 2 args with --local/-l" +msgstr "" + +#: pkg/recipe/compose.go:249 +#, c-format +msgid "add '- \"%s\"' manually to the 'app' service in %s" +msgstr "" + +#: cli/server/add.go:21 +msgid "add [[server] | --local] [flags]" +msgstr "" + +#: pkg/app/app.go:510 +#, c-format +msgid "adding env vars to %s service config" +msgstr "" + +#: cli/app/secret.go:483 cli/app/secret.go:523 cli/app/backup.go:239 +#: cli/server/prune.go:90 cli/recipe/fetch.go:114 cli/updater/updater.go:553 +msgid "all" +msgstr "" + +#: pkg/lint/recipe.go:82 +msgid "all images use a tag" +msgstr "" + +#: pkg/lint/recipe.go:162 +msgid "all services have images" +msgstr "" + +#: cli/app/restart.go:161 +msgid "all-services" +msgstr "" + +#: cli/recipe/upgrade.go:377 +msgid "all-tags" +msgstr "" + +#: pkg/container/container.go:48 +msgid "ambiguous container list received, prompting for input" +msgstr "" + +#: pkg/service/service.go:57 pkg/service/service.go:111 +msgid "ambiguous service list received, prompting for input" +msgstr "" + +#: cli/run.go:28 +msgid "app" +msgstr "" + +#: cli/app/app.go:9 +msgid "app [cmd] [args] [flags]" +msgstr "" + +#: pkg/dns/dns.go:52 +#, c-format +msgid "app domain %s (%s) does not appear to resolve to app server %s (%s)?" +msgstr "" + +#: cli/recipe/release.go:185 +msgid "app service is missing image tag?" +msgstr "" + +#: cli/app/remove.go:53 +msgid "are you sure?" +msgstr "" + +#: pkg/recipe/git.go:156 +#, c-format +msgid "attempting to checkout '%s' as chaos commit" +msgstr "" + +#: cli/server/add.go:76 cli/server/add.go:103 +#, c-format +msgid "attempting to create client for %s" +msgstr "" + +#: pkg/secret/secret.go:212 +#, c-format +msgid "attempting to generate and store %s on %s" +msgstr "" + +#: cli/upgrade.go:37 pkg/secret/pass.go:23 pkg/secret/pass.go:45 +#, c-format +msgid "attempting to run %s" +msgstr "" + +#: cli/app/upgrade.go:269 cli/app/deploy.go:231 +#, c-format +msgid "attempting to run post deploy commands, saw: %s" +msgstr "" + +#: cli/app/restart.go:108 +#, c-format +msgid "attempting to scale %s to 0" +msgstr "" + +#: cli/app/restart.go:133 +#, c-format +msgid "attempting to scale %s to 1" +msgstr "" + +#: cli/run.go:29 +msgid "autocomplete" +msgstr "" + +#: cli/complete.go:11 +msgid "autocomplete [bash|zsh|fish|powershell]" +msgstr "" + +#: cli/app/secret.go:38 cli/app/secret.go:170 cli/app/secret.go:280 +#: cli/app/upgrade.go:58 cli/app/rollback.go:58 cli/app/deploy.go:59 +#: cli/app/logs.go:32 pkg/autocomplete/autocomplete.go:17 +#: pkg/autocomplete/autocomplete.go:32 pkg/autocomplete/autocomplete.go:43 +#: pkg/autocomplete/autocomplete.go:59 pkg/autocomplete/autocomplete.go:77 +#: pkg/autocomplete/autocomplete.go:93 pkg/autocomplete/autocomplete.go:99 +#: pkg/autocomplete/autocomplete.go:114 +#, c-format +msgid "autocomplete failed: %s" +msgstr "" + +#: cli/app/new.go:374 +msgid "automatically generate secrets" +msgstr "" + +#: cli/recipe/fetch.go:125 +msgid "automatically set ssh remote" +msgstr "" + +#: cli/updater/updater.go:299 +#, c-format +msgid "available updates for %s: %s" +msgstr "" + +#: cli/app/backup.go:214 +msgid "b" +msgstr "" + +#: cli/app/backup.go:213 +msgid "backup [cmd] [args] [flags]" +msgstr "" + +#: cli/recipe/list.go:41 +msgid "backups" +msgstr "" + +#: pkg/web/web.go:45 +#, c-format +msgid "bad status: %s" +msgstr "" + +#: pkg/upstream/convert/volume.go:113 +msgid "bind options are incompatible with type tmpfs" +msgstr "" + +#: pkg/upstream/convert/volume.go:47 +msgid "bind options are incompatible with type volume" +msgstr "" + +#: cli/updater/updater.go:176 +#, c-format +msgid "boolean label %s could not be found for %s, set default to false." +msgstr "" + +#: pkg/config/env.go:74 pkg/config/env.go:107 +#, c-format +msgid "broken symlink in your abra config folders: %s" +msgstr "" + +#: cli/app/upgrade.go:451 cli/app/rollback.go:340 cli/app/deploy.go:351 +#: cli/app/cp.go:30 cli/app/backup.go:135 +msgid "c" +msgstr "" + +#: pkg/recipe/git.go:52 +#, c-format +msgid "can not redeploy chaos version (%s) without --chaos" +msgstr "" + +#: cli/recipe/upgrade.go:318 +#, c-format +msgid "can upgrade service: %s, image: %s, tag: %s ::" +msgstr "" + +#: cli/app/cp.go:272 +msgid "can't copy dir to file" +msgstr "" + +#: cli/internal/validate.go:35 +#, c-format +msgid "can't read local recipes: %s" +msgstr "" + +#: cli/updater/updater.go:197 +#, c-format +msgid "can't separate key from value: %s (this variable is probably unset)" +msgstr "" + +#: pkg/catalogue/catalogue.go:59 pkg/recipe/git.go:245 +#, c-format +msgid "cannot ensure %s is up-to-date, no git remotes configured" +msgstr "" + +#: cli/app/config.go:36 pkg/app/app.go:52 +#, c-format +msgid "cannot find app with name %s" +msgstr "" + +#: cli/recipe/release.go:88 +#, c-format +msgid "cannot parse %s, invalid tag specified?" +msgstr "" + +#: pkg/dns/dns.go:38 pkg/dns/dns.go:47 +#, c-format +msgid "cannot resolve ipv4 for %s?" +msgstr "" + +#: cli/recipe/release.go:93 +msgid "cannot specify tag and bump type at the same time" +msgstr "" + +#: cli/app/secret.go:57 +msgid "cannot use '[secret] [version]' and '--all' together" +msgstr "" + +#: cli/app/cmd.go:101 +msgid "cannot use --local & --user together" +msgstr "" + +#: cli/recipe/fetch.go:48 +msgid "cannot use [recipe] and --all/-a together" +msgstr "" + +#: cli/app/secret.go:307 +msgid "cannot use [secret] and --all/-a together" +msgstr "" + +#: cli/server/add.go:53 +msgid "cannot use [server] and --local together" +msgstr "" + +#: cli/app/restart.go:68 +msgid "cannot use [service] and --all-services/-a together" +msgstr "" + +#: cli/app/new.go:70 cli/app/deploy.go:262 +msgid "cannot use [version] and --chaos together" +msgstr "" + +#: cli/run.go:30 +msgid "catalogue" +msgstr "" + +#: cli/catalogue/catalogue.go:234 +msgid "catalogue [cmd] [args] [flags]" +msgstr "" + +#: pkg/catalogue/catalogue.go:21 +msgid "catalogue is missing, retrieving now" +msgstr "" + +#: cli/recipe/list.go:38 +msgid "category" +msgstr "" + +#: cli/app/config.go:17 +msgid "cfg" +msgstr "" + +#: cli/app/secret.go:475 cli/app/secret.go:515 cli/app/secret.go:539 +#: cli/app/secret.go:547 cli/app/restore.go:131 cli/app/ps.go:206 +#: cli/app/cmd.go:272 cli/app/new.go:379 cli/app/check.go:87 +#: cli/app/restart.go:154 cli/app/deploy.go:326 cli/app/cp.go:378 +#: cli/app/backup.go:287 cli/app/backup.go:303 cli/app/labels.go:135 +#: cli/catalogue/catalogue.go:271 cli/recipe/lint.go:128 +#: cli/updater/updater.go:537 +msgid "chaos" +msgstr "" + +#: cli/app/check.go:17 +msgid "check [flags]" +msgstr "" + +#: cli/updater/updater.go:548 +msgid "check for major updates" +msgstr "" + +#: cli/app/upgrade.go:415 cli/app/undeploy.go:51 cli/app/deploy.go:88 +#, c-format +msgid "checking whether %s is already deployed" +msgstr "" + +#: cli/app/check.go:18 +msgid "chk" +msgstr "" + +#: pkg/recipe/recipe.go:74 +#, c-format +msgid "choosing %s as latest version of %s" +msgstr "" + +#: cli/recipe/sync.go:188 +#, c-format +msgid "choosing %s as new version for %s" +msgstr "" + +#: cli/app/rollback.go:145 +#, c-format +msgid "choosing %s as version to rollback" +msgstr "" + +#: cli/app/upgrade.go:152 +#, c-format +msgid "choosing %s as version to upgrade" +msgstr "" + +#: cli/recipe/release.go:249 +#, c-format +msgid "chore: publish %s release" +msgstr "" + +#: cli/catalogue/catalogue.go:191 +msgid "chore: publish new catalogue release changes" +msgstr "" + +#: cli/app/cmd.go:23 +msgid "cmd" +msgstr "" + +#: pkg/recipe/git.go:458 +#, c-format +msgid "collected %s for %s" +msgstr "" + +#: cli/catalogue/catalogue.go:89 +msgid "collecting catalogue metadata" +msgstr "" + +#: pkg/app/app.go:295 +#, c-format +msgid "collecting metadata from %v servers: %s" +msgstr "" + +#: pkg/recipe/recipe.go:549 +msgid "collecting recipe listing" +msgstr "" + +#: pkg/upstream/commandconn/commandconn.go:162 +#, c-format +msgid "command %v did not exit after %v: stderr=%q" +msgstr "" + +#: pkg/upstream/commandconn/commandconn.go:172 +#, c-format +msgid "" +"command %v has exited with %v, please make sure the URL is valid, and Docker " +"18.09 or later is installed on the remote host: stderr=%s" +msgstr "" + +#: cli/app/cmd.go:22 +msgid "" +"command [service | --local] [[args] [flags] | [flags] -- " +"[args]]" +msgstr "" + +#: pkg/upstream/commandconn/commandconn.go:239 +#, c-format +msgid "commandConn.Close: CloseRead: %v" +msgstr "" + +#: pkg/upstream/commandconn/commandconn.go:65 +#, c-format +msgid "commandconn (%s):" +msgstr "" + +#: pkg/upstream/commandconn/commandconn.go:141 +msgid "commandconn: failed to wait" +msgstr "" + +#: pkg/upstream/commandconn/commandconn.go:49 +#, c-format +msgid "commandconn: starting %s with %v" +msgstr "" + +#: pkg/lint/recipe.go:68 +msgid "compose config has expected version" +msgstr "" + +#: pkg/recipe/recipe.go:205 +#, c-format +msgid "compose: %s, " +msgstr "" + +#: pkg/client/configs.go:35 +#, c-format +msgid "conf %s: %s" +msgstr "" + +#: cli/app/config.go:16 +msgid "config [flags]" +msgstr "" + +#: pkg/config/abra.go:37 +#, c-format +msgid "config file loaded from: %s" +msgstr "" + +#: pkg/upstream/convert/service.go:153 pkg/upstream/convert/service.go:168 +#, c-format +msgid "config not found: %s" +msgstr "" + +#: pkg/lint/recipe.go:169 +msgid "config version are vendored" +msgstr "" + +#: pkg/ssh/ssh.go:20 +#, c-format +msgid "connection timed out for %s" +msgstr "" + +#: pkg/recipe/compose.go:191 +#, c-format +msgid "considering %s config(s) for label update" +msgstr "" + +#: pkg/recipe/compose.go:123 +#, c-format +msgid "considering %s config(s) for tag update" +msgstr "" + +#: cli/app/undeploy.go:128 cli/server/prune.go:45 +#, c-format +msgid "containers pruned: %d; space reclaimed: %s" +msgstr "" + +#: pkg/client/context.go:66 +msgid "context 'default' cannot be removed" +msgstr "" + +#: cli/server/add.go:163 +#, c-format +msgid "context for %s already exists" +msgstr "" + +#: pkg/context/context.go:34 +msgid "context lacks Docker endpoint" +msgstr "" + +#: cli/recipe/sync.go:197 pkg/recipe/compose.go:229 +#, c-format +msgid "coop-cloud.${STACK_NAME}.version=%s" +msgstr "" + +#: pkg/app/app.go:400 +#, c-format +msgid "copied & templated %s to %s" +msgstr "" + +#: cli/app/cp.go:164 +#, c-format +msgid "copy %s from local to %s on container" +msgstr "" + +#: cli/app/cp.go:245 +#, c-format +msgid "copy: %s" +msgstr "" + +#: pkg/ssh/ssh.go:18 +#, c-format +msgid "could not resolve hostname for %s" +msgstr "" + +#: pkg/envfile/envfile.go:73 +#, c-format +msgid "couldn't parse %s" +msgstr "" + +#: pkg/upstream/container/hijack.go:172 +#, c-format +msgid "couldn't send EOF: %s" +msgstr "" + +#: cli/app/cp.go:29 +msgid "cp [flags]" +msgstr "" + +#: cli/app/backup.go:134 +msgid "create [flags]" +msgstr "" + +#: pkg/lint/recipe.go:134 +msgid "create an example .env.sample" +msgstr "" + +#: cli/app/cp.go:145 cli/app/cp.go:184 +#, c-format +msgid "create remote directory: %s" +msgstr "" + +#: pkg/client/client.go:88 +#, c-format +msgid "created client for %s" +msgstr "" + +#: cli/recipe/release.go:387 +#, c-format +msgid "created tag %s at %s" +msgstr "" + +#: pkg/client/context.go:26 +#, c-format +msgid "created the %s context" +msgstr "" + +#: cli/server/add.go:168 +#, c-format +msgid "creating context with domain %s" +msgstr "" + +#: pkg/lint/recipe.go:22 +msgid "critical" +msgstr "" + +#: cli/recipe/lint.go:115 +#, c-format +msgid "critical errors present in %s config" +msgstr "" + +#: cli/app/rollback.go:270 +#, c-format +msgid "current deployment '%s' is not a known version for %s" +msgstr "" + +#: cli/recipe/release.go:492 +#, c-format +msgid "current: %s, new: %s, correct?" +msgstr "" + +#: cli/app/deploy.go:29 cli/app/backup.go:66 cli/recipe/diff.go:14 +#: cli/updater/updater.go:491 +msgid "d" +msgstr "" + +#: cli/updater/updater.go:490 +msgid "debug" +msgstr "" + +#: cli/app/secret.go:253 +#, c-format +msgid "deleted %s successfully from local pass store" +msgstr "" + +#: cli/app/secret.go:246 +#, c-format +msgid "deleted %s successfully from server" +msgstr "" + +#: cli/app/deploy.go:28 +msgid "deploy [version] [flags]" +msgstr "" + +#: pkg/lint/recipe.go:147 +msgid "deploy labels stanza present" +msgstr "" + +#: cli/internal/deploy.go:109 +msgid "deployment cancelled" +msgstr "" + +#: cli/app/cp.go:273 +msgid "destination directory does not exist" +msgstr "" + +#: pkg/recipe/git.go:367 +#, c-format +msgid "detected %s as tags for recipe %s" +msgstr "" + +#: pkg/formatter/formatter.go:80 +msgid "detected ABRA_CI=1" +msgstr "" + +#: cli/recipe/upgrade.go:199 +#, c-format +msgid "detected compatible upgradable tags %s for %s" +msgstr "" + +#: cli/recipe/upgrade.go:172 +#, c-format +msgid "detected potential upgradable tags %s for %s" +msgstr "" + +#: pkg/recipe/recipe.go:435 +#, c-format +msgid "detected versions %s for %s" +msgstr "" + +#: cli/app/cmd.go:131 cli/app/cmd.go:176 +msgid "did not detect any command arguments" +msgstr "" + +#: cli/recipe/upgrade.go:119 +#, c-format +msgid "did not find versions file for %s" +msgstr "" + +#: cli/recipe/diff.go:13 +msgid "diff [flags]" +msgstr "" + +#: pkg/recipe/recipe.go:202 +#, c-format +msgid "dir: %s, " +msgstr "" + +#: pkg/config/env.go:98 +#, c-format +msgid "directory is empty: %s" +msgstr "" + +#: pkg/recipe/recipe.go:201 +#, c-format +msgid "dirty: %v, " +msgstr "" + +#: cli/app/upgrade.go:453 cli/app/rollback.go:342 cli/app/deploy.go:353 +msgid "disable converge logic checks" +msgstr "" + +#: cli/app/upgrade.go:445 cli/app/rollback.go:334 cli/app/deploy.go:345 +msgid "disable public DNS checks" +msgstr "" + +#: cli/app/cmd.go:267 +msgid "disable remote TTY" +msgstr "" + +#: cli/recipe/release.go:552 +#, c-format +msgid "discovered %s as currently synced recipe label" +msgstr "" + +#: cli/app/run.go:105 +msgid "do not request a TTY" +msgstr "" + +#: pkg/ssh/ssh.go:26 +msgid "docker: is the daemon running / your user has docker permissions?" +msgstr "" + +#: cli/app/new.go:355 +msgid "domain" +msgstr "" + +#: cli/app/new.go:358 +msgid "domain name for app" +msgstr "" + +#: cli/run.go:108 +msgid "don't forget to run 'sudo mandb'" +msgstr "" + +#: cli/updater/updater.go:386 +#, c-format +msgid "don't update %s due to chaos deployment" +msgstr "" + +#: cli/updater/updater.go:396 +#, c-format +msgid "" +"don't update %s due to disabled auto updates or missing ENABLE_AUTO_UPDATE " +"env" +msgstr "" + +#: cli/updater/updater.go:376 +#, c-format +msgid "don't update %s due to missing recipe name" +msgstr "" + +#: cli/updater/updater.go:406 +#, c-format +msgid "don't update %s due to no new version" +msgstr "" + +#: cli/app/backup.go:65 +msgid "download [flags]" +msgstr "" + +#: pkg/git/add.go:22 +#, c-format +msgid "dry run: adding %s" +msgstr "" + +#: cli/recipe/release.go:277 +#, c-format +msgid "dry run: move release note from 'next' to %s" +msgstr "" + +#: pkg/git/commit.go:44 +msgid "dry run: no changes commited" +msgstr "" + +#: cli/recipe/release.go:342 +msgid "dry run: no changes committed" +msgstr "" + +#: cli/catalogue/catalogue.go:227 cli/recipe/release.go:394 +msgid "dry run: no changes published" +msgstr "" + +#: pkg/git/push.go:13 +#, c-format +msgid "dry run: no git changes pushed in %s" +msgstr "" + +#: cli/recipe/release.go:367 +#, c-format +msgid "dry run: no git tag created (%s)" +msgstr "" + +#: cli/recipe/sync.go:203 +#, c-format +msgid "dry run: not syncing label %s for recipe %s" +msgstr "" + +#: pkg/git/remote.go:15 +#, c-format +msgid "dry run: remote %s (%s) not created" +msgstr "" + +#: cli/catalogue/catalogue.go:255 cli/recipe/sync.go:222 +#: cli/recipe/release.go:576 +msgid "dry-run" +msgstr "" + +#: pkg/upstream/convert/service.go:122 +#, c-format +msgid "duplicate config target for %s not allowed" +msgstr "" + +#: pkg/upstream/convert/service.go:43 +#, c-format +msgid "duplicate secret target for %s not allowed" +msgstr "" + +#: cli/app/env.go:16 cli/recipe/new.go:124 cli/recipe/lint.go:137 +msgid "e" +msgstr "" + +#: cli/recipe/list.go:42 +msgid "email" +msgstr "" + +#: cli/app/restore.go:126 +msgid "enable pre/post-hook command execution" +msgstr "" + +#: cli/internal/validate.go:82 +#, c-format +msgid "ensure %s recipe compose.* files include \"version: '3.8'\"" +msgstr "" + +#: pkg/lint/recipe.go:69 +msgid "ensure 'version: \"3.8\"' in compose configs" +msgstr "" + +#: pkg/lint/recipe.go:163 +msgid "ensure \"image: ...\" set on all services" +msgstr "" + +#: cli/app/deploy.go:107 +#, c-format +msgid "ensure recipe: %s" +msgstr "" + +#: pkg/recipe/git.go:50 +#, c-format +msgid "ensuring env version %s" +msgstr "" + +#: cli/recipe/upgrade.go:280 +msgid "" +"enter / return to confirm, choose 'skip' to not upgrade this tag, vim mode " +"is enabled" +msgstr "" + +#: cli/app/env.go:15 +msgid "env [flags]" +msgstr "" + +#: pkg/app/app.go:248 +#, c-format +msgid "env file for %s couldn't be read: %s" +msgstr "" + +#: pkg/app/app.go:253 +#, c-format +msgid "env file for %s has issues: %s" +msgstr "" + +#: pkg/ui/deploy.go:83 +#, c-format +msgid "err: %v, " +msgstr "" + +#: cli/recipe/lint.go:74 cli/recipe/lint.go:136 pkg/lint/recipe.go:132 +#: pkg/lint/recipe.go:139 pkg/lint/recipe.go:146 pkg/lint/recipe.go:153 +#: pkg/lint/recipe.go:161 pkg/lint/recipe.go:168 pkg/lint/recipe.go:175 +msgid "error" +msgstr "" + +#: pkg/config/abra.go:34 +#, c-format +msgid "error loading config file: %s" +msgstr "" + +#: pkg/config/abra.go:26 +#, c-format +msgid "error reading config file: %s" +msgstr "" + +#: pkg/upstream/container/hijack.go:135 +#, c-format +msgid "error receiveStdout: %s" +msgstr "" + +#: pkg/upstream/container/tty.go:39 +#, c-format +msgid "error resize: %s" +msgstr "" + +#: pkg/upstream/container/hijack.go:167 +#, c-format +msgid "error sendStdin: %s" +msgstr "" + +#: pkg/upstream/container/exec.go:43 +msgid "exec ID empty" +msgstr "" + +#: cli/recipe/release.go:501 +msgid "exiting as requested" +msgstr "" + +#: pkg/container/container.go:44 +#, c-format +msgid "expected 1 container but found %v: %s" +msgstr "" + +#: pkg/service/service.go:53 pkg/service/service.go:107 +#, c-format +msgid "expected 1 service but found %v: %s" +msgstr "" + +#: cli/app/secret.go:500 cli/app/upgrade.go:435 cli/app/rollback.go:324 +#: cli/app/deploy.go:335 cli/app/remove.go:156 cli/app/volume.go:199 +#: cli/recipe/fetch.go:19 cli/recipe/fetch.go:131 +msgid "f" +msgstr "" + +#: pkg/recipe/recipe.go:175 +#, c-format +msgid "failed to check git status of %s: %s" +msgstr "" + +#: pkg/recipe/git.go:225 pkg/recipe/git.go:406 pkg/git/branch.go:95 +#, c-format +msgid "failed to check out %s in %s" +msgstr "" + +#: cli/recipe/release.go:510 +#, c-format +msgid "failed to commit changes: %s" +msgstr "" + +#: cli/updater/updater.go:251 +#, c-format +msgid "failed to determine deployed version of %s" +msgstr "" + +#: pkg/container/container.go:69 +msgid "failed to match chosen container" +msgstr "" + +#: pkg/service/service.go:77 pkg/service/service.go:131 +msgid "failed to match chosen service" +msgstr "" + +#: pkg/client/registry.go:20 +#, c-format +msgid "failed to parse image %s, saw: %s" +msgstr "" + +#: cli/recipe/release.go:518 +#, c-format +msgid "failed to publish new release: %s" +msgstr "" + +#: pkg/upstream/container/tty.go:66 +msgid "failed to resize tty, using default size" +msgstr "" + +#: cli/app/new.go:118 +#, c-format +msgid "failed to retrieve latest commit for %s: %s" +msgstr "" + +#: pkg/git/branch.go:68 +#, c-format +msgid "failed to select default branch in %s" +msgstr "" + +#: cli/recipe/release.go:514 +#, c-format +msgid "failed to tag release: %s" +msgstr "" + +#: cli/recipe/fetch.go:18 +msgid "fetch [recipe | --all] [flags]" +msgstr "" + +#: cli/recipe/fetch.go:117 +msgid "fetch all recipes" +msgstr "" + +#: pkg/catalogue/catalogue.go:84 pkg/recipe/git.go:278 +#, c-format +msgid "fetched latest git changes for %s" +msgstr "" + +#: cli/recipe/fetch.go:93 +msgid "fetching latest recipes..." +msgstr "" + +#: pkg/recipe/recipe.go:555 +#, c-format +msgid "fetching repo metadata from %s" +msgstr "" + +#: cli/app/secret.go:499 +msgid "file" +msgstr "" + +#: cli/app/remove.go:148 +#, c-format +msgid "file: %s removed" +msgstr "" + +#: pkg/lint/recipe.go:111 +msgid "fill out all the metadata" +msgstr "" + +#: cli/recipe/list.go:108 +msgid "filter by recipe" +msgstr "" + +#: cli/updater/updater.go:202 +#, c-format +msgid "for %s read env %s with value: %s from docker service" +msgstr "" + +#: cli/app/upgrade.go:434 cli/app/rollback.go:323 cli/app/deploy.go:334 +#: cli/app/remove.go:155 cli/app/volume.go:198 cli/recipe/fetch.go:130 +msgid "force" +msgstr "" + +#: cli/recipe/fetch.go:133 +msgid "force re-fetch" +msgstr "" + +#: cli/recipe/upgrade.go:91 +#, c-format +msgid "found versions file for %s" +msgstr "" + +#: cli/app/secret.go:25 cli/catalogue/catalogue.go:25 +msgid "g" +msgstr "" + +#: cli/internal/validate.go:75 +msgid "generate" +msgstr "" + +#: cli/app/secret.go:24 +msgid "generate [[secret] [version] | --all] [flags]" +msgstr "" + +#: cli/catalogue/catalogue.go:24 +msgid "generate [recipe] [flags]" +msgstr "" + +#: cli/app/secret.go:486 +msgid "generate all secrets" +msgstr "" + +#: pkg/secret/secret.go:62 pkg/secret/secret.go:80 +#, c-format +msgid "generated %s" +msgstr "" + +#: pkg/secret/secret.go:268 +#, c-format +msgid "generated and stored %v on %s" +msgstr "" + +#: cli/catalogue/catalogue.go:175 +#, c-format +msgid "generated recipe catalogue: %s" +msgstr "" + +#: cli/app/secret.go:134 +#, c-format +msgid "generated secrets %s shown again, please take note of them %s" +msgstr "" + +#: cli/app/deploy.go:101 +#, c-format +msgid "get deploy version: %s" +msgstr "" + +#: pkg/app/compose.go:80 +#, c-format +msgid "get label '%s'" +msgstr "" + +#: pkg/git/init.go:38 +#, c-format +msgid "git add: %s" +msgstr "" + +#: pkg/git/init.go:21 +#, c-format +msgid "git branch rename: %s" +msgstr "" + +#: pkg/git/commit.go:42 +msgid "git changes commited" +msgstr "" + +#: pkg/git/push.go:31 +msgid "git changes pushed" +msgstr "" + +#: pkg/recipe/git.go:410 +#, c-format +msgid "git checkout: %s in %s" +msgstr "" + +#: pkg/git/clone.go:64 pkg/git/clone.go:102 +#, c-format +msgid "git clone %s: cancelled due to interrupt" +msgstr "" + +#: pkg/git/clone.go:49 +#, c-format +msgid "git clone: %s" +msgstr "" + +#: pkg/git/clone.go:89 +#, c-format +msgid "git clone: %s already exists" +msgstr "" + +#: pkg/git/clone.go:59 pkg/git/clone.go:78 pkg/git/clone.go:87 +#, c-format +msgid "git clone: %s cloned successfully" +msgstr "" + +#: pkg/git/clone.go:68 +msgid "git clone: main branch failed, attempting master branch" +msgstr "" + +#: pkg/git/init.go:47 +#, c-format +msgid "git commit: %s" +msgstr "" + +#: pkg/git/init.go:17 +#, c-format +msgid "git init: %s" +msgstr "" + +#: pkg/git/init.go:29 +#, c-format +msgid "git open: %s" +msgstr "" + +#: pkg/git/read.go:50 +#, c-format +msgid "git status: %s: %s" +msgstr "" + +#: pkg/git/read.go:52 +#, c-format +msgid "git status: %s: clean" +msgstr "" + +#: pkg/git/push.go:40 +msgid "git tags pushed" +msgstr "" + +#: pkg/recipe/recipe.go:203 +#, c-format +msgid "git url: %s, " +msgstr "" + +#: pkg/git/init.go:34 +#, c-format +msgid "git worktree: %s" +msgstr "" + +#: cli/recipe/new.go:123 +msgid "git-email" +msgstr "" + +#: cli/recipe/new.go:115 +msgid "git-name" +msgstr "" + +#: pkg/lint/recipe.go:117 +msgid "git.coopcloud.tech repo exists" +msgstr "" + +#: pkg/recipe/git.go:378 +#, c-format +msgid "git: opening repository in %s" +msgstr "" + +#: cli/recipe/new.go:87 +msgid "happy hacking 🎉" +msgstr "" + +#: pkg/lint/recipe.go:103 +msgid "has published catalogue version" +msgstr "" + +#: cli/recipe/list.go:40 +msgid "healthcheck" +msgstr "" + +#: pkg/lint/recipe.go:75 +msgid "healthcheck enabled for all services" +msgstr "" + +#: cli/app/restore.go:123 +msgid "hooks" +msgstr "" + +#: cli/app/secret.go:144 +msgid "i" +msgstr "" + +#: pkg/ui/deploy.go:84 +#, c-format +msgid "id: %s, " +msgstr "" + +#: cli/run.go:141 +msgid "ignore .env version checkout" +msgstr "" + +#: cli/app/secret.go:478 cli/app/secret.go:518 cli/app/secret.go:542 +#: cli/app/secret.go:550 cli/app/restore.go:134 cli/app/ps.go:209 +#: cli/app/cmd.go:275 cli/app/new.go:382 cli/app/check.go:90 +#: cli/app/restart.go:157 cli/app/deploy.go:329 cli/app/cp.go:381 +#: cli/app/backup.go:290 cli/app/backup.go:306 cli/app/labels.go:138 +#: cli/catalogue/catalogue.go:274 cli/recipe/lint.go:131 +#: cli/updater/updater.go:540 +msgid "ignore uncommitted recipes changes" +msgstr "" + +#: cli/app/undeploy.go:143 cli/server/prune.go:66 +#, c-format +msgid "images pruned: %d; space reclaimed: %s" +msgstr "" + +#: pkg/lint/recipe.go:148 +msgid "include \"deploy: labels: ...\" stanza" +msgstr "" + +#: pkg/lint/recipe.go:155 +msgid "include \"traefik.enable=true\" deploy label" +msgstr "" + +#: cli/app/backup.go:274 +msgid "include secrets" +msgstr "" + +#: cli/app/backup.go:250 +msgid "include timestamps" +msgstr "" + +#: cli/app/backup.go:282 +msgid "include volumes" +msgstr "" + +#: cli/app/restore.go:74 +#, c-format +msgid "including CONTAINER=%s in backupbot exec invocation" +msgstr "" + +#: cli/app/backup.go:107 +#, c-format +msgid "including INCLUDE_PATH=%s in backupbot exec invocation" +msgstr "" + +#: cli/app/restore.go:62 +#, c-format +msgid "including NONINTERACTIVE=%v in backupbot exec invocation" +msgstr "" + +#: cli/app/restore.go:79 +#, c-format +msgid "including NO_COMMANDS=%v in backupbot exec invocation" +msgstr "" + +#: cli/app/backup.go:167 +#, c-format +msgid "including RETRIES=%s in backupbot exec invocation" +msgstr "" + +#: cli/app/backup.go:112 +#, c-format +msgid "including SECRETS=%v in backupbot exec invocation" +msgstr "" + +#: cli/app/backup.go:49 +#, c-format +msgid "including SHOW_ALL=%v in backupbot exec invocation" +msgstr "" + +#: cli/app/restore.go:52 cli/app/backup.go:44 cli/app/backup.go:102 +#, c-format +msgid "including SNAPSHOT=%s in backupbot exec invocation" +msgstr "" + +#: cli/app/restore.go:57 +#, c-format +msgid "including TARGET=%s in backupbot exec invocation" +msgstr "" + +#: cli/app/backup.go:54 +#, c-format +msgid "including TIMESTAMPS=%v in backupbot exec invocation" +msgstr "" + +#: cli/app/restore.go:68 +#, c-format +msgid "including VOLUMES=%s in backupbot exec invocation" +msgstr "" + +#: cli/app/backup.go:117 +#, c-format +msgid "including VOLUMES=%v in backupbot exec invocation" +msgstr "" + +#: cli/recipe/upgrade.go:348 cli/recipe/sync.go:233 cli/recipe/release.go:587 +msgid "increase the major part of the version" +msgstr "" + +#: cli/recipe/upgrade.go:356 cli/recipe/sync.go:241 cli/recipe/release.go:595 +msgid "increase the minor part of the version" +msgstr "" + +#: cli/recipe/upgrade.go:364 cli/recipe/sync.go:249 cli/recipe/release.go:603 +msgid "increase the patch part of the version" +msgstr "" + +#: pkg/git/init.go:50 +#, c-format +msgid "init committed all files for new git repo in %s" +msgstr "" + +#: pkg/git/init.go:24 +#, c-format +msgid "initialised new git repo in %s" +msgstr "" + +#: cli/app/undeploy.go:88 +msgid "initialising undeploy" +msgstr "" + +#: cli/app/secret.go:143 +msgid "insert [flags]" +msgstr "" + +#: cli/upgrade.go:55 +msgid "install release candidate (may contain bugs)" +msgstr "" + +#: pkg/upstream/convert/volume.go:90 +msgid "invalid bind source, source cannot be empty" +msgstr "" + +#: pkg/upstream/convert/service.go:813 +#, c-format +msgid "invalid credential spec: cannot specify both %s and %s" +msgstr "" + +#: pkg/upstream/convert/service.go:815 +#, c-format +msgid "invalid credential spec: cannot specify both %s, and %s" +msgstr "" + +#: pkg/upstream/convert/service.go:834 +#, c-format +msgid "" +"invalid credential spec: spec specifies config %v, but no such config can be " +"found" +msgstr "" + +#: pkg/upstream/container/hijack.go:100 +#, c-format +msgid "invalid detach escape keys, using default: %s" +msgstr "" + +#: pkg/lint/recipe.go:504 +msgid "invalid lightweight tag detected" +msgstr "" + +#: pkg/upstream/convert/volume.go:130 +msgid "invalid npipe source, source cannot be empty" +msgstr "" + +#: pkg/recipe/recipe.go:147 +#, c-format +msgid "invalid recipe: %s" +msgstr "" + +#: pkg/upstream/convert/volume.go:110 +msgid "invalid tmpfs source, source must be empty" +msgstr "" + +#: cli/recipe/sync.go:193 +#, c-format +msgid "invalid version %s specified" +msgstr "" + +#: cli/updater/updater.go:471 +msgid "kadabra [cmd] [flags]" +msgstr "" + +#: cli/updater/updater.go:484 +#, c-format +msgid "kadabra version %s, commit %s" +msgstr "" + +#: cli/app/cmd.go:249 cli/app/logs.go:19 cli/server/add.go:200 +#: cli/recipe/lint.go:16 +msgid "l" +msgstr "" + +#: cli/app/labels.go:23 +msgid "labels [flags]" +msgstr "" + +#: cli/app/list.go:177 +msgid "latest" +msgstr "" + +#: cli/recipe/release.go:487 +#, c-format +msgid "latest git tag (%s) and synced label (%s) are the same?" +msgstr "" + +#: cli/app/labels.go:24 +msgid "lb" +msgstr "" + +#: cli/recipe/lint.go:14 +msgid "lint [flags]" +msgstr "" + +#: pkg/lint/recipe.go:187 +#, c-format +msgid "linting for critical errors in %s configs" +msgstr "" + +#: pkg/lint/recipe.go:215 +#, c-format +msgid "linting successful, %s is well configured" +msgstr "" + +#: cli/recipe/list.go:18 +msgid "list" +msgstr "" + +#: cli/app/secret.go:372 +msgid "list " +msgstr "" + +#: cli/app/cmd.go:196 cli/app/backup.go:15 cli/app/volume.go:19 +msgid "list [flags]" +msgstr "" + +#: cli/app/list.go:43 cli/server/list.go:18 +msgid "list [flags]" +msgstr "" + +#: cli/recipe/upgrade.go:380 +msgid "list all tags, not just upgrades" +msgstr "" + +#: cli/app/backup.go:234 cli/app/backup.go:258 +msgid "list specific snapshot" +msgstr "" + +#: pkg/app/app.go:41 +#, c-format +msgid "loaded app %s: %s" +msgstr "" + +#: cli/app/cmd.go:248 cli/server/add.go:199 cli/server/list.go:69 +msgid "local" +msgstr "" + +#: cli/app/cp.go:108 +#, c-format +msgid "local %s " +msgstr "" + +#: cli/server/add.go:86 +msgid "local server already exists" +msgstr "" + +#: cli/server/add.go:84 +msgid "local server successfully added" +msgstr "" + +#: cli/app/logs.go:18 +msgid "logs [service] [flags]" +msgstr "" + +#: pkg/lint/recipe.go:124 +msgid "long secret names" +msgstr "" + +#: cli/app/secret.go:373 cli/app/cmd.go:197 cli/app/backup.go:16 +#: cli/app/volume.go:20 cli/app/list.go:44 cli/server/list.go:19 +#: cli/recipe/list.go:20 +msgid "ls" +msgstr "" + +#: cli/app/secret.go:460 cli/app/secret.go:556 cli/app/ps.go:199 +#: cli/app/list.go:312 cli/server/list.go:100 cli/recipe/upgrade.go:370 +#: cli/recipe/version.go:132 cli/recipe/list.go:98 cli/updater/updater.go:546 +msgid "m" +msgstr "" + +#: cli/app/secret.go:459 cli/app/secret.go:555 cli/app/ps.go:198 +#: cli/app/list.go:311 cli/server/list.go:99 cli/recipe/upgrade.go:369 +#: cli/recipe/version.go:131 cli/recipe/list.go:97 +msgid "machine" +msgstr "" + +#: cli/recipe/release.go:78 +#, c-format +msgid "main app service version for %s is empty?" +msgstr "" + +#: cli/recipe/upgrade.go:345 cli/recipe/sync.go:230 cli/recipe/release.go:584 +#: cli/updater/updater.go:545 cli/internal/recipe.go:46 +#: cli/internal/recipe.go:64 cli/internal/recipe.go:78 +msgid "major" +msgstr "" + +#: cli/recipe/upgrade.go:101 +#, c-format +msgid "malformed version pin specification: %s" +msgstr "" + +#: cli/run.go:31 +msgid "man" +msgstr "" + +#: cli/run.go:81 +msgid "man [flags]" +msgstr "" + +#: cli/recipe/upgrade.go:353 cli/recipe/sync.go:238 cli/recipe/release.go:592 +#: cli/internal/recipe.go:46 cli/internal/recipe.go:66 +#: cli/internal/recipe.go:80 +msgid "minor" +msgstr "" + +#: cli/recipe/fetch.go:44 +msgid "missing [recipe] or --all/-a" +msgstr "" + +#: cli/app/restart.go:64 +msgid "missing [service]" +msgstr "" + +#: cli/server/add.go:57 +msgid "missing argument or --local/-l flag" +msgstr "" + +#: cli/app/secret.go:53 +msgid "missing arguments [secret]/[version] or '--all'" +msgstr "" + +#: cli/updater/updater.go:103 +msgid "missing arguments or --all/-a flag" +msgstr "" + +#: pkg/secret/secret.go:126 +#, c-format +msgid "missing version for secret? (%s)" +msgstr "" + +#: cli/app/new.go:48 cli/recipe/new.go:35 cli/updater/updater.go:36 +#: cli/updater/updater.go:499 +msgid "n" +msgstr "" + +#: cli/recipe/list.go:37 +msgid "name" +msgstr "" + +#: pkg/lint/recipe.go:141 +msgid "name a servce 'app'" +msgstr "" + +#: pkg/ui/deploy.go:85 +#, c-format +msgid "name: %s, " +msgstr "" + +#: cli/app/undeploy.go:135 cli/server/prune.go:52 +#, c-format +msgid "networks pruned: %d" +msgstr "" + +#: cli/recipe/new.go:34 +msgid "new [flags]" +msgstr "" + +#: cli/app/new.go:47 +msgid "new [recipe] [version] [flags]" +msgstr "" + +#: cli/catalogue/catalogue.go:223 +#, c-format +msgid "new changes published: %s" +msgstr "" + +#: cli/recipe/new.go:86 +#, c-format +msgid "new recipe '%s' created: %s" +msgstr "" + +#: cli/recipe/release.go:413 +#, c-format +msgid "new release published: %s" +msgstr "" + +#: pkg/git/read.go:88 +#, c-format +msgid "no %s exists, not reading any global gitignore config" +msgstr "" + +#: pkg/git/read.go:130 +#, c-format +msgid "no %s exists, skipping reading gitignore paths" +msgstr "" + +#: cli/updater/updater.go:155 pkg/app/compose.go:86 +#, c-format +msgid "no %s label found for %s" +msgstr "" + +#: cli/recipe/release.go:415 +msgid "no -p/--publish passed, not publishing" +msgstr "" + +#: pkg/git/common.go:13 +#, c-format +msgid "no .git directory in %s?" +msgstr "" + +#: pkg/recipe/compose.go:29 +#, c-format +msgid "no COMPOSE_FILE detected, loading default: %s" +msgstr "" + +#: cli/internal/validate.go:96 +msgid "no app provided" +msgstr "" + +#: cli/app/rollback.go:120 +msgid "no available downgrades" +msgstr "" + +#: cli/app/upgrade.go:127 +msgid "no available upgrades" +msgstr "" + +#: cli/updater/updater.go:224 +#, c-format +msgid "no available upgrades for %s" +msgstr "" + +#: cli/internal/backup.go:26 +msgid "no backupbot discovered, is it deployed?" +msgstr "" + +#: cli/catalogue/catalogue.go:187 cli/recipe/release.go:353 +#, c-format +msgid "no changes discovered in %s, nothing to publish?" +msgstr "" + +#: pkg/git/commit.go:14 +msgid "no commit message specified?" +msgstr "" + +#: pkg/config/abra.go:20 +msgid "no config file found" +msgstr "" + +#: cli/app/remove.go:94 +msgid "no configs to remove" +msgstr "" + +#: pkg/container/container.go:31 +#, c-format +msgid "no containers matching the %v filter found?" +msgstr "" + +#: cli/app/new.go:275 cli/internal/validate.go:129 +msgid "no domain provided" +msgstr "" + +#: pkg/recipe/compose.go:248 +msgid "no existing label found, automagic insertion not supported yet" +msgstr "" + +#: cli/recipe/sync.go:72 +#, c-format +msgid "no git tags found for %s" +msgstr "" + +#: cli/recipe/upgrade.go:177 +#, c-format +msgid "" +"no new versions available for %s, assuming %s is the latest (use -a/--all-" +"tags to see all anyway)" +msgstr "" + +#: cli/updater/updater.go:273 +#, fuzzy, c-format +msgid "no published releases for %s in the recipe catalogue?" +msgstr "TRANSLATE THIS FUCKING SHIT ALREADY" + +#: cli/internal/validate.go:64 +#, c-format +msgid "no recipe '%s' exists?" +msgstr "" + +#: cli/internal/validate.go:59 +msgid "no recipe name provided" +msgstr "" + +#: cli/app/secret.go:210 +#, c-format +msgid "no secret %s available for recipe %s?" +msgstr "" + +#: cli/app/secret.go:311 +msgid "no secret(s) specified?" +msgstr "" + +#: cli/app/secret.go:102 +msgid "no secrets generated" +msgstr "" + +#: cli/app/secret.go:437 +#, c-format +msgid "no secrets stored for %s" +msgstr "" + +#: cli/app/remove.go:119 +msgid "no secrets to remove" +msgstr "" + +#: cli/app/secret.go:366 +msgid "no secrets to remove?" +msgstr "" + +#: cli/app/new.go:324 cli/internal/validate.go:167 +msgid "no server provided" +msgstr "" + +#: cli/app/cmd.go:168 +#, c-format +msgid "no service %s for %s?" +msgstr "" + +#: pkg/service/service.go:41 +#, c-format +msgid "no services deployed matching label '%s'?" +msgstr "" + +#: pkg/service/service.go:30 +msgid "no services deployed?" +msgstr "" + +#: pkg/service/service.go:95 +#, c-format +msgid "no services matching the %v filter found?" +msgstr "" + +#: cli/recipe/release.go:133 +#, c-format +msgid "" +"no tag specified and no previous tag available for %s, assuming this is the " +"initial release" +msgstr "" + +#: pkg/lint/recipe.go:89 +msgid "no unstable tags" +msgstr "" + +#: cli/internal/recipe.go:70 cli/internal/recipe.go:85 +msgid "no version bump type specififed?" +msgstr "" + +#: cli/app/volume.go:71 +#, c-format +msgid "no volumes created for %s" +msgstr "" + +#: cli/app/volume.go:184 +msgid "no volumes removed" +msgstr "" + +#: cli/app/remove.go:141 +msgid "no volumes to remove" +msgstr "" + +#: cli/app/upgrade.go:450 cli/app/rollback.go:339 cli/app/deploy.go:350 +msgid "no-converge-checks" +msgstr "" + +#: cli/app/upgrade.go:442 cli/app/rollback.go:331 cli/app/deploy.go:342 +msgid "no-domain-checks" +msgstr "" + +#: cli/updater/updater.go:498 +msgid "no-input" +msgstr "" + +#: cli/app/run.go:102 +msgid "no-tty" +msgstr "" + +#: cli/internal/deploy.go:159 +#, c-format +msgid "not enough arguments: %s" +msgstr "" + +#: pkg/secret/secret.go:119 +msgid "not generating app secrets, none enabled in recipe config" +msgstr "" + +#: cli/internal/command.go:93 +msgid "not requesting a remote TTY" +msgstr "" + +#: cli/recipe/upgrade.go:300 +#, c-format +msgid "not upgrading %s, skipping as requested" +msgstr "" + +#: cli/recipe/upgrade.go:239 +#, c-format +msgid "" +"not upgrading from %s to %s for %s, because the upgrade type is more serious " +"than what user wants" +msgstr "" + +#: cli/updater/updater.go:35 +msgid "notify [flags]" +msgstr "" + +#: cli/app/backup.go:298 +msgid "number of retry attempts" +msgstr "" + +#: cli/app/cp.go:85 +msgid "one of / arguments must take $SERVICE:$PATH form" +msgstr "" + +#: pkg/lint/recipe.go:140 +msgid "one service named 'app'" +msgstr "" + +#: pkg/lint/recipe.go:176 +msgid "only annotated tags used for recipe version" +msgstr "" + +#: cli/recipe/lint.go:139 +msgid "only show errors" +msgstr "" + +#: cli/app/upgrade.go:461 +msgid "only show release notes" +msgstr "" + +#: cli/app/logs.go:96 +msgid "only tail stderr" +msgstr "" + +#: cli/app/secret.go:468 cli/app/secret.go:492 cli/app/secret.go:532 +#: cli/app/ps.go:29 cli/app/undeploy.go:156 cli/app/new.go:364 +#: cli/app/backup.go:264 cli/catalogue/catalogue.go:248 cli/server/prune.go:16 +#: cli/recipe/list.go:106 cli/recipe/release.go:609 +msgid "p" +msgstr "" + +#: cli/recipe/upgrade.go:158 +#, c-format +msgid "parsed %s for %s" +msgstr "" + +#: pkg/recipe/compose.go:159 +#, c-format +msgid "parsed %s from %s" +msgstr "" + +#: cli/app/cmd.go:128 cli/app/cmd.go:174 +#, c-format +msgid "parsed following command arguments: %s" +msgstr "" + +#: cli/app/upgrade.go:318 +#, c-format +msgid "parsing chosen upgrade version failed: %s" +msgstr "" + +#: cli/app/upgrade.go:362 +#, c-format +msgid "parsing deployed version failed: %s" +msgstr "" + +#: cli/app/upgrade.go:323 +#, c-format +msgid "parsing deployment version failed: %s" +msgstr "" + +#: cli/app/upgrade.go:329 cli/app/upgrade.go:368 +#, c-format +msgid "parsing recipe version failed: %s" +msgstr "" + +#: cli/app/secret.go:467 cli/app/secret.go:491 cli/app/secret.go:531 +#: cli/app/new.go:363 +msgid "pass" +msgstr "" + +#: pkg/secret/pass.go:15 pkg/secret/pass.go:37 +msgid "pass command not found on $PATH, is it installed?" +msgstr "" + +#: cli/recipe/upgrade.go:361 cli/recipe/sync.go:246 cli/recipe/release.go:600 +#: cli/internal/recipe.go:46 cli/internal/recipe.go:68 +#: cli/internal/recipe.go:82 +msgid "patch" +msgstr "" + +#: cli/app/backup.go:263 +msgid "path" +msgstr "" + +#: cli/recipe/list.go:105 +msgid "pattern" +msgstr "" + +#: cli/app/upgrade.go:437 cli/app/rollback.go:326 cli/app/deploy.go:337 +#: cli/app/remove.go:158 cli/app/volume.go:201 +msgid "perform action without further prompt" +msgstr "" + +#: cli/recipe/release.go:562 +#, c-format +msgid "please fix your synced label for %s and re-run this command" +msgstr "" + +#: cli/app/rollback.go:238 +#, c-format +msgid "please select a downgrade (version: %s):" +msgstr "" + +#: cli/app/rollback.go:243 +#, c-format +msgid "please select a downgrade (version: %s, chaos: %s):" +msgstr "" + +#: cli/app/upgrade.go:285 +#, c-format +msgid "please select an upgrade (version: %s):" +msgstr "" + +#: cli/app/upgrade.go:290 +#, c-format +msgid "please select an upgrade (version: %s, chaos: %s):" +msgstr "" + +#: cli/run.go:133 +msgid "prefer offline & filesystem access" +msgstr "" + +#: cli/recipe/release.go:128 +msgid "previous git tags detected, assuming this is a new semver release" +msgstr "" + +#: cli/app/secret.go:462 cli/app/secret.go:558 cli/app/ps.go:201 +#: cli/app/list.go:314 cli/server/list.go:102 cli/recipe/upgrade.go:372 +#: cli/recipe/version.go:134 cli/recipe/list.go:100 +msgid "print machine-readable output" +msgstr "" + +#: pkg/recipe/git.go:398 +#, c-format +msgid "processing %s for %s" +msgstr "" + +#: cli/app/undeploy.go:155 +msgid "prune" +msgstr "" + +#: cli/server/prune.go:15 +msgid "prune [flags]" +msgstr "" + +#: cli/app/undeploy.go:158 +msgid "prune unused containers, networks, and dangling images" +msgstr "" + +#: cli/app/ps.go:28 +msgid "ps [flags]" +msgstr "" + +#: cli/catalogue/catalogue.go:247 cli/recipe/release.go:608 +msgid "publish" +msgstr "" + +#: pkg/lint/recipe.go:104 +msgid "publish a recipe version to the catalogue" +msgstr "" + +#: cli/catalogue/catalogue.go:250 cli/recipe/release.go:611 +msgid "publish changes to git.coopcloud.tech" +msgstr "" + +#: cli/recipe/release.go:400 +msgid "publish new release?" +msgstr "" + +#: pkg/app/app.go:424 +msgid "querying remote servers..." +msgstr "" + +#: cli/app/run.go:22 cli/app/upgrade.go:459 cli/app/backup.go:296 +#: cli/app/list.go:297 cli/catalogue/catalogue.go:256 cli/recipe/sync.go:223 +#: cli/recipe/recipe.go:11 cli/recipe/release.go:577 +msgid "r" +msgstr "" + +#: cli/app/restart.go:22 +msgid "re" +msgstr "" + +#: pkg/recipe/git.go:151 +#, c-format +msgid "read %s as tags for recipe %s" +msgstr "" + +#: pkg/app/app.go:577 pkg/envfile/envfile.go:42 pkg/envfile/envfile.go:80 +#: pkg/config/env.go:50 +#, c-format +msgid "read %s from %s" +msgstr "" + +#: pkg/app/app.go:579 +#, c-format +msgid "read 0 command names from %s" +msgstr "" + +#: pkg/envfile/envfile.go:82 +#, c-format +msgid "read 0 env var exports from %s" +msgstr "" + +#: pkg/config/abra.go:77 +msgid "read abra dir from $ABRA_DIR" +msgstr "" + +#: pkg/config/abra.go:81 +msgid "read abra dir from config file" +msgstr "" + +#: pkg/git/read.go:149 +#, c-format +msgid "read global ignore paths: %s" +msgstr "" + +#: pkg/recipe/recipe.go:406 +#, c-format +msgid "read recipe catalogue from file system cache in %s" +msgstr "" + +#: pkg/ui/deploy.go:86 +#, c-format +msgid "reader: %v, " +msgstr "" + +#: cli/app/secret.go:216 +#, c-format +msgid "reading secret from file: %s" +msgstr "" + +#: pkg/recipe/recipe.go:206 +#, c-format +msgid "readme: %s, " +msgstr "" + +#: cli/app/list.go:296 cli/app/list.go:303 cli/run.go:32 +msgid "recipe" +msgstr "" + +#: pkg/recipe/recipe.go:458 +#, c-format +msgid "recipe %s does not exist?" +msgstr "" + +#: pkg/lint/recipe.go:212 +#, c-format +msgid "" +"recipe '%s' failed lint checks:\n" +"%s" +msgstr "" + +#: cli/recipe/recipe.go:10 +msgid "recipe [cmd] [args] [flags]" +msgstr "" + +#: pkg/recipe/recipe.go:462 +#, c-format +msgid "recipe metadata retrieved for %s" +msgstr "" + +#: pkg/lint/recipe.go:125 +msgid "reduce length of secret names to 12 chars" +msgstr "" + +#: cli/recipe/lint.go:32 +msgid "ref" +msgstr "" + +#: cli/recipe/release.go:26 +msgid "release [version] [flags]" +msgstr "" + +#: cli/app/upgrade.go:458 +msgid "releasenotes" +msgstr "" + +#: cli/app/cp.go:117 cli/app/cp.go:199 cli/app/cp.go:210 +#, c-format +msgid "remote path: %s" +msgstr "" + +#: cli/app/cp.go:197 +#, c-format +msgid "remote: %s does not exist" +msgstr "" + +#: cli/app/secret.go:260 +msgid "remove [[secret] | --all] [flags]" +msgstr "" + +#: cli/app/remove.go:19 +msgid "remove [flags]" +msgstr "" + +#: cli/app/volume.go:76 +msgid "remove [volume] [flags]" +msgstr "" + +#: cli/server/remove.go:17 +msgid "remove [flags]" +msgstr "" + +#: cli/app/secret.go:526 +msgid "remove all secrets" +msgstr "" + +#: cli/server/prune.go:93 +msgid "remove all unused images" +msgstr "" + +#: cli/app/secret.go:534 +msgid "remove generated secrets from a local pass store" +msgstr "" + +#: cli/server/prune.go:101 +msgid "remove volumes" +msgstr "" + +#: cli/recipe/new.go:62 +#, c-format +msgid "removed .git repo in %s" +msgstr "" + +#: pkg/recipe/recipe.go:138 +#, c-format +msgid "removed dirty suffix from .env version: %s -> %s" +msgstr "" + +#: cli/recipe/release.go:537 +#, c-format +msgid "removed freshly created tag %s" +msgstr "" + +#: cli/server/prune.go:56 +msgid "removing all images, not only dangling ones" +msgstr "" + +#: cli/app/remove.go:89 +#, c-format +msgid "removing configs failed: %s" +msgstr "" + +#: cli/app/volume.go:150 +#, c-format +msgid "removing volume %s failed: %s" +msgstr "" + +#: cli/app/remove.go:136 cli/app/volume.go:179 +#, c-format +msgid "removing volumes failed: %s" +msgstr "" + +#: pkg/lint/recipe.go:177 +msgid "replace lightweight tag with annotated tag" +msgstr "" + +#: pkg/upstream/convert/service.go:774 +msgid "replicas can only be used with replicated mode" +msgstr "" + +#: pkg/git/init.go:65 +#, c-format +msgid "repo config: %s" +msgstr "" + +#: pkg/git/init.go:70 +#, c-format +msgid "repo set config: %s" +msgstr "" + +#: cli/catalogue/catalogue.go:258 cli/recipe/sync.go:225 +#: cli/recipe/release.go:579 +msgid "report changes that would be made" +msgstr "" + +#: cli/app/cmd.go:259 +msgid "request remote user" +msgstr "" + +#: cli/app/cmd.go:46 +msgid "requires at least 2 arguments with --local/-l" +msgstr "" + +#: cli/app/cmd.go:67 +msgid "requires at least 3 arguments" +msgstr "" + +#: cli/recipe/reset.go:13 +msgid "reset [flags]" +msgstr "" + +#: cli/recipe/lint.go:37 +msgid "resolve" +msgstr "" + +#: cli/app/restart.go:21 +msgid "restart [[service] | --all-services] [flags]" +msgstr "" + +#: cli/app/restart.go:164 +msgid "restart all services" +msgstr "" + +#: cli/app/restore.go:16 +msgid "restore [flags]" +msgstr "" + +#: cli/app/restore.go:110 +msgid "restore specific services" +msgstr "" + +#: cli/app/restore.go:118 +msgid "restore specific volumes" +msgstr "" + +#: cli/app/backup.go:295 +msgid "retries" +msgstr "" + +#: cli/updater/updater.go:239 +#, c-format +msgid "retrieve deployed version whether %s is already deployed" +msgstr "" + +#: cli/internal/backup.go:29 +#, c-format +msgid "retrieved %s as backup enabled service" +msgstr "" + +#: cli/app/cp.go:72 cli/internal/command.go:39 +#, c-format +msgid "retrieved %s as target container on %s" +msgstr "" + +#: pkg/recipe/compose.go:59 +#, c-format +msgid "retrieved %s configs for %s" +msgstr "" + +#: pkg/app/app.go:501 +#, c-format +msgid "retrieved %s for %s" +msgstr "" + +#: cli/recipe/upgrade.go:139 +#, c-format +msgid "retrieved %s from remote registry for %s" +msgstr "" + +#: pkg/config/env.go:37 +#, c-format +msgid "retrieved %v servers: %s" +msgstr "" + +#: pkg/app/app.go:487 +#, c-format +msgid "retrieved app statuses: %s" +msgstr "" + +#: cli/recipe/version.go:39 +msgid "retrieved versions from local recipe repository" +msgstr "" + +#: pkg/recipe/recipe.go:659 +msgid "retrieving recipes" +msgstr "" + +#: cli/app/rollback.go:26 cli/recipe/release.go:27 +msgid "rl" +msgstr "" + +#: cli/app/secret.go:261 cli/app/remove.go:20 cli/app/volume.go:93 +#: cli/server/remove.go:18 +msgid "rm" +msgstr "" + +#: cli/app/rollback.go:25 +msgid "rollback [version] [flags]" +msgstr "" + +#: pkg/ui/deploy.go:333 +msgid "rolled back" +msgstr "" + +#: cli/app/restore.go:17 cli/recipe/reset.go:14 +msgid "rs" +msgstr "" + +#: cli/recipe/lint.go:33 +msgid "rule" +msgstr "" + +#: cli/app/run.go:21 +msgid "run [[args] [flags] | [flags] -- [args]]" +msgstr "" + +#: cli/app/run.go:113 +msgid "run command as user" +msgstr "" + +#: cli/app/cmd.go:251 +msgid "run command locally" +msgstr "" + +#: cli/app/upgrade.go:266 cli/app/deploy.go:229 +#, c-format +msgid "run the following post-deploy commands: %s" +msgstr "" + +#: cli/internal/backup.go:62 +#, c-format +msgid "running backup %s on %s with exec config %v" +msgstr "" + +#: cli/internal/deploy.go:189 +#, c-format +msgid "running command %s %s within the context of %s_%s" +msgstr "" + +#: cli/app/cmd.go:171 +#, c-format +msgid "running command %s within the context of %s_%s" +msgstr "" + +#: cli/internal/command.go:84 +#, c-format +msgid "running command with user %s" +msgstr "" + +#: cli/internal/command.go:81 +#, c-format +msgid "running command: %s" +msgstr "" + +#: cli/internal/deploy.go:167 +#, c-format +msgid "running post-command '%s %s' in container %s" +msgstr "" + +#: cli/app/secret.go:443 cli/app/restore.go:108 cli/app/new.go:341 +#: cli/app/logs.go:94 cli/app/backup.go:179 cli/app/backup.go:232 +#: cli/app/backup.go:256 cli/app/list.go:320 cli/catalogue/catalogue.go:264 +#: cli/server/server.go:11 cli/recipe/fetch.go:123 cli/recipe/sync.go:21 +msgid "s" +msgstr "" + +#: pkg/recipe/recipe.go:207 +#, c-format +msgid "sample env: %s, " +msgstr "" + +#: cli/recipe/lint.go:35 +msgid "satisfied" +msgstr "" + +#: pkg/secret/secret.go:139 +#, c-format +msgid "secret %s is > %d chars when combined with %s" +msgstr "" + +#: pkg/lint/recipe.go:481 +#, c-format +msgid "secret %s is longer than 12 characters" +msgstr "" + +#: cli/app/secret.go:442 +msgid "secret [cmd] [args] [flags]" +msgstr "" + +#: pkg/upstream/convert/service.go:72 +#, c-format +msgid "secret not found: %s" +msgstr "" + +#: cli/app/deploy.go:276 +#, c-format +msgid "secret not generated: %s" +msgstr "" + +#: cli/app/remove.go:116 +#, c-format +msgid "secret: %s removed" +msgstr "" + +#: cli/app/new.go:371 cli/app/backup.go:271 +msgid "secrets" +msgstr "" + +#: cli/app/new.go:211 +#, c-format +msgid "secrets are %s shown again, please save them %s" +msgstr "" + +#: cli/app/new.go:340 cli/app/new.go:347 cli/app/list.go:319 +#: cli/app/list.go:326 cli/run.go:33 +msgid "server" +msgstr "" + +#: pkg/app/app.go:301 +#, c-format +msgid "server %s doesn't exist? Run \"abra server ls\" to check" +msgstr "" + +#: cli/server/server.go:10 +msgid "server [cmd] [args] [flags]" +msgstr "" + +#: cli/server/add.go:184 +#, c-format +msgid "server dir for %s already created" +msgstr "" + +#: cli/internal/validate.go:171 +msgid "server doesn't exist?" +msgstr "" + +#: cli/server/add.go:141 +#, c-format +msgid "serverAdd: cleanUp: %s is not empty, aborting cleanup" +msgstr "" + +#: cli/server/add.go:128 +#, c-format +msgid "serverAdd: cleanUp: cleaning up context for %s" +msgstr "" + +#: cli/server/add.go:146 +#, c-format +msgid "serverAdd: cleanUp: failed to remove %s: %s" +msgstr "" + +#: cli/server/add.go:137 +#, c-format +msgid "serverAdd: cleanUp: unable to list files in %s: %s" +msgstr "" + +#: cli/recipe/upgrade.go:222 +#, c-format +msgid "" +"service %s is at version %s, but pinned to %s, please correct your " +"compose.yml file manually!" +msgstr "" + +#: cli/recipe/upgrade.go:218 +#, c-format +msgid "service %s, image %s pinned to %s, no compatible upgrade found" +msgstr "" + +#: cli/app/restore.go:107 +msgid "services" +msgstr "" + +#: cli/app/services.go:21 +msgid "services [flags]" +msgstr "" + +#: pkg/git/init.go:73 +msgid "set 'main' as the default branch" +msgstr "" + +#: pkg/app/compose.go:68 +#, c-format +msgid "set label 'coop-cloud.%s.autoupdate' to %s for %s" +msgstr "" + +#: pkg/app/compose.go:30 +#, c-format +msgid "set label 'coop-cloud.%s.chaos' to %v for %s" +msgstr "" + +#: pkg/app/compose.go:41 +#, c-format +msgid "set label 'coop-cloud.%s.chaos-version' to %v for %s" +msgstr "" + +#: pkg/app/compose.go:51 +#, c-format +msgid "set label 'coop-cloud.%s.version' to %v for %s" +msgstr "" + +#: pkg/app/compose.go:18 +#, c-format +msgid "set recipe label 'coop-cloud.%s.recipe' to %s for %s" +msgstr "" + +#: pkg/git/init.go:60 +#, c-format +msgid "set reference: %s" +msgstr "" + +#: cli/app/upgrade.go:240 cli/app/rollback.go:202 cli/app/deploy.go:203 +#, c-format +msgid "set waiting timeout to %d second(s)" +msgstr "" + +#: cli/recipe/lint.go:34 +msgid "severity" +msgstr "" + +#: cli/app/backup.go:242 +msgid "show all paths" +msgstr "" + +#: cli/app/list.go:291 +msgid "show app deployment status" +msgstr "" + +#: cli/app/list.go:299 +msgid "show apps of a specific recipe" +msgstr "" + +#: cli/app/list.go:322 +msgid "show apps of a specific server" +msgstr "" + +#: cli/run.go:117 cli/updater/updater.go:493 +msgid "show debug messages" +msgstr "" + +#: cli/app/logs.go:101 +msgid "since" +msgstr "" + +#: cli/app/new.go:309 +#, c-format +msgid "single server detected, choosing %s automatically" +msgstr "" + +#: cli/catalogue/catalogue.go:266 +msgid "skip updating recipe repositories" +msgstr "" + +#: cli/catalogue/catalogue.go:263 +msgid "skip-updates" +msgstr "" + +#: cli/recipe/lint.go:36 +msgid "skipped" +msgstr "" + +#: pkg/lint/recipe.go:55 +#, c-format +msgid "skipping %s based on skip condition" +msgstr "" + +#: cli/recipe/lint.go:53 +#, c-format +msgid "skipping %s, does not have level \"error\"" +msgstr "" + +#: cli/app/deploy.go:180 +msgid "skipping domain checks" +msgstr "" + +#: cli/app/deploy.go:177 +msgid "skipping domain checks, no DOMAIN=... configured" +msgstr "" + +#: pkg/secret/secret.go:207 +#, c-format +msgid "skipping generation of %s (generate=false)" +msgstr "" + +#: pkg/app/app.go:685 +#, c-format +msgid "skipping version %s write as already exists in %s.env" +msgstr "" + +#: pkg/app/app.go:679 +#, c-format +msgid "skipping writing version %s because dry run" +msgstr "" + +#: cli/app/backup.go:231 cli/app/backup.go:255 +msgid "snapshot" +msgstr "" + +#: cli/app/backup.go:178 +msgid "snapshots [flags]" +msgstr "" + +#: cli/app/new.go:343 +msgid "specify server for new app" +msgstr "" + +#: cli/app/services.go:22 +msgid "sr" +msgstr "" + +#: cli/recipe/fetch.go:122 +msgid "ssh" +msgstr "" + +#: cli/server/add.go:107 +#, c-format +msgid "ssh %s error: %s" +msgstr "" + +#: pkg/ssh/ssh.go:22 +#, c-format +msgid "ssh auth: permission denied for %s" +msgstr "" + +#: pkg/upstream/commandconn/connection.go:38 +msgid "ssh host connection is not valid" +msgstr "" + +#: pkg/recipe/recipe.go:204 +#, c-format +msgid "ssh url: %s, " +msgstr "" + +#: cli/app/list.go:288 cli/recipe/list.go:39 +msgid "status" +msgstr "" + +#: pkg/ui/deploy.go:88 +#, c-format +msgid "status: %s}" +msgstr "" + +#: cli/app/logs.go:93 +msgid "stderr" +msgstr "" + +#: cli/app/secret.go:470 cli/app/secret.go:494 +msgid "store generated secrets in a local pass store" +msgstr "" + +#: cli/app/new.go:366 +msgid "store secrets in a local pass store" +msgstr "" + +#: pkg/formatter/formatter.go:238 +#, c-format +msgid "stripped %s to %s for parsing" +msgstr "" + +#: pkg/ui/deploy.go:330 +msgid "succeeded" +msgstr "" + +#: pkg/recipe/git.go:178 +#, c-format +msgid "successfully checked %s out to %s in %s" +msgstr "" + +#: pkg/git/branch.go:99 +#, c-format +msgid "successfully checked out %v in %s" +msgstr "" + +#: pkg/server/server.go:25 +#, c-format +msgid "successfully created %s" +msgstr "" + +#: pkg/client/client.go:97 +#, c-format +msgid "swarm mode not enabled on %s?" +msgstr "" + +#: pkg/client/client.go:100 +msgid "swarm mode not enabled on local server?" +msgstr "" + +#: cli/recipe/sync.go:20 +msgid "sync [version] [flags]" +msgstr "" + +#: pkg/recipe/compose.go:243 +#, c-format +msgid "synced label %s to service %s" +msgstr "" + +#: cli/app/secret.go:508 cli/app/restore.go:100 cli/app/run.go:103 +#: cli/app/backup.go:248 +msgid "t" +msgstr "" + +#: pkg/lint/recipe.go:90 +msgid "tag all images with stable tags" +msgstr "" + +#: cli/recipe/sync.go:129 +#, c-format +msgid "tag at commit %s is unannotated or otherwise broken" +msgstr "" + +#: cli/recipe/upgrade.go:296 +#, c-format +msgid "tag upgraded from %s to %s for %s" +msgstr "" + +#: pkg/lint/recipe.go:96 +msgid "tags use semver-like format" +msgstr "" + +#: cli/app/logs.go:104 +msgid "tail logs since YYYY-MM-DDTHH:MM:SSZ" +msgstr "" + +#: pkg/logs/logs.go:86 +#, c-format +msgid "tailLogs: unable to copy buffer: %s" +msgstr "" + +#: cli/app/restore.go:99 +msgid "target" +msgstr "" + +#: cli/app/restore.go:102 +msgid "target path" +msgstr "" + +#: pkg/upstream/convert/service.go:604 +msgid "test and disable can't be set at the same time" +msgstr "" + +#: cli/recipe/list.go:43 +msgid "tests" +msgstr "" + +#: pkg/app/compose.go:95 +#, c-format +msgid "timeout label: %s" +msgstr "" + +#: cli/app/backup.go:247 +msgid "timestamps" +msgstr "" + +#: pkg/upstream/convert/volume.go:96 +msgid "tmpfs options are incompatible with type bind" +msgstr "" + +#: pkg/upstream/convert/volume.go:136 +msgid "tmpfs options are incompatible with type npipe" +msgstr "" + +#: pkg/upstream/convert/volume.go:44 +msgid "tmpfs options are incompatible with type volume" +msgstr "" + +#: cli/run.go:125 cli/updater/updater.go:501 +msgid "toggle non-interactive mode" +msgstr "" + +#: pkg/lint/recipe.go:154 +msgid "traefik routing enabled" +msgstr "" + +#: cli/app/secret.go:502 +msgid "treat input as a file" +msgstr "" + +#: cli/app/secret.go:507 +msgid "trim" +msgstr "" + +#: cli/app/secret.go:510 +msgid "trim input" +msgstr "" + +#: cli/app/new.go:236 pkg/app/app.go:141 +#, c-format +msgid "trimming %s to %s to avoid runtime limits" +msgstr "" + +#: cli/app/cmd.go:264 +msgid "tty" +msgstr "" + +#: cli/app/run.go:111 cli/app/cmd.go:257 cli/recipe/upgrade.go:42 +#: cli/updater/updater.go:75 +msgid "u" +msgstr "" + +#: cli/app/undeploy.go:23 +msgid "un" +msgstr "" + +#: pkg/recipe/git.go:187 +#, c-format +msgid "unable to check git clean status in %s: %s" +msgstr "" + +#: pkg/recipe/git.go:256 +#, c-format +msgid "unable to check out default branch in %s: %s" +msgstr "" + +#: pkg/git/clone.go:100 +#, c-format +msgid "unable to clean up git clone of %s: %s" +msgstr "" + +#: cli/app/list.go:148 +#, c-format +msgid "unable to clone %s: %s" +msgstr "" + +#: pkg/ssh/ssh.go:24 +#, c-format +msgid "unable to connect to %s, please check your SSH config" +msgstr "" + +#: cli/recipe/sync.go:74 +msgid "unable to continue, input required for initial version" +msgstr "" + +#: cli/app/ps.go:165 +#, c-format +msgid "unable to convert to JSON: %s" +msgstr "" + +#: cli/run.go:49 +#, c-format +msgid "unable to create %s: %s" +msgstr "" + +#: cli/server/add.go:100 +#, c-format +msgid "unable to create local context: %s" +msgstr "" + +#: cli/recipe/upgrade.go:247 +#, c-format +msgid "unable to determine versioning semantics of %s, listing all tags" +msgstr "" + +#: pkg/lint/recipe.go:261 pkg/recipe/files.go:17 +#, c-format +msgid "unable to discover .env.sample for %s" +msgstr "" + +#: cli/recipe/fetch.go:64 +#, c-format +msgid "unable to discover SSH remote for %s" +msgstr "" + +#: pkg/recipe/git.go:262 +#, c-format +msgid "unable to fetch tags in %s: %s" +msgstr "" + +#: pkg/recipe/git.go:274 +#, c-format +msgid "unable to git pull in %s: %s" +msgstr "" + +#: pkg/lint/recipe.go:496 +#, c-format +msgid "unable to list local tags for %s" +msgstr "" + +#: pkg/git/diff.go:30 +msgid "unable to locate git command, cannot output diff" +msgstr "" + +#: cli/recipe/fetch.go:70 pkg/lint/recipe.go:491 pkg/recipe/git.go:236 +#: pkg/git/read.go:26 +#, c-format +msgid "unable to open %s: %s" +msgstr "" + +#: pkg/recipe/git.go:251 +#, c-format +msgid "unable to open git work tree in %s: %s" +msgstr "" + +#: pkg/git/read.go:31 +#, c-format +msgid "unable to open worktree of %s: %s" +msgstr "" + +#: cli/recipe/upgrade.go:154 +#, c-format +msgid "unable to parse %s, error was: %s, skipping upgrade for %s" +msgstr "" + +#: pkg/recipe/compose.go:153 +#, c-format +msgid "unable to parse %s, skipping" +msgstr "" + +#: cli/run.go:100 +#, c-format +msgid "unable to proceed, %s does not exist?" +msgstr "" + +#: pkg/git/read.go:45 +#, c-format +msgid "unable to query status of %s: %s" +msgstr "" + +#: pkg/recipe/git.go:241 +#, c-format +msgid "unable to read remotes in %s: %s" +msgstr "" + +#: cli/recipe/upgrade.go:148 +#, c-format +msgid "unable to read tag for image %s, is it missing? skipping upgrade for %s" +msgstr "" + +#: cli/recipe/release.go:549 +#, c-format +msgid "" +"unable to read version for %s from synced label. Did you try running \"abra " +"recipe sync %s\" already?" +msgstr "" + +#: cli/recipe/fetch.go:74 +#, c-format +msgid "unable to remove default remote in %s: %s" +msgstr "" + +#: cli/app/volume.go:145 +#, c-format +msgid "unable to remove volume: no volume with name '%s'?" +msgstr "" + +#: cli/app/secret.go:124 cli/app/secret.go:424 cli/server/list.go:82 +#: cli/recipe/version.go:106 cli/recipe/list.go:77 +#, c-format +msgid "unable to render to JSON: %s" +msgstr "" + +#: pkg/recipe/git.go:160 +#, c-format +msgid "unable to resolve '%s': %s" +msgstr "" + +#: cli/server/add.go:114 +#, c-format +msgid "unable to resolve IPv4 for %s" +msgstr "" + +#: cli/app/list.go:153 +#, c-format +msgid "unable to retrieve tags for %s: %s" +msgstr "" + +#: pkg/upstream/container/hijack.go:82 +#, c-format +msgid "unable to set IO streams as raw terminal: %s" +msgstr "" + +#: cli/recipe/fetch.go:81 +#, c-format +msgid "unable to set SSH remote in %s: %s" +msgstr "" + +#: pkg/upstream/container/hijack.go:43 +#, c-format +msgid "unable to setup input stream: %s" +msgstr "" + +#: cli/internal/validate.go:84 +#, c-format +msgid "unable to validate recipe: %s" +msgstr "" + +#: pkg/upstream/convert/service.go:462 +#, c-format +msgid "undefined config %q" +msgstr "" + +#: pkg/upstream/convert/service.go:375 +#, c-format +msgid "undefined network %q" +msgstr "" + +#: pkg/upstream/convert/service.go:414 +#, c-format +msgid "undefined secret %q" +msgstr "" + +#: pkg/upstream/convert/volume.go:56 +#, c-format +msgid "undefined volume %q" +msgstr "" + +#: cli/app/undeploy.go:22 +msgid "undeploy [flags]" +msgstr "" + +#: cli/app/undeploy.go:104 +msgid "undeploy succeeded 🟢" +msgstr "" + +#: pkg/upstream/commandconn/commandconn.go:255 +#, c-format +msgid "unimplemented call: SetDeadline(%v)" +msgstr "" + +#: pkg/upstream/commandconn/commandconn.go:259 +#, c-format +msgid "unimplemented call: SetReadDeadline(%v)" +msgstr "" + +#: pkg/upstream/commandconn/commandconn.go:263 +#, c-format +msgid "unimplemented call: SetWriteDeadline(%v)" +msgstr "" + +#: cli/app/ps.go:119 cli/app/ps.go:120 cli/app/ps.go:121 cli/app/ps.go:122 +#: cli/app/ps.go:123 cli/app/list.go:111 cli/app/list.go:112 +#: cli/app/list.go:113 cli/app/list.go:114 cli/app/list.go:115 +#: cli/app/list.go:175 cli/app/labels.go:71 cli/server/list.go:59 +#: cli/server/list.go:71 +msgid "unknown" +msgstr "" + +#: cli/app/rollback.go:142 +msgid "unknown deployed version, unable to downgrade" +msgstr "" + +#: cli/app/upgrade.go:149 +msgid "unknown deployed version, unable to upgrade" +msgstr "" + +#: pkg/upstream/convert/service.go:780 +#, c-format +msgid "unknown mode: %s" +msgstr "" + +#: pkg/upstream/convert/service.go:652 +#, c-format +msgid "unknown restart policy: %s" +msgstr "" + +#: pkg/client/client.go:44 +#, c-format +msgid "unknown server, run \"abra server add %s\"?" +msgstr "" + +#: cli/app/cp.go:253 +#, c-format +msgid "untar: %s" +msgstr "" + +#: cli/app/upgrade.go:30 +msgid "up" +msgstr "" + +#: cli/updater/updater.go:556 +msgid "update all deployed apps" +msgstr "" + +#: pkg/recipe/compose.go:171 pkg/recipe/compose.go:237 +#, c-format +msgid "updating %s to %s in %s" +msgstr "" + +#: cli/run.go:34 +msgid "upgrade" +msgstr "" + +#: cli/updater/updater.go:444 +#, c-format +msgid "upgrade %s (%s) to version %s" +msgstr "" + +#: cli/app/upgrade.go:29 +msgid "upgrade [version] [flags]" +msgstr "" + +#: cli/recipe/upgrade.go:41 +msgid "upgrade [flags]" +msgstr "" + +#: cli/updater/updater.go:74 +msgid "upgrade [[stack] [recipe] | --all] [flags]" +msgstr "" + +#: cli/upgrade.go:16 +msgid "upgrade [flags]" +msgstr "" + +#: cli/recipe/upgrade.go:243 +#, c-format +msgid "upgrade to which tag? (service: %s, image: %s, tag: %s)" +msgstr "" + +#: cli/recipe/upgrade.go:249 +#, c-format +msgid "upgrade to which tag? (service: %s, tag: %s)" +msgstr "" + +#: cli/recipe/upgrade.go:216 +#, c-format +msgid "upgrading service %s from %s to %s (pinned tag: %s)" +msgstr "" + +#: pkg/lint/recipe.go:118 +msgid "upload your recipe to git.coopcloud.tech/coop-cloud/..." +msgstr "" + +#: cli/recipe/release.go:556 +#, c-format +msgid "use %s as the new version?" +msgstr "" + +#: pkg/lint/recipe.go:83 +msgid "use a tag for all images" +msgstr "" + +#: cli/server/add.go:202 +msgid "use local server" +msgstr "" + +#: pkg/lint/recipe.go:97 +msgid "use semver-like tags" +msgstr "" + +#: cli/app/run.go:110 cli/app/cmd.go:256 +msgid "user" +msgstr "" + +#: pkg/config/abra.go:88 +msgid "using default abra dir" +msgstr "" + +#: cli/app/restore.go:116 cli/app/backup.go:280 cli/server/prune.go:99 +#: cli/recipe/version.go:18 +msgid "v" +msgstr "" + +#: cli/internal/validate.go:106 +#, c-format +msgid "validated %s as app argument" +msgstr "" + +#: cli/internal/validate.go:132 +#, c-format +msgid "validated %s as domain argument" +msgstr "" + +#: cli/internal/validate.go:88 +#, c-format +msgid "validated %s as recipe argument" +msgstr "" + +#: cli/internal/validate.go:174 +#, c-format +msgid "validated %s as server argument" +msgstr "" + +#: pkg/lint/recipe.go:170 +msgid "vendor config versions in an abra.sh" +msgstr "" + +#: pkg/app/app.go:683 +#, c-format +msgid "version %s saved to %s.env" +msgstr "" + +#: pkg/recipe/recipe.go:200 +#, c-format +msgid "version : %s, " +msgstr "" + +#: pkg/recipe/recipe.go:130 +#, c-format +msgid "version seems invalid: %s" +msgstr "" + +#: pkg/app/app.go:622 +#, c-format +msgid "version wiped from %s.env" +msgstr "" + +#: cli/app/deploy.go:303 +#, c-format +msgid "version: can not redeploy chaos version %s" +msgstr "" + +#: cli/app/deploy.go:290 +#, c-format +msgid "version: taking chaos version: %s" +msgstr "" + +#: cli/app/deploy.go:311 +#, c-format +msgid "version: taking deployed version: %s" +msgstr "" + +#: cli/app/deploy.go:316 +#, c-format +msgid "version: taking new recipe version: %s" +msgstr "" + +#: cli/app/deploy.go:305 +#, c-format +msgid "version: taking version from .env file: %s" +msgstr "" + +#: cli/app/deploy.go:296 +#, c-format +msgid "version: taking version from cli arg: %s" +msgstr "" + +#: cli/recipe/version.go:17 +msgid "versions [flags]" +msgstr "" + +#: cli/app/volume.go:191 +msgid "vl" +msgstr "" + +#: cli/app/volume.go:153 +#, c-format +msgid "volume %s removed successfully" +msgstr "" + +#: pkg/client/volumes.go:41 +#, c-format +msgid "volume %s: %s" +msgstr "" + +#: cli/app/volume.go:190 +msgid "volume [cmd] [args] [flags]" +msgstr "" + +#: pkg/upstream/convert/volume.go:93 +msgid "volume options are incompatible with type bind" +msgstr "" + +#: pkg/upstream/convert/volume.go:133 +msgid "volume options are incompatible with type npipe" +msgstr "" + +#: pkg/upstream/convert/volume.go:116 +msgid "volume options are incompatible with type tmpfs" +msgstr "" + +#: pkg/upstream/convert/volume.go:162 +msgid "volume type must be volume, bind, tmpfs or npipe" +msgstr "" + +#: cli/app/restore.go:115 cli/app/backup.go:279 cli/server/prune.go:98 +msgid "volumes" +msgstr "" + +#: cli/app/backup.go:266 +msgid "volumes path" +msgstr "" + +#: cli/server/prune.go:75 +#, c-format +msgid "volumes pruned: %d; space reclaimed: %s" +msgstr "" + +#: pkg/lint/recipe.go:21 pkg/lint/recipe.go:67 pkg/lint/recipe.go:74 +#: pkg/lint/recipe.go:81 pkg/lint/recipe.go:88 pkg/lint/recipe.go:95 +#: pkg/lint/recipe.go:102 pkg/lint/recipe.go:109 pkg/lint/recipe.go:116 +#: pkg/lint/recipe.go:123 +msgid "warn" +msgstr "" + +#: pkg/container/container.go:52 +msgid "which container are you looking for?" +msgstr "" + +#: cli/app/config.go:42 +msgid "which editor do you wish to use?" +msgstr "" + +#: pkg/service/service.go:61 pkg/service/service.go:115 +msgid "which service are you looking for?" +msgstr "" + +#: cli/recipe/sync.go:96 +msgid "which version do you want to begin with?" +msgstr "" + +#: cli/app/volume.go:161 +msgid "which volumes do you want to remove?" +msgstr "" + +#: pkg/lint/recipe.go:76 +msgid "wire up healthchecks" +msgstr "" + +#: pkg/ui/deploy.go:87 +#, c-format +msgid "writer: %v, " +msgstr "" + +#: cli/app/upgrade.go:274 cli/app/undeploy.go:107 cli/app/new.go:224 +#: cli/app/rollback.go:227 cli/app/deploy.go:236 +#, c-format +msgid "writing recipe version failed: %s" +msgstr "" + +#: cli/recipe/upgrade.go:346 cli/recipe/sync.go:231 cli/recipe/release.go:585 +msgid "x" +msgstr "" + +#: cli/recipe/upgrade.go:354 cli/recipe/sync.go:239 cli/recipe/release.go:593 +msgid "y" +msgstr "" + +#: cli/recipe/release.go:430 +msgid "you can only use one of: --major, --minor, --patch" +msgstr "" + +#: cli/recipe/upgrade.go:75 +msgid "you can only use one of: --major, --minor, --patch." +msgstr "" + +#: cli/recipe/sync.go:154 +msgid "you can only use one version flag: --major, --minor or --patch" +msgstr "" + +#: cli/recipe/upgrade.go:362 cli/recipe/sync.go:247 cli/recipe/release.go:601 +msgid "z" +msgstr "" + +#: pkg/ui/deploy.go:82 +#, c-format +msgid "{decoder: %v, " +msgstr "" + +#: pkg/recipe/recipe.go:199 +#, c-format +msgid "{name: %s, " +msgstr "" diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go deleted file mode 100644 index bdb0a531..00000000 --- a/pkg/integration/integration.go +++ /dev/null @@ -1,12 +0,0 @@ -package integration - -import ( - "os" - "testing" -) - -func skipIfNotIntegration(t *testing.T) { - if os.Getenv("ABRA_INTEGRATION") == "" { - t.Skip("missing 'ABRA_INTEGRATION', not running integration tests") - } -} diff --git a/pkg/lang/lang.go b/pkg/lang/lang.go deleted file mode 100644 index a8f456e8..00000000 --- a/pkg/lang/lang.go +++ /dev/null @@ -1,30 +0,0 @@ -package lang - -import ( - "os" - "strings" -) - -func GetLocale() string { - if loc := os.Getenv("LC_MESSAGES"); loc != "" { - return NormalizeLocale(loc) - } - - if loc := os.Getenv("LANG"); loc != "" { - return NormalizeLocale(loc) - } - - return "C.UTF-8" -} - -func NormalizeLocale(loc string) string { - if idx := strings.Index(loc, "."); idx != -1 { - return loc[:idx] - } - - if idx := strings.Index(loc, "@"); idx != -1 { - return loc[:idx] - } - - return loc -} diff --git a/pkg/lint/recipe.go b/pkg/lint/recipe.go index 1855b6e2..82ef3eb8 100644 --- a/pkg/lint/recipe.go +++ b/pkg/lint/recipe.go @@ -1,11 +1,13 @@ package lint import ( + "errors" "fmt" "net/http" "os" "path" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/recipe" recipePkg "coopcloud.tech/abra/pkg/recipe" @@ -16,8 +18,8 @@ import ( ) var ( - Warn = "warn" - Critical = "critical" + Warn = i18n.G("warn") + Critical = i18n.G("critical") ) type LintFunction func(recipe.Recipe) (bool, error) @@ -47,10 +49,10 @@ func (l LintRule) Skip(recipe recipe.Recipe) bool { if l.SkipCondition != nil { ok, err := l.SkipCondition(recipe) if err != nil { - log.Debugf("%s: skip condition: %s", l.Ref, err) + log.Debug(i18n.G("%s: skip condition: %s", l.Ref, err)) } if ok { - log.Debugf("skipping %s based on skip condition", l.Ref) + log.Debug(i18n.G("skipping %s based on skip condition", l.Ref)) return true } } @@ -62,117 +64,117 @@ var LintRules = map[string][]LintRule{ "warn": { { Ref: "R001", - Level: "warn", - Description: "compose config has expected version", - HowToResolve: "ensure 'version: \"3.8\"' in compose configs", + Level: i18n.G("warn"), + Description: i18n.G("compose config has expected version"), + HowToResolve: i18n.G("ensure 'version: \"3.8\"' in compose configs"), Function: LintComposeVersion, }, { Ref: "R002", - Level: "warn", - Description: "healthcheck enabled for all services", - HowToResolve: "wire up healthchecks", + Level: i18n.G("warn"), + Description: i18n.G("healthcheck enabled for all services"), + HowToResolve: i18n.G("wire up healthchecks"), Function: LintHealthchecks, }, { Ref: "R003", - Level: "warn", - Description: "all images use a tag", - HowToResolve: "use a tag for all images", + Level: i18n.G("warn"), + Description: i18n.G("all images use a tag"), + HowToResolve: i18n.G("use a tag for all images"), Function: LintAllImagesTagged, }, { Ref: "R004", - Level: "warn", - Description: "no unstable tags", - HowToResolve: "tag all images with stable tags", + Level: i18n.G("warn"), + Description: i18n.G("no unstable tags"), + HowToResolve: i18n.G("tag all images with stable tags"), Function: LintNoUnstableTags, }, { Ref: "R005", - Level: "warn", - Description: "tags use semver-like format", - HowToResolve: "use semver-like tags", + Level: i18n.G("warn"), + Description: i18n.G("tags use semver-like format"), + HowToResolve: i18n.G("use semver-like tags"), Function: LintSemverLikeTags, }, { Ref: "R006", - Level: "warn", - Description: "has published catalogue version", - HowToResolve: "publish a recipe version to the catalogue", + Level: i18n.G("warn"), + Description: i18n.G("has published catalogue version"), + HowToResolve: i18n.G("publish a recipe version to the catalogue"), Function: LintHasPublishedVersion, }, { Ref: "R007", - Level: "warn", - Description: "README.md metadata filled in", - HowToResolve: "fill out all the metadata", + Level: i18n.G("warn"), + Description: i18n.G("README.md metadata filled in"), + HowToResolve: i18n.G("fill out all the metadata"), Function: LintMetadataFilledIn, }, { Ref: "R013", - Level: "warn", - Description: "git.coopcloud.tech repo exists", - HowToResolve: "upload your recipe to git.coopcloud.tech/coop-cloud/...", + Level: i18n.G("warn"), + Description: i18n.G("git.coopcloud.tech repo exists"), + HowToResolve: i18n.G("upload your recipe to git.coopcloud.tech/coop-cloud/..."), Function: LintHasRecipeRepo, }, { Ref: "R015", - Level: "warn", - Description: "long secret names", - HowToResolve: "reduce length of secret names to 12 chars", + Level: i18n.G("warn"), + Description: i18n.G("long secret names"), + HowToResolve: i18n.G("reduce length of secret names to 12 chars"), Function: LintSecretLengths, }, }, "error": { { Ref: "R008", - Level: "error", - Description: ".env.sample provided", - HowToResolve: "create an example .env.sample", + Level: i18n.G("error"), + Description: i18n.G(".env.sample provided"), + HowToResolve: i18n.G("create an example .env.sample"), Function: LintEnvConfigPresent, }, { Ref: "R009", - Level: "error", - Description: "one service named 'app'", - HowToResolve: "name a servce 'app'", + Level: i18n.G("error"), + Description: i18n.G("one service named 'app'"), + HowToResolve: i18n.G("name a servce 'app'"), Function: LintAppService, }, { Ref: "R015", - Level: "error", - Description: "deploy labels stanza present", - HowToResolve: "include \"deploy: labels: ...\" stanza", + Level: i18n.G("error"), + Description: i18n.G("deploy labels stanza present"), + HowToResolve: i18n.G("include \"deploy: labels: ...\" stanza"), Function: LintDeployLabelsPresent, }, { Ref: "R010", - Level: "error", - Description: "traefik routing enabled", - HowToResolve: "include \"traefik.enable=true\" deploy label", + Level: i18n.G("error"), + Description: i18n.G("traefik routing enabled"), + HowToResolve: i18n.G("include \"traefik.enable=true\" deploy label"), Function: LintTraefikEnabled, SkipCondition: LintTraefikEnabledSkipCondition, }, { Ref: "R011", - Level: "error", - Description: "all services have images", - HowToResolve: "ensure \"image: ...\" set on all services", + Level: i18n.G("error"), + Description: i18n.G("all services have images"), + HowToResolve: i18n.G("ensure \"image: ...\" set on all services"), Function: LintImagePresent, }, { Ref: "R012", - Level: "error", - Description: "config version are vendored", - HowToResolve: "vendor config versions in an abra.sh", + Level: i18n.G("error"), + Description: i18n.G("config version are vendored"), + HowToResolve: i18n.G("vendor config versions in an abra.sh"), Function: LintAbraShVendors, }, { Ref: "R014", - Level: "error", - Description: "only annotated tags used for recipe version", - HowToResolve: "replace lightweight tag with annotated tag", + Level: i18n.G("error"), + Description: i18n.G("only annotated tags used for recipe version"), + HowToResolve: i18n.G("replace lightweight tag with annotated tag"), Function: LintValidTags, }, }, @@ -182,9 +184,9 @@ var LintRules = map[string][]LintRule{ // used in code paths such as "app deploy" to avoid nasty surprises but not for // the typical linting commands, which do handle other levels. func LintForErrors(recipe recipe.Recipe) error { - log.Debugf("linting for critical errors in %s configs", recipe.Name) + log.Debug(i18n.G("linting for critical errors in %s configs", recipe.Name)) - var errors string + var errs string for level := range LintRules { if level != "error" { @@ -198,19 +200,19 @@ func LintForErrors(recipe recipe.Recipe) error { ok, err := rule.Function(recipe) if err != nil { - errors += fmt.Sprintf("\nlint %s: %s", rule.Ref, err) + errs += i18n.G("\nlint %s: %s", rule.Ref, err) } if !ok { - errors += fmt.Sprintf("\n * %s (%s)", rule.Description, rule.Ref) + errs += fmt.Sprintf("\n * %s (%s)", rule.Description, rule.Ref) } } } - if len(errors) > 0 { - return fmt.Errorf("recipe '%s' failed lint checks:\n"+errors[1:], recipe.Name) + if len(errs) > 0 { + return errors.New(i18n.G("recipe '%s' failed lint checks:\n%s", recipe.Name, errs[1:])) } - log.Debugf("linting successful, %s is well configured", recipe.Name) + log.Debug(i18n.G("linting successful, %s is well configured", recipe.Name)) return nil } @@ -256,7 +258,7 @@ func LintAppService(recipe recipe.Recipe) (bool, error) { func LintTraefikEnabledSkipCondition(r recipe.Recipe) (bool, error) { sampleEnv, err := r.SampleEnv() if err != nil { - return false, fmt.Errorf("Unable to discover .env.sample for %s", r.Name) + return false, errors.New(i18n.G("unable to discover .env.sample for %s", r.Name)) } if _, ok := sampleEnv["DOMAIN"]; !ok { @@ -476,7 +478,7 @@ func LintSecretLengths(recipe recipe.Recipe) (bool, error) { } for name := range config.Secrets { if len(name) > 12 { - return false, fmt.Errorf("secret %s is longer than 12 characters", name) + return false, errors.New(i18n.G("secret %s is longer than 12 characters", name)) } } @@ -486,12 +488,12 @@ func LintSecretLengths(recipe recipe.Recipe) (bool, error) { func LintValidTags(recipe recipe.Recipe) (bool, error) { repo, err := git.PlainOpen(recipe.Dir) if err != nil { - return false, fmt.Errorf("unable to open %s: %s", recipe.Dir, err) + return false, errors.New(i18n.G("unable to open %s: %s", recipe.Dir, err)) } iter, err := repo.Tags() if err != nil { - log.Fatalf("unable to list local tags for %s", recipe.Name) + log.Fatal(i18n.G("unable to list local tags for %s", recipe.Name)) } if err := iter.ForEach(func(ref *plumbing.Reference) error { @@ -499,7 +501,7 @@ func LintValidTags(recipe recipe.Recipe) (bool, error) { if err != nil { switch err { case plumbing.ErrObjectNotFound: - return fmt.Errorf("invalid lightweight tag detected") + return errors.New(i18n.G("invalid lightweight tag detected")) default: return err } diff --git a/pkg/logs/logs.go b/pkg/logs/logs.go index 0820d9dc..54d6f2c6 100644 --- a/pkg/logs/logs.go +++ b/pkg/logs/logs.go @@ -3,12 +3,14 @@ package logs import ( "bufio" "context" + "errors" "fmt" "io" "os" "os/signal" "sync" + "coopcloud.tech/abra/pkg/i18n" "github.com/docker/docker/api/types" containerTypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" @@ -81,7 +83,7 @@ func TailLogs( } if _, err = io.Copy(os.Stdout, logs); err != nil && err != io.EOF { - errCh <- fmt.Errorf("tailLogs: unable to copy buffer: %s", err) + errCh <- errors.New(i18n.G("tailLogs: unable to copy buffer: %s", err)) } } }(service.ID) diff --git a/pkg/recipe/compose.go b/pkg/recipe/compose.go index 736e5f70..6faea26c 100644 --- a/pkg/recipe/compose.go +++ b/pkg/recipe/compose.go @@ -1,6 +1,7 @@ package recipe import ( + "errors" "fmt" "io/ioutil" "os" @@ -8,6 +9,7 @@ import ( "strings" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/upstream/stack" loader "coopcloud.tech/abra/pkg/upstream/stack" @@ -24,7 +26,7 @@ func (r Recipe) GetComposeFiles(appEnv map[string]string) ([]string, error) { if err := ensurePathExists(r.ComposePath); err != nil { return []string{}, err } - log.Debugf("no COMPOSE_FILE detected, loading default: %s", r.ComposePath) + log.Debug(i18n.G("no COMPOSE_FILE detected, loading default: %s", r.ComposePath)) return []string{r.ComposePath}, nil } @@ -33,7 +35,7 @@ func (r Recipe) GetComposeFiles(appEnv map[string]string) ([]string, error) { if err := ensurePathExists(path); err != nil { return []string{}, err } - log.Debugf("COMPOSE_FILE detected, loading %s", path) + log.Debug(i18n.G("COMPOSE_FILE detected, loading %s", path)) return []string{path}, nil } @@ -42,7 +44,7 @@ func (r Recipe) GetComposeFiles(appEnv map[string]string) ([]string, error) { numComposeFiles := strings.Count(composeFileEnvVar, ":") + 1 envVars := strings.SplitN(composeFileEnvVar, ":", numComposeFiles) if len(envVars) != numComposeFiles { - return composeFiles, fmt.Errorf("COMPOSE_FILE (=\"%s\") parsing failed?", composeFileEnvVar) + return composeFiles, errors.New(i18n.G("COMPOSE_FILE (=\"%s\") parsing failed?", composeFileEnvVar)) } for _, file := range envVars { @@ -53,8 +55,8 @@ func (r Recipe) GetComposeFiles(appEnv map[string]string) ([]string, error) { composeFiles = append(composeFiles, path) } - log.Debugf("COMPOSE_FILE detected (%s), loading %s", composeFileEnvVar, strings.Join(envVars, ", ")) - log.Debugf("retrieved %s configs for %s", strings.Join(composeFiles, ", "), r.Name) + log.Debug(i18n.G("COMPOSE_FILE detected (%s), loading %s", composeFileEnvVar, strings.Join(envVars, ", "))) + log.Debug(i18n.G("retrieved %s configs for %s", strings.Join(composeFiles, ", "), r.Name)) return composeFiles, nil } @@ -67,7 +69,7 @@ func (r Recipe) GetComposeConfig(env map[string]string) (*composetypes.Config, e } if len(composeFiles) == 0 { - return nil, fmt.Errorf("%s is missing a compose.yml or compose.*.yml file?", r.Name) + return nil, errors.New(i18n.G("%s is missing a compose.yml or compose.*.yml file?", r.Name)) } if env == nil { @@ -102,7 +104,7 @@ func (r Recipe) GetVersionLabelLocal() (string, error) { } if label == "" { - return label, fmt.Errorf("%s has no version label? try running \"abra recipe sync %s\" first?", r.Name, r.Name) + return label, errors.New(i18n.G("%s has no version label? try running \"abra recipe sync %s\" first?", r.Name, r.Name)) } return label, nil @@ -118,7 +120,7 @@ func (r Recipe) UpdateTag(image, tag string) (bool, error) { return false, err } - log.Debugf("considering %s config(s) for tag update", strings.Join(composeFiles, ", ")) + log.Debug(i18n.G("considering %s config(s) for tag update", strings.Join(composeFiles, ", "))) for _, composeFile := range composeFiles { opts := stack.Deploy{Composefiles: []string{composeFile}} @@ -148,13 +150,13 @@ func (r Recipe) UpdateTag(image, tag string) (bool, error) { case reference.NamedTagged: composeTag = img.(reference.NamedTagged).Tag() default: - log.Debugf("unable to parse %s, skipping", img) + log.Debug(i18n.G("unable to parse %s, skipping", img)) continue } composeImage := formatter.StripTagMeta(reference.Path(img)) - log.Debugf("parsed %s from %s", composeTag, service.Image) + log.Debug(i18n.G("parsed %s from %s", composeTag, service.Image)) if image == composeImage { bytes, err := ioutil.ReadFile(composeFile) @@ -166,7 +168,7 @@ func (r Recipe) UpdateTag(image, tag string) (bool, error) { new := fmt.Sprintf("%s:%s", composeImage, tag) replacedBytes := strings.Replace(string(bytes), old, new, -1) - log.Debugf("updating %s to %s in %s", old, new, compose.Filename) + log.Debug(i18n.G("updating %s to %s in %s", old, new, compose.Filename)) if err := os.WriteFile(compose.Filename, []byte(replacedBytes), 0o764); err != nil { return false, err @@ -186,7 +188,7 @@ func (r Recipe) UpdateLabel(pattern, serviceName, label string) error { return err } - log.Debugf("considering %s config(s) for label update", strings.Join(composeFiles, ", ")) + log.Debug(i18n.G("considering %s config(s) for label update", strings.Join(composeFiles, ", "))) for _, composeFile := range composeFiles { opts := stack.Deploy{Composefiles: []string{composeFile}} @@ -224,27 +226,27 @@ func (r Recipe) UpdateLabel(pattern, serviceName, label string) error { return err } - old := fmt.Sprintf("coop-cloud.${STACK_NAME}.version=%s", value) + old := i18n.G("coop-cloud.${STACK_NAME}.version=%s", value) replacedBytes := strings.Replace(string(bytes), old, label, -1) if old == label { - log.Warnf("%s is already set, nothing to do?", label) + log.Warnf(i18n.G("%s is already set, nothing to do?", label)) return nil } - log.Debugf("updating %s to %s in %s", old, label, compose.Filename) + log.Debug(i18n.G("updating %s to %s in %s", old, label, compose.Filename)) if err := ioutil.WriteFile(compose.Filename, []byte(replacedBytes), 0o764); err != nil { return err } - log.Infof("synced label %s to service %s", label, serviceName) + log.Infof(i18n.G("synced label %s to service %s", label, serviceName)) } } if !discovered { - log.Warn("no existing label found, automagic insertion not supported yet") - log.Fatalf("add '- \"%s\"' manually to the 'app' service in %s", label, composeFile) + log.Warn(i18n.G("no existing label found, automagic insertion not supported yet")) + log.Fatal(i18n.G("add '- \"%s\"' manually to the 'app' service in %s", label, composeFile)) } } diff --git a/pkg/recipe/files.go b/pkg/recipe/files.go index d3408fa4..cfd16942 100644 --- a/pkg/recipe/files.go +++ b/pkg/recipe/files.go @@ -1,18 +1,20 @@ package recipe import ( + "errors" "fmt" "os" "path" "coopcloud.tech/abra/pkg/envfile" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" ) func (r Recipe) SampleEnv() (map[string]string, error) { sampleEnv, err := envfile.ReadEnv(r.SampleEnvPath) if err != nil { - return sampleEnv, fmt.Errorf("unable to discover .env.sample for %s", r.Name) + return sampleEnv, errors.New(i18n.G("unable to discover .env.sample for %s", r.Name)) } return sampleEnv, nil } @@ -31,7 +33,7 @@ func (r Recipe) GetReleaseNotes(version string) (string, error) { return "", err } - title := formatter.BoldStyle.Render(fmt.Sprintf("%s release notes:", version)) + title := formatter.BoldStyle.Render(i18n.G("%s release notes:", version)) withTitle := fmt.Sprintf("%s\n%s\n", title, releaseNotes) return withTitle, nil diff --git a/pkg/recipe/git.go b/pkg/recipe/git.go index e430d71a..73b17f2a 100644 --- a/pkg/recipe/git.go +++ b/pkg/recipe/git.go @@ -1,6 +1,7 @@ package recipe import ( + "errors" "fmt" "os" "slices" @@ -10,6 +11,7 @@ import ( "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/formatter" gitPkg "coopcloud.tech/abra/pkg/git" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/tagcmp" "github.com/distribution/reference" @@ -45,9 +47,9 @@ func (r Recipe) Ensure(ctx EnsureContext) error { } if r.EnvVersion != "" && !ctx.IgnoreEnvVersion { - log.Debugf("ensuring env version %s", r.EnvVersion) + log.Debug(i18n.G("ensuring env version %s", r.EnvVersion)) if strings.Contains(r.EnvVersion, "+U") { - return fmt.Errorf("can not redeploy chaos version (%s) without --chaos", r.EnvVersion) + return errors.New(i18n.G("can not redeploy chaos version (%s) without --chaos", r.EnvVersion)) } if _, err := r.EnsureVersion(r.EnvVersion); err != nil { @@ -146,16 +148,16 @@ func (r Recipe) EnsureVersion(version string) (bool, error) { joinedTags := strings.Join(parsedTags, ", ") if joinedTags != "" { - log.Debugf("read %s as tags for recipe %s", joinedTags, r.Name) + log.Debug(i18n.G("read %s as tags for recipe %s", joinedTags, r.Name)) } var opts *git.CheckoutOptions if tagRef.String() == "" { - log.Debugf("attempting to checkout '%s' as chaos commit", version) + log.Debug(i18n.G("attempting to checkout '%s' as chaos commit", version)) hash, err := repo.ResolveRevision(plumbing.Revision(version)) if err != nil { - log.Fatalf("unable to resolve '%s': %s", version, err) + log.Fatal(i18n.G("unable to resolve '%s': %s", version, err)) } opts = &git.CheckoutOptions{Hash: *hash, Create: false, Force: true} @@ -173,7 +175,7 @@ func (r Recipe) EnsureVersion(version string) (bool, error) { return isChaosCommit, nil } - log.Debugf("successfully checked %s out to %s in %s", r.Name, tagRef.Short(), r.Dir) + log.Debug(i18n.G("successfully checked %s out to %s in %s", r.Name, tagRef.Short(), r.Dir)) return isChaosCommit, nil } @@ -182,11 +184,11 @@ func (r Recipe) EnsureVersion(version string) (bool, error) { func (r Recipe) EnsureIsClean() error { isClean, err := gitPkg.IsClean(r.Dir) if err != nil { - return fmt.Errorf("unable to check git clean status in %s: %s", r.Dir, err) + return errors.New(i18n.G("unable to check git clean status in %s: %s", r.Dir, err)) } if !isClean { - return fmt.Errorf("%s (%s) has locally unstaged changes?", r.Name, r.Dir) + return errors.New(i18n.G("%s (%s) has locally unstaged changes?", r.Name, r.Dir)) } return nil @@ -220,7 +222,7 @@ func (r Recipe) EnsureLatest() error { } if err := worktree.Checkout(checkOutOpts); err != nil { - log.Debugf("failed to check out %s in %s", branch, r.Dir) + log.Debug(i18n.G("failed to check out %s in %s", branch, r.Dir)) return err } @@ -231,33 +233,33 @@ func (r Recipe) EnsureLatest() error { func (r Recipe) EnsureUpToDate() error { repo, err := git.PlainOpen(r.Dir) if err != nil { - return fmt.Errorf("unable to open %s: %s", r.Dir, err) + return errors.New(i18n.G("unable to open %s: %s", r.Dir, err)) } remotes, err := repo.Remotes() if err != nil { - return fmt.Errorf("unable to read remotes in %s: %s", r.Dir, err) + return errors.New(i18n.G("unable to read remotes in %s: %s", r.Dir, err)) } if len(remotes) == 0 { - log.Debugf("cannot ensure %s is up-to-date, no git remotes configured", r.Name) + log.Debug(i18n.G("cannot ensure %s is up-to-date, no git remotes configured", r.Name)) return nil } worktree, err := repo.Worktree() if err != nil { - return fmt.Errorf("unable to open git work tree in %s: %s", r.Dir, err) + return errors.New(i18n.G("unable to open git work tree in %s: %s", r.Dir, err)) } branch, err := gitPkg.CheckoutDefaultBranch(repo, r.Dir) if err != nil { - return fmt.Errorf("unable to check out default branch in %s: %s", r.Dir, err) + return errors.New(i18n.G("unable to check out default branch in %s: %s", r.Dir, err)) } fetchOpts := &git.FetchOptions{Tags: git.AllTags} if err := repo.Fetch(fetchOpts); err != nil { if !strings.Contains(err.Error(), "already up-to-date") { - return fmt.Errorf("unable to fetch tags in %s: %s", r.Dir, err) + return errors.New(i18n.G("unable to fetch tags in %s: %s", r.Dir, err)) } } @@ -269,11 +271,11 @@ func (r Recipe) EnsureUpToDate() error { if err := worktree.Pull(opts); err != nil { if !strings.Contains(err.Error(), "already up-to-date") { - return fmt.Errorf("unable to git pull in %s: %s", r.Dir, err) + return errors.New(i18n.G("unable to git pull in %s: %s", r.Dir, err)) } } - log.Debugf("fetched latest git changes for %s", r.Name) + log.Debug(i18n.G("fetched latest git changes for %s", r.Name)) return nil } @@ -362,7 +364,7 @@ func (r Recipe) Tags() ([]string, error) { return version1.IsLessThan(version2) }) - log.Debugf("detected %s as tags for recipe %s", strings.Join(tags, ", "), r.Name) + log.Debug(i18n.G("detected %s as tags for recipe %s", strings.Join(tags, ", "), r.Name)) return tags, nil } @@ -373,7 +375,7 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, []string, error) { versions := RecipeVersions{} - log.Debugf("git: opening repository in %s", r.Dir) + log.Debug(i18n.G("git: opening repository in %s", r.Dir)) repo, err := git.PlainOpen(r.Dir) if err != nil { @@ -393,7 +395,7 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, []string, error) { if err := gitTags.ForEach(func(ref *plumbing.Reference) (err error) { tag := strings.TrimPrefix(string(ref.Name()), "refs/tags/") - log.Debugf("processing %s for %s", tag, r.Name) + log.Debug(i18n.G("processing %s for %s", tag, r.Name)) checkOutOpts := &git.CheckoutOptions{ Create: false, @@ -401,11 +403,11 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, []string, error) { Branch: plumbing.ReferenceName(ref.Name()), } if err := worktree.Checkout(checkOutOpts); err != nil { - log.Debugf("failed to check out %s in %s", tag, r.Dir) + log.Debug(i18n.G("failed to check out %s in %s", tag, r.Dir)) return err } - log.Debugf("git checkout: %s in %s", ref.Name(), r.Dir) + log.Debug(i18n.G("git checkout: %s in %s", ref.Name(), r.Dir)) config, err := r.GetComposeConfig(nil) if err != nil { @@ -429,7 +431,7 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, []string, error) { case reference.NamedTagged: tag = img.(reference.NamedTagged).Tag() case reference.Named: - warnMsg = append(warnMsg, fmt.Sprintf("%s service is missing image tag?", path)) + warnMsg = append(warnMsg, i18n.G("%s service is missing image tag?", path)) continue } @@ -453,7 +455,7 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, []string, error) { sortRecipeVersions(versions) - log.Debugf("collected %s for %s", versions, r.Dir) + log.Debug(i18n.G("collected %s for %s", versions, r.Dir)) var uniqueWarnings []string for _, w := range warnMsg { diff --git a/pkg/recipe/recipe.go b/pkg/recipe/recipe.go index 728fadca..85ac951a 100644 --- a/pkg/recipe/recipe.go +++ b/pkg/recipe/recipe.go @@ -12,6 +12,7 @@ import ( "strconv" "strings" + "coopcloud.tech/abra/pkg/i18n" "github.com/go-git/go-git/v5" "coopcloud.tech/abra/pkg/catalogue" @@ -70,7 +71,7 @@ func (r RecipeMeta) LatestVersion() string { version = tag } - log.Debugf("choosing %s as latest version of %s", version, r.Name) + log.Debug(i18n.G("choosing %s as latest version of %s", version, r.Name)) return version } @@ -126,7 +127,7 @@ func Get(name string) Recipe { if strings.Contains(name, ":") { split := strings.Split(name, ":") if len(split) > 2 { - log.Fatalf("version seems invalid: %s", name) + log.Fatal(i18n.G("version seems invalid: %s", name)) } name = split[0] @@ -134,7 +135,7 @@ func Get(name string) Recipe { versionRaw = version if strings.HasSuffix(version, config.DIRTY_DEFAULT) { version = strings.Replace(split[1], config.DIRTY_DEFAULT, "", 1) - log.Debugf("removed dirty suffix from .env version: %s -> %s", split[1], version) + log.Debug(i18n.G("removed dirty suffix from .env version: %s -> %s", split[1], version)) } } @@ -143,7 +144,7 @@ func Get(name string) Recipe { if strings.Contains(name, "/") { u, err := url.Parse(name) if err != nil { - log.Fatalf("invalid recipe: %s", err) + log.Fatal(i18n.G("invalid recipe: %s", err)) } u.Scheme = "https" gitURL = u.String() + ".git" @@ -171,7 +172,7 @@ func Get(name string) Recipe { dirty, err := r.IsDirty() if err != nil && !errors.Is(err, git.ErrRepositoryNotExists) { - log.Fatalf("failed to check git status of %s: %s", r.Name, err) + log.Fatal(i18n.G("failed to check git status of %s: %s", r.Name, err)) } r.Dirty = dirty @@ -195,16 +196,16 @@ type Recipe struct { // String outputs a human-friendly string representation. func (r Recipe) String() string { - out := fmt.Sprintf("{name: %s, ", r.Name) - out += fmt.Sprintf("version : %s, ", r.EnvVersion) - out += fmt.Sprintf("dirty: %v, ", r.Dirty) - out += fmt.Sprintf("dir: %s, ", r.Dir) - out += fmt.Sprintf("git url: %s, ", r.GitURL) - out += fmt.Sprintf("ssh url: %s, ", r.SSHURL) - out += fmt.Sprintf("compose: %s, ", r.ComposePath) - out += fmt.Sprintf("readme: %s, ", r.ReadmePath) - out += fmt.Sprintf("sample env: %s, ", r.SampleEnvPath) - out += fmt.Sprintf("abra.sh: %s}", r.AbraShPath) + out := i18n.G("{name: %s, ", r.Name) + out += i18n.G("version : %s, ", r.EnvVersion) + out += i18n.G("dirty: %v, ", r.Dirty) + out += i18n.G("dir: %s, ", r.Dir) + out += i18n.G("git url: %s, ", r.GitURL) + out += i18n.G("ssh url: %s, ", r.SSHURL) + out += i18n.G("compose: %s, ", r.ComposePath) + out += i18n.G("readme: %s, ", r.ReadmePath) + out += i18n.G("sample env: %s, ", r.SampleEnvPath) + out += i18n.G("abra.sh: %s}", r.AbraShPath) return out } @@ -233,7 +234,7 @@ func GetRecipeFeaturesAndCategory(r Recipe) (Features, string, []string, error) feat = Features{} ) - log.Debugf("%s: attempt recipe metadata parse", r.ReadmePath) + log.Debug(i18n.G("%s: attempt recipe metadata parse", r.ReadmePath)) readmeFS, err := ioutil.ReadFile(r.ReadmePath) if err != nil { @@ -321,12 +322,12 @@ func GetImageMetadata(imageRowString, recipeName string) (Image, []string, error if imageRowString != "" { warnMsgs = append( warnMsgs, - fmt.Sprintf("%s: image meta has incorrect format: %s", recipeName, imageRowString), + i18n.G("%s: image meta has incorrect format: %s", recipeName, imageRowString), ) } else { warnMsgs = append( warnMsgs, - fmt.Sprintf("%s: image meta is empty?", recipeName), + i18n.G("%s: image meta is empty?", recipeName), ) } @@ -357,14 +358,14 @@ func GetImageMetadata(imageRowString, recipeName string) (Image, []string, error func GetStringInBetween(recipeName, str, start, end string) (result string, err error) { s := strings.Index(str, start) if s == -1 { - return "", fmt.Errorf("%s: marker string %s not found", recipeName, start) + return "", errors.New(i18n.G("%s: marker string %s not found", recipeName, start)) } s += len(start) e := strings.Index(str[s:], end) if e == -1 { - return "", fmt.Errorf("%s: end marker %s not found", recipeName, end) + return "", errors.New(i18n.G("%s: end marker %s not found", recipeName, end)) } return str[s : s+e], nil @@ -402,7 +403,7 @@ func readRecipeCatalogueFS(target interface{}) error { return err } - log.Debugf("read recipe catalogue from file system cache in %s", config.RECIPES_JSON) + log.Debug(i18n.G("read recipe catalogue from file system cache in %s", config.RECIPES_JSON)) return nil } @@ -431,7 +432,7 @@ func VersionsOfService(recipe, serviceName string, offline bool) ([]string, erro } } - log.Debugf("detected versions %s for %s", strings.Join(versions, ", "), recipe) + log.Debug(i18n.G("detected versions %s for %s", strings.Join(versions, ", "), recipe)) return versions, nil } @@ -454,11 +455,11 @@ func GetRecipeMeta(recipeName string, offline bool) (RecipeMeta, error) { recipeMeta, ok := catl[recipeName] if !ok { return RecipeMeta{}, RecipeMissingFromCatalogue{ - err: fmt.Sprintf("recipe %s does not exist?", recipeName), + err: i18n.G("recipe %s does not exist?", recipeName), } } - log.Debugf("recipe metadata retrieved for %s", recipeName) + log.Debug(i18n.G("recipe metadata retrieved for %s", recipeName)) return recipeMeta, nil } @@ -545,13 +546,13 @@ func ReadReposMetadata(debug bool) (RepoCatalogue, error) { reposMeta := make(RepoCatalogue) pageIdx := 1 - bar := formatter.CreateProgressbar(-1, "collecting recipe listing") + bar := formatter.CreateProgressbar(-1, i18n.G("collecting recipe listing")) for { var reposList []RepoMeta pagedURL := fmt.Sprintf("%s?page=%v", ReposMetadataURL, pageIdx) - log.Debugf("fetching repo metadata from %s", pagedURL) + log.Debug(i18n.G("fetching repo metadata from %s", pagedURL)) if err := web.ReadJSON(pagedURL, &reposList); err != nil { return reposMeta, err @@ -655,7 +656,7 @@ func UpdateRepositories(repos RepoCatalogue, recipeName string, debug bool) erro cloneLimiter := limit.New(3) - retrieveBar := formatter.CreateProgressbar(barLength, "retrieving recipes") + retrieveBar := formatter.CreateProgressbar(barLength, i18n.G("retrieving recipes")) ch := make(chan string, barLength) for _, repoMeta := range repos { go func(rm RepoMeta) { diff --git a/pkg/secret/pass.go b/pkg/secret/pass.go index 1763e1f4..83b0926e 100644 --- a/pkg/secret/pass.go +++ b/pkg/secret/pass.go @@ -5,13 +5,14 @@ import ( "fmt" "os/exec" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" ) // PassInsertSecret inserts a secret into a pass store. func PassInsertSecret(secretValue, secretName, appName, server string) error { if _, err := exec.LookPath("pass"); err != nil { - return errors.New("pass command not found on $PATH, is it installed?") + return errors.New(i18n.G("pass command not found on $PATH, is it installed?")) } cmd := fmt.Sprintf( @@ -19,13 +20,13 @@ func PassInsertSecret(secretValue, secretName, appName, server string) error { secretValue, server, appName, secretName, ) - log.Debugf("attempting to run %s", cmd) + log.Debug(i18n.G("attempting to run %s", cmd)) if err := exec.Command("bash", "-c", cmd).Run(); err != nil { return err } - log.Infof("%s inserted into pass store", secretName) + log.Info(i18n.G("%s inserted into pass store", secretName)) return nil } @@ -33,7 +34,7 @@ func PassInsertSecret(secretValue, secretName, appName, server string) error { // PassRmSecret deletes a secret from a pass store. func PassRmSecret(secretName, appName, server string) error { if _, err := exec.LookPath("pass"); err != nil { - return errors.New("pass command not found on $PATH, is it installed?") + return errors.New(i18n.G("pass command not found on $PATH, is it installed?")) } cmd := fmt.Sprintf( @@ -41,13 +42,13 @@ func PassRmSecret(secretName, appName, server string) error { server, appName, secretName, ) - log.Debugf("attempting to run %s", cmd) + log.Debug(i18n.G("attempting to run %s", cmd)) if err := exec.Command("bash", "-c", cmd).Run(); err != nil { return err } - log.Infof("%s removed from pass store", secretName) + log.Info(i18n.G("%s removed from pass store", secretName)) return nil } diff --git a/pkg/secret/secret.go b/pkg/secret/secret.go index f8366c7b..2f9c9529 100644 --- a/pkg/secret/secret.go +++ b/pkg/secret/secret.go @@ -5,6 +5,7 @@ package secret import ( "context" + "errors" "fmt" "slices" "strconv" @@ -15,6 +16,7 @@ import ( "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/envfile" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/upstream/stack" loader "coopcloud.tech/abra/pkg/upstream/stack" @@ -57,7 +59,7 @@ func GeneratePassword(length uint, charset string) (string, error) { return "", err } - log.Debugf("generated %s", strings.Join(passwords, ", ")) + log.Debug(i18n.G("generated %s", strings.Join(passwords, ", "))) return passwords[0], nil } @@ -75,7 +77,7 @@ func GeneratePassphrase() (string, error) { return "", err } - log.Debugf("generated %s", strings.Join(passphrases, ", ")) + log.Debug(i18n.G("generated %s", strings.Join(passphrases, ", "))) return passphrases[0], nil } @@ -114,18 +116,18 @@ func ReadSecretsConfig(appEnvPath string, composeFiles []string, stackName strin } if len(enabledSecrets) == 0 { - log.Debugf("not generating app secrets, none enabled in recipe config") + log.Debug(i18n.G("not generating app secrets, none enabled in recipe config")) return nil, nil } secretValues := map[string]Secret{} for secretId, secretConfig := range composeConfig.Secrets { if string(secretConfig.Name[len(secretConfig.Name)-1]) == "_" { - return nil, fmt.Errorf("missing version for secret? (%s)", secretId) + return nil, errors.New(i18n.G("missing version for secret? (%s)", secretId)) } if !(slices.Contains(enabledSecrets, secretId)) { - log.Warnf("%s not enabled in recipe config, skipping", secretId) + log.Warnf(i18n.G("%s not enabled in recipe config, skipping", secretId)) continue } @@ -134,7 +136,7 @@ func ReadSecretsConfig(appEnvPath string, composeFiles []string, stackName strin value := Secret{Version: secretVersion, RemoteName: secretConfig.Name} if len(value.RemoteName) > config.MAX_DOCKER_SECRET_LENGTH { - return nil, fmt.Errorf("secret %s is > %d chars when combined with %s", secretId, config.MAX_DOCKER_SECRET_LENGTH, stackName) + return nil, errors.New(i18n.G("secret %s is > %d chars when combined with %s", secretId, config.MAX_DOCKER_SECRET_LENGTH, stackName)) } // Check if the length modifier is set for this secret. @@ -202,12 +204,12 @@ func GenerateSecrets(cl *dockerClient.Client, secrets map[string]Secret, server defer wg.Done() if secret.SkipGenerate { - log.Debugf("skipping generation of %s (generate=false)", secretName) + log.Debug(i18n.G("skipping generation of %s (generate=false)", secretName)) ch <- nil return } - log.Debugf("attempting to generate and store %s on %s", secret.RemoteName, server) + log.Debug(i18n.G("attempting to generate and store %s on %s", secret.RemoteName, server)) if secret.Length > 0 { password, err := GeneratePassword(uint(secret.Length), secret.Charset) @@ -218,7 +220,7 @@ func GenerateSecrets(cl *dockerClient.Client, secrets map[string]Secret, server if err := client.StoreSecret(cl, secret.RemoteName, password, server); err != nil { if strings.Contains(err.Error(), "AlreadyExists") { - log.Warnf("%s already exists", secret.RemoteName) + log.Warnf(i18n.G("%s already exists", secret.RemoteName)) ch <- nil } else { ch <- err @@ -238,7 +240,7 @@ func GenerateSecrets(cl *dockerClient.Client, secrets map[string]Secret, server if err := client.StoreSecret(cl, secret.RemoteName, passphrase, server); err != nil { if strings.Contains(err.Error(), "AlreadyExists") { - log.Warnf("%s already exists", secret.RemoteName) + log.Warnf(i18n.G("%s already exists", secret.RemoteName)) ch <- nil } else { ch <- err @@ -263,7 +265,7 @@ func GenerateSecrets(cl *dockerClient.Client, secrets map[string]Secret, server } } - log.Debugf("generated and stored %v on %s", secrets, server) + log.Debug(i18n.G("generated and stored %v on %s", secrets, server)) return secretsGenerated, nil } diff --git a/pkg/server/server.go b/pkg/server/server.go index 8f723a69..b9288ac6 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -5,6 +5,7 @@ import ( "path" "coopcloud.tech/abra/pkg/config" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" ) @@ -17,11 +18,11 @@ func CreateServerDir(serverName string) error { return err } - log.Debugf("%s already exists", serverPath) + log.Debug(i18n.G("%s already exists", serverPath)) return nil } - log.Debugf("successfully created %s", serverPath) + log.Debug(i18n.G("successfully created %s", serverPath)) return nil } diff --git a/pkg/service/service.go b/pkg/service/service.go index 32d1f8f6..c0eef8a6 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -2,10 +2,12 @@ package service import ( "context" + "errors" "fmt" "strings" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/AlecAivazis/survey/v2" "github.com/docker/docker/api/types" @@ -25,7 +27,7 @@ func GetServiceByLabel(c context.Context, cl *client.Client, label string, promp } if len(services) == 0 { - return swarm.Service{}, fmt.Errorf("no services deployed?") + return swarm.Service{}, errors.New(i18n.G("no services deployed?")) } var matchingServices []swarm.Service @@ -36,7 +38,7 @@ func GetServiceByLabel(c context.Context, cl *client.Client, label string, promp } if len(matchingServices) == 0 { - return swarm.Service{}, fmt.Errorf("no services deployed matching label '%s'?", label) + return swarm.Service{}, errors.New(i18n.G("no services deployed matching label '%s'?", label)) } if len(matchingServices) > 1 { @@ -48,15 +50,15 @@ func GetServiceByLabel(c context.Context, cl *client.Client, label string, promp } if !prompt { - err := fmt.Errorf("expected 1 service but found %v: %s", len(matchingServices), strings.Join(servicesRaw, " ")) + err := errors.New(i18n.G("expected 1 service but found %v: %s", len(matchingServices), strings.Join(servicesRaw, " "))) return swarm.Service{}, err } - log.Warnf("ambiguous service list received, prompting for input") + log.Warn(i18n.G("ambiguous service list received, prompting for input")) var response string prompt := &survey.Select{ - Message: "which service are you looking for?", + Message: i18n.G("which service are you looking for?"), Options: servicesRaw, } @@ -72,7 +74,7 @@ func GetServiceByLabel(c context.Context, cl *client.Client, label string, promp } } - log.Fatal("failed to match chosen service") + log.Fatal(i18n.G("failed to match chosen service")) } return matchingServices[0], nil @@ -90,7 +92,7 @@ func GetService(c context.Context, cl *client.Client, filters filters.Args, prom if len(services) == 0 { filter := filters.Get("name")[0] - return swarm.Service{}, fmt.Errorf("no services matching the %v filter found?", filter) + return swarm.Service{}, errors.New(i18n.G("no services matching the %v filter found?", filter)) } if len(services) != 1 { @@ -98,19 +100,19 @@ func GetService(c context.Context, cl *client.Client, filters filters.Args, prom for _, service := range services { serviceName := service.Spec.Name created := formatter.HumanDuration(service.CreatedAt.Unix()) - servicesRaw = append(servicesRaw, fmt.Sprintf("%s (created %v)", serviceName, created)) + servicesRaw = append(servicesRaw, i18n.G("%s (created %v)", serviceName, created)) } if !prompt { - err := fmt.Errorf("expected 1 service but found %v: %s", len(services), strings.Join(servicesRaw, " ")) + err := errors.New(i18n.G("expected 1 service but found %v: %s", len(services), strings.Join(servicesRaw, " "))) return swarm.Service{}, err } - log.Warnf("ambiguous service list received, prompting for input") + log.Warn(i18n.G("ambiguous service list received, prompting for input")) var response string prompt := &survey.Select{ - Message: "which service are you looking for?", + Message: i18n.G("which service are you looking for?"), Options: servicesRaw, } @@ -126,7 +128,7 @@ func GetService(c context.Context, cl *client.Client, filters filters.Args, prom } } - log.Fatal("failed to match chosen service") + log.Fatal(i18n.G("failed to match chosen service")) } return services[0], nil diff --git a/pkg/ssh/ssh.go b/pkg/ssh/ssh.go index 96489f43..99a46efb 100644 --- a/pkg/ssh/ssh.go +++ b/pkg/ssh/ssh.go @@ -1,8 +1,10 @@ package ssh import ( - "fmt" + "errors" "strings" + + "coopcloud.tech/abra/pkg/i18n" ) // Fatal is a error output wrapper which aims to make SSH failures easier to @@ -11,17 +13,17 @@ func Fatal(hostname string, err error) error { out := err.Error() if strings.Contains(out, "Host key verification failed.") { - return fmt.Errorf("SSH host key verification failed for %s", hostname) + return errors.New(i18n.G("SSH host key verification failed for %s", hostname)) } else if strings.Contains(out, "Could not resolve hostname") { - return fmt.Errorf("could not resolve hostname for %s", hostname) + return errors.New(i18n.G("could not resolve hostname for %s", hostname)) } else if strings.Contains(out, "Connection timed out") { - return fmt.Errorf("connection timed out for %s", hostname) + return errors.New(i18n.G("connection timed out for %s", hostname)) } else if strings.Contains(out, "Permission denied") { - return fmt.Errorf("ssh auth: permission denied for %s", hostname) + return errors.New(i18n.G("ssh auth: permission denied for %s", hostname)) } else if strings.Contains(out, "Network is unreachable") { - return fmt.Errorf("unable to connect to %s, please check your SSH config", hostname) + return errors.New(i18n.G("unable to connect to %s, please check your SSH config", hostname)) } else if strings.Contains(out, "Is the docker daemon running") { - return fmt.Errorf("docker: is the daemon running / your user has docker permissions?") + return errors.New(i18n.G("docker: is the daemon running / your user has docker permissions?")) } return err diff --git a/pkg/ui/deploy.go b/pkg/ui/deploy.go index 5b3a8ea8..20a8c87a 100644 --- a/pkg/ui/deploy.go +++ b/pkg/ui/deploy.go @@ -3,13 +3,13 @@ package ui import ( "context" "encoding/json" - "fmt" "io" "sort" "strings" "time" "coopcloud.tech/abra/pkg/formatter" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/logs" tea "github.com/charmbracelet/bubbletea" "github.com/docker/cli/cli/command/service/progress" @@ -79,13 +79,13 @@ type stream struct { } func (s stream) String() string { - out := fmt.Sprintf("{decoder: %v, ", s.decoder) - out += fmt.Sprintf("err: %v, ", s.Err) - out += fmt.Sprintf("id: %s, ", s.id) - out += fmt.Sprintf("name: %s, ", s.Name) - out += fmt.Sprintf("reader: %v, ", s.reader) - out += fmt.Sprintf("writer: %v, ", s.writer) - out += fmt.Sprintf("status: %s, ", s.status) + out := i18n.G("{decoder: %v, ", s.decoder) + out += i18n.G("err: %v, ", s.Err) + out += i18n.G("id: %s, ", s.id) + out += i18n.G("name: %s, ", s.Name) + out += i18n.G("reader: %v, ", s.reader) + out += i18n.G("writer: %v, ", s.writer) + out += i18n.G("status: %s}", s.status) return out } @@ -118,7 +118,7 @@ func (s stream) process() tea.Msg { func (s stream) healthcheck(m Model) tea.Msg { filters := filters.NewArgs() - filters.Add("name", fmt.Sprintf("^%s", s.Name)) + filters.Add("name", i18n.G("^%s", s.Name)) containers, err := m.cl.ContainerList(m.ctx, containerTypes.ListOptions{Filters: filters}) if err != nil { @@ -327,10 +327,10 @@ func (m Model) View() string { status := stream.status if strings.Contains(stream.status, "converged") && !stream.rollback { - status = "succeeded" + status = i18n.G("succeeded") } if strings.Contains(stream.status, "rolled back") { - status = "rolled back" + status = i18n.G("rolled back") } retries := 0 @@ -338,7 +338,7 @@ func (m Model) View() string { retries = stream.retries } - output := fmt.Sprintf("%s: %s (retries: %v, healthcheck: %s)", + output := i18n.G("%s: %s (retries: %v, healthcheck: %s)", formatter.BoldStyle.Render(short), status, retries, diff --git a/pkg/upstream/commandconn/commandconn.go b/pkg/upstream/commandconn/commandconn.go index 9f76c50f..3b40218a 100644 --- a/pkg/upstream/commandconn/commandconn.go +++ b/pkg/upstream/commandconn/commandconn.go @@ -17,7 +17,6 @@ package commandconn import ( "bytes" "context" - "fmt" "io" "net" "os" @@ -27,6 +26,7 @@ import ( "syscall" "time" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/pkg/errors" exec "golang.org/x/sys/execabs" @@ -46,7 +46,7 @@ func New(ctx context.Context, cmd string, args ...string) (net.Conn, error) { ) c.cmd = exec.CommandContext(ctx, cmd, args...) // we assume that args never contains sensitive information - log.Debugf("commandconn: starting %s with %v", cmd, args) + log.Debug(i18n.G("commandconn: starting %s with %v", cmd, args)) c.cmd.Env = os.Environ() c.cmd.SysProcAttr = &syscall.SysProcAttr{} setPdeathsig(c.cmd) @@ -62,7 +62,7 @@ func New(ctx context.Context, cmd string, args ...string) (net.Conn, error) { c.cmd.Stderr = &stderrWriter{ stderrMu: &c.stderrMu, stderr: &c.stderr, - debugPrefix: fmt.Sprintf("commandconn (%s):", cmd), + debugPrefix: i18n.G("commandconn (%s):", cmd), } c.localAddr = dummyAddr{network: "dummy", s: "dummy-0"} c.remoteAddr = dummyAddr{network: "dummy", s: "dummy-1"} @@ -138,7 +138,7 @@ func (c *commandConn) kill() error { return nil } } - return errors.Wrapf(werr, "commandconn: failed to wait") + return errors.Wrap(werr, i18n.G("commandconn: failed to wait")) } func (c *commandConn) onEOF(eof error) error { @@ -159,7 +159,7 @@ func (c *commandConn) onEOF(eof error) error { c.stderrMu.Lock() stderr := c.stderr.String() c.stderrMu.Unlock() - return errors.Errorf("command %v did not exit after %v: stderr=%q", c.cmd.Args, eof, stderr) + return errors.New(i18n.G("command %v did not exit after %v: stderr=%q", c.cmd.Args, eof, stderr)) } } c.cmdMutex.Unlock() @@ -169,7 +169,7 @@ func (c *commandConn) onEOF(eof error) error { c.stderrMu.Lock() stderr := c.stderr.String() c.stderrMu.Unlock() - return errors.Errorf("command %v has exited with %v, please make sure the URL is valid, and Docker 18.09 or later is installed on the remote host: stderr=%s", c.cmd.Args, werr, stderr) + return errors.New(i18n.G("command %v has exited with %v, please make sure the URL is valid, and Docker 18.09 or later is installed on the remote host: stderr=%s", c.cmd.Args, werr, stderr)) } func ignorableCloseError(err error) bool { @@ -236,7 +236,7 @@ func (c *commandConn) Write(p []byte) (int, error) { func (c *commandConn) Close() error { var err error if err = c.CloseRead(); err != nil { - log.Warnf("commandConn.Close: CloseRead: %v", err) + log.Warnf(i18n.G("commandConn.Close: CloseRead: %v", err)) } if err = c.CloseWrite(); err != nil { // muted because https://github.com/docker/compose/issues/8544 @@ -252,15 +252,15 @@ func (c *commandConn) RemoteAddr() net.Addr { return c.remoteAddr } func (c *commandConn) SetDeadline(t time.Time) error { - log.Debugf("unimplemented call: SetDeadline(%v)", t) + log.Debug(i18n.G("unimplemented call: SetDeadline(%v)", t)) return nil } func (c *commandConn) SetReadDeadline(t time.Time) error { - log.Debugf("unimplemented call: SetReadDeadline(%v)", t) + log.Debug(i18n.G("unimplemented call: SetReadDeadline(%v)", t)) return nil } func (c *commandConn) SetWriteDeadline(t time.Time) error { - log.Debugf("unimplemented call: SetWriteDeadline(%v)", t) + log.Debug(i18n.G("unimplemented call: SetWriteDeadline(%v)", t)) return nil } diff --git a/pkg/upstream/commandconn/connection.go b/pkg/upstream/commandconn/connection.go index f48a77af..93e7a142 100644 --- a/pkg/upstream/commandconn/connection.go +++ b/pkg/upstream/commandconn/connection.go @@ -6,6 +6,7 @@ import ( "net" "net/url" + "coopcloud.tech/abra/pkg/i18n" "github.com/docker/cli/cli/connhelper" "github.com/docker/cli/cli/connhelper/ssh" "github.com/docker/cli/cli/context/docker" @@ -34,7 +35,7 @@ func getConnectionHelper(daemonURL string, sshFlags []string) (*connhelper.Conne case "ssh": ctxConnDetails, err := ssh.ParseURL(daemonURL) if err != nil { - return nil, errors.Wrap(err, "ssh host connection is not valid") + return nil, errors.Wrap(err, i18n.G("ssh host connection is not valid")) } return &connhelper.ConnectionHelper{ diff --git a/pkg/upstream/container/exec.go b/pkg/upstream/container/exec.go index ff551008..7e90ea29 100644 --- a/pkg/upstream/container/exec.go +++ b/pkg/upstream/container/exec.go @@ -6,6 +6,7 @@ import ( "fmt" "io" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -39,7 +40,7 @@ func RunExec(dockerCli command.Cli, client *apiclient.Client, containerID string execID := response.ID if execID == "" { - return nil, errors.New("exec ID empty") + return nil, errors.New(i18n.G("exec ID empty")) } if execOptions.Detach { @@ -104,12 +105,12 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, client *apiclie if execOpts.Tty && dockerCli.In().IsTerminal() { if err := MonitorTtySize(ctx, client, dockerCli, execID, true); err != nil { - fmt.Fprintln(dockerCli.Err(), "Error monitoring TTY size:", err) + fmt.Fprintln(dockerCli.Err(), i18n.G("Error monitoring TTY size:"), err) } } if err := <-errCh; err != nil { - log.Debugf("Error hijack: %s", err) + log.Debug(i18n.G("Error hijack: %s", err)) return out, err } diff --git a/pkg/upstream/container/hijack.go b/pkg/upstream/container/hijack.go index f2a9990e..0f544888 100644 --- a/pkg/upstream/container/hijack.go +++ b/pkg/upstream/container/hijack.go @@ -2,11 +2,12 @@ package container // https://github.com/docker/cli/blob/master/cli/command/conta import ( "context" - "fmt" + "errors" "io" "runtime" "sync" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/docker/cli/cli/command" "github.com/docker/docker/api/types" @@ -39,7 +40,7 @@ type hijackedIOStreamer struct { func (h *hijackedIOStreamer) stream(ctx context.Context) error { restoreInput, err := h.setupInput() if err != nil { - return fmt.Errorf("unable to setup input stream: %s", err) + return errors.New(i18n.G("unable to setup input stream: %s", err)) } defer restoreInput() @@ -78,7 +79,7 @@ func (h *hijackedIOStreamer) setupInput() (restore func(), err error) { } if err := setRawTerminal(h.streams); err != nil { - return nil, fmt.Errorf("unable to set IO streams as raw terminal: %s", err) + return nil, errors.New(i18n.G("unable to set IO streams as raw terminal: %s", err)) } // Use sync.Once so we may call restore multiple times but ensure we @@ -96,7 +97,7 @@ func (h *hijackedIOStreamer) setupInput() (restore func(), err error) { if h.detachKeys != "" { customEscapeKeys, err := term.ToBytes(h.detachKeys) if err != nil { - log.Warnf("invalid detach escape keys, using default: %s", err) + log.Warnf(i18n.G("invalid detach escape keys, using default: %s", err)) } else { escapeKeys = customEscapeKeys } @@ -128,10 +129,10 @@ func (h *hijackedIOStreamer) beginOutputStream(restoreInput func()) <-chan error _, err = stdcopy.StdCopy(h.outputStream, h.errorStream, h.resp.Reader) } - log.Debug("[hijack] End of stdout") + log.Debug(i18n.G("[hijack] end of stdout")) if err != nil { - log.Debugf("Error receiveStdout: %s", err) + log.Debug(i18n.G("error receiveStdout: %s", err)) } outputDone <- err @@ -152,7 +153,7 @@ func (h *hijackedIOStreamer) beginInputStream(restoreInput func()) (doneC <-chan // messages will be in normal type. restoreInput() - log.Debug("[hijack] End of stdin") + log.Debug(i18n.G("[hijack] End of stdin")) if _, ok := err.(term.EscapeError); ok { detached <- err @@ -163,12 +164,12 @@ func (h *hijackedIOStreamer) beginInputStream(restoreInput func()) (doneC <-chan // This error will also occur on the receive // side (from stdout) where it will be // propagated back to the caller. - log.Debugf("Error sendStdin: %s", err) + log.Debug(i18n.G("error sendStdin: %s", err)) } } if err := h.resp.CloseWrite(); err != nil { - log.Debugf("Couldn't send EOF: %s", err) + log.Debug(i18n.G("couldn't send EOF: %s", err)) } close(inputDone) diff --git a/pkg/upstream/container/tty.go b/pkg/upstream/container/tty.go index 2a5a55ae..0b975c35 100644 --- a/pkg/upstream/container/tty.go +++ b/pkg/upstream/container/tty.go @@ -8,6 +8,7 @@ import ( "runtime" "time" + "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/log" "github.com/docker/cli/cli/command" "github.com/docker/docker/api/types/container" @@ -35,7 +36,7 @@ func resizeTtyTo(ctx context.Context, client client.ContainerAPIClient, id strin } if err != nil { - log.Debugf("Error resize: %s\r", err) + log.Debug(fmt.Sprintf("%s\r", i18n.G("error resize: %s", err))) } return err } @@ -62,7 +63,7 @@ func initTtySize(ctx context.Context, client *apiclient.Client, cli command.Cli, } } if err != nil { - fmt.Fprintln(cli.Err(), "failed to resize tty, using default size") + fmt.Fprintln(cli.Err(), i18n.G("failed to resize tty, using default size")) } }() } diff --git a/pkg/upstream/convert/service.go b/pkg/upstream/convert/service.go index 16cd0cfa..035d343e 100644 --- a/pkg/upstream/convert/service.go +++ b/pkg/upstream/convert/service.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "coopcloud.tech/abra/pkg/i18n" composetypes "github.com/docker/cli/cli/compose/types" "github.com/docker/cli/opts" "github.com/docker/docker/api/types" @@ -39,7 +40,7 @@ func ParseSecrets(client client.SecretAPIClient, requestedSecrets []*swarmtypes. for _, secret := range requestedSecrets { if _, exists := secretRefs[secret.File.Name]; exists { - return nil, errors.Errorf("duplicate secret target for %s not allowed", secret.SecretName) + return nil, errors.New(i18n.G("duplicate secret target for %s not allowed", secret.SecretName)) } secretRef := new(swarmtypes.SecretReference) *secretRef = *secret @@ -68,7 +69,7 @@ func ParseSecrets(client client.SecretAPIClient, requestedSecrets []*swarmtypes. for _, ref := range secretRefs { id, ok := foundSecrets[ref.SecretName] if !ok { - return nil, errors.Errorf("secret not found: %s", ref.SecretName) + return nil, errors.New(i18n.G("secret not found: %s", ref.SecretName)) } // set the id for the ref to properly assign in swarm @@ -118,7 +119,7 @@ func ParseConfigs(client client.ConfigAPIClient, requestedConfigs []*swarmtypes. } if _, exists := configRefs[config.File.Name]; exists { - return nil, errors.Errorf("duplicate config target for %s not allowed", config.ConfigName) + return nil, errors.New(i18n.G("duplicate config target for %s not allowed", config.ConfigName)) } configRefs[config.File.Name] = configRef @@ -149,7 +150,7 @@ func ParseConfigs(client client.ConfigAPIClient, requestedConfigs []*swarmtypes. for _, ref := range configRefs { id, ok := foundConfigs[ref.ConfigName] if !ok { - return nil, errors.Errorf("config not found: %s", ref.ConfigName) + return nil, errors.New(i18n.G("config not found: %s", ref.ConfigName)) } // set the id for the ref to properly assign in swarm @@ -164,7 +165,7 @@ func ParseConfigs(client client.ConfigAPIClient, requestedConfigs []*swarmtypes. for _, ref := range runtimeRefs { id, ok := foundConfigs[ref.ConfigName] if !ok { - return nil, errors.Errorf("config not found: %s", ref.ConfigName) + return nil, errors.New(i18n.G("config not found: %s", ref.ConfigName)) } ref.ConfigID = id @@ -371,7 +372,7 @@ func convertServiceNetworks( for networkName, network := range networks { networkConfig, ok := networkConfigs[networkName] if !ok && networkName != defaultNetwork { - return nil, errors.Errorf("undefined network %q", networkName) + return nil, errors.New(i18n.G("undefined network %q", networkName)) } var aliases []string if network != nil { @@ -410,7 +411,7 @@ func convertServiceSecrets( lookup := func(key string) (composetypes.FileObjectConfig, error) { secretSpec, exists := secretSpecs[key] if !exists { - return composetypes.FileObjectConfig{}, errors.Errorf("undefined secret %q", key) + return composetypes.FileObjectConfig{}, errors.New(i18n.G("undefined secret %q", key)) } return composetypes.FileObjectConfig(secretSpec), nil } @@ -458,7 +459,7 @@ func convertServiceConfigObjs( lookup := func(key string) (composetypes.FileObjectConfig, error) { configSpec, exists := configSpecs[key] if !exists { - return composetypes.FileObjectConfig{}, errors.Errorf("undefined config %q", key) + return composetypes.FileObjectConfig{}, errors.New(i18n.G("undefined config %q", key)) } return composetypes.FileObjectConfig(configSpec), nil } @@ -600,7 +601,7 @@ func convertHealthcheck(healthcheck *composetypes.HealthCheckConfig) (*container ) if healthcheck.Disable { if len(healthcheck.Test) != 0 { - return nil, errors.Errorf("test and disable can't be set at the same time") + return nil, errors.New(i18n.G("test and disable can't be set at the same time")) } return &container.HealthConfig{ Test: []string{"NONE"}, @@ -648,7 +649,7 @@ func convertRestartPolicy(restart string, source *composetypes.RestartPolicy) (* MaxAttempts: &attempts, }, nil default: - return nil, errors.Errorf("unknown restart policy: %s", restart) + return nil, errors.New(i18n.G("unknown restart policy: %s", restart)) } } @@ -770,13 +771,13 @@ func convertDeployMode(mode string, replicas *uint64) (swarm.ServiceMode, error) switch mode { case "global": if replicas != nil { - return serviceMode, errors.Errorf("replicas can only be used with replicated mode") + return serviceMode, errors.New(i18n.G("replicas can only be used with replicated mode")) } serviceMode.Global = &swarm.GlobalService{} case "replicated", "": serviceMode.Replicated = &swarm.ReplicatedService{Replicas: replicas} default: - return serviceMode, errors.Errorf("Unknown mode: %s", mode) + return serviceMode, errors.New(i18n.G("unknown mode: %s", mode)) } return serviceMode, nil } @@ -809,9 +810,9 @@ func convertCredentialSpec(namespace Namespace, spec composetypes.CredentialSpec case l == 0: return nil, nil case l == 2: - return nil, errors.Errorf("invalid credential spec: cannot specify both %s and %s", o[0], o[1]) + return nil, errors.New(i18n.G("invalid credential spec: cannot specify both %s and %s", o[0], o[1])) case l > 2: - return nil, errors.Errorf("invalid credential spec: cannot specify both %s, and %s", strings.Join(o[:l-1], ", "), o[l-1]) + return nil, errors.New(i18n.G("invalid credential spec: cannot specify both %s, and %s", strings.Join(o[:l-1], ", "), o[l-1])) } swarmCredSpec := swarm.CredentialSpec(spec) // if we're using a swarm Config for the credential spec, over-write it @@ -830,7 +831,7 @@ func convertCredentialSpec(namespace Namespace, spec composetypes.CredentialSpec return &swarmCredSpec, nil } } - return nil, errors.Errorf("invalid credential spec: spec specifies config %v, but no such config can be found", swarmCredSpec.Config) + return nil, errors.New(i18n.G("invalid credential spec: spec specifies config %v, but no such config can be found", swarmCredSpec.Config)) } return &swarmCredSpec, nil } diff --git a/pkg/upstream/convert/volume.go b/pkg/upstream/convert/volume.go index 387fb727..577f30de 100644 --- a/pkg/upstream/convert/volume.go +++ b/pkg/upstream/convert/volume.go @@ -1,6 +1,7 @@ package convert // https://github.com/docker/cli/blob/master/cli/compose/convert/volume.go import ( + "coopcloud.tech/abra/pkg/i18n" composetypes "github.com/docker/cli/cli/compose/types" "github.com/docker/docker/api/types/mount" "github.com/pkg/errors" @@ -40,10 +41,10 @@ func handleVolumeToMount( result := createMountFromVolume(volume) if volume.Tmpfs != nil { - return mount.Mount{}, errors.New("tmpfs options are incompatible with type volume") + return mount.Mount{}, errors.New(i18n.G("tmpfs options are incompatible with type volume")) } if volume.Bind != nil { - return mount.Mount{}, errors.New("bind options are incompatible with type volume") + return mount.Mount{}, errors.New(i18n.G("bind options are incompatible with type volume")) } // Anonymous volumes if volume.Source == "" { @@ -52,7 +53,7 @@ func handleVolumeToMount( stackVolume, exists := stackVolumes[volume.Source] if !exists { - return mount.Mount{}, errors.Errorf("undefined volume %q", volume.Source) + return mount.Mount{}, errors.New(i18n.G("undefined volume %q", volume.Source)) } result.Source = namespace.Scope(volume.Source) @@ -86,13 +87,13 @@ func handleBindToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, er result := createMountFromVolume(volume) if volume.Source == "" { - return mount.Mount{}, errors.New("invalid bind source, source cannot be empty") + return mount.Mount{}, errors.New(i18n.G("invalid bind source, source cannot be empty")) } if volume.Volume != nil { - return mount.Mount{}, errors.New("volume options are incompatible with type bind") + return mount.Mount{}, errors.New(i18n.G("volume options are incompatible with type bind")) } if volume.Tmpfs != nil { - return mount.Mount{}, errors.New("tmpfs options are incompatible with type bind") + return mount.Mount{}, errors.New(i18n.G("tmpfs options are incompatible with type bind")) } if volume.Bind != nil { result.BindOptions = &mount.BindOptions{ @@ -106,13 +107,13 @@ func handleTmpfsToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, e result := createMountFromVolume(volume) if volume.Source != "" { - return mount.Mount{}, errors.New("invalid tmpfs source, source must be empty") + return mount.Mount{}, errors.New(i18n.G("invalid tmpfs source, source must be empty")) } if volume.Bind != nil { - return mount.Mount{}, errors.New("bind options are incompatible with type tmpfs") + return mount.Mount{}, errors.New(i18n.G("bind options are incompatible with type tmpfs")) } if volume.Volume != nil { - return mount.Mount{}, errors.New("volume options are incompatible with type tmpfs") + return mount.Mount{}, errors.New(i18n.G("volume options are incompatible with type tmpfs")) } if volume.Tmpfs != nil { result.TmpfsOptions = &mount.TmpfsOptions{ @@ -126,13 +127,13 @@ func handleNpipeToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, e result := createMountFromVolume(volume) if volume.Source == "" { - return mount.Mount{}, errors.New("invalid npipe source, source cannot be empty") + return mount.Mount{}, errors.New(i18n.G("invalid npipe source, source cannot be empty")) } if volume.Volume != nil { - return mount.Mount{}, errors.New("volume options are incompatible with type npipe") + return mount.Mount{}, errors.New(i18n.G("volume options are incompatible with type npipe")) } if volume.Tmpfs != nil { - return mount.Mount{}, errors.New("tmpfs options are incompatible with type npipe") + return mount.Mount{}, errors.New(i18n.G("tmpfs options are incompatible with type npipe")) } if volume.Bind != nil { result.BindOptions = &mount.BindOptions{ @@ -158,5 +159,5 @@ func convertVolumeToMount( case "npipe": return handleNpipeToMount(volume) } - return mount.Mount{}, errors.New("volume type must be volume, bind, tmpfs or npipe") + return mount.Mount{}, errors.New(i18n.G("volume type must be volume, bind, tmpfs or npipe")) } diff --git a/pkg/web/web.go b/pkg/web/web.go index b1f871ab..09f764ec 100644 --- a/pkg/web/web.go +++ b/pkg/web/web.go @@ -3,11 +3,13 @@ package web import ( "encoding/json" - "fmt" + "errors" "io" "net/http" "os" "time" + + "coopcloud.tech/abra/pkg/i18n" ) // Timeout is the time it takes before a web request bails out waiting for a @@ -40,7 +42,7 @@ func GetFile(filepath string, url string) (err error) { defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return fmt.Errorf("bad status: %s", resp.Status) + return errors.New(i18n.G("bad status: %s", resp.Status)) } _, err = io.Copy(out, resp.Body) diff --git a/vendor/modules.txt b/vendor/modules.txt index b6d67d59..0595f6a0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -7,8 +7,6 @@ dario.cat/mergo # git.coopcloud.tech/toolshed/godotenv v1.5.2-0.20250103171850-4d0ca41daa5c ## explicit; go 1.12 git.coopcloud.tech/toolshed/godotenv -# github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 -## explicit; go 1.20 # github.com/AlecAivazis/survey/v2 v2.3.7 ## explicit; go 1.13 github.com/AlecAivazis/survey/v2 @@ -237,8 +235,6 @@ github.com/docker/docker/pkg/streamformatter ## explicit; go 1.21 github.com/docker/docker-credential-helpers/client github.com/docker/docker-credential-helpers/credentials -# github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c -## explicit # github.com/docker/go-connections v0.6.0 ## explicit; go 1.18 github.com/docker/go-connections/nat @@ -267,8 +263,6 @@ github.com/erikgeiser/coninput # github.com/felixge/httpsnoop v1.0.4 ## explicit; go 1.13 github.com/felixge/httpsnoop -# github.com/fsnotify/fsnotify v1.6.0 -## explicit; go 1.16 # github.com/fvbommel/sortorder v1.1.0 ## explicit; go 1.13 github.com/fvbommel/sortorder @@ -433,8 +427,6 @@ github.com/mgutz/ansi # github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db ## explicit github.com/mitchellh/colorstring -# github.com/mitchellh/mapstructure v1.5.0 -## explicit; go 1.14 # github.com/moby/docker-image-spec v1.3.1 ## explicit; go 1.18 github.com/moby/docker-image-spec/specs-go/v1 @@ -449,8 +441,6 @@ github.com/moby/patternmatcher # github.com/moby/sys/atomicwriter v0.1.0 ## explicit; go 1.18 github.com/moby/sys/atomicwriter -# github.com/moby/sys/mountinfo v0.7.2 -## explicit; go 1.17 # github.com/moby/sys/sequential v0.6.0 ## explicit; go 1.17 github.com/moby/sys/sequential @@ -494,8 +484,6 @@ github.com/opencontainers/image-spec/specs-go/v1 ## explicit; go 1.18 # github.com/opencontainers/runtime-spec v1.1.0 ## explicit -# github.com/pelletier/go-toml v1.9.5 -## explicit; go 1.12 # github.com/pjbgf/sha1cd v0.4.0 ## explicit; go 1.21 github.com/pjbgf/sha1cd