feat: recover from middleware panics

Ayman Bagabas created

* Disable bubbletea catching panics
* Bubble up bubbletea panics to soft-serve
* Add errorLog to Config

Change summary

config/config.go        | 11 +++++++++--
go.mod                  |  6 +++---
go.sum                  | 12 ++++++------
internal/tui/session.go |  5 ++++-
server/server.go        | 10 +++++++---
5 files changed, 29 insertions(+), 15 deletions(-)

Detailed changes

config/config.go 🔗

@@ -22,13 +22,14 @@ type Config struct {
 	RepoPath         string   `env:"SOFT_SERVE_REPO_PATH" envDefault:".repos"`
 	InitialAdminKeys []string `env:"SOFT_SERVE_INITIAL_ADMIN_KEY" envSeparator:"\n"`
 	Callbacks        Callbacks
+	ErrorLog         *log.Logger
 }
 
 // DefaultConfig returns a Config with the values populated with the defaults
 // or specified environment variables.
 func DefaultConfig() *Config {
-	var cfg Config
-	if err := env.Parse(&cfg); err != nil {
+	cfg := &Config{ErrorLog: log.Default()}
+	if err := env.Parse(cfg); err != nil {
 		log.Fatalln(err)
 	}
 	if cfg.KeyPath == "" {
@@ -43,3 +44,9 @@ func (c *Config) WithCallbacks(callbacks Callbacks) *Config {
 	c.Callbacks = callbacks
 	return c
 }
+
+// WithErrorLogger sets the error logger for the configuration.
+func (c *Config) WithErrorLogger(logger *log.Logger) *Config {
+	c.ErrorLog = logger
+	return c
+}

go.mod 🔗

@@ -5,10 +5,10 @@ go 1.17
 require (
 	github.com/caarlos0/env/v6 v6.9.1
 	github.com/charmbracelet/bubbles v0.10.0
-	github.com/charmbracelet/bubbletea v0.19.3
+	github.com/charmbracelet/bubbletea v0.19.4-0.20220208181305-42cd4c31919c
 	github.com/charmbracelet/glamour v0.4.0
 	github.com/charmbracelet/lipgloss v0.4.0
-	github.com/charmbracelet/wish v0.1.2
+	github.com/charmbracelet/wish v0.2.1-0.20220208182816-534842b53d2a
 	github.com/dustin/go-humanize v1.0.0
 	github.com/gliderlabs/ssh v0.3.3
 	github.com/go-git/go-billy/v5 v5.3.1
@@ -42,7 +42,7 @@ require (
 	github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a // indirect
 	github.com/mitchellh/go-homedir v1.1.0 // indirect
 	github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect
-	github.com/muesli/termenv v0.9.0 // indirect
+	github.com/muesli/termenv v0.11.0 // indirect
 	github.com/olekukonko/tablewriter v0.0.5 // indirect
 	github.com/rivo/uniseg v0.2.0 // indirect
 	github.com/sergi/go-diff v1.2.0 // indirect

go.sum 🔗

@@ -22,9 +22,9 @@ github.com/caarlos0/env/v6 v6.9.1 h1:zOkkjM0F6ltnQ5eBX6IPI41UP/KDGEK7rRPwGCNos8k
 github.com/caarlos0/env/v6 v6.9.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc=
 github.com/charmbracelet/bubbles v0.10.0 h1:ZYqBwnmFGp91HSRRbhxKq5jr6bUPsVUBdkrGGWtv0Wk=
 github.com/charmbracelet/bubbles v0.10.0/go.mod h1:4tiDrWzH1MTD4t5NnrcthaedmI3MxU0FIutax7//dvk=
-github.com/charmbracelet/bubbletea v0.19.0/go.mod h1:VuXF2pToRxDUHcBUcPmCRUHRvFATM4Ckb/ql1rBl3KA=
-github.com/charmbracelet/bubbletea v0.19.3 h1:OKeO/Y13rQQqt4snX+lePB0QrnW80UdrMNolnCcmoAw=
 github.com/charmbracelet/bubbletea v0.19.3/go.mod h1:VuXF2pToRxDUHcBUcPmCRUHRvFATM4Ckb/ql1rBl3KA=
+github.com/charmbracelet/bubbletea v0.19.4-0.20220208181305-42cd4c31919c h1:hcS4xdVQwblKo8xuA5gRO/jql+yCVfnBlOwWcZrxOmA=
+github.com/charmbracelet/bubbletea v0.19.4-0.20220208181305-42cd4c31919c/go.mod h1:5nPeULOIxbAMykb3ggwhw1kruS7nP+Y4Za9yEH4J27U=
 github.com/charmbracelet/glamour v0.4.0 h1:scR+smyB7WdmrlIaff6IVlm48P48JaNM7JypM/VGl4k=
 github.com/charmbracelet/glamour v0.4.0/go.mod h1:9ZRtG19AUIzcTm7FGLGbq3D5WKQ5UyZBbQsMQN0XIqc=
 github.com/charmbracelet/harmonica v0.1.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
@@ -33,8 +33,8 @@ github.com/charmbracelet/keygen v0.1.2/go.mod h1:kFQ3Cvop12fXWX1K29vxDxV9x8ujG4w
 github.com/charmbracelet/lipgloss v0.3.0/go.mod h1:VkhdBS2eNAmRkTwRKLJCFhCOVkjntMusBDxv7TXahuk=
 github.com/charmbracelet/lipgloss v0.4.0 h1:768h64EFkGUr8V5yAKV7/Ta0NiVceiPaV+PphaW1K9g=
 github.com/charmbracelet/lipgloss v0.4.0/go.mod h1:vmdkHvce7UzX6xkyf4cca8WlwdQ5RQr8fzta+xl7BOM=
-github.com/charmbracelet/wish v0.1.2 h1:YoKQYLsC3M6rIIP0PS/tM+5nVQb5ij+6SvGKKedjV6s=
-github.com/charmbracelet/wish v0.1.2/go.mod h1:tD+sb5aS1SSX0t7hIZXXUonv2YbnFNCnU6qfOolKKUE=
+github.com/charmbracelet/wish v0.2.1-0.20220208182816-534842b53d2a h1:dDdOcIedpXZ13xGfwFDd1ZlTUXotX945xXtz+7rHBK8=
+github.com/charmbracelet/wish v0.2.1-0.20220208182816-534842b53d2a/go.mod h1:8O/9uZMnzct6WZP3MgFMlcfwWfd2jfTPIhenfF7KqeE=
 github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
 github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
 github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
@@ -88,7 +88,6 @@ github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
 github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-isatty v0.0.14-0.20210829144114-504425e14f74/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@@ -109,8 +108,9 @@ github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIW
 github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
 github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
 github.com/muesli/termenv v0.8.1/go.mod h1:kzt/D/4a88RoheZmwfqorY3A+tnsSMA9HJC/fQSFKo0=
-github.com/muesli/termenv v0.9.0 h1:wnbOaGz+LUR3jNT0zOzinPnyDaCZUQRZj9GxK8eRVl8=
 github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw=
+github.com/muesli/termenv v0.11.0 h1:fwNUbu2mfWlgicwG7qYzs06aOI8Z/zKPAv8J4uKbT+o=
+github.com/muesli/termenv v0.11.0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
 github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=

internal/tui/session.go 🔗

@@ -30,6 +30,9 @@ func SessionHandler(cfg *config.Config) func(ssh.Session) (tea.Model, []tea.Prog
 		if cfg.Cfg.Callbacks != nil {
 			cfg.Cfg.Callbacks.Tui("view")
 		}
-		return NewBubble(cfg, scfg), []tea.ProgramOption{tea.WithAltScreen()}
+		return NewBubble(cfg, scfg), []tea.ProgramOption{
+			tea.WithAltScreen(),
+			tea.WithoutCatchPanics(),
+		}
 	}
 }

server/server.go 🔗

@@ -13,6 +13,7 @@ import (
 	bm "github.com/charmbracelet/wish/bubbletea"
 	gm "github.com/charmbracelet/wish/git"
 	lm "github.com/charmbracelet/wish/logging"
+	rm "github.com/charmbracelet/wish/recover"
 	"github.com/gliderlabs/ssh"
 )
 
@@ -34,9 +35,12 @@ func NewServer(cfg *config.Config) *Server {
 		log.Fatal(err)
 	}
 	mw := []wish.Middleware{
-		bm.Middleware(tui.SessionHandler(ac)),
-		gm.Middleware(cfg.RepoPath, ac),
-		lm.Middleware(),
+		rm.MiddlewareWithLogger(
+			cfg.ErrorLog,
+			bm.Middleware(tui.SessionHandler(ac)),
+			gm.Middleware(cfg.RepoPath, ac),
+			lm.Middleware(),
+		),
 	}
 	s, err := wish.NewServer(
 		ssh.PublicKeyAuth(ac.PublicKeyHandler),