diff --git a/internal/homesick/cli/cli.go b/internal/homesick/cli/cli.go index d18dd41..9df13d2 100644 --- a/internal/homesick/cli/cli.go +++ b/internal/homesick/cli/cli.go @@ -176,7 +176,9 @@ type cdCmd struct { Castle string `arg:"" optional:"" name:"CASTLE" help:"Castle name."` } -type openCmd struct{} +type openCmd struct { + Castle string `arg:"" optional:"" name:"CASTLE" help:"Castle name."` +} type execCmd struct{} @@ -186,20 +188,22 @@ type rcCmd struct { Castle string `arg:"" optional:"" name:"CASTLE" help:"Castle name."` } -type generateCmd struct{} +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 *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) } -func (c *destroyCmd) Run(app *core.App) error { return app.Destroy(defaultCastle(c.Castle)) } -func (c *cdCmd) Run(app *core.App) error { return app.ShowPath(defaultCastle(c.Castle)) } -func (c *openCmd) Run() error { return notImplemented("open") } -func (c *execCmd) Run() error { return notImplemented("exec") } -func (c *execAllCmd) Run() error { return notImplemented("exec_all") } -func (c *rcCmd) Run(app *core.App) error { return app.Rc(defaultCastle(c.Castle)) } -func (c *generateCmd) Run() error { return notImplemented("generate") } +func (c *destroyCmd) Run(app *core.App) error { return app.Destroy(defaultCastle(c.Castle)) } +func (c *cdCmd) Run(app *core.App) error { return app.ShowPath(defaultCastle(c.Castle)) } +func (c *openCmd) Run(app *core.App) error { return app.Open(defaultCastle(c.Castle)) } +func (c *execCmd) Run() error { return notImplemented("exec") } +func (c *execAllCmd) Run() error { return notImplemented("exec_all") } +func (c *rcCmd) Run(app *core.App) error { return app.Rc(defaultCastle(c.Castle)) } +func (c *generateCmd) Run(app *core.App) error { return app.Generate(c.Path) } func defaultCastle(castle string) string { if strings.TrimSpace(castle) == "" { diff --git a/internal/homesick/core/core.go b/internal/homesick/core/core.go index a16cd6e..b09d441 100644 --- a/internal/homesick/core/core.go +++ b/internal/homesick/core/core.go @@ -192,6 +192,68 @@ func (a *App) Destroy(castle string) error { return os.RemoveAll(castleRoot) } +func (a *App) Open(castle string) error { + if strings.TrimSpace(castle) == "" { + castle = "dotfiles" + } + + editor := strings.TrimSpace(os.Getenv("EDITOR")) + if editor == "" { + return errors.New("the $EDITOR environment variable must be set to use this command") + } + + castleHome := filepath.Join(a.ReposDir, castle, "home") + if info, err := os.Stat(castleHome); err != nil || !info.IsDir() { + return fmt.Errorf("could not open %s, expected %s to exist and contain dotfiles", castle, castleHome) + } + + castleRoot := filepath.Join(a.ReposDir, castle) + cmd := exec.Command("sh", "-c", editor+" .") + cmd.Dir = castleRoot + cmd.Stdout = a.Stdout + cmd.Stderr = a.Stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("open failed: %w", err) + } + + return nil +} + +func (a *App) Generate(castlePath string) error { + trimmed := strings.TrimSpace(castlePath) + if trimmed == "" { + return errors.New("generate requires PATH") + } + + absCastle, err := filepath.Abs(trimmed) + if err != nil { + return err + } + + if err := os.MkdirAll(absCastle, 0o755); err != nil { + return err + } + + if err := runGitWithIO(absCastle, a.Stdout, a.Stderr, "init"); err != nil { + return err + } + + githubUser := "" + if out, cfgErr := gitOutput(absCastle, "config", "github.user"); cfgErr == nil { + githubUser = strings.TrimSpace(out) + } + + if githubUser != "" { + repoName := filepath.Base(absCastle) + url := fmt.Sprintf("git@github.com:%s/%s.git", githubUser, repoName) + if err := runGitWithIO(absCastle, a.Stdout, a.Stderr, "remote", "add", "origin", url); err != nil { + return err + } + } + + return os.MkdirAll(filepath.Join(absCastle, "home"), 0o755) +} + func (a *App) Link(castle string) error { if strings.TrimSpace(castle) == "" { castle = "dotfiles"