mirror of
https://github.com/jimeh/evm.git
synced 2026-02-19 07:26:40 +00:00
277 lines
5.3 KiB
Go
277 lines
5.3 KiB
Go
package manager
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/sethvargo/go-envconfig"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
var ErrConfig = fmt.Errorf("%w", Err)
|
|
|
|
type Mode string
|
|
|
|
const (
|
|
User Mode = "user"
|
|
System Mode = "system"
|
|
)
|
|
|
|
type ConfigFile struct {
|
|
Paths ConfigFilePaths `yaml:"paths" json:"paths"`
|
|
}
|
|
|
|
type ConfigFilePaths struct {
|
|
Shims string `yaml:"shims" json:"shims" env:"EVM_SHIMS,overwrite"`
|
|
Sources string `yaml:"sources" json:"sources" env:"EVM_SOURCES,overwrite"`
|
|
Versions string `yaml:"versions" json:"versions" env:"EVM_VERSIONS,overwrite"`
|
|
}
|
|
|
|
type Config struct {
|
|
Mode Mode `yaml:"mode" json:"mode"`
|
|
Current CurrentConfig `yaml:"current" json:"current"`
|
|
Paths PathsConfig `yaml:"paths" json:"paths"`
|
|
}
|
|
|
|
type CurrentConfig struct {
|
|
Version string `yaml:"version" json:"version"`
|
|
SetBy string `yaml:"set_by,omitempty" json:"set_by,omitempty"`
|
|
}
|
|
|
|
type PathsConfig struct {
|
|
Binary string `yaml:"binary" json:"binary"`
|
|
Root string `yaml:"root" json:"root"`
|
|
Shims string `yaml:"shims" json:"shims"`
|
|
Sources string `yaml:"sources" json:"sources"`
|
|
Versions string `yaml:"versions" json:"versions"`
|
|
}
|
|
|
|
func NewConfig() (*Config, error) {
|
|
mode := Mode(os.Getenv("EVM_MODE"))
|
|
if mode != System {
|
|
mode = User
|
|
}
|
|
|
|
defaultRoot := filepath.Join(string(os.PathSeparator), "opt", "evm")
|
|
if mode == User {
|
|
defaultRoot = filepath.Join("$HOME", ".evm")
|
|
}
|
|
|
|
if v := os.Getenv("EVM_ROOT"); v != "" {
|
|
defaultRoot = v
|
|
}
|
|
|
|
conf := &Config{
|
|
Mode: mode,
|
|
Paths: PathsConfig{
|
|
Root: defaultRoot,
|
|
Shims: "$EVM_ROOT/shims",
|
|
Sources: "$EVM_ROOT/sources",
|
|
Versions: "$EVM_ROOT/versions",
|
|
},
|
|
}
|
|
|
|
var err error
|
|
conf.Paths.Root, err = conf.normalizePath(conf.Paths.Root)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = conf.load()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conf.Paths.Shims, err = conf.normalizePath(conf.Paths.Shims)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conf.Paths.Sources, err = conf.normalizePath(conf.Paths.Sources)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conf.Paths.Versions, err = conf.normalizePath(conf.Paths.Versions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conf.Paths.Binary, err = os.Executable()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = conf.PopulateCurrent()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return conf, nil
|
|
}
|
|
|
|
const currentFileName = "current"
|
|
|
|
func (conf *Config) PopulateCurrent() error {
|
|
if v := os.Getenv("EVM_VERSION"); v != "" {
|
|
conf.Current.Version = strings.TrimSpace(v)
|
|
conf.Current.SetBy = "EVM_VERSION environment variable"
|
|
|
|
return nil
|
|
}
|
|
|
|
currentFile := filepath.Join(conf.Paths.Root, currentFileName)
|
|
b, err := os.ReadFile(currentFile)
|
|
if err != nil {
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
if len(b) > 0 {
|
|
conf.Current.Version = strings.TrimSpace(string(b))
|
|
conf.Current.SetBy = currentFile
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var configFileNames = []string{
|
|
"config.yaml",
|
|
"config.yml",
|
|
"config.json",
|
|
"evm.yaml",
|
|
"evm.yml",
|
|
"evm.json",
|
|
}
|
|
|
|
func (c *Config) load() error {
|
|
var path string
|
|
for _, name := range configFileNames {
|
|
f := filepath.Join(c.Paths.Root, name)
|
|
|
|
_, err := os.Stat(f)
|
|
if err != nil {
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
continue
|
|
}
|
|
return err
|
|
}
|
|
|
|
path = f
|
|
break
|
|
}
|
|
|
|
cf := &ConfigFile{}
|
|
if path != "" {
|
|
var err error
|
|
cf, err = c.loadConfigFile(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
err := envconfig.Process(context.Background(), cf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if cf.Paths.Shims != "" {
|
|
c.Paths.Shims = cf.Paths.Shims
|
|
}
|
|
if cf.Paths.Sources != "" {
|
|
c.Paths.Sources = cf.Paths.Sources
|
|
}
|
|
if cf.Paths.Versions != "" {
|
|
c.Paths.Versions = cf.Paths.Versions
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Config) loadConfigFile(path string) (*ConfigFile, error) {
|
|
if path == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
content, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cf := &ConfigFile{}
|
|
|
|
buf := bytes.NewBuffer(content)
|
|
switch filepath.Ext(path) {
|
|
case ".yaml", ".yml":
|
|
dec := yaml.NewDecoder(buf)
|
|
dec.KnownFields(true)
|
|
err = dec.Decode(cf)
|
|
case ".json":
|
|
dec := json.NewDecoder(buf)
|
|
dec.DisallowUnknownFields()
|
|
err = dec.Decode(cf)
|
|
default:
|
|
return nil, fmt.Errorf(
|
|
`%w"%s" does not have a ".yaml", ".yml", `+
|
|
`or ".json" file extension`,
|
|
ErrConfig, path,
|
|
)
|
|
}
|
|
if err != nil && !errors.Is(err, io.EOF) {
|
|
return nil, err
|
|
}
|
|
|
|
return cf, nil
|
|
}
|
|
|
|
func (c *Config) normalizePath(path string) (string, error) {
|
|
path = strings.TrimSpace(path)
|
|
|
|
var homePrefix string
|
|
switch {
|
|
case strings.HasPrefix(path, "$HOME") ||
|
|
strings.HasPrefix(path, "$home"):
|
|
homePrefix = path[0:5]
|
|
case strings.HasPrefix(path, "~"):
|
|
homePrefix = path[0:1]
|
|
}
|
|
|
|
if homePrefix != "" {
|
|
if c.Mode == System {
|
|
return "", fmt.Errorf(
|
|
`%wEVM_MODE is set to "%s" which prohibits `+
|
|
`using "$HOME" or "~" in EVM_ROOT`,
|
|
ErrConfig, string(System),
|
|
)
|
|
}
|
|
|
|
var home string
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
path = filepath.Join(
|
|
home, strings.TrimPrefix(path, homePrefix))
|
|
}
|
|
|
|
if c.Paths.Root == "" {
|
|
return path, nil
|
|
}
|
|
|
|
if strings.HasPrefix(path, "$EVM_ROOT") {
|
|
path = filepath.Join(c.Paths.Root, path[9:])
|
|
} else if !filepath.IsAbs(path) {
|
|
path = filepath.Join(c.Paths.Root, path)
|
|
}
|
|
|
|
return path, nil
|
|
}
|