这是indexloc提供的服务,不要输入任何密码
Skip to content

feat(cli): add "save-config" subcommand to store configuration #244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
103 changes: 76 additions & 27 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,25 @@ func BuildRootCommand(opt *Options) (*cobra.Command, error) {
},
})

saveConfigCmd := &cobra.Command{
Use: "save-config",
Short: "Save the current configuration to the default config path",
Long: "This command saves the current configuration to the default config path. It is useful for persisting the configuration across runs.",
RunE: func(cmd *cobra.Command, args []string) error {
configPath, err := opt.WriteConfigurationFile()
if err != nil {
return fmt.Errorf("failed to write config file: %w", err)
}
fmt.Printf(`Configuration saved to "%s"`, configPath)
return nil // Exit early if we are just saving the config
},
}
rootCmd.AddCommand(saveConfigCmd)

if err := opt.bindCLIFlags(saveConfigCmd.Flags()); err != nil {
return nil, err
}

if err := opt.bindCLIFlags(rootCmd.Flags()); err != nil {
return nil, err
}
Expand All @@ -96,9 +115,9 @@ type Options struct {
KubeConfigPath string `json:"kubeConfigPath,omitempty"`
PromptTemplateFilePath string `json:"promptTemplateFilePath,omitempty"`
ExtraPromptPaths []string `json:"extraPromptPaths,omitempty"`
TracePath string `json:"tracePath,omitempty"`
TracePath string `json:"-"`
RemoveWorkDir bool `json:"removeWorkDir,omitempty"`
ToolConfigPaths []string `json:"toolConfigPaths,omitempty"`
ToolConfigPaths []string `json:"-"`

// UserInterface is the type of user interface to use.
UserInterface UserInterface `json:"userInterface,omitempty"`
Expand Down Expand Up @@ -180,45 +199,79 @@ func (o *Options) LoadConfiguration(b []byte) error {
return nil
}

func (o *Options) LoadConfigurationFile() error {
configPaths := defaultConfigPaths
for _, configPath := range configPaths {
pathWithPlaceholdersExpanded := configPath
// expandConfigPath replaces {CONFIG} and {HOME} tokens in a path with actual directories.
func expandConfigPath(configPath string) (string, error) {
pathWithPlaceholdersExpanded := configPath

if strings.Contains(pathWithPlaceholdersExpanded, "{CONFIG}") {
configDir, err := os.UserConfigDir()
if err != nil {
return fmt.Errorf("getting user config directory (for config file path %q): %w", configPath, err)
}
pathWithPlaceholdersExpanded = strings.ReplaceAll(pathWithPlaceholdersExpanded, "{CONFIG}", configDir)
if strings.Contains(pathWithPlaceholdersExpanded, "{CONFIG}") {
configDir, err := os.UserConfigDir()
if err != nil {
return "", fmt.Errorf("getting user config directory (for config file path %q): %w", configPath, err)
}
pathWithPlaceholdersExpanded = strings.ReplaceAll(pathWithPlaceholdersExpanded, "{CONFIG}", configDir)
}

if strings.Contains(pathWithPlaceholdersExpanded, "{HOME}") {
homeDir, err := os.UserHomeDir()
if err != nil {
return fmt.Errorf("getting user home directory (for config file path %q): %w", configPath, err)
}
pathWithPlaceholdersExpanded = strings.ReplaceAll(pathWithPlaceholdersExpanded, "{HOME}", homeDir)
if strings.Contains(pathWithPlaceholdersExpanded, "{HOME}") {
homeDir, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("getting user home directory (for config file path %q): %w", configPath, err)
}
pathWithPlaceholdersExpanded = strings.ReplaceAll(pathWithPlaceholdersExpanded, "{HOME}", homeDir)
}
return pathWithPlaceholdersExpanded, nil
}

configPath = filepath.Clean(pathWithPlaceholdersExpanded)
configBytes, err := os.ReadFile(configPath)
func (o *Options) LoadConfigurationFile() error {
for _, configPath := range defaultConfigPaths {
finalPath, err := expandConfigPath(configPath)
if err != nil {
fmt.Fprintf(os.Stderr, "warning: could not expand config path %q: %v\n", configPath, err)
continue
}
configBytes, err := os.ReadFile(filepath.Clean(finalPath))
if err != nil {
if os.IsNotExist(err) {
// ignore missing config files, they are optional
} else {
fmt.Fprintf(os.Stderr, "warning: could not load defaults from %q: %v\n", configPath, err)
fmt.Fprintf(os.Stderr, "warning: could not load defaults from %q: %v\n", finalPath, err)
}
}
if len(configBytes) > 0 {
if err := o.LoadConfiguration(configBytes); err != nil {
fmt.Fprintf(os.Stderr, "warning: error loading configuration from %q: %v\n", configPath, err)
fmt.Fprintf(os.Stderr, "warning: error loading configuration from %q: %v\n", finalPath, err)
}
}
}
return nil
}

func (o *Options) WriteConfigurationFile() (string, error) {
configData, err := yaml.Marshal(o)
if err != nil {
return "", fmt.Errorf("marshaling config: %w", err)
}

var writeErrors []error
for _, configPath := range defaultConfigPaths {
finalPath, err := expandConfigPath(configPath)
if err != nil {
writeErrors = append(writeErrors, fmt.Errorf("expand path %q: %w", configPath, err))
continue
}
// Ensure parent directory exists
if err := os.MkdirAll(filepath.Dir(finalPath), 0o755); err != nil {
writeErrors = append(writeErrors, fmt.Errorf("mkdir %q: %w", filepath.Dir(finalPath), err))
continue
}
if err := os.WriteFile(finalPath, configData, 0o644); err != nil {
writeErrors = append(writeErrors, fmt.Errorf("write file %q: %w", finalPath, err))
continue
}
return finalPath, nil
}
return "", fmt.Errorf("failed to write config to any path: %v", writeErrors)
}

func main() {
ctx := context.Background()

Expand Down Expand Up @@ -271,11 +324,7 @@ func run(ctx context.Context) error {
// do this early, before the third-party code logs anything.
redirectStdLogToKlog()

if err := rootCmd.ExecuteContext(ctx); err != nil {
return err
}

return nil
return rootCmd.ExecuteContext(ctx)
}

func (opt *Options) bindCLIFlags(f *pflag.FlagSet) error {
Expand Down