Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ func (c *Config) Set(keys []string, value string) {
}

func (c *Config) deepCopy() *Config {
c.mu.RLock()
defer c.mu.RUnlock()
return ReadFromString(c.entries.String())
}

Expand All @@ -145,6 +147,28 @@ var Read = func(fallback *Config) (*Config, error) {
return cfg, loadErr
}

// Reload re-reads the gh configuration files from disk and refreshes the
// configuration returned by Read in place. The cached *Config keeps the same
// pointer, so every existing holder of it observes the new values.
//
// Any in-memory configuration changes that have not been written to disk are
// discarded by the refresh. Callers that need such changes preserved must Write
// them before calling Reload.
var Reload = func(fallback *Config) error {
cached, err := Read(fallback)
if err != nil {
return err
}
fresh, err := load(generalConfigFile(), hostsConfigFile(), fallback)
if err != nil {
return err
}
cached.mu.Lock()
defer cached.mu.Unlock()
cached.entries = fresh.entries
return nil
}

// ReadFromString takes a yaml string and returns a Config.
func ReadFromString(str string) *Config {
m, _ := mapFromString(str)
Expand Down
45 changes: 45 additions & 0 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"
"path/filepath"
"runtime"
"sync"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -290,6 +291,50 @@ func TestCacheDir(t *testing.T) {

}

func TestReadAndReload(t *testing.T) {
once = sync.Once{}
cfg, loadErr = nil, nil
t.Cleanup(func() {
once = sync.Once{}
cfg, loadErr = nil, nil
})

dir := t.TempDir()
t.Setenv("GH_CONFIG_DIR", dir)
path := filepath.Join(dir, "config.yml")
require.NoError(t, os.WriteFile(path, []byte("editor: vim\n"), 0o600))

c, err := Read(nil)
require.NoError(t, err)
editor, err := c.Get([]string{"editor"})
require.NoError(t, err)
assert.Equal(t, "vim", editor)

require.NoError(t, os.WriteFile(path, []byte("editor: nano\n"), 0o600))

// Read still serves the cached value until an explicit Reload.
c, err = Read(nil)
require.NoError(t, err)
editor, err = c.Get([]string{"editor"})
require.NoError(t, err)
assert.Equal(t, "vim", editor)

require.NoError(t, Reload(nil))

// The previously obtained pointer observes the refreshed value in place.
editor, err = c.Get([]string{"editor"})
require.NoError(t, err)
assert.Equal(t, "nano", editor)

// Read returns the same, refreshed *Config pointer.
reread, err := Read(nil)
require.NoError(t, err)
assert.Same(t, c, reread)
editor, err = reread.Get([]string{"editor"})
require.NoError(t, err)
assert.Equal(t, "nano", editor)
}

func TestLoad(t *testing.T) {
tempDir := t.TempDir()
globalFilePath := filepath.Join(tempDir, "config.yml")
Expand Down
Loading