45ec4a89b24f7fbeeda727dbcb8a3db59c4efd63
- Author
- Toby Padilla <toby@charm.sh>
- Committer
- Toby Padilla <toby@charm.sh>
- Date
Message
Diff
This diff is truncated to protect this page.
1diff --git a/git/git.go b/git/git.go
2index 87a9658ef1e2816928674ef2737e6e5983366793..3996165a5cde47ed3cbfcae7f5054d64dd9a7257 100644
3--- a/git/git.go
4+++ b/git/git.go
5@@ -37,21 +37,19 @@ func (cl CommitLog) Less(i, j int) bool {
6 }
7
8 type RepoSource struct {
9+ Path string
10 mtx sync.Mutex
11- path string
12 repos []*Repo
13 commits CommitLog
14 readmeTransform ReadmeTransform
15 }
16
17-func NewRepoSource(repoPath string, poll time.Duration, rf ReadmeTransform) *RepoSource {
18- rs := &RepoSource{path: repoPath, readmeTransform: rf}
19- go func() {
20- for {
21- rs.loadRepos()
22- time.Sleep(poll)
23- }
24- }()
25+func NewRepoSource(repoPath string, rf ReadmeTransform) *RepoSource {
26+ err := os.MkdirAll(repoPath, os.ModeDir|os.FileMode(0700))
27+ if err != nil {
28+ log.Fatal(err)
29+ }
30+ rs := &RepoSource{Path: repoPath, readmeTransform: rf}
31 return rs
32 }
33
34@@ -72,6 +70,21 @@ func (rs *RepoSource) GetRepo(name string) (*Repo, error) {
35 return nil, ErrMissingRepo
36 }
37
38+func (rs *RepoSource) InitRepo(name string, bare bool) (*Repo, error) {
39+ rs.mtx.Lock()
40+ defer rs.mtx.Unlock()
41+ rg, err := git.PlainInit(rs.Path+string(os.PathSeparator)+name, bare)
42+ if err != nil {
43+ return nil, err
44+ }
45+ r := &Repo{
46+ Name: name,
47+ Repository: rg,
48+ }
49+ rs.repos = append(rs.repos, r)
50+ return r, nil
51+}
52+
53 func (rs *RepoSource) GetCommits(limit int) []RepoCommit {
54 rs.mtx.Lock()
55 defer rs.mtx.Unlock()
56@@ -81,42 +94,51 @@ func (rs *RepoSource) GetCommits(limit int) []RepoCommit {
57 return rs.commits[:limit]
58 }
59
60-func (rs *RepoSource) loadRepos() {
61+func (rs *RepoSource) LoadRepos() error {
62 rs.mtx.Lock()
63 defer rs.mtx.Unlock()
64- rd, err := os.ReadDir(rs.path)
65+ rd, err := os.ReadDir(rs.Path)
66 if err != nil {
67- return
68+ return err
69 }
70 rs.repos = make([]*Repo, 0)
71 rs.commits = make([]RepoCommit, 0)
72 for _, de := range rd {
73 rn := de.Name()
74- r := &Repo{Name: rn}
75- rg, err := git.PlainOpen(rs.path + string(os.PathSeparator) + rn)
76+ rg, err := git.PlainOpen(rs.Path + string(os.PathSeparator) + rn)
77 if err != nil {
78- log.Fatal(err)
79+ return err
80 }
81- r.Repository = rg
82- l, err := rg.Log(&git.LogOptions{All: true})
83- if err != nil {
84- log.Fatal(err)
85- }
86- l.ForEach(func(c *object.Commit) error {
87- if r.LastUpdated == nil {
88- r.LastUpdated = &c.Author.When
89- rf, err := c.File("README.md")
90+ r, err := rs.loadRepo(rn, rg)
91+ rs.repos = append(rs.repos, r)
92+ }
93+ return nil
94+}
95+
96+func (rs *RepoSource) loadRepo(name string, rg *git.Repository) (*Repo, error) {
97+ r := &Repo{Name: name}
98+ r.Repository = rg
99+ l, err := rg.Log(&git.LogOptions{All: true})
100+ if err != nil {
101+ return nil, err
102+ }
103+ err = l.ForEach(func(c *object.Commit) error {
104+ if r.LastUpdated == nil {
105diff --git a/tui/bubble.go b/tui/bubble.go
106index d16ca4bc0411eb73d18bfd73c9c4c93b5bdf8086..4bc44308435a7803317896a1e4b6dc0b4e70277c 100644
107--- a/tui/bubble.go
108+++ b/tui/bubble.go
109@@ -6,7 +6,6 @@ import (
110 "smoothie/git"
111 "smoothie/tui/bubbles/commits"
112 "smoothie/tui/bubbles/selection"
113- "time"
114
115 "github.com/charmbracelet/bubbles/viewport"
116 tea "github.com/charmbracelet/bubbletea"
117@@ -30,6 +29,7 @@ type Bubble struct {
118 error string
119 width int
120 height int
121+ session ssh.Session
122 windowChanges <-chan ssh.Window
123 repoSource *git.RepoSource
124 repos []*git.Repo
125@@ -40,17 +40,25 @@ type Bubble struct {
126 readmeViewport *ViewportBubble
127 }
128
129-func NewBubble(width int, height int, windowChanges <-chan ssh.Window, repoSource *git.RepoSource) *Bubble {
130+type Config struct {
131+ Width int
132+ Height int
133+ Session ssh.Session
134+ WindowChanges <-chan ssh.Window
135+ RepoSource *git.RepoSource
136+}
137+
138+func NewBubble(cfg Config) *Bubble {
139 b := &Bubble{
140- width: width,
141- height: height,
142- windowChanges: windowChanges,
143- repoSource: repoSource,
144+ width: cfg.Width,
145+ height: cfg.Height,
146+ windowChanges: cfg.WindowChanges,
147+ repoSource: cfg.RepoSource,
148 boxes: make([]tea.Model, 2),
149 readmeViewport: &ViewportBubble{
150 Viewport: &viewport.Model{
151 Width: boxRightWidth - horizontalPadding - 2,
152- Height: height - verticalPadding - viewportHeightConstant,
153+ Height: cfg.Height - verticalPadding - viewportHeightConstant,
154 },
155 },
156 }
157@@ -140,19 +148,21 @@ func glamourReadme(md string) string {
158 }
159
160 func SessionHandler(reposPath string) func(ssh.Session) (tea.Model, []tea.ProgramOption) {
161- rs := git.NewRepoSource(reposPath, time.Second*10, glamourReadme)
162+ rs := git.NewRepoSource(reposPath, glamourReadme)
163 return func(s ssh.Session) (tea.Model, []tea.ProgramOption) {
164 if len(s.Command()) == 0 {
165 pty, changes, active := s.Pty()
166 if !active {
167 return nil, nil
168 }
169- return NewBubble(
170- pty.Window.Width,
171- pty.Window.Height,
172- changes,
173- rs),
174- []tea.ProgramOption{tea.WithAltScreen()}
175+ cfg := Config{
176+ Width: pty.Window.Width,
177+ Height: pty.Window.Height,
178+ WindowChanges: changes,
179+ RepoSource: rs,
180+ Session: s,
181+ }
182+ return NewBubble(cfg), []tea.ProgramOption{tea.WithAltScreen()}
183 }
184 return nil, nil
185 }
186diff --git a/tui/bubbles/selection/bubble.go b/tui/bubbles/selection/bubble.go
187index b957872e07b5d28a266b29fb9c280f182db0f8f7..c052723ed6af6d6c62a8cc832442b4f03e986611 100644
188--- a/tui/bubbles/selection/bubble.go
189+++ b/tui/bubbles/selection/bubble.go
190@@ -63,8 +63,11 @@ func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
191 }
192
193 func (b *Bubble) sendMessage() tea.Msg {
194- return SelectedMsg{
195- Name: b.Items[b.selectedItem],
196- Index: b.selectedItem,
197+ if b.selectedItem >= 0 && b.selectedItem < len(b.Items) {
198+ return SelectedMsg{
199+ Name: b.Items[b.selectedItem],
200+ Index: b.selectedItem,
201+ }
202 }
203+ return nil
204 }
205diff --git a/tui/commands.go b/tui/commands.go
206index d1749ae0e9d103ca481a8f58e036212492abb642..704f9072e3958fdeb014e7eea7a6abd092649fb7 100644
207--- a/tui/commands.go
208+++ b/tui/commands.go
209@@ -1,10 +1,15 @@
210 package tui
211
212 import (
213+ "os"
214+ "path/filepath"
215+ "smoothie/git"
216 "smoothie/tui/bubbles/commits"
217 "smoothie/tui/bubbles/selection"
218
219 tea "github.com/charmbracelet/bubbletea"
220+ gg "github.com/go-git/go-git/v5"
221+ "github.com/go-git/go-git/v5/plumbing/object"
222 )
223
224 type windowMsg struct{}
225@@ -21,24 +26,76 @@ func (b *Bubble) windowChangesCmd() tea.Msg {
226 return windowMsg{}
227 }
228
229-func (b *Bubble) getRepoCmd(name string) tea.Cmd {
230- return func() tea.Msg {
231- r, err := b.repoSource.GetRepo(name)
232+func (b *Bubble) loadGitCmd() tea.Msg {
233+ cn := "config"
234+ err := b.repoSource.LoadRepos()
235+ cr, err := b.repoSource.GetRepo(cn)
236+ if err == git.ErrMissingRepo {
237+ cr, err = b.repoSource.InitRepo(cn, false)
238 if err != nil {
239 return errMsg{err}
240 }
241- b.readmeViewport.Viewport.GotoTop()
242- b.readmeViewport.Viewport.Height = b.height - verticalPadding - viewportHeightConstant
243- b.readmeViewport.Viewport.Width = boxLeftWidth - 2
244- b.readmeViewport.Viewport.SetContent(r.Readme)
245- b.boxes[1] = b.readmeViewport
246- b.activeBox = 1
247- return nil
248- }
249-}
250
251-func (b *Bubble) loadGitCmd() tea.Msg {
252+ // Add default README and config
253+ rp := filepath.Join(b.repoSource.Path, cn, "README.md")
254+ rf, err := os.Create(rp)
255+ if err != nil {
256+ return errMsg{err}
257+ }
258+ defer rf.Close()
259+ _, err = rf.WriteString(defaultReadme)
260+ if err != nil {
261+ return errMsg{err}
262+ }
263+ err = rf.Sync()
264+ if err != nil {
265+ return errMsg{err}
266+ }
267+ cp := filepath.Join(b.repoSource.Path, cn, "config.json")
268+ cf, err := os.Create(cp)
269+ if err != nil {
270+ return errMsg{err}
271+ }
272+ defer cf.Close()
273+ _, err = cf.WriteString(defaultConfig)
274+ if err != nil {
275+ return errMsg{err}
276+ }
277+ err = cf.Sync()
278+ if err != nil {
279+ return errMsg{err}
280+ }
281+ wt, err := cr.Repository.Worktree()
282+ if err != nil {
283+ return errMsg{err}
284+ }
285+ _, err = wt.Add("README.md")
286+ if err != nil {
287+ return errMsg{err}
288+ }
289+ _, err = wt.Add("config.json")
290+ if err != nil {
291+ return errMsg{err}
292+ }
293+ _, err = wt.Commit("Default init", &gg.CommitOptions{
294+ All: true,
295+ Author: &object.Signature{
296+ Name: "Smoothie Server",
297+ Email: "vt100@charm.sh",
298+ },
299+ })
300+ if err != nil {
301+ return errMsg{err}
302+ }
303+ err = b.repoSource.LoadRepos()
304+ if err != nil {
305+ return errMsg{err}
306+ }
307+ } else if err != nil {
308+ return errMsg{err}
309diff --git a/tui/defaults.go b/tui/defaults.go
310new file mode 100644
311index 0000000000000000000000000000000000000000..e6200c18a72275b8a9e0195b0b1ac54eeb3e384d
312--- /dev/null
313+++ b/tui/defaults.go
314@@ -0,0 +1,15 @@
315+package tui
316+
317+const defaultReadme = "# Smoothie\nWelcome to Smoothie. To setup your own configuration, please clone this repo."
318+
319+const defaultConfig = `{
320+ "name": "Smoothie",
321+ "show_all_repos": true,
322+ "menu": [
323+ {
324+ "name": "Home",
325+ "repo": "config",
326+ "note": ""
327+ }
328+ ]
329+}`