Detailed changes
@@ -11,6 +11,7 @@ import (
"path/filepath"
"strings"
+ "github.com/charmbracelet/log"
"github.com/charmbracelet/soft-serve/server/backend"
"github.com/charmbracelet/soft-serve/server/backend/sqlite"
"github.com/charmbracelet/soft-serve/server/config"
@@ -18,53 +19,57 @@ import (
"github.com/spf13/cobra"
)
-var (
- confixCtxKey = "config"
- backendCtxKey = "backend"
-)
-
var (
configPath string
+ logFileCtxKey = struct{}{}
+
hookCmd = &cobra.Command{
Use: "hook",
Short: "Run git server hooks",
Long: "Handles Soft Serve git server hooks.",
Hidden: true,
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
+ ctx := cmd.Context()
cfg, err := config.ParseConfig(configPath)
if err != nil {
return fmt.Errorf("could not parse config: %w", err)
}
- customHooksPath := filepath.Join(filepath.Dir(configPath), "hooks")
- if _, err := os.Stat(customHooksPath); err != nil && os.IsNotExist(err) {
- os.MkdirAll(customHooksPath, os.ModePerm)
- // Generate update hook example without executable permissions
- hookPath := filepath.Join(customHooksPath, "update.sample")
- if err := os.WriteFile(hookPath, []byte(updateHookExample), 0744); err != nil {
- return fmt.Errorf("failed to generate update hook example: %w", err)
- }
+ ctx = config.WithContext(ctx, cfg)
+
+ logPath := filepath.Join(cfg.DataPath, "log", "hooks.log")
+ f, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
+ if err != nil {
+ return fmt.Errorf("opening file: %w", err)
}
+ ctx = context.WithValue(ctx, logFileCtxKey, f)
+ logger := log.FromContext(ctx)
+ logger.SetOutput(f)
+ ctx = log.WithContext(ctx, logger)
+ cmd.SetContext(ctx)
+
// Set up the backend
// TODO: support other backends
- sb, err := sqlite.NewSqliteBackend(cmd.Context(), cfg)
+ sb, err := sqlite.NewSqliteBackend(ctx)
if err != nil {
return fmt.Errorf("failed to create sqlite backend: %w", err)
}
cfg = cfg.WithBackend(sb)
- cmd.SetContext(context.WithValue(cmd.Context(), confixCtxKey, cfg))
- cmd.SetContext(context.WithValue(cmd.Context(), backendCtxKey, sb))
-
return nil
},
+ PersistentPostRunE: func(cmd *cobra.Command, _ []string) error {
+ f := cmd.Context().Value(logFileCtxKey).(*os.File)
+ return f.Close()
+ },
}
hooksRunE = func(cmd *cobra.Command, args []string) error {
- cfg := cmd.Context().Value(confixCtxKey).(*config.Config)
+ ctx := cmd.Context()
+ cfg := config.FromContext(ctx)
hks := cfg.Backend.(backend.Hooks)
// This is set in the server before invoking git-receive-pack/git-upload-pack
@@ -119,7 +124,7 @@ var (
// Custom hooks
if stat, err := os.Stat(customHookPath); err == nil && !stat.IsDir() && stat.Mode()&0o111 != 0 {
// If the custom hook is executable, run it
- if err := runCommand(cmd.Context(), &buf, stdout, stderr, customHookPath, args...); err != nil {
+ if err := runCommand(ctx, &buf, stdout, stderr, customHookPath, args...); err != nil {
return fmt.Errorf("failed to run custom hook: %w", err)
}
}
@@ -2,6 +2,7 @@ package main
import (
"encoding/json"
+ "errors"
"fmt"
"io"
"os"
@@ -27,15 +28,18 @@ var (
Short: "Migrate config to new format",
Hidden: true,
RunE: func(cmd *cobra.Command, _ []string) error {
+ ctx := cmd.Context()
+
+ logger := log.FromContext(ctx)
// Disable logging timestamp
- log.SetReportTimestamp(false)
+ logger.SetReportTimestamp(false)
keyPath := os.Getenv("SOFT_SERVE_KEY_PATH")
reposPath := os.Getenv("SOFT_SERVE_REPO_PATH")
bindAddr := os.Getenv("SOFT_SERVE_BIND_ADDRESS")
- ctx := cmd.Context()
cfg := config.DefaultConfig()
- sb, err := sqlite.NewSqliteBackend(ctx, cfg)
+ ctx = config.WithContext(ctx, cfg)
+ sb, err := sqlite.NewSqliteBackend(ctx)
if err != nil {
return fmt.Errorf("failed to create sqlite backend: %w", err)
}
@@ -43,13 +47,13 @@ var (
cfg = cfg.WithBackend(sb)
// Set SSH listen address
- log.Info("Setting SSH listen address...")
+ logger.Info("Setting SSH listen address...")
if bindAddr != "" {
cfg.SSH.ListenAddr = bindAddr
}
// Copy SSH host key
- log.Info("Copying SSH host key...")
+ logger.Info("Copying SSH host key...")
if keyPath != "" {
if err := os.MkdirAll(filepath.Join(cfg.DataPath, "ssh"), os.ModePerm); err != nil {
return fmt.Errorf("failed to create ssh directory: %w", err)
@@ -60,14 +64,14 @@ var (
}
if err := copyFile(keyPath+".pub", filepath.Join(cfg.DataPath, "ssh", filepath.Base(keyPath))+".pub"); err != nil {
- log.Errorf("failed to copy ssh key: %s", err)
+ logger.Errorf("failed to copy ssh key: %s", err)
}
cfg.SSH.KeyPath = filepath.Join(cfg.DataPath, "ssh", filepath.Base(keyPath))
}
// Read config
- log.Info("Reading config repository...")
+ logger.Info("Reading config repository...")
r, err := git.Open(filepath.Join(reposPath, "config"))
if err != nil {
return fmt.Errorf("failed to open config repo: %w", err)
@@ -119,7 +123,7 @@ var (
cfg.SSH.PublicURL = fmt.Sprintf("ssh://%s:%d", ocfg.Host, ocfg.Port)
// Set server settings
- log.Info("Setting server settings...")
+ logger.Info("Setting server settings...")
if cfg.Backend.SetAllowKeyless(ocfg.AllowKeyless) != nil {
fmt.Fprintf(os.Stderr, "failed to set allow keyless\n")
}
@@ -132,7 +136,7 @@ var (
// Copy repos
if reposPath != "" {
- log.Info("Copying repos...")
+ logger.Info("Copying repos...")
if err := os.MkdirAll(filepath.Join(cfg.DataPath, "repos"), os.ModePerm); err != nil {
return fmt.Errorf("failed to create repos directory: %w", err)
}
@@ -151,7 +155,7 @@ var (
continue
}
- log.Infof(" Copying repo %s", dir.Name())
+ logger.Infof(" Copying repo %s", dir.Name())
src := filepath.Join(reposPath, utils.SanitizeRepo(dir.Name()))
dst := filepath.Join(cfg.DataPath, "repos", utils.SanitizeRepo(dir.Name())) + ".git"
if err := os.MkdirAll(dst, os.ModePerm); err != nil {
@@ -168,7 +172,7 @@ var (
}
if hasReadme {
- log.Infof(" Copying readme from \"config\" to \".soft-serve\"")
+ logger.Infof(" Copying readme from \"config\" to \".soft-serve\"")
// Switch to main branch
bcmd := git.NewCommand("branch", "-M", "main")
@@ -236,7 +240,7 @@ var (
}
// Set repos metadata & collabs
- log.Info("Setting repos metadata & collabs...")
+ logger.Info("Setting repos metadata & collabs...")
for _, r := range ocfg.Repos {
repo, name := r.Repo, r.Name
// Special case for config repo
@@ -246,26 +250,26 @@ var (
}
if err := sb.SetProjectName(repo, name); err != nil {
- log.Errorf("failed to set repo name to %s: %s", repo, err)
+ logger.Errorf("failed to set repo name to %s: %s", repo, err)
}
if err := sb.SetDescription(repo, r.Note); err != nil {
- log.Errorf("failed to set repo description to %s: %s", repo, err)
+ logger.Errorf("failed to set repo description to %s: %s", repo, err)
}
if err := sb.SetPrivate(repo, r.Private); err != nil {
- log.Errorf("failed to set repo private to %s: %s", repo, err)
+ logger.Errorf("failed to set repo private to %s: %s", repo, err)
}
for _, collab := range r.Collabs {
if err := sb.AddCollaborator(repo, collab); err != nil {
- log.Errorf("failed to add repo collab to %s: %s", repo, err)
+ logger.Errorf("failed to add repo collab to %s: %s", repo, err)
}
}
}
// Create users & collabs
- log.Info("Creating users & collabs...")
+ logger.Info("Creating users & collabs...")
for _, user := range ocfg.Users {
keys := make(map[string]ssh.PublicKey)
for _, key := range user.PublicKeys {
@@ -284,23 +288,23 @@ var (
username := strings.ToLower(user.Name)
username = strings.ReplaceAll(username, " ", "-")
- log.Infof("Creating user %q", username)
+ logger.Infof("Creating user %q", username)
if _, err := sb.CreateUser(username, backend.UserOptions{
Admin: user.Admin,
PublicKeys: pubkeys,
}); err != nil {
- log.Errorf("failed to create user: %s", err)
+ logger.Errorf("failed to create user: %s", err)
}
for _, repo := range user.CollabRepos {
if err := sb.AddCollaborator(repo, username); err != nil {
- log.Errorf("failed to add user collab to %s: %s\n", repo, err)
+ logger.Errorf("failed to add user collab to %s: %s\n", repo, err)
}
}
}
- log.Info("Writing config...")
- defer log.Info("Done!")
+ logger.Info("Writing config...")
+ defer logger.Info("Done!")
return config.WriteConfig(filepath.Join(cfg.DataPath, "config.yaml"), cfg)
},
}
@@ -371,21 +375,23 @@ func copyDir(src string, dst string) error {
if fds, err = os.ReadDir(src); err != nil {
return err
}
+
for _, fd := range fds {
srcfp := filepath.Join(src, fd.Name())
dstfp := filepath.Join(dst, fd.Name())
if fd.IsDir() {
if err = copyDir(srcfp, dstfp); err != nil {
- log.Error("failed to copy directory", "err", err)
+ err = errors.Join(err, err)
}
} else {
if err = copyFile(srcfp, dstfp); err != nil {
- log.Error("failed to copy file", "err", err)
+ err = errors.Join(err, err)
}
}
}
- return nil
+
+ return err
}
// Config is the configuration for the server.
@@ -4,9 +4,11 @@ import (
"context"
"os"
"runtime/debug"
+ "strconv"
+ "strings"
+ "time"
"github.com/charmbracelet/log"
- _ "github.com/charmbracelet/soft-serve/log"
"github.com/spf13/cobra"
)
@@ -51,15 +53,24 @@ func init() {
}
func main() {
+ ctx := context.Background()
logger := log.NewWithOptions(os.Stderr, log.Options{
ReportTimestamp: true,
- TimeFormat: "2006-01-02",
+ TimeFormat: time.DateOnly,
})
- if os.Getenv("SOFT_SERVE_DEBUG") == "true" {
+ if debug, _ := strconv.ParseBool(os.Getenv("SOFT_SERVE_DEBUG")); debug {
logger.SetLevel(log.DebugLevel)
}
- ctx := context.Background()
+ switch strings.ToLower(os.Getenv("SOFT_SERVE_LOG_FORMAT")) {
+ case "json":
+ logger.SetFormatter(log.JSONFormatter)
+ case "logfmt":
+ logger.SetFormatter(log.LogfmtFormatter)
+ case "text":
+ logger.SetFormatter(log.TextFormatter)
+ }
+
ctx = log.WithContext(ctx, logger)
if err := rootCmd.ExecuteContext(ctx); err != nil {
os.Exit(1)
@@ -5,10 +5,10 @@ import (
"fmt"
"os"
"os/signal"
+ "path/filepath"
"syscall"
"time"
- _ "github.com/charmbracelet/soft-serve/log"
"github.com/charmbracelet/soft-serve/server"
"github.com/charmbracelet/soft-serve/server/config"
"github.com/spf13/cobra"
@@ -20,10 +20,31 @@ var (
Short: "Start the server",
Long: "Start the server",
Args: cobra.NoArgs,
- RunE: func(cmd *cobra.Command, args []string) error {
+ RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
cfg := config.DefaultConfig()
- s, err := server.NewServer(ctx, cfg)
+ ctx = config.WithContext(ctx, cfg)
+ cmd.SetContext(ctx)
+
+ // Create custom hooks directory if it doesn't exist
+ customHooksPath := filepath.Join(cfg.DataPath, "hooks")
+ if _, err := os.Stat(customHooksPath); err != nil && os.IsNotExist(err) {
+ os.MkdirAll(customHooksPath, os.ModePerm) // nolint: errcheck
+ // Generate update hook example without executable permissions
+ hookPath := filepath.Join(customHooksPath, "update.sample")
+ // nolint: gosec
+ if err := os.WriteFile(hookPath, []byte(updateHookExample), 0744); err != nil {
+ return fmt.Errorf("failed to generate update hook example: %w", err)
+ }
+ }
+
+ // Create log directory if it doesn't exist
+ logPath := filepath.Join(cfg.DataPath, "log")
+ if _, err := os.Stat(logPath); err != nil && os.IsNotExist(err) {
+ os.MkdirAll(logPath, os.ModePerm) // nolint: errcheck
+ }
+
+ s, err := server.NewServer(ctx)
if err != nil {
return fmt.Errorf("start server: %w", err)
}
@@ -46,8 +46,9 @@ func main() {
}
ctx := context.Background()
cfg := config.DefaultConfig()
+ ctx = config.WithContext(ctx, cfg)
cfg.SSH.ListenAddr = fmt.Sprintf(":%d", *port)
- s, err := server.NewServer(ctx, cfg)
+ s, err := server.NewServer(ctx)
if err != nil {
log.Fatal(err)
}
@@ -1,14 +0,0 @@
-// Package log initializes the logger for Soft Serve modules.
-package log
-
-import (
- "os"
-
- "github.com/charmbracelet/log"
-)
-
-func init() {
- if os.Getenv("SOFT_SERVE_DEBUG") == "true" {
- log.SetLevel(log.DebugLevel)
- }
-}
@@ -66,7 +66,7 @@ func (d *SqliteBackend) init() error {
for _, k := range d.cfg.InitialAdminKeys {
pk, _, err := backend.ParseAuthorizedKey(k)
if err != nil {
- logger.Error("error parsing initial admin key, skipping", "key", k, "err", err)
+ d.logger.Error("error parsing initial admin key, skipping", "key", k, "err", err)
continue
}
@@ -4,7 +4,6 @@ import (
"io"
"sync"
- "github.com/charmbracelet/log"
"github.com/charmbracelet/soft-serve/server/backend"
)
@@ -12,28 +11,28 @@ import (
//
// It implements Hooks.
func (d *SqliteBackend) PostReceive(stdout io.Writer, stderr io.Writer, repo string, args []backend.HookArg) {
- log.WithPrefix("backend.sqlite.hooks").Debug("post-receive hook called", "repo", repo, "args", args)
+ d.logger.Debug("post-receive hook called", "repo", repo, "args", args)
}
// PreReceive is called by the git pre-receive hook.
//
// It implements Hooks.
func (d *SqliteBackend) PreReceive(stdout io.Writer, stderr io.Writer, repo string, args []backend.HookArg) {
- log.WithPrefix("backend.sqlite.hooks").Debug("pre-receive hook called", "repo", repo, "args", args)
+ d.logger.Debug("pre-receive hook called", "repo", repo, "args", args)
}
// Update is called by the git update hook.
//
// It implements Hooks.
func (d *SqliteBackend) Update(stdout io.Writer, stderr io.Writer, repo string, arg backend.HookArg) {
- log.WithPrefix("backend.sqlite.hooks").Debug("update hook called", "repo", repo, "arg", arg)
+ d.logger.Debug("update hook called", "repo", repo, "arg", arg)
}
// PostUpdate is called by the git post-update hook.
//
// It implements Hooks.
func (d *SqliteBackend) PostUpdate(stdout io.Writer, stderr io.Writer, repo string, args ...string) {
- log.WithPrefix("backend.sqlite.hooks").Debug("post-update hook called", "repo", repo, "args", args)
+ d.logger.Debug("post-update hook called", "repo", repo, "args", args)
var wg sync.WaitGroup
@@ -44,18 +43,18 @@ func (d *SqliteBackend) PostUpdate(stdout io.Writer, stderr io.Writer, repo stri
rr, err := d.Repository(repo)
if err != nil {
- log.WithPrefix("backend.sqlite.hooks").Error("error getting repository", "repo", repo, "err", err)
+ d.logger.Error("error getting repository", "repo", repo, "err", err)
return
}
r, err := rr.Open()
if err != nil {
- log.WithPrefix("backend.sqlite.hooks").Error("error opening repository", "repo", repo, "err", err)
+ d.logger.Error("error opening repository", "repo", repo, "err", err)
return
}
if err := r.UpdateServerInfo(); err != nil {
- log.WithPrefix("backend.sqlite.hooks").Error("error updating server-info", "repo", repo, "err", err)
+ d.logger.Error("error updating server-info", "repo", repo, "err", err)
return
}
}()
@@ -17,17 +17,14 @@ import (
_ "modernc.org/sqlite"
)
-var (
- logger = log.WithPrefix("backend.sqlite")
-)
-
// SqliteBackend is a backend that uses a SQLite database as a Soft Serve
// backend.
type SqliteBackend struct {
- cfg *config.Config
- ctx context.Context
- dp string
- db *sqlx.DB
+ cfg *config.Config
+ ctx context.Context
+ dp string
+ db *sqlx.DB
+ logger *log.Logger
}
var _ backend.Backend = (*SqliteBackend)(nil)
@@ -37,7 +34,8 @@ func (d *SqliteBackend) reposPath() string {
}
// NewSqliteBackend creates a new SqliteBackend.
-func NewSqliteBackend(ctx context.Context, cfg *config.Config) (*SqliteBackend, error) {
+func NewSqliteBackend(ctx context.Context) (*SqliteBackend, error) {
+ cfg := config.FromContext(ctx)
dataPath := cfg.DataPath
if err := os.MkdirAll(dataPath, os.ModePerm); err != nil {
return nil, err
@@ -50,10 +48,11 @@ func NewSqliteBackend(ctx context.Context, cfg *config.Config) (*SqliteBackend,
}
d := &SqliteBackend{
- cfg: cfg,
- ctx: ctx,
- dp: dataPath,
- db: db,
+ cfg: cfg,
+ ctx: ctx,
+ dp: dataPath,
+ db: db,
+ logger: log.FromContext(ctx).WithPrefix("sqlite"),
}
if err := d.init(); err != nil {
@@ -137,13 +136,13 @@ func (d *SqliteBackend) CreateRepository(name string, opts backend.RepositoryOpt
rr, err := git.Init(rp, true)
if err != nil {
- logger.Debug("failed to create repository", "err", err)
+ d.logger.Debug("failed to create repository", "err", err)
cleanup() // nolint: errcheck
return nil, err
}
if err := rr.UpdateServerInfo(); err != nil {
- logger.Debug("failed to update server info", "err", err)
+ d.logger.Debug("failed to update server info", "err", err)
cleanup() // nolint: errcheck
return nil, err
}
@@ -154,7 +153,7 @@ func (d *SqliteBackend) CreateRepository(name string, opts backend.RepositoryOpt
name, opts.ProjectName, opts.Description, opts.Private, opts.Mirror, opts.Hidden)
return err
}); err != nil {
- logger.Debug("failed to create repository in database", "err", err)
+ d.logger.Debug("failed to create repository in database", "err", err)
return nil, wrapDbErr(err)
}
@@ -192,7 +191,7 @@ func (d *SqliteBackend) ImportRepository(name string, remote string, opts backen
}
if err := git.Clone(remote, rp, copts); err != nil {
- logger.Error("failed to clone repository", "err", err, "mirror", opts.Mirror, "remote", remote, "path", rp)
+ d.logger.Error("failed to clone repository", "err", err, "mirror", opts.Mirror, "remote", remote, "path", rp)
return nil, err
}
@@ -311,7 +310,7 @@ func (d *SqliteBackend) Repository(repo string) (backend.Repository, error) {
}
if count == 0 {
- logger.Warn("repository exists but not found in database", "repo", repo)
+ d.logger.Warn("repository exists but not found in database", "repo", repo)
return nil, ErrRepoNotExist
}
@@ -180,7 +180,7 @@ func (d *SqliteBackend) CreateUser(username string, opts backend.UserOptions) (b
if len(opts.PublicKeys) > 0 {
userID, err := r.LastInsertId()
if err != nil {
- logger.Error("error getting last insert id")
+ d.logger.Error("error getting last insert id")
return err
}
@@ -1,6 +1,7 @@
package config
import (
+ "context"
"errors"
"fmt"
"os"
@@ -88,6 +89,10 @@ type Config struct {
// Stats is the configuration for the stats server.
Stats StatsConfig `envPrefix:"STATS_" yaml:"stats"`
+ // LogFormat is the format of the logs.
+ // Valid values are "json", "logfmt", and "text".
+ LogFormat string `env:"LOG_FORMAT" yaml:"log_format"`
+
// InitialAdminKeys is a list of public keys that will be added to the list of admins.
InitialAdminKeys []string `env:"INITIAL_ADMIN_KEYS" envSeparator:"\n" yaml:"initial_admin_keys"`
@@ -101,8 +106,9 @@ type Config struct {
func parseConfig(path string) (*Config, error) {
dataPath := filepath.Dir(path)
cfg := &Config{
- Name: "Soft Serve",
- DataPath: dataPath,
+ Name: "Soft Serve",
+ LogFormat: "text",
+ DataPath: dataPath,
SSH: SSHConfig{
ListenAddr: ":23231",
PublicURL: "ssh://localhost:23231",
@@ -273,3 +279,21 @@ func parseAuthKeys(aks []string) []ssh.PublicKey {
func (c *Config) AdminKeys() []ssh.PublicKey {
return parseAuthKeys(c.InitialAdminKeys)
}
+
+var (
+ configCtxKey = struct{ string }{"config"}
+)
+
+// WithContext returns a new context with the configuration attached.
+func WithContext(ctx context.Context, cfg *Config) context.Context {
+ return context.WithValue(ctx, configCtxKey, cfg)
+}
+
+// FromContext returns the configuration from the context.
+func FromContext(ctx context.Context) *Config {
+ if c, ok := ctx.Value(configCtxKey).(*Config); ok {
+ return c
+ }
+
+ return DefaultConfig()
+}
@@ -12,6 +12,9 @@ var (
# This is the name that will be displayed in the UI.
name: "{{ .Name }}"
+# Log format to use. Valid values are "json", "logfmt", and "text".
+log_format: "{{ .LogFormat }}"
+
# The SSH server configuration.
ssh:
# The address on which the SSH server will listen.
@@ -39,7 +39,7 @@ func (l cronLogger) Error(err error, msg string, keysAndValues ...interface{}) {
// NewCronScheduler returns a new Cron.
func NewCronScheduler(ctx context.Context) *CronScheduler {
- logger := cronLogger{log.FromContext(ctx).WithPrefix("server.cron")}
+ logger := cronLogger{log.FromContext(ctx).WithPrefix("cron")}
return &CronScheduler{
Cron: cron.New(cron.WithLogger(logger)),
}
@@ -19,10 +19,6 @@ import (
"github.com/prometheus/client_golang/prometheus/promauto"
)
-var (
- logger = log.WithPrefix("server.daemon")
-)
-
var (
uploadPackGitCounter = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "soft_serve",
@@ -81,6 +77,7 @@ func (m *connections) CloseAll() {
// GitDaemon represents a Git daemon.
type GitDaemon struct {
+ ctx context.Context
listener net.Listener
addr string
finished chan struct{}
@@ -88,16 +85,20 @@ type GitDaemon struct {
cfg *config.Config
wg sync.WaitGroup
once sync.Once
+ logger *log.Logger
}
// NewDaemon returns a new Git daemon.
-func NewGitDaemon(cfg *config.Config) (*GitDaemon, error) {
+func NewGitDaemon(ctx context.Context) (*GitDaemon, error) {
+ cfg := config.FromContext(ctx)
addr := cfg.Git.ListenAddr
d := &GitDaemon{
+ ctx: ctx,
addr: addr,
finished: make(chan struct{}, 1),
cfg: cfg,
conns: connections{m: make(map[net.Conn]struct{})},
+ logger: log.FromContext(ctx).WithPrefix("gitdaemon"),
}
listener, err := net.Listen("tcp", d.addr)
if err != nil {
@@ -122,7 +123,7 @@ func (d *GitDaemon) Start() error {
case <-d.finished:
return ErrServerClosed
default:
- logger.Debugf("git: error accepting connection: %v", err)
+ d.logger.Debugf("git: error accepting connection: %v", err)
}
if ne, ok := err.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
@@ -141,8 +142,8 @@ func (d *GitDaemon) Start() error {
// Close connection if there are too many open connections.
if d.conns.Size()+1 >= d.cfg.Git.MaxConnections {
- logger.Debugf("git: max connections reached, closing %s", conn.RemoteAddr())
- fatal(conn, git.ErrMaxConnections)
+ d.logger.Debugf("git: max connections reached, closing %s", conn.RemoteAddr())
+ d.fatal(conn, git.ErrMaxConnections)
continue
}
@@ -154,10 +155,10 @@ func (d *GitDaemon) Start() error {
}
}
-func fatal(c net.Conn, err error) {
+func (d *GitDaemon) fatal(c net.Conn, err error) {
git.WritePktline(c, err)
if err := c.Close(); err != nil {
- logger.Debugf("git: error closing connection: %v", err)
+ d.logger.Debugf("git: error closing connection: %v", err)
}
}
@@ -185,10 +186,10 @@ func (d *GitDaemon) handleClient(conn net.Conn) {
if !s.Scan() {
if err := s.Err(); err != nil {
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
- fatal(c, git.ErrTimeout)
+ d.fatal(c, git.ErrTimeout)
} else {
- logger.Debugf("git: error scanning pktline: %v", err)
- fatal(c, git.ErrSystemMalfunction)
+ d.logger.Debugf("git: error scanning pktline: %v", err)
+ d.fatal(c, git.ErrSystemMalfunction)
}
}
return
@@ -199,14 +200,14 @@ func (d *GitDaemon) handleClient(conn net.Conn) {
select {
case <-ctx.Done():
if err := ctx.Err(); err != nil {
- logger.Debugf("git: connection context error: %v", err)
+ d.logger.Debugf("git: connection context error: %v", err)
}
return
case <-readc:
line := s.Bytes()
split := bytes.SplitN(line, []byte{' '}, 2)
if len(split) != 2 {
- fatal(c, git.ErrInvalidRequest)
+ d.fatal(c, git.ErrInvalidRequest)
return
}
@@ -220,36 +221,36 @@ func (d *GitDaemon) handleClient(conn net.Conn) {
gitPack = git.UploadArchive
counter = uploadArchiveGitCounter
default:
- fatal(c, git.ErrInvalidRequest)
+ d.fatal(c, git.ErrInvalidRequest)
return
}
opts := bytes.Split(split[1], []byte{'\x00'})
if len(opts) == 0 {
- fatal(c, git.ErrInvalidRequest)
+ d.fatal(c, git.ErrInvalidRequest)
return
}
if !d.cfg.Backend.AllowKeyless() {
- fatal(c, git.ErrNotAuthed)
+ d.fatal(c, git.ErrNotAuthed)
return
}
name := utils.SanitizeRepo(string(opts[0]))
- logger.Debugf("git: connect %s %s %s", c.RemoteAddr(), cmd, name)
- defer logger.Debugf("git: disconnect %s %s %s", c.RemoteAddr(), cmd, name)
+ d.logger.Debugf("git: connect %s %s %s", c.RemoteAddr(), cmd, name)
+ defer d.logger.Debugf("git: disconnect %s %s %s", c.RemoteAddr(), cmd, name)
// git bare repositories should end in ".git"
// https://git-scm.com/docs/gitrepository-layout
repo := name + ".git"
reposDir := filepath.Join(d.cfg.DataPath, "repos")
if err := git.EnsureWithin(reposDir, repo); err != nil {
- fatal(c, err)
+ d.fatal(c, err)
return
}
auth := d.cfg.Backend.AccessLevel(name, "")
if auth < backend.ReadOnlyAccess {
- fatal(c, git.ErrNotAuthed)
+ d.fatal(c, git.ErrNotAuthed)
return
}
@@ -260,7 +261,7 @@ func (d *GitDaemon) handleClient(conn net.Conn) {
}
if err := gitPack(ctx, c, c, c, filepath.Join(reposDir, repo), envs...); err != nil {
- fatal(c, err)
+ d.fatal(c, err)
return
}
@@ -32,13 +32,14 @@ func TestMain(m *testing.M) {
os.Setenv("SOFT_SERVE_GIT_MAX_TIMEOUT", "100")
os.Setenv("SOFT_SERVE_GIT_IDLE_TIMEOUT", "1")
os.Setenv("SOFT_SERVE_GIT_LISTEN_ADDR", fmt.Sprintf(":%d", test.RandomPort()))
+ ctx := context.TODO()
cfg := config.DefaultConfig()
- d, err := NewGitDaemon(cfg)
+ ctx = config.WithContext(ctx, cfg)
+ d, err := NewGitDaemon(ctx)
if err != nil {
log.Fatal(err)
}
- ctx := context.TODO()
- fb, err := sqlite.NewSqliteBackend(ctx, cfg)
+ fb, err := sqlite.NewSqliteBackend(ctx)
if err != nil {
log.Fatal(err)
}
@@ -12,6 +12,7 @@ import (
"github.com/charmbracelet/log"
"github.com/charmbracelet/soft-serve/git"
+ "github.com/charmbracelet/soft-serve/server/config"
"github.com/go-git/go-git/v5/plumbing/format/pktline"
"golang.org/x/sync/errgroup"
)
@@ -78,12 +79,16 @@ func ReceivePack(ctx context.Context, in io.Reader, out io.Writer, er io.Writer,
// RunGit runs a git command in the given repo.
func RunGit(ctx context.Context, in io.Reader, out io.Writer, er io.Writer, dir string, envs []string, args ...string) error {
- logger := log.WithPrefix("server.git")
+ cfg := config.FromContext(ctx)
+ logger := log.FromContext(ctx).WithPrefix("rungit")
c := exec.CommandContext(ctx, "git", args...)
c.Dir = dir
c.Env = append(c.Env, envs...)
- c.Env = append(c.Env, "SOFT_SERVE_DEBUG="+os.Getenv("SOFT_SERVE_DEBUG"))
c.Env = append(c.Env, "PATH="+os.Getenv("PATH"))
+ c.Env = append(c.Env, "SOFT_SERVE_DEBUG="+os.Getenv("SOFT_SERVE_DEBUG"))
+ if cfg != nil {
+ c.Env = append(c.Env, "SOFT_SERVE_LOG_FORMAT="+cfg.LogFormat)
+ }
stdin, err := c.StdinPipe()
if err != nil {
@@ -5,7 +5,6 @@ import (
"path/filepath"
"github.com/charmbracelet/soft-serve/git"
- "github.com/charmbracelet/soft-serve/server/config"
)
var (
@@ -15,9 +14,10 @@ var (
)
// mirrorJob runs the (pull) mirror job task.
-func mirrorJob(cfg *config.Config) func() {
+func (s *Server) mirrorJob() func() {
+ cfg := s.Config
b := cfg.Backend
- logger := logger.WithPrefix("server.mirrorJob")
+ logger := s.logger
return func() {
repos, err := b.Repositories()
if err != nil {
@@ -20,10 +20,6 @@ import (
"golang.org/x/sync/errgroup"
)
-var (
- logger = log.WithPrefix("server")
-)
-
// Server is the Soft Serve server.
type Server struct {
SSHServer *sshsrv.SSHServer
@@ -33,7 +29,9 @@ type Server struct {
Cron *cron.CronScheduler
Config *config.Config
Backend backend.Backend
- ctx context.Context
+
+ logger *log.Logger
+ ctx context.Context
}
// NewServer returns a new *ssh.Server configured to serve Soft Serve. The SSH
@@ -41,10 +39,12 @@ type Server struct {
// key can be provided with authKey. If authKey is provided, access will be
// restricted to that key. If authKey is not provided, the server will be
// publicly writable until configured otherwise by cloning the `config` repo.
-func NewServer(ctx context.Context, cfg *config.Config) (*Server, error) {
+func NewServer(ctx context.Context) (*Server, error) {
+ cfg := config.FromContext(ctx)
+
var err error
if cfg.Backend == nil {
- sb, err := sqlite.NewSqliteBackend(ctx, cfg)
+ sb, err := sqlite.NewSqliteBackend(ctx)
if err != nil {
return nil, fmt.Errorf("create backend: %w", err)
}
@@ -56,28 +56,29 @@ func NewServer(ctx context.Context, cfg *config.Config) (*Server, error) {
Cron: cron.NewCronScheduler(ctx),
Config: cfg,
Backend: cfg.Backend,
+ logger: log.FromContext(ctx).WithPrefix("server"),
ctx: ctx,
}
// Add cron jobs.
- srv.Cron.AddFunc(jobSpecs["mirror"], mirrorJob(cfg))
+ srv.Cron.AddFunc(jobSpecs["mirror"], srv.mirrorJob())
- srv.SSHServer, err = sshsrv.NewSSHServer(cfg)
+ srv.SSHServer, err = sshsrv.NewSSHServer(ctx)
if err != nil {
return nil, fmt.Errorf("create ssh server: %w", err)
}
- srv.GitDaemon, err = daemon.NewGitDaemon(cfg)
+ srv.GitDaemon, err = daemon.NewGitDaemon(ctx)
if err != nil {
return nil, fmt.Errorf("create git daemon: %w", err)
}
- srv.HTTPServer, err = web.NewHTTPServer(cfg)
+ srv.HTTPServer, err = web.NewHTTPServer(ctx)
if err != nil {
return nil, fmt.Errorf("create http server: %w", err)
}
- srv.StatsServer, err = stats.NewStatsServer(cfg)
+ srv.StatsServer, err = stats.NewStatsServer(ctx)
if err != nil {
return nil, fmt.Errorf("create stats server: %w", err)
}
@@ -101,38 +102,37 @@ func start(ctx context.Context, fn func() error) error {
// Start starts the SSH server.
func (s *Server) Start() error {
- logger := log.FromContext(s.ctx).WithPrefix("server")
errg, ctx := errgroup.WithContext(s.ctx)
errg.Go(func() error {
- logger.Print("Starting Git daemon", "addr", s.Config.Git.ListenAddr)
+ s.logger.Print("Starting Git daemon", "addr", s.Config.Git.ListenAddr)
if err := start(ctx, s.GitDaemon.Start); !errors.Is(err, daemon.ErrServerClosed) {
return err
}
return nil
})
errg.Go(func() error {
- logger.Print("Starting HTTP server", "addr", s.Config.HTTP.ListenAddr)
+ s.logger.Print("Starting HTTP server", "addr", s.Config.HTTP.ListenAddr)
if err := start(ctx, s.HTTPServer.ListenAndServe); !errors.Is(err, http.ErrServerClosed) {
return err
}
return nil
})
errg.Go(func() error {
- logger.Print("Starting SSH server", "addr", s.Config.SSH.ListenAddr)
+ s.logger.Print("Starting SSH server", "addr", s.Config.SSH.ListenAddr)
if err := start(ctx, s.SSHServer.ListenAndServe); !errors.Is(err, ssh.ErrServerClosed) {
return err
}
return nil
})
errg.Go(func() error {
- logger.Print("Starting Stats server", "addr", s.Config.Stats.ListenAddr)
+ s.logger.Print("Starting Stats server", "addr", s.Config.Stats.ListenAddr)
if err := start(ctx, s.StatsServer.ListenAndServe); !errors.Is(err, http.ErrServerClosed) {
return err
}
return nil
})
errg.Go(func() error {
- logger.Print("Starting cron scheduler")
+ s.logger.Print("Starting cron scheduler")
s.Cron.Start()
return nil
})
@@ -25,10 +25,11 @@ func setupServer(tb testing.TB) (*Server, *config.Config, string) {
tb.Setenv("SOFT_SERVE_INITIAL_ADMIN_KEY", authorizedKey(pub))
tb.Setenv("SOFT_SERVE_SSH_LISTEN_ADDR", sshPort)
tb.Setenv("SOFT_SERVE_GIT_LISTEN_ADDR", fmt.Sprintf(":%d", test.RandomPort()))
+ ctx := context.TODO()
cfg := config.DefaultConfig()
+ ctx = config.WithContext(ctx, cfg)
tb.Log("configuring server")
- ctx := context.TODO()
- s, err := NewServer(ctx, cfg)
+ s, err := NewServer(ctx)
if err != nil {
tb.Fatal(err)
}
@@ -59,7 +59,8 @@ func setup(tb testing.TB) (*gossh.Session, func() error) {
})
ctx := context.TODO()
cfg := config.DefaultConfig()
- fb, err := sqlite.NewSqliteBackend(ctx, cfg)
+ ctx = config.WithContext(ctx, cfg)
+ fb, err := sqlite.NewSqliteBackend(ctx)
if err != nil {
log.Fatal(err)
}
@@ -26,10 +26,6 @@ import (
gossh "golang.org/x/crypto/ssh"
)
-var (
- logger = log.WithPrefix("server.ssh")
-)
-
var (
publicKeyCounter = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "soft_serve",
@@ -76,15 +72,22 @@ var (
// SSHServer is a SSH server that implements the git protocol.
type SSHServer struct {
- srv *ssh.Server
- cfg *config.Config
+ srv *ssh.Server
+ cfg *config.Config
+ ctx context.Context
+ logger *log.Logger
}
// NewSSHServer returns a new SSHServer.
-func NewSSHServer(cfg *config.Config) (*SSHServer, error) {
+func NewSSHServer(ctx context.Context) (*SSHServer, error) {
+ cfg := config.FromContext(ctx)
var err error
- s := &SSHServer{cfg: cfg}
- logger := logger.StandardLog(log.StandardLogOptions{ForceLevel: log.DebugLevel})
+ s := &SSHServer{
+ cfg: cfg,
+ ctx: ctx,
+ logger: log.FromContext(ctx).WithPrefix("ssh"),
+ }
+ logger := s.logger.StandardLog(log.StandardLogOptions{ForceLevel: log.DebugLevel})
mw := []wish.Middleware{
rm.MiddlewareWithLogger(
logger,
@@ -151,7 +154,7 @@ func (s *SSHServer) PublicKeyHandler(ctx ssh.Context, pk ssh.PublicKey) (allowed
}(&allowed)
ac := s.cfg.Backend.AccessLevelByPublicKey("", pk)
- logger.Debugf("access level for %q: %s", ak, ac)
+ s.logger.Debugf("access level for %q: %s", ak, ac)
allowed = ac >= backend.ReadOnlyAccess
return
}
@@ -168,7 +171,7 @@ func (s *SSHServer) KeyboardInteractiveHandler(ctx ssh.Context, _ gossh.Keyboard
// checked for access on a per repo basis for a ssh.Session public key.
// Hooks.Push and Hooks.Fetch will be called on successful completion of
// their commands.
-func (s *SSHServer) Middleware(cfg *config.Config) wish.Middleware {
+func (ss *SSHServer) Middleware(cfg *config.Config) wish.Middleware {
return func(sh ssh.Handler) ssh.Handler {
return func(s ssh.Session) {
func() {
@@ -196,7 +199,7 @@ func (s *SSHServer) Middleware(cfg *config.Config) wish.Middleware {
"SOFT_SERVE_PUBLIC_KEY=" + ak,
}
- logger.Debug("git middleware", "cmd", gc, "access", access.String())
+ ss.logger.Debug("git middleware", "cmd", gc, "access", access.String())
repoDir := filepath.Join(reposDir, repo)
switch gc {
case git.ReceivePackBin:
@@ -11,15 +11,18 @@ import (
// StatsServer is a server for collecting and reporting statistics.
type StatsServer struct {
+ ctx context.Context
cfg *config.Config
server *http.Server
}
// NewStatsServer returns a new StatsServer.
-func NewStatsServer(cfg *config.Config) (*StatsServer, error) {
+func NewStatsServer(ctx context.Context) (*StatsServer, error) {
+ cfg := config.FromContext(ctx)
mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.Handler())
return &StatsServer{
+ ctx: ctx,
cfg: cfg,
server: &http.Server{
Addr: cfg.Stats.ListenAddr,
@@ -24,10 +24,6 @@ import (
"goji.io/pattern"
)
-var (
- logger = log.WithPrefix("server.web")
-)
-
var (
gitHttpCounter = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "soft_serve",
@@ -64,18 +60,17 @@ func (r *logWriter) WriteHeader(code int) {
r.ResponseWriter.WriteHeader(code)
}
-func loggingMiddleware(next http.Handler) http.Handler {
- logger := logger.WithPrefix("server.http")
+func (s *HTTPServer) loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
writer := &logWriter{code: http.StatusOK, ResponseWriter: w}
- logger.Debug("request",
+ s.logger.Debug("request",
"method", r.Method,
"uri", r.RequestURI,
"addr", r.RemoteAddr)
next.ServeHTTP(writer, r)
elapsed := time.Since(start)
- logger.Debug("response",
+ s.logger.Debug("response",
"status", fmt.Sprintf("%d %s", writer.code, http.StatusText(writer.code)),
"bytes", humanize.Bytes(uint64(writer.bytes)),
"time", elapsed)
@@ -84,15 +79,20 @@ func loggingMiddleware(next http.Handler) http.Handler {
// HTTPServer is an http server.
type HTTPServer struct {
+ ctx context.Context
cfg *config.Config
server *http.Server
dirHandler http.Handler
+ logger *log.Logger
}
-func NewHTTPServer(cfg *config.Config) (*HTTPServer, error) {
+func NewHTTPServer(ctx context.Context) (*HTTPServer, error) {
+ cfg := config.FromContext(ctx)
mux := goji.NewMux()
s := &HTTPServer{
+ ctx: ctx,
cfg: cfg,
+ logger: log.FromContext(ctx).WithPrefix("http"),
dirHandler: http.FileServer(http.Dir(filepath.Join(cfg.DataPath, "repos"))),
server: &http.Server{
Addr: cfg.HTTP.ListenAddr,
@@ -104,7 +104,7 @@ func NewHTTPServer(cfg *config.Config) (*HTTPServer, error) {
},
}
- mux.Use(loggingMiddleware)
+ mux.Use(s.loggingMiddleware)
for _, m := range []Matcher{
getInfoRefs,
getHead,
@@ -306,7 +306,7 @@ func (s *HTTPServer) handleGit(w http.ResponseWriter, r *http.Request) {
repo := pat.Param(r, "repo")
repo = utils.SanitizeRepo(repo) + ".git"
if _, err := s.cfg.Backend.Repository(repo); err != nil {
- logger.Debug("repository not found", "repo", repo, "err", err)
+ s.logger.Debug("repository not found", "repo", repo, "err", err)
http.NotFound(w, r)
return
}
@@ -3,6 +3,7 @@ package common
import (
"context"
+ "github.com/charmbracelet/log"
"github.com/charmbracelet/soft-serve/git"
"github.com/charmbracelet/soft-serve/server/config"
"github.com/charmbracelet/soft-serve/ui/keymap"
@@ -30,6 +31,7 @@ type Common struct {
KeyMap *keymap.KeyMap
Zone *zone.Manager
Output *termenv.Output
+ Logger *log.Logger
}
// NewCommon returns a new Common struct.
@@ -45,6 +47,7 @@ func NewCommon(ctx context.Context, out *termenv.Output, width, height int) Comm
Styles: styles.DefaultStyles(),
KeyMap: keymap.DefaultKeyMap(),
Zone: zone.New(),
+ Logger: log.FromContext(ctx).WithPrefix("ui"),
}
}
@@ -393,7 +393,7 @@ func (l *Log) countCommitsCmd() tea.Msg {
}
count, err := r.CountCommits(l.ref)
if err != nil {
- logger.Debugf("ui: error counting commits: %v", err)
+ l.common.Logger.Debugf("ui: error counting commits: %v", err)
return common.ErrorMsg(err)
}
return LogCountMsg(count)
@@ -423,7 +423,7 @@ func (l *Log) updateCommitsCmd() tea.Msg {
// CommitsByPage pages start at 1
cc, err := r.CommitsByPage(l.ref, page+1, limit)
if err != nil {
- logger.Debugf("ui: error loading commits: %v", err)
+ l.common.Logger.Debugf("ui: error loading commits: %v", err)
return common.ErrorMsg(err)
}
for i, c := range cc {
@@ -445,12 +445,12 @@ func (l *Log) selectCommitCmd(commit *git.Commit) tea.Cmd {
func (l *Log) loadDiffCmd() tea.Msg {
r, err := l.repo.Open()
if err != nil {
- logger.Debugf("ui: error loading diff repository: %v", err)
+ l.common.Logger.Debugf("ui: error loading diff repository: %v", err)
return common.ErrorMsg(err)
}
diff, err := r.Diff(l.selectedCommit)
if err != nil {
- logger.Debugf("ui: error loading diff: %v", err)
+ l.common.Logger.Debugf("ui: error loading diff: %v", err)
return common.ErrorMsg(err)
}
return LogDiffMsg(diff)
@@ -181,7 +181,7 @@ func (r *Refs) updateItemsCmd() tea.Msg {
}
refs, err := rr.References()
if err != nil {
- logger.Debugf("ui: error getting references: %v", err)
+ r.common.Logger.Debugf("ui: error getting references: %v", err)
return common.ErrorMsg(err)
}
for _, ref := range refs {
@@ -228,10 +228,8 @@ func UpdateRefCmd(repo backend.Repository) tea.Cmd {
}
ref, err := r.HEAD()
if err != nil {
- logger.Debugf("ui: error getting HEAD reference: %v", err)
return common.ErrorMsg(err)
}
- logger.Debugf("HEAD: %s", ref.Name())
return RefMsg(ref)
}
}
@@ -8,7 +8,6 @@ import (
"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
- "github.com/charmbracelet/log"
"github.com/charmbracelet/soft-serve/git"
"github.com/charmbracelet/soft-serve/server/backend"
"github.com/charmbracelet/soft-serve/ui/common"
@@ -17,10 +16,6 @@ import (
"github.com/charmbracelet/soft-serve/ui/components/tabs"
)
-var (
- logger = log.WithPrefix("ui.repo")
-)
-
type state int
const (
@@ -92,6 +87,7 @@ func New(c common.Common) *Repo {
for i, t := range []tab{readmeTab, filesTab, commitsTab, branchesTab, tagsTab} {
ts[i] = t.String()
}
+ c.Logger = c.Logger.WithPrefix("ui.repo")
tb := tabs.New(c, ts)
readme := NewReadme(c)
log := NewLog(c)
@@ -8,7 +8,6 @@ import (
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
- "github.com/charmbracelet/log"
"github.com/charmbracelet/soft-serve/server/backend"
"github.com/charmbracelet/soft-serve/ui/common"
"github.com/charmbracelet/soft-serve/ui/components/code"
@@ -20,10 +19,6 @@ const (
defaultNoContent = "No readme found.\n\nCreate a `.soft-serve` repository and add a `README.md` file to display readme."
)
-var (
- logger = log.WithPrefix("ui.selection")
-)
-
type pane int
const (
@@ -219,7 +214,7 @@ func (s *Selection) Init() tea.Cmd {
if al >= backend.ReadOnlyAccess {
item, err := NewItem(r, cfg)
if err != nil {
- logger.Debugf("ui: failed to create item for %s: %v", r.Name(), err)
+ s.common.Logger.Debugf("ui: failed to create item for %s: %v", r.Name(), err)
continue
}
sortedItems = append(sortedItems, item)
@@ -7,7 +7,6 @@ import (
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
- "github.com/charmbracelet/log"
"github.com/charmbracelet/soft-serve/server/backend"
"github.com/charmbracelet/soft-serve/ui/common"
"github.com/charmbracelet/soft-serve/ui/components/footer"
@@ -17,10 +16,6 @@ import (
"github.com/charmbracelet/soft-serve/ui/pages/selection"
)
-var (
- logger = log.WithPrefix("ui")
-)
-
type page int
const (
@@ -165,7 +160,7 @@ func (ui *UI) IsFiltering() bool {
// Update implements tea.Model.
func (ui *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
- logger.Debugf("msg received: %T", msg)
+ ui.common.Logger.Debugf("msg received: %T", msg)
cmds := make([]tea.Cmd, 0)
switch msg := msg.(type) {
case tea.WindowSizeMsg:
@@ -295,7 +290,7 @@ func (ui *UI) openRepo(rn string) (backend.Repository, error) {
}
repos, err := cfg.Backend.Repositories()
if err != nil {
- logger.Debugf("ui: failed to list repos: %v", err)
+ ui.common.Logger.Debugf("ui: failed to list repos: %v", err)
return nil, err
}
for _, r := range repos {