init
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package config
|
||||
|
||||
const (
|
||||
// DefaultConfigDir is the directory for global config
|
||||
DefaultConfigDir = ".config/aevs"
|
||||
|
||||
// DefaultGlobalConfigFile is the global config filename
|
||||
DefaultGlobalConfigFile = "config.yaml"
|
||||
|
||||
// DefaultProjectConfigFile is the project config filename
|
||||
DefaultProjectConfigFile = "aevs.yaml"
|
||||
|
||||
// DefaultStorageType is the default storage backend
|
||||
DefaultStorageType = "s3"
|
||||
|
||||
// DefaultRegion is the default AWS region
|
||||
DefaultRegion = "us-east-1"
|
||||
|
||||
// DefaultEndpoint is the default S3 endpoint
|
||||
DefaultEndpoint = "https://s3.amazonaws.com"
|
||||
|
||||
// ArchiveFileName is the name of the archive in storage
|
||||
ArchiveFileName = "envs.tar.gz"
|
||||
|
||||
// MetadataFileName is the name of the metadata file in storage
|
||||
MetadataFileName = "metadata.json"
|
||||
)
|
||||
@@ -0,0 +1,89 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// GlobalConfig represents ~/.config/aevs/config.yaml
|
||||
type GlobalConfig struct {
|
||||
Storage StorageConfig `yaml:"storage"`
|
||||
}
|
||||
|
||||
// StorageConfig holds S3-compatible storage credentials
|
||||
type StorageConfig struct {
|
||||
Type string `yaml:"type"` // storage type: "s3"
|
||||
Endpoint string `yaml:"endpoint"` // S3 endpoint URL
|
||||
Region string `yaml:"region"` // AWS region (optional, default: us-east-1)
|
||||
Bucket string `yaml:"bucket"` // S3 bucket name
|
||||
AccessKey string `yaml:"access_key"` // AWS Access Key ID
|
||||
SecretKey string `yaml:"secret_key"` // AWS Secret Access Key
|
||||
}
|
||||
|
||||
// GlobalConfigPath returns the full path to the global config file
|
||||
func GlobalConfigPath() string {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return filepath.Join(home, DefaultConfigDir, DefaultGlobalConfigFile)
|
||||
}
|
||||
|
||||
// GlobalConfigExists checks if the global config file exists
|
||||
func GlobalConfigExists() bool {
|
||||
path := GlobalConfigPath()
|
||||
if path == "" {
|
||||
return false
|
||||
}
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// LoadGlobalConfig loads the global config from ~/.config/aevs/config.yaml
|
||||
func LoadGlobalConfig() (*GlobalConfig, error) {
|
||||
path := GlobalConfigPath()
|
||||
if path == "" {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cfg GlobalConfig
|
||||
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
// SaveGlobalConfig saves the global config to ~/.config/aevs/config.yaml with 0600 permissions
|
||||
func SaveGlobalConfig(cfg *GlobalConfig) error {
|
||||
path := GlobalConfigPath()
|
||||
if path == "" {
|
||||
return os.ErrNotExist
|
||||
}
|
||||
|
||||
// Ensure directory exists
|
||||
dir := filepath.Dir(path)
|
||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Marshal to YAML
|
||||
data, err := yaml.Marshal(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write with secure permissions (0600)
|
||||
if err := os.WriteFile(path, data, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// ProjectConfig represents ./aevs.yaml
|
||||
type ProjectConfig struct {
|
||||
Project string `yaml:"project"` // unique project identifier
|
||||
Files []string `yaml:"files"` // list of file paths to sync
|
||||
}
|
||||
|
||||
// projectNameRegex validates project names (only a-z, 0-9, -, _)
|
||||
var projectNameRegex = regexp.MustCompile(`^[a-z0-9_-]+$`)
|
||||
|
||||
// LoadProjectConfig loads a project config from the specified path
|
||||
func LoadProjectConfig(path string) (*ProjectConfig, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cfg ProjectConfig
|
||||
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Validate project name
|
||||
if err := ValidateProjectName(cfg.Project); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
// SaveProjectConfig saves a project config to the specified path
|
||||
func SaveProjectConfig(path string, cfg *ProjectConfig) error {
|
||||
// Validate project name before saving
|
||||
if err := ValidateProjectName(cfg.Project); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Marshal to YAML
|
||||
data, err := yaml.Marshal(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write file
|
||||
if err := os.WriteFile(path, data, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateProjectName checks if the project name contains only a-z, 0-9, -, _
|
||||
func ValidateProjectName(name string) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("project name cannot be empty")
|
||||
}
|
||||
|
||||
if !projectNameRegex.MatchString(name) {
|
||||
return fmt.Errorf("invalid project name %q; use only a-z, 0-9, -, _", name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user