mirror of
https://codeberg.org/git-pages/git-pages.git
synced 2026-05-19 05:31:54 +00:00
113 lines
2.9 KiB
Go
113 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/go-git/go-billy/v6/osfs"
|
|
"github.com/go-git/go-git/v6"
|
|
"github.com/go-git/go-git/v6/plumbing"
|
|
"github.com/go-git/go-git/v6/plumbing/cache"
|
|
"github.com/go-git/go-git/v6/plumbing/filemode"
|
|
"github.com/go-git/go-git/v6/plumbing/object"
|
|
"github.com/go-git/go-git/v6/storage/filesystem"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
func FetchRepository(ctx context.Context, repoURL string, branch string) (*Manifest, error) {
|
|
baseDir, err := os.MkdirTemp("", "fetchRepo")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("mkdtemp: %w", err)
|
|
}
|
|
defer os.RemoveAll(baseDir)
|
|
|
|
fs := osfs.New(baseDir, osfs.WithBoundOS())
|
|
cache := cache.NewObjectLRUDefault()
|
|
storer := filesystem.NewStorageWithOptions(fs, cache, filesystem.Options{
|
|
ExclusiveAccess: true,
|
|
LargeObjectThreshold: int64(config.Limits.GitLargeObjectThreshold.Bytes()),
|
|
})
|
|
repo, err := git.CloneContext(ctx, storer, nil, &git.CloneOptions{
|
|
Bare: true,
|
|
URL: repoURL,
|
|
ReferenceName: plumbing.ReferenceName(branch),
|
|
SingleBranch: true,
|
|
Depth: 1,
|
|
Tags: git.NoTags,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("git clone: %w", err)
|
|
}
|
|
|
|
ref, err := repo.Head()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("git head: %w", err)
|
|
}
|
|
|
|
commit, err := repo.CommitObject(ref.Hash())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("git commit: %w", err)
|
|
}
|
|
|
|
tree, err := repo.TreeObject(commit.TreeHash)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("git tree: %w", err)
|
|
}
|
|
|
|
walker := object.NewTreeWalker(tree, true, make(map[plumbing.Hash]bool))
|
|
defer walker.Close()
|
|
|
|
manifest := Manifest{
|
|
RepoUrl: proto.String(repoURL),
|
|
Branch: proto.String(branch),
|
|
Commit: proto.String(ref.Hash().String()),
|
|
Contents: map[string]*Entry{
|
|
"": {Type: Type_Directory.Enum()},
|
|
},
|
|
}
|
|
for {
|
|
name, entry, err := walker.Next()
|
|
if err == io.EOF {
|
|
break
|
|
} else if err != nil {
|
|
return nil, fmt.Errorf("git walker: %w", err)
|
|
} else {
|
|
manifestEntry := Entry{}
|
|
if entry.Mode.IsFile() {
|
|
blob, err := repo.BlobObject(entry.Hash)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("git blob %s: %w", name, err)
|
|
}
|
|
|
|
reader, err := blob.Reader()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("git blob open: %w", err)
|
|
}
|
|
defer reader.Close()
|
|
|
|
data, err := io.ReadAll(reader)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("git blob read: %w", err)
|
|
}
|
|
|
|
if entry.Mode == filemode.Symlink {
|
|
manifestEntry.Type = Type_Symlink.Enum()
|
|
} else {
|
|
manifestEntry.Type = Type_InlineFile.Enum()
|
|
}
|
|
manifestEntry.Size = proto.Uint32(uint32(blob.Size))
|
|
manifestEntry.Data = data
|
|
} else if entry.Mode == filemode.Dir {
|
|
manifestEntry.Type = Type_Directory.Enum()
|
|
} else {
|
|
AddProblem(&manifest, name, "unsupported mode %#o", entry.Mode)
|
|
continue
|
|
}
|
|
manifest.Contents[name] = &manifestEntry
|
|
}
|
|
}
|
|
return &manifest, nil
|
|
}
|