1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2024-12-12 11:15:00 +02:00
lazygit/pkg/app/app.go

272 lines
6.5 KiB
Go
Raw Normal View History

package app
import (
"bufio"
"fmt"
2021-10-23 00:52:19 +02:00
"io"
"log"
"os"
"path/filepath"
"strings"
"github.com/go-errors/errors"
"github.com/sirupsen/logrus"
2022-03-20 01:19:14 +02:00
"github.com/jesseduffield/generics/slices"
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
"github.com/jesseduffield/lazygit/pkg/commands"
2022-08-15 14:59:34 +02:00
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"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"
2018-08-13 12:26:02 +02:00
"github.com/jesseduffield/lazygit/pkg/gui"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/logs"
2018-08-19 15:28:29 +02:00
"github.com/jesseduffield/lazygit/pkg/updates"
)
// 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
}
2022-08-09 12:27:44 +02:00
func Run(
config config.AppConfigurer,
common *common.Common,
startArgs appTypes.StartArgs,
2022-08-09 12:27:44 +02:00
) {
app, err := NewApp(config, common)
2018-08-26 07:46:18 +02:00
if err == nil {
err = app.Run(startArgs)
2019-03-16 01:37:31 +02:00
}
2018-08-13 13:16:21 +02:00
if err != nil {
if errorMessage, known := knownError(common.Tr, err); known {
log.Fatal(errorMessage)
}
newErr := errors.Wrap(err, 0)
stackTrace := newErr.ErrorStack()
app.Log.Error(stackTrace)
2022-05-18 13:33:35 +02:00
log.Fatalf("%s: %s\n\n%s", common.Tr.ErrorOccurred, constants.Links.Issues, stackTrace)
2018-08-26 07:46:18 +02:00
}
}
func NewCommon(config config.AppConfigurer) (*common.Common, error) {
2021-12-29 03:03:35 +02:00
userConfig := config.GetUserConfig()
var err error
log := newLogger(config)
2021-12-29 03:03:35 +02:00
tr, err := i18n.NewTranslationSetFromConfig(log, userConfig.Gui.Language)
if err != nil {
return nil, err
}
return &common.Common{
Log: log,
Tr: tr,
2021-12-29 03:03:35 +02:00
UserConfig: userConfig,
Debug: config.GetDebug(),
}, nil
}
func newLogger(cfg config.AppConfigurer) *logrus.Entry {
if cfg.GetDebug() {
logPath, err := config.LogPath()
if err != nil {
log.Fatal(err)
}
return logs.NewDevelopmentLogger(logPath)
} else {
return logs.NewProductionLogger()
}
}
// NewApp bootstrap a new application
func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
app := &App{
closers: []io.Closer{},
Config: config,
Common: common,
2019-02-18 12:29:43 +02:00
}
app.OSCommand = oscommands.NewOSCommand(common, config, oscommands.GetPlatform(), oscommands.NewNullGuiIO(app.Log))
2019-02-18 12:29:43 +02:00
updater, err := updates.NewUpdater(common, config, app.OSCommand)
if err != nil {
2018-08-18 11:43:58 +02:00
return app, err
}
2022-03-15 15:12:26 +02:00
dirName, err := os.Getwd()
if err != nil {
return app, err
}
2022-12-26 16:43:08 +02:00
gitVersion, err := app.validateGitVersion()
if err != nil {
return app, err
}
showRecentRepos, err := app.setupRepo()
if err != nil {
return app, err
}
// used for testing purposes
if os.Getenv("SHOW_RECENT_REPOS") == "true" {
showRecentRepos = true
}
app.Gui, err = gui.NewGui(common, config, gitVersion, updater, showRecentRepos, dirName)
2018-08-13 12:26:02 +02:00
if err != nil {
2018-08-18 11:43:58 +02:00
return app, err
2018-08-13 12:26:02 +02:00
}
return app, nil
}
2022-12-26 16:43:08 +02:00
func (app *App) validateGitVersion() (*git_commands.GitVersion, error) {
version, err := git_commands.GetGitVersion(app.OSCommand)
// if we get an error anywhere here we'll show the same status
2020-10-04 02:00:48 +02:00
minVersionError := errors.New(app.Tr.MinGitVersionError)
if err != nil {
2022-12-26 16:43:08 +02:00
return nil, minVersionError
}
2020-09-18 13:00:03 +02:00
if version.IsOlderThan(2, 20, 0) {
2022-12-26 16:43:08 +02:00
return nil, minVersionError
2020-09-18 13:00:03 +02:00
}
2022-12-26 16:43:08 +02:00
return version, nil
}
func isDirectoryAGitRepository(dir string) (bool, error) {
info, err := os.Stat(filepath.Join(dir, ".git"))
return info != nil, err
}
2022-08-01 21:05:35 +02:00
func openRecentRepo(app *App) bool {
for _, repoDir := range app.Config.GetAppState().RecentRepos {
if isRepo, _ := isDirectoryAGitRepository(repoDir); isRepo {
if err := os.Chdir(repoDir); err == nil {
return true
}
}
}
return false
}
func (app *App) setupRepo() (bool, error) {
if env.GetGitDirEnv() != "" {
2022-01-08 05:10:01 +02:00
// we've been given the git dir directly. We'll verify this dir when initializing our Git object
2020-09-27 07:36:04 +02:00
return false, nil
}
// if we are not in a git repo, we ask if we want to `git init`
2021-03-30 13:17:42 +02:00
if err := commands.VerifyInGitRepo(app.OSCommand); err != nil {
2020-05-28 06:20:13 +02:00
cwd, err := os.Getwd()
if err != nil {
return false, err
}
2022-07-30 00:55:34 +02:00
if isRepo, err := isDirectoryAGitRepository(cwd); isRepo {
return false, err
2020-05-28 06:20:13 +02:00
}
var shouldInitRepo bool
initialBranchArg := ""
switch app.UserConfig.NotARepository {
case "prompt":
// Offer to initialize a new repository in current directory.
fmt.Print(app.Tr.CreateRepo)
response, _ := bufio.NewReader(os.Stdin).ReadString('\n')
shouldInitRepo = (strings.Trim(response, " \r\n") == "y")
if shouldInitRepo {
2022-06-30 13:53:58 +02:00
// Ask for the initial branch name
fmt.Print(app.Tr.InitialBranch)
response, _ := bufio.NewReader(os.Stdin).ReadString('\n')
if trimmedResponse := strings.Trim(response, " \r\n"); len(trimmedResponse) > 0 {
initialBranchArg += "--initial-branch=" + trimmedResponse
2022-06-30 13:53:58 +02:00
}
}
case "create":
shouldInitRepo = true
case "skip":
shouldInitRepo = false
case "quit":
fmt.Fprintln(os.Stderr, app.Tr.NotARepository)
os.Exit(1)
default:
fmt.Fprintln(os.Stderr, app.Tr.IncorrectNotARepository)
os.Exit(1)
}
if shouldInitRepo {
args := []string{"git", "init"}
if initialBranchArg != "" {
args = append(args, initialBranchArg)
}
if err := app.OSCommand.Cmd.New(args).Run(); err != nil {
return false, err
}
return false, nil
}
// check if we have a recent repo we can open
for _, repoDir := range app.Config.GetAppState().RecentRepos {
if isRepo, _ := isDirectoryAGitRepository(repoDir); isRepo {
if err := os.Chdir(repoDir); err == nil {
return true, nil
}
}
}
fmt.Fprintln(os.Stderr, app.Tr.NoRecentRepositories)
os.Exit(1)
}
2021-03-30 13:17:42 +02:00
2022-07-30 00:55:34 +02:00
// Run this afterward so that the previous repo creation steps can run without this interfering
2022-08-15 14:59:34 +02:00
if isBare, err := git_commands.IsBareRepo(app.OSCommand); isBare {
2022-07-30 00:55:34 +02:00
if err != nil {
return false, err
}
2022-08-01 20:39:39 +02:00
fmt.Print(app.Tr.BareRepo)
response, _ := bufio.NewReader(os.Stdin).ReadString('\n')
2022-08-18 19:26:34 +02:00
2022-08-01 22:54:54 +02:00
if shouldOpenRecent := strings.Trim(response, " \r\n") == "y"; !shouldOpenRecent {
2022-08-01 21:05:35 +02:00
os.Exit(0)
2022-07-30 00:55:34 +02:00
}
2022-08-01 22:54:54 +02:00
if didOpenRepo := openRecentRepo(app); didOpenRepo {
return true, nil
}
fmt.Println(app.Tr.NoRecentRepositories)
os.Exit(1)
2022-07-30 00:55:34 +02:00
}
return false, nil
}
func (app *App) Run(startArgs appTypes.StartArgs) error {
err := app.Gui.RunAndHandleError(startArgs)
return err
}
// Close closes any resources
func (app *App) Close() error {
2022-03-20 01:19:14 +02:00
return slices.TryForEach(app.closers, func(closer io.Closer) error {
return closer.Close()
})
}