From acf4a68a4f6cd79cf3adce0d51017bd3dd8c4d44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Ga=C4=87e=C5=A1a?= Date: Tue, 19 Sep 2023 17:25:07 +0200 Subject: [PATCH] use gogit for get blob implementation --- gitrpc/internal/gitea/blob.go | 87 +++++++++++++---------------------- 1 file changed, 32 insertions(+), 55 deletions(-) diff --git a/gitrpc/internal/gitea/blob.go b/gitrpc/internal/gitea/blob.go index 04becf7c8..5066ebe49 100644 --- a/gitrpc/internal/gitea/blob.go +++ b/gitrpc/internal/gitea/blob.go @@ -5,89 +5,66 @@ package gitea import ( - "bytes" "context" "fmt" "io" "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. 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. - // Instead, we just use the gitea helper methods ourselves. - stdIn, stdOut, cancel := git.CatFileBatch(ctx, repoPath) - - _, err := stdIn.Write([]byte(sha + "\n")) + repo, err := g.repoProvider.Get(ctx, repoPath) if err != nil { - cancel() - return nil, fmt.Errorf("failed to write blob sha to git stdin: %w", err) + return nil, fmt.Errorf("failed to open repository: %w", err) } - objectSHA, objectType, objectSize, err := git.ReadBatchLine(stdOut) + blob, err := repo.BlobObject(gogitplumbing.NewHash(sha)) if err != nil { - cancel() - return nil, processGiteaErrorf(err, "failed to read cat-file batch line") - } - - 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) + if err == gogitplumbing.ErrObjectNotFound { + return nil, types.ErrNotFound + } + return nil, fmt.Errorf("failed to get blob object: %w", err) } + objectSize := blob.Size contentSize := objectSize - if sizeLimit > 0 && sizeLimit < contentSize { + if sizeLimit > 0 && 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{ SHA: sha, Size: objectSize, ContentSize: contentSize, - Content: &exactLimitReader{ - 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 - }, - }, + Content: LimitReadCloser(reader, contentSize), }, nil } -// exactLimitReader reads the content of a reader and ensures no more than the specified bytes will be requested from -// the underlaying reader. This is required for readers that don't ensure completion after reading all remaining bytes. -// io.LimitReader doesn't work as it waits for bytes that never come, io.SectionReader would requrie an io.ReaderAt. -type exactLimitReader struct { - reader io.Reader - remainingBytes int64 - close func() error -} - -func (r *exactLimitReader) Read(p []byte) (int, error) { - if r.remainingBytes <= 0 { - return 0, io.EOF +func LimitReadCloser(r io.ReadCloser, n int64) io.ReadCloser { + return limitReadCloser{ + r: io.LimitReader(r, n), + c: r, } - - 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 { - return r.close() +// limitReadCloser implements io.ReadCloser interface. +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() }