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 generated file: collapsed
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
@@ -0,0 +1,673 @@
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/token.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+// Scope is a permission carried by a token. The API knows scopes only; any
28+// grouping into presets is a client concern.
29+type Scope int32
30+
31+const (
32+ // Not set. Rejected in requests, never returned by the server.
33+ Scope_SCOPE_UNSPECIFIED Scope = 0
34+ // SCOPE_REPO_READ allows reading private repositories (public ones need no
35+ // token). Wire name: repo:read.
36+ Scope_SCOPE_REPO_READ Scope = 1
37+ // SCOPE_REPO_WRITE allows pushing and creating repositories. Wire name:
38+ // repo:write.
39+ Scope_SCOPE_REPO_WRITE Scope = 2
40+ // SCOPE_REPO_ADMIN allows deleting a repository and changing its visibility,
41+ // description or default branch. Wire name: repo:admin.
42+ Scope_SCOPE_REPO_ADMIN Scope = 3
43+ // SCOPE_MR_WRITE allows creating, commenting on and closing merge requests.
44+ // Wire name: mr:write.
45+ Scope_SCOPE_MR_WRITE Scope = 4
46+ // SCOPE_MR_MERGE allows merging merge requests. Wire name: mr:merge.
47+ Scope_SCOPE_MR_MERGE Scope = 5
48+ // SCOPE_CI_WRITE allows re-running or cancelling CI runs. Wire name:
49+ // ci:write.
50+ Scope_SCOPE_CI_WRITE Scope = 6
51+ // SCOPE_SECRETS_WRITE allows setting and deleting secrets and variables.
52+ // Wire name: secrets:write.
53+ Scope_SCOPE_SECRETS_WRITE Scope = 7
54+ // SCOPE_RUNNER_REGISTER allows registering a CI runner. Wire name:
55+ // runner:register.
56+ Scope_SCOPE_RUNNER_REGISTER Scope = 8
57+ // SCOPE_TOKEN_ADMIN allows creating, listing and revoking tokens. Wire name:
58+ // token:admin.
59+ Scope_SCOPE_TOKEN_ADMIN Scope = 9
60+)
61+
62+// Enum value maps for Scope.
63+var (
64+ Scope_name = map[int32]string{
65+ 0: "SCOPE_UNSPECIFIED",
66+ 1: "SCOPE_REPO_READ",
67+ 2: "SCOPE_REPO_WRITE",
68+ 3: "SCOPE_REPO_ADMIN",
69+ 4: "SCOPE_MR_WRITE",
70+ 5: "SCOPE_MR_MERGE",
71+ 6: "SCOPE_CI_WRITE",
72+ 7: "SCOPE_SECRETS_WRITE",
73+ 8: "SCOPE_RUNNER_REGISTER",
74+ 9: "SCOPE_TOKEN_ADMIN",
75+ }
76+ Scope_value = map[string]int32{
77+ "SCOPE_UNSPECIFIED": 0,
78+ "SCOPE_REPO_READ": 1,
79+ "SCOPE_REPO_WRITE": 2,
80+ "SCOPE_REPO_ADMIN": 3,
81+ "SCOPE_MR_WRITE": 4,
82+ "SCOPE_MR_MERGE": 5,
83+ "SCOPE_CI_WRITE": 6,
84+ "SCOPE_SECRETS_WRITE": 7,
85+ "SCOPE_RUNNER_REGISTER": 8,
86+ "SCOPE_TOKEN_ADMIN": 9,
87+ }
88+)
89+
90+func (x Scope) Enum() *Scope {
91+ p := new(Scope)
92+ *p = x
93+ return p
94+}
95+
96+func (x Scope) String() string {
97+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
98+}
99+
100+func (Scope) Descriptor() protoreflect.EnumDescriptor {
101+ return file_ermite_token_proto_enumTypes[0].Descriptor()
102+}
103+
104+func (Scope) Type() protoreflect.EnumType {
105+ return &file_ermite_token_proto_enumTypes[0]
106+}
107+
108+func (x Scope) Number() protoreflect.EnumNumber {
109+ return protoreflect.EnumNumber(x)
110+}
111+
112+// Deprecated: Use Scope.Descriptor instead.
113+func (Scope) EnumDescriptor() ([]byte, []int) {
114+ return file_ermite_token_proto_rawDescGZIP(), []int{0}
115+}
116+
117+// Token describes a stored token. It never contains the secret nor its hash.
118+type Token struct {
119+ state protoimpl.MessageState `protogen:"open.v1"`
120+ // Id identifies the token in ListTokens and RevokeToken. It is not the
121+ // secret and cannot be used to authenticate.
122+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
123+ // Label is a human-readable name chosen at creation.
124+ Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty"`
125+ // Scopes are the permissions of the token. Never empty.
126+ Scopes []Scope `protobuf:"varint,3,rep,packed,name=scopes,proto3,enum=ermite.Scope" json:"scopes,omitempty"`
127+ // Repos restricts the token to these repository names. Empty means every
128+ // repository.
129+ Repos []string `protobuf:"bytes,4,rep,name=repos,proto3" json:"repos,omitempty"`
130+ // ExpiresAt is when the token stops working. Unset means it never expires.
131+ ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
132+ // CreatedAt is when the token was created.
133+ CreatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
134+ // LastUsedAt is when the token last authenticated a request. Unset if never
135+ // used. It may lag behind the real time by a short delay.
136+ LastUsedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=last_used_at,json=lastUsedAt,proto3" json:"last_used_at,omitempty"`
137+ unknownFields protoimpl.UnknownFields
138+ sizeCache protoimpl.SizeCache
139+}
140+
141+func (x *Token) Reset() {
142+ *x = Token{}
143+ mi := &file_ermite_token_proto_msgTypes[0]
144+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
145+ ms.StoreMessageInfo(mi)
146+}
147+
148+func (x *Token) String() string {
149+ return protoimpl.X.MessageStringOf(x)
150+}
151+
152+func (*Token) ProtoMessage() {}
153+
154+func (x *Token) ProtoReflect() protoreflect.Message {
155+ mi := &file_ermite_token_proto_msgTypes[0]
156+ if x != nil {
157+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
158+ if ms.LoadMessageInfo() == nil {
159+ ms.StoreMessageInfo(mi)
160+ }
161+ return ms
162+ }
163+ return mi.MessageOf(x)
164+}
165+
166+// Deprecated: Use Token.ProtoReflect.Descriptor instead.
167+func (*Token) Descriptor() ([]byte, []int) {
168+ return file_ermite_token_proto_rawDescGZIP(), []int{0}
169+}
170+
171+func (x *Token) GetId() string {
172+ if x != nil {
173+ return x.Id
174+ }
175+ return ""
176+}
177+
178+func (x *Token) GetLabel() string {
179+ if x != nil {
180+ return x.Label
181+ }
182+ return ""
183+}
184+
185+func (x *Token) GetScopes() []Scope {
186+ if x != nil {
187+ return x.Scopes
188+ }
189+ return nil
190+}
191+
192+func (x *Token) GetRepos() []string {
193+ if x != nil {
194+ return x.Repos
195+ }
196+ return nil
197+}
198+
199+func (x *Token) GetExpiresAt() *timestamppb.Timestamp {
200+ if x != nil {
201+ return x.ExpiresAt
202+ }
203+ return nil
204+}
205+
206+func (x *Token) GetCreatedAt() *timestamppb.Timestamp {
207+ if x != nil {
208+ return x.CreatedAt
209+ }
210+ return nil
211+}
212+
213+func (x *Token) GetLastUsedAt() *timestamppb.Timestamp {
214+ if x != nil {
215+ return x.LastUsedAt
216+ }
217+ return nil
218+}
219+
220+// CreateTokenRequest describes the token to create.
221+type CreateTokenRequest struct {
222+ state protoimpl.MessageState `protogen:"open.v1"`
223+ // Label is a human-readable name, for example "laptop". Not required to be
224+ // unique.
225+ Label string `protobuf:"bytes,1,opt,name=label,proto3" json:"label,omitempty"`
226+ // Scopes are the permissions to grant. At least one is required.
227+ Scopes []Scope `protobuf:"varint,2,rep,packed,name=scopes,proto3,enum=ermite.Scope" json:"scopes,omitempty"`
228+ // Repos restricts the token to these repository names. Empty means every
229+ // repository.
230+ Repos []string `protobuf:"bytes,3,rep,name=repos,proto3" json:"repos,omitempty"`
231+ // ExpiresAt is when the token stops working. Unset means no expiry. It must
232+ // be in the future.
233+ ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
234+ unknownFields protoimpl.UnknownFields
235+ sizeCache protoimpl.SizeCache
236+}
237+
238+func (x *CreateTokenRequest) Reset() {
239+ *x = CreateTokenRequest{}
240+ mi := &file_ermite_token_proto_msgTypes[1]
241+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
242+ ms.StoreMessageInfo(mi)
243+}
244+
245+func (x *CreateTokenRequest) String() string {
246+ return protoimpl.X.MessageStringOf(x)
247+}
248+
249+func (*CreateTokenRequest) ProtoMessage() {}
250+
251+func (x *CreateTokenRequest) ProtoReflect() protoreflect.Message {
252+ mi := &file_ermite_token_proto_msgTypes[1]
253+ if x != nil {
254+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
255+ if ms.LoadMessageInfo() == nil {
256+ ms.StoreMessageInfo(mi)
257+ }
258+ return ms
259+ }
260+ return mi.MessageOf(x)
261+}
262+
263+// Deprecated: Use CreateTokenRequest.ProtoReflect.Descriptor instead.
264+func (*CreateTokenRequest) Descriptor() ([]byte, []int) {
265+ return file_ermite_token_proto_rawDescGZIP(), []int{1}
266+}
267+
268+func (x *CreateTokenRequest) GetLabel() string {
269+ if x != nil {
270+ return x.Label
271+ }
272+ return ""
273+}
274+
275+func (x *CreateTokenRequest) GetScopes() []Scope {
276+ if x != nil {
277+ return x.Scopes
278+ }
279+ return nil
280+}
281+
282+func (x *CreateTokenRequest) GetRepos() []string {
283+ if x != nil {
284+ return x.Repos
285+ }
286+ return nil
287+}
288+
289+func (x *CreateTokenRequest) GetExpiresAt() *timestamppb.Timestamp {
290+ if x != nil {
291+ return x.ExpiresAt
292+ }
293+ return nil
294+}
295+
296+// CreateTokenResponse holds the new token and its secret.
297+type CreateTokenResponse struct {
298+ state protoimpl.MessageState `protogen:"open.v1"`
299+ // Token describes the created token.
300+ Token *Token `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
301+ // Secret is the bearer secret to present as "Authorization: Bearer <secret>".
302+ // It is returned once and never stored in clear text by the server.
303+ Secret string `protobuf:"bytes,2,opt,name=secret,proto3" json:"secret,omitempty"`
304+ unknownFields protoimpl.UnknownFields
305+ sizeCache protoimpl.SizeCache
306+}
307+
308+func (x *CreateTokenResponse) Reset() {
309+ *x = CreateTokenResponse{}
310+ mi := &file_ermite_token_proto_msgTypes[2]
311+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
312+ ms.StoreMessageInfo(mi)
313+}
314+
315+func (x *CreateTokenResponse) String() string {
316+ return protoimpl.X.MessageStringOf(x)
317+}
318+
319+func (*CreateTokenResponse) ProtoMessage() {}
320+
321+func (x *CreateTokenResponse) ProtoReflect() protoreflect.Message {
322+ mi := &file_ermite_token_proto_msgTypes[2]
323+ if x != nil {
324+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
325+ if ms.LoadMessageInfo() == nil {
326+ ms.StoreMessageInfo(mi)
327+ }
328+ return ms
329+ }
330+ return mi.MessageOf(x)
331+}
332+
333+// Deprecated: Use CreateTokenResponse.ProtoReflect.Descriptor instead.
334+func (*CreateTokenResponse) Descriptor() ([]byte, []int) {
335+ return file_ermite_token_proto_rawDescGZIP(), []int{2}
336+}
337+
338+func (x *CreateTokenResponse) GetToken() *Token {
339+ if x != nil {
340+ return x.Token
341+ }
342+ return nil
343+}
344+
345+func (x *CreateTokenResponse) GetSecret() string {
346+ if x != nil {
347+ return x.Secret
348+ }
349+ return ""
350+}
351+
352+// ListTokensRequest selects a page of tokens.
353+type ListTokensRequest struct {
354+ state protoimpl.MessageState `protogen:"open.v1"`
355+ // PageSize is the maximum number of tokens to return. 0 means the server
356+ // default; values above the server maximum are lowered to it.
357+ PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
358+ // PageToken is the next_page_token of a previous response. Empty for the
359+ // first page.
360+ PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"`
361+ unknownFields protoimpl.UnknownFields
362+ sizeCache protoimpl.SizeCache
363+}
364+
365+func (x *ListTokensRequest) Reset() {
366+ *x = ListTokensRequest{}
367+ mi := &file_ermite_token_proto_msgTypes[3]
368+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
369+ ms.StoreMessageInfo(mi)
370+}
371+
372+func (x *ListTokensRequest) String() string {
373+ return protoimpl.X.MessageStringOf(x)
374+}
375+
376+func (*ListTokensRequest) ProtoMessage() {}
377+
378+func (x *ListTokensRequest) ProtoReflect() protoreflect.Message {
379+ mi := &file_ermite_token_proto_msgTypes[3]
380+ if x != nil {
381+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
382+ if ms.LoadMessageInfo() == nil {
383+ ms.StoreMessageInfo(mi)
384+ }
385+ return ms
386+ }
387+ return mi.MessageOf(x)
388+}
389+
390+// Deprecated: Use ListTokensRequest.ProtoReflect.Descriptor instead.
391+func (*ListTokensRequest) Descriptor() ([]byte, []int) {
392+ return file_ermite_token_proto_rawDescGZIP(), []int{3}
393+}
394+
395+func (x *ListTokensRequest) GetPageSize() int32 {
396+ if x != nil {
397+ return x.PageSize
398+ }
399+ return 0
400+}
401+
402+func (x *ListTokensRequest) GetPageToken() string {
403+ if x != nil {
404+ return x.PageToken
405+ }
406+ return ""
407+}
408+
409+// ListTokensResponse is one page of tokens.
410+type ListTokensResponse struct {
411+ state protoimpl.MessageState `protogen:"open.v1"`
412+ // Tokens are the tokens of this page, newest first.
413+ Tokens []*Token `protobuf:"bytes,1,rep,name=tokens,proto3" json:"tokens,omitempty"`
414+ // NextPageToken is passed as page_token to get the next page. Empty when
415+ // there is no next page.
416+ NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"`
417+ unknownFields protoimpl.UnknownFields
418+ sizeCache protoimpl.SizeCache
419+}
420+
421+func (x *ListTokensResponse) Reset() {
422+ *x = ListTokensResponse{}
423+ mi := &file_ermite_token_proto_msgTypes[4]
424+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
425+ ms.StoreMessageInfo(mi)
426+}
427+
428+func (x *ListTokensResponse) String() string {
429+ return protoimpl.X.MessageStringOf(x)
430+}
431+
432+func (*ListTokensResponse) ProtoMessage() {}
433+
434+func (x *ListTokensResponse) ProtoReflect() protoreflect.Message {
435+ mi := &file_ermite_token_proto_msgTypes[4]
436+ if x != nil {
437+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
438+ if ms.LoadMessageInfo() == nil {
439+ ms.StoreMessageInfo(mi)
440+ }
441+ return ms
442+ }
443+ return mi.MessageOf(x)
444+}
445+
446+// Deprecated: Use ListTokensResponse.ProtoReflect.Descriptor instead.
447+func (*ListTokensResponse) Descriptor() ([]byte, []int) {
448+ return file_ermite_token_proto_rawDescGZIP(), []int{4}
449+}
450+
451+func (x *ListTokensResponse) GetTokens() []*Token {
452+ if x != nil {
453+ return x.Tokens
454+ }
455+ return nil
456+}
457+
458+func (x *ListTokensResponse) GetNextPageToken() string {
459+ if x != nil {
460+ return x.NextPageToken
461+ }
462+ return ""
463+}
464+
465+// RevokeTokenRequest names the token to revoke.
466+type RevokeTokenRequest struct {
467+ state protoimpl.MessageState `protogen:"open.v1"`
468+ // Id is the Token.id to revoke.
469+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
470+ unknownFields protoimpl.UnknownFields
471+ sizeCache protoimpl.SizeCache
472+}
473+
474+func (x *RevokeTokenRequest) Reset() {
475+ *x = RevokeTokenRequest{}
476+ mi := &file_ermite_token_proto_msgTypes[5]
477+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
478+ ms.StoreMessageInfo(mi)
479+}
480+
481+func (x *RevokeTokenRequest) String() string {
482+ return protoimpl.X.MessageStringOf(x)
483+}
484+
485+func (*RevokeTokenRequest) ProtoMessage() {}
486+
487+func (x *RevokeTokenRequest) ProtoReflect() protoreflect.Message {
488+ mi := &file_ermite_token_proto_msgTypes[5]
489+ if x != nil {
490+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
491+ if ms.LoadMessageInfo() == nil {
492+ ms.StoreMessageInfo(mi)
493+ }
494+ return ms
495+ }
496+ return mi.MessageOf(x)
497+}
498+
499+// Deprecated: Use RevokeTokenRequest.ProtoReflect.Descriptor instead.
500+func (*RevokeTokenRequest) Descriptor() ([]byte, []int) {
501+ return file_ermite_token_proto_rawDescGZIP(), []int{5}
502+}
503+
504+func (x *RevokeTokenRequest) GetId() string {
505+ if x != nil {
506+ return x.Id
507+ }
508+ return ""
509+}
510+
511+// RevokeTokenResponse is empty.
512+type RevokeTokenResponse struct {
513+ state protoimpl.MessageState `protogen:"open.v1"`
514+ unknownFields protoimpl.UnknownFields
515+ sizeCache protoimpl.SizeCache
516+}
517+
518+func (x *RevokeTokenResponse) Reset() {
519+ *x = RevokeTokenResponse{}
520+ mi := &file_ermite_token_proto_msgTypes[6]
521+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
522+ ms.StoreMessageInfo(mi)
523+}
524+
525+func (x *RevokeTokenResponse) String() string {
526+ return protoimpl.X.MessageStringOf(x)
527+}
528+
529+func (*RevokeTokenResponse) ProtoMessage() {}
530+
531+func (x *RevokeTokenResponse) ProtoReflect() protoreflect.Message {
532+ mi := &file_ermite_token_proto_msgTypes[6]
533+ if x != nil {
534+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
535+ if ms.LoadMessageInfo() == nil {
536+ ms.StoreMessageInfo(mi)
537+ }
538+ return ms
539+ }
540+ return mi.MessageOf(x)
541+}
542+
543+// Deprecated: Use RevokeTokenResponse.ProtoReflect.Descriptor instead.
544+func (*RevokeTokenResponse) Descriptor() ([]byte, []int) {
545+ return file_ermite_token_proto_rawDescGZIP(), []int{6}
546+}
547+
548+var File_ermite_token_proto protoreflect.FileDescriptor
549+
550+const file_ermite_token_proto_rawDesc = "" +
551+ "\n" +
552+ "\x12ermite/token.proto\x12\x06ermite\x1a\x1fgoogle/protobuf/timestamp.proto\"\x9e\x02\n" +
553+ "\x05Token\x12\x0e\n" +
554+ "\x02id\x18\x01 \x01(\tR\x02id\x12\x14\n" +
555+ "\x05label\x18\x02 \x01(\tR\x05label\x12%\n" +
556+ "\x06scopes\x18\x03 \x03(\x0e2\r.ermite.ScopeR\x06scopes\x12\x14\n" +
557+ "\x05repos\x18\x04 \x03(\tR\x05repos\x129\n" +
558+ "\n" +
559+ "expires_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\x129\n" +
560+ "\n" +
561+ "created_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x12<\n" +
562+ "\flast_used_at\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\n" +
563+ "lastUsedAt\"\xa2\x01\n" +
564+ "\x12CreateTokenRequest\x12\x14\n" +
565+ "\x05label\x18\x01 \x01(\tR\x05label\x12%\n" +
566+ "\x06scopes\x18\x02 \x03(\x0e2\r.ermite.ScopeR\x06scopes\x12\x14\n" +
567+ "\x05repos\x18\x03 \x03(\tR\x05repos\x129\n" +
568+ "\n" +
569+ "expires_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\"R\n" +
570+ "\x13CreateTokenResponse\x12#\n" +
571+ "\x05token\x18\x01 \x01(\v2\r.ermite.TokenR\x05token\x12\x16\n" +
572+ "\x06secret\x18\x02 \x01(\tR\x06secret\"O\n" +
573+ "\x11ListTokensRequest\x12\x1b\n" +
574+ "\tpage_size\x18\x01 \x01(\x05R\bpageSize\x12\x1d\n" +
575+ "\n" +
576+ "page_token\x18\x02 \x01(\tR\tpageToken\"c\n" +
577+ "\x12ListTokensResponse\x12%\n" +
578+ "\x06tokens\x18\x01 \x03(\v2\r.ermite.TokenR\x06tokens\x12&\n" +
579+ "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"$\n" +
580+ "\x12RevokeTokenRequest\x12\x0e\n" +
581+ "\x02id\x18\x01 \x01(\tR\x02id\"\x15\n" +
582+ "\x13RevokeTokenResponse*\xe6\x01\n" +
583+ "\x05Scope\x12\x15\n" +
584+ "\x11SCOPE_UNSPECIFIED\x10\x00\x12\x13\n" +
585+ "\x0fSCOPE_REPO_READ\x10\x01\x12\x14\n" +
586+ "\x10SCOPE_REPO_WRITE\x10\x02\x12\x14\n" +
587+ "\x10SCOPE_REPO_ADMIN\x10\x03\x12\x12\n" +
588+ "\x0eSCOPE_MR_WRITE\x10\x04\x12\x12\n" +
589+ "\x0eSCOPE_MR_MERGE\x10\x05\x12\x12\n" +
590+ "\x0eSCOPE_CI_WRITE\x10\x06\x12\x17\n" +
591+ "\x13SCOPE_SECRETS_WRITE\x10\a\x12\x19\n" +
592+ "\x15SCOPE_RUNNER_REGISTER\x10\b\x12\x15\n" +
593+ "\x11SCOPE_TOKEN_ADMIN\x10\t2\xe3\x01\n" +
594+ "\fTokenService\x12F\n" +
595+ "\vCreateToken\x12\x1a.ermite.CreateTokenRequest\x1a\x1b.ermite.CreateTokenResponse\x12C\n" +
596+ "\n" +
597+ "ListTokens\x12\x19.ermite.ListTokensRequest\x1a\x1a.ermite.ListTokensResponse\x12F\n" +
598+ "\vRevokeToken\x12\x1a.ermite.RevokeTokenRequest\x1a\x1b.ermite.RevokeTokenResponseBq\n" +
599+ "\n" +
600+ "com.ermiteB\n" +
601+ "TokenProtoP\x01Z\x1fbnema.dev/ermite-api/gen/ermite\xa2\x02\x03EXX\xaa\x02\x06Ermite\xca\x02\x06Ermite\xe2\x02\x12Ermite\\GPBMetadata\xea\x02\x06Ermiteb\x06proto3"
602+
603+var (
604+ file_ermite_token_proto_rawDescOnce sync.Once
605+ file_ermite_token_proto_rawDescData []byte
606+)
607+
608+func file_ermite_token_proto_rawDescGZIP() []byte {
609+ file_ermite_token_proto_rawDescOnce.Do(func() {
610+ file_ermite_token_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ermite_token_proto_rawDesc), len(file_ermite_token_proto_rawDesc)))
611+ })
612+ return file_ermite_token_proto_rawDescData
613+}
614+
615+var file_ermite_token_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
616+var file_ermite_token_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
617+var file_ermite_token_proto_goTypes = []any{
618+ (Scope)(0), // 0: ermite.Scope
619+ (*Token)(nil), // 1: ermite.Token
620+ (*CreateTokenRequest)(nil), // 2: ermite.CreateTokenRequest
621+ (*CreateTokenResponse)(nil), // 3: ermite.CreateTokenResponse
622+ (*ListTokensRequest)(nil), // 4: ermite.ListTokensRequest
623+ (*ListTokensResponse)(nil), // 5: ermite.ListTokensResponse
624+ (*RevokeTokenRequest)(nil), // 6: ermite.RevokeTokenRequest
625+ (*RevokeTokenResponse)(nil), // 7: ermite.RevokeTokenResponse
626+ (*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp
627+}
628+var file_ermite_token_proto_depIdxs = []int32{
629+ 0, // 0: ermite.Token.scopes:type_name -> ermite.Scope
630+ 8, // 1: ermite.Token.expires_at:type_name -> google.protobuf.Timestamp
631+ 8, // 2: ermite.Token.created_at:type_name -> google.protobuf.Timestamp
632+ 8, // 3: ermite.Token.last_used_at:type_name -> google.protobuf.Timestamp
633+ 0, // 4: ermite.CreateTokenRequest.scopes:type_name -> ermite.Scope
634+ 8, // 5: ermite.CreateTokenRequest.expires_at:type_name -> google.protobuf.Timestamp
635+ 1, // 6: ermite.CreateTokenResponse.token:type_name -> ermite.Token
636+ 1, // 7: ermite.ListTokensResponse.tokens:type_name -> ermite.Token
637+ 2, // 8: ermite.TokenService.CreateToken:input_type -> ermite.CreateTokenRequest
638+ 4, // 9: ermite.TokenService.ListTokens:input_type -> ermite.ListTokensRequest
639+ 6, // 10: ermite.TokenService.RevokeToken:input_type -> ermite.RevokeTokenRequest
640+ 3, // 11: ermite.TokenService.CreateToken:output_type -> ermite.CreateTokenResponse
641+ 5, // 12: ermite.TokenService.ListTokens:output_type -> ermite.ListTokensResponse
642+ 7, // 13: ermite.TokenService.RevokeToken:output_type -> ermite.RevokeTokenResponse
643+ 11, // [11:14] is the sub-list for method output_type
644+ 8, // [8:11] is the sub-list for method input_type
645+ 8, // [8:8] is the sub-list for extension type_name
646+ 8, // [8:8] is the sub-list for extension extendee
647+ 0, // [0:8] is the sub-list for field type_name
648+}
649+
650+func init() { file_ermite_token_proto_init() }
651+func file_ermite_token_proto_init() {
652+ if File_ermite_token_proto != nil {
653+ return
654+ }
655+ type x struct{}
656+ out := protoimpl.TypeBuilder{
657+ File: protoimpl.DescBuilder{
658+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
659+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_token_proto_rawDesc), len(file_ermite_token_proto_rawDesc)),
660+ NumEnums: 1,
661+ NumMessages: 7,
662+ NumExtensions: 0,
663+ NumServices: 1,
664+ },
665+ GoTypes: file_ermite_token_proto_goTypes,
666+ DependencyIndexes: file_ermite_token_proto_depIdxs,
667+ EnumInfos: file_ermite_token_proto_enumTypes,
668+ MessageInfos: file_ermite_token_proto_msgTypes,
669+ }.Build()
670+ File_ermite_token_proto = out.File
671+ file_ermite_token_proto_goTypes = nil
672+ file_ermite_token_proto_depIdxs = nil
673+}
@@ -0,0 +1,673 @@
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/token.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+// Scope is a permission carried by a token. The API knows scopes only; any
28+// grouping into presets is a client concern.
29+type Scope int32
30+
31+const (
32+ // Not set. Rejected in requests, never returned by the server.
33+ Scope_SCOPE_UNSPECIFIED Scope = 0
34+ // SCOPE_REPO_READ allows reading private repositories (public ones need no
35+ // token). Wire name: repo:read.
36+ Scope_SCOPE_REPO_READ Scope = 1
37+ // SCOPE_REPO_WRITE allows pushing and creating repositories. Wire name:
38+ // repo:write.
39+ Scope_SCOPE_REPO_WRITE Scope = 2
40+ // SCOPE_REPO_ADMIN allows deleting a repository and changing its visibility,
41+ // description or default branch. Wire name: repo:admin.
42+ Scope_SCOPE_REPO_ADMIN Scope = 3
43+ // SCOPE_MR_WRITE allows creating, commenting on and closing merge requests.
44+ // Wire name: mr:write.
45+ Scope_SCOPE_MR_WRITE Scope = 4
46+ // SCOPE_MR_MERGE allows merging merge requests. Wire name: mr:merge.
47+ Scope_SCOPE_MR_MERGE Scope = 5
48+ // SCOPE_CI_WRITE allows re-running or cancelling CI runs. Wire name:
49+ // ci:write.
50+ Scope_SCOPE_CI_WRITE Scope = 6
51+ // SCOPE_SECRETS_WRITE allows setting and deleting secrets and variables.
52+ // Wire name: secrets:write.
53+ Scope_SCOPE_SECRETS_WRITE Scope = 7
54+ // SCOPE_RUNNER_REGISTER allows registering a CI runner. Wire name:
55+ // runner:register.
56+ Scope_SCOPE_RUNNER_REGISTER Scope = 8
57+ // SCOPE_TOKEN_ADMIN allows creating, listing and revoking tokens. Wire name:
58+ // token:admin.
59+ Scope_SCOPE_TOKEN_ADMIN Scope = 9
60+)
61+
62+// Enum value maps for Scope.
63+var (
64+ Scope_name = map[int32]string{
65+ 0: "SCOPE_UNSPECIFIED",
66+ 1: "SCOPE_REPO_READ",
67+ 2: "SCOPE_REPO_WRITE",
68+ 3: "SCOPE_REPO_ADMIN",
69+ 4: "SCOPE_MR_WRITE",
70+ 5: "SCOPE_MR_MERGE",
71+ 6: "SCOPE_CI_WRITE",
72+ 7: "SCOPE_SECRETS_WRITE",
73+ 8: "SCOPE_RUNNER_REGISTER",
74+ 9: "SCOPE_TOKEN_ADMIN",
75+ }
76+ Scope_value = map[string]int32{
77+ "SCOPE_UNSPECIFIED": 0,
78+ "SCOPE_REPO_READ": 1,
79+ "SCOPE_REPO_WRITE": 2,
80+ "SCOPE_REPO_ADMIN": 3,
81+ "SCOPE_MR_WRITE": 4,
82+ "SCOPE_MR_MERGE": 5,
83+ "SCOPE_CI_WRITE": 6,
84+ "SCOPE_SECRETS_WRITE": 7,
85+ "SCOPE_RUNNER_REGISTER": 8,
86+ "SCOPE_TOKEN_ADMIN": 9,
87+ }
88+)
89+
90+func (x Scope) Enum() *Scope {
91+ p := new(Scope)
92+ *p = x
93+ return p
94+}
95+
96+func (x Scope) String() string {
97+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
98+}
99+
100+func (Scope) Descriptor() protoreflect.EnumDescriptor {
101+ return file_ermite_token_proto_enumTypes[0].Descriptor()
102+}
103+
104+func (Scope) Type() protoreflect.EnumType {
105+ return &file_ermite_token_proto_enumTypes[0]
106+}
107+
108+func (x Scope) Number() protoreflect.EnumNumber {
109+ return protoreflect.EnumNumber(x)
110+}
111+
112+// Deprecated: Use Scope.Descriptor instead.
113+func (Scope) EnumDescriptor() ([]byte, []int) {
114+ return file_ermite_token_proto_rawDescGZIP(), []int{0}
115+}
116+
117+// Token describes a stored token. It never contains the secret nor its hash.
118+type Token struct {
119+ state protoimpl.MessageState `protogen:"open.v1"`
120+ // Id identifies the token in ListTokens and RevokeToken. It is not the
121+ // secret and cannot be used to authenticate.
122+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
123+ // Label is a human-readable name chosen at creation.
124+ Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty"`
125+ // Scopes are the permissions of the token. Never empty.
126+ Scopes []Scope `protobuf:"varint,3,rep,packed,name=scopes,proto3,enum=ermite.Scope" json:"scopes,omitempty"`
127+ // Repos restricts the token to these repository names. Empty means every
128+ // repository.
129+ Repos []string `protobuf:"bytes,4,rep,name=repos,proto3" json:"repos,omitempty"`
130+ // ExpiresAt is when the token stops working. Unset means it never expires.
131+ ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
132+ // CreatedAt is when the token was created.
133+ CreatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
134+ // LastUsedAt is when the token last authenticated a request. Unset if never
135+ // used. It may lag behind the real time by a short delay.
136+ LastUsedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=last_used_at,json=lastUsedAt,proto3" json:"last_used_at,omitempty"`
137+ unknownFields protoimpl.UnknownFields
138+ sizeCache protoimpl.SizeCache
139+}
140+
141+func (x *Token) Reset() {
142+ *x = Token{}
143+ mi := &file_ermite_token_proto_msgTypes[0]
144+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
145+ ms.StoreMessageInfo(mi)
146+}
147+
148+func (x *Token) String() string {
149+ return protoimpl.X.MessageStringOf(x)
150+}
151+
152+func (*Token) ProtoMessage() {}
153+
154+func (x *Token) ProtoReflect() protoreflect.Message {
155+ mi := &file_ermite_token_proto_msgTypes[0]
156+ if x != nil {
157+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
158+ if ms.LoadMessageInfo() == nil {
159+ ms.StoreMessageInfo(mi)
160+ }
161+ return ms
162+ }
163+ return mi.MessageOf(x)
164+}
165+
166+// Deprecated: Use Token.ProtoReflect.Descriptor instead.
167+func (*Token) Descriptor() ([]byte, []int) {
168+ return file_ermite_token_proto_rawDescGZIP(), []int{0}
169+}
170+
171+func (x *Token) GetId() string {
172+ if x != nil {
173+ return x.Id
174+ }
175+ return ""
176+}
177+
178+func (x *Token) GetLabel() string {
179+ if x != nil {
180+ return x.Label
181+ }
182+ return ""
183+}
184+
185+func (x *Token) GetScopes() []Scope {
186+ if x != nil {
187+ return x.Scopes
188+ }
189+ return nil
190+}
191+
192+func (x *Token) GetRepos() []string {
193+ if x != nil {
194+ return x.Repos
195+ }
196+ return nil
197+}
198+
199+func (x *Token) GetExpiresAt() *timestamppb.Timestamp {
200+ if x != nil {
201+ return x.ExpiresAt
202+ }
203+ return nil
204+}
205+
206+func (x *Token) GetCreatedAt() *timestamppb.Timestamp {
207+ if x != nil {
208+ return x.CreatedAt
209+ }
210+ return nil
211+}
212+
213+func (x *Token) GetLastUsedAt() *timestamppb.Timestamp {
214+ if x != nil {
215+ return x.LastUsedAt
216+ }
217+ return nil
218+}
219+
220+// CreateTokenRequest describes the token to create.
221+type CreateTokenRequest struct {
222+ state protoimpl.MessageState `protogen:"open.v1"`
223+ // Label is a human-readable name, for example "laptop". Not required to be
224+ // unique.
225+ Label string `protobuf:"bytes,1,opt,name=label,proto3" json:"label,omitempty"`
226+ // Scopes are the permissions to grant. At least one is required.
227+ Scopes []Scope `protobuf:"varint,2,rep,packed,name=scopes,proto3,enum=ermite.Scope" json:"scopes,omitempty"`
228+ // Repos restricts the token to these repository names. Empty means every
229+ // repository.
230+ Repos []string `protobuf:"bytes,3,rep,name=repos,proto3" json:"repos,omitempty"`
231+ // ExpiresAt is when the token stops working. Unset means no expiry. It must
232+ // be in the future.
233+ ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
234+ unknownFields protoimpl.UnknownFields
235+ sizeCache protoimpl.SizeCache
236+}
237+
238+func (x *CreateTokenRequest) Reset() {
239+ *x = CreateTokenRequest{}
240+ mi := &file_ermite_token_proto_msgTypes[1]
241+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
242+ ms.StoreMessageInfo(mi)
243+}
244+
245+func (x *CreateTokenRequest) String() string {
246+ return protoimpl.X.MessageStringOf(x)
247+}
248+
249+func (*CreateTokenRequest) ProtoMessage() {}
250+
251+func (x *CreateTokenRequest) ProtoReflect() protoreflect.Message {
252+ mi := &file_ermite_token_proto_msgTypes[1]
253+ if x != nil {
254+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
255+ if ms.LoadMessageInfo() == nil {
256+ ms.StoreMessageInfo(mi)
257+ }
258+ return ms
259+ }
260+ return mi.MessageOf(x)
261+}
262+
263+// Deprecated: Use CreateTokenRequest.ProtoReflect.Descriptor instead.
264+func (*CreateTokenRequest) Descriptor() ([]byte, []int) {
265+ return file_ermite_token_proto_rawDescGZIP(), []int{1}
266+}
267+
268+func (x *CreateTokenRequest) GetLabel() string {
269+ if x != nil {
270+ return x.Label
271+ }
272+ return ""
273+}
274+
275+func (x *CreateTokenRequest) GetScopes() []Scope {
276+ if x != nil {
277+ return x.Scopes
278+ }
279+ return nil
280+}
281+
282+func (x *CreateTokenRequest) GetRepos() []string {
283+ if x != nil {
284+ return x.Repos
285+ }
286+ return nil
287+}
288+
289+func (x *CreateTokenRequest) GetExpiresAt() *timestamppb.Timestamp {
290+ if x != nil {
291+ return x.ExpiresAt
292+ }
293+ return nil
294+}
295+
296+// CreateTokenResponse holds the new token and its secret.
297+type CreateTokenResponse struct {
298+ state protoimpl.MessageState `protogen:"open.v1"`
299+ // Token describes the created token.
300+ Token *Token `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
301+ // Secret is the bearer secret to present as "Authorization: Bearer <secret>".
302+ // It is returned once and never stored in clear text by the server.
303+ Secret string `protobuf:"bytes,2,opt,name=secret,proto3" json:"secret,omitempty"`
304+ unknownFields protoimpl.UnknownFields
305+ sizeCache protoimpl.SizeCache
306+}
307+
308+func (x *CreateTokenResponse) Reset() {
309+ *x = CreateTokenResponse{}
310+ mi := &file_ermite_token_proto_msgTypes[2]
311+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
312+ ms.StoreMessageInfo(mi)
313+}
314+
315+func (x *CreateTokenResponse) String() string {
316+ return protoimpl.X.MessageStringOf(x)
317+}
318+
319+func (*CreateTokenResponse) ProtoMessage() {}
320+
321+func (x *CreateTokenResponse) ProtoReflect() protoreflect.Message {
322+ mi := &file_ermite_token_proto_msgTypes[2]
323+ if x != nil {
324+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
325+ if ms.LoadMessageInfo() == nil {
326+ ms.StoreMessageInfo(mi)
327+ }
328+ return ms
329+ }
330+ return mi.MessageOf(x)
331+}
332+
333+// Deprecated: Use CreateTokenResponse.ProtoReflect.Descriptor instead.
334+func (*CreateTokenResponse) Descriptor() ([]byte, []int) {
335+ return file_ermite_token_proto_rawDescGZIP(), []int{2}
336+}
337+
338+func (x *CreateTokenResponse) GetToken() *Token {
339+ if x != nil {
340+ return x.Token
341+ }
342+ return nil
343+}
344+
345+func (x *CreateTokenResponse) GetSecret() string {
346+ if x != nil {
347+ return x.Secret
348+ }
349+ return ""
350+}
351+
352+// ListTokensRequest selects a page of tokens.
353+type ListTokensRequest struct {
354+ state protoimpl.MessageState `protogen:"open.v1"`
355+ // PageSize is the maximum number of tokens to return. 0 means the server
356+ // default; values above the server maximum are lowered to it.
357+ PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
358+ // PageToken is the next_page_token of a previous response. Empty for the
359+ // first page.
360+ PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"`
361+ unknownFields protoimpl.UnknownFields
362+ sizeCache protoimpl.SizeCache
363+}
364+
365+func (x *ListTokensRequest) Reset() {
366+ *x = ListTokensRequest{}
367+ mi := &file_ermite_token_proto_msgTypes[3]
368+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
369+ ms.StoreMessageInfo(mi)
370+}
371+
372+func (x *ListTokensRequest) String() string {
373+ return protoimpl.X.MessageStringOf(x)
374+}
375+
376+func (*ListTokensRequest) ProtoMessage() {}
377+
378+func (x *ListTokensRequest) ProtoReflect() protoreflect.Message {
379+ mi := &file_ermite_token_proto_msgTypes[3]
380+ if x != nil {
381+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
382+ if ms.LoadMessageInfo() == nil {
383+ ms.StoreMessageInfo(mi)
384+ }
385+ return ms
386+ }
387+ return mi.MessageOf(x)
388+}
389+
390+// Deprecated: Use ListTokensRequest.ProtoReflect.Descriptor instead.
391+func (*ListTokensRequest) Descriptor() ([]byte, []int) {
392+ return file_ermite_token_proto_rawDescGZIP(), []int{3}
393+}
394+
395+func (x *ListTokensRequest) GetPageSize() int32 {
396+ if x != nil {
397+ return x.PageSize
398+ }
399+ return 0
400+}
401+
402+func (x *ListTokensRequest) GetPageToken() string {
403+ if x != nil {
404+ return x.PageToken
405+ }
406+ return ""
407+}
408+
409+// ListTokensResponse is one page of tokens.
410+type ListTokensResponse struct {
411+ state protoimpl.MessageState `protogen:"open.v1"`
412+ // Tokens are the tokens of this page, newest first.
413+ Tokens []*Token `protobuf:"bytes,1,rep,name=tokens,proto3" json:"tokens,omitempty"`
414+ // NextPageToken is passed as page_token to get the next page. Empty when
415+ // there is no next page.
416+ NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"`
417+ unknownFields protoimpl.UnknownFields
418+ sizeCache protoimpl.SizeCache
419+}
420+
421+func (x *ListTokensResponse) Reset() {
422+ *x = ListTokensResponse{}
423+ mi := &file_ermite_token_proto_msgTypes[4]
424+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
425+ ms.StoreMessageInfo(mi)
426+}
427+
428+func (x *ListTokensResponse) String() string {
429+ return protoimpl.X.MessageStringOf(x)
430+}
431+
432+func (*ListTokensResponse) ProtoMessage() {}
433+
434+func (x *ListTokensResponse) ProtoReflect() protoreflect.Message {
435+ mi := &file_ermite_token_proto_msgTypes[4]
436+ if x != nil {
437+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
438+ if ms.LoadMessageInfo() == nil {
439+ ms.StoreMessageInfo(mi)
440+ }
441+ return ms
442+ }
443+ return mi.MessageOf(x)
444+}
445+
446+// Deprecated: Use ListTokensResponse.ProtoReflect.Descriptor instead.
447+func (*ListTokensResponse) Descriptor() ([]byte, []int) {
448+ return file_ermite_token_proto_rawDescGZIP(), []int{4}
449+}
450+
451+func (x *ListTokensResponse) GetTokens() []*Token {
452+ if x != nil {
453+ return x.Tokens
454+ }
455+ return nil
456+}
457+
458+func (x *ListTokensResponse) GetNextPageToken() string {
459+ if x != nil {
460+ return x.NextPageToken
461+ }
462+ return ""
463+}
464+
465+// RevokeTokenRequest names the token to revoke.
466+type RevokeTokenRequest struct {
467+ state protoimpl.MessageState `protogen:"open.v1"`
468+ // Id is the Token.id to revoke.
469+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
470+ unknownFields protoimpl.UnknownFields
471+ sizeCache protoimpl.SizeCache
472+}
473+
474+func (x *RevokeTokenRequest) Reset() {
475+ *x = RevokeTokenRequest{}
476+ mi := &file_ermite_token_proto_msgTypes[5]
477+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
478+ ms.StoreMessageInfo(mi)
479+}
480+
481+func (x *RevokeTokenRequest) String() string {
482+ return protoimpl.X.MessageStringOf(x)
483+}
484+
485+func (*RevokeTokenRequest) ProtoMessage() {}
486+
487+func (x *RevokeTokenRequest) ProtoReflect() protoreflect.Message {
488+ mi := &file_ermite_token_proto_msgTypes[5]
489+ if x != nil {
490+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
491+ if ms.LoadMessageInfo() == nil {
492+ ms.StoreMessageInfo(mi)
493+ }
494+ return ms
495+ }
496+ return mi.MessageOf(x)
497+}
498+
499+// Deprecated: Use RevokeTokenRequest.ProtoReflect.Descriptor instead.
500+func (*RevokeTokenRequest) Descriptor() ([]byte, []int) {
501+ return file_ermite_token_proto_rawDescGZIP(), []int{5}
502+}
503+
504+func (x *RevokeTokenRequest) GetId() string {
505+ if x != nil {
506+ return x.Id
507+ }
508+ return ""
509+}
510+
511+// RevokeTokenResponse is empty.
512+type RevokeTokenResponse struct {
513+ state protoimpl.MessageState `protogen:"open.v1"`
514+ unknownFields protoimpl.UnknownFields
515+ sizeCache protoimpl.SizeCache
516+}
517+
518+func (x *RevokeTokenResponse) Reset() {
519+ *x = RevokeTokenResponse{}
520+ mi := &file_ermite_token_proto_msgTypes[6]
521+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
522+ ms.StoreMessageInfo(mi)
523+}
524+
525+func (x *RevokeTokenResponse) String() string {
526+ return protoimpl.X.MessageStringOf(x)
527+}
528+
529+func (*RevokeTokenResponse) ProtoMessage() {}
530+
531+func (x *RevokeTokenResponse) ProtoReflect() protoreflect.Message {
532+ mi := &file_ermite_token_proto_msgTypes[6]
533+ if x != nil {
534+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
535+ if ms.LoadMessageInfo() == nil {
536+ ms.StoreMessageInfo(mi)
537+ }
538+ return ms
539+ }
540+ return mi.MessageOf(x)
541+}
542+
543+// Deprecated: Use RevokeTokenResponse.ProtoReflect.Descriptor instead.
544+func (*RevokeTokenResponse) Descriptor() ([]byte, []int) {
545+ return file_ermite_token_proto_rawDescGZIP(), []int{6}
546+}
547+
548+var File_ermite_token_proto protoreflect.FileDescriptor
549+
550+const file_ermite_token_proto_rawDesc = "" +
551+ "\n" +
552+ "\x12ermite/token.proto\x12\x06ermite\x1a\x1fgoogle/protobuf/timestamp.proto\"\x9e\x02\n" +
553+ "\x05Token\x12\x0e\n" +
554+ "\x02id\x18\x01 \x01(\tR\x02id\x12\x14\n" +
555+ "\x05label\x18\x02 \x01(\tR\x05label\x12%\n" +
556+ "\x06scopes\x18\x03 \x03(\x0e2\r.ermite.ScopeR\x06scopes\x12\x14\n" +
557+ "\x05repos\x18\x04 \x03(\tR\x05repos\x129\n" +
558+ "\n" +
559+ "expires_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\x129\n" +
560+ "\n" +
561+ "created_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x12<\n" +
562+ "\flast_used_at\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\n" +
563+ "lastUsedAt\"\xa2\x01\n" +
564+ "\x12CreateTokenRequest\x12\x14\n" +
565+ "\x05label\x18\x01 \x01(\tR\x05label\x12%\n" +
566+ "\x06scopes\x18\x02 \x03(\x0e2\r.ermite.ScopeR\x06scopes\x12\x14\n" +
567+ "\x05repos\x18\x03 \x03(\tR\x05repos\x129\n" +
568+ "\n" +
569+ "expires_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\"R\n" +
570+ "\x13CreateTokenResponse\x12#\n" +
571+ "\x05token\x18\x01 \x01(\v2\r.ermite.TokenR\x05token\x12\x16\n" +
572+ "\x06secret\x18\x02 \x01(\tR\x06secret\"O\n" +
573+ "\x11ListTokensRequest\x12\x1b\n" +
574+ "\tpage_size\x18\x01 \x01(\x05R\bpageSize\x12\x1d\n" +
575+ "\n" +
576+ "page_token\x18\x02 \x01(\tR\tpageToken\"c\n" +
577+ "\x12ListTokensResponse\x12%\n" +
578+ "\x06tokens\x18\x01 \x03(\v2\r.ermite.TokenR\x06tokens\x12&\n" +
579+ "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"$\n" +
580+ "\x12RevokeTokenRequest\x12\x0e\n" +
581+ "\x02id\x18\x01 \x01(\tR\x02id\"\x15\n" +
582+ "\x13RevokeTokenResponse*\xe6\x01\n" +
583+ "\x05Scope\x12\x15\n" +
584+ "\x11SCOPE_UNSPECIFIED\x10\x00\x12\x13\n" +
585+ "\x0fSCOPE_REPO_READ\x10\x01\x12\x14\n" +
586+ "\x10SCOPE_REPO_WRITE\x10\x02\x12\x14\n" +
587+ "\x10SCOPE_REPO_ADMIN\x10\x03\x12\x12\n" +
588+ "\x0eSCOPE_MR_WRITE\x10\x04\x12\x12\n" +
589+ "\x0eSCOPE_MR_MERGE\x10\x05\x12\x12\n" +
590+ "\x0eSCOPE_CI_WRITE\x10\x06\x12\x17\n" +
591+ "\x13SCOPE_SECRETS_WRITE\x10\a\x12\x19\n" +
592+ "\x15SCOPE_RUNNER_REGISTER\x10\b\x12\x15\n" +
593+ "\x11SCOPE_TOKEN_ADMIN\x10\t2\xe3\x01\n" +
594+ "\fTokenService\x12F\n" +
595+ "\vCreateToken\x12\x1a.ermite.CreateTokenRequest\x1a\x1b.ermite.CreateTokenResponse\x12C\n" +
596+ "\n" +
597+ "ListTokens\x12\x19.ermite.ListTokensRequest\x1a\x1a.ermite.ListTokensResponse\x12F\n" +
598+ "\vRevokeToken\x12\x1a.ermite.RevokeTokenRequest\x1a\x1b.ermite.RevokeTokenResponseBq\n" +
599+ "\n" +
600+ "com.ermiteB\n" +
601+ "TokenProtoP\x01Z\x1fbnema.dev/ermite-api/gen/ermite\xa2\x02\x03EXX\xaa\x02\x06Ermite\xca\x02\x06Ermite\xe2\x02\x12Ermite\\GPBMetadata\xea\x02\x06Ermiteb\x06proto3"
602+
603+var (
604+ file_ermite_token_proto_rawDescOnce sync.Once
605+ file_ermite_token_proto_rawDescData []byte
606+)
607+
608+func file_ermite_token_proto_rawDescGZIP() []byte {
609+ file_ermite_token_proto_rawDescOnce.Do(func() {
610+ file_ermite_token_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ermite_token_proto_rawDesc), len(file_ermite_token_proto_rawDesc)))
611+ })
612+ return file_ermite_token_proto_rawDescData
613+}
614+
615+var file_ermite_token_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
616+var file_ermite_token_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
617+var file_ermite_token_proto_goTypes = []any{
618+ (Scope)(0), // 0: ermite.Scope
619+ (*Token)(nil), // 1: ermite.Token
620+ (*CreateTokenRequest)(nil), // 2: ermite.CreateTokenRequest
621+ (*CreateTokenResponse)(nil), // 3: ermite.CreateTokenResponse
622+ (*ListTokensRequest)(nil), // 4: ermite.ListTokensRequest
623+ (*ListTokensResponse)(nil), // 5: ermite.ListTokensResponse
624+ (*RevokeTokenRequest)(nil), // 6: ermite.RevokeTokenRequest
625+ (*RevokeTokenResponse)(nil), // 7: ermite.RevokeTokenResponse
626+ (*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp
627+}
628+var file_ermite_token_proto_depIdxs = []int32{
629+ 0, // 0: ermite.Token.scopes:type_name -> ermite.Scope
630+ 8, // 1: ermite.Token.expires_at:type_name -> google.protobuf.Timestamp
631+ 8, // 2: ermite.Token.created_at:type_name -> google.protobuf.Timestamp
632+ 8, // 3: ermite.Token.last_used_at:type_name -> google.protobuf.Timestamp
633+ 0, // 4: ermite.CreateTokenRequest.scopes:type_name -> ermite.Scope
634+ 8, // 5: ermite.CreateTokenRequest.expires_at:type_name -> google.protobuf.Timestamp
635+ 1, // 6: ermite.CreateTokenResponse.token:type_name -> ermite.Token
636+ 1, // 7: ermite.ListTokensResponse.tokens:type_name -> ermite.Token
637+ 2, // 8: ermite.TokenService.CreateToken:input_type -> ermite.CreateTokenRequest
638+ 4, // 9: ermite.TokenService.ListTokens:input_type -> ermite.ListTokensRequest
639+ 6, // 10: ermite.TokenService.RevokeToken:input_type -> ermite.RevokeTokenRequest
640+ 3, // 11: ermite.TokenService.CreateToken:output_type -> ermite.CreateTokenResponse
641+ 5, // 12: ermite.TokenService.ListTokens:output_type -> ermite.ListTokensResponse
642+ 7, // 13: ermite.TokenService.RevokeToken:output_type -> ermite.RevokeTokenResponse
643+ 11, // [11:14] is the sub-list for method output_type
644+ 8, // [8:11] is the sub-list for method input_type
645+ 8, // [8:8] is the sub-list for extension type_name
646+ 8, // [8:8] is the sub-list for extension extendee
647+ 0, // [0:8] is the sub-list for field type_name
648+}
649+
650+func init() { file_ermite_token_proto_init() }
651+func file_ermite_token_proto_init() {
652+ if File_ermite_token_proto != nil {
653+ return
654+ }
655+ type x struct{}
656+ out := protoimpl.TypeBuilder{
657+ File: protoimpl.DescBuilder{
658+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
659+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_token_proto_rawDesc), len(file_ermite_token_proto_rawDesc)),
660+ NumEnums: 1,
661+ NumMessages: 7,
662+ NumExtensions: 0,
663+ NumServices: 1,
664+ },
665+ GoTypes: file_ermite_token_proto_goTypes,
666+ DependencyIndexes: file_ermite_token_proto_depIdxs,
667+ EnumInfos: file_ermite_token_proto_enumTypes,
668+ MessageInfos: file_ermite_token_proto_msgTypes,
669+ }.Build()
670+ File_ermite_token_proto = out.File
671+ file_ermite_token_proto_goTypes = nil
672+ file_ermite_token_proto_depIdxs = nil
673+}
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