diff options
author | Zeger-Jan van de Weg <git@zjvandeweg.nl> | 2019-07-05 12:28:17 +0300 |
---|---|---|
committer | Zeger-Jan van de Weg <git@zjvandeweg.nl> | 2019-07-05 12:28:17 +0300 |
commit | 2375f7a95e0b0affc7e3439083b084af348505b4 (patch) | |
tree | 2693a49918682c12ea22e4085b8c569ac9e62497 | |
parent | 873a408c49ef1b34fce0cd47a80aa192f426d7c0 (diff) | |
parent | b9c9aec5cf53de4ea1a7fc3b067dd56d7828e080 (diff) |
Merge branch 'jv-host-gitaly-proto' into 'master'
Start preparation for migrating .proto files
See merge request gitlab-org/gitaly!1349
-rwxr-xr-x | _support/migrate-proto | 55 | ||||
-rw-r--r-- | _support/run.rb | 7 | ||||
-rw-r--r-- | proto/CONTRIBUTING.md | 12 | ||||
-rw-r--r-- | proto/DEPRECATION.md | 16 | ||||
-rw-r--r-- | proto/LICENSE | 21 | ||||
-rw-r--r-- | proto/README.md | 7 | ||||
-rw-r--r-- | proto/README.orig.md | 319 | ||||
-rw-r--r-- | proto/RELEASE.md | 30 | ||||
-rw-r--r-- | proto/VERSION | 1 | ||||
-rw-r--r-- | proto/blob.proto | 118 | ||||
-rw-r--r-- | proto/cleanup.proto | 69 | ||||
-rw-r--r-- | proto/commit.proto | 386 | ||||
-rw-r--r-- | proto/conflicts.proto | 66 | ||||
-rw-r--r-- | proto/diff.proto | 145 | ||||
-rw-r--r-- | proto/namespace.proto | 60 | ||||
-rw-r--r-- | proto/objectpool.proto | 102 | ||||
-rw-r--r-- | proto/operations.proto | 432 | ||||
-rw-r--r-- | proto/ref.proto | 317 | ||||
-rw-r--r-- | proto/remote.proto | 118 | ||||
-rw-r--r-- | proto/repository-service.proto | 594 | ||||
-rw-r--r-- | proto/server.proto | 29 | ||||
-rw-r--r-- | proto/shared.proto | 135 | ||||
-rw-r--r-- | proto/smarthttp.proto | 91 | ||||
-rw-r--r-- | proto/ssh.proto | 99 | ||||
-rw-r--r-- | proto/storage.proto | 37 | ||||
-rw-r--r-- | proto/wiki.proto | 204 |
26 files changed, 3470 insertions, 0 deletions
diff --git a/_support/migrate-proto b/_support/migrate-proto new file mode 100755 index 000000000..c1529a5a4 --- /dev/null +++ b/_support/migrate-proto @@ -0,0 +1,55 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require_relative 'run.rb' + +require 'tempfile' + +SOURCE = 'https://gitlab.com/gitlab-org/gitaly-proto' + +def main + root = capture!(%w[git rev-parse --show-toplevel]).chomp + + Dir.mktmpdir do |dir| + Dir.chdir(dir) do + clone + modify + copy(root) + end + end +end + +def clone + run!(%W[git clone --quiet --depth=1 #{SOURCE}.git .]) +end + +def modify + Dir['*.proto'].each do |proto| + tmp = proto + '.bak' + run2!(%W[sed s/gitlab-org\\/gitaly-proto/gitlab-org\\/gitaly\\/proto/ #{proto}], out: tmp) + FileUtils.mv(tmp, proto) + end + + FileUtils.mv('README.md', 'README.orig.md') + + commit_id = capture!(%w[git rev-parse HEAD]).chomp + readme = <<EOS +# Vendored copy of gitaly-proto + +Vendored from #{SOURCE} at #{commit_id}. + +Migration in progress, see +https://gitlab.com/gitlab-org/gitaly/issues/1761. Do not edit files in +this directory, your changes will be ignored and overwritten. +EOS + IO.write('README.md', readme) +end + +def copy(root) + dest = File.join(root, 'proto') + FileUtils.rm(Dir["#{dest}/*.proto"]) + file_list = Dir['*.proto'] + Dir['*.md'] + %w[LICENSE VERSION] + FileUtils.cp(file_list, dest) +end + +main diff --git a/_support/run.rb b/_support/run.rb index f6d84829a..a62959180 100644 --- a/_support/run.rb +++ b/_support/run.rb @@ -5,6 +5,13 @@ def run!(cmd, chdir='.') end end +def run2!(cmd, chdir: '.', out: 1) + GitalySupport.print_cmd(cmd) + unless system(*cmd, chdir: chdir, out: out) + GitalySupport.fail_cmd!(cmd) + end +end + def capture!(cmd, chdir='.') GitalySupport.print_cmd(cmd) output = IO.popen(cmd, chdir: chdir) { |io| io.read } diff --git a/proto/CONTRIBUTING.md b/proto/CONTRIBUTING.md new file mode 100644 index 000000000..442c2b00c --- /dev/null +++ b/proto/CONTRIBUTING.md @@ -0,0 +1,12 @@ +## Developer Certificate of Origin + License + +By contributing to GitLab B.V., You accept and agree to the following terms and +conditions for Your present and future Contributions submitted to GitLab B.V. +Except for the license granted herein to GitLab B.V. and recipients of software +distributed by GitLab B.V., You reserve all right, title, and interest in and to +Your Contributions. All Contributions are subject to the following DCO + License +terms. + +[DCO + License](https://gitlab.com/gitlab-org/dco/blob/master/README.md) + +_This notice should stay as the first item in the CONTRIBUTING.md file._ diff --git a/proto/DEPRECATION.md b/proto/DEPRECATION.md new file mode 100644 index 000000000..f2f145bc0 --- /dev/null +++ b/proto/DEPRECATION.md @@ -0,0 +1,16 @@ +# RPC deprecation process for gitaly-proto + +First create a deprecation issue at +https://gitlab.com/gitlab-org/gitaly/issues with the title `Deprecate +RPC FooBar`. Use label `Deprecation`. Below is a template for the +issue description. + +``` +We are deprecating RPC FooBar because **REASONS**. + +- [ ] put a deprecation comment `// DEPRECATED: <ISSUE-LINK>` in gitaly-proto **Merge Request LINK** +- [ ] find all client-side uses of RPC and list below +- [ ] update all client-side uses to no longer use RPC **ADD Merge Request LINKS** +- [ ] wait for a GitLab release in which the RPC is no longer occurring in client side code **LINK TO GITLAB-CE RELEASE TAG** +- [ ] delete the server side implementation of the old RPC in https://gitlab.com/gitlab-org/gitaly **Merge Request LINK** +``` diff --git a/proto/LICENSE b/proto/LICENSE new file mode 100644 index 000000000..c7344c56d --- /dev/null +++ b/proto/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016-2017 GitLab B.V. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/proto/README.md b/proto/README.md new file mode 100644 index 000000000..0584f35e3 --- /dev/null +++ b/proto/README.md @@ -0,0 +1,7 @@ +# Vendored copy of gitaly-proto + +Vendored from https://gitlab.com/gitlab-org/gitaly-proto at f5d7f3cf76555acb5a14ae80eb7db4ad9ff2c553. + +Migration in progress, see +https://gitlab.com/gitlab-org/gitaly/issues/1761. Do not edit files in +this directory, your changes will be ignored and overwritten. diff --git a/proto/README.orig.md b/proto/README.orig.md new file mode 100644 index 000000000..67dae178b --- /dev/null +++ b/proto/README.orig.md @@ -0,0 +1,319 @@ +# Protobuf specifications and client libraries for Gitaly + +Gitaly is part of GitLab. It is a [server +application](https://gitlab.com/gitlab-org/gitaly) that uses its own +gRPC protocol to communicate with its clients. This repository +contains the protocol definition and automatically generated wrapper +code for Go and Ruby. + +The .proto files define the remote procedure calls for interacting +with Gitaly. We keep auto-generated client libraries for Ruby and Go +in their respective subdirectories. The list of RPCs can be +[found here](https://gitlab-org.gitlab.io/gitaly-proto/). + +Run `make` from the root of the repository to regenerate the client +libraries after updating .proto files. + +See +[developers.google.com](https://developers.google.com/protocol-buffers/docs/proto3) +for documentation of the 'proto3' Protocol buffer specification +language. + +## Issues + +We have disabled the issue tracker of the gitaly-proto project. Please use the +[Gitaly issue tracker](https://gitlab.com/gitlab-org/gitaly/issues). + +## gRPC/Protobuf concepts + +The core Protobuf concepts we use are rpc, service and message. We use +these to define the Gitaly **protocol**. + +- **rpc** a function that can be called from the client and that gets + executed on the server. Belongs to a service. Can have one of four + request/response signatures: message/message (example: get metadata for + commit xxx), message/stream (example: get contents of blob xxx), + stream/message (example: create new blob with contents xxx), + stream/stream (example: git SSH session). +- **service** a logical group of RPC's. +- **message** like a JSON object except it has pre-defined types. +- **stream** an unbounded sequence of messages. In the Ruby clients + this looks like an Enumerator. + +gRPC provides an implementation framework based on these Protobuf concepts. + +- A gRPC **server** implements one or more services behind a network + listener. Example: the Gitaly server application. +- The gRPC toolchain automatically generates **client libraries** that + handle serialization and connection management. Example: the Go + client package and Ruby gem in this repository. +- gRPC **clients** use the client libraries to make remote procedure + calls. These clients must decide what network address to reach their + gRPC servers on and handle connection reuse: it is possible to + spread different gRPC services over multiple connections to the same + gRPC server. +- Officially a gRPC connection is called a **channel**. In the Go gRPC + library these channels are called **client connections** because + 'channel' is already a concept in Go itself. In Ruby a gRPC channel + is an instance of GRPC::Core::Channel. We use the word 'connection' + in this document. The underlying transport of gRPC, HTTP/2, allows + multiple remote procedure calls to happen at the same time on a + single connection to a gRPC server. In principle, a multi-threaded + gRPC client needs only one connection to a gRPC server. + +## Design decisions + +1. In Gitaly's case there is one server application + https://gitlab.com/gitlab-org/gitaly which implements all services + in the protocol. +1. In default GitLab installations each Gitaly client interacts with + exactly 1 Gitaly server, on the same host, via a Unix domain socket. + In a larger installation each Gitaly client will interact with many + different Gitaly servers (one per GitLab storage shard) via TCP + connections. +1. Gitaly uses + [grpc.Errorf](https://godoc.org/google.golang.org/grpc#Errorf) to + return meaningful + [errors](https://godoc.org/google.golang.org/grpc/codes#Code) to its + clients. +1. Each RPC `FooBar` has its own `FooBarRequest` and `FooBarResponse` + message types. Try to keep the structure of these messages as flat as + possible. Only add abstractions when they have a practical benefit. +1. We never make backwards incompatible changes to an RPC that is + already implemented on either the client side or server side. + Instead we just create a new RPC call and start a deprecation + procedure (see below) for the old one. +1. It is encouraged to put comments (starting with `//`) in .proto files. + Please put comments on their own lines. This will cause them to be + treated as documentation by the protoc compiler. +1. When choosing an RPC name don't use the service name as context. + Good: `service CommitService { rpc CommitExists }`. Bad: + `service CommitService { rpc Exists }`. + +### RPC naming conventions + +Gitaly-Proto has RPCs that are resource based, for example when querying for a +commit. Another class of RPCs are operations, where the result might be empty +or one of the RPC error codes but the fact that the operation took place is +of importance. + +For all RPCs, start the name with a verb, followed by an entity, and if required +followed by a further specification. For example: +- GetCommit +- RepackRepositoryIncremental +- CreateRepositoryFromBundle + +For resource RPCs the verbs in use are limited to: Get, List, Create, Update, +Delete, or Is. Where both Get and List as verbs denote these operations have no side +effects. These verbs differ in terms of the expected number of results the query +yields. Get queries are limited to one result, and are expected to return one +result to the client. List queries have zero or more results, and generally will +create a gRPC stream for their results. When the `Is` verb is used, this RPC +is expected to return a boolean, or an error. For example: `IsRepositoryEmpty`. + + +When an operation based RPC is defined, the verb should map to the first verb in +the Git command it represents. Example; FetchRemote. + +Note that the current interface defined in this repository does not yet abide +fully to these conventions. Newly defined RPCs should, though, so eventually +gitaly-proto converges to a common standard. + +### Common field names and types + +As a general principle, remember that Git does not enforce encodings on +most data inside repositories, so we can rarely assume data to be a +Protobuf "string" (which implies UTF-8). + +1. `bytes revision`: for fields that accept any of branch names / tag + names / commit ID's. Uses `bytes` to be encoding agnostic. +2. `string commit_id`: for fields that accept a commit ID. +3. `bytes ref`: for fields that accept a refname. +4. `bytes path`: for paths inside Git repositories, i.e., inside Git + `tree` objects. +5. `string relative_path`: for paths on disk on a Gitaly server, + created by "us" (GitLab the application) instead of the user, we + want to use UTF-8, or better, ASCII. + +### Stream patterns + +These are some patterns we already use, or want to use going forward. + +#### Stream response of many small items + +``` +rpc FooBar(FooBarRequest) returns (stream FooBarResponse); + +message FooBarResponse { + message Item { + // ... + } + repeated Item items = 1; +} +``` + +A typical example of an "Item" would be a commit. To avoid the penalty +of network IO for each Item we return, we batch them together. You can +think of this as a kind of buffered IO at the level of the Item +messages. In Go, to ease the bookkeeping you can use +[gitlab.com/gitlab-org/gitaly/internal/helper/chunker](https://godoc.org/gitlab.com/gitlab-org/gitaly/internal/helper/chunker). + +#### Single large item split over multiple messages + +``` +rpc FooBar(FooBarRequest) returns (stream FooBarResponse); + +message FooBarResponse { + message Header { + // ... + } + + oneof payload { + Header header = 1; + bytes data = 2; + } +} +``` + +A typical example of a large item would be the contents of a Git blob. +The header might contain the blob OID and the blob size. Only the first +message in the response stream has `header` set, all others have `data` +but no `header`. + +In the particular case where you're sending back raw binary data from +Go, you can use +[gitlab.com/gitlab-org/gitaly/streamio](https://godoc.org/gitlab.com/gitlab-org/gitaly/streamio) +to turn your gRPC response stream into an `io.Writer`. + +> Note that a number of existing RPC's do not use this pattern exactly; +> they don't use `oneof`. In practice this creates ambiguity (does the +> first message contain non-empty `data`?) and encourages complex +> optimization in the server implementation (trying to squeeze data into +> the first response message). Using `oneof` avoids this ambiguity. + +#### Many large items split over multiple messages + +``` +rpc FooBar(FooBarRequest) returns (stream FooBarResponse); + +message FooBarResponse { + message Header { + // ... + } + + oneof payload { + Header header = 1; + bytes data = 2; + } +} +``` + +This looks the same as the "single large item" case above, except +whenever a new large item begins, we send a new message with a non-empty +`header` field. + +#### Footers + +If the RPC requires it we can also send a footer using `oneof`. But by +default, we prefer headers. + +### RPC Annotations + +In preparation for Gitaly HA, we are now requiring all RPC's to be annotated +with an appropriate designation. All methods must contain one of the following lines: + +- `option (op_type).op = ACCESSOR;` + - Designates an RPC as being read-only (i.e. side effect free) +- `option (op_type).op = MUTATOR;` + - Designates that an RPC modifies the repository + +Failing to designate an RPC correctly will result in a CI error. For example: + +`--gitaly_out: server.proto: Method ServerInfo missing op_type option` + +Additionally, all mutator RPC's require additional annotations to clearly +indicate what is being modified: + +- When an RPC modifies a server-wide resource, the scope should specify `SERVER`. +- When an RPC modifies a specific repository, the scope should specify `REPOSITORY`. + - Additionally, every RPC with `REPOSITORY` scope, should also specify the target repository. + +The target repository represents the location or address of the repository +being modified by the operation. This is needed by Praefect (Gitaly HA) in +order to properly schedule replications to keep repository replicas up to date. + +The target repository annotation specifies where the target repository can be +found in the message. The annotation looks similar to an IP address, but +variable in length (e.g. "1", "1.1", "1.1.1"). Each dot delimited field +represents the field number of how to traverse the protobuf request message to +find the target repository. The target repository **must** be of protobuf +message type `gitaly.Repository`. + +See our examples of [valid](go/internal/linter/testdata/valid.proto) and +[invalid](go/internal/linter/testdata/invalid.proto) proto annotations. + +### Go Package + +If adding new protobuf files, make sure to correctly set the `go_package` option +near the top of the file: + +`option go_package = "gitlab.com/gitlab-org/gitaly-proto/go/gitalypb";` + +This allows other protobuf files to locate and import the Go generated stubs. If +you forget to add a `go_package` option, you may receive an error similar to: + +`blob.proto is missing the go_package option` + +## Contributing + +The CI at https://gitlab.com/gitlab-org/gitaly-proto regenerates the +client libraries to guard against the mistake of updating the .proto +files but not the client libraries. This check uses `git diff` to look +for changes. Some of the code in the Go client libraries is sensitive +to implementation details of the Go standard library (specifically, +the output of gzip). **Use the same Go version as .gitlab-ci.yml (Go +1.11)** when generating new client libraries for a merge request. + +[DCO + License](CONTRIBUTING.md) + +### Build process + +After you change or add a .proto file you need to re-generate the Go +and Ruby libraries before committing your change. + +``` +# Re-generate Go and Ruby libraries +make generate +``` + +## How to deprecate an RPC call + +See [DEPRECATION.md](DEPRECATION.md). + +## Release + +This will tag and release the gitaly-proto library, including +pushing the gem to rubygems.org + +``` +make release version=X.Y.Z +``` + + +## How to manually push the gem + +If the release script fails the gem may not be pushed. This is how you can do that after the fact: + +```shell +# Use a sub-shell to limit scope of 'set -e' +( + set -e + + # Replace X.Y.Z with the version you are pushing + GEM_VERSION=X.Y.Z + + git checkout v$GEM_VERSION + gem build gitaly.gemspec + gem push gitaly-$GEM_VERSION.gem +) +``` diff --git a/proto/RELEASE.md b/proto/RELEASE.md new file mode 100644 index 000000000..db92f239e --- /dev/null +++ b/proto/RELEASE.md @@ -0,0 +1,30 @@ +# Gitaly-proto release process + +## Requirements + +- Ruby +- Bundler +- Go 1.10 + +## 1. Install dependencies + +If you have done a release before this may not be needed. + +``` +_support/install-protoc +``` + +## 2. Release + +This will: + +- do a final consistency check +- create a version bump commit +- create a tag +- build the gem +- ask for confirmation +- push the gem and the tag out to the world + +``` +_support/release 1.2.3 +``` diff --git a/proto/VERSION b/proto/VERSION new file mode 100644 index 000000000..39fc130ef --- /dev/null +++ b/proto/VERSION @@ -0,0 +1 @@ +1.36.0 diff --git a/proto/blob.proto b/proto/blob.proto new file mode 100644 index 000000000..f786a26c5 --- /dev/null +++ b/proto/blob.proto @@ -0,0 +1,118 @@ +syntax = "proto3"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +import "shared.proto"; + +service BlobService { + // GetBlob returns the contents of a blob object referenced by its object + // ID. We use a stream to return a chunked arbitrarily large binary + // response + rpc GetBlob(GetBlobRequest) returns (stream GetBlobResponse) { + option (op_type).op = ACCESSOR; + } + rpc GetBlobs(GetBlobsRequest) returns (stream GetBlobsResponse) { + option (op_type).op = ACCESSOR; + } + rpc GetLFSPointers(GetLFSPointersRequest) returns (stream GetLFSPointersResponse) { + option (op_type).op = ACCESSOR; + } + rpc GetNewLFSPointers(GetNewLFSPointersRequest) returns (stream GetNewLFSPointersResponse) { + option (op_type).op = ACCESSOR; + } + rpc GetAllLFSPointers(GetAllLFSPointersRequest) returns (stream GetAllLFSPointersResponse) { + option (op_type).op = ACCESSOR; + } +} + +message GetBlobRequest { + + Repository repository = 1; + // Object ID (SHA1) of the blob we want to get + string oid = 2; + // Maximum number of bytes we want to receive. Use '-1' to get the full blob no matter how big. + int64 limit = 3; +} + +message GetBlobResponse { + // Blob size; present only in first response message + int64 size = 1; + // Chunk of blob data + bytes data = 2; + // Object ID of the actual blob returned. Empty if no blob was found. + string oid = 3; +} + +message GetBlobsRequest { + + message RevisionPath { + string revision = 1; + bytes path = 2; + } + + Repository repository = 1; + // Revision/Path pairs of the blobs we want to get. + repeated RevisionPath revision_paths = 2; + // Maximum number of bytes we want to receive. Use '-1' to get the full blobs no matter how big. + int64 limit = 3; +} + +message GetBlobsResponse { + // Blob size; present only on the first message per blob + int64 size = 1; + // Chunk of blob data, could span over multiple messages. + bytes data = 2; + // Object ID of the current blob. Only present on the first message per blob. Empty if no blob was found. + string oid = 3; + bool is_submodule = 4; + int32 mode = 5; + string revision = 6; + bytes path = 7; +} + +message LFSPointer { + int64 size = 1; + bytes data = 2; + string oid = 3; +} + +message NewBlobObject { + int64 size = 1; + string oid = 2; + bytes path = 3; +} + +message GetLFSPointersRequest { + + Repository repository = 1; + repeated string blob_ids = 2; +} + +message GetLFSPointersResponse { + repeated LFSPointer lfs_pointers = 1; +} + +message GetNewLFSPointersRequest { + + Repository repository = 1; + bytes revision = 2; + int32 limit = 3; + // Note: When `not_in_all` is true, `not_in_refs` is ignored + bool not_in_all = 4; + repeated bytes not_in_refs = 5; +} + +message GetNewLFSPointersResponse { + repeated LFSPointer lfs_pointers = 1; +} + +message GetAllLFSPointersRequest { + Repository repository = 1; + bytes revision = 2; +} + +message GetAllLFSPointersResponse { + repeated LFSPointer lfs_pointers = 1; +} diff --git a/proto/cleanup.proto b/proto/cleanup.proto new file mode 100644 index 000000000..276a17e02 --- /dev/null +++ b/proto/cleanup.proto @@ -0,0 +1,69 @@ +syntax = "proto3"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +import "shared.proto"; + +service CleanupService { + // Deprecated in favour of ApplyBfgObjectMapStream + rpc ApplyBfgObjectMap(stream ApplyBfgObjectMapRequest) returns (ApplyBfgObjectMapResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + + rpc ApplyBfgObjectMapStream(stream ApplyBfgObjectMapStreamRequest) returns (stream ApplyBfgObjectMapStreamResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + + rpc CloseSession(CloseSessionRequest) returns (CloseSessionResponse) { + option (op_type) = { + op: MUTATOR + scope_level: SERVER, + }; + } +} + +message ApplyBfgObjectMapRequest { + Repository repository = 1; + // A raw object-map file as generated by BFG: https://rtyley.github.io/bfg-repo-cleaner + // Each line in the file has two object SHAs, space-separated - the original + // SHA of the object, and the SHA after BFG has rewritten the object. + bytes object_map = 2; +} + +message ApplyBfgObjectMapResponse {} + +message ApplyBfgObjectMapStreamRequest { + // Only available on the first message + Repository repository = 1; + + // A raw object-map file as generated by BFG: https://rtyley.github.io/bfg-repo-cleaner + // Each line in the file has two object SHAs, space-separated - the original + // SHA of the object, and the SHA after BFG has rewritten the object. + bytes object_map = 2; +} + +message ApplyBfgObjectMapStreamResponse { + // We send back each parsed entry in the request's object map so the client + // can take action + message Entry { + ObjectType type = 1; + string old_oid = 2; + string new_oid = 3; + } + + repeated Entry entries = 1; +} + +message CloseSessionRequest{ + string session_id = 1; +} + +message CloseSessionResponse{} diff --git a/proto/commit.proto b/proto/commit.proto new file mode 100644 index 000000000..d338809fb --- /dev/null +++ b/proto/commit.proto @@ -0,0 +1,386 @@ +syntax = "proto3"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +import "shared.proto"; +import "google/protobuf/timestamp.proto"; + +service CommitService { + rpc CommitIsAncestor(CommitIsAncestorRequest) returns (CommitIsAncestorResponse) { + option (op_type).op = ACCESSOR; + } + rpc TreeEntry(TreeEntryRequest) returns (stream TreeEntryResponse) { + option (op_type).op = ACCESSOR; + } + rpc CommitsBetween(CommitsBetweenRequest) returns (stream CommitsBetweenResponse) { + option (op_type).op = ACCESSOR; + } + rpc CountCommits(CountCommitsRequest) returns (CountCommitsResponse) { + option (op_type).op = ACCESSOR; + } + rpc CountDivergingCommits(CountDivergingCommitsRequest) returns (CountDivergingCommitsResponse) { + option (op_type).op = ACCESSOR; + } + rpc GetTreeEntries(GetTreeEntriesRequest) returns (stream GetTreeEntriesResponse) { + option (op_type).op = ACCESSOR; + } + rpc ListFiles(ListFilesRequest) returns (stream ListFilesResponse) { + option (op_type).op = ACCESSOR; + } + rpc FindCommit(FindCommitRequest) returns (FindCommitResponse) { + option (op_type).op = ACCESSOR; + } + rpc CommitStats(CommitStatsRequest) returns (CommitStatsResponse) { + option (op_type).op = ACCESSOR; + } + // Use a stream to paginate the result set + rpc FindAllCommits(FindAllCommitsRequest) returns (stream FindAllCommitsResponse) { + option (op_type).op = ACCESSOR; + } + rpc FindCommits(FindCommitsRequest) returns (stream FindCommitsResponse) { + option (op_type).op = ACCESSOR; + } + rpc CommitLanguages(CommitLanguagesRequest) returns (CommitLanguagesResponse) { + option (op_type).op = ACCESSOR; + } + rpc RawBlame(RawBlameRequest) returns (stream RawBlameResponse) { + option (op_type).op = ACCESSOR; + } + rpc LastCommitForPath(LastCommitForPathRequest) returns (LastCommitForPathResponse) { + option (op_type).op = ACCESSOR; + } + rpc ListLastCommitsForTree(ListLastCommitsForTreeRequest) returns (stream ListLastCommitsForTreeResponse) { + option (op_type).op = ACCESSOR; + } + rpc CommitsByMessage(CommitsByMessageRequest) returns (stream CommitsByMessageResponse) { + option (op_type).op = ACCESSOR; + } + rpc ListCommitsByOid(ListCommitsByOidRequest) returns (stream ListCommitsByOidResponse) { + option (op_type).op = ACCESSOR; + } + rpc FilterShasWithSignatures(stream FilterShasWithSignaturesRequest) returns (stream FilterShasWithSignaturesResponse) { + option (op_type).op = ACCESSOR; + } + + // ExtractCommitSignature returns a stream because the signed text may be + // arbitrarily large and signature verification is impossible without the + // full text. + rpc ExtractCommitSignature(ExtractCommitSignatureRequest) returns (stream ExtractCommitSignatureResponse) { + option (op_type).op = ACCESSOR; + } + rpc GetCommitSignatures(GetCommitSignaturesRequest) returns (stream GetCommitSignaturesResponse) { + option (op_type).op = ACCESSOR; + } + + rpc GetCommitMessages(GetCommitMessagesRequest) returns (stream GetCommitMessagesResponse) { + option (op_type).op = ACCESSOR; + } +} + +message CommitStatsRequest { + Repository repository = 1; + bytes revision = 2; +} + +message CommitStatsResponse { + // OID is the commit. Empty means not found + string oid = 1; + int32 additions = 2; + int32 deletions = 3; +} + +message CommitIsAncestorRequest { + Repository repository = 1; + string ancestor_id = 2; + string child_id = 3; +} + +message CommitIsAncestorResponse { + bool value = 1; +} + +message TreeEntryRequest { + Repository repository = 1; + // commit ID or refname + bytes revision = 2; + // entry path relative to repository root + bytes path = 3; + int64 limit = 4; +} + +message TreeEntryResponse { + // TODO: Replace this enum with ObjectType in shared.proto + enum ObjectType { + COMMIT = 0; + BLOB = 1; + TREE = 2; + TAG = 3; + } + ObjectType type = 1; + // SHA1 object ID + string oid = 2; + int64 size = 3; + // file mode + int32 mode = 4; + // raw object contents + bytes data = 5; +} + +message CommitsBetweenRequest { + Repository repository = 1; + bytes from = 2; + bytes to = 3; +} + +message CommitsBetweenResponse { + repeated GitCommit commits = 1; +} + +message CountCommitsRequest { + Repository repository = 1; + bytes revision = 2; + google.protobuf.Timestamp after = 3; + google.protobuf.Timestamp before = 4; + bytes path = 5; + int32 max_count = 6; + // all and revision are mutually exclusive + bool all = 7; +} + +message CountCommitsResponse { + int32 count = 1; +} + +message CountDivergingCommitsRequest { + Repository repository = 1; + bytes from = 2; + bytes to = 3; + reserved 4; + reserved 5; + reserved 6; + int32 max_count = 7; +} + +message CountDivergingCommitsResponse { + int32 left_count = 1; + int32 right_count = 2; +} + +message TreeEntry { + // TODO: Replace this enum with ObjectType in shared.proto + enum EntryType { + BLOB = 0; + TREE = 1; + COMMIT = 3; + } + // OID of the object this tree entry points to + string oid = 1; + // OID of the tree attached to commit_oid + string root_oid = 2; + // Path relative to repository root + bytes path = 3; + EntryType type = 4; + // File mode e.g. 0644 + int32 mode = 5; + // The commit object via which this entry was retrieved + string commit_oid = 6; + // Relative path of the first subdir that doesn't have only one directory descendant + bytes flat_path = 7; +} + +message GetTreeEntriesRequest { + Repository repository = 1; + bytes revision = 2; + bytes path = 3; + bool recursive = 4; +} + +message GetTreeEntriesResponse { + repeated TreeEntry entries = 1; +} + +message ListFilesRequest { + Repository repository = 1; + bytes revision = 2; +} + +// A single 'page' of the paginated response +message ListFilesResponse { + // Remember to force encoding utf-8 on the client side + repeated bytes paths = 1; +} + +message FindCommitRequest { + Repository repository = 1; + bytes revision = 2; +} + +message FindCommitResponse { + // commit is nil when the commit was not found + GitCommit commit = 1; +} + +message ListCommitsByOidRequest { + Repository repository = 1; + repeated string oid = 2; +} + +message ListCommitsByOidResponse { + repeated GitCommit commits = 1; +} + +message FindAllCommitsRequest { + Repository repository = 1; + // When nil, return all commits reachable by any branch in the repo + bytes revision = 2; + int32 max_count = 3; + int32 skip = 4; + enum Order { + NONE = 0; + TOPO = 1; + DATE = 2; + } + Order order = 5; +} + +// A single 'page' of the result set +message FindAllCommitsResponse { + repeated GitCommit commits = 1; +} + +message FindCommitsRequest { + Repository repository = 1; + bytes revision = 2; + int32 limit = 3; + int32 offset = 4; + repeated bytes paths = 5; + bool follow = 6; + bool skip_merges = 7; + bool disable_walk = 8; + google.protobuf.Timestamp after = 9; + google.protobuf.Timestamp before = 10; + // all and revision are mutually exclusive + bool all = 11; +} + +// A single 'page' of the result set +message FindCommitsResponse { + repeated GitCommit commits = 1; +} + +message CommitLanguagesRequest { + Repository repository = 1; + bytes revision = 2; +} + +message CommitLanguagesResponse { + message Language { + string name = 1; + float share = 2; + string color = 3; + } + repeated Language languages = 1; +} + +message RawBlameRequest { + Repository repository = 1; + bytes revision = 2; + bytes path = 3; +} + +message RawBlameResponse { + bytes data = 1; +} + +message LastCommitForPathRequest { + Repository repository = 1; + bytes revision = 2; + bytes path = 3; +} + +message LastCommitForPathResponse { + // commit is nil when the commit was not found + GitCommit commit = 1; +} + +message ListLastCommitsForTreeRequest { + Repository repository = 1; + string revision = 2; + bytes path = 3; + + // limit == -1 will get the last commit for all paths + int32 limit = 4; + int32 offset = 5; +} + +message ListLastCommitsForTreeResponse { + message CommitForTree { + reserved 1; + + GitCommit commit = 2; + reserved 3; + bytes path_bytes = 4; + } + repeated CommitForTree commits = 1; +} + +message CommitsByMessageRequest { + Repository repository = 1; + bytes revision = 2; + int32 offset = 3; + int32 limit = 4; + bytes path = 5; + string query = 6; +} + +// One 'page' of the paginated response of CommitsByMessage +message CommitsByMessageResponse { + repeated GitCommit commits = 1; +} + +message FilterShasWithSignaturesRequest { + Repository repository = 1; + repeated bytes shas = 2; +} + +message FilterShasWithSignaturesResponse { + repeated bytes shas = 1; +} + +message ExtractCommitSignatureRequest { + Repository repository = 1; + string commit_id = 2; +} + +// Either of the 'signature' and 'signed_text' fields may be present. It +// is up to the caller to stitch them together. +message ExtractCommitSignatureResponse { + bytes signature = 1; + bytes signed_text = 2; +} + +message GetCommitSignaturesRequest { + Repository repository = 1; + repeated string commit_ids = 2; +} + +message GetCommitSignaturesResponse { + // Only present for a new commit signature data. + string commit_id = 1; + // See ExtractCommitSignatureResponse above for how these fields should be handled. + bytes signature = 2; + bytes signed_text = 3; +} + +message GetCommitMessagesRequest { + Repository repository = 1; + repeated string commit_ids = 2; +} + +message GetCommitMessagesResponse { + // Only present for a new commit message + string commit_id = 1; + bytes message = 2; +} diff --git a/proto/conflicts.proto b/proto/conflicts.proto new file mode 100644 index 000000000..f0c20d02f --- /dev/null +++ b/proto/conflicts.proto @@ -0,0 +1,66 @@ +syntax = "proto3"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +import "shared.proto"; + +service ConflictsService { + rpc ListConflictFiles(ListConflictFilesRequest) returns (stream ListConflictFilesResponse) { + option (op_type).op = ACCESSOR; + } + rpc ResolveConflicts(stream ResolveConflictsRequest) returns (ResolveConflictsResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1.1" + }; + } +} + +message ListConflictFilesRequest { + Repository repository = 1; + string our_commit_oid = 2; + string their_commit_oid = 3; +} + +message ConflictFileHeader { + Repository repository = 1; + string commit_oid = 2; + bytes their_path = 3; + bytes our_path = 4; + int32 our_mode = 5; +} + +message ConflictFile { + oneof conflict_file_payload { + ConflictFileHeader header = 1; + bytes content = 2; + } +} + +message ListConflictFilesResponse { + repeated ConflictFile files = 1; +} + +message ResolveConflictsRequestHeader { + Repository repository = 1; + string our_commit_oid = 2; + Repository target_repository = 3; + string their_commit_oid = 4; + bytes source_branch = 5; + bytes target_branch = 6; + bytes commit_message = 7; + User user = 8; +} + +message ResolveConflictsRequest { + oneof resolve_conflicts_request_payload { + ResolveConflictsRequestHeader header = 1; + bytes files_json = 2; + } +} + +message ResolveConflictsResponse { + string resolution_error = 1; +} diff --git a/proto/diff.proto b/proto/diff.proto new file mode 100644 index 000000000..7548ad88f --- /dev/null +++ b/proto/diff.proto @@ -0,0 +1,145 @@ +syntax = "proto3"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +import "shared.proto"; + +service DiffService { + // Returns stream of CommitDiffResponse with patches chunked over messages + rpc CommitDiff(CommitDiffRequest) returns (stream CommitDiffResponse) { + option (op_type).op = ACCESSOR; + } + // Return a stream so we can divide the response in chunks of deltas + rpc CommitDelta(CommitDeltaRequest) returns (stream CommitDeltaResponse) { + option (op_type).op = ACCESSOR; + } + rpc CommitPatch(CommitPatchRequest) returns (stream CommitPatchResponse) { + option (op_type).op = ACCESSOR; + } + rpc RawDiff(RawDiffRequest) returns (stream RawDiffResponse) { + option (op_type).op = ACCESSOR; + } + rpc RawPatch(RawPatchRequest) returns (stream RawPatchResponse) { + option (op_type).op = ACCESSOR; + } + rpc DiffStats(DiffStatsRequest) returns (stream DiffStatsResponse) { + option (op_type).op = ACCESSOR; + } +} + +message CommitDiffRequest { + Repository repository = 1; + string left_commit_id = 2; + string right_commit_id = 3; + bool ignore_whitespace_change = 4; + repeated bytes paths = 5; + bool collapse_diffs = 6; + bool enforce_limits = 7; + + // These limits are only enforced when enforce_limits == true. + int32 max_files = 8; + int32 max_lines = 9; + int32 max_bytes = 10; + // Limitation of a single diff patch, + // patches surpassing this limit are pruned by default. + // If this is 0 you will get back empty patches. + int32 max_patch_bytes = 14; + + // These limits are only enforced if collapse_diffs == true. + int32 safe_max_files = 11; + int32 safe_max_lines = 12; + int32 safe_max_bytes = 13; +} + +// A CommitDiffResponse corresponds to a single changed file in a commit. +message CommitDiffResponse { + reserved 8; + + bytes from_path = 1; + bytes to_path = 2; + // Blob ID as returned via `git diff --full-index` + string from_id = 3; + string to_id = 4; + int32 old_mode = 5; + int32 new_mode = 6; + bool binary = 7; + bytes raw_patch_data = 9; + bool end_of_patch = 10; + // Indicates the diff file at which we overflow according to the limitations sent, + // in which case only this attribute will be set. + bool overflow_marker = 11; + // Indicates the patch surpassed a "safe" limit and was therefore pruned, but + // the client may still request the full patch on a separate request. + bool collapsed = 12; + // Indicates the patch was pruned since it surpassed a hard limit, and can + // therefore not be expanded. + bool too_large = 13; +} + +message CommitDeltaRequest { + Repository repository = 1; + string left_commit_id = 2; + string right_commit_id = 3; + repeated bytes paths = 4; +} + +message CommitDelta { + bytes from_path = 1; + bytes to_path = 2; + // Blob ID as returned via `git diff --full-index` + string from_id = 3; + string to_id = 4; + int32 old_mode = 5; + int32 new_mode = 6; +} + +message CommitDeltaResponse { + repeated CommitDelta deltas = 1; +} + +message CommitPatchRequest { + Repository repository = 1; + bytes revision = 2; +} + +message CommitPatchResponse { + bytes data = 1; +} + +message RawDiffRequest { + Repository repository = 1; + string left_commit_id = 2; + string right_commit_id = 3; +} + +message RawDiffResponse { + bytes data = 1; +} + +message RawPatchRequest { + Repository repository = 1; + string left_commit_id = 2; + string right_commit_id = 3; +} + +message RawPatchResponse { + bytes data = 1; +} + +message DiffStatsRequest { + Repository repository = 1; + string left_commit_id = 2; + string right_commit_id = 3; +} + +message DiffStats { + bytes path = 1; + int32 additions = 2; + int32 deletions = 3; +} + +message DiffStatsResponse { + repeated DiffStats stats = 1; +} diff --git a/proto/namespace.proto b/proto/namespace.proto new file mode 100644 index 000000000..1a8aa2294 --- /dev/null +++ b/proto/namespace.proto @@ -0,0 +1,60 @@ +syntax = "proto3"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +import "shared.proto"; + +service NamespaceService { + rpc AddNamespace(AddNamespaceRequest) returns (AddNamespaceResponse) { + option (op_type) = { + op: MUTATOR + scope_level: SERVER, + }; + } + rpc RemoveNamespace(RemoveNamespaceRequest) returns (RemoveNamespaceResponse) { + option (op_type) = { + op: MUTATOR + scope_level: SERVER, + }; + } + rpc RenameNamespace(RenameNamespaceRequest) returns (RenameNamespaceResponse) { + option (op_type) = { + op: MUTATOR + scope_level: SERVER, + }; + } + rpc NamespaceExists(NamespaceExistsRequest) returns (NamespaceExistsResponse) { + option (op_type).op = ACCESSOR; + } +} + +message AddNamespaceRequest { + string storage_name = 1; + string name = 2; +} + +message RemoveNamespaceRequest { + string storage_name = 1; + string name = 2; +} + +message RenameNamespaceRequest { + string storage_name = 1; + string from = 2; + string to = 3; +} + +message NamespaceExistsRequest { + string storage_name = 1; + string name = 2; +} + +message NamespaceExistsResponse { + bool exists = 1; +} + +message AddNamespaceResponse {} +message RemoveNamespaceResponse {} +message RenameNamespaceResponse {} diff --git a/proto/objectpool.proto b/proto/objectpool.proto new file mode 100644 index 000000000..c0aedd55b --- /dev/null +++ b/proto/objectpool.proto @@ -0,0 +1,102 @@ +syntax = "proto3"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +import "shared.proto"; + +service ObjectPoolService { + rpc CreateObjectPool(CreateObjectPoolRequest) returns (CreateObjectPoolResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1.1" + }; + } + rpc DeleteObjectPool(DeleteObjectPoolRequest) returns (DeleteObjectPoolResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1.1" + }; + } + + // Repositories are assumed to be stored on the same disk + rpc LinkRepositoryToObjectPool(LinkRepositoryToObjectPoolRequest) returns (LinkRepositoryToObjectPoolResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1.1" + }; + } + rpc UnlinkRepositoryFromObjectPool(UnlinkRepositoryFromObjectPoolRequest) returns (UnlinkRepositoryFromObjectPoolResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + + rpc ReduplicateRepository(ReduplicateRepositoryRequest) returns (ReduplicateRepositoryResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc DisconnectGitAlternates(DisconnectGitAlternatesRequest) returns (DisconnectGitAlternatesResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc FetchIntoObjectPool(FetchIntoObjectPoolRequest) returns (FetchIntoObjectPoolResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } +} + +// Creates an object pool from the repository. The client is responsible for +// joining this pool later with this repository. +message CreateObjectPoolRequest { + ObjectPool object_pool = 1; + Repository origin = 2; +} +message CreateObjectPoolResponse {} + +// Removes the directory from disk, caller is responsible for leaving the object +// pool before calling this RPC +message DeleteObjectPoolRequest { + ObjectPool object_pool = 1; +} +message DeleteObjectPoolResponse {} + +message LinkRepositoryToObjectPoolRequest { + ObjectPool object_pool = 1; + Repository repository = 2; +} +message LinkRepositoryToObjectPoolResponse {} + +// This RPC doesn't require the ObjectPool as it will remove the alternates file +// from the pool participant. The caller is responsible no data loss occurs. +message UnlinkRepositoryFromObjectPoolRequest { + Repository repository = 1; + ObjectPool object_pool = 2; +} +message UnlinkRepositoryFromObjectPoolResponse {} + +message ReduplicateRepositoryRequest { + Repository repository = 1; +} +message ReduplicateRepositoryResponse {} + +message DisconnectGitAlternatesRequest { + Repository repository = 1; +} + +message DisconnectGitAlternatesResponse {} + +message FetchIntoObjectPoolRequest { + Repository origin = 1; + ObjectPool object_pool = 2; + bool repack = 3; +} +message FetchIntoObjectPoolResponse {} diff --git a/proto/operations.proto b/proto/operations.proto new file mode 100644 index 000000000..681b0b075 --- /dev/null +++ b/proto/operations.proto @@ -0,0 +1,432 @@ +syntax = "proto3"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +import "shared.proto"; + +service OperationService { + rpc UserCreateBranch(UserCreateBranchRequest) returns (UserCreateBranchResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc UserUpdateBranch(UserUpdateBranchRequest) returns (UserUpdateBranchResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc UserDeleteBranch(UserDeleteBranchRequest) returns (UserDeleteBranchResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc UserCreateTag(UserCreateTagRequest) returns (UserCreateTagResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc UserDeleteTag(UserDeleteTagRequest) returns (UserDeleteTagResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc UserMergeToRef(UserMergeToRefRequest) returns (UserMergeToRefResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc UserMergeBranch(stream UserMergeBranchRequest) returns (stream UserMergeBranchResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc UserFFBranch(UserFFBranchRequest) returns (UserFFBranchResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc UserCherryPick(UserCherryPickRequest) returns (UserCherryPickResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc UserCommitFiles(stream UserCommitFilesRequest) returns (UserCommitFilesResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1.1" + }; + } + rpc UserRebase(UserRebaseRequest) returns (UserRebaseResponse) { + option deprecated = true; + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc UserRebaseConfirmable(stream UserRebaseConfirmableRequest) returns (stream UserRebaseConfirmableResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1.1" + }; + } + rpc UserRevert(UserRevertRequest) returns (UserRevertResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc UserSquash(UserSquashRequest) returns (UserSquashResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc UserApplyPatch(stream UserApplyPatchRequest) returns (UserApplyPatchResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1.1" + }; + } + rpc UserUpdateSubmodule(UserUpdateSubmoduleRequest) returns (UserUpdateSubmoduleResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } +} + +message UserCreateBranchRequest { + Repository repository = 1; + bytes branch_name = 2; + User user = 3; + bytes start_point = 4; +} + +message UserCreateBranchResponse { + Branch branch = 1; + // Error returned by the pre-receive hook. If no error was thrown, + // it's the empty string ("") + string pre_receive_error = 2; +} + +message UserUpdateBranchRequest { + Repository repository = 1; + bytes branch_name = 2; + User user = 3; + bytes newrev = 4; + bytes oldrev = 5; +} + +message UserUpdateBranchResponse { + string pre_receive_error = 1; +} + +message UserDeleteBranchRequest { + Repository repository = 1; + bytes branch_name = 2; + User user = 3; +} + +message UserDeleteBranchResponse { + string pre_receive_error = 1; +} + +message UserDeleteTagRequest { + Repository repository = 1; + bytes tag_name = 2; + User user = 3; +} + +message UserDeleteTagResponse { + string pre_receive_error = 1; +} + +message UserCreateTagRequest { + Repository repository = 1; + bytes tag_name = 2; + User user = 3; + bytes target_revision = 4; + bytes message = 5; +} + +message UserCreateTagResponse { + Tag tag = 1; + bool exists = 2; + string pre_receive_error = 3; +} + +message UserMergeBranchRequest { + // First message + Repository repository = 1; + User user = 2; + string commit_id = 3; + bytes branch = 4; + bytes message = 5; + + // Second message + // Tell the server to apply the merge to the branch + bool apply = 6; +} + +message UserMergeBranchResponse { + // First message + // The merge commit the branch will be updated to. The caller can still abort the merge. + string commit_id = 1; + + reserved 2; + // Second message + // If set, the merge has been applied to the branch. + OperationBranchUpdate branch_update = 3; + string pre_receive_error = 4; +} + +message UserMergeToRefRequest { + // UserMergeRef creates a merge commit and updates target_ref to point to that + // new commit. The first parent of the merge commit (the main line) is taken + // from first_parent_ref. The second parent is specified by its commit ID in source_sha. + // If target_ref already exists it will be overwritten. + Repository repository = 1; + User user = 2; + string source_sha = 3; + // branch is deprecated in favor of `first_parent_ref`. + bytes branch = 4; + bytes target_ref = 5; + bytes message = 6; + bytes first_parent_ref = 7; +} + +message UserMergeToRefResponse { + string commit_id = 1; + string pre_receive_error = 2; +} + +message OperationBranchUpdate { + // If this string is non-empty the branch has been updated. + string commit_id = 1; + // Used for cache invalidation in GitLab + bool repo_created = 2; + // Used for cache invalidation in GitLab + bool branch_created = 3; +} + +message UserFFBranchRequest { + Repository repository = 1; + User user = 2; + string commit_id = 3; + bytes branch = 4; +} + +message UserFFBranchResponse { + OperationBranchUpdate branch_update = 1; + string pre_receive_error = 2; +} + +message UserCherryPickRequest { + Repository repository = 1; + User user = 2; + GitCommit commit = 3; + bytes branch_name = 4; + bytes message = 5; + bytes start_branch_name = 6; + Repository start_repository = 7; +} + +message UserCherryPickResponse { + OperationBranchUpdate branch_update = 1; + string create_tree_error = 2; + string commit_error = 3; + string pre_receive_error = 4; +} + +message UserRevertRequest { + Repository repository = 1; + User user = 2; + GitCommit commit = 3; + bytes branch_name = 4; + bytes message = 5; + bytes start_branch_name = 6; + Repository start_repository = 7; +} + +message UserRevertResponse { + OperationBranchUpdate branch_update = 1; + string create_tree_error = 2; + string commit_error = 3; + string pre_receive_error = 4; +} + +message UserCommitFilesActionHeader { + enum ActionType { + CREATE = 0; + CREATE_DIR = 1; + UPDATE = 2; + MOVE = 3; + DELETE = 4; + CHMOD = 5; + } + ActionType action = 1; + bytes file_path = 2; + bytes previous_path = 3; + bool base64_content = 4; + bool execute_filemode = 5; + // Move actions that change the file path, but not its content, should set + // infer_content to true instead of populating the content field. Ignored for + // other action types. + bool infer_content = 6; +} + +message UserCommitFilesAction { + oneof user_commit_files_action_payload { + UserCommitFilesActionHeader header = 1; + bytes content = 2; + } +} + +message UserCommitFilesRequestHeader { + Repository repository = 1; + User user = 2; + bytes branch_name = 3; + bytes commit_message = 4; + bytes commit_author_name = 5; + bytes commit_author_email = 6; + bytes start_branch_name = 7; + Repository start_repository = 8; + bool force = 9; + string start_sha = 10; +} + +message UserCommitFilesRequest { + oneof user_commit_files_request_payload { + // For each request stream there should be first a request with a header and + // then n requests with actions + UserCommitFilesRequestHeader header = 1; + UserCommitFilesAction action = 2; + } +} + +message UserCommitFilesResponse { + OperationBranchUpdate branch_update = 1; + string index_error = 2; + string pre_receive_error = 3; +} + +// DEPRECATED: https://gitlab.com/gitlab-org/gitaly/issues/1628 +message UserRebaseRequest { + option deprecated = true; + + Repository repository = 1; + User user = 2; + string rebase_id = 3; + bytes branch = 4; + string branch_sha = 5; + Repository remote_repository = 6; + bytes remote_branch = 7; +} + +// DEPRECATED: https://gitlab.com/gitlab-org/gitaly/issues/1628 +message UserRebaseResponse { + option deprecated = true; + + string rebase_sha = 1; + string pre_receive_error = 2; + string git_error = 3; +} + +message UserRebaseConfirmableRequest { + message Header { + Repository repository = 1; + User user = 2; + string rebase_id = 3; + bytes branch = 4; + string branch_sha = 5; + Repository remote_repository = 6; + bytes remote_branch = 7; + } + + oneof user_rebase_confirmable_request_payload { + // For each request stream there must be first a request with a header + // containing details about the rebase to perform. + Header header = 1; + // A second request must be made to confirm that the rebase should + // be applied to the branch. + bool apply = 2; + } +} + +message UserRebaseConfirmableResponse { + oneof user_rebase_confirmable_response_payload { + // The first response will contain the rebase commit the branch will + // be updated to. The caller can still abort the rebase. + string rebase_sha = 1; + // The second response confirms that the rebase has been applied to + // the branch. + bool rebase_applied = 2; + } + string pre_receive_error = 3; + string git_error = 4; +} + +message UserSquashRequest { + Repository repository = 1; + User user = 2; + string squash_id = 3; + bytes branch = 4; + string start_sha = 5; + string end_sha = 6; + User author = 7; + bytes commit_message = 8; +} + +message UserSquashResponse { + string squash_sha = 1; + // DEPRECATED: https://gitlab.com/gitlab-org/gitaly/proto/merge_requests/161 + reserved 2; + reserved "pre_receive_error"; + string git_error = 3; +} + +message UserApplyPatchRequest { + message Header { + Repository repository = 1; + User user = 2; + bytes target_branch = 3; + } + + oneof user_apply_patch_request_payload { + Header header = 1; + bytes patches = 2; + } +} + +message UserApplyPatchResponse { + OperationBranchUpdate branch_update = 1; +} + +message UserUpdateSubmoduleRequest { + Repository repository = 1; + User user = 2; + string commit_sha = 3; + bytes branch = 4; + bytes submodule = 5; + bytes commit_message = 6; +} + +message UserUpdateSubmoduleResponse { + OperationBranchUpdate branch_update = 1; + string pre_receive_error = 2; + // DEPRECATED: https://gitlab.com/gitlab-org/gitaly/proto/merge_requests/237 + reserved 3; + reserved "create_tree_error"; + string commit_error = 4; +} diff --git a/proto/ref.proto b/proto/ref.proto new file mode 100644 index 000000000..a749d0965 --- /dev/null +++ b/proto/ref.proto @@ -0,0 +1,317 @@ +syntax = "proto3"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +import "blob.proto"; +import "shared.proto"; +import "google/protobuf/timestamp.proto"; + +service RefService { + rpc FindDefaultBranchName(FindDefaultBranchNameRequest) returns (FindDefaultBranchNameResponse) { + option (op_type).op = ACCESSOR; + } + rpc FindAllBranchNames(FindAllBranchNamesRequest) returns (stream FindAllBranchNamesResponse) { + option (op_type).op = ACCESSOR; + + } + rpc FindAllTagNames(FindAllTagNamesRequest) returns (stream FindAllTagNamesResponse) { + option (op_type).op = ACCESSOR; + } + // Find a Ref matching the given constraints. Response may be empty. + rpc FindRefName(FindRefNameRequest) returns (FindRefNameResponse) { + option (op_type).op = ACCESSOR; + } + // Return a stream so we can divide the response in chunks of branches + rpc FindLocalBranches(FindLocalBranchesRequest) returns (stream FindLocalBranchesResponse) { + option (op_type).op = ACCESSOR; + } + rpc FindAllBranches(FindAllBranchesRequest) returns (stream FindAllBranchesResponse) { + option (op_type).op = ACCESSOR; + } + rpc FindAllTags(FindAllTagsRequest) returns (stream FindAllTagsResponse) { + option (op_type).op = ACCESSOR; + } + rpc FindAllRemoteBranches(FindAllRemoteBranchesRequest) returns (stream FindAllRemoteBranchesResponse) { + option (op_type).op = ACCESSOR; + } + rpc RefExists(RefExistsRequest) returns (RefExistsResponse) { + option (op_type).op = ACCESSOR; + } + rpc CreateBranch(CreateBranchRequest) returns (CreateBranchResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc DeleteBranch(DeleteBranchRequest) returns (DeleteBranchResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc FindBranch(FindBranchRequest) returns (FindBranchResponse) { + option (op_type).op = ACCESSOR; + } + rpc DeleteRefs(DeleteRefsRequest) returns (DeleteRefsResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + + rpc ListBranchNamesContainingCommit(ListBranchNamesContainingCommitRequest) returns (stream ListBranchNamesContainingCommitResponse) { + option (op_type).op = ACCESSOR; + } + rpc ListTagNamesContainingCommit(ListTagNamesContainingCommitRequest) returns (stream ListTagNamesContainingCommitResponse) { + option (op_type).op = ACCESSOR; + } + + rpc GetTagMessages(GetTagMessagesRequest) returns (stream GetTagMessagesResponse) { + option (op_type).op = ACCESSOR; + } + + // Returns commits that are only reachable from the ref passed + rpc ListNewCommits(ListNewCommitsRequest) returns (stream ListNewCommitsResponse) { + option (op_type).op = ACCESSOR; + } + + rpc ListNewBlobs(ListNewBlobsRequest) returns (stream ListNewBlobsResponse) { + option (op_type).op = ACCESSOR; + } + rpc PackRefs(PackRefsRequest) returns (PackRefsResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } +} + +message ListNewBlobsRequest { + Repository repository = 1; + string commit_id = 2; + // Limit the number of revs to be returned fro mgit-rev-list + // If the limit is set to zero, all items will be returned + uint32 limit = 3; +} + +message ListNewBlobsResponse { + repeated NewBlobObject new_blob_objects = 1; +} + +message FindDefaultBranchNameRequest { + Repository repository = 1; +} + +message FindDefaultBranchNameResponse { + bytes name = 1; +} + +message FindAllBranchNamesRequest { + Repository repository = 1; +} + +message FindAllBranchNamesResponse { + repeated bytes names = 1; +} + +message FindAllTagNamesRequest { + Repository repository = 1; +} + +message FindAllTagNamesResponse { + repeated bytes names = 1; +} + +message FindRefNameRequest { + Repository repository = 1; + // Require that the resulting ref contains this commit as an ancestor + string commit_id = 2; + // Example prefix: "refs/heads/". Type bytes because that is the type of ref names. + bytes prefix = 3; +} + +message FindRefNameResponse { + // Example name: "refs/heads/master". Cannot assume UTF8, so the type is bytes. + bytes name = 1; +} + +message FindLocalBranchesRequest { + Repository repository = 1; + enum SortBy { + NAME = 0; + UPDATED_ASC = 1; + UPDATED_DESC = 2; + } + SortBy sort_by = 2; +} + +message FindLocalBranchesResponse { + repeated FindLocalBranchResponse branches = 1; +} + +message FindLocalBranchResponse { + bytes name = 1; + string commit_id = 2; + bytes commit_subject = 3; + FindLocalBranchCommitAuthor commit_author = 4; + FindLocalBranchCommitAuthor commit_committer = 5; +} + +message FindLocalBranchCommitAuthor { + bytes name = 1; + bytes email = 2; + google.protobuf.Timestamp date = 3; +} + +message FindAllBranchesRequest { + Repository repository = 1; + // Only return branches that are merged into root ref + bool merged_only = 2; + // If merged_only is true, this is a list of branches from which we + // return those merged into the root ref + repeated bytes merged_branches = 3; +} + +message FindAllBranchesResponse { + message Branch { + bytes name = 1; + GitCommit target = 2; + } + repeated Branch branches = 1; +} + +message FindAllTagsRequest { + Repository repository = 1; +} + +message FindAllTagsResponse { + repeated Tag tags = 1; +} + +message RefExistsRequest { + Repository repository = 1; + // Any ref, e.g. 'refs/heads/master' or 'refs/tags/v1.0.1'. Must start with 'refs/'. + bytes ref = 2; +} + +message RefExistsResponse { + bool value = 1; +} + +message CreateBranchRequest { + Repository repository = 1; + bytes name = 2; + bytes start_point = 3; +} + +message CreateBranchResponse { + enum Status { + OK = 0; + ERR_EXISTS = 1; + ERR_INVALID = 2; + ERR_INVALID_START_POINT = 3; + } + Status status = 1; + Branch branch = 2; +} + +message DeleteBranchRequest { + Repository repository = 1; + bytes name = 2; +} + +// Not clear if we need to do status signaling; we can add fields later. +message DeleteBranchResponse {} + +message FindBranchRequest { + Repository repository = 1; + // Name can be 'master' but also 'refs/heads/master' + bytes name = 2; +} + +message FindBranchResponse { + Branch branch = 1; +} + +message DeleteRefsRequest{ + Repository repository = 1; + // The following two fields are mutually exclusive + repeated bytes except_with_prefix = 2; + repeated bytes refs = 3; +} + +message DeleteRefsResponse { + string git_error = 1; +} + +message ListBranchNamesContainingCommitRequest { + Repository repository = 1; + string commit_id = 2; + + // Limit the number of tag names to be returned + // If the limit is set to zero, all items will be returned + uint32 limit = 3; +} + +message ListBranchNamesContainingCommitResponse { + reserved 1; + repeated bytes branch_names = 2; +} + +message ListTagNamesContainingCommitRequest { + Repository repository = 1; + string commit_id = 2; + + // Limit the number of tag names to be returned + // If the limit is set to zero, all items will be returned + uint32 limit = 3; +} + +message ListTagNamesContainingCommitResponse { + reserved 1; + repeated bytes tag_names = 2; +} + +message GetTagMessagesRequest { + reserved 2; + reserved "tag_names"; + + Repository repository = 1; + repeated string tag_ids = 3; +} + +message GetTagMessagesResponse { + reserved 1; + reserved "tag_name"; + + bytes message = 2; + // Only present for a new tag message + string tag_id = 3; +} + +message ListNewCommitsRequest { + Repository repository = 1; + string commit_id = 2; +} + +message ListNewCommitsResponse { + repeated GitCommit commits = 1; +} + +message FindAllRemoteBranchesRequest { + Repository repository = 1; + string remote_name = 2; +} + +message FindAllRemoteBranchesResponse { + repeated Branch branches = 1; +} + +message PackRefsRequest { + Repository repository = 1; + bool all_refs = 2; +} + +message PackRefsResponse{} diff --git a/proto/remote.proto b/proto/remote.proto new file mode 100644 index 000000000..9b76b149c --- /dev/null +++ b/proto/remote.proto @@ -0,0 +1,118 @@ +syntax = "proto3"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +import "shared.proto"; + +service RemoteService { + rpc AddRemote(AddRemoteRequest) returns (AddRemoteResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc FetchInternalRemote(FetchInternalRemoteRequest) returns (FetchInternalRemoteResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc RemoveRemote(RemoveRemoteRequest) returns (RemoveRemoteResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + + rpc UpdateRemoteMirror(stream UpdateRemoteMirrorRequest) returns (UpdateRemoteMirrorResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc FindRemoteRepository(FindRemoteRepositoryRequest) returns (FindRemoteRepositoryResponse) { + option (op_type).op = ACCESSOR; + } + rpc FindRemoteRootRef(FindRemoteRootRefRequest) returns (FindRemoteRootRefResponse) { + option (op_type).op = ACCESSOR; + } + rpc ListRemotes(ListRemotesRequest) returns (stream ListRemotesResponse) { + option (op_type).op = ACCESSOR; + } +} + +message AddRemoteRequest { + Repository repository = 1; + string name = 2; + string url = 3; + // DEPRECATED: https://gitlab.com/gitlab-org/gitaly/proto/merge_requests/137 + reserved 4; + reserved "mirror_refmap"; + // If any, the remote is configured as a mirror with those mappings + repeated string mirror_refmaps = 5; +} + +message AddRemoteResponse {} + +message RemoveRemoteRequest { + Repository repository = 1; + string name = 2; +} + +message RemoveRemoteResponse { + bool result = 1; +} + +message FetchInternalRemoteRequest { + Repository repository = 1; + Repository remote_repository = 2; +} + +message FetchInternalRemoteResponse { + bool result = 1; +} + +message UpdateRemoteMirrorRequest { + Repository repository = 1; + string ref_name = 2; + repeated bytes only_branches_matching = 3; + string ssh_key = 4; + string known_hosts = 5; +} + +message UpdateRemoteMirrorResponse {} + +message FindRemoteRepositoryRequest { + string remote = 1; +} + +// This migth throw a GRPC Unavailable code, to signal the request failure +// is transient. +message FindRemoteRepositoryResponse { + bool exists = 1; +} + +message FindRemoteRootRefRequest { + Repository repository = 1; + string remote = 2; +} + +message FindRemoteRootRefResponse { + string ref = 1; +} + +message ListRemotesRequest { + Repository repository = 1; +} + +message ListRemotesResponse { + message Remote { + string name = 1; + string fetch_url = 2; + string push_url = 3; + } + + repeated Remote remotes = 1; +} diff --git a/proto/repository-service.proto b/proto/repository-service.proto new file mode 100644 index 000000000..017564338 --- /dev/null +++ b/proto/repository-service.proto @@ -0,0 +1,594 @@ +syntax = "proto3"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +import "shared.proto"; + +service RepositoryService { + rpc RepositoryExists(RepositoryExistsRequest) returns (RepositoryExistsResponse) { + option (op_type).op = ACCESSOR; + } + rpc RepackIncremental(RepackIncrementalRequest) returns (RepackIncrementalResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc RepackFull(RepackFullRequest) returns (RepackFullResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc GarbageCollect(GarbageCollectRequest) returns (GarbageCollectResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc RepositorySize(RepositorySizeRequest) returns (RepositorySizeResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc ApplyGitattributes(ApplyGitattributesRequest) returns (ApplyGitattributesResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc FetchRemote(FetchRemoteRequest) returns (FetchRemoteResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc CreateRepository(CreateRepositoryRequest) returns (CreateRepositoryResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc GetArchive(GetArchiveRequest) returns (stream GetArchiveResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc HasLocalBranches(HasLocalBranchesRequest) returns (HasLocalBranchesResponse) { + option (op_type).op = ACCESSOR; + } + rpc FetchSourceBranch(FetchSourceBranchRequest) returns (FetchSourceBranchResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc Fsck(FsckRequest) returns (FsckResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc WriteRef(WriteRefRequest) returns (WriteRefResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc FindMergeBase(FindMergeBaseRequest) returns (FindMergeBaseResponse) { + option (op_type).op = ACCESSOR; + } + rpc CreateFork(CreateForkRequest) returns (CreateForkResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc IsRebaseInProgress(IsRebaseInProgressRequest) returns (IsRebaseInProgressResponse) { + option (op_type).op = ACCESSOR; + } + rpc IsSquashInProgress(IsSquashInProgressRequest) returns (IsSquashInProgressResponse) { + option (op_type).op = ACCESSOR; + } + + rpc CreateRepositoryFromURL(CreateRepositoryFromURLRequest) returns (CreateRepositoryFromURLResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc CreateBundle(CreateBundleRequest) returns (stream CreateBundleResponse) { + option (op_type) = { + op: MUTATOR + scope_level: SERVER, + }; + } + rpc CreateRepositoryFromBundle(stream CreateRepositoryFromBundleRequest) returns (CreateRepositoryFromBundleResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc WriteConfig(WriteConfigRequest) returns (WriteConfigResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc SetConfig(SetConfigRequest) returns (SetConfigResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc DeleteConfig(DeleteConfigRequest) returns (DeleteConfigResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc FindLicense(FindLicenseRequest) returns (FindLicenseResponse) { + option (op_type).op = ACCESSOR; + } + rpc GetInfoAttributes(GetInfoAttributesRequest) returns (stream GetInfoAttributesResponse) { + option (op_type).op = ACCESSOR; + } + rpc CalculateChecksum(CalculateChecksumRequest) returns (CalculateChecksumResponse) { + option (op_type).op = ACCESSOR; + } + rpc Cleanup(CleanupRequest) returns (CleanupResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc GetSnapshot(GetSnapshotRequest) returns (stream GetSnapshotResponse) { + option (op_type).op = ACCESSOR; + } + rpc CreateRepositoryFromSnapshot(CreateRepositoryFromSnapshotRequest) returns (CreateRepositoryFromSnapshotResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc GetRawChanges(GetRawChangesRequest) returns (stream GetRawChangesResponse) { + option (op_type).op = ACCESSOR; + } + rpc SearchFilesByContent(SearchFilesByContentRequest) returns (stream SearchFilesByContentResponse) { + option (op_type).op = ACCESSOR; + } + rpc SearchFilesByName(SearchFilesByNameRequest) returns (stream SearchFilesByNameResponse) { + option (op_type).op = ACCESSOR; + } + rpc RestoreCustomHooks(stream RestoreCustomHooksRequest) returns (RestoreCustomHooksResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc BackupCustomHooks(BackupCustomHooksRequest) returns (stream BackupCustomHooksResponse) { + option (op_type).op = ACCESSOR; + } + rpc PreFetch(PreFetchRequest) returns (PreFetchResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc FetchHTTPRemote(FetchHTTPRemoteRequest) returns (FetchHTTPRemoteResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc GetObjectDirectorySize(GetObjectDirectorySizeRequest) returns (GetObjectDirectorySizeResponse) { + option (op_type).op = ACCESSOR; + } + rpc CloneFromPool(CloneFromPoolRequest) returns (CloneFromPoolResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc CloneFromPoolInternal(CloneFromPoolInternalRequest) returns (CloneFromPoolInternalResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } +} + +message RepositoryExistsRequest { + Repository repository = 1; +} + +message RepositoryExistsResponse { + bool exists = 1; +} + +message RepackIncrementalRequest { + Repository repository = 1; +} + +message RepackIncrementalResponse {} + +message RepackFullRequest { + Repository repository = 1; + bool create_bitmap = 2; +} + +message RepackFullResponse {} + +message GarbageCollectRequest { + Repository repository = 1; + bool create_bitmap = 2; +} + +message GarbageCollectResponse {} + +message CleanupRequest { + Repository repository = 1; +} + +message CleanupResponse {} + +message RepositorySizeRequest { + Repository repository = 1; +} + +message RepositorySizeResponse { + // Repository size in kilobytes + int64 size = 1; +} + +message ApplyGitattributesRequest { + Repository repository = 1; + bytes revision = 2; +} + +message ApplyGitattributesResponse {} + +message FetchRemoteRequest { + Repository repository = 1; + string remote = 2; + bool force = 3; + bool no_tags = 4; + int32 timeout = 5; + string ssh_key = 6; + string known_hosts = 7; + reserved 8; + bool no_prune = 9; + Remote remote_params = 10; +} + +message FetchRemoteResponse {} + +message CreateRepositoryRequest { + Repository repository = 1; +} + +message CreateRepositoryResponse {} + +message GetArchiveRequest { + enum Format { + ZIP = 0; + TAR = 1; + TAR_GZ = 2; + TAR_BZ2 = 3; + } + + Repository repository = 1; + string commit_id = 2; + string prefix = 3; + Format format = 4; + bytes path = 5; +} + +message GetArchiveResponse { + bytes data = 1; +} + +message HasLocalBranchesRequest { + Repository repository = 1; +} + +message HasLocalBranchesResponse { + bool value = 1; +} + +message FetchSourceBranchRequest { + Repository repository = 1; + Repository source_repository = 2; + bytes source_branch = 3; + bytes target_ref = 4; +} + +message FetchSourceBranchResponse { + bool result = 1; +} + +message FsckRequest { + Repository repository = 1; +} + +message FsckResponse { + bytes error = 1; +} + +message WriteRefRequest { + Repository repository = 1; + bytes ref = 2; + bytes revision = 3; + bytes old_revision = 4; + bool force = 5; + // This used to be a boolean indicating whether or not to shell out or use + // the rugged implementation + reserved 6; +} + +message WriteRefResponse { + // This used to contain an error message. Since we're shelling out + // all exceptions are wrapped in GRPC errors. + reserved 1; +} + +message FindMergeBaseRequest { + Repository repository = 1; + // We use a repeated field because rugged supports finding a base + // for more than 2 revisions, so if we needed that in the future we don't + // need to change the protocol. + repeated bytes revisions = 2; +} + +message FindMergeBaseResponse { + string base = 1; +} + +message CreateForkRequest { + Repository repository = 1; + Repository source_repository = 2; +} + +message CreateForkResponse {} + +message IsRebaseInProgressRequest { + Repository repository = 1; + string rebase_id = 2; +} + +message IsRebaseInProgressResponse { + bool in_progress = 1; +} + +message IsSquashInProgressRequest { + Repository repository = 1; + string squash_id = 2; +} + +message IsSquashInProgressResponse { + bool in_progress = 1; +} + +message CreateRepositoryFromURLRequest { + Repository repository = 1; + string url = 2; +} + +message CreateRepositoryFromURLResponse {} + +message CreateBundleRequest { + Repository repository = 1; +} + +message CreateBundleResponse { + bytes data = 1; +} + +message WriteConfigRequest { + Repository repository = 1; + string full_path = 2; +} + +message WriteConfigResponse { + bytes error = 1; +} + +message SetConfigRequest { + Repository repository = 1; + message Entry { + string key = 1; + oneof value { + string value_str = 2; + int32 value_int32 = 3; + bool value_bool = 4; + } + } + repeated Entry entries = 2; +} + +message SetConfigResponse {} + +message DeleteConfigRequest { + Repository repository = 1; + repeated string keys = 2; +} + +message DeleteConfigResponse {} + +message RestoreCustomHooksRequest { + Repository repository = 1; + bytes data = 2; +} + +message RestoreCustomHooksResponse {} + +message BackupCustomHooksRequest { + Repository repository = 1; +} + +message BackupCustomHooksResponse { + bytes data = 1; +} + +message CreateRepositoryFromBundleRequest { + // Only available on the first message + Repository repository = 1; + bytes data = 2; +} + +message CreateRepositoryFromBundleResponse {} + +message FindLicenseRequest { + Repository repository = 1; +} + +message FindLicenseResponse { + string license_short_name = 1; +} + +message GetInfoAttributesRequest { + Repository repository = 1; +} + +message GetInfoAttributesResponse { + bytes attributes = 1; +} + +message CalculateChecksumRequest { + Repository repository = 1; +} + +message CalculateChecksumResponse { + string checksum = 1; +} + +message GetSnapshotRequest { + Repository repository = 1; +} + +message GetSnapshotResponse { + bytes data = 1; +} + +message CreateRepositoryFromSnapshotRequest { + Repository repository = 1; + string http_url = 2; + string http_auth = 3; +} + +message CreateRepositoryFromSnapshotResponse {} + +message GetRawChangesRequest { + Repository repository = 1; + string from_revision = 2; + string to_revision = 3; +} + +message GetRawChangesResponse { + message RawChange { + enum Operation { + UNKNOWN = 0; + ADDED = 1; + COPIED = 2; + DELETED = 3; + MODIFIED = 4; + RENAMED = 5; + TYPE_CHANGED = 6; + } + + string blob_id = 1; + int64 size= 2; + + // use fields 9 and 10 in place of 3 and 4 (respectively) + string new_path = 3 [deprecated=true]; + string old_path = 4 [deprecated=true]; + + Operation operation= 5; + string raw_operation = 6; + int32 old_mode = 7; + int32 new_mode = 8; + + // the following fields, 9 and 10, will eventually replace 3 and 4 + bytes new_path_bytes = 9; + bytes old_path_bytes = 10; + } + + repeated RawChange raw_changes = 1; +} + +message SearchFilesByNameRequest { + Repository repository = 1; + string query = 2; + bytes ref = 3; +} + +message SearchFilesByNameResponse { + repeated bytes files = 1; +} + +message SearchFilesByContentRequest { + Repository repository = 1; + string query = 2; + bytes ref = 3; + bool chunked_response = 4; +} + +message SearchFilesByContentResponse { + repeated bytes matches = 1; + bytes match_data = 2; + bool end_of_match = 3; +} + +message PreFetchRequest { + Repository source_repository = 1; + Repository target_repository = 2; + ObjectPool object_pool = 3; +} + +message PreFetchResponse {} + +message Remote { + string url = 1; + string name = 2; + string http_authorization_header = 3; + repeated string mirror_refmaps = 4; +} + +message FetchHTTPRemoteRequest { + Repository repository = 1; + Remote remote = 2; + int32 timeout = 3; +} + +message FetchHTTPRemoteResponse {} + +message GetObjectDirectorySizeRequest { + Repository repository = 1; +} + +message GetObjectDirectorySizeResponse { + // Object directory size in kilobytes + int64 size = 1; +} + +message CloneFromPoolRequest { + Repository repository = 1; + ObjectPool pool = 2; + Remote remote = 3; +} + +message CloneFromPoolResponse { +} + +message CloneFromPoolInternalRequest { + Repository repository = 1; + ObjectPool pool = 2; + Repository source_repository = 3; +} + +message CloneFromPoolInternalResponse { +} diff --git a/proto/server.proto b/proto/server.proto new file mode 100644 index 000000000..f56dded92 --- /dev/null +++ b/proto/server.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +import "shared.proto"; + +service ServerService { + rpc ServerInfo(ServerInfoRequest) returns (ServerInfoResponse) { + option (op_type).op = ACCESSOR; + } +} + +message ServerInfoRequest {} + +message ServerInfoResponse { + message StorageStatus { + string storage_name = 1; + bool readable = 2; + bool writeable = 3; + string fs_type = 4; + string filesystem_id = 5; + } + + string server_version = 1; + string git_version = 2; + repeated StorageStatus storage_statuses = 3; +} diff --git a/proto/shared.proto b/proto/shared.proto new file mode 100644 index 000000000..c252349c9 --- /dev/null +++ b/proto/shared.proto @@ -0,0 +1,135 @@ +syntax = "proto3"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +import "google/protobuf/timestamp.proto"; +import "google/protobuf/descriptor.proto"; + +message OperationMsg { + enum Operation { + UNKNOWN = 0; + MUTATOR = 1; + ACCESSOR = 2; + } + + Operation op = 1; + + enum Scope { + REPOSITORY = 0; + SERVER = 1; + } + + // Scope level indicates how a mutating RPC affects Gitaly: + // - REPOSITORY: mutation is scoped to only a single repo + // - SERVER: mutation affects the entire server and potentially all repos + Scope scope_level = 2; + + // If this operation modifies a repository, this field will + // specify the location of the Repository field within the + // request message. The field is specified in an OID style + // formatted string. + // + // For example, if the target repository is at the top level + // of a message at field 1, then the string will be "1" + // + // If the target repository is nested deeper in the message, + // then it will be necessary to specify a nested OID string. + // + // For example, the following OID refers to a target repo field + // nested in a one-of field, both at field one: "1.1" + string target_repository_field = 3; +} + +enum ObjectType { + UNKNOWN = 0; + COMMIT = 1; + BLOB = 2; + TREE = 3; + TAG = 4; +} + +extend google.protobuf.MethodOptions { + // Random high number.. + OperationMsg op_type = 82303; +} + +message Repository { + // DEPRECATED: https://gitlab.com/gitlab-org/gitaly/issues/151 + reserved 1; + reserved "path"; + + string storage_name = 2; + string relative_path = 3; + // Sets the GIT_OBJECT_DIRECTORY envvar on git commands to the value of this field. + // It influences the object storage directory the SHA1 directories are created underneath. + string git_object_directory = 4; + // Sets the GIT_ALTERNATE_OBJECT_DIRECTORIES envvar on git commands to the values of this field. + // It influences the list of Git object directories which can be used to search for Git objects. + repeated string git_alternate_object_directories = 5; + // Used in callbacks to GitLab so that it knows what repository the event is + // associated with. May be left empty on RPC's that do not perform callbacks. + // During project creation, `gl_repository` may not be known. + string gl_repository = 6; + reserved 7; + // The human-readable GitLab project path (e.g. gitlab-org/gitlab-ce). + // When hashed storage is use, this associates a project path with its + // path on disk. The name can change over time (e.g. when a project is + // renamed). This is primarily used for logging/debugging at the + // moment. + string gl_project_path = 8; +} + +// Corresponds to Gitlab::Git::Commit +message GitCommit { + string id = 1; + bytes subject = 2; + bytes body = 3; + CommitAuthor author = 4; + CommitAuthor committer = 5; + repeated string parent_ids = 6; + // If body exceeds a certain threshold, it will be nullified, + // but its size will be set in body_size so we can know if + // a commit had a body in the first place. + int64 body_size = 7; +} + +message CommitAuthor { + bytes name = 1; + bytes email = 2; + google.protobuf.Timestamp date = 3; +} + +message ExitStatus { + int32 value = 1; +} + +// Corresponds to Gitlab::Git::Branch +message Branch { + bytes name = 1; + GitCommit target_commit = 2; +} + +message Tag { + bytes name = 1; + string id = 2; + GitCommit target_commit = 3; + // If message exceeds a certain threshold, it will be nullified, + // but its size will be set in message_size so we can know if + // a tag had a message in the first place. + bytes message = 4; + int64 message_size = 5; + CommitAuthor tagger = 6; +} + +message User { + string gl_id = 1; + bytes name = 2; + bytes email = 3; + string gl_username = 4; +} + +message ObjectPool { + Repository repository = 1; +} diff --git a/proto/smarthttp.proto b/proto/smarthttp.proto new file mode 100644 index 000000000..92311b169 --- /dev/null +++ b/proto/smarthttp.proto @@ -0,0 +1,91 @@ +syntax = "proto3"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +import "shared.proto"; + +service SmartHTTPService { + // The response body for GET /info/refs?service=git-upload-pack + // Will be invoked when the user executes a `git fetch`, meaning the server + // will upload the packs to that user. The user doesn't upload new objects. + rpc InfoRefsUploadPack(InfoRefsRequest) returns (stream InfoRefsResponse) { + option (op_type).op = ACCESSOR; + } + + // The response body for GET /info/refs?service=git-receive-pack + // Will be invoked when the user executes a `git push`, meaning the server + // will receive new objects in the pack from the user. + rpc InfoRefsReceivePack(InfoRefsRequest) returns (stream InfoRefsResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + + // Request and response body for POST /upload-pack + rpc PostUploadPack(stream PostUploadPackRequest) returns (stream PostUploadPackResponse) { + option (op_type).op = ACCESSOR; + } + + // Request and response body for POST /receive-pack + rpc PostReceivePack(stream PostReceivePackRequest) returns (stream PostReceivePackResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } +} + +message InfoRefsRequest { + Repository repository = 1; + // Parameters to use with git -c (key=value pairs) + repeated string git_config_options = 2; + + // Git protocol version + string git_protocol = 3; +} + +message InfoRefsResponse { + bytes data = 1; +} + +message PostUploadPackRequest { + // repository should only be present in the first message of the stream + Repository repository = 1; + // Raw data to be copied to stdin of 'git upload-pack' + bytes data = 2; + // Parameters to use with git -c (key=value pairs) + repeated string git_config_options = 3; + + // Git protocol version + string git_protocol = 4; +} + +message PostUploadPackResponse { + // Raw data from stdout of 'git upload-pack' + bytes data = 1; +} + +message PostReceivePackRequest { + // repository should only be present in the first message of the stream + Repository repository = 1; + // Raw data to be copied to stdin of 'git receive-pack' + bytes data = 2; + // gl_id, gl_repository, and gl_username become env variables, used by the Git {pre,post}-receive + // hooks. They should only be present in the first message of the stream. + string gl_id = 3; + string gl_repository = 4; + string gl_username = 5; + // Git protocol version + string git_protocol = 6; + + // Parameters to use with git -c (key=value pairs) + repeated string git_config_options = 7; +} + +message PostReceivePackResponse { + // Raw data from stdout of 'git receive-pack' + bytes data = 1; +} diff --git a/proto/ssh.proto b/proto/ssh.proto new file mode 100644 index 000000000..ee8d15fdb --- /dev/null +++ b/proto/ssh.proto @@ -0,0 +1,99 @@ +syntax = "proto3"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +import "shared.proto"; + +service SSHService { + // To forward 'git upload-pack' to Gitaly for SSH sessions + rpc SSHUploadPack(stream SSHUploadPackRequest) returns (stream SSHUploadPackResponse) { + option (op_type).op = ACCESSOR; + } + + // To forward 'git receive-pack' to Gitaly for SSH sessions + rpc SSHReceivePack(stream SSHReceivePackRequest) returns (stream SSHReceivePackResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + + // To forward 'git upload-archive' to Gitaly for SSH sessions + rpc SSHUploadArchive(stream SSHUploadArchiveRequest) returns (stream SSHUploadArchiveResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } +} + +message SSHUploadPackRequest { + // 'repository' must be present in the first message. + Repository repository = 1; + // A chunk of raw data to be copied to 'git upload-pack' standard input + bytes stdin = 2; + // Prevent re-use of field id 3 and/or the "git_config_parameters" name + reserved 3; + reserved "git_config_parameters"; + // Parameters to use with git -c (key=value pairs) + repeated string git_config_options = 4; + + // Git protocol version + string git_protocol = 5; +} + +message SSHUploadPackResponse { + // A chunk of raw data from 'git upload-pack' standard output + bytes stdout = 1; + // A chunk of raw data from 'git upload-pack' standard error + bytes stderr = 2; + // This field may be nil. This is intentional: only when the remote + // command has finished can we return its exit status. + ExitStatus exit_status = 3; +} + +message SSHReceivePackRequest { + // 'repository' must be present in the first message. + Repository repository = 1; + // A chunk of raw data to be copied to 'git upload-pack' standard input + bytes stdin = 2; + // Contents of GL_ID, GL_REPOSITORY, and GL_USERNAME environment variables + // for 'git receive-pack' + string gl_id = 3; + string gl_repository = 4; + string gl_username = 5; + + // Git protocol version + string git_protocol = 6; + + // Parameters to use with git -c (key=value pairs) + repeated string git_config_options = 7; +} + +message SSHReceivePackResponse { + // A chunk of raw data from 'git receive-pack' standard output + bytes stdout = 1; + // A chunk of raw data from 'git receive-pack' standard error + bytes stderr = 2; + // This field may be nil. This is intentional: only when the remote + // command has finished can we return its exit status. + ExitStatus exit_status = 3; +} + +message SSHUploadArchiveRequest { + // 'repository' must be present in the first message. + Repository repository = 1; + // A chunk of raw data to be copied to 'git upload-archive' standard input + bytes stdin = 2; +} + +message SSHUploadArchiveResponse { + // A chunk of raw data from 'git upload-archive' standard output + bytes stdout = 1; + // A chunk of raw data from 'git upload-archive' standard error + bytes stderr = 2; + // This value will only be set on the last message + ExitStatus exit_status = 3; +} diff --git a/proto/storage.proto b/proto/storage.proto new file mode 100644 index 000000000..903c8137c --- /dev/null +++ b/proto/storage.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +import "shared.proto"; + +service StorageService { + rpc ListDirectories(ListDirectoriesRequest) returns (stream ListDirectoriesResponse) { + option (op_type) = { + op: MUTATOR + scope_level: SERVER, + }; + } + rpc DeleteAllRepositories(DeleteAllRepositoriesRequest) returns (DeleteAllRepositoriesResponse) { + option (op_type) = { + op: MUTATOR + scope_level: SERVER, + }; + } +} + +message ListDirectoriesRequest { + string storage_name = 1; + uint32 depth = 2; +} + +message ListDirectoriesResponse { + repeated string paths = 1; +} + +message DeleteAllRepositoriesRequest { + string storage_name = 1; +} + +message DeleteAllRepositoriesResponse {} diff --git a/proto/wiki.proto b/proto/wiki.proto new file mode 100644 index 000000000..00f0a9c8f --- /dev/null +++ b/proto/wiki.proto @@ -0,0 +1,204 @@ +syntax = "proto3"; + +import "shared.proto"; + +package gitaly; + +option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"; + +service WikiService { + rpc WikiGetPageVersions(WikiGetPageVersionsRequest) returns (stream WikiGetPageVersionsResponse) { + option (op_type).op = ACCESSOR; + } + rpc WikiWritePage(stream WikiWritePageRequest) returns (WikiWritePageResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc WikiUpdatePage(stream WikiUpdatePageRequest) returns (WikiUpdatePageResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + rpc WikiDeletePage(WikiDeletePageRequest) returns (WikiDeletePageResponse) { + option (op_type) = { + op: MUTATOR + target_repository_field: "1" + }; + } + // WikiFindPage returns a stream because the page's raw_data field may be arbitrarily large. + rpc WikiFindPage(WikiFindPageRequest) returns (stream WikiFindPageResponse) { + option (op_type).op = ACCESSOR; + } + rpc WikiFindFile(WikiFindFileRequest) returns (stream WikiFindFileResponse) { + option (op_type).op = ACCESSOR; + } + rpc WikiGetAllPages(WikiGetAllPagesRequest) returns (stream WikiGetAllPagesResponse) { + option (op_type).op = ACCESSOR; + } + rpc WikiListPages(WikiListPagesRequest) returns (stream WikiListPagesResponse) { + option (op_type).op = ACCESSOR; + } + rpc WikiGetFormattedData(WikiGetFormattedDataRequest) returns (stream WikiGetFormattedDataResponse) { + option (op_type).op = ACCESSOR; + } +} + +message WikiCommitDetails { + bytes name = 1; + bytes email = 2; + bytes message = 3; + int32 user_id = 4; + bytes user_name = 5; +} + +message WikiPageVersion { + GitCommit commit = 1; + string format = 2; +} + +message WikiPage { + // These fields are only present in the first message of a WikiPage stream + WikiPageVersion version = 1; + string format = 2; + bytes title = 3; + string url_path = 4; + bytes path = 5; + bytes name = 6; + bool historical = 7; + + // This field is present in all messages of a WikiPage stream + bytes raw_data = 8; +} + +message WikiGetPageVersionsRequest { + Repository repository = 1; + bytes page_path = 2; + + int32 page = 3; + int32 per_page = 4; +} + +message WikiGetPageVersionsResponse { + repeated WikiPageVersion versions = 1; +} + +// This message is sent in a stream because the 'content' field may be large. +message WikiWritePageRequest { + // These following fields are only present in the first message. + Repository repository = 1; + bytes name = 2; + string format = 3; + WikiCommitDetails commit_details = 4; + // This field is present in all messages. + bytes content = 5; +} + +message WikiWritePageResponse { + bytes duplicate_error = 1; +} + +message WikiUpdatePageRequest { + // There fields are only present in the first message of the stream + Repository repository = 1; + bytes page_path = 2; + bytes title = 3; + string format = 4; + WikiCommitDetails commit_details = 5; + + // This field is present in all messages + bytes content = 6; +} + +message WikiUpdatePageResponse { + bytes error = 1; +} + +message WikiDeletePageRequest { + Repository repository = 1; + bytes page_path = 2; + WikiCommitDetails commit_details = 3; +} + +message WikiDeletePageResponse {} + +message WikiFindPageRequest { + Repository repository = 1; + bytes title = 2; + bytes revision = 3; + bytes directory = 4; +} + +// WikiFindPageResponse is a stream because we need multiple WikiPage +// messages to send the raw_data field. +message WikiFindPageResponse { + WikiPage page = 1; +} + +message WikiFindFileRequest { + Repository repository = 1; + bytes name = 2; + // Optional: revision + bytes revision = 3; +} + +message WikiFindFileResponse { + // If 'name' is empty, the file was not found. + bytes name = 1; + string mime_type = 2; + bytes raw_data = 3; + bytes path = 4; +} + +message WikiGetAllPagesRequest { + Repository repository = 1; + // Passing 0 means no limit is applied + uint32 limit = 2; + bool direction_desc = 3; + + enum SortBy { + TITLE = 0; + CREATED_AT = 1; + } + SortBy sort = 4; +} + +// The WikiGetAllPagesResponse stream is a concatenation of WikiPage streams +message WikiGetAllPagesResponse { + WikiPage page = 1; + // When end_of_page is true it signals a change of page for the next Response message (if any) + bool end_of_page = 2; +} + +message WikiGetFormattedDataRequest { + Repository repository = 1; + bytes title = 2; + bytes revision = 3; + bytes directory = 4; +} + +message WikiGetFormattedDataResponse { + bytes data = 1; +} + +message WikiListPagesRequest { + Repository repository = 1; + // Passing 0 means no limit is applied + uint32 limit = 2; + bool direction_desc = 3; + + enum SortBy { + TITLE = 0; + CREATED_AT = 1; + } + SortBy sort = 4; + + uint32 offset = 5; +} + +// The WikiListPagesResponse stream is a concatenation of WikiPage streams without content +message WikiListPagesResponse { + WikiPage page = 1; +} |