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

266 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"
"regexp"
"strconv"
"strings"
"github.com/go-errors/errors"
2022-03-20 01:19:14 +02:00
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/commands"
2021-10-23 00:52:19 +02:00
"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"
2018-08-13 12:26:02 +02:00
"github.com/jesseduffield/lazygit/pkg/gui"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/i18n"
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
Updater *updates.Updater // may only need this on the Gui
}
func Run(config config.AppConfigurer, common *common.Common, startArgs types.StartArgs) {
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
}
// 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
var err error
app.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
}
showRecentRepos, err := app.setupRepo()
if err != nil {
return app, err
}
2022-01-02 01:34:33 +02:00
gitConfig := git_config.NewStdCachedGitConfig(app.Log)
2020-09-27 07:36:04 +02:00
app.Gui, err = gui.NewGui(common, config, gitConfig, app.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
}
func (app *App) validateGitVersion() error {
2021-12-29 05:33:38 +02:00
output, err := app.OSCommand.Cmd.New("git --version").RunWithOutput()
// 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 {
return minVersionError
}
2020-09-18 13:00:03 +02:00
if isGitVersionValid(output) {
return nil
}
return minVersionError
}
func isGitVersionValid(versionStr string) bool {
// output should be something like: 'git version 2.23.0 (blah)'
re := regexp.MustCompile(`[^\d]+([\d\.]+)`)
matches := re.FindStringSubmatch(versionStr)
if len(matches) == 0 {
return false
}
gitVersion := matches[1]
majorVersion, err := strconv.Atoi(gitVersion[0:1])
if err != nil {
2020-09-18 13:00:03 +02:00
return false
}
if majorVersion < 2 {
2020-09-18 13:00:03 +02:00
return false
}
2020-09-18 13:00:03 +02:00
return true
}
func isDirectoryAGitRepository(dir string) (bool, error) {
info, err := os.Stat(filepath.Join(dir, ".git"))
return info != nil, err
}
2022-07-30 00:55:34 +02:00
func isBareRepo(osCommand *oscommands.OSCommand) (bool, error) {
res, err := osCommand.Cmd.New("git rev-parse --is-bare-repository").DontLog().RunWithOutput()
if err != nil {
return false, err
}
// The command returns output with a newline, so we need to strip
return strconv.ParseBool(strings.TrimSpace(res))
}
func (app *App) setupRepo() (bool, error) {
if err := app.validateGitVersion(); err != nil {
return false, err
}
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
}
shouldInitRepo := true
2021-12-29 03:03:35 +02:00
notARepository := app.UserConfig.NotARepository
2022-06-30 13:53:58 +02:00
initialBranch := ""
if notARepository == "prompt" {
// Offer to initialize a new repository in current directory.
fmt.Print(app.Tr.CreateRepo)
response, _ := bufio.NewReader(os.Stdin).ReadString('\n')
if strings.Trim(response, " \r\n") != "y" {
shouldInitRepo = false
2022-06-30 13:53:58 +02:00
} else {
// 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 {
initialBranch += "--initial-branch=" + trimmedResponse
}
}
} else if notARepository == "skip" {
shouldInitRepo = false
}
if !shouldInitRepo {
// 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.Println(app.Tr.NoRecentRepositories)
os.Exit(1)
}
2022-06-30 13:53:58 +02:00
if err := app.OSCommand.Cmd.New("git init " + initialBranch).Run(); err != nil {
return false, err
}
}
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
if isBare, err := isBareRepo(app.OSCommand); isBare {
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')
shouldOpenRecent := strings.Trim(response, " \r\n") == "y"
if shouldOpenRecent {
for _, repoDir := range app.Config.GetAppState().RecentRepos {
if isRepo, _ := isDirectoryAGitRepository(repoDir); isRepo {
if err := os.Chdir(repoDir); err == nil {
return true, nil
2022-08-01 18:05:16 +02:00
}
}
}
2022-08-01 18:41:20 +02:00
2022-08-01 20:39:39 +02:00
fmt.Println(app.Tr.NoRecentRepositories)
os.Exit(1)
2022-07-30 00:55:34 +02:00
}
2022-08-01 20:39:39 +02:00
os.Exit(0)
2022-07-30 00:55:34 +02:00
}
return false, nil
}
func (app *App) Run(startArgs types.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()
})
}