From 71e20925f670ad71d83beef9fc369de6e19ddaac Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Tue, 24 Mar 2026 08:39:08 -0700 Subject: [PATCH] M9: init --merge to add new devices without overwriting Add --merge flag to init that loads existing config, skips devices whose UUID is already configured, and appends only new discoveries. --force and --merge are mutually exclusive. Uses Config.Save() from M8. Error message now suggests both --force and --merge. Co-Authored-By: Claude Opus 4.6 (1M context) --- cmd/init.go | 52 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/cmd/init.go b/cmd/init.go index a0f7976..3d94799 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -3,16 +3,17 @@ package cmd import ( "fmt" "os" - "path/filepath" "git.wntrmute.dev/kyle/arca/internal/config" "git.wntrmute.dev/kyle/arca/internal/udisks" "github.com/godbus/dbus/v5" "github.com/spf13/cobra" - "gopkg.in/yaml.v3" ) -var forceInit bool +var ( + forceInit bool + mergeInit bool +) var initCmd = &cobra.Command{ Use: "init", @@ -23,16 +24,25 @@ var initCmd = &cobra.Command{ func init() { initCmd.Flags().BoolVarP(&forceInit, "force", "f", false, "overwrite existing config file") + initCmd.Flags().BoolVar(&mergeInit, "merge", false, "add new devices to existing config without overwriting") + initCmd.MarkFlagsMutuallyExclusive("force", "merge") rootCmd.AddCommand(initCmd) } func runInit(cmd *cobra.Command, args []string) error { cfgPath := config.Path() - if !forceInit { - if _, err := os.Stat(cfgPath); err == nil { - return fmt.Errorf("config already exists at %s (use --force to overwrite)", cfgPath) + // Load existing config for merge, or start fresh. + var cfg *config.Config + if mergeInit { + cfg = config.Load() + } else { + if !forceInit { + if _, err := os.Stat(cfgPath); err == nil { + return fmt.Errorf("config already exists at %s (use --force to overwrite or --merge to add new devices)", cfgPath) + } } + cfg = &config.Config{Devices: make(map[string]config.DeviceConfig)} } client, err := udisks.NewClient() @@ -51,43 +61,42 @@ func runInit(cmd *cobra.Command, args []string) error { return fmt.Errorf("detecting root device: %w", err) } - cfg := config.Config{ - Devices: make(map[string]config.DeviceConfig), - } - + added := 0 for _, dev := range encrypted { if isRootBacking(dev.ObjectPath, rootBacking) { fmt.Fprintf(os.Stderr, "Skipping %s (root filesystem)\n", dev.DevicePath) continue } + if cfg.HasUUID(dev.UUID) { + fmt.Fprintf(os.Stderr, "Skipping %s (already configured)\n", dev.DevicePath) + continue + } + alias := aliasFromUUID(dev.UUID) cfg.Devices[alias] = config.DeviceConfig{ UUID: dev.UUID, Methods: []string{"passphrase"}, } fmt.Fprintf(os.Stderr, "Found %s (UUID %s) -> alias %q\n", dev.DevicePath, dev.UUID, alias) + added++ } - if len(cfg.Devices) == 0 { + if added == 0 && !mergeInit { fmt.Println("No non-root LUKS devices found.") return nil } - data, err := yaml.Marshal(&cfg) - if err != nil { - return fmt.Errorf("marshaling config: %w", err) + if added == 0 && mergeInit { + fmt.Println("No new devices to add.") + return nil } - if err := os.MkdirAll(filepath.Dir(cfgPath), 0o755); err != nil { - return fmt.Errorf("creating config directory: %w", err) + if err := cfg.Save(); err != nil { + return fmt.Errorf("saving config: %w", err) } - if err := os.WriteFile(cfgPath, data, 0o644); err != nil { - return fmt.Errorf("writing config: %w", err) - } - - fmt.Printf("Config written to %s\n", cfgPath) + fmt.Printf("Config written to %s (%d device(s) added)\n", cfgPath, added) return nil } @@ -99,4 +108,3 @@ func isRootBacking(path dbus.ObjectPath, rootDevices []dbus.ObjectPath) bool { } return false } -