mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-11 04:15:48 +02:00
auto-updates
This commit is contained in:
parent
f24c95aede
commit
21f6e9ba87
14 changed files with 359 additions and 166 deletions
|
@ -13,6 +13,9 @@ builds:
|
||||||
- arm
|
- arm
|
||||||
- arm64
|
- arm64
|
||||||
- 386
|
- 386
|
||||||
|
# Default is `-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}`.
|
||||||
|
ldflags:
|
||||||
|
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.buildSource=binaryRelease
|
||||||
|
|
||||||
archive:
|
archive:
|
||||||
replacements:
|
replacements:
|
||||||
|
|
11
main.go
11
main.go
|
@ -12,9 +12,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
commit string
|
commit string
|
||||||
version = "unversioned"
|
version = "unversioned"
|
||||||
date string
|
date string
|
||||||
|
buildSource = "unknown"
|
||||||
|
|
||||||
debuggingFlag = flag.Bool("debug", false, "a boolean")
|
debuggingFlag = flag.Bool("debug", false, "a boolean")
|
||||||
versionFlag = flag.Bool("v", false, "Print the current version")
|
versionFlag = flag.Bool("v", false, "Print the current version")
|
||||||
|
@ -28,10 +29,10 @@ func projectPath(path string) string {
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if *versionFlag {
|
if *versionFlag {
|
||||||
fmt.Printf("commit=%s, build date=%s, version=%s, os=%s, arch=%s\n", commit, date, version, runtime.GOOS, runtime.GOARCH)
|
fmt.Printf("commit=%s, build date=%s, build source=%s, version=%s, os=%s, arch=%s\n", commit, date, buildSource, version, runtime.GOOS, runtime.GOARCH)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
appConfig, err := config.NewAppConfig("lazygit", version, commit, date, debuggingFlag)
|
appConfig, err := config.NewAppConfig("lazygit", version, commit, date, buildSource, debuggingFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,12 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui"
|
"github.com/jesseduffield/lazygit/pkg/gui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||||
"github.com/jesseduffield/lazygit/pkg/updates"
|
"github.com/jesseduffield/lazygit/pkg/updates"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// App struct
|
// App struct
|
||||||
|
|
|
@ -7,9 +7,9 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
gitconfig "github.com/tcnksm/go-gitconfig"
|
gitconfig "github.com/tcnksm/go-gitconfig"
|
||||||
gogit "gopkg.in/src-d/go-git.v4"
|
gogit "gopkg.in/src-d/go-git.v4"
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,19 +2,24 @@ package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/shibukawa/configdir"
|
"github.com/shibukawa/configdir"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AppConfig contains the base configuration fields required for lazygit.
|
// AppConfig contains the base configuration fields required for lazygit.
|
||||||
type AppConfig struct {
|
type AppConfig struct {
|
||||||
Debug bool `long:"debug" env:"DEBUG" default:"false"`
|
Debug bool `long:"debug" env:"DEBUG" default:"false"`
|
||||||
Version string `long:"version" env:"VERSION" default:"unversioned"`
|
Version string `long:"version" env:"VERSION" default:"unversioned"`
|
||||||
Commit string `long:"commit" env:"COMMIT"`
|
Commit string `long:"commit" env:"COMMIT"`
|
||||||
BuildDate string `long:"build-date" env:"BUILD_DATE"`
|
BuildDate string `long:"build-date" env:"BUILD_DATE"`
|
||||||
Name string `long:"name" env:"NAME" default:"lazygit"`
|
Name string `long:"name" env:"NAME" default:"lazygit"`
|
||||||
UserConfig *viper.Viper
|
BuildSource string `long:"build-source" env:"BUILD_SOURCE" default:""`
|
||||||
|
UserConfig *viper.Viper
|
||||||
|
AppState *AppState
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppConfigurer interface allows individual app config structs to inherit Fields
|
// AppConfigurer interface allows individual app config structs to inherit Fields
|
||||||
|
@ -25,25 +30,37 @@ type AppConfigurer interface {
|
||||||
GetCommit() string
|
GetCommit() string
|
||||||
GetBuildDate() string
|
GetBuildDate() string
|
||||||
GetName() string
|
GetName() string
|
||||||
|
GetBuildSource() string
|
||||||
GetUserConfig() *viper.Viper
|
GetUserConfig() *viper.Viper
|
||||||
InsertToUserConfig(string, string) error
|
GetAppState() *AppState
|
||||||
|
WriteToUserConfig(string, string) error
|
||||||
|
SaveAppState() error
|
||||||
|
LoadAppState() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAppConfig makes a new app config
|
// NewAppConfig makes a new app config
|
||||||
func NewAppConfig(name, version, commit, date string, debuggingFlag *bool) (*AppConfig, error) {
|
func NewAppConfig(name, version, commit, date string, buildSource string, debuggingFlag *bool) (*AppConfig, error) {
|
||||||
userConfig, err := LoadUserConfig()
|
defaultConfig := getDefaultConfig()
|
||||||
|
userConfig, err := LoadConfig("config", defaultConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
appConfig := &AppConfig{
|
appConfig := &AppConfig{
|
||||||
Name: "lazygit",
|
Name: "lazygit",
|
||||||
Version: version,
|
Version: version,
|
||||||
Commit: commit,
|
Commit: commit,
|
||||||
BuildDate: date,
|
BuildDate: date,
|
||||||
Debug: *debuggingFlag,
|
Debug: *debuggingFlag,
|
||||||
UserConfig: userConfig,
|
BuildSource: buildSource,
|
||||||
|
UserConfig: userConfig,
|
||||||
|
AppState: &AppState{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := appConfig.LoadAppState(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return appConfig, nil
|
return appConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,73 +89,122 @@ func (c *AppConfig) GetName() string {
|
||||||
return c.Name
|
return c.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBuildSource returns the source of the build. For builds from goreleaser
|
||||||
|
// this will be binaryBuild
|
||||||
|
func (c *AppConfig) GetBuildSource() string {
|
||||||
|
return c.BuildSource
|
||||||
|
}
|
||||||
|
|
||||||
// GetUserConfig returns the user config
|
// GetUserConfig returns the user config
|
||||||
func (c *AppConfig) GetUserConfig() *viper.Viper {
|
func (c *AppConfig) GetUserConfig() *viper.Viper {
|
||||||
return c.UserConfig
|
return c.UserConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func newViper() (*viper.Viper, error) {
|
// GetAppState returns the app state
|
||||||
|
func (c *AppConfig) GetAppState() *AppState {
|
||||||
|
return c.AppState
|
||||||
|
}
|
||||||
|
|
||||||
|
func newViper(filename string) (*viper.Viper, error) {
|
||||||
v := viper.New()
|
v := viper.New()
|
||||||
v.SetConfigType("yaml")
|
v.SetConfigType("yaml")
|
||||||
v.SetConfigName("config")
|
v.SetConfigName(filename)
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadUserConfig gets the user's config
|
// LoadConfig gets the user's config
|
||||||
func LoadUserConfig() (*viper.Viper, error) {
|
func LoadConfig(filename string, defaults []byte) (*viper.Viper, error) {
|
||||||
v, err := newViper()
|
v, err := newViper(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if err = LoadDefaultConfig(v); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err = LoadUserConfigFromFile(v); err != nil {
|
if defaults != nil {
|
||||||
|
if err = LoadDefaults(v, defaults); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err = LoadAndMergeFile(v, filename+".yml"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadDefaultConfig loads in the defaults defined in this file
|
// LoadDefaults loads in the defaults defined in this file
|
||||||
func LoadDefaultConfig(v *viper.Viper) error {
|
func LoadDefaults(v *viper.Viper, defaults []byte) error {
|
||||||
defaults := getDefaultConfig()
|
|
||||||
return v.ReadConfig(bytes.NewBuffer(defaults))
|
return v.ReadConfig(bytes.NewBuffer(defaults))
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadUserConfigFromFile Loads the user config from their config file, creating
|
func prepareConfigFile(filename string) (string, error) {
|
||||||
// the file as an empty config if it does not exist
|
|
||||||
func LoadUserConfigFromFile(v *viper.Viper) error {
|
|
||||||
// chucking my name there is not for vanity purposes, the xdg spec (and that
|
// chucking my name there is not for vanity purposes, the xdg spec (and that
|
||||||
// function) requires a vendor name. May as well line up with github
|
// function) requires a vendor name. May as well line up with github
|
||||||
configDirs := configdir.New("jesseduffield", "lazygit")
|
configDirs := configdir.New("jesseduffield", "lazygit")
|
||||||
folder := configDirs.QueryFolderContainsFile("config.yml")
|
folder := configDirs.QueryFolderContainsFile(filename)
|
||||||
if folder == nil {
|
if folder == nil {
|
||||||
// create the file as an empty config and load it
|
// create the file as empty
|
||||||
folders := configDirs.QueryFolders(configdir.Global)
|
folders := configDirs.QueryFolders(configdir.Global)
|
||||||
if err := folders[0].WriteFile("config.yml", []byte{}); err != nil {
|
if err := folders[0].WriteFile(filename, []byte{}); err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
folder = configDirs.QueryFolderContainsFile("config.yml")
|
folder = configDirs.QueryFolderContainsFile(filename)
|
||||||
}
|
}
|
||||||
v.AddConfigPath(folder.Path)
|
return filepath.Join(folder.Path, filename), nil
|
||||||
return v.MergeInConfig()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InsertToUserConfig adds a key/value pair to the user's config and saves it
|
// LoadAndMergeFile Loads the config/state file, creating
|
||||||
func (c *AppConfig) InsertToUserConfig(key, value string) error {
|
// the file as an empty one if it does not exist
|
||||||
// making a new viper object so that we're not writing any defaults back
|
func LoadAndMergeFile(v *viper.Viper, filename string) error {
|
||||||
// to the user's config file
|
configPath, err := prepareConfigFile(filename)
|
||||||
v, err := newViper()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = LoadUserConfigFromFile(v); err != nil {
|
|
||||||
|
v.AddConfigPath(filepath.Dir(configPath))
|
||||||
|
return v.MergeInConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteToUserConfig adds a key/value pair to the user's config and saves it
|
||||||
|
func (c *AppConfig) WriteToUserConfig(key, value string) error {
|
||||||
|
// reloading the user config directly (without defaults) so that we're not
|
||||||
|
// writing any defaults back to the user's config
|
||||||
|
v, err := LoadConfig("config", nil)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
v.Set(key, value)
|
v.Set(key, value)
|
||||||
return v.WriteConfig()
|
return v.WriteConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveAppState marhsalls the AppState struct and writes it to the disk
|
||||||
|
func (c *AppConfig) SaveAppState() error {
|
||||||
|
marshalledAppState, err := yaml.Marshal(c.AppState)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
filepath, err := prepareConfigFile("state.yml")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutil.WriteFile(filepath, marshalledAppState, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AppConfig) LoadAppState() error {
|
||||||
|
filepath, err := prepareConfigFile("state.yml")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
appStateBytes, err := ioutil.ReadFile(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(appStateBytes) == 0 {
|
||||||
|
return yaml.Unmarshal(getDefaultAppState(), c.AppState)
|
||||||
|
}
|
||||||
|
return yaml.Unmarshal(appStateBytes, c.AppState)
|
||||||
|
}
|
||||||
|
|
||||||
func getDefaultConfig() []byte {
|
func getDefaultConfig() []byte {
|
||||||
return []byte(`
|
return []byte(`
|
||||||
gui:
|
gui:
|
||||||
|
@ -156,7 +222,23 @@ func getDefaultConfig() []byte {
|
||||||
# stuff relating to git
|
# stuff relating to git
|
||||||
os:
|
os:
|
||||||
# stuff relating to the OS
|
# stuff relating to the OS
|
||||||
|
update:
|
||||||
|
method: prompt # can be prompt | background | never
|
||||||
|
days: 7 # only applies for prompt/background update methods
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppState stores data between runs of the app like when the last update check
|
||||||
|
// was performed and which other repos have been checked out
|
||||||
|
type AppState struct {
|
||||||
|
LastUpdateCheck int64
|
||||||
|
RecentRepos []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDefaultAppState() []byte {
|
||||||
|
return []byte(`
|
||||||
|
lastUpdateCheck: 0
|
||||||
|
recentRepos: []
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
44
pkg/gui/app_status_manager.go
Normal file
44
pkg/gui/app_status_manager.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package gui
|
||||||
|
|
||||||
|
import "github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
|
||||||
|
type appStatus struct {
|
||||||
|
name string
|
||||||
|
statusType string
|
||||||
|
duration int
|
||||||
|
}
|
||||||
|
|
||||||
|
type statusManager struct {
|
||||||
|
statuses []appStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *statusManager) removeStatus(name string) {
|
||||||
|
newStatuses := []appStatus{}
|
||||||
|
for _, status := range m.statuses {
|
||||||
|
if status.name != name {
|
||||||
|
newStatuses = append(newStatuses, status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.statuses = newStatuses
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *statusManager) addWaitingStatus(name string) {
|
||||||
|
m.removeStatus(name)
|
||||||
|
newStatus := appStatus{
|
||||||
|
name: name,
|
||||||
|
statusType: "waiting",
|
||||||
|
duration: 0,
|
||||||
|
}
|
||||||
|
m.statuses = append([]appStatus{newStatus}, m.statuses...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *statusManager) getStatusString() string {
|
||||||
|
if len(m.statuses) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
topStatus := m.statuses[0]
|
||||||
|
if topStatus.statusType == "waiting" {
|
||||||
|
return topStatus.name + " " + utils.Loader()
|
||||||
|
}
|
||||||
|
return topStatus.name
|
||||||
|
}
|
|
@ -58,20 +58,30 @@ func (gui *Gui) getConfirmationPanelDimensions(g *gocui.Gui, prompt string) (int
|
||||||
|
|
||||||
func (gui *Gui) createPromptPanel(g *gocui.Gui, currentView *gocui.View, title string, handleConfirm func(*gocui.Gui, *gocui.View) error) error {
|
func (gui *Gui) createPromptPanel(g *gocui.Gui, currentView *gocui.View, title string, handleConfirm func(*gocui.Gui, *gocui.View) error) error {
|
||||||
gui.onNewPopupPanel()
|
gui.onNewPopupPanel()
|
||||||
// only need to fit one line
|
confirmationView, err := gui.prepareConfirmationPanel(currentView, title, "")
|
||||||
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(g, "")
|
if err != nil {
|
||||||
if confirmationView, err := g.SetView("confirmation", x0, y0, x1, y1, 0); err != nil {
|
return err
|
||||||
if err != gocui.ErrUnknownView {
|
}
|
||||||
return err
|
confirmationView.Editable = true
|
||||||
}
|
return gui.setKeyBindings(g, handleConfirm, nil)
|
||||||
|
}
|
||||||
|
|
||||||
confirmationView.Editable = true
|
func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt string) (*gocui.View, error) {
|
||||||
|
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(gui.g, prompt)
|
||||||
|
confirmationView, err := gui.g.SetView("confirmation", x0, y0, x1, y1, 0)
|
||||||
|
if err != nil {
|
||||||
|
if err != gocui.ErrUnknownView {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
confirmationView.Title = title
|
confirmationView.Title = title
|
||||||
confirmationView.FgColor = gocui.ColorWhite
|
confirmationView.FgColor = gocui.ColorWhite
|
||||||
gui.switchFocus(g, currentView, confirmationView)
|
|
||||||
return gui.setKeyBindings(g, handleConfirm, nil)
|
|
||||||
}
|
}
|
||||||
return nil
|
confirmationView.Clear()
|
||||||
|
|
||||||
|
if err := gui.switchFocus(gui.g, currentView, confirmationView); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return confirmationView, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) onNewPopupPanel() {
|
func (gui *Gui) onNewPopupPanel() {
|
||||||
|
@ -93,18 +103,15 @@ func (gui *Gui) createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, t
|
||||||
gui.Log.Error(errMessage)
|
gui.Log.Error(errMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(g, prompt)
|
confirmationView, err := gui.prepareConfirmationPanel(currentView, title, prompt)
|
||||||
if confirmationView, err := g.SetView("confirmation", x0, y0, x1, y1, 0); err != nil {
|
if err != nil {
|
||||||
if err != gocui.ErrUnknownView {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
confirmationView.Title = title
|
|
||||||
confirmationView.FgColor = gocui.ColorWhite
|
|
||||||
gui.renderString(g, "confirmation", prompt)
|
|
||||||
gui.switchFocus(g, currentView, confirmationView)
|
|
||||||
return gui.setKeyBindings(g, handleConfirm, handleClose)
|
|
||||||
}
|
}
|
||||||
return nil
|
confirmationView.Editable = false
|
||||||
|
if err := gui.renderString(g, "confirmation", prompt); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return gui.setKeyBindings(g, handleConfirm, handleClose)
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -131,7 +138,9 @@ func (gui *Gui) setKeyBindings(g *gocui.Gui, handleConfirm, handleClose func(*go
|
||||||
"keyBindConfirm": "enter",
|
"keyBindConfirm": "enter",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
gui.renderString(g, "options", actions)
|
if err := gui.renderString(g, "options", actions); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := g.SetKeybinding("confirmation", gocui.KeyEnter, gocui.ModNone, gui.wrappedConfirmationFunction(handleConfirm)); err != nil {
|
if err := g.SetKeybinding("confirmation", gocui.KeyEnter, gocui.ModNone, gui.wrappedConfirmationFunction(handleConfirm)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
135
pkg/gui/gui.go
135
pkg/gui/gui.go
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||||
"github.com/jesseduffield/lazygit/pkg/updates"
|
"github.com/jesseduffield/lazygit/pkg/updates"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -56,16 +57,17 @@ type Teml i18n.Teml
|
||||||
|
|
||||||
// Gui wraps the gocui Gui object which handles rendering and events
|
// Gui wraps the gocui Gui object which handles rendering and events
|
||||||
type Gui struct {
|
type Gui struct {
|
||||||
g *gocui.Gui
|
g *gocui.Gui
|
||||||
Log *logrus.Logger
|
Log *logrus.Logger
|
||||||
GitCommand *commands.GitCommand
|
GitCommand *commands.GitCommand
|
||||||
OSCommand *commands.OSCommand
|
OSCommand *commands.OSCommand
|
||||||
SubProcess *exec.Cmd
|
SubProcess *exec.Cmd
|
||||||
State guiState
|
State guiState
|
||||||
Config config.AppConfigurer
|
Config config.AppConfigurer
|
||||||
Tr *i18n.Localizer
|
Tr *i18n.Localizer
|
||||||
Errors SentinelErrors
|
Errors SentinelErrors
|
||||||
Updater *updates.Updater
|
Updater *updates.Updater
|
||||||
|
statusManager *statusManager
|
||||||
}
|
}
|
||||||
|
|
||||||
type guiState struct {
|
type guiState struct {
|
||||||
|
@ -98,13 +100,14 @@ func NewGui(log *logrus.Logger, gitCommand *commands.GitCommand, oSCommand *comm
|
||||||
}
|
}
|
||||||
|
|
||||||
gui := &Gui{
|
gui := &Gui{
|
||||||
Log: log,
|
Log: log,
|
||||||
GitCommand: gitCommand,
|
GitCommand: gitCommand,
|
||||||
OSCommand: oSCommand,
|
OSCommand: oSCommand,
|
||||||
State: initialState,
|
State: initialState,
|
||||||
Config: config,
|
Config: config,
|
||||||
Tr: tr,
|
Tr: tr,
|
||||||
Updater: updater,
|
Updater: updater,
|
||||||
|
statusManager: &statusManager{},
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.GenerateSentinelErrors()
|
gui.GenerateSentinelErrors()
|
||||||
|
@ -141,13 +144,6 @@ func max(a, b int) int {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) setAppStatus(status string) error {
|
|
||||||
if err := gui.renderString(gui.g, "appStatus", status); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// layout is called for every screen re-render e.g. when the screen is resized
|
// layout is called for every screen re-render e.g. when the screen is resized
|
||||||
func (gui *Gui) layout(g *gocui.Gui) error {
|
func (gui *Gui) layout(g *gocui.Gui) error {
|
||||||
g.Highlight = true
|
g.Highlight = true
|
||||||
|
@ -162,10 +158,10 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||||
minimumHeight := 16
|
minimumHeight := 16
|
||||||
minimumWidth := 10
|
minimumWidth := 10
|
||||||
|
|
||||||
appStatusView, _ := g.View("appStatus")
|
appStatus := gui.statusManager.getStatusString()
|
||||||
appStatusOptionsBoundary := -2
|
appStatusOptionsBoundary := 0
|
||||||
if appStatusView != nil && len(appStatusView.Buffer()) > 2 {
|
if appStatus != "" {
|
||||||
appStatusOptionsBoundary = len(appStatusView.Buffer()) + 2
|
appStatusOptionsBoundary = len(appStatus) + 2
|
||||||
}
|
}
|
||||||
|
|
||||||
panelSpacing := 1
|
panelSpacing := 1
|
||||||
|
@ -257,7 +253,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||||
|
|
||||||
if gui.getCommitMessageView(g) == nil {
|
if gui.getCommitMessageView(g) == nil {
|
||||||
// doesn't matter where this view starts because it will be hidden
|
// doesn't matter where this view starts because it will be hidden
|
||||||
if commitMessageView, err := g.SetView("commitMessage", 0, 0, width, height, 0); err != nil {
|
if commitMessageView, err := g.SetView("commitMessage", 0, 0, width/2, height/2, 0); err != nil {
|
||||||
if err != gocui.ErrUnknownView {
|
if err != gocui.ErrUnknownView {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -268,18 +264,14 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
appStatusLeft := -1
|
if appStatusView, err := g.SetView("appStatus", -1, optionsTop, width, optionsTop+2, 0); err != nil {
|
||||||
if appStatusOptionsBoundary < 2 {
|
|
||||||
appStatusLeft = -5
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, err := g.SetView("appStatus", appStatusLeft, optionsTop, appStatusOptionsBoundary, optionsTop+2, 0); err != nil {
|
|
||||||
if err != gocui.ErrUnknownView {
|
if err != gocui.ErrUnknownView {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.BgColor = gocui.ColorDefault
|
appStatusView.BgColor = gocui.ColorDefault
|
||||||
v.FgColor = gocui.ColorCyan
|
appStatusView.FgColor = gocui.ColorCyan
|
||||||
v.Frame = false
|
appStatusView.Frame = false
|
||||||
|
g.SetViewOnBottom("appStatus")
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := g.SetView("version", optionsVersionBoundary-1, optionsTop, width, optionsTop+2, 0); err != nil {
|
if v, err := g.SetView("version", optionsVersionBoundary-1, optionsTop, width, optionsTop+2, 0); err != nil {
|
||||||
|
@ -293,9 +285,9 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// gui.Updater.CheckForNewUpdate(gui.onUpdateCheckFinish)
|
// these are only called once (it's a place to put all the things you want
|
||||||
|
// to happen on startup after the screen is first rendered)
|
||||||
// these are only called once
|
gui.Updater.CheckForNewUpdate(gui.onUpdateCheckFinish, false)
|
||||||
gui.handleFileSelect(g, filesView)
|
gui.handleFileSelect(g, filesView)
|
||||||
gui.refreshFiles(g)
|
gui.refreshFiles(g)
|
||||||
gui.refreshBranches(g)
|
gui.refreshBranches(g)
|
||||||
|
@ -313,32 +305,42 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||||
|
|
||||||
func (gui *Gui) onUpdateFinish(err error) error {
|
func (gui *Gui) onUpdateFinish(err error) error {
|
||||||
gui.State.Updating = false
|
gui.State.Updating = false
|
||||||
gui.setAppStatus("")
|
gui.statusManager.removeStatus("updating")
|
||||||
if err != nil {
|
if err := gui.renderString(gui.g, "appStatus", ""); err != nil {
|
||||||
gui.createErrorPanel(gui.g, "Update failed: "+err.Error())
|
return err
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return gui.createErrorPanel(gui.g, "Update failed: "+err.Error())
|
||||||
}
|
}
|
||||||
// TODO: on attempted quit, if downloading is true, ask if sure the user wants to quit
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) onUpdateCheckFinish(newVersion string, err error) error {
|
func (gui *Gui) onUpdateCheckFinish(newVersion string, err error) error {
|
||||||
newVersion = "v0.1.72"
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// ignoring the error for now
|
// ignoring the error for now so that I'm not annoying users
|
||||||
|
gui.Log.Error(err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if newVersion == "" {
|
if newVersion == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if gui.Config.GetUserConfig().Get("update.method") == "background" {
|
||||||
|
gui.startUpdating(newVersion)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
title := "New version available!"
|
title := "New version available!"
|
||||||
message := "Download latest version? (enter/esc)"
|
message := "Download latest version? (enter/esc)"
|
||||||
// TODO: support nil view in createConfirmationPanel or always go back to filesPanel when there is no previous view
|
currentView := gui.g.CurrentView()
|
||||||
return gui.createConfirmationPanel(gui.g, nil, title, message, func(g *gocui.Gui, v *gocui.View) error {
|
return gui.createConfirmationPanel(gui.g, currentView, title, message, func(g *gocui.Gui, v *gocui.View) error {
|
||||||
gui.State.Updating = true
|
gui.startUpdating(newVersion)
|
||||||
gui.setAppStatus("updating...")
|
|
||||||
gui.Updater.Update(newVersion, gui.onUpdateFinish)
|
|
||||||
return nil
|
return nil
|
||||||
}, nil) // TODO: set config value saying not to check for another while if user hits escape
|
}, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) startUpdating(newVersion string) {
|
||||||
|
gui.State.Updating = true
|
||||||
|
gui.statusManager.addWaitingStatus("updating")
|
||||||
|
gui.Updater.Update(newVersion, gui.onUpdateFinish)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) fetch(g *gocui.Gui) error {
|
func (gui *Gui) fetch(g *gocui.Gui) error {
|
||||||
|
@ -348,22 +350,26 @@ func (gui *Gui) fetch(g *gocui.Gui) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) updateLoader(g *gocui.Gui) error {
|
func (gui *Gui) updateLoader(g *gocui.Gui) error {
|
||||||
viewNames := []string{"confirmation", "appStatus"}
|
if view, _ := g.View("confirmation"); view != nil {
|
||||||
for _, viewName := range viewNames {
|
content := gui.trimmedContent(view)
|
||||||
if view, _ := g.View(viewName); view != nil {
|
if strings.Contains(content, "...") {
|
||||||
content := gui.trimmedContent(view)
|
staticContent := strings.Split(content, "...")[0] + "..."
|
||||||
if strings.Contains(content, "...") {
|
if err := gui.renderString(g, "confirmation", staticContent+" "+utils.Loader()); err != nil {
|
||||||
staticContent := strings.Split(content, "...")[0] + "..."
|
return err
|
||||||
if viewName == "appStatus" {
|
|
||||||
// panic(staticContent + " " + gui.loader())
|
|
||||||
}
|
|
||||||
gui.renderString(g, viewName, staticContent+" "+gui.loader())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) renderAppStatus(g *gocui.Gui) error {
|
||||||
|
appStatus := gui.statusManager.getStatusString()
|
||||||
|
if appStatus != "" {
|
||||||
|
return gui.renderString(gui.g, "appStatus", appStatus)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (gui *Gui) goEvery(g *gocui.Gui, interval time.Duration, function func(*gocui.Gui) error) {
|
func (gui *Gui) goEvery(g *gocui.Gui, interval time.Duration, function func(*gocui.Gui) error) {
|
||||||
go func() {
|
go func() {
|
||||||
for range time.Tick(interval) {
|
for range time.Tick(interval) {
|
||||||
|
@ -396,7 +402,8 @@ func (gui *Gui) Run() error {
|
||||||
|
|
||||||
gui.goEvery(g, time.Second*60, gui.fetch)
|
gui.goEvery(g, time.Second*60, gui.fetch)
|
||||||
gui.goEvery(g, time.Second*10, gui.refreshFiles)
|
gui.goEvery(g, time.Second*10, gui.refreshFiles)
|
||||||
gui.goEvery(g, time.Millisecond*10, gui.updateLoader)
|
gui.goEvery(g, time.Millisecond*50, gui.updateLoader)
|
||||||
|
gui.goEvery(g, time.Millisecond*50, gui.renderAppStatus)
|
||||||
|
|
||||||
g.SetManagerFunc(gui.layout)
|
g.SetManagerFunc(gui.layout)
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ func (gui *Gui) keybindings(g *gocui.Gui) error {
|
||||||
{ViewName: "", Key: 'R', Modifier: gocui.ModNone, Handler: gui.handleRefresh},
|
{ViewName: "", Key: 'R', Modifier: gocui.ModNone, Handler: gui.handleRefresh},
|
||||||
{ViewName: "status", Key: 'e', Modifier: gocui.ModNone, Handler: gui.handleEditConfig},
|
{ViewName: "status", Key: 'e', Modifier: gocui.ModNone, Handler: gui.handleEditConfig},
|
||||||
{ViewName: "status", Key: 'o', Modifier: gocui.ModNone, Handler: gui.handleOpenConfig},
|
{ViewName: "status", Key: 'o', Modifier: gocui.ModNone, Handler: gui.handleOpenConfig},
|
||||||
|
{ViewName: "status", Key: 'u', Modifier: gocui.ModNone, Handler: gui.handleCheckForUpdate},
|
||||||
{ViewName: "files", Key: 'c', Modifier: gocui.ModNone, Handler: gui.handleCommitPress},
|
{ViewName: "files", Key: 'c', Modifier: gocui.ModNone, Handler: gui.handleCommitPress},
|
||||||
{ViewName: "files", Key: 'C', Modifier: gocui.ModNone, Handler: gui.handleCommitEditorPress},
|
{ViewName: "files", Key: 'C', Modifier: gocui.ModNone, Handler: gui.handleCommitEditorPress},
|
||||||
{ViewName: "files", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: gui.handleFilePress},
|
{ViewName: "files", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: gui.handleFilePress},
|
||||||
|
|
|
@ -45,9 +45,15 @@ func (gui *Gui) renderStatusOptions(g *gocui.Gui) error {
|
||||||
return gui.renderOptionsMap(g, map[string]string{
|
return gui.renderOptionsMap(g, map[string]string{
|
||||||
"o": gui.Tr.SLocalize("OpenConfig"),
|
"o": gui.Tr.SLocalize("OpenConfig"),
|
||||||
"e": gui.Tr.SLocalize("EditConfig"),
|
"e": gui.Tr.SLocalize("EditConfig"),
|
||||||
|
"u": gui.Tr.SLocalize("CheckForUpdate"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleCheckForUpdate(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
gui.Updater.CheckForNewUpdate(gui.onUpdateCheckFinish, true)
|
||||||
|
return gui.createMessagePanel(gui.g, v, "Checking for updates", "Checking for updates...")
|
||||||
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
|
||||||
dashboardString := fmt.Sprintf(
|
dashboardString := fmt.Sprintf(
|
||||||
"%s\n\n%s\n\n%s\n\n%s\n\n%s",
|
"%s\n\n%s\n\n%s\n\n%s\n\n%s",
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
@ -222,9 +221,6 @@ func (gui *Gui) renderString(g *gocui.Gui, viewName, s string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if viewName == "appStatus" {
|
|
||||||
gui.Log.Info(s)
|
|
||||||
}
|
|
||||||
v.Clear()
|
v.Clear()
|
||||||
output := string(bom.Clean([]byte(s)))
|
output := string(bom.Clean([]byte(s)))
|
||||||
output = utils.NormalizeLinefeeds(output)
|
output = utils.NormalizeLinefeeds(output)
|
||||||
|
@ -248,14 +244,6 @@ func (gui *Gui) renderOptionsMap(g *gocui.Gui, optionsMap map[string]string) err
|
||||||
return gui.renderString(g, "options", gui.optionsMapToString(optionsMap))
|
return gui.renderString(g, "options", gui.optionsMapToString(optionsMap))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) loader() string {
|
|
||||||
characters := "|/-\\"
|
|
||||||
now := time.Now()
|
|
||||||
nanos := now.UnixNano()
|
|
||||||
index := nanos / 50000000 % int64(len(characters))
|
|
||||||
return characters[index : index+1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: refactor properly
|
// TODO: refactor properly
|
||||||
func (gui *Gui) getFilesView(g *gocui.Gui) *gocui.View {
|
func (gui *Gui) getFilesView(g *gocui.Gui) *gocui.View {
|
||||||
v, _ := g.View("files")
|
v, _ := g.View("files")
|
||||||
|
|
|
@ -312,6 +312,9 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "ForcePushPrompt",
|
ID: "ForcePushPrompt",
|
||||||
Other: "Your branch has diverged from the remote branch. Press 'esc' to cancel, or 'enter' to force push.",
|
Other: "Your branch has diverged from the remote branch. Press 'esc' to cancel, or 'enter' to force push.",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "CheckForUpdate",
|
||||||
|
Other: "Check for update",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kardianos/osext"
|
"github.com/kardianos/osext"
|
||||||
|
@ -21,10 +22,9 @@ import (
|
||||||
|
|
||||||
// Update checks for updates and does updates
|
// Update checks for updates and does updates
|
||||||
type Updater struct {
|
type Updater struct {
|
||||||
LastChecked string
|
Log *logrus.Entry
|
||||||
Log *logrus.Entry
|
Config config.AppConfigurer
|
||||||
Config config.AppConfigurer
|
OSCommand *commands.OSCommand
|
||||||
OSCommand *commands.OSCommand
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updater implements the check and update methods
|
// Updater implements the check and update methods
|
||||||
|
@ -42,16 +42,14 @@ func NewUpdater(log *logrus.Logger, config config.AppConfigurer, osCommand *comm
|
||||||
contextLogger := log.WithField("context", "updates")
|
contextLogger := log.WithField("context", "updates")
|
||||||
|
|
||||||
updater := &Updater{
|
updater := &Updater{
|
||||||
LastChecked: "today",
|
Log: contextLogger,
|
||||||
Log: contextLogger,
|
Config: config,
|
||||||
Config: config,
|
OSCommand: osCommand,
|
||||||
OSCommand: osCommand,
|
|
||||||
}
|
}
|
||||||
return updater, nil
|
return updater, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Updater) getLatestVersionNumber() (string, error) {
|
func (u *Updater) getLatestVersionNumber() (string, error) {
|
||||||
time.Sleep(5)
|
|
||||||
req, err := http.NewRequest("GET", projectUrl+"/releases/latest", nil)
|
req, err := http.NewRequest("GET", projectUrl+"/releases/latest", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -76,21 +74,41 @@ func (u *Updater) getLatestVersionNumber() (string, error) {
|
||||||
return dat["tag_name"].(string), nil
|
return dat["tag_name"].(string), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *Updater) RecordLastUpdateCheck() error {
|
||||||
|
u.Config.GetAppState().LastUpdateCheck = time.Now().Unix()
|
||||||
|
return u.Config.SaveAppState()
|
||||||
|
}
|
||||||
|
|
||||||
|
// expecting version to be of the form `v12.34.56`
|
||||||
|
func (u *Updater) majorVersionDiffers(oldVersion, newVersion string) bool {
|
||||||
|
return strings.Split(oldVersion, ".")[0] != strings.Split(newVersion, ".")[0]
|
||||||
|
}
|
||||||
|
|
||||||
func (u *Updater) checkForNewUpdate() (string, error) {
|
func (u *Updater) checkForNewUpdate() (string, error) {
|
||||||
u.Log.Info("Checking for an updated version")
|
u.Log.Info("Checking for an updated version")
|
||||||
// if u.Config.GetVersion() == "unversioned" {
|
if u.Config.GetVersion() == "unversioned" {
|
||||||
// u.Log.Info("Current version is not built from an official release so we won't check for an update")
|
u.Log.Info("Current version is not built from an official release so we won't check for an update")
|
||||||
// return "", nil
|
return "", nil
|
||||||
// }
|
}
|
||||||
newVersion, err := u.getLatestVersionNumber()
|
newVersion, err := u.getLatestVersionNumber()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
u.Log.Info("Current version is " + u.Config.GetVersion())
|
u.Log.Info("Current version is " + u.Config.GetVersion())
|
||||||
u.Log.Info("New version is " + newVersion)
|
u.Log.Info("New version is " + newVersion)
|
||||||
// if newVersion == u.Config.GetVersion() {
|
|
||||||
// return "", nil
|
if err := u.RecordLastUpdateCheck(); err != nil {
|
||||||
// }
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if newVersion == u.Config.GetVersion() {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.majorVersionDiffers(u.Config.GetVersion(), newVersion) {
|
||||||
|
u.Log.Info("New version has non-backwards compatible changes.")
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
rawUrl, err := u.getBinaryUrl(newVersion)
|
rawUrl, err := u.getBinaryUrl(newVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -102,11 +120,16 @@ func (u *Updater) checkForNewUpdate() (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
u.Log.Info("Verified resource is available, ready to update")
|
u.Log.Info("Verified resource is available, ready to update")
|
||||||
|
|
||||||
return newVersion, nil
|
return newVersion, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckForNewUpdate checks if there is an available update
|
// CheckForNewUpdate checks if there is an available update
|
||||||
func (u *Updater) CheckForNewUpdate(onFinish func(string, error) error) {
|
func (u *Updater) CheckForNewUpdate(onFinish func(string, error) error, userRequested bool) {
|
||||||
|
if !userRequested && u.skipUpdateCheck() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
newVersion, err := u.checkForNewUpdate()
|
newVersion, err := u.checkForNewUpdate()
|
||||||
if err = onFinish(newVersion, err); err != nil {
|
if err = onFinish(newVersion, err); err != nil {
|
||||||
|
@ -115,6 +138,23 @@ func (u *Updater) CheckForNewUpdate(onFinish func(string, error) error) {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *Updater) skipUpdateCheck() bool {
|
||||||
|
if u.Config.GetBuildSource() != "buildBinary" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
userConfig := u.Config.GetUserConfig()
|
||||||
|
if userConfig.Get("update.method") == "never" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTimestamp := time.Now().Unix()
|
||||||
|
lastUpdateCheck := u.Config.GetAppState().LastUpdateCheck
|
||||||
|
days := userConfig.GetInt64("update.days")
|
||||||
|
|
||||||
|
return (currentTimestamp-lastUpdateCheck)/(60*60*24) < days
|
||||||
|
}
|
||||||
|
|
||||||
func (u *Updater) mappedOs(os string) string {
|
func (u *Updater) mappedOs(os string) string {
|
||||||
osMap := map[string]string{
|
osMap := map[string]string{
|
||||||
"darwin": "Darwin",
|
"darwin": "Darwin",
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
)
|
)
|
||||||
|
@ -81,3 +82,11 @@ func GetProjectRoot() string {
|
||||||
}
|
}
|
||||||
return strings.Split(dir, "lazygit")[0] + "lazygit"
|
return strings.Split(dir, "lazygit")[0] + "lazygit"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Loader() string {
|
||||||
|
characters := "|/-\\"
|
||||||
|
now := time.Now()
|
||||||
|
nanos := now.UnixNano()
|
||||||
|
index := nanos / 50000000 % int64(len(characters))
|
||||||
|
return characters[index : index+1]
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue