mirror of
				https://github.com/jesseduffield/lazygit.git
				synced 2025-10-30 23:57:43 +02:00 
			
		
		
		
	clearer separation of concerns when bootstrapping application
This commit is contained in:
		
							
								
								
									
										29
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								main.go
									
									
									
									
									
								
							| @@ -8,12 +8,12 @@ import ( | ||||
| 	"path/filepath" | ||||
| 	"runtime" | ||||
|  | ||||
| 	"github.com/go-errors/errors" | ||||
| 	"github.com/integrii/flaggy" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/app" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/app/daemon" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/config" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/constants" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/env" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/logs" | ||||
| 	yaml "github.com/jesseduffield/yaml" | ||||
| ) | ||||
|  | ||||
| @@ -117,7 +117,7 @@ func main() { | ||||
| 	} | ||||
|  | ||||
| 	if logFlag { | ||||
| 		app.TailLogs() | ||||
| 		logs.TailLogs() | ||||
| 		os.Exit(0) | ||||
| 	} | ||||
|  | ||||
| @@ -138,20 +138,15 @@ func main() { | ||||
| 		log.Fatal(err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	app, err := app.NewApp(appConfig) | ||||
|  | ||||
| 	if err == nil { | ||||
| 		err = app.Run(filterPath) | ||||
| 	} | ||||
|  | ||||
| 	common, err := app.NewCommon(appConfig) | ||||
| 	if err != nil { | ||||
| 		if errorMessage, known := app.KnownError(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", app.Tr.ErrorOccurred, constants.Links.Issues, stackTrace)) | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	if daemon.InDaemonMode() { | ||||
| 		daemon.Handle(common) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	app.Run(appConfig, common, filterPath) | ||||
| } | ||||
|   | ||||
							
								
								
									
										201
									
								
								pkg/app/app.go
									
									
									
									
									
								
							
							
						
						
									
										201
									
								
								pkg/app/app.go
									
									
									
									
									
								
							| @@ -2,10 +2,8 @@ package app | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| @@ -13,118 +11,81 @@ import ( | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/aybabtme/humanlog" | ||||
| 	"github.com/go-errors/errors" | ||||
|  | ||||
| 	"github.com/jesseduffield/generics/slices" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/git_config" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/oscommands" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/common" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/config" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/constants" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/env" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/i18n" | ||||
| 	"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 { | ||||
| 	*common.Common | ||||
| 	closers       []io.Closer | ||||
| 	Config        config.AppConfigurer | ||||
| 	OSCommand     *oscommands.OSCommand | ||||
| 	Gui           *gui.Gui | ||||
| 	Updater       *updates.Updater // may only need this on the Gui | ||||
| 	ClientContext string | ||||
| 	closers   []io.Closer | ||||
| 	Config    config.AppConfigurer | ||||
| 	OSCommand *oscommands.OSCommand | ||||
| 	Gui       *gui.Gui | ||||
| 	Updater   *updates.Updater // may only need this on the Gui | ||||
| } | ||||
|  | ||||
| type errorMapping struct { | ||||
| 	originalError string | ||||
| 	newError      string | ||||
| } | ||||
| func Run(config config.AppConfigurer, common *common.Common, filterPath string) { | ||||
| 	app, err := NewApp(config, common) | ||||
|  | ||||
| func newProductionLogger() *logrus.Logger { | ||||
| 	log := logrus.New() | ||||
| 	log.Out = ioutil.Discard | ||||
| 	log.SetLevel(logrus.ErrorLevel) | ||||
| 	return log | ||||
| } | ||||
| 	if err == nil { | ||||
| 		err = app.Run(filterPath) | ||||
| 	} | ||||
|  | ||||
| func getLogLevel() logrus.Level { | ||||
| 	strLevel := os.Getenv("LOG_LEVEL") | ||||
| 	level, err := logrus.ParseLevel(strLevel) | ||||
| 	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 { | ||||
| 	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) { | ||||
| func NewCommon(config config.AppConfigurer) (*common.Common, error) { | ||||
| 	userConfig := config.GetUserConfig() | ||||
|  | ||||
| 	app := &App{ | ||||
| 		closers: []io.Closer{}, | ||||
| 		Config:  config, | ||||
| 	} | ||||
| 	var err error | ||||
| 	log := newLogger(config) | ||||
| 	tr, err := i18n.NewTranslationSetFromConfig(log, userConfig.Gui.Language) | ||||
| 	if err != nil { | ||||
| 		return app, err | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	app.Common = &common.Common{ | ||||
| 	return &common.Common{ | ||||
| 		Log:        log, | ||||
| 		Tr:         tr, | ||||
| 		UserConfig: userConfig, | ||||
| 		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.ClientContext = os.Getenv("LAZYGIT_CLIENT_COMMAND") | ||||
| 	if app.ClientContext != "" { | ||||
| 		return app, nil | ||||
| 	} | ||||
| 	app.OSCommand = oscommands.NewOSCommand(common, config, oscommands.GetPlatform(), oscommands.NewNullGuiIO(app.Log)) | ||||
|  | ||||
| 	app.OSCommand = oscommands.NewOSCommand(app.Common, config, oscommands.GetPlatform(), oscommands.NewNullGuiIO(log)) | ||||
|  | ||||
| 	app.Updater, err = updates.NewUpdater(app.Common, config, app.OSCommand) | ||||
| 	var err error | ||||
| 	app.Updater, err = updates.NewUpdater(common, config, app.OSCommand) | ||||
| 	if err != nil { | ||||
| 		return app, err | ||||
| 	} | ||||
| @@ -141,7 +102,7 @@ func NewApp(config config.AppConfigurer) (*App, error) { | ||||
|  | ||||
| 	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 { | ||||
| 		return app, err | ||||
| 	} | ||||
| @@ -243,97 +204,13 @@ func (app *App) setupRepo() (bool, 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) | ||||
| 	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 | ||||
| func (app *App) Close() error { | ||||
| 	return slices.TryForEach(app.closers, func(closer io.Closer) error { | ||||
| 		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 | ||||
|  | ||||
| import ( | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"os" | ||||
|  | ||||
| 	"github.com/aybabtme/humanlog" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/secureexec" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/config" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| 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) | ||||
| func newLogger(config config.AppConfigurer) *logrus.Entry { | ||||
| 	var log *logrus.Logger | ||||
| 	if config.GetDebug() || os.Getenv("DEBUG") == "TRUE" { | ||||
| 		log = newDevelopmentLogger() | ||||
| 	} else { | ||||
| 		log = newProductionLogger() | ||||
| 	} | ||||
|  | ||||
| 	if err := humanlog.Scanner(stdout, os.Stdout, opts); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	// highly recommended: tail -f development.log | humanlog | ||||
| 	// https://github.com/aybabtme/humanlog | ||||
| 	log.Formatter = &logrus.JSONFormatter{} | ||||
|  | ||||
| 	if err := cmd.Wait(); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	os.Exit(0) | ||||
| 	return log.WithFields(logrus.Fields{ | ||||
| 		"debug":     config.GetDebug(), | ||||
| 		"version":   config.GetVersion(), | ||||
| 		"commit":    config.GetCommit(), | ||||
| 		"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 { | ||||
| 		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" | ||||
| 		file, err := os.Create(path) | ||||
| 		if err != nil { | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import ( | ||||
|  | ||||
| 	"github.com/go-errors/errors" | ||||
| 	"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/oscommands" | ||||
| ) | ||||
| @@ -109,8 +110,8 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(baseSha string, todo | ||||
| 	} | ||||
|  | ||||
| 	cmdObj.AddEnvVars( | ||||
| 		"LAZYGIT_CLIENT_COMMAND=INTERACTIVE_REBASE", | ||||
| 		"LAZYGIT_REBASE_TODO="+todo, | ||||
| 		daemon.DaemonKindEnvKey+"="+string(daemon.InteractiveRebase), | ||||
| 		daemon.RebaseTODOEnvKey+"="+todo, | ||||
| 		"DEBUG="+debug, | ||||
| 		"LANG=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() | ||||
| 	return cmdObj. | ||||
| 		AddEnvVars( | ||||
| 			"LAZYGIT_CLIENT_COMMAND=EXIT_IMMEDIATELY", | ||||
| 			daemon.DaemonKindEnvKey+"="+string(daemon.ExitImmediately), | ||||
| 			"GIT_EDITOR="+lazyGitPath, | ||||
| 			"EDITOR="+lazyGitPath, | ||||
| 			"VISUAL="+lazyGitPath, | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"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/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/oscommands" | ||||
| @@ -61,7 +62,7 @@ func TestRebaseSkipEditorCommand(t *testing.T) { | ||||
| 			`^VISUAL=.*$`, | ||||
| 			`^EDITOR=.*$`, | ||||
| 			`^GIT_EDITOR=.*$`, | ||||
| 			"^LAZYGIT_CLIENT_COMMAND=EXIT_IMMEDIATELY$", | ||||
| 			"^" + daemon.DaemonKindEnvKey + "=" + string(daemon.ExitImmediately) + "$", | ||||
| 		} { | ||||
| 			regexStr := regexStr | ||||
| 			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 | ||||
|  | ||||
| import "os" | ||||
| import ( | ||||
| 	"os" | ||||
| ) | ||||
|  | ||||
| // This package encapsulates accessing/mutating the ENV of the program. | ||||
|  | ||||
| func GetGitDirEnv() string { | ||||
| 	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 | ||||
| // +build windows | ||||
| 
 | ||||
| package app | ||||
| package logs | ||||
| 
 | ||||
| import ( | ||||
| 	"bufio" | ||||
		Reference in New Issue
	
	Block a user