package cmd import ( "fmt" "github.com/jensbecker/homelab/knecht/portainer" "github.com/jensbecker/homelab/knecht/stack" "github.com/spf13/cobra" ) var updateCmd = &cobra.Command{ Use: "update ", Short: "Update an existing stack, preserving env vars", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { _, client, svcPath, err := setup() if err != nil { return err } name := args[0] local, err := stack.Get(svcPath, name) if err != nil { return err } remote, err := client.GetStackByName(name) if err != nil { return err } if remote == nil { return fmt.Errorf("stack %q not found — use `knecht deploy %s` to create it", name, name) } compose, err := local.ReadCompose() if err != nil { return err } // Check for missing env keys and prompt for values exampleKeys, err := local.EnvExampleKeys() if err != nil { return err } env, err := mergeEnvVars(remote.Env, exampleKeys) if err != nil { return err } s, err := client.UpdateStack(remote.ID, compose, env) if err != nil { return err } fmt.Printf("Updated stack %q (id %d)\n", s.Name, s.ID) return nil }, } // mergeEnvVars takes existing Portainer env vars and prompts for any keys // present in .env.example but missing from Portainer. func mergeEnvVars(existing []portainer.EnvVar, exampleKeys []string) ([]portainer.EnvVar, error) { envMap := make(map[string]string, len(existing)) for _, e := range existing { envMap[e.Name] = e.Value } for _, key := range exampleKeys { if _, ok := envMap[key]; !ok { fmt.Printf("Missing env var %q — enter value (leave empty to skip): ", key) var val string fmt.Scanln(&val) envMap[key] = val } } result := make([]portainer.EnvVar, 0, len(envMap)) for k, v := range envMap { result = append(result, portainer.EnvVar{Name: k, Value: v}) } return result, nil } func init() { rootCmd.AddCommand(updateCmd) }