github.com/andrewrech/lazygit@v0.8.1/pkg/app/app.go (about)

     1  package app
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/heroku/rollrus"
    13  	"github.com/jesseduffield/lazygit/pkg/commands"
    14  	"github.com/jesseduffield/lazygit/pkg/config"
    15  	"github.com/jesseduffield/lazygit/pkg/gui"
    16  	"github.com/jesseduffield/lazygit/pkg/i18n"
    17  	"github.com/jesseduffield/lazygit/pkg/updates"
    18  	"github.com/shibukawa/configdir"
    19  	"github.com/sirupsen/logrus"
    20  )
    21  
    22  // we hit the limit: putting this here for now
    23  const HaveNotHitRollbarLimit = false
    24  
    25  // App struct
    26  type App struct {
    27  	closers []io.Closer
    28  
    29  	Config        config.AppConfigurer
    30  	Log           *logrus.Entry
    31  	OSCommand     *commands.OSCommand
    32  	GitCommand    *commands.GitCommand
    33  	Gui           *gui.Gui
    34  	Tr            *i18n.Localizer
    35  	Updater       *updates.Updater // may only need this on the Gui
    36  	ClientContext string
    37  }
    38  
    39  func newProductionLogger(config config.AppConfigurer) *logrus.Logger {
    40  	log := logrus.New()
    41  	log.Out = ioutil.Discard
    42  	log.SetLevel(logrus.ErrorLevel)
    43  	return log
    44  }
    45  
    46  func globalConfigDir() string {
    47  	configDirs := configdir.New("jesseduffield", "lazygit")
    48  	configDir := configDirs.QueryFolders(configdir.Global)[0]
    49  	return configDir.Path
    50  }
    51  
    52  func getLogLevel() logrus.Level {
    53  	strLevel := os.Getenv("LOG_LEVEL")
    54  	level, err := logrus.ParseLevel(strLevel)
    55  	if err != nil {
    56  		return logrus.DebugLevel
    57  	}
    58  	return level
    59  }
    60  
    61  func newDevelopmentLogger(config config.AppConfigurer) *logrus.Logger {
    62  	log := logrus.New()
    63  	log.SetLevel(getLogLevel())
    64  	file, err := os.OpenFile(filepath.Join(globalConfigDir(), "development.log"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    65  	if err != nil {
    66  		panic("unable to log to file") // TODO: don't panic (also, remove this call to the `panic` function)
    67  	}
    68  	log.SetOutput(file)
    69  	return log
    70  }
    71  
    72  func newLogger(config config.AppConfigurer) *logrus.Entry {
    73  	var log *logrus.Logger
    74  	environment := "production"
    75  	if config.GetDebug() || os.Getenv("DEBUG") == "TRUE" {
    76  		environment = "development"
    77  		log = newDevelopmentLogger(config)
    78  	} else {
    79  		log = newProductionLogger(config)
    80  	}
    81  
    82  	// highly recommended: tail -f development.log | humanlog
    83  	// https://github.com/aybabtme/humanlog
    84  	log.Formatter = &logrus.JSONFormatter{}
    85  
    86  	if HaveNotHitRollbarLimit && config.GetUserConfig().GetString("reporting") == "on" {
    87  		// this isn't really a secret token: it only has permission to push new rollbar items
    88  		hook := rollrus.NewHook("23432119147a4367abf7c0de2aa99a2d", environment)
    89  		log.Hooks.Add(hook)
    90  	}
    91  	return log.WithFields(logrus.Fields{
    92  		"debug":     config.GetDebug(),
    93  		"version":   config.GetVersion(),
    94  		"commit":    config.GetCommit(),
    95  		"buildDate": config.GetBuildDate(),
    96  	})
    97  }
    98  
    99  // NewApp bootstrap a new application
   100  func NewApp(config config.AppConfigurer) (*App, error) {
   101  	app := &App{
   102  		closers: []io.Closer{},
   103  		Config:  config,
   104  	}
   105  	var err error
   106  	app.Log = newLogger(config)
   107  	app.Tr = i18n.NewLocalizer(app.Log)
   108  
   109  	// if we are being called in 'demon' mode, we can just return here
   110  	app.ClientContext = os.Getenv("LAZYGIT_CLIENT_COMMAND")
   111  	if app.ClientContext != "" {
   112  		return app, nil
   113  	}
   114  
   115  	app.OSCommand = commands.NewOSCommand(app.Log, config)
   116  
   117  	app.Updater, err = updates.NewUpdater(app.Log, config, app.OSCommand, app.Tr)
   118  	if err != nil {
   119  		return app, err
   120  	}
   121  
   122  	if err := app.setupRepo(); err != nil {
   123  		return app, err
   124  	}
   125  
   126  	app.GitCommand, err = commands.NewGitCommand(app.Log, app.OSCommand, app.Tr, app.Config)
   127  	if err != nil {
   128  		return app, err
   129  	}
   130  	app.Gui, err = gui.NewGui(app.Log, app.GitCommand, app.OSCommand, app.Tr, config, app.Updater)
   131  	if err != nil {
   132  		return app, err
   133  	}
   134  	return app, nil
   135  }
   136  
   137  func (app *App) setupRepo() error {
   138  	// if we are not in a git repo, we ask if we want to `git init`
   139  	if err := app.OSCommand.RunCommand("git status"); err != nil {
   140  		if !strings.Contains(err.Error(), "Not a git repository") {
   141  			return err
   142  		}
   143  		fmt.Print(app.Tr.SLocalize("CreateRepo"))
   144  		response, _ := bufio.NewReader(os.Stdin).ReadString('\n')
   145  		if strings.Trim(response, " \n") != "y" {
   146  			os.Exit(1)
   147  		}
   148  		if err := app.OSCommand.RunCommand("git init"); err != nil {
   149  			return err
   150  		}
   151  	}
   152  	return nil
   153  }
   154  
   155  func (app *App) Run() error {
   156  	if app.ClientContext == "INTERACTIVE_REBASE" {
   157  		return app.Rebase()
   158  	}
   159  
   160  	if app.ClientContext == "EXIT_IMMEDIATELY" {
   161  		os.Exit(0)
   162  	}
   163  
   164  	return app.Gui.RunWithSubprocesses()
   165  }
   166  
   167  // Rebase contains logic for when we've been run in demon mode, meaning we've
   168  // given lazygit as a command for git to call e.g. to edit a file
   169  func (app *App) Rebase() error {
   170  	app.Log.Info("Lazygit invoked as interactive rebase demon")
   171  	app.Log.Info("args: ", os.Args)
   172  
   173  	if strings.HasSuffix(os.Args[1], "git-rebase-todo") {
   174  		if err := ioutil.WriteFile(os.Args[1], []byte(os.Getenv("LAZYGIT_REBASE_TODO")), 0644); err != nil {
   175  			return err
   176  		}
   177  
   178  	} else if strings.HasSuffix(os.Args[1], ".git/COMMIT_EDITMSG") {
   179  		// if we are rebasing and squashing, we'll see a COMMIT_EDITMSG
   180  		// but in this case we don't need to edit it, so we'll just return
   181  	} else {
   182  		app.Log.Info("Lazygit demon did not match on any use cases")
   183  	}
   184  
   185  	return nil
   186  }
   187  
   188  // Close closes any resources
   189  func (app *App) Close() error {
   190  	for _, closer := range app.closers {
   191  		err := closer.Close()
   192  		if err != nil {
   193  			return err
   194  		}
   195  	}
   196  	return nil
   197  }