Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Tobler <jtobler@gitlab.com>2023-12-06 19:44:15 +0300
committerJustin Tobler <jtobler@gitlab.com>2023-12-06 19:44:15 +0300
commit761861553cbf9b15d2d642b7e313a576a306950d (patch)
treea0a983eb74e11b7445cd92634062e35485e4a525
parent9b54bcc1d8e0ddcefcfdf9142a16778d02a23957 (diff)
parentec32915c508b8c98765640867175916cd2b77215 (diff)
Merge branch 'jt/analysis-service' into 'master'
analysis: Implement `CheckBlobsGenerated` RPC See merge request https://gitlab.com/gitlab-org/gitaly/-/merge_requests/6560 Merged-by: Justin Tobler <jtobler@gitlab.com> Approved-by: Patrick Steinhardt <psteinhardt@gitlab.com> Reviewed-by: Patrick Steinhardt <psteinhardt@gitlab.com> Reviewed-by: Justin Tobler <jtobler@gitlab.com> Reviewed-by: karthik nayak <knayak@gitlab.com>
-rw-r--r--internal/gitaly/service/analysis/check_generated.go177
-rw-r--r--internal/gitaly/service/analysis/check_generated_test.go374
-rw-r--r--internal/gitaly/service/analysis/server.go33
-rw-r--r--internal/gitaly/service/analysis/testhelper_test.go11
-rw-r--r--internal/gitaly/service/setup/register.go2
-rw-r--r--proto/analysis.proto62
-rw-r--r--proto/go/gitalypb/analysis.pb.go403
-rw-r--r--proto/go/gitalypb/analysis_grpc.pb.go155
-rw-r--r--proto/go/gitalypb/protolist.go1
9 files changed, 1218 insertions, 0 deletions
diff --git a/internal/gitaly/service/analysis/check_generated.go b/internal/gitaly/service/analysis/check_generated.go
new file mode 100644
index 000000000..708efddca
--- /dev/null
+++ b/internal/gitaly/service/analysis/check_generated.go
@@ -0,0 +1,177 @@
+package analysis
+
+import (
+ "errors"
+ "fmt"
+ "io"
+
+ "github.com/go-enry/go-enry/v2"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/git"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/helper/chunk"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/structerr"
+ "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb"
+ "golang.org/x/sync/errgroup"
+ "google.golang.org/protobuf/proto"
+)
+
+func (s *server) CheckBlobsGenerated(stream gitalypb.AnalysisService_CheckBlobsGeneratedServer) (returnedErr error) {
+ req, err := stream.Recv()
+ if err != nil {
+ return fmt.Errorf("receiving first request: %w", err)
+ }
+
+ repository := req.GetRepository()
+ if err := s.locator.ValidateRepository(repository); err != nil {
+ return structerr.NewInvalidArgument("%w", err)
+ }
+
+ ctx := stream.Context()
+ repo := s.localrepo(repository)
+
+ reader, readerCancel, err := s.catfileCache.ObjectReader(ctx, repo)
+ if err != nil {
+ return fmt.Errorf("retrieving object reader: %w", err)
+ }
+ defer readerCancel()
+
+ queue, queueCancel, err := reader.ObjectQueue(ctx)
+ if err != nil {
+ return fmt.Errorf("retrieving object queue: %w", err)
+ }
+ defer queueCancel()
+
+ group, groupCtx := errgroup.WithContext(ctx)
+ requestsChan := make(chan *gitalypb.CheckBlobsGeneratedRequest)
+
+ // The output of git-cat-file(1) is processed in a separate goroutine to allow requests to
+ // continuously queue additional objects for processing.
+ group.Go(func() error {
+ chunkSender := chunk.New(&checkBlobsGeneratedSender{stream: stream})
+
+ for req := range requestsChan {
+ for _, blob := range req.GetBlobs() {
+ object, err := queue.ReadObject(ctx)
+ if err != nil {
+ return fmt.Errorf("reading object: %w", err)
+ }
+
+ // The requested Git revisions must always resolve to a blob. Otherwise, there is
+ // not a file to perform the generation check on.
+ if !object.IsBlob() {
+ return structerr.NewInvalidArgument("object is not a blob")
+ }
+
+ // Read an arbitrary number of bytes that is considered enough to determine whether
+ // the file is generated.
+ content, err := io.ReadAll(io.LimitReader(object, 2048))
+ if err != nil {
+ return fmt.Errorf("reading blob content: %w", err)
+ }
+
+ // Any remaining blob data must be consumed before reading the next object. This is
+ // quite inefficient, but there is currently no alternative because git-cat-file(1)
+ // cannot be asked to limit the number of bytes it's outputting.
+ if _, err := io.Copy(io.Discard, object); err != nil {
+ return fmt.Errorf("discarding remaining blob content: %w", err)
+ }
+
+ if err := chunkSender.Send(&gitalypb.CheckBlobsGeneratedResponse_Blob{
+ Revision: blob.Revision,
+ Generated: enry.IsGenerated(string(blob.Path), content),
+ }); err != nil {
+ return fmt.Errorf("sending response: %w", err)
+ }
+ }
+
+ // The sender is flushed for each received request message so that at least one response
+ // message is always produced.
+ if err := chunkSender.Flush(); err != nil {
+ return fmt.Errorf("flushing response: %w", err)
+ }
+ }
+
+ return nil
+ })
+
+ // Ensure that the sending goroutine always closes and any lingering requests are first
+ // processed before the surrounding function returns.
+ defer func() {
+ close(requestsChan)
+ if err := group.Wait(); err != nil && returnedErr == nil {
+ returnedErr = err
+ }
+ }()
+
+ for {
+ if err := validateCheckBlobsGeneratedRequest(req); err != nil {
+ return structerr.NewInvalidArgument("validating request: %w", err)
+ }
+
+ // Queue up all revisions specified in the request for processing through git-cat-file(1).
+ for _, blob := range req.GetBlobs() {
+ if err := queue.RequestObject(ctx, git.Revision(blob.Revision)); err != nil {
+ return fmt.Errorf("requesting object: %w", err)
+ }
+ }
+
+ if err := queue.Flush(ctx); err != nil {
+ return fmt.Errorf("flushing queue: %w", err)
+ }
+
+ select {
+ // When performing the file generation check the file path is used to gain additional
+ // insight. Send the request to the processing goroutine to provide file paths and context
+ // for how to batch response messages.
+ case requestsChan <- req:
+ // The group context is cancelled when the sending goroutine exits with an error.
+ case <-groupCtx.Done():
+ return nil
+ }
+
+ req, err = stream.Recv()
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ break
+ }
+
+ return fmt.Errorf("receiving next request: %w", err)
+ }
+ }
+
+ return nil
+}
+
+func validateCheckBlobsGeneratedRequest(req *gitalypb.CheckBlobsGeneratedRequest) error {
+ if len(req.Blobs) == 0 {
+ return errors.New("empty blobs")
+ }
+
+ for _, blob := range req.Blobs {
+ if err := git.ValidateRevision(blob.Revision, git.AllowPathScopedRevision()); err != nil {
+ return err
+ }
+
+ if len(blob.GetPath()) == 0 {
+ return errors.New("empty path")
+ }
+ }
+
+ return nil
+}
+
+type checkBlobsGeneratedSender struct {
+ stream gitalypb.AnalysisService_CheckBlobsGeneratedServer
+ response *gitalypb.CheckBlobsGeneratedResponse
+}
+
+func (s *checkBlobsGeneratedSender) Reset() {
+ s.response = &gitalypb.CheckBlobsGeneratedResponse{}
+}
+
+func (s *checkBlobsGeneratedSender) Append(m proto.Message) {
+ s.response.Blobs = append(s.response.Blobs, m.(*gitalypb.CheckBlobsGeneratedResponse_Blob))
+}
+
+func (s *checkBlobsGeneratedSender) Send() error {
+ return s.stream.Send(s.response)
+}
diff --git a/internal/gitaly/service/analysis/check_generated_test.go b/internal/gitaly/service/analysis/check_generated_test.go
new file mode 100644
index 000000000..a3ca63de9
--- /dev/null
+++ b/internal/gitaly/service/analysis/check_generated_test.go
@@ -0,0 +1,374 @@
+package analysis
+
+import (
+ "errors"
+ "io"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/git/gittest"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/service"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/service/repository"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/structerr"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testcfg"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testserver"
+ "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials/insecure"
+)
+
+func TestCheckBlobsGenerated(t *testing.T) {
+ t.Parallel()
+
+ ctx := testhelper.Context(t)
+ cfg := testcfg.Build(t)
+
+ addr := testserver.RunGitalyServer(t, cfg, func(srv *grpc.Server, deps *service.Dependencies) {
+ gitalypb.RegisterAnalysisServiceServer(srv, NewServer(deps))
+ gitalypb.RegisterRepositoryServiceServer(srv, repository.NewServer(deps))
+ })
+ cfg.SocketPath = addr
+
+ conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
+ require.NoError(t, err)
+ t.Cleanup(func() {
+ testhelper.MustClose(t, conn)
+ })
+
+ client := gitalypb.NewAnalysisServiceClient(conn)
+
+ type setupData struct {
+ requests []*gitalypb.CheckBlobsGeneratedRequest
+ expectedResponses []*gitalypb.CheckBlobsGeneratedResponse
+ expectedError error
+ }
+
+ for _, tc := range []struct {
+ desc string
+ setup func(t *testing.T) setupData
+ }{
+ {
+ desc: "repository not set",
+ setup: func(t *testing.T) setupData {
+ return setupData{
+ requests: []*gitalypb.CheckBlobsGeneratedRequest{
+ {
+ Repository: nil,
+ },
+ },
+ expectedError: structerr.NewInvalidArgument("repository not set"),
+ }
+ },
+ },
+ {
+ desc: "blobs not set",
+ setup: func(t *testing.T) setupData {
+ repo, _ := gittest.CreateRepository(t, ctx, cfg)
+
+ return setupData{
+ requests: []*gitalypb.CheckBlobsGeneratedRequest{
+ {
+ Repository: repo,
+ Blobs: nil,
+ },
+ },
+ expectedError: structerr.NewInvalidArgument("validating request: empty blobs"),
+ }
+ },
+ },
+ {
+ desc: "revision not set",
+ setup: func(t *testing.T) setupData {
+ repo, _ := gittest.CreateRepository(t, ctx, cfg)
+
+ return setupData{
+ requests: []*gitalypb.CheckBlobsGeneratedRequest{
+ {
+ Repository: repo,
+ Blobs: []*gitalypb.CheckBlobsGeneratedRequest_Blob{
+ {
+ Revision: nil,
+ Path: []byte("path/to/file"),
+ },
+ },
+ },
+ },
+ expectedError: structerr.NewInvalidArgument("validating request: empty revision"),
+ }
+ },
+ },
+ {
+ desc: "invalid revision",
+ setup: func(t *testing.T) setupData {
+ repo, _ := gittest.CreateRepository(t, ctx, cfg)
+
+ return setupData{
+ requests: []*gitalypb.CheckBlobsGeneratedRequest{
+ {
+ Repository: repo,
+ Blobs: []*gitalypb.CheckBlobsGeneratedRequest_Blob{
+ {
+ Revision: []byte("not a valid revision"),
+ Path: []byte("path/to/file"),
+ },
+ },
+ },
+ },
+ expectedError: structerr.NewInvalidArgument("validating request: revision can't contain whitespace"),
+ }
+ },
+ },
+ {
+ desc: "path not set",
+ setup: func(t *testing.T) setupData {
+ repo, _ := gittest.CreateRepository(t, ctx, cfg)
+
+ return setupData{
+ requests: []*gitalypb.CheckBlobsGeneratedRequest{
+ {
+ Repository: repo,
+ Blobs: []*gitalypb.CheckBlobsGeneratedRequest_Blob{
+ {
+ Revision: []byte("foo:bar"),
+ Path: nil,
+ },
+ },
+ },
+ },
+ expectedError: structerr.NewInvalidArgument("validating request: empty path"),
+ }
+ },
+ },
+ {
+ desc: "object not found",
+ setup: func(t *testing.T) setupData {
+ repo, _ := gittest.CreateRepository(t, ctx, cfg)
+ revision := "foo:bar"
+
+ return setupData{
+ requests: []*gitalypb.CheckBlobsGeneratedRequest{
+ {
+ Repository: repo,
+ Blobs: []*gitalypb.CheckBlobsGeneratedRequest_Blob{
+ {
+ Revision: []byte(revision),
+ Path: []byte("bar"),
+ },
+ },
+ },
+ },
+ expectedError: testhelper.WithInterceptedMetadata(
+ structerr.NewInternal("reading object: object not found"),
+ "revision",
+ revision,
+ ),
+ }
+ },
+ },
+ {
+ desc: "revision does not resolve to a blob",
+ setup: func(t *testing.T) setupData {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg)
+ commitID := gittest.WriteCommit(t, cfg, repoPath)
+
+ return setupData{
+ requests: []*gitalypb.CheckBlobsGeneratedRequest{
+ {
+ Repository: repo,
+ Blobs: []*gitalypb.CheckBlobsGeneratedRequest_Blob{
+ {
+ Revision: []byte(commitID),
+ Path: []byte("bar"),
+ },
+ },
+ },
+ },
+ expectedError: structerr.NewInvalidArgument("object is not a blob"),
+ }
+ },
+ },
+ {
+ desc: "detect generated blob via file path",
+ setup: func(t *testing.T) setupData {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg)
+ blobID := gittest.WriteBlob(t, cfg, repoPath, []byte("foobar"))
+
+ return setupData{
+ requests: []*gitalypb.CheckBlobsGeneratedRequest{
+ {
+ Repository: repo,
+ Blobs: []*gitalypb.CheckBlobsGeneratedRequest_Blob{
+ {
+ Revision: []byte(blobID),
+ Path: []byte("Gopkg.lock"),
+ },
+ },
+ },
+ },
+ expectedResponses: []*gitalypb.CheckBlobsGeneratedResponse{
+ {
+ Blobs: []*gitalypb.CheckBlobsGeneratedResponse_Blob{
+ {
+ Revision: []byte(blobID),
+ Generated: true,
+ },
+ },
+ },
+ },
+ }
+ },
+ },
+ {
+ desc: "detect generated blob via content",
+ setup: func(t *testing.T) setupData {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg)
+ gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("foo"), gittest.WithTreeEntries(
+ gittest.TreeEntry{Mode: "100644", Path: "bar.go", Content: "Code generated by\nfoobar\n"},
+ ))
+
+ return setupData{
+ requests: []*gitalypb.CheckBlobsGeneratedRequest{
+ {
+ Repository: repo,
+ Blobs: []*gitalypb.CheckBlobsGeneratedRequest_Blob{
+ {
+ Revision: []byte("foo:bar.go"),
+ Path: []byte("bar.go"),
+ },
+ },
+ },
+ },
+ expectedResponses: []*gitalypb.CheckBlobsGeneratedResponse{
+ {
+ Blobs: []*gitalypb.CheckBlobsGeneratedResponse_Blob{
+ {
+ Revision: []byte("foo:bar.go"),
+ Generated: true,
+ },
+ },
+ },
+ },
+ }
+ },
+ },
+ {
+ desc: "detect non-generated blob",
+ setup: func(t *testing.T) setupData {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg)
+ blobID := gittest.WriteBlob(t, cfg, repoPath, []byte("foobar"))
+
+ return setupData{
+ requests: []*gitalypb.CheckBlobsGeneratedRequest{
+ {
+ Repository: repo,
+ Blobs: []*gitalypb.CheckBlobsGeneratedRequest_Blob{
+ {
+ Revision: []byte(blobID),
+ Path: []byte("foobar.go"),
+ },
+ },
+ },
+ },
+ expectedResponses: []*gitalypb.CheckBlobsGeneratedResponse{
+ {
+ Blobs: []*gitalypb.CheckBlobsGeneratedResponse_Blob{
+ {
+ Revision: []byte(blobID),
+ Generated: false,
+ },
+ },
+ },
+ },
+ }
+ },
+ },
+ {
+ desc: "check stream of files",
+ setup: func(t *testing.T) setupData {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg)
+ generatedBlobID := gittest.WriteBlob(t, cfg, repoPath, []byte("Code generated by\nfoobar\n"))
+ normalBlobID := gittest.WriteBlob(t, cfg, repoPath, []byte("foobar"))
+
+ return setupData{
+ requests: []*gitalypb.CheckBlobsGeneratedRequest{
+ {
+ Repository: repo,
+ Blobs: []*gitalypb.CheckBlobsGeneratedRequest_Blob{
+ {
+ Revision: []byte(generatedBlobID),
+ Path: []byte("foo.go"),
+ },
+ {
+ Revision: []byte(normalBlobID),
+ Path: []byte("bar.go"),
+ },
+ },
+ },
+ {
+ Blobs: []*gitalypb.CheckBlobsGeneratedRequest_Blob{
+ {
+ Revision: []byte(normalBlobID),
+ Path: []byte("bar.go"),
+ },
+ },
+ },
+ },
+ expectedResponses: []*gitalypb.CheckBlobsGeneratedResponse{
+ {
+ Blobs: []*gitalypb.CheckBlobsGeneratedResponse_Blob{
+ {
+ Revision: []byte(generatedBlobID),
+ Generated: true,
+ },
+ {
+ Revision: []byte(normalBlobID),
+ Generated: false,
+ },
+ },
+ },
+ {
+ Blobs: []*gitalypb.CheckBlobsGeneratedResponse_Blob{
+ {
+ Revision: []byte(normalBlobID),
+ Generated: false,
+ },
+ },
+ },
+ },
+ }
+ },
+ },
+ } {
+ tc := tc
+ t.Run(tc.desc, func(t *testing.T) {
+ t.Parallel()
+ testSetup := tc.setup(t)
+
+ stream, err := client.CheckBlobsGenerated(ctx)
+ require.NoError(t, err)
+
+ for _, req := range testSetup.requests {
+ err := stream.Send(req)
+ require.NoError(t, err)
+ }
+
+ require.NoError(t, stream.CloseSend())
+
+ var actualResponses []*gitalypb.CheckBlobsGeneratedResponse
+ for {
+ resp, err := stream.Recv()
+ if err != nil {
+ if !errors.Is(err, io.EOF) {
+ testhelper.RequireGrpcError(t, testSetup.expectedError, err)
+ }
+ break
+ }
+
+ actualResponses = append(actualResponses, resp)
+ }
+
+ testhelper.ProtoEqual(t, testSetup.expectedResponses, actualResponses)
+ })
+ }
+}
diff --git a/internal/gitaly/service/analysis/server.go b/internal/gitaly/service/analysis/server.go
new file mode 100644
index 000000000..965ac72af
--- /dev/null
+++ b/internal/gitaly/service/analysis/server.go
@@ -0,0 +1,33 @@
+package analysis
+
+import (
+ "gitlab.com/gitlab-org/gitaly/v16/internal/git"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/git/catfile"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/git/localrepo"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/service"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/storage"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/log"
+ "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb"
+)
+
+type server struct {
+ gitalypb.UnimplementedAnalysisServiceServer
+ logger log.Logger
+ locator storage.Locator
+ gitCmdFactory git.CommandFactory
+ catfileCache catfile.Cache
+}
+
+// NewServer creates a new instance of the gRPC AnalysisService.
+func NewServer(deps *service.Dependencies) gitalypb.AnalysisServiceServer {
+ return &server{
+ logger: deps.GetLogger(),
+ locator: deps.GetLocator(),
+ gitCmdFactory: deps.GetGitCmdFactory(),
+ catfileCache: deps.GetCatfileCache(),
+ }
+}
+
+func (s *server) localrepo(repo storage.Repository) *localrepo.Repo {
+ return localrepo.New(s.logger, s.locator, s.gitCmdFactory, s.catfileCache, repo)
+}
diff --git a/internal/gitaly/service/analysis/testhelper_test.go b/internal/gitaly/service/analysis/testhelper_test.go
new file mode 100644
index 000000000..4b3cbd7dd
--- /dev/null
+++ b/internal/gitaly/service/analysis/testhelper_test.go
@@ -0,0 +1,11 @@
+package analysis
+
+import (
+ "testing"
+
+ "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper"
+)
+
+func TestMain(m *testing.M) {
+ testhelper.Run(m)
+}
diff --git a/internal/gitaly/service/setup/register.go b/internal/gitaly/service/setup/register.go
index 4076abb84..e05c8fa07 100644
--- a/internal/gitaly/service/setup/register.go
+++ b/internal/gitaly/service/setup/register.go
@@ -5,6 +5,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/service"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/service/analysis"
"gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/service/blob"
"gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/service/cleanup"
"gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/service/commit"
@@ -51,6 +52,7 @@ var (
// RegisterAll will register all the known gRPC services on the provided gRPC service instance.
func RegisterAll(srv *grpc.Server, deps *service.Dependencies) {
+ gitalypb.RegisterAnalysisServiceServer(srv, analysis.NewServer(deps))
gitalypb.RegisterBlobServiceServer(srv, blob.NewServer(deps))
gitalypb.RegisterCleanupServiceServer(srv, cleanup.NewServer(deps))
gitalypb.RegisterCommitServiceServer(srv, commit.NewServer(deps))
diff --git a/proto/analysis.proto b/proto/analysis.proto
new file mode 100644
index 000000000..ced5b9cb9
--- /dev/null
+++ b/proto/analysis.proto
@@ -0,0 +1,62 @@
+syntax = "proto3";
+
+package gitaly;
+
+import "lint.proto";
+import "shared.proto";
+
+option go_package = "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb";
+
+// AnalysisService is a gRPC service providing RPCs that analyze objects in a repository.
+service AnalysisService {
+
+ // CheckBlobsGenerated checks a provided set of blobs in a specified repository to determine
+ // whether the file is considered generated. This RPC supports bidirectional streaming because the
+ // client may specify any number of files to check across multiple request messages and the server
+ // responds to each request with a separate set of response messages.
+ //
+ // Each provided blob in the request is validated when received. Improperly formatted requests
+ // result in RPC termination and an error being returned to the client. The RPC also aborts and
+ // returns an error if requests are made that list Git revisions which do not resolve to a blob
+ // and/or cannot be found in the repository.
+ rpc CheckBlobsGenerated(stream CheckBlobsGeneratedRequest) returns (stream CheckBlobsGeneratedResponse) {
+ option (op_type) = {
+ op: ACCESSOR
+ };
+ }
+}
+
+// CheckBlobsGeneratedRequest is a request for the CheckBlobsGenerated RPC. The client may send
+// multiple requests through the stream to check multiple sets of files. The first request must have
+// the repository field set. Every request, including the first, must contain a blob set with at
+// least a single entry.
+message CheckBlobsGeneratedRequest {
+ // Blob defines the required information to determine if a blob is generated.
+ message Blob {
+ // revision is a Git revision that resolves to a blob.
+ bytes revision = 1;
+ // path is the file path of the blob and is used to gain insight as to whether the blob is
+ // generated.
+ bytes path = 2;
+ }
+
+ // repository is the repository that generated files are being checked for.
+ Repository repository = 1 [(target_repository)=true];
+ // blobs is a set of blobs that the generated file check is being performed on.
+ repeated Blob blobs = 2;
+}
+
+// CheckBlobsGeneratedResponse is a response for the CheckBlobsGenerated RPC. If the client sends
+// multiple requests, the server responds with a separate response message for each request.
+message CheckBlobsGeneratedResponse {
+ // Blob defines the status of the generated blob check for a revision.
+ message Blob {
+ // revision is the Git revision of the checked blob provided in the request.
+ bytes revision = 1;
+ // generated is the result of the file generation check for a particular blob.
+ bool generated = 2;
+ }
+
+ // blobs is a set of blobs that the generated file check has been performed on.
+ repeated Blob blobs = 1;
+}
diff --git a/proto/go/gitalypb/analysis.pb.go b/proto/go/gitalypb/analysis.pb.go
new file mode 100644
index 000000000..d19efdc18
--- /dev/null
+++ b/proto/go/gitalypb/analysis.pb.go
@@ -0,0 +1,403 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.31.0
+// protoc v4.23.1
+// source: analysis.proto
+
+package gitalypb
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// CheckBlobsGeneratedRequest is a request for the CheckBlobsGenerated RPC. The client may send
+// multiple requests through the stream to check multiple sets of files. The first request must have
+// the repository field set. Every request, including the first, must contain a blob set with at
+// least a single entry.
+type CheckBlobsGeneratedRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // repository is the repository that generated files are being checked for.
+ Repository *Repository `protobuf:"bytes,1,opt,name=repository,proto3" json:"repository,omitempty"`
+ // blobs is a set of blobs that the generated file check is being performed on.
+ Blobs []*CheckBlobsGeneratedRequest_Blob `protobuf:"bytes,2,rep,name=blobs,proto3" json:"blobs,omitempty"`
+}
+
+func (x *CheckBlobsGeneratedRequest) Reset() {
+ *x = CheckBlobsGeneratedRequest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_analysis_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CheckBlobsGeneratedRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CheckBlobsGeneratedRequest) ProtoMessage() {}
+
+func (x *CheckBlobsGeneratedRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_analysis_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CheckBlobsGeneratedRequest.ProtoReflect.Descriptor instead.
+func (*CheckBlobsGeneratedRequest) Descriptor() ([]byte, []int) {
+ return file_analysis_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *CheckBlobsGeneratedRequest) GetRepository() *Repository {
+ if x != nil {
+ return x.Repository
+ }
+ return nil
+}
+
+func (x *CheckBlobsGeneratedRequest) GetBlobs() []*CheckBlobsGeneratedRequest_Blob {
+ if x != nil {
+ return x.Blobs
+ }
+ return nil
+}
+
+// CheckBlobsGeneratedResponse is a response for the CheckBlobsGenerated RPC. If the client sends
+// multiple requests, the server responds with a separate response message for each request.
+type CheckBlobsGeneratedResponse struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // blobs is a set of blobs that the generated file check has been performed on.
+ Blobs []*CheckBlobsGeneratedResponse_Blob `protobuf:"bytes,1,rep,name=blobs,proto3" json:"blobs,omitempty"`
+}
+
+func (x *CheckBlobsGeneratedResponse) Reset() {
+ *x = CheckBlobsGeneratedResponse{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_analysis_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CheckBlobsGeneratedResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CheckBlobsGeneratedResponse) ProtoMessage() {}
+
+func (x *CheckBlobsGeneratedResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_analysis_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CheckBlobsGeneratedResponse.ProtoReflect.Descriptor instead.
+func (*CheckBlobsGeneratedResponse) Descriptor() ([]byte, []int) {
+ return file_analysis_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *CheckBlobsGeneratedResponse) GetBlobs() []*CheckBlobsGeneratedResponse_Blob {
+ if x != nil {
+ return x.Blobs
+ }
+ return nil
+}
+
+// Blob defines the required information to determine if a blob is generated.
+type CheckBlobsGeneratedRequest_Blob struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // revision is a Git revision that resolves to a blob.
+ Revision []byte `protobuf:"bytes,1,opt,name=revision,proto3" json:"revision,omitempty"`
+ // path is the file path of the blob and is used to gain insight as to whether the blob is
+ // generated.
+ Path []byte `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
+}
+
+func (x *CheckBlobsGeneratedRequest_Blob) Reset() {
+ *x = CheckBlobsGeneratedRequest_Blob{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_analysis_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CheckBlobsGeneratedRequest_Blob) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CheckBlobsGeneratedRequest_Blob) ProtoMessage() {}
+
+func (x *CheckBlobsGeneratedRequest_Blob) ProtoReflect() protoreflect.Message {
+ mi := &file_analysis_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CheckBlobsGeneratedRequest_Blob.ProtoReflect.Descriptor instead.
+func (*CheckBlobsGeneratedRequest_Blob) Descriptor() ([]byte, []int) {
+ return file_analysis_proto_rawDescGZIP(), []int{0, 0}
+}
+
+func (x *CheckBlobsGeneratedRequest_Blob) GetRevision() []byte {
+ if x != nil {
+ return x.Revision
+ }
+ return nil
+}
+
+func (x *CheckBlobsGeneratedRequest_Blob) GetPath() []byte {
+ if x != nil {
+ return x.Path
+ }
+ return nil
+}
+
+// Blob defines the status of the generated blob check for a revision.
+type CheckBlobsGeneratedResponse_Blob struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // revision is the Git revision of the checked blob provided in the request.
+ Revision []byte `protobuf:"bytes,1,opt,name=revision,proto3" json:"revision,omitempty"`
+ // generated is the result of the file generation check for a particular blob.
+ Generated bool `protobuf:"varint,2,opt,name=generated,proto3" json:"generated,omitempty"`
+}
+
+func (x *CheckBlobsGeneratedResponse_Blob) Reset() {
+ *x = CheckBlobsGeneratedResponse_Blob{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_analysis_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CheckBlobsGeneratedResponse_Blob) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CheckBlobsGeneratedResponse_Blob) ProtoMessage() {}
+
+func (x *CheckBlobsGeneratedResponse_Blob) ProtoReflect() protoreflect.Message {
+ mi := &file_analysis_proto_msgTypes[3]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CheckBlobsGeneratedResponse_Blob.ProtoReflect.Descriptor instead.
+func (*CheckBlobsGeneratedResponse_Blob) Descriptor() ([]byte, []int) {
+ return file_analysis_proto_rawDescGZIP(), []int{1, 0}
+}
+
+func (x *CheckBlobsGeneratedResponse_Blob) GetRevision() []byte {
+ if x != nil {
+ return x.Revision
+ }
+ return nil
+}
+
+func (x *CheckBlobsGeneratedResponse_Blob) GetGenerated() bool {
+ if x != nil {
+ return x.Generated
+ }
+ return false
+}
+
+var File_analysis_proto protoreflect.FileDescriptor
+
+var file_analysis_proto_rawDesc = []byte{
+ 0x0a, 0x0e, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x12, 0x06, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x1a, 0x0a, 0x6c, 0x69, 0x6e, 0x74, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0c, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x22, 0xcd, 0x01, 0x0a, 0x1a, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x6c, 0x6f, 0x62,
+ 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x12, 0x38, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52,
+ 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x42, 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52,
+ 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x3d, 0x0a, 0x05, 0x62,
+ 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x67, 0x69, 0x74,
+ 0x61, 0x6c, 0x79, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x6c, 0x6f, 0x62, 0x73, 0x47, 0x65,
+ 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x42,
+ 0x6c, 0x6f, 0x62, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x1a, 0x36, 0x0a, 0x04, 0x42, 0x6c,
+ 0x6f, 0x62, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12,
+ 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, 0x61,
+ 0x74, 0x68, 0x22, 0x9f, 0x01, 0x0a, 0x1b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x6c, 0x6f, 0x62,
+ 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+ 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
+ 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b,
+ 0x42, 0x6c, 0x6f, 0x62, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65,
+ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x05, 0x62, 0x6c, 0x6f,
+ 0x62, 0x73, 0x1a, 0x40, 0x0a, 0x04, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65,
+ 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, 0x65,
+ 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
+ 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72,
+ 0x61, 0x74, 0x65, 0x64, 0x32, 0x7d, 0x0a, 0x0f, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73,
+ 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6a, 0x0a, 0x13, 0x43, 0x68, 0x65, 0x63, 0x6b,
+ 0x42, 0x6c, 0x6f, 0x62, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x12, 0x22,
+ 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x6c, 0x6f,
+ 0x62, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x43, 0x68, 0x65, 0x63,
+ 0x6b, 0x42, 0x6c, 0x6f, 0x62, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x52,
+ 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x02, 0x28,
+ 0x01, 0x30, 0x01, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2d, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x69, 0x74,
+ 0x61, 0x6c, 0x79, 0x2f, 0x76, 0x31, 0x36, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f,
+ 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x33,
+}
+
+var (
+ file_analysis_proto_rawDescOnce sync.Once
+ file_analysis_proto_rawDescData = file_analysis_proto_rawDesc
+)
+
+func file_analysis_proto_rawDescGZIP() []byte {
+ file_analysis_proto_rawDescOnce.Do(func() {
+ file_analysis_proto_rawDescData = protoimpl.X.CompressGZIP(file_analysis_proto_rawDescData)
+ })
+ return file_analysis_proto_rawDescData
+}
+
+var file_analysis_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_analysis_proto_goTypes = []interface{}{
+ (*CheckBlobsGeneratedRequest)(nil), // 0: gitaly.CheckBlobsGeneratedRequest
+ (*CheckBlobsGeneratedResponse)(nil), // 1: gitaly.CheckBlobsGeneratedResponse
+ (*CheckBlobsGeneratedRequest_Blob)(nil), // 2: gitaly.CheckBlobsGeneratedRequest.Blob
+ (*CheckBlobsGeneratedResponse_Blob)(nil), // 3: gitaly.CheckBlobsGeneratedResponse.Blob
+ (*Repository)(nil), // 4: gitaly.Repository
+}
+var file_analysis_proto_depIdxs = []int32{
+ 4, // 0: gitaly.CheckBlobsGeneratedRequest.repository:type_name -> gitaly.Repository
+ 2, // 1: gitaly.CheckBlobsGeneratedRequest.blobs:type_name -> gitaly.CheckBlobsGeneratedRequest.Blob
+ 3, // 2: gitaly.CheckBlobsGeneratedResponse.blobs:type_name -> gitaly.CheckBlobsGeneratedResponse.Blob
+ 0, // 3: gitaly.AnalysisService.CheckBlobsGenerated:input_type -> gitaly.CheckBlobsGeneratedRequest
+ 1, // 4: gitaly.AnalysisService.CheckBlobsGenerated:output_type -> gitaly.CheckBlobsGeneratedResponse
+ 4, // [4:5] is the sub-list for method output_type
+ 3, // [3:4] is the sub-list for method input_type
+ 3, // [3:3] is the sub-list for extension type_name
+ 3, // [3:3] is the sub-list for extension extendee
+ 0, // [0:3] is the sub-list for field type_name
+}
+
+func init() { file_analysis_proto_init() }
+func file_analysis_proto_init() {
+ if File_analysis_proto != nil {
+ return
+ }
+ file_lint_proto_init()
+ file_shared_proto_init()
+ if !protoimpl.UnsafeEnabled {
+ file_analysis_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CheckBlobsGeneratedRequest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_analysis_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CheckBlobsGeneratedResponse); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_analysis_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CheckBlobsGeneratedRequest_Blob); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_analysis_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*CheckBlobsGeneratedResponse_Blob); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_analysis_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 4,
+ NumExtensions: 0,
+ NumServices: 1,
+ },
+ GoTypes: file_analysis_proto_goTypes,
+ DependencyIndexes: file_analysis_proto_depIdxs,
+ MessageInfos: file_analysis_proto_msgTypes,
+ }.Build()
+ File_analysis_proto = out.File
+ file_analysis_proto_rawDesc = nil
+ file_analysis_proto_goTypes = nil
+ file_analysis_proto_depIdxs = nil
+}
diff --git a/proto/go/gitalypb/analysis_grpc.pb.go b/proto/go/gitalypb/analysis_grpc.pb.go
new file mode 100644
index 000000000..04055e50e
--- /dev/null
+++ b/proto/go/gitalypb/analysis_grpc.pb.go
@@ -0,0 +1,155 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.2.0
+// - protoc v4.23.1
+// source: analysis.proto
+
+package gitalypb
+
+import (
+ context "context"
+ grpc "google.golang.org/grpc"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+// AnalysisServiceClient is the client API for AnalysisService service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type AnalysisServiceClient interface {
+ // CheckBlobsGenerated checks a provided set of blobs in a specified repository to determine
+ // whether the file is considered generated. This RPC supports bidirectional streaming because the
+ // client may specify any number of files to check across multiple request messages and the server
+ // responds to each request with a separate set of response messages.
+ //
+ // Each provided blob in the request is validated when received. Improperly formatted requests
+ // result in RPC termination and an error being returned to the client. The RPC also aborts and
+ // returns an error if requests are made that list Git revisions which do not resolve to a blob
+ // and/or cannot be found in the repository.
+ CheckBlobsGenerated(ctx context.Context, opts ...grpc.CallOption) (AnalysisService_CheckBlobsGeneratedClient, error)
+}
+
+type analysisServiceClient struct {
+ cc grpc.ClientConnInterface
+}
+
+func NewAnalysisServiceClient(cc grpc.ClientConnInterface) AnalysisServiceClient {
+ return &analysisServiceClient{cc}
+}
+
+func (c *analysisServiceClient) CheckBlobsGenerated(ctx context.Context, opts ...grpc.CallOption) (AnalysisService_CheckBlobsGeneratedClient, error) {
+ stream, err := c.cc.NewStream(ctx, &AnalysisService_ServiceDesc.Streams[0], "/gitaly.AnalysisService/CheckBlobsGenerated", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &analysisServiceCheckBlobsGeneratedClient{stream}
+ return x, nil
+}
+
+type AnalysisService_CheckBlobsGeneratedClient interface {
+ Send(*CheckBlobsGeneratedRequest) error
+ Recv() (*CheckBlobsGeneratedResponse, error)
+ grpc.ClientStream
+}
+
+type analysisServiceCheckBlobsGeneratedClient struct {
+ grpc.ClientStream
+}
+
+func (x *analysisServiceCheckBlobsGeneratedClient) Send(m *CheckBlobsGeneratedRequest) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *analysisServiceCheckBlobsGeneratedClient) Recv() (*CheckBlobsGeneratedResponse, error) {
+ m := new(CheckBlobsGeneratedResponse)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+// AnalysisServiceServer is the server API for AnalysisService service.
+// All implementations must embed UnimplementedAnalysisServiceServer
+// for forward compatibility
+type AnalysisServiceServer interface {
+ // CheckBlobsGenerated checks a provided set of blobs in a specified repository to determine
+ // whether the file is considered generated. This RPC supports bidirectional streaming because the
+ // client may specify any number of files to check across multiple request messages and the server
+ // responds to each request with a separate set of response messages.
+ //
+ // Each provided blob in the request is validated when received. Improperly formatted requests
+ // result in RPC termination and an error being returned to the client. The RPC also aborts and
+ // returns an error if requests are made that list Git revisions which do not resolve to a blob
+ // and/or cannot be found in the repository.
+ CheckBlobsGenerated(AnalysisService_CheckBlobsGeneratedServer) error
+ mustEmbedUnimplementedAnalysisServiceServer()
+}
+
+// UnimplementedAnalysisServiceServer must be embedded to have forward compatible implementations.
+type UnimplementedAnalysisServiceServer struct {
+}
+
+func (UnimplementedAnalysisServiceServer) CheckBlobsGenerated(AnalysisService_CheckBlobsGeneratedServer) error {
+ return status.Errorf(codes.Unimplemented, "method CheckBlobsGenerated not implemented")
+}
+func (UnimplementedAnalysisServiceServer) mustEmbedUnimplementedAnalysisServiceServer() {}
+
+// UnsafeAnalysisServiceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to AnalysisServiceServer will
+// result in compilation errors.
+type UnsafeAnalysisServiceServer interface {
+ mustEmbedUnimplementedAnalysisServiceServer()
+}
+
+func RegisterAnalysisServiceServer(s grpc.ServiceRegistrar, srv AnalysisServiceServer) {
+ s.RegisterService(&AnalysisService_ServiceDesc, srv)
+}
+
+func _AnalysisService_CheckBlobsGenerated_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(AnalysisServiceServer).CheckBlobsGenerated(&analysisServiceCheckBlobsGeneratedServer{stream})
+}
+
+type AnalysisService_CheckBlobsGeneratedServer interface {
+ Send(*CheckBlobsGeneratedResponse) error
+ Recv() (*CheckBlobsGeneratedRequest, error)
+ grpc.ServerStream
+}
+
+type analysisServiceCheckBlobsGeneratedServer struct {
+ grpc.ServerStream
+}
+
+func (x *analysisServiceCheckBlobsGeneratedServer) Send(m *CheckBlobsGeneratedResponse) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *analysisServiceCheckBlobsGeneratedServer) Recv() (*CheckBlobsGeneratedRequest, error) {
+ m := new(CheckBlobsGeneratedRequest)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+// AnalysisService_ServiceDesc is the grpc.ServiceDesc for AnalysisService service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var AnalysisService_ServiceDesc = grpc.ServiceDesc{
+ ServiceName: "gitaly.AnalysisService",
+ HandlerType: (*AnalysisServiceServer)(nil),
+ Methods: []grpc.MethodDesc{},
+ Streams: []grpc.StreamDesc{
+ {
+ StreamName: "CheckBlobsGenerated",
+ Handler: _AnalysisService_CheckBlobsGenerated_Handler,
+ ServerStreams: true,
+ ClientStreams: true,
+ },
+ },
+ Metadata: "analysis.proto",
+}
diff --git a/proto/go/gitalypb/protolist.go b/proto/go/gitalypb/protolist.go
index 30e8c28a3..b7e92510a 100644
--- a/proto/go/gitalypb/protolist.go
+++ b/proto/go/gitalypb/protolist.go
@@ -4,6 +4,7 @@ package gitalypb
// GitalyProtos is a list of gitaly protobuf files
var GitalyProtos = []string{
+ "analysis.proto",
"blob.proto",
"cleanup.proto",
"commit.proto",