detached a137f54

feat(api): add repository, git, token, event and server services

commit
a137f54f984aa8c562bf2bb9f3ca6bac0cc2c994
author
brice · · 2026-10-10 06:07 UTC
parent
037b5e9
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
20 files · +6623 -5
M README.md +17 -3
@@ -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, no services defined yet.
5+Status: early development. The contract defines five services (below); the server that implements them is being built.
66
77 ```go
88 import "bnema.dev/ermite-api/gen/ermite"
@@ -10,10 +10,24 @@ import "bnema.dev/ermite-api/gen/ermite"
1010
1111 The contract has no version number. It only changes in backward-compatible ways (new fields, messages, RPCs and services), and CI rejects any breaking change.
1212
13+## Services
14+
15+Handlers are mounted under `/-/api/` by the server. Errors are Connect codes.
16+
17+| Service | RPCs |
18+|---|---|
19+| `RepoService` | `ListRepos`, `GetRepo`, `CreateRepo`, `UpdateRepo` (field mask), `DeleteRepo` |
20+| `GitService` | `ListRefs`, `GetTree`, `GetBlob` (streamed), `ListCommits`, `GetCommit` (structured diff), `ResolveRevision` |
21+| `TokenService` | `CreateToken` (secret shown once), `ListTokens`, `RevokeToken` |
22+| `EventService` | `Watch` (streamed, resumable) |
23+| `ServerService` | `GetVersion` (server version and feature list) |
24+
25+Tokens carry scopes only; presets such as `cli` or `agent` are a client concern.
26+
1327 ## Layout
1428
15-- `proto/ermite/`: `.proto` files of the `ermite` package, checked by `buf lint` and `buf breaking`.
16-- `gen/`: generated Go code, committed. Never edit it by hand.
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`.
30+- `gen/ermite/`: generated Go messages. `gen/ermite/ermiteconnect/`: generated Connect clients and handlers. Committed, never edited by hand.
1731
1832 ## Generate
1933
@@ -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, no services defined yet.5+Status: early development. The contract defines five 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"
@@ -10,10 +10,24 @@ import "bnema.dev/ermite-api/gen/ermite"
10 10
11 The contract has no version number. It only changes in backward-compatible ways (new fields, messages, RPCs and services), and CI rejects any breaking change.11 The contract has no version number. It only changes in backward-compatible ways (new fields, messages, RPCs and services), and CI rejects any breaking change.
12 12
13+## Services
14+
15+Handlers are mounted under `/-/api/` by the server. Errors are Connect codes.
16+
17+| Service | RPCs |
18+|---|---|
19+| `RepoService` | `ListRepos`, `GetRepo`, `CreateRepo`, `UpdateRepo` (field mask), `DeleteRepo` |
20+| `GitService` | `ListRefs`, `GetTree`, `GetBlob` (streamed), `ListCommits`, `GetCommit` (structured diff), `ResolveRevision` |
21+| `TokenService` | `CreateToken` (secret shown once), `ListTokens`, `RevokeToken` |
22+| `EventService` | `Watch` (streamed, resumable) |
23+| `ServerService` | `GetVersion` (server version and feature list) |
24+
25+Tokens carry scopes only; presets such as `cli` or `agent` are a client concern.
26+
13 ## Layout27 ## Layout
14 28
15-- `proto/ermite/`: `.proto` files of the `ermite` package, checked by `buf lint` and `buf breaking`.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`.
16-- `gen/`: generated Go code, committed. Never edit it by hand.30+- `gen/ermite/`: generated Go messages. `gen/ermite/ermiteconnect/`: generated Connect clients and handlers. Committed, never edited by hand.
17 31
18 ## Generate32 ## Generate
19 33
A gen/ermite/ermiteconnect/event.connect.go +128 -0
@@ -0,0 +1,128 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/event.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+ // EventServiceName is the fully-qualified name of the EventService service.
27+ EventServiceName = "ermite.EventService"
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+ // EventServiceWatchProcedure is the fully-qualified name of the EventService's Watch RPC.
39+ EventServiceWatchProcedure = "/ermite.EventService/Watch"
40+)
41+
42+// EventServiceClient is a client for the ermite.EventService service.
43+type EventServiceClient interface {
44+ // Watch streams events until the client cancels the call or the server shuts
45+ // down. Events are filtered by the caller's access: events of a private
46+ // repository are only sent to callers that may read it.
47+ //
48+ // 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.
50+ // When resume_after is no longer buffered (or unknown), the stream begins
51+ // with a Gap event, then continues with the oldest buffered event: the client
52+ // must reload its state. Without resume_after, only new events are sent.
53+ Watch(context.Context, *connect.Request[ermite.WatchRequest]) (*connect.ServerStreamForClient[ermite.WatchResponse], error)
54+}
55+
56+// NewEventServiceClient constructs a client for the ermite.EventService service. By default, it
57+// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
58+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
59+// connect.WithGRPCWeb() options.
60+//
61+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
62+// http://api.acme.com or https://acme.com/grpc).
63+func NewEventServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) EventServiceClient {
64+ baseURL = strings.TrimRight(baseURL, "/")
65+ eventServiceMethods := ermite.File_ermite_event_proto.Services().ByName("EventService").Methods()
66+ return &eventServiceClient{
67+ watch: connect.NewClient[ermite.WatchRequest, ermite.WatchResponse](
68+ httpClient,
69+ baseURL+EventServiceWatchProcedure,
70+ connect.WithSchema(eventServiceMethods.ByName("Watch")),
71+ connect.WithClientOptions(opts...),
72+ ),
73+ }
74+}
75+
76+// eventServiceClient implements EventServiceClient.
77+type eventServiceClient struct {
78+ watch *connect.Client[ermite.WatchRequest, ermite.WatchResponse]
79+}
80+
81+// Watch calls ermite.EventService.Watch.
82+func (c *eventServiceClient) Watch(ctx context.Context, req *connect.Request[ermite.WatchRequest]) (*connect.ServerStreamForClient[ermite.WatchResponse], error) {
83+ return c.watch.CallServerStream(ctx, req)
84+}
85+
86+// EventServiceHandler is an implementation of the ermite.EventService service.
87+type EventServiceHandler interface {
88+ // Watch streams events until the client cancels the call or the server shuts
89+ // down. Events are filtered by the caller's access: events of a private
90+ // repository are only sent to callers that may read it.
91+ //
92+ // The server keeps a bounded buffer of recent events. With resume_after set,
93+ // buffered events after that id are replayed first, then live events follow.
94+ // When resume_after is no longer buffered (or unknown), the stream begins
95+ // with a Gap event, then continues with the oldest buffered event: the client
96+ // must reload its state. Without resume_after, only new events are sent.
97+ Watch(context.Context, *connect.Request[ermite.WatchRequest], *connect.ServerStream[ermite.WatchResponse]) error
98+}
99+
100+// NewEventServiceHandler builds an HTTP handler from the service implementation. It returns the
101+// path on which to mount the handler and the handler itself.
102+//
103+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
104+// and JSON codecs. They also support gzip compression.
105+func NewEventServiceHandler(svc EventServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
106+ eventServiceMethods := ermite.File_ermite_event_proto.Services().ByName("EventService").Methods()
107+ eventServiceWatchHandler := connect.NewServerStreamHandler(
108+ EventServiceWatchProcedure,
109+ svc.Watch,
110+ connect.WithSchema(eventServiceMethods.ByName("Watch")),
111+ connect.WithHandlerOptions(opts...),
112+ )
113+ return "/ermite.EventService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
114+ switch r.URL.Path {
115+ case EventServiceWatchProcedure:
116+ eventServiceWatchHandler.ServeHTTP(w, r)
117+ default:
118+ http.NotFound(w, r)
119+ }
120+ })
121+}
122+
123+// UnimplementedEventServiceHandler returns CodeUnimplemented from all methods.
124+type UnimplementedEventServiceHandler struct{}
125+
126+func (UnimplementedEventServiceHandler) Watch(context.Context, *connect.Request[ermite.WatchRequest], *connect.ServerStream[ermite.WatchResponse]) error {
127+ return connect.NewError(connect.CodeUnimplemented, errors.New("ermite.EventService.Watch is not implemented"))
128+}
@@ -0,0 +1,128 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/event.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+ // EventServiceName is the fully-qualified name of the EventService service.
27+ EventServiceName = "ermite.EventService"
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+ // EventServiceWatchProcedure is the fully-qualified name of the EventService's Watch RPC.
39+ EventServiceWatchProcedure = "/ermite.EventService/Watch"
40+)
41+
42+// EventServiceClient is a client for the ermite.EventService service.
43+type EventServiceClient interface {
44+ // Watch streams events until the client cancels the call or the server shuts
45+ // down. Events are filtered by the caller's access: events of a private
46+ // repository are only sent to callers that may read it.
47+ //
48+ // 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.
50+ // When resume_after is no longer buffered (or unknown), the stream begins
51+ // with a Gap event, then continues with the oldest buffered event: the client
52+ // must reload its state. Without resume_after, only new events are sent.
53+ Watch(context.Context, *connect.Request[ermite.WatchRequest]) (*connect.ServerStreamForClient[ermite.WatchResponse], error)
54+}
55+
56+// NewEventServiceClient constructs a client for the ermite.EventService service. By default, it
57+// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
58+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
59+// connect.WithGRPCWeb() options.
60+//
61+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
62+// http://api.acme.com or https://acme.com/grpc).
63+func NewEventServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) EventServiceClient {
64+ baseURL = strings.TrimRight(baseURL, "/")
65+ eventServiceMethods := ermite.File_ermite_event_proto.Services().ByName("EventService").Methods()
66+ return &eventServiceClient{
67+ watch: connect.NewClient[ermite.WatchRequest, ermite.WatchResponse](
68+ httpClient,
69+ baseURL+EventServiceWatchProcedure,
70+ connect.WithSchema(eventServiceMethods.ByName("Watch")),
71+ connect.WithClientOptions(opts...),
72+ ),
73+ }
74+}
75+
76+// eventServiceClient implements EventServiceClient.
77+type eventServiceClient struct {
78+ watch *connect.Client[ermite.WatchRequest, ermite.WatchResponse]
79+}
80+
81+// Watch calls ermite.EventService.Watch.
82+func (c *eventServiceClient) Watch(ctx context.Context, req *connect.Request[ermite.WatchRequest]) (*connect.ServerStreamForClient[ermite.WatchResponse], error) {
83+ return c.watch.CallServerStream(ctx, req)
84+}
85+
86+// EventServiceHandler is an implementation of the ermite.EventService service.
87+type EventServiceHandler interface {
88+ // Watch streams events until the client cancels the call or the server shuts
89+ // down. Events are filtered by the caller's access: events of a private
90+ // repository are only sent to callers that may read it.
91+ //
92+ // The server keeps a bounded buffer of recent events. With resume_after set,
93+ // buffered events after that id are replayed first, then live events follow.
94+ // When resume_after is no longer buffered (or unknown), the stream begins
95+ // with a Gap event, then continues with the oldest buffered event: the client
96+ // must reload its state. Without resume_after, only new events are sent.
97+ Watch(context.Context, *connect.Request[ermite.WatchRequest], *connect.ServerStream[ermite.WatchResponse]) error
98+}
99+
100+// NewEventServiceHandler builds an HTTP handler from the service implementation. It returns the
101+// path on which to mount the handler and the handler itself.
102+//
103+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
104+// and JSON codecs. They also support gzip compression.
105+func NewEventServiceHandler(svc EventServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
106+ eventServiceMethods := ermite.File_ermite_event_proto.Services().ByName("EventService").Methods()
107+ eventServiceWatchHandler := connect.NewServerStreamHandler(
108+ EventServiceWatchProcedure,
109+ svc.Watch,
110+ connect.WithSchema(eventServiceMethods.ByName("Watch")),
111+ connect.WithHandlerOptions(opts...),
112+ )
113+ return "/ermite.EventService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
114+ switch r.URL.Path {
115+ case EventServiceWatchProcedure:
116+ eventServiceWatchHandler.ServeHTTP(w, r)
117+ default:
118+ http.NotFound(w, r)
119+ }
120+ })
121+}
122+
123+// UnimplementedEventServiceHandler returns CodeUnimplemented from all methods.
124+type UnimplementedEventServiceHandler struct{}
125+
126+func (UnimplementedEventServiceHandler) Watch(context.Context, *connect.Request[ermite.WatchRequest], *connect.ServerStream[ermite.WatchResponse]) error {
127+ return connect.NewError(connect.CodeUnimplemented, errors.New("ermite.EventService.Watch is not implemented"))
128+}
A gen/ermite/ermiteconnect/git.connect.go +281 -0
@@ -0,0 +1,281 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/git.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+ // GitServiceName is the fully-qualified name of the GitService service.
27+ GitServiceName = "ermite.GitService"
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+ // GitServiceListRefsProcedure is the fully-qualified name of the GitService's ListRefs RPC.
39+ GitServiceListRefsProcedure = "/ermite.GitService/ListRefs"
40+ // GitServiceGetTreeProcedure is the fully-qualified name of the GitService's GetTree RPC.
41+ GitServiceGetTreeProcedure = "/ermite.GitService/GetTree"
42+ // GitServiceGetBlobProcedure is the fully-qualified name of the GitService's GetBlob RPC.
43+ GitServiceGetBlobProcedure = "/ermite.GitService/GetBlob"
44+ // GitServiceListCommitsProcedure is the fully-qualified name of the GitService's ListCommits RPC.
45+ GitServiceListCommitsProcedure = "/ermite.GitService/ListCommits"
46+ // GitServiceGetCommitProcedure is the fully-qualified name of the GitService's GetCommit RPC.
47+ GitServiceGetCommitProcedure = "/ermite.GitService/GetCommit"
48+ // GitServiceResolveRevisionProcedure is the fully-qualified name of the GitService's
49+ // ResolveRevision RPC.
50+ GitServiceResolveRevisionProcedure = "/ermite.GitService/ResolveRevision"
51+)
52+
53+// GitServiceClient is a client for the ermite.GitService service.
54+type GitServiceClient interface {
55+ // ListRefs lists the branches, tags and HEAD of a repository. Refs are
56+ // ordered by kind (HEAD, branches, tags) then by name.
57+ ListRefs(context.Context, *connect.Request[ermite.ListRefsRequest]) (*connect.Response[ermite.ListRefsResponse], error)
58+ // GetTree lists the entries of a directory at a revision. Fails with
59+ // not_found if the path does not exist and invalid_argument if it is not a
60+ // directory.
61+ GetTree(context.Context, *connect.Request[ermite.GetTreeRequest]) (*connect.Response[ermite.GetTreeResponse], error)
62+ // GetBlob streams the content of a file at a revision, in chunks of at most
63+ // 64 KiB. The first message carries the metadata in info, then the chunks
64+ // follow; the memory used by the server does not depend on the file size.
65+ // Fails with not_found if the path does not exist and invalid_argument if it
66+ // is not a file.
67+ GetBlob(context.Context, *connect.Request[ermite.GetBlobRequest]) (*connect.ServerStreamForClient[ermite.GetBlobResponse], error)
68+ // ListCommits lists the history reachable from a revision, newest first
69+ // (Git's default order), optionally limited to the commits that touched a
70+ // path.
71+ ListCommits(context.Context, *connect.Request[ermite.ListCommitsRequest]) (*connect.Response[ermite.ListCommitsResponse], error)
72+ // GetCommit returns one commit with its statistics and its diff.
73+ GetCommit(context.Context, *connect.Request[ermite.GetCommitRequest]) (*connect.Response[ermite.GetCommitResponse], error)
74+ // ResolveRevision turns a revision into the id of the commit it designates.
75+ ResolveRevision(context.Context, *connect.Request[ermite.ResolveRevisionRequest]) (*connect.Response[ermite.ResolveRevisionResponse], error)
76+}
77+
78+// NewGitServiceClient constructs a client for the ermite.GitService service. By default, it uses
79+// the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
80+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
81+// connect.WithGRPCWeb() options.
82+//
83+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
84+// http://api.acme.com or https://acme.com/grpc).
85+func NewGitServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) GitServiceClient {
86+ baseURL = strings.TrimRight(baseURL, "/")
87+ gitServiceMethods := ermite.File_ermite_git_proto.Services().ByName("GitService").Methods()
88+ return &gitServiceClient{
89+ listRefs: connect.NewClient[ermite.ListRefsRequest, ermite.ListRefsResponse](
90+ httpClient,
91+ baseURL+GitServiceListRefsProcedure,
92+ connect.WithSchema(gitServiceMethods.ByName("ListRefs")),
93+ connect.WithClientOptions(opts...),
94+ ),
95+ getTree: connect.NewClient[ermite.GetTreeRequest, ermite.GetTreeResponse](
96+ httpClient,
97+ baseURL+GitServiceGetTreeProcedure,
98+ connect.WithSchema(gitServiceMethods.ByName("GetTree")),
99+ connect.WithClientOptions(opts...),
100+ ),
101+ getBlob: connect.NewClient[ermite.GetBlobRequest, ermite.GetBlobResponse](
102+ httpClient,
103+ baseURL+GitServiceGetBlobProcedure,
104+ connect.WithSchema(gitServiceMethods.ByName("GetBlob")),
105+ connect.WithClientOptions(opts...),
106+ ),
107+ listCommits: connect.NewClient[ermite.ListCommitsRequest, ermite.ListCommitsResponse](
108+ httpClient,
109+ baseURL+GitServiceListCommitsProcedure,
110+ connect.WithSchema(gitServiceMethods.ByName("ListCommits")),
111+ connect.WithClientOptions(opts...),
112+ ),
113+ getCommit: connect.NewClient[ermite.GetCommitRequest, ermite.GetCommitResponse](
114+ httpClient,
115+ baseURL+GitServiceGetCommitProcedure,
116+ connect.WithSchema(gitServiceMethods.ByName("GetCommit")),
117+ connect.WithClientOptions(opts...),
118+ ),
119+ resolveRevision: connect.NewClient[ermite.ResolveRevisionRequest, ermite.ResolveRevisionResponse](
120+ httpClient,
121+ baseURL+GitServiceResolveRevisionProcedure,
122+ connect.WithSchema(gitServiceMethods.ByName("ResolveRevision")),
123+ connect.WithClientOptions(opts...),
124+ ),
125+ }
126+}
127+
128+// gitServiceClient implements GitServiceClient.
129+type gitServiceClient struct {
130+ listRefs *connect.Client[ermite.ListRefsRequest, ermite.ListRefsResponse]
131+ getTree *connect.Client[ermite.GetTreeRequest, ermite.GetTreeResponse]
132+ getBlob *connect.Client[ermite.GetBlobRequest, ermite.GetBlobResponse]
133+ listCommits *connect.Client[ermite.ListCommitsRequest, ermite.ListCommitsResponse]
134+ getCommit *connect.Client[ermite.GetCommitRequest, ermite.GetCommitResponse]
135+ resolveRevision *connect.Client[ermite.ResolveRevisionRequest, ermite.ResolveRevisionResponse]
136+}
137+
138+// ListRefs calls ermite.GitService.ListRefs.
139+func (c *gitServiceClient) ListRefs(ctx context.Context, req *connect.Request[ermite.ListRefsRequest]) (*connect.Response[ermite.ListRefsResponse], error) {
140+ return c.listRefs.CallUnary(ctx, req)
141+}
142+
143+// GetTree calls ermite.GitService.GetTree.
144+func (c *gitServiceClient) GetTree(ctx context.Context, req *connect.Request[ermite.GetTreeRequest]) (*connect.Response[ermite.GetTreeResponse], error) {
145+ return c.getTree.CallUnary(ctx, req)
146+}
147+
148+// GetBlob calls ermite.GitService.GetBlob.
149+func (c *gitServiceClient) GetBlob(ctx context.Context, req *connect.Request[ermite.GetBlobRequest]) (*connect.ServerStreamForClient[ermite.GetBlobResponse], error) {
150+ return c.getBlob.CallServerStream(ctx, req)
151+}
152+
153+// ListCommits calls ermite.GitService.ListCommits.
154+func (c *gitServiceClient) ListCommits(ctx context.Context, req *connect.Request[ermite.ListCommitsRequest]) (*connect.Response[ermite.ListCommitsResponse], error) {
155+ return c.listCommits.CallUnary(ctx, req)
156+}
157+
158+// GetCommit calls ermite.GitService.GetCommit.
159+func (c *gitServiceClient) GetCommit(ctx context.Context, req *connect.Request[ermite.GetCommitRequest]) (*connect.Response[ermite.GetCommitResponse], error) {
160+ return c.getCommit.CallUnary(ctx, req)
161+}
162+
163+// ResolveRevision calls ermite.GitService.ResolveRevision.
164+func (c *gitServiceClient) ResolveRevision(ctx context.Context, req *connect.Request[ermite.ResolveRevisionRequest]) (*connect.Response[ermite.ResolveRevisionResponse], error) {
165+ return c.resolveRevision.CallUnary(ctx, req)
166+}
167+
168+// GitServiceHandler is an implementation of the ermite.GitService service.
169+type GitServiceHandler interface {
170+ // ListRefs lists the branches, tags and HEAD of a repository. Refs are
171+ // ordered by kind (HEAD, branches, tags) then by name.
172+ ListRefs(context.Context, *connect.Request[ermite.ListRefsRequest]) (*connect.Response[ermite.ListRefsResponse], error)
173+ // GetTree lists the entries of a directory at a revision. Fails with
174+ // not_found if the path does not exist and invalid_argument if it is not a
175+ // directory.
176+ GetTree(context.Context, *connect.Request[ermite.GetTreeRequest]) (*connect.Response[ermite.GetTreeResponse], error)
177+ // GetBlob streams the content of a file at a revision, in chunks of at most
178+ // 64 KiB. The first message carries the metadata in info, then the chunks
179+ // follow; the memory used by the server does not depend on the file size.
180+ // Fails with not_found if the path does not exist and invalid_argument if it
181+ // is not a file.
182+ GetBlob(context.Context, *connect.Request[ermite.GetBlobRequest], *connect.ServerStream[ermite.GetBlobResponse]) error
183+ // ListCommits lists the history reachable from a revision, newest first
184+ // (Git's default order), optionally limited to the commits that touched a
185+ // path.
186+ ListCommits(context.Context, *connect.Request[ermite.ListCommitsRequest]) (*connect.Response[ermite.ListCommitsResponse], error)
187+ // GetCommit returns one commit with its statistics and its diff.
188+ GetCommit(context.Context, *connect.Request[ermite.GetCommitRequest]) (*connect.Response[ermite.GetCommitResponse], error)
189+ // ResolveRevision turns a revision into the id of the commit it designates.
190+ ResolveRevision(context.Context, *connect.Request[ermite.ResolveRevisionRequest]) (*connect.Response[ermite.ResolveRevisionResponse], error)
191+}
192+
193+// NewGitServiceHandler builds an HTTP handler from the service implementation. It returns the path
194+// on which to mount the handler and the handler itself.
195+//
196+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
197+// and JSON codecs. They also support gzip compression.
198+func NewGitServiceHandler(svc GitServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
199+ gitServiceMethods := ermite.File_ermite_git_proto.Services().ByName("GitService").Methods()
200+ gitServiceListRefsHandler := connect.NewUnaryHandler(
201+ GitServiceListRefsProcedure,
202+ svc.ListRefs,
203+ connect.WithSchema(gitServiceMethods.ByName("ListRefs")),
204+ connect.WithHandlerOptions(opts...),
205+ )
206+ gitServiceGetTreeHandler := connect.NewUnaryHandler(
207+ GitServiceGetTreeProcedure,
208+ svc.GetTree,
209+ connect.WithSchema(gitServiceMethods.ByName("GetTree")),
210+ connect.WithHandlerOptions(opts...),
211+ )
212+ gitServiceGetBlobHandler := connect.NewServerStreamHandler(
213+ GitServiceGetBlobProcedure,
214+ svc.GetBlob,
215+ connect.WithSchema(gitServiceMethods.ByName("GetBlob")),
216+ connect.WithHandlerOptions(opts...),
217+ )
218+ gitServiceListCommitsHandler := connect.NewUnaryHandler(
219+ GitServiceListCommitsProcedure,
220+ svc.ListCommits,
221+ connect.WithSchema(gitServiceMethods.ByName("ListCommits")),
222+ connect.WithHandlerOptions(opts...),
223+ )
224+ gitServiceGetCommitHandler := connect.NewUnaryHandler(
225+ GitServiceGetCommitProcedure,
226+ svc.GetCommit,
227+ connect.WithSchema(gitServiceMethods.ByName("GetCommit")),
228+ connect.WithHandlerOptions(opts...),
229+ )
230+ gitServiceResolveRevisionHandler := connect.NewUnaryHandler(
231+ GitServiceResolveRevisionProcedure,
232+ svc.ResolveRevision,
233+ connect.WithSchema(gitServiceMethods.ByName("ResolveRevision")),
234+ connect.WithHandlerOptions(opts...),
235+ )
236+ return "/ermite.GitService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
237+ switch r.URL.Path {
238+ case GitServiceListRefsProcedure:
239+ gitServiceListRefsHandler.ServeHTTP(w, r)
240+ case GitServiceGetTreeProcedure:
241+ gitServiceGetTreeHandler.ServeHTTP(w, r)
242+ case GitServiceGetBlobProcedure:
243+ gitServiceGetBlobHandler.ServeHTTP(w, r)
244+ case GitServiceListCommitsProcedure:
245+ gitServiceListCommitsHandler.ServeHTTP(w, r)
246+ case GitServiceGetCommitProcedure:
247+ gitServiceGetCommitHandler.ServeHTTP(w, r)
248+ case GitServiceResolveRevisionProcedure:
249+ gitServiceResolveRevisionHandler.ServeHTTP(w, r)
250+ default:
251+ http.NotFound(w, r)
252+ }
253+ })
254+}
255+
256+// UnimplementedGitServiceHandler returns CodeUnimplemented from all methods.
257+type UnimplementedGitServiceHandler struct{}
258+
259+func (UnimplementedGitServiceHandler) ListRefs(context.Context, *connect.Request[ermite.ListRefsRequest]) (*connect.Response[ermite.ListRefsResponse], error) {
260+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.ListRefs is not implemented"))
261+}
262+
263+func (UnimplementedGitServiceHandler) GetTree(context.Context, *connect.Request[ermite.GetTreeRequest]) (*connect.Response[ermite.GetTreeResponse], error) {
264+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.GetTree is not implemented"))
265+}
266+
267+func (UnimplementedGitServiceHandler) GetBlob(context.Context, *connect.Request[ermite.GetBlobRequest], *connect.ServerStream[ermite.GetBlobResponse]) error {
268+ return connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.GetBlob is not implemented"))
269+}
270+
271+func (UnimplementedGitServiceHandler) ListCommits(context.Context, *connect.Request[ermite.ListCommitsRequest]) (*connect.Response[ermite.ListCommitsResponse], error) {
272+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.ListCommits is not implemented"))
273+}
274+
275+func (UnimplementedGitServiceHandler) GetCommit(context.Context, *connect.Request[ermite.GetCommitRequest]) (*connect.Response[ermite.GetCommitResponse], error) {
276+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.GetCommit is not implemented"))
277+}
278+
279+func (UnimplementedGitServiceHandler) ResolveRevision(context.Context, *connect.Request[ermite.ResolveRevisionRequest]) (*connect.Response[ermite.ResolveRevisionResponse], error) {
280+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.ResolveRevision is not implemented"))
281+}
@@ -0,0 +1,281 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/git.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+ // GitServiceName is the fully-qualified name of the GitService service.
27+ GitServiceName = "ermite.GitService"
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+ // GitServiceListRefsProcedure is the fully-qualified name of the GitService's ListRefs RPC.
39+ GitServiceListRefsProcedure = "/ermite.GitService/ListRefs"
40+ // GitServiceGetTreeProcedure is the fully-qualified name of the GitService's GetTree RPC.
41+ GitServiceGetTreeProcedure = "/ermite.GitService/GetTree"
42+ // GitServiceGetBlobProcedure is the fully-qualified name of the GitService's GetBlob RPC.
43+ GitServiceGetBlobProcedure = "/ermite.GitService/GetBlob"
44+ // GitServiceListCommitsProcedure is the fully-qualified name of the GitService's ListCommits RPC.
45+ GitServiceListCommitsProcedure = "/ermite.GitService/ListCommits"
46+ // GitServiceGetCommitProcedure is the fully-qualified name of the GitService's GetCommit RPC.
47+ GitServiceGetCommitProcedure = "/ermite.GitService/GetCommit"
48+ // GitServiceResolveRevisionProcedure is the fully-qualified name of the GitService's
49+ // ResolveRevision RPC.
50+ GitServiceResolveRevisionProcedure = "/ermite.GitService/ResolveRevision"
51+)
52+
53+// GitServiceClient is a client for the ermite.GitService service.
54+type GitServiceClient interface {
55+ // ListRefs lists the branches, tags and HEAD of a repository. Refs are
56+ // ordered by kind (HEAD, branches, tags) then by name.
57+ ListRefs(context.Context, *connect.Request[ermite.ListRefsRequest]) (*connect.Response[ermite.ListRefsResponse], error)
58+ // GetTree lists the entries of a directory at a revision. Fails with
59+ // not_found if the path does not exist and invalid_argument if it is not a
60+ // directory.
61+ GetTree(context.Context, *connect.Request[ermite.GetTreeRequest]) (*connect.Response[ermite.GetTreeResponse], error)
62+ // GetBlob streams the content of a file at a revision, in chunks of at most
63+ // 64 KiB. The first message carries the metadata in info, then the chunks
64+ // follow; the memory used by the server does not depend on the file size.
65+ // Fails with not_found if the path does not exist and invalid_argument if it
66+ // is not a file.
67+ GetBlob(context.Context, *connect.Request[ermite.GetBlobRequest]) (*connect.ServerStreamForClient[ermite.GetBlobResponse], error)
68+ // ListCommits lists the history reachable from a revision, newest first
69+ // (Git's default order), optionally limited to the commits that touched a
70+ // path.
71+ ListCommits(context.Context, *connect.Request[ermite.ListCommitsRequest]) (*connect.Response[ermite.ListCommitsResponse], error)
72+ // GetCommit returns one commit with its statistics and its diff.
73+ GetCommit(context.Context, *connect.Request[ermite.GetCommitRequest]) (*connect.Response[ermite.GetCommitResponse], error)
74+ // ResolveRevision turns a revision into the id of the commit it designates.
75+ ResolveRevision(context.Context, *connect.Request[ermite.ResolveRevisionRequest]) (*connect.Response[ermite.ResolveRevisionResponse], error)
76+}
77+
78+// NewGitServiceClient constructs a client for the ermite.GitService service. By default, it uses
79+// the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
80+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
81+// connect.WithGRPCWeb() options.
82+//
83+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
84+// http://api.acme.com or https://acme.com/grpc).
85+func NewGitServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) GitServiceClient {
86+ baseURL = strings.TrimRight(baseURL, "/")
87+ gitServiceMethods := ermite.File_ermite_git_proto.Services().ByName("GitService").Methods()
88+ return &gitServiceClient{
89+ listRefs: connect.NewClient[ermite.ListRefsRequest, ermite.ListRefsResponse](
90+ httpClient,
91+ baseURL+GitServiceListRefsProcedure,
92+ connect.WithSchema(gitServiceMethods.ByName("ListRefs")),
93+ connect.WithClientOptions(opts...),
94+ ),
95+ getTree: connect.NewClient[ermite.GetTreeRequest, ermite.GetTreeResponse](
96+ httpClient,
97+ baseURL+GitServiceGetTreeProcedure,
98+ connect.WithSchema(gitServiceMethods.ByName("GetTree")),
99+ connect.WithClientOptions(opts...),
100+ ),
101+ getBlob: connect.NewClient[ermite.GetBlobRequest, ermite.GetBlobResponse](
102+ httpClient,
103+ baseURL+GitServiceGetBlobProcedure,
104+ connect.WithSchema(gitServiceMethods.ByName("GetBlob")),
105+ connect.WithClientOptions(opts...),
106+ ),
107+ listCommits: connect.NewClient[ermite.ListCommitsRequest, ermite.ListCommitsResponse](
108+ httpClient,
109+ baseURL+GitServiceListCommitsProcedure,
110+ connect.WithSchema(gitServiceMethods.ByName("ListCommits")),
111+ connect.WithClientOptions(opts...),
112+ ),
113+ getCommit: connect.NewClient[ermite.GetCommitRequest, ermite.GetCommitResponse](
114+ httpClient,
115+ baseURL+GitServiceGetCommitProcedure,
116+ connect.WithSchema(gitServiceMethods.ByName("GetCommit")),
117+ connect.WithClientOptions(opts...),
118+ ),
119+ resolveRevision: connect.NewClient[ermite.ResolveRevisionRequest, ermite.ResolveRevisionResponse](
120+ httpClient,
121+ baseURL+GitServiceResolveRevisionProcedure,
122+ connect.WithSchema(gitServiceMethods.ByName("ResolveRevision")),
123+ connect.WithClientOptions(opts...),
124+ ),
125+ }
126+}
127+
128+// gitServiceClient implements GitServiceClient.
129+type gitServiceClient struct {
130+ listRefs *connect.Client[ermite.ListRefsRequest, ermite.ListRefsResponse]
131+ getTree *connect.Client[ermite.GetTreeRequest, ermite.GetTreeResponse]
132+ getBlob *connect.Client[ermite.GetBlobRequest, ermite.GetBlobResponse]
133+ listCommits *connect.Client[ermite.ListCommitsRequest, ermite.ListCommitsResponse]
134+ getCommit *connect.Client[ermite.GetCommitRequest, ermite.GetCommitResponse]
135+ resolveRevision *connect.Client[ermite.ResolveRevisionRequest, ermite.ResolveRevisionResponse]
136+}
137+
138+// ListRefs calls ermite.GitService.ListRefs.
139+func (c *gitServiceClient) ListRefs(ctx context.Context, req *connect.Request[ermite.ListRefsRequest]) (*connect.Response[ermite.ListRefsResponse], error) {
140+ return c.listRefs.CallUnary(ctx, req)
141+}
142+
143+// GetTree calls ermite.GitService.GetTree.
144+func (c *gitServiceClient) GetTree(ctx context.Context, req *connect.Request[ermite.GetTreeRequest]) (*connect.Response[ermite.GetTreeResponse], error) {
145+ return c.getTree.CallUnary(ctx, req)
146+}
147+
148+// GetBlob calls ermite.GitService.GetBlob.
149+func (c *gitServiceClient) GetBlob(ctx context.Context, req *connect.Request[ermite.GetBlobRequest]) (*connect.ServerStreamForClient[ermite.GetBlobResponse], error) {
150+ return c.getBlob.CallServerStream(ctx, req)
151+}
152+
153+// ListCommits calls ermite.GitService.ListCommits.
154+func (c *gitServiceClient) ListCommits(ctx context.Context, req *connect.Request[ermite.ListCommitsRequest]) (*connect.Response[ermite.ListCommitsResponse], error) {
155+ return c.listCommits.CallUnary(ctx, req)
156+}
157+
158+// GetCommit calls ermite.GitService.GetCommit.
159+func (c *gitServiceClient) GetCommit(ctx context.Context, req *connect.Request[ermite.GetCommitRequest]) (*connect.Response[ermite.GetCommitResponse], error) {
160+ return c.getCommit.CallUnary(ctx, req)
161+}
162+
163+// ResolveRevision calls ermite.GitService.ResolveRevision.
164+func (c *gitServiceClient) ResolveRevision(ctx context.Context, req *connect.Request[ermite.ResolveRevisionRequest]) (*connect.Response[ermite.ResolveRevisionResponse], error) {
165+ return c.resolveRevision.CallUnary(ctx, req)
166+}
167+
168+// GitServiceHandler is an implementation of the ermite.GitService service.
169+type GitServiceHandler interface {
170+ // ListRefs lists the branches, tags and HEAD of a repository. Refs are
171+ // ordered by kind (HEAD, branches, tags) then by name.
172+ ListRefs(context.Context, *connect.Request[ermite.ListRefsRequest]) (*connect.Response[ermite.ListRefsResponse], error)
173+ // GetTree lists the entries of a directory at a revision. Fails with
174+ // not_found if the path does not exist and invalid_argument if it is not a
175+ // directory.
176+ GetTree(context.Context, *connect.Request[ermite.GetTreeRequest]) (*connect.Response[ermite.GetTreeResponse], error)
177+ // GetBlob streams the content of a file at a revision, in chunks of at most
178+ // 64 KiB. The first message carries the metadata in info, then the chunks
179+ // follow; the memory used by the server does not depend on the file size.
180+ // Fails with not_found if the path does not exist and invalid_argument if it
181+ // is not a file.
182+ GetBlob(context.Context, *connect.Request[ermite.GetBlobRequest], *connect.ServerStream[ermite.GetBlobResponse]) error
183+ // ListCommits lists the history reachable from a revision, newest first
184+ // (Git's default order), optionally limited to the commits that touched a
185+ // path.
186+ ListCommits(context.Context, *connect.Request[ermite.ListCommitsRequest]) (*connect.Response[ermite.ListCommitsResponse], error)
187+ // GetCommit returns one commit with its statistics and its diff.
188+ GetCommit(context.Context, *connect.Request[ermite.GetCommitRequest]) (*connect.Response[ermite.GetCommitResponse], error)
189+ // ResolveRevision turns a revision into the id of the commit it designates.
190+ ResolveRevision(context.Context, *connect.Request[ermite.ResolveRevisionRequest]) (*connect.Response[ermite.ResolveRevisionResponse], error)
191+}
192+
193+// NewGitServiceHandler builds an HTTP handler from the service implementation. It returns the path
194+// on which to mount the handler and the handler itself.
195+//
196+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
197+// and JSON codecs. They also support gzip compression.
198+func NewGitServiceHandler(svc GitServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
199+ gitServiceMethods := ermite.File_ermite_git_proto.Services().ByName("GitService").Methods()
200+ gitServiceListRefsHandler := connect.NewUnaryHandler(
201+ GitServiceListRefsProcedure,
202+ svc.ListRefs,
203+ connect.WithSchema(gitServiceMethods.ByName("ListRefs")),
204+ connect.WithHandlerOptions(opts...),
205+ )
206+ gitServiceGetTreeHandler := connect.NewUnaryHandler(
207+ GitServiceGetTreeProcedure,
208+ svc.GetTree,
209+ connect.WithSchema(gitServiceMethods.ByName("GetTree")),
210+ connect.WithHandlerOptions(opts...),
211+ )
212+ gitServiceGetBlobHandler := connect.NewServerStreamHandler(
213+ GitServiceGetBlobProcedure,
214+ svc.GetBlob,
215+ connect.WithSchema(gitServiceMethods.ByName("GetBlob")),
216+ connect.WithHandlerOptions(opts...),
217+ )
218+ gitServiceListCommitsHandler := connect.NewUnaryHandler(
219+ GitServiceListCommitsProcedure,
220+ svc.ListCommits,
221+ connect.WithSchema(gitServiceMethods.ByName("ListCommits")),
222+ connect.WithHandlerOptions(opts...),
223+ )
224+ gitServiceGetCommitHandler := connect.NewUnaryHandler(
225+ GitServiceGetCommitProcedure,
226+ svc.GetCommit,
227+ connect.WithSchema(gitServiceMethods.ByName("GetCommit")),
228+ connect.WithHandlerOptions(opts...),
229+ )
230+ gitServiceResolveRevisionHandler := connect.NewUnaryHandler(
231+ GitServiceResolveRevisionProcedure,
232+ svc.ResolveRevision,
233+ connect.WithSchema(gitServiceMethods.ByName("ResolveRevision")),
234+ connect.WithHandlerOptions(opts...),
235+ )
236+ return "/ermite.GitService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
237+ switch r.URL.Path {
238+ case GitServiceListRefsProcedure:
239+ gitServiceListRefsHandler.ServeHTTP(w, r)
240+ case GitServiceGetTreeProcedure:
241+ gitServiceGetTreeHandler.ServeHTTP(w, r)
242+ case GitServiceGetBlobProcedure:
243+ gitServiceGetBlobHandler.ServeHTTP(w, r)
244+ case GitServiceListCommitsProcedure:
245+ gitServiceListCommitsHandler.ServeHTTP(w, r)
246+ case GitServiceGetCommitProcedure:
247+ gitServiceGetCommitHandler.ServeHTTP(w, r)
248+ case GitServiceResolveRevisionProcedure:
249+ gitServiceResolveRevisionHandler.ServeHTTP(w, r)
250+ default:
251+ http.NotFound(w, r)
252+ }
253+ })
254+}
255+
256+// UnimplementedGitServiceHandler returns CodeUnimplemented from all methods.
257+type UnimplementedGitServiceHandler struct{}
258+
259+func (UnimplementedGitServiceHandler) ListRefs(context.Context, *connect.Request[ermite.ListRefsRequest]) (*connect.Response[ermite.ListRefsResponse], error) {
260+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.ListRefs is not implemented"))
261+}
262+
263+func (UnimplementedGitServiceHandler) GetTree(context.Context, *connect.Request[ermite.GetTreeRequest]) (*connect.Response[ermite.GetTreeResponse], error) {
264+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.GetTree is not implemented"))
265+}
266+
267+func (UnimplementedGitServiceHandler) GetBlob(context.Context, *connect.Request[ermite.GetBlobRequest], *connect.ServerStream[ermite.GetBlobResponse]) error {
268+ return connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.GetBlob is not implemented"))
269+}
270+
271+func (UnimplementedGitServiceHandler) ListCommits(context.Context, *connect.Request[ermite.ListCommitsRequest]) (*connect.Response[ermite.ListCommitsResponse], error) {
272+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.ListCommits is not implemented"))
273+}
274+
275+func (UnimplementedGitServiceHandler) GetCommit(context.Context, *connect.Request[ermite.GetCommitRequest]) (*connect.Response[ermite.GetCommitResponse], error) {
276+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.GetCommit is not implemented"))
277+}
278+
279+func (UnimplementedGitServiceHandler) ResolveRevision(context.Context, *connect.Request[ermite.ResolveRevisionRequest]) (*connect.Response[ermite.ResolveRevisionResponse], error) {
280+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.ResolveRevision is not implemented"))
281+}
A gen/ermite/ermiteconnect/repo.connect.go +242 -0
@@ -0,0 +1,242 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/repo.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+ // RepoServiceName is the fully-qualified name of the RepoService service.
27+ RepoServiceName = "ermite.RepoService"
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+ // RepoServiceListReposProcedure is the fully-qualified name of the RepoService's ListRepos RPC.
39+ RepoServiceListReposProcedure = "/ermite.RepoService/ListRepos"
40+ // RepoServiceGetRepoProcedure is the fully-qualified name of the RepoService's GetRepo RPC.
41+ RepoServiceGetRepoProcedure = "/ermite.RepoService/GetRepo"
42+ // RepoServiceCreateRepoProcedure is the fully-qualified name of the RepoService's CreateRepo RPC.
43+ RepoServiceCreateRepoProcedure = "/ermite.RepoService/CreateRepo"
44+ // RepoServiceUpdateRepoProcedure is the fully-qualified name of the RepoService's UpdateRepo RPC.
45+ RepoServiceUpdateRepoProcedure = "/ermite.RepoService/UpdateRepo"
46+ // RepoServiceDeleteRepoProcedure is the fully-qualified name of the RepoService's DeleteRepo RPC.
47+ RepoServiceDeleteRepoProcedure = "/ermite.RepoService/DeleteRepo"
48+)
49+
50+// RepoServiceClient is a client for the ermite.RepoService service.
51+type RepoServiceClient interface {
52+ // ListRepos lists the repositories the caller may see, ordered by name.
53+ ListRepos(context.Context, *connect.Request[ermite.ListReposRequest]) (*connect.Response[ermite.ListReposResponse], error)
54+ // GetRepo returns one repository. Fails with not_found if it does not exist
55+ // or is private and not readable by the caller.
56+ GetRepo(context.Context, *connect.Request[ermite.GetRepoRequest]) (*connect.Response[ermite.GetRepoResponse], error)
57+ // CreateRepo creates an empty repository. Needs the repo:write scope. Fails
58+ // with already_exists if the name is taken and invalid_argument if the name
59+ // is not valid.
60+ 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.
63+ UpdateRepo(context.Context, *connect.Request[ermite.UpdateRepoRequest]) (*connect.Response[ermite.UpdateRepoResponse], error)
64+ // 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+ DeleteRepo(context.Context, *connect.Request[ermite.DeleteRepoRequest]) (*connect.Response[ermite.DeleteRepoResponse], error)
67+}
68+
69+// NewRepoServiceClient constructs a client for the ermite.RepoService service. By default, it uses
70+// the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
71+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
72+// connect.WithGRPCWeb() options.
73+//
74+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
75+// http://api.acme.com or https://acme.com/grpc).
76+func NewRepoServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) RepoServiceClient {
77+ baseURL = strings.TrimRight(baseURL, "/")
78+ repoServiceMethods := ermite.File_ermite_repo_proto.Services().ByName("RepoService").Methods()
79+ return &repoServiceClient{
80+ listRepos: connect.NewClient[ermite.ListReposRequest, ermite.ListReposResponse](
81+ httpClient,
82+ baseURL+RepoServiceListReposProcedure,
83+ connect.WithSchema(repoServiceMethods.ByName("ListRepos")),
84+ connect.WithClientOptions(opts...),
85+ ),
86+ getRepo: connect.NewClient[ermite.GetRepoRequest, ermite.GetRepoResponse](
87+ httpClient,
88+ baseURL+RepoServiceGetRepoProcedure,
89+ connect.WithSchema(repoServiceMethods.ByName("GetRepo")),
90+ connect.WithClientOptions(opts...),
91+ ),
92+ createRepo: connect.NewClient[ermite.CreateRepoRequest, ermite.CreateRepoResponse](
93+ httpClient,
94+ baseURL+RepoServiceCreateRepoProcedure,
95+ connect.WithSchema(repoServiceMethods.ByName("CreateRepo")),
96+ connect.WithClientOptions(opts...),
97+ ),
98+ updateRepo: connect.NewClient[ermite.UpdateRepoRequest, ermite.UpdateRepoResponse](
99+ httpClient,
100+ baseURL+RepoServiceUpdateRepoProcedure,
101+ connect.WithSchema(repoServiceMethods.ByName("UpdateRepo")),
102+ connect.WithClientOptions(opts...),
103+ ),
104+ deleteRepo: connect.NewClient[ermite.DeleteRepoRequest, ermite.DeleteRepoResponse](
105+ httpClient,
106+ baseURL+RepoServiceDeleteRepoProcedure,
107+ connect.WithSchema(repoServiceMethods.ByName("DeleteRepo")),
108+ connect.WithClientOptions(opts...),
109+ ),
110+ }
111+}
112+
113+// repoServiceClient implements RepoServiceClient.
114+type repoServiceClient struct {
115+ listRepos *connect.Client[ermite.ListReposRequest, ermite.ListReposResponse]
116+ getRepo *connect.Client[ermite.GetRepoRequest, ermite.GetRepoResponse]
117+ createRepo *connect.Client[ermite.CreateRepoRequest, ermite.CreateRepoResponse]
118+ updateRepo *connect.Client[ermite.UpdateRepoRequest, ermite.UpdateRepoResponse]
119+ deleteRepo *connect.Client[ermite.DeleteRepoRequest, ermite.DeleteRepoResponse]
120+}
121+
122+// ListRepos calls ermite.RepoService.ListRepos.
123+func (c *repoServiceClient) ListRepos(ctx context.Context, req *connect.Request[ermite.ListReposRequest]) (*connect.Response[ermite.ListReposResponse], error) {
124+ return c.listRepos.CallUnary(ctx, req)
125+}
126+
127+// GetRepo calls ermite.RepoService.GetRepo.
128+func (c *repoServiceClient) GetRepo(ctx context.Context, req *connect.Request[ermite.GetRepoRequest]) (*connect.Response[ermite.GetRepoResponse], error) {
129+ return c.getRepo.CallUnary(ctx, req)
130+}
131+
132+// CreateRepo calls ermite.RepoService.CreateRepo.
133+func (c *repoServiceClient) CreateRepo(ctx context.Context, req *connect.Request[ermite.CreateRepoRequest]) (*connect.Response[ermite.CreateRepoResponse], error) {
134+ return c.createRepo.CallUnary(ctx, req)
135+}
136+
137+// UpdateRepo calls ermite.RepoService.UpdateRepo.
138+func (c *repoServiceClient) UpdateRepo(ctx context.Context, req *connect.Request[ermite.UpdateRepoRequest]) (*connect.Response[ermite.UpdateRepoResponse], error) {
139+ return c.updateRepo.CallUnary(ctx, req)
140+}
141+
142+// DeleteRepo calls ermite.RepoService.DeleteRepo.
143+func (c *repoServiceClient) DeleteRepo(ctx context.Context, req *connect.Request[ermite.DeleteRepoRequest]) (*connect.Response[ermite.DeleteRepoResponse], error) {
144+ return c.deleteRepo.CallUnary(ctx, req)
145+}
146+
147+// RepoServiceHandler is an implementation of the ermite.RepoService service.
148+type RepoServiceHandler interface {
149+ // ListRepos lists the repositories the caller may see, ordered by name.
150+ ListRepos(context.Context, *connect.Request[ermite.ListReposRequest]) (*connect.Response[ermite.ListReposResponse], error)
151+ // GetRepo returns one repository. Fails with not_found if it does not exist
152+ // or is private and not readable by the caller.
153+ 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+ // with already_exists if the name is taken and invalid_argument if the name
156+ // is not valid.
157+ 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(context.Context, *connect.Request[ermite.UpdateRepoRequest]) (*connect.Response[ermite.UpdateRepoResponse], error)
161+ // 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.
163+ DeleteRepo(context.Context, *connect.Request[ermite.DeleteRepoRequest]) (*connect.Response[ermite.DeleteRepoResponse], error)
164+}
165+
166+// NewRepoServiceHandler builds an HTTP handler from the service implementation. It returns the path
167+// on which to mount the handler and the handler itself.
168+//
169+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
170+// and JSON codecs. They also support gzip compression.
171+func NewRepoServiceHandler(svc RepoServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
172+ repoServiceMethods := ermite.File_ermite_repo_proto.Services().ByName("RepoService").Methods()
173+ repoServiceListReposHandler := connect.NewUnaryHandler(
174+ RepoServiceListReposProcedure,
175+ svc.ListRepos,
176+ connect.WithSchema(repoServiceMethods.ByName("ListRepos")),
177+ connect.WithHandlerOptions(opts...),
178+ )
179+ repoServiceGetRepoHandler := connect.NewUnaryHandler(
180+ RepoServiceGetRepoProcedure,
181+ svc.GetRepo,
182+ connect.WithSchema(repoServiceMethods.ByName("GetRepo")),
183+ connect.WithHandlerOptions(opts...),
184+ )
185+ repoServiceCreateRepoHandler := connect.NewUnaryHandler(
186+ RepoServiceCreateRepoProcedure,
187+ svc.CreateRepo,
188+ connect.WithSchema(repoServiceMethods.ByName("CreateRepo")),
189+ connect.WithHandlerOptions(opts...),
190+ )
191+ repoServiceUpdateRepoHandler := connect.NewUnaryHandler(
192+ RepoServiceUpdateRepoProcedure,
193+ svc.UpdateRepo,
194+ connect.WithSchema(repoServiceMethods.ByName("UpdateRepo")),
195+ connect.WithHandlerOptions(opts...),
196+ )
197+ repoServiceDeleteRepoHandler := connect.NewUnaryHandler(
198+ RepoServiceDeleteRepoProcedure,
199+ svc.DeleteRepo,
200+ connect.WithSchema(repoServiceMethods.ByName("DeleteRepo")),
201+ connect.WithHandlerOptions(opts...),
202+ )
203+ return "/ermite.RepoService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
204+ switch r.URL.Path {
205+ case RepoServiceListReposProcedure:
206+ repoServiceListReposHandler.ServeHTTP(w, r)
207+ case RepoServiceGetRepoProcedure:
208+ repoServiceGetRepoHandler.ServeHTTP(w, r)
209+ case RepoServiceCreateRepoProcedure:
210+ repoServiceCreateRepoHandler.ServeHTTP(w, r)
211+ case RepoServiceUpdateRepoProcedure:
212+ repoServiceUpdateRepoHandler.ServeHTTP(w, r)
213+ case RepoServiceDeleteRepoProcedure:
214+ repoServiceDeleteRepoHandler.ServeHTTP(w, r)
215+ default:
216+ http.NotFound(w, r)
217+ }
218+ })
219+}
220+
221+// UnimplementedRepoServiceHandler returns CodeUnimplemented from all methods.
222+type UnimplementedRepoServiceHandler struct{}
223+
224+func (UnimplementedRepoServiceHandler) ListRepos(context.Context, *connect.Request[ermite.ListReposRequest]) (*connect.Response[ermite.ListReposResponse], error) {
225+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RepoService.ListRepos is not implemented"))
226+}
227+
228+func (UnimplementedRepoServiceHandler) GetRepo(context.Context, *connect.Request[ermite.GetRepoRequest]) (*connect.Response[ermite.GetRepoResponse], error) {
229+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RepoService.GetRepo is not implemented"))
230+}
231+
232+func (UnimplementedRepoServiceHandler) CreateRepo(context.Context, *connect.Request[ermite.CreateRepoRequest]) (*connect.Response[ermite.CreateRepoResponse], error) {
233+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RepoService.CreateRepo is not implemented"))
234+}
235+
236+func (UnimplementedRepoServiceHandler) UpdateRepo(context.Context, *connect.Request[ermite.UpdateRepoRequest]) (*connect.Response[ermite.UpdateRepoResponse], error) {
237+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RepoService.UpdateRepo is not implemented"))
238+}
239+
240+func (UnimplementedRepoServiceHandler) DeleteRepo(context.Context, *connect.Request[ermite.DeleteRepoRequest]) (*connect.Response[ermite.DeleteRepoResponse], error) {
241+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RepoService.DeleteRepo is not implemented"))
242+}
@@ -0,0 +1,242 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/repo.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+ // RepoServiceName is the fully-qualified name of the RepoService service.
27+ RepoServiceName = "ermite.RepoService"
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+ // RepoServiceListReposProcedure is the fully-qualified name of the RepoService's ListRepos RPC.
39+ RepoServiceListReposProcedure = "/ermite.RepoService/ListRepos"
40+ // RepoServiceGetRepoProcedure is the fully-qualified name of the RepoService's GetRepo RPC.
41+ RepoServiceGetRepoProcedure = "/ermite.RepoService/GetRepo"
42+ // RepoServiceCreateRepoProcedure is the fully-qualified name of the RepoService's CreateRepo RPC.
43+ RepoServiceCreateRepoProcedure = "/ermite.RepoService/CreateRepo"
44+ // RepoServiceUpdateRepoProcedure is the fully-qualified name of the RepoService's UpdateRepo RPC.
45+ RepoServiceUpdateRepoProcedure = "/ermite.RepoService/UpdateRepo"
46+ // RepoServiceDeleteRepoProcedure is the fully-qualified name of the RepoService's DeleteRepo RPC.
47+ RepoServiceDeleteRepoProcedure = "/ermite.RepoService/DeleteRepo"
48+)
49+
50+// RepoServiceClient is a client for the ermite.RepoService service.
51+type RepoServiceClient interface {
52+ // ListRepos lists the repositories the caller may see, ordered by name.
53+ ListRepos(context.Context, *connect.Request[ermite.ListReposRequest]) (*connect.Response[ermite.ListReposResponse], error)
54+ // GetRepo returns one repository. Fails with not_found if it does not exist
55+ // or is private and not readable by the caller.
56+ GetRepo(context.Context, *connect.Request[ermite.GetRepoRequest]) (*connect.Response[ermite.GetRepoResponse], error)
57+ // CreateRepo creates an empty repository. Needs the repo:write scope. Fails
58+ // with already_exists if the name is taken and invalid_argument if the name
59+ // is not valid.
60+ 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.
63+ UpdateRepo(context.Context, *connect.Request[ermite.UpdateRepoRequest]) (*connect.Response[ermite.UpdateRepoResponse], error)
64+ // 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+ DeleteRepo(context.Context, *connect.Request[ermite.DeleteRepoRequest]) (*connect.Response[ermite.DeleteRepoResponse], error)
67+}
68+
69+// NewRepoServiceClient constructs a client for the ermite.RepoService service. By default, it uses
70+// the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
71+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
72+// connect.WithGRPCWeb() options.
73+//
74+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
75+// http://api.acme.com or https://acme.com/grpc).
76+func NewRepoServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) RepoServiceClient {
77+ baseURL = strings.TrimRight(baseURL, "/")
78+ repoServiceMethods := ermite.File_ermite_repo_proto.Services().ByName("RepoService").Methods()
79+ return &repoServiceClient{
80+ listRepos: connect.NewClient[ermite.ListReposRequest, ermite.ListReposResponse](
81+ httpClient,
82+ baseURL+RepoServiceListReposProcedure,
83+ connect.WithSchema(repoServiceMethods.ByName("ListRepos")),
84+ connect.WithClientOptions(opts...),
85+ ),
86+ getRepo: connect.NewClient[ermite.GetRepoRequest, ermite.GetRepoResponse](
87+ httpClient,
88+ baseURL+RepoServiceGetRepoProcedure,
89+ connect.WithSchema(repoServiceMethods.ByName("GetRepo")),
90+ connect.WithClientOptions(opts...),
91+ ),
92+ createRepo: connect.NewClient[ermite.CreateRepoRequest, ermite.CreateRepoResponse](
93+ httpClient,
94+ baseURL+RepoServiceCreateRepoProcedure,
95+ connect.WithSchema(repoServiceMethods.ByName("CreateRepo")),
96+ connect.WithClientOptions(opts...),
97+ ),
98+ updateRepo: connect.NewClient[ermite.UpdateRepoRequest, ermite.UpdateRepoResponse](
99+ httpClient,
100+ baseURL+RepoServiceUpdateRepoProcedure,
101+ connect.WithSchema(repoServiceMethods.ByName("UpdateRepo")),
102+ connect.WithClientOptions(opts...),
103+ ),
104+ deleteRepo: connect.NewClient[ermite.DeleteRepoRequest, ermite.DeleteRepoResponse](
105+ httpClient,
106+ baseURL+RepoServiceDeleteRepoProcedure,
107+ connect.WithSchema(repoServiceMethods.ByName("DeleteRepo")),
108+ connect.WithClientOptions(opts...),
109+ ),
110+ }
111+}
112+
113+// repoServiceClient implements RepoServiceClient.
114+type repoServiceClient struct {
115+ listRepos *connect.Client[ermite.ListReposRequest, ermite.ListReposResponse]
116+ getRepo *connect.Client[ermite.GetRepoRequest, ermite.GetRepoResponse]
117+ createRepo *connect.Client[ermite.CreateRepoRequest, ermite.CreateRepoResponse]
118+ updateRepo *connect.Client[ermite.UpdateRepoRequest, ermite.UpdateRepoResponse]
119+ deleteRepo *connect.Client[ermite.DeleteRepoRequest, ermite.DeleteRepoResponse]
120+}
121+
122+// ListRepos calls ermite.RepoService.ListRepos.
123+func (c *repoServiceClient) ListRepos(ctx context.Context, req *connect.Request[ermite.ListReposRequest]) (*connect.Response[ermite.ListReposResponse], error) {
124+ return c.listRepos.CallUnary(ctx, req)
125+}
126+
127+// GetRepo calls ermite.RepoService.GetRepo.
128+func (c *repoServiceClient) GetRepo(ctx context.Context, req *connect.Request[ermite.GetRepoRequest]) (*connect.Response[ermite.GetRepoResponse], error) {
129+ return c.getRepo.CallUnary(ctx, req)
130+}
131+
132+// CreateRepo calls ermite.RepoService.CreateRepo.
133+func (c *repoServiceClient) CreateRepo(ctx context.Context, req *connect.Request[ermite.CreateRepoRequest]) (*connect.Response[ermite.CreateRepoResponse], error) {
134+ return c.createRepo.CallUnary(ctx, req)
135+}
136+
137+// UpdateRepo calls ermite.RepoService.UpdateRepo.
138+func (c *repoServiceClient) UpdateRepo(ctx context.Context, req *connect.Request[ermite.UpdateRepoRequest]) (*connect.Response[ermite.UpdateRepoResponse], error) {
139+ return c.updateRepo.CallUnary(ctx, req)
140+}
141+
142+// DeleteRepo calls ermite.RepoService.DeleteRepo.
143+func (c *repoServiceClient) DeleteRepo(ctx context.Context, req *connect.Request[ermite.DeleteRepoRequest]) (*connect.Response[ermite.DeleteRepoResponse], error) {
144+ return c.deleteRepo.CallUnary(ctx, req)
145+}
146+
147+// RepoServiceHandler is an implementation of the ermite.RepoService service.
148+type RepoServiceHandler interface {
149+ // ListRepos lists the repositories the caller may see, ordered by name.
150+ ListRepos(context.Context, *connect.Request[ermite.ListReposRequest]) (*connect.Response[ermite.ListReposResponse], error)
151+ // GetRepo returns one repository. Fails with not_found if it does not exist
152+ // or is private and not readable by the caller.
153+ 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+ // with already_exists if the name is taken and invalid_argument if the name
156+ // is not valid.
157+ 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(context.Context, *connect.Request[ermite.UpdateRepoRequest]) (*connect.Response[ermite.UpdateRepoResponse], error)
161+ // 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.
163+ DeleteRepo(context.Context, *connect.Request[ermite.DeleteRepoRequest]) (*connect.Response[ermite.DeleteRepoResponse], error)
164+}
165+
166+// NewRepoServiceHandler builds an HTTP handler from the service implementation. It returns the path
167+// on which to mount the handler and the handler itself.
168+//
169+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
170+// and JSON codecs. They also support gzip compression.
171+func NewRepoServiceHandler(svc RepoServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
172+ repoServiceMethods := ermite.File_ermite_repo_proto.Services().ByName("RepoService").Methods()
173+ repoServiceListReposHandler := connect.NewUnaryHandler(
174+ RepoServiceListReposProcedure,
175+ svc.ListRepos,
176+ connect.WithSchema(repoServiceMethods.ByName("ListRepos")),
177+ connect.WithHandlerOptions(opts...),
178+ )
179+ repoServiceGetRepoHandler := connect.NewUnaryHandler(
180+ RepoServiceGetRepoProcedure,
181+ svc.GetRepo,
182+ connect.WithSchema(repoServiceMethods.ByName("GetRepo")),
183+ connect.WithHandlerOptions(opts...),
184+ )
185+ repoServiceCreateRepoHandler := connect.NewUnaryHandler(
186+ RepoServiceCreateRepoProcedure,
187+ svc.CreateRepo,
188+ connect.WithSchema(repoServiceMethods.ByName("CreateRepo")),
189+ connect.WithHandlerOptions(opts...),
190+ )
191+ repoServiceUpdateRepoHandler := connect.NewUnaryHandler(
192+ RepoServiceUpdateRepoProcedure,
193+ svc.UpdateRepo,
194+ connect.WithSchema(repoServiceMethods.ByName("UpdateRepo")),
195+ connect.WithHandlerOptions(opts...),
196+ )
197+ repoServiceDeleteRepoHandler := connect.NewUnaryHandler(
198+ RepoServiceDeleteRepoProcedure,
199+ svc.DeleteRepo,
200+ connect.WithSchema(repoServiceMethods.ByName("DeleteRepo")),
201+ connect.WithHandlerOptions(opts...),
202+ )
203+ return "/ermite.RepoService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
204+ switch r.URL.Path {
205+ case RepoServiceListReposProcedure:
206+ repoServiceListReposHandler.ServeHTTP(w, r)
207+ case RepoServiceGetRepoProcedure:
208+ repoServiceGetRepoHandler.ServeHTTP(w, r)
209+ case RepoServiceCreateRepoProcedure:
210+ repoServiceCreateRepoHandler.ServeHTTP(w, r)
211+ case RepoServiceUpdateRepoProcedure:
212+ repoServiceUpdateRepoHandler.ServeHTTP(w, r)
213+ case RepoServiceDeleteRepoProcedure:
214+ repoServiceDeleteRepoHandler.ServeHTTP(w, r)
215+ default:
216+ http.NotFound(w, r)
217+ }
218+ })
219+}
220+
221+// UnimplementedRepoServiceHandler returns CodeUnimplemented from all methods.
222+type UnimplementedRepoServiceHandler struct{}
223+
224+func (UnimplementedRepoServiceHandler) ListRepos(context.Context, *connect.Request[ermite.ListReposRequest]) (*connect.Response[ermite.ListReposResponse], error) {
225+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RepoService.ListRepos is not implemented"))
226+}
227+
228+func (UnimplementedRepoServiceHandler) GetRepo(context.Context, *connect.Request[ermite.GetRepoRequest]) (*connect.Response[ermite.GetRepoResponse], error) {
229+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RepoService.GetRepo is not implemented"))
230+}
231+
232+func (UnimplementedRepoServiceHandler) CreateRepo(context.Context, *connect.Request[ermite.CreateRepoRequest]) (*connect.Response[ermite.CreateRepoResponse], error) {
233+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RepoService.CreateRepo is not implemented"))
234+}
235+
236+func (UnimplementedRepoServiceHandler) UpdateRepo(context.Context, *connect.Request[ermite.UpdateRepoRequest]) (*connect.Response[ermite.UpdateRepoResponse], error) {
237+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RepoService.UpdateRepo is not implemented"))
238+}
239+
240+func (UnimplementedRepoServiceHandler) DeleteRepo(context.Context, *connect.Request[ermite.DeleteRepoRequest]) (*connect.Response[ermite.DeleteRepoResponse], error) {
241+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.RepoService.DeleteRepo is not implemented"))
242+}
A gen/ermite/ermiteconnect/server.connect.go +117 -0
@@ -0,0 +1,117 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/server.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+ // ServerServiceName is the fully-qualified name of the ServerService service.
27+ ServerServiceName = "ermite.ServerService"
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+ // ServerServiceGetVersionProcedure is the fully-qualified name of the ServerService's GetVersion
39+ // RPC.
40+ ServerServiceGetVersionProcedure = "/ermite.ServerService/GetVersion"
41+)
42+
43+// ServerServiceClient is a client for the ermite.ServerService service.
44+type ServerServiceClient interface {
45+ // GetVersion returns the server version and the optional capabilities of the
46+ // contract it implements. It needs no authentication, so a client can call
47+ // it first to check compatibility.
48+ GetVersion(context.Context, *connect.Request[ermite.GetVersionRequest]) (*connect.Response[ermite.GetVersionResponse], error)
49+}
50+
51+// NewServerServiceClient constructs a client for the ermite.ServerService service. By default, it
52+// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
53+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
54+// connect.WithGRPCWeb() options.
55+//
56+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
57+// http://api.acme.com or https://acme.com/grpc).
58+func NewServerServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) ServerServiceClient {
59+ baseURL = strings.TrimRight(baseURL, "/")
60+ serverServiceMethods := ermite.File_ermite_server_proto.Services().ByName("ServerService").Methods()
61+ return &serverServiceClient{
62+ getVersion: connect.NewClient[ermite.GetVersionRequest, ermite.GetVersionResponse](
63+ httpClient,
64+ baseURL+ServerServiceGetVersionProcedure,
65+ connect.WithSchema(serverServiceMethods.ByName("GetVersion")),
66+ connect.WithClientOptions(opts...),
67+ ),
68+ }
69+}
70+
71+// serverServiceClient implements ServerServiceClient.
72+type serverServiceClient struct {
73+ getVersion *connect.Client[ermite.GetVersionRequest, ermite.GetVersionResponse]
74+}
75+
76+// GetVersion calls ermite.ServerService.GetVersion.
77+func (c *serverServiceClient) GetVersion(ctx context.Context, req *connect.Request[ermite.GetVersionRequest]) (*connect.Response[ermite.GetVersionResponse], error) {
78+ return c.getVersion.CallUnary(ctx, req)
79+}
80+
81+// ServerServiceHandler is an implementation of the ermite.ServerService service.
82+type ServerServiceHandler interface {
83+ // GetVersion returns the server version and the optional capabilities of the
84+ // contract it implements. It needs no authentication, so a client can call
85+ // it first to check compatibility.
86+ GetVersion(context.Context, *connect.Request[ermite.GetVersionRequest]) (*connect.Response[ermite.GetVersionResponse], error)
87+}
88+
89+// NewServerServiceHandler builds an HTTP handler from the service implementation. It returns the
90+// path on which to mount the handler and the handler itself.
91+//
92+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
93+// and JSON codecs. They also support gzip compression.
94+func NewServerServiceHandler(svc ServerServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
95+ serverServiceMethods := ermite.File_ermite_server_proto.Services().ByName("ServerService").Methods()
96+ serverServiceGetVersionHandler := connect.NewUnaryHandler(
97+ ServerServiceGetVersionProcedure,
98+ svc.GetVersion,
99+ connect.WithSchema(serverServiceMethods.ByName("GetVersion")),
100+ connect.WithHandlerOptions(opts...),
101+ )
102+ return "/ermite.ServerService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
103+ switch r.URL.Path {
104+ case ServerServiceGetVersionProcedure:
105+ serverServiceGetVersionHandler.ServeHTTP(w, r)
106+ default:
107+ http.NotFound(w, r)
108+ }
109+ })
110+}
111+
112+// UnimplementedServerServiceHandler returns CodeUnimplemented from all methods.
113+type UnimplementedServerServiceHandler struct{}
114+
115+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"))
117+}
@@ -0,0 +1,117 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/server.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+ // ServerServiceName is the fully-qualified name of the ServerService service.
27+ ServerServiceName = "ermite.ServerService"
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+ // ServerServiceGetVersionProcedure is the fully-qualified name of the ServerService's GetVersion
39+ // RPC.
40+ ServerServiceGetVersionProcedure = "/ermite.ServerService/GetVersion"
41+)
42+
43+// ServerServiceClient is a client for the ermite.ServerService service.
44+type ServerServiceClient interface {
45+ // GetVersion returns the server version and the optional capabilities of the
46+ // contract it implements. It needs no authentication, so a client can call
47+ // it first to check compatibility.
48+ GetVersion(context.Context, *connect.Request[ermite.GetVersionRequest]) (*connect.Response[ermite.GetVersionResponse], error)
49+}
50+
51+// NewServerServiceClient constructs a client for the ermite.ServerService service. By default, it
52+// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
53+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
54+// connect.WithGRPCWeb() options.
55+//
56+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
57+// http://api.acme.com or https://acme.com/grpc).
58+func NewServerServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) ServerServiceClient {
59+ baseURL = strings.TrimRight(baseURL, "/")
60+ serverServiceMethods := ermite.File_ermite_server_proto.Services().ByName("ServerService").Methods()
61+ return &serverServiceClient{
62+ getVersion: connect.NewClient[ermite.GetVersionRequest, ermite.GetVersionResponse](
63+ httpClient,
64+ baseURL+ServerServiceGetVersionProcedure,
65+ connect.WithSchema(serverServiceMethods.ByName("GetVersion")),
66+ connect.WithClientOptions(opts...),
67+ ),
68+ }
69+}
70+
71+// serverServiceClient implements ServerServiceClient.
72+type serverServiceClient struct {
73+ getVersion *connect.Client[ermite.GetVersionRequest, ermite.GetVersionResponse]
74+}
75+
76+// GetVersion calls ermite.ServerService.GetVersion.
77+func (c *serverServiceClient) GetVersion(ctx context.Context, req *connect.Request[ermite.GetVersionRequest]) (*connect.Response[ermite.GetVersionResponse], error) {
78+ return c.getVersion.CallUnary(ctx, req)
79+}
80+
81+// ServerServiceHandler is an implementation of the ermite.ServerService service.
82+type ServerServiceHandler interface {
83+ // GetVersion returns the server version and the optional capabilities of the
84+ // contract it implements. It needs no authentication, so a client can call
85+ // it first to check compatibility.
86+ GetVersion(context.Context, *connect.Request[ermite.GetVersionRequest]) (*connect.Response[ermite.GetVersionResponse], error)
87+}
88+
89+// NewServerServiceHandler builds an HTTP handler from the service implementation. It returns the
90+// path on which to mount the handler and the handler itself.
91+//
92+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
93+// and JSON codecs. They also support gzip compression.
94+func NewServerServiceHandler(svc ServerServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
95+ serverServiceMethods := ermite.File_ermite_server_proto.Services().ByName("ServerService").Methods()
96+ serverServiceGetVersionHandler := connect.NewUnaryHandler(
97+ ServerServiceGetVersionProcedure,
98+ svc.GetVersion,
99+ connect.WithSchema(serverServiceMethods.ByName("GetVersion")),
100+ connect.WithHandlerOptions(opts...),
101+ )
102+ return "/ermite.ServerService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
103+ switch r.URL.Path {
104+ case ServerServiceGetVersionProcedure:
105+ serverServiceGetVersionHandler.ServeHTTP(w, r)
106+ default:
107+ http.NotFound(w, r)
108+ }
109+ })
110+}
111+
112+// UnimplementedServerServiceHandler returns CodeUnimplemented from all methods.
113+type UnimplementedServerServiceHandler struct{}
114+
115+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"))
117+}
A gen/ermite/ermiteconnect/token.connect.go +182 -0
@@ -0,0 +1,182 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/token.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+ // TokenServiceName is the fully-qualified name of the TokenService service.
27+ TokenServiceName = "ermite.TokenService"
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+ // TokenServiceCreateTokenProcedure is the fully-qualified name of the TokenService's CreateToken
39+ // RPC.
40+ TokenServiceCreateTokenProcedure = "/ermite.TokenService/CreateToken"
41+ // TokenServiceListTokensProcedure is the fully-qualified name of the TokenService's ListTokens RPC.
42+ TokenServiceListTokensProcedure = "/ermite.TokenService/ListTokens"
43+ // TokenServiceRevokeTokenProcedure is the fully-qualified name of the TokenService's RevokeToken
44+ // RPC.
45+ TokenServiceRevokeTokenProcedure = "/ermite.TokenService/RevokeToken"
46+)
47+
48+// TokenServiceClient is a client for the ermite.TokenService service.
49+type TokenServiceClient interface {
50+ // CreateToken creates a token and returns its secret. The secret is shown
51+ // only in this response and cannot be retrieved later. Fails with
52+ // permission_denied when the new token would exceed the caller's limits and
53+ // with invalid_argument when scopes is empty or contains SCOPE_UNSPECIFIED.
54+ CreateToken(context.Context, *connect.Request[ermite.CreateTokenRequest]) (*connect.Response[ermite.CreateTokenResponse], error)
55+ // ListTokens lists stored tokens, newest first.
56+ ListTokens(context.Context, *connect.Request[ermite.ListTokensRequest]) (*connect.Response[ermite.ListTokensResponse], error)
57+ // RevokeToken deletes a token; it stops working immediately. Fails with
58+ // not_found if the id is unknown.
59+ RevokeToken(context.Context, *connect.Request[ermite.RevokeTokenRequest]) (*connect.Response[ermite.RevokeTokenResponse], error)
60+}
61+
62+// NewTokenServiceClient constructs a client for the ermite.TokenService service. By default, it
63+// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
64+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
65+// connect.WithGRPCWeb() options.
66+//
67+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
68+// http://api.acme.com or https://acme.com/grpc).
69+func NewTokenServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) TokenServiceClient {
70+ baseURL = strings.TrimRight(baseURL, "/")
71+ tokenServiceMethods := ermite.File_ermite_token_proto.Services().ByName("TokenService").Methods()
72+ return &tokenServiceClient{
73+ createToken: connect.NewClient[ermite.CreateTokenRequest, ermite.CreateTokenResponse](
74+ httpClient,
75+ baseURL+TokenServiceCreateTokenProcedure,
76+ connect.WithSchema(tokenServiceMethods.ByName("CreateToken")),
77+ connect.WithClientOptions(opts...),
78+ ),
79+ listTokens: connect.NewClient[ermite.ListTokensRequest, ermite.ListTokensResponse](
80+ httpClient,
81+ baseURL+TokenServiceListTokensProcedure,
82+ connect.WithSchema(tokenServiceMethods.ByName("ListTokens")),
83+ connect.WithClientOptions(opts...),
84+ ),
85+ revokeToken: connect.NewClient[ermite.RevokeTokenRequest, ermite.RevokeTokenResponse](
86+ httpClient,
87+ baseURL+TokenServiceRevokeTokenProcedure,
88+ connect.WithSchema(tokenServiceMethods.ByName("RevokeToken")),
89+ connect.WithClientOptions(opts...),
90+ ),
91+ }
92+}
93+
94+// tokenServiceClient implements TokenServiceClient.
95+type tokenServiceClient struct {
96+ createToken *connect.Client[ermite.CreateTokenRequest, ermite.CreateTokenResponse]
97+ listTokens *connect.Client[ermite.ListTokensRequest, ermite.ListTokensResponse]
98+ revokeToken *connect.Client[ermite.RevokeTokenRequest, ermite.RevokeTokenResponse]
99+}
100+
101+// CreateToken calls ermite.TokenService.CreateToken.
102+func (c *tokenServiceClient) CreateToken(ctx context.Context, req *connect.Request[ermite.CreateTokenRequest]) (*connect.Response[ermite.CreateTokenResponse], error) {
103+ return c.createToken.CallUnary(ctx, req)
104+}
105+
106+// ListTokens calls ermite.TokenService.ListTokens.
107+func (c *tokenServiceClient) ListTokens(ctx context.Context, req *connect.Request[ermite.ListTokensRequest]) (*connect.Response[ermite.ListTokensResponse], error) {
108+ return c.listTokens.CallUnary(ctx, req)
109+}
110+
111+// RevokeToken calls ermite.TokenService.RevokeToken.
112+func (c *tokenServiceClient) RevokeToken(ctx context.Context, req *connect.Request[ermite.RevokeTokenRequest]) (*connect.Response[ermite.RevokeTokenResponse], error) {
113+ return c.revokeToken.CallUnary(ctx, req)
114+}
115+
116+// TokenServiceHandler is an implementation of the ermite.TokenService service.
117+type TokenServiceHandler interface {
118+ // CreateToken creates a token and returns its secret. The secret is shown
119+ // only in this response and cannot be retrieved later. Fails with
120+ // permission_denied when the new token would exceed the caller's limits and
121+ // with invalid_argument when scopes is empty or contains SCOPE_UNSPECIFIED.
122+ CreateToken(context.Context, *connect.Request[ermite.CreateTokenRequest]) (*connect.Response[ermite.CreateTokenResponse], error)
123+ // ListTokens lists stored tokens, newest first.
124+ ListTokens(context.Context, *connect.Request[ermite.ListTokensRequest]) (*connect.Response[ermite.ListTokensResponse], error)
125+ // RevokeToken deletes a token; it stops working immediately. Fails with
126+ // not_found if the id is unknown.
127+ RevokeToken(context.Context, *connect.Request[ermite.RevokeTokenRequest]) (*connect.Response[ermite.RevokeTokenResponse], error)
128+}
129+
130+// NewTokenServiceHandler builds an HTTP handler from the service implementation. It returns the
131+// path on which to mount the handler and the handler itself.
132+//
133+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
134+// and JSON codecs. They also support gzip compression.
135+func NewTokenServiceHandler(svc TokenServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
136+ tokenServiceMethods := ermite.File_ermite_token_proto.Services().ByName("TokenService").Methods()
137+ tokenServiceCreateTokenHandler := connect.NewUnaryHandler(
138+ TokenServiceCreateTokenProcedure,
139+ svc.CreateToken,
140+ connect.WithSchema(tokenServiceMethods.ByName("CreateToken")),
141+ connect.WithHandlerOptions(opts...),
142+ )
143+ tokenServiceListTokensHandler := connect.NewUnaryHandler(
144+ TokenServiceListTokensProcedure,
145+ svc.ListTokens,
146+ connect.WithSchema(tokenServiceMethods.ByName("ListTokens")),
147+ connect.WithHandlerOptions(opts...),
148+ )
149+ tokenServiceRevokeTokenHandler := connect.NewUnaryHandler(
150+ TokenServiceRevokeTokenProcedure,
151+ svc.RevokeToken,
152+ connect.WithSchema(tokenServiceMethods.ByName("RevokeToken")),
153+ connect.WithHandlerOptions(opts...),
154+ )
155+ return "/ermite.TokenService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
156+ switch r.URL.Path {
157+ case TokenServiceCreateTokenProcedure:
158+ tokenServiceCreateTokenHandler.ServeHTTP(w, r)
159+ case TokenServiceListTokensProcedure:
160+ tokenServiceListTokensHandler.ServeHTTP(w, r)
161+ case TokenServiceRevokeTokenProcedure:
162+ tokenServiceRevokeTokenHandler.ServeHTTP(w, r)
163+ default:
164+ http.NotFound(w, r)
165+ }
166+ })
167+}
168+
169+// UnimplementedTokenServiceHandler returns CodeUnimplemented from all methods.
170+type UnimplementedTokenServiceHandler struct{}
171+
172+func (UnimplementedTokenServiceHandler) CreateToken(context.Context, *connect.Request[ermite.CreateTokenRequest]) (*connect.Response[ermite.CreateTokenResponse], error) {
173+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.TokenService.CreateToken is not implemented"))
174+}
175+
176+func (UnimplementedTokenServiceHandler) ListTokens(context.Context, *connect.Request[ermite.ListTokensRequest]) (*connect.Response[ermite.ListTokensResponse], error) {
177+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.TokenService.ListTokens is not implemented"))
178+}
179+
180+func (UnimplementedTokenServiceHandler) RevokeToken(context.Context, *connect.Request[ermite.RevokeTokenRequest]) (*connect.Response[ermite.RevokeTokenResponse], error) {
181+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.TokenService.RevokeToken is not implemented"))
182+}
@@ -0,0 +1,182 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/token.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+ // TokenServiceName is the fully-qualified name of the TokenService service.
27+ TokenServiceName = "ermite.TokenService"
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+ // TokenServiceCreateTokenProcedure is the fully-qualified name of the TokenService's CreateToken
39+ // RPC.
40+ TokenServiceCreateTokenProcedure = "/ermite.TokenService/CreateToken"
41+ // TokenServiceListTokensProcedure is the fully-qualified name of the TokenService's ListTokens RPC.
42+ TokenServiceListTokensProcedure = "/ermite.TokenService/ListTokens"
43+ // TokenServiceRevokeTokenProcedure is the fully-qualified name of the TokenService's RevokeToken
44+ // RPC.
45+ TokenServiceRevokeTokenProcedure = "/ermite.TokenService/RevokeToken"
46+)
47+
48+// TokenServiceClient is a client for the ermite.TokenService service.
49+type TokenServiceClient interface {
50+ // CreateToken creates a token and returns its secret. The secret is shown
51+ // only in this response and cannot be retrieved later. Fails with
52+ // permission_denied when the new token would exceed the caller's limits and
53+ // with invalid_argument when scopes is empty or contains SCOPE_UNSPECIFIED.
54+ CreateToken(context.Context, *connect.Request[ermite.CreateTokenRequest]) (*connect.Response[ermite.CreateTokenResponse], error)
55+ // ListTokens lists stored tokens, newest first.
56+ ListTokens(context.Context, *connect.Request[ermite.ListTokensRequest]) (*connect.Response[ermite.ListTokensResponse], error)
57+ // RevokeToken deletes a token; it stops working immediately. Fails with
58+ // not_found if the id is unknown.
59+ RevokeToken(context.Context, *connect.Request[ermite.RevokeTokenRequest]) (*connect.Response[ermite.RevokeTokenResponse], error)
60+}
61+
62+// NewTokenServiceClient constructs a client for the ermite.TokenService service. By default, it
63+// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
64+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
65+// connect.WithGRPCWeb() options.
66+//
67+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
68+// http://api.acme.com or https://acme.com/grpc).
69+func NewTokenServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) TokenServiceClient {
70+ baseURL = strings.TrimRight(baseURL, "/")
71+ tokenServiceMethods := ermite.File_ermite_token_proto.Services().ByName("TokenService").Methods()
72+ return &tokenServiceClient{
73+ createToken: connect.NewClient[ermite.CreateTokenRequest, ermite.CreateTokenResponse](
74+ httpClient,
75+ baseURL+TokenServiceCreateTokenProcedure,
76+ connect.WithSchema(tokenServiceMethods.ByName("CreateToken")),
77+ connect.WithClientOptions(opts...),
78+ ),
79+ listTokens: connect.NewClient[ermite.ListTokensRequest, ermite.ListTokensResponse](
80+ httpClient,
81+ baseURL+TokenServiceListTokensProcedure,
82+ connect.WithSchema(tokenServiceMethods.ByName("ListTokens")),
83+ connect.WithClientOptions(opts...),
84+ ),
85+ revokeToken: connect.NewClient[ermite.RevokeTokenRequest, ermite.RevokeTokenResponse](
86+ httpClient,
87+ baseURL+TokenServiceRevokeTokenProcedure,
88+ connect.WithSchema(tokenServiceMethods.ByName("RevokeToken")),
89+ connect.WithClientOptions(opts...),
90+ ),
91+ }
92+}
93+
94+// tokenServiceClient implements TokenServiceClient.
95+type tokenServiceClient struct {
96+ createToken *connect.Client[ermite.CreateTokenRequest, ermite.CreateTokenResponse]
97+ listTokens *connect.Client[ermite.ListTokensRequest, ermite.ListTokensResponse]
98+ revokeToken *connect.Client[ermite.RevokeTokenRequest, ermite.RevokeTokenResponse]
99+}
100+
101+// CreateToken calls ermite.TokenService.CreateToken.
102+func (c *tokenServiceClient) CreateToken(ctx context.Context, req *connect.Request[ermite.CreateTokenRequest]) (*connect.Response[ermite.CreateTokenResponse], error) {
103+ return c.createToken.CallUnary(ctx, req)
104+}
105+
106+// ListTokens calls ermite.TokenService.ListTokens.
107+func (c *tokenServiceClient) ListTokens(ctx context.Context, req *connect.Request[ermite.ListTokensRequest]) (*connect.Response[ermite.ListTokensResponse], error) {
108+ return c.listTokens.CallUnary(ctx, req)
109+}
110+
111+// RevokeToken calls ermite.TokenService.RevokeToken.
112+func (c *tokenServiceClient) RevokeToken(ctx context.Context, req *connect.Request[ermite.RevokeTokenRequest]) (*connect.Response[ermite.RevokeTokenResponse], error) {
113+ return c.revokeToken.CallUnary(ctx, req)
114+}
115+
116+// TokenServiceHandler is an implementation of the ermite.TokenService service.
117+type TokenServiceHandler interface {
118+ // CreateToken creates a token and returns its secret. The secret is shown
119+ // only in this response and cannot be retrieved later. Fails with
120+ // permission_denied when the new token would exceed the caller's limits and
121+ // with invalid_argument when scopes is empty or contains SCOPE_UNSPECIFIED.
122+ CreateToken(context.Context, *connect.Request[ermite.CreateTokenRequest]) (*connect.Response[ermite.CreateTokenResponse], error)
123+ // ListTokens lists stored tokens, newest first.
124+ ListTokens(context.Context, *connect.Request[ermite.ListTokensRequest]) (*connect.Response[ermite.ListTokensResponse], error)
125+ // RevokeToken deletes a token; it stops working immediately. Fails with
126+ // not_found if the id is unknown.
127+ RevokeToken(context.Context, *connect.Request[ermite.RevokeTokenRequest]) (*connect.Response[ermite.RevokeTokenResponse], error)
128+}
129+
130+// NewTokenServiceHandler builds an HTTP handler from the service implementation. It returns the
131+// path on which to mount the handler and the handler itself.
132+//
133+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
134+// and JSON codecs. They also support gzip compression.
135+func NewTokenServiceHandler(svc TokenServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
136+ tokenServiceMethods := ermite.File_ermite_token_proto.Services().ByName("TokenService").Methods()
137+ tokenServiceCreateTokenHandler := connect.NewUnaryHandler(
138+ TokenServiceCreateTokenProcedure,
139+ svc.CreateToken,
140+ connect.WithSchema(tokenServiceMethods.ByName("CreateToken")),
141+ connect.WithHandlerOptions(opts...),
142+ )
143+ tokenServiceListTokensHandler := connect.NewUnaryHandler(
144+ TokenServiceListTokensProcedure,
145+ svc.ListTokens,
146+ connect.WithSchema(tokenServiceMethods.ByName("ListTokens")),
147+ connect.WithHandlerOptions(opts...),
148+ )
149+ tokenServiceRevokeTokenHandler := connect.NewUnaryHandler(
150+ TokenServiceRevokeTokenProcedure,
151+ svc.RevokeToken,
152+ connect.WithSchema(tokenServiceMethods.ByName("RevokeToken")),
153+ connect.WithHandlerOptions(opts...),
154+ )
155+ return "/ermite.TokenService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
156+ switch r.URL.Path {
157+ case TokenServiceCreateTokenProcedure:
158+ tokenServiceCreateTokenHandler.ServeHTTP(w, r)
159+ case TokenServiceListTokensProcedure:
160+ tokenServiceListTokensHandler.ServeHTTP(w, r)
161+ case TokenServiceRevokeTokenProcedure:
162+ tokenServiceRevokeTokenHandler.ServeHTTP(w, r)
163+ default:
164+ http.NotFound(w, r)
165+ }
166+ })
167+}
168+
169+// UnimplementedTokenServiceHandler returns CodeUnimplemented from all methods.
170+type UnimplementedTokenServiceHandler struct{}
171+
172+func (UnimplementedTokenServiceHandler) CreateToken(context.Context, *connect.Request[ermite.CreateTokenRequest]) (*connect.Response[ermite.CreateTokenResponse], error) {
173+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.TokenService.CreateToken is not implemented"))
174+}
175+
176+func (UnimplementedTokenServiceHandler) ListTokens(context.Context, *connect.Request[ermite.ListTokensRequest]) (*connect.Response[ermite.ListTokensResponse], error) {
177+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.TokenService.ListTokens is not implemented"))
178+}
179+
180+func (UnimplementedTokenServiceHandler) RevokeToken(context.Context, *connect.Request[ermite.RevokeTokenRequest]) (*connect.Response[ermite.RevokeTokenResponse], error) {
181+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.TokenService.RevokeToken is not implemented"))
182+}
A gen/ermite/event.pb.go +703 -0
@@ -0,0 +1,703 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-go. DO NOT EDIT.
4+// versions:
5+// protoc-gen-go v1.36.12
6+// protoc (unknown)
7+// source: ermite/event.proto
8+
9+package ermite
10+
11+import (
12+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
13+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
14+ timestamppb "google.golang.org/protobuf/types/known/timestamppb"
15+ reflect "reflect"
16+ sync "sync"
17+ unsafe "unsafe"
18+)
19+
20+const (
21+ // Verify that this generated code is sufficiently up-to-date.
22+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
23+ // Verify that runtime/protoimpl is sufficiently up-to-date.
24+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
25+)
26+
27+// WatchRequest selects the events to receive.
28+type WatchRequest struct {
29+ state protoimpl.MessageState `protogen:"open.v1"`
30+ // Topics are the subjects to follow: "home" for every repository visible to
31+ // the caller, or "repo:<name>" for one repository. Empty means "home".
32+ // Unknown topics are invalid_argument. A "repo:<name>" topic for a repository
33+ // that does not exist or is not visible is not_found.
34+ Topics []string `protobuf:"bytes,1,rep,name=topics,proto3" json:"topics,omitempty"`
35+ // ResumeAfter is the Event.id of the last event the client has seen. Empty
36+ // to receive only new events.
37+ ResumeAfter string `protobuf:"bytes,2,opt,name=resume_after,json=resumeAfter,proto3" json:"resume_after,omitempty"`
38+ unknownFields protoimpl.UnknownFields
39+ sizeCache protoimpl.SizeCache
40+}
41+
42+func (x *WatchRequest) Reset() {
43+ *x = WatchRequest{}
44+ mi := &file_ermite_event_proto_msgTypes[0]
45+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
46+ ms.StoreMessageInfo(mi)
47+}
48+
49+func (x *WatchRequest) String() string {
50+ return protoimpl.X.MessageStringOf(x)
51+}
52+
53+func (*WatchRequest) ProtoMessage() {}
54+
55+func (x *WatchRequest) ProtoReflect() protoreflect.Message {
56+ mi := &file_ermite_event_proto_msgTypes[0]
57+ if x != nil {
58+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
59+ if ms.LoadMessageInfo() == nil {
60+ ms.StoreMessageInfo(mi)
61+ }
62+ return ms
63+ }
64+ return mi.MessageOf(x)
65+}
66+
67+// Deprecated: Use WatchRequest.ProtoReflect.Descriptor instead.
68+func (*WatchRequest) Descriptor() ([]byte, []int) {
69+ return file_ermite_event_proto_rawDescGZIP(), []int{0}
70+}
71+
72+func (x *WatchRequest) GetTopics() []string {
73+ if x != nil {
74+ return x.Topics
75+ }
76+ return nil
77+}
78+
79+func (x *WatchRequest) GetResumeAfter() string {
80+ if x != nil {
81+ return x.ResumeAfter
82+ }
83+ return ""
84+}
85+
86+// WatchResponse carries one event of the stream.
87+type WatchResponse struct {
88+ state protoimpl.MessageState `protogen:"open.v1"`
89+ // Event is the event.
90+ Event *Event `protobuf:"bytes,1,opt,name=event,proto3" json:"event,omitempty"`
91+ unknownFields protoimpl.UnknownFields
92+ sizeCache protoimpl.SizeCache
93+}
94+
95+func (x *WatchResponse) Reset() {
96+ *x = WatchResponse{}
97+ mi := &file_ermite_event_proto_msgTypes[1]
98+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
99+ ms.StoreMessageInfo(mi)
100+}
101+
102+func (x *WatchResponse) String() string {
103+ return protoimpl.X.MessageStringOf(x)
104+}
105+
106+func (*WatchResponse) ProtoMessage() {}
107+
108+func (x *WatchResponse) ProtoReflect() protoreflect.Message {
109+ mi := &file_ermite_event_proto_msgTypes[1]
110+ if x != nil {
111+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
112+ if ms.LoadMessageInfo() == nil {
113+ ms.StoreMessageInfo(mi)
114+ }
115+ return ms
116+ }
117+ return mi.MessageOf(x)
118+}
119+
120+// Deprecated: Use WatchResponse.ProtoReflect.Descriptor instead.
121+func (*WatchResponse) Descriptor() ([]byte, []int) {
122+ return file_ermite_event_proto_rawDescGZIP(), []int{1}
123+}
124+
125+func (x *WatchResponse) GetEvent() *Event {
126+ if x != nil {
127+ return x.Event
128+ }
129+ return nil
130+}
131+
132+// Event is something that happened on the forge.
133+type Event struct {
134+ state protoimpl.MessageState `protogen:"open.v1"`
135+ // Id identifies the event and is passed as resume_after to resume a stream.
136+ // It is opaque and only comparable for equality; clients must not parse it.
137+ // Empty on a Gap event.
138+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
139+ // Time is when the event happened.
140+ Time *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=time,proto3" json:"time,omitempty"`
141+ // Kind is the type and payload of the event. New kinds may be added; clients
142+ // must ignore kinds they do not know.
143+ //
144+ // Types that are valid to be assigned to Kind:
145+ //
146+ // *Event_RepoCreated
147+ // *Event_RepoUpdated
148+ // *Event_RepoDeleted
149+ // *Event_Pushed
150+ // *Event_Gap
151+ Kind isEvent_Kind `protobuf_oneof:"kind"`
152+ unknownFields protoimpl.UnknownFields
153+ sizeCache protoimpl.SizeCache
154+}
155+
156+func (x *Event) Reset() {
157+ *x = Event{}
158+ mi := &file_ermite_event_proto_msgTypes[2]
159+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
160+ ms.StoreMessageInfo(mi)
161+}
162+
163+func (x *Event) String() string {
164+ return protoimpl.X.MessageStringOf(x)
165+}
166+
167+func (*Event) ProtoMessage() {}
168+
169+func (x *Event) ProtoReflect() protoreflect.Message {
170+ mi := &file_ermite_event_proto_msgTypes[2]
171+ if x != nil {
172+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
173+ if ms.LoadMessageInfo() == nil {
174+ ms.StoreMessageInfo(mi)
175+ }
176+ return ms
177+ }
178+ return mi.MessageOf(x)
179+}
180+
181+// Deprecated: Use Event.ProtoReflect.Descriptor instead.
182+func (*Event) Descriptor() ([]byte, []int) {
183+ return file_ermite_event_proto_rawDescGZIP(), []int{2}
184+}
185+
186+func (x *Event) GetId() string {
187+ if x != nil {
188+ return x.Id
189+ }
190+ return ""
191+}
192+
193+func (x *Event) GetTime() *timestamppb.Timestamp {
194+ if x != nil {
195+ return x.Time
196+ }
197+ return nil
198+}
199+
200+func (x *Event) GetKind() isEvent_Kind {
201+ if x != nil {
202+ return x.Kind
203+ }
204+ return nil
205+}
206+
207+func (x *Event) GetRepoCreated() *RepoCreated {
208+ if x != nil {
209+ if x, ok := x.Kind.(*Event_RepoCreated); ok {
210+ return x.RepoCreated
211+ }
212+ }
213+ return nil
214+}
215+
216+func (x *Event) GetRepoUpdated() *RepoUpdated {
217+ if x != nil {
218+ if x, ok := x.Kind.(*Event_RepoUpdated); ok {
219+ return x.RepoUpdated
220+ }
221+ }
222+ return nil
223+}
224+
225+func (x *Event) GetRepoDeleted() *RepoDeleted {
226+ if x != nil {
227+ if x, ok := x.Kind.(*Event_RepoDeleted); ok {
228+ return x.RepoDeleted
229+ }
230+ }
231+ return nil
232+}
233+
234+func (x *Event) GetPushed() *Pushed {
235+ if x != nil {
236+ if x, ok := x.Kind.(*Event_Pushed); ok {
237+ return x.Pushed
238+ }
239+ }
240+ return nil
241+}
242+
243+func (x *Event) GetGap() *Gap {
244+ if x != nil {
245+ if x, ok := x.Kind.(*Event_Gap); ok {
246+ return x.Gap
247+ }
248+ }
249+ return nil
250+}
251+
252+type isEvent_Kind interface {
253+ isEvent_Kind()
254+}
255+
256+type Event_RepoCreated struct {
257+ // RepoCreated: a repository was created.
258+ RepoCreated *RepoCreated `protobuf:"bytes,3,opt,name=repo_created,json=repoCreated,proto3,oneof"`
259+}
260+
261+type Event_RepoUpdated struct {
262+ // RepoUpdated: repository metadata changed.
263+ RepoUpdated *RepoUpdated `protobuf:"bytes,4,opt,name=repo_updated,json=repoUpdated,proto3,oneof"`
264+}
265+
266+type Event_RepoDeleted struct {
267+ // RepoDeleted: a repository was deleted.
268+ RepoDeleted *RepoDeleted `protobuf:"bytes,5,opt,name=repo_deleted,json=repoDeleted,proto3,oneof"`
269+}
270+
271+type Event_Pushed struct {
272+ // Pushed: refs of a repository were updated by a push.
273+ Pushed *Pushed `protobuf:"bytes,6,opt,name=pushed,proto3,oneof"`
274+}
275+
276+type Event_Gap struct {
277+ // Gap: events were lost, reload state.
278+ Gap *Gap `protobuf:"bytes,7,opt,name=gap,proto3,oneof"`
279+}
280+
281+func (*Event_RepoCreated) isEvent_Kind() {}
282+
283+func (*Event_RepoUpdated) isEvent_Kind() {}
284+
285+func (*Event_RepoDeleted) isEvent_Kind() {}
286+
287+func (*Event_Pushed) isEvent_Kind() {}
288+
289+func (*Event_Gap) isEvent_Kind() {}
290+
291+// RepoCreated reports a new repository.
292+type RepoCreated struct {
293+ state protoimpl.MessageState `protogen:"open.v1"`
294+ // Repo is the new repository.
295+ Repo *Repo `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
296+ unknownFields protoimpl.UnknownFields
297+ sizeCache protoimpl.SizeCache
298+}
299+
300+func (x *RepoCreated) Reset() {
301+ *x = RepoCreated{}
302+ mi := &file_ermite_event_proto_msgTypes[3]
303+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
304+ ms.StoreMessageInfo(mi)
305+}
306+
307+func (x *RepoCreated) String() string {
308+ return protoimpl.X.MessageStringOf(x)
309+}
310+
311+func (*RepoCreated) ProtoMessage() {}
312+
313+func (x *RepoCreated) ProtoReflect() protoreflect.Message {
314+ mi := &file_ermite_event_proto_msgTypes[3]
315+ if x != nil {
316+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
317+ if ms.LoadMessageInfo() == nil {
318+ ms.StoreMessageInfo(mi)
319+ }
320+ return ms
321+ }
322+ return mi.MessageOf(x)
323+}
324+
325+// Deprecated: Use RepoCreated.ProtoReflect.Descriptor instead.
326+func (*RepoCreated) Descriptor() ([]byte, []int) {
327+ return file_ermite_event_proto_rawDescGZIP(), []int{3}
328+}
329+
330+func (x *RepoCreated) GetRepo() *Repo {
331+ if x != nil {
332+ return x.Repo
333+ }
334+ return nil
335+}
336+
337+// RepoUpdated reports a metadata change.
338+type RepoUpdated struct {
339+ state protoimpl.MessageState `protogen:"open.v1"`
340+ // Repo is the repository after the change.
341+ Repo *Repo `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
342+ unknownFields protoimpl.UnknownFields
343+ sizeCache protoimpl.SizeCache
344+}
345+
346+func (x *RepoUpdated) Reset() {
347+ *x = RepoUpdated{}
348+ mi := &file_ermite_event_proto_msgTypes[4]
349+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
350+ ms.StoreMessageInfo(mi)
351+}
352+
353+func (x *RepoUpdated) String() string {
354+ return protoimpl.X.MessageStringOf(x)
355+}
356+
357+func (*RepoUpdated) ProtoMessage() {}
358+
359+func (x *RepoUpdated) ProtoReflect() protoreflect.Message {
360+ mi := &file_ermite_event_proto_msgTypes[4]
361+ if x != nil {
362+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
363+ if ms.LoadMessageInfo() == nil {
364+ ms.StoreMessageInfo(mi)
365+ }
366+ return ms
367+ }
368+ return mi.MessageOf(x)
369+}
370+
371+// Deprecated: Use RepoUpdated.ProtoReflect.Descriptor instead.
372+func (*RepoUpdated) Descriptor() ([]byte, []int) {
373+ return file_ermite_event_proto_rawDescGZIP(), []int{4}
374+}
375+
376+func (x *RepoUpdated) GetRepo() *Repo {
377+ if x != nil {
378+ return x.Repo
379+ }
380+ return nil
381+}
382+
383+// RepoDeleted reports a deleted repository.
384+type RepoDeleted struct {
385+ state protoimpl.MessageState `protogen:"open.v1"`
386+ // Repo is the name of the deleted repository.
387+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
388+ unknownFields protoimpl.UnknownFields
389+ sizeCache protoimpl.SizeCache
390+}
391+
392+func (x *RepoDeleted) Reset() {
393+ *x = RepoDeleted{}
394+ mi := &file_ermite_event_proto_msgTypes[5]
395+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
396+ ms.StoreMessageInfo(mi)
397+}
398+
399+func (x *RepoDeleted) String() string {
400+ return protoimpl.X.MessageStringOf(x)
401+}
402+
403+func (*RepoDeleted) ProtoMessage() {}
404+
405+func (x *RepoDeleted) ProtoReflect() protoreflect.Message {
406+ mi := &file_ermite_event_proto_msgTypes[5]
407+ if x != nil {
408+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
409+ if ms.LoadMessageInfo() == nil {
410+ ms.StoreMessageInfo(mi)
411+ }
412+ return ms
413+ }
414+ return mi.MessageOf(x)
415+}
416+
417+// Deprecated: Use RepoDeleted.ProtoReflect.Descriptor instead.
418+func (*RepoDeleted) Descriptor() ([]byte, []int) {
419+ return file_ermite_event_proto_rawDescGZIP(), []int{5}
420+}
421+
422+func (x *RepoDeleted) GetRepo() string {
423+ if x != nil {
424+ return x.Repo
425+ }
426+ return ""
427+}
428+
429+// Pushed reports the ref updates of one push.
430+type Pushed struct {
431+ state protoimpl.MessageState `protogen:"open.v1"`
432+ // Repo is the name of the repository.
433+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
434+ // RefUpdates are the refs changed by the push.
435+ RefUpdates []*RefUpdate `protobuf:"bytes,2,rep,name=ref_updates,json=refUpdates,proto3" json:"ref_updates,omitempty"`
436+ unknownFields protoimpl.UnknownFields
437+ sizeCache protoimpl.SizeCache
438+}
439+
440+func (x *Pushed) Reset() {
441+ *x = Pushed{}
442+ mi := &file_ermite_event_proto_msgTypes[6]
443+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
444+ ms.StoreMessageInfo(mi)
445+}
446+
447+func (x *Pushed) String() string {
448+ return protoimpl.X.MessageStringOf(x)
449+}
450+
451+func (*Pushed) ProtoMessage() {}
452+
453+func (x *Pushed) ProtoReflect() protoreflect.Message {
454+ mi := &file_ermite_event_proto_msgTypes[6]
455+ if x != nil {
456+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
457+ if ms.LoadMessageInfo() == nil {
458+ ms.StoreMessageInfo(mi)
459+ }
460+ return ms
461+ }
462+ return mi.MessageOf(x)
463+}
464+
465+// Deprecated: Use Pushed.ProtoReflect.Descriptor instead.
466+func (*Pushed) Descriptor() ([]byte, []int) {
467+ return file_ermite_event_proto_rawDescGZIP(), []int{6}
468+}
469+
470+func (x *Pushed) GetRepo() string {
471+ if x != nil {
472+ return x.Repo
473+ }
474+ return ""
475+}
476+
477+func (x *Pushed) GetRefUpdates() []*RefUpdate {
478+ if x != nil {
479+ return x.RefUpdates
480+ }
481+ return nil
482+}
483+
484+// RefUpdate is the change of one ref.
485+type RefUpdate struct {
486+ state protoimpl.MessageState `protogen:"open.v1"`
487+ // Ref is the full ref name, for example "refs/heads/main".
488+ Ref string `protobuf:"bytes,1,opt,name=ref,proto3" json:"ref,omitempty"`
489+ // OldId is the object id before the update. Empty when the ref was created.
490+ OldId string `protobuf:"bytes,2,opt,name=old_id,json=oldId,proto3" json:"old_id,omitempty"`
491+ // NewId is the object id after the update. Empty when the ref was deleted.
492+ NewId string `protobuf:"bytes,3,opt,name=new_id,json=newId,proto3" json:"new_id,omitempty"`
493+ unknownFields protoimpl.UnknownFields
494+ sizeCache protoimpl.SizeCache
495+}
496+
497+func (x *RefUpdate) Reset() {
498+ *x = RefUpdate{}
499+ mi := &file_ermite_event_proto_msgTypes[7]
500+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
501+ ms.StoreMessageInfo(mi)
502+}
503+
504+func (x *RefUpdate) String() string {
505+ return protoimpl.X.MessageStringOf(x)
506+}
507+
508+func (*RefUpdate) ProtoMessage() {}
509+
510+func (x *RefUpdate) ProtoReflect() protoreflect.Message {
511+ mi := &file_ermite_event_proto_msgTypes[7]
512+ if x != nil {
513+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
514+ if ms.LoadMessageInfo() == nil {
515+ ms.StoreMessageInfo(mi)
516+ }
517+ return ms
518+ }
519+ return mi.MessageOf(x)
520+}
521+
522+// Deprecated: Use RefUpdate.ProtoReflect.Descriptor instead.
523+func (*RefUpdate) Descriptor() ([]byte, []int) {
524+ return file_ermite_event_proto_rawDescGZIP(), []int{7}
525+}
526+
527+func (x *RefUpdate) GetRef() string {
528+ if x != nil {
529+ return x.Ref
530+ }
531+ return ""
532+}
533+
534+func (x *RefUpdate) GetOldId() string {
535+ if x != nil {
536+ return x.OldId
537+ }
538+ return ""
539+}
540+
541+func (x *RefUpdate) GetNewId() string {
542+ if x != nil {
543+ return x.NewId
544+ }
545+ return ""
546+}
547+
548+// Gap tells the client that events were lost: the requested resume point is
549+// older than the server's buffer. It has no fields. The client should reload
550+// the state it displays (repositories, refs) and carry on with the next events.
551+type Gap struct {
552+ state protoimpl.MessageState `protogen:"open.v1"`
553+ unknownFields protoimpl.UnknownFields
554+ sizeCache protoimpl.SizeCache
555+}
556+
557+func (x *Gap) Reset() {
558+ *x = Gap{}
559+ mi := &file_ermite_event_proto_msgTypes[8]
560+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
561+ ms.StoreMessageInfo(mi)
562+}
563+
564+func (x *Gap) String() string {
565+ return protoimpl.X.MessageStringOf(x)
566+}
567+
568+func (*Gap) ProtoMessage() {}
569+
570+func (x *Gap) ProtoReflect() protoreflect.Message {
571+ mi := &file_ermite_event_proto_msgTypes[8]
572+ if x != nil {
573+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
574+ if ms.LoadMessageInfo() == nil {
575+ ms.StoreMessageInfo(mi)
576+ }
577+ return ms
578+ }
579+ return mi.MessageOf(x)
580+}
581+
582+// Deprecated: Use Gap.ProtoReflect.Descriptor instead.
583+func (*Gap) Descriptor() ([]byte, []int) {
584+ return file_ermite_event_proto_rawDescGZIP(), []int{8}
585+}
586+
587+var File_ermite_event_proto protoreflect.FileDescriptor
588+
589+const file_ermite_event_proto_rawDesc = "" +
590+ "\n" +
591+ "\x12ermite/event.proto\x12\x06ermite\x1a\x11ermite/repo.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"I\n" +
592+ "\fWatchRequest\x12\x16\n" +
593+ "\x06topics\x18\x01 \x03(\tR\x06topics\x12!\n" +
594+ "\fresume_after\x18\x02 \x01(\tR\vresumeAfter\"4\n" +
595+ "\rWatchResponse\x12#\n" +
596+ "\x05event\x18\x01 \x01(\v2\r.ermite.EventR\x05event\"\xc8\x02\n" +
597+ "\x05Event\x12\x0e\n" +
598+ "\x02id\x18\x01 \x01(\tR\x02id\x12.\n" +
599+ "\x04time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x04time\x128\n" +
600+ "\frepo_created\x18\x03 \x01(\v2\x13.ermite.RepoCreatedH\x00R\vrepoCreated\x128\n" +
601+ "\frepo_updated\x18\x04 \x01(\v2\x13.ermite.RepoUpdatedH\x00R\vrepoUpdated\x128\n" +
602+ "\frepo_deleted\x18\x05 \x01(\v2\x13.ermite.RepoDeletedH\x00R\vrepoDeleted\x12(\n" +
603+ "\x06pushed\x18\x06 \x01(\v2\x0e.ermite.PushedH\x00R\x06pushed\x12\x1f\n" +
604+ "\x03gap\x18\a \x01(\v2\v.ermite.GapH\x00R\x03gapB\x06\n" +
605+ "\x04kind\"/\n" +
606+ "\vRepoCreated\x12 \n" +
607+ "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"/\n" +
608+ "\vRepoUpdated\x12 \n" +
609+ "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"!\n" +
610+ "\vRepoDeleted\x12\x12\n" +
611+ "\x04repo\x18\x01 \x01(\tR\x04repo\"P\n" +
612+ "\x06Pushed\x12\x12\n" +
613+ "\x04repo\x18\x01 \x01(\tR\x04repo\x122\n" +
614+ "\vref_updates\x18\x02 \x03(\v2\x11.ermite.RefUpdateR\n" +
615+ "refUpdates\"K\n" +
616+ "\tRefUpdate\x12\x10\n" +
617+ "\x03ref\x18\x01 \x01(\tR\x03ref\x12\x15\n" +
618+ "\x06old_id\x18\x02 \x01(\tR\x05oldId\x12\x15\n" +
619+ "\x06new_id\x18\x03 \x01(\tR\x05newId\"\x05\n" +
620+ "\x03Gap2F\n" +
621+ "\fEventService\x126\n" +
622+ "\x05Watch\x12\x14.ermite.WatchRequest\x1a\x15.ermite.WatchResponse0\x01Bq\n" +
623+ "\n" +
624+ "com.ermiteB\n" +
625+ "EventProtoP\x01Z\x1fbnema.dev/ermite-api/gen/ermite\xa2\x02\x03EXX\xaa\x02\x06Ermite\xca\x02\x06Ermite\xe2\x02\x12Ermite\\GPBMetadata\xea\x02\x06Ermiteb\x06proto3"
626+
627+var (
628+ file_ermite_event_proto_rawDescOnce sync.Once
629+ file_ermite_event_proto_rawDescData []byte
630+)
631+
632+func file_ermite_event_proto_rawDescGZIP() []byte {
633+ file_ermite_event_proto_rawDescOnce.Do(func() {
634+ file_ermite_event_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ermite_event_proto_rawDesc), len(file_ermite_event_proto_rawDesc)))
635+ })
636+ return file_ermite_event_proto_rawDescData
637+}
638+
639+var file_ermite_event_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
640+var file_ermite_event_proto_goTypes = []any{
641+ (*WatchRequest)(nil), // 0: ermite.WatchRequest
642+ (*WatchResponse)(nil), // 1: ermite.WatchResponse
643+ (*Event)(nil), // 2: ermite.Event
644+ (*RepoCreated)(nil), // 3: ermite.RepoCreated
645+ (*RepoUpdated)(nil), // 4: ermite.RepoUpdated
646+ (*RepoDeleted)(nil), // 5: ermite.RepoDeleted
647+ (*Pushed)(nil), // 6: ermite.Pushed
648+ (*RefUpdate)(nil), // 7: ermite.RefUpdate
649+ (*Gap)(nil), // 8: ermite.Gap
650+ (*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp
651+ (*Repo)(nil), // 10: ermite.Repo
652+}
653+var file_ermite_event_proto_depIdxs = []int32{
654+ 2, // 0: ermite.WatchResponse.event:type_name -> ermite.Event
655+ 9, // 1: ermite.Event.time:type_name -> google.protobuf.Timestamp
656+ 3, // 2: ermite.Event.repo_created:type_name -> ermite.RepoCreated
657+ 4, // 3: ermite.Event.repo_updated:type_name -> ermite.RepoUpdated
658+ 5, // 4: ermite.Event.repo_deleted:type_name -> ermite.RepoDeleted
659+ 6, // 5: ermite.Event.pushed:type_name -> ermite.Pushed
660+ 8, // 6: ermite.Event.gap:type_name -> ermite.Gap
661+ 10, // 7: ermite.RepoCreated.repo:type_name -> ermite.Repo
662+ 10, // 8: ermite.RepoUpdated.repo:type_name -> ermite.Repo
663+ 7, // 9: ermite.Pushed.ref_updates:type_name -> ermite.RefUpdate
664+ 0, // 10: ermite.EventService.Watch:input_type -> ermite.WatchRequest
665+ 1, // 11: ermite.EventService.Watch:output_type -> ermite.WatchResponse
666+ 11, // [11:12] is the sub-list for method output_type
667+ 10, // [10:11] is the sub-list for method input_type
668+ 10, // [10:10] is the sub-list for extension type_name
669+ 10, // [10:10] is the sub-list for extension extendee
670+ 0, // [0:10] is the sub-list for field type_name
671+}
672+
673+func init() { file_ermite_event_proto_init() }
674+func file_ermite_event_proto_init() {
675+ if File_ermite_event_proto != nil {
676+ return
677+ }
678+ file_ermite_repo_proto_init()
679+ file_ermite_event_proto_msgTypes[2].OneofWrappers = []any{
680+ (*Event_RepoCreated)(nil),
681+ (*Event_RepoUpdated)(nil),
682+ (*Event_RepoDeleted)(nil),
683+ (*Event_Pushed)(nil),
684+ (*Event_Gap)(nil),
685+ }
686+ type x struct{}
687+ out := protoimpl.TypeBuilder{
688+ File: protoimpl.DescBuilder{
689+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
690+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_event_proto_rawDesc), len(file_ermite_event_proto_rawDesc)),
691+ NumEnums: 0,
692+ NumMessages: 9,
693+ NumExtensions: 0,
694+ NumServices: 1,
695+ },
696+ GoTypes: file_ermite_event_proto_goTypes,
697+ DependencyIndexes: file_ermite_event_proto_depIdxs,
698+ MessageInfos: file_ermite_event_proto_msgTypes,
699+ }.Build()
700+ File_ermite_event_proto = out.File
701+ file_ermite_event_proto_goTypes = nil
702+ file_ermite_event_proto_depIdxs = nil
703+}
@@ -0,0 +1,703 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-go. DO NOT EDIT.
4+// versions:
5+// protoc-gen-go v1.36.12
6+// protoc (unknown)
7+// source: ermite/event.proto
8+
9+package ermite
10+
11+import (
12+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
13+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
14+ timestamppb "google.golang.org/protobuf/types/known/timestamppb"
15+ reflect "reflect"
16+ sync "sync"
17+ unsafe "unsafe"
18+)
19+
20+const (
21+ // Verify that this generated code is sufficiently up-to-date.
22+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
23+ // Verify that runtime/protoimpl is sufficiently up-to-date.
24+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
25+)
26+
27+// WatchRequest selects the events to receive.
28+type WatchRequest struct {
29+ state protoimpl.MessageState `protogen:"open.v1"`
30+ // Topics are the subjects to follow: "home" for every repository visible to
31+ // the caller, or "repo:<name>" for one repository. Empty means "home".
32+ // Unknown topics are invalid_argument. A "repo:<name>" topic for a repository
33+ // that does not exist or is not visible is not_found.
34+ Topics []string `protobuf:"bytes,1,rep,name=topics,proto3" json:"topics,omitempty"`
35+ // ResumeAfter is the Event.id of the last event the client has seen. Empty
36+ // to receive only new events.
37+ ResumeAfter string `protobuf:"bytes,2,opt,name=resume_after,json=resumeAfter,proto3" json:"resume_after,omitempty"`
38+ unknownFields protoimpl.UnknownFields
39+ sizeCache protoimpl.SizeCache
40+}
41+
42+func (x *WatchRequest) Reset() {
43+ *x = WatchRequest{}
44+ mi := &file_ermite_event_proto_msgTypes[0]
45+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
46+ ms.StoreMessageInfo(mi)
47+}
48+
49+func (x *WatchRequest) String() string {
50+ return protoimpl.X.MessageStringOf(x)
51+}
52+
53+func (*WatchRequest) ProtoMessage() {}
54+
55+func (x *WatchRequest) ProtoReflect() protoreflect.Message {
56+ mi := &file_ermite_event_proto_msgTypes[0]
57+ if x != nil {
58+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
59+ if ms.LoadMessageInfo() == nil {
60+ ms.StoreMessageInfo(mi)
61+ }
62+ return ms
63+ }
64+ return mi.MessageOf(x)
65+}
66+
67+// Deprecated: Use WatchRequest.ProtoReflect.Descriptor instead.
68+func (*WatchRequest) Descriptor() ([]byte, []int) {
69+ return file_ermite_event_proto_rawDescGZIP(), []int{0}
70+}
71+
72+func (x *WatchRequest) GetTopics() []string {
73+ if x != nil {
74+ return x.Topics
75+ }
76+ return nil
77+}
78+
79+func (x *WatchRequest) GetResumeAfter() string {
80+ if x != nil {
81+ return x.ResumeAfter
82+ }
83+ return ""
84+}
85+
86+// WatchResponse carries one event of the stream.
87+type WatchResponse struct {
88+ state protoimpl.MessageState `protogen:"open.v1"`
89+ // Event is the event.
90+ Event *Event `protobuf:"bytes,1,opt,name=event,proto3" json:"event,omitempty"`
91+ unknownFields protoimpl.UnknownFields
92+ sizeCache protoimpl.SizeCache
93+}
94+
95+func (x *WatchResponse) Reset() {
96+ *x = WatchResponse{}
97+ mi := &file_ermite_event_proto_msgTypes[1]
98+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
99+ ms.StoreMessageInfo(mi)
100+}
101+
102+func (x *WatchResponse) String() string {
103+ return protoimpl.X.MessageStringOf(x)
104+}
105+
106+func (*WatchResponse) ProtoMessage() {}
107+
108+func (x *WatchResponse) ProtoReflect() protoreflect.Message {
109+ mi := &file_ermite_event_proto_msgTypes[1]
110+ if x != nil {
111+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
112+ if ms.LoadMessageInfo() == nil {
113+ ms.StoreMessageInfo(mi)
114+ }
115+ return ms
116+ }
117+ return mi.MessageOf(x)
118+}
119+
120+// Deprecated: Use WatchResponse.ProtoReflect.Descriptor instead.
121+func (*WatchResponse) Descriptor() ([]byte, []int) {
122+ return file_ermite_event_proto_rawDescGZIP(), []int{1}
123+}
124+
125+func (x *WatchResponse) GetEvent() *Event {
126+ if x != nil {
127+ return x.Event
128+ }
129+ return nil
130+}
131+
132+// Event is something that happened on the forge.
133+type Event struct {
134+ state protoimpl.MessageState `protogen:"open.v1"`
135+ // Id identifies the event and is passed as resume_after to resume a stream.
136+ // It is opaque and only comparable for equality; clients must not parse it.
137+ // Empty on a Gap event.
138+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
139+ // Time is when the event happened.
140+ Time *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=time,proto3" json:"time,omitempty"`
141+ // Kind is the type and payload of the event. New kinds may be added; clients
142+ // must ignore kinds they do not know.
143+ //
144+ // Types that are valid to be assigned to Kind:
145+ //
146+ // *Event_RepoCreated
147+ // *Event_RepoUpdated
148+ // *Event_RepoDeleted
149+ // *Event_Pushed
150+ // *Event_Gap
151+ Kind isEvent_Kind `protobuf_oneof:"kind"`
152+ unknownFields protoimpl.UnknownFields
153+ sizeCache protoimpl.SizeCache
154+}
155+
156+func (x *Event) Reset() {
157+ *x = Event{}
158+ mi := &file_ermite_event_proto_msgTypes[2]
159+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
160+ ms.StoreMessageInfo(mi)
161+}
162+
163+func (x *Event) String() string {
164+ return protoimpl.X.MessageStringOf(x)
165+}
166+
167+func (*Event) ProtoMessage() {}
168+
169+func (x *Event) ProtoReflect() protoreflect.Message {
170+ mi := &file_ermite_event_proto_msgTypes[2]
171+ if x != nil {
172+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
173+ if ms.LoadMessageInfo() == nil {
174+ ms.StoreMessageInfo(mi)
175+ }
176+ return ms
177+ }
178+ return mi.MessageOf(x)
179+}
180+
181+// Deprecated: Use Event.ProtoReflect.Descriptor instead.
182+func (*Event) Descriptor() ([]byte, []int) {
183+ return file_ermite_event_proto_rawDescGZIP(), []int{2}
184+}
185+
186+func (x *Event) GetId() string {
187+ if x != nil {
188+ return x.Id
189+ }
190+ return ""
191+}
192+
193+func (x *Event) GetTime() *timestamppb.Timestamp {
194+ if x != nil {
195+ return x.Time
196+ }
197+ return nil
198+}
199+
200+func (x *Event) GetKind() isEvent_Kind {
201+ if x != nil {
202+ return x.Kind
203+ }
204+ return nil
205+}
206+
207+func (x *Event) GetRepoCreated() *RepoCreated {
208+ if x != nil {
209+ if x, ok := x.Kind.(*Event_RepoCreated); ok {
210+ return x.RepoCreated
211+ }
212+ }
213+ return nil
214+}
215+
216+func (x *Event) GetRepoUpdated() *RepoUpdated {
217+ if x != nil {
218+ if x, ok := x.Kind.(*Event_RepoUpdated); ok {
219+ return x.RepoUpdated
220+ }
221+ }
222+ return nil
223+}
224+
225+func (x *Event) GetRepoDeleted() *RepoDeleted {
226+ if x != nil {
227+ if x, ok := x.Kind.(*Event_RepoDeleted); ok {
228+ return x.RepoDeleted
229+ }
230+ }
231+ return nil
232+}
233+
234+func (x *Event) GetPushed() *Pushed {
235+ if x != nil {
236+ if x, ok := x.Kind.(*Event_Pushed); ok {
237+ return x.Pushed
238+ }
239+ }
240+ return nil
241+}
242+
243+func (x *Event) GetGap() *Gap {
244+ if x != nil {
245+ if x, ok := x.Kind.(*Event_Gap); ok {
246+ return x.Gap
247+ }
248+ }
249+ return nil
250+}
251+
252+type isEvent_Kind interface {
253+ isEvent_Kind()
254+}
255+
256+type Event_RepoCreated struct {
257+ // RepoCreated: a repository was created.
258+ RepoCreated *RepoCreated `protobuf:"bytes,3,opt,name=repo_created,json=repoCreated,proto3,oneof"`
259+}
260+
261+type Event_RepoUpdated struct {
262+ // RepoUpdated: repository metadata changed.
263+ RepoUpdated *RepoUpdated `protobuf:"bytes,4,opt,name=repo_updated,json=repoUpdated,proto3,oneof"`
264+}
265+
266+type Event_RepoDeleted struct {
267+ // RepoDeleted: a repository was deleted.
268+ RepoDeleted *RepoDeleted `protobuf:"bytes,5,opt,name=repo_deleted,json=repoDeleted,proto3,oneof"`
269+}
270+
271+type Event_Pushed struct {
272+ // Pushed: refs of a repository were updated by a push.
273+ Pushed *Pushed `protobuf:"bytes,6,opt,name=pushed,proto3,oneof"`
274+}
275+
276+type Event_Gap struct {
277+ // Gap: events were lost, reload state.
278+ Gap *Gap `protobuf:"bytes,7,opt,name=gap,proto3,oneof"`
279+}
280+
281+func (*Event_RepoCreated) isEvent_Kind() {}
282+
283+func (*Event_RepoUpdated) isEvent_Kind() {}
284+
285+func (*Event_RepoDeleted) isEvent_Kind() {}
286+
287+func (*Event_Pushed) isEvent_Kind() {}
288+
289+func (*Event_Gap) isEvent_Kind() {}
290+
291+// RepoCreated reports a new repository.
292+type RepoCreated struct {
293+ state protoimpl.MessageState `protogen:"open.v1"`
294+ // Repo is the new repository.
295+ Repo *Repo `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
296+ unknownFields protoimpl.UnknownFields
297+ sizeCache protoimpl.SizeCache
298+}
299+
300+func (x *RepoCreated) Reset() {
301+ *x = RepoCreated{}
302+ mi := &file_ermite_event_proto_msgTypes[3]
303+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
304+ ms.StoreMessageInfo(mi)
305+}
306+
307+func (x *RepoCreated) String() string {
308+ return protoimpl.X.MessageStringOf(x)
309+}
310+
311+func (*RepoCreated) ProtoMessage() {}
312+
313+func (x *RepoCreated) ProtoReflect() protoreflect.Message {
314+ mi := &file_ermite_event_proto_msgTypes[3]
315+ if x != nil {
316+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
317+ if ms.LoadMessageInfo() == nil {
318+ ms.StoreMessageInfo(mi)
319+ }
320+ return ms
321+ }
322+ return mi.MessageOf(x)
323+}
324+
325+// Deprecated: Use RepoCreated.ProtoReflect.Descriptor instead.
326+func (*RepoCreated) Descriptor() ([]byte, []int) {
327+ return file_ermite_event_proto_rawDescGZIP(), []int{3}
328+}
329+
330+func (x *RepoCreated) GetRepo() *Repo {
331+ if x != nil {
332+ return x.Repo
333+ }
334+ return nil
335+}
336+
337+// RepoUpdated reports a metadata change.
338+type RepoUpdated struct {
339+ state protoimpl.MessageState `protogen:"open.v1"`
340+ // Repo is the repository after the change.
341+ Repo *Repo `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
342+ unknownFields protoimpl.UnknownFields
343+ sizeCache protoimpl.SizeCache
344+}
345+
346+func (x *RepoUpdated) Reset() {
347+ *x = RepoUpdated{}
348+ mi := &file_ermite_event_proto_msgTypes[4]
349+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
350+ ms.StoreMessageInfo(mi)
351+}
352+
353+func (x *RepoUpdated) String() string {
354+ return protoimpl.X.MessageStringOf(x)
355+}
356+
357+func (*RepoUpdated) ProtoMessage() {}
358+
359+func (x *RepoUpdated) ProtoReflect() protoreflect.Message {
360+ mi := &file_ermite_event_proto_msgTypes[4]
361+ if x != nil {
362+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
363+ if ms.LoadMessageInfo() == nil {
364+ ms.StoreMessageInfo(mi)
365+ }
366+ return ms
367+ }
368+ return mi.MessageOf(x)
369+}
370+
371+// Deprecated: Use RepoUpdated.ProtoReflect.Descriptor instead.
372+func (*RepoUpdated) Descriptor() ([]byte, []int) {
373+ return file_ermite_event_proto_rawDescGZIP(), []int{4}
374+}
375+
376+func (x *RepoUpdated) GetRepo() *Repo {
377+ if x != nil {
378+ return x.Repo
379+ }
380+ return nil
381+}
382+
383+// RepoDeleted reports a deleted repository.
384+type RepoDeleted struct {
385+ state protoimpl.MessageState `protogen:"open.v1"`
386+ // Repo is the name of the deleted repository.
387+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
388+ unknownFields protoimpl.UnknownFields
389+ sizeCache protoimpl.SizeCache
390+}
391+
392+func (x *RepoDeleted) Reset() {
393+ *x = RepoDeleted{}
394+ mi := &file_ermite_event_proto_msgTypes[5]
395+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
396+ ms.StoreMessageInfo(mi)
397+}
398+
399+func (x *RepoDeleted) String() string {
400+ return protoimpl.X.MessageStringOf(x)
401+}
402+
403+func (*RepoDeleted) ProtoMessage() {}
404+
405+func (x *RepoDeleted) ProtoReflect() protoreflect.Message {
406+ mi := &file_ermite_event_proto_msgTypes[5]
407+ if x != nil {
408+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
409+ if ms.LoadMessageInfo() == nil {
410+ ms.StoreMessageInfo(mi)
411+ }
412+ return ms
413+ }
414+ return mi.MessageOf(x)
415+}
416+
417+// Deprecated: Use RepoDeleted.ProtoReflect.Descriptor instead.
418+func (*RepoDeleted) Descriptor() ([]byte, []int) {
419+ return file_ermite_event_proto_rawDescGZIP(), []int{5}
420+}
421+
422+func (x *RepoDeleted) GetRepo() string {
423+ if x != nil {
424+ return x.Repo
425+ }
426+ return ""
427+}
428+
429+// Pushed reports the ref updates of one push.
430+type Pushed struct {
431+ state protoimpl.MessageState `protogen:"open.v1"`
432+ // Repo is the name of the repository.
433+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
434+ // RefUpdates are the refs changed by the push.
435+ RefUpdates []*RefUpdate `protobuf:"bytes,2,rep,name=ref_updates,json=refUpdates,proto3" json:"ref_updates,omitempty"`
436+ unknownFields protoimpl.UnknownFields
437+ sizeCache protoimpl.SizeCache
438+}
439+
440+func (x *Pushed) Reset() {
441+ *x = Pushed{}
442+ mi := &file_ermite_event_proto_msgTypes[6]
443+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
444+ ms.StoreMessageInfo(mi)
445+}
446+
447+func (x *Pushed) String() string {
448+ return protoimpl.X.MessageStringOf(x)
449+}
450+
451+func (*Pushed) ProtoMessage() {}
452+
453+func (x *Pushed) ProtoReflect() protoreflect.Message {
454+ mi := &file_ermite_event_proto_msgTypes[6]
455+ if x != nil {
456+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
457+ if ms.LoadMessageInfo() == nil {
458+ ms.StoreMessageInfo(mi)
459+ }
460+ return ms
461+ }
462+ return mi.MessageOf(x)
463+}
464+
465+// Deprecated: Use Pushed.ProtoReflect.Descriptor instead.
466+func (*Pushed) Descriptor() ([]byte, []int) {
467+ return file_ermite_event_proto_rawDescGZIP(), []int{6}
468+}
469+
470+func (x *Pushed) GetRepo() string {
471+ if x != nil {
472+ return x.Repo
473+ }
474+ return ""
475+}
476+
477+func (x *Pushed) GetRefUpdates() []*RefUpdate {
478+ if x != nil {
479+ return x.RefUpdates
480+ }
481+ return nil
482+}
483+
484+// RefUpdate is the change of one ref.
485+type RefUpdate struct {
486+ state protoimpl.MessageState `protogen:"open.v1"`
487+ // Ref is the full ref name, for example "refs/heads/main".
488+ Ref string `protobuf:"bytes,1,opt,name=ref,proto3" json:"ref,omitempty"`
489+ // OldId is the object id before the update. Empty when the ref was created.
490+ OldId string `protobuf:"bytes,2,opt,name=old_id,json=oldId,proto3" json:"old_id,omitempty"`
491+ // NewId is the object id after the update. Empty when the ref was deleted.
492+ NewId string `protobuf:"bytes,3,opt,name=new_id,json=newId,proto3" json:"new_id,omitempty"`
493+ unknownFields protoimpl.UnknownFields
494+ sizeCache protoimpl.SizeCache
495+}
496+
497+func (x *RefUpdate) Reset() {
498+ *x = RefUpdate{}
499+ mi := &file_ermite_event_proto_msgTypes[7]
500+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
501+ ms.StoreMessageInfo(mi)
502+}
503+
504+func (x *RefUpdate) String() string {
505+ return protoimpl.X.MessageStringOf(x)
506+}
507+
508+func (*RefUpdate) ProtoMessage() {}
509+
510+func (x *RefUpdate) ProtoReflect() protoreflect.Message {
511+ mi := &file_ermite_event_proto_msgTypes[7]
512+ if x != nil {
513+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
514+ if ms.LoadMessageInfo() == nil {
515+ ms.StoreMessageInfo(mi)
516+ }
517+ return ms
518+ }
519+ return mi.MessageOf(x)
520+}
521+
522+// Deprecated: Use RefUpdate.ProtoReflect.Descriptor instead.
523+func (*RefUpdate) Descriptor() ([]byte, []int) {
524+ return file_ermite_event_proto_rawDescGZIP(), []int{7}
525+}
526+
527+func (x *RefUpdate) GetRef() string {
528+ if x != nil {
529+ return x.Ref
530+ }
531+ return ""
532+}
533+
534+func (x *RefUpdate) GetOldId() string {
535+ if x != nil {
536+ return x.OldId
537+ }
538+ return ""
539+}
540+
541+func (x *RefUpdate) GetNewId() string {
542+ if x != nil {
543+ return x.NewId
544+ }
545+ return ""
546+}
547+
548+// Gap tells the client that events were lost: the requested resume point is
549+// older than the server's buffer. It has no fields. The client should reload
550+// the state it displays (repositories, refs) and carry on with the next events.
551+type Gap struct {
552+ state protoimpl.MessageState `protogen:"open.v1"`
553+ unknownFields protoimpl.UnknownFields
554+ sizeCache protoimpl.SizeCache
555+}
556+
557+func (x *Gap) Reset() {
558+ *x = Gap{}
559+ mi := &file_ermite_event_proto_msgTypes[8]
560+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
561+ ms.StoreMessageInfo(mi)
562+}
563+
564+func (x *Gap) String() string {
565+ return protoimpl.X.MessageStringOf(x)
566+}
567+
568+func (*Gap) ProtoMessage() {}
569+
570+func (x *Gap) ProtoReflect() protoreflect.Message {
571+ mi := &file_ermite_event_proto_msgTypes[8]
572+ if x != nil {
573+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
574+ if ms.LoadMessageInfo() == nil {
575+ ms.StoreMessageInfo(mi)
576+ }
577+ return ms
578+ }
579+ return mi.MessageOf(x)
580+}
581+
582+// Deprecated: Use Gap.ProtoReflect.Descriptor instead.
583+func (*Gap) Descriptor() ([]byte, []int) {
584+ return file_ermite_event_proto_rawDescGZIP(), []int{8}
585+}
586+
587+var File_ermite_event_proto protoreflect.FileDescriptor
588+
589+const file_ermite_event_proto_rawDesc = "" +
590+ "\n" +
591+ "\x12ermite/event.proto\x12\x06ermite\x1a\x11ermite/repo.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"I\n" +
592+ "\fWatchRequest\x12\x16\n" +
593+ "\x06topics\x18\x01 \x03(\tR\x06topics\x12!\n" +
594+ "\fresume_after\x18\x02 \x01(\tR\vresumeAfter\"4\n" +
595+ "\rWatchResponse\x12#\n" +
596+ "\x05event\x18\x01 \x01(\v2\r.ermite.EventR\x05event\"\xc8\x02\n" +
597+ "\x05Event\x12\x0e\n" +
598+ "\x02id\x18\x01 \x01(\tR\x02id\x12.\n" +
599+ "\x04time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x04time\x128\n" +
600+ "\frepo_created\x18\x03 \x01(\v2\x13.ermite.RepoCreatedH\x00R\vrepoCreated\x128\n" +
601+ "\frepo_updated\x18\x04 \x01(\v2\x13.ermite.RepoUpdatedH\x00R\vrepoUpdated\x128\n" +
602+ "\frepo_deleted\x18\x05 \x01(\v2\x13.ermite.RepoDeletedH\x00R\vrepoDeleted\x12(\n" +
603+ "\x06pushed\x18\x06 \x01(\v2\x0e.ermite.PushedH\x00R\x06pushed\x12\x1f\n" +
604+ "\x03gap\x18\a \x01(\v2\v.ermite.GapH\x00R\x03gapB\x06\n" +
605+ "\x04kind\"/\n" +
606+ "\vRepoCreated\x12 \n" +
607+ "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"/\n" +
608+ "\vRepoUpdated\x12 \n" +
609+ "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"!\n" +
610+ "\vRepoDeleted\x12\x12\n" +
611+ "\x04repo\x18\x01 \x01(\tR\x04repo\"P\n" +
612+ "\x06Pushed\x12\x12\n" +
613+ "\x04repo\x18\x01 \x01(\tR\x04repo\x122\n" +
614+ "\vref_updates\x18\x02 \x03(\v2\x11.ermite.RefUpdateR\n" +
615+ "refUpdates\"K\n" +
616+ "\tRefUpdate\x12\x10\n" +
617+ "\x03ref\x18\x01 \x01(\tR\x03ref\x12\x15\n" +
618+ "\x06old_id\x18\x02 \x01(\tR\x05oldId\x12\x15\n" +
619+ "\x06new_id\x18\x03 \x01(\tR\x05newId\"\x05\n" +
620+ "\x03Gap2F\n" +
621+ "\fEventService\x126\n" +
622+ "\x05Watch\x12\x14.ermite.WatchRequest\x1a\x15.ermite.WatchResponse0\x01Bq\n" +
623+ "\n" +
624+ "com.ermiteB\n" +
625+ "EventProtoP\x01Z\x1fbnema.dev/ermite-api/gen/ermite\xa2\x02\x03EXX\xaa\x02\x06Ermite\xca\x02\x06Ermite\xe2\x02\x12Ermite\\GPBMetadata\xea\x02\x06Ermiteb\x06proto3"
626+
627+var (
628+ file_ermite_event_proto_rawDescOnce sync.Once
629+ file_ermite_event_proto_rawDescData []byte
630+)
631+
632+func file_ermite_event_proto_rawDescGZIP() []byte {
633+ file_ermite_event_proto_rawDescOnce.Do(func() {
634+ file_ermite_event_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ermite_event_proto_rawDesc), len(file_ermite_event_proto_rawDesc)))
635+ })
636+ return file_ermite_event_proto_rawDescData
637+}
638+
639+var file_ermite_event_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
640+var file_ermite_event_proto_goTypes = []any{
641+ (*WatchRequest)(nil), // 0: ermite.WatchRequest
642+ (*WatchResponse)(nil), // 1: ermite.WatchResponse
643+ (*Event)(nil), // 2: ermite.Event
644+ (*RepoCreated)(nil), // 3: ermite.RepoCreated
645+ (*RepoUpdated)(nil), // 4: ermite.RepoUpdated
646+ (*RepoDeleted)(nil), // 5: ermite.RepoDeleted
647+ (*Pushed)(nil), // 6: ermite.Pushed
648+ (*RefUpdate)(nil), // 7: ermite.RefUpdate
649+ (*Gap)(nil), // 8: ermite.Gap
650+ (*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp
651+ (*Repo)(nil), // 10: ermite.Repo
652+}
653+var file_ermite_event_proto_depIdxs = []int32{
654+ 2, // 0: ermite.WatchResponse.event:type_name -> ermite.Event
655+ 9, // 1: ermite.Event.time:type_name -> google.protobuf.Timestamp
656+ 3, // 2: ermite.Event.repo_created:type_name -> ermite.RepoCreated
657+ 4, // 3: ermite.Event.repo_updated:type_name -> ermite.RepoUpdated
658+ 5, // 4: ermite.Event.repo_deleted:type_name -> ermite.RepoDeleted
659+ 6, // 5: ermite.Event.pushed:type_name -> ermite.Pushed
660+ 8, // 6: ermite.Event.gap:type_name -> ermite.Gap
661+ 10, // 7: ermite.RepoCreated.repo:type_name -> ermite.Repo
662+ 10, // 8: ermite.RepoUpdated.repo:type_name -> ermite.Repo
663+ 7, // 9: ermite.Pushed.ref_updates:type_name -> ermite.RefUpdate
664+ 0, // 10: ermite.EventService.Watch:input_type -> ermite.WatchRequest
665+ 1, // 11: ermite.EventService.Watch:output_type -> ermite.WatchResponse
666+ 11, // [11:12] is the sub-list for method output_type
667+ 10, // [10:11] is the sub-list for method input_type
668+ 10, // [10:10] is the sub-list for extension type_name
669+ 10, // [10:10] is the sub-list for extension extendee
670+ 0, // [0:10] is the sub-list for field type_name
671+}
672+
673+func init() { file_ermite_event_proto_init() }
674+func file_ermite_event_proto_init() {
675+ if File_ermite_event_proto != nil {
676+ return
677+ }
678+ file_ermite_repo_proto_init()
679+ file_ermite_event_proto_msgTypes[2].OneofWrappers = []any{
680+ (*Event_RepoCreated)(nil),
681+ (*Event_RepoUpdated)(nil),
682+ (*Event_RepoDeleted)(nil),
683+ (*Event_Pushed)(nil),
684+ (*Event_Gap)(nil),
685+ }
686+ type x struct{}
687+ out := protoimpl.TypeBuilder{
688+ File: protoimpl.DescBuilder{
689+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
690+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_event_proto_rawDesc), len(file_ermite_event_proto_rawDesc)),
691+ NumEnums: 0,
692+ NumMessages: 9,
693+ NumExtensions: 0,
694+ NumServices: 1,
695+ },
696+ GoTypes: file_ermite_event_proto_goTypes,
697+ DependencyIndexes: file_ermite_event_proto_depIdxs,
698+ MessageInfos: file_ermite_event_proto_msgTypes,
699+ }.Build()
700+ File_ermite_event_proto = out.File
701+ file_ermite_event_proto_goTypes = nil
702+ file_ermite_event_proto_depIdxs = nil
703+}
A gen/ermite/git.pb.go +2046 -0 generated file: collapsed
A gen/ermite/repo.pb.go +857 -0 generated file: collapsed
A gen/ermite/server.pb.go +188 -0 generated file: collapsed
A gen/ermite/token.pb.go +673 -0 generated file: collapsed
A gen/ermite/types.pb.go +258 -0 generated file: collapsed
A gen_test.go +92 -0
@@ -0,0 +1,92 @@
1+// SPDX-License-Identifier: MIT
2+
3+package api_test
4+
5+import (
6+ "testing"
7+
8+ "google.golang.org/protobuf/proto"
9+ "google.golang.org/protobuf/types/known/fieldmaskpb"
10+
11+ "bnema.dev/ermite-api/gen/ermite"
12+ "bnema.dev/ermite-api/gen/ermite/ermiteconnect"
13+)
14+
15+// TestProcedures pins the procedure paths: they are the wire contract.
16+func TestProcedures(t *testing.T) {
17+ tests := []struct{ got, want string }{
18+ {ermiteconnect.RepoServiceListReposProcedure, "/ermite.RepoService/ListRepos"},
19+ {ermiteconnect.RepoServiceGetRepoProcedure, "/ermite.RepoService/GetRepo"},
20+ {ermiteconnect.RepoServiceCreateRepoProcedure, "/ermite.RepoService/CreateRepo"},
21+ {ermiteconnect.RepoServiceUpdateRepoProcedure, "/ermite.RepoService/UpdateRepo"},
22+ {ermiteconnect.RepoServiceDeleteRepoProcedure, "/ermite.RepoService/DeleteRepo"},
23+ {ermiteconnect.GitServiceListRefsProcedure, "/ermite.GitService/ListRefs"},
24+ {ermiteconnect.GitServiceGetTreeProcedure, "/ermite.GitService/GetTree"},
25+ {ermiteconnect.GitServiceGetBlobProcedure, "/ermite.GitService/GetBlob"},
26+ {ermiteconnect.GitServiceListCommitsProcedure, "/ermite.GitService/ListCommits"},
27+ {ermiteconnect.GitServiceGetCommitProcedure, "/ermite.GitService/GetCommit"},
28+ {ermiteconnect.GitServiceResolveRevisionProcedure, "/ermite.GitService/ResolveRevision"},
29+ {ermiteconnect.TokenServiceCreateTokenProcedure, "/ermite.TokenService/CreateToken"},
30+ {ermiteconnect.TokenServiceListTokensProcedure, "/ermite.TokenService/ListTokens"},
31+ {ermiteconnect.TokenServiceRevokeTokenProcedure, "/ermite.TokenService/RevokeToken"},
32+ {ermiteconnect.EventServiceWatchProcedure, "/ermite.EventService/Watch"},
33+ {ermiteconnect.ServerServiceGetVersionProcedure, "/ermite.ServerService/GetVersion"},
34+ }
35+ for _, tt := range tests {
36+ if tt.got != tt.want {
37+ t.Errorf("procedure = %q, want %q", tt.got, tt.want)
38+ }
39+ }
40+}
41+
42+// TestEnumNumbers pins enum numbers: they are part of the wire contract.
43+func TestEnumNumbers(t *testing.T) {
44+ if got := ermite.Scope_SCOPE_TOKEN_ADMIN; got != 9 {
45+ t.Errorf("SCOPE_TOKEN_ADMIN = %d, want 9", got)
46+ }
47+ if got := ermite.FileStatus_FILE_STATUS_TYPE_CHANGED; got != 6 {
48+ t.Errorf("FILE_STATUS_TYPE_CHANGED = %d, want 6", got)
49+ }
50+ if got := ermite.Visibility_VISIBILITY_PRIVATE; got != 2 {
51+ t.Errorf("VISIBILITY_PRIVATE = %d, want 2", got)
52+ }
53+}
54+
55+// TestEventRoundTrip checks that an event with a oneof kind survives the wire.
56+func TestEventRoundTrip(t *testing.T) {
57+ in := &ermite.WatchResponse{Event: &ermite.Event{
58+ Id: "42",
59+ Kind: &ermite.Event_Pushed{Pushed: &ermite.Pushed{
60+ Repo: "ermite",
61+ RefUpdates: []*ermite.RefUpdate{
62+ {Ref: "refs/heads/main", OldId: "a", NewId: "b"},
63+ },
64+ }},
65+ }}
66+ b, err := proto.Marshal(in)
67+ if err != nil {
68+ t.Fatal(err)
69+ }
70+ var out ermite.WatchResponse
71+ if err := proto.Unmarshal(b, &out); err != nil {
72+ t.Fatal(err)
73+ }
74+ if !proto.Equal(in, &out) {
75+ t.Errorf("round trip mismatch: %v != %v", &out, in)
76+ }
77+ if out.GetEvent().GetPushed().GetRefUpdates()[0].GetNewId() != "b" {
78+ t.Errorf("unexpected ref update: %v", &out)
79+ }
80+}
81+
82+// TestUpdateRepoMask checks that the request carries a field mask.
83+func TestUpdateRepoMask(t *testing.T) {
84+ req := &ermite.UpdateRepoRequest{
85+ Repo: "ermite",
86+ Visibility: ermite.Visibility_VISIBILITY_PUBLIC,
87+ UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"visibility"}},
88+ }
89+ if got := req.GetUpdateMask().GetPaths(); len(got) != 1 || got[0] != "visibility" {
90+ t.Errorf("paths = %v", got)
91+ }
92+}
@@ -0,0 +1,92 @@
1+// SPDX-License-Identifier: MIT
2+
3+package api_test
4+
5+import (
6+ "testing"
7+
8+ "google.golang.org/protobuf/proto"
9+ "google.golang.org/protobuf/types/known/fieldmaskpb"
10+
11+ "bnema.dev/ermite-api/gen/ermite"
12+ "bnema.dev/ermite-api/gen/ermite/ermiteconnect"
13+)
14+
15+// TestProcedures pins the procedure paths: they are the wire contract.
16+func TestProcedures(t *testing.T) {
17+ tests := []struct{ got, want string }{
18+ {ermiteconnect.RepoServiceListReposProcedure, "/ermite.RepoService/ListRepos"},
19+ {ermiteconnect.RepoServiceGetRepoProcedure, "/ermite.RepoService/GetRepo"},
20+ {ermiteconnect.RepoServiceCreateRepoProcedure, "/ermite.RepoService/CreateRepo"},
21+ {ermiteconnect.RepoServiceUpdateRepoProcedure, "/ermite.RepoService/UpdateRepo"},
22+ {ermiteconnect.RepoServiceDeleteRepoProcedure, "/ermite.RepoService/DeleteRepo"},
23+ {ermiteconnect.GitServiceListRefsProcedure, "/ermite.GitService/ListRefs"},
24+ {ermiteconnect.GitServiceGetTreeProcedure, "/ermite.GitService/GetTree"},
25+ {ermiteconnect.GitServiceGetBlobProcedure, "/ermite.GitService/GetBlob"},
26+ {ermiteconnect.GitServiceListCommitsProcedure, "/ermite.GitService/ListCommits"},
27+ {ermiteconnect.GitServiceGetCommitProcedure, "/ermite.GitService/GetCommit"},
28+ {ermiteconnect.GitServiceResolveRevisionProcedure, "/ermite.GitService/ResolveRevision"},
29+ {ermiteconnect.TokenServiceCreateTokenProcedure, "/ermite.TokenService/CreateToken"},
30+ {ermiteconnect.TokenServiceListTokensProcedure, "/ermite.TokenService/ListTokens"},
31+ {ermiteconnect.TokenServiceRevokeTokenProcedure, "/ermite.TokenService/RevokeToken"},
32+ {ermiteconnect.EventServiceWatchProcedure, "/ermite.EventService/Watch"},
33+ {ermiteconnect.ServerServiceGetVersionProcedure, "/ermite.ServerService/GetVersion"},
34+ }
35+ for _, tt := range tests {
36+ if tt.got != tt.want {
37+ t.Errorf("procedure = %q, want %q", tt.got, tt.want)
38+ }
39+ }
40+}
41+
42+// TestEnumNumbers pins enum numbers: they are part of the wire contract.
43+func TestEnumNumbers(t *testing.T) {
44+ if got := ermite.Scope_SCOPE_TOKEN_ADMIN; got != 9 {
45+ t.Errorf("SCOPE_TOKEN_ADMIN = %d, want 9", got)
46+ }
47+ if got := ermite.FileStatus_FILE_STATUS_TYPE_CHANGED; got != 6 {
48+ t.Errorf("FILE_STATUS_TYPE_CHANGED = %d, want 6", got)
49+ }
50+ if got := ermite.Visibility_VISIBILITY_PRIVATE; got != 2 {
51+ t.Errorf("VISIBILITY_PRIVATE = %d, want 2", got)
52+ }
53+}
54+
55+// TestEventRoundTrip checks that an event with a oneof kind survives the wire.
56+func TestEventRoundTrip(t *testing.T) {
57+ in := &ermite.WatchResponse{Event: &ermite.Event{
58+ Id: "42",
59+ Kind: &ermite.Event_Pushed{Pushed: &ermite.Pushed{
60+ Repo: "ermite",
61+ RefUpdates: []*ermite.RefUpdate{
62+ {Ref: "refs/heads/main", OldId: "a", NewId: "b"},
63+ },
64+ }},
65+ }}
66+ b, err := proto.Marshal(in)
67+ if err != nil {
68+ t.Fatal(err)
69+ }
70+ var out ermite.WatchResponse
71+ if err := proto.Unmarshal(b, &out); err != nil {
72+ t.Fatal(err)
73+ }
74+ if !proto.Equal(in, &out) {
75+ t.Errorf("round trip mismatch: %v != %v", &out, in)
76+ }
77+ if out.GetEvent().GetPushed().GetRefUpdates()[0].GetNewId() != "b" {
78+ t.Errorf("unexpected ref update: %v", &out)
79+ }
80+}
81+
82+// TestUpdateRepoMask checks that the request carries a field mask.
83+func TestUpdateRepoMask(t *testing.T) {
84+ req := &ermite.UpdateRepoRequest{
85+ Repo: "ermite",
86+ Visibility: ermite.Visibility_VISIBILITY_PUBLIC,
87+ UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"visibility"}},
88+ }
89+ if got := req.GetUpdateMask().GetPaths(); len(got) != 1 || got[0] != "visibility" {
90+ t.Errorf("paths = %v", got)
91+ }
92+}
M go.mod +2 -2
@@ -8,6 +8,6 @@ tool (
88 )
99
1010 require (
11- connectrpc.com/connect v1.21.0 // indirect
12- google.golang.org/protobuf v1.36.12 // indirect
11+ connectrpc.com/connect v1.21.0
12+ google.golang.org/protobuf v1.36.12
1313 )
@@ -8,6 +8,6 @@ tool (
8 )8 )
9 9
10 require (10 require (
11- connectrpc.com/connect v1.21.0 // indirect11+ connectrpc.com/connect v1.21.0
12- google.golang.org/protobuf v1.36.12 // indirect12+ google.golang.org/protobuf v1.36.12
13 )13 )
A proto/ermite/event.proto +105 -0
@@ -0,0 +1,105 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "ermite/repo.proto";
8+import "google/protobuf/timestamp.proto";
9+
10+// EventService streams what happens on the forge.
11+service EventService {
12+ // Watch streams events until the client cancels the call or the server shuts
13+ // 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+ //
16+ // 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.
18+ // When resume_after is no longer buffered (or unknown), the stream begins
19+ // with a Gap event, then continues with the oldest buffered event: the client
20+ // must reload its state. Without resume_after, only new events are sent.
21+ rpc Watch(WatchRequest) returns (stream WatchResponse);
22+}
23+
24+// WatchRequest selects the events to receive.
25+message WatchRequest {
26+ // Topics are the subjects to follow: "home" for every repository visible to
27+ // the caller, or "repo:<name>" for one repository. Empty means "home".
28+ // Unknown topics are invalid_argument. A "repo:<name>" topic for a repository
29+ // that does not exist or is not visible is not_found.
30+ repeated string topics = 1;
31+ // ResumeAfter is the Event.id of the last event the client has seen. Empty
32+ // to receive only new events.
33+ string resume_after = 2;
34+}
35+
36+// WatchResponse carries one event of the stream.
37+message WatchResponse {
38+ // Event is the event.
39+ Event event = 1;
40+}
41+
42+// Event is something that happened on the forge.
43+message Event {
44+ // Id identifies the event and is passed as resume_after to resume a stream.
45+ // It is opaque and only comparable for equality; clients must not parse it.
46+ // Empty on a Gap event.
47+ string id = 1;
48+ // Time is when the event happened.
49+ google.protobuf.Timestamp time = 2;
50+ // Kind is the type and payload of the event. New kinds may be added; clients
51+ // must ignore kinds they do not know.
52+ oneof kind {
53+ // RepoCreated: a repository was created.
54+ RepoCreated repo_created = 3;
55+ // RepoUpdated: repository metadata changed.
56+ RepoUpdated repo_updated = 4;
57+ // RepoDeleted: a repository was deleted.
58+ RepoDeleted repo_deleted = 5;
59+ // Pushed: refs of a repository were updated by a push.
60+ Pushed pushed = 6;
61+ // Gap: events were lost, reload state.
62+ Gap gap = 7;
63+ }
64+}
65+
66+// RepoCreated reports a new repository.
67+message RepoCreated {
68+ // Repo is the new repository.
69+ Repo repo = 1;
70+}
71+
72+// RepoUpdated reports a metadata change.
73+message RepoUpdated {
74+ // Repo is the repository after the change.
75+ Repo repo = 1;
76+}
77+
78+// RepoDeleted reports a deleted repository.
79+message RepoDeleted {
80+ // Repo is the name of the deleted repository.
81+ string repo = 1;
82+}
83+
84+// Pushed reports the ref updates of one push.
85+message Pushed {
86+ // Repo is the name of the repository.
87+ string repo = 1;
88+ // RefUpdates are the refs changed by the push.
89+ repeated RefUpdate ref_updates = 2;
90+}
91+
92+// RefUpdate is the change of one ref.
93+message RefUpdate {
94+ // Ref is the full ref name, for example "refs/heads/main".
95+ string ref = 1;
96+ // OldId is the object id before the update. Empty when the ref was created.
97+ string old_id = 2;
98+ // NewId is the object id after the update. Empty when the ref was deleted.
99+ string new_id = 3;
100+}
101+
102+// Gap tells the client that events were lost: the requested resume point is
103+// older than the server's buffer. It has no fields. The client should reload
104+// the state it displays (repositories, refs) and carry on with the next events.
105+message Gap {}
@@ -0,0 +1,105 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "ermite/repo.proto";
8+import "google/protobuf/timestamp.proto";
9+
10+// EventService streams what happens on the forge.
11+service EventService {
12+ // Watch streams events until the client cancels the call or the server shuts
13+ // 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+ //
16+ // 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.
18+ // When resume_after is no longer buffered (or unknown), the stream begins
19+ // with a Gap event, then continues with the oldest buffered event: the client
20+ // must reload its state. Without resume_after, only new events are sent.
21+ rpc Watch(WatchRequest) returns (stream WatchResponse);
22+}
23+
24+// WatchRequest selects the events to receive.
25+message WatchRequest {
26+ // Topics are the subjects to follow: "home" for every repository visible to
27+ // the caller, or "repo:<name>" for one repository. Empty means "home".
28+ // Unknown topics are invalid_argument. A "repo:<name>" topic for a repository
29+ // that does not exist or is not visible is not_found.
30+ repeated string topics = 1;
31+ // ResumeAfter is the Event.id of the last event the client has seen. Empty
32+ // to receive only new events.
33+ string resume_after = 2;
34+}
35+
36+// WatchResponse carries one event of the stream.
37+message WatchResponse {
38+ // Event is the event.
39+ Event event = 1;
40+}
41+
42+// Event is something that happened on the forge.
43+message Event {
44+ // Id identifies the event and is passed as resume_after to resume a stream.
45+ // It is opaque and only comparable for equality; clients must not parse it.
46+ // Empty on a Gap event.
47+ string id = 1;
48+ // Time is when the event happened.
49+ google.protobuf.Timestamp time = 2;
50+ // Kind is the type and payload of the event. New kinds may be added; clients
51+ // must ignore kinds they do not know.
52+ oneof kind {
53+ // RepoCreated: a repository was created.
54+ RepoCreated repo_created = 3;
55+ // RepoUpdated: repository metadata changed.
56+ RepoUpdated repo_updated = 4;
57+ // RepoDeleted: a repository was deleted.
58+ RepoDeleted repo_deleted = 5;
59+ // Pushed: refs of a repository were updated by a push.
60+ Pushed pushed = 6;
61+ // Gap: events were lost, reload state.
62+ Gap gap = 7;
63+ }
64+}
65+
66+// RepoCreated reports a new repository.
67+message RepoCreated {
68+ // Repo is the new repository.
69+ Repo repo = 1;
70+}
71+
72+// RepoUpdated reports a metadata change.
73+message RepoUpdated {
74+ // Repo is the repository after the change.
75+ Repo repo = 1;
76+}
77+
78+// RepoDeleted reports a deleted repository.
79+message RepoDeleted {
80+ // Repo is the name of the deleted repository.
81+ string repo = 1;
82+}
83+
84+// Pushed reports the ref updates of one push.
85+message Pushed {
86+ // Repo is the name of the repository.
87+ string repo = 1;
88+ // RefUpdates are the refs changed by the push.
89+ repeated RefUpdate ref_updates = 2;
90+}
91+
92+// RefUpdate is the change of one ref.
93+message RefUpdate {
94+ // Ref is the full ref name, for example "refs/heads/main".
95+ string ref = 1;
96+ // OldId is the object id before the update. Empty when the ref was created.
97+ string old_id = 2;
98+ // NewId is the object id after the update. Empty when the ref was deleted.
99+ string new_id = 3;
100+}
101+
102+// Gap tells the client that events were lost: the requested resume point is
103+// older than the server's buffer. It has no fields. The client should reload
104+// the state it displays (repositories, refs) and carry on with the next events.
105+message Gap {}
A proto/ermite/git.proto +388 -0
@@ -0,0 +1,388 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "ermite/types.proto";
8+
9+// RefKind is the kind of a Git ref.
10+enum RefKind {
11+ // Not set. In ListRefsRequest.kinds it is rejected; never returned.
12+ REF_KIND_UNSPECIFIED = 0;
13+ // A branch (refs/heads/*).
14+ REF_KIND_BRANCH = 1;
15+ // A tag (refs/tags/*), lightweight or annotated.
16+ REF_KIND_TAG = 2;
17+ // The symbolic ref HEAD, that is the default branch.
18+ REF_KIND_HEAD = 3;
19+}
20+
21+// EntryKind is the kind of an entry of a Git tree.
22+enum EntryKind {
23+ // Not set. Never returned.
24+ ENTRY_KIND_UNSPECIFIED = 0;
25+ // A regular, non-executable file (mode 100644).
26+ ENTRY_KIND_FILE = 1;
27+ // An executable file (mode 100755).
28+ ENTRY_KIND_EXECUTABLE = 2;
29+ // A directory (mode 040000).
30+ ENTRY_KIND_DIRECTORY = 3;
31+ // A symbolic link (mode 120000). The blob content is the link target.
32+ ENTRY_KIND_SYMLINK = 4;
33+ // A submodule commit (mode 160000). The id is a commit of another
34+ // repository, which this one does not contain.
35+ ENTRY_KIND_SUBMODULE = 5;
36+}
37+
38+// FileStatus tells what a commit did to a file.
39+enum FileStatus {
40+ // Not set. Never returned.
41+ FILE_STATUS_UNSPECIFIED = 0;
42+ // The file was added.
43+ FILE_STATUS_ADDED = 1;
44+ // The file was deleted.
45+ FILE_STATUS_DELETED = 2;
46+ // The content or the mode of the file changed.
47+ FILE_STATUS_MODIFIED = 3;
48+ // The file was moved, possibly with changes. Rename detection is done by Git.
49+ FILE_STATUS_RENAMED = 4;
50+ // The file was copied from another file, possibly with changes.
51+ FILE_STATUS_COPIED = 5;
52+ // The type of the entry changed, for example a file became a symbolic link.
53+ FILE_STATUS_TYPE_CHANGED = 6;
54+}
55+
56+// LineKind tells how a line of a hunk relates to the two sides of a diff.
57+enum LineKind {
58+ // Not set. Never returned.
59+ LINE_KIND_UNSPECIFIED = 0;
60+ // The line is present, unchanged, on both sides.
61+ LINE_KIND_CONTEXT = 1;
62+ // The line exists only on the new side.
63+ LINE_KIND_ADDITION = 2;
64+ // The line exists only on the old side.
65+ LINE_KIND_DELETION = 3;
66+}
67+
68+// GitService reads the Git data of repositories. It is read-only: pushes go
69+// through Git itself.
70+//
71+// Every RPC takes the repository name. A repository that does not exist or
72+// that the caller cannot read gives not_found. A revision is a branch name, a
73+// tag name or a commit id (full, or an unambiguous abbreviation of at least 7
74+// characters); empty means HEAD. A revision or path that does not exist gives
75+// not_found; so does an empty repository, which has no commit to resolve.
76+service GitService {
77+ // ListRefs lists the branches, tags and HEAD of a repository. Refs are
78+ // ordered by kind (HEAD, branches, tags) then by name.
79+ rpc ListRefs(ListRefsRequest) returns (ListRefsResponse);
80+ // GetTree lists the entries of a directory at a revision. Fails with
81+ // not_found if the path does not exist and invalid_argument if it is not a
82+ // directory.
83+ rpc GetTree(GetTreeRequest) returns (GetTreeResponse);
84+ // GetBlob streams the content of a file at a revision, in chunks of at most
85+ // 64 KiB. The first message carries the metadata in info, then the chunks
86+ // follow; the memory used by the server does not depend on the file size.
87+ // Fails with not_found if the path does not exist and invalid_argument if it
88+ // is not a file.
89+ rpc GetBlob(GetBlobRequest) returns (stream GetBlobResponse);
90+ // ListCommits lists the history reachable from a revision, newest first
91+ // (Git's default order), optionally limited to the commits that touched a
92+ // path.
93+ rpc ListCommits(ListCommitsRequest) returns (ListCommitsResponse);
94+ // GetCommit returns one commit with its statistics and its diff.
95+ rpc GetCommit(GetCommitRequest) returns (GetCommitResponse);
96+ // ResolveRevision turns a revision into the id of the commit it designates.
97+ rpc ResolveRevision(ResolveRevisionRequest) returns (ResolveRevisionResponse);
98+}
99+
100+// Ref is a branch, a tag or HEAD.
101+message Ref {
102+ // Name is the full ref name, for example "refs/heads/main" or
103+ // "refs/tags/v1.0.0". For HEAD it is "HEAD".
104+ string name = 1;
105+ // ShortName is the name without its "refs/heads/" or "refs/tags/" prefix. For
106+ // HEAD it is the short name of the default branch, or empty if HEAD points
107+ // to a branch that does not exist yet.
108+ string short_name = 2;
109+ // Kind is the kind of the ref.
110+ RefKind kind = 3;
111+ // Id is the id of the object the ref points to. For an annotated tag it is
112+ // the id of the tag object.
113+ string id = 4;
114+ // CommitId is the id of the commit the ref designates once annotated tags are
115+ // followed. It equals id for branches and lightweight tags. It is empty for
116+ // a tag that points to something other than a commit.
117+ string commit_id = 5;
118+}
119+
120+// ListRefsRequest selects the refs to list.
121+message ListRefsRequest {
122+ // Repo is the repository name.
123+ string repo = 1;
124+ // Kinds restricts the result to these kinds. Empty means all kinds.
125+ repeated RefKind kinds = 2;
126+ // PageSize is the maximum number of refs to return. 0 means the server
127+ // default; values above the server maximum are lowered to it.
128+ int32 page_size = 3;
129+ // PageToken is the next_page_token of a previous response. Empty for the
130+ // first page.
131+ string page_token = 4;
132+}
133+
134+// ListRefsResponse is one page of refs.
135+message ListRefsResponse {
136+ // Refs are the refs of this page.
137+ repeated Ref refs = 1;
138+ // NextPageToken is passed as page_token to get the next page. Empty when
139+ // there is no next page.
140+ string next_page_token = 2;
141+}
142+
143+// TreeEntry is one entry of a directory.
144+message TreeEntry {
145+ // Name is the name of the entry in its directory, without slash.
146+ string name = 1;
147+ // Path is the path of the entry from the repository root, with "/"
148+ // separators and no leading slash.
149+ string path = 2;
150+ // Kind is the kind of the entry.
151+ EntryKind kind = 3;
152+ // Mode is the Git file mode, for example 0o100644 (33188 in decimal).
153+ uint32 mode = 4;
154+ // Id is the object id of the blob, tree or submodule commit.
155+ string id = 5;
156+ // Size is the size in bytes of a file or symbolic link. It is 0 for
157+ // directories and submodules.
158+ int64 size = 6;
159+ // LastCommit is the latest commit that changed the entry. Set only when the
160+ // request asked for it with include_last_commit.
161+ Commit last_commit = 7;
162+}
163+
164+// GetTreeRequest selects a directory.
165+message GetTreeRequest {
166+ // Repo is the repository name.
167+ string repo = 1;
168+ // Revision is a branch, a tag or a commit id. Empty means HEAD.
169+ string revision = 2;
170+ // Path is the directory, from the repository root, with "/" separators.
171+ // Empty means the root.
172+ string path = 3;
173+ // IncludeLastCommit asks for the last commit of every entry in last_commit.
174+ // It costs one history walk, so it is off by default and the server may be
175+ // slow on large directories.
176+ bool include_last_commit = 4;
177+}
178+
179+// GetTreeResponse lists a directory.
180+message GetTreeResponse {
181+ // CommitId is the id of the commit the revision resolved to.
182+ string commit_id = 1;
183+ // TreeId is the object id of the listed directory.
184+ string tree_id = 2;
185+ // Entries are the entries of the directory: directories first, then the other
186+ // entries, each group ordered by name.
187+ repeated TreeEntry entries = 3;
188+}
189+
190+// BlobInfo describes a file.
191+message BlobInfo {
192+ // Id is the object id of the blob.
193+ string id = 1;
194+ // Path is the path of the file from the repository root.
195+ string path = 2;
196+ // Size is the size of the whole content in bytes.
197+ int64 size = 3;
198+ // Binary is true if the content is binary, with Git's rule: a NUL byte in the
199+ // first 8,000 bytes.
200+ bool binary = 4;
201+ // MediaType is the detected media type, for example "text/plain" or
202+ // "image/png", from the file name and the first bytes. It is
203+ // "application/octet-stream" when unknown.
204+ string media_type = 5;
205+ // Mode is the Git file mode, for example 0o100644 (33188 in decimal).
206+ uint32 mode = 6;
207+}
208+
209+// GetBlobRequest selects a file.
210+message GetBlobRequest {
211+ // Repo is the repository name.
212+ string repo = 1;
213+ // Revision is a branch, a tag or a commit id. Empty means HEAD.
214+ string revision = 2;
215+ // Path is the file, from the repository root. It is required.
216+ string path = 3;
217+}
218+
219+// GetBlobResponse is one message of the content stream.
220+message GetBlobResponse {
221+ // Info describes the file. Set in the first message of the stream only.
222+ BlobInfo info = 1;
223+ // Chunk is the next part of the content, at most 64 KiB, in order. The first
224+ // message may have an empty chunk. The chunks of a stream add up to
225+ // info.size bytes.
226+ bytes chunk = 2;
227+}
228+
229+// ListCommitsRequest selects a page of history.
230+message ListCommitsRequest {
231+ // Repo is the repository name.
232+ string repo = 1;
233+ // Revision is where the history starts: a branch, a tag or a commit id.
234+ // Empty means HEAD.
235+ string revision = 2;
236+ // Path restricts the result to the commits that changed this file or
237+ // directory. Empty means the whole history.
238+ string path = 3;
239+ // PageSize is the maximum number of commits to return. 0 means the server
240+ // default; values above 100 are lowered to 100.
241+ int32 page_size = 4;
242+ // PageToken is the next_page_token of a previous response, to continue the
243+ // same listing. Empty for the first page. Other fields must be unchanged.
244+ string page_token = 5;
245+}
246+
247+// ListCommitsResponse is one page of commits.
248+message ListCommitsResponse {
249+ // Commits are the commits of this page, newest first.
250+ repeated Commit commits = 1;
251+ // NextPageToken is passed as page_token to get the next page. Empty when
252+ // there is no next page.
253+ string next_page_token = 2;
254+}
255+
256+// GetCommitRequest selects a commit.
257+message GetCommitRequest {
258+ // Repo is the repository name.
259+ string repo = 1;
260+ // Revision is a branch, a tag or a commit id. Empty means HEAD.
261+ string revision = 2;
262+}
263+
264+// GetCommitResponse holds a commit, its statistics and its diff.
265+message GetCommitResponse {
266+ // Commit is the commit metadata.
267+ Commit commit = 1;
268+ // Stats are the totals over all the changed files, including files that the
269+ // diff omits or truncates.
270+ DiffStats stats = 2;
271+ // Diff is the structured diff against the first parent, or against the empty
272+ // tree for a root commit. For a merge commit this is the change relative to
273+ // the first parent.
274+ Diff diff = 3;
275+}
276+
277+// DiffStats are the totals of a diff.
278+message DiffStats {
279+ // FilesChanged is the number of changed files.
280+ int32 files_changed = 1;
281+ // Additions is the number of added lines. Binary files count for 0.
282+ int32 additions = 2;
283+ // Deletions is the number of deleted lines. Binary files count for 0.
284+ int32 deletions = 3;
285+}
286+
287+// Diff is the structured difference introduced by a commit.
288+//
289+// The server limits its size: at most 300 files, at most 2,000 lines in one
290+// file and at most 20,000 lines in total (hunk lines, context included). These
291+// numbers are server behaviour and may change; clients must rely on the
292+// truncated flags, not on the numbers.
293+message Diff {
294+ // Files are the changed files, ordered by path. Files past the file limit are
295+ // omitted.
296+ repeated FileDiff files = 1;
297+ // Truncated is true when the diff is incomplete: files were omitted, or at
298+ // least one listed file has truncated set. DiffStats still count everything.
299+ bool truncated = 2;
300+}
301+
302+// FileDiff is the change of one file.
303+message FileDiff {
304+ // OldPath is the path before the commit. Empty for an added file.
305+ string old_path = 1;
306+ // NewPath is the path after the commit. Empty for a deleted file.
307+ string new_path = 2;
308+ // Status tells what happened to the file.
309+ FileStatus status = 3;
310+ // OldMode is the Git file mode before the commit, or 0 for an added file.
311+ uint32 old_mode = 4;
312+ // NewMode is the Git file mode after the commit, or 0 for a deleted file.
313+ uint32 new_mode = 5;
314+ // OldId is the blob id before the commit. Empty for an added file.
315+ string old_id = 6;
316+ // NewId is the blob id after the commit. Empty for a deleted file.
317+ string new_id = 7;
318+ // Binary is true if Git treats the file as binary (numstat reports "-"). A
319+ // binary file has no hunks and additions and deletions are 0.
320+ bool binary = 8;
321+ // Additions is the number of added lines.
322+ int32 additions = 9;
323+ // Deletions is the number of deleted lines.
324+ int32 deletions = 10;
325+ // Similarity is the percentage (0-100) of unchanged content for a renamed or
326+ // copied file, and 0 otherwise.
327+ int32 similarity = 11;
328+ // Truncated is true when the file exceeded a diff limit: hunks is then empty
329+ // although the file changed. Additions and deletions remain accurate.
330+ bool truncated = 12;
331+ // Hunks are the changed regions, in file order. Empty for a binary file, a
332+ // truncated file, a pure rename and a mode-only change.
333+ repeated Hunk hunks = 13;
334+}
335+
336+// Hunk is a contiguous changed region with its context.
337+message Hunk {
338+ // OldStart is the 1-based first line of the region in the old file. 0 when
339+ // old_lines is 0 and the file was added.
340+ int32 old_start = 1;
341+ // OldLines is the number of old-side lines in the hunk.
342+ int32 old_lines = 2;
343+ // NewStart is the 1-based first line of the region in the new file. 0 when
344+ // new_lines is 0 and the file was deleted.
345+ int32 new_start = 3;
346+ // NewLines is the number of new-side lines in the hunk.
347+ int32 new_lines = 4;
348+ // Header is the text after the second "@@" of the hunk header, usually the
349+ // enclosing function. It may be empty.
350+ string header = 5;
351+ // Lines are the lines of the hunk, in order.
352+ repeated Line lines = 6;
353+}
354+
355+// Line is one line of a hunk.
356+message Line {
357+ // Kind tells which side the line belongs to.
358+ LineKind kind = 1;
359+ // OldNumber is the 1-based line number on the old side. 0 for an addition.
360+ int32 old_number = 2;
361+ // NewNumber is the 1-based line number on the new side. 0 for a deletion.
362+ int32 new_number = 3;
363+ // Text is the content without the line terminator. Bytes that are not valid
364+ // UTF-8 are replaced by the replacement character.
365+ string text = 4;
366+ // NoNewlineAtEof is true when this is the last line of its side and the file
367+ // does not end with a line break there.
368+ bool no_newline_at_eof = 5;
369+}
370+
371+// ResolveRevisionRequest names a revision.
372+message ResolveRevisionRequest {
373+ // Repo is the repository name.
374+ string repo = 1;
375+ // Revision is a branch, a tag or a commit id. Empty means HEAD.
376+ string revision = 2;
377+}
378+
379+// ResolveRevisionResponse holds the resolved commit.
380+message ResolveRevisionResponse {
381+ // CommitId is the full id of the commit the revision designates, after
382+ // following annotated tags.
383+ string commit_id = 1;
384+ // Ref is the full name of the ref the revision matched, for example
385+ // "refs/heads/main". Empty when the revision was a commit id or for HEAD
386+ // when it is resolved by default.
387+ string ref = 2;
388+}
@@ -0,0 +1,388 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "ermite/types.proto";
8+
9+// RefKind is the kind of a Git ref.
10+enum RefKind {
11+ // Not set. In ListRefsRequest.kinds it is rejected; never returned.
12+ REF_KIND_UNSPECIFIED = 0;
13+ // A branch (refs/heads/*).
14+ REF_KIND_BRANCH = 1;
15+ // A tag (refs/tags/*), lightweight or annotated.
16+ REF_KIND_TAG = 2;
17+ // The symbolic ref HEAD, that is the default branch.
18+ REF_KIND_HEAD = 3;
19+}
20+
21+// EntryKind is the kind of an entry of a Git tree.
22+enum EntryKind {
23+ // Not set. Never returned.
24+ ENTRY_KIND_UNSPECIFIED = 0;
25+ // A regular, non-executable file (mode 100644).
26+ ENTRY_KIND_FILE = 1;
27+ // An executable file (mode 100755).
28+ ENTRY_KIND_EXECUTABLE = 2;
29+ // A directory (mode 040000).
30+ ENTRY_KIND_DIRECTORY = 3;
31+ // A symbolic link (mode 120000). The blob content is the link target.
32+ ENTRY_KIND_SYMLINK = 4;
33+ // A submodule commit (mode 160000). The id is a commit of another
34+ // repository, which this one does not contain.
35+ ENTRY_KIND_SUBMODULE = 5;
36+}
37+
38+// FileStatus tells what a commit did to a file.
39+enum FileStatus {
40+ // Not set. Never returned.
41+ FILE_STATUS_UNSPECIFIED = 0;
42+ // The file was added.
43+ FILE_STATUS_ADDED = 1;
44+ // The file was deleted.
45+ FILE_STATUS_DELETED = 2;
46+ // The content or the mode of the file changed.
47+ FILE_STATUS_MODIFIED = 3;
48+ // The file was moved, possibly with changes. Rename detection is done by Git.
49+ FILE_STATUS_RENAMED = 4;
50+ // The file was copied from another file, possibly with changes.
51+ FILE_STATUS_COPIED = 5;
52+ // The type of the entry changed, for example a file became a symbolic link.
53+ FILE_STATUS_TYPE_CHANGED = 6;
54+}
55+
56+// LineKind tells how a line of a hunk relates to the two sides of a diff.
57+enum LineKind {
58+ // Not set. Never returned.
59+ LINE_KIND_UNSPECIFIED = 0;
60+ // The line is present, unchanged, on both sides.
61+ LINE_KIND_CONTEXT = 1;
62+ // The line exists only on the new side.
63+ LINE_KIND_ADDITION = 2;
64+ // The line exists only on the old side.
65+ LINE_KIND_DELETION = 3;
66+}
67+
68+// GitService reads the Git data of repositories. It is read-only: pushes go
69+// through Git itself.
70+//
71+// Every RPC takes the repository name. A repository that does not exist or
72+// that the caller cannot read gives not_found. A revision is a branch name, a
73+// tag name or a commit id (full, or an unambiguous abbreviation of at least 7
74+// characters); empty means HEAD. A revision or path that does not exist gives
75+// not_found; so does an empty repository, which has no commit to resolve.
76+service GitService {
77+ // ListRefs lists the branches, tags and HEAD of a repository. Refs are
78+ // ordered by kind (HEAD, branches, tags) then by name.
79+ rpc ListRefs(ListRefsRequest) returns (ListRefsResponse);
80+ // GetTree lists the entries of a directory at a revision. Fails with
81+ // not_found if the path does not exist and invalid_argument if it is not a
82+ // directory.
83+ rpc GetTree(GetTreeRequest) returns (GetTreeResponse);
84+ // GetBlob streams the content of a file at a revision, in chunks of at most
85+ // 64 KiB. The first message carries the metadata in info, then the chunks
86+ // follow; the memory used by the server does not depend on the file size.
87+ // Fails with not_found if the path does not exist and invalid_argument if it
88+ // is not a file.
89+ rpc GetBlob(GetBlobRequest) returns (stream GetBlobResponse);
90+ // ListCommits lists the history reachable from a revision, newest first
91+ // (Git's default order), optionally limited to the commits that touched a
92+ // path.
93+ rpc ListCommits(ListCommitsRequest) returns (ListCommitsResponse);
94+ // GetCommit returns one commit with its statistics and its diff.
95+ rpc GetCommit(GetCommitRequest) returns (GetCommitResponse);
96+ // ResolveRevision turns a revision into the id of the commit it designates.
97+ rpc ResolveRevision(ResolveRevisionRequest) returns (ResolveRevisionResponse);
98+}
99+
100+// Ref is a branch, a tag or HEAD.
101+message Ref {
102+ // Name is the full ref name, for example "refs/heads/main" or
103+ // "refs/tags/v1.0.0". For HEAD it is "HEAD".
104+ string name = 1;
105+ // ShortName is the name without its "refs/heads/" or "refs/tags/" prefix. For
106+ // HEAD it is the short name of the default branch, or empty if HEAD points
107+ // to a branch that does not exist yet.
108+ string short_name = 2;
109+ // Kind is the kind of the ref.
110+ RefKind kind = 3;
111+ // Id is the id of the object the ref points to. For an annotated tag it is
112+ // the id of the tag object.
113+ string id = 4;
114+ // CommitId is the id of the commit the ref designates once annotated tags are
115+ // followed. It equals id for branches and lightweight tags. It is empty for
116+ // a tag that points to something other than a commit.
117+ string commit_id = 5;
118+}
119+
120+// ListRefsRequest selects the refs to list.
121+message ListRefsRequest {
122+ // Repo is the repository name.
123+ string repo = 1;
124+ // Kinds restricts the result to these kinds. Empty means all kinds.
125+ repeated RefKind kinds = 2;
126+ // PageSize is the maximum number of refs to return. 0 means the server
127+ // default; values above the server maximum are lowered to it.
128+ int32 page_size = 3;
129+ // PageToken is the next_page_token of a previous response. Empty for the
130+ // first page.
131+ string page_token = 4;
132+}
133+
134+// ListRefsResponse is one page of refs.
135+message ListRefsResponse {
136+ // Refs are the refs of this page.
137+ repeated Ref refs = 1;
138+ // NextPageToken is passed as page_token to get the next page. Empty when
139+ // there is no next page.
140+ string next_page_token = 2;
141+}
142+
143+// TreeEntry is one entry of a directory.
144+message TreeEntry {
145+ // Name is the name of the entry in its directory, without slash.
146+ string name = 1;
147+ // Path is the path of the entry from the repository root, with "/"
148+ // separators and no leading slash.
149+ string path = 2;
150+ // Kind is the kind of the entry.
151+ EntryKind kind = 3;
152+ // Mode is the Git file mode, for example 0o100644 (33188 in decimal).
153+ uint32 mode = 4;
154+ // Id is the object id of the blob, tree or submodule commit.
155+ string id = 5;
156+ // Size is the size in bytes of a file or symbolic link. It is 0 for
157+ // directories and submodules.
158+ int64 size = 6;
159+ // LastCommit is the latest commit that changed the entry. Set only when the
160+ // request asked for it with include_last_commit.
161+ Commit last_commit = 7;
162+}
163+
164+// GetTreeRequest selects a directory.
165+message GetTreeRequest {
166+ // Repo is the repository name.
167+ string repo = 1;
168+ // Revision is a branch, a tag or a commit id. Empty means HEAD.
169+ string revision = 2;
170+ // Path is the directory, from the repository root, with "/" separators.
171+ // Empty means the root.
172+ string path = 3;
173+ // IncludeLastCommit asks for the last commit of every entry in last_commit.
174+ // It costs one history walk, so it is off by default and the server may be
175+ // slow on large directories.
176+ bool include_last_commit = 4;
177+}
178+
179+// GetTreeResponse lists a directory.
180+message GetTreeResponse {
181+ // CommitId is the id of the commit the revision resolved to.
182+ string commit_id = 1;
183+ // TreeId is the object id of the listed directory.
184+ string tree_id = 2;
185+ // Entries are the entries of the directory: directories first, then the other
186+ // entries, each group ordered by name.
187+ repeated TreeEntry entries = 3;
188+}
189+
190+// BlobInfo describes a file.
191+message BlobInfo {
192+ // Id is the object id of the blob.
193+ string id = 1;
194+ // Path is the path of the file from the repository root.
195+ string path = 2;
196+ // Size is the size of the whole content in bytes.
197+ int64 size = 3;
198+ // Binary is true if the content is binary, with Git's rule: a NUL byte in the
199+ // first 8,000 bytes.
200+ bool binary = 4;
201+ // MediaType is the detected media type, for example "text/plain" or
202+ // "image/png", from the file name and the first bytes. It is
203+ // "application/octet-stream" when unknown.
204+ string media_type = 5;
205+ // Mode is the Git file mode, for example 0o100644 (33188 in decimal).
206+ uint32 mode = 6;
207+}
208+
209+// GetBlobRequest selects a file.
210+message GetBlobRequest {
211+ // Repo is the repository name.
212+ string repo = 1;
213+ // Revision is a branch, a tag or a commit id. Empty means HEAD.
214+ string revision = 2;
215+ // Path is the file, from the repository root. It is required.
216+ string path = 3;
217+}
218+
219+// GetBlobResponse is one message of the content stream.
220+message GetBlobResponse {
221+ // Info describes the file. Set in the first message of the stream only.
222+ BlobInfo info = 1;
223+ // Chunk is the next part of the content, at most 64 KiB, in order. The first
224+ // message may have an empty chunk. The chunks of a stream add up to
225+ // info.size bytes.
226+ bytes chunk = 2;
227+}
228+
229+// ListCommitsRequest selects a page of history.
230+message ListCommitsRequest {
231+ // Repo is the repository name.
232+ string repo = 1;
233+ // Revision is where the history starts: a branch, a tag or a commit id.
234+ // Empty means HEAD.
235+ string revision = 2;
236+ // Path restricts the result to the commits that changed this file or
237+ // directory. Empty means the whole history.
238+ string path = 3;
239+ // PageSize is the maximum number of commits to return. 0 means the server
240+ // default; values above 100 are lowered to 100.
241+ int32 page_size = 4;
242+ // PageToken is the next_page_token of a previous response, to continue the
243+ // same listing. Empty for the first page. Other fields must be unchanged.
244+ string page_token = 5;
245+}
246+
247+// ListCommitsResponse is one page of commits.
248+message ListCommitsResponse {
249+ // Commits are the commits of this page, newest first.
250+ repeated Commit commits = 1;
251+ // NextPageToken is passed as page_token to get the next page. Empty when
252+ // there is no next page.
253+ string next_page_token = 2;
254+}
255+
256+// GetCommitRequest selects a commit.
257+message GetCommitRequest {
258+ // Repo is the repository name.
259+ string repo = 1;
260+ // Revision is a branch, a tag or a commit id. Empty means HEAD.
261+ string revision = 2;
262+}
263+
264+// GetCommitResponse holds a commit, its statistics and its diff.
265+message GetCommitResponse {
266+ // Commit is the commit metadata.
267+ Commit commit = 1;
268+ // Stats are the totals over all the changed files, including files that the
269+ // diff omits or truncates.
270+ DiffStats stats = 2;
271+ // Diff is the structured diff against the first parent, or against the empty
272+ // tree for a root commit. For a merge commit this is the change relative to
273+ // the first parent.
274+ Diff diff = 3;
275+}
276+
277+// DiffStats are the totals of a diff.
278+message DiffStats {
279+ // FilesChanged is the number of changed files.
280+ int32 files_changed = 1;
281+ // Additions is the number of added lines. Binary files count for 0.
282+ int32 additions = 2;
283+ // Deletions is the number of deleted lines. Binary files count for 0.
284+ int32 deletions = 3;
285+}
286+
287+// Diff is the structured difference introduced by a commit.
288+//
289+// The server limits its size: at most 300 files, at most 2,000 lines in one
290+// file and at most 20,000 lines in total (hunk lines, context included). These
291+// numbers are server behaviour and may change; clients must rely on the
292+// truncated flags, not on the numbers.
293+message Diff {
294+ // Files are the changed files, ordered by path. Files past the file limit are
295+ // omitted.
296+ repeated FileDiff files = 1;
297+ // Truncated is true when the diff is incomplete: files were omitted, or at
298+ // least one listed file has truncated set. DiffStats still count everything.
299+ bool truncated = 2;
300+}
301+
302+// FileDiff is the change of one file.
303+message FileDiff {
304+ // OldPath is the path before the commit. Empty for an added file.
305+ string old_path = 1;
306+ // NewPath is the path after the commit. Empty for a deleted file.
307+ string new_path = 2;
308+ // Status tells what happened to the file.
309+ FileStatus status = 3;
310+ // OldMode is the Git file mode before the commit, or 0 for an added file.
311+ uint32 old_mode = 4;
312+ // NewMode is the Git file mode after the commit, or 0 for a deleted file.
313+ uint32 new_mode = 5;
314+ // OldId is the blob id before the commit. Empty for an added file.
315+ string old_id = 6;
316+ // NewId is the blob id after the commit. Empty for a deleted file.
317+ string new_id = 7;
318+ // Binary is true if Git treats the file as binary (numstat reports "-"). A
319+ // binary file has no hunks and additions and deletions are 0.
320+ bool binary = 8;
321+ // Additions is the number of added lines.
322+ int32 additions = 9;
323+ // Deletions is the number of deleted lines.
324+ int32 deletions = 10;
325+ // Similarity is the percentage (0-100) of unchanged content for a renamed or
326+ // copied file, and 0 otherwise.
327+ int32 similarity = 11;
328+ // Truncated is true when the file exceeded a diff limit: hunks is then empty
329+ // although the file changed. Additions and deletions remain accurate.
330+ bool truncated = 12;
331+ // Hunks are the changed regions, in file order. Empty for a binary file, a
332+ // truncated file, a pure rename and a mode-only change.
333+ repeated Hunk hunks = 13;
334+}
335+
336+// Hunk is a contiguous changed region with its context.
337+message Hunk {
338+ // OldStart is the 1-based first line of the region in the old file. 0 when
339+ // old_lines is 0 and the file was added.
340+ int32 old_start = 1;
341+ // OldLines is the number of old-side lines in the hunk.
342+ int32 old_lines = 2;
343+ // NewStart is the 1-based first line of the region in the new file. 0 when
344+ // new_lines is 0 and the file was deleted.
345+ int32 new_start = 3;
346+ // NewLines is the number of new-side lines in the hunk.
347+ int32 new_lines = 4;
348+ // Header is the text after the second "@@" of the hunk header, usually the
349+ // enclosing function. It may be empty.
350+ string header = 5;
351+ // Lines are the lines of the hunk, in order.
352+ repeated Line lines = 6;
353+}
354+
355+// Line is one line of a hunk.
356+message Line {
357+ // Kind tells which side the line belongs to.
358+ LineKind kind = 1;
359+ // OldNumber is the 1-based line number on the old side. 0 for an addition.
360+ int32 old_number = 2;
361+ // NewNumber is the 1-based line number on the new side. 0 for a deletion.
362+ int32 new_number = 3;
363+ // Text is the content without the line terminator. Bytes that are not valid
364+ // UTF-8 are replaced by the replacement character.
365+ string text = 4;
366+ // NoNewlineAtEof is true when this is the last line of its side and the file
367+ // does not end with a line break there.
368+ bool no_newline_at_eof = 5;
369+}
370+
371+// ResolveRevisionRequest names a revision.
372+message ResolveRevisionRequest {
373+ // Repo is the repository name.
374+ string repo = 1;
375+ // Revision is a branch, a tag or a commit id. Empty means HEAD.
376+ string revision = 2;
377+}
378+
379+// ResolveRevisionResponse holds the resolved commit.
380+message ResolveRevisionResponse {
381+ // CommitId is the full id of the commit the revision designates, after
382+ // following annotated tags.
383+ string commit_id = 1;
384+ // Ref is the full name of the ref the revision matched, for example
385+ // "refs/heads/main". Empty when the revision was a commit id or for HEAD
386+ // when it is resolved by default.
387+ string ref = 2;
388+}
A proto/ermite/repo.proto +145 -0
@@ -0,0 +1,145 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "google/protobuf/field_mask.proto";
8+import "google/protobuf/timestamp.proto";
9+
10+// Visibility says who may read a repository.
11+enum Visibility {
12+ // Not set. In requests, the server applies its default (private on create,
13+ // unchanged on update). Never returned by the server.
14+ VISIBILITY_UNSPECIFIED = 0;
15+ // Anyone, including anonymous callers, may read the repository.
16+ VISIBILITY_PUBLIC = 1;
17+ // Only callers holding the repo:read scope on the repository may read it.
18+ // Everyone else gets not_found, never permission_denied.
19+ VISIBILITY_PRIVATE = 2;
20+}
21+
22+// Repo is the metadata of a repository. Git-owned state (refs, trees, history)
23+// is read through GitService.
24+message Repo {
25+ // Name is the unique, lowercase repository name: 1-100 characters from
26+ // [a-z0-9._-], without a ".git" suffix. It is also the URL path: /<name>.
27+ string name = 1;
28+ // Description is a free one-line description. It may be empty.
29+ string description = 2;
30+ // Visibility is who may read the repository.
31+ Visibility visibility = 3;
32+ // DefaultBranch is the short name of the branch HEAD points to, for example
33+ // "main". It is set even when the branch does not exist yet (empty
34+ // repository).
35+ string default_branch = 4;
36+ // CreatedAt is when the repository was created.
37+ google.protobuf.Timestamp created_at = 5;
38+ // PushedAt is the time of the last push. It is unset until the first push.
39+ google.protobuf.Timestamp pushed_at = 6;
40+}
41+
42+// RepoService manages repositories and their metadata.
43+//
44+// Anonymous callers see public repositories only. A private repository that the
45+// caller cannot read is reported as not_found.
46+service RepoService {
47+ // ListRepos lists the repositories the caller may see, ordered by name.
48+ rpc ListRepos(ListReposRequest) returns (ListReposResponse);
49+ // GetRepo returns one repository. Fails with not_found if it does not exist
50+ // or is private and not readable by the caller.
51+ rpc GetRepo(GetRepoRequest) returns (GetRepoResponse);
52+ // CreateRepo creates an empty repository. Needs the repo:write scope. Fails
53+ // with already_exists if the name is taken and invalid_argument if the name
54+ // is not valid.
55+ rpc CreateRepo(CreateRepoRequest) returns (CreateRepoResponse);
56+ // UpdateRepo changes the fields named in update_mask. Needs the repo:admin
57+ // scope.
58+ rpc UpdateRepo(UpdateRepoRequest) returns (UpdateRepoResponse);
59+ // 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.
61+ rpc DeleteRepo(DeleteRepoRequest) returns (DeleteRepoResponse);
62+}
63+
64+// ListReposRequest selects a page of repositories.
65+message ListReposRequest {
66+ // PageSize is the maximum number of repositories to return. 0 means the
67+ // server default; values above the server maximum are lowered to it.
68+ int32 page_size = 1;
69+ // PageToken is the next_page_token of a previous response, to get the page
70+ // after it. Empty for the first page.
71+ string page_token = 2;
72+}
73+
74+// ListReposResponse is one page of repositories.
75+message ListReposResponse {
76+ // Repos are the repositories of this page, ordered by name.
77+ repeated Repo repos = 1;
78+ // NextPageToken is passed as page_token to get the next page. Empty when
79+ // there is no next page.
80+ string next_page_token = 2;
81+}
82+
83+// GetRepoRequest names the repository to read.
84+message GetRepoRequest {
85+ // Repo is the repository name.
86+ string repo = 1;
87+}
88+
89+// GetRepoResponse holds the repository.
90+message GetRepoResponse {
91+ // Repo is the repository metadata.
92+ Repo repo = 1;
93+}
94+
95+// CreateRepoRequest describes the repository to create.
96+message CreateRepoRequest {
97+ // Name is the name of the new repository. Names are lowercase; an uppercase
98+ // name is rejected with invalid_argument.
99+ string name = 1;
100+ // Description is an optional one-line description.
101+ string description = 2;
102+ // Visibility of the new repository. VISIBILITY_UNSPECIFIED means private.
103+ Visibility visibility = 3;
104+}
105+
106+// CreateRepoResponse holds the new repository.
107+message CreateRepoResponse {
108+ // Repo is the created repository.
109+ Repo repo = 1;
110+}
111+
112+// UpdateRepoRequest changes some fields of a repository.
113+message UpdateRepoRequest {
114+ // Repo is the name of the repository to change. Repositories cannot be
115+ // renamed.
116+ string repo = 1;
117+ // Description is the new description. Used only if update_mask contains
118+ // "description"; an empty value then clears it.
119+ string description = 2;
120+ // Visibility is the new visibility. Used only if update_mask contains
121+ // "visibility"; it must not be VISIBILITY_UNSPECIFIED then.
122+ Visibility visibility = 3;
123+ // DefaultBranch is the short name of the new default branch. Used only if
124+ // update_mask contains "default_branch". The branch must exist, otherwise
125+ // the call fails with failed_precondition.
126+ 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.
129+ google.protobuf.FieldMask update_mask = 5;
130+}
131+
132+// UpdateRepoResponse holds the repository after the update.
133+message UpdateRepoResponse {
134+ // Repo is the updated repository.
135+ Repo repo = 1;
136+}
137+
138+// DeleteRepoRequest names the repository to delete.
139+message DeleteRepoRequest {
140+ // Repo is the repository name.
141+ string repo = 1;
142+}
143+
144+// DeleteRepoResponse is empty.
145+message DeleteRepoResponse {}
@@ -0,0 +1,145 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "google/protobuf/field_mask.proto";
8+import "google/protobuf/timestamp.proto";
9+
10+// Visibility says who may read a repository.
11+enum Visibility {
12+ // Not set. In requests, the server applies its default (private on create,
13+ // unchanged on update). Never returned by the server.
14+ VISIBILITY_UNSPECIFIED = 0;
15+ // Anyone, including anonymous callers, may read the repository.
16+ VISIBILITY_PUBLIC = 1;
17+ // Only callers holding the repo:read scope on the repository may read it.
18+ // Everyone else gets not_found, never permission_denied.
19+ VISIBILITY_PRIVATE = 2;
20+}
21+
22+// Repo is the metadata of a repository. Git-owned state (refs, trees, history)
23+// is read through GitService.
24+message Repo {
25+ // Name is the unique, lowercase repository name: 1-100 characters from
26+ // [a-z0-9._-], without a ".git" suffix. It is also the URL path: /<name>.
27+ string name = 1;
28+ // Description is a free one-line description. It may be empty.
29+ string description = 2;
30+ // Visibility is who may read the repository.
31+ Visibility visibility = 3;
32+ // DefaultBranch is the short name of the branch HEAD points to, for example
33+ // "main". It is set even when the branch does not exist yet (empty
34+ // repository).
35+ string default_branch = 4;
36+ // CreatedAt is when the repository was created.
37+ google.protobuf.Timestamp created_at = 5;
38+ // PushedAt is the time of the last push. It is unset until the first push.
39+ google.protobuf.Timestamp pushed_at = 6;
40+}
41+
42+// RepoService manages repositories and their metadata.
43+//
44+// Anonymous callers see public repositories only. A private repository that the
45+// caller cannot read is reported as not_found.
46+service RepoService {
47+ // ListRepos lists the repositories the caller may see, ordered by name.
48+ rpc ListRepos(ListReposRequest) returns (ListReposResponse);
49+ // GetRepo returns one repository. Fails with not_found if it does not exist
50+ // or is private and not readable by the caller.
51+ rpc GetRepo(GetRepoRequest) returns (GetRepoResponse);
52+ // CreateRepo creates an empty repository. Needs the repo:write scope. Fails
53+ // with already_exists if the name is taken and invalid_argument if the name
54+ // is not valid.
55+ rpc CreateRepo(CreateRepoRequest) returns (CreateRepoResponse);
56+ // UpdateRepo changes the fields named in update_mask. Needs the repo:admin
57+ // scope.
58+ rpc UpdateRepo(UpdateRepoRequest) returns (UpdateRepoResponse);
59+ // 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.
61+ rpc DeleteRepo(DeleteRepoRequest) returns (DeleteRepoResponse);
62+}
63+
64+// ListReposRequest selects a page of repositories.
65+message ListReposRequest {
66+ // PageSize is the maximum number of repositories to return. 0 means the
67+ // server default; values above the server maximum are lowered to it.
68+ int32 page_size = 1;
69+ // PageToken is the next_page_token of a previous response, to get the page
70+ // after it. Empty for the first page.
71+ string page_token = 2;
72+}
73+
74+// ListReposResponse is one page of repositories.
75+message ListReposResponse {
76+ // Repos are the repositories of this page, ordered by name.
77+ repeated Repo repos = 1;
78+ // NextPageToken is passed as page_token to get the next page. Empty when
79+ // there is no next page.
80+ string next_page_token = 2;
81+}
82+
83+// GetRepoRequest names the repository to read.
84+message GetRepoRequest {
85+ // Repo is the repository name.
86+ string repo = 1;
87+}
88+
89+// GetRepoResponse holds the repository.
90+message GetRepoResponse {
91+ // Repo is the repository metadata.
92+ Repo repo = 1;
93+}
94+
95+// CreateRepoRequest describes the repository to create.
96+message CreateRepoRequest {
97+ // Name is the name of the new repository. Names are lowercase; an uppercase
98+ // name is rejected with invalid_argument.
99+ string name = 1;
100+ // Description is an optional one-line description.
101+ string description = 2;
102+ // Visibility of the new repository. VISIBILITY_UNSPECIFIED means private.
103+ Visibility visibility = 3;
104+}
105+
106+// CreateRepoResponse holds the new repository.
107+message CreateRepoResponse {
108+ // Repo is the created repository.
109+ Repo repo = 1;
110+}
111+
112+// UpdateRepoRequest changes some fields of a repository.
113+message UpdateRepoRequest {
114+ // Repo is the name of the repository to change. Repositories cannot be
115+ // renamed.
116+ string repo = 1;
117+ // Description is the new description. Used only if update_mask contains
118+ // "description"; an empty value then clears it.
119+ string description = 2;
120+ // Visibility is the new visibility. Used only if update_mask contains
121+ // "visibility"; it must not be VISIBILITY_UNSPECIFIED then.
122+ Visibility visibility = 3;
123+ // DefaultBranch is the short name of the new default branch. Used only if
124+ // update_mask contains "default_branch". The branch must exist, otherwise
125+ // the call fails with failed_precondition.
126+ 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.
129+ google.protobuf.FieldMask update_mask = 5;
130+}
131+
132+// UpdateRepoResponse holds the repository after the update.
133+message UpdateRepoResponse {
134+ // Repo is the updated repository.
135+ Repo repo = 1;
136+}
137+
138+// DeleteRepoRequest names the repository to delete.
139+message DeleteRepoRequest {
140+ // Repo is the repository name.
141+ string repo = 1;
142+}
143+
144+// DeleteRepoResponse is empty.
145+message DeleteRepoResponse {}
A proto/ermite/server.proto +30 -0
@@ -0,0 +1,30 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+// ServerService describes the server itself.
8+service ServerService {
9+ // GetVersion returns the server version and the optional capabilities of the
10+ // contract it implements. It needs no authentication, so a client can call
11+ // it first to check compatibility.
12+ rpc GetVersion(GetVersionRequest) returns (GetVersionResponse);
13+}
14+
15+// GetVersionRequest is empty.
16+message GetVersionRequest {}
17+
18+// GetVersionResponse describes the running server.
19+message GetVersionResponse {
20+ // Version is the server release, for example "v0.4.0", or "devel" for an
21+ // untagged build. Informational: clients must not compare it to decide what
22+ // is supported, they use features instead.
23+ string version = 1;
24+ // Features lists identifiers of the optional capabilities of the contract
25+ // that this server implements, for example "repo.update" or
26+ // "events.resume". A client that needs a capability missing from this list
27+ // should warn instead of failing on an unimplemented RPC or ignored field.
28+ // Identifiers are lowercase, dot-separated and never reused.
29+ repeated string features = 2;
30+}
@@ -0,0 +1,30 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+// ServerService describes the server itself.
8+service ServerService {
9+ // GetVersion returns the server version and the optional capabilities of the
10+ // contract it implements. It needs no authentication, so a client can call
11+ // it first to check compatibility.
12+ rpc GetVersion(GetVersionRequest) returns (GetVersionResponse);
13+}
14+
15+// GetVersionRequest is empty.
16+message GetVersionRequest {}
17+
18+// GetVersionResponse describes the running server.
19+message GetVersionResponse {
20+ // Version is the server release, for example "v0.4.0", or "devel" for an
21+ // untagged build. Informational: clients must not compare it to decide what
22+ // is supported, they use features instead.
23+ string version = 1;
24+ // Features lists identifiers of the optional capabilities of the contract
25+ // that this server implements, for example "repo.update" or
26+ // "events.resume". A client that needs a capability missing from this list
27+ // should warn instead of failing on an unimplemented RPC or ignored field.
28+ // Identifiers are lowercase, dot-separated and never reused.
29+ repeated string features = 2;
30+}
A proto/ermite/token.proto +132 -0
@@ -0,0 +1,132 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "google/protobuf/timestamp.proto";
8+
9+// Scope is a permission carried by a token. The API knows scopes only; any
10+// grouping into presets is a client concern.
11+enum Scope {
12+ // Not set. Rejected in requests, never returned by the server.
13+ SCOPE_UNSPECIFIED = 0;
14+ // SCOPE_REPO_READ allows reading private repositories (public ones need no
15+ // token). Wire name: repo:read.
16+ SCOPE_REPO_READ = 1;
17+ // SCOPE_REPO_WRITE allows pushing and creating repositories. Wire name:
18+ // repo:write.
19+ SCOPE_REPO_WRITE = 2;
20+ // SCOPE_REPO_ADMIN allows deleting a repository and changing its visibility,
21+ // description or default branch. Wire name: repo:admin.
22+ SCOPE_REPO_ADMIN = 3;
23+ // SCOPE_MR_WRITE allows creating, commenting on and closing merge requests.
24+ // Wire name: mr:write.
25+ SCOPE_MR_WRITE = 4;
26+ // SCOPE_MR_MERGE allows merging merge requests. Wire name: mr:merge.
27+ SCOPE_MR_MERGE = 5;
28+ // SCOPE_CI_WRITE allows re-running or cancelling CI runs. Wire name:
29+ // ci:write.
30+ SCOPE_CI_WRITE = 6;
31+ // SCOPE_SECRETS_WRITE allows setting and deleting secrets and variables.
32+ // Wire name: secrets:write.
33+ SCOPE_SECRETS_WRITE = 7;
34+ // SCOPE_RUNNER_REGISTER allows registering a CI runner. Wire name:
35+ // runner:register.
36+ SCOPE_RUNNER_REGISTER = 8;
37+ // SCOPE_TOKEN_ADMIN allows creating, listing and revoking tokens. Wire name:
38+ // token:admin.
39+ SCOPE_TOKEN_ADMIN = 9;
40+}
41+
42+// Token describes a stored token. It never contains the secret nor its hash.
43+message Token {
44+ // Id identifies the token in ListTokens and RevokeToken. It is not the
45+ // secret and cannot be used to authenticate.
46+ string id = 1;
47+ // Label is a human-readable name chosen at creation.
48+ string label = 2;
49+ // Scopes are the permissions of the token. Never empty.
50+ repeated Scope scopes = 3;
51+ // Repos restricts the token to these repository names. Empty means every
52+ // repository.
53+ repeated string repos = 4;
54+ // ExpiresAt is when the token stops working. Unset means it never expires.
55+ google.protobuf.Timestamp expires_at = 5;
56+ // CreatedAt is when the token was created.
57+ google.protobuf.Timestamp created_at = 6;
58+ // LastUsedAt is when the token last authenticated a request. Unset if never
59+ // used. It may lag behind the real time by a short delay.
60+ google.protobuf.Timestamp last_used_at = 7;
61+}
62+
63+// TokenService manages stored tokens. Every RPC needs the token:admin scope.
64+//
65+// A token can only create tokens that are within its own limits: scopes that
66+// it holds, repositories that it may access and an expiry no later than its
67+// own. The root token (ERMITE_ADMIN_TOKEN) is not a stored token and is never
68+// listed.
69+service TokenService {
70+ // CreateToken creates a token and returns its secret. The secret is shown
71+ // only in this response and cannot be retrieved later. Fails with
72+ // permission_denied when the new token would exceed the caller's limits and
73+ // with invalid_argument when scopes is empty or contains SCOPE_UNSPECIFIED.
74+ rpc CreateToken(CreateTokenRequest) returns (CreateTokenResponse);
75+ // ListTokens lists stored tokens, newest first.
76+ rpc ListTokens(ListTokensRequest) returns (ListTokensResponse);
77+ // RevokeToken deletes a token; it stops working immediately. Fails with
78+ // not_found if the id is unknown.
79+ rpc RevokeToken(RevokeTokenRequest) returns (RevokeTokenResponse);
80+}
81+
82+// CreateTokenRequest describes the token to create.
83+message CreateTokenRequest {
84+ // Label is a human-readable name, for example "laptop". Not required to be
85+ // unique.
86+ string label = 1;
87+ // Scopes are the permissions to grant. At least one is required.
88+ repeated Scope scopes = 2;
89+ // Repos restricts the token to these repository names. Empty means every
90+ // repository.
91+ repeated string repos = 3;
92+ // ExpiresAt is when the token stops working. Unset means no expiry. It must
93+ // be in the future.
94+ google.protobuf.Timestamp expires_at = 4;
95+}
96+
97+// CreateTokenResponse holds the new token and its secret.
98+message CreateTokenResponse {
99+ // Token describes the created token.
100+ Token token = 1;
101+ // Secret is the bearer secret to present as "Authorization: Bearer <secret>".
102+ // It is returned once and never stored in clear text by the server.
103+ string secret = 2;
104+}
105+
106+// ListTokensRequest selects a page of tokens.
107+message ListTokensRequest {
108+ // PageSize is the maximum number of tokens to return. 0 means the server
109+ // default; values above the server maximum are lowered to it.
110+ int32 page_size = 1;
111+ // PageToken is the next_page_token of a previous response. Empty for the
112+ // first page.
113+ string page_token = 2;
114+}
115+
116+// ListTokensResponse is one page of tokens.
117+message ListTokensResponse {
118+ // Tokens are the tokens of this page, newest first.
119+ repeated Token tokens = 1;
120+ // NextPageToken is passed as page_token to get the next page. Empty when
121+ // there is no next page.
122+ string next_page_token = 2;
123+}
124+
125+// RevokeTokenRequest names the token to revoke.
126+message RevokeTokenRequest {
127+ // Id is the Token.id to revoke.
128+ string id = 1;
129+}
130+
131+// RevokeTokenResponse is empty.
132+message RevokeTokenResponse {}
@@ -0,0 +1,132 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "google/protobuf/timestamp.proto";
8+
9+// Scope is a permission carried by a token. The API knows scopes only; any
10+// grouping into presets is a client concern.
11+enum Scope {
12+ // Not set. Rejected in requests, never returned by the server.
13+ SCOPE_UNSPECIFIED = 0;
14+ // SCOPE_REPO_READ allows reading private repositories (public ones need no
15+ // token). Wire name: repo:read.
16+ SCOPE_REPO_READ = 1;
17+ // SCOPE_REPO_WRITE allows pushing and creating repositories. Wire name:
18+ // repo:write.
19+ SCOPE_REPO_WRITE = 2;
20+ // SCOPE_REPO_ADMIN allows deleting a repository and changing its visibility,
21+ // description or default branch. Wire name: repo:admin.
22+ SCOPE_REPO_ADMIN = 3;
23+ // SCOPE_MR_WRITE allows creating, commenting on and closing merge requests.
24+ // Wire name: mr:write.
25+ SCOPE_MR_WRITE = 4;
26+ // SCOPE_MR_MERGE allows merging merge requests. Wire name: mr:merge.
27+ SCOPE_MR_MERGE = 5;
28+ // SCOPE_CI_WRITE allows re-running or cancelling CI runs. Wire name:
29+ // ci:write.
30+ SCOPE_CI_WRITE = 6;
31+ // SCOPE_SECRETS_WRITE allows setting and deleting secrets and variables.
32+ // Wire name: secrets:write.
33+ SCOPE_SECRETS_WRITE = 7;
34+ // SCOPE_RUNNER_REGISTER allows registering a CI runner. Wire name:
35+ // runner:register.
36+ SCOPE_RUNNER_REGISTER = 8;
37+ // SCOPE_TOKEN_ADMIN allows creating, listing and revoking tokens. Wire name:
38+ // token:admin.
39+ SCOPE_TOKEN_ADMIN = 9;
40+}
41+
42+// Token describes a stored token. It never contains the secret nor its hash.
43+message Token {
44+ // Id identifies the token in ListTokens and RevokeToken. It is not the
45+ // secret and cannot be used to authenticate.
46+ string id = 1;
47+ // Label is a human-readable name chosen at creation.
48+ string label = 2;
49+ // Scopes are the permissions of the token. Never empty.
50+ repeated Scope scopes = 3;
51+ // Repos restricts the token to these repository names. Empty means every
52+ // repository.
53+ repeated string repos = 4;
54+ // ExpiresAt is when the token stops working. Unset means it never expires.
55+ google.protobuf.Timestamp expires_at = 5;
56+ // CreatedAt is when the token was created.
57+ google.protobuf.Timestamp created_at = 6;
58+ // LastUsedAt is when the token last authenticated a request. Unset if never
59+ // used. It may lag behind the real time by a short delay.
60+ google.protobuf.Timestamp last_used_at = 7;
61+}
62+
63+// TokenService manages stored tokens. Every RPC needs the token:admin scope.
64+//
65+// A token can only create tokens that are within its own limits: scopes that
66+// it holds, repositories that it may access and an expiry no later than its
67+// own. The root token (ERMITE_ADMIN_TOKEN) is not a stored token and is never
68+// listed.
69+service TokenService {
70+ // CreateToken creates a token and returns its secret. The secret is shown
71+ // only in this response and cannot be retrieved later. Fails with
72+ // permission_denied when the new token would exceed the caller's limits and
73+ // with invalid_argument when scopes is empty or contains SCOPE_UNSPECIFIED.
74+ rpc CreateToken(CreateTokenRequest) returns (CreateTokenResponse);
75+ // ListTokens lists stored tokens, newest first.
76+ rpc ListTokens(ListTokensRequest) returns (ListTokensResponse);
77+ // RevokeToken deletes a token; it stops working immediately. Fails with
78+ // not_found if the id is unknown.
79+ rpc RevokeToken(RevokeTokenRequest) returns (RevokeTokenResponse);
80+}
81+
82+// CreateTokenRequest describes the token to create.
83+message CreateTokenRequest {
84+ // Label is a human-readable name, for example "laptop". Not required to be
85+ // unique.
86+ string label = 1;
87+ // Scopes are the permissions to grant. At least one is required.
88+ repeated Scope scopes = 2;
89+ // Repos restricts the token to these repository names. Empty means every
90+ // repository.
91+ repeated string repos = 3;
92+ // ExpiresAt is when the token stops working. Unset means no expiry. It must
93+ // be in the future.
94+ google.protobuf.Timestamp expires_at = 4;
95+}
96+
97+// CreateTokenResponse holds the new token and its secret.
98+message CreateTokenResponse {
99+ // Token describes the created token.
100+ Token token = 1;
101+ // Secret is the bearer secret to present as "Authorization: Bearer <secret>".
102+ // It is returned once and never stored in clear text by the server.
103+ string secret = 2;
104+}
105+
106+// ListTokensRequest selects a page of tokens.
107+message ListTokensRequest {
108+ // PageSize is the maximum number of tokens to return. 0 means the server
109+ // default; values above the server maximum are lowered to it.
110+ int32 page_size = 1;
111+ // PageToken is the next_page_token of a previous response. Empty for the
112+ // first page.
113+ string page_token = 2;
114+}
115+
116+// ListTokensResponse is one page of tokens.
117+message ListTokensResponse {
118+ // Tokens are the tokens of this page, newest first.
119+ repeated Token tokens = 1;
120+ // NextPageToken is passed as page_token to get the next page. Empty when
121+ // there is no next page.
122+ string next_page_token = 2;
123+}
124+
125+// RevokeTokenRequest names the token to revoke.
126+message RevokeTokenRequest {
127+ // Id is the Token.id to revoke.
128+ string id = 1;
129+}
130+
131+// RevokeTokenResponse is empty.
132+message RevokeTokenResponse {}
A proto/ermite/types.proto +37 -0
@@ -0,0 +1,37 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "google/protobuf/timestamp.proto";
8+
9+// Signature identifies who did something in Git and when: the author or the
10+// committer of a commit.
11+message Signature {
12+ // Name is the person's name as recorded in the commit.
13+ string name = 1;
14+ // Email is the person's email address as recorded in the commit.
15+ string email = 2;
16+ // Time is when the action happened. The time zone offset of the commit is
17+ // not kept.
18+ google.protobuf.Timestamp time = 3;
19+}
20+
21+// Commit is the metadata of one Git commit.
22+message Commit {
23+ // Id is the full object id (40 lowercase hexadecimal characters for SHA-1).
24+ string id = 1;
25+ // ParentIds are the ids of the parent commits, first parent first. Empty for
26+ // a root commit.
27+ repeated string parent_ids = 2;
28+ // Author is who wrote the change.
29+ Signature author = 3;
30+ // Committer is who recorded the commit.
31+ Signature committer = 4;
32+ // Summary is the first line of the message, without the line break.
33+ string summary = 5;
34+ // Message is the full commit message, including the summary. Text that is
35+ // not valid UTF-8 is converted with the replacement character.
36+ string message = 6;
37+}
@@ -0,0 +1,37 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "google/protobuf/timestamp.proto";
8+
9+// Signature identifies who did something in Git and when: the author or the
10+// committer of a commit.
11+message Signature {
12+ // Name is the person's name as recorded in the commit.
13+ string name = 1;
14+ // Email is the person's email address as recorded in the commit.
15+ string email = 2;
16+ // Time is when the action happened. The time zone offset of the commit is
17+ // not kept.
18+ google.protobuf.Timestamp time = 3;
19+}
20+
21+// Commit is the metadata of one Git commit.
22+message Commit {
23+ // Id is the full object id (40 lowercase hexadecimal characters for SHA-1).
24+ string id = 1;
25+ // ParentIds are the ids of the parent commits, first parent first. Empty for
26+ // a root commit.
27+ repeated string parent_ids = 2;
28+ // Author is who wrote the change.
29+ Signature author = 3;
30+ // Committer is who recorded the commit.
31+ Signature committer = 4;
32+ // Summary is the first line of the message, without the line break.
33+ string summary = 5;
34+ // Message is the full commit message, including the summary. Text that is
35+ // not valid UTF-8 is converted with the replacement character.
36+ string message = 6;
37+}

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