mirror of
https://github.com/chenasraf/wand.git
synced 2026-05-17 17:38:02 +00:00
feat: change wand file via arg or env var
This commit is contained in:
12
README.md
12
README.md
@@ -114,6 +114,18 @@ wand test --help
|
||||
|
||||
The first config file found is used.
|
||||
|
||||
You can override config discovery with an explicit path:
|
||||
|
||||
```bash
|
||||
# via flag
|
||||
wand --wand-file ./other-config.yml build
|
||||
|
||||
# via environment variable
|
||||
WAND_FILE=./other-config.yml wand build
|
||||
```
|
||||
|
||||
The `--wand-file` flag takes precedence over `WAND_FILE`.
|
||||
|
||||
---
|
||||
|
||||
## 📖 Config Reference
|
||||
|
||||
@@ -87,10 +87,16 @@ type rawConfig struct {
|
||||
Commands map[string]Command `yaml:",inline"`
|
||||
}
|
||||
|
||||
func loadConfig() (*Config, map[string]Command, error) {
|
||||
configPath, err := findConfigFile()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
func loadConfig(explicitPath string) (*Config, map[string]Command, error) {
|
||||
var configPath string
|
||||
var err error
|
||||
if explicitPath != "" {
|
||||
configPath = explicitPath
|
||||
} else {
|
||||
configPath, err = findConfigFile()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(configPath)
|
||||
|
||||
@@ -115,7 +115,7 @@ build:
|
||||
cmd: go build
|
||||
`)
|
||||
|
||||
cfg, commands, err := loadConfig()
|
||||
cfg, commands, err := loadConfig("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -154,7 +154,7 @@ parent:
|
||||
cmd: echo grandchild
|
||||
`)
|
||||
|
||||
_, commands, err := loadConfig()
|
||||
_, commands, err := loadConfig("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -185,7 +185,7 @@ main:
|
||||
cmd: echo test
|
||||
`)
|
||||
|
||||
cfg, commands, err := loadConfig()
|
||||
cfg, commands, err := loadConfig("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -212,7 +212,7 @@ main:
|
||||
cmd: echo test
|
||||
`)
|
||||
|
||||
cfg, _, err := loadConfig()
|
||||
cfg, _, err := loadConfig("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -239,7 +239,7 @@ main:
|
||||
cmd: echo test
|
||||
`)
|
||||
|
||||
cfg, _, err := loadConfig()
|
||||
cfg, _, err := loadConfig("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -258,7 +258,7 @@ main:
|
||||
MY_VAR: hello
|
||||
`)
|
||||
|
||||
_, commands, err := loadConfig()
|
||||
_, commands, err := loadConfig("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -329,7 +329,7 @@ deploy:
|
||||
confirm: "Deploy to production?"
|
||||
`)
|
||||
|
||||
_, commands, err := loadConfig()
|
||||
_, commands, err := loadConfig("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -348,7 +348,7 @@ deploy:
|
||||
confirm: true
|
||||
`)
|
||||
|
||||
_, commands, err := loadConfig()
|
||||
_, commands, err := loadConfig("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -367,7 +367,7 @@ build:
|
||||
aliases: [b, compile]
|
||||
`)
|
||||
|
||||
_, commands, err := loadConfig()
|
||||
_, commands, err := loadConfig("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -386,7 +386,7 @@ main:
|
||||
working_dir: /tmp
|
||||
`)
|
||||
|
||||
_, commands, err := loadConfig()
|
||||
_, commands, err := loadConfig("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -396,6 +396,28 @@ main:
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfig_ExplicitPath(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
configPath := filepath.Join(dir, "custom.yml")
|
||||
err := os.WriteFile(configPath, []byte(`
|
||||
main:
|
||||
description: from explicit
|
||||
cmd: echo explicit
|
||||
`), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, commands, err := loadConfig(configPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if commands["main"].Description != "from explicit" {
|
||||
t.Errorf("description = %q, want 'from explicit'", commands["main"].Description)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfig_NoConfigFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
origDir, _ := os.Getwd()
|
||||
@@ -408,7 +430,7 @@ func TestLoadConfig_NoConfigFile(t *testing.T) {
|
||||
// Remove HOME to prevent finding a real ~/.wand.yml
|
||||
t.Setenv("HOME", dir)
|
||||
|
||||
_, _, err := loadConfig()
|
||||
_, _, err := loadConfig("")
|
||||
if err == nil {
|
||||
t.Error("expected error for missing config, got nil")
|
||||
}
|
||||
@@ -434,7 +456,7 @@ main:
|
||||
viper.Reset()
|
||||
}()
|
||||
|
||||
_, commands, err := loadConfig()
|
||||
_, commands, err := loadConfig("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
21
cmd/root.go
21
cmd/root.go
@@ -1,12 +1,16 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func Execute() error {
|
||||
cfg, commands, err := loadConfig()
|
||||
configFile := resolveConfigFile()
|
||||
cfg, commands, err := loadConfig(configFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -17,6 +21,8 @@ func Execute() error {
|
||||
SilenceErrors: true,
|
||||
}
|
||||
|
||||
rootCmd.PersistentFlags().String("wand-file", "", "path to wand config file (overrides discovery)")
|
||||
|
||||
if main, ok := commands["main"]; ok {
|
||||
rootCmd.Short = main.Description
|
||||
rootCmd.Args = cobra.ArbitraryArgs
|
||||
@@ -62,6 +68,19 @@ func buildCobraCommand(cfg *Config, name string, cmd Command) *cobra.Command {
|
||||
return c
|
||||
}
|
||||
|
||||
// resolveConfigFile extracts --wand-file from os.Args or WAND_FILE env before cobra parses.
|
||||
func resolveConfigFile() string {
|
||||
for i, arg := range os.Args {
|
||||
if arg == "--wand-file" && i+1 < len(os.Args) {
|
||||
return os.Args[i+1]
|
||||
}
|
||||
if strings.HasPrefix(arg, "--wand-file=") {
|
||||
return strings.TrimPrefix(arg, "--wand-file=")
|
||||
}
|
||||
}
|
||||
return os.Getenv("WAND_FILE")
|
||||
}
|
||||
|
||||
func registerFlags(c *cobra.Command, flags map[string]Flag) {
|
||||
lo.ForEach(
|
||||
lo.Entries(flags),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -285,6 +287,68 @@ build:
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveConfigFile_Flag(t *testing.T) {
|
||||
origArgs := setArgs("wand", "--wand-file", "/tmp/custom.yml", "build")
|
||||
defer restoreArgs(origArgs)
|
||||
|
||||
got := resolveConfigFile()
|
||||
if got != "/tmp/custom.yml" {
|
||||
t.Errorf("resolveConfigFile() = %q, want /tmp/custom.yml", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveConfigFile_FlagEquals(t *testing.T) {
|
||||
origArgs := setArgs("wand", "--wand-file=/tmp/custom.yml", "build")
|
||||
defer restoreArgs(origArgs)
|
||||
|
||||
got := resolveConfigFile()
|
||||
if got != "/tmp/custom.yml" {
|
||||
t.Errorf("resolveConfigFile() = %q, want /tmp/custom.yml", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveConfigFile_EnvVar(t *testing.T) {
|
||||
origArgs := setArgs("wand", "build")
|
||||
defer restoreArgs(origArgs)
|
||||
|
||||
t.Setenv("WAND_FILE", "/tmp/env.yml")
|
||||
|
||||
got := resolveConfigFile()
|
||||
if got != "/tmp/env.yml" {
|
||||
t.Errorf("resolveConfigFile() = %q, want /tmp/env.yml", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveConfigFile_FlagOverridesEnv(t *testing.T) {
|
||||
origArgs := setArgs("wand", "--wand-file", "/tmp/flag.yml")
|
||||
defer restoreArgs(origArgs)
|
||||
|
||||
t.Setenv("WAND_FILE", "/tmp/env.yml")
|
||||
|
||||
got := resolveConfigFile()
|
||||
if got != "/tmp/flag.yml" {
|
||||
t.Errorf("resolveConfigFile() = %q, want /tmp/flag.yml (flag should override env)", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecute_WithWandFileFlag(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
configPath := filepath.Join(dir, "custom.yml")
|
||||
_ = os.WriteFile(configPath, []byte(`
|
||||
main:
|
||||
description: custom
|
||||
cmd: echo custom
|
||||
`), 0644)
|
||||
|
||||
origArgs := setArgs("wand", "--wand-file", configPath)
|
||||
defer restoreArgs(origArgs)
|
||||
|
||||
err := Execute()
|
||||
if err != nil {
|
||||
t.Fatalf("Execute() failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecute_NoMain(t *testing.T) {
|
||||
setupTestConfig(t, `
|
||||
build:
|
||||
|
||||
Reference in New Issue
Block a user