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 }