diff --git a/cmd/cmd.go b/cmd/cmd.go index 6cbdf4dd..e6a6977e 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -105,6 +105,8 @@ func preRun(cmd *cobra.Command, _ []string) error { log.Err(err).Msg("Notifications ping finished failed") } }) + + cmd.SetContext(notifier.NewContext(cmd.Context(), handler)) } } diff --git a/go.mod b/go.mod index eccaea50..59215e75 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/gabe565/go-spinners v1.0.1 github.com/klauspost/pgzip v1.2.6 github.com/mattn/go-isatty v0.0.20 + github.com/muesli/termenv v0.15.2 github.com/rs/zerolog v1.32.0 github.com/schollz/progressbar/v3 v3.14.2 github.com/spf13/cobra v1.8.0 @@ -103,7 +104,6 @@ require ( github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect - github.com/muesli/termenv v0.15.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/pelletier/go-toml/v2 v2.2.0 // indirect diff --git a/internal/actions/dump/dump.go b/internal/actions/dump/dump.go index bb55563d..fdc30243 100644 --- a/internal/actions/dump/dump.go +++ b/internal/actions/dump/dump.go @@ -17,11 +17,13 @@ import ( "github.com/clevyr/kubedb/internal/database/sqlformat" "github.com/clevyr/kubedb/internal/github" "github.com/clevyr/kubedb/internal/kubernetes" + "github.com/clevyr/kubedb/internal/notifier" "github.com/clevyr/kubedb/internal/progressbar" "github.com/clevyr/kubedb/internal/storage" "github.com/clevyr/kubedb/internal/tui" "github.com/clevyr/kubedb/internal/util" gzip "github.com/klauspost/pgzip" + "github.com/muesli/termenv" "github.com/rs/zerolog/log" "github.com/spf13/viper" "golang.org/x/sync/errgroup" @@ -178,6 +180,12 @@ func (action Dump) Run(ctx context.Context) error { Stringer("took", time.Since(startTime).Truncate(10*time.Millisecond)). Stringer("size", sizeW). Msg("dump complete") + + if handler, ok := notifier.FromContext(ctx); ok { + if logger, ok := handler.(notifier.Logs); ok { + logger.SetLog(action.summary(nil, time.Since(startTime).Truncate(10*time.Millisecond), sizeW, true)) + } + } return nil } @@ -202,13 +210,15 @@ func (action Dump) buildCommand() (*command.Builder, error) { return cmd, nil } -func (action Dump) printSummary(err error, took time.Duration, size *util.SizeWriter) { - out := os.Stdout - if action.Filename == "-" { - out = os.Stderr +func (action Dump) summary(err error, took time.Duration, size *util.SizeWriter, plain bool) string { + var r *lipgloss.Renderer + if plain { + r = lipgloss.NewRenderer(os.Stdout, termenv.WithTTY(false)) + r.SetColorProfile(termenv.Ascii) + r.SetHasDarkBackground(lipgloss.HasDarkBackground()) } - t := tui.MinimalTable(). + t := tui.MinimalTable(r). Row("Context", action.Context). Row("Namespace", action.Namespace). Row("Pod", action.DBPod.Name) @@ -218,18 +228,24 @@ func (action Dump) printSummary(err error, took time.Duration, size *util.SizeWr if action.Database != "" { t.Row("Database", action.Database) } - t.Row("File", tui.OutPath(action.Filename)). + t.Row("File", tui.OutPath(action.Filename, r)). Row("Took", took.String()) if err != nil { - t.Row("Error", lipgloss.NewStyle().Foreground(lipgloss.Color("1")).Render(err.Error())) + t.Row("Error", lipgloss.NewStyle().Renderer(r).Foreground(lipgloss.Color("1")).Render(err.Error())) } else if size != nil { t.Row("Size", size.String()) } - _, _ = io.WriteString(out, - lipgloss.JoinVertical(lipgloss.Center, - tui.HeaderStyle().PaddingTop(1).Render("Dump Summary"), - t.Render(), - )+"\n", + return lipgloss.JoinVertical(lipgloss.Center, + tui.HeaderStyle(r).Render("Dump Summary"), + t.Render(), ) } + +func (action Dump) printSummary(err error, took time.Duration, size *util.SizeWriter) { + out := os.Stdout + if action.Filename == "-" { + out = os.Stderr + } + _, _ = io.WriteString(out, "\n"+action.summary(err, took, size, false)+"\n") +} diff --git a/internal/actions/portforward/portforward.go b/internal/actions/portforward/portforward.go index 3413c8d7..1a14413b 100644 --- a/internal/actions/portforward/portforward.go +++ b/internal/actions/portforward/portforward.go @@ -79,12 +79,12 @@ func (a PortForward) printTable() { Uint16("remote", a.Port). Msg("port forward is ready") - info := tui.MinimalTable(). + info := tui.MinimalTable(nil). Row("Context", a.Context). Row("Namespace", a.Namespace). Row("Pod", a.DBPod.Name) - params := tui.MinimalTable(). + params := tui.MinimalTable(nil). Row("Type", a.Dialect.PrettyName()). Row("Namespace", a.Namespace). Row("Hostname", "localhost"). @@ -115,8 +115,8 @@ func (a PortForward) printTable() { } } - headerStyle := tui.HeaderStyle() - italicStyle := tui.BorderStyle().Italic(true) + headerStyle := tui.HeaderStyle(nil) + italicStyle := tui.BorderStyle(nil).Italic(true) data := lipgloss.JoinVertical(lipgloss.Left, lipgloss.JoinVertical(lipgloss.Center, headerStyle.Render("Database Instance"), @@ -129,7 +129,7 @@ func (a PortForward) printTable() { ), "", headerStyle.Render("Tip:")+ - tui.BorderStyle().Render(" If you're connecting from a Docker container, set the hostname to ")+ + tui.BorderStyle(nil).Render(" If you're connecting from a Docker container, set the hostname to ")+ italicStyle.Render("host.docker.internal"), ) diff --git a/internal/actions/restore/restore.go b/internal/actions/restore/restore.go index 6197d9dc..79942a59 100644 --- a/internal/actions/restore/restore.go +++ b/internal/actions/restore/restore.go @@ -227,7 +227,7 @@ func (action Restore) runInDatabasePod(ctx context.Context, r *io.PipeReader, st } func (action Restore) Table() *table.Table { - t := tui.MinimalTable(). + t := tui.MinimalTable(nil). Row("Context", action.Context). Row("Namespace", action.Namespace). Row("Pod", action.DBPod.Name) @@ -237,7 +237,7 @@ func (action Restore) Table() *table.Table { if action.Database != "" { t.Row("Database", action.Database) } - t.Row("File", tui.InPath(action.Filename)) + t.Row("File", tui.InPath(action.Filename, nil)) return t } @@ -263,7 +263,7 @@ func (action Restore) printSummary(err error, took time.Duration, size *util.Siz fmt.Println( //nolint:forbidigo lipgloss.JoinVertical(lipgloss.Center, - tui.HeaderStyle().PaddingTop(1).Render("Restore Summary"), + tui.HeaderStyle(nil).PaddingTop(1).Render("Restore Summary"), t.Render(), ), ) diff --git a/internal/notifier/healthchecks.go b/internal/notifier/healthchecks.go index b7c0c4df..1dc28260 100644 --- a/internal/notifier/healthchecks.go +++ b/internal/notifier/healthchecks.go @@ -11,21 +11,22 @@ import ( "time" ) +var _ Logs = &Healthchecks{} + func NewHealthchecks(url string) (Notifier, error) { if url == "" { return nil, fmt.Errorf("healthchecks %w", ErrEmptyURL) } - return &Healthchecks{ - url: url, - }, nil + return &Healthchecks{url: url}, nil } type Healthchecks struct { url string + log string } -func (h Healthchecks) SendStatus(status Status, log string) error { +func (h *Healthchecks) SendStatus(status Status, log string) error { var statusStr string switch status { case StatusStart: @@ -65,13 +66,22 @@ func (h Healthchecks) SendStatus(status Status, log string) error { return nil } -func (h Healthchecks) Started() error { +func (h *Healthchecks) Started() error { return h.SendStatus(StatusStart, "") } -func (h Healthchecks) Finished(err error) error { +func (h *Healthchecks) SetLog(log string) { + h.log = log +} + +func (h *Healthchecks) Finished(err error) error { if err == nil { - return h.SendStatus(StatusSuccess, "") + return h.SendStatus(StatusSuccess, h.log) + } + + msg := "Error: " + err.Error() + if h.log != "" { + msg += "\n\n" + h.log } - return h.SendStatus(StatusFailure, "Error: "+err.Error()) + return h.SendStatus(StatusFailure, msg) } diff --git a/internal/notifier/notifier.go b/internal/notifier/notifier.go index 4adb7ba1..c1eb00b5 100644 --- a/internal/notifier/notifier.go +++ b/internal/notifier/notifier.go @@ -25,6 +25,10 @@ type Notifier interface { Finished(err error) error } +type Logs interface { + SetLog(log string) +} + func New(handler, url string) (Notifier, error) { switch strings.ToLower(handler) { case "healthchecks": diff --git a/internal/tui/filepath.go b/internal/tui/filepath.go index 549ea3fe..8354c1e0 100644 --- a/internal/tui/filepath.go +++ b/internal/tui/filepath.go @@ -7,16 +7,16 @@ import ( "github.com/charmbracelet/lipgloss" ) -func InPath(path string) string { - style := lipgloss.NewStyle().Italic(true) +func InPath(path string, r *lipgloss.Renderer) string { + style := lipgloss.NewStyle().Renderer(r).Italic(true) if path == "-" { return "stdin" } return style.Render(CleanPath(path)) } -func OutPath(path string) string { - style := lipgloss.NewStyle().Italic(true) +func OutPath(path string, r *lipgloss.Renderer) string { + style := lipgloss.NewStyle().Renderer(r).Italic(true) if path == "-" { return "stdout" } diff --git a/internal/tui/styles.go b/internal/tui/styles.go index 590f1525..5e140693 100644 --- a/internal/tui/styles.go +++ b/internal/tui/styles.go @@ -2,6 +2,8 @@ package tui import "github.com/charmbracelet/lipgloss" -func HeaderStyle() lipgloss.Style { - return lipgloss.NewStyle().Bold(true).Foreground(lipgloss.AdaptiveColor{Light: "#5A56E0", Dark: "#7571F9"}) +func HeaderStyle(r *lipgloss.Renderer) lipgloss.Style { + return lipgloss.NewStyle().Renderer(r). + Bold(true). + Foreground(lipgloss.AdaptiveColor{Light: "#5A56E0", Dark: "#7571F9"}) } diff --git a/internal/tui/table.go b/internal/tui/table.go index a64fcef6..db07886a 100644 --- a/internal/tui/table.go +++ b/internal/tui/table.go @@ -5,16 +5,17 @@ import ( "github.com/charmbracelet/lipgloss/table" ) -func BorderStyle() lipgloss.Style { - return lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "", Dark: "243"}) +func BorderStyle(r *lipgloss.Renderer) lipgloss.Style { + return lipgloss.NewStyle().Renderer(r). + Foreground(lipgloss.AdaptiveColor{Light: "", Dark: "243"}) } -func MinimalTable() *table.Table { - colStyle := BorderStyle().Padding(0, 1) +func MinimalTable(r *lipgloss.Renderer) *table.Table { + colStyle := BorderStyle(r).Padding(0, 1) firstColStyle := colStyle.Copy().Align(lipgloss.Right).Bold(true) return table.New(). - BorderStyle(BorderStyle()). + BorderStyle(BorderStyle(r)). StyleFunc(func(_, col int) lipgloss.Style { if col == 0 { return firstColStyle