Skip to content
Draft
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
3 changes: 1 addition & 2 deletions appcontext/appcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/PlakarKorp/pkg"
"github.com/PlakarKorp/plakar/config"
"github.com/PlakarKorp/plakar/cookies"
"github.com/PlakarKorp/plakar/utils"
)

type AppContext struct {
Expand Down Expand Up @@ -97,7 +96,7 @@ func (c *AppContext) GetPkgManager() *pkg.Manager {
}

func (c *AppContext) ReloadConfig() error {
cfg, err := utils.LoadConfig(c.ConfigDir)
cfg, err := config.Load(c.ConfigDir)
if err != nil {
return err
}
Expand Down
24 changes: 8 additions & 16 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package config
import (
"fmt"
"strings"

"maps"
)

type Config struct {
Expand Down Expand Up @@ -42,25 +40,21 @@ func (c *Config) GetRepository(name string) (map[string]string, error) {
}
if _, ok := kv["location"]; !ok {
return nil, fmt.Errorf("repository %s has no location", name)
} else {
res := make(map[string]string)
maps.Copy(res, kv)
return res, nil
}

return resolve(kv)
}

func (c *Config) HasSource(name string) bool {
_, ok := c.Sources[name]
return ok
}

func (c *Config) GetSource(name string) (map[string]string, bool) {
func (c *Config) GetSource(name string) (map[string]string, error) {
if kv, ok := c.Sources[name]; !ok {
return nil, false
return nil, fmt.Errorf("failed to retrieve configuration for source %q", name)
} else {
res := make(map[string]string)
maps.Copy(res, kv)
return res, ok
return resolve(kv)
}
}

Expand All @@ -69,12 +63,10 @@ func (c *Config) HasDestination(name string) bool {
return ok
}

func (c *Config) GetDestination(name string) (map[string]string, bool) {
func (c *Config) GetDestination(name string) (map[string]string, error) {
if kv, ok := c.Destinations[name]; !ok {
return nil, false
return nil, fmt.Errorf("failed to retrieve configuration for destination %q", name)
} else {
res := make(map[string]string)
maps.Copy(res, kv)
return res, ok
return resolve(kv)
}
}
8 changes: 4 additions & 4 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ func TestGetSource(t *testing.T) {
}

// Test non-existent source
source, ok := cfg.GetSource("test-source")
require.False(t, ok)
source, err := cfg.GetSource("test-source")
require.NotNil(t, err)
require.Nil(t, source)

// Test existing source
cfg.Sources["test-source"] = SourceConfig{"url": "test://url"}
source, ok = cfg.GetSource("test-source")
require.True(t, ok)
source, err = cfg.GetSource("test-source")
require.Nil(t, err)
require.Equal(t, "test://url", source["url"])
}
202 changes: 202 additions & 0 deletions config/load.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package config

import (
"fmt"
"io"
"os"
"path/filepath"

"go.yaml.in/yaml/v3"
)

const CONFIG_VERSION = "v1.0.0"

type (
storesConfig struct {
Version string `yaml:"version"`
Default string `yaml:"default,omitempty"`
Stores map[string]map[string]string `yaml:"stores"`
}

sourcesConfig struct {
Version string `yaml:"version"`
Sources map[string]map[string]string `yaml:"sources"`
}

destinationsConfig struct {
Version string `yaml:"version"`
Destinations map[string]map[string]string `yaml:"destinations"`
}
)

func load(file string, dst any) error {
f, err := os.Open(file)
if err != nil {
if os.IsNotExist(err) {
return err
}
return fmt.Errorf("failed to open file %s: %w", file, err)
}
defer f.Close()

info, err := f.Stat()
if err != nil {
return fmt.Errorf("failed to stat file %s: %w", file, err)
}
if info.Size() == 0 {
return nil
}

// try to load the new format
err = yaml.NewDecoder(f).Decode(dst)
var version string
switch t := dst.(type) {
case *storesConfig:
version = t.Version
case *destinationsConfig:
version = t.Version
case *sourcesConfig:
version = t.Version
default:
return fmt.Errorf("invalid configuration type %v", t)
}
if err == nil && version == CONFIG_VERSION {
return nil
}

// fallback to the previous format
_, err = f.Seek(0, io.SeekStart)
if err != nil {
return fmt.Errorf("failed to rewind config file: %w", err)
}

switch t := dst.(type) {
case *storesConfig:
err = yaml.NewDecoder(f).Decode(&t.Stores)
if err == nil {
for k, v := range t.Stores {
if _, ok := v[".isDefault"]; ok {
if t.Default != "" {
return fmt.Errorf("multiple default store")
}
t.Default = k
delete(v, ".isDefault")
}
}
}
case *destinationsConfig:
err = yaml.NewDecoder(f).Decode(&t.Destinations)
case *sourcesConfig:
err = yaml.NewDecoder(f).Decode(&t.Sources)
}
if err != nil {
return fmt.Errorf("failed to parse config file: %w", err)
}

return nil
}

func loadFallback(dir string) (*Config, error) {
// Load old config if found
oldpath := filepath.Join(dir, "plakar.yml")
cfg, err := LoadOldConfigIfExists(oldpath)
if err != nil {
return nil, fmt.Errorf("error reading file %s: %w", oldpath, err)
}

// Save the config in the new format right now
if err := Save(dir, cfg); err != nil {
return nil, fmt.Errorf("failed to update config file: %w", err)
}
// Do we want to remove the old file?
return cfg, nil
}

func Load(dir string) (*Config, error) {
sources := sourcesConfig{}
destinations := destinationsConfig{}
stores := storesConfig{}

err := load(filepath.Join(dir, "sources.yml"), &sources)
if err != nil {
if os.IsNotExist(err) {
return loadFallback(dir)
}
return nil, err
}

err = load(filepath.Join(dir, "destinations.yml"), &destinations)
if err != nil {
if os.IsNotExist(err) {
return loadFallback(dir)
}
return nil, err
}

err = load(filepath.Join(dir, "stores.yml"), &stores)
if err != nil && os.IsNotExist(err) {
// try to load former file
err = load("klosets.yml", &stores)
}
if err != nil {
if os.IsNotExist(err) {
return loadFallback(dir)
}
return nil, err
}

cfg := NewConfig()
cfg.Sources = sources.Sources
cfg.Destinations = destinations.Destinations
cfg.Repositories = stores.Stores
cfg.DefaultRepository = stores.Default

return cfg, nil
}

func save(file string, src any) error {
tmpFile, err := os.CreateTemp(filepath.Dir(file), "config.*.yml")
if err != nil {
return err
}

err = yaml.NewEncoder(tmpFile).Encode(src)
tmpFile.Close()

if err == nil {
err = os.Rename(tmpFile.Name(), file)
}

if err != nil {
os.Remove(tmpFile.Name())
return err
}

return nil
}

func Save(dir string, cfg *Config) error {
err := save(filepath.Join(dir, "sources.yml"), sourcesConfig{
Version: CONFIG_VERSION,
Sources: cfg.Sources,
})
if err != nil {
return err
}
err = save(filepath.Join(dir, "destinations.yml"), destinationsConfig{
Version: CONFIG_VERSION,
Destinations: cfg.Destinations,
})
if err != nil {
return err
}
err = save(filepath.Join(dir, "stores.yml"), storesConfig{
Version: CONFIG_VERSION,
Default: cfg.DefaultRepository,
Stores: cfg.Repositories,
})
if err != nil {
return err
}
return nil
}
16 changes: 7 additions & 9 deletions utils/oldconfig.go → config/old.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
package utils
package config

import (
"fmt"
"maps"
"os"

"go.yaml.in/yaml/v3"

"github.com/PlakarKorp/plakar/config"
)

type OldConfig struct {
DefaultRepository string `yaml:"default-repo"`
Repositories map[string]config.RepositoryConfig `yaml:"repositories"`
Remotes map[string]config.SourceConfig `yaml:"remotes"`
DefaultRepository string `yaml:"default-repo"`
Repositories map[string]RepositoryConfig `yaml:"repositories"`
Remotes map[string]SourceConfig `yaml:"remotes"`
}

func LoadOldConfigIfExists(configFile string) (*config.Config, error) {
cfg := config.NewConfig()
func LoadOldConfigIfExists(configFile string) (*Config, error) {
cfg := NewConfig()

f, err := os.Open(configFile)
if err != nil {
Expand All @@ -36,7 +34,7 @@ func LoadOldConfigIfExists(configFile string) (*config.Config, error) {
cfg.DefaultRepository = old.DefaultRepository
cfg.Repositories = old.Repositories
cfg.Sources = old.Remotes
cfg.Destinations = make(map[string]config.DestinationConfig)
cfg.Destinations = make(map[string]DestinationConfig)
for key, val := range cfg.Sources {
res := make(map[string]string)
maps.Copy(res, val)
Expand Down
Loading
Loading