2024-01-15 22:26:51 +00:00
|
|
|
package git
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"io/fs"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"sort"
|
|
|
|
|
|
|
|
"github.com/dustin/go-humanize"
|
|
|
|
"github.com/go-git/go-git/v5"
|
|
|
|
"github.com/go-git/go-git/v5/plumbing"
|
|
|
|
"github.com/go-git/go-git/v5/plumbing/object"
|
|
|
|
)
|
|
|
|
|
|
|
|
// EnsureRepo ensures that the repo exists in the given directory
|
|
|
|
func EnsureRepo(dir string, repo string) error {
|
|
|
|
exists, err := PathExists(dir)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !exists {
|
|
|
|
err = os.MkdirAll(dir, os.ModeDir|os.FileMode(0o700))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rp := filepath.Join(dir, repo)
|
|
|
|
exists, err = PathExists(rp)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !exists {
|
|
|
|
_, err := git.PlainInit(rp, true)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// PathExists checks if a path exists and returns true if it does
|
|
|
|
func PathExists(path string) (bool, error) {
|
|
|
|
_, err := os.Stat(path)
|
|
|
|
if err == nil {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return true, err
|
|
|
|
}
|
|
|
|
|
2024-01-19 04:41:16 +00:00
|
|
|
// Tree returns the git tree at a given ref/rev
|
2024-01-15 22:26:51 +00:00
|
|
|
func (r Repo) Tree(ref string) (*object.Tree, error) {
|
|
|
|
g, err := r.Git()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
hash, err := g.ResolveRevision(plumbing.Revision(ref))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
c, err := g.CommitObject(*hash)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.Tree()
|
|
|
|
}
|
|
|
|
|
2024-01-19 04:41:16 +00:00
|
|
|
// FileInfo is the information for a file in a tree
|
2024-01-15 22:26:51 +00:00
|
|
|
type FileInfo struct {
|
|
|
|
Path string
|
|
|
|
IsDir bool
|
|
|
|
Mode string
|
|
|
|
Size string
|
|
|
|
}
|
|
|
|
|
2024-01-19 04:41:16 +00:00
|
|
|
// Name returns the last part of the FileInfo.Path
|
2024-01-15 22:26:51 +00:00
|
|
|
func (f FileInfo) Name() string {
|
|
|
|
return filepath.Base(f.Path)
|
|
|
|
}
|
|
|
|
|
2024-01-19 04:41:16 +00:00
|
|
|
// Dir returns the given dirpath in the given ref as a slice of FileInfo
|
|
|
|
// Sorted alphabetically, dirs first
|
2024-01-15 22:26:51 +00:00
|
|
|
func (r Repo) Dir(ref, path string) ([]FileInfo, error) {
|
|
|
|
t, err := r.Tree(ref)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if path != "" {
|
|
|
|
t, err = t.Tree(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fis := make([]FileInfo, 0)
|
|
|
|
for _, entry := range t.Entries {
|
|
|
|
fm, err := entry.Mode.ToOSFileMode()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
size, err := t.Size(entry.Name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
fis = append(fis, FileInfo{
|
|
|
|
Path: filepath.Join(path, entry.Name),
|
|
|
|
IsDir: fm.IsDir(),
|
|
|
|
Mode: fm.String(),
|
|
|
|
Size: humanize.Bytes(uint64(size)),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
sort.Slice(fis, func(i, j int) bool {
|
|
|
|
fi1 := fis[i]
|
|
|
|
fi2 := fis[j]
|
|
|
|
return (fi1.IsDir && !fi2.IsDir) || fi1.Name() < fi2.Name()
|
|
|
|
})
|
|
|
|
|
|
|
|
return fis, nil
|
|
|
|
}
|
|
|
|
|
2024-01-19 04:41:16 +00:00
|
|
|
// FileContent returns the content of a file in the git tree at a given ref/rev
|
2024-01-15 22:26:51 +00:00
|
|
|
func (r Repo) FileContent(ref, file string) (string, error) {
|
|
|
|
t, err := r.Tree(ref)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := t.File(file)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
content, err := f.Contents()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return content, nil
|
|
|
|
}
|