mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-12 12:55:47 +02:00
Support per-repo config files
For now we only support .git/lazygit.yml; in the future we would also like to support ./.lazygit.yml, but that one will need a trust prompt as it could be versioned, which adds quite a bit of complexity, so we leave that for later. We do, however, support config files in parent directories (all the way up to the root directory). This makes it possible to add a config file that applies to multiple repos at once. Useful if you want to set different options for all your work repos vs. all your open-source repos, for instance.
This commit is contained in:
parent
d6d48f2866
commit
74ed1ac584
2 changed files with 95 additions and 25 deletions
|
@ -21,6 +21,7 @@ type AppConfig struct {
|
||||||
name string `long:"name" env:"NAME" default:"lazygit"`
|
name string `long:"name" env:"NAME" default:"lazygit"`
|
||||||
buildSource string `long:"build-source" env:"BUILD_SOURCE" default:""`
|
buildSource string `long:"build-source" env:"BUILD_SOURCE" default:""`
|
||||||
userConfig *UserConfig
|
userConfig *UserConfig
|
||||||
|
globalUserConfigFiles []*ConfigFile
|
||||||
userConfigFiles []*ConfigFile
|
userConfigFiles []*ConfigFile
|
||||||
userConfigDir string
|
userConfigDir string
|
||||||
tempDir string
|
tempDir string
|
||||||
|
@ -38,6 +39,7 @@ type AppConfigurer interface {
|
||||||
GetUserConfig() *UserConfig
|
GetUserConfig() *UserConfig
|
||||||
GetUserConfigPaths() []string
|
GetUserConfigPaths() []string
|
||||||
GetUserConfigDir() string
|
GetUserConfigDir() string
|
||||||
|
ReloadUserConfigForRepo(repoConfigFiles []*ConfigFile) error
|
||||||
GetTempDir() string
|
GetTempDir() string
|
||||||
|
|
||||||
GetAppState() *AppState
|
GetAppState() *AppState
|
||||||
|
@ -49,11 +51,13 @@ type ConfigFilePolicy int
|
||||||
const (
|
const (
|
||||||
ConfigFilePolicyCreateIfMissing ConfigFilePolicy = iota
|
ConfigFilePolicyCreateIfMissing ConfigFilePolicy = iota
|
||||||
ConfigFilePolicyErrorIfMissing
|
ConfigFilePolicyErrorIfMissing
|
||||||
|
ConfigFilePolicySkipIfMissing
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigFile struct {
|
type ConfigFile struct {
|
||||||
Path string
|
Path string
|
||||||
Policy ConfigFilePolicy
|
Policy ConfigFilePolicy
|
||||||
|
exists bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAppConfig makes a new app config
|
// NewAppConfig makes a new app config
|
||||||
|
@ -114,6 +118,7 @@ func NewAppConfig(
|
||||||
debug: debuggingFlag,
|
debug: debuggingFlag,
|
||||||
buildSource: buildSource,
|
buildSource: buildSource,
|
||||||
userConfig: userConfig,
|
userConfig: userConfig,
|
||||||
|
globalUserConfigFiles: configFiles,
|
||||||
userConfigFiles: configFiles,
|
userConfigFiles: configFiles,
|
||||||
userConfigDir: configDir,
|
userConfigDir: configDir,
|
||||||
tempDir: tempDir,
|
tempDir: tempDir,
|
||||||
|
@ -141,7 +146,10 @@ func loadUserConfigWithDefaults(configFiles []*ConfigFile) (*UserConfig, error)
|
||||||
func loadUserConfig(configFiles []*ConfigFile, base *UserConfig) (*UserConfig, error) {
|
func loadUserConfig(configFiles []*ConfigFile, base *UserConfig) (*UserConfig, error) {
|
||||||
for _, configFile := range configFiles {
|
for _, configFile := range configFiles {
|
||||||
path := configFile.Path
|
path := configFile.Path
|
||||||
if _, err := os.Stat(path); err != nil {
|
_, err := os.Stat(path)
|
||||||
|
if err == nil {
|
||||||
|
configFile.exists = true
|
||||||
|
} else {
|
||||||
if !os.IsNotExist(err) {
|
if !os.IsNotExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -150,6 +158,10 @@ func loadUserConfig(configFiles []*ConfigFile, base *UserConfig) (*UserConfig, e
|
||||||
case ConfigFilePolicyErrorIfMissing:
|
case ConfigFilePolicyErrorIfMissing:
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
||||||
|
case ConfigFilePolicySkipIfMissing:
|
||||||
|
configFile.exists = false
|
||||||
|
continue
|
||||||
|
|
||||||
case ConfigFilePolicyCreateIfMissing:
|
case ConfigFilePolicyCreateIfMissing:
|
||||||
file, err := os.Create(path)
|
file, err := os.Create(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -160,6 +172,8 @@ func loadUserConfig(configFiles []*ConfigFile, base *UserConfig) (*UserConfig, e
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
file.Close()
|
file.Close()
|
||||||
|
|
||||||
|
configFile.exists = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,8 +274,8 @@ func (c *AppConfig) GetAppState() *AppState {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AppConfig) GetUserConfigPaths() []string {
|
func (c *AppConfig) GetUserConfigPaths() []string {
|
||||||
return lo.Map(c.userConfigFiles, func(f *ConfigFile, _ int) string {
|
return lo.FilterMap(c.userConfigFiles, func(f *ConfigFile, _ int) (string, bool) {
|
||||||
return f.Path
|
return f.Path, f.exists
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,6 +283,18 @@ func (c *AppConfig) GetUserConfigDir() string {
|
||||||
return c.userConfigDir
|
return c.userConfigDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *AppConfig) ReloadUserConfigForRepo(repoConfigFiles []*ConfigFile) error {
|
||||||
|
configFiles := append(c.globalUserConfigFiles, repoConfigFiles...)
|
||||||
|
userConfig, err := loadUserConfigWithDefaults(configFiles)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.userConfig = userConfig
|
||||||
|
c.userConfigFiles = configFiles
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *AppConfig) GetTempDir() string {
|
func (c *AppConfig) GetTempDir() string {
|
||||||
return c.tempDir
|
return c.tempDir
|
||||||
}
|
}
|
||||||
|
@ -365,7 +391,7 @@ func loadAppState() (*AppState, error) {
|
||||||
// SaveGlobalUserConfig saves the UserConfig back to disk. This is only used in
|
// SaveGlobalUserConfig saves the UserConfig back to disk. This is only used in
|
||||||
// integration tests, so we are a bit sloppy with error handling.
|
// integration tests, so we are a bit sloppy with error handling.
|
||||||
func (c *AppConfig) SaveGlobalUserConfig() {
|
func (c *AppConfig) SaveGlobalUserConfig() {
|
||||||
if len(c.userConfigFiles) != 1 {
|
if len(c.globalUserConfigFiles) != 1 {
|
||||||
panic("expected exactly one global user config file")
|
panic("expected exactly one global user config file")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,7 +400,7 @@ func (c *AppConfig) SaveGlobalUserConfig() {
|
||||||
log.Fatalf("error marshalling user config: %v", err)
|
log.Fatalf("error marshalling user config: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.WriteFile(c.userConfigFiles[0].Path, yamlContent, 0o644)
|
err = os.WriteFile(c.globalUserConfigFiles[0].Path, yamlContent, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error saving user config: %v", err)
|
log.Fatalf("error saving user config: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -307,6 +308,16 @@ func (gui *Gui) onNewRepo(startArgs appTypes.StartArgs, contextKey types.Context
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = gui.Config.ReloadUserConfigForRepo(gui.getPerRepoConfigFiles())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = gui.onUserConfigLoaded()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
contextToPush := gui.resetState(startArgs)
|
contextToPush := gui.resetState(startArgs)
|
||||||
|
|
||||||
gui.resetHelpersAndControllers()
|
gui.resetHelpersAndControllers()
|
||||||
|
@ -342,6 +353,39 @@ func (gui *Gui) onNewRepo(startArgs appTypes.StartArgs, contextKey types.Context
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) getPerRepoConfigFiles() []*config.ConfigFile {
|
||||||
|
repoConfigFiles := []*config.ConfigFile{
|
||||||
|
// TODO: add filepath.Join(gui.git.RepoPaths.RepoPath(), ".lazygit.yml"),
|
||||||
|
// with trust prompt
|
||||||
|
{
|
||||||
|
Path: filepath.Join(gui.git.RepoPaths.RepoGitDirPath(), "lazygit.yml"),
|
||||||
|
Policy: config.ConfigFilePolicySkipIfMissing,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
prevDir := gui.c.Git().RepoPaths.RepoPath()
|
||||||
|
dir := filepath.Dir(prevDir)
|
||||||
|
for dir != prevDir {
|
||||||
|
repoConfigFiles = utils.Prepend(repoConfigFiles, &config.ConfigFile{
|
||||||
|
Path: filepath.Join(dir, ".lazygit.yml"),
|
||||||
|
Policy: config.ConfigFilePolicySkipIfMissing,
|
||||||
|
})
|
||||||
|
prevDir = dir
|
||||||
|
dir = filepath.Dir(dir)
|
||||||
|
}
|
||||||
|
return repoConfigFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) onUserConfigLoaded() error {
|
||||||
|
userConfig := gui.Config.GetUserConfig()
|
||||||
|
gui.Common.SetUserConfig(userConfig)
|
||||||
|
|
||||||
|
gui.setColorScheme()
|
||||||
|
gui.configureViewProperties()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// resetState reuses the repo state from our repo state map, if the repo was
|
// resetState reuses the repo state from our repo state map, if the repo was
|
||||||
// open before; otherwise it creates a new one.
|
// open before; otherwise it creates a new one.
|
||||||
func (gui *Gui) resetState(startArgs appTypes.StartArgs) types.Context {
|
func (gui *Gui) resetState(startArgs appTypes.StartArgs) types.Context {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue