Commit e231f245 authored by Alejandro Rodríguez's avatar Alejandro Rodríguez

Implement Gitaly call for archive requests

parent a531ccd5
......@@ -2,6 +2,10 @@
Formerly known as 'gitlab-git-http-server'.
UNRELEASED
- Implement Gitaly call for archive requests !199
v3.1.0
- Add histograms to routes !184
......
......@@ -371,6 +371,62 @@ func TestGetBlobProxiedToGitalyInterruptedStream(t *testing.T) {
}
}
func TestGetArchiveProxiedToGitalySuccessfully(t *testing.T) {
gitalyServer, socketPath := startGitalyServer(t, codes.OK)
defer gitalyServer.Stop()
gitalyAddress := "unix://" + socketPath
repoStorage := "default"
oid := "54fcc214b94e78d7a41a9a8fe6d87a5e59500e51"
repoRelativePath := "foo/bar.git"
archivePath := "my/path"
archivePrefix := "repo-1"
jsonParams := fmt.Sprintf(`{"GitalyServer":{"Address":"%s","Token":""},"GitalyRepository":{"storage_name":"%s","relative_path":"%s"},"ArchivePath":"%s","ArchivePrefix":"%s","CommitId":"%s"}`,
gitalyAddress, repoStorage, repoRelativePath, archivePath, archivePrefix, oid)
expectedBody := testhelper.GitalyGetArchiveResponseMock
archiveLength := len(expectedBody)
resp, body, err := doSendDataRequest("/archive.tar.gz", "git-archive", jsonParams)
require.NoError(t, err)
assert.Equal(t, 200, resp.StatusCode, "GET %q: status code", resp.Request.URL)
assert.Equal(t, expectedBody, string(body), "GET %q: response body", resp.Request.URL)
assert.Equal(t, archiveLength, len(body), "GET %q: body size", resp.Request.URL)
}
func TestGetArchiveProxiedToGitalyInterruptedStream(t *testing.T) {
gitalyServer, socketPath := startGitalyServer(t, codes.OK)
defer gitalyServer.Stop()
gitalyAddress := "unix://" + socketPath
repoStorage := "default"
oid := "54fcc214b94e78d7a41a9a8fe6d87a5e59500e51"
repoRelativePath := "foo/bar.git"
archivePath := "my/path"
archivePrefix := "repo-1"
jsonParams := fmt.Sprintf(`{"GitalyServer":{"Address":"%s","Token":""},"GitalyRepository":{"storage_name":"%s","relative_path":"%s"},"ArchivePath":"%s","ArchivePrefix":"%s","CommitId":"%s"}`,
gitalyAddress, repoStorage, repoRelativePath, archivePath, archivePrefix, oid)
resp, _, err := doSendDataRequest("/archive.tar.gz", "git-archive", jsonParams)
require.NoError(t, err)
// This causes the server stream to be interrupted instead of consumed entirely.
resp.Body.Close()
done := make(chan struct{})
go func() {
gitalyServer.WaitGroup.Wait()
close(done)
}()
select {
case <-done:
return
case <-time.After(10 * time.Second):
t.Fatal("time out waiting for gitaly handler to return")
}
}
type combinedServer struct {
*grpc.Server
*testhelper.GitalyTestServer
......@@ -388,6 +444,7 @@ func startGitalyServer(t *testing.T, finalMessageCode codes.Code) (*combinedServ
gitalyServer := testhelper.NewGitalyServer(finalMessageCode)
pb.RegisterSmartHTTPServiceServer(server, gitalyServer)
pb.RegisterBlobServiceServer(server, gitalyServer)
pb.RegisterRepositoryServiceServer(server, gitalyServer)
go server.Serve(listener)
......
......@@ -14,6 +14,8 @@ import (
"path/filepath"
"time"
pb "gitlab.com/gitlab-org/gitaly-proto/go"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/gitaly"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/senddata"
......@@ -22,10 +24,12 @@ import (
type archive struct{ senddata.Prefix }
type archiveParams struct {
RepoPath string
ArchivePath string
ArchivePrefix string
CommitId string
RepoPath string
ArchivePath string
ArchivePrefix string
CommitId string
GitalyServer gitaly.Server
GitalyRepository pb.Repository
}
var (
......@@ -84,7 +88,16 @@ func (a *archive) Inject(w http.ResponseWriter, r *http.Request, sendData string
defer tempFile.Close()
defer os.Remove(tempFile.Name())
archiveReader, err := newArchiveReader(r.Context(), params.RepoPath, format, params.ArchivePrefix, params.CommitId)
var archiveReader io.Reader
if params.GitalyServer.Address != "" {
archiveReader, err = handleArchiveWithGitaly(r, params, format)
if err != nil {
err = fmt.Errorf("operations.GetArchive: %v", err)
}
} else {
archiveReader, err = newArchiveReader(r.Context(), params.RepoPath, format, params.ArchivePrefix, params.CommitId)
}
if err != nil {
helper.Fail500(w, r, err)
return
......@@ -106,10 +119,26 @@ func (a *archive) Inject(w http.ResponseWriter, r *http.Request, sendData string
}
}
func setArchiveHeaders(w http.ResponseWriter, format ArchiveFormat, archiveFilename string) {
func handleArchiveWithGitaly(r *http.Request, params archiveParams, format pb.GetArchiveRequest_Format) (io.Reader, error) {
c, err := gitaly.NewRepositoryClient(params.GitalyServer)
if err != nil {
return nil, err
}
request := &pb.GetArchiveRequest{
Repository: &params.GitalyRepository,
CommitId: params.CommitId,
Prefix: params.ArchivePrefix,
Format: format,
}
return c.ArchiveReader(r.Context(), request)
}
func setArchiveHeaders(w http.ResponseWriter, format pb.GetArchiveRequest_Format, archiveFilename string) {
w.Header().Del("Content-Length")
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, archiveFilename))
if format == ZipFormat {
if format == pb.GetArchiveRequest_ZIP {
w.Header().Set("Content-Type", "application/zip")
} else {
w.Header().Set("Content-Type", "application/octet-stream")
......@@ -136,20 +165,20 @@ func finalizeCachedArchive(tempFile *os.File, archivePath string) error {
return nil
}
func parseBasename(basename string) (ArchiveFormat, bool) {
var format ArchiveFormat
func parseBasename(basename string) (pb.GetArchiveRequest_Format, bool) {
var format pb.GetArchiveRequest_Format
switch basename {
case "archive.zip":
format = ZipFormat
format = pb.GetArchiveRequest_ZIP
case "archive.tar":
format = TarFormat
format = pb.GetArchiveRequest_TAR
case "archive", "archive.tar.gz", "archive.tgz", "archive.gz":
format = TarGzFormat
format = pb.GetArchiveRequest_TAR_GZ
case "archive.tar.bz2", "archive.tbz", "archive.tbz2", "archive.tb2", "archive.bz2":
format = TarBz2Format
format = pb.GetArchiveRequest_TAR_BZ2
default:
return InvalidFormat, false
return format, false
}
return format, true
......
......@@ -5,23 +5,24 @@ import (
"net/http/httptest"
"testing"
pb "gitlab.com/gitlab-org/gitaly-proto/go"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
)
func TestParseBasename(t *testing.T) {
for _, testCase := range []struct {
in string
out ArchiveFormat
out pb.GetArchiveRequest_Format
}{
{"", TarGzFormat},
{".tar.gz", TarGzFormat},
{".tgz", TarGzFormat},
{".gz", TarGzFormat},
{".tar.bz2", TarBz2Format},
{".tbz", TarBz2Format},
{".tbz2", TarBz2Format},
{".tb2", TarBz2Format},
{".bz2", TarBz2Format},
{"", pb.GetArchiveRequest_TAR_GZ},
{".tar.gz", pb.GetArchiveRequest_TAR_GZ},
{".tgz", pb.GetArchiveRequest_TAR_GZ},
{".gz", pb.GetArchiveRequest_TAR_GZ},
{".tar.bz2", pb.GetArchiveRequest_TAR_BZ2},
{".tbz", pb.GetArchiveRequest_TAR_BZ2},
{".tbz2", pb.GetArchiveRequest_TAR_BZ2},
{".tb2", pb.GetArchiveRequest_TAR_BZ2},
{".bz2", pb.GetArchiveRequest_TAR_BZ2},
} {
basename := "archive" + testCase.in
out, ok := parseBasename(basename)
......@@ -51,12 +52,13 @@ func TestFinalizeArchive(t *testing.T) {
func TestSetArchiveHeaders(t *testing.T) {
for _, testCase := range []struct {
in ArchiveFormat
in pb.GetArchiveRequest_Format
out string
}{
{ZipFormat, "application/zip"},
{TarFormat, "application/octet-stream"},
{InvalidFormat, "application/octet-stream"},
{pb.GetArchiveRequest_ZIP, "application/zip"},
{pb.GetArchiveRequest_TAR, "application/octet-stream"},
{pb.GetArchiveRequest_TAR_GZ, "application/octet-stream"},
{pb.GetArchiveRequest_TAR_BZ2, "application/octet-stream"},
} {
w := httptest.NewRecorder()
......
......@@ -7,28 +7,19 @@ import (
"os/exec"
"syscall"
pb "gitlab.com/gitlab-org/gitaly-proto/go"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
)
type ArchiveFormat int
const (
InvalidFormat ArchiveFormat = iota
ZipFormat
TarFormat
TarGzFormat
TarBz2Format
)
func parseArchiveFormat(format ArchiveFormat) (*exec.Cmd, string) {
func parseArchiveFormat(format pb.GetArchiveRequest_Format) (*exec.Cmd, string) {
switch format {
case TarFormat:
case pb.GetArchiveRequest_TAR:
return nil, "tar"
case TarGzFormat:
case pb.GetArchiveRequest_TAR_GZ:
return exec.Command("gzip", "-c", "-n"), "tar"
case TarBz2Format:
case pb.GetArchiveRequest_TAR_BZ2:
return exec.Command("bzip2", "-c"), "tar"
case ZipFormat:
case pb.GetArchiveRequest_ZIP:
return nil, "zip"
default:
return nil, "invalid format"
......@@ -70,7 +61,7 @@ func (a *archiveReader) wait() error {
return nil
}
func newArchiveReader(ctx context.Context, repoPath string, format ArchiveFormat, archivePrefix string, commitId string) (a *archiveReader, err error) {
func newArchiveReader(ctx context.Context, repoPath string, format pb.GetArchiveRequest_Format, archivePrefix string, commitId string) (a *archiveReader, err error) {
a = &archiveReader{}
compressCmd, formatArg := parseArchiveFormat(format)
......
......@@ -41,6 +41,15 @@ func NewBlobClient(server Server) (*BlobClient, error) {
return &BlobClient{grpcClient}, nil
}
func NewRepositoryClient(server Server) (*RepositoryClient, error) {
conn, err := getOrCreateConnection(server)
if err != nil {
return nil, err
}
grpcClient := pb.NewRepositoryServiceClient(conn)
return &RepositoryClient{grpcClient}, nil
}
func getOrCreateConnection(server Server) (*grpc.ClientConn, error) {
cache.RLock()
conn := cache.connections[server]
......
package gitaly
import (
"context"
"fmt"
"io"
pb "gitlab.com/gitlab-org/gitaly-proto/go"
"gitlab.com/gitlab-org/gitaly/streamio"
)
// RepositoryClient encapsulates RepositoryService calls
type RepositoryClient struct {
pb.RepositoryServiceClient
}
// ArchiveReader performs a GetArchive Gitaly request and returns an io.Reader
// for the response
func (client *RepositoryClient) ArchiveReader(ctx context.Context, request *pb.GetArchiveRequest) (io.Reader, error) {
c, err := client.GetArchive(ctx, request)
if err != nil {
return nil, fmt.Errorf("RepositoryService::GetArchive: %v", err)
}
return streamio.NewReader(func() ([]byte, error) {
resp, err := c.Recv()
return resp.GetData(), err
}), nil
}
......@@ -24,6 +24,7 @@ type GitalyTestServer struct {
var (
GitalyInfoRefsResponseMock = strings.Repeat("Mock Gitaly InfoRefsResponse data", 100000)
GitalyGetBlobResponseMock = strings.Repeat("Mock Gitaly GetBlobResponse data", 100000)
GitalyGetArchiveResponseMock = strings.Repeat("Mock Gitaly GetArchiveResponse data", 100000)
GitalyReceivePackResponseMock []byte
GitalyUploadPackResponseMock []byte
)
......@@ -208,6 +209,63 @@ func (s *GitalyTestServer) GetBlob(in *pb.GetBlobRequest, stream pb.BlobService_
return s.finalError()
}
func (s *GitalyTestServer) GetArchive(in *pb.GetArchiveRequest, stream pb.RepositoryService_GetArchiveServer) error {
s.WaitGroup.Add(1)
defer s.WaitGroup.Done()
if err := validateRepository(in.GetRepository()); err != nil {
return err
}
nSends, err := sendBytes([]byte(GitalyGetArchiveResponseMock), 100, func(p []byte) error {
return stream.Send(&pb.GetArchiveResponse{Data: p})
})
if err != nil {
return err
}
if nSends <= 1 {
panic("should have sent more than one message")
}
return s.finalError()
}
func (s *GitalyTestServer) RepositoryExists(context.Context, *pb.RepositoryExistsRequest) (*pb.RepositoryExistsResponse, error) {
return nil, nil
}
func (s *GitalyTestServer) RepackIncremental(context.Context, *pb.RepackIncrementalRequest) (*pb.RepackIncrementalResponse, error) {
return nil, nil
}
func (s *GitalyTestServer) RepackFull(context.Context, *pb.RepackFullRequest) (*pb.RepackFullResponse, error) {
return nil, nil
}
func (s *GitalyTestServer) GarbageCollect(context.Context, *pb.GarbageCollectRequest) (*pb.GarbageCollectResponse, error) {
return nil, nil
}
func (s *GitalyTestServer) RepositorySize(context.Context, *pb.RepositorySizeRequest) (*pb.RepositorySizeResponse, error) {
return nil, nil
}
func (s *GitalyTestServer) ApplyGitattributes(context.Context, *pb.ApplyGitattributesRequest) (*pb.ApplyGitattributesResponse, error) {
return nil, nil
}
func (s *GitalyTestServer) FetchRemote(context.Context, *pb.FetchRemoteRequest) (*pb.FetchRemoteResponse, error) {
return nil, nil
}
func (s *GitalyTestServer) CreateRepository(context.Context, *pb.CreateRepositoryRequest) (*pb.CreateRepositoryResponse, error) {
return nil, nil
}
func (s *GitalyTestServer) Exists(context.Context, *pb.RepositoryExistsRequest) (*pb.RepositoryExistsResponse, error) {
return nil, nil
}
// sendBytes returns the number of times the 'sender' function was called and an error.
func sendBytes(data []byte, chunkSize int, sender func([]byte) error) (int, error) {
i := 0
......
......@@ -69,8 +69,12 @@ It has these top-level messages:
PostReceiveResponse
UserCreateBranchRequest
UserCreateBranchResponse
UserDeleteBranchRequest
UserDeleteBranchResponse
UserDeleteTagRequest
UserDeleteTagResponse
UserCreateTagRequest
UserCreateTagResponse
FindDefaultBranchNameRequest
FindDefaultBranchNameResponse
FindAllBranchNamesRequest
......@@ -111,11 +115,14 @@ It has these top-level messages:
FetchRemoteResponse
CreateRepositoryRequest
CreateRepositoryResponse
GetArchiveRequest
GetArchiveResponse
Repository
GitCommit
CommitAuthor
ExitStatus
Branch
Tag
User
InfoRefsRequest
InfoRefsResponse
......
......@@ -197,6 +197,46 @@ func (m *Branch) GetTargetCommit() *GitCommit {
return nil
}
type Tag struct {
Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Id string `protobuf:"bytes,2,opt,name=id" json:"id,omitempty"`
TargetCommit *GitCommit `protobuf:"bytes,3,opt,name=target_commit,json=targetCommit" json:"target_commit,omitempty"`
Message []byte `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"`
}
func (m *Tag) Reset() { *m = Tag{} }
func (m *Tag) String() string { return proto.CompactTextString(m) }
func (*Tag) ProtoMessage() {}
func (*Tag) Descriptor() ([]byte, []int) { return fileDescriptor9, []int{5} }
func (m *Tag) GetName() []byte {
if m != nil {
return m.Name
}
return nil
}
func (m *Tag) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *Tag) GetTargetCommit() *GitCommit {
if m != nil {
return m.TargetCommit
}
return nil
}
func (m *Tag) GetMessage() []byte {
if m != nil {
return m.Message
}
return nil
}
type User struct {
GlId string `protobuf:"bytes,1,opt,name=gl_id,json=glId" json:"gl_id,omitempty"`
Name []byte `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
......@@ -206,7 +246,7 @@ type User struct {
func (m *User) Reset() { *m = User{} }
func (m *User) String() string { return proto.CompactTextString(m) }
func (*User) ProtoMessage() {}
func (*User) Descriptor() ([]byte, []int) { return fileDescriptor9, []int{5} }
func (*User) Descriptor() ([]byte, []int) { return fileDescriptor9, []int{6} }
func (m *User) GetGlId() string {
if m != nil {
......@@ -235,41 +275,44 @@ func init() {
proto.RegisterType((*CommitAuthor)(nil), "gitaly.CommitAuthor")
proto.RegisterType((*ExitStatus)(nil), "gitaly.ExitStatus")
proto.RegisterType((*Branch)(nil), "gitaly.Branch")
proto.RegisterType((*Tag)(nil), "gitaly.Tag")
proto.RegisterType((*User)(nil), "gitaly.User")
}
func init() { proto.RegisterFile("shared.proto", fileDescriptor9) }
var fileDescriptor9 = []byte{
// 468 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x52, 0xc1, 0x6e, 0xd3, 0x40,
0x10, 0x55, 0x1c, 0xc7, 0xe0, 0x89, 0x8b, 0x60, 0xc9, 0xc1, 0xaa, 0x54, 0x11, 0xcc, 0xa5, 0x07,
0xe4, 0xa2, 0x20, 0x71, 0x2f, 0x50, 0x55, 0xe5, 0x00, 0x68, 0x29, 0x67, 0x6b, 0x13, 0x0f, 0xeb,
0x45, 0xeb, 0xac, 0xb5, 0x3b, 0xae, 0xc8, 0x8d, 0xef, 0xe3, 0xab, 0x90, 0x77, 0xe3, 0xb4, 0xa0,
0xaa, 0xb7, 0x9d, 0xd9, 0xf7, 0x66, 0xde, 0x9b, 0x19, 0xc8, 0x5c, 0x23, 0x2c, 0xd6, 0x65, 0x67,
0x0d, 0x19, 0x96, 0x48, 0x45, 0x42, 0xef, 0x8e, 0x5f, 0x48, 0x63, 0xa4, 0xc6, 0x33, 0x9f, 0x5d,
0xf7, 0x3f, 0xce, 0x48, 0xb5, 0xe8, 0x48, 0xb4, 0x5d, 0x00, 0x16, 0xbf, 0x23, 0x00, 0x8e, 0x9d,
0x71, 0x8a, 0x8c, 0xdd, 0xb1, 0x97, 0x90, 0x39, 0x32, 0x56, 0x48, 0xac, 0xb6, 0xa2, 0xc5, 0x3c,
0x5a, 0x4e, 0x4e, 0x53, 0x3e, 0xdf, 0xe7, 0x3e, 0x8b, 0x16, 0xd9, 0x2b, 0x38, 0xb2, 0xa8, 0x05,
0xa9, 0x1b, 0xac, 0x3a, 0x41, 0x4d, 0x3e, 0xf5, 0x98, 0x6c, 0x4c, 0x7e, 0x15, 0xd4, 0xb0, 0x37,
0xb0, 0x90, 0x8a, 0x2a, 0xb3, 0xfe, 0x89, 0x1b, 0xaa, 0x6a, 0x65, 0x71, 0x33, 0xd4, 0xcf, 0x63,
0x8f, 0x65, 0x52, 0xd1, 0x17, 0xff, 0xf5, 0x71, 0xfc, 0x61, 0x97, 0xb0, 0x1c, 0x18, 0x42, 0x13,
0xda, 0xad, 0x20, 0xfc, 0x9f, 0xab, 0xd0, 0xe5, 0xb3, 0xe5, 0xf4, 0x34, 0xe5, 0x27, 0x52, 0xd1,
0xf9, 0x08, 0xfb, 0xb7, 0x8c, 0x42, 0x37, 0xe8, 0x93, 0xba, 0xb2, 0x07, 0x4f, 0x79, 0x12, 0xf4,
0x49, 0x7d, 0xeb, 0xf3, 0x53, 0xfc, 0x78, 0xf2, 0x34, 0xe2, 0xf1, 0xa0, 0xbf, 0xf8, 0x33, 0x81,
0xf4, 0x52, 0xd1, 0x07, 0xd3, 0xb6, 0x8a, 0xd8, 0x13, 0x88, 0x54, 0x9d, 0x4f, 0x3c, 0x27, 0x52,
0x35, 0xcb, 0xe1, 0x91, 0xeb, 0x7d, 0x13, 0x3f, 0x8c, 0x8c, 0x8f, 0x21, 0x63, 0x10, 0xaf, 0x4d,
0xbd, 0xf3, 0xfe, 0x33, 0xee, 0xdf, 0xec, 0x35, 0x24, 0xa2, 0xa7, 0xc6, 0x58, 0xef, 0x74, 0xbe,
0x5a, 0x94, 0x61, 0x11, 0x65, 0xa8, 0x7e, 0xee, 0xff, 0xf8, 0x1e, 0xc3, 0x56, 0x90, 0x6e, 0x7c,
0x9e, 0xd0, 0xe6, 0xb3, 0x07, 0x08, 0xb7, 0x30, 0x76, 0x02, 0xd0, 0x09, 0x8b, 0x5b, 0xaa, 0x54,
0xed, 0xf2, 0xc4, 0x4f, 0x24, 0x0d, 0x99, 0xab, 0xda, 0x15, 0x0d, 0x64, 0x77, 0x99, 0x83, 0x48,
0xbf, 0xc8, 0x49, 0x10, 0x39, 0xbc, 0xd9, 0x02, 0x66, 0xd8, 0x0a, 0xa5, 0xf7, 0x86, 0x42, 0xc0,
0x4a, 0x88, 0x6b, 0x41, 0xe8, 0xed, 0xcc, 0x57, 0xc7, 0x65, 0xb8, 0x9c, 0x72, 0xbc, 0x9c, 0xf2,
0x7a, 0xbc, 0x1c, 0xee, 0x71, 0x45, 0x01, 0x70, 0xf1, 0x4b, 0xd1, 0x37, 0x12, 0xd4, 0xbb, 0xa1,
0xe6, 0x8d, 0xd0, 0x7d, 0x68, 0x34, 0xe3, 0x21, 0x28, 0xae, 0x21, 0x79, 0x6f, 0xc5, 0x76, 0xd3,
0xdc, 0xab, 0xe3, 0x1d, 0x1c, 0x91, 0xb0, 0x12, 0xa9, 0x0a, 0xf6, 0xbc, 0x9e, 0xf9, 0xea, 0xd9,
0x38, 0x82, 0xc3, 0x52, 0x78, 0x16, 0x70, 0x21, 0x2a, 0x2e, 0x20, 0xfe, 0xee, 0xd0, 0xb2, 0xe7,
0x30, 0x93, 0xba, 0x3a, 0x6c, 0x2b, 0x96, 0xfa, 0xaa, 0x3e, 0x34, 0x8a, 0xee, 0x33, 0x3c, 0xbd,
0x63, 0x78, 0x9d, 0x78, 0x6b, 0x6f, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x86, 0x24, 0x75, 0x89,
0x3a, 0x03, 0x00, 0x00,
// 500 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0xc1, 0x6e, 0xd3, 0x40,
0x10, 0x55, 0x1c, 0xc7, 0x90, 0x89, 0x8b, 0x60, 0xe9, 0xc1, 0xaa, 0x54, 0x11, 0xcc, 0xa5, 0x07,
0xe4, 0xa2, 0x20, 0x71, 0x2f, 0x50, 0x55, 0xe5, 0x00, 0x68, 0x09, 0x67, 0x6b, 0x12, 0x0f, 0xeb,
0x45, 0x76, 0x36, 0xda, 0x9d, 0x54, 0x44, 0x5c, 0xf8, 0x3e, 0xbe, 0x0a, 0x79, 0x37, 0x4e, 0x0b,
0x44, 0x88, 0xdb, 0xce, 0xec, 0x9b, 0x99, 0xf7, 0xe6, 0x0d, 0xa4, 0xae, 0x46, 0x4b, 0x55, 0xb1,
0xb6, 0x86, 0x8d, 0x48, 0x94, 0x66, 0x6c, 0xb6, 0x27, 0x4f, 0x94, 0x31, 0xaa, 0xa1, 0x73, 0x9f,
0x5d, 0x6c, 0xbe, 0x9c, 0xb3, 0x6e, 0xc9, 0x31, 0xb6, 0xeb, 0x00, 0xcc, 0x7f, 0x44, 0x00, 0x92,
0xd6, 0xc6, 0x69, 0x36, 0x76, 0x2b, 0x9e, 0x42, 0xea, 0xd8, 0x58, 0x54, 0x54, 0xae, 0xb0, 0xa5,
0x2c, 0x9a, 0x0e, 0xce, 0xc6, 0x72, 0xb2, 0xcb, 0xbd, 0xc7, 0x96, 0xc4, 0x33, 0x38, 0xb2, 0xd4,
0x20, 0xeb, 0x1b, 0x2a, 0xd7, 0xc8, 0x75, 0x36, 0xf4, 0x98, 0xb4, 0x4f, 0x7e, 0x44, 0xae, 0xc5,
0x0b, 0x38, 0x56, 0x9a, 0x4b, 0xb3, 0xf8, 0x4a, 0x4b, 0x2e, 0x2b, 0x6d, 0x69, 0xd9, 0xf5, 0xcf,
0x62, 0x8f, 0x15, 0x4a, 0xf3, 0x07, 0xff, 0xf5, 0xb6, 0xff, 0x11, 0x57, 0x30, 0xed, 0x2a, 0xb0,
0x61, 0xb2, 0x2b, 0x64, 0xfa, 0xb3, 0x56, 0x93, 0xcb, 0x46, 0xd3, 0xe1, 0xd9, 0x58, 0x9e, 0x2a,
0xcd, 0x17, 0x3d, 0xec, 0xf7, 0x36, 0x9a, 0x5c, 0xc7, 0x4f, 0x35, 0xa5, 0xdd, 0x6b, 0xca, 0x92,
0xc0, 0x4f, 0x35, 0xb7, 0x3a, 0xdf, 0xc5, 0xf7, 0x07, 0x0f, 0x23, 0x19, 0x77, 0xfc, 0xf3, 0x9f,
0x03, 0x18, 0x5f, 0x69, 0x7e, 0x63, 0xda, 0x56, 0xb3, 0x78, 0x00, 0x91, 0xae, 0xb2, 0x81, 0xaf,
0x89, 0x74, 0x25, 0x32, 0xb8, 0xe7, 0x36, 0x7e, 0x88, 0x5f, 0x46, 0x2a, 0xfb, 0x50, 0x08, 0x88,
0x17, 0xa6, 0xda, 0x7a, 0xfd, 0xa9, 0xf4, 0x6f, 0xf1, 0x1c, 0x12, 0xdc, 0x70, 0x6d, 0xac, 0x57,
0x3a, 0x99, 0x1d, 0x17, 0xc1, 0x88, 0x22, 0x74, 0xbf, 0xf0, 0x7f, 0x72, 0x87, 0x11, 0x33, 0x18,
0x2f, 0x7d, 0x9e, 0xc9, 0x66, 0xa3, 0x7f, 0x14, 0xdc, 0xc2, 0xc4, 0x29, 0xc0, 0x1a, 0x2d, 0xad,
0xb8, 0xd4, 0x95, 0xcb, 0x12, 0xbf, 0x91, 0x71, 0xc8, 0x5c, 0x57, 0x2e, 0xaf, 0x21, 0xbd, 0x5b,
0xd9, 0x91, 0xf4, 0x46, 0x0e, 0x02, 0xc9, 0xee, 0x2d, 0x8e, 0x61, 0x44, 0x2d, 0xea, 0x66, 0x27,
0x28, 0x04, 0xa2, 0x80, 0xb8, 0x42, 0x26, 0x2f, 0x67, 0x32, 0x3b, 0x29, 0xc2, 0xe5, 0x14, 0xfd,
0xe5, 0x14, 0xf3, 0xfe, 0x72, 0xa4, 0xc7, 0xe5, 0x39, 0xc0, 0xe5, 0x37, 0xcd, 0x9f, 0x18, 0x79,
0xe3, 0xba, 0x9e, 0x37, 0xd8, 0x6c, 0xc2, 0xa0, 0x91, 0x0c, 0x41, 0x3e, 0x87, 0xe4, 0xb5, 0xc5,
0xd5, 0xb2, 0x3e, 0xc8, 0xe3, 0x15, 0x1c, 0x31, 0x5a, 0x45, 0x5c, 0x06, 0x79, 0x9e, 0xcf, 0x64,
0xf6, 0xa8, 0x5f, 0xc1, 0xde, 0x14, 0x99, 0x06, 0x5c, 0x88, 0xf2, 0xef, 0x30, 0x9c, 0xa3, 0x3a,
0xd8, 0x32, 0xb8, 0x17, 0xed, 0xdd, 0xfb, 0x6b, 0xc4, 0xf0, 0xbf, 0x46, 0x74, 0xae, 0xb7, 0xe4,
0x1c, 0x2a, 0xf2, 0x46, 0xa6, 0xb2, 0x0f, 0xf3, 0x4b, 0x88, 0x3f, 0x3b, 0xb2, 0xe2, 0x31, 0x8c,
0x54, 0x53, 0xee, 0x4f, 0x25, 0x56, 0xcd, 0x75, 0xb5, 0xa7, 0x14, 0x1d, 0xda, 0xf6, 0xf0, 0xce,
0xb6, 0x17, 0x89, 0xdf, 0xeb, 0xcb, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xaa, 0x7f, 0xf5, 0xea,
0xb7, 0x03, 0x00, 0x00,
}
......@@ -152,12 +152,12 @@
"revisionTime": "2016-11-17T07:43:51Z"
},
{
"checksumSHA1": "A2jWY7L3EazZt0xdKFKMDOGXCdk=",
"checksumSHA1": "tisAil16tojFqhqWYbs2kXwBYyk=",
"path": "gitlab.com/gitlab-org/gitaly-proto/go",
"revision": "b61fee8cd76e282d15a3c719f7f71a4f71ef0d6c",
"revisionTime": "2017-09-20T19:16:33Z",
"version": "v0.35.0",
"versionExact": "v0.35.0"
"revision": "12872bd8dad9dc72328b2c590386e67a17c65612",
"revisionTime": "2017-09-27T21:53:01Z",
"version": "v0.38.0",
"versionExact": "v0.38.0"
},
{
"checksumSHA1": "dUHJbKas746n5fLzlwxHb6FOCxs=",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment