Fix config-not-found errors, dev-build hint, unify output writer
ResolveConfigPath now stats explicit paths from --config and $VAULTIK_CONFIG and produces an actionable error naming the bad path and suggesting 'vaultik config init' (with the right path in the --config case). The default-search failure message lists the paths it tried. The scanner no longer hard-codes os.Stdout vs io.Discard based on EnableProgress. ScannerConfig and ScannerParams take an explicit Output io.Writer, and the Vaultik caller passes v.Stdout — which itself is set to io.Discard in --cron mode. One knob controls both scanner-level and Vaultik-level user-facing output. The version command prints a hint when Version == "dev" telling the user this is a development build without embedded version metadata.
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/adrg/xdg"
|
"github.com/adrg/xdg"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -63,13 +64,21 @@ func GetRootFlags() RootFlags {
|
|||||||
|
|
||||||
// ResolveConfigPath resolves the config file path from flags, environment, or default.
|
// ResolveConfigPath resolves the config file path from flags, environment, or default.
|
||||||
// Search order: --config flag, VAULTIK_CONFIG env, XDG config dir, /etc/vaultik/config.yml.
|
// Search order: --config flag, VAULTIK_CONFIG env, XDG config dir, /etc/vaultik/config.yml.
|
||||||
|
// Explicit paths from --config and $VAULTIK_CONFIG are checked for existence
|
||||||
|
// so the user gets a clear error instead of a downstream YAML parser failure.
|
||||||
func ResolveConfigPath() (string, error) {
|
func ResolveConfigPath() (string, error) {
|
||||||
if rootFlags.ConfigPath != "" {
|
if path := rootFlags.ConfigPath; path != "" {
|
||||||
return rootFlags.ConfigPath, nil
|
if _, err := os.Stat(path); err != nil {
|
||||||
|
return "", fmt.Errorf("config file from --config not found: %s (run 'vaultik config init --config %s' to create it)", path, path)
|
||||||
|
}
|
||||||
|
return path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if envPath := os.Getenv("VAULTIK_CONFIG"); envPath != "" {
|
if path := os.Getenv("VAULTIK_CONFIG"); path != "" {
|
||||||
return envPath, nil
|
if _, err := os.Stat(path); err != nil {
|
||||||
|
return "", fmt.Errorf("config file from $VAULTIK_CONFIG not found: %s (unset VAULTIK_CONFIG, point it at an existing file, or run 'vaultik config init')", path)
|
||||||
|
}
|
||||||
|
return path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, path := range defaultConfigPaths() {
|
for _, path := range defaultConfigPaths() {
|
||||||
@@ -78,7 +87,7 @@ func ResolveConfigPath() (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", fmt.Errorf("no config file found; run 'vaultik config init' to create one, or specify with --config")
|
return "", fmt.Errorf("no config file found at %s (run 'vaultik config init' to create the default config, or pass --config <path>)", strings.Join(defaultConfigPaths(), " or "))
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultConfigPaths returns the ordered list of config paths to search.
|
// defaultConfigPaths returns the ordered list of config paths to search.
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ func NewVersionCommand() *cobra.Command {
|
|||||||
fmt.Printf(" commit: %s\n", globals.Commit)
|
fmt.Printf(" commit: %s\n", globals.Commit)
|
||||||
fmt.Printf(" go: %s\n", runtime.Version())
|
fmt.Printf(" go: %s\n", runtime.Version())
|
||||||
fmt.Printf(" os/arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
|
fmt.Printf(" os/arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
|
||||||
|
if globals.Version == "dev" {
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("This is a development build (no version information embedded).")
|
||||||
|
fmt.Println("Build a release binary with 'make vaultik' or download from")
|
||||||
|
fmt.Println("https://sneak.berlin/go/vaultik for embedded version metadata.")
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package snapshot
|
package snapshot
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
"sneak.berlin/go/vaultik/internal/config"
|
"sneak.berlin/go/vaultik/internal/config"
|
||||||
@@ -11,6 +13,7 @@ import (
|
|||||||
// ScannerParams holds parameters for scanner creation
|
// ScannerParams holds parameters for scanner creation
|
||||||
type ScannerParams struct {
|
type ScannerParams struct {
|
||||||
EnableProgress bool
|
EnableProgress bool
|
||||||
|
Output io.Writer // Where one-off scanner messages go; nil disables them
|
||||||
Fs afero.Fs
|
Fs afero.Fs
|
||||||
Exclude []string // Exclude patterns (combined global + snapshot-specific)
|
Exclude []string // Exclude patterns (combined global + snapshot-specific)
|
||||||
SkipErrors bool // Skip file read errors (log loudly but continue)
|
SkipErrors bool // Skip file read errors (log loudly but continue)
|
||||||
@@ -46,6 +49,7 @@ func provideScannerFactory(cfg *config.Config, repos *database.Repositories, sto
|
|||||||
CompressionLevel: cfg.CompressionLevel,
|
CompressionLevel: cfg.CompressionLevel,
|
||||||
AgeRecipients: cfg.AgeRecipients,
|
AgeRecipients: cfg.AgeRecipients,
|
||||||
EnableProgress: params.EnableProgress,
|
EnableProgress: params.EnableProgress,
|
||||||
|
Output: params.Output,
|
||||||
Exclude: excludes,
|
Exclude: excludes,
|
||||||
SkipErrors: params.SkipErrors,
|
SkipErrors: params.SkipErrors,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -93,7 +93,8 @@ type ScannerConfig struct {
|
|||||||
MaxBlobSize int64
|
MaxBlobSize int64
|
||||||
CompressionLevel int
|
CompressionLevel int
|
||||||
AgeRecipients []string // Optional, empty means no encryption
|
AgeRecipients []string // Optional, empty means no encryption
|
||||||
EnableProgress bool // Enable progress reporting
|
EnableProgress bool // Enable the live progress reporter (ETAs, throughput)
|
||||||
|
Output io.Writer // Where one-off scanner messages go; nil disables them
|
||||||
Exclude []string // Glob patterns for files/directories to exclude
|
Exclude []string // Glob patterns for files/directories to exclude
|
||||||
SkipErrors bool // Skip file read errors (log loudly but continue)
|
SkipErrors bool // Skip file read errors (log loudly but continue)
|
||||||
}
|
}
|
||||||
@@ -142,9 +143,9 @@ func NewScanner(cfg ScannerConfig) *Scanner {
|
|||||||
// Compile exclude patterns
|
// Compile exclude patterns
|
||||||
compiledExclude := compileExcludePatterns(cfg.Exclude)
|
compiledExclude := compileExcludePatterns(cfg.Exclude)
|
||||||
|
|
||||||
output := io.Writer(io.Discard)
|
output := cfg.Output
|
||||||
if cfg.EnableProgress {
|
if output == nil {
|
||||||
output = os.Stdout
|
output = io.Discard
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Scanner{
|
return &Scanner{
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ func (v *Vaultik) createNamedSnapshot(opts *SnapshotCreateOptions, hostname, sna
|
|||||||
|
|
||||||
scanner := v.ScannerFactory(snapshot.ScannerParams{
|
scanner := v.ScannerFactory(snapshot.ScannerParams{
|
||||||
EnableProgress: !opts.Cron,
|
EnableProgress: !opts.Cron,
|
||||||
|
Output: v.Stdout,
|
||||||
Fs: v.Fs,
|
Fs: v.Fs,
|
||||||
Exclude: v.Config.GetExcludes(snapName),
|
Exclude: v.Config.GetExcludes(snapName),
|
||||||
SkipErrors: opts.SkipErrors,
|
SkipErrors: opts.SkipErrors,
|
||||||
|
|||||||
Reference in New Issue
Block a user