From 45ec4a89b24f7fbeeda727dbcb8a3db59c4efd63 Mon Sep 17 00:00:00 2001 From: Toby Padilla Date: Sat, 7 Aug 2021 18:44:35 -0500 Subject: [PATCH] Create a config repo with defaults --- git/git.go | 88 ++++++++++++++++++----------- tui/bubble.go | 38 ++++++++----- tui/bubbles/selection/bubble.go | 9 ++- tui/commands.go | 99 ++++++++++++++++++++++++++++----- tui/defaults.go | 15 +++++ 5 files changed, 186 insertions(+), 63 deletions(-) create mode 100644 tui/defaults.go diff --git a/git/git.go b/git/git.go index 87a9658ef1e2816928674ef2737e6e5983366793..3996165a5cde47ed3cbfcae7f5054d64dd9a7257 100644 --- a/git/git.go +++ b/git/git.go @@ -37,21 +37,19 @@ func (cl CommitLog) Less(i, j int) bool { } type RepoSource struct { + Path string mtx sync.Mutex - path string repos []*Repo commits CommitLog readmeTransform ReadmeTransform } -func NewRepoSource(repoPath string, poll time.Duration, rf ReadmeTransform) *RepoSource { - rs := &RepoSource{path: repoPath, readmeTransform: rf} - go func() { - for { - rs.loadRepos() - time.Sleep(poll) - } - }() +func NewRepoSource(repoPath string, rf ReadmeTransform) *RepoSource { + err := os.MkdirAll(repoPath, os.ModeDir|os.FileMode(0700)) + if err != nil { + log.Fatal(err) + } + rs := &RepoSource{Path: repoPath, readmeTransform: rf} return rs } @@ -72,6 +70,21 @@ func (rs *RepoSource) GetRepo(name string) (*Repo, error) { return nil, ErrMissingRepo } +func (rs *RepoSource) InitRepo(name string, bare bool) (*Repo, error) { + rs.mtx.Lock() + defer rs.mtx.Unlock() + rg, err := git.PlainInit(rs.Path+string(os.PathSeparator)+name, bare) + if err != nil { + return nil, err + } + r := &Repo{ + Name: name, + Repository: rg, + } + rs.repos = append(rs.repos, r) + return r, nil +} + func (rs *RepoSource) GetCommits(limit int) []RepoCommit { rs.mtx.Lock() defer rs.mtx.Unlock() @@ -81,42 +94,51 @@ func (rs *RepoSource) GetCommits(limit int) []RepoCommit { return rs.commits[:limit] } -func (rs *RepoSource) loadRepos() { +func (rs *RepoSource) LoadRepos() error { rs.mtx.Lock() defer rs.mtx.Unlock() - rd, err := os.ReadDir(rs.path) + rd, err := os.ReadDir(rs.Path) if err != nil { - return + return err } rs.repos = make([]*Repo, 0) rs.commits = make([]RepoCommit, 0) for _, de := range rd { rn := de.Name() - r := &Repo{Name: rn} - rg, err := git.PlainOpen(rs.path + string(os.PathSeparator) + rn) + rg, err := git.PlainOpen(rs.Path + string(os.PathSeparator) + rn) if err != nil { - log.Fatal(err) + return err } - r.Repository = rg - l, err := rg.Log(&git.LogOptions{All: true}) - if err != nil { - log.Fatal(err) - } - l.ForEach(func(c *object.Commit) error { - if r.LastUpdated == nil { - r.LastUpdated = &c.Author.When - rf, err := c.File("README.md") + r, err := rs.loadRepo(rn, rg) + rs.repos = append(rs.repos, r) + } + return nil +} + +func (rs *RepoSource) loadRepo(name string, rg *git.Repository) (*Repo, error) { + r := &Repo{Name: name} + r.Repository = rg + l, err := rg.Log(&git.LogOptions{All: true}) + if err != nil { + return nil, err + } + err = l.ForEach(func(c *object.Commit) error { + if r.LastUpdated == nil { + r.LastUpdated = &c.Author.When + rf, err := c.File("README.md") + if err == nil { + rmd, err := rf.Contents() if err == nil { - rmd, err := rf.Contents() - if err == nil { - r.Readme = rs.readmeTransform(rmd) - } + r.Readme = rs.readmeTransform(rmd) } } - rs.commits = append(rs.commits, RepoCommit{Name: rn, Commit: c}) - return nil - }) - sort.Sort(rs.commits) - rs.repos = append(rs.repos, r) + } + rs.commits = append(rs.commits, RepoCommit{Name: name, Commit: c}) + return nil + }) + if err != nil { + return nil, err } + sort.Sort(rs.commits) + return r, nil } diff --git a/tui/bubble.go b/tui/bubble.go index d16ca4bc0411eb73d18bfd73c9c4c93b5bdf8086..4bc44308435a7803317896a1e4b6dc0b4e70277c 100644 --- a/tui/bubble.go +++ b/tui/bubble.go @@ -6,7 +6,6 @@ import ( "smoothie/git" "smoothie/tui/bubbles/commits" "smoothie/tui/bubbles/selection" - "time" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" @@ -30,6 +29,7 @@ type Bubble struct { error string width int height int + session ssh.Session windowChanges <-chan ssh.Window repoSource *git.RepoSource repos []*git.Repo @@ -40,17 +40,25 @@ type Bubble struct { readmeViewport *ViewportBubble } -func NewBubble(width int, height int, windowChanges <-chan ssh.Window, repoSource *git.RepoSource) *Bubble { +type Config struct { + Width int + Height int + Session ssh.Session + WindowChanges <-chan ssh.Window + RepoSource *git.RepoSource +} + +func NewBubble(cfg Config) *Bubble { b := &Bubble{ - width: width, - height: height, - windowChanges: windowChanges, - repoSource: repoSource, + width: cfg.Width, + height: cfg.Height, + windowChanges: cfg.WindowChanges, + repoSource: cfg.RepoSource, boxes: make([]tea.Model, 2), readmeViewport: &ViewportBubble{ Viewport: &viewport.Model{ Width: boxRightWidth - horizontalPadding - 2, - Height: height - verticalPadding - viewportHeightConstant, + Height: cfg.Height - verticalPadding - viewportHeightConstant, }, }, } @@ -140,19 +148,21 @@ func glamourReadme(md string) string { } func SessionHandler(reposPath string) func(ssh.Session) (tea.Model, []tea.ProgramOption) { - rs := git.NewRepoSource(reposPath, time.Second*10, glamourReadme) + rs := git.NewRepoSource(reposPath, glamourReadme) return func(s ssh.Session) (tea.Model, []tea.ProgramOption) { if len(s.Command()) == 0 { pty, changes, active := s.Pty() if !active { return nil, nil } - return NewBubble( - pty.Window.Width, - pty.Window.Height, - changes, - rs), - []tea.ProgramOption{tea.WithAltScreen()} + cfg := Config{ + Width: pty.Window.Width, + Height: pty.Window.Height, + WindowChanges: changes, + RepoSource: rs, + Session: s, + } + return NewBubble(cfg), []tea.ProgramOption{tea.WithAltScreen()} } return nil, nil } diff --git a/tui/bubbles/selection/bubble.go b/tui/bubbles/selection/bubble.go index b957872e07b5d28a266b29fb9c280f182db0f8f7..c052723ed6af6d6c62a8cc832442b4f03e986611 100644 --- a/tui/bubbles/selection/bubble.go +++ b/tui/bubbles/selection/bubble.go @@ -63,8 +63,11 @@ func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } func (b *Bubble) sendMessage() tea.Msg { - return SelectedMsg{ - Name: b.Items[b.selectedItem], - Index: b.selectedItem, + if b.selectedItem >= 0 && b.selectedItem < len(b.Items) { + return SelectedMsg{ + Name: b.Items[b.selectedItem], + Index: b.selectedItem, + } } + return nil } diff --git a/tui/commands.go b/tui/commands.go index d1749ae0e9d103ca481a8f58e036212492abb642..704f9072e3958fdeb014e7eea7a6abd092649fb7 100644 --- a/tui/commands.go +++ b/tui/commands.go @@ -1,10 +1,15 @@ package tui import ( + "os" + "path/filepath" + "smoothie/git" "smoothie/tui/bubbles/commits" "smoothie/tui/bubbles/selection" tea "github.com/charmbracelet/bubbletea" + gg "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/object" ) type windowMsg struct{} @@ -21,24 +26,76 @@ func (b *Bubble) windowChangesCmd() tea.Msg { return windowMsg{} } -func (b *Bubble) getRepoCmd(name string) tea.Cmd { - return func() tea.Msg { - r, err := b.repoSource.GetRepo(name) +func (b *Bubble) loadGitCmd() tea.Msg { + cn := "config" + err := b.repoSource.LoadRepos() + cr, err := b.repoSource.GetRepo(cn) + if err == git.ErrMissingRepo { + cr, err = b.repoSource.InitRepo(cn, false) if err != nil { return errMsg{err} } - b.readmeViewport.Viewport.GotoTop() - b.readmeViewport.Viewport.Height = b.height - verticalPadding - viewportHeightConstant - b.readmeViewport.Viewport.Width = boxLeftWidth - 2 - b.readmeViewport.Viewport.SetContent(r.Readme) - b.boxes[1] = b.readmeViewport - b.activeBox = 1 - return nil - } -} -func (b *Bubble) loadGitCmd() tea.Msg { + // Add default README and config + rp := filepath.Join(b.repoSource.Path, cn, "README.md") + rf, err := os.Create(rp) + if err != nil { + return errMsg{err} + } + defer rf.Close() + _, err = rf.WriteString(defaultReadme) + if err != nil { + return errMsg{err} + } + err = rf.Sync() + if err != nil { + return errMsg{err} + } + cp := filepath.Join(b.repoSource.Path, cn, "config.json") + cf, err := os.Create(cp) + if err != nil { + return errMsg{err} + } + defer cf.Close() + _, err = cf.WriteString(defaultConfig) + if err != nil { + return errMsg{err} + } + err = cf.Sync() + if err != nil { + return errMsg{err} + } + wt, err := cr.Repository.Worktree() + if err != nil { + return errMsg{err} + } + _, err = wt.Add("README.md") + if err != nil { + return errMsg{err} + } + _, err = wt.Add("config.json") + if err != nil { + return errMsg{err} + } + _, err = wt.Commit("Default init", &gg.CommitOptions{ + All: true, + Author: &object.Signature{ + Name: "Smoothie Server", + Email: "vt100@charm.sh", + }, + }) + if err != nil { + return errMsg{err} + } + err = b.repoSource.LoadRepos() + if err != nil { + return errMsg{err} + } + } else if err != nil { + return errMsg{err} + } b.repos = b.repoSource.AllRepos() + rs := make([]string, 0) for _, r := range b.repos { rs = append(rs, r.Name) @@ -55,3 +112,19 @@ func (b *Bubble) loadGitCmd() tea.Msg { b.state = loadedState return nil } + +func (b *Bubble) getRepoCmd(name string) tea.Cmd { + return func() tea.Msg { + r, err := b.repoSource.GetRepo(name) + if err != nil { + return errMsg{err} + } + b.readmeViewport.Viewport.GotoTop() + b.readmeViewport.Viewport.Height = b.height - verticalPadding - viewportHeightConstant + b.readmeViewport.Viewport.Width = boxLeftWidth - 2 + b.readmeViewport.Viewport.SetContent(r.Readme) + b.boxes[1] = b.readmeViewport + b.activeBox = 1 + return nil + } +} diff --git a/tui/defaults.go b/tui/defaults.go new file mode 100644 index 0000000000000000000000000000000000000000..e6200c18a72275b8a9e0195b0b1ac54eeb3e384d --- /dev/null +++ b/tui/defaults.go @@ -0,0 +1,15 @@ +package tui + +const defaultReadme = "# Smoothie\nWelcome to Smoothie. To setup your own configuration, please clone this repo." + +const defaultConfig = `{ + "name": "Smoothie", + "show_all_repos": true, + "menu": [ + { + "name": "Home", + "repo": "config", + "note": "" + } + ] +}`