Files

162 lines
3.6 KiB
Go

package credhelper
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/charmbracelet/huh"
"github.com/spf13/cobra"
)
func newConfigureDockerCmd() *cobra.Command {
return &cobra.Command{
Use: "configure-docker",
Short: "Configure Docker to use this credential helper",
Long: "Adds or updates the credHelpers entry in ~/.docker/config.json\nfor all configured registries.",
RunE: runConfigureDocker,
}
}
func runConfigureDocker(cmd *cobra.Command, args []string) error {
sc, err := loadConfig()
if err != nil {
return fmt.Errorf("loading config: %w", err)
}
if len(sc.Registries) == 0 {
fmt.Fprintf(os.Stderr, "No registries configured.\n")
fmt.Fprintf(os.Stderr, "Run: %s login\n", cfg.BinaryName)
return nil
}
// Collect registry hosts
var hosts []string
for url := range sc.Registries {
host := strings.TrimPrefix(url, "https://")
host = strings.TrimPrefix(host, "http://")
hosts = append(hosts, host)
}
dockerConfigPath := getDockerConfigPath()
// Load existing Docker config
dockerCfg := loadDockerConfig()
if dockerCfg == nil {
dockerCfg = make(map[string]any)
}
// Get or create credHelpers
helpers, ok := dockerCfg["credHelpers"]
if !ok {
helpers = make(map[string]any)
}
helpersMap, ok := helpers.(map[string]any)
if !ok {
helpersMap = make(map[string]any)
}
helper := helperName(cfg)
// Check what needs to change
var toAdd []string
for _, host := range hosts {
current, exists := helpersMap[host]
if !exists || current != helper {
toAdd = append(toAdd, host)
}
}
if len(toAdd) == 0 {
fmt.Printf("Docker is already configured for all registries.\n")
return nil
}
fmt.Printf("Will update %s:\n", dockerConfigPath)
for _, host := range toAdd {
fmt.Printf(" + credHelpers[%q] = %q\n", host, helper)
}
fmt.Println()
var confirm bool
err = huh.NewConfirm().
Title("Apply changes?").
Value(&confirm).
Run()
if err != nil || !confirm {
fmt.Fprintf(os.Stderr, "Cancelled.\n")
return nil
}
// Apply changes
for _, host := range toAdd {
helpersMap[host] = helper
}
dockerCfg["credHelpers"] = helpersMap
// Remove conflicting credsStore if it exists and we're adding credHelpers
if _, hasStore := dockerCfg["credsStore"]; hasStore {
fmt.Fprintf(os.Stderr, "Note: credsStore is set — credHelpers takes precedence for configured registries.\n")
}
if err := saveDockerConfig(dockerConfigPath, dockerCfg); err != nil {
return fmt.Errorf("saving Docker config: %w", err)
}
fmt.Printf("Docker configured successfully.\n")
return nil
}
// getDockerConfigPath returns the path to Docker's config.json
func getDockerConfigPath() string {
// Check DOCKER_CONFIG env var first
if dir := os.Getenv("DOCKER_CONFIG"); dir != "" {
return filepath.Join(dir, "config.json")
}
homeDir, err := os.UserHomeDir()
if err != nil {
return ""
}
return filepath.Join(homeDir, ".docker", "config.json")
}
// loadDockerConfig loads Docker's config.json as a generic map
func loadDockerConfig() map[string]any {
path := getDockerConfigPath()
if path == "" {
return nil
}
data, err := os.ReadFile(path)
if err != nil {
return nil
}
var config map[string]any
if err := json.Unmarshal(data, &config); err != nil {
return nil
}
return config
}
// saveDockerConfig writes Docker's config.json
func saveDockerConfig(path string, config map[string]any) error {
// Ensure directory exists
dir := filepath.Dir(path)
if err := os.MkdirAll(dir, 0700); err != nil {
return err
}
data, err := json.MarshalIndent(config, "", "\t")
if err != nil {
return err
}
data = append(data, '\n')
return os.WriteFile(path, data, 0600)
}