diff --git a/internal/homesick/cli/cli.go b/internal/homesick/cli/cli.go index 0b5d0df..90acf6c 100644 --- a/internal/homesick/cli/cli.go +++ b/internal/homesick/cli/cli.go @@ -156,6 +156,7 @@ func (c *versionCmd) Run(app *core.App) error { } type pullCmd struct { + All bool `help:"Pull all castles."` Castle string `arg:"" optional:"" name:"CASTLE" help:"Castle name."` } @@ -197,7 +198,15 @@ type generateCmd struct { Path string `arg:"" name:"PATH" help:"Path to generate a homesick-ready castle repository."` } -func (c *pullCmd) Run(app *core.App) error { return app.Pull(defaultCastle(c.Castle)) } +func (c *pullCmd) Run(app *core.App) error { + if c.All { + if strings.TrimSpace(c.Castle) != "" { + return errors.New("pull accepts either --all or CASTLE, not both") + } + return app.PullAll() + } + return app.Pull(defaultCastle(c.Castle)) +} func (c *pushCmd) Run(app *core.App) error { return app.Push(defaultCastle(c.Castle)) } func (c *commitCmd) Run(app *core.App) error { return app.Commit(defaultCastle(c.Castle), c.Message) diff --git a/internal/homesick/core/core.go b/internal/homesick/core/core.go index 22b4124..876ab1a 100644 --- a/internal/homesick/core/core.go +++ b/internal/homesick/core/core.go @@ -140,6 +140,45 @@ func (a *App) Pull(castle string) error { return runGitWithIO(filepath.Join(a.ReposDir, castle), a.Stdout, a.Stderr, "pull") } +func (a *App) PullAll() error { + if _, err := os.Stat(a.ReposDir); err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil + } + return err + } + + var castles []string + err := filepath.WalkDir(a.ReposDir, func(path string, d fs.DirEntry, walkErr error) error { + if walkErr != nil { + return walkErr + } + if !d.IsDir() || d.Name() != ".git" { + return nil + } + + castleRoot := filepath.Dir(path) + rel, err := filepath.Rel(a.ReposDir, castleRoot) + if err != nil { + return err + } + castles = append(castles, rel) + return filepath.SkipDir + }) + if err != nil { + return err + } + + sort.Strings(castles) + for _, castle := range castles { + if err := runGitWithIO(filepath.Join(a.ReposDir, castle), a.Stdout, a.Stderr, "pull"); err != nil { + return fmt.Errorf("pull --all failed for %q: %w", castle, err) + } + } + + return nil +} + func (a *App) Push(castle string) error { if strings.TrimSpace(castle) == "" { castle = "dotfiles"