42774038e3a8e5c0913c0f520c9cf7563a84e028

Author
Ayman Bagabas <ayman.bagabas@gmail.com>
Committer
Ayman Bagabas <ayman.bagabas@gmail.com>
Date

Message

feat(server): add tree & blob server commands

Replace show & list

Diff

This diff is truncated to protect this page.

  1diff --git a/git/repo.go b/git/repo.go
  2index 9d1f49d776e763fa2fb30be2c36fcc3f6259305f..e91df362e4558b87ab00ae8a89c5e5c59d2ffd2a 100644
  3--- a/git/repo.go
  4+++ b/git/repo.go
  5@@ -114,6 +114,19 @@ func (r *Repository) References() ([]*Reference, error) {
  6 	return rrefs, nil
  7 }
  8 
  9+// LsTree returns the tree for the given reference.
 10+func (r *Repository) LsTree(ref string) (*Tree, error) {
 11+	tree, err := r.Repository.LsTree(ref)
 12+	if err != nil {
 13+		return nil, err
 14+	}
 15+	return &Tree{
 16+		Tree:       tree,
 17+		Path:       "",
 18+		Repository: r,
 19+	}, nil
 20+}
 21+
 22 // Tree returns the tree for the given reference.
 23 func (r *Repository) Tree(ref *Reference) (*Tree, error) {
 24 	if ref == nil {
 25@@ -123,15 +136,7 @@ func (r *Repository) Tree(ref *Reference) (*Tree, error) {
 26 		}
 27 		ref = rref
 28 	}
 29-	tree, err := r.LsTree(ref.Hash.String())
 30-	if err != nil {
 31-		return nil, err
 32-	}
 33-	return &Tree{
 34-		Tree:       tree,
 35-		Path:       "",
 36-		Repository: r,
 37-	}, nil
 38+	return r.LsTree(ref.Hash.String())
 39 }
 40 
 41 // TreePath returns the tree for the given path.
 42diff --git a/server/cmd/show.go b/server/cmd/blob.go
 43rename from server/cmd/show.go
 44rename to server/cmd/blob.go
 45index 14981dc27d95c1ecaa97ec189c3b615e57dbf5b8..58e2361f9342c32d1c482b8b2f5ad2031104c00f 100644
 46--- a/server/cmd/show.go
 47+++ b/server/cmd/blob.go
 48@@ -7,7 +7,7 @@ import (
 49 	"github.com/alecthomas/chroma/lexers"
 50 	gansi "github.com/charmbracelet/glamour/ansi"
 51 	"github.com/charmbracelet/lipgloss"
 52-	"github.com/charmbracelet/soft-serve/server/backend"
 53+	"github.com/charmbracelet/soft-serve/git"
 54 	"github.com/charmbracelet/soft-serve/ui/common"
 55 	"github.com/muesli/termenv"
 56 	"github.com/spf13/cobra"
 57@@ -21,60 +21,99 @@ var (
 58 	filemodeStyle  = lipgloss.NewStyle().Foreground(lipgloss.Color("#777777"))
 59 )
 60 
 61-// showCommand returns a command that prints the contents of a file.
 62-func showCommand() *cobra.Command {
 63+// blobCommand returns a command that prints the contents of a file.
 64+func blobCommand() *cobra.Command {
 65 	var linenumber bool
 66 	var color bool
 67+	var raw bool
 68 
 69-	showCmd := &cobra.Command{
 70-		Use:               "show PATH",
 71-		Aliases:           []string{"cat"},
 72-		Short:             "Read the contents of file at path.",
 73-		Args:              cobra.ExactArgs(1),
 74+	cmd := &cobra.Command{
 75+		Use:               "blob REPOSITORY [REFERENCE] [PATH]",
 76+		Aliases:           []string{"cat", "show"},
 77+		Short:             "Print out the contents of file at path.",
 78+		Args:              cobra.RangeArgs(1, 3),
 79 		PersistentPreRunE: checkIfReadable,
 80 		RunE: func(cmd *cobra.Command, args []string) error {
 81 			cfg, _ := fromContext(cmd)
 82-			// FIXME: nested repos are not supported.
 83-			ps := strings.Split(args[0], "/")
 84-			rn := strings.TrimSuffix(ps[0], ".git")
 85-			fp := strings.Join(ps[1:], "/")
 86-			var repo backend.Repository
 87-			repoExists := false
 88-			repos, err := cfg.Backend.Repositories()
 89+			rn := args[0]
 90+			ref := ""
 91+			fp := ""
 92+			switch len(args) {
 93+			case 2:
 94+				fp = args[1]
 95+			case 3:
 96+				ref = args[1]
 97+				fp = args[2]
 98+			}
 99+
100+			repo, err := cfg.Backend.Repository(rn)
101 			if err != nil {
102 				return err
103 			}
104-			for _, rp := range repos {
105-				if rp.Name() == rn {
106-					repoExists = true
107-					repo = rp
108-					break
109-				}
110-			}
111-			if !repoExists {
112-				return ErrRepoNotFound
113-			}
114-			c, _, err := backend.LatestFile(repo, fp)
115+
116+			r, err := repo.Open()
117 			if err != nil {
118 				return err
119 			}
120-			if color {
121-				c, err = withFormatting(fp, c)
122+
123+			if ref == "" {
124+				head, err := r.HEAD()
125 				if err != nil {
126 					return err
127 				}
128+				ref = head.Hash.String()
129+			}
130+
131+			tree, err := r.LsTree(ref)
132+			if err != nil {
133+				return err
134+			}
135+
136+			te, err := tree.TreeEntry(fp)
137+			if err != nil {
138+				return err
139+			}
140+
141+			if te.Type() != "blob" {
142+				return git.ErrFileNotFound
143+			}
144+
145+			bts, err := te.Contents()
146+			if err != nil {
147+				return err
148diff --git a/server/cmd/cmd.go b/server/cmd/cmd.go
149index e32836abb31be39b40829fddcb9f8082aaa8b60e..a23ee8afdb5c752b710b576b579aeec1fdbb5b18 100644
150--- a/server/cmd/cmd.go
151+++ b/server/cmd/cmd.go
152@@ -55,8 +55,9 @@ func rootCommand() *cobra.Command {
153 		listCommand(),
154 		privateCommand(),
155 		renameCommand(),
156-		showCommand(),
157+		blobCommand(),
158 		tagCommand(),
159+		treeCommand(),
160 	)
161 
162 	return rootCmd
163diff --git a/server/cmd/list.go b/server/cmd/list.go
164index 86f95d7770087a404814c5c23313a5ec03ac61bc..8ed558a15cdfc55d8c9cd19889b03a258c8988d4 100644
165--- a/server/cmd/list.go
166+++ b/server/cmd/list.go
167@@ -1,11 +1,6 @@
168 package cmd
169 
170 import (
171-	"fmt"
172-	"path/filepath"
173-	"strings"
174-
175-	"github.com/charmbracelet/soft-serve/git"
176 	"github.com/charmbracelet/soft-serve/server/backend"
177 	"github.com/spf13/cobra"
178 )
179@@ -13,80 +8,20 @@ import (
180 // listCommand returns a command that list file or directory at path.
181 func listCommand() *cobra.Command {
182 	listCmd := &cobra.Command{
183-		Use:     "list PATH",
184+		Use:     "list",
185 		Aliases: []string{"ls"},
186-		Short:   "List files at repository.",
187-		Args:    cobra.RangeArgs(0, 1),
188+		Short:   "List repositories.",
189+		Args:    cobra.NoArgs,
190 		RunE: func(cmd *cobra.Command, args []string) error {
191 			cfg, s := fromContext(cmd)
192-			rn := ""
193-			path := ""
194-			ps := []string{}
195-			if len(args) > 0 {
196-				// FIXME: nested repos are not supported.
197-				path = filepath.Clean(args[0])
198-				ps = strings.Split(path, "/")
199-				rn = strings.TrimSuffix(ps[0], ".git")
200-				auth := cfg.Backend.AccessLevel(rn, s.PublicKey())
201-				if auth < backend.ReadOnlyAccess {
202-					return ErrUnauthorized
203-				}
204-			}
205-			if path == "" || path == "." || path == "/" {
206-				repos, err := cfg.Backend.Repositories()
207-				if err != nil {
208-					return err
209-				}
210-				for _, r := range repos {
211-					if cfg.Backend.AccessLevel(r.Name(), s.PublicKey()) >= backend.ReadOnlyAccess {
212-						cmd.Println(r.Name())
213-					}
214-				}
215-				return nil
216-			}
217-			rr, err := cfg.Backend.Repository(rn)
218-			if err != nil {
219-				return err
220-			}
221-			r, err := rr.Open()
222-			if err != nil {
223-				return err
224-			}
225-			head, err := r.HEAD()
226-			if err != nil {
227-				if bs, err := r.Branches(); err != nil && len(bs) == 0 {
228-					return fmt.Errorf("repository is empty")
229-				}
230-				return err
231-			}
232-			tree, err := r.TreePath(head, "")
233+			repos, err := cfg.Backend.Repositories()
234 			if err != nil {
235 				return err
236 			}
237-			subpath := strings.Join(ps[1:], "/")
238-			ents := git.Entries{}
239-			te, err := tree.TreeEntry(subpath)
240-			if err == git.ErrRevisionNotExist {
241-				return ErrFileNotFound
242-			}
243-			if err != nil {
244-				return err
245-			}
246-			if te.Type() == "tree" {
247-				tree, err = tree.SubTree(subpath)
248-				if err != nil {
249-					return err
250+			for _, r := range repos {
251+				if cfg.Backend.AccessLevel(r.Name(), s.PublicKey()) >= backend.ReadOnlyAccess {
252+					cmd.Println(r.Name())
253 				}
254-				ents, err = tree.Entries()
255-				if err != nil {
256-					return err
257-				}
258-			} else {
259-				ents = append(ents, te)
260-			}
261-			ents.Sort()
262-			for _, ent := range ents {
263-				cmd.Printf("%s\t%d\t %s\n", ent.Mode(), ent.Size(), ent.Name())
264 			}
265 			return nil
266 		},
267diff --git a/server/cmd/tree.go b/server/cmd/tree.go
268new file mode 100644
269index 0000000000000000000000000000000000000000..43be304c6a1eb640ed7748d1408ad6b1fdb5b128
270--- /dev/null
271+++ b/server/cmd/tree.go
272@@ -0,0 +1,99 @@
273+package cmd
274+
275+import (
276+	"fmt"
277+
278+	"github.com/charmbracelet/soft-serve/git"
279+	"github.com/dustin/go-humanize"
280+	"github.com/spf13/cobra"
281+)
282+
283+// treeCommand returns a command that list file or directory at path.
284+func treeCommand() *cobra.Command {
285+	cmd := &cobra.Command{
286+		Use:               "tree REPOSITORY [REFERENCE] [PATH]",
287+		Short:             "Print repository tree at path.",
288+		Args:              cobra.RangeArgs(1, 3),
289+		PersistentPreRunE: checkIfReadable,
290+		RunE: func(cmd *cobra.Command, args []string) error {
291+			cfg, _ := fromContext(cmd)
292+			rn := args[0]
293+			path := ""
294+			ref := ""
295+			switch len(args) {
296+			case 2:
297+				path = args[1]
298+			case 3:
299+				ref = args[1]
300+				path = args[2]
301+			}
302+			rr, err := cfg.Backend.Repository(rn)
303+			if err != nil {
304+				return err
305+			}
306+
307+			r, err := rr.Open()
308+			if err != nil {
309+				return err
310+			}
311+
312+			if ref == "" {
313+				head, err := r.HEAD()
314+				if err != nil {
315+					if bs, err := r.Branches(); err != nil && len(bs) == 0 {
316+						return fmt.Errorf("repository is empty")
317+					}
318+					return err
319+				}
320+
321+				ref = head.Hash.String()
322+			}
323+
324+			tree, err := r.LsTree(ref)
325+			if err != nil {
326+				return err
327+			}
328+
329+			ents := git.Entries{}
330+			if path != "" && path != "/" {
331+				te, err := tree.TreeEntry(path)
332+				if err == git.ErrRevisionNotExist {
333+					return ErrFileNotFound
334+				}
335+				if err != nil {
336+					return err
337+				}
338+				if te.Type() == "tree" {
339+					tree, err = tree.SubTree(path)
340+					if err != nil {
341+						return err
342+					}
343+					ents, err = tree.Entries()
344+					if err != nil {
345+						return err
346+					}
347+				} else {
348+					ents = append(ents, te)
349+				}
350+			} else {
351+				ents, err = tree.Entries()
352+				if err != nil {
353+					return err
354+				}
355+			}
356+			ents.Sort()
357+			for _, ent := range ents {
358+				size := ent.Size()
359+				ssize := ""
360+				if size == 0 {
361+					ssize = "-"
362+				} else {
363+					ssize = humanize.Bytes(uint64(size))
364+				}
365+				cmd.Printf("%s\t%s\t %s\n", ent.Mode(), ssize, ent.Name())
366+			}
367+			return nil
368+		},
369+	}
370+	return cmd
371+}