From 1e58693e3c2efd527fa869abbdf324a26f2d46d5 Mon Sep 17 00:00:00 2001 From: Toby Padilla Date: Sun, 8 Aug 2021 15:49:29 -0500 Subject: [PATCH] Use config.json in config repo for menu and settings --- git/git.go | 20 +++++++ main.go | 3 +- tui/bubble.go | 75 ++++++++++++++++++------- tui/bubbles/selection/bubble.go | 1 - tui/commands.go | 97 +++++++-------------------------- tui/defaults.go | 74 +++++++++++++++++++++++++ 6 files changed, 169 insertions(+), 101 deletions(-) diff --git a/git/git.go b/git/git.go index 3996165a5cde47ed3cbfcae7f5054d64dd9a7257..5858e9c30e10d333cb4cd20489e7274d2c5794b5 100644 --- a/git/git.go +++ b/git/git.go @@ -142,3 +142,23 @@ func (rs *RepoSource) loadRepo(name string, rg *git.Repository) (*Repo, error) { sort.Sort(rs.commits) return r, nil } + +func (r *Repo) LatestFile(path string) (string, error) { + lg, err := r.Repository.Log(&git.LogOptions{}) + if err != nil { + return "", err + } + c, err := lg.Next() + if err != nil { + return "", err + } + f, err := c.File(path) + if err != nil { + return "", nil + } + content, err := f.Contents() + if err != nil { + return "", nil + } + return content, nil +} diff --git a/main.go b/main.go index 537c061f1a55b480c0e341b991cdcdbf2c31d633..fae768a156c2b39d21e290129740bbc017f19615 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( gm "smoothie/server/middleware/git" lm "smoothie/server/middleware/logging" "smoothie/tui" + "time" "github.com/meowgorithm/babyenv" ) @@ -27,7 +28,7 @@ func main() { s, err := server.NewServer( cfg.Port, cfg.KeyPath, - bm.Middleware(tui.SessionHandler(cfg.RepoPath)), + bm.Middleware(tui.SessionHandler(cfg.RepoPath, time.Second*5)), gm.Middleware(cfg.RepoPath, cfg.RepoAuthPath), lm.Middleware(), ) diff --git a/tui/bubble.go b/tui/bubble.go index 4bc44308435a7803317896a1e4b6dc0b4e70277c..8c8c810c7feb613ca33cfa5640aaa14897041edf 100644 --- a/tui/bubble.go +++ b/tui/bubble.go @@ -1,11 +1,13 @@ package tui import ( + "encoding/json" "fmt" "log" "smoothie/git" "smoothie/tui/bubbles/commits" "smoothie/tui/bubbles/selection" + "time" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" @@ -24,14 +26,33 @@ const ( quitState ) +type MenuEntry struct { + Name string `json:"name"` + Repo string `json:"repo"` +} + +type Config struct { + Name string `json:"name"` + ShowAllRepos bool `json:"show_all_repos"` + Menu []MenuEntry `json:"menu"` + RepoSource *git.RepoSource +} + +type SessionConfig struct { + Width int + Height int + WindowChanges <-chan ssh.Window +} + type Bubble struct { + config *Config state sessionState error string width int height int - session ssh.Session windowChanges <-chan ssh.Window repoSource *git.RepoSource + repoMenu []MenuEntry repos []*git.Repo boxes []tea.Model activeBox int @@ -40,25 +61,18 @@ type Bubble struct { readmeViewport *ViewportBubble } -type Config struct { - Width int - Height int - Session ssh.Session - WindowChanges <-chan ssh.Window - RepoSource *git.RepoSource -} - -func NewBubble(cfg Config) *Bubble { +func NewBubble(cfg *Config, sCfg *SessionConfig) *Bubble { b := &Bubble{ - width: cfg.Width, - height: cfg.Height, - windowChanges: cfg.WindowChanges, + config: cfg, + width: sCfg.Width, + height: sCfg.Height, + windowChanges: sCfg.WindowChanges, repoSource: cfg.RepoSource, boxes: make([]tea.Model, 2), readmeViewport: &ViewportBubble{ Viewport: &viewport.Model{ Width: boxRightWidth - horizontalPadding - 2, - Height: cfg.Height - verticalPadding - viewportHeightConstant, + Height: sCfg.Height - verticalPadding - viewportHeightConstant, }, }, } @@ -91,7 +105,7 @@ func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) { b.width = msg.Width b.height = msg.Height case selection.SelectedMsg: - cmds = append(cmds, b.getRepoCmd(b.repos[msg.Index].Name)) + cmds = append(cmds, b.getRepoCmd(b.repoMenu[msg.Index].Repo)) } if b.state == loadedState { ab, cmd := b.boxes[b.activeBox].Update(msg) @@ -114,7 +128,7 @@ func (b *Bubble) viewForBox(i int, width int) string { } func (b *Bubble) View() string { - h := headerStyle.Width(b.width - horizontalPadding).Render("Charm Beta") + h := headerStyle.Width(b.width - horizontalPadding).Render(b.config.Name) f := footerStyle.Render("") s := "" content := "" @@ -147,22 +161,41 @@ func glamourReadme(md string) string { return mdt } -func SessionHandler(reposPath string) func(ssh.Session) (tea.Model, []tea.ProgramOption) { +func SessionHandler(reposPath string, repoPoll time.Duration) func(ssh.Session) (tea.Model, []tea.ProgramOption) { + appCfg := &Config{} rs := git.NewRepoSource(reposPath, glamourReadme) + appCfg.RepoSource = rs + go func() { + for { + _ = rs.LoadRepos() + cr, err := rs.GetRepo("config") + if err != nil { + log.Fatalf("cannot load config repo: %s", err) + } + cs, err := cr.LatestFile("config.json") + err = json.Unmarshal([]byte(cs), appCfg) + time.Sleep(repoPoll) + } + }() + err := createDefaultConfigRepo(rs) + if err != nil { + if err != nil { + log.Fatalf("cannot create config repo: %s", err) + } + } + return func(s ssh.Session) (tea.Model, []tea.ProgramOption) { if len(s.Command()) == 0 { pty, changes, active := s.Pty() if !active { return nil, nil } - cfg := Config{ + cfg := &SessionConfig{ Width: pty.Window.Width, Height: pty.Window.Height, WindowChanges: changes, - RepoSource: rs, - Session: s, } - return NewBubble(cfg), []tea.ProgramOption{tea.WithAltScreen()} + return NewBubble(appCfg, cfg), []tea.ProgramOption{tea.WithAltScreen()} } return nil, nil } diff --git a/tui/bubbles/selection/bubble.go b/tui/bubbles/selection/bubble.go index c052723ed6af6d6c62a8cc832442b4f03e986611..ab4f0c6bdff16af252f5d958632348a0af6970ba 100644 --- a/tui/bubbles/selection/bubble.go +++ b/tui/bubbles/selection/bubble.go @@ -22,7 +22,6 @@ func NewBubble(items []string) *Bubble { NormalStyle: normalStyle, SelectedStyle: selectedStyle, Items: items, - selectedItem: -1, } } diff --git a/tui/commands.go b/tui/commands.go index 704f9072e3958fdeb014e7eea7a6abd092649fb7..a32ad7d4c89d7fd75c307d80d8014e8df7224e92 100644 --- a/tui/commands.go +++ b/tui/commands.go @@ -1,15 +1,10 @@ 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{} @@ -27,78 +22,26 @@ func (b *Bubble) windowChangesCmd() tea.Msg { } 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} - } - - // 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() - + mes := make([]MenuEntry, 0) rs := make([]string, 0) - for _, r := range b.repos { - rs = append(rs, r.Name) + for _, me := range b.config.Menu { + mes = append(mes, me) + } + if b.config.ShowAllRepos { + OUTER: + for _, r := range b.repos { + for _, me := range mes { + if r.Name == me.Repo { + continue OUTER + } + } + mes = append(mes, MenuEntry{Name: r.Name, Repo: r.Name}) + } + } + b.repoMenu = mes + for _, me := range mes { + rs = append(rs, me.Name) } b.repoSelect = selection.NewBubble(rs) b.boxes[0] = b.repoSelect @@ -107,10 +50,8 @@ func (b *Bubble) loadGitCmd() tea.Msg { boxRightWidth-horizontalPadding-2, b.repoSource.GetCommits(200), ) - b.boxes[1] = b.commitsLog - b.activeBox = 0 b.state = loadedState - return nil + return b.getRepoCmd("config")() } func (b *Bubble) getRepoCmd(name string) tea.Cmd { diff --git a/tui/defaults.go b/tui/defaults.go index e6200c18a72275b8a9e0195b0b1ac54eeb3e384d..6724b8101a430fe080e08ff2795ac2b6c0bac983 100644 --- a/tui/defaults.go +++ b/tui/defaults.go @@ -1,5 +1,14 @@ package tui +import ( + "os" + "path/filepath" + "smoothie/git" + + gg "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/object" +) + const defaultReadme = "# Smoothie\nWelcome to Smoothie. To setup your own configuration, please clone this repo." const defaultConfig = `{ @@ -13,3 +22,68 @@ const defaultConfig = `{ } ] }` + +func createFile(path string, content string) error { + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + _, err = f.WriteString(content) + if err != nil { + return err + } + return f.Sync() +} + +func createDefaultConfigRepo(rs *git.RepoSource) error { + cn := "config" + err := rs.LoadRepos() + cr, err := rs.GetRepo(cn) + if err == git.ErrMissingRepo { + cr, err = rs.InitRepo(cn, false) + if err != nil { + return err + } + + rp := filepath.Join(rs.Path, cn, "README.md") + err = createFile(rp, defaultReadme) + if err != nil { + return err + } + cp := filepath.Join(rs.Path, cn, "config.json") + err = createFile(cp, defaultConfig) + if err != nil { + return err + } + wt, err := cr.Repository.Worktree() + if err != nil { + return err + } + _, err = wt.Add("README.md") + if err != nil { + return err + } + _, err = wt.Add("config.json") + if err != nil { + return err + } + _, err = wt.Commit("Default init", &gg.CommitOptions{ + All: true, + Author: &object.Signature{ + Name: "Smoothie Server", + Email: "vt100@charm.sh", + }, + }) + if err != nil { + return err + } + err = rs.LoadRepos() + if err != nil { + return err + } + } else if err != nil { + return err + } + return nil +}