detached 303f037

feat(api): add merge requests, web sessions and signing key

Add MergeRequestService, SessionService, ServerService.GetSigningKey,
RepoSettings (merge modes, failing checks) on Repo and UpdateRepo, the
MergeMode enum and the merge request event kinds. Compatible additions only.
commit
303f037eb5ed30b94157b8eecadcea11ec97509b
author
brice · · 2026-10-10 15:50 UTC
parent
6c7e2b3
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
19 files · +3961 -140
M README.md +6 -4
@@ -2,7 +2,7 @@
22
33 The public API contract of [ermite](https://git.bnema.dev/bnema/ermite), a single-user Git forge: protobuf definitions for its ConnectRPC services and the Go code generated from them.
44
5-Status: early development. The contract defines five services (below); the server that implements them is being built.
5+Status: early development. The contract defines seven services (below); the server that implements them is being built.
66
77 ```go
88 import "bnema.dev/ermite-api/gen/ermite"
@@ -16,17 +16,19 @@ Handlers are mounted under `/-/api/` by the server. Errors are Connect codes.
1616
1717 | Service | RPCs |
1818 |---|---|
19-| `RepoService` | `ListRepos`, `GetRepo`, `CreateRepo`, `UpdateRepo` (field mask), `DeleteRepo` |
19+| `RepoService` | `ListRepos`, `GetRepo`, `CreateRepo`, `UpdateRepo` (field mask, merge settings), `DeleteRepo` |
2020 | `GitService` | `ListRefs`, `GetTree`, `GetBlob` (streamed), `ListCommits`, `GetCommit` (structured diff), `ResolveRevision` |
2121 | `TokenService` | `CreateToken` (secret shown once), `ListTokens`, `RevokeToken` |
22+| `MergeRequestService` | `CreateMergeRequest`, `ListMergeRequests`, `GetMergeRequest` (commits, diff, comments), `AddComment`, `ResolveComment`, `CloseMergeRequest`, `MergeMergeRequest`, `MarkRead` |
23+| `SessionService` | `CreateLoginLink`, `SignOutEverywhere` (owner only) |
2224 | `EventService` | `Watch` (streamed, resumable) |
23-| `ServerService` | `GetVersion` (server version and feature list) |
25+| `ServerService` | `GetVersion` (server version and feature list), `GetSigningKey` (commit signing key) |
2426
2527 Tokens carry scopes only; presets such as `cli` or `agent` are a client concern.
2628
2729 ## Layout
2830
29-- `proto/ermite/`: `.proto` files of the `ermite` package, one per service (`repo`, `git`, `token`, `event`, `server`) plus shared `types`. Checked by `buf lint` and `buf breaking`.
31+- `proto/ermite/`: `.proto` files of the `ermite` package, one per service (`repo`, `git`, `merge_request`, `token`, `session`, `event`, `server`) plus shared `types`. Checked by `buf lint` and `buf breaking`.
3032 - `gen/ermite/`: generated Go messages. `gen/ermite/ermiteconnect/`: generated Connect clients and handlers. Committed, never edited by hand.
3133
3234 ## Generate
@@ -2,7 +2,7 @@
2 2
3 The public API contract of [ermite](https://git.bnema.dev/bnema/ermite), a single-user Git forge: protobuf definitions for its ConnectRPC services and the Go code generated from them.3 The public API contract of [ermite](https://git.bnema.dev/bnema/ermite), a single-user Git forge: protobuf definitions for its ConnectRPC services and the Go code generated from them.
4 4
5-Status: early development. The contract defines five services (below); the server that implements them is being built.5+Status: early development. The contract defines seven services (below); the server that implements them is being built.
6 6
7 ```go7 ```go
8 import "bnema.dev/ermite-api/gen/ermite"8 import "bnema.dev/ermite-api/gen/ermite"
@@ -16,17 +16,19 @@ Handlers are mounted under `/-/api/` by the server. Errors are Connect codes.
16 16
17 | Service | RPCs |17 | Service | RPCs |
18 |---|---|18 |---|---|
19-| `RepoService` | `ListRepos`, `GetRepo`, `CreateRepo`, `UpdateRepo` (field mask), `DeleteRepo` |19+| `RepoService` | `ListRepos`, `GetRepo`, `CreateRepo`, `UpdateRepo` (field mask, merge settings), `DeleteRepo` |
20 | `GitService` | `ListRefs`, `GetTree`, `GetBlob` (streamed), `ListCommits`, `GetCommit` (structured diff), `ResolveRevision` |20 | `GitService` | `ListRefs`, `GetTree`, `GetBlob` (streamed), `ListCommits`, `GetCommit` (structured diff), `ResolveRevision` |
21 | `TokenService` | `CreateToken` (secret shown once), `ListTokens`, `RevokeToken` |21 | `TokenService` | `CreateToken` (secret shown once), `ListTokens`, `RevokeToken` |
22+| `MergeRequestService` | `CreateMergeRequest`, `ListMergeRequests`, `GetMergeRequest` (commits, diff, comments), `AddComment`, `ResolveComment`, `CloseMergeRequest`, `MergeMergeRequest`, `MarkRead` |
23+| `SessionService` | `CreateLoginLink`, `SignOutEverywhere` (owner only) |
22 | `EventService` | `Watch` (streamed, resumable) |24 | `EventService` | `Watch` (streamed, resumable) |
23-| `ServerService` | `GetVersion` (server version and feature list) |25+| `ServerService` | `GetVersion` (server version and feature list), `GetSigningKey` (commit signing key) |
24 26
25 Tokens carry scopes only; presets such as `cli` or `agent` are a client concern.27 Tokens carry scopes only; presets such as `cli` or `agent` are a client concern.
26 28
27 ## Layout29 ## Layout
28 30
29-- `proto/ermite/`: `.proto` files of the `ermite` package, one per service (`repo`, `git`, `token`, `event`, `server`) plus shared `types`. Checked by `buf lint` and `buf breaking`.31+- `proto/ermite/`: `.proto` files of the `ermite` package, one per service (`repo`, `git`, `merge_request`, `token`, `session`, `event`, `server`) plus shared `types`. Checked by `buf lint` and `buf breaking`.
30 - `gen/ermite/`: generated Go messages. `gen/ermite/ermiteconnect/`: generated Connect clients and handlers. Committed, never edited by hand.32 - `gen/ermite/`: generated Go messages. `gen/ermite/ermiteconnect/`: generated Connect clients and handlers. Committed, never edited by hand.
31 33
32 ## Generate34 ## Generate
M gen/ermite/ermiteconnect/event.connect.go +18 -0
@@ -45,6 +45,15 @@ type EventServiceClient interface {
4545 // down. Events are filtered by the caller's access: events of a private
4646 // repository are only sent to callers that may read it.
4747 //
48+ // The events are repo_created, repo_updated, repo_deleted, pushed, gap and,
49+ // for merge requests, merge_request_opened, merge_request_merged,
50+ // merge_request_closed and comment_added. Each one is delivered on the
51+ // "repo:<name>" topic of its repository and on "home", subject to the
52+ // visibility of the repository. There is no event for a change of title or
53+ // description, nor for a resolved comment: after a gap, or to see such
54+ // changes, clients read the merge request again. A merge publishes
55+ // merge_request_merged, then pushed for the target branch.
56+ //
4857 // The server keeps a bounded buffer of recent events. With resume_after set,
4958 // buffered events after that id are replayed first, then live events follow.
5059 // When resume_after is no longer buffered (or unknown), the stream begins
@@ -98,6 +107,15 @@ type EventServiceHandler interface {
98107 // down. Events are filtered by the caller's access: events of a private
99108 // repository are only sent to callers that may read it.
100109 //
110+ // The events are repo_created, repo_updated, repo_deleted, pushed, gap and,
111+ // for merge requests, merge_request_opened, merge_request_merged,
112+ // merge_request_closed and comment_added. Each one is delivered on the
113+ // "repo:<name>" topic of its repository and on "home", subject to the
114+ // visibility of the repository. There is no event for a change of title or
115+ // description, nor for a resolved comment: after a gap, or to see such
116+ // changes, clients read the merge request again. A merge publishes
117+ // merge_request_merged, then pushed for the target branch.
118+ //
101119 // The server keeps a bounded buffer of recent events. With resume_after set,
102120 // buffered events after that id are replayed first, then live events follow.
103121 // When resume_after is no longer buffered (or unknown), the stream begins
@@ -45,6 +45,15 @@ type EventServiceClient interface {
45 // down. Events are filtered by the caller's access: events of a private45 // down. Events are filtered by the caller's access: events of a private
46 // repository are only sent to callers that may read it.46 // repository are only sent to callers that may read it.
47 //47 //
48+ // The events are repo_created, repo_updated, repo_deleted, pushed, gap and,
49+ // for merge requests, merge_request_opened, merge_request_merged,
50+ // merge_request_closed and comment_added. Each one is delivered on the
51+ // "repo:<name>" topic of its repository and on "home", subject to the
52+ // visibility of the repository. There is no event for a change of title or
53+ // description, nor for a resolved comment: after a gap, or to see such
54+ // changes, clients read the merge request again. A merge publishes
55+ // merge_request_merged, then pushed for the target branch.
56+ //
48 // The server keeps a bounded buffer of recent events. With resume_after set,57 // The server keeps a bounded buffer of recent events. With resume_after set,
49 // buffered events after that id are replayed first, then live events follow.58 // buffered events after that id are replayed first, then live events follow.
50 // When resume_after is no longer buffered (or unknown), the stream begins59 // When resume_after is no longer buffered (or unknown), the stream begins
@@ -98,6 +107,15 @@ type EventServiceHandler interface {
98 // down. Events are filtered by the caller's access: events of a private107 // down. Events are filtered by the caller's access: events of a private
99 // repository are only sent to callers that may read it.108 // repository are only sent to callers that may read it.
100 //109 //
110+ // The events are repo_created, repo_updated, repo_deleted, pushed, gap and,
111+ // for merge requests, merge_request_opened, merge_request_merged,
112+ // merge_request_closed and comment_added. Each one is delivered on the
113+ // "repo:<name>" topic of its repository and on "home", subject to the
114+ // visibility of the repository. There is no event for a change of title or
115+ // description, nor for a resolved comment: after a gap, or to see such
116+ // changes, clients read the merge request again. A merge publishes
117+ // merge_request_merged, then pushed for the target branch.
118+ //
101 // The server keeps a bounded buffer of recent events. With resume_after set,119 // The server keeps a bounded buffer of recent events. With resume_after set,
102 // buffered events after that id are replayed first, then live events follow.120 // buffered events after that id are replayed first, then live events follow.
103 // When resume_after is no longer buffered (or unknown), the stream begins121 // When resume_after is no longer buffered (or unknown), the stream begins
A gen/ermite/ermiteconnect/merge_request.connect.go +396 -0
@@ -0,0 +1,396 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/merge_request.proto
6+
7+package ermiteconnect
8+
9+import (
10+ ermite "bnema.dev/ermite-api/gen/ermite"
11+ connect "connectrpc.com/connect"
12+ context "context"
13+ errors "errors"
14+ http "net/http"
15+ strings "strings"
16+)
17+
18+// This is a compile-time assertion to ensure that this generated file and the connect package are
19+// compatible. If you get a compiler error that this constant is not defined, this code was
20+// generated with a version of connect newer than the one compiled into your binary. You can fix the
21+// problem by either regenerating this code with an older version of connect or updating the connect
22+// version compiled into your binary.
23+const _ = connect.IsAtLeastVersion1_13_0
24+
25+const (
26+ // MergeRequestServiceName is the fully-qualified name of the MergeRequestService service.
27+ MergeRequestServiceName = "ermite.MergeRequestService"
28+)
29+
30+// These constants are the fully-qualified names of the RPCs defined in this package. They're
31+// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
32+//
33+// Note that these are different from the fully-qualified method names used by
34+// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
35+// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
36+// period.
37+const (
38+ // MergeRequestServiceCreateMergeRequestProcedure is the fully-qualified name of the
39+ // MergeRequestService's CreateMergeRequest RPC.
40+ MergeRequestServiceCreateMergeRequestProcedure = "/ermite.MergeRequestService/CreateMergeRequest"
41+ // MergeRequestServiceListMergeRequestsProcedure is the fully-qualified name of the
42+ // MergeRequestService's ListMergeRequests RPC.
43+ MergeRequestServiceListMergeRequestsProcedure = "/ermite.MergeRequestService/ListMergeRequests"
44+ // MergeRequestServiceGetMergeRequestProcedure is the fully-qualified name of the
45+ // MergeRequestService's GetMergeRequest RPC.
46+ MergeRequestServiceGetMergeRequestProcedure = "/ermite.MergeRequestService/GetMergeRequest"
47+ // MergeRequestServiceAddCommentProcedure is the fully-qualified name of the MergeRequestService's
48+ // AddComment RPC.
49+ MergeRequestServiceAddCommentProcedure = "/ermite.MergeRequestService/AddComment"
50+ // MergeRequestServiceResolveCommentProcedure is the fully-qualified name of the
51+ // MergeRequestService's ResolveComment RPC.
52+ MergeRequestServiceResolveCommentProcedure = "/ermite.MergeRequestService/ResolveComment"
53+ // MergeRequestServiceCloseMergeRequestProcedure is the fully-qualified name of the
54+ // MergeRequestService's CloseMergeRequest RPC.
55+ MergeRequestServiceCloseMergeRequestProcedure = "/ermite.MergeRequestService/CloseMergeRequest"
56+ // MergeRequestServiceMergeMergeRequestProcedure is the fully-qualified name of the
57+ // MergeRequestService's MergeMergeRequest RPC.
58+ MergeRequestServiceMergeMergeRequestProcedure = "/ermite.MergeRequestService/MergeMergeRequest"
59+ // MergeRequestServiceMarkReadProcedure is the fully-qualified name of the MergeRequestService's
60+ // MarkRead RPC.
61+ MergeRequestServiceMarkReadProcedure = "/ermite.MergeRequestService/MarkRead"
62+)
63+
64+// MergeRequestServiceClient is a client for the ermite.MergeRequestService service.
65+type MergeRequestServiceClient interface {
66+ // CreateMergeRequest opens a merge request. Needs the mr:write scope. Fails
67+ // with invalid_argument if a text rule is broken, if a branch does not exist
68+ // or if source and target are the same branch, and with already_exists if an
69+ // open merge request already has the same source and target.
70+ CreateMergeRequest(context.Context, *connect.Request[ermite.CreateMergeRequestRequest]) (*connect.Response[ermite.CreateMergeRequestResponse], error)
71+ // ListMergeRequests lists the merge requests of a repository, highest
72+ // number (newest) first, optionally restricted to one state. The listed
73+ // merge requests carry no diff or comments; head_id is set. Fails with
74+ // not_found if the repository does not exist or is not readable.
75+ ListMergeRequests(context.Context, *connect.Request[ermite.ListMergeRequestsRequest]) (*connect.Response[ermite.ListMergeRequestsResponse], error)
76+ // GetMergeRequest returns one merge request with its commits, its diff and
77+ // its comments. The diff compares the target with the head (what the merge
78+ // would bring: "target...head"), or, with since_last_read, the head the
79+ // caller last marked read with the current head. Fails with not_found if
80+ // the repository or the number is unknown.
81+ GetMergeRequest(context.Context, *connect.Request[ermite.GetMergeRequestRequest]) (*connect.Response[ermite.GetMergeRequestResponse], error)
82+ // AddComment adds a general comment or a comment on a line of the diff.
83+ // Needs the mr:write scope. Fails with invalid_argument if the text breaks
84+ // a rule or if path, line and side do not designate a line of the current
85+ // diff of the merge request, and with failed_precondition if the merge
86+ // request is not open.
87+ AddComment(context.Context, *connect.Request[ermite.AddCommentRequest]) (*connect.Response[ermite.AddCommentResponse], error)
88+ // ResolveComment marks a comment resolved or unresolved. Needs the mr:write
89+ // scope. Fails with not_found if the comment is unknown and with
90+ // failed_precondition if the merge request is not open. Setting the state
91+ // the comment already has succeeds and changes nothing.
92+ ResolveComment(context.Context, *connect.Request[ermite.ResolveCommentRequest]) (*connect.Response[ermite.ResolveCommentResponse], error)
93+ // CloseMergeRequest closes an open merge request without merging it. Needs
94+ // the mr:write scope. Fails with failed_precondition if the merge request
95+ // is not open. A closed merge request cannot be reopened: open a new one.
96+ CloseMergeRequest(context.Context, *connect.Request[ermite.CloseMergeRequestRequest]) (*connect.Response[ermite.CloseMergeRequestResponse], error)
97+ // MergeMergeRequest merges an open merge request. Needs the mr:merge scope.
98+ // The caller passes the head it reviewed: the server merges only if the
99+ // source branch still points to it, so a push between review and merge is
100+ // never merged unseen. Fails with invalid_argument if head_id is empty or
101+ // not a commit id, and with failed_precondition if the merge request is not
102+ // open, if the source branch no longer points to head_id ("branch moved,
103+ // read again"), if the target is not an ancestor of the head (rebase and
104+ // push again), if the mode is not allowed by RepoSettings, or if a check
105+ // fails and RepoSettings.failing_checks_block is set. Merges of one
106+ // repository run one at a time. Repeating a call that succeeded fails with
107+ // failed_precondition (not open). On success the server publishes
108+ // MergeRequestMerged then Pushed for the target branch.
109+ MergeMergeRequest(context.Context, *connect.Request[ermite.MergeMergeRequestRequest]) (*connect.Response[ermite.MergeMergeRequestResponse], error)
110+ // MarkRead records the head the caller has reviewed, for the since_last_read
111+ // option of GetMergeRequest. There is one read position per merge request,
112+ // the reviewer's. Needs the mr:write scope. Marking a commit that does not
113+ // exist in the repository is invalid_argument.
114+ MarkRead(context.Context, *connect.Request[ermite.MarkReadRequest]) (*connect.Response[ermite.MarkReadResponse], error)
115+}
116+
117+// NewMergeRequestServiceClient constructs a client for the ermite.MergeRequestService service. By
118+// default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses,
119+// and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the
120+// connect.WithGRPC() or connect.WithGRPCWeb() options.
121+//
122+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
123+// http://api.acme.com or https://acme.com/grpc).
124+func NewMergeRequestServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) MergeRequestServiceClient {
125+ baseURL = strings.TrimRight(baseURL, "/")
126+ mergeRequestServiceMethods := ermite.File_ermite_merge_request_proto.Services().ByName("MergeRequestService").Methods()
127+ return &mergeRequestServiceClient{
128+ createMergeRequest: connect.NewClient[ermite.CreateMergeRequestRequest, ermite.CreateMergeRequestResponse](
129+ httpClient,
130+ baseURL+MergeRequestServiceCreateMergeRequestProcedure,
131+ connect.WithSchema(mergeRequestServiceMethods.ByName("CreateMergeRequest")),
132+ connect.WithClientOptions(opts...),
133+ ),
134+ listMergeRequests: connect.NewClient[ermite.ListMergeRequestsRequest, ermite.ListMergeRequestsResponse](
135+ httpClient,
136+ baseURL+MergeRequestServiceListMergeRequestsProcedure,
137+ connect.WithSchema(mergeRequestServiceMethods.ByName("ListMergeRequests")),
138+ connect.WithClientOptions(opts...),
139+ ),
140+ getMergeRequest: connect.NewClient[ermite.GetMergeRequestRequest, ermite.GetMergeRequestResponse](
141+ httpClient,
142+ baseURL+MergeRequestServiceGetMergeRequestProcedure,
143+ connect.WithSchema(mergeRequestServiceMethods.ByName("GetMergeRequest")),
144+ connect.WithClientOptions(opts...),
145+ ),
146+ addComment: connect.NewClient[ermite.AddCommentRequest, ermite.AddCommentResponse](
147+ httpClient,
148+ baseURL+MergeRequestServiceAddCommentProcedure,
149+ connect.WithSchema(mergeRequestServiceMethods.ByName("AddComment")),
150+ connect.WithClientOptions(opts...),
151+ ),
152+ resolveComment: connect.NewClient[ermite.ResolveCommentRequest, ermite.ResolveCommentResponse](
153+ httpClient,
154+ baseURL+MergeRequestServiceResolveCommentProcedure,
155+ connect.WithSchema(mergeRequestServiceMethods.ByName("ResolveComment")),
156+ connect.WithClientOptions(opts...),
157+ ),
158+ closeMergeRequest: connect.NewClient[ermite.CloseMergeRequestRequest, ermite.CloseMergeRequestResponse](
159+ httpClient,
160+ baseURL+MergeRequestServiceCloseMergeRequestProcedure,
161+ connect.WithSchema(mergeRequestServiceMethods.ByName("CloseMergeRequest")),
162+ connect.WithClientOptions(opts...),
163+ ),
164+ mergeMergeRequest: connect.NewClient[ermite.MergeMergeRequestRequest, ermite.MergeMergeRequestResponse](
165+ httpClient,
166+ baseURL+MergeRequestServiceMergeMergeRequestProcedure,
167+ connect.WithSchema(mergeRequestServiceMethods.ByName("MergeMergeRequest")),
168+ connect.WithClientOptions(opts...),
169+ ),
170+ markRead: connect.NewClient[ermite.MarkReadRequest, ermite.MarkReadResponse](
171+ httpClient,
172+ baseURL+MergeRequestServiceMarkReadProcedure,
173+ connect.WithSchema(mergeRequestServiceMethods.ByName("MarkRead")),
174+ connect.WithClientOptions(opts...),
175+ ),
176+ }
177+}
178+
179+// mergeRequestServiceClient implements MergeRequestServiceClient.
180+type mergeRequestServiceClient struct {
181+ createMergeRequest *connect.Client[ermite.CreateMergeRequestRequest, ermite.CreateMergeRequestResponse]
182+ listMergeRequests *connect.Client[ermite.ListMergeRequestsRequest, ermite.ListMergeRequestsResponse]
183+ getMergeRequest *connect.Client[ermite.GetMergeRequestRequest, ermite.GetMergeRequestResponse]
184+ addComment *connect.Client[ermite.AddCommentRequest, ermite.AddCommentResponse]
185+ resolveComment *connect.Client[ermite.ResolveCommentRequest, ermite.ResolveCommentResponse]
186+ closeMergeRequest *connect.Client[ermite.CloseMergeRequestRequest, ermite.CloseMergeRequestResponse]
187+ mergeMergeRequest *connect.Client[ermite.MergeMergeRequestRequest, ermite.MergeMergeRequestResponse]
188+ markRead *connect.Client[ermite.MarkReadRequest, ermite.MarkReadResponse]
189+}
190+
191+// CreateMergeRequest calls ermite.MergeRequestService.CreateMergeRequest.
192+func (c *mergeRequestServiceClient) CreateMergeRequest(ctx context.Context, req *connect.Request[ermite.CreateMergeRequestRequest]) (*connect.Response[ermite.CreateMergeRequestResponse], error) {
193+ return c.createMergeRequest.CallUnary(ctx, req)
194+}
195+
196+// ListMergeRequests calls ermite.MergeRequestService.ListMergeRequests.
197+func (c *mergeRequestServiceClient) ListMergeRequests(ctx context.Context, req *connect.Request[ermite.ListMergeRequestsRequest]) (*connect.Response[ermite.ListMergeRequestsResponse], error) {
198+ return c.listMergeRequests.CallUnary(ctx, req)
199+}
200+
201+// GetMergeRequest calls ermite.MergeRequestService.GetMergeRequest.
202+func (c *mergeRequestServiceClient) GetMergeRequest(ctx context.Context, req *connect.Request[ermite.GetMergeRequestRequest]) (*connect.Response[ermite.GetMergeRequestResponse], error) {
203+ return c.getMergeRequest.CallUnary(ctx, req)
204+}
205+
206+// AddComment calls ermite.MergeRequestService.AddComment.
207+func (c *mergeRequestServiceClient) AddComment(ctx context.Context, req *connect.Request[ermite.AddCommentRequest]) (*connect.Response[ermite.AddCommentResponse], error) {
208+ return c.addComment.CallUnary(ctx, req)
209+}
210+
211+// ResolveComment calls ermite.MergeRequestService.ResolveComment.
212+func (c *mergeRequestServiceClient) ResolveComment(ctx context.Context, req *connect.Request[ermite.ResolveCommentRequest]) (*connect.Response[ermite.ResolveCommentResponse], error) {
213+ return c.resolveComment.CallUnary(ctx, req)
214+}
215+
216+// CloseMergeRequest calls ermite.MergeRequestService.CloseMergeRequest.
217+func (c *mergeRequestServiceClient) CloseMergeRequest(ctx context.Context, req *connect.Request[ermite.CloseMergeRequestRequest]) (*connect.Response[ermite.CloseMergeRequestResponse], error) {
218+ return c.closeMergeRequest.CallUnary(ctx, req)
219+}
220+
221+// MergeMergeRequest calls ermite.MergeRequestService.MergeMergeRequest.
222+func (c *mergeRequestServiceClient) MergeMergeRequest(ctx context.Context, req *connect.Request[ermite.MergeMergeRequestRequest]) (*connect.Response[ermite.MergeMergeRequestResponse], error) {
223+ return c.mergeMergeRequest.CallUnary(ctx, req)
224+}
225+
226+// MarkRead calls ermite.MergeRequestService.MarkRead.
227+func (c *mergeRequestServiceClient) MarkRead(ctx context.Context, req *connect.Request[ermite.MarkReadRequest]) (*connect.Response[ermite.MarkReadResponse], error) {
228+ return c.markRead.CallUnary(ctx, req)
229+}
230+
231+// MergeRequestServiceHandler is an implementation of the ermite.MergeRequestService service.
232+type MergeRequestServiceHandler interface {
233+ // CreateMergeRequest opens a merge request. Needs the mr:write scope. Fails
234+ // with invalid_argument if a text rule is broken, if a branch does not exist
235+ // or if source and target are the same branch, and with already_exists if an
236+ // open merge request already has the same source and target.
237+ CreateMergeRequest(context.Context, *connect.Request[ermite.CreateMergeRequestRequest]) (*connect.Response[ermite.CreateMergeRequestResponse], error)
238+ // ListMergeRequests lists the merge requests of a repository, highest
239+ // number (newest) first, optionally restricted to one state. The listed
240+ // merge requests carry no diff or comments; head_id is set. Fails with
241+ // not_found if the repository does not exist or is not readable.
242+ ListMergeRequests(context.Context, *connect.Request[ermite.ListMergeRequestsRequest]) (*connect.Response[ermite.ListMergeRequestsResponse], error)
243+ // GetMergeRequest returns one merge request with its commits, its diff and
244+ // its comments. The diff compares the target with the head (what the merge
245+ // would bring: "target...head"), or, with since_last_read, the head the
246+ // caller last marked read with the current head. Fails with not_found if
247+ // the repository or the number is unknown.
248+ GetMergeRequest(context.Context, *connect.Request[ermite.GetMergeRequestRequest]) (*connect.Response[ermite.GetMergeRequestResponse], error)
249+ // AddComment adds a general comment or a comment on a line of the diff.
250+ // Needs the mr:write scope. Fails with invalid_argument if the text breaks
251+ // a rule or if path, line and side do not designate a line of the current
252+ // diff of the merge request, and with failed_precondition if the merge
253+ // request is not open.
254+ AddComment(context.Context, *connect.Request[ermite.AddCommentRequest]) (*connect.Response[ermite.AddCommentResponse], error)
255+ // ResolveComment marks a comment resolved or unresolved. Needs the mr:write
256+ // scope. Fails with not_found if the comment is unknown and with
257+ // failed_precondition if the merge request is not open. Setting the state
258+ // the comment already has succeeds and changes nothing.
259+ ResolveComment(context.Context, *connect.Request[ermite.ResolveCommentRequest]) (*connect.Response[ermite.ResolveCommentResponse], error)
260+ // CloseMergeRequest closes an open merge request without merging it. Needs
261+ // the mr:write scope. Fails with failed_precondition if the merge request
262+ // is not open. A closed merge request cannot be reopened: open a new one.
263+ CloseMergeRequest(context.Context, *connect.Request[ermite.CloseMergeRequestRequest]) (*connect.Response[ermite.CloseMergeRequestResponse], error)
264+ // MergeMergeRequest merges an open merge request. Needs the mr:merge scope.
265+ // The caller passes the head it reviewed: the server merges only if the
266+ // source branch still points to it, so a push between review and merge is
267+ // never merged unseen. Fails with invalid_argument if head_id is empty or
268+ // not a commit id, and with failed_precondition if the merge request is not
269+ // open, if the source branch no longer points to head_id ("branch moved,
270+ // read again"), if the target is not an ancestor of the head (rebase and
271+ // push again), if the mode is not allowed by RepoSettings, or if a check
272+ // fails and RepoSettings.failing_checks_block is set. Merges of one
273+ // repository run one at a time. Repeating a call that succeeded fails with
274+ // failed_precondition (not open). On success the server publishes
275+ // MergeRequestMerged then Pushed for the target branch.
276+ MergeMergeRequest(context.Context, *connect.Request[ermite.MergeMergeRequestRequest]) (*connect.Response[ermite.MergeMergeRequestResponse], error)
277+ // MarkRead records the head the caller has reviewed, for the since_last_read
278+ // option of GetMergeRequest. There is one read position per merge request,
279+ // the reviewer's. Needs the mr:write scope. Marking a commit that does not
280+ // exist in the repository is invalid_argument.
281+ MarkRead(context.Context, *connect.Request[ermite.MarkReadRequest]) (*connect.Response[ermite.MarkReadResponse], error)
282+}
283+
284+// NewMergeRequestServiceHandler builds an HTTP handler from the service implementation. It returns
285+// the path on which to mount the handler and the handler itself.
286+//
287+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
288+// and JSON codecs. They also support gzip compression.
289+func NewMergeRequestServiceHandler(svc MergeRequestServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
290+ mergeRequestServiceMethods := ermite.File_ermite_merge_request_proto.Services().ByName("MergeRequestService").Methods()
291+ mergeRequestServiceCreateMergeRequestHandler := connect.NewUnaryHandler(
292+ MergeRequestServiceCreateMergeRequestProcedure,
293+ svc.CreateMergeRequest,
294+ connect.WithSchema(mergeRequestServiceMethods.ByName("CreateMergeRequest")),
295+ connect.WithHandlerOptions(opts...),
296+ )
297+ mergeRequestServiceListMergeRequestsHandler := connect.NewUnaryHandler(
298+ MergeRequestServiceListMergeRequestsProcedure,
299+ svc.ListMergeRequests,
300+ connect.WithSchema(mergeRequestServiceMethods.ByName("ListMergeRequests")),
301+ connect.WithHandlerOptions(opts...),
302+ )
303+ mergeRequestServiceGetMergeRequestHandler := connect.NewUnaryHandler(
304+ MergeRequestServiceGetMergeRequestProcedure,
305+ svc.GetMergeRequest,
306+ connect.WithSchema(mergeRequestServiceMethods.ByName("GetMergeRequest")),
307+ connect.WithHandlerOptions(opts...),
308+ )
309+ mergeRequestServiceAddCommentHandler := connect.NewUnaryHandler(
310+ MergeRequestServiceAddCommentProcedure,
311+ svc.AddComment,
312+ connect.WithSchema(mergeRequestServiceMethods.ByName("AddComment")),
313+ connect.WithHandlerOptions(opts...),
314+ )
315+ mergeRequestServiceResolveCommentHandler := connect.NewUnaryHandler(
316+ MergeRequestServiceResolveCommentProcedure,
317+ svc.ResolveComment,
318+ connect.WithSchema(mergeRequestServiceMethods.ByName("ResolveComment")),
319+ connect.WithHandlerOptions(opts...),
320+ )
321+ mergeRequestServiceCloseMergeRequestHandler := connect.NewUnaryHandler(
322+ MergeRequestServiceCloseMergeRequestProcedure,
323+ svc.CloseMergeRequest,
324+ connect.WithSchema(mergeRequestServiceMethods.ByName("CloseMergeRequest")),
325+ connect.WithHandlerOptions(opts...),
326+ )
327+ mergeRequestServiceMergeMergeRequestHandler := connect.NewUnaryHandler(
328+ MergeRequestServiceMergeMergeRequestProcedure,
329+ svc.MergeMergeRequest,
330+ connect.WithSchema(mergeRequestServiceMethods.ByName("MergeMergeRequest")),
331+ connect.WithHandlerOptions(opts...),
332+ )
333+ mergeRequestServiceMarkReadHandler := connect.NewUnaryHandler(
334+ MergeRequestServiceMarkReadProcedure,
335+ svc.MarkRead,
336+ connect.WithSchema(mergeRequestServiceMethods.ByName("MarkRead")),
337+ connect.WithHandlerOptions(opts...),
338+ )
339+ return "/ermite.MergeRequestService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
340+ switch r.URL.Path {
341+ case MergeRequestServiceCreateMergeRequestProcedure:
342+ mergeRequestServiceCreateMergeRequestHandler.ServeHTTP(w, r)
343+ case MergeRequestServiceListMergeRequestsProcedure:
344+ mergeRequestServiceListMergeRequestsHandler.ServeHTTP(w, r)
345+ case MergeRequestServiceGetMergeRequestProcedure:
346+ mergeRequestServiceGetMergeRequestHandler.ServeHTTP(w, r)
347+ case MergeRequestServiceAddCommentProcedure:
348+ mergeRequestServiceAddCommentHandler.ServeHTTP(w, r)
349+ case MergeRequestServiceResolveCommentProcedure:
350+ mergeRequestServiceResolveCommentHandler.ServeHTTP(w, r)
351+ case MergeRequestServiceCloseMergeRequestProcedure:
352+ mergeRequestServiceCloseMergeRequestHandler.ServeHTTP(w, r)
353+ case MergeRequestServiceMergeMergeRequestProcedure:
354+ mergeRequestServiceMergeMergeRequestHandler.ServeHTTP(w, r)
355+ case MergeRequestServiceMarkReadProcedure:
356+ mergeRequestServiceMarkReadHandler.ServeHTTP(w, r)
357+ default:
358+ http.NotFound(w, r)
359+ }
360+ })
361+}
362+
363+// UnimplementedMergeRequestServiceHandler returns CodeUnimplemented from all methods.
364+type UnimplementedMergeRequestServiceHandler struct{}
365+
366+func (UnimplementedMergeRequestServiceHandler) CreateMergeRequest(context.Context, *connect.Request[ermite.CreateMergeRequestRequest]) (*connect.Response[ermite.CreateMergeRequestResponse], error) {
367+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MergeRequestService.CreateMergeRequest is not implemented"))
368+}
369+
370+func (UnimplementedMergeRequestServiceHandler) ListMergeRequests(context.Context, *connect.Request[ermite.ListMergeRequestsRequest]) (*connect.Response[ermite.ListMergeRequestsResponse], error) {
371+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MergeRequestService.ListMergeRequests is not implemented"))
372+}
373+
374+func (UnimplementedMergeRequestServiceHandler) GetMergeRequest(context.Context, *connect.Request[ermite.GetMergeRequestRequest]) (*connect.Response[ermite.GetMergeRequestResponse], error) {
375+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MergeRequestService.GetMergeRequest is not implemented"))
376+}
377+
378+func (UnimplementedMergeRequestServiceHandler) AddComment(context.Context, *connect.Request[ermite.AddCommentRequest]) (*connect.Response[ermite.AddCommentResponse], error) {
379+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MergeRequestService.AddComment is not implemented"))
380+}
381+
382+func (UnimplementedMergeRequestServiceHandler) ResolveComment(context.Context, *connect.Request[ermite.ResolveCommentRequest]) (*connect.Response[ermite.ResolveCommentResponse], error) {
383+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MergeRequestService.ResolveComment is not implemented"))
384+}
385+
386+func (UnimplementedMergeRequestServiceHandler) CloseMergeRequest(context.Context, *connect.Request[ermite.CloseMergeRequestRequest]) (*connect.Response[ermite.CloseMergeRequestResponse], error) {
387+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MergeRequestService.CloseMergeRequest is not implemented"))
388+}
389+
390+func (UnimplementedMergeRequestServiceHandler) MergeMergeRequest(context.Context, *connect.Request[ermite.MergeMergeRequestRequest]) (*connect.Response[ermite.MergeMergeRequestResponse], error) {
391+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MergeRequestService.MergeMergeRequest is not implemented"))
392+}
393+
394+func (UnimplementedMergeRequestServiceHandler) MarkRead(context.Context, *connect.Request[ermite.MarkReadRequest]) (*connect.Response[ermite.MarkReadResponse], error) {
395+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MergeRequestService.MarkRead is not implemented"))
396+}
@@ -0,0 +1,396 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/merge_request.proto
6+
7+package ermiteconnect
8+
9+import (
10+ ermite "bnema.dev/ermite-api/gen/ermite"
11+ connect "connectrpc.com/connect"
12+ context "context"
13+ errors "errors"
14+ http "net/http"
15+ strings "strings"
16+)
17+
18+// This is a compile-time assertion to ensure that this generated file and the connect package are
19+// compatible. If you get a compiler error that this constant is not defined, this code was
20+// generated with a version of connect newer than the one compiled into your binary. You can fix the
21+// problem by either regenerating this code with an older version of connect or updating the connect
22+// version compiled into your binary.
23+const _ = connect.IsAtLeastVersion1_13_0
24+
25+const (
26+ // MergeRequestServiceName is the fully-qualified name of the MergeRequestService service.
27+ MergeRequestServiceName = "ermite.MergeRequestService"
28+)
29+
30+// These constants are the fully-qualified names of the RPCs defined in this package. They're
31+// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
32+//
33+// Note that these are different from the fully-qualified method names used by
34+// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
35+// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
36+// period.
37+const (
38+ // MergeRequestServiceCreateMergeRequestProcedure is the fully-qualified name of the
39+ // MergeRequestService's CreateMergeRequest RPC.
40+ MergeRequestServiceCreateMergeRequestProcedure = "/ermite.MergeRequestService/CreateMergeRequest"
41+ // MergeRequestServiceListMergeRequestsProcedure is the fully-qualified name of the
42+ // MergeRequestService's ListMergeRequests RPC.
43+ MergeRequestServiceListMergeRequestsProcedure = "/ermite.MergeRequestService/ListMergeRequests"
44+ // MergeRequestServiceGetMergeRequestProcedure is the fully-qualified name of the
45+ // MergeRequestService's GetMergeRequest RPC.
46+ MergeRequestServiceGetMergeRequestProcedure = "/ermite.MergeRequestService/GetMergeRequest"
47+ // MergeRequestServiceAddCommentProcedure is the fully-qualified name of the MergeRequestService's
48+ // AddComment RPC.
49+ MergeRequestServiceAddCommentProcedure = "/ermite.MergeRequestService/AddComment"
50+ // MergeRequestServiceResolveCommentProcedure is the fully-qualified name of the
51+ // MergeRequestService's ResolveComment RPC.
52+ MergeRequestServiceResolveCommentProcedure = "/ermite.MergeRequestService/ResolveComment"
53+ // MergeRequestServiceCloseMergeRequestProcedure is the fully-qualified name of the
54+ // MergeRequestService's CloseMergeRequest RPC.
55+ MergeRequestServiceCloseMergeRequestProcedure = "/ermite.MergeRequestService/CloseMergeRequest"
56+ // MergeRequestServiceMergeMergeRequestProcedure is the fully-qualified name of the
57+ // MergeRequestService's MergeMergeRequest RPC.
58+ MergeRequestServiceMergeMergeRequestProcedure = "/ermite.MergeRequestService/MergeMergeRequest"
59+ // MergeRequestServiceMarkReadProcedure is the fully-qualified name of the MergeRequestService's
60+ // MarkRead RPC.
61+ MergeRequestServiceMarkReadProcedure = "/ermite.MergeRequestService/MarkRead"
62+)
63+
64+// MergeRequestServiceClient is a client for the ermite.MergeRequestService service.
65+type MergeRequestServiceClient interface {
66+ // CreateMergeRequest opens a merge request. Needs the mr:write scope. Fails
67+ // with invalid_argument if a text rule is broken, if a branch does not exist
68+ // or if source and target are the same branch, and with already_exists if an
69+ // open merge request already has the same source and target.
70+ CreateMergeRequest(context.Context, *connect.Request[ermite.CreateMergeRequestRequest]) (*connect.Response[ermite.CreateMergeRequestResponse], error)
71+ // ListMergeRequests lists the merge requests of a repository, highest
72+ // number (newest) first, optionally restricted to one state. The listed
73+ // merge requests carry no diff or comments; head_id is set. Fails with
74+ // not_found if the repository does not exist or is not readable.
75+ ListMergeRequests(context.Context, *connect.Request[ermite.ListMergeRequestsRequest]) (*connect.Response[ermite.ListMergeRequestsResponse], error)
76+ // GetMergeRequest returns one merge request with its commits, its diff and
77+ // its comments. The diff compares the target with the head (what the merge
78+ // would bring: "target...head"), or, with since_last_read, the head the
79+ // caller last marked read with the current head. Fails with not_found if
80+ // the repository or the number is unknown.
81+ GetMergeRequest(context.Context, *connect.Request[ermite.GetMergeRequestRequest]) (*connect.Response[ermite.GetMergeRequestResponse], error)
82+ // AddComment adds a general comment or a comment on a line of the diff.
83+ // Needs the mr:write scope. Fails with invalid_argument if the text breaks
84+ // a rule or if path, line and side do not designate a line of the current
85+ // diff of the merge request, and with failed_precondition if the merge
86+ // request is not open.
87+ AddComment(context.Context, *connect.Request[ermite.AddCommentRequest]) (*connect.Response[ermite.AddCommentResponse], error)
88+ // ResolveComment marks a comment resolved or unresolved. Needs the mr:write
89+ // scope. Fails with not_found if the comment is unknown and with
90+ // failed_precondition if the merge request is not open. Setting the state
91+ // the comment already has succeeds and changes nothing.
92+ ResolveComment(context.Context, *connect.Request[ermite.ResolveCommentRequest]) (*connect.Response[ermite.ResolveCommentResponse], error)
93+ // CloseMergeRequest closes an open merge request without merging it. Needs
94+ // the mr:write scope. Fails with failed_precondition if the merge request
95+ // is not open. A closed merge request cannot be reopened: open a new one.
96+ CloseMergeRequest(context.Context, *connect.Request[ermite.CloseMergeRequestRequest]) (*connect.Response[ermite.CloseMergeRequestResponse], error)
97+ // MergeMergeRequest merges an open merge request. Needs the mr:merge scope.
98+ // The caller passes the head it reviewed: the server merges only if the
99+ // source branch still points to it, so a push between review and merge is
100+ // never merged unseen. Fails with invalid_argument if head_id is empty or
101+ // not a commit id, and with failed_precondition if the merge request is not
102+ // open, if the source branch no longer points to head_id ("branch moved,
103+ // read again"), if the target is not an ancestor of the head (rebase and
104+ // push again), if the mode is not allowed by RepoSettings, or if a check
105+ // fails and RepoSettings.failing_checks_block is set. Merges of one
106+ // repository run one at a time. Repeating a call that succeeded fails with
107+ // failed_precondition (not open). On success the server publishes
108+ // MergeRequestMerged then Pushed for the target branch.
109+ MergeMergeRequest(context.Context, *connect.Request[ermite.MergeMergeRequestRequest]) (*connect.Response[ermite.MergeMergeRequestResponse], error)
110+ // MarkRead records the head the caller has reviewed, for the since_last_read
111+ // option of GetMergeRequest. There is one read position per merge request,
112+ // the reviewer's. Needs the mr:write scope. Marking a commit that does not
113+ // exist in the repository is invalid_argument.
114+ MarkRead(context.Context, *connect.Request[ermite.MarkReadRequest]) (*connect.Response[ermite.MarkReadResponse], error)
115+}
116+
117+// NewMergeRequestServiceClient constructs a client for the ermite.MergeRequestService service. By
118+// default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses,
119+// and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the
120+// connect.WithGRPC() or connect.WithGRPCWeb() options.
121+//
122+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
123+// http://api.acme.com or https://acme.com/grpc).
124+func NewMergeRequestServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) MergeRequestServiceClient {
125+ baseURL = strings.TrimRight(baseURL, "/")
126+ mergeRequestServiceMethods := ermite.File_ermite_merge_request_proto.Services().ByName("MergeRequestService").Methods()
127+ return &mergeRequestServiceClient{
128+ createMergeRequest: connect.NewClient[ermite.CreateMergeRequestRequest, ermite.CreateMergeRequestResponse](
129+ httpClient,
130+ baseURL+MergeRequestServiceCreateMergeRequestProcedure,
131+ connect.WithSchema(mergeRequestServiceMethods.ByName("CreateMergeRequest")),
132+ connect.WithClientOptions(opts...),
133+ ),
134+ listMergeRequests: connect.NewClient[ermite.ListMergeRequestsRequest, ermite.ListMergeRequestsResponse](
135+ httpClient,
136+ baseURL+MergeRequestServiceListMergeRequestsProcedure,
137+ connect.WithSchema(mergeRequestServiceMethods.ByName("ListMergeRequests")),
138+ connect.WithClientOptions(opts...),
139+ ),
140+ getMergeRequest: connect.NewClient[ermite.GetMergeRequestRequest, ermite.GetMergeRequestResponse](
141+ httpClient,
142+ baseURL+MergeRequestServiceGetMergeRequestProcedure,
143+ connect.WithSchema(mergeRequestServiceMethods.ByName("GetMergeRequest")),
144+ connect.WithClientOptions(opts...),
145+ ),
146+ addComment: connect.NewClient[ermite.AddCommentRequest, ermite.AddCommentResponse](
147+ httpClient,
148+ baseURL+MergeRequestServiceAddCommentProcedure,
149+ connect.WithSchema(mergeRequestServiceMethods.ByName("AddComment")),
150+ connect.WithClientOptions(opts...),
151+ ),
152+ resolveComment: connect.NewClient[ermite.ResolveCommentRequest, ermite.ResolveCommentResponse](
153+ httpClient,
154+ baseURL+MergeRequestServiceResolveCommentProcedure,
155+ connect.WithSchema(mergeRequestServiceMethods.ByName("ResolveComment")),
156+ connect.WithClientOptions(opts...),
157+ ),
158+ closeMergeRequest: connect.NewClient[ermite.CloseMergeRequestRequest, ermite.CloseMergeRequestResponse](
159+ httpClient,
160+ baseURL+MergeRequestServiceCloseMergeRequestProcedure,
161+ connect.WithSchema(mergeRequestServiceMethods.ByName("CloseMergeRequest")),
162+ connect.WithClientOptions(opts...),
163+ ),
164+ mergeMergeRequest: connect.NewClient[ermite.MergeMergeRequestRequest, ermite.MergeMergeRequestResponse](
165+ httpClient,
166+ baseURL+MergeRequestServiceMergeMergeRequestProcedure,
167+ connect.WithSchema(mergeRequestServiceMethods.ByName("MergeMergeRequest")),
168+ connect.WithClientOptions(opts...),
169+ ),
170+ markRead: connect.NewClient[ermite.MarkReadRequest, ermite.MarkReadResponse](
171+ httpClient,
172+ baseURL+MergeRequestServiceMarkReadProcedure,
173+ connect.WithSchema(mergeRequestServiceMethods.ByName("MarkRead")),
174+ connect.WithClientOptions(opts...),
175+ ),
176+ }
177+}
178+
179+// mergeRequestServiceClient implements MergeRequestServiceClient.
180+type mergeRequestServiceClient struct {
181+ createMergeRequest *connect.Client[ermite.CreateMergeRequestRequest, ermite.CreateMergeRequestResponse]
182+ listMergeRequests *connect.Client[ermite.ListMergeRequestsRequest, ermite.ListMergeRequestsResponse]
183+ getMergeRequest *connect.Client[ermite.GetMergeRequestRequest, ermite.GetMergeRequestResponse]
184+ addComment *connect.Client[ermite.AddCommentRequest, ermite.AddCommentResponse]
185+ resolveComment *connect.Client[ermite.ResolveCommentRequest, ermite.ResolveCommentResponse]
186+ closeMergeRequest *connect.Client[ermite.CloseMergeRequestRequest, ermite.CloseMergeRequestResponse]
187+ mergeMergeRequest *connect.Client[ermite.MergeMergeRequestRequest, ermite.MergeMergeRequestResponse]
188+ markRead *connect.Client[ermite.MarkReadRequest, ermite.MarkReadResponse]
189+}
190+
191+// CreateMergeRequest calls ermite.MergeRequestService.CreateMergeRequest.
192+func (c *mergeRequestServiceClient) CreateMergeRequest(ctx context.Context, req *connect.Request[ermite.CreateMergeRequestRequest]) (*connect.Response[ermite.CreateMergeRequestResponse], error) {
193+ return c.createMergeRequest.CallUnary(ctx, req)
194+}
195+
196+// ListMergeRequests calls ermite.MergeRequestService.ListMergeRequests.
197+func (c *mergeRequestServiceClient) ListMergeRequests(ctx context.Context, req *connect.Request[ermite.ListMergeRequestsRequest]) (*connect.Response[ermite.ListMergeRequestsResponse], error) {
198+ return c.listMergeRequests.CallUnary(ctx, req)
199+}
200+
201+// GetMergeRequest calls ermite.MergeRequestService.GetMergeRequest.
202+func (c *mergeRequestServiceClient) GetMergeRequest(ctx context.Context, req *connect.Request[ermite.GetMergeRequestRequest]) (*connect.Response[ermite.GetMergeRequestResponse], error) {
203+ return c.getMergeRequest.CallUnary(ctx, req)
204+}
205+
206+// AddComment calls ermite.MergeRequestService.AddComment.
207+func (c *mergeRequestServiceClient) AddComment(ctx context.Context, req *connect.Request[ermite.AddCommentRequest]) (*connect.Response[ermite.AddCommentResponse], error) {
208+ return c.addComment.CallUnary(ctx, req)
209+}
210+
211+// ResolveComment calls ermite.MergeRequestService.ResolveComment.
212+func (c *mergeRequestServiceClient) ResolveComment(ctx context.Context, req *connect.Request[ermite.ResolveCommentRequest]) (*connect.Response[ermite.ResolveCommentResponse], error) {
213+ return c.resolveComment.CallUnary(ctx, req)
214+}
215+
216+// CloseMergeRequest calls ermite.MergeRequestService.CloseMergeRequest.
217+func (c *mergeRequestServiceClient) CloseMergeRequest(ctx context.Context, req *connect.Request[ermite.CloseMergeRequestRequest]) (*connect.Response[ermite.CloseMergeRequestResponse], error) {
218+ return c.closeMergeRequest.CallUnary(ctx, req)
219+}
220+
221+// MergeMergeRequest calls ermite.MergeRequestService.MergeMergeRequest.
222+func (c *mergeRequestServiceClient) MergeMergeRequest(ctx context.Context, req *connect.Request[ermite.MergeMergeRequestRequest]) (*connect.Response[ermite.MergeMergeRequestResponse], error) {
223+ return c.mergeMergeRequest.CallUnary(ctx, req)
224+}
225+
226+// MarkRead calls ermite.MergeRequestService.MarkRead.
227+func (c *mergeRequestServiceClient) MarkRead(ctx context.Context, req *connect.Request[ermite.MarkReadRequest]) (*connect.Response[ermite.MarkReadResponse], error) {
228+ return c.markRead.CallUnary(ctx, req)
229+}
230+
231+// MergeRequestServiceHandler is an implementation of the ermite.MergeRequestService service.
232+type MergeRequestServiceHandler interface {
233+ // CreateMergeRequest opens a merge request. Needs the mr:write scope. Fails
234+ // with invalid_argument if a text rule is broken, if a branch does not exist
235+ // or if source and target are the same branch, and with already_exists if an
236+ // open merge request already has the same source and target.
237+ CreateMergeRequest(context.Context, *connect.Request[ermite.CreateMergeRequestRequest]) (*connect.Response[ermite.CreateMergeRequestResponse], error)
238+ // ListMergeRequests lists the merge requests of a repository, highest
239+ // number (newest) first, optionally restricted to one state. The listed
240+ // merge requests carry no diff or comments; head_id is set. Fails with
241+ // not_found if the repository does not exist or is not readable.
242+ ListMergeRequests(context.Context, *connect.Request[ermite.ListMergeRequestsRequest]) (*connect.Response[ermite.ListMergeRequestsResponse], error)
243+ // GetMergeRequest returns one merge request with its commits, its diff and
244+ // its comments. The diff compares the target with the head (what the merge
245+ // would bring: "target...head"), or, with since_last_read, the head the
246+ // caller last marked read with the current head. Fails with not_found if
247+ // the repository or the number is unknown.
248+ GetMergeRequest(context.Context, *connect.Request[ermite.GetMergeRequestRequest]) (*connect.Response[ermite.GetMergeRequestResponse], error)
249+ // AddComment adds a general comment or a comment on a line of the diff.
250+ // Needs the mr:write scope. Fails with invalid_argument if the text breaks
251+ // a rule or if path, line and side do not designate a line of the current
252+ // diff of the merge request, and with failed_precondition if the merge
253+ // request is not open.
254+ AddComment(context.Context, *connect.Request[ermite.AddCommentRequest]) (*connect.Response[ermite.AddCommentResponse], error)
255+ // ResolveComment marks a comment resolved or unresolved. Needs the mr:write
256+ // scope. Fails with not_found if the comment is unknown and with
257+ // failed_precondition if the merge request is not open. Setting the state
258+ // the comment already has succeeds and changes nothing.
259+ ResolveComment(context.Context, *connect.Request[ermite.ResolveCommentRequest]) (*connect.Response[ermite.ResolveCommentResponse], error)
260+ // CloseMergeRequest closes an open merge request without merging it. Needs
261+ // the mr:write scope. Fails with failed_precondition if the merge request
262+ // is not open. A closed merge request cannot be reopened: open a new one.
263+ CloseMergeRequest(context.Context, *connect.Request[ermite.CloseMergeRequestRequest]) (*connect.Response[ermite.CloseMergeRequestResponse], error)
264+ // MergeMergeRequest merges an open merge request. Needs the mr:merge scope.
265+ // The caller passes the head it reviewed: the server merges only if the
266+ // source branch still points to it, so a push between review and merge is
267+ // never merged unseen. Fails with invalid_argument if head_id is empty or
268+ // not a commit id, and with failed_precondition if the merge request is not
269+ // open, if the source branch no longer points to head_id ("branch moved,
270+ // read again"), if the target is not an ancestor of the head (rebase and
271+ // push again), if the mode is not allowed by RepoSettings, or if a check
272+ // fails and RepoSettings.failing_checks_block is set. Merges of one
273+ // repository run one at a time. Repeating a call that succeeded fails with
274+ // failed_precondition (not open). On success the server publishes
275+ // MergeRequestMerged then Pushed for the target branch.
276+ MergeMergeRequest(context.Context, *connect.Request[ermite.MergeMergeRequestRequest]) (*connect.Response[ermite.MergeMergeRequestResponse], error)
277+ // MarkRead records the head the caller has reviewed, for the since_last_read
278+ // option of GetMergeRequest. There is one read position per merge request,
279+ // the reviewer's. Needs the mr:write scope. Marking a commit that does not
280+ // exist in the repository is invalid_argument.
281+ MarkRead(context.Context, *connect.Request[ermite.MarkReadRequest]) (*connect.Response[ermite.MarkReadResponse], error)
282+}
283+
284+// NewMergeRequestServiceHandler builds an HTTP handler from the service implementation. It returns
285+// the path on which to mount the handler and the handler itself.
286+//
287+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
288+// and JSON codecs. They also support gzip compression.
289+func NewMergeRequestServiceHandler(svc MergeRequestServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
290+ mergeRequestServiceMethods := ermite.File_ermite_merge_request_proto.Services().ByName("MergeRequestService").Methods()
291+ mergeRequestServiceCreateMergeRequestHandler := connect.NewUnaryHandler(
292+ MergeRequestServiceCreateMergeRequestProcedure,
293+ svc.CreateMergeRequest,
294+ connect.WithSchema(mergeRequestServiceMethods.ByName("CreateMergeRequest")),
295+ connect.WithHandlerOptions(opts...),
296+ )
297+ mergeRequestServiceListMergeRequestsHandler := connect.NewUnaryHandler(
298+ MergeRequestServiceListMergeRequestsProcedure,
299+ svc.ListMergeRequests,
300+ connect.WithSchema(mergeRequestServiceMethods.ByName("ListMergeRequests")),
301+ connect.WithHandlerOptions(opts...),
302+ )
303+ mergeRequestServiceGetMergeRequestHandler := connect.NewUnaryHandler(
304+ MergeRequestServiceGetMergeRequestProcedure,
305+ svc.GetMergeRequest,
306+ connect.WithSchema(mergeRequestServiceMethods.ByName("GetMergeRequest")),
307+ connect.WithHandlerOptions(opts...),
308+ )
309+ mergeRequestServiceAddCommentHandler := connect.NewUnaryHandler(
310+ MergeRequestServiceAddCommentProcedure,
311+ svc.AddComment,
312+ connect.WithSchema(mergeRequestServiceMethods.ByName("AddComment")),
313+ connect.WithHandlerOptions(opts...),
314+ )
315+ mergeRequestServiceResolveCommentHandler := connect.NewUnaryHandler(
316+ MergeRequestServiceResolveCommentProcedure,
317+ svc.ResolveComment,
318+ connect.WithSchema(mergeRequestServiceMethods.ByName("ResolveComment")),
319+ connect.WithHandlerOptions(opts...),
320+ )
321+ mergeRequestServiceCloseMergeRequestHandler := connect.NewUnaryHandler(
322+ MergeRequestServiceCloseMergeRequestProcedure,
323+ svc.CloseMergeRequest,
324+ connect.WithSchema(mergeRequestServiceMethods.ByName("CloseMergeRequest")),
325+ connect.WithHandlerOptions(opts...),
326+ )
327+ mergeRequestServiceMergeMergeRequestHandler := connect.NewUnaryHandler(
328+ MergeRequestServiceMergeMergeRequestProcedure,
329+ svc.MergeMergeRequest,
330+ connect.WithSchema(mergeRequestServiceMethods.ByName("MergeMergeRequest")),
331+ connect.WithHandlerOptions(opts...),
332+ )
333+ mergeRequestServiceMarkReadHandler := connect.NewUnaryHandler(
334+ MergeRequestServiceMarkReadProcedure,
335+ svc.MarkRead,
336+ connect.WithSchema(mergeRequestServiceMethods.ByName("MarkRead")),
337+ connect.WithHandlerOptions(opts...),
338+ )
339+ return "/ermite.MergeRequestService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
340+ switch r.URL.Path {
341+ case MergeRequestServiceCreateMergeRequestProcedure:
342+ mergeRequestServiceCreateMergeRequestHandler.ServeHTTP(w, r)
343+ case MergeRequestServiceListMergeRequestsProcedure:
344+ mergeRequestServiceListMergeRequestsHandler.ServeHTTP(w, r)
345+ case MergeRequestServiceGetMergeRequestProcedure:
346+ mergeRequestServiceGetMergeRequestHandler.ServeHTTP(w, r)
347+ case MergeRequestServiceAddCommentProcedure:
348+ mergeRequestServiceAddCommentHandler.ServeHTTP(w, r)
349+ case MergeRequestServiceResolveCommentProcedure:
350+ mergeRequestServiceResolveCommentHandler.ServeHTTP(w, r)
351+ case MergeRequestServiceCloseMergeRequestProcedure:
352+ mergeRequestServiceCloseMergeRequestHandler.ServeHTTP(w, r)
353+ case MergeRequestServiceMergeMergeRequestProcedure:
354+ mergeRequestServiceMergeMergeRequestHandler.ServeHTTP(w, r)
355+ case MergeRequestServiceMarkReadProcedure:
356+ mergeRequestServiceMarkReadHandler.ServeHTTP(w, r)
357+ default:
358+ http.NotFound(w, r)
359+ }
360+ })
361+}
362+
363+// UnimplementedMergeRequestServiceHandler returns CodeUnimplemented from all methods.
364+type UnimplementedMergeRequestServiceHandler struct{}
365+
366+func (UnimplementedMergeRequestServiceHandler) CreateMergeRequest(context.Context, *connect.Request[ermite.CreateMergeRequestRequest]) (*connect.Response[ermite.CreateMergeRequestResponse], error) {
367+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MergeRequestService.CreateMergeRequest is not implemented"))
368+}
369+
370+func (UnimplementedMergeRequestServiceHandler) ListMergeRequests(context.Context, *connect.Request[ermite.ListMergeRequestsRequest]) (*connect.Response[ermite.ListMergeRequestsResponse], error) {
371+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MergeRequestService.ListMergeRequests is not implemented"))
372+}
373+
374+func (UnimplementedMergeRequestServiceHandler) GetMergeRequest(context.Context, *connect.Request[ermite.GetMergeRequestRequest]) (*connect.Response[ermite.GetMergeRequestResponse], error) {
375+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MergeRequestService.GetMergeRequest is not implemented"))
376+}
377+
378+func (UnimplementedMergeRequestServiceHandler) AddComment(context.Context, *connect.Request[ermite.AddCommentRequest]) (*connect.Response[ermite.AddCommentResponse], error) {
379+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MergeRequestService.AddComment is not implemented"))
380+}
381+
382+func (UnimplementedMergeRequestServiceHandler) ResolveComment(context.Context, *connect.Request[ermite.ResolveCommentRequest]) (*connect.Response[ermite.ResolveCommentResponse], error) {
383+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MergeRequestService.ResolveComment is not implemented"))
384+}
385+
386+func (UnimplementedMergeRequestServiceHandler) CloseMergeRequest(context.Context, *connect.Request[ermite.CloseMergeRequestRequest]) (*connect.Response[ermite.CloseMergeRequestResponse], error) {
387+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MergeRequestService.CloseMergeRequest is not implemented"))
388+}
389+
390+func (UnimplementedMergeRequestServiceHandler) MergeMergeRequest(context.Context, *connect.Request[ermite.MergeMergeRequestRequest]) (*connect.Response[ermite.MergeMergeRequestResponse], error) {
391+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MergeRequestService.MergeMergeRequest is not implemented"))
392+}
393+
394+func (UnimplementedMergeRequestServiceHandler) MarkRead(context.Context, *connect.Request[ermite.MarkReadRequest]) (*connect.Response[ermite.MarkReadResponse], error) {
395+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MergeRequestService.MarkRead is not implemented"))
396+}
M gen/ermite/ermiteconnect/repo.connect.go +8 -6
@@ -54,12 +54,13 @@ type RepoServiceClient interface {
5454 // GetRepo returns one repository. Fails with not_found if it does not exist
5555 // or is private and not readable by the caller.
5656 GetRepo(context.Context, *connect.Request[ermite.GetRepoRequest]) (*connect.Response[ermite.GetRepoResponse], error)
57- // CreateRepo creates an empty repository. Needs the repo:write scope. Fails
57+ // CreateRepo creates an empty repository with the default settings. Needs
58+ // the repo:write scope. Fails
5859 // with already_exists if the name is taken and invalid_argument if the name
5960 // is not valid.
6061 CreateRepo(context.Context, *connect.Request[ermite.CreateRepoRequest]) (*connect.Response[ermite.CreateRepoResponse], error)
61- // UpdateRepo changes the fields named in update_mask. Needs the repo:admin
62- // scope.
62+ // UpdateRepo changes the fields named in update_mask, including the merge
63+ // settings. Needs the repo:admin scope.
6364 UpdateRepo(context.Context, *connect.Request[ermite.UpdateRepoRequest]) (*connect.Response[ermite.UpdateRepoResponse], error)
6465 // DeleteRepo deletes a repository. Needs the repo:admin scope. The server
6566 // moves the data aside rather than destroying it; there is no undelete RPC.
@@ -151,12 +152,13 @@ type RepoServiceHandler interface {
151152 // GetRepo returns one repository. Fails with not_found if it does not exist
152153 // or is private and not readable by the caller.
153154 GetRepo(context.Context, *connect.Request[ermite.GetRepoRequest]) (*connect.Response[ermite.GetRepoResponse], error)
154- // CreateRepo creates an empty repository. Needs the repo:write scope. Fails
155+ // CreateRepo creates an empty repository with the default settings. Needs
156+ // the repo:write scope. Fails
155157 // with already_exists if the name is taken and invalid_argument if the name
156158 // is not valid.
157159 CreateRepo(context.Context, *connect.Request[ermite.CreateRepoRequest]) (*connect.Response[ermite.CreateRepoResponse], error)
158- // UpdateRepo changes the fields named in update_mask. Needs the repo:admin
159- // scope.
160+ // UpdateRepo changes the fields named in update_mask, including the merge
161+ // settings. Needs the repo:admin scope.
160162 UpdateRepo(context.Context, *connect.Request[ermite.UpdateRepoRequest]) (*connect.Response[ermite.UpdateRepoResponse], error)
161163 // DeleteRepo deletes a repository. Needs the repo:admin scope. The server
162164 // moves the data aside rather than destroying it; there is no undelete RPC.
@@ -54,12 +54,13 @@ type RepoServiceClient interface {
54 // GetRepo returns one repository. Fails with not_found if it does not exist54 // GetRepo returns one repository. Fails with not_found if it does not exist
55 // or is private and not readable by the caller.55 // or is private and not readable by the caller.
56 GetRepo(context.Context, *connect.Request[ermite.GetRepoRequest]) (*connect.Response[ermite.GetRepoResponse], error)56 GetRepo(context.Context, *connect.Request[ermite.GetRepoRequest]) (*connect.Response[ermite.GetRepoResponse], error)
57- // CreateRepo creates an empty repository. Needs the repo:write scope. Fails57+ // CreateRepo creates an empty repository with the default settings. Needs
58+ // the repo:write scope. Fails
58 // with already_exists if the name is taken and invalid_argument if the name59 // with already_exists if the name is taken and invalid_argument if the name
59 // is not valid.60 // is not valid.
60 CreateRepo(context.Context, *connect.Request[ermite.CreateRepoRequest]) (*connect.Response[ermite.CreateRepoResponse], error)61 CreateRepo(context.Context, *connect.Request[ermite.CreateRepoRequest]) (*connect.Response[ermite.CreateRepoResponse], error)
61- // UpdateRepo changes the fields named in update_mask. Needs the repo:admin62+ // UpdateRepo changes the fields named in update_mask, including the merge
62- // scope.63+ // settings. Needs the repo:admin scope.
63 UpdateRepo(context.Context, *connect.Request[ermite.UpdateRepoRequest]) (*connect.Response[ermite.UpdateRepoResponse], error)64 UpdateRepo(context.Context, *connect.Request[ermite.UpdateRepoRequest]) (*connect.Response[ermite.UpdateRepoResponse], error)
64 // DeleteRepo deletes a repository. Needs the repo:admin scope. The server65 // DeleteRepo deletes a repository. Needs the repo:admin scope. The server
65 // moves the data aside rather than destroying it; there is no undelete RPC.66 // moves the data aside rather than destroying it; there is no undelete RPC.
@@ -151,12 +152,13 @@ type RepoServiceHandler interface {
151 // GetRepo returns one repository. Fails with not_found if it does not exist152 // GetRepo returns one repository. Fails with not_found if it does not exist
152 // or is private and not readable by the caller.153 // or is private and not readable by the caller.
153 GetRepo(context.Context, *connect.Request[ermite.GetRepoRequest]) (*connect.Response[ermite.GetRepoResponse], error)154 GetRepo(context.Context, *connect.Request[ermite.GetRepoRequest]) (*connect.Response[ermite.GetRepoResponse], error)
154- // CreateRepo creates an empty repository. Needs the repo:write scope. Fails155+ // CreateRepo creates an empty repository with the default settings. Needs
156+ // the repo:write scope. Fails
155 // with already_exists if the name is taken and invalid_argument if the name157 // with already_exists if the name is taken and invalid_argument if the name
156 // is not valid.158 // is not valid.
157 CreateRepo(context.Context, *connect.Request[ermite.CreateRepoRequest]) (*connect.Response[ermite.CreateRepoResponse], error)159 CreateRepo(context.Context, *connect.Request[ermite.CreateRepoRequest]) (*connect.Response[ermite.CreateRepoResponse], error)
158- // UpdateRepo changes the fields named in update_mask. Needs the repo:admin160+ // UpdateRepo changes the fields named in update_mask, including the merge
159- // scope.161+ // settings. Needs the repo:admin scope.
160 UpdateRepo(context.Context, *connect.Request[ermite.UpdateRepoRequest]) (*connect.Response[ermite.UpdateRepoResponse], error)162 UpdateRepo(context.Context, *connect.Request[ermite.UpdateRepoRequest]) (*connect.Response[ermite.UpdateRepoResponse], error)
161 // DeleteRepo deletes a repository. Needs the repo:admin scope. The server163 // DeleteRepo deletes a repository. Needs the repo:admin scope. The server
162 // moves the data aside rather than destroying it; there is no undelete RPC.164 // moves the data aside rather than destroying it; there is no undelete RPC.
M gen/ermite/ermiteconnect/server.connect.go +40 -1
@@ -38,6 +38,9 @@ const (
3838 // ServerServiceGetVersionProcedure is the fully-qualified name of the ServerService's GetVersion
3939 // RPC.
4040 ServerServiceGetVersionProcedure = "/ermite.ServerService/GetVersion"
41+ // ServerServiceGetSigningKeyProcedure is the fully-qualified name of the ServerService's
42+ // GetSigningKey RPC.
43+ ServerServiceGetSigningKeyProcedure = "/ermite.ServerService/GetSigningKey"
4144 )
4245
4346 // ServerServiceClient is a client for the ermite.ServerService service.
@@ -46,6 +49,12 @@ type ServerServiceClient interface {
4649 // contract it implements. It needs no authentication, so a client can call
4750 // it first to check compatibility.
4851 GetVersion(context.Context, *connect.Request[ermite.GetVersionRequest]) (*connect.Response[ermite.GetVersionResponse], error)
52+ // GetSigningKey returns the public key with which the server signs the
53+ // commits it creates (squash merges, see MergeMode). It needs no
54+ // authentication: the key is public, and anyone verifying a commit needs it.
55+ // Servers that implement it list "signing_key" in
56+ // GetVersionResponse.features.
57+ GetSigningKey(context.Context, *connect.Request[ermite.GetSigningKeyRequest]) (*connect.Response[ermite.GetSigningKeyResponse], error)
4958 }
5059
5160 // NewServerServiceClient constructs a client for the ermite.ServerService service. By default, it
@@ -65,12 +74,19 @@ func NewServerServiceClient(httpClient connect.HTTPClient, baseURL string, opts
6574 connect.WithSchema(serverServiceMethods.ByName("GetVersion")),
6675 connect.WithClientOptions(opts...),
6776 ),
77+ getSigningKey: connect.NewClient[ermite.GetSigningKeyRequest, ermite.GetSigningKeyResponse](
78+ httpClient,
79+ baseURL+ServerServiceGetSigningKeyProcedure,
80+ connect.WithSchema(serverServiceMethods.ByName("GetSigningKey")),
81+ connect.WithClientOptions(opts...),
82+ ),
6883 }
6984 }
7085
7186 // serverServiceClient implements ServerServiceClient.
7287 type serverServiceClient struct {
73- getVersion *connect.Client[ermite.GetVersionRequest, ermite.GetVersionResponse]
88+ getVersion *connect.Client[ermite.GetVersionRequest, ermite.GetVersionResponse]
89+ getSigningKey *connect.Client[ermite.GetSigningKeyRequest, ermite.GetSigningKeyResponse]
7490 }
7591
7692 // GetVersion calls ermite.ServerService.GetVersion.
@@ -78,12 +94,23 @@ func (c *serverServiceClient) GetVersion(ctx context.Context, req *connect.Reque
7894 return c.getVersion.CallUnary(ctx, req)
7995 }
8096
97+// GetSigningKey calls ermite.ServerService.GetSigningKey.
98+func (c *serverServiceClient) GetSigningKey(ctx context.Context, req *connect.Request[ermite.GetSigningKeyRequest]) (*connect.Response[ermite.GetSigningKeyResponse], error) {
99+ return c.getSigningKey.CallUnary(ctx, req)
100+}
101+
81102 // ServerServiceHandler is an implementation of the ermite.ServerService service.
82103 type ServerServiceHandler interface {
83104 // GetVersion returns the server version and the optional capabilities of the
84105 // contract it implements. It needs no authentication, so a client can call
85106 // it first to check compatibility.
86107 GetVersion(context.Context, *connect.Request[ermite.GetVersionRequest]) (*connect.Response[ermite.GetVersionResponse], error)
108+ // GetSigningKey returns the public key with which the server signs the
109+ // commits it creates (squash merges, see MergeMode). It needs no
110+ // authentication: the key is public, and anyone verifying a commit needs it.
111+ // Servers that implement it list "signing_key" in
112+ // GetVersionResponse.features.
113+ GetSigningKey(context.Context, *connect.Request[ermite.GetSigningKeyRequest]) (*connect.Response[ermite.GetSigningKeyResponse], error)
87114 }
88115
89116 // NewServerServiceHandler builds an HTTP handler from the service implementation. It returns the
@@ -99,10 +126,18 @@ func NewServerServiceHandler(svc ServerServiceHandler, opts ...connect.HandlerOp
99126 connect.WithSchema(serverServiceMethods.ByName("GetVersion")),
100127 connect.WithHandlerOptions(opts...),
101128 )
129+ serverServiceGetSigningKeyHandler := connect.NewUnaryHandler(
130+ ServerServiceGetSigningKeyProcedure,
131+ svc.GetSigningKey,
132+ connect.WithSchema(serverServiceMethods.ByName("GetSigningKey")),
133+ connect.WithHandlerOptions(opts...),
134+ )
102135 return "/ermite.ServerService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
103136 switch r.URL.Path {
104137 case ServerServiceGetVersionProcedure:
105138 serverServiceGetVersionHandler.ServeHTTP(w, r)
139+ case ServerServiceGetSigningKeyProcedure:
140+ serverServiceGetSigningKeyHandler.ServeHTTP(w, r)
106141 default:
107142 http.NotFound(w, r)
108143 }
@@ -115,3 +150,7 @@ type UnimplementedServerServiceHandler struct{}
115150 func (UnimplementedServerServiceHandler) GetVersion(context.Context, *connect.Request[ermite.GetVersionRequest]) (*connect.Response[ermite.GetVersionResponse], error) {
116151 return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ServerService.GetVersion is not implemented"))
117152 }
153+
154+func (UnimplementedServerServiceHandler) GetSigningKey(context.Context, *connect.Request[ermite.GetSigningKeyRequest]) (*connect.Response[ermite.GetSigningKeyResponse], error) {
155+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ServerService.GetSigningKey is not implemented"))
156+}
@@ -38,6 +38,9 @@ const (
38 // ServerServiceGetVersionProcedure is the fully-qualified name of the ServerService's GetVersion38 // ServerServiceGetVersionProcedure is the fully-qualified name of the ServerService's GetVersion
39 // RPC.39 // RPC.
40 ServerServiceGetVersionProcedure = "/ermite.ServerService/GetVersion"40 ServerServiceGetVersionProcedure = "/ermite.ServerService/GetVersion"
41+ // ServerServiceGetSigningKeyProcedure is the fully-qualified name of the ServerService's
42+ // GetSigningKey RPC.
43+ ServerServiceGetSigningKeyProcedure = "/ermite.ServerService/GetSigningKey"
41 )44 )
42 45
43 // ServerServiceClient is a client for the ermite.ServerService service.46 // ServerServiceClient is a client for the ermite.ServerService service.
@@ -46,6 +49,12 @@ type ServerServiceClient interface {
46 // contract it implements. It needs no authentication, so a client can call49 // contract it implements. It needs no authentication, so a client can call
47 // it first to check compatibility.50 // it first to check compatibility.
48 GetVersion(context.Context, *connect.Request[ermite.GetVersionRequest]) (*connect.Response[ermite.GetVersionResponse], error)51 GetVersion(context.Context, *connect.Request[ermite.GetVersionRequest]) (*connect.Response[ermite.GetVersionResponse], error)
52+ // GetSigningKey returns the public key with which the server signs the
53+ // commits it creates (squash merges, see MergeMode). It needs no
54+ // authentication: the key is public, and anyone verifying a commit needs it.
55+ // Servers that implement it list "signing_key" in
56+ // GetVersionResponse.features.
57+ GetSigningKey(context.Context, *connect.Request[ermite.GetSigningKeyRequest]) (*connect.Response[ermite.GetSigningKeyResponse], error)
49 }58 }
50 59
51 // NewServerServiceClient constructs a client for the ermite.ServerService service. By default, it60 // NewServerServiceClient constructs a client for the ermite.ServerService service. By default, it
@@ -65,12 +74,19 @@ func NewServerServiceClient(httpClient connect.HTTPClient, baseURL string, opts
65 connect.WithSchema(serverServiceMethods.ByName("GetVersion")),74 connect.WithSchema(serverServiceMethods.ByName("GetVersion")),
66 connect.WithClientOptions(opts...),75 connect.WithClientOptions(opts...),
67 ),76 ),
77+ getSigningKey: connect.NewClient[ermite.GetSigningKeyRequest, ermite.GetSigningKeyResponse](
78+ httpClient,
79+ baseURL+ServerServiceGetSigningKeyProcedure,
80+ connect.WithSchema(serverServiceMethods.ByName("GetSigningKey")),
81+ connect.WithClientOptions(opts...),
82+ ),
68 }83 }
69 }84 }
70 85
71 // serverServiceClient implements ServerServiceClient.86 // serverServiceClient implements ServerServiceClient.
72 type serverServiceClient struct {87 type serverServiceClient struct {
73- getVersion *connect.Client[ermite.GetVersionRequest, ermite.GetVersionResponse]88+ getVersion *connect.Client[ermite.GetVersionRequest, ermite.GetVersionResponse]
89+ getSigningKey *connect.Client[ermite.GetSigningKeyRequest, ermite.GetSigningKeyResponse]
74 }90 }
75 91
76 // GetVersion calls ermite.ServerService.GetVersion.92 // GetVersion calls ermite.ServerService.GetVersion.
@@ -78,12 +94,23 @@ func (c *serverServiceClient) GetVersion(ctx context.Context, req *connect.Reque
78 return c.getVersion.CallUnary(ctx, req)94 return c.getVersion.CallUnary(ctx, req)
79 }95 }
80 96
97+// GetSigningKey calls ermite.ServerService.GetSigningKey.
98+func (c *serverServiceClient) GetSigningKey(ctx context.Context, req *connect.Request[ermite.GetSigningKeyRequest]) (*connect.Response[ermite.GetSigningKeyResponse], error) {
99+ return c.getSigningKey.CallUnary(ctx, req)
100+}
101+
81 // ServerServiceHandler is an implementation of the ermite.ServerService service.102 // ServerServiceHandler is an implementation of the ermite.ServerService service.
82 type ServerServiceHandler interface {103 type ServerServiceHandler interface {
83 // GetVersion returns the server version and the optional capabilities of the104 // GetVersion returns the server version and the optional capabilities of the
84 // contract it implements. It needs no authentication, so a client can call105 // contract it implements. It needs no authentication, so a client can call
85 // it first to check compatibility.106 // it first to check compatibility.
86 GetVersion(context.Context, *connect.Request[ermite.GetVersionRequest]) (*connect.Response[ermite.GetVersionResponse], error)107 GetVersion(context.Context, *connect.Request[ermite.GetVersionRequest]) (*connect.Response[ermite.GetVersionResponse], error)
108+ // GetSigningKey returns the public key with which the server signs the
109+ // commits it creates (squash merges, see MergeMode). It needs no
110+ // authentication: the key is public, and anyone verifying a commit needs it.
111+ // Servers that implement it list "signing_key" in
112+ // GetVersionResponse.features.
113+ GetSigningKey(context.Context, *connect.Request[ermite.GetSigningKeyRequest]) (*connect.Response[ermite.GetSigningKeyResponse], error)
87 }114 }
88 115
89 // NewServerServiceHandler builds an HTTP handler from the service implementation. It returns the116 // NewServerServiceHandler builds an HTTP handler from the service implementation. It returns the
@@ -99,10 +126,18 @@ func NewServerServiceHandler(svc ServerServiceHandler, opts ...connect.HandlerOp
99 connect.WithSchema(serverServiceMethods.ByName("GetVersion")),126 connect.WithSchema(serverServiceMethods.ByName("GetVersion")),
100 connect.WithHandlerOptions(opts...),127 connect.WithHandlerOptions(opts...),
101 )128 )
129+ serverServiceGetSigningKeyHandler := connect.NewUnaryHandler(
130+ ServerServiceGetSigningKeyProcedure,
131+ svc.GetSigningKey,
132+ connect.WithSchema(serverServiceMethods.ByName("GetSigningKey")),
133+ connect.WithHandlerOptions(opts...),
134+ )
102 return "/ermite.ServerService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {135 return "/ermite.ServerService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
103 switch r.URL.Path {136 switch r.URL.Path {
104 case ServerServiceGetVersionProcedure:137 case ServerServiceGetVersionProcedure:
105 serverServiceGetVersionHandler.ServeHTTP(w, r)138 serverServiceGetVersionHandler.ServeHTTP(w, r)
139+ case ServerServiceGetSigningKeyProcedure:
140+ serverServiceGetSigningKeyHandler.ServeHTTP(w, r)
106 default:141 default:
107 http.NotFound(w, r)142 http.NotFound(w, r)
108 }143 }
@@ -115,3 +150,7 @@ type UnimplementedServerServiceHandler struct{}
115 func (UnimplementedServerServiceHandler) GetVersion(context.Context, *connect.Request[ermite.GetVersionRequest]) (*connect.Response[ermite.GetVersionResponse], error) {150 func (UnimplementedServerServiceHandler) GetVersion(context.Context, *connect.Request[ermite.GetVersionRequest]) (*connect.Response[ermite.GetVersionResponse], error) {
116 return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ServerService.GetVersion is not implemented"))151 return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ServerService.GetVersion is not implemented"))
117 }152 }
153+
154+func (UnimplementedServerServiceHandler) GetSigningKey(context.Context, *connect.Request[ermite.GetSigningKeyRequest]) (*connect.Response[ermite.GetSigningKeyResponse], error) {
155+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ServerService.GetSigningKey is not implemented"))
156+}
A gen/ermite/ermiteconnect/session.connect.go +158 -0
@@ -0,0 +1,158 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/session.proto
6+
7+package ermiteconnect
8+
9+import (
10+ ermite "bnema.dev/ermite-api/gen/ermite"
11+ connect "connectrpc.com/connect"
12+ context "context"
13+ errors "errors"
14+ http "net/http"
15+ strings "strings"
16+)
17+
18+// This is a compile-time assertion to ensure that this generated file and the connect package are
19+// compatible. If you get a compiler error that this constant is not defined, this code was
20+// generated with a version of connect newer than the one compiled into your binary. You can fix the
21+// problem by either regenerating this code with an older version of connect or updating the connect
22+// version compiled into your binary.
23+const _ = connect.IsAtLeastVersion1_13_0
24+
25+const (
26+ // SessionServiceName is the fully-qualified name of the SessionService service.
27+ SessionServiceName = "ermite.SessionService"
28+)
29+
30+// These constants are the fully-qualified names of the RPCs defined in this package. They're
31+// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
32+//
33+// Note that these are different from the fully-qualified method names used by
34+// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
35+// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
36+// period.
37+const (
38+ // SessionServiceCreateLoginLinkProcedure is the fully-qualified name of the SessionService's
39+ // CreateLoginLink RPC.
40+ SessionServiceCreateLoginLinkProcedure = "/ermite.SessionService/CreateLoginLink"
41+ // SessionServiceSignOutEverywhereProcedure is the fully-qualified name of the SessionService's
42+ // SignOutEverywhere RPC.
43+ SessionServiceSignOutEverywhereProcedure = "/ermite.SessionService/SignOutEverywhere"
44+)
45+
46+// SessionServiceClient is a client for the ermite.SessionService service.
47+type SessionServiceClient interface {
48+ // CreateLoginLink creates a one-time link that signs the owner's browser
49+ // in. The link is valid for a short time (about a minute), can be used
50+ // once, and is kept in memory only: a server restart voids it. Opening it
51+ // shows a confirmation page; only confirming it consumes the link and sets
52+ // the session. Creating a new link does not void an earlier one. Fails with
53+ // resource_exhausted when too many links are pending.
54+ CreateLoginLink(context.Context, *connect.Request[ermite.CreateLoginLinkRequest]) (*connect.Response[ermite.CreateLoginLinkResponse], error)
55+ // SignOutEverywhere ends every browser session at once, including those on
56+ // other devices. Pending login links are not affected. Tokens are not
57+ // affected either: revoke them with TokenService.
58+ SignOutEverywhere(context.Context, *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error)
59+}
60+
61+// NewSessionServiceClient constructs a client for the ermite.SessionService service. By default, it
62+// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
63+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
64+// connect.WithGRPCWeb() options.
65+//
66+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
67+// http://api.acme.com or https://acme.com/grpc).
68+func NewSessionServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) SessionServiceClient {
69+ baseURL = strings.TrimRight(baseURL, "/")
70+ sessionServiceMethods := ermite.File_ermite_session_proto.Services().ByName("SessionService").Methods()
71+ return &sessionServiceClient{
72+ createLoginLink: connect.NewClient[ermite.CreateLoginLinkRequest, ermite.CreateLoginLinkResponse](
73+ httpClient,
74+ baseURL+SessionServiceCreateLoginLinkProcedure,
75+ connect.WithSchema(sessionServiceMethods.ByName("CreateLoginLink")),
76+ connect.WithClientOptions(opts...),
77+ ),
78+ signOutEverywhere: connect.NewClient[ermite.SignOutEverywhereRequest, ermite.SignOutEverywhereResponse](
79+ httpClient,
80+ baseURL+SessionServiceSignOutEverywhereProcedure,
81+ connect.WithSchema(sessionServiceMethods.ByName("SignOutEverywhere")),
82+ connect.WithClientOptions(opts...),
83+ ),
84+ }
85+}
86+
87+// sessionServiceClient implements SessionServiceClient.
88+type sessionServiceClient struct {
89+ createLoginLink *connect.Client[ermite.CreateLoginLinkRequest, ermite.CreateLoginLinkResponse]
90+ signOutEverywhere *connect.Client[ermite.SignOutEverywhereRequest, ermite.SignOutEverywhereResponse]
91+}
92+
93+// CreateLoginLink calls ermite.SessionService.CreateLoginLink.
94+func (c *sessionServiceClient) CreateLoginLink(ctx context.Context, req *connect.Request[ermite.CreateLoginLinkRequest]) (*connect.Response[ermite.CreateLoginLinkResponse], error) {
95+ return c.createLoginLink.CallUnary(ctx, req)
96+}
97+
98+// SignOutEverywhere calls ermite.SessionService.SignOutEverywhere.
99+func (c *sessionServiceClient) SignOutEverywhere(ctx context.Context, req *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error) {
100+ return c.signOutEverywhere.CallUnary(ctx, req)
101+}
102+
103+// SessionServiceHandler is an implementation of the ermite.SessionService service.
104+type SessionServiceHandler interface {
105+ // CreateLoginLink creates a one-time link that signs the owner's browser
106+ // in. The link is valid for a short time (about a minute), can be used
107+ // once, and is kept in memory only: a server restart voids it. Opening it
108+ // shows a confirmation page; only confirming it consumes the link and sets
109+ // the session. Creating a new link does not void an earlier one. Fails with
110+ // resource_exhausted when too many links are pending.
111+ CreateLoginLink(context.Context, *connect.Request[ermite.CreateLoginLinkRequest]) (*connect.Response[ermite.CreateLoginLinkResponse], error)
112+ // SignOutEverywhere ends every browser session at once, including those on
113+ // other devices. Pending login links are not affected. Tokens are not
114+ // affected either: revoke them with TokenService.
115+ SignOutEverywhere(context.Context, *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error)
116+}
117+
118+// NewSessionServiceHandler builds an HTTP handler from the service implementation. It returns the
119+// path on which to mount the handler and the handler itself.
120+//
121+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
122+// and JSON codecs. They also support gzip compression.
123+func NewSessionServiceHandler(svc SessionServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
124+ sessionServiceMethods := ermite.File_ermite_session_proto.Services().ByName("SessionService").Methods()
125+ sessionServiceCreateLoginLinkHandler := connect.NewUnaryHandler(
126+ SessionServiceCreateLoginLinkProcedure,
127+ svc.CreateLoginLink,
128+ connect.WithSchema(sessionServiceMethods.ByName("CreateLoginLink")),
129+ connect.WithHandlerOptions(opts...),
130+ )
131+ sessionServiceSignOutEverywhereHandler := connect.NewUnaryHandler(
132+ SessionServiceSignOutEverywhereProcedure,
133+ svc.SignOutEverywhere,
134+ connect.WithSchema(sessionServiceMethods.ByName("SignOutEverywhere")),
135+ connect.WithHandlerOptions(opts...),
136+ )
137+ return "/ermite.SessionService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
138+ switch r.URL.Path {
139+ case SessionServiceCreateLoginLinkProcedure:
140+ sessionServiceCreateLoginLinkHandler.ServeHTTP(w, r)
141+ case SessionServiceSignOutEverywhereProcedure:
142+ sessionServiceSignOutEverywhereHandler.ServeHTTP(w, r)
143+ default:
144+ http.NotFound(w, r)
145+ }
146+ })
147+}
148+
149+// UnimplementedSessionServiceHandler returns CodeUnimplemented from all methods.
150+type UnimplementedSessionServiceHandler struct{}
151+
152+func (UnimplementedSessionServiceHandler) CreateLoginLink(context.Context, *connect.Request[ermite.CreateLoginLinkRequest]) (*connect.Response[ermite.CreateLoginLinkResponse], error) {
153+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.SessionService.CreateLoginLink is not implemented"))
154+}
155+
156+func (UnimplementedSessionServiceHandler) SignOutEverywhere(context.Context, *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error) {
157+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.SessionService.SignOutEverywhere is not implemented"))
158+}
@@ -0,0 +1,158 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/session.proto
6+
7+package ermiteconnect
8+
9+import (
10+ ermite "bnema.dev/ermite-api/gen/ermite"
11+ connect "connectrpc.com/connect"
12+ context "context"
13+ errors "errors"
14+ http "net/http"
15+ strings "strings"
16+)
17+
18+// This is a compile-time assertion to ensure that this generated file and the connect package are
19+// compatible. If you get a compiler error that this constant is not defined, this code was
20+// generated with a version of connect newer than the one compiled into your binary. You can fix the
21+// problem by either regenerating this code with an older version of connect or updating the connect
22+// version compiled into your binary.
23+const _ = connect.IsAtLeastVersion1_13_0
24+
25+const (
26+ // SessionServiceName is the fully-qualified name of the SessionService service.
27+ SessionServiceName = "ermite.SessionService"
28+)
29+
30+// These constants are the fully-qualified names of the RPCs defined in this package. They're
31+// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
32+//
33+// Note that these are different from the fully-qualified method names used by
34+// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
35+// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
36+// period.
37+const (
38+ // SessionServiceCreateLoginLinkProcedure is the fully-qualified name of the SessionService's
39+ // CreateLoginLink RPC.
40+ SessionServiceCreateLoginLinkProcedure = "/ermite.SessionService/CreateLoginLink"
41+ // SessionServiceSignOutEverywhereProcedure is the fully-qualified name of the SessionService's
42+ // SignOutEverywhere RPC.
43+ SessionServiceSignOutEverywhereProcedure = "/ermite.SessionService/SignOutEverywhere"
44+)
45+
46+// SessionServiceClient is a client for the ermite.SessionService service.
47+type SessionServiceClient interface {
48+ // CreateLoginLink creates a one-time link that signs the owner's browser
49+ // in. The link is valid for a short time (about a minute), can be used
50+ // once, and is kept in memory only: a server restart voids it. Opening it
51+ // shows a confirmation page; only confirming it consumes the link and sets
52+ // the session. Creating a new link does not void an earlier one. Fails with
53+ // resource_exhausted when too many links are pending.
54+ CreateLoginLink(context.Context, *connect.Request[ermite.CreateLoginLinkRequest]) (*connect.Response[ermite.CreateLoginLinkResponse], error)
55+ // SignOutEverywhere ends every browser session at once, including those on
56+ // other devices. Pending login links are not affected. Tokens are not
57+ // affected either: revoke them with TokenService.
58+ SignOutEverywhere(context.Context, *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error)
59+}
60+
61+// NewSessionServiceClient constructs a client for the ermite.SessionService service. By default, it
62+// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
63+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
64+// connect.WithGRPCWeb() options.
65+//
66+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
67+// http://api.acme.com or https://acme.com/grpc).
68+func NewSessionServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) SessionServiceClient {
69+ baseURL = strings.TrimRight(baseURL, "/")
70+ sessionServiceMethods := ermite.File_ermite_session_proto.Services().ByName("SessionService").Methods()
71+ return &sessionServiceClient{
72+ createLoginLink: connect.NewClient[ermite.CreateLoginLinkRequest, ermite.CreateLoginLinkResponse](
73+ httpClient,
74+ baseURL+SessionServiceCreateLoginLinkProcedure,
75+ connect.WithSchema(sessionServiceMethods.ByName("CreateLoginLink")),
76+ connect.WithClientOptions(opts...),
77+ ),
78+ signOutEverywhere: connect.NewClient[ermite.SignOutEverywhereRequest, ermite.SignOutEverywhereResponse](
79+ httpClient,
80+ baseURL+SessionServiceSignOutEverywhereProcedure,
81+ connect.WithSchema(sessionServiceMethods.ByName("SignOutEverywhere")),
82+ connect.WithClientOptions(opts...),
83+ ),
84+ }
85+}
86+
87+// sessionServiceClient implements SessionServiceClient.
88+type sessionServiceClient struct {
89+ createLoginLink *connect.Client[ermite.CreateLoginLinkRequest, ermite.CreateLoginLinkResponse]
90+ signOutEverywhere *connect.Client[ermite.SignOutEverywhereRequest, ermite.SignOutEverywhereResponse]
91+}
92+
93+// CreateLoginLink calls ermite.SessionService.CreateLoginLink.
94+func (c *sessionServiceClient) CreateLoginLink(ctx context.Context, req *connect.Request[ermite.CreateLoginLinkRequest]) (*connect.Response[ermite.CreateLoginLinkResponse], error) {
95+ return c.createLoginLink.CallUnary(ctx, req)
96+}
97+
98+// SignOutEverywhere calls ermite.SessionService.SignOutEverywhere.
99+func (c *sessionServiceClient) SignOutEverywhere(ctx context.Context, req *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error) {
100+ return c.signOutEverywhere.CallUnary(ctx, req)
101+}
102+
103+// SessionServiceHandler is an implementation of the ermite.SessionService service.
104+type SessionServiceHandler interface {
105+ // CreateLoginLink creates a one-time link that signs the owner's browser
106+ // in. The link is valid for a short time (about a minute), can be used
107+ // once, and is kept in memory only: a server restart voids it. Opening it
108+ // shows a confirmation page; only confirming it consumes the link and sets
109+ // the session. Creating a new link does not void an earlier one. Fails with
110+ // resource_exhausted when too many links are pending.
111+ CreateLoginLink(context.Context, *connect.Request[ermite.CreateLoginLinkRequest]) (*connect.Response[ermite.CreateLoginLinkResponse], error)
112+ // SignOutEverywhere ends every browser session at once, including those on
113+ // other devices. Pending login links are not affected. Tokens are not
114+ // affected either: revoke them with TokenService.
115+ SignOutEverywhere(context.Context, *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error)
116+}
117+
118+// NewSessionServiceHandler builds an HTTP handler from the service implementation. It returns the
119+// path on which to mount the handler and the handler itself.
120+//
121+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
122+// and JSON codecs. They also support gzip compression.
123+func NewSessionServiceHandler(svc SessionServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
124+ sessionServiceMethods := ermite.File_ermite_session_proto.Services().ByName("SessionService").Methods()
125+ sessionServiceCreateLoginLinkHandler := connect.NewUnaryHandler(
126+ SessionServiceCreateLoginLinkProcedure,
127+ svc.CreateLoginLink,
128+ connect.WithSchema(sessionServiceMethods.ByName("CreateLoginLink")),
129+ connect.WithHandlerOptions(opts...),
130+ )
131+ sessionServiceSignOutEverywhereHandler := connect.NewUnaryHandler(
132+ SessionServiceSignOutEverywhereProcedure,
133+ svc.SignOutEverywhere,
134+ connect.WithSchema(sessionServiceMethods.ByName("SignOutEverywhere")),
135+ connect.WithHandlerOptions(opts...),
136+ )
137+ return "/ermite.SessionService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
138+ switch r.URL.Path {
139+ case SessionServiceCreateLoginLinkProcedure:
140+ sessionServiceCreateLoginLinkHandler.ServeHTTP(w, r)
141+ case SessionServiceSignOutEverywhereProcedure:
142+ sessionServiceSignOutEverywhereHandler.ServeHTTP(w, r)
143+ default:
144+ http.NotFound(w, r)
145+ }
146+ })
147+}
148+
149+// UnimplementedSessionServiceHandler returns CodeUnimplemented from all methods.
150+type UnimplementedSessionServiceHandler struct{}
151+
152+func (UnimplementedSessionServiceHandler) CreateLoginLink(context.Context, *connect.Request[ermite.CreateLoginLinkRequest]) (*connect.Response[ermite.CreateLoginLinkResponse], error) {
153+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.SessionService.CreateLoginLink is not implemented"))
154+}
155+
156+func (UnimplementedSessionServiceHandler) SignOutEverywhere(context.Context, *connect.Request[ermite.SignOutEverywhereRequest]) (*connect.Response[ermite.SignOutEverywhereResponse], error) {
157+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.SessionService.SignOutEverywhere is not implemented"))
158+}
M gen/ermite/event.pb.go +331 -25 generated file: collapsed
A gen/ermite/merge_request.pb.go +1716 -0 generated file: collapsed
M gen/ermite/repo.pb.go +179 -81
@@ -80,6 +80,68 @@ func (Visibility) EnumDescriptor() ([]byte, []int) {
8080 return file_ermite_repo_proto_rawDescGZIP(), []int{0}
8181 }
8282
83+// RepoSettings are the settings of a repository that govern its merge
84+// requests.
85+type RepoSettings struct {
86+ state protoimpl.MessageState `protogen:"open.v1"`
87+ // AllowedMergeModes are the modes with which a merge request of the
88+ // repository may be merged. It is never empty in a response and has no
89+ // duplicate. Both MERGE_MODE_FAST_FORWARD and MERGE_MODE_SQUASH are allowed
90+ // by default. MERGE_MODE_UNSPECIFIED is rejected.
91+ AllowedMergeModes []MergeMode `protobuf:"varint,1,rep,packed,name=allowed_merge_modes,json=allowedMergeModes,proto3,enum=ermite.MergeMode" json:"allowed_merge_modes,omitempty"`
92+ // FailingChecksBlock says whether a merge request whose checks fail cannot be
93+ // merged. It is true at creation of the repository and always stored
94+ // explicitly, so a later change of the server default never alters an
95+ // existing repository.
96+ FailingChecksBlock bool `protobuf:"varint,2,opt,name=failing_checks_block,json=failingChecksBlock,proto3" json:"failing_checks_block,omitempty"`
97+ unknownFields protoimpl.UnknownFields
98+ sizeCache protoimpl.SizeCache
99+}
100+
101+func (x *RepoSettings) Reset() {
102+ *x = RepoSettings{}
103+ mi := &file_ermite_repo_proto_msgTypes[0]
104+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
105+ ms.StoreMessageInfo(mi)
106+}
107+
108+func (x *RepoSettings) String() string {
109+ return protoimpl.X.MessageStringOf(x)
110+}
111+
112+func (*RepoSettings) ProtoMessage() {}
113+
114+func (x *RepoSettings) ProtoReflect() protoreflect.Message {
115+ mi := &file_ermite_repo_proto_msgTypes[0]
116+ if x != nil {
117+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
118+ if ms.LoadMessageInfo() == nil {
119+ ms.StoreMessageInfo(mi)
120+ }
121+ return ms
122+ }
123+ return mi.MessageOf(x)
124+}
125+
126+// Deprecated: Use RepoSettings.ProtoReflect.Descriptor instead.
127+func (*RepoSettings) Descriptor() ([]byte, []int) {
128+ return file_ermite_repo_proto_rawDescGZIP(), []int{0}
129+}
130+
131+func (x *RepoSettings) GetAllowedMergeModes() []MergeMode {
132+ if x != nil {
133+ return x.AllowedMergeModes
134+ }
135+ return nil
136+}
137+
138+func (x *RepoSettings) GetFailingChecksBlock() bool {
139+ if x != nil {
140+ return x.FailingChecksBlock
141+ }
142+ return false
143+}
144+
83145 // Repo is the metadata of a repository. Git-owned state (refs, trees, history)
84146 // is read through GitService.
85147 type Repo struct {
@@ -98,14 +160,17 @@ type Repo struct {
98160 // CreatedAt is when the repository was created.
99161 CreatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
100162 // PushedAt is the time of the last push. It is unset until the first push.
101- PushedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=pushed_at,json=pushedAt,proto3" json:"pushed_at,omitempty"`
163+ PushedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=pushed_at,json=pushedAt,proto3" json:"pushed_at,omitempty"`
164+ // Settings are the merge settings of the repository. Always set in a
165+ // response, with the defaults for a repository that never changed them.
166+ Settings *RepoSettings `protobuf:"bytes,7,opt,name=settings,proto3" json:"settings,omitempty"`
102167 unknownFields protoimpl.UnknownFields
103168 sizeCache protoimpl.SizeCache
104169 }
105170
106171 func (x *Repo) Reset() {
107172 *x = Repo{}
108- mi := &file_ermite_repo_proto_msgTypes[0]
173+ mi := &file_ermite_repo_proto_msgTypes[1]
109174 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
110175 ms.StoreMessageInfo(mi)
111176 }
@@ -117,7 +182,7 @@ func (x *Repo) String() string {
117182 func (*Repo) ProtoMessage() {}
118183
119184 func (x *Repo) ProtoReflect() protoreflect.Message {
120- mi := &file_ermite_repo_proto_msgTypes[0]
185+ mi := &file_ermite_repo_proto_msgTypes[1]
121186 if x != nil {
122187 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
123188 if ms.LoadMessageInfo() == nil {
@@ -130,7 +195,7 @@ func (x *Repo) ProtoReflect() protoreflect.Message {
130195
131196 // Deprecated: Use Repo.ProtoReflect.Descriptor instead.
132197 func (*Repo) Descriptor() ([]byte, []int) {
133- return file_ermite_repo_proto_rawDescGZIP(), []int{0}
198+ return file_ermite_repo_proto_rawDescGZIP(), []int{1}
134199 }
135200
136201 func (x *Repo) GetName() string {
@@ -175,6 +240,13 @@ func (x *Repo) GetPushedAt() *timestamppb.Timestamp {
175240 return nil
176241 }
177242
243+func (x *Repo) GetSettings() *RepoSettings {
244+ if x != nil {
245+ return x.Settings
246+ }
247+ return nil
248+}
249+
178250 // ListReposRequest selects a page of repositories.
179251 type ListReposRequest struct {
180252 state protoimpl.MessageState `protogen:"open.v1"`
@@ -190,7 +262,7 @@ type ListReposRequest struct {
190262
191263 func (x *ListReposRequest) Reset() {
192264 *x = ListReposRequest{}
193- mi := &file_ermite_repo_proto_msgTypes[1]
265+ mi := &file_ermite_repo_proto_msgTypes[2]
194266 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
195267 ms.StoreMessageInfo(mi)
196268 }
@@ -202,7 +274,7 @@ func (x *ListReposRequest) String() string {
202274 func (*ListReposRequest) ProtoMessage() {}
203275
204276 func (x *ListReposRequest) ProtoReflect() protoreflect.Message {
205- mi := &file_ermite_repo_proto_msgTypes[1]
277+ mi := &file_ermite_repo_proto_msgTypes[2]
206278 if x != nil {
207279 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
208280 if ms.LoadMessageInfo() == nil {
@@ -215,7 +287,7 @@ func (x *ListReposRequest) ProtoReflect() protoreflect.Message {
215287
216288 // Deprecated: Use ListReposRequest.ProtoReflect.Descriptor instead.
217289 func (*ListReposRequest) Descriptor() ([]byte, []int) {
218- return file_ermite_repo_proto_rawDescGZIP(), []int{1}
290+ return file_ermite_repo_proto_rawDescGZIP(), []int{2}
219291 }
220292
221293 func (x *ListReposRequest) GetPageSize() int32 {
@@ -246,7 +318,7 @@ type ListReposResponse struct {
246318
247319 func (x *ListReposResponse) Reset() {
248320 *x = ListReposResponse{}
249- mi := &file_ermite_repo_proto_msgTypes[2]
321+ mi := &file_ermite_repo_proto_msgTypes[3]
250322 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
251323 ms.StoreMessageInfo(mi)
252324 }
@@ -258,7 +330,7 @@ func (x *ListReposResponse) String() string {
258330 func (*ListReposResponse) ProtoMessage() {}
259331
260332 func (x *ListReposResponse) ProtoReflect() protoreflect.Message {
261- mi := &file_ermite_repo_proto_msgTypes[2]
333+ mi := &file_ermite_repo_proto_msgTypes[3]
262334 if x != nil {
263335 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
264336 if ms.LoadMessageInfo() == nil {
@@ -271,7 +343,7 @@ func (x *ListReposResponse) ProtoReflect() protoreflect.Message {
271343
272344 // Deprecated: Use ListReposResponse.ProtoReflect.Descriptor instead.
273345 func (*ListReposResponse) Descriptor() ([]byte, []int) {
274- return file_ermite_repo_proto_rawDescGZIP(), []int{2}
346+ return file_ermite_repo_proto_rawDescGZIP(), []int{3}
275347 }
276348
277349 func (x *ListReposResponse) GetRepos() []*Repo {
@@ -299,7 +371,7 @@ type GetRepoRequest struct {
299371
300372 func (x *GetRepoRequest) Reset() {
301373 *x = GetRepoRequest{}
302- mi := &file_ermite_repo_proto_msgTypes[3]
374+ mi := &file_ermite_repo_proto_msgTypes[4]
303375 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
304376 ms.StoreMessageInfo(mi)
305377 }
@@ -311,7 +383,7 @@ func (x *GetRepoRequest) String() string {
311383 func (*GetRepoRequest) ProtoMessage() {}
312384
313385 func (x *GetRepoRequest) ProtoReflect() protoreflect.Message {
314- mi := &file_ermite_repo_proto_msgTypes[3]
386+ mi := &file_ermite_repo_proto_msgTypes[4]
315387 if x != nil {
316388 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
317389 if ms.LoadMessageInfo() == nil {
@@ -324,7 +396,7 @@ func (x *GetRepoRequest) ProtoReflect() protoreflect.Message {
324396
325397 // Deprecated: Use GetRepoRequest.ProtoReflect.Descriptor instead.
326398 func (*GetRepoRequest) Descriptor() ([]byte, []int) {
327- return file_ermite_repo_proto_rawDescGZIP(), []int{3}
399+ return file_ermite_repo_proto_rawDescGZIP(), []int{4}
328400 }
329401
330402 func (x *GetRepoRequest) GetRepo() string {
@@ -345,7 +417,7 @@ type GetRepoResponse struct {
345417
346418 func (x *GetRepoResponse) Reset() {
347419 *x = GetRepoResponse{}
348- mi := &file_ermite_repo_proto_msgTypes[4]
420+ mi := &file_ermite_repo_proto_msgTypes[5]
349421 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
350422 ms.StoreMessageInfo(mi)
351423 }
@@ -357,7 +429,7 @@ func (x *GetRepoResponse) String() string {
357429 func (*GetRepoResponse) ProtoMessage() {}
358430
359431 func (x *GetRepoResponse) ProtoReflect() protoreflect.Message {
360- mi := &file_ermite_repo_proto_msgTypes[4]
432+ mi := &file_ermite_repo_proto_msgTypes[5]
361433 if x != nil {
362434 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
363435 if ms.LoadMessageInfo() == nil {
@@ -370,7 +442,7 @@ func (x *GetRepoResponse) ProtoReflect() protoreflect.Message {
370442
371443 // Deprecated: Use GetRepoResponse.ProtoReflect.Descriptor instead.
372444 func (*GetRepoResponse) Descriptor() ([]byte, []int) {
373- return file_ermite_repo_proto_rawDescGZIP(), []int{4}
445+ return file_ermite_repo_proto_rawDescGZIP(), []int{5}
374446 }
375447
376448 func (x *GetRepoResponse) GetRepo() *Repo {
@@ -396,7 +468,7 @@ type CreateRepoRequest struct {
396468
397469 func (x *CreateRepoRequest) Reset() {
398470 *x = CreateRepoRequest{}
399- mi := &file_ermite_repo_proto_msgTypes[5]
471+ mi := &file_ermite_repo_proto_msgTypes[6]
400472 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
401473 ms.StoreMessageInfo(mi)
402474 }
@@ -408,7 +480,7 @@ func (x *CreateRepoRequest) String() string {
408480 func (*CreateRepoRequest) ProtoMessage() {}
409481
410482 func (x *CreateRepoRequest) ProtoReflect() protoreflect.Message {
411- mi := &file_ermite_repo_proto_msgTypes[5]
483+ mi := &file_ermite_repo_proto_msgTypes[6]
412484 if x != nil {
413485 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
414486 if ms.LoadMessageInfo() == nil {
@@ -421,7 +493,7 @@ func (x *CreateRepoRequest) ProtoReflect() protoreflect.Message {
421493
422494 // Deprecated: Use CreateRepoRequest.ProtoReflect.Descriptor instead.
423495 func (*CreateRepoRequest) Descriptor() ([]byte, []int) {
424- return file_ermite_repo_proto_rawDescGZIP(), []int{5}
496+ return file_ermite_repo_proto_rawDescGZIP(), []int{6}
425497 }
426498
427499 func (x *CreateRepoRequest) GetName() string {
@@ -456,7 +528,7 @@ type CreateRepoResponse struct {
456528
457529 func (x *CreateRepoResponse) Reset() {
458530 *x = CreateRepoResponse{}
459- mi := &file_ermite_repo_proto_msgTypes[6]
531+ mi := &file_ermite_repo_proto_msgTypes[7]
460532 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
461533 ms.StoreMessageInfo(mi)
462534 }
@@ -468,7 +540,7 @@ func (x *CreateRepoResponse) String() string {
468540 func (*CreateRepoResponse) ProtoMessage() {}
469541
470542 func (x *CreateRepoResponse) ProtoReflect() protoreflect.Message {
471- mi := &file_ermite_repo_proto_msgTypes[6]
543+ mi := &file_ermite_repo_proto_msgTypes[7]
472544 if x != nil {
473545 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
474546 if ms.LoadMessageInfo() == nil {
@@ -481,7 +553,7 @@ func (x *CreateRepoResponse) ProtoReflect() protoreflect.Message {
481553
482554 // Deprecated: Use CreateRepoResponse.ProtoReflect.Descriptor instead.
483555 func (*CreateRepoResponse) Descriptor() ([]byte, []int) {
484- return file_ermite_repo_proto_rawDescGZIP(), []int{6}
556+ return file_ermite_repo_proto_rawDescGZIP(), []int{7}
485557 }
486558
487559 func (x *CreateRepoResponse) GetRepo() *Repo {
@@ -507,16 +579,24 @@ type UpdateRepoRequest struct {
507579 // update_mask contains "default_branch". The branch must exist, otherwise
508580 // the call fails with failed_precondition.
509581 DefaultBranch string `protobuf:"bytes,4,opt,name=default_branch,json=defaultBranch,proto3" json:"default_branch,omitempty"`
510- // UpdateMask lists the fields to change: "description", "visibility" and/or
511- // "default_branch". It must not be empty. Other paths are invalid_argument.
512- UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,5,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"`
582+ // UpdateMask lists the fields to change: "description", "visibility",
583+ // "default_branch", "settings.allowed_merge_modes" and/or
584+ // "settings.failing_checks_block". "settings" stands for both settings
585+ // paths. It must not be empty. Other paths are invalid_argument.
586+ UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,5,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"`
587+ // Settings holds the new merge settings. Only the fields named in
588+ // update_mask are used. allowed_merge_modes must then not be empty nor
589+ // contain MERGE_MODE_UNSPECIFIED, otherwise the call fails with
590+ // invalid_argument. Changing settings needs the repo:admin scope, like any
591+ // other field.
592+ Settings *RepoSettings `protobuf:"bytes,6,opt,name=settings,proto3" json:"settings,omitempty"`
513593 unknownFields protoimpl.UnknownFields
514594 sizeCache protoimpl.SizeCache
515595 }
516596
517597 func (x *UpdateRepoRequest) Reset() {
518598 *x = UpdateRepoRequest{}
519- mi := &file_ermite_repo_proto_msgTypes[7]
599+ mi := &file_ermite_repo_proto_msgTypes[8]
520600 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
521601 ms.StoreMessageInfo(mi)
522602 }
@@ -528,7 +608,7 @@ func (x *UpdateRepoRequest) String() string {
528608 func (*UpdateRepoRequest) ProtoMessage() {}
529609
530610 func (x *UpdateRepoRequest) ProtoReflect() protoreflect.Message {
531- mi := &file_ermite_repo_proto_msgTypes[7]
611+ mi := &file_ermite_repo_proto_msgTypes[8]
532612 if x != nil {
533613 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
534614 if ms.LoadMessageInfo() == nil {
@@ -541,7 +621,7 @@ func (x *UpdateRepoRequest) ProtoReflect() protoreflect.Message {
541621
542622 // Deprecated: Use UpdateRepoRequest.ProtoReflect.Descriptor instead.
543623 func (*UpdateRepoRequest) Descriptor() ([]byte, []int) {
544- return file_ermite_repo_proto_rawDescGZIP(), []int{7}
624+ return file_ermite_repo_proto_rawDescGZIP(), []int{8}
545625 }
546626
547627 func (x *UpdateRepoRequest) GetRepo() string {
@@ -579,6 +659,13 @@ func (x *UpdateRepoRequest) GetUpdateMask() *fieldmaskpb.FieldMask {
579659 return nil
580660 }
581661
662+func (x *UpdateRepoRequest) GetSettings() *RepoSettings {
663+ if x != nil {
664+ return x.Settings
665+ }
666+ return nil
667+}
668+
582669 // UpdateRepoResponse holds the repository after the update.
583670 type UpdateRepoResponse struct {
584671 state protoimpl.MessageState `protogen:"open.v1"`
@@ -590,7 +677,7 @@ type UpdateRepoResponse struct {
590677
591678 func (x *UpdateRepoResponse) Reset() {
592679 *x = UpdateRepoResponse{}
593- mi := &file_ermite_repo_proto_msgTypes[8]
680+ mi := &file_ermite_repo_proto_msgTypes[9]
594681 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
595682 ms.StoreMessageInfo(mi)
596683 }
@@ -602,7 +689,7 @@ func (x *UpdateRepoResponse) String() string {
602689 func (*UpdateRepoResponse) ProtoMessage() {}
603690
604691 func (x *UpdateRepoResponse) ProtoReflect() protoreflect.Message {
605- mi := &file_ermite_repo_proto_msgTypes[8]
692+ mi := &file_ermite_repo_proto_msgTypes[9]
606693 if x != nil {
607694 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
608695 if ms.LoadMessageInfo() == nil {
@@ -615,7 +702,7 @@ func (x *UpdateRepoResponse) ProtoReflect() protoreflect.Message {
615702
616703 // Deprecated: Use UpdateRepoResponse.ProtoReflect.Descriptor instead.
617704 func (*UpdateRepoResponse) Descriptor() ([]byte, []int) {
618- return file_ermite_repo_proto_rawDescGZIP(), []int{8}
705+ return file_ermite_repo_proto_rawDescGZIP(), []int{9}
619706 }
620707
621708 func (x *UpdateRepoResponse) GetRepo() *Repo {
@@ -636,7 +723,7 @@ type DeleteRepoRequest struct {
636723
637724 func (x *DeleteRepoRequest) Reset() {
638725 *x = DeleteRepoRequest{}
639- mi := &file_ermite_repo_proto_msgTypes[9]
726+ mi := &file_ermite_repo_proto_msgTypes[10]
640727 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
641728 ms.StoreMessageInfo(mi)
642729 }
@@ -648,7 +735,7 @@ func (x *DeleteRepoRequest) String() string {
648735 func (*DeleteRepoRequest) ProtoMessage() {}
649736
650737 func (x *DeleteRepoRequest) ProtoReflect() protoreflect.Message {
651- mi := &file_ermite_repo_proto_msgTypes[9]
738+ mi := &file_ermite_repo_proto_msgTypes[10]
652739 if x != nil {
653740 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
654741 if ms.LoadMessageInfo() == nil {
@@ -661,7 +748,7 @@ func (x *DeleteRepoRequest) ProtoReflect() protoreflect.Message {
661748
662749 // Deprecated: Use DeleteRepoRequest.ProtoReflect.Descriptor instead.
663750 func (*DeleteRepoRequest) Descriptor() ([]byte, []int) {
664- return file_ermite_repo_proto_rawDescGZIP(), []int{9}
751+ return file_ermite_repo_proto_rawDescGZIP(), []int{10}
665752 }
666753
667754 func (x *DeleteRepoRequest) GetRepo() string {
@@ -680,7 +767,7 @@ type DeleteRepoResponse struct {
680767
681768 func (x *DeleteRepoResponse) Reset() {
682769 *x = DeleteRepoResponse{}
683- mi := &file_ermite_repo_proto_msgTypes[10]
770+ mi := &file_ermite_repo_proto_msgTypes[11]
684771 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
685772 ms.StoreMessageInfo(mi)
686773 }
@@ -692,7 +779,7 @@ func (x *DeleteRepoResponse) String() string {
692779 func (*DeleteRepoResponse) ProtoMessage() {}
693780
694781 func (x *DeleteRepoResponse) ProtoReflect() protoreflect.Message {
695- mi := &file_ermite_repo_proto_msgTypes[10]
782+ mi := &file_ermite_repo_proto_msgTypes[11]
696783 if x != nil {
697784 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
698785 if ms.LoadMessageInfo() == nil {
@@ -705,14 +792,17 @@ func (x *DeleteRepoResponse) ProtoReflect() protoreflect.Message {
705792
706793 // Deprecated: Use DeleteRepoResponse.ProtoReflect.Descriptor instead.
707794 func (*DeleteRepoResponse) Descriptor() ([]byte, []int) {
708- return file_ermite_repo_proto_rawDescGZIP(), []int{10}
795+ return file_ermite_repo_proto_rawDescGZIP(), []int{11}
709796 }
710797
711798 var File_ermite_repo_proto protoreflect.FileDescriptor
712799
713800 const file_ermite_repo_proto_rawDesc = "" +
714801 "\n" +
715- "\x11ermite/repo.proto\x12\x06ermite\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x8b\x02\n" +
802+ "\x11ermite/repo.proto\x12\x06ermite\x1a google/protobuf/field_mask.proto\x1a\x12ermite/types.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x83\x01\n" +
803+ "\fRepoSettings\x12A\n" +
804+ "\x13allowed_merge_modes\x18\x01 \x03(\x0e2\x11.ermite.MergeModeR\x11allowedMergeModes\x120\n" +
805+ "\x14failing_checks_block\x18\x02 \x01(\bR\x12failingChecksBlock\"\xbd\x02\n" +
716806 "\x04Repo\x12\x12\n" +
717807 "\x04name\x18\x01 \x01(\tR\x04name\x12 \n" +
718808 "\vdescription\x18\x02 \x01(\tR\vdescription\x122\n" +
@@ -722,7 +812,8 @@ const file_ermite_repo_proto_rawDesc = "" +
722812 "\x0edefault_branch\x18\x04 \x01(\tR\rdefaultBranch\x129\n" +
723813 "\n" +
724814 "created_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x127\n" +
725- "\tpushed_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\bpushedAt\"N\n" +
815+ "\tpushed_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\bpushedAt\x120\n" +
816+ "\bsettings\x18\a \x01(\v2\x14.ermite.RepoSettingsR\bsettings\"N\n" +
726817 "\x10ListReposRequest\x12\x1b\n" +
727818 "\tpage_size\x18\x01 \x01(\x05R\bpageSize\x12\x1d\n" +
728819 "\n" +
@@ -741,7 +832,7 @@ const file_ermite_repo_proto_rawDesc = "" +
741832 "visibility\x18\x03 \x01(\x0e2\x12.ermite.VisibilityR\n" +
742833 "visibility\"6\n" +
743834 "\x12CreateRepoResponse\x12 \n" +
744- "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"\xe1\x01\n" +
835+ "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"\x93\x02\n" +
745836 "\x11UpdateRepoRequest\x12\x12\n" +
746837 "\x04repo\x18\x01 \x01(\tR\x04repo\x12 \n" +
747838 "\vdescription\x18\x02 \x01(\tR\vdescription\x122\n" +
@@ -750,7 +841,8 @@ const file_ermite_repo_proto_rawDesc = "" +
750841 "visibility\x12%\n" +
751842 "\x0edefault_branch\x18\x04 \x01(\tR\rdefaultBranch\x12;\n" +
752843 "\vupdate_mask\x18\x05 \x01(\v2\x1a.google.protobuf.FieldMaskR\n" +
753- "updateMask\"6\n" +
844+ "updateMask\x120\n" +
845+ "\bsettings\x18\x06 \x01(\v2\x14.ermite.RepoSettingsR\bsettings\"6\n" +
754846 "\x12UpdateRepoResponse\x12 \n" +
755847 "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"'\n" +
756848 "\x11DeleteRepoRequest\x12\x12\n" +
@@ -786,49 +878,54 @@ func file_ermite_repo_proto_rawDescGZIP() []byte {
786878 }
787879
788880 var file_ermite_repo_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
789-var file_ermite_repo_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
881+var file_ermite_repo_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
790882 var file_ermite_repo_proto_goTypes = []any{
791883 (Visibility)(0), // 0: ermite.Visibility
792- (*Repo)(nil), // 1: ermite.Repo
793- (*ListReposRequest)(nil), // 2: ermite.ListReposRequest
794- (*ListReposResponse)(nil), // 3: ermite.ListReposResponse
795- (*GetRepoRequest)(nil), // 4: ermite.GetRepoRequest
796- (*GetRepoResponse)(nil), // 5: ermite.GetRepoResponse
797- (*CreateRepoRequest)(nil), // 6: ermite.CreateRepoRequest
798- (*CreateRepoResponse)(nil), // 7: ermite.CreateRepoResponse
799- (*UpdateRepoRequest)(nil), // 8: ermite.UpdateRepoRequest
800- (*UpdateRepoResponse)(nil), // 9: ermite.UpdateRepoResponse
801- (*DeleteRepoRequest)(nil), // 10: ermite.DeleteRepoRequest
802- (*DeleteRepoResponse)(nil), // 11: ermite.DeleteRepoResponse
803- (*timestamppb.Timestamp)(nil), // 12: google.protobuf.Timestamp
804- (*fieldmaskpb.FieldMask)(nil), // 13: google.protobuf.FieldMask
884+ (*RepoSettings)(nil), // 1: ermite.RepoSettings
885+ (*Repo)(nil), // 2: ermite.Repo
886+ (*ListReposRequest)(nil), // 3: ermite.ListReposRequest
887+ (*ListReposResponse)(nil), // 4: ermite.ListReposResponse
888+ (*GetRepoRequest)(nil), // 5: ermite.GetRepoRequest
889+ (*GetRepoResponse)(nil), // 6: ermite.GetRepoResponse
890+ (*CreateRepoRequest)(nil), // 7: ermite.CreateRepoRequest
891+ (*CreateRepoResponse)(nil), // 8: ermite.CreateRepoResponse
892+ (*UpdateRepoRequest)(nil), // 9: ermite.UpdateRepoRequest
893+ (*UpdateRepoResponse)(nil), // 10: ermite.UpdateRepoResponse
894+ (*DeleteRepoRequest)(nil), // 11: ermite.DeleteRepoRequest
895+ (*DeleteRepoResponse)(nil), // 12: ermite.DeleteRepoResponse
896+ (MergeMode)(0), // 13: ermite.MergeMode
897+ (*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp
898+ (*fieldmaskpb.FieldMask)(nil), // 15: google.protobuf.FieldMask
805899 }
806900 var file_ermite_repo_proto_depIdxs = []int32{
807- 0, // 0: ermite.Repo.visibility:type_name -> ermite.Visibility
808- 12, // 1: ermite.Repo.created_at:type_name -> google.protobuf.Timestamp
809- 12, // 2: ermite.Repo.pushed_at:type_name -> google.protobuf.Timestamp
810- 1, // 3: ermite.ListReposResponse.repos:type_name -> ermite.Repo
811- 1, // 4: ermite.GetRepoResponse.repo:type_name -> ermite.Repo
812- 0, // 5: ermite.CreateRepoRequest.visibility:type_name -> ermite.Visibility
813- 1, // 6: ermite.CreateRepoResponse.repo:type_name -> ermite.Repo
814- 0, // 7: ermite.UpdateRepoRequest.visibility:type_name -> ermite.Visibility
815- 13, // 8: ermite.UpdateRepoRequest.update_mask:type_name -> google.protobuf.FieldMask
816- 1, // 9: ermite.UpdateRepoResponse.repo:type_name -> ermite.Repo
817- 2, // 10: ermite.RepoService.ListRepos:input_type -> ermite.ListReposRequest
818- 4, // 11: ermite.RepoService.GetRepo:input_type -> ermite.GetRepoRequest
819- 6, // 12: ermite.RepoService.CreateRepo:input_type -> ermite.CreateRepoRequest
820- 8, // 13: ermite.RepoService.UpdateRepo:input_type -> ermite.UpdateRepoRequest
821- 10, // 14: ermite.RepoService.DeleteRepo:input_type -> ermite.DeleteRepoRequest
822- 3, // 15: ermite.RepoService.ListRepos:output_type -> ermite.ListReposResponse
823- 5, // 16: ermite.RepoService.GetRepo:output_type -> ermite.GetRepoResponse
824- 7, // 17: ermite.RepoService.CreateRepo:output_type -> ermite.CreateRepoResponse
825- 9, // 18: ermite.RepoService.UpdateRepo:output_type -> ermite.UpdateRepoResponse
826- 11, // 19: ermite.RepoService.DeleteRepo:output_type -> ermite.DeleteRepoResponse
827- 15, // [15:20] is the sub-list for method output_type
828- 10, // [10:15] is the sub-list for method input_type
829- 10, // [10:10] is the sub-list for extension type_name
830- 10, // [10:10] is the sub-list for extension extendee
831- 0, // [0:10] is the sub-list for field type_name
901+ 13, // 0: ermite.RepoSettings.allowed_merge_modes:type_name -> ermite.MergeMode
902+ 0, // 1: ermite.Repo.visibility:type_name -> ermite.Visibility
903+ 14, // 2: ermite.Repo.created_at:type_name -> google.protobuf.Timestamp
904+ 14, // 3: ermite.Repo.pushed_at:type_name -> google.protobuf.Timestamp
905+ 1, // 4: ermite.Repo.settings:type_name -> ermite.RepoSettings
906+ 2, // 5: ermite.ListReposResponse.repos:type_name -> ermite.Repo
907+ 2, // 6: ermite.GetRepoResponse.repo:type_name -> ermite.Repo
908+ 0, // 7: ermite.CreateRepoRequest.visibility:type_name -> ermite.Visibility
909+ 2, // 8: ermite.CreateRepoResponse.repo:type_name -> ermite.Repo
910+ 0, // 9: ermite.UpdateRepoRequest.visibility:type_name -> ermite.Visibility
911+ 15, // 10: ermite.UpdateRepoRequest.update_mask:type_name -> google.protobuf.FieldMask
912+ 1, // 11: ermite.UpdateRepoRequest.settings:type_name -> ermite.RepoSettings
913+ 2, // 12: ermite.UpdateRepoResponse.repo:type_name -> ermite.Repo
914+ 3, // 13: ermite.RepoService.ListRepos:input_type -> ermite.ListReposRequest
915+ 5, // 14: ermite.RepoService.GetRepo:input_type -> ermite.GetRepoRequest
916+ 7, // 15: ermite.RepoService.CreateRepo:input_type -> ermite.CreateRepoRequest
917+ 9, // 16: ermite.RepoService.UpdateRepo:input_type -> ermite.UpdateRepoRequest
918+ 11, // 17: ermite.RepoService.DeleteRepo:input_type -> ermite.DeleteRepoRequest
919+ 4, // 18: ermite.RepoService.ListRepos:output_type -> ermite.ListReposResponse
920+ 6, // 19: ermite.RepoService.GetRepo:output_type -> ermite.GetRepoResponse
921+ 8, // 20: ermite.RepoService.CreateRepo:output_type -> ermite.CreateRepoResponse
922+ 10, // 21: ermite.RepoService.UpdateRepo:output_type -> ermite.UpdateRepoResponse
923+ 12, // 22: ermite.RepoService.DeleteRepo:output_type -> ermite.DeleteRepoResponse
924+ 18, // [18:23] is the sub-list for method output_type
925+ 13, // [13:18] is the sub-list for method input_type
926+ 13, // [13:13] is the sub-list for extension type_name
927+ 13, // [13:13] is the sub-list for extension extendee
928+ 0, // [0:13] is the sub-list for field type_name
832929 }
833930
834931 func init() { file_ermite_repo_proto_init() }
@@ -836,13 +933,14 @@ func file_ermite_repo_proto_init() {
836933 if File_ermite_repo_proto != nil {
837934 return
838935 }
936+ file_ermite_types_proto_init()
839937 type x struct{}
840938 out := protoimpl.TypeBuilder{
841939 File: protoimpl.DescBuilder{
842940 GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
843941 RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_repo_proto_rawDesc), len(file_ermite_repo_proto_rawDesc)),
844942 NumEnums: 1,
845- NumMessages: 11,
943+ NumMessages: 12,
846944 NumExtensions: 0,
847945 NumServices: 1,
848946 },
@@ -80,6 +80,68 @@ func (Visibility) EnumDescriptor() ([]byte, []int) {
80 return file_ermite_repo_proto_rawDescGZIP(), []int{0}80 return file_ermite_repo_proto_rawDescGZIP(), []int{0}
81 }81 }
82 82
83+// RepoSettings are the settings of a repository that govern its merge
84+// requests.
85+type RepoSettings struct {
86+ state protoimpl.MessageState `protogen:"open.v1"`
87+ // AllowedMergeModes are the modes with which a merge request of the
88+ // repository may be merged. It is never empty in a response and has no
89+ // duplicate. Both MERGE_MODE_FAST_FORWARD and MERGE_MODE_SQUASH are allowed
90+ // by default. MERGE_MODE_UNSPECIFIED is rejected.
91+ AllowedMergeModes []MergeMode `protobuf:"varint,1,rep,packed,name=allowed_merge_modes,json=allowedMergeModes,proto3,enum=ermite.MergeMode" json:"allowed_merge_modes,omitempty"`
92+ // FailingChecksBlock says whether a merge request whose checks fail cannot be
93+ // merged. It is true at creation of the repository and always stored
94+ // explicitly, so a later change of the server default never alters an
95+ // existing repository.
96+ FailingChecksBlock bool `protobuf:"varint,2,opt,name=failing_checks_block,json=failingChecksBlock,proto3" json:"failing_checks_block,omitempty"`
97+ unknownFields protoimpl.UnknownFields
98+ sizeCache protoimpl.SizeCache
99+}
100+
101+func (x *RepoSettings) Reset() {
102+ *x = RepoSettings{}
103+ mi := &file_ermite_repo_proto_msgTypes[0]
104+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
105+ ms.StoreMessageInfo(mi)
106+}
107+
108+func (x *RepoSettings) String() string {
109+ return protoimpl.X.MessageStringOf(x)
110+}
111+
112+func (*RepoSettings) ProtoMessage() {}
113+
114+func (x *RepoSettings) ProtoReflect() protoreflect.Message {
115+ mi := &file_ermite_repo_proto_msgTypes[0]
116+ if x != nil {
117+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
118+ if ms.LoadMessageInfo() == nil {
119+ ms.StoreMessageInfo(mi)
120+ }
121+ return ms
122+ }
123+ return mi.MessageOf(x)
124+}
125+
126+// Deprecated: Use RepoSettings.ProtoReflect.Descriptor instead.
127+func (*RepoSettings) Descriptor() ([]byte, []int) {
128+ return file_ermite_repo_proto_rawDescGZIP(), []int{0}
129+}
130+
131+func (x *RepoSettings) GetAllowedMergeModes() []MergeMode {
132+ if x != nil {
133+ return x.AllowedMergeModes
134+ }
135+ return nil
136+}
137+
138+func (x *RepoSettings) GetFailingChecksBlock() bool {
139+ if x != nil {
140+ return x.FailingChecksBlock
141+ }
142+ return false
143+}
144+
83 // Repo is the metadata of a repository. Git-owned state (refs, trees, history)145 // Repo is the metadata of a repository. Git-owned state (refs, trees, history)
84 // is read through GitService.146 // is read through GitService.
85 type Repo struct {147 type Repo struct {
@@ -98,14 +160,17 @@ type Repo struct {
98 // CreatedAt is when the repository was created.160 // CreatedAt is when the repository was created.
99 CreatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`161 CreatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
100 // PushedAt is the time of the last push. It is unset until the first push.162 // PushedAt is the time of the last push. It is unset until the first push.
101- PushedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=pushed_at,json=pushedAt,proto3" json:"pushed_at,omitempty"`163+ PushedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=pushed_at,json=pushedAt,proto3" json:"pushed_at,omitempty"`
164+ // Settings are the merge settings of the repository. Always set in a
165+ // response, with the defaults for a repository that never changed them.
166+ Settings *RepoSettings `protobuf:"bytes,7,opt,name=settings,proto3" json:"settings,omitempty"`
102 unknownFields protoimpl.UnknownFields167 unknownFields protoimpl.UnknownFields
103 sizeCache protoimpl.SizeCache168 sizeCache protoimpl.SizeCache
104 }169 }
105 170
106 func (x *Repo) Reset() {171 func (x *Repo) Reset() {
107 *x = Repo{}172 *x = Repo{}
108- mi := &file_ermite_repo_proto_msgTypes[0]173+ mi := &file_ermite_repo_proto_msgTypes[1]
109 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))174 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
110 ms.StoreMessageInfo(mi)175 ms.StoreMessageInfo(mi)
111 }176 }
@@ -117,7 +182,7 @@ func (x *Repo) String() string {
117 func (*Repo) ProtoMessage() {}182 func (*Repo) ProtoMessage() {}
118 183
119 func (x *Repo) ProtoReflect() protoreflect.Message {184 func (x *Repo) ProtoReflect() protoreflect.Message {
120- mi := &file_ermite_repo_proto_msgTypes[0]185+ mi := &file_ermite_repo_proto_msgTypes[1]
121 if x != nil {186 if x != nil {
122 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))187 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
123 if ms.LoadMessageInfo() == nil {188 if ms.LoadMessageInfo() == nil {
@@ -130,7 +195,7 @@ func (x *Repo) ProtoReflect() protoreflect.Message {
130 195
131 // Deprecated: Use Repo.ProtoReflect.Descriptor instead.196 // Deprecated: Use Repo.ProtoReflect.Descriptor instead.
132 func (*Repo) Descriptor() ([]byte, []int) {197 func (*Repo) Descriptor() ([]byte, []int) {
133- return file_ermite_repo_proto_rawDescGZIP(), []int{0}198+ return file_ermite_repo_proto_rawDescGZIP(), []int{1}
134 }199 }
135 200
136 func (x *Repo) GetName() string {201 func (x *Repo) GetName() string {
@@ -175,6 +240,13 @@ func (x *Repo) GetPushedAt() *timestamppb.Timestamp {
175 return nil240 return nil
176 }241 }
177 242
243+func (x *Repo) GetSettings() *RepoSettings {
244+ if x != nil {
245+ return x.Settings
246+ }
247+ return nil
248+}
249+
178 // ListReposRequest selects a page of repositories.250 // ListReposRequest selects a page of repositories.
179 type ListReposRequest struct {251 type ListReposRequest struct {
180 state protoimpl.MessageState `protogen:"open.v1"`252 state protoimpl.MessageState `protogen:"open.v1"`
@@ -190,7 +262,7 @@ type ListReposRequest struct {
190 262
191 func (x *ListReposRequest) Reset() {263 func (x *ListReposRequest) Reset() {
192 *x = ListReposRequest{}264 *x = ListReposRequest{}
193- mi := &file_ermite_repo_proto_msgTypes[1]265+ mi := &file_ermite_repo_proto_msgTypes[2]
194 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))266 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
195 ms.StoreMessageInfo(mi)267 ms.StoreMessageInfo(mi)
196 }268 }
@@ -202,7 +274,7 @@ func (x *ListReposRequest) String() string {
202 func (*ListReposRequest) ProtoMessage() {}274 func (*ListReposRequest) ProtoMessage() {}
203 275
204 func (x *ListReposRequest) ProtoReflect() protoreflect.Message {276 func (x *ListReposRequest) ProtoReflect() protoreflect.Message {
205- mi := &file_ermite_repo_proto_msgTypes[1]277+ mi := &file_ermite_repo_proto_msgTypes[2]
206 if x != nil {278 if x != nil {
207 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))279 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
208 if ms.LoadMessageInfo() == nil {280 if ms.LoadMessageInfo() == nil {
@@ -215,7 +287,7 @@ func (x *ListReposRequest) ProtoReflect() protoreflect.Message {
215 287
216 // Deprecated: Use ListReposRequest.ProtoReflect.Descriptor instead.288 // Deprecated: Use ListReposRequest.ProtoReflect.Descriptor instead.
217 func (*ListReposRequest) Descriptor() ([]byte, []int) {289 func (*ListReposRequest) Descriptor() ([]byte, []int) {
218- return file_ermite_repo_proto_rawDescGZIP(), []int{1}290+ return file_ermite_repo_proto_rawDescGZIP(), []int{2}
219 }291 }
220 292
221 func (x *ListReposRequest) GetPageSize() int32 {293 func (x *ListReposRequest) GetPageSize() int32 {
@@ -246,7 +318,7 @@ type ListReposResponse struct {
246 318
247 func (x *ListReposResponse) Reset() {319 func (x *ListReposResponse) Reset() {
248 *x = ListReposResponse{}320 *x = ListReposResponse{}
249- mi := &file_ermite_repo_proto_msgTypes[2]321+ mi := &file_ermite_repo_proto_msgTypes[3]
250 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))322 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
251 ms.StoreMessageInfo(mi)323 ms.StoreMessageInfo(mi)
252 }324 }
@@ -258,7 +330,7 @@ func (x *ListReposResponse) String() string {
258 func (*ListReposResponse) ProtoMessage() {}330 func (*ListReposResponse) ProtoMessage() {}
259 331
260 func (x *ListReposResponse) ProtoReflect() protoreflect.Message {332 func (x *ListReposResponse) ProtoReflect() protoreflect.Message {
261- mi := &file_ermite_repo_proto_msgTypes[2]333+ mi := &file_ermite_repo_proto_msgTypes[3]
262 if x != nil {334 if x != nil {
263 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))335 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
264 if ms.LoadMessageInfo() == nil {336 if ms.LoadMessageInfo() == nil {
@@ -271,7 +343,7 @@ func (x *ListReposResponse) ProtoReflect() protoreflect.Message {
271 343
272 // Deprecated: Use ListReposResponse.ProtoReflect.Descriptor instead.344 // Deprecated: Use ListReposResponse.ProtoReflect.Descriptor instead.
273 func (*ListReposResponse) Descriptor() ([]byte, []int) {345 func (*ListReposResponse) Descriptor() ([]byte, []int) {
274- return file_ermite_repo_proto_rawDescGZIP(), []int{2}346+ return file_ermite_repo_proto_rawDescGZIP(), []int{3}
275 }347 }
276 348
277 func (x *ListReposResponse) GetRepos() []*Repo {349 func (x *ListReposResponse) GetRepos() []*Repo {
@@ -299,7 +371,7 @@ type GetRepoRequest struct {
299 371
300 func (x *GetRepoRequest) Reset() {372 func (x *GetRepoRequest) Reset() {
301 *x = GetRepoRequest{}373 *x = GetRepoRequest{}
302- mi := &file_ermite_repo_proto_msgTypes[3]374+ mi := &file_ermite_repo_proto_msgTypes[4]
303 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))375 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
304 ms.StoreMessageInfo(mi)376 ms.StoreMessageInfo(mi)
305 }377 }
@@ -311,7 +383,7 @@ func (x *GetRepoRequest) String() string {
311 func (*GetRepoRequest) ProtoMessage() {}383 func (*GetRepoRequest) ProtoMessage() {}
312 384
313 func (x *GetRepoRequest) ProtoReflect() protoreflect.Message {385 func (x *GetRepoRequest) ProtoReflect() protoreflect.Message {
314- mi := &file_ermite_repo_proto_msgTypes[3]386+ mi := &file_ermite_repo_proto_msgTypes[4]
315 if x != nil {387 if x != nil {
316 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))388 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
317 if ms.LoadMessageInfo() == nil {389 if ms.LoadMessageInfo() == nil {
@@ -324,7 +396,7 @@ func (x *GetRepoRequest) ProtoReflect() protoreflect.Message {
324 396
325 // Deprecated: Use GetRepoRequest.ProtoReflect.Descriptor instead.397 // Deprecated: Use GetRepoRequest.ProtoReflect.Descriptor instead.
326 func (*GetRepoRequest) Descriptor() ([]byte, []int) {398 func (*GetRepoRequest) Descriptor() ([]byte, []int) {
327- return file_ermite_repo_proto_rawDescGZIP(), []int{3}399+ return file_ermite_repo_proto_rawDescGZIP(), []int{4}
328 }400 }
329 401
330 func (x *GetRepoRequest) GetRepo() string {402 func (x *GetRepoRequest) GetRepo() string {
@@ -345,7 +417,7 @@ type GetRepoResponse struct {
345 417
346 func (x *GetRepoResponse) Reset() {418 func (x *GetRepoResponse) Reset() {
347 *x = GetRepoResponse{}419 *x = GetRepoResponse{}
348- mi := &file_ermite_repo_proto_msgTypes[4]420+ mi := &file_ermite_repo_proto_msgTypes[5]
349 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))421 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
350 ms.StoreMessageInfo(mi)422 ms.StoreMessageInfo(mi)
351 }423 }
@@ -357,7 +429,7 @@ func (x *GetRepoResponse) String() string {
357 func (*GetRepoResponse) ProtoMessage() {}429 func (*GetRepoResponse) ProtoMessage() {}
358 430
359 func (x *GetRepoResponse) ProtoReflect() protoreflect.Message {431 func (x *GetRepoResponse) ProtoReflect() protoreflect.Message {
360- mi := &file_ermite_repo_proto_msgTypes[4]432+ mi := &file_ermite_repo_proto_msgTypes[5]
361 if x != nil {433 if x != nil {
362 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))434 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
363 if ms.LoadMessageInfo() == nil {435 if ms.LoadMessageInfo() == nil {
@@ -370,7 +442,7 @@ func (x *GetRepoResponse) ProtoReflect() protoreflect.Message {
370 442
371 // Deprecated: Use GetRepoResponse.ProtoReflect.Descriptor instead.443 // Deprecated: Use GetRepoResponse.ProtoReflect.Descriptor instead.
372 func (*GetRepoResponse) Descriptor() ([]byte, []int) {444 func (*GetRepoResponse) Descriptor() ([]byte, []int) {
373- return file_ermite_repo_proto_rawDescGZIP(), []int{4}445+ return file_ermite_repo_proto_rawDescGZIP(), []int{5}
374 }446 }
375 447
376 func (x *GetRepoResponse) GetRepo() *Repo {448 func (x *GetRepoResponse) GetRepo() *Repo {
@@ -396,7 +468,7 @@ type CreateRepoRequest struct {
396 468
397 func (x *CreateRepoRequest) Reset() {469 func (x *CreateRepoRequest) Reset() {
398 *x = CreateRepoRequest{}470 *x = CreateRepoRequest{}
399- mi := &file_ermite_repo_proto_msgTypes[5]471+ mi := &file_ermite_repo_proto_msgTypes[6]
400 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))472 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
401 ms.StoreMessageInfo(mi)473 ms.StoreMessageInfo(mi)
402 }474 }
@@ -408,7 +480,7 @@ func (x *CreateRepoRequest) String() string {
408 func (*CreateRepoRequest) ProtoMessage() {}480 func (*CreateRepoRequest) ProtoMessage() {}
409 481
410 func (x *CreateRepoRequest) ProtoReflect() protoreflect.Message {482 func (x *CreateRepoRequest) ProtoReflect() protoreflect.Message {
411- mi := &file_ermite_repo_proto_msgTypes[5]483+ mi := &file_ermite_repo_proto_msgTypes[6]
412 if x != nil {484 if x != nil {
413 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))485 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
414 if ms.LoadMessageInfo() == nil {486 if ms.LoadMessageInfo() == nil {
@@ -421,7 +493,7 @@ func (x *CreateRepoRequest) ProtoReflect() protoreflect.Message {
421 493
422 // Deprecated: Use CreateRepoRequest.ProtoReflect.Descriptor instead.494 // Deprecated: Use CreateRepoRequest.ProtoReflect.Descriptor instead.
423 func (*CreateRepoRequest) Descriptor() ([]byte, []int) {495 func (*CreateRepoRequest) Descriptor() ([]byte, []int) {
424- return file_ermite_repo_proto_rawDescGZIP(), []int{5}496+ return file_ermite_repo_proto_rawDescGZIP(), []int{6}
425 }497 }
426 498
427 func (x *CreateRepoRequest) GetName() string {499 func (x *CreateRepoRequest) GetName() string {
@@ -456,7 +528,7 @@ type CreateRepoResponse struct {
456 528
457 func (x *CreateRepoResponse) Reset() {529 func (x *CreateRepoResponse) Reset() {
458 *x = CreateRepoResponse{}530 *x = CreateRepoResponse{}
459- mi := &file_ermite_repo_proto_msgTypes[6]531+ mi := &file_ermite_repo_proto_msgTypes[7]
460 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))532 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
461 ms.StoreMessageInfo(mi)533 ms.StoreMessageInfo(mi)
462 }534 }
@@ -468,7 +540,7 @@ func (x *CreateRepoResponse) String() string {
468 func (*CreateRepoResponse) ProtoMessage() {}540 func (*CreateRepoResponse) ProtoMessage() {}
469 541
470 func (x *CreateRepoResponse) ProtoReflect() protoreflect.Message {542 func (x *CreateRepoResponse) ProtoReflect() protoreflect.Message {
471- mi := &file_ermite_repo_proto_msgTypes[6]543+ mi := &file_ermite_repo_proto_msgTypes[7]
472 if x != nil {544 if x != nil {
473 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))545 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
474 if ms.LoadMessageInfo() == nil {546 if ms.LoadMessageInfo() == nil {
@@ -481,7 +553,7 @@ func (x *CreateRepoResponse) ProtoReflect() protoreflect.Message {
481 553
482 // Deprecated: Use CreateRepoResponse.ProtoReflect.Descriptor instead.554 // Deprecated: Use CreateRepoResponse.ProtoReflect.Descriptor instead.
483 func (*CreateRepoResponse) Descriptor() ([]byte, []int) {555 func (*CreateRepoResponse) Descriptor() ([]byte, []int) {
484- return file_ermite_repo_proto_rawDescGZIP(), []int{6}556+ return file_ermite_repo_proto_rawDescGZIP(), []int{7}
485 }557 }
486 558
487 func (x *CreateRepoResponse) GetRepo() *Repo {559 func (x *CreateRepoResponse) GetRepo() *Repo {
@@ -507,16 +579,24 @@ type UpdateRepoRequest struct {
507 // update_mask contains "default_branch". The branch must exist, otherwise579 // update_mask contains "default_branch". The branch must exist, otherwise
508 // the call fails with failed_precondition.580 // the call fails with failed_precondition.
509 DefaultBranch string `protobuf:"bytes,4,opt,name=default_branch,json=defaultBranch,proto3" json:"default_branch,omitempty"`581 DefaultBranch string `protobuf:"bytes,4,opt,name=default_branch,json=defaultBranch,proto3" json:"default_branch,omitempty"`
510- // UpdateMask lists the fields to change: "description", "visibility" and/or582+ // UpdateMask lists the fields to change: "description", "visibility",
511- // "default_branch". It must not be empty. Other paths are invalid_argument.583+ // "default_branch", "settings.allowed_merge_modes" and/or
512- UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,5,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"`584+ // "settings.failing_checks_block". "settings" stands for both settings
585+ // paths. It must not be empty. Other paths are invalid_argument.
586+ UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,5,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"`
587+ // Settings holds the new merge settings. Only the fields named in
588+ // update_mask are used. allowed_merge_modes must then not be empty nor
589+ // contain MERGE_MODE_UNSPECIFIED, otherwise the call fails with
590+ // invalid_argument. Changing settings needs the repo:admin scope, like any
591+ // other field.
592+ Settings *RepoSettings `protobuf:"bytes,6,opt,name=settings,proto3" json:"settings,omitempty"`
513 unknownFields protoimpl.UnknownFields593 unknownFields protoimpl.UnknownFields
514 sizeCache protoimpl.SizeCache594 sizeCache protoimpl.SizeCache
515 }595 }
516 596
517 func (x *UpdateRepoRequest) Reset() {597 func (x *UpdateRepoRequest) Reset() {
518 *x = UpdateRepoRequest{}598 *x = UpdateRepoRequest{}
519- mi := &file_ermite_repo_proto_msgTypes[7]599+ mi := &file_ermite_repo_proto_msgTypes[8]
520 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))600 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
521 ms.StoreMessageInfo(mi)601 ms.StoreMessageInfo(mi)
522 }602 }
@@ -528,7 +608,7 @@ func (x *UpdateRepoRequest) String() string {
528 func (*UpdateRepoRequest) ProtoMessage() {}608 func (*UpdateRepoRequest) ProtoMessage() {}
529 609
530 func (x *UpdateRepoRequest) ProtoReflect() protoreflect.Message {610 func (x *UpdateRepoRequest) ProtoReflect() protoreflect.Message {
531- mi := &file_ermite_repo_proto_msgTypes[7]611+ mi := &file_ermite_repo_proto_msgTypes[8]
532 if x != nil {612 if x != nil {
533 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))613 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
534 if ms.LoadMessageInfo() == nil {614 if ms.LoadMessageInfo() == nil {
@@ -541,7 +621,7 @@ func (x *UpdateRepoRequest) ProtoReflect() protoreflect.Message {
541 621
542 // Deprecated: Use UpdateRepoRequest.ProtoReflect.Descriptor instead.622 // Deprecated: Use UpdateRepoRequest.ProtoReflect.Descriptor instead.
543 func (*UpdateRepoRequest) Descriptor() ([]byte, []int) {623 func (*UpdateRepoRequest) Descriptor() ([]byte, []int) {
544- return file_ermite_repo_proto_rawDescGZIP(), []int{7}624+ return file_ermite_repo_proto_rawDescGZIP(), []int{8}
545 }625 }
546 626
547 func (x *UpdateRepoRequest) GetRepo() string {627 func (x *UpdateRepoRequest) GetRepo() string {
@@ -579,6 +659,13 @@ func (x *UpdateRepoRequest) GetUpdateMask() *fieldmaskpb.FieldMask {
579 return nil659 return nil
580 }660 }
581 661
662+func (x *UpdateRepoRequest) GetSettings() *RepoSettings {
663+ if x != nil {
664+ return x.Settings
665+ }
666+ return nil
667+}
668+
582 // UpdateRepoResponse holds the repository after the update.669 // UpdateRepoResponse holds the repository after the update.
583 type UpdateRepoResponse struct {670 type UpdateRepoResponse struct {
584 state protoimpl.MessageState `protogen:"open.v1"`671 state protoimpl.MessageState `protogen:"open.v1"`
@@ -590,7 +677,7 @@ type UpdateRepoResponse struct {
590 677
591 func (x *UpdateRepoResponse) Reset() {678 func (x *UpdateRepoResponse) Reset() {
592 *x = UpdateRepoResponse{}679 *x = UpdateRepoResponse{}
593- mi := &file_ermite_repo_proto_msgTypes[8]680+ mi := &file_ermite_repo_proto_msgTypes[9]
594 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))681 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
595 ms.StoreMessageInfo(mi)682 ms.StoreMessageInfo(mi)
596 }683 }
@@ -602,7 +689,7 @@ func (x *UpdateRepoResponse) String() string {
602 func (*UpdateRepoResponse) ProtoMessage() {}689 func (*UpdateRepoResponse) ProtoMessage() {}
603 690
604 func (x *UpdateRepoResponse) ProtoReflect() protoreflect.Message {691 func (x *UpdateRepoResponse) ProtoReflect() protoreflect.Message {
605- mi := &file_ermite_repo_proto_msgTypes[8]692+ mi := &file_ermite_repo_proto_msgTypes[9]
606 if x != nil {693 if x != nil {
607 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))694 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
608 if ms.LoadMessageInfo() == nil {695 if ms.LoadMessageInfo() == nil {
@@ -615,7 +702,7 @@ func (x *UpdateRepoResponse) ProtoReflect() protoreflect.Message {
615 702
616 // Deprecated: Use UpdateRepoResponse.ProtoReflect.Descriptor instead.703 // Deprecated: Use UpdateRepoResponse.ProtoReflect.Descriptor instead.
617 func (*UpdateRepoResponse) Descriptor() ([]byte, []int) {704 func (*UpdateRepoResponse) Descriptor() ([]byte, []int) {
618- return file_ermite_repo_proto_rawDescGZIP(), []int{8}705+ return file_ermite_repo_proto_rawDescGZIP(), []int{9}
619 }706 }
620 707
621 func (x *UpdateRepoResponse) GetRepo() *Repo {708 func (x *UpdateRepoResponse) GetRepo() *Repo {
@@ -636,7 +723,7 @@ type DeleteRepoRequest struct {
636 723
637 func (x *DeleteRepoRequest) Reset() {724 func (x *DeleteRepoRequest) Reset() {
638 *x = DeleteRepoRequest{}725 *x = DeleteRepoRequest{}
639- mi := &file_ermite_repo_proto_msgTypes[9]726+ mi := &file_ermite_repo_proto_msgTypes[10]
640 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))727 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
641 ms.StoreMessageInfo(mi)728 ms.StoreMessageInfo(mi)
642 }729 }
@@ -648,7 +735,7 @@ func (x *DeleteRepoRequest) String() string {
648 func (*DeleteRepoRequest) ProtoMessage() {}735 func (*DeleteRepoRequest) ProtoMessage() {}
649 736
650 func (x *DeleteRepoRequest) ProtoReflect() protoreflect.Message {737 func (x *DeleteRepoRequest) ProtoReflect() protoreflect.Message {
651- mi := &file_ermite_repo_proto_msgTypes[9]738+ mi := &file_ermite_repo_proto_msgTypes[10]
652 if x != nil {739 if x != nil {
653 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))740 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
654 if ms.LoadMessageInfo() == nil {741 if ms.LoadMessageInfo() == nil {
@@ -661,7 +748,7 @@ func (x *DeleteRepoRequest) ProtoReflect() protoreflect.Message {
661 748
662 // Deprecated: Use DeleteRepoRequest.ProtoReflect.Descriptor instead.749 // Deprecated: Use DeleteRepoRequest.ProtoReflect.Descriptor instead.
663 func (*DeleteRepoRequest) Descriptor() ([]byte, []int) {750 func (*DeleteRepoRequest) Descriptor() ([]byte, []int) {
664- return file_ermite_repo_proto_rawDescGZIP(), []int{9}751+ return file_ermite_repo_proto_rawDescGZIP(), []int{10}
665 }752 }
666 753
667 func (x *DeleteRepoRequest) GetRepo() string {754 func (x *DeleteRepoRequest) GetRepo() string {
@@ -680,7 +767,7 @@ type DeleteRepoResponse struct {
680 767
681 func (x *DeleteRepoResponse) Reset() {768 func (x *DeleteRepoResponse) Reset() {
682 *x = DeleteRepoResponse{}769 *x = DeleteRepoResponse{}
683- mi := &file_ermite_repo_proto_msgTypes[10]770+ mi := &file_ermite_repo_proto_msgTypes[11]
684 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))771 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
685 ms.StoreMessageInfo(mi)772 ms.StoreMessageInfo(mi)
686 }773 }
@@ -692,7 +779,7 @@ func (x *DeleteRepoResponse) String() string {
692 func (*DeleteRepoResponse) ProtoMessage() {}779 func (*DeleteRepoResponse) ProtoMessage() {}
693 780
694 func (x *DeleteRepoResponse) ProtoReflect() protoreflect.Message {781 func (x *DeleteRepoResponse) ProtoReflect() protoreflect.Message {
695- mi := &file_ermite_repo_proto_msgTypes[10]782+ mi := &file_ermite_repo_proto_msgTypes[11]
696 if x != nil {783 if x != nil {
697 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))784 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
698 if ms.LoadMessageInfo() == nil {785 if ms.LoadMessageInfo() == nil {
@@ -705,14 +792,17 @@ func (x *DeleteRepoResponse) ProtoReflect() protoreflect.Message {
705 792
706 // Deprecated: Use DeleteRepoResponse.ProtoReflect.Descriptor instead.793 // Deprecated: Use DeleteRepoResponse.ProtoReflect.Descriptor instead.
707 func (*DeleteRepoResponse) Descriptor() ([]byte, []int) {794 func (*DeleteRepoResponse) Descriptor() ([]byte, []int) {
708- return file_ermite_repo_proto_rawDescGZIP(), []int{10}795+ return file_ermite_repo_proto_rawDescGZIP(), []int{11}
709 }796 }
710 797
711 var File_ermite_repo_proto protoreflect.FileDescriptor798 var File_ermite_repo_proto protoreflect.FileDescriptor
712 799
713 const file_ermite_repo_proto_rawDesc = "" +800 const file_ermite_repo_proto_rawDesc = "" +
714 "\n" +801 "\n" +
715- "\x11ermite/repo.proto\x12\x06ermite\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x8b\x02\n" +802+ "\x11ermite/repo.proto\x12\x06ermite\x1a google/protobuf/field_mask.proto\x1a\x12ermite/types.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x83\x01\n" +
803+ "\fRepoSettings\x12A\n" +
804+ "\x13allowed_merge_modes\x18\x01 \x03(\x0e2\x11.ermite.MergeModeR\x11allowedMergeModes\x120\n" +
805+ "\x14failing_checks_block\x18\x02 \x01(\bR\x12failingChecksBlock\"\xbd\x02\n" +
716 "\x04Repo\x12\x12\n" +806 "\x04Repo\x12\x12\n" +
717 "\x04name\x18\x01 \x01(\tR\x04name\x12 \n" +807 "\x04name\x18\x01 \x01(\tR\x04name\x12 \n" +
718 "\vdescription\x18\x02 \x01(\tR\vdescription\x122\n" +808 "\vdescription\x18\x02 \x01(\tR\vdescription\x122\n" +
@@ -722,7 +812,8 @@ const file_ermite_repo_proto_rawDesc = "" +
722 "\x0edefault_branch\x18\x04 \x01(\tR\rdefaultBranch\x129\n" +812 "\x0edefault_branch\x18\x04 \x01(\tR\rdefaultBranch\x129\n" +
723 "\n" +813 "\n" +
724 "created_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x127\n" +814 "created_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x127\n" +
725- "\tpushed_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\bpushedAt\"N\n" +815+ "\tpushed_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\bpushedAt\x120\n" +
816+ "\bsettings\x18\a \x01(\v2\x14.ermite.RepoSettingsR\bsettings\"N\n" +
726 "\x10ListReposRequest\x12\x1b\n" +817 "\x10ListReposRequest\x12\x1b\n" +
727 "\tpage_size\x18\x01 \x01(\x05R\bpageSize\x12\x1d\n" +818 "\tpage_size\x18\x01 \x01(\x05R\bpageSize\x12\x1d\n" +
728 "\n" +819 "\n" +
@@ -741,7 +832,7 @@ const file_ermite_repo_proto_rawDesc = "" +
741 "visibility\x18\x03 \x01(\x0e2\x12.ermite.VisibilityR\n" +832 "visibility\x18\x03 \x01(\x0e2\x12.ermite.VisibilityR\n" +
742 "visibility\"6\n" +833 "visibility\"6\n" +
743 "\x12CreateRepoResponse\x12 \n" +834 "\x12CreateRepoResponse\x12 \n" +
744- "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"\xe1\x01\n" +835+ "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"\x93\x02\n" +
745 "\x11UpdateRepoRequest\x12\x12\n" +836 "\x11UpdateRepoRequest\x12\x12\n" +
746 "\x04repo\x18\x01 \x01(\tR\x04repo\x12 \n" +837 "\x04repo\x18\x01 \x01(\tR\x04repo\x12 \n" +
747 "\vdescription\x18\x02 \x01(\tR\vdescription\x122\n" +838 "\vdescription\x18\x02 \x01(\tR\vdescription\x122\n" +
@@ -750,7 +841,8 @@ const file_ermite_repo_proto_rawDesc = "" +
750 "visibility\x12%\n" +841 "visibility\x12%\n" +
751 "\x0edefault_branch\x18\x04 \x01(\tR\rdefaultBranch\x12;\n" +842 "\x0edefault_branch\x18\x04 \x01(\tR\rdefaultBranch\x12;\n" +
752 "\vupdate_mask\x18\x05 \x01(\v2\x1a.google.protobuf.FieldMaskR\n" +843 "\vupdate_mask\x18\x05 \x01(\v2\x1a.google.protobuf.FieldMaskR\n" +
753- "updateMask\"6\n" +844+ "updateMask\x120\n" +
845+ "\bsettings\x18\x06 \x01(\v2\x14.ermite.RepoSettingsR\bsettings\"6\n" +
754 "\x12UpdateRepoResponse\x12 \n" +846 "\x12UpdateRepoResponse\x12 \n" +
755 "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"'\n" +847 "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"'\n" +
756 "\x11DeleteRepoRequest\x12\x12\n" +848 "\x11DeleteRepoRequest\x12\x12\n" +
@@ -786,49 +878,54 @@ func file_ermite_repo_proto_rawDescGZIP() []byte {
786 }878 }
787 879
788 var file_ermite_repo_proto_enumTypes = make([]protoimpl.EnumInfo, 1)880 var file_ermite_repo_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
789-var file_ermite_repo_proto_msgTypes = make([]protoimpl.MessageInfo, 11)881+var file_ermite_repo_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
790 var file_ermite_repo_proto_goTypes = []any{882 var file_ermite_repo_proto_goTypes = []any{
791 (Visibility)(0), // 0: ermite.Visibility883 (Visibility)(0), // 0: ermite.Visibility
792- (*Repo)(nil), // 1: ermite.Repo884+ (*RepoSettings)(nil), // 1: ermite.RepoSettings
793- (*ListReposRequest)(nil), // 2: ermite.ListReposRequest885+ (*Repo)(nil), // 2: ermite.Repo
794- (*ListReposResponse)(nil), // 3: ermite.ListReposResponse886+ (*ListReposRequest)(nil), // 3: ermite.ListReposRequest
795- (*GetRepoRequest)(nil), // 4: ermite.GetRepoRequest887+ (*ListReposResponse)(nil), // 4: ermite.ListReposResponse
796- (*GetRepoResponse)(nil), // 5: ermite.GetRepoResponse888+ (*GetRepoRequest)(nil), // 5: ermite.GetRepoRequest
797- (*CreateRepoRequest)(nil), // 6: ermite.CreateRepoRequest889+ (*GetRepoResponse)(nil), // 6: ermite.GetRepoResponse
798- (*CreateRepoResponse)(nil), // 7: ermite.CreateRepoResponse890+ (*CreateRepoRequest)(nil), // 7: ermite.CreateRepoRequest
799- (*UpdateRepoRequest)(nil), // 8: ermite.UpdateRepoRequest891+ (*CreateRepoResponse)(nil), // 8: ermite.CreateRepoResponse
800- (*UpdateRepoResponse)(nil), // 9: ermite.UpdateRepoResponse892+ (*UpdateRepoRequest)(nil), // 9: ermite.UpdateRepoRequest
801- (*DeleteRepoRequest)(nil), // 10: ermite.DeleteRepoRequest893+ (*UpdateRepoResponse)(nil), // 10: ermite.UpdateRepoResponse
802- (*DeleteRepoResponse)(nil), // 11: ermite.DeleteRepoResponse894+ (*DeleteRepoRequest)(nil), // 11: ermite.DeleteRepoRequest
803- (*timestamppb.Timestamp)(nil), // 12: google.protobuf.Timestamp895+ (*DeleteRepoResponse)(nil), // 12: ermite.DeleteRepoResponse
804- (*fieldmaskpb.FieldMask)(nil), // 13: google.protobuf.FieldMask896+ (MergeMode)(0), // 13: ermite.MergeMode
897+ (*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp
898+ (*fieldmaskpb.FieldMask)(nil), // 15: google.protobuf.FieldMask
805 }899 }
806 var file_ermite_repo_proto_depIdxs = []int32{900 var file_ermite_repo_proto_depIdxs = []int32{
807- 0, // 0: ermite.Repo.visibility:type_name -> ermite.Visibility901+ 13, // 0: ermite.RepoSettings.allowed_merge_modes:type_name -> ermite.MergeMode
808- 12, // 1: ermite.Repo.created_at:type_name -> google.protobuf.Timestamp902+ 0, // 1: ermite.Repo.visibility:type_name -> ermite.Visibility
809- 12, // 2: ermite.Repo.pushed_at:type_name -> google.protobuf.Timestamp903+ 14, // 2: ermite.Repo.created_at:type_name -> google.protobuf.Timestamp
810- 1, // 3: ermite.ListReposResponse.repos:type_name -> ermite.Repo904+ 14, // 3: ermite.Repo.pushed_at:type_name -> google.protobuf.Timestamp
811- 1, // 4: ermite.GetRepoResponse.repo:type_name -> ermite.Repo905+ 1, // 4: ermite.Repo.settings:type_name -> ermite.RepoSettings
812- 0, // 5: ermite.CreateRepoRequest.visibility:type_name -> ermite.Visibility906+ 2, // 5: ermite.ListReposResponse.repos:type_name -> ermite.Repo
813- 1, // 6: ermite.CreateRepoResponse.repo:type_name -> ermite.Repo907+ 2, // 6: ermite.GetRepoResponse.repo:type_name -> ermite.Repo
814- 0, // 7: ermite.UpdateRepoRequest.visibility:type_name -> ermite.Visibility908+ 0, // 7: ermite.CreateRepoRequest.visibility:type_name -> ermite.Visibility
815- 13, // 8: ermite.UpdateRepoRequest.update_mask:type_name -> google.protobuf.FieldMask909+ 2, // 8: ermite.CreateRepoResponse.repo:type_name -> ermite.Repo
816- 1, // 9: ermite.UpdateRepoResponse.repo:type_name -> ermite.Repo910+ 0, // 9: ermite.UpdateRepoRequest.visibility:type_name -> ermite.Visibility
817- 2, // 10: ermite.RepoService.ListRepos:input_type -> ermite.ListReposRequest911+ 15, // 10: ermite.UpdateRepoRequest.update_mask:type_name -> google.protobuf.FieldMask
818- 4, // 11: ermite.RepoService.GetRepo:input_type -> ermite.GetRepoRequest912+ 1, // 11: ermite.UpdateRepoRequest.settings:type_name -> ermite.RepoSettings
819- 6, // 12: ermite.RepoService.CreateRepo:input_type -> ermite.CreateRepoRequest913+ 2, // 12: ermite.UpdateRepoResponse.repo:type_name -> ermite.Repo
820- 8, // 13: ermite.RepoService.UpdateRepo:input_type -> ermite.UpdateRepoRequest914+ 3, // 13: ermite.RepoService.ListRepos:input_type -> ermite.ListReposRequest
821- 10, // 14: ermite.RepoService.DeleteRepo:input_type -> ermite.DeleteRepoRequest915+ 5, // 14: ermite.RepoService.GetRepo:input_type -> ermite.GetRepoRequest
822- 3, // 15: ermite.RepoService.ListRepos:output_type -> ermite.ListReposResponse916+ 7, // 15: ermite.RepoService.CreateRepo:input_type -> ermite.CreateRepoRequest
823- 5, // 16: ermite.RepoService.GetRepo:output_type -> ermite.GetRepoResponse917+ 9, // 16: ermite.RepoService.UpdateRepo:input_type -> ermite.UpdateRepoRequest
824- 7, // 17: ermite.RepoService.CreateRepo:output_type -> ermite.CreateRepoResponse918+ 11, // 17: ermite.RepoService.DeleteRepo:input_type -> ermite.DeleteRepoRequest
825- 9, // 18: ermite.RepoService.UpdateRepo:output_type -> ermite.UpdateRepoResponse919+ 4, // 18: ermite.RepoService.ListRepos:output_type -> ermite.ListReposResponse
826- 11, // 19: ermite.RepoService.DeleteRepo:output_type -> ermite.DeleteRepoResponse920+ 6, // 19: ermite.RepoService.GetRepo:output_type -> ermite.GetRepoResponse
827- 15, // [15:20] is the sub-list for method output_type921+ 8, // 20: ermite.RepoService.CreateRepo:output_type -> ermite.CreateRepoResponse
828- 10, // [10:15] is the sub-list for method input_type922+ 10, // 21: ermite.RepoService.UpdateRepo:output_type -> ermite.UpdateRepoResponse
829- 10, // [10:10] is the sub-list for extension type_name923+ 12, // 22: ermite.RepoService.DeleteRepo:output_type -> ermite.DeleteRepoResponse
830- 10, // [10:10] is the sub-list for extension extendee924+ 18, // [18:23] is the sub-list for method output_type
831- 0, // [0:10] is the sub-list for field type_name925+ 13, // [13:18] is the sub-list for method input_type
926+ 13, // [13:13] is the sub-list for extension type_name
927+ 13, // [13:13] is the sub-list for extension extendee
928+ 0, // [0:13] is the sub-list for field type_name
832 }929 }
833 930
834 func init() { file_ermite_repo_proto_init() }931 func init() { file_ermite_repo_proto_init() }
@@ -836,13 +933,14 @@ func file_ermite_repo_proto_init() {
836 if File_ermite_repo_proto != nil {933 if File_ermite_repo_proto != nil {
837 return934 return
838 }935 }
936+ file_ermite_types_proto_init()
839 type x struct{}937 type x struct{}
840 out := protoimpl.TypeBuilder{938 out := protoimpl.TypeBuilder{
841 File: protoimpl.DescBuilder{939 File: protoimpl.DescBuilder{
842 GoPackagePath: reflect.TypeOf(x{}).PkgPath(),940 GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
843 RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_repo_proto_rawDesc), len(file_ermite_repo_proto_rawDesc)),941 RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_repo_proto_rawDesc), len(file_ermite_repo_proto_rawDesc)),
844 NumEnums: 1,942 NumEnums: 1,
845- NumMessages: 11,943+ NumMessages: 12,
846 NumExtensions: 0,944 NumExtensions: 0,
847 NumServices: 1,945 NumServices: 1,
848 },946 },
M gen/ermite/server.pb.go +148 -9 generated file: collapsed
A gen/ermite/session.pb.go +268 -0 generated file: collapsed
M gen/ermite/types.pb.go +78 -8 generated file: collapsed
M gen_test.go +61 -0
@@ -30,7 +30,18 @@ func TestProcedures(t *testing.T) {
3030 {ermiteconnect.TokenServiceListTokensProcedure, "/ermite.TokenService/ListTokens"},
3131 {ermiteconnect.TokenServiceRevokeTokenProcedure, "/ermite.TokenService/RevokeToken"},
3232 {ermiteconnect.EventServiceWatchProcedure, "/ermite.EventService/Watch"},
33+ {ermiteconnect.MergeRequestServiceCreateMergeRequestProcedure, "/ermite.MergeRequestService/CreateMergeRequest"},
34+ {ermiteconnect.MergeRequestServiceListMergeRequestsProcedure, "/ermite.MergeRequestService/ListMergeRequests"},
35+ {ermiteconnect.MergeRequestServiceGetMergeRequestProcedure, "/ermite.MergeRequestService/GetMergeRequest"},
36+ {ermiteconnect.MergeRequestServiceAddCommentProcedure, "/ermite.MergeRequestService/AddComment"},
37+ {ermiteconnect.MergeRequestServiceResolveCommentProcedure, "/ermite.MergeRequestService/ResolveComment"},
38+ {ermiteconnect.MergeRequestServiceCloseMergeRequestProcedure, "/ermite.MergeRequestService/CloseMergeRequest"},
39+ {ermiteconnect.MergeRequestServiceMergeMergeRequestProcedure, "/ermite.MergeRequestService/MergeMergeRequest"},
40+ {ermiteconnect.MergeRequestServiceMarkReadProcedure, "/ermite.MergeRequestService/MarkRead"},
41+ {ermiteconnect.SessionServiceCreateLoginLinkProcedure, "/ermite.SessionService/CreateLoginLink"},
42+ {ermiteconnect.SessionServiceSignOutEverywhereProcedure, "/ermite.SessionService/SignOutEverywhere"},
3343 {ermiteconnect.ServerServiceGetVersionProcedure, "/ermite.ServerService/GetVersion"},
44+ {ermiteconnect.ServerServiceGetSigningKeyProcedure, "/ermite.ServerService/GetSigningKey"},
3445 }
3546 for _, tt := range tests {
3647 if tt.got != tt.want {
@@ -50,6 +61,15 @@ func TestEnumNumbers(t *testing.T) {
5061 if got := ermite.Visibility_VISIBILITY_PRIVATE; got != 2 {
5162 t.Errorf("VISIBILITY_PRIVATE = %d, want 2", got)
5263 }
64+ if got := ermite.MergeMode_MERGE_MODE_SQUASH; got != 2 {
65+ t.Errorf("MERGE_MODE_SQUASH = %d, want 2", got)
66+ }
67+ if got := ermite.MergeRequestState_MERGE_REQUEST_STATE_CLOSED; got != 3 {
68+ t.Errorf("MERGE_REQUEST_STATE_CLOSED = %d, want 3", got)
69+ }
70+ if got := ermite.DiffSide_DIFF_SIDE_NEW; got != 2 {
71+ t.Errorf("DIFF_SIDE_NEW = %d, want 2", got)
72+ }
5373 }
5474
5575 // TestEventRoundTrip checks that an event with a oneof kind survives the wire.
@@ -90,3 +110,44 @@ func TestUpdateRepoMask(t *testing.T) {
90110 t.Errorf("paths = %v", got)
91111 }
92112 }
113+
114+// TestMergeRequestEventRoundTrip checks the merge request event kinds and the
115+// repository settings survive the wire.
116+func TestMergeRequestEventRoundTrip(t *testing.T) {
117+ in := &ermite.WatchResponse{Event: &ermite.Event{
118+ Id: "43",
119+ Kind: &ermite.Event_CommentAdded{CommentAdded: &ermite.CommentAdded{
120+ Repo: "ermite",
121+ Number: 7,
122+ Comment: &ermite.Comment{
123+ Id: "c1", Path: "a.go", Line: 3, Side: ermite.DiffSide_DIFF_SIDE_OLD, Text: "hi",
124+ },
125+ }},
126+ }}
127+ b, err := proto.Marshal(in)
128+ if err != nil {
129+ t.Fatal(err)
130+ }
131+ var out ermite.WatchResponse
132+ if err := proto.Unmarshal(b, &out); err != nil {
133+ t.Fatal(err)
134+ }
135+ if !proto.Equal(in, &out) {
136+ t.Errorf("round trip mismatch: %v != %v", &out, in)
137+ }
138+ if got := out.GetEvent().GetCommentAdded().GetNumber(); got != 7 {
139+ t.Errorf("number = %d, want 7", got)
140+ }
141+
142+ req := &ermite.UpdateRepoRequest{
143+ Repo: "ermite",
144+ Settings: &ermite.RepoSettings{
145+ AllowedMergeModes: []ermite.MergeMode{ermite.MergeMode_MERGE_MODE_SQUASH},
146+ FailingChecksBlock: true,
147+ },
148+ UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"settings.allowed_merge_modes", "settings.failing_checks_block"}},
149+ }
150+ if got := req.GetSettings().GetAllowedMergeModes(); len(got) != 1 || got[0] != ermite.MergeMode_MERGE_MODE_SQUASH {
151+ t.Errorf("modes = %v", got)
152+ }
153+}
@@ -30,7 +30,18 @@ func TestProcedures(t *testing.T) {
30 {ermiteconnect.TokenServiceListTokensProcedure, "/ermite.TokenService/ListTokens"},30 {ermiteconnect.TokenServiceListTokensProcedure, "/ermite.TokenService/ListTokens"},
31 {ermiteconnect.TokenServiceRevokeTokenProcedure, "/ermite.TokenService/RevokeToken"},31 {ermiteconnect.TokenServiceRevokeTokenProcedure, "/ermite.TokenService/RevokeToken"},
32 {ermiteconnect.EventServiceWatchProcedure, "/ermite.EventService/Watch"},32 {ermiteconnect.EventServiceWatchProcedure, "/ermite.EventService/Watch"},
33+ {ermiteconnect.MergeRequestServiceCreateMergeRequestProcedure, "/ermite.MergeRequestService/CreateMergeRequest"},
34+ {ermiteconnect.MergeRequestServiceListMergeRequestsProcedure, "/ermite.MergeRequestService/ListMergeRequests"},
35+ {ermiteconnect.MergeRequestServiceGetMergeRequestProcedure, "/ermite.MergeRequestService/GetMergeRequest"},
36+ {ermiteconnect.MergeRequestServiceAddCommentProcedure, "/ermite.MergeRequestService/AddComment"},
37+ {ermiteconnect.MergeRequestServiceResolveCommentProcedure, "/ermite.MergeRequestService/ResolveComment"},
38+ {ermiteconnect.MergeRequestServiceCloseMergeRequestProcedure, "/ermite.MergeRequestService/CloseMergeRequest"},
39+ {ermiteconnect.MergeRequestServiceMergeMergeRequestProcedure, "/ermite.MergeRequestService/MergeMergeRequest"},
40+ {ermiteconnect.MergeRequestServiceMarkReadProcedure, "/ermite.MergeRequestService/MarkRead"},
41+ {ermiteconnect.SessionServiceCreateLoginLinkProcedure, "/ermite.SessionService/CreateLoginLink"},
42+ {ermiteconnect.SessionServiceSignOutEverywhereProcedure, "/ermite.SessionService/SignOutEverywhere"},
33 {ermiteconnect.ServerServiceGetVersionProcedure, "/ermite.ServerService/GetVersion"},43 {ermiteconnect.ServerServiceGetVersionProcedure, "/ermite.ServerService/GetVersion"},
44+ {ermiteconnect.ServerServiceGetSigningKeyProcedure, "/ermite.ServerService/GetSigningKey"},
34 }45 }
35 for _, tt := range tests {46 for _, tt := range tests {
36 if tt.got != tt.want {47 if tt.got != tt.want {
@@ -50,6 +61,15 @@ func TestEnumNumbers(t *testing.T) {
50 if got := ermite.Visibility_VISIBILITY_PRIVATE; got != 2 {61 if got := ermite.Visibility_VISIBILITY_PRIVATE; got != 2 {
51 t.Errorf("VISIBILITY_PRIVATE = %d, want 2", got)62 t.Errorf("VISIBILITY_PRIVATE = %d, want 2", got)
52 }63 }
64+ if got := ermite.MergeMode_MERGE_MODE_SQUASH; got != 2 {
65+ t.Errorf("MERGE_MODE_SQUASH = %d, want 2", got)
66+ }
67+ if got := ermite.MergeRequestState_MERGE_REQUEST_STATE_CLOSED; got != 3 {
68+ t.Errorf("MERGE_REQUEST_STATE_CLOSED = %d, want 3", got)
69+ }
70+ if got := ermite.DiffSide_DIFF_SIDE_NEW; got != 2 {
71+ t.Errorf("DIFF_SIDE_NEW = %d, want 2", got)
72+ }
53 }73 }
54 74
55 // TestEventRoundTrip checks that an event with a oneof kind survives the wire.75 // TestEventRoundTrip checks that an event with a oneof kind survives the wire.
@@ -90,3 +110,44 @@ func TestUpdateRepoMask(t *testing.T) {
90 t.Errorf("paths = %v", got)110 t.Errorf("paths = %v", got)
91 }111 }
92 }112 }
113+
114+// TestMergeRequestEventRoundTrip checks the merge request event kinds and the
115+// repository settings survive the wire.
116+func TestMergeRequestEventRoundTrip(t *testing.T) {
117+ in := &ermite.WatchResponse{Event: &ermite.Event{
118+ Id: "43",
119+ Kind: &ermite.Event_CommentAdded{CommentAdded: &ermite.CommentAdded{
120+ Repo: "ermite",
121+ Number: 7,
122+ Comment: &ermite.Comment{
123+ Id: "c1", Path: "a.go", Line: 3, Side: ermite.DiffSide_DIFF_SIDE_OLD, Text: "hi",
124+ },
125+ }},
126+ }}
127+ b, err := proto.Marshal(in)
128+ if err != nil {
129+ t.Fatal(err)
130+ }
131+ var out ermite.WatchResponse
132+ if err := proto.Unmarshal(b, &out); err != nil {
133+ t.Fatal(err)
134+ }
135+ if !proto.Equal(in, &out) {
136+ t.Errorf("round trip mismatch: %v != %v", &out, in)
137+ }
138+ if got := out.GetEvent().GetCommentAdded().GetNumber(); got != 7 {
139+ t.Errorf("number = %d, want 7", got)
140+ }
141+
142+ req := &ermite.UpdateRepoRequest{
143+ Repo: "ermite",
144+ Settings: &ermite.RepoSettings{
145+ AllowedMergeModes: []ermite.MergeMode{ermite.MergeMode_MERGE_MODE_SQUASH},
146+ FailingChecksBlock: true,
147+ },
148+ UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"settings.allowed_merge_modes", "settings.failing_checks_block"}},
149+ }
150+ if got := req.GetSettings().GetAllowedMergeModes(); len(got) != 1 || got[0] != ermite.MergeMode_MERGE_MODE_SQUASH {
151+ t.Errorf("modes = %v", got)
152+ }
153+}
M proto/ermite/event.proto +49 -1
@@ -4,6 +4,7 @@ syntax = "proto3";
44
55 package ermite;
66
7+import "ermite/merge_request.proto";
78 import "ermite/repo.proto";
89 import "google/protobuf/timestamp.proto";
910
@@ -13,6 +14,15 @@ service EventService {
1314 // down. Events are filtered by the caller's access: events of a private
1415 // repository are only sent to callers that may read it.
1516 //
17+ // The events are repo_created, repo_updated, repo_deleted, pushed, gap and,
18+ // for merge requests, merge_request_opened, merge_request_merged,
19+ // merge_request_closed and comment_added. Each one is delivered on the
20+ // "repo:<name>" topic of its repository and on "home", subject to the
21+ // visibility of the repository. There is no event for a change of title or
22+ // description, nor for a resolved comment: after a gap, or to see such
23+ // changes, clients read the merge request again. A merge publishes
24+ // merge_request_merged, then pushed for the target branch.
25+ //
1626 // The server keeps a bounded buffer of recent events. With resume_after set,
1727 // buffered events after that id are replayed first, then live events follow.
1828 // When resume_after is no longer buffered (or unknown), the stream begins
@@ -69,6 +79,14 @@ message Event {
6979 Pushed pushed = 6;
7080 // Gap: events were lost, reload state.
7181 Gap gap = 7;
82+ // MergeRequestOpened: a merge request was opened.
83+ MergeRequestOpened merge_request_opened = 8;
84+ // MergeRequestMerged: a merge request was merged.
85+ MergeRequestMerged merge_request_merged = 9;
86+ // MergeRequestClosed: a merge request was closed without merging.
87+ MergeRequestClosed merge_request_closed = 10;
88+ // CommentAdded: a comment was added to a merge request.
89+ CommentAdded comment_added = 11;
7290 }
7391 }
7492
@@ -108,7 +126,37 @@ message RefUpdate {
108126 string new_id = 3;
109127 }
110128
129+// MergeRequestOpened reports a new merge request.
130+message MergeRequestOpened {
131+ // MergeRequest is the new merge request.
132+ MergeRequest merge_request = 1;
133+}
134+
135+// MergeRequestMerged reports a merged merge request. It is published before
136+// the Pushed event of the target branch.
137+message MergeRequestMerged {
138+ // MergeRequest is the merge request after the merge.
139+ MergeRequest merge_request = 1;
140+}
141+
142+// MergeRequestClosed reports a merge request closed without merging.
143+message MergeRequestClosed {
144+ // MergeRequest is the merge request after closing.
145+ MergeRequest merge_request = 1;
146+}
147+
148+// CommentAdded reports a new comment on a merge request.
149+message CommentAdded {
150+ // Repo is the name of the repository.
151+ string repo = 1;
152+ // Number is the number of the merge request.
153+ int64 number = 2;
154+ // Comment is the new comment.
155+ Comment comment = 3;
156+}
157+
111158 // Gap tells the client that events were lost: the requested resume point is
112159 // older than the server's buffer. It has no fields. The client should reload
113-// the state it displays (repositories, refs) and carry on with the next events.
160+// the state it displays (repositories, refs, merge requests and their
161+// comments) and carry on with the next events.
114162 message Gap {}
@@ -4,6 +4,7 @@ syntax = "proto3";
4 4
5 package ermite;5 package ermite;
6 6
7+import "ermite/merge_request.proto";
7 import "ermite/repo.proto";8 import "ermite/repo.proto";
8 import "google/protobuf/timestamp.proto";9 import "google/protobuf/timestamp.proto";
9 10
@@ -13,6 +14,15 @@ service EventService {
13 // down. Events are filtered by the caller's access: events of a private14 // down. Events are filtered by the caller's access: events of a private
14 // repository are only sent to callers that may read it.15 // repository are only sent to callers that may read it.
15 //16 //
17+ // The events are repo_created, repo_updated, repo_deleted, pushed, gap and,
18+ // for merge requests, merge_request_opened, merge_request_merged,
19+ // merge_request_closed and comment_added. Each one is delivered on the
20+ // "repo:<name>" topic of its repository and on "home", subject to the
21+ // visibility of the repository. There is no event for a change of title or
22+ // description, nor for a resolved comment: after a gap, or to see such
23+ // changes, clients read the merge request again. A merge publishes
24+ // merge_request_merged, then pushed for the target branch.
25+ //
16 // The server keeps a bounded buffer of recent events. With resume_after set,26 // The server keeps a bounded buffer of recent events. With resume_after set,
17 // buffered events after that id are replayed first, then live events follow.27 // buffered events after that id are replayed first, then live events follow.
18 // When resume_after is no longer buffered (or unknown), the stream begins28 // When resume_after is no longer buffered (or unknown), the stream begins
@@ -69,6 +79,14 @@ message Event {
69 Pushed pushed = 6;79 Pushed pushed = 6;
70 // Gap: events were lost, reload state.80 // Gap: events were lost, reload state.
71 Gap gap = 7;81 Gap gap = 7;
82+ // MergeRequestOpened: a merge request was opened.
83+ MergeRequestOpened merge_request_opened = 8;
84+ // MergeRequestMerged: a merge request was merged.
85+ MergeRequestMerged merge_request_merged = 9;
86+ // MergeRequestClosed: a merge request was closed without merging.
87+ MergeRequestClosed merge_request_closed = 10;
88+ // CommentAdded: a comment was added to a merge request.
89+ CommentAdded comment_added = 11;
72 }90 }
73 }91 }
74 92
@@ -108,7 +126,37 @@ message RefUpdate {
108 string new_id = 3;126 string new_id = 3;
109 }127 }
110 128
129+// MergeRequestOpened reports a new merge request.
130+message MergeRequestOpened {
131+ // MergeRequest is the new merge request.
132+ MergeRequest merge_request = 1;
133+}
134+
135+// MergeRequestMerged reports a merged merge request. It is published before
136+// the Pushed event of the target branch.
137+message MergeRequestMerged {
138+ // MergeRequest is the merge request after the merge.
139+ MergeRequest merge_request = 1;
140+}
141+
142+// MergeRequestClosed reports a merge request closed without merging.
143+message MergeRequestClosed {
144+ // MergeRequest is the merge request after closing.
145+ MergeRequest merge_request = 1;
146+}
147+
148+// CommentAdded reports a new comment on a merge request.
149+message CommentAdded {
150+ // Repo is the name of the repository.
151+ string repo = 1;
152+ // Number is the number of the merge request.
153+ int64 number = 2;
154+ // Comment is the new comment.
155+ Comment comment = 3;
156+}
157+
111 // Gap tells the client that events were lost: the requested resume point is158 // Gap tells the client that events were lost: the requested resume point is
112 // older than the server's buffer. It has no fields. The client should reload159 // older than the server's buffer. It has no fields. The client should reload
113-// the state it displays (repositories, refs) and carry on with the next events.160+// the state it displays (repositories, refs, merge requests and their
161+// comments) and carry on with the next events.
114 message Gap {}162 message Gap {}
A proto/ermite/merge_request.proto +371 -0
@@ -0,0 +1,371 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "ermite/git.proto";
8+import "ermite/types.proto";
9+import "google/protobuf/timestamp.proto";
10+
11+// MergeRequestState is where a merge request is in its life.
12+enum MergeRequestState {
13+ // Not set. In ListMergeRequestsRequest.state it means every state. Never
14+ // returned by the server.
15+ MERGE_REQUEST_STATE_UNSPECIFIED = 0;
16+ // The merge request awaits review; it can be commented on, merged or closed.
17+ MERGE_REQUEST_STATE_OPEN = 1;
18+ // The source branch was merged into the target. Final.
19+ MERGE_REQUEST_STATE_MERGED = 2;
20+ // The merge request was closed without merging. Final.
21+ MERGE_REQUEST_STATE_CLOSED = 3;
22+}
23+
24+// DiffSide is the side of a diff a line comment is attached to.
25+enum DiffSide {
26+ // Not set. On a Comment it means the comment is general, not attached to a
27+ // line. In AddCommentRequest it is valid only for a general comment.
28+ DIFF_SIDE_UNSPECIFIED = 0;
29+ // The old side: the line is a Line.old_number (a deletion or a context line).
30+ DIFF_SIDE_OLD = 1;
31+ // The new side: the line is a Line.new_number (an addition or a context
32+ // line).
33+ DIFF_SIDE_NEW = 2;
34+}
35+
36+// MergeRequest asks to merge one branch of a repository into another branch
37+// of the same repository. It is about a branch, not about who wrote it.
38+message MergeRequest {
39+ // Repo is the name of the repository.
40+ string repo = 1;
41+ // Number identifies the merge request within its repository, starting at 1
42+ // and never reused. It is written "!<number>" in messages and URLs.
43+ int64 number = 2;
44+ // Title is a one-line plain-text title, at most 120 characters.
45+ string title = 3;
46+ // Description is a plain-text description, at most 8,000 characters. It may
47+ // be empty. Clients show it escaped, with line breaks kept.
48+ string description = 4;
49+ // SourceBranch is the short name of the branch to merge, for example
50+ // "feat/x".
51+ string source_branch = 5;
52+ // TargetBranch is the short name of the branch to merge into, for example
53+ // "main".
54+ string target_branch = 6;
55+ // State is where the merge request is in its life.
56+ MergeRequestState state = 7;
57+ // Author names who created the merge request: the label of the token that
58+ // was used, "root" for the root token, or "owner" for a web session. It is
59+ // informational: labels are not unique and the token may be gone.
60+ string author = 8;
61+ // CreatedAt is when the merge request was created.
62+ google.protobuf.Timestamp created_at = 9;
63+ // UpdatedAt is when the merge request last changed: title, description,
64+ // state, or a comment added or resolved. Pushes to the source branch do not
65+ // change it.
66+ google.protobuf.Timestamp updated_at = 10;
67+ // HeadId is the id of the commit the source branch points to. While the
68+ // merge request is open it is read from Git at every call, so it changes
69+ // when the branch is pushed; it is empty if the branch was deleted. Once
70+ // merged or closed it is the head at that moment.
71+ string head_id = 11;
72+ // MergedCommitId is the id of the commit the target branch points to after
73+ // the merge: the head for a fast-forward, the new commit for a squash.
74+ // Empty unless state is MERGED.
75+ string merged_commit_id = 12;
76+ // MergeMode is how the merge request was merged. MERGE_MODE_UNSPECIFIED
77+ // unless state is MERGED.
78+ MergeMode merge_mode = 13;
79+}
80+
81+// Comment is a note on a merge request, either general or attached to a line
82+// of its diff.
83+message Comment {
84+ // Id identifies the comment within its merge request. Opaque.
85+ string id = 1;
86+ // Author names who wrote the comment, like MergeRequest.author.
87+ string author = 2;
88+ // Path is the path of the commented file in the diff (the new path, or the
89+ // old path of a deleted file). Empty for a general comment.
90+ string path = 3;
91+ // Line is the 1-based line number on side. 0 for a general comment.
92+ int32 line = 4;
93+ // Side tells whether line counts on the old or the new side of the diff.
94+ // DIFF_SIDE_UNSPECIFIED for a general comment.
95+ DiffSide side = 5;
96+ // CommitId is the head of the merge request when the comment was written.
97+ // When it differs from the current head the line may have moved; clients
98+ // may show the comment as outdated.
99+ string commit_id = 6;
100+ // Text is the plain-text content, at most 8,000 characters. Clients show it
101+ // escaped, with line breaks kept.
102+ string text = 7;
103+ // Resolved is true once the comment was marked resolved.
104+ bool resolved = 8;
105+ // CreatedAt is when the comment was written.
106+ google.protobuf.Timestamp created_at = 9;
107+}
108+
109+// MergeRequestService reviews and merges branches.
110+//
111+// Every RPC takes the repository name and, except for creation and listing,
112+// the merge request number. A repository that does not exist or that the
113+// caller cannot read gives not_found; so does an unknown number. Reading
114+// (ListMergeRequests, GetMergeRequest) needs the same access as reading the
115+// repository: nothing for a public one, the repo:read scope for a private
116+// one. Writing needs a scope on the repository, given per RPC; a caller
117+// without it gets permission_denied.
118+//
119+// Text fields follow the same rules everywhere: valid UTF-8, no control
120+// characters (except the line break in description and comment text) and no
121+// Unicode bidirectional override or isolate characters; title is one line of
122+// at most 120 characters, description and comment text at most 8,000
123+// characters. A request that breaks a rule is rejected whole with
124+// invalid_argument, and the error carries a BadRequest detail naming the
125+// fields.
126+//
127+// Servers that implement this service list "merge_requests" in
128+// GetVersionResponse.features.
129+service MergeRequestService {
130+ // CreateMergeRequest opens a merge request. Needs the mr:write scope. Fails
131+ // with invalid_argument if a text rule is broken, if a branch does not exist
132+ // or if source and target are the same branch, and with already_exists if an
133+ // open merge request already has the same source and target.
134+ rpc CreateMergeRequest(CreateMergeRequestRequest) returns (CreateMergeRequestResponse);
135+ // ListMergeRequests lists the merge requests of a repository, highest
136+ // number (newest) first, optionally restricted to one state. The listed
137+ // merge requests carry no diff or comments; head_id is set. Fails with
138+ // not_found if the repository does not exist or is not readable.
139+ rpc ListMergeRequests(ListMergeRequestsRequest) returns (ListMergeRequestsResponse);
140+ // GetMergeRequest returns one merge request with its commits, its diff and
141+ // its comments. The diff compares the target with the head (what the merge
142+ // would bring: "target...head"), or, with since_last_read, the head the
143+ // caller last marked read with the current head. Fails with not_found if
144+ // the repository or the number is unknown.
145+ rpc GetMergeRequest(GetMergeRequestRequest) returns (GetMergeRequestResponse);
146+ // AddComment adds a general comment or a comment on a line of the diff.
147+ // Needs the mr:write scope. Fails with invalid_argument if the text breaks
148+ // a rule or if path, line and side do not designate a line of the current
149+ // diff of the merge request, and with failed_precondition if the merge
150+ // request is not open.
151+ rpc AddComment(AddCommentRequest) returns (AddCommentResponse);
152+ // ResolveComment marks a comment resolved or unresolved. Needs the mr:write
153+ // scope. Fails with not_found if the comment is unknown and with
154+ // failed_precondition if the merge request is not open. Setting the state
155+ // the comment already has succeeds and changes nothing.
156+ rpc ResolveComment(ResolveCommentRequest) returns (ResolveCommentResponse);
157+ // CloseMergeRequest closes an open merge request without merging it. Needs
158+ // the mr:write scope. Fails with failed_precondition if the merge request
159+ // is not open. A closed merge request cannot be reopened: open a new one.
160+ rpc CloseMergeRequest(CloseMergeRequestRequest) returns (CloseMergeRequestResponse);
161+ // MergeMergeRequest merges an open merge request. Needs the mr:merge scope.
162+ // The caller passes the head it reviewed: the server merges only if the
163+ // source branch still points to it, so a push between review and merge is
164+ // never merged unseen. Fails with invalid_argument if head_id is empty or
165+ // not a commit id, and with failed_precondition if the merge request is not
166+ // open, if the source branch no longer points to head_id ("branch moved,
167+ // read again"), if the target is not an ancestor of the head (rebase and
168+ // push again), if the mode is not allowed by RepoSettings, or if a check
169+ // fails and RepoSettings.failing_checks_block is set. Merges of one
170+ // repository run one at a time. Repeating a call that succeeded fails with
171+ // failed_precondition (not open). On success the server publishes
172+ // MergeRequestMerged then Pushed for the target branch.
173+ rpc MergeMergeRequest(MergeMergeRequestRequest) returns (MergeMergeRequestResponse);
174+ // MarkRead records the head the caller has reviewed, for the since_last_read
175+ // option of GetMergeRequest. There is one read position per merge request,
176+ // the reviewer's. Needs the mr:write scope. Marking a commit that does not
177+ // exist in the repository is invalid_argument.
178+ rpc MarkRead(MarkReadRequest) returns (MarkReadResponse);
179+}
180+
181+// CreateMergeRequestRequest describes the merge request to open.
182+message CreateMergeRequestRequest {
183+ // Repo is the repository name.
184+ string repo = 1;
185+ // Title is the one-line title. Required.
186+ string title = 2;
187+ // Description is the optional description.
188+ string description = 3;
189+ // SourceBranch is the short name of the branch to merge. Required; the
190+ // branch must exist.
191+ string source_branch = 4;
192+ // TargetBranch is the short name of the branch to merge into. Empty means
193+ // the default branch of the repository. The branch must exist and differ
194+ // from source_branch.
195+ string target_branch = 5;
196+}
197+
198+// CreateMergeRequestResponse holds the new merge request.
199+message CreateMergeRequestResponse {
200+ // MergeRequest is the created merge request.
201+ MergeRequest merge_request = 1;
202+}
203+
204+// ListMergeRequestsRequest selects a page of merge requests.
205+message ListMergeRequestsRequest {
206+ // Repo is the repository name.
207+ string repo = 1;
208+ // State restricts the result to one state. MERGE_REQUEST_STATE_UNSPECIFIED
209+ // means every state.
210+ MergeRequestState state = 2;
211+ // PageSize is the maximum number of merge requests to return. 0 means the
212+ // server default; values above the server maximum are lowered to it.
213+ int32 page_size = 3;
214+ // PageToken is the next_page_token of a previous response, to continue the
215+ // same listing. Empty for the first page. Other fields must be unchanged.
216+ string page_token = 4;
217+}
218+
219+// ListMergeRequestsResponse is one page of merge requests.
220+message ListMergeRequestsResponse {
221+ // MergeRequests are the merge requests of this page, highest number first.
222+ repeated MergeRequest merge_requests = 1;
223+ // NextPageToken is passed as page_token to get the next page. Empty when
224+ // there is no next page.
225+ string next_page_token = 2;
226+}
227+
228+// GetMergeRequestRequest selects a merge request.
229+message GetMergeRequestRequest {
230+ // Repo is the repository name.
231+ string repo = 1;
232+ // Number is the merge request number.
233+ int64 number = 2;
234+ // SinceLastRead asks for the diff between the head recorded by MarkRead and
235+ // the current head instead of the diff against the target. It works after a
236+ // force-push as long as the recorded commit still exists in the repository.
237+ // When nothing was marked read, or the recorded commit is gone, the response
238+ // holds the diff against the target and says so in last_read_id and
239+ // last_read_lost.
240+ bool since_last_read = 3;
241+}
242+
243+// GetMergeRequestResponse holds a merge request with its review data.
244+message GetMergeRequestResponse {
245+ // MergeRequest is the merge request.
246+ MergeRequest merge_request = 1;
247+ // Commits are the commits of the head that are not in the target
248+ // ("target..head"), newest first. At most 100 are returned (a server limit
249+ // that may change; rely on commits_truncated, not on the number). Empty when
250+ // the source branch was deleted.
251+ repeated Commit commits = 2;
252+ // CommitsTruncated is true when the branch has more commits than commits
253+ // holds.
254+ bool commits_truncated = 3;
255+ // DiffBaseId is the id of the commit the diff compares from: the merge base
256+ // of the target and the head, or the last read head when since_last_read
257+ // applied. Empty when the source branch was deleted; diff is then empty.
258+ string diff_base_id = 4;
259+ // Stats are the totals of the diff, including files that it omits or
260+ // truncates.
261+ DiffStats stats = 5;
262+ // Diff is the structured diff from diff_base_id to the head, with the same
263+ // limits and truncated flags as GetCommitResponse.diff.
264+ Diff diff = 6;
265+ // Comments are every comment of the merge request, oldest first.
266+ repeated Comment comments = 7;
267+ // LastReadId is the head recorded by MarkRead, whether or not
268+ // since_last_read was set. Empty when nothing was marked read.
269+ string last_read_id = 8;
270+ // LastReadLost is true when since_last_read was set, a head was recorded,
271+ // but that commit no longer exists in the repository: the diff is then the
272+ // diff against the target. A client should tell the reader to review the
273+ // whole change again.
274+ bool last_read_lost = 9;
275+}
276+
277+// AddCommentRequest describes the comment to add.
278+message AddCommentRequest {
279+ // Repo is the repository name.
280+ string repo = 1;
281+ // Number is the merge request number.
282+ int64 number = 2;
283+ // Path is the file the comment is attached to, as given by the diff of the
284+ // merge request: FileDiff.new_path, or old_path for a deleted file. Empty
285+ // for a general comment; then line must be 0 and side unspecified.
286+ string path = 3;
287+ // Line is the 1-based line number on side. It must be the old_number or
288+ // new_number of a Line of a hunk of the file; a line outside the hunks is
289+ // invalid_argument. 0 for a general comment.
290+ int32 line = 4;
291+ // Side is the side line counts on. Required when path is set.
292+ DiffSide side = 5;
293+ // Text is the plain-text content. Required.
294+ string text = 6;
295+}
296+
297+// AddCommentResponse holds the new comment.
298+message AddCommentResponse {
299+ // Comment is the created comment.
300+ Comment comment = 1;
301+}
302+
303+// ResolveCommentRequest changes the resolved state of a comment.
304+message ResolveCommentRequest {
305+ // Repo is the repository name.
306+ string repo = 1;
307+ // Number is the merge request number.
308+ int64 number = 2;
309+ // CommentId is the Comment.id to change.
310+ string comment_id = 3;
311+ // Resolved is the new state: true to resolve, false to unresolve.
312+ bool resolved = 4;
313+}
314+
315+// ResolveCommentResponse holds the comment after the change.
316+message ResolveCommentResponse {
317+ // Comment is the updated comment.
318+ Comment comment = 1;
319+}
320+
321+// CloseMergeRequestRequest names the merge request to close.
322+message CloseMergeRequestRequest {
323+ // Repo is the repository name.
324+ string repo = 1;
325+ // Number is the merge request number.
326+ int64 number = 2;
327+}
328+
329+// CloseMergeRequestResponse holds the merge request after closing.
330+message CloseMergeRequestResponse {
331+ // MergeRequest is the closed merge request.
332+ MergeRequest merge_request = 1;
333+}
334+
335+// MergeMergeRequestRequest describes the merge to perform.
336+message MergeMergeRequestRequest {
337+ // Repo is the repository name.
338+ string repo = 1;
339+ // Number is the merge request number.
340+ int64 number = 2;
341+ // Mode is how to merge. MERGE_MODE_UNSPECIFIED means fast-forward. The mode
342+ // must be in RepoSettings.allowed_merge_modes of the repository.
343+ MergeMode mode = 3;
344+ // HeadId is the full id of the commit the caller reviewed, that is the
345+ // MergeRequest.head_id it was shown. Required. The merge happens only if the
346+ // source branch still points to it.
347+ string head_id = 4;
348+}
349+
350+// MergeMergeRequestResponse holds the merged merge request.
351+message MergeMergeRequestResponse {
352+ // MergeRequest is the merge request after the merge: state MERGED,
353+ // merged_commit_id set to the new tip of the target branch and merge_mode
354+ // set to the mode used.
355+ MergeRequest merge_request = 1;
356+}
357+
358+// MarkReadRequest records a read position.
359+message MarkReadRequest {
360+ // Repo is the repository name.
361+ string repo = 1;
362+ // Number is the merge request number.
363+ int64 number = 2;
364+ // HeadId is the full id of the commit the caller has reviewed, usually the
365+ // MergeRequest.head_id it was shown. Empty means the current head of the
366+ // source branch.
367+ string head_id = 3;
368+}
369+
370+// MarkReadResponse is empty.
371+message MarkReadResponse {}
@@ -0,0 +1,371 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "ermite/git.proto";
8+import "ermite/types.proto";
9+import "google/protobuf/timestamp.proto";
10+
11+// MergeRequestState is where a merge request is in its life.
12+enum MergeRequestState {
13+ // Not set. In ListMergeRequestsRequest.state it means every state. Never
14+ // returned by the server.
15+ MERGE_REQUEST_STATE_UNSPECIFIED = 0;
16+ // The merge request awaits review; it can be commented on, merged or closed.
17+ MERGE_REQUEST_STATE_OPEN = 1;
18+ // The source branch was merged into the target. Final.
19+ MERGE_REQUEST_STATE_MERGED = 2;
20+ // The merge request was closed without merging. Final.
21+ MERGE_REQUEST_STATE_CLOSED = 3;
22+}
23+
24+// DiffSide is the side of a diff a line comment is attached to.
25+enum DiffSide {
26+ // Not set. On a Comment it means the comment is general, not attached to a
27+ // line. In AddCommentRequest it is valid only for a general comment.
28+ DIFF_SIDE_UNSPECIFIED = 0;
29+ // The old side: the line is a Line.old_number (a deletion or a context line).
30+ DIFF_SIDE_OLD = 1;
31+ // The new side: the line is a Line.new_number (an addition or a context
32+ // line).
33+ DIFF_SIDE_NEW = 2;
34+}
35+
36+// MergeRequest asks to merge one branch of a repository into another branch
37+// of the same repository. It is about a branch, not about who wrote it.
38+message MergeRequest {
39+ // Repo is the name of the repository.
40+ string repo = 1;
41+ // Number identifies the merge request within its repository, starting at 1
42+ // and never reused. It is written "!<number>" in messages and URLs.
43+ int64 number = 2;
44+ // Title is a one-line plain-text title, at most 120 characters.
45+ string title = 3;
46+ // Description is a plain-text description, at most 8,000 characters. It may
47+ // be empty. Clients show it escaped, with line breaks kept.
48+ string description = 4;
49+ // SourceBranch is the short name of the branch to merge, for example
50+ // "feat/x".
51+ string source_branch = 5;
52+ // TargetBranch is the short name of the branch to merge into, for example
53+ // "main".
54+ string target_branch = 6;
55+ // State is where the merge request is in its life.
56+ MergeRequestState state = 7;
57+ // Author names who created the merge request: the label of the token that
58+ // was used, "root" for the root token, or "owner" for a web session. It is
59+ // informational: labels are not unique and the token may be gone.
60+ string author = 8;
61+ // CreatedAt is when the merge request was created.
62+ google.protobuf.Timestamp created_at = 9;
63+ // UpdatedAt is when the merge request last changed: title, description,
64+ // state, or a comment added or resolved. Pushes to the source branch do not
65+ // change it.
66+ google.protobuf.Timestamp updated_at = 10;
67+ // HeadId is the id of the commit the source branch points to. While the
68+ // merge request is open it is read from Git at every call, so it changes
69+ // when the branch is pushed; it is empty if the branch was deleted. Once
70+ // merged or closed it is the head at that moment.
71+ string head_id = 11;
72+ // MergedCommitId is the id of the commit the target branch points to after
73+ // the merge: the head for a fast-forward, the new commit for a squash.
74+ // Empty unless state is MERGED.
75+ string merged_commit_id = 12;
76+ // MergeMode is how the merge request was merged. MERGE_MODE_UNSPECIFIED
77+ // unless state is MERGED.
78+ MergeMode merge_mode = 13;
79+}
80+
81+// Comment is a note on a merge request, either general or attached to a line
82+// of its diff.
83+message Comment {
84+ // Id identifies the comment within its merge request. Opaque.
85+ string id = 1;
86+ // Author names who wrote the comment, like MergeRequest.author.
87+ string author = 2;
88+ // Path is the path of the commented file in the diff (the new path, or the
89+ // old path of a deleted file). Empty for a general comment.
90+ string path = 3;
91+ // Line is the 1-based line number on side. 0 for a general comment.
92+ int32 line = 4;
93+ // Side tells whether line counts on the old or the new side of the diff.
94+ // DIFF_SIDE_UNSPECIFIED for a general comment.
95+ DiffSide side = 5;
96+ // CommitId is the head of the merge request when the comment was written.
97+ // When it differs from the current head the line may have moved; clients
98+ // may show the comment as outdated.
99+ string commit_id = 6;
100+ // Text is the plain-text content, at most 8,000 characters. Clients show it
101+ // escaped, with line breaks kept.
102+ string text = 7;
103+ // Resolved is true once the comment was marked resolved.
104+ bool resolved = 8;
105+ // CreatedAt is when the comment was written.
106+ google.protobuf.Timestamp created_at = 9;
107+}
108+
109+// MergeRequestService reviews and merges branches.
110+//
111+// Every RPC takes the repository name and, except for creation and listing,
112+// the merge request number. A repository that does not exist or that the
113+// caller cannot read gives not_found; so does an unknown number. Reading
114+// (ListMergeRequests, GetMergeRequest) needs the same access as reading the
115+// repository: nothing for a public one, the repo:read scope for a private
116+// one. Writing needs a scope on the repository, given per RPC; a caller
117+// without it gets permission_denied.
118+//
119+// Text fields follow the same rules everywhere: valid UTF-8, no control
120+// characters (except the line break in description and comment text) and no
121+// Unicode bidirectional override or isolate characters; title is one line of
122+// at most 120 characters, description and comment text at most 8,000
123+// characters. A request that breaks a rule is rejected whole with
124+// invalid_argument, and the error carries a BadRequest detail naming the
125+// fields.
126+//
127+// Servers that implement this service list "merge_requests" in
128+// GetVersionResponse.features.
129+service MergeRequestService {
130+ // CreateMergeRequest opens a merge request. Needs the mr:write scope. Fails
131+ // with invalid_argument if a text rule is broken, if a branch does not exist
132+ // or if source and target are the same branch, and with already_exists if an
133+ // open merge request already has the same source and target.
134+ rpc CreateMergeRequest(CreateMergeRequestRequest) returns (CreateMergeRequestResponse);
135+ // ListMergeRequests lists the merge requests of a repository, highest
136+ // number (newest) first, optionally restricted to one state. The listed
137+ // merge requests carry no diff or comments; head_id is set. Fails with
138+ // not_found if the repository does not exist or is not readable.
139+ rpc ListMergeRequests(ListMergeRequestsRequest) returns (ListMergeRequestsResponse);
140+ // GetMergeRequest returns one merge request with its commits, its diff and
141+ // its comments. The diff compares the target with the head (what the merge
142+ // would bring: "target...head"), or, with since_last_read, the head the
143+ // caller last marked read with the current head. Fails with not_found if
144+ // the repository or the number is unknown.
145+ rpc GetMergeRequest(GetMergeRequestRequest) returns (GetMergeRequestResponse);
146+ // AddComment adds a general comment or a comment on a line of the diff.
147+ // Needs the mr:write scope. Fails with invalid_argument if the text breaks
148+ // a rule or if path, line and side do not designate a line of the current
149+ // diff of the merge request, and with failed_precondition if the merge
150+ // request is not open.
151+ rpc AddComment(AddCommentRequest) returns (AddCommentResponse);
152+ // ResolveComment marks a comment resolved or unresolved. Needs the mr:write
153+ // scope. Fails with not_found if the comment is unknown and with
154+ // failed_precondition if the merge request is not open. Setting the state
155+ // the comment already has succeeds and changes nothing.
156+ rpc ResolveComment(ResolveCommentRequest) returns (ResolveCommentResponse);
157+ // CloseMergeRequest closes an open merge request without merging it. Needs
158+ // the mr:write scope. Fails with failed_precondition if the merge request
159+ // is not open. A closed merge request cannot be reopened: open a new one.
160+ rpc CloseMergeRequest(CloseMergeRequestRequest) returns (CloseMergeRequestResponse);
161+ // MergeMergeRequest merges an open merge request. Needs the mr:merge scope.
162+ // The caller passes the head it reviewed: the server merges only if the
163+ // source branch still points to it, so a push between review and merge is
164+ // never merged unseen. Fails with invalid_argument if head_id is empty or
165+ // not a commit id, and with failed_precondition if the merge request is not
166+ // open, if the source branch no longer points to head_id ("branch moved,
167+ // read again"), if the target is not an ancestor of the head (rebase and
168+ // push again), if the mode is not allowed by RepoSettings, or if a check
169+ // fails and RepoSettings.failing_checks_block is set. Merges of one
170+ // repository run one at a time. Repeating a call that succeeded fails with
171+ // failed_precondition (not open). On success the server publishes
172+ // MergeRequestMerged then Pushed for the target branch.
173+ rpc MergeMergeRequest(MergeMergeRequestRequest) returns (MergeMergeRequestResponse);
174+ // MarkRead records the head the caller has reviewed, for the since_last_read
175+ // option of GetMergeRequest. There is one read position per merge request,
176+ // the reviewer's. Needs the mr:write scope. Marking a commit that does not
177+ // exist in the repository is invalid_argument.
178+ rpc MarkRead(MarkReadRequest) returns (MarkReadResponse);
179+}
180+
181+// CreateMergeRequestRequest describes the merge request to open.
182+message CreateMergeRequestRequest {
183+ // Repo is the repository name.
184+ string repo = 1;
185+ // Title is the one-line title. Required.
186+ string title = 2;
187+ // Description is the optional description.
188+ string description = 3;
189+ // SourceBranch is the short name of the branch to merge. Required; the
190+ // branch must exist.
191+ string source_branch = 4;
192+ // TargetBranch is the short name of the branch to merge into. Empty means
193+ // the default branch of the repository. The branch must exist and differ
194+ // from source_branch.
195+ string target_branch = 5;
196+}
197+
198+// CreateMergeRequestResponse holds the new merge request.
199+message CreateMergeRequestResponse {
200+ // MergeRequest is the created merge request.
201+ MergeRequest merge_request = 1;
202+}
203+
204+// ListMergeRequestsRequest selects a page of merge requests.
205+message ListMergeRequestsRequest {
206+ // Repo is the repository name.
207+ string repo = 1;
208+ // State restricts the result to one state. MERGE_REQUEST_STATE_UNSPECIFIED
209+ // means every state.
210+ MergeRequestState state = 2;
211+ // PageSize is the maximum number of merge requests to return. 0 means the
212+ // server default; values above the server maximum are lowered to it.
213+ int32 page_size = 3;
214+ // PageToken is the next_page_token of a previous response, to continue the
215+ // same listing. Empty for the first page. Other fields must be unchanged.
216+ string page_token = 4;
217+}
218+
219+// ListMergeRequestsResponse is one page of merge requests.
220+message ListMergeRequestsResponse {
221+ // MergeRequests are the merge requests of this page, highest number first.
222+ repeated MergeRequest merge_requests = 1;
223+ // NextPageToken is passed as page_token to get the next page. Empty when
224+ // there is no next page.
225+ string next_page_token = 2;
226+}
227+
228+// GetMergeRequestRequest selects a merge request.
229+message GetMergeRequestRequest {
230+ // Repo is the repository name.
231+ string repo = 1;
232+ // Number is the merge request number.
233+ int64 number = 2;
234+ // SinceLastRead asks for the diff between the head recorded by MarkRead and
235+ // the current head instead of the diff against the target. It works after a
236+ // force-push as long as the recorded commit still exists in the repository.
237+ // When nothing was marked read, or the recorded commit is gone, the response
238+ // holds the diff against the target and says so in last_read_id and
239+ // last_read_lost.
240+ bool since_last_read = 3;
241+}
242+
243+// GetMergeRequestResponse holds a merge request with its review data.
244+message GetMergeRequestResponse {
245+ // MergeRequest is the merge request.
246+ MergeRequest merge_request = 1;
247+ // Commits are the commits of the head that are not in the target
248+ // ("target..head"), newest first. At most 100 are returned (a server limit
249+ // that may change; rely on commits_truncated, not on the number). Empty when
250+ // the source branch was deleted.
251+ repeated Commit commits = 2;
252+ // CommitsTruncated is true when the branch has more commits than commits
253+ // holds.
254+ bool commits_truncated = 3;
255+ // DiffBaseId is the id of the commit the diff compares from: the merge base
256+ // of the target and the head, or the last read head when since_last_read
257+ // applied. Empty when the source branch was deleted; diff is then empty.
258+ string diff_base_id = 4;
259+ // Stats are the totals of the diff, including files that it omits or
260+ // truncates.
261+ DiffStats stats = 5;
262+ // Diff is the structured diff from diff_base_id to the head, with the same
263+ // limits and truncated flags as GetCommitResponse.diff.
264+ Diff diff = 6;
265+ // Comments are every comment of the merge request, oldest first.
266+ repeated Comment comments = 7;
267+ // LastReadId is the head recorded by MarkRead, whether or not
268+ // since_last_read was set. Empty when nothing was marked read.
269+ string last_read_id = 8;
270+ // LastReadLost is true when since_last_read was set, a head was recorded,
271+ // but that commit no longer exists in the repository: the diff is then the
272+ // diff against the target. A client should tell the reader to review the
273+ // whole change again.
274+ bool last_read_lost = 9;
275+}
276+
277+// AddCommentRequest describes the comment to add.
278+message AddCommentRequest {
279+ // Repo is the repository name.
280+ string repo = 1;
281+ // Number is the merge request number.
282+ int64 number = 2;
283+ // Path is the file the comment is attached to, as given by the diff of the
284+ // merge request: FileDiff.new_path, or old_path for a deleted file. Empty
285+ // for a general comment; then line must be 0 and side unspecified.
286+ string path = 3;
287+ // Line is the 1-based line number on side. It must be the old_number or
288+ // new_number of a Line of a hunk of the file; a line outside the hunks is
289+ // invalid_argument. 0 for a general comment.
290+ int32 line = 4;
291+ // Side is the side line counts on. Required when path is set.
292+ DiffSide side = 5;
293+ // Text is the plain-text content. Required.
294+ string text = 6;
295+}
296+
297+// AddCommentResponse holds the new comment.
298+message AddCommentResponse {
299+ // Comment is the created comment.
300+ Comment comment = 1;
301+}
302+
303+// ResolveCommentRequest changes the resolved state of a comment.
304+message ResolveCommentRequest {
305+ // Repo is the repository name.
306+ string repo = 1;
307+ // Number is the merge request number.
308+ int64 number = 2;
309+ // CommentId is the Comment.id to change.
310+ string comment_id = 3;
311+ // Resolved is the new state: true to resolve, false to unresolve.
312+ bool resolved = 4;
313+}
314+
315+// ResolveCommentResponse holds the comment after the change.
316+message ResolveCommentResponse {
317+ // Comment is the updated comment.
318+ Comment comment = 1;
319+}
320+
321+// CloseMergeRequestRequest names the merge request to close.
322+message CloseMergeRequestRequest {
323+ // Repo is the repository name.
324+ string repo = 1;
325+ // Number is the merge request number.
326+ int64 number = 2;
327+}
328+
329+// CloseMergeRequestResponse holds the merge request after closing.
330+message CloseMergeRequestResponse {
331+ // MergeRequest is the closed merge request.
332+ MergeRequest merge_request = 1;
333+}
334+
335+// MergeMergeRequestRequest describes the merge to perform.
336+message MergeMergeRequestRequest {
337+ // Repo is the repository name.
338+ string repo = 1;
339+ // Number is the merge request number.
340+ int64 number = 2;
341+ // Mode is how to merge. MERGE_MODE_UNSPECIFIED means fast-forward. The mode
342+ // must be in RepoSettings.allowed_merge_modes of the repository.
343+ MergeMode mode = 3;
344+ // HeadId is the full id of the commit the caller reviewed, that is the
345+ // MergeRequest.head_id it was shown. Required. The merge happens only if the
346+ // source branch still points to it.
347+ string head_id = 4;
348+}
349+
350+// MergeMergeRequestResponse holds the merged merge request.
351+message MergeMergeRequestResponse {
352+ // MergeRequest is the merge request after the merge: state MERGED,
353+ // merged_commit_id set to the new tip of the target branch and merge_mode
354+ // set to the mode used.
355+ MergeRequest merge_request = 1;
356+}
357+
358+// MarkReadRequest records a read position.
359+message MarkReadRequest {
360+ // Repo is the repository name.
361+ string repo = 1;
362+ // Number is the merge request number.
363+ int64 number = 2;
364+ // HeadId is the full id of the commit the caller has reviewed, usually the
365+ // MergeRequest.head_id it was shown. Empty means the current head of the
366+ // source branch.
367+ string head_id = 3;
368+}
369+
370+// MarkReadResponse is empty.
371+message MarkReadResponse {}
M proto/ermite/repo.proto +33 -5
@@ -5,6 +5,7 @@ syntax = "proto3";
55 package ermite;
66
77 import "google/protobuf/field_mask.proto";
8+import "ermite/types.proto";
89 import "google/protobuf/timestamp.proto";
910
1011 // Visibility says who may read a repository.
@@ -19,6 +20,21 @@ enum Visibility {
1920 VISIBILITY_PRIVATE = 2;
2021 }
2122
23+// RepoSettings are the settings of a repository that govern its merge
24+// requests.
25+message RepoSettings {
26+ // AllowedMergeModes are the modes with which a merge request of the
27+ // repository may be merged. It is never empty in a response and has no
28+ // duplicate. Both MERGE_MODE_FAST_FORWARD and MERGE_MODE_SQUASH are allowed
29+ // by default. MERGE_MODE_UNSPECIFIED is rejected.
30+ repeated MergeMode allowed_merge_modes = 1;
31+ // FailingChecksBlock says whether a merge request whose checks fail cannot be
32+ // merged. It is true at creation of the repository and always stored
33+ // explicitly, so a later change of the server default never alters an
34+ // existing repository.
35+ bool failing_checks_block = 2;
36+}
37+
2238 // Repo is the metadata of a repository. Git-owned state (refs, trees, history)
2339 // is read through GitService.
2440 message Repo {
@@ -37,6 +53,9 @@ message Repo {
3753 google.protobuf.Timestamp created_at = 5;
3854 // PushedAt is the time of the last push. It is unset until the first push.
3955 google.protobuf.Timestamp pushed_at = 6;
56+ // Settings are the merge settings of the repository. Always set in a
57+ // response, with the defaults for a repository that never changed them.
58+ RepoSettings settings = 7;
4059 }
4160
4261 // RepoService manages repositories and their metadata.
@@ -49,12 +68,13 @@ service RepoService {
4968 // GetRepo returns one repository. Fails with not_found if it does not exist
5069 // or is private and not readable by the caller.
5170 rpc GetRepo(GetRepoRequest) returns (GetRepoResponse);
52- // CreateRepo creates an empty repository. Needs the repo:write scope. Fails
71+ // CreateRepo creates an empty repository with the default settings. Needs
72+ // the repo:write scope. Fails
5373 // with already_exists if the name is taken and invalid_argument if the name
5474 // is not valid.
5575 rpc CreateRepo(CreateRepoRequest) returns (CreateRepoResponse);
56- // UpdateRepo changes the fields named in update_mask. Needs the repo:admin
57- // scope.
76+ // UpdateRepo changes the fields named in update_mask, including the merge
77+ // settings. Needs the repo:admin scope.
5878 rpc UpdateRepo(UpdateRepoRequest) returns (UpdateRepoResponse);
5979 // DeleteRepo deletes a repository. Needs the repo:admin scope. The server
6080 // moves the data aside rather than destroying it; there is no undelete RPC.
@@ -124,9 +144,17 @@ message UpdateRepoRequest {
124144 // update_mask contains "default_branch". The branch must exist, otherwise
125145 // the call fails with failed_precondition.
126146 string default_branch = 4;
127- // UpdateMask lists the fields to change: "description", "visibility" and/or
128- // "default_branch". It must not be empty. Other paths are invalid_argument.
147+ // UpdateMask lists the fields to change: "description", "visibility",
148+ // "default_branch", "settings.allowed_merge_modes" and/or
149+ // "settings.failing_checks_block". "settings" stands for both settings
150+ // paths. It must not be empty. Other paths are invalid_argument.
129151 google.protobuf.FieldMask update_mask = 5;
152+ // Settings holds the new merge settings. Only the fields named in
153+ // update_mask are used. allowed_merge_modes must then not be empty nor
154+ // contain MERGE_MODE_UNSPECIFIED, otherwise the call fails with
155+ // invalid_argument. Changing settings needs the repo:admin scope, like any
156+ // other field.
157+ RepoSettings settings = 6;
130158 }
131159
132160 // UpdateRepoResponse holds the repository after the update.
@@ -5,6 +5,7 @@ syntax = "proto3";
5 package ermite;5 package ermite;
6 6
7 import "google/protobuf/field_mask.proto";7 import "google/protobuf/field_mask.proto";
8+import "ermite/types.proto";
8 import "google/protobuf/timestamp.proto";9 import "google/protobuf/timestamp.proto";
9 10
10 // Visibility says who may read a repository.11 // Visibility says who may read a repository.
@@ -19,6 +20,21 @@ enum Visibility {
19 VISIBILITY_PRIVATE = 2;20 VISIBILITY_PRIVATE = 2;
20 }21 }
21 22
23+// RepoSettings are the settings of a repository that govern its merge
24+// requests.
25+message RepoSettings {
26+ // AllowedMergeModes are the modes with which a merge request of the
27+ // repository may be merged. It is never empty in a response and has no
28+ // duplicate. Both MERGE_MODE_FAST_FORWARD and MERGE_MODE_SQUASH are allowed
29+ // by default. MERGE_MODE_UNSPECIFIED is rejected.
30+ repeated MergeMode allowed_merge_modes = 1;
31+ // FailingChecksBlock says whether a merge request whose checks fail cannot be
32+ // merged. It is true at creation of the repository and always stored
33+ // explicitly, so a later change of the server default never alters an
34+ // existing repository.
35+ bool failing_checks_block = 2;
36+}
37+
22 // Repo is the metadata of a repository. Git-owned state (refs, trees, history)38 // Repo is the metadata of a repository. Git-owned state (refs, trees, history)
23 // is read through GitService.39 // is read through GitService.
24 message Repo {40 message Repo {
@@ -37,6 +53,9 @@ message Repo {
37 google.protobuf.Timestamp created_at = 5;53 google.protobuf.Timestamp created_at = 5;
38 // PushedAt is the time of the last push. It is unset until the first push.54 // PushedAt is the time of the last push. It is unset until the first push.
39 google.protobuf.Timestamp pushed_at = 6;55 google.protobuf.Timestamp pushed_at = 6;
56+ // Settings are the merge settings of the repository. Always set in a
57+ // response, with the defaults for a repository that never changed them.
58+ RepoSettings settings = 7;
40 }59 }
41 60
42 // RepoService manages repositories and their metadata.61 // RepoService manages repositories and their metadata.
@@ -49,12 +68,13 @@ service RepoService {
49 // GetRepo returns one repository. Fails with not_found if it does not exist68 // GetRepo returns one repository. Fails with not_found if it does not exist
50 // or is private and not readable by the caller.69 // or is private and not readable by the caller.
51 rpc GetRepo(GetRepoRequest) returns (GetRepoResponse);70 rpc GetRepo(GetRepoRequest) returns (GetRepoResponse);
52- // CreateRepo creates an empty repository. Needs the repo:write scope. Fails71+ // CreateRepo creates an empty repository with the default settings. Needs
72+ // the repo:write scope. Fails
53 // with already_exists if the name is taken and invalid_argument if the name73 // with already_exists if the name is taken and invalid_argument if the name
54 // is not valid.74 // is not valid.
55 rpc CreateRepo(CreateRepoRequest) returns (CreateRepoResponse);75 rpc CreateRepo(CreateRepoRequest) returns (CreateRepoResponse);
56- // UpdateRepo changes the fields named in update_mask. Needs the repo:admin76+ // UpdateRepo changes the fields named in update_mask, including the merge
57- // scope.77+ // settings. Needs the repo:admin scope.
58 rpc UpdateRepo(UpdateRepoRequest) returns (UpdateRepoResponse);78 rpc UpdateRepo(UpdateRepoRequest) returns (UpdateRepoResponse);
59 // DeleteRepo deletes a repository. Needs the repo:admin scope. The server79 // DeleteRepo deletes a repository. Needs the repo:admin scope. The server
60 // moves the data aside rather than destroying it; there is no undelete RPC.80 // moves the data aside rather than destroying it; there is no undelete RPC.
@@ -124,9 +144,17 @@ message UpdateRepoRequest {
124 // update_mask contains "default_branch". The branch must exist, otherwise144 // update_mask contains "default_branch". The branch must exist, otherwise
125 // the call fails with failed_precondition.145 // the call fails with failed_precondition.
126 string default_branch = 4;146 string default_branch = 4;
127- // UpdateMask lists the fields to change: "description", "visibility" and/or147+ // UpdateMask lists the fields to change: "description", "visibility",
128- // "default_branch". It must not be empty. Other paths are invalid_argument.148+ // "default_branch", "settings.allowed_merge_modes" and/or
149+ // "settings.failing_checks_block". "settings" stands for both settings
150+ // paths. It must not be empty. Other paths are invalid_argument.
129 google.protobuf.FieldMask update_mask = 5;151 google.protobuf.FieldMask update_mask = 5;
152+ // Settings holds the new merge settings. Only the fields named in
153+ // update_mask are used. allowed_merge_modes must then not be empty nor
154+ // contain MERGE_MODE_UNSPECIFIED, otherwise the call fails with
155+ // invalid_argument. Changing settings needs the repo:admin scope, like any
156+ // other field.
157+ RepoSettings settings = 6;
130 }158 }
131 159
132 // UpdateRepoResponse holds the repository after the update.160 // UpdateRepoResponse holds the repository after the update.
M proto/ermite/server.proto +30 -0
@@ -10,6 +10,12 @@ service ServerService {
1010 // contract it implements. It needs no authentication, so a client can call
1111 // it first to check compatibility.
1212 rpc GetVersion(GetVersionRequest) returns (GetVersionResponse);
13+ // GetSigningKey returns the public key with which the server signs the
14+ // commits it creates (squash merges, see MergeMode). It needs no
15+ // authentication: the key is public, and anyone verifying a commit needs it.
16+ // Servers that implement it list "signing_key" in
17+ // GetVersionResponse.features.
18+ rpc GetSigningKey(GetSigningKeyRequest) returns (GetSigningKeyResponse);
1319 }
1420
1521 // GetVersionRequest is empty.
@@ -28,3 +34,27 @@ message GetVersionResponse {
2834 // Identifiers are lowercase, dot-separated and never reused.
2935 repeated string features = 2;
3036 }
37+
38+// GetSigningKeyRequest is empty.
39+message GetSigningKeyRequest {}
40+
41+// GetSigningKeyResponse describes the commit signing key of the server.
42+message GetSigningKeyResponse {
43+ // PublicKey is the public key as one OpenSSH authorized_keys line, for
44+ // example "ssh-ed25519 AAAA...". Commits are signed in the SSH signature
45+ // format (SSHSIG).
46+ string public_key = 1;
47+ // Fingerprint is the SHA-256 fingerprint of the key, in the form
48+ // "SHA256:<base64>" that ssh-keygen prints.
49+ string fingerprint = 2;
50+ // CommitterName is the name the server records as the committer of the
51+ // commits it creates.
52+ string committer_name = 3;
53+ // CommitterEmail is the email address the server records as the committer of
54+ // the commits it creates. It is the principal of allowed_signers.
55+ string committer_email = 4;
56+ // AllowedSigners is a ready-to-use line for an allowed signers file
57+ // ("<committer_email> <public_key>"), to verify these commits with
58+ // "git config gpg.ssh.allowedSignersFile".
59+ string allowed_signers = 5;
60+}
@@ -10,6 +10,12 @@ service ServerService {
10 // contract it implements. It needs no authentication, so a client can call10 // contract it implements. It needs no authentication, so a client can call
11 // it first to check compatibility.11 // it first to check compatibility.
12 rpc GetVersion(GetVersionRequest) returns (GetVersionResponse);12 rpc GetVersion(GetVersionRequest) returns (GetVersionResponse);
13+ // GetSigningKey returns the public key with which the server signs the
14+ // commits it creates (squash merges, see MergeMode). It needs no
15+ // authentication: the key is public, and anyone verifying a commit needs it.
16+ // Servers that implement it list "signing_key" in
17+ // GetVersionResponse.features.
18+ rpc GetSigningKey(GetSigningKeyRequest) returns (GetSigningKeyResponse);
13 }19 }
14 20
15 // GetVersionRequest is empty.21 // GetVersionRequest is empty.
@@ -28,3 +34,27 @@ message GetVersionResponse {
28 // Identifiers are lowercase, dot-separated and never reused.34 // Identifiers are lowercase, dot-separated and never reused.
29 repeated string features = 2;35 repeated string features = 2;
30 }36 }
37+
38+// GetSigningKeyRequest is empty.
39+message GetSigningKeyRequest {}
40+
41+// GetSigningKeyResponse describes the commit signing key of the server.
42+message GetSigningKeyResponse {
43+ // PublicKey is the public key as one OpenSSH authorized_keys line, for
44+ // example "ssh-ed25519 AAAA...". Commits are signed in the SSH signature
45+ // format (SSHSIG).
46+ string public_key = 1;
47+ // Fingerprint is the SHA-256 fingerprint of the key, in the form
48+ // "SHA256:<base64>" that ssh-keygen prints.
49+ string fingerprint = 2;
50+ // CommitterName is the name the server records as the committer of the
51+ // commits it creates.
52+ string committer_name = 3;
53+ // CommitterEmail is the email address the server records as the committer of
54+ // the commits it creates. It is the principal of allowed_signers.
55+ string committer_email = 4;
56+ // AllowedSigners is a ready-to-use line for an allowed signers file
57+ // ("<committer_email> <public_key>"), to verify these commits with
58+ // "git config gpg.ssh.allowedSignersFile".
59+ string allowed_signers = 5;
60+}
A proto/ermite/session.proto +51 -0
@@ -0,0 +1,51 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "google/protobuf/timestamp.proto";
8+
9+// SessionService manages the owner's browser sessions. The browser itself
10+// never calls it: it signs in by opening the link that CreateLoginLink
11+// returns, and signs out by posting to the web's logout route.
12+//
13+// Every RPC is owner-only: it needs the root token, or a stored token that
14+// holds every scope and is not restricted to repositories. Any other caller
15+// gets permission_denied. Sessions do not go through this API: the Connect
16+// API accepts bearer tokens only and ignores cookies.
17+//
18+// Servers that implement this service list "session" in
19+// GetVersionResponse.features.
20+service SessionService {
21+ // CreateLoginLink creates a one-time link that signs the owner's browser
22+ // in. The link is valid for a short time (about a minute), can be used
23+ // once, and is kept in memory only: a server restart voids it. Opening it
24+ // shows a confirmation page; only confirming it consumes the link and sets
25+ // the session. Creating a new link does not void an earlier one. Fails with
26+ // resource_exhausted when too many links are pending.
27+ rpc CreateLoginLink(CreateLoginLinkRequest) returns (CreateLoginLinkResponse);
28+ // SignOutEverywhere ends every browser session at once, including those on
29+ // other devices. Pending login links are not affected. Tokens are not
30+ // affected either: revoke them with TokenService.
31+ rpc SignOutEverywhere(SignOutEverywhereRequest) returns (SignOutEverywhereResponse);
32+}
33+
34+// CreateLoginLinkRequest is empty.
35+message CreateLoginLinkRequest {}
36+
37+// CreateLoginLinkResponse holds the link.
38+message CreateLoginLinkResponse {
39+ // Url is the absolute link to open in a browser, on the public URL of the
40+ // server. It holds a secret: clients print it to the terminal and nowhere
41+ // else, and never log it.
42+ string url = 1;
43+ // ExpiresAt is when the link stops working if it is not used.
44+ google.protobuf.Timestamp expires_at = 2;
45+}
46+
47+// SignOutEverywhereRequest is empty.
48+message SignOutEverywhereRequest {}
49+
50+// SignOutEverywhereResponse is empty.
51+message SignOutEverywhereResponse {}
@@ -0,0 +1,51 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "google/protobuf/timestamp.proto";
8+
9+// SessionService manages the owner's browser sessions. The browser itself
10+// never calls it: it signs in by opening the link that CreateLoginLink
11+// returns, and signs out by posting to the web's logout route.
12+//
13+// Every RPC is owner-only: it needs the root token, or a stored token that
14+// holds every scope and is not restricted to repositories. Any other caller
15+// gets permission_denied. Sessions do not go through this API: the Connect
16+// API accepts bearer tokens only and ignores cookies.
17+//
18+// Servers that implement this service list "session" in
19+// GetVersionResponse.features.
20+service SessionService {
21+ // CreateLoginLink creates a one-time link that signs the owner's browser
22+ // in. The link is valid for a short time (about a minute), can be used
23+ // once, and is kept in memory only: a server restart voids it. Opening it
24+ // shows a confirmation page; only confirming it consumes the link and sets
25+ // the session. Creating a new link does not void an earlier one. Fails with
26+ // resource_exhausted when too many links are pending.
27+ rpc CreateLoginLink(CreateLoginLinkRequest) returns (CreateLoginLinkResponse);
28+ // SignOutEverywhere ends every browser session at once, including those on
29+ // other devices. Pending login links are not affected. Tokens are not
30+ // affected either: revoke them with TokenService.
31+ rpc SignOutEverywhere(SignOutEverywhereRequest) returns (SignOutEverywhereResponse);
32+}
33+
34+// CreateLoginLinkRequest is empty.
35+message CreateLoginLinkRequest {}
36+
37+// CreateLoginLinkResponse holds the link.
38+message CreateLoginLinkResponse {
39+ // Url is the absolute link to open in a browser, on the public URL of the
40+ // server. It holds a secret: clients print it to the terminal and nowhere
41+ // else, and never log it.
42+ string url = 1;
43+ // ExpiresAt is when the link stops working if it is not used.
44+ google.protobuf.Timestamp expires_at = 2;
45+}
46+
47+// SignOutEverywhereRequest is empty.
48+message SignOutEverywhereRequest {}
49+
50+// SignOutEverywhereResponse is empty.
51+message SignOutEverywhereResponse {}
M proto/ermite/types.proto +20 -0
@@ -35,3 +35,23 @@ message Commit {
3535 // not valid UTF-8 is converted with the replacement character.
3636 string message = 6;
3737 }
38+
39+// MergeMode is how a merge request is merged. Both modes need the target to
40+// be an ancestor of the head: a branch behind its target is rebased by its
41+// author and pushed again, the server never rebases nor creates merge commits.
42+enum MergeMode {
43+ // Not set. In MergeMergeRequestRequest.mode it means fast-forward. Rejected
44+ // in RepoSettings.allowed_merge_modes. Returned on a merge request that was
45+ // not merged.
46+ MERGE_MODE_UNSPECIFIED = 0;
47+ // The target branch moves to the head. No commit is created and the
48+ // signatures of the branch's commits are kept.
49+ MERGE_MODE_FAST_FORWARD = 1;
50+ // One new commit is created with the tree of the head and the target as
51+ // only parent, signed by the server's key (see ServerService.GetSigningKey).
52+ // Its author is the author of the first commit of the branch, the other
53+ // authors are added as "Co-authored-by:" trailers, and its message is the
54+ // title, " (!<number>)", a blank line and the list of the squashed commits'
55+ // summaries.
56+ MERGE_MODE_SQUASH = 2;
57+}
@@ -35,3 +35,23 @@ message Commit {
35 // not valid UTF-8 is converted with the replacement character.35 // not valid UTF-8 is converted with the replacement character.
36 string message = 6;36 string message = 6;
37 }37 }
38+
39+// MergeMode is how a merge request is merged. Both modes need the target to
40+// be an ancestor of the head: a branch behind its target is rebased by its
41+// author and pushed again, the server never rebases nor creates merge commits.
42+enum MergeMode {
43+ // Not set. In MergeMergeRequestRequest.mode it means fast-forward. Rejected
44+ // in RepoSettings.allowed_merge_modes. Returned on a merge request that was
45+ // not merged.
46+ MERGE_MODE_UNSPECIFIED = 0;
47+ // The target branch moves to the head. No commit is created and the
48+ // signatures of the branch's commits are kept.
49+ MERGE_MODE_FAST_FORWARD = 1;
50+ // One new commit is created with the tree of the head and the target as
51+ // only parent, signed by the server's key (see ServerService.GetSigningKey).
52+ // Its author is the author of the first commit of the branch, the other
53+ // authors are added as "Co-authored-by:" trailers, and its message is the
54+ // title, " (!<number>)", a blank line and the list of the squashed commits'
55+ // summaries.
56+ MERGE_MODE_SQUASH = 2;
57+}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 18

↑↓ moveenter openesc close