use gogit for get blob implementation

This commit is contained in:
Marko Gaćeša 2023-09-19 17:25:07 +02:00
parent 548f446575
commit acf4a68a4f

View File

@ -5,89 +5,66 @@
package gitea package gitea
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"io" "io"
"github.com/harness/gitness/gitrpc/internal/types" "github.com/harness/gitness/gitrpc/internal/types"
"code.gitea.io/gitea/modules/git" gogitplumbing "github.com/go-git/go-git/v5/plumbing"
) )
// GetBlob returns the blob for the given object sha. // GetBlob returns the blob for the given object sha.
func (g Adapter) GetBlob(ctx context.Context, repoPath string, sha string, sizeLimit int64) (*types.BlobReader, error) { func (g Adapter) GetBlob(ctx context.Context, repoPath string, sha string, sizeLimit int64) (*types.BlobReader, error) {
// Note: We are avoiding gitea blob implementation, as that is tied to the lifetime of the repository object. repo, err := g.repoProvider.Get(ctx, repoPath)
// Instead, we just use the gitea helper methods ourselves.
stdIn, stdOut, cancel := git.CatFileBatch(ctx, repoPath)
_, err := stdIn.Write([]byte(sha + "\n"))
if err != nil { if err != nil {
cancel() return nil, fmt.Errorf("failed to open repository: %w", err)
return nil, fmt.Errorf("failed to write blob sha to git stdin: %w", err)
} }
objectSHA, objectType, objectSize, err := git.ReadBatchLine(stdOut) blob, err := repo.BlobObject(gogitplumbing.NewHash(sha))
if err != nil { if err != nil {
cancel() if err == gogitplumbing.ErrObjectNotFound {
return nil, processGiteaErrorf(err, "failed to read cat-file batch line") return nil, types.ErrNotFound
} }
return nil, fmt.Errorf("failed to get blob object: %w", err)
if string(objectSHA) != sha {
cancel()
return nil, fmt.Errorf("cat-file returned object sha '%s' but expected '%s'", objectSHA, sha)
}
if objectType != string(git.ObjectBlob) {
cancel()
return nil, fmt.Errorf("cat-file returned object type '%s' but expected '%s'", objectType, git.ObjectBlob)
} }
objectSize := blob.Size
contentSize := objectSize contentSize := objectSize
if sizeLimit > 0 && sizeLimit < contentSize { if sizeLimit > 0 && contentSize > sizeLimit {
contentSize = sizeLimit contentSize = sizeLimit
} }
reader, err := blob.Reader()
if err != nil {
return nil, fmt.Errorf("failed to open blob object: %w", err)
}
return &types.BlobReader{ return &types.BlobReader{
SHA: sha, SHA: sha,
Size: objectSize, Size: objectSize,
ContentSize: contentSize, ContentSize: contentSize,
Content: &exactLimitReader{ Content: LimitReadCloser(reader, contentSize),
reader: stdOut,
remainingBytes: contentSize,
close: func() error {
// TODO: is there a better (but short) way to clear the buffer?
// gitea is .Discard()'ing elements here until it's empty.
stdOut.Reset(bytes.NewBuffer([]byte{}))
cancel()
return nil
},
},
}, nil }, nil
} }
// exactLimitReader reads the content of a reader and ensures no more than the specified bytes will be requested from func LimitReadCloser(r io.ReadCloser, n int64) io.ReadCloser {
// the underlaying reader. This is required for readers that don't ensure completion after reading all remaining bytes. return limitReadCloser{
// io.LimitReader doesn't work as it waits for bytes that never come, io.SectionReader would requrie an io.ReaderAt. r: io.LimitReader(r, n),
type exactLimitReader struct { c: r,
reader io.Reader
remainingBytes int64
close func() error
}
func (r *exactLimitReader) Read(p []byte) (int, error) {
if r.remainingBytes <= 0 {
return 0, io.EOF
} }
if int64(len(p)) > r.remainingBytes {
p = p[0:r.remainingBytes]
}
n, err := r.reader.Read(p)
r.remainingBytes -= int64(n)
return n, err
} }
func (r *exactLimitReader) Close() error { // limitReadCloser implements io.ReadCloser interface.
return r.close() type limitReadCloser struct {
r io.Reader
c io.Closer
}
func (l limitReadCloser) Read(p []byte) (n int, err error) {
return l.r.Read(p)
}
func (l limitReadCloser) Close() error {
return l.c.Close()
} }