mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-11 04:15:48 +02:00
clearer separation of concerns when bootstrapping application
This commit is contained in:
parent
cf80978f15
commit
cd5b041b0f
12 changed files with 328 additions and 205 deletions
25
main.go
25
main.go
|
@ -8,12 +8,12 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
|
||||||
"github.com/integrii/flaggy"
|
"github.com/integrii/flaggy"
|
||||||
"github.com/jesseduffield/lazygit/pkg/app"
|
"github.com/jesseduffield/lazygit/pkg/app"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/app/daemon"
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/constants"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/env"
|
"github.com/jesseduffield/lazygit/pkg/env"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/logs"
|
||||||
yaml "github.com/jesseduffield/yaml"
|
yaml "github.com/jesseduffield/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if logFlag {
|
if logFlag {
|
||||||
app.TailLogs()
|
logs.TailLogs()
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,20 +138,15 @@ func main() {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
app, err := app.NewApp(appConfig)
|
common, err := app.NewCommon(appConfig)
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
err = app.Run(filterPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errorMessage, known := app.KnownError(err); known {
|
log.Fatal(err)
|
||||||
log.Fatal(errorMessage)
|
|
||||||
}
|
}
|
||||||
newErr := errors.Wrap(err, 0)
|
|
||||||
stackTrace := newErr.ErrorStack()
|
|
||||||
app.Log.Error(stackTrace)
|
|
||||||
|
|
||||||
log.Fatal(fmt.Sprintf("%s: %s\n\n%s", app.Tr.ErrorOccurred, constants.Links.Issues, stackTrace))
|
if daemon.InDaemonMode() {
|
||||||
|
daemon.Handle(common)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.Run(appConfig, common, filterPath)
|
||||||
}
|
}
|
||||||
|
|
191
pkg/app/app.go
191
pkg/app/app.go
|
@ -2,10 +2,8 @@ package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -13,21 +11,23 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/aybabtme/humanlog"
|
"github.com/go-errors/errors"
|
||||||
|
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/common"
|
"github.com/jesseduffield/lazygit/pkg/common"
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/constants"
|
||||||
"github.com/jesseduffield/lazygit/pkg/env"
|
"github.com/jesseduffield/lazygit/pkg/env"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui"
|
"github.com/jesseduffield/lazygit/pkg/gui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||||
"github.com/jesseduffield/lazygit/pkg/updates"
|
"github.com/jesseduffield/lazygit/pkg/updates"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// App struct
|
// App is the struct that's instantiated from within main.go and it manages
|
||||||
|
// bootstrapping and running the application.
|
||||||
type App struct {
|
type App struct {
|
||||||
*common.Common
|
*common.Common
|
||||||
closers []io.Closer
|
closers []io.Closer
|
||||||
|
@ -35,96 +35,57 @@ type App struct {
|
||||||
OSCommand *oscommands.OSCommand
|
OSCommand *oscommands.OSCommand
|
||||||
Gui *gui.Gui
|
Gui *gui.Gui
|
||||||
Updater *updates.Updater // may only need this on the Gui
|
Updater *updates.Updater // may only need this on the Gui
|
||||||
ClientContext string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type errorMapping struct {
|
func Run(config config.AppConfigurer, common *common.Common, filterPath string) {
|
||||||
originalError string
|
app, err := NewApp(config, common)
|
||||||
newError string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newProductionLogger() *logrus.Logger {
|
if err == nil {
|
||||||
log := logrus.New()
|
err = app.Run(filterPath)
|
||||||
log.Out = ioutil.Discard
|
}
|
||||||
log.SetLevel(logrus.ErrorLevel)
|
|
||||||
return log
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLogLevel() logrus.Level {
|
|
||||||
strLevel := os.Getenv("LOG_LEVEL")
|
|
||||||
level, err := logrus.ParseLevel(strLevel)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return logrus.DebugLevel
|
if errorMessage, known := knownError(common.Tr, err); known {
|
||||||
|
log.Fatal(errorMessage)
|
||||||
|
}
|
||||||
|
newErr := errors.Wrap(err, 0)
|
||||||
|
stackTrace := newErr.ErrorStack()
|
||||||
|
app.Log.Error(stackTrace)
|
||||||
|
|
||||||
|
log.Fatal(fmt.Sprintf("%s: %s\n\n%s", common.Tr.ErrorOccurred, constants.Links.Issues, stackTrace))
|
||||||
}
|
}
|
||||||
return level
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDevelopmentLogger() *logrus.Logger {
|
func NewCommon(config config.AppConfigurer) (*common.Common, error) {
|
||||||
logger := logrus.New()
|
|
||||||
logger.SetLevel(getLogLevel())
|
|
||||||
logPath, err := config.LogPath()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o666)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Unable to log to log file: %v", err)
|
|
||||||
}
|
|
||||||
logger.SetOutput(file)
|
|
||||||
return logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func newLogger(config config.AppConfigurer) *logrus.Entry {
|
|
||||||
var log *logrus.Logger
|
|
||||||
if config.GetDebug() || os.Getenv("DEBUG") == "TRUE" {
|
|
||||||
log = newDevelopmentLogger()
|
|
||||||
} else {
|
|
||||||
log = newProductionLogger()
|
|
||||||
}
|
|
||||||
|
|
||||||
// highly recommended: tail -f development.log | humanlog
|
|
||||||
// https://github.com/aybabtme/humanlog
|
|
||||||
log.Formatter = &logrus.JSONFormatter{}
|
|
||||||
|
|
||||||
return log.WithFields(logrus.Fields{
|
|
||||||
"debug": config.GetDebug(),
|
|
||||||
"version": config.GetVersion(),
|
|
||||||
"commit": config.GetCommit(),
|
|
||||||
"buildDate": config.GetBuildDate(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewApp bootstrap a new application
|
|
||||||
func NewApp(config config.AppConfigurer) (*App, error) {
|
|
||||||
userConfig := config.GetUserConfig()
|
userConfig := config.GetUserConfig()
|
||||||
|
|
||||||
app := &App{
|
|
||||||
closers: []io.Closer{},
|
|
||||||
Config: config,
|
|
||||||
}
|
|
||||||
var err error
|
var err error
|
||||||
log := newLogger(config)
|
log := newLogger(config)
|
||||||
tr, err := i18n.NewTranslationSetFromConfig(log, userConfig.Gui.Language)
|
tr, err := i18n.NewTranslationSetFromConfig(log, userConfig.Gui.Language)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Common = &common.Common{
|
return &common.Common{
|
||||||
Log: log,
|
Log: log,
|
||||||
Tr: tr,
|
Tr: tr,
|
||||||
UserConfig: userConfig,
|
UserConfig: userConfig,
|
||||||
Debug: config.GetDebug(),
|
Debug: config.GetDebug(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewApp bootstrap a new application
|
||||||
|
func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
|
||||||
|
app := &App{
|
||||||
|
closers: []io.Closer{},
|
||||||
|
Config: config,
|
||||||
|
Common: common,
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we are being called in 'demon' mode, we can just return here
|
app.OSCommand = oscommands.NewOSCommand(common, config, oscommands.GetPlatform(), oscommands.NewNullGuiIO(app.Log))
|
||||||
app.ClientContext = os.Getenv("LAZYGIT_CLIENT_COMMAND")
|
|
||||||
if app.ClientContext != "" {
|
|
||||||
return app, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
app.OSCommand = oscommands.NewOSCommand(app.Common, config, oscommands.GetPlatform(), oscommands.NewNullGuiIO(log))
|
var err error
|
||||||
|
app.Updater, err = updates.NewUpdater(common, config, app.OSCommand)
|
||||||
app.Updater, err = updates.NewUpdater(app.Common, config, app.OSCommand)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app, err
|
return app, err
|
||||||
}
|
}
|
||||||
|
@ -141,7 +102,7 @@ func NewApp(config config.AppConfigurer) (*App, error) {
|
||||||
|
|
||||||
gitConfig := git_config.NewStdCachedGitConfig(app.Log)
|
gitConfig := git_config.NewStdCachedGitConfig(app.Log)
|
||||||
|
|
||||||
app.Gui, err = gui.NewGui(app.Common, config, gitConfig, app.Updater, showRecentRepos, dirName)
|
app.Gui, err = gui.NewGui(common, config, gitConfig, app.Updater, showRecentRepos, dirName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app, err
|
return app, err
|
||||||
}
|
}
|
||||||
|
@ -243,97 +204,13 @@ func (app *App) setupRepo() (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) Run(filterPath string) error {
|
func (app *App) Run(filterPath string) error {
|
||||||
if app.ClientContext == "INTERACTIVE_REBASE" {
|
|
||||||
return app.Rebase()
|
|
||||||
}
|
|
||||||
|
|
||||||
if app.ClientContext == "EXIT_IMMEDIATELY" {
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := app.Gui.RunAndHandleError(filterPath)
|
err := app.Gui.RunAndHandleError(filterPath)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func gitDir() string {
|
|
||||||
dir := env.GetGitDirEnv()
|
|
||||||
if dir == "" {
|
|
||||||
return ".git"
|
|
||||||
}
|
|
||||||
return dir
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rebase contains logic for when we've been run in demon mode, meaning we've
|
|
||||||
// given lazygit as a command for git to call e.g. to edit a file
|
|
||||||
func (app *App) Rebase() error {
|
|
||||||
app.Log.Info("Lazygit invoked as interactive rebase demon")
|
|
||||||
app.Log.Info("args: ", os.Args)
|
|
||||||
|
|
||||||
if strings.HasSuffix(os.Args[1], "git-rebase-todo") {
|
|
||||||
if err := ioutil.WriteFile(os.Args[1], []byte(os.Getenv("LAZYGIT_REBASE_TODO")), 0o644); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else if strings.HasSuffix(os.Args[1], filepath.Join(gitDir(), "COMMIT_EDITMSG")) { // TODO: test
|
|
||||||
// if we are rebasing and squashing, we'll see a COMMIT_EDITMSG
|
|
||||||
// but in this case we don't need to edit it, so we'll just return
|
|
||||||
} else {
|
|
||||||
app.Log.Info("Lazygit demon did not match on any use cases")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes any resources
|
// Close closes any resources
|
||||||
func (app *App) Close() error {
|
func (app *App) Close() error {
|
||||||
return slices.TryForEach(app.closers, func(closer io.Closer) error {
|
return slices.TryForEach(app.closers, func(closer io.Closer) error {
|
||||||
return closer.Close()
|
return closer.Close()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// KnownError takes an error and tells us whether it's an error that we know about where we can print a nicely formatted version of it rather than panicking with a stack trace
|
|
||||||
func (app *App) KnownError(err error) (string, bool) {
|
|
||||||
errorMessage := err.Error()
|
|
||||||
|
|
||||||
knownErrorMessages := []string{app.Tr.MinGitVersionError}
|
|
||||||
|
|
||||||
if slices.Contains(knownErrorMessages, errorMessage) {
|
|
||||||
return errorMessage, true
|
|
||||||
}
|
|
||||||
|
|
||||||
mappings := []errorMapping{
|
|
||||||
{
|
|
||||||
originalError: "fatal: not a git repository",
|
|
||||||
newError: app.Tr.NotARepository,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if mapping, ok := slices.Find(mappings, func(mapping errorMapping) bool {
|
|
||||||
return strings.Contains(errorMessage, mapping.originalError)
|
|
||||||
}); ok {
|
|
||||||
return mapping.newError, true
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func TailLogs() {
|
|
||||||
logFilePath, err := config.LogPath()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Tailing log file %s\n\n", logFilePath)
|
|
||||||
|
|
||||||
opts := humanlog.DefaultOptions
|
|
||||||
opts.Truncates = false
|
|
||||||
|
|
||||||
_, err = os.Stat(logFilePath)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
log.Fatal("Log file does not exist. Run `lazygit --debug` first to create the log file")
|
|
||||||
}
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
TailLogsForPlatform(logFilePath, opts)
|
|
||||||
}
|
|
||||||
|
|
107
pkg/app/daemon/daemon.go
Normal file
107
pkg/app/daemon/daemon.go
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/common"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sometimes lazygit will be invoked in daemon mode from a parent lazygit process.
|
||||||
|
// We do this when git lets us supply a program to run within a git command.
|
||||||
|
// For example, if we want to ensure that a git command doesn't hang due to
|
||||||
|
// waiting for an editor to save a commit message, we can tell git to invoke lazygit
|
||||||
|
// as the editor via 'GIT_EDITOR=lazygit', and use the env var
|
||||||
|
// 'LAZYGIT_DAEMON_KIND=EXIT_IMMEDIATELY' to specify that we want to run lazygit
|
||||||
|
// as a daemon which simply exits immediately. Any additional arguments we want
|
||||||
|
// to pass to a daemon can be done via other env vars.
|
||||||
|
|
||||||
|
type DaemonKind string
|
||||||
|
|
||||||
|
const (
|
||||||
|
InteractiveRebase DaemonKind = "INTERACTIVE_REBASE"
|
||||||
|
ExitImmediately DaemonKind = "EXIT_IMMEDIATELY"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DaemonKindEnvKey string = "LAZYGIT_DAEMON_KIND"
|
||||||
|
RebaseTODOEnvKey string = "LAZYGIT_REBASE_TODO"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Daemon interface {
|
||||||
|
Run() error
|
||||||
|
}
|
||||||
|
|
||||||
|
func Handle(common *common.Common) {
|
||||||
|
d := getDaemon(common)
|
||||||
|
if d == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Run(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func InDaemonMode() bool {
|
||||||
|
return getDaemonKind() != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDaemon(common *common.Common) Daemon {
|
||||||
|
switch getDaemonKind() {
|
||||||
|
case InteractiveRebase:
|
||||||
|
return &rebaseDaemon{c: common}
|
||||||
|
case ExitImmediately:
|
||||||
|
return &exitImmediatelyDaemon{c: common}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDaemonKind() DaemonKind {
|
||||||
|
return DaemonKind(os.Getenv(DaemonKindEnvKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
type rebaseDaemon struct {
|
||||||
|
c *common.Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *rebaseDaemon) Run() error {
|
||||||
|
self.c.Log.Info("Lazygit invoked as interactive rebase demon")
|
||||||
|
self.c.Log.Info("args: ", os.Args)
|
||||||
|
|
||||||
|
if strings.HasSuffix(os.Args[1], "git-rebase-todo") {
|
||||||
|
if err := ioutil.WriteFile(os.Args[1], []byte(os.Getenv(RebaseTODOEnvKey)), 0o644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if strings.HasSuffix(os.Args[1], filepath.Join(gitDir(), "COMMIT_EDITMSG")) { // TODO: test
|
||||||
|
// if we are rebasing and squashing, we'll see a COMMIT_EDITMSG
|
||||||
|
// but in this case we don't need to edit it, so we'll just return
|
||||||
|
} else {
|
||||||
|
self.c.Log.Info("Lazygit demon did not match on any use cases")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func gitDir() string {
|
||||||
|
dir := env.GetGitDirEnv()
|
||||||
|
if dir == "" {
|
||||||
|
return ".git"
|
||||||
|
}
|
||||||
|
return dir
|
||||||
|
}
|
||||||
|
|
||||||
|
type exitImmediatelyDaemon struct {
|
||||||
|
c *common.Common
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *exitImmediatelyDaemon) Run() error {
|
||||||
|
return nil
|
||||||
|
}
|
39
pkg/app/errors.go
Normal file
39
pkg/app/errors.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/generics/slices"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||||
|
)
|
||||||
|
|
||||||
|
type errorMapping struct {
|
||||||
|
originalError string
|
||||||
|
newError string
|
||||||
|
}
|
||||||
|
|
||||||
|
// knownError takes an error and tells us whether it's an error that we know about where we can print a nicely formatted version of it rather than panicking with a stack trace
|
||||||
|
func knownError(tr *i18n.TranslationSet, err error) (string, bool) {
|
||||||
|
errorMessage := err.Error()
|
||||||
|
|
||||||
|
knownErrorMessages := []string{tr.MinGitVersionError}
|
||||||
|
|
||||||
|
if slices.Contains(knownErrorMessages, errorMessage) {
|
||||||
|
return errorMessage, true
|
||||||
|
}
|
||||||
|
|
||||||
|
mappings := []errorMapping{
|
||||||
|
{
|
||||||
|
originalError: "fatal: not a git repository",
|
||||||
|
newError: tr.NotARepository,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if mapping, ok := slices.Find(mappings, func(mapping errorMapping) bool {
|
||||||
|
return strings.Contains(errorMessage, mapping.originalError)
|
||||||
|
}); ok {
|
||||||
|
return mapping.newError, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", false
|
||||||
|
}
|
|
@ -1,31 +1,61 @@
|
||||||
//go:build !windows
|
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/aybabtme/humanlog"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TailLogsForPlatform(logFilePath string, opts *humanlog.HandlerOptions) {
|
func newLogger(config config.AppConfigurer) *logrus.Entry {
|
||||||
cmd := secureexec.Command("tail", "-f", logFilePath)
|
var log *logrus.Logger
|
||||||
|
if config.GetDebug() || os.Getenv("DEBUG") == "TRUE" {
|
||||||
stdout, _ := cmd.StdoutPipe()
|
log = newDevelopmentLogger()
|
||||||
if err := cmd.Start(); err != nil {
|
} else {
|
||||||
log.Fatal(err)
|
log = newProductionLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := humanlog.Scanner(stdout, os.Stdout, opts); err != nil {
|
// highly recommended: tail -f development.log | humanlog
|
||||||
log.Fatal(err)
|
// https://github.com/aybabtme/humanlog
|
||||||
}
|
log.Formatter = &logrus.JSONFormatter{}
|
||||||
|
|
||||||
if err := cmd.Wait(); err != nil {
|
return log.WithFields(logrus.Fields{
|
||||||
log.Fatal(err)
|
"debug": config.GetDebug(),
|
||||||
}
|
"version": config.GetVersion(),
|
||||||
|
"commit": config.GetCommit(),
|
||||||
os.Exit(0)
|
"buildDate": config.GetBuildDate(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProductionLogger() *logrus.Logger {
|
||||||
|
log := logrus.New()
|
||||||
|
log.Out = ioutil.Discard
|
||||||
|
log.SetLevel(logrus.ErrorLevel)
|
||||||
|
return log
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDevelopmentLogger() *logrus.Logger {
|
||||||
|
logger := logrus.New()
|
||||||
|
logger.SetLevel(getLogLevel())
|
||||||
|
logPath, err := config.LogPath()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o666)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Unable to log to log file: %v", err)
|
||||||
|
}
|
||||||
|
logger.SetOutput(file)
|
||||||
|
return logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLogLevel() logrus.Level {
|
||||||
|
strLevel := os.Getenv("LOG_LEVEL")
|
||||||
|
level, err := logrus.ParseLevel(strLevel)
|
||||||
|
if err != nil {
|
||||||
|
return logrus.DebugLevel
|
||||||
|
}
|
||||||
|
return level
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,11 @@ func generateAtDir(cheatsheetDir string) {
|
||||||
|
|
||||||
for lang := range translationSetsByLang {
|
for lang := range translationSetsByLang {
|
||||||
mConfig.GetUserConfig().Gui.Language = lang
|
mConfig.GetUserConfig().Gui.Language = lang
|
||||||
mApp, _ := app.NewApp(mConfig)
|
common, err := app.NewCommon(mConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
mApp, _ := app.NewApp(mConfig, common)
|
||||||
path := cheatsheetDir + "/Keybindings_" + lang + ".md"
|
path := cheatsheetDir + "/Keybindings_" + lang + ".md"
|
||||||
file, err := os.Create(path)
|
file, err := os.Create(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/jesseduffield/generics/slices"
|
"github.com/jesseduffield/generics/slices"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/app/daemon"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
)
|
)
|
||||||
|
@ -109,8 +110,8 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(baseSha string, todo
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdObj.AddEnvVars(
|
cmdObj.AddEnvVars(
|
||||||
"LAZYGIT_CLIENT_COMMAND=INTERACTIVE_REBASE",
|
daemon.DaemonKindEnvKey+"="+string(daemon.InteractiveRebase),
|
||||||
"LAZYGIT_REBASE_TODO="+todo,
|
daemon.RebaseTODOEnvKey+"="+todo,
|
||||||
"DEBUG="+debug,
|
"DEBUG="+debug,
|
||||||
"LANG=en_US.UTF-8", // Force using EN as language
|
"LANG=en_US.UTF-8", // Force using EN as language
|
||||||
"LC_ALL=en_US.UTF-8", // Force using EN as language
|
"LC_ALL=en_US.UTF-8", // Force using EN as language
|
||||||
|
@ -297,7 +298,7 @@ func (self *RebaseCommands) runSkipEditorCommand(cmdObj oscommands.ICmdObj) erro
|
||||||
lazyGitPath := oscommands.GetLazygitPath()
|
lazyGitPath := oscommands.GetLazygitPath()
|
||||||
return cmdObj.
|
return cmdObj.
|
||||||
AddEnvVars(
|
AddEnvVars(
|
||||||
"LAZYGIT_CLIENT_COMMAND=EXIT_IMMEDIATELY",
|
daemon.DaemonKindEnvKey+"="+string(daemon.ExitImmediately),
|
||||||
"GIT_EDITOR="+lazyGitPath,
|
"GIT_EDITOR="+lazyGitPath,
|
||||||
"EDITOR="+lazyGitPath,
|
"EDITOR="+lazyGitPath,
|
||||||
"VISUAL="+lazyGitPath,
|
"VISUAL="+lazyGitPath,
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/app/daemon"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
|
@ -61,7 +62,7 @@ func TestRebaseSkipEditorCommand(t *testing.T) {
|
||||||
`^VISUAL=.*$`,
|
`^VISUAL=.*$`,
|
||||||
`^EDITOR=.*$`,
|
`^EDITOR=.*$`,
|
||||||
`^GIT_EDITOR=.*$`,
|
`^GIT_EDITOR=.*$`,
|
||||||
"^LAZYGIT_CLIENT_COMMAND=EXIT_IMMEDIATELY$",
|
"^" + daemon.DaemonKindEnvKey + "=" + string(daemon.ExitImmediately) + "$",
|
||||||
} {
|
} {
|
||||||
regexStr := regexStr
|
regexStr := regexStr
|
||||||
foundMatch := lo.ContainsBy(envVars, func(envVar string) bool {
|
foundMatch := lo.ContainsBy(envVars, func(envVar string) bool {
|
||||||
|
|
6
pkg/env/env.go
vendored
6
pkg/env/env.go
vendored
|
@ -1,6 +1,10 @@
|
||||||
package env
|
package env
|
||||||
|
|
||||||
import "os"
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This package encapsulates accessing/mutating the ENV of the program.
|
||||||
|
|
||||||
func GetGitDirEnv() string {
|
func GetGitDirEnv() string {
|
||||||
return os.Getenv("GIT_DIR")
|
return os.Getenv("GIT_DIR")
|
||||||
|
|
34
pkg/logs/logs.go
Normal file
34
pkg/logs/logs.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package logs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/aybabtme/humanlog"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TailLogs lets us run `lazygit --logs` to print the logs produced by other lazygit processes.
|
||||||
|
// This makes for easier debugging.
|
||||||
|
func TailLogs() {
|
||||||
|
logFilePath, err := config.LogPath()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Tailing log file %s\n\n", logFilePath)
|
||||||
|
|
||||||
|
opts := humanlog.DefaultOptions
|
||||||
|
opts.Truncates = false
|
||||||
|
|
||||||
|
_, err = os.Stat(logFilePath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
log.Fatal("Log file does not exist. Run `lazygit --debug` first to create the log file")
|
||||||
|
}
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
TailLogsForPlatform(logFilePath, opts)
|
||||||
|
}
|
31
pkg/logs/logs_default.go
Normal file
31
pkg/logs/logs_default.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package logs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/aybabtme/humanlog"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TailLogsForPlatform(logFilePath string, opts *humanlog.HandlerOptions) {
|
||||||
|
cmd := secureexec.Command("tail", "-f", logFilePath)
|
||||||
|
|
||||||
|
stdout, _ := cmd.StdoutPipe()
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := humanlog.Scanner(stdout, os.Stdout, opts); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cmd.Wait(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
//go:build windows
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package app
|
package logs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
Loading…
Add table
Add a link
Reference in a new issue