detached acbb5c2

#6 feat(proto): mirror service and mirror events

state
merged · acbb5c2 · fast-forward
branch
feat/push-mirror → main
opened
bnema ·
updated
CommitSubjectAuthorDateSignatureStatus
acbb5c2feat(proto): report per-repository sync errors, drop the reason from mirror_failed and tidy mirror docsbriceGPG Verified
f5fe69adocs(proto): send mirror_failed once per incident, not on failing to pausedbriceGPG Verified
6cfb42dfeat(proto): add the mirror service and mirror eventsbriceGPG Verified

Checks 4

7 files · +2825 -41
4/4
M docs/services.md +32 -0
@@ -18,3 +18,35 @@
1818 | `EnvironmentService` | `CreateEnvironment`, `ListEnvironments`, `DeleteEnvironment` |
1919 | `ArtifactService` | `ListArtifacts`, `DownloadArtifact` (streamed, resumable), `DeleteArtifact` |
2020 | `JobArtifactService` | `CreateJobArtifact`, `UploadJobArtifactBlock` (client stream), `CommitJobArtifact`, `FinalizeJobArtifact`, `ListJobArtifacts`, `DownloadJobArtifact` (streamed, resumable), `DeleteJobArtifact`. Scoped to the run of the job token |
21+
22+## MirrorService
23+
24+Servers implementing push mirrors advertise `mirror` in `GetVersionResponse.features`.
25+No new token scope is introduced.
26+
27+| RPC | Access and behavior |
28+|---|---|
29+| `GetMirrorKey` | Non-anonymous caller; generates the public SSH key on first call. Without `ERMITE_SECRETS_KEY`, returns `failed_precondition` |
30+| `RotateMirrorKey` | Owner rights: admin token or a token with every scope and no repository or branch limits. Replaces the key immediately and returns the previous fingerprint |
31+| `GetMirrorConfig`, `SetMirrorConfig` | Owner rights. Set patches only present fields; invalid fields return `invalid_argument` with field violations |
32+| `SetRepoMirror` | `repo:admin` on the repository. Sets an override, even if global mirroring is disabled; empty cron and absent all-branches inherit the global values |
33+| `RemoveRepoMirror` | `repo:admin` on the repository. Removes the override |
34+| `SetRepoMirrorExcluded` | `repo:admin` on the repository. Opts out of or into the global template; a URL override wins over exclusion |
35+| `GetMirrorStatus` | Read access to the repository. Error `detail` is returned only with `repo:admin` |
36+| `ListMirrorStatus` | Owner rights |
37+| `SyncMirror` | `repo:admin` for `repo`, or owner rights for `all=true` (with empty `repo`). Returns per-repository results, including queued and no-op indicators and a per-repository `error`; `all=true` continues past failing repositories and skips disabled ones |
38+
39+Cron expressions have five fields, run in UTC and must be at least five minutes
40+apart. Mirrors push the default branch (or every branch with `all_branches`) and
41+tags. `force` lifts a pause, skips the no-op check and the 60-second manual sync
42+throttle; it **never** performs a Git force push. Pushes are atomic: one rejected
43+ref fails the whole push. After key rotation, add the new public key to remotes
44+and sync with `force` to recover from authentication failures. Configuration can
45+be saved without `ERMITE_SECRETS_KEY`, but pushes cannot run without it.
46+
47+`EventService.Watch` adds `mirror_failed` (field 23, `MirrorFailed`) when a mirror
48+starts failing (ok or disabled to failing or paused, once per incident), and `mirror_recovered`
49+(field 24, `MirrorRecovered`) when a failing or paused mirror succeeds again.
50+These are transition events, not events for every attempt. They are delivered on
51+`repo:<name>` and `home`, subject to repository visibility. Neither event carries
52+a URL, reason or error detail (`MirrorFailed` field 4, `reason`, was removed).
@@ -18,3 +18,35 @@
18 | `EnvironmentService` | `CreateEnvironment`, `ListEnvironments`, `DeleteEnvironment` |18 | `EnvironmentService` | `CreateEnvironment`, `ListEnvironments`, `DeleteEnvironment` |
19 | `ArtifactService` | `ListArtifacts`, `DownloadArtifact` (streamed, resumable), `DeleteArtifact` |19 | `ArtifactService` | `ListArtifacts`, `DownloadArtifact` (streamed, resumable), `DeleteArtifact` |
20 | `JobArtifactService` | `CreateJobArtifact`, `UploadJobArtifactBlock` (client stream), `CommitJobArtifact`, `FinalizeJobArtifact`, `ListJobArtifacts`, `DownloadJobArtifact` (streamed, resumable), `DeleteJobArtifact`. Scoped to the run of the job token |20 | `JobArtifactService` | `CreateJobArtifact`, `UploadJobArtifactBlock` (client stream), `CommitJobArtifact`, `FinalizeJobArtifact`, `ListJobArtifacts`, `DownloadJobArtifact` (streamed, resumable), `DeleteJobArtifact`. Scoped to the run of the job token |
21+
22+## MirrorService
23+
24+Servers implementing push mirrors advertise `mirror` in `GetVersionResponse.features`.
25+No new token scope is introduced.
26+
27+| RPC | Access and behavior |
28+|---|---|
29+| `GetMirrorKey` | Non-anonymous caller; generates the public SSH key on first call. Without `ERMITE_SECRETS_KEY`, returns `failed_precondition` |
30+| `RotateMirrorKey` | Owner rights: admin token or a token with every scope and no repository or branch limits. Replaces the key immediately and returns the previous fingerprint |
31+| `GetMirrorConfig`, `SetMirrorConfig` | Owner rights. Set patches only present fields; invalid fields return `invalid_argument` with field violations |
32+| `SetRepoMirror` | `repo:admin` on the repository. Sets an override, even if global mirroring is disabled; empty cron and absent all-branches inherit the global values |
33+| `RemoveRepoMirror` | `repo:admin` on the repository. Removes the override |
34+| `SetRepoMirrorExcluded` | `repo:admin` on the repository. Opts out of or into the global template; a URL override wins over exclusion |
35+| `GetMirrorStatus` | Read access to the repository. Error `detail` is returned only with `repo:admin` |
36+| `ListMirrorStatus` | Owner rights |
37+| `SyncMirror` | `repo:admin` for `repo`, or owner rights for `all=true` (with empty `repo`). Returns per-repository results, including queued and no-op indicators and a per-repository `error`; `all=true` continues past failing repositories and skips disabled ones |
38+
39+Cron expressions have five fields, run in UTC and must be at least five minutes
40+apart. Mirrors push the default branch (or every branch with `all_branches`) and
41+tags. `force` lifts a pause, skips the no-op check and the 60-second manual sync
42+throttle; it **never** performs a Git force push. Pushes are atomic: one rejected
43+ref fails the whole push. After key rotation, add the new public key to remotes
44+and sync with `force` to recover from authentication failures. Configuration can
45+be saved without `ERMITE_SECRETS_KEY`, but pushes cannot run without it.
46+
47+`EventService.Watch` adds `mirror_failed` (field 23, `MirrorFailed`) when a mirror
48+starts failing (ok or disabled to failing or paused, once per incident), and `mirror_recovered`
49+(field 24, `MirrorRecovered`) when a failing or paused mirror succeeds again.
50+These are transition events, not events for every attempt. They are delivered on
51+`repo:<name>` and `home`, subject to repository visibility. Neither event carries
52+a URL, reason or error detail (`MirrorFailed` field 4, `reason`, was removed).
A gen/ermite/ermiteconnect/mirror.connect.go +404 -0
@@ -0,0 +1,404 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/mirror.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+ // MirrorServiceName is the fully-qualified name of the MirrorService service.
27+ MirrorServiceName = "ermite.MirrorService"
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+ // MirrorServiceGetMirrorKeyProcedure is the fully-qualified name of the MirrorService's
39+ // GetMirrorKey RPC.
40+ MirrorServiceGetMirrorKeyProcedure = "/ermite.MirrorService/GetMirrorKey"
41+ // MirrorServiceRotateMirrorKeyProcedure is the fully-qualified name of the MirrorService's
42+ // RotateMirrorKey RPC.
43+ MirrorServiceRotateMirrorKeyProcedure = "/ermite.MirrorService/RotateMirrorKey"
44+ // MirrorServiceGetMirrorConfigProcedure is the fully-qualified name of the MirrorService's
45+ // GetMirrorConfig RPC.
46+ MirrorServiceGetMirrorConfigProcedure = "/ermite.MirrorService/GetMirrorConfig"
47+ // MirrorServiceSetMirrorConfigProcedure is the fully-qualified name of the MirrorService's
48+ // SetMirrorConfig RPC.
49+ MirrorServiceSetMirrorConfigProcedure = "/ermite.MirrorService/SetMirrorConfig"
50+ // MirrorServiceSetRepoMirrorProcedure is the fully-qualified name of the MirrorService's
51+ // SetRepoMirror RPC.
52+ MirrorServiceSetRepoMirrorProcedure = "/ermite.MirrorService/SetRepoMirror"
53+ // MirrorServiceRemoveRepoMirrorProcedure is the fully-qualified name of the MirrorService's
54+ // RemoveRepoMirror RPC.
55+ MirrorServiceRemoveRepoMirrorProcedure = "/ermite.MirrorService/RemoveRepoMirror"
56+ // MirrorServiceSetRepoMirrorExcludedProcedure is the fully-qualified name of the MirrorService's
57+ // SetRepoMirrorExcluded RPC.
58+ MirrorServiceSetRepoMirrorExcludedProcedure = "/ermite.MirrorService/SetRepoMirrorExcluded"
59+ // MirrorServiceGetMirrorStatusProcedure is the fully-qualified name of the MirrorService's
60+ // GetMirrorStatus RPC.
61+ MirrorServiceGetMirrorStatusProcedure = "/ermite.MirrorService/GetMirrorStatus"
62+ // MirrorServiceListMirrorStatusProcedure is the fully-qualified name of the MirrorService's
63+ // ListMirrorStatus RPC.
64+ MirrorServiceListMirrorStatusProcedure = "/ermite.MirrorService/ListMirrorStatus"
65+ // MirrorServiceSyncMirrorProcedure is the fully-qualified name of the MirrorService's SyncMirror
66+ // RPC.
67+ MirrorServiceSyncMirrorProcedure = "/ermite.MirrorService/SyncMirror"
68+)
69+
70+// MirrorServiceClient is a client for the ermite.MirrorService service.
71+type MirrorServiceClient interface {
72+ // GetMirrorKey returns the public mirror key, generating it on first use.
73+ // Needs a non-anonymous caller. Fails with failed_precondition without
74+ // ERMITE_SECRETS_KEY.
75+ GetMirrorKey(context.Context, *connect.Request[ermite.GetMirrorKeyRequest]) (*connect.Response[ermite.GetMirrorKeyResponse], error)
76+ // RotateMirrorKey replaces the key immediately. Add the new key to remotes
77+ // and sync with force to recover from authentication failures.
78+ RotateMirrorKey(context.Context, *connect.Request[ermite.RotateMirrorKeyRequest]) (*connect.Response[ermite.RotateMirrorKeyResponse], error)
79+ // GetMirrorConfig returns the global mirror policy.
80+ GetMirrorConfig(context.Context, *connect.Request[ermite.GetMirrorConfigRequest]) (*connect.Response[ermite.GetMirrorConfigResponse], error)
81+ // SetMirrorConfig patches the global policy. Unset fields are unchanged.
82+ // Invalid fields give invalid_argument with field violations.
83+ SetMirrorConfig(context.Context, *connect.Request[ermite.SetMirrorConfigRequest]) (*connect.Response[ermite.SetMirrorConfigResponse], error)
84+ // SetRepoMirror sets an override, even when global mirroring is disabled.
85+ SetRepoMirror(context.Context, *connect.Request[ermite.SetRepoMirrorRequest]) (*connect.Response[ermite.SetRepoMirrorResponse], error)
86+ // RemoveRepoMirror removes the override so the global policy applies again.
87+ RemoveRepoMirror(context.Context, *connect.Request[ermite.RemoveRepoMirrorRequest]) (*connect.Response[ermite.RemoveRepoMirrorResponse], error)
88+ // SetRepoMirrorExcluded excludes or includes a repository in the global
89+ // policy. An explicit URL override takes precedence.
90+ SetRepoMirrorExcluded(context.Context, *connect.Request[ermite.SetRepoMirrorExcludedRequest]) (*connect.Response[ermite.SetRepoMirrorExcludedResponse], error)
91+ // GetMirrorStatus returns the resolved target and status of one repository.
92+ GetMirrorStatus(context.Context, *connect.Request[ermite.GetMirrorStatusRequest]) (*connect.Response[ermite.GetMirrorStatusResponse], error)
93+ // ListMirrorStatus lists mirror statuses and needs owner rights.
94+ ListMirrorStatus(context.Context, *connect.Request[ermite.ListMirrorStatusRequest]) (*connect.Response[ermite.ListMirrorStatusResponse], error)
95+ // SyncMirror syncs Repo or, with All, every repository. Paused mirrors need
96+ // Force. Manual requests without Force are at least 60 seconds apart.
97+ SyncMirror(context.Context, *connect.Request[ermite.SyncMirrorRequest]) (*connect.Response[ermite.SyncMirrorResponse], error)
98+}
99+
100+// NewMirrorServiceClient constructs a client for the ermite.MirrorService service. By default, it
101+// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
102+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
103+// connect.WithGRPCWeb() options.
104+//
105+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
106+// http://api.acme.com or https://acme.com/grpc).
107+func NewMirrorServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) MirrorServiceClient {
108+ baseURL = strings.TrimRight(baseURL, "/")
109+ mirrorServiceMethods := ermite.File_ermite_mirror_proto.Services().ByName("MirrorService").Methods()
110+ return &mirrorServiceClient{
111+ getMirrorKey: connect.NewClient[ermite.GetMirrorKeyRequest, ermite.GetMirrorKeyResponse](
112+ httpClient,
113+ baseURL+MirrorServiceGetMirrorKeyProcedure,
114+ connect.WithSchema(mirrorServiceMethods.ByName("GetMirrorKey")),
115+ connect.WithClientOptions(opts...),
116+ ),
117+ rotateMirrorKey: connect.NewClient[ermite.RotateMirrorKeyRequest, ermite.RotateMirrorKeyResponse](
118+ httpClient,
119+ baseURL+MirrorServiceRotateMirrorKeyProcedure,
120+ connect.WithSchema(mirrorServiceMethods.ByName("RotateMirrorKey")),
121+ connect.WithClientOptions(opts...),
122+ ),
123+ getMirrorConfig: connect.NewClient[ermite.GetMirrorConfigRequest, ermite.GetMirrorConfigResponse](
124+ httpClient,
125+ baseURL+MirrorServiceGetMirrorConfigProcedure,
126+ connect.WithSchema(mirrorServiceMethods.ByName("GetMirrorConfig")),
127+ connect.WithClientOptions(opts...),
128+ ),
129+ setMirrorConfig: connect.NewClient[ermite.SetMirrorConfigRequest, ermite.SetMirrorConfigResponse](
130+ httpClient,
131+ baseURL+MirrorServiceSetMirrorConfigProcedure,
132+ connect.WithSchema(mirrorServiceMethods.ByName("SetMirrorConfig")),
133+ connect.WithClientOptions(opts...),
134+ ),
135+ setRepoMirror: connect.NewClient[ermite.SetRepoMirrorRequest, ermite.SetRepoMirrorResponse](
136+ httpClient,
137+ baseURL+MirrorServiceSetRepoMirrorProcedure,
138+ connect.WithSchema(mirrorServiceMethods.ByName("SetRepoMirror")),
139+ connect.WithClientOptions(opts...),
140+ ),
141+ removeRepoMirror: connect.NewClient[ermite.RemoveRepoMirrorRequest, ermite.RemoveRepoMirrorResponse](
142+ httpClient,
143+ baseURL+MirrorServiceRemoveRepoMirrorProcedure,
144+ connect.WithSchema(mirrorServiceMethods.ByName("RemoveRepoMirror")),
145+ connect.WithClientOptions(opts...),
146+ ),
147+ setRepoMirrorExcluded: connect.NewClient[ermite.SetRepoMirrorExcludedRequest, ermite.SetRepoMirrorExcludedResponse](
148+ httpClient,
149+ baseURL+MirrorServiceSetRepoMirrorExcludedProcedure,
150+ connect.WithSchema(mirrorServiceMethods.ByName("SetRepoMirrorExcluded")),
151+ connect.WithClientOptions(opts...),
152+ ),
153+ getMirrorStatus: connect.NewClient[ermite.GetMirrorStatusRequest, ermite.GetMirrorStatusResponse](
154+ httpClient,
155+ baseURL+MirrorServiceGetMirrorStatusProcedure,
156+ connect.WithSchema(mirrorServiceMethods.ByName("GetMirrorStatus")),
157+ connect.WithClientOptions(opts...),
158+ ),
159+ listMirrorStatus: connect.NewClient[ermite.ListMirrorStatusRequest, ermite.ListMirrorStatusResponse](
160+ httpClient,
161+ baseURL+MirrorServiceListMirrorStatusProcedure,
162+ connect.WithSchema(mirrorServiceMethods.ByName("ListMirrorStatus")),
163+ connect.WithClientOptions(opts...),
164+ ),
165+ syncMirror: connect.NewClient[ermite.SyncMirrorRequest, ermite.SyncMirrorResponse](
166+ httpClient,
167+ baseURL+MirrorServiceSyncMirrorProcedure,
168+ connect.WithSchema(mirrorServiceMethods.ByName("SyncMirror")),
169+ connect.WithClientOptions(opts...),
170+ ),
171+ }
172+}
173+
174+// mirrorServiceClient implements MirrorServiceClient.
175+type mirrorServiceClient struct {
176+ getMirrorKey *connect.Client[ermite.GetMirrorKeyRequest, ermite.GetMirrorKeyResponse]
177+ rotateMirrorKey *connect.Client[ermite.RotateMirrorKeyRequest, ermite.RotateMirrorKeyResponse]
178+ getMirrorConfig *connect.Client[ermite.GetMirrorConfigRequest, ermite.GetMirrorConfigResponse]
179+ setMirrorConfig *connect.Client[ermite.SetMirrorConfigRequest, ermite.SetMirrorConfigResponse]
180+ setRepoMirror *connect.Client[ermite.SetRepoMirrorRequest, ermite.SetRepoMirrorResponse]
181+ removeRepoMirror *connect.Client[ermite.RemoveRepoMirrorRequest, ermite.RemoveRepoMirrorResponse]
182+ setRepoMirrorExcluded *connect.Client[ermite.SetRepoMirrorExcludedRequest, ermite.SetRepoMirrorExcludedResponse]
183+ getMirrorStatus *connect.Client[ermite.GetMirrorStatusRequest, ermite.GetMirrorStatusResponse]
184+ listMirrorStatus *connect.Client[ermite.ListMirrorStatusRequest, ermite.ListMirrorStatusResponse]
185+ syncMirror *connect.Client[ermite.SyncMirrorRequest, ermite.SyncMirrorResponse]
186+}
187+
188+// GetMirrorKey calls ermite.MirrorService.GetMirrorKey.
189+func (c *mirrorServiceClient) GetMirrorKey(ctx context.Context, req *connect.Request[ermite.GetMirrorKeyRequest]) (*connect.Response[ermite.GetMirrorKeyResponse], error) {
190+ return c.getMirrorKey.CallUnary(ctx, req)
191+}
192+
193+// RotateMirrorKey calls ermite.MirrorService.RotateMirrorKey.
194+func (c *mirrorServiceClient) RotateMirrorKey(ctx context.Context, req *connect.Request[ermite.RotateMirrorKeyRequest]) (*connect.Response[ermite.RotateMirrorKeyResponse], error) {
195+ return c.rotateMirrorKey.CallUnary(ctx, req)
196+}
197+
198+// GetMirrorConfig calls ermite.MirrorService.GetMirrorConfig.
199+func (c *mirrorServiceClient) GetMirrorConfig(ctx context.Context, req *connect.Request[ermite.GetMirrorConfigRequest]) (*connect.Response[ermite.GetMirrorConfigResponse], error) {
200+ return c.getMirrorConfig.CallUnary(ctx, req)
201+}
202+
203+// SetMirrorConfig calls ermite.MirrorService.SetMirrorConfig.
204+func (c *mirrorServiceClient) SetMirrorConfig(ctx context.Context, req *connect.Request[ermite.SetMirrorConfigRequest]) (*connect.Response[ermite.SetMirrorConfigResponse], error) {
205+ return c.setMirrorConfig.CallUnary(ctx, req)
206+}
207+
208+// SetRepoMirror calls ermite.MirrorService.SetRepoMirror.
209+func (c *mirrorServiceClient) SetRepoMirror(ctx context.Context, req *connect.Request[ermite.SetRepoMirrorRequest]) (*connect.Response[ermite.SetRepoMirrorResponse], error) {
210+ return c.setRepoMirror.CallUnary(ctx, req)
211+}
212+
213+// RemoveRepoMirror calls ermite.MirrorService.RemoveRepoMirror.
214+func (c *mirrorServiceClient) RemoveRepoMirror(ctx context.Context, req *connect.Request[ermite.RemoveRepoMirrorRequest]) (*connect.Response[ermite.RemoveRepoMirrorResponse], error) {
215+ return c.removeRepoMirror.CallUnary(ctx, req)
216+}
217+
218+// SetRepoMirrorExcluded calls ermite.MirrorService.SetRepoMirrorExcluded.
219+func (c *mirrorServiceClient) SetRepoMirrorExcluded(ctx context.Context, req *connect.Request[ermite.SetRepoMirrorExcludedRequest]) (*connect.Response[ermite.SetRepoMirrorExcludedResponse], error) {
220+ return c.setRepoMirrorExcluded.CallUnary(ctx, req)
221+}
222+
223+// GetMirrorStatus calls ermite.MirrorService.GetMirrorStatus.
224+func (c *mirrorServiceClient) GetMirrorStatus(ctx context.Context, req *connect.Request[ermite.GetMirrorStatusRequest]) (*connect.Response[ermite.GetMirrorStatusResponse], error) {
225+ return c.getMirrorStatus.CallUnary(ctx, req)
226+}
227+
228+// ListMirrorStatus calls ermite.MirrorService.ListMirrorStatus.
229+func (c *mirrorServiceClient) ListMirrorStatus(ctx context.Context, req *connect.Request[ermite.ListMirrorStatusRequest]) (*connect.Response[ermite.ListMirrorStatusResponse], error) {
230+ return c.listMirrorStatus.CallUnary(ctx, req)
231+}
232+
233+// SyncMirror calls ermite.MirrorService.SyncMirror.
234+func (c *mirrorServiceClient) SyncMirror(ctx context.Context, req *connect.Request[ermite.SyncMirrorRequest]) (*connect.Response[ermite.SyncMirrorResponse], error) {
235+ return c.syncMirror.CallUnary(ctx, req)
236+}
237+
238+// MirrorServiceHandler is an implementation of the ermite.MirrorService service.
239+type MirrorServiceHandler interface {
240+ // GetMirrorKey returns the public mirror key, generating it on first use.
241+ // Needs a non-anonymous caller. Fails with failed_precondition without
242+ // ERMITE_SECRETS_KEY.
243+ GetMirrorKey(context.Context, *connect.Request[ermite.GetMirrorKeyRequest]) (*connect.Response[ermite.GetMirrorKeyResponse], error)
244+ // RotateMirrorKey replaces the key immediately. Add the new key to remotes
245+ // and sync with force to recover from authentication failures.
246+ RotateMirrorKey(context.Context, *connect.Request[ermite.RotateMirrorKeyRequest]) (*connect.Response[ermite.RotateMirrorKeyResponse], error)
247+ // GetMirrorConfig returns the global mirror policy.
248+ GetMirrorConfig(context.Context, *connect.Request[ermite.GetMirrorConfigRequest]) (*connect.Response[ermite.GetMirrorConfigResponse], error)
249+ // SetMirrorConfig patches the global policy. Unset fields are unchanged.
250+ // Invalid fields give invalid_argument with field violations.
251+ SetMirrorConfig(context.Context, *connect.Request[ermite.SetMirrorConfigRequest]) (*connect.Response[ermite.SetMirrorConfigResponse], error)
252+ // SetRepoMirror sets an override, even when global mirroring is disabled.
253+ SetRepoMirror(context.Context, *connect.Request[ermite.SetRepoMirrorRequest]) (*connect.Response[ermite.SetRepoMirrorResponse], error)
254+ // RemoveRepoMirror removes the override so the global policy applies again.
255+ RemoveRepoMirror(context.Context, *connect.Request[ermite.RemoveRepoMirrorRequest]) (*connect.Response[ermite.RemoveRepoMirrorResponse], error)
256+ // SetRepoMirrorExcluded excludes or includes a repository in the global
257+ // policy. An explicit URL override takes precedence.
258+ SetRepoMirrorExcluded(context.Context, *connect.Request[ermite.SetRepoMirrorExcludedRequest]) (*connect.Response[ermite.SetRepoMirrorExcludedResponse], error)
259+ // GetMirrorStatus returns the resolved target and status of one repository.
260+ GetMirrorStatus(context.Context, *connect.Request[ermite.GetMirrorStatusRequest]) (*connect.Response[ermite.GetMirrorStatusResponse], error)
261+ // ListMirrorStatus lists mirror statuses and needs owner rights.
262+ ListMirrorStatus(context.Context, *connect.Request[ermite.ListMirrorStatusRequest]) (*connect.Response[ermite.ListMirrorStatusResponse], error)
263+ // SyncMirror syncs Repo or, with All, every repository. Paused mirrors need
264+ // Force. Manual requests without Force are at least 60 seconds apart.
265+ SyncMirror(context.Context, *connect.Request[ermite.SyncMirrorRequest]) (*connect.Response[ermite.SyncMirrorResponse], error)
266+}
267+
268+// NewMirrorServiceHandler builds an HTTP handler from the service implementation. It returns the
269+// path on which to mount the handler and the handler itself.
270+//
271+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
272+// and JSON codecs. They also support gzip compression.
273+func NewMirrorServiceHandler(svc MirrorServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
274+ mirrorServiceMethods := ermite.File_ermite_mirror_proto.Services().ByName("MirrorService").Methods()
275+ mirrorServiceGetMirrorKeyHandler := connect.NewUnaryHandler(
276+ MirrorServiceGetMirrorKeyProcedure,
277+ svc.GetMirrorKey,
278+ connect.WithSchema(mirrorServiceMethods.ByName("GetMirrorKey")),
279+ connect.WithHandlerOptions(opts...),
280+ )
281+ mirrorServiceRotateMirrorKeyHandler := connect.NewUnaryHandler(
282+ MirrorServiceRotateMirrorKeyProcedure,
283+ svc.RotateMirrorKey,
284+ connect.WithSchema(mirrorServiceMethods.ByName("RotateMirrorKey")),
285+ connect.WithHandlerOptions(opts...),
286+ )
287+ mirrorServiceGetMirrorConfigHandler := connect.NewUnaryHandler(
288+ MirrorServiceGetMirrorConfigProcedure,
289+ svc.GetMirrorConfig,
290+ connect.WithSchema(mirrorServiceMethods.ByName("GetMirrorConfig")),
291+ connect.WithHandlerOptions(opts...),
292+ )
293+ mirrorServiceSetMirrorConfigHandler := connect.NewUnaryHandler(
294+ MirrorServiceSetMirrorConfigProcedure,
295+ svc.SetMirrorConfig,
296+ connect.WithSchema(mirrorServiceMethods.ByName("SetMirrorConfig")),
297+ connect.WithHandlerOptions(opts...),
298+ )
299+ mirrorServiceSetRepoMirrorHandler := connect.NewUnaryHandler(
300+ MirrorServiceSetRepoMirrorProcedure,
301+ svc.SetRepoMirror,
302+ connect.WithSchema(mirrorServiceMethods.ByName("SetRepoMirror")),
303+ connect.WithHandlerOptions(opts...),
304+ )
305+ mirrorServiceRemoveRepoMirrorHandler := connect.NewUnaryHandler(
306+ MirrorServiceRemoveRepoMirrorProcedure,
307+ svc.RemoveRepoMirror,
308+ connect.WithSchema(mirrorServiceMethods.ByName("RemoveRepoMirror")),
309+ connect.WithHandlerOptions(opts...),
310+ )
311+ mirrorServiceSetRepoMirrorExcludedHandler := connect.NewUnaryHandler(
312+ MirrorServiceSetRepoMirrorExcludedProcedure,
313+ svc.SetRepoMirrorExcluded,
314+ connect.WithSchema(mirrorServiceMethods.ByName("SetRepoMirrorExcluded")),
315+ connect.WithHandlerOptions(opts...),
316+ )
317+ mirrorServiceGetMirrorStatusHandler := connect.NewUnaryHandler(
318+ MirrorServiceGetMirrorStatusProcedure,
319+ svc.GetMirrorStatus,
320+ connect.WithSchema(mirrorServiceMethods.ByName("GetMirrorStatus")),
321+ connect.WithHandlerOptions(opts...),
322+ )
323+ mirrorServiceListMirrorStatusHandler := connect.NewUnaryHandler(
324+ MirrorServiceListMirrorStatusProcedure,
325+ svc.ListMirrorStatus,
326+ connect.WithSchema(mirrorServiceMethods.ByName("ListMirrorStatus")),
327+ connect.WithHandlerOptions(opts...),
328+ )
329+ mirrorServiceSyncMirrorHandler := connect.NewUnaryHandler(
330+ MirrorServiceSyncMirrorProcedure,
331+ svc.SyncMirror,
332+ connect.WithSchema(mirrorServiceMethods.ByName("SyncMirror")),
333+ connect.WithHandlerOptions(opts...),
334+ )
335+ return "/ermite.MirrorService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
336+ switch r.URL.Path {
337+ case MirrorServiceGetMirrorKeyProcedure:
338+ mirrorServiceGetMirrorKeyHandler.ServeHTTP(w, r)
339+ case MirrorServiceRotateMirrorKeyProcedure:
340+ mirrorServiceRotateMirrorKeyHandler.ServeHTTP(w, r)
341+ case MirrorServiceGetMirrorConfigProcedure:
342+ mirrorServiceGetMirrorConfigHandler.ServeHTTP(w, r)
343+ case MirrorServiceSetMirrorConfigProcedure:
344+ mirrorServiceSetMirrorConfigHandler.ServeHTTP(w, r)
345+ case MirrorServiceSetRepoMirrorProcedure:
346+ mirrorServiceSetRepoMirrorHandler.ServeHTTP(w, r)
347+ case MirrorServiceRemoveRepoMirrorProcedure:
348+ mirrorServiceRemoveRepoMirrorHandler.ServeHTTP(w, r)
349+ case MirrorServiceSetRepoMirrorExcludedProcedure:
350+ mirrorServiceSetRepoMirrorExcludedHandler.ServeHTTP(w, r)
351+ case MirrorServiceGetMirrorStatusProcedure:
352+ mirrorServiceGetMirrorStatusHandler.ServeHTTP(w, r)
353+ case MirrorServiceListMirrorStatusProcedure:
354+ mirrorServiceListMirrorStatusHandler.ServeHTTP(w, r)
355+ case MirrorServiceSyncMirrorProcedure:
356+ mirrorServiceSyncMirrorHandler.ServeHTTP(w, r)
357+ default:
358+ http.NotFound(w, r)
359+ }
360+ })
361+}
362+
363+// UnimplementedMirrorServiceHandler returns CodeUnimplemented from all methods.
364+type UnimplementedMirrorServiceHandler struct{}
365+
366+func (UnimplementedMirrorServiceHandler) GetMirrorKey(context.Context, *connect.Request[ermite.GetMirrorKeyRequest]) (*connect.Response[ermite.GetMirrorKeyResponse], error) {
367+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.GetMirrorKey is not implemented"))
368+}
369+
370+func (UnimplementedMirrorServiceHandler) RotateMirrorKey(context.Context, *connect.Request[ermite.RotateMirrorKeyRequest]) (*connect.Response[ermite.RotateMirrorKeyResponse], error) {
371+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.RotateMirrorKey is not implemented"))
372+}
373+
374+func (UnimplementedMirrorServiceHandler) GetMirrorConfig(context.Context, *connect.Request[ermite.GetMirrorConfigRequest]) (*connect.Response[ermite.GetMirrorConfigResponse], error) {
375+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.GetMirrorConfig is not implemented"))
376+}
377+
378+func (UnimplementedMirrorServiceHandler) SetMirrorConfig(context.Context, *connect.Request[ermite.SetMirrorConfigRequest]) (*connect.Response[ermite.SetMirrorConfigResponse], error) {
379+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.SetMirrorConfig is not implemented"))
380+}
381+
382+func (UnimplementedMirrorServiceHandler) SetRepoMirror(context.Context, *connect.Request[ermite.SetRepoMirrorRequest]) (*connect.Response[ermite.SetRepoMirrorResponse], error) {
383+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.SetRepoMirror is not implemented"))
384+}
385+
386+func (UnimplementedMirrorServiceHandler) RemoveRepoMirror(context.Context, *connect.Request[ermite.RemoveRepoMirrorRequest]) (*connect.Response[ermite.RemoveRepoMirrorResponse], error) {
387+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.RemoveRepoMirror is not implemented"))
388+}
389+
390+func (UnimplementedMirrorServiceHandler) SetRepoMirrorExcluded(context.Context, *connect.Request[ermite.SetRepoMirrorExcludedRequest]) (*connect.Response[ermite.SetRepoMirrorExcludedResponse], error) {
391+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.SetRepoMirrorExcluded is not implemented"))
392+}
393+
394+func (UnimplementedMirrorServiceHandler) GetMirrorStatus(context.Context, *connect.Request[ermite.GetMirrorStatusRequest]) (*connect.Response[ermite.GetMirrorStatusResponse], error) {
395+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.GetMirrorStatus is not implemented"))
396+}
397+
398+func (UnimplementedMirrorServiceHandler) ListMirrorStatus(context.Context, *connect.Request[ermite.ListMirrorStatusRequest]) (*connect.Response[ermite.ListMirrorStatusResponse], error) {
399+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.ListMirrorStatus is not implemented"))
400+}
401+
402+func (UnimplementedMirrorServiceHandler) SyncMirror(context.Context, *connect.Request[ermite.SyncMirrorRequest]) (*connect.Response[ermite.SyncMirrorResponse], error) {
403+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.SyncMirror is not implemented"))
404+}
@@ -0,0 +1,404 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/mirror.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+ // MirrorServiceName is the fully-qualified name of the MirrorService service.
27+ MirrorServiceName = "ermite.MirrorService"
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+ // MirrorServiceGetMirrorKeyProcedure is the fully-qualified name of the MirrorService's
39+ // GetMirrorKey RPC.
40+ MirrorServiceGetMirrorKeyProcedure = "/ermite.MirrorService/GetMirrorKey"
41+ // MirrorServiceRotateMirrorKeyProcedure is the fully-qualified name of the MirrorService's
42+ // RotateMirrorKey RPC.
43+ MirrorServiceRotateMirrorKeyProcedure = "/ermite.MirrorService/RotateMirrorKey"
44+ // MirrorServiceGetMirrorConfigProcedure is the fully-qualified name of the MirrorService's
45+ // GetMirrorConfig RPC.
46+ MirrorServiceGetMirrorConfigProcedure = "/ermite.MirrorService/GetMirrorConfig"
47+ // MirrorServiceSetMirrorConfigProcedure is the fully-qualified name of the MirrorService's
48+ // SetMirrorConfig RPC.
49+ MirrorServiceSetMirrorConfigProcedure = "/ermite.MirrorService/SetMirrorConfig"
50+ // MirrorServiceSetRepoMirrorProcedure is the fully-qualified name of the MirrorService's
51+ // SetRepoMirror RPC.
52+ MirrorServiceSetRepoMirrorProcedure = "/ermite.MirrorService/SetRepoMirror"
53+ // MirrorServiceRemoveRepoMirrorProcedure is the fully-qualified name of the MirrorService's
54+ // RemoveRepoMirror RPC.
55+ MirrorServiceRemoveRepoMirrorProcedure = "/ermite.MirrorService/RemoveRepoMirror"
56+ // MirrorServiceSetRepoMirrorExcludedProcedure is the fully-qualified name of the MirrorService's
57+ // SetRepoMirrorExcluded RPC.
58+ MirrorServiceSetRepoMirrorExcludedProcedure = "/ermite.MirrorService/SetRepoMirrorExcluded"
59+ // MirrorServiceGetMirrorStatusProcedure is the fully-qualified name of the MirrorService's
60+ // GetMirrorStatus RPC.
61+ MirrorServiceGetMirrorStatusProcedure = "/ermite.MirrorService/GetMirrorStatus"
62+ // MirrorServiceListMirrorStatusProcedure is the fully-qualified name of the MirrorService's
63+ // ListMirrorStatus RPC.
64+ MirrorServiceListMirrorStatusProcedure = "/ermite.MirrorService/ListMirrorStatus"
65+ // MirrorServiceSyncMirrorProcedure is the fully-qualified name of the MirrorService's SyncMirror
66+ // RPC.
67+ MirrorServiceSyncMirrorProcedure = "/ermite.MirrorService/SyncMirror"
68+)
69+
70+// MirrorServiceClient is a client for the ermite.MirrorService service.
71+type MirrorServiceClient interface {
72+ // GetMirrorKey returns the public mirror key, generating it on first use.
73+ // Needs a non-anonymous caller. Fails with failed_precondition without
74+ // ERMITE_SECRETS_KEY.
75+ GetMirrorKey(context.Context, *connect.Request[ermite.GetMirrorKeyRequest]) (*connect.Response[ermite.GetMirrorKeyResponse], error)
76+ // RotateMirrorKey replaces the key immediately. Add the new key to remotes
77+ // and sync with force to recover from authentication failures.
78+ RotateMirrorKey(context.Context, *connect.Request[ermite.RotateMirrorKeyRequest]) (*connect.Response[ermite.RotateMirrorKeyResponse], error)
79+ // GetMirrorConfig returns the global mirror policy.
80+ GetMirrorConfig(context.Context, *connect.Request[ermite.GetMirrorConfigRequest]) (*connect.Response[ermite.GetMirrorConfigResponse], error)
81+ // SetMirrorConfig patches the global policy. Unset fields are unchanged.
82+ // Invalid fields give invalid_argument with field violations.
83+ SetMirrorConfig(context.Context, *connect.Request[ermite.SetMirrorConfigRequest]) (*connect.Response[ermite.SetMirrorConfigResponse], error)
84+ // SetRepoMirror sets an override, even when global mirroring is disabled.
85+ SetRepoMirror(context.Context, *connect.Request[ermite.SetRepoMirrorRequest]) (*connect.Response[ermite.SetRepoMirrorResponse], error)
86+ // RemoveRepoMirror removes the override so the global policy applies again.
87+ RemoveRepoMirror(context.Context, *connect.Request[ermite.RemoveRepoMirrorRequest]) (*connect.Response[ermite.RemoveRepoMirrorResponse], error)
88+ // SetRepoMirrorExcluded excludes or includes a repository in the global
89+ // policy. An explicit URL override takes precedence.
90+ SetRepoMirrorExcluded(context.Context, *connect.Request[ermite.SetRepoMirrorExcludedRequest]) (*connect.Response[ermite.SetRepoMirrorExcludedResponse], error)
91+ // GetMirrorStatus returns the resolved target and status of one repository.
92+ GetMirrorStatus(context.Context, *connect.Request[ermite.GetMirrorStatusRequest]) (*connect.Response[ermite.GetMirrorStatusResponse], error)
93+ // ListMirrorStatus lists mirror statuses and needs owner rights.
94+ ListMirrorStatus(context.Context, *connect.Request[ermite.ListMirrorStatusRequest]) (*connect.Response[ermite.ListMirrorStatusResponse], error)
95+ // SyncMirror syncs Repo or, with All, every repository. Paused mirrors need
96+ // Force. Manual requests without Force are at least 60 seconds apart.
97+ SyncMirror(context.Context, *connect.Request[ermite.SyncMirrorRequest]) (*connect.Response[ermite.SyncMirrorResponse], error)
98+}
99+
100+// NewMirrorServiceClient constructs a client for the ermite.MirrorService service. By default, it
101+// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
102+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
103+// connect.WithGRPCWeb() options.
104+//
105+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
106+// http://api.acme.com or https://acme.com/grpc).
107+func NewMirrorServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) MirrorServiceClient {
108+ baseURL = strings.TrimRight(baseURL, "/")
109+ mirrorServiceMethods := ermite.File_ermite_mirror_proto.Services().ByName("MirrorService").Methods()
110+ return &mirrorServiceClient{
111+ getMirrorKey: connect.NewClient[ermite.GetMirrorKeyRequest, ermite.GetMirrorKeyResponse](
112+ httpClient,
113+ baseURL+MirrorServiceGetMirrorKeyProcedure,
114+ connect.WithSchema(mirrorServiceMethods.ByName("GetMirrorKey")),
115+ connect.WithClientOptions(opts...),
116+ ),
117+ rotateMirrorKey: connect.NewClient[ermite.RotateMirrorKeyRequest, ermite.RotateMirrorKeyResponse](
118+ httpClient,
119+ baseURL+MirrorServiceRotateMirrorKeyProcedure,
120+ connect.WithSchema(mirrorServiceMethods.ByName("RotateMirrorKey")),
121+ connect.WithClientOptions(opts...),
122+ ),
123+ getMirrorConfig: connect.NewClient[ermite.GetMirrorConfigRequest, ermite.GetMirrorConfigResponse](
124+ httpClient,
125+ baseURL+MirrorServiceGetMirrorConfigProcedure,
126+ connect.WithSchema(mirrorServiceMethods.ByName("GetMirrorConfig")),
127+ connect.WithClientOptions(opts...),
128+ ),
129+ setMirrorConfig: connect.NewClient[ermite.SetMirrorConfigRequest, ermite.SetMirrorConfigResponse](
130+ httpClient,
131+ baseURL+MirrorServiceSetMirrorConfigProcedure,
132+ connect.WithSchema(mirrorServiceMethods.ByName("SetMirrorConfig")),
133+ connect.WithClientOptions(opts...),
134+ ),
135+ setRepoMirror: connect.NewClient[ermite.SetRepoMirrorRequest, ermite.SetRepoMirrorResponse](
136+ httpClient,
137+ baseURL+MirrorServiceSetRepoMirrorProcedure,
138+ connect.WithSchema(mirrorServiceMethods.ByName("SetRepoMirror")),
139+ connect.WithClientOptions(opts...),
140+ ),
141+ removeRepoMirror: connect.NewClient[ermite.RemoveRepoMirrorRequest, ermite.RemoveRepoMirrorResponse](
142+ httpClient,
143+ baseURL+MirrorServiceRemoveRepoMirrorProcedure,
144+ connect.WithSchema(mirrorServiceMethods.ByName("RemoveRepoMirror")),
145+ connect.WithClientOptions(opts...),
146+ ),
147+ setRepoMirrorExcluded: connect.NewClient[ermite.SetRepoMirrorExcludedRequest, ermite.SetRepoMirrorExcludedResponse](
148+ httpClient,
149+ baseURL+MirrorServiceSetRepoMirrorExcludedProcedure,
150+ connect.WithSchema(mirrorServiceMethods.ByName("SetRepoMirrorExcluded")),
151+ connect.WithClientOptions(opts...),
152+ ),
153+ getMirrorStatus: connect.NewClient[ermite.GetMirrorStatusRequest, ermite.GetMirrorStatusResponse](
154+ httpClient,
155+ baseURL+MirrorServiceGetMirrorStatusProcedure,
156+ connect.WithSchema(mirrorServiceMethods.ByName("GetMirrorStatus")),
157+ connect.WithClientOptions(opts...),
158+ ),
159+ listMirrorStatus: connect.NewClient[ermite.ListMirrorStatusRequest, ermite.ListMirrorStatusResponse](
160+ httpClient,
161+ baseURL+MirrorServiceListMirrorStatusProcedure,
162+ connect.WithSchema(mirrorServiceMethods.ByName("ListMirrorStatus")),
163+ connect.WithClientOptions(opts...),
164+ ),
165+ syncMirror: connect.NewClient[ermite.SyncMirrorRequest, ermite.SyncMirrorResponse](
166+ httpClient,
167+ baseURL+MirrorServiceSyncMirrorProcedure,
168+ connect.WithSchema(mirrorServiceMethods.ByName("SyncMirror")),
169+ connect.WithClientOptions(opts...),
170+ ),
171+ }
172+}
173+
174+// mirrorServiceClient implements MirrorServiceClient.
175+type mirrorServiceClient struct {
176+ getMirrorKey *connect.Client[ermite.GetMirrorKeyRequest, ermite.GetMirrorKeyResponse]
177+ rotateMirrorKey *connect.Client[ermite.RotateMirrorKeyRequest, ermite.RotateMirrorKeyResponse]
178+ getMirrorConfig *connect.Client[ermite.GetMirrorConfigRequest, ermite.GetMirrorConfigResponse]
179+ setMirrorConfig *connect.Client[ermite.SetMirrorConfigRequest, ermite.SetMirrorConfigResponse]
180+ setRepoMirror *connect.Client[ermite.SetRepoMirrorRequest, ermite.SetRepoMirrorResponse]
181+ removeRepoMirror *connect.Client[ermite.RemoveRepoMirrorRequest, ermite.RemoveRepoMirrorResponse]
182+ setRepoMirrorExcluded *connect.Client[ermite.SetRepoMirrorExcludedRequest, ermite.SetRepoMirrorExcludedResponse]
183+ getMirrorStatus *connect.Client[ermite.GetMirrorStatusRequest, ermite.GetMirrorStatusResponse]
184+ listMirrorStatus *connect.Client[ermite.ListMirrorStatusRequest, ermite.ListMirrorStatusResponse]
185+ syncMirror *connect.Client[ermite.SyncMirrorRequest, ermite.SyncMirrorResponse]
186+}
187+
188+// GetMirrorKey calls ermite.MirrorService.GetMirrorKey.
189+func (c *mirrorServiceClient) GetMirrorKey(ctx context.Context, req *connect.Request[ermite.GetMirrorKeyRequest]) (*connect.Response[ermite.GetMirrorKeyResponse], error) {
190+ return c.getMirrorKey.CallUnary(ctx, req)
191+}
192+
193+// RotateMirrorKey calls ermite.MirrorService.RotateMirrorKey.
194+func (c *mirrorServiceClient) RotateMirrorKey(ctx context.Context, req *connect.Request[ermite.RotateMirrorKeyRequest]) (*connect.Response[ermite.RotateMirrorKeyResponse], error) {
195+ return c.rotateMirrorKey.CallUnary(ctx, req)
196+}
197+
198+// GetMirrorConfig calls ermite.MirrorService.GetMirrorConfig.
199+func (c *mirrorServiceClient) GetMirrorConfig(ctx context.Context, req *connect.Request[ermite.GetMirrorConfigRequest]) (*connect.Response[ermite.GetMirrorConfigResponse], error) {
200+ return c.getMirrorConfig.CallUnary(ctx, req)
201+}
202+
203+// SetMirrorConfig calls ermite.MirrorService.SetMirrorConfig.
204+func (c *mirrorServiceClient) SetMirrorConfig(ctx context.Context, req *connect.Request[ermite.SetMirrorConfigRequest]) (*connect.Response[ermite.SetMirrorConfigResponse], error) {
205+ return c.setMirrorConfig.CallUnary(ctx, req)
206+}
207+
208+// SetRepoMirror calls ermite.MirrorService.SetRepoMirror.
209+func (c *mirrorServiceClient) SetRepoMirror(ctx context.Context, req *connect.Request[ermite.SetRepoMirrorRequest]) (*connect.Response[ermite.SetRepoMirrorResponse], error) {
210+ return c.setRepoMirror.CallUnary(ctx, req)
211+}
212+
213+// RemoveRepoMirror calls ermite.MirrorService.RemoveRepoMirror.
214+func (c *mirrorServiceClient) RemoveRepoMirror(ctx context.Context, req *connect.Request[ermite.RemoveRepoMirrorRequest]) (*connect.Response[ermite.RemoveRepoMirrorResponse], error) {
215+ return c.removeRepoMirror.CallUnary(ctx, req)
216+}
217+
218+// SetRepoMirrorExcluded calls ermite.MirrorService.SetRepoMirrorExcluded.
219+func (c *mirrorServiceClient) SetRepoMirrorExcluded(ctx context.Context, req *connect.Request[ermite.SetRepoMirrorExcludedRequest]) (*connect.Response[ermite.SetRepoMirrorExcludedResponse], error) {
220+ return c.setRepoMirrorExcluded.CallUnary(ctx, req)
221+}
222+
223+// GetMirrorStatus calls ermite.MirrorService.GetMirrorStatus.
224+func (c *mirrorServiceClient) GetMirrorStatus(ctx context.Context, req *connect.Request[ermite.GetMirrorStatusRequest]) (*connect.Response[ermite.GetMirrorStatusResponse], error) {
225+ return c.getMirrorStatus.CallUnary(ctx, req)
226+}
227+
228+// ListMirrorStatus calls ermite.MirrorService.ListMirrorStatus.
229+func (c *mirrorServiceClient) ListMirrorStatus(ctx context.Context, req *connect.Request[ermite.ListMirrorStatusRequest]) (*connect.Response[ermite.ListMirrorStatusResponse], error) {
230+ return c.listMirrorStatus.CallUnary(ctx, req)
231+}
232+
233+// SyncMirror calls ermite.MirrorService.SyncMirror.
234+func (c *mirrorServiceClient) SyncMirror(ctx context.Context, req *connect.Request[ermite.SyncMirrorRequest]) (*connect.Response[ermite.SyncMirrorResponse], error) {
235+ return c.syncMirror.CallUnary(ctx, req)
236+}
237+
238+// MirrorServiceHandler is an implementation of the ermite.MirrorService service.
239+type MirrorServiceHandler interface {
240+ // GetMirrorKey returns the public mirror key, generating it on first use.
241+ // Needs a non-anonymous caller. Fails with failed_precondition without
242+ // ERMITE_SECRETS_KEY.
243+ GetMirrorKey(context.Context, *connect.Request[ermite.GetMirrorKeyRequest]) (*connect.Response[ermite.GetMirrorKeyResponse], error)
244+ // RotateMirrorKey replaces the key immediately. Add the new key to remotes
245+ // and sync with force to recover from authentication failures.
246+ RotateMirrorKey(context.Context, *connect.Request[ermite.RotateMirrorKeyRequest]) (*connect.Response[ermite.RotateMirrorKeyResponse], error)
247+ // GetMirrorConfig returns the global mirror policy.
248+ GetMirrorConfig(context.Context, *connect.Request[ermite.GetMirrorConfigRequest]) (*connect.Response[ermite.GetMirrorConfigResponse], error)
249+ // SetMirrorConfig patches the global policy. Unset fields are unchanged.
250+ // Invalid fields give invalid_argument with field violations.
251+ SetMirrorConfig(context.Context, *connect.Request[ermite.SetMirrorConfigRequest]) (*connect.Response[ermite.SetMirrorConfigResponse], error)
252+ // SetRepoMirror sets an override, even when global mirroring is disabled.
253+ SetRepoMirror(context.Context, *connect.Request[ermite.SetRepoMirrorRequest]) (*connect.Response[ermite.SetRepoMirrorResponse], error)
254+ // RemoveRepoMirror removes the override so the global policy applies again.
255+ RemoveRepoMirror(context.Context, *connect.Request[ermite.RemoveRepoMirrorRequest]) (*connect.Response[ermite.RemoveRepoMirrorResponse], error)
256+ // SetRepoMirrorExcluded excludes or includes a repository in the global
257+ // policy. An explicit URL override takes precedence.
258+ SetRepoMirrorExcluded(context.Context, *connect.Request[ermite.SetRepoMirrorExcludedRequest]) (*connect.Response[ermite.SetRepoMirrorExcludedResponse], error)
259+ // GetMirrorStatus returns the resolved target and status of one repository.
260+ GetMirrorStatus(context.Context, *connect.Request[ermite.GetMirrorStatusRequest]) (*connect.Response[ermite.GetMirrorStatusResponse], error)
261+ // ListMirrorStatus lists mirror statuses and needs owner rights.
262+ ListMirrorStatus(context.Context, *connect.Request[ermite.ListMirrorStatusRequest]) (*connect.Response[ermite.ListMirrorStatusResponse], error)
263+ // SyncMirror syncs Repo or, with All, every repository. Paused mirrors need
264+ // Force. Manual requests without Force are at least 60 seconds apart.
265+ SyncMirror(context.Context, *connect.Request[ermite.SyncMirrorRequest]) (*connect.Response[ermite.SyncMirrorResponse], error)
266+}
267+
268+// NewMirrorServiceHandler builds an HTTP handler from the service implementation. It returns the
269+// path on which to mount the handler and the handler itself.
270+//
271+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
272+// and JSON codecs. They also support gzip compression.
273+func NewMirrorServiceHandler(svc MirrorServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
274+ mirrorServiceMethods := ermite.File_ermite_mirror_proto.Services().ByName("MirrorService").Methods()
275+ mirrorServiceGetMirrorKeyHandler := connect.NewUnaryHandler(
276+ MirrorServiceGetMirrorKeyProcedure,
277+ svc.GetMirrorKey,
278+ connect.WithSchema(mirrorServiceMethods.ByName("GetMirrorKey")),
279+ connect.WithHandlerOptions(opts...),
280+ )
281+ mirrorServiceRotateMirrorKeyHandler := connect.NewUnaryHandler(
282+ MirrorServiceRotateMirrorKeyProcedure,
283+ svc.RotateMirrorKey,
284+ connect.WithSchema(mirrorServiceMethods.ByName("RotateMirrorKey")),
285+ connect.WithHandlerOptions(opts...),
286+ )
287+ mirrorServiceGetMirrorConfigHandler := connect.NewUnaryHandler(
288+ MirrorServiceGetMirrorConfigProcedure,
289+ svc.GetMirrorConfig,
290+ connect.WithSchema(mirrorServiceMethods.ByName("GetMirrorConfig")),
291+ connect.WithHandlerOptions(opts...),
292+ )
293+ mirrorServiceSetMirrorConfigHandler := connect.NewUnaryHandler(
294+ MirrorServiceSetMirrorConfigProcedure,
295+ svc.SetMirrorConfig,
296+ connect.WithSchema(mirrorServiceMethods.ByName("SetMirrorConfig")),
297+ connect.WithHandlerOptions(opts...),
298+ )
299+ mirrorServiceSetRepoMirrorHandler := connect.NewUnaryHandler(
300+ MirrorServiceSetRepoMirrorProcedure,
301+ svc.SetRepoMirror,
302+ connect.WithSchema(mirrorServiceMethods.ByName("SetRepoMirror")),
303+ connect.WithHandlerOptions(opts...),
304+ )
305+ mirrorServiceRemoveRepoMirrorHandler := connect.NewUnaryHandler(
306+ MirrorServiceRemoveRepoMirrorProcedure,
307+ svc.RemoveRepoMirror,
308+ connect.WithSchema(mirrorServiceMethods.ByName("RemoveRepoMirror")),
309+ connect.WithHandlerOptions(opts...),
310+ )
311+ mirrorServiceSetRepoMirrorExcludedHandler := connect.NewUnaryHandler(
312+ MirrorServiceSetRepoMirrorExcludedProcedure,
313+ svc.SetRepoMirrorExcluded,
314+ connect.WithSchema(mirrorServiceMethods.ByName("SetRepoMirrorExcluded")),
315+ connect.WithHandlerOptions(opts...),
316+ )
317+ mirrorServiceGetMirrorStatusHandler := connect.NewUnaryHandler(
318+ MirrorServiceGetMirrorStatusProcedure,
319+ svc.GetMirrorStatus,
320+ connect.WithSchema(mirrorServiceMethods.ByName("GetMirrorStatus")),
321+ connect.WithHandlerOptions(opts...),
322+ )
323+ mirrorServiceListMirrorStatusHandler := connect.NewUnaryHandler(
324+ MirrorServiceListMirrorStatusProcedure,
325+ svc.ListMirrorStatus,
326+ connect.WithSchema(mirrorServiceMethods.ByName("ListMirrorStatus")),
327+ connect.WithHandlerOptions(opts...),
328+ )
329+ mirrorServiceSyncMirrorHandler := connect.NewUnaryHandler(
330+ MirrorServiceSyncMirrorProcedure,
331+ svc.SyncMirror,
332+ connect.WithSchema(mirrorServiceMethods.ByName("SyncMirror")),
333+ connect.WithHandlerOptions(opts...),
334+ )
335+ return "/ermite.MirrorService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
336+ switch r.URL.Path {
337+ case MirrorServiceGetMirrorKeyProcedure:
338+ mirrorServiceGetMirrorKeyHandler.ServeHTTP(w, r)
339+ case MirrorServiceRotateMirrorKeyProcedure:
340+ mirrorServiceRotateMirrorKeyHandler.ServeHTTP(w, r)
341+ case MirrorServiceGetMirrorConfigProcedure:
342+ mirrorServiceGetMirrorConfigHandler.ServeHTTP(w, r)
343+ case MirrorServiceSetMirrorConfigProcedure:
344+ mirrorServiceSetMirrorConfigHandler.ServeHTTP(w, r)
345+ case MirrorServiceSetRepoMirrorProcedure:
346+ mirrorServiceSetRepoMirrorHandler.ServeHTTP(w, r)
347+ case MirrorServiceRemoveRepoMirrorProcedure:
348+ mirrorServiceRemoveRepoMirrorHandler.ServeHTTP(w, r)
349+ case MirrorServiceSetRepoMirrorExcludedProcedure:
350+ mirrorServiceSetRepoMirrorExcludedHandler.ServeHTTP(w, r)
351+ case MirrorServiceGetMirrorStatusProcedure:
352+ mirrorServiceGetMirrorStatusHandler.ServeHTTP(w, r)
353+ case MirrorServiceListMirrorStatusProcedure:
354+ mirrorServiceListMirrorStatusHandler.ServeHTTP(w, r)
355+ case MirrorServiceSyncMirrorProcedure:
356+ mirrorServiceSyncMirrorHandler.ServeHTTP(w, r)
357+ default:
358+ http.NotFound(w, r)
359+ }
360+ })
361+}
362+
363+// UnimplementedMirrorServiceHandler returns CodeUnimplemented from all methods.
364+type UnimplementedMirrorServiceHandler struct{}
365+
366+func (UnimplementedMirrorServiceHandler) GetMirrorKey(context.Context, *connect.Request[ermite.GetMirrorKeyRequest]) (*connect.Response[ermite.GetMirrorKeyResponse], error) {
367+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.GetMirrorKey is not implemented"))
368+}
369+
370+func (UnimplementedMirrorServiceHandler) RotateMirrorKey(context.Context, *connect.Request[ermite.RotateMirrorKeyRequest]) (*connect.Response[ermite.RotateMirrorKeyResponse], error) {
371+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.RotateMirrorKey is not implemented"))
372+}
373+
374+func (UnimplementedMirrorServiceHandler) GetMirrorConfig(context.Context, *connect.Request[ermite.GetMirrorConfigRequest]) (*connect.Response[ermite.GetMirrorConfigResponse], error) {
375+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.GetMirrorConfig is not implemented"))
376+}
377+
378+func (UnimplementedMirrorServiceHandler) SetMirrorConfig(context.Context, *connect.Request[ermite.SetMirrorConfigRequest]) (*connect.Response[ermite.SetMirrorConfigResponse], error) {
379+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.SetMirrorConfig is not implemented"))
380+}
381+
382+func (UnimplementedMirrorServiceHandler) SetRepoMirror(context.Context, *connect.Request[ermite.SetRepoMirrorRequest]) (*connect.Response[ermite.SetRepoMirrorResponse], error) {
383+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.SetRepoMirror is not implemented"))
384+}
385+
386+func (UnimplementedMirrorServiceHandler) RemoveRepoMirror(context.Context, *connect.Request[ermite.RemoveRepoMirrorRequest]) (*connect.Response[ermite.RemoveRepoMirrorResponse], error) {
387+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.RemoveRepoMirror is not implemented"))
388+}
389+
390+func (UnimplementedMirrorServiceHandler) SetRepoMirrorExcluded(context.Context, *connect.Request[ermite.SetRepoMirrorExcludedRequest]) (*connect.Response[ermite.SetRepoMirrorExcludedResponse], error) {
391+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.SetRepoMirrorExcluded is not implemented"))
392+}
393+
394+func (UnimplementedMirrorServiceHandler) GetMirrorStatus(context.Context, *connect.Request[ermite.GetMirrorStatusRequest]) (*connect.Response[ermite.GetMirrorStatusResponse], error) {
395+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.GetMirrorStatus is not implemented"))
396+}
397+
398+func (UnimplementedMirrorServiceHandler) ListMirrorStatus(context.Context, *connect.Request[ermite.ListMirrorStatusRequest]) (*connect.Response[ermite.ListMirrorStatusResponse], error) {
399+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.ListMirrorStatus is not implemented"))
400+}
401+
402+func (UnimplementedMirrorServiceHandler) SyncMirror(context.Context, *connect.Request[ermite.SyncMirrorRequest]) (*connect.Response[ermite.SyncMirrorResponse], error) {
403+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.MirrorService.SyncMirror is not implemented"))
404+}
M gen/ermite/event.pb.go +206 -41
@@ -171,6 +171,8 @@ type Event struct {
171171 // *Event_IssueCommented
172172 // *Event_IssueSubmitted
173173 // *Event_MergeRequestMergeability
174+ // *Event_MirrorFailed
175+ // *Event_MirrorRecovered
174176 Kind isEvent_Kind `protobuf_oneof:"kind"`
175177 unknownFields protoimpl.UnknownFields
176178 sizeCache protoimpl.SizeCache
@@ -407,6 +409,24 @@ func (x *Event) GetMergeRequestMergeability() *MergeRequestMergeability {
407409 return nil
408410 }
409411
412+func (x *Event) GetMirrorFailed() *MirrorFailed {
413+ if x != nil {
414+ if x, ok := x.Kind.(*Event_MirrorFailed); ok {
415+ return x.MirrorFailed
416+ }
417+ }
418+ return nil
419+}
420+
421+func (x *Event) GetMirrorRecovered() *MirrorRecovered {
422+ if x != nil {
423+ if x, ok := x.Kind.(*Event_MirrorRecovered); ok {
424+ return x.MirrorRecovered
425+ }
426+ }
427+ return nil
428+}
429+
410430 type isEvent_Kind interface {
411431 isEvent_Kind()
412432 }
@@ -513,6 +533,16 @@ type Event_MergeRequestMergeability struct {
513533 MergeRequestMergeability *MergeRequestMergeability `protobuf:"bytes,22,opt,name=merge_request_mergeability,json=mergeRequestMergeability,proto3,oneof"`
514534 }
515535
536+type Event_MirrorFailed struct {
537+ // MirrorFailed: a mirror entered failing or paused.
538+ MirrorFailed *MirrorFailed `protobuf:"bytes,23,opt,name=mirror_failed,json=mirrorFailed,proto3,oneof"`
539+}
540+
541+type Event_MirrorRecovered struct {
542+ // MirrorRecovered: a failing or paused mirror succeeded again.
543+ MirrorRecovered *MirrorRecovered `protobuf:"bytes,24,opt,name=mirror_recovered,json=mirrorRecovered,proto3,oneof"`
544+}
545+
516546 func (*Event_RepoCreated) isEvent_Kind() {}
517547
518548 func (*Event_RepoUpdated) isEvent_Kind() {}
@@ -553,6 +583,10 @@ func (*Event_IssueSubmitted) isEvent_Kind() {}
553583
554584 func (*Event_MergeRequestMergeability) isEvent_Kind() {}
555585
586+func (*Event_MirrorFailed) isEvent_Kind() {}
587+
588+func (*Event_MirrorRecovered) isEvent_Kind() {}
589+
556590 // RepoCreated reports a new repository.
557591 type RepoCreated struct {
558592 state protoimpl.MessageState `protogen:"open.v1"`
@@ -1668,17 +1702,131 @@ func (x *JobUpdated) GetJob() *RunJob {
16681702 return nil
16691703 }
16701704
1705+// MirrorFailed reports the start of a failure incident: a transition from ok
1706+// or disabled into failing or paused. It is sent once per incident, not for
1707+// each failed attempt nor for failing to paused. Servers with feature "mirror"
1708+// deliver it on "repo:<name>" and "home", subject to repository visibility. It
1709+// carries no URL, reason or error detail; read the status for those.
1710+type MirrorFailed struct {
1711+ state protoimpl.MessageState `protogen:"open.v1"`
1712+ // Repo is the repository name.
1713+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1714+ // State is the new mirror state.
1715+ State MirrorState `protobuf:"varint,2,opt,name=state,proto3,enum=ermite.MirrorState" json:"state,omitempty"`
1716+ // FailureClass is the failure category.
1717+ FailureClass string `protobuf:"bytes,3,opt,name=failure_class,json=failureClass,proto3" json:"failure_class,omitempty"`
1718+ unknownFields protoimpl.UnknownFields
1719+ sizeCache protoimpl.SizeCache
1720+}
1721+
1722+func (x *MirrorFailed) Reset() {
1723+ *x = MirrorFailed{}
1724+ mi := &file_ermite_event_proto_msgTypes[24]
1725+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1726+ ms.StoreMessageInfo(mi)
1727+}
1728+
1729+func (x *MirrorFailed) String() string {
1730+ return protoimpl.X.MessageStringOf(x)
1731+}
1732+
1733+func (*MirrorFailed) ProtoMessage() {}
1734+
1735+func (x *MirrorFailed) ProtoReflect() protoreflect.Message {
1736+ mi := &file_ermite_event_proto_msgTypes[24]
1737+ if x != nil {
1738+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1739+ if ms.LoadMessageInfo() == nil {
1740+ ms.StoreMessageInfo(mi)
1741+ }
1742+ return ms
1743+ }
1744+ return mi.MessageOf(x)
1745+}
1746+
1747+// Deprecated: Use MirrorFailed.ProtoReflect.Descriptor instead.
1748+func (*MirrorFailed) Descriptor() ([]byte, []int) {
1749+ return file_ermite_event_proto_rawDescGZIP(), []int{24}
1750+}
1751+
1752+func (x *MirrorFailed) GetRepo() string {
1753+ if x != nil {
1754+ return x.Repo
1755+ }
1756+ return ""
1757+}
1758+
1759+func (x *MirrorFailed) GetState() MirrorState {
1760+ if x != nil {
1761+ return x.State
1762+ }
1763+ return MirrorState_MIRROR_STATE_UNSPECIFIED
1764+}
1765+
1766+func (x *MirrorFailed) GetFailureClass() string {
1767+ if x != nil {
1768+ return x.FailureClass
1769+ }
1770+ return ""
1771+}
1772+
1773+// MirrorRecovered reports a successful sync after failing or paused. Delivery
1774+// follows MirrorFailed. It carries no URL or error detail.
1775+type MirrorRecovered struct {
1776+ state protoimpl.MessageState `protogen:"open.v1"`
1777+ // Repo is the repository name.
1778+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1779+ unknownFields protoimpl.UnknownFields
1780+ sizeCache protoimpl.SizeCache
1781+}
1782+
1783+func (x *MirrorRecovered) Reset() {
1784+ *x = MirrorRecovered{}
1785+ mi := &file_ermite_event_proto_msgTypes[25]
1786+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1787+ ms.StoreMessageInfo(mi)
1788+}
1789+
1790+func (x *MirrorRecovered) String() string {
1791+ return protoimpl.X.MessageStringOf(x)
1792+}
1793+
1794+func (*MirrorRecovered) ProtoMessage() {}
1795+
1796+func (x *MirrorRecovered) ProtoReflect() protoreflect.Message {
1797+ mi := &file_ermite_event_proto_msgTypes[25]
1798+ if x != nil {
1799+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1800+ if ms.LoadMessageInfo() == nil {
1801+ ms.StoreMessageInfo(mi)
1802+ }
1803+ return ms
1804+ }
1805+ return mi.MessageOf(x)
1806+}
1807+
1808+// Deprecated: Use MirrorRecovered.ProtoReflect.Descriptor instead.
1809+func (*MirrorRecovered) Descriptor() ([]byte, []int) {
1810+ return file_ermite_event_proto_rawDescGZIP(), []int{25}
1811+}
1812+
1813+func (x *MirrorRecovered) GetRepo() string {
1814+ if x != nil {
1815+ return x.Repo
1816+ }
1817+ return ""
1818+}
1819+
16711820 var File_ermite_event_proto protoreflect.FileDescriptor
16721821
16731822 const file_ermite_event_proto_rawDesc = "" +
16741823 "\n" +
1675- "\x12ermite/event.proto\x12\x06ermite\x1a\x12ermite/issue.proto\x1a\x1aermite/merge_request.proto\x1a\x11ermite/repo.proto\x1a\x10ermite/run.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"I\n" +
1824+ "\x12ermite/event.proto\x12\x06ermite\x1a\x12ermite/issue.proto\x1a\x1aermite/merge_request.proto\x1a\x13ermite/mirror.proto\x1a\x11ermite/repo.proto\x1a\x10ermite/run.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"I\n" +
16761825 "\fWatchRequest\x12\x16\n" +
16771826 "\x06topics\x18\x01 \x03(\tR\x06topics\x12!\n" +
16781827 "\fresume_after\x18\x02 \x01(\tR\vresumeAfter\"4\n" +
16791828 "\rWatchResponse\x12#\n" +
1680- "\x05event\x18\x01 \x01(\v2\r.ermite.EventR\x05event\"\xc8\n" +
1681- "\n" +
1829+ "\x05event\x18\x01 \x01(\v2\r.ermite.EventR\x05event\"\xcb\v\n" +
16821830 "\x05Event\x12\x0e\n" +
16831831 "\x02id\x18\x01 \x01(\tR\x02id\x12.\n" +
16841832 "\x04time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x04time\x128\n" +
@@ -1704,7 +1852,9 @@ const file_ermite_event_proto_rawDesc = "" +
17041852 "\x0eissue_reopened\x18\x13 \x01(\v2\x15.ermite.IssueReopenedH\x00R\rissueReopened\x12A\n" +
17051853 "\x0fissue_commented\x18\x14 \x01(\v2\x16.ermite.IssueCommentedH\x00R\x0eissueCommented\x12A\n" +
17061854 "\x0fissue_submitted\x18\x15 \x01(\v2\x16.ermite.IssueSubmittedH\x00R\x0eissueSubmitted\x12`\n" +
1707- "\x1amerge_request_mergeability\x18\x16 \x01(\v2 .ermite.MergeRequestMergeabilityH\x00R\x18mergeRequestMergeabilityB\x06\n" +
1855+ "\x1amerge_request_mergeability\x18\x16 \x01(\v2 .ermite.MergeRequestMergeabilityH\x00R\x18mergeRequestMergeability\x12;\n" +
1856+ "\rmirror_failed\x18\x17 \x01(\v2\x14.ermite.MirrorFailedH\x00R\fmirrorFailed\x12D\n" +
1857+ "\x10mirror_recovered\x18\x18 \x01(\v2\x17.ermite.MirrorRecoveredH\x00R\x0fmirrorRecoveredB\x06\n" +
17081858 "\x04kind\"/\n" +
17091859 "\vRepoCreated\x12 \n" +
17101860 "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"/\n" +
@@ -1765,7 +1915,13 @@ const file_ermite_event_proto_rawDesc = "" +
17651915 "\x03run\x18\x01 \x01(\v2\v.ermite.RunR\x03run\".\n" +
17661916 "\n" +
17671917 "JobUpdated\x12 \n" +
1768- "\x03job\x18\x01 \x01(\v2\x0e.ermite.RunJobR\x03job2F\n" +
1918+ "\x03job\x18\x01 \x01(\v2\x0e.ermite.RunJobR\x03job\"\x80\x01\n" +
1919+ "\fMirrorFailed\x12\x12\n" +
1920+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12)\n" +
1921+ "\x05state\x18\x02 \x01(\x0e2\x13.ermite.MirrorStateR\x05state\x12#\n" +
1922+ "\rfailure_class\x18\x03 \x01(\tR\ffailureClassJ\x04\b\x04\x10\x05R\x06reason\"%\n" +
1923+ "\x0fMirrorRecovered\x12\x12\n" +
1924+ "\x04repo\x18\x01 \x01(\tR\x04repo2F\n" +
17691925 "\fEventService\x126\n" +
17701926 "\x05Watch\x12\x14.ermite.WatchRequest\x1a\x15.ermite.WatchResponse0\x01Bq\n" +
17711927 "\n" +
@@ -1784,7 +1940,7 @@ func file_ermite_event_proto_rawDescGZIP() []byte {
17841940 return file_ermite_event_proto_rawDescData
17851941 }
17861942
1787-var file_ermite_event_proto_msgTypes = make([]protoimpl.MessageInfo, 24)
1943+var file_ermite_event_proto_msgTypes = make([]protoimpl.MessageInfo, 26)
17881944 var file_ermite_event_proto_goTypes = []any{
17891945 (*WatchRequest)(nil), // 0: ermite.WatchRequest
17901946 (*WatchResponse)(nil), // 1: ermite.WatchResponse
@@ -1810,19 +1966,22 @@ var file_ermite_event_proto_goTypes = []any{
18101966 (*Gap)(nil), // 21: ermite.Gap
18111967 (*RunUpdated)(nil), // 22: ermite.RunUpdated
18121968 (*JobUpdated)(nil), // 23: ermite.JobUpdated
1813- (*timestamppb.Timestamp)(nil), // 24: google.protobuf.Timestamp
1814- (*Repo)(nil), // 25: ermite.Repo
1815- (*MergeRequest)(nil), // 26: ermite.MergeRequest
1816- (*Mergeability)(nil), // 27: ermite.Mergeability
1817- (*Comment)(nil), // 28: ermite.Comment
1818- (*Issue)(nil), // 29: ermite.Issue
1819- (*IssueComment)(nil), // 30: ermite.IssueComment
1820- (*Run)(nil), // 31: ermite.Run
1821- (*RunJob)(nil), // 32: ermite.RunJob
1969+ (*MirrorFailed)(nil), // 24: ermite.MirrorFailed
1970+ (*MirrorRecovered)(nil), // 25: ermite.MirrorRecovered
1971+ (*timestamppb.Timestamp)(nil), // 26: google.protobuf.Timestamp
1972+ (*Repo)(nil), // 27: ermite.Repo
1973+ (*MergeRequest)(nil), // 28: ermite.MergeRequest
1974+ (*Mergeability)(nil), // 29: ermite.Mergeability
1975+ (*Comment)(nil), // 30: ermite.Comment
1976+ (*Issue)(nil), // 31: ermite.Issue
1977+ (*IssueComment)(nil), // 32: ermite.IssueComment
1978+ (*Run)(nil), // 33: ermite.Run
1979+ (*RunJob)(nil), // 34: ermite.RunJob
1980+ (MirrorState)(0), // 35: ermite.MirrorState
18221981 }
18231982 var file_ermite_event_proto_depIdxs = []int32{
18241983 2, // 0: ermite.WatchResponse.event:type_name -> ermite.Event
1825- 24, // 1: ermite.Event.time:type_name -> google.protobuf.Timestamp
1984+ 26, // 1: ermite.Event.time:type_name -> google.protobuf.Timestamp
18261985 3, // 2: ermite.Event.repo_created:type_name -> ermite.RepoCreated
18271986 4, // 3: ermite.Event.repo_updated:type_name -> ermite.RepoUpdated
18281987 5, // 4: ermite.Event.repo_deleted:type_name -> ermite.RepoDeleted
@@ -1843,30 +2002,33 @@ var file_ermite_event_proto_depIdxs = []int32{
18432002 19, // 19: ermite.Event.issue_commented:type_name -> ermite.IssueCommented
18442003 20, // 20: ermite.Event.issue_submitted:type_name -> ermite.IssueSubmitted
18452004 10, // 21: ermite.Event.merge_request_mergeability:type_name -> ermite.MergeRequestMergeability
1846- 25, // 22: ermite.RepoCreated.repo:type_name -> ermite.Repo
1847- 25, // 23: ermite.RepoUpdated.repo:type_name -> ermite.Repo
1848- 7, // 24: ermite.Pushed.ref_updates:type_name -> ermite.RefUpdate
1849- 26, // 25: ermite.MergeRequestOpened.merge_request:type_name -> ermite.MergeRequest
1850- 26, // 26: ermite.MergeRequestMerged.merge_request:type_name -> ermite.MergeRequest
1851- 27, // 27: ermite.MergeRequestMergeability.mergeability:type_name -> ermite.Mergeability
1852- 26, // 28: ermite.MergeRequestClosed.merge_request:type_name -> ermite.MergeRequest
1853- 28, // 29: ermite.CommentAdded.comment:type_name -> ermite.Comment
1854- 28, // 30: ermite.CommentEdited.comment:type_name -> ermite.Comment
1855- 28, // 31: ermite.CommentDeleted.comment:type_name -> ermite.Comment
1856- 28, // 32: ermite.CommentResolved.comment:type_name -> ermite.Comment
1857- 29, // 33: ermite.IssueOpened.issue:type_name -> ermite.Issue
1858- 29, // 34: ermite.IssueClosed.issue:type_name -> ermite.Issue
1859- 29, // 35: ermite.IssueReopened.issue:type_name -> ermite.Issue
1860- 30, // 36: ermite.IssueCommented.comment:type_name -> ermite.IssueComment
1861- 31, // 37: ermite.RunUpdated.run:type_name -> ermite.Run
1862- 32, // 38: ermite.JobUpdated.job:type_name -> ermite.RunJob
1863- 0, // 39: ermite.EventService.Watch:input_type -> ermite.WatchRequest
1864- 1, // 40: ermite.EventService.Watch:output_type -> ermite.WatchResponse
1865- 40, // [40:41] is the sub-list for method output_type
1866- 39, // [39:40] is the sub-list for method input_type
1867- 39, // [39:39] is the sub-list for extension type_name
1868- 39, // [39:39] is the sub-list for extension extendee
1869- 0, // [0:39] is the sub-list for field type_name
2005+ 24, // 22: ermite.Event.mirror_failed:type_name -> ermite.MirrorFailed
2006+ 25, // 23: ermite.Event.mirror_recovered:type_name -> ermite.MirrorRecovered
2007+ 27, // 24: ermite.RepoCreated.repo:type_name -> ermite.Repo
2008+ 27, // 25: ermite.RepoUpdated.repo:type_name -> ermite.Repo
2009+ 7, // 26: ermite.Pushed.ref_updates:type_name -> ermite.RefUpdate
2010+ 28, // 27: ermite.MergeRequestOpened.merge_request:type_name -> ermite.MergeRequest
2011+ 28, // 28: ermite.MergeRequestMerged.merge_request:type_name -> ermite.MergeRequest
2012+ 29, // 29: ermite.MergeRequestMergeability.mergeability:type_name -> ermite.Mergeability
2013+ 28, // 30: ermite.MergeRequestClosed.merge_request:type_name -> ermite.MergeRequest
2014+ 30, // 31: ermite.CommentAdded.comment:type_name -> ermite.Comment
2015+ 30, // 32: ermite.CommentEdited.comment:type_name -> ermite.Comment
2016+ 30, // 33: ermite.CommentDeleted.comment:type_name -> ermite.Comment
2017+ 30, // 34: ermite.CommentResolved.comment:type_name -> ermite.Comment
2018+ 31, // 35: ermite.IssueOpened.issue:type_name -> ermite.Issue
2019+ 31, // 36: ermite.IssueClosed.issue:type_name -> ermite.Issue
2020+ 31, // 37: ermite.IssueReopened.issue:type_name -> ermite.Issue
2021+ 32, // 38: ermite.IssueCommented.comment:type_name -> ermite.IssueComment
2022+ 33, // 39: ermite.RunUpdated.run:type_name -> ermite.Run
2023+ 34, // 40: ermite.JobUpdated.job:type_name -> ermite.RunJob
2024+ 35, // 41: ermite.MirrorFailed.state:type_name -> ermite.MirrorState
2025+ 0, // 42: ermite.EventService.Watch:input_type -> ermite.WatchRequest
2026+ 1, // 43: ermite.EventService.Watch:output_type -> ermite.WatchResponse
2027+ 43, // [43:44] is the sub-list for method output_type
2028+ 42, // [42:43] is the sub-list for method input_type
2029+ 42, // [42:42] is the sub-list for extension type_name
2030+ 42, // [42:42] is the sub-list for extension extendee
2031+ 0, // [0:42] is the sub-list for field type_name
18702032 }
18712033
18722034 func init() { file_ermite_event_proto_init() }
@@ -1876,6 +2038,7 @@ func file_ermite_event_proto_init() {
18762038 }
18772039 file_ermite_issue_proto_init()
18782040 file_ermite_merge_request_proto_init()
2041+ file_ermite_mirror_proto_init()
18792042 file_ermite_repo_proto_init()
18802043 file_ermite_run_proto_init()
18812044 file_ermite_event_proto_msgTypes[2].OneofWrappers = []any{
@@ -1899,6 +2062,8 @@ func file_ermite_event_proto_init() {
18992062 (*Event_IssueCommented)(nil),
19002063 (*Event_IssueSubmitted)(nil),
19012064 (*Event_MergeRequestMergeability)(nil),
2065+ (*Event_MirrorFailed)(nil),
2066+ (*Event_MirrorRecovered)(nil),
19022067 }
19032068 type x struct{}
19042069 out := protoimpl.TypeBuilder{
@@ -1906,7 +2071,7 @@ func file_ermite_event_proto_init() {
19062071 GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
19072072 RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_event_proto_rawDesc), len(file_ermite_event_proto_rawDesc)),
19082073 NumEnums: 0,
1909- NumMessages: 24,
2074+ NumMessages: 26,
19102075 NumExtensions: 0,
19112076 NumServices: 1,
19122077 },
@@ -171,6 +171,8 @@ type Event struct {
171 // *Event_IssueCommented171 // *Event_IssueCommented
172 // *Event_IssueSubmitted172 // *Event_IssueSubmitted
173 // *Event_MergeRequestMergeability173 // *Event_MergeRequestMergeability
174+ // *Event_MirrorFailed
175+ // *Event_MirrorRecovered
174 Kind isEvent_Kind `protobuf_oneof:"kind"`176 Kind isEvent_Kind `protobuf_oneof:"kind"`
175 unknownFields protoimpl.UnknownFields177 unknownFields protoimpl.UnknownFields
176 sizeCache protoimpl.SizeCache178 sizeCache protoimpl.SizeCache
@@ -407,6 +409,24 @@ func (x *Event) GetMergeRequestMergeability() *MergeRequestMergeability {
407 return nil409 return nil
408 }410 }
409 411
412+func (x *Event) GetMirrorFailed() *MirrorFailed {
413+ if x != nil {
414+ if x, ok := x.Kind.(*Event_MirrorFailed); ok {
415+ return x.MirrorFailed
416+ }
417+ }
418+ return nil
419+}
420+
421+func (x *Event) GetMirrorRecovered() *MirrorRecovered {
422+ if x != nil {
423+ if x, ok := x.Kind.(*Event_MirrorRecovered); ok {
424+ return x.MirrorRecovered
425+ }
426+ }
427+ return nil
428+}
429+
410 type isEvent_Kind interface {430 type isEvent_Kind interface {
411 isEvent_Kind()431 isEvent_Kind()
412 }432 }
@@ -513,6 +533,16 @@ type Event_MergeRequestMergeability struct {
513 MergeRequestMergeability *MergeRequestMergeability `protobuf:"bytes,22,opt,name=merge_request_mergeability,json=mergeRequestMergeability,proto3,oneof"`533 MergeRequestMergeability *MergeRequestMergeability `protobuf:"bytes,22,opt,name=merge_request_mergeability,json=mergeRequestMergeability,proto3,oneof"`
514 }534 }
515 535
536+type Event_MirrorFailed struct {
537+ // MirrorFailed: a mirror entered failing or paused.
538+ MirrorFailed *MirrorFailed `protobuf:"bytes,23,opt,name=mirror_failed,json=mirrorFailed,proto3,oneof"`
539+}
540+
541+type Event_MirrorRecovered struct {
542+ // MirrorRecovered: a failing or paused mirror succeeded again.
543+ MirrorRecovered *MirrorRecovered `protobuf:"bytes,24,opt,name=mirror_recovered,json=mirrorRecovered,proto3,oneof"`
544+}
545+
516 func (*Event_RepoCreated) isEvent_Kind() {}546 func (*Event_RepoCreated) isEvent_Kind() {}
517 547
518 func (*Event_RepoUpdated) isEvent_Kind() {}548 func (*Event_RepoUpdated) isEvent_Kind() {}
@@ -553,6 +583,10 @@ func (*Event_IssueSubmitted) isEvent_Kind() {}
553 583
554 func (*Event_MergeRequestMergeability) isEvent_Kind() {}584 func (*Event_MergeRequestMergeability) isEvent_Kind() {}
555 585
586+func (*Event_MirrorFailed) isEvent_Kind() {}
587+
588+func (*Event_MirrorRecovered) isEvent_Kind() {}
589+
556 // RepoCreated reports a new repository.590 // RepoCreated reports a new repository.
557 type RepoCreated struct {591 type RepoCreated struct {
558 state protoimpl.MessageState `protogen:"open.v1"`592 state protoimpl.MessageState `protogen:"open.v1"`
@@ -1668,17 +1702,131 @@ func (x *JobUpdated) GetJob() *RunJob {
1668 return nil1702 return nil
1669 }1703 }
1670 1704
1705+// MirrorFailed reports the start of a failure incident: a transition from ok
1706+// or disabled into failing or paused. It is sent once per incident, not for
1707+// each failed attempt nor for failing to paused. Servers with feature "mirror"
1708+// deliver it on "repo:<name>" and "home", subject to repository visibility. It
1709+// carries no URL, reason or error detail; read the status for those.
1710+type MirrorFailed struct {
1711+ state protoimpl.MessageState `protogen:"open.v1"`
1712+ // Repo is the repository name.
1713+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1714+ // State is the new mirror state.
1715+ State MirrorState `protobuf:"varint,2,opt,name=state,proto3,enum=ermite.MirrorState" json:"state,omitempty"`
1716+ // FailureClass is the failure category.
1717+ FailureClass string `protobuf:"bytes,3,opt,name=failure_class,json=failureClass,proto3" json:"failure_class,omitempty"`
1718+ unknownFields protoimpl.UnknownFields
1719+ sizeCache protoimpl.SizeCache
1720+}
1721+
1722+func (x *MirrorFailed) Reset() {
1723+ *x = MirrorFailed{}
1724+ mi := &file_ermite_event_proto_msgTypes[24]
1725+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1726+ ms.StoreMessageInfo(mi)
1727+}
1728+
1729+func (x *MirrorFailed) String() string {
1730+ return protoimpl.X.MessageStringOf(x)
1731+}
1732+
1733+func (*MirrorFailed) ProtoMessage() {}
1734+
1735+func (x *MirrorFailed) ProtoReflect() protoreflect.Message {
1736+ mi := &file_ermite_event_proto_msgTypes[24]
1737+ if x != nil {
1738+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1739+ if ms.LoadMessageInfo() == nil {
1740+ ms.StoreMessageInfo(mi)
1741+ }
1742+ return ms
1743+ }
1744+ return mi.MessageOf(x)
1745+}
1746+
1747+// Deprecated: Use MirrorFailed.ProtoReflect.Descriptor instead.
1748+func (*MirrorFailed) Descriptor() ([]byte, []int) {
1749+ return file_ermite_event_proto_rawDescGZIP(), []int{24}
1750+}
1751+
1752+func (x *MirrorFailed) GetRepo() string {
1753+ if x != nil {
1754+ return x.Repo
1755+ }
1756+ return ""
1757+}
1758+
1759+func (x *MirrorFailed) GetState() MirrorState {
1760+ if x != nil {
1761+ return x.State
1762+ }
1763+ return MirrorState_MIRROR_STATE_UNSPECIFIED
1764+}
1765+
1766+func (x *MirrorFailed) GetFailureClass() string {
1767+ if x != nil {
1768+ return x.FailureClass
1769+ }
1770+ return ""
1771+}
1772+
1773+// MirrorRecovered reports a successful sync after failing or paused. Delivery
1774+// follows MirrorFailed. It carries no URL or error detail.
1775+type MirrorRecovered struct {
1776+ state protoimpl.MessageState `protogen:"open.v1"`
1777+ // Repo is the repository name.
1778+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1779+ unknownFields protoimpl.UnknownFields
1780+ sizeCache protoimpl.SizeCache
1781+}
1782+
1783+func (x *MirrorRecovered) Reset() {
1784+ *x = MirrorRecovered{}
1785+ mi := &file_ermite_event_proto_msgTypes[25]
1786+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1787+ ms.StoreMessageInfo(mi)
1788+}
1789+
1790+func (x *MirrorRecovered) String() string {
1791+ return protoimpl.X.MessageStringOf(x)
1792+}
1793+
1794+func (*MirrorRecovered) ProtoMessage() {}
1795+
1796+func (x *MirrorRecovered) ProtoReflect() protoreflect.Message {
1797+ mi := &file_ermite_event_proto_msgTypes[25]
1798+ if x != nil {
1799+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1800+ if ms.LoadMessageInfo() == nil {
1801+ ms.StoreMessageInfo(mi)
1802+ }
1803+ return ms
1804+ }
1805+ return mi.MessageOf(x)
1806+}
1807+
1808+// Deprecated: Use MirrorRecovered.ProtoReflect.Descriptor instead.
1809+func (*MirrorRecovered) Descriptor() ([]byte, []int) {
1810+ return file_ermite_event_proto_rawDescGZIP(), []int{25}
1811+}
1812+
1813+func (x *MirrorRecovered) GetRepo() string {
1814+ if x != nil {
1815+ return x.Repo
1816+ }
1817+ return ""
1818+}
1819+
1671 var File_ermite_event_proto protoreflect.FileDescriptor1820 var File_ermite_event_proto protoreflect.FileDescriptor
1672 1821
1673 const file_ermite_event_proto_rawDesc = "" +1822 const file_ermite_event_proto_rawDesc = "" +
1674 "\n" +1823 "\n" +
1675- "\x12ermite/event.proto\x12\x06ermite\x1a\x12ermite/issue.proto\x1a\x1aermite/merge_request.proto\x1a\x11ermite/repo.proto\x1a\x10ermite/run.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"I\n" +1824+ "\x12ermite/event.proto\x12\x06ermite\x1a\x12ermite/issue.proto\x1a\x1aermite/merge_request.proto\x1a\x13ermite/mirror.proto\x1a\x11ermite/repo.proto\x1a\x10ermite/run.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"I\n" +
1676 "\fWatchRequest\x12\x16\n" +1825 "\fWatchRequest\x12\x16\n" +
1677 "\x06topics\x18\x01 \x03(\tR\x06topics\x12!\n" +1826 "\x06topics\x18\x01 \x03(\tR\x06topics\x12!\n" +
1678 "\fresume_after\x18\x02 \x01(\tR\vresumeAfter\"4\n" +1827 "\fresume_after\x18\x02 \x01(\tR\vresumeAfter\"4\n" +
1679 "\rWatchResponse\x12#\n" +1828 "\rWatchResponse\x12#\n" +
1680- "\x05event\x18\x01 \x01(\v2\r.ermite.EventR\x05event\"\xc8\n" +1829+ "\x05event\x18\x01 \x01(\v2\r.ermite.EventR\x05event\"\xcb\v\n" +
1681- "\n" +
1682 "\x05Event\x12\x0e\n" +1830 "\x05Event\x12\x0e\n" +
1683 "\x02id\x18\x01 \x01(\tR\x02id\x12.\n" +1831 "\x02id\x18\x01 \x01(\tR\x02id\x12.\n" +
1684 "\x04time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x04time\x128\n" +1832 "\x04time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x04time\x128\n" +
@@ -1704,7 +1852,9 @@ const file_ermite_event_proto_rawDesc = "" +
1704 "\x0eissue_reopened\x18\x13 \x01(\v2\x15.ermite.IssueReopenedH\x00R\rissueReopened\x12A\n" +1852 "\x0eissue_reopened\x18\x13 \x01(\v2\x15.ermite.IssueReopenedH\x00R\rissueReopened\x12A\n" +
1705 "\x0fissue_commented\x18\x14 \x01(\v2\x16.ermite.IssueCommentedH\x00R\x0eissueCommented\x12A\n" +1853 "\x0fissue_commented\x18\x14 \x01(\v2\x16.ermite.IssueCommentedH\x00R\x0eissueCommented\x12A\n" +
1706 "\x0fissue_submitted\x18\x15 \x01(\v2\x16.ermite.IssueSubmittedH\x00R\x0eissueSubmitted\x12`\n" +1854 "\x0fissue_submitted\x18\x15 \x01(\v2\x16.ermite.IssueSubmittedH\x00R\x0eissueSubmitted\x12`\n" +
1707- "\x1amerge_request_mergeability\x18\x16 \x01(\v2 .ermite.MergeRequestMergeabilityH\x00R\x18mergeRequestMergeabilityB\x06\n" +1855+ "\x1amerge_request_mergeability\x18\x16 \x01(\v2 .ermite.MergeRequestMergeabilityH\x00R\x18mergeRequestMergeability\x12;\n" +
1856+ "\rmirror_failed\x18\x17 \x01(\v2\x14.ermite.MirrorFailedH\x00R\fmirrorFailed\x12D\n" +
1857+ "\x10mirror_recovered\x18\x18 \x01(\v2\x17.ermite.MirrorRecoveredH\x00R\x0fmirrorRecoveredB\x06\n" +
1708 "\x04kind\"/\n" +1858 "\x04kind\"/\n" +
1709 "\vRepoCreated\x12 \n" +1859 "\vRepoCreated\x12 \n" +
1710 "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"/\n" +1860 "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"/\n" +
@@ -1765,7 +1915,13 @@ const file_ermite_event_proto_rawDesc = "" +
1765 "\x03run\x18\x01 \x01(\v2\v.ermite.RunR\x03run\".\n" +1915 "\x03run\x18\x01 \x01(\v2\v.ermite.RunR\x03run\".\n" +
1766 "\n" +1916 "\n" +
1767 "JobUpdated\x12 \n" +1917 "JobUpdated\x12 \n" +
1768- "\x03job\x18\x01 \x01(\v2\x0e.ermite.RunJobR\x03job2F\n" +1918+ "\x03job\x18\x01 \x01(\v2\x0e.ermite.RunJobR\x03job\"\x80\x01\n" +
1919+ "\fMirrorFailed\x12\x12\n" +
1920+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12)\n" +
1921+ "\x05state\x18\x02 \x01(\x0e2\x13.ermite.MirrorStateR\x05state\x12#\n" +
1922+ "\rfailure_class\x18\x03 \x01(\tR\ffailureClassJ\x04\b\x04\x10\x05R\x06reason\"%\n" +
1923+ "\x0fMirrorRecovered\x12\x12\n" +
1924+ "\x04repo\x18\x01 \x01(\tR\x04repo2F\n" +
1769 "\fEventService\x126\n" +1925 "\fEventService\x126\n" +
1770 "\x05Watch\x12\x14.ermite.WatchRequest\x1a\x15.ermite.WatchResponse0\x01Bq\n" +1926 "\x05Watch\x12\x14.ermite.WatchRequest\x1a\x15.ermite.WatchResponse0\x01Bq\n" +
1771 "\n" +1927 "\n" +
@@ -1784,7 +1940,7 @@ func file_ermite_event_proto_rawDescGZIP() []byte {
1784 return file_ermite_event_proto_rawDescData1940 return file_ermite_event_proto_rawDescData
1785 }1941 }
1786 1942
1787-var file_ermite_event_proto_msgTypes = make([]protoimpl.MessageInfo, 24)1943+var file_ermite_event_proto_msgTypes = make([]protoimpl.MessageInfo, 26)
1788 var file_ermite_event_proto_goTypes = []any{1944 var file_ermite_event_proto_goTypes = []any{
1789 (*WatchRequest)(nil), // 0: ermite.WatchRequest1945 (*WatchRequest)(nil), // 0: ermite.WatchRequest
1790 (*WatchResponse)(nil), // 1: ermite.WatchResponse1946 (*WatchResponse)(nil), // 1: ermite.WatchResponse
@@ -1810,19 +1966,22 @@ var file_ermite_event_proto_goTypes = []any{
1810 (*Gap)(nil), // 21: ermite.Gap1966 (*Gap)(nil), // 21: ermite.Gap
1811 (*RunUpdated)(nil), // 22: ermite.RunUpdated1967 (*RunUpdated)(nil), // 22: ermite.RunUpdated
1812 (*JobUpdated)(nil), // 23: ermite.JobUpdated1968 (*JobUpdated)(nil), // 23: ermite.JobUpdated
1813- (*timestamppb.Timestamp)(nil), // 24: google.protobuf.Timestamp1969+ (*MirrorFailed)(nil), // 24: ermite.MirrorFailed
1814- (*Repo)(nil), // 25: ermite.Repo1970+ (*MirrorRecovered)(nil), // 25: ermite.MirrorRecovered
1815- (*MergeRequest)(nil), // 26: ermite.MergeRequest1971+ (*timestamppb.Timestamp)(nil), // 26: google.protobuf.Timestamp
1816- (*Mergeability)(nil), // 27: ermite.Mergeability1972+ (*Repo)(nil), // 27: ermite.Repo
1817- (*Comment)(nil), // 28: ermite.Comment1973+ (*MergeRequest)(nil), // 28: ermite.MergeRequest
1818- (*Issue)(nil), // 29: ermite.Issue1974+ (*Mergeability)(nil), // 29: ermite.Mergeability
1819- (*IssueComment)(nil), // 30: ermite.IssueComment1975+ (*Comment)(nil), // 30: ermite.Comment
1820- (*Run)(nil), // 31: ermite.Run1976+ (*Issue)(nil), // 31: ermite.Issue
1821- (*RunJob)(nil), // 32: ermite.RunJob1977+ (*IssueComment)(nil), // 32: ermite.IssueComment
1978+ (*Run)(nil), // 33: ermite.Run
1979+ (*RunJob)(nil), // 34: ermite.RunJob
1980+ (MirrorState)(0), // 35: ermite.MirrorState
1822 }1981 }
1823 var file_ermite_event_proto_depIdxs = []int32{1982 var file_ermite_event_proto_depIdxs = []int32{
1824 2, // 0: ermite.WatchResponse.event:type_name -> ermite.Event1983 2, // 0: ermite.WatchResponse.event:type_name -> ermite.Event
1825- 24, // 1: ermite.Event.time:type_name -> google.protobuf.Timestamp1984+ 26, // 1: ermite.Event.time:type_name -> google.protobuf.Timestamp
1826 3, // 2: ermite.Event.repo_created:type_name -> ermite.RepoCreated1985 3, // 2: ermite.Event.repo_created:type_name -> ermite.RepoCreated
1827 4, // 3: ermite.Event.repo_updated:type_name -> ermite.RepoUpdated1986 4, // 3: ermite.Event.repo_updated:type_name -> ermite.RepoUpdated
1828 5, // 4: ermite.Event.repo_deleted:type_name -> ermite.RepoDeleted1987 5, // 4: ermite.Event.repo_deleted:type_name -> ermite.RepoDeleted
@@ -1843,30 +2002,33 @@ var file_ermite_event_proto_depIdxs = []int32{
1843 19, // 19: ermite.Event.issue_commented:type_name -> ermite.IssueCommented2002 19, // 19: ermite.Event.issue_commented:type_name -> ermite.IssueCommented
1844 20, // 20: ermite.Event.issue_submitted:type_name -> ermite.IssueSubmitted2003 20, // 20: ermite.Event.issue_submitted:type_name -> ermite.IssueSubmitted
1845 10, // 21: ermite.Event.merge_request_mergeability:type_name -> ermite.MergeRequestMergeability2004 10, // 21: ermite.Event.merge_request_mergeability:type_name -> ermite.MergeRequestMergeability
1846- 25, // 22: ermite.RepoCreated.repo:type_name -> ermite.Repo2005+ 24, // 22: ermite.Event.mirror_failed:type_name -> ermite.MirrorFailed
1847- 25, // 23: ermite.RepoUpdated.repo:type_name -> ermite.Repo2006+ 25, // 23: ermite.Event.mirror_recovered:type_name -> ermite.MirrorRecovered
1848- 7, // 24: ermite.Pushed.ref_updates:type_name -> ermite.RefUpdate2007+ 27, // 24: ermite.RepoCreated.repo:type_name -> ermite.Repo
1849- 26, // 25: ermite.MergeRequestOpened.merge_request:type_name -> ermite.MergeRequest2008+ 27, // 25: ermite.RepoUpdated.repo:type_name -> ermite.Repo
1850- 26, // 26: ermite.MergeRequestMerged.merge_request:type_name -> ermite.MergeRequest2009+ 7, // 26: ermite.Pushed.ref_updates:type_name -> ermite.RefUpdate
1851- 27, // 27: ermite.MergeRequestMergeability.mergeability:type_name -> ermite.Mergeability2010+ 28, // 27: ermite.MergeRequestOpened.merge_request:type_name -> ermite.MergeRequest
1852- 26, // 28: ermite.MergeRequestClosed.merge_request:type_name -> ermite.MergeRequest2011+ 28, // 28: ermite.MergeRequestMerged.merge_request:type_name -> ermite.MergeRequest
1853- 28, // 29: ermite.CommentAdded.comment:type_name -> ermite.Comment2012+ 29, // 29: ermite.MergeRequestMergeability.mergeability:type_name -> ermite.Mergeability
1854- 28, // 30: ermite.CommentEdited.comment:type_name -> ermite.Comment2013+ 28, // 30: ermite.MergeRequestClosed.merge_request:type_name -> ermite.MergeRequest
1855- 28, // 31: ermite.CommentDeleted.comment:type_name -> ermite.Comment2014+ 30, // 31: ermite.CommentAdded.comment:type_name -> ermite.Comment
1856- 28, // 32: ermite.CommentResolved.comment:type_name -> ermite.Comment2015+ 30, // 32: ermite.CommentEdited.comment:type_name -> ermite.Comment
1857- 29, // 33: ermite.IssueOpened.issue:type_name -> ermite.Issue2016+ 30, // 33: ermite.CommentDeleted.comment:type_name -> ermite.Comment
1858- 29, // 34: ermite.IssueClosed.issue:type_name -> ermite.Issue2017+ 30, // 34: ermite.CommentResolved.comment:type_name -> ermite.Comment
1859- 29, // 35: ermite.IssueReopened.issue:type_name -> ermite.Issue2018+ 31, // 35: ermite.IssueOpened.issue:type_name -> ermite.Issue
1860- 30, // 36: ermite.IssueCommented.comment:type_name -> ermite.IssueComment2019+ 31, // 36: ermite.IssueClosed.issue:type_name -> ermite.Issue
1861- 31, // 37: ermite.RunUpdated.run:type_name -> ermite.Run2020+ 31, // 37: ermite.IssueReopened.issue:type_name -> ermite.Issue
1862- 32, // 38: ermite.JobUpdated.job:type_name -> ermite.RunJob2021+ 32, // 38: ermite.IssueCommented.comment:type_name -> ermite.IssueComment
1863- 0, // 39: ermite.EventService.Watch:input_type -> ermite.WatchRequest2022+ 33, // 39: ermite.RunUpdated.run:type_name -> ermite.Run
1864- 1, // 40: ermite.EventService.Watch:output_type -> ermite.WatchResponse2023+ 34, // 40: ermite.JobUpdated.job:type_name -> ermite.RunJob
1865- 40, // [40:41] is the sub-list for method output_type2024+ 35, // 41: ermite.MirrorFailed.state:type_name -> ermite.MirrorState
1866- 39, // [39:40] is the sub-list for method input_type2025+ 0, // 42: ermite.EventService.Watch:input_type -> ermite.WatchRequest
1867- 39, // [39:39] is the sub-list for extension type_name2026+ 1, // 43: ermite.EventService.Watch:output_type -> ermite.WatchResponse
1868- 39, // [39:39] is the sub-list for extension extendee2027+ 43, // [43:44] is the sub-list for method output_type
1869- 0, // [0:39] is the sub-list for field type_name2028+ 42, // [42:43] is the sub-list for method input_type
2029+ 42, // [42:42] is the sub-list for extension type_name
2030+ 42, // [42:42] is the sub-list for extension extendee
2031+ 0, // [0:42] is the sub-list for field type_name
1870 }2032 }
1871 2033
1872 func init() { file_ermite_event_proto_init() }2034 func init() { file_ermite_event_proto_init() }
@@ -1876,6 +2038,7 @@ func file_ermite_event_proto_init() {
1876 }2038 }
1877 file_ermite_issue_proto_init()2039 file_ermite_issue_proto_init()
1878 file_ermite_merge_request_proto_init()2040 file_ermite_merge_request_proto_init()
2041+ file_ermite_mirror_proto_init()
1879 file_ermite_repo_proto_init()2042 file_ermite_repo_proto_init()
1880 file_ermite_run_proto_init()2043 file_ermite_run_proto_init()
1881 file_ermite_event_proto_msgTypes[2].OneofWrappers = []any{2044 file_ermite_event_proto_msgTypes[2].OneofWrappers = []any{
@@ -1899,6 +2062,8 @@ func file_ermite_event_proto_init() {
1899 (*Event_IssueCommented)(nil),2062 (*Event_IssueCommented)(nil),
1900 (*Event_IssueSubmitted)(nil),2063 (*Event_IssueSubmitted)(nil),
1901 (*Event_MergeRequestMergeability)(nil),2064 (*Event_MergeRequestMergeability)(nil),
2065+ (*Event_MirrorFailed)(nil),
2066+ (*Event_MirrorRecovered)(nil),
1902 }2067 }
1903 type x struct{}2068 type x struct{}
1904 out := protoimpl.TypeBuilder{2069 out := protoimpl.TypeBuilder{
@@ -1906,7 +2071,7 @@ func file_ermite_event_proto_init() {
1906 GoPackagePath: reflect.TypeOf(x{}).PkgPath(),2071 GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
1907 RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_event_proto_rawDesc), len(file_ermite_event_proto_rawDesc)),2072 RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_event_proto_rawDesc), len(file_ermite_event_proto_rawDesc)),
1908 NumEnums: 0,2073 NumEnums: 0,
1909- NumMessages: 24,2074+ NumMessages: 26,
1910 NumExtensions: 0,2075 NumExtensions: 0,
1911 NumServices: 1,2076 NumServices: 1,
1912 },2077 },
A gen/ermite/mirror.pb.go +1820 -0 generated file: collapsed
M gen_test.go +45 -0
@@ -19,6 +19,17 @@ import (
1919 // TestProcedures pins the procedure paths: they are the wire contract.
2020 func TestProcedures(t *testing.T) {
2121 tests := []struct{ got, want string }{
22+ {ermiteconnect.MirrorServiceGetMirrorKeyProcedure, "/ermite.MirrorService/GetMirrorKey"},
23+ {ermiteconnect.MirrorServiceRotateMirrorKeyProcedure, "/ermite.MirrorService/RotateMirrorKey"},
24+ {ermiteconnect.MirrorServiceGetMirrorConfigProcedure, "/ermite.MirrorService/GetMirrorConfig"},
25+ {ermiteconnect.MirrorServiceSetMirrorConfigProcedure, "/ermite.MirrorService/SetMirrorConfig"},
26+ {ermiteconnect.MirrorServiceSetRepoMirrorProcedure, "/ermite.MirrorService/SetRepoMirror"},
27+ {ermiteconnect.MirrorServiceRemoveRepoMirrorProcedure, "/ermite.MirrorService/RemoveRepoMirror"},
28+ {ermiteconnect.MirrorServiceSetRepoMirrorExcludedProcedure, "/ermite.MirrorService/SetRepoMirrorExcluded"},
29+ {ermiteconnect.MirrorServiceGetMirrorStatusProcedure, "/ermite.MirrorService/GetMirrorStatus"},
30+ {ermiteconnect.MirrorServiceListMirrorStatusProcedure, "/ermite.MirrorService/ListMirrorStatus"},
31+ {ermiteconnect.MirrorServiceSyncMirrorProcedure, "/ermite.MirrorService/SyncMirror"},
32+
2233 {ermiteconnect.RepoServiceListReposProcedure, "/ermite.RepoService/ListRepos"},
2334 {ermiteconnect.RepoServiceGetRepoProcedure, "/ermite.RepoService/GetRepo"},
2435 {ermiteconnect.RepoServiceCreateRepoProcedure, "/ermite.RepoService/CreateRepo"},
@@ -569,3 +580,37 @@ func TestCI3RoundTrip(t *testing.T) {
569580 }
570581 }
571582 }
583+
584+// TestMirrorWire pins event numbers and verifies presence of explicit zero patches.
585+func TestMirrorWire(t *testing.T) {
586+ fields := (&ermite.Event{}).ProtoReflect().Descriptor().Fields()
587+ for name, number := range map[protoreflect.Name]protoreflect.FieldNumber{"mirror_failed": 23, "mirror_recovered": 24} {
588+ if fd := fields.ByName(name); fd == nil || fd.Number() != number || fd.ContainingOneof().Name() != "kind" {
589+ t.Fatalf("Event.%s: %v, want kind field %d", name, fd, number)
590+ }
591+ }
592+ for _, msg := range []proto.Message{&ermite.MirrorFailed{}, &ermite.MirrorRecovered{}} {
593+ fields := msg.ProtoReflect().Descriptor().Fields()
594+ if fields.ByName("url") != nil || fields.ByName("detail") != nil {
595+ t.Fatalf("%T leaks URL or detail", msg)
596+ }
597+ }
598+ for _, in := range []proto.Message{
599+ &ermite.SetMirrorConfigRequest{Enabled: proto.Bool(false), UrlTemplate: proto.String(""), Cron: proto.String(""), AllBranches: proto.Bool(false)},
600+ &ermite.SetRepoMirrorRequest{Repo: "repo", AllBranches: proto.Bool(false)},
601+ &ermite.Event{Kind: &ermite.Event_MirrorFailed{MirrorFailed: &ermite.MirrorFailed{Repo: "repo", State: ermite.MirrorState_MIRROR_STATE_PAUSED, FailureClass: "diverged"}}},
602+ &ermite.Event{Kind: &ermite.Event_MirrorRecovered{MirrorRecovered: &ermite.MirrorRecovered{Repo: "repo"}}},
603+ } {
604+ b, err := proto.Marshal(in)
605+ if err != nil {
606+ t.Fatal(err)
607+ }
608+ out := in.ProtoReflect().New().Interface()
609+ if err := proto.Unmarshal(b, out); err != nil {
610+ t.Fatal(err)
611+ }
612+ if !proto.Equal(in, out) {
613+ t.Errorf("%T: round trip mismatch: %v != %v", in, out, in)
614+ }
615+ }
616+}
@@ -19,6 +19,17 @@ import (
19 // TestProcedures pins the procedure paths: they are the wire contract.19 // TestProcedures pins the procedure paths: they are the wire contract.
20 func TestProcedures(t *testing.T) {20 func TestProcedures(t *testing.T) {
21 tests := []struct{ got, want string }{21 tests := []struct{ got, want string }{
22+ {ermiteconnect.MirrorServiceGetMirrorKeyProcedure, "/ermite.MirrorService/GetMirrorKey"},
23+ {ermiteconnect.MirrorServiceRotateMirrorKeyProcedure, "/ermite.MirrorService/RotateMirrorKey"},
24+ {ermiteconnect.MirrorServiceGetMirrorConfigProcedure, "/ermite.MirrorService/GetMirrorConfig"},
25+ {ermiteconnect.MirrorServiceSetMirrorConfigProcedure, "/ermite.MirrorService/SetMirrorConfig"},
26+ {ermiteconnect.MirrorServiceSetRepoMirrorProcedure, "/ermite.MirrorService/SetRepoMirror"},
27+ {ermiteconnect.MirrorServiceRemoveRepoMirrorProcedure, "/ermite.MirrorService/RemoveRepoMirror"},
28+ {ermiteconnect.MirrorServiceSetRepoMirrorExcludedProcedure, "/ermite.MirrorService/SetRepoMirrorExcluded"},
29+ {ermiteconnect.MirrorServiceGetMirrorStatusProcedure, "/ermite.MirrorService/GetMirrorStatus"},
30+ {ermiteconnect.MirrorServiceListMirrorStatusProcedure, "/ermite.MirrorService/ListMirrorStatus"},
31+ {ermiteconnect.MirrorServiceSyncMirrorProcedure, "/ermite.MirrorService/SyncMirror"},
32+
22 {ermiteconnect.RepoServiceListReposProcedure, "/ermite.RepoService/ListRepos"},33 {ermiteconnect.RepoServiceListReposProcedure, "/ermite.RepoService/ListRepos"},
23 {ermiteconnect.RepoServiceGetRepoProcedure, "/ermite.RepoService/GetRepo"},34 {ermiteconnect.RepoServiceGetRepoProcedure, "/ermite.RepoService/GetRepo"},
24 {ermiteconnect.RepoServiceCreateRepoProcedure, "/ermite.RepoService/CreateRepo"},35 {ermiteconnect.RepoServiceCreateRepoProcedure, "/ermite.RepoService/CreateRepo"},
@@ -569,3 +580,37 @@ func TestCI3RoundTrip(t *testing.T) {
569 }580 }
570 }581 }
571 }582 }
583+
584+// TestMirrorWire pins event numbers and verifies presence of explicit zero patches.
585+func TestMirrorWire(t *testing.T) {
586+ fields := (&ermite.Event{}).ProtoReflect().Descriptor().Fields()
587+ for name, number := range map[protoreflect.Name]protoreflect.FieldNumber{"mirror_failed": 23, "mirror_recovered": 24} {
588+ if fd := fields.ByName(name); fd == nil || fd.Number() != number || fd.ContainingOneof().Name() != "kind" {
589+ t.Fatalf("Event.%s: %v, want kind field %d", name, fd, number)
590+ }
591+ }
592+ for _, msg := range []proto.Message{&ermite.MirrorFailed{}, &ermite.MirrorRecovered{}} {
593+ fields := msg.ProtoReflect().Descriptor().Fields()
594+ if fields.ByName("url") != nil || fields.ByName("detail") != nil {
595+ t.Fatalf("%T leaks URL or detail", msg)
596+ }
597+ }
598+ for _, in := range []proto.Message{
599+ &ermite.SetMirrorConfigRequest{Enabled: proto.Bool(false), UrlTemplate: proto.String(""), Cron: proto.String(""), AllBranches: proto.Bool(false)},
600+ &ermite.SetRepoMirrorRequest{Repo: "repo", AllBranches: proto.Bool(false)},
601+ &ermite.Event{Kind: &ermite.Event_MirrorFailed{MirrorFailed: &ermite.MirrorFailed{Repo: "repo", State: ermite.MirrorState_MIRROR_STATE_PAUSED, FailureClass: "diverged"}}},
602+ &ermite.Event{Kind: &ermite.Event_MirrorRecovered{MirrorRecovered: &ermite.MirrorRecovered{Repo: "repo"}}},
603+ } {
604+ b, err := proto.Marshal(in)
605+ if err != nil {
606+ t.Fatal(err)
607+ }
608+ out := in.ProtoReflect().New().Interface()
609+ if err := proto.Unmarshal(b, out); err != nil {
610+ t.Fatal(err)
611+ }
612+ if !proto.Equal(in, out) {
613+ t.Errorf("%T: round trip mismatch: %v != %v", in, out, in)
614+ }
615+ }
616+}
M proto/ermite/event.proto +29 -0
@@ -6,6 +6,7 @@ package ermite;
66
77 import "ermite/issue.proto";
88 import "ermite/merge_request.proto";
9+import "ermite/mirror.proto";
910 import "ermite/repo.proto";
1011 import "ermite/run.proto";
1112 import "google/protobuf/timestamp.proto";
@@ -135,6 +136,10 @@ message Event {
135136 // MergeRequestMergeability: the mergeability of an open merge request
136137 // changed.
137138 MergeRequestMergeability merge_request_mergeability = 22;
139+ // MirrorFailed: a mirror entered failing or paused.
140+ MirrorFailed mirror_failed = 23;
141+ // MirrorRecovered: a failing or paused mirror succeeded again.
142+ MirrorRecovered mirror_recovered = 24;
138143 }
139144 }
140145
@@ -306,3 +311,27 @@ message JobUpdated {
306311 // Job is the job after the change, with its steps.
307312 RunJob job = 1;
308313 }
314+
315+// MirrorFailed reports the start of a failure incident: a transition from ok
316+// or disabled into failing or paused. It is sent once per incident, not for
317+// each failed attempt nor for failing to paused. Servers with feature "mirror"
318+// deliver it on "repo:<name>" and "home", subject to repository visibility. It
319+// carries no URL, reason or error detail; read the status for those.
320+message MirrorFailed {
321+ // Repo is the repository name.
322+ string repo = 1;
323+ // State is the new mirror state.
324+ MirrorState state = 2;
325+ // FailureClass is the failure category.
326+ string failure_class = 3;
327+
328+ reserved 4;
329+ reserved "reason";
330+}
331+
332+// MirrorRecovered reports a successful sync after failing or paused. Delivery
333+// follows MirrorFailed. It carries no URL or error detail.
334+message MirrorRecovered {
335+ // Repo is the repository name.
336+ string repo = 1;
337+}
@@ -6,6 +6,7 @@ package ermite;
6 6
7 import "ermite/issue.proto";7 import "ermite/issue.proto";
8 import "ermite/merge_request.proto";8 import "ermite/merge_request.proto";
9+import "ermite/mirror.proto";
9 import "ermite/repo.proto";10 import "ermite/repo.proto";
10 import "ermite/run.proto";11 import "ermite/run.proto";
11 import "google/protobuf/timestamp.proto";12 import "google/protobuf/timestamp.proto";
@@ -135,6 +136,10 @@ message Event {
135 // MergeRequestMergeability: the mergeability of an open merge request136 // MergeRequestMergeability: the mergeability of an open merge request
136 // changed.137 // changed.
137 MergeRequestMergeability merge_request_mergeability = 22;138 MergeRequestMergeability merge_request_mergeability = 22;
139+ // MirrorFailed: a mirror entered failing or paused.
140+ MirrorFailed mirror_failed = 23;
141+ // MirrorRecovered: a failing or paused mirror succeeded again.
142+ MirrorRecovered mirror_recovered = 24;
138 }143 }
139 }144 }
140 145
@@ -306,3 +311,27 @@ message JobUpdated {
306 // Job is the job after the change, with its steps.311 // Job is the job after the change, with its steps.
307 RunJob job = 1;312 RunJob job = 1;
308 }313 }
314+
315+// MirrorFailed reports the start of a failure incident: a transition from ok
316+// or disabled into failing or paused. It is sent once per incident, not for
317+// each failed attempt nor for failing to paused. Servers with feature "mirror"
318+// deliver it on "repo:<name>" and "home", subject to repository visibility. It
319+// carries no URL, reason or error detail; read the status for those.
320+message MirrorFailed {
321+ // Repo is the repository name.
322+ string repo = 1;
323+ // State is the new mirror state.
324+ MirrorState state = 2;
325+ // FailureClass is the failure category.
326+ string failure_class = 3;
327+
328+ reserved 4;
329+ reserved "reason";
330+}
331+
332+// MirrorRecovered reports a successful sync after failing or paused. Delivery
333+// follows MirrorFailed. It carries no URL or error detail.
334+message MirrorRecovered {
335+ // Repo is the repository name.
336+ string repo = 1;
337+}
A proto/ermite/mirror.proto +289 -0
@@ -0,0 +1,289 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "google/protobuf/timestamp.proto";
8+
9+// MirrorService configures the push mirrors of the server. Global settings and
10+// key rotation need the admin token or a token with every scope and no
11+// repository or branch limits; per-repository settings and SyncMirror need
12+// repo:admin on the repository; status needs read access. GetMirrorKey needs a
13+// non-anonymous caller. ListMirrorStatus and SyncMirror with All need owner
14+// rights. No new token scope is required. Servers implementing this service
15+// list "mirror" in GetVersionResponse.features.
16+service MirrorService {
17+ // GetMirrorKey returns the public mirror key, generating it on first use.
18+ // Needs a non-anonymous caller. Fails with failed_precondition without
19+ // ERMITE_SECRETS_KEY.
20+ rpc GetMirrorKey(GetMirrorKeyRequest) returns (GetMirrorKeyResponse);
21+ // RotateMirrorKey replaces the key immediately. Add the new key to remotes
22+ // and sync with force to recover from authentication failures.
23+ rpc RotateMirrorKey(RotateMirrorKeyRequest) returns (RotateMirrorKeyResponse);
24+ // GetMirrorConfig returns the global mirror policy.
25+ rpc GetMirrorConfig(GetMirrorConfigRequest) returns (GetMirrorConfigResponse);
26+ // SetMirrorConfig patches the global policy. Unset fields are unchanged.
27+ // Invalid fields give invalid_argument with field violations.
28+ rpc SetMirrorConfig(SetMirrorConfigRequest) returns (SetMirrorConfigResponse);
29+ // SetRepoMirror sets an override, even when global mirroring is disabled.
30+ rpc SetRepoMirror(SetRepoMirrorRequest) returns (SetRepoMirrorResponse);
31+ // RemoveRepoMirror removes the override so the global policy applies again.
32+ rpc RemoveRepoMirror(RemoveRepoMirrorRequest) returns (RemoveRepoMirrorResponse);
33+ // SetRepoMirrorExcluded excludes or includes a repository in the global
34+ // policy. An explicit URL override takes precedence.
35+ rpc SetRepoMirrorExcluded(SetRepoMirrorExcludedRequest) returns (SetRepoMirrorExcludedResponse);
36+ // GetMirrorStatus returns the resolved target and status of one repository.
37+ rpc GetMirrorStatus(GetMirrorStatusRequest) returns (GetMirrorStatusResponse);
38+ // ListMirrorStatus lists mirror statuses and needs owner rights.
39+ rpc ListMirrorStatus(ListMirrorStatusRequest) returns (ListMirrorStatusResponse);
40+ // SyncMirror syncs Repo or, with All, every repository. Paused mirrors need
41+ // Force. Manual requests without Force are at least 60 seconds apart.
42+ rpc SyncMirror(SyncMirrorRequest) returns (SyncMirrorResponse);
43+}
44+
45+// MirrorKey is the public SSH key used for push mirrors. Private key material
46+// is never returned.
47+message MirrorKey {
48+ // AuthorizedKey is the SSH authorized_keys line.
49+ string authorized_key = 1;
50+ // Fingerprint is the SHA256 fingerprint of the public key.
51+ string fingerprint = 2;
52+ // CreatedAt is when the key was created.
53+ google.protobuf.Timestamp created_at = 3;
54+}
55+
56+// GetMirrorKeyRequest selects the input of GetMirrorKey.
57+message GetMirrorKeyRequest {}
58+
59+// GetMirrorKeyResponse holds the result of GetMirrorKey.
60+message GetMirrorKeyResponse {
61+ // Key is the public mirror key.
62+ MirrorKey key = 1;
63+}
64+
65+// RotateMirrorKeyRequest selects the input of RotateMirrorKey.
66+message RotateMirrorKeyRequest {}
67+
68+// RotateMirrorKeyResponse holds the result of RotateMirrorKey.
69+message RotateMirrorKeyResponse {
70+ // Key is the public mirror key.
71+ MirrorKey key = 1;
72+ // PreviousFingerprint is the fingerprint of the replaced key.
73+ string previous_fingerprint = 2;
74+}
75+
76+// MirrorConfig is the server-wide push mirror policy.
77+message MirrorConfig {
78+ // Enabled says whether mirroring is enabled.
79+ bool enabled = 1;
80+ // URLTemplate is an SSH URL containing "{repo}", replaced with the
81+ // repository name.
82+ string url_template = 2;
83+ // Cron is a five-field cron expression in UTC, no more frequent than every
84+ // five minutes.
85+ string cron = 3;
86+ // AllBranches includes every branch rather than only the default branch.
87+ // Tags are always included.
88+ bool all_branches = 4;
89+ // UpdatedAt is when the configuration was last changed.
90+ google.protobuf.Timestamp updated_at = 5;
91+}
92+
93+// GetMirrorConfigRequest selects the input of GetMirrorConfig.
94+message GetMirrorConfigRequest {}
95+
96+// GetMirrorConfigResponse holds the result of GetMirrorConfig.
97+message GetMirrorConfigResponse {
98+ // Config is the global mirror policy.
99+ MirrorConfig config = 1;
100+}
101+
102+// SetMirrorConfigRequest selects the input of SetMirrorConfig.
103+message SetMirrorConfigRequest {
104+ // Enabled says whether mirroring is enabled. Unset means unchanged.
105+ optional bool enabled = 1;
106+ // URLTemplate is an SSH URL containing "{repo}", replaced with the
107+ // repository name. Unset means unchanged.
108+ optional string url_template = 2;
109+ // Cron is a five-field cron expression in UTC, no more frequent than every
110+ // five minutes. Unset means unchanged.
111+ optional string cron = 3;
112+ // AllBranches includes every branch rather than only the default branch.
113+ // Tags are always included. Unset means unchanged.
114+ optional bool all_branches = 4;
115+}
116+
117+// SetMirrorConfigResponse holds the result of SetMirrorConfig.
118+message SetMirrorConfigResponse {
119+ // Config is the global mirror policy.
120+ MirrorConfig config = 1;
121+}
122+
123+// MirrorSource says which configuration applies to a repository.
124+enum MirrorSource {
125+ // MIRROR_SOURCE_DISABLED: nothing applies; MIRROR_SOURCE_GLOBAL: the global
126+ // template; MIRROR_SOURCE_OVERRIDE: a per-repository URL;
127+ // MIRROR_SOURCE_EXCLUDED: opted out of the global template.
128+ MIRROR_SOURCE_UNSPECIFIED = 0;
129+ MIRROR_SOURCE_DISABLED = 1;
130+ MIRROR_SOURCE_GLOBAL = 2;
131+ MIRROR_SOURCE_OVERRIDE = 3;
132+ MIRROR_SOURCE_EXCLUDED = 4;
133+}
134+
135+// MirrorTarget is the resolved push mirror target of a repository.
136+message MirrorTarget {
137+ // Repo is the repository name.
138+ string repo = 1;
139+ // Enabled says whether mirroring is enabled.
140+ bool enabled = 2;
141+ // URL is the SSH destination of the mirror.
142+ string url = 3;
143+ // Cron is a five-field cron expression in UTC, no more frequent than every
144+ // five minutes.
145+ string cron = 4;
146+ // AllBranches includes every branch rather than only the default branch.
147+ // Tags are always included.
148+ bool all_branches = 5;
149+ // Source is the configuration used to resolve the target.
150+ MirrorSource source = 6;
151+}
152+
153+// SetRepoMirrorRequest selects the input of SetRepoMirror.
154+message SetRepoMirrorRequest {
155+ // Repo is the repository name.
156+ string repo = 1;
157+ // URL is the SSH destination of the mirror.
158+ string url = 2;
159+ // Cron is a five-field cron expression in UTC, no more frequent than every
160+ // five minutes.
161+ // Empty means inherit the global cron.
162+ string cron = 3;
163+ // AllBranches includes every branch rather than only the default branch.
164+ // Tags are always included. Unset means inherit the global setting.
165+ optional bool all_branches = 4;
166+}
167+
168+// SetRepoMirrorResponse holds the result of SetRepoMirror.
169+message SetRepoMirrorResponse {
170+ // Target is the resolved mirror target.
171+ MirrorTarget target = 1;
172+}
173+
174+// RemoveRepoMirrorRequest selects the input of RemoveRepoMirror.
175+message RemoveRepoMirrorRequest {
176+ // Repo is the repository name.
177+ string repo = 1;
178+}
179+
180+// RemoveRepoMirrorResponse holds the result of RemoveRepoMirror.
181+message RemoveRepoMirrorResponse {}
182+
183+// SetRepoMirrorExcludedRequest selects the input of SetRepoMirrorExcluded.
184+message SetRepoMirrorExcludedRequest {
185+ // Repo is the repository name.
186+ string repo = 1;
187+ // Excluded opts out of the global template, unless a URL override exists.
188+ bool excluded = 2;
189+}
190+
191+// SetRepoMirrorExcludedResponse holds the result of SetRepoMirrorExcluded.
192+message SetRepoMirrorExcludedResponse {
193+ // Target is the resolved mirror target.
194+ MirrorTarget target = 1;
195+}
196+
197+// MirrorState is the current state of a push mirror.
198+enum MirrorState {
199+ // MIRROR_STATE_OK: last sync succeeded; MIRROR_STATE_FAILING: retrying with
200+ // backoff; MIRROR_STATE_PAUSED: stopped until a forced sync or a config change.
201+ MIRROR_STATE_UNSPECIFIED = 0;
202+ MIRROR_STATE_DISABLED = 1;
203+ MIRROR_STATE_OK = 2;
204+ MIRROR_STATE_FAILING = 3;
205+ MIRROR_STATE_PAUSED = 4;
206+}
207+
208+// MirrorStatus describes a repository mirror and its last attempts.
209+message MirrorStatus {
210+ // Target is the resolved mirror target.
211+ MirrorTarget target = 1;
212+ // State is the current mirror state.
213+ MirrorState state = 2;
214+ // FailureClass is the failure category, empty when there is no failure.
215+ string failure_class = 3;
216+ // Reason explains the current state.
217+ string reason = 4;
218+ // Detail is the last error detail, returned only to callers with repo:admin
219+ // on the repository.
220+ string detail = 5;
221+ // ConsecutiveFailures counts failed attempts since the last success or
222+ // reset.
223+ int32 consecutive_failures = 6;
224+ // LastAttemptAt is when the last sync was attempted.
225+ google.protobuf.Timestamp last_attempt_at = 7;
226+ // LastSuccessAt is when the last sync succeeded, including a no-op.
227+ google.protobuf.Timestamp last_success_at = 8;
228+ // NextRetryAt is the next retry time while failing; otherwise unset.
229+ google.protobuf.Timestamp next_retry_at = 9;
230+ // NextRunAt is the next scheduled sync time, or the retry time while
231+ // failing.
232+ google.protobuf.Timestamp next_run_at = 10;
233+ // PausedAt is when the mirror was paused; otherwise unset.
234+ google.protobuf.Timestamp paused_at = 11;
235+}
236+
237+// GetMirrorStatusRequest selects the input of GetMirrorStatus.
238+message GetMirrorStatusRequest {
239+ // Repo is the repository name.
240+ string repo = 1;
241+}
242+
243+// GetMirrorStatusResponse holds the result of GetMirrorStatus.
244+message GetMirrorStatusResponse {
245+ // Status is the current mirror status.
246+ MirrorStatus status = 1;
247+}
248+
249+// ListMirrorStatusRequest selects the input of ListMirrorStatus.
250+message ListMirrorStatusRequest {}
251+
252+// ListMirrorStatusResponse holds the result of ListMirrorStatus.
253+message ListMirrorStatusResponse {
254+ // Statuses are the mirror statuses.
255+ repeated MirrorStatus statuses = 1;
256+}
257+
258+// SyncMirrorRequest selects the input of SyncMirror.
259+message SyncMirrorRequest {
260+ // Repo is the repository name.
261+ string repo = 1;
262+ // All selects every repository and needs owner rights. Repo must be empty
263+ // when true.
264+ bool all = 2;
265+ // Force lifts a pause and skips the no-op check and the 60-second manual
266+ // throttle. It never forces a Git push.
267+ bool force = 3;
268+}
269+
270+// SyncMirrorResult is the result of a sync request for one repository.
271+message SyncMirrorResult {
272+ // Repo is the repository name.
273+ string repo = 1;
274+ // Queued says another sync is running and this request was queued.
275+ bool queued = 2;
276+ // NoOp says the refs already matched the last successful snapshot.
277+ bool no_op = 3;
278+ // Status is the current mirror status.
279+ MirrorStatus status = 4;
280+ // Error is a human-readable failure of this repository's sync, empty on
281+ // success. It never contains the mirror URL.
282+ string error = 5;
283+}
284+
285+// SyncMirrorResponse holds the result of SyncMirror.
286+message SyncMirrorResponse {
287+ // Results are the per-repository sync results.
288+ repeated SyncMirrorResult results = 1;
289+}
@@ -0,0 +1,289 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "google/protobuf/timestamp.proto";
8+
9+// MirrorService configures the push mirrors of the server. Global settings and
10+// key rotation need the admin token or a token with every scope and no
11+// repository or branch limits; per-repository settings and SyncMirror need
12+// repo:admin on the repository; status needs read access. GetMirrorKey needs a
13+// non-anonymous caller. ListMirrorStatus and SyncMirror with All need owner
14+// rights. No new token scope is required. Servers implementing this service
15+// list "mirror" in GetVersionResponse.features.
16+service MirrorService {
17+ // GetMirrorKey returns the public mirror key, generating it on first use.
18+ // Needs a non-anonymous caller. Fails with failed_precondition without
19+ // ERMITE_SECRETS_KEY.
20+ rpc GetMirrorKey(GetMirrorKeyRequest) returns (GetMirrorKeyResponse);
21+ // RotateMirrorKey replaces the key immediately. Add the new key to remotes
22+ // and sync with force to recover from authentication failures.
23+ rpc RotateMirrorKey(RotateMirrorKeyRequest) returns (RotateMirrorKeyResponse);
24+ // GetMirrorConfig returns the global mirror policy.
25+ rpc GetMirrorConfig(GetMirrorConfigRequest) returns (GetMirrorConfigResponse);
26+ // SetMirrorConfig patches the global policy. Unset fields are unchanged.
27+ // Invalid fields give invalid_argument with field violations.
28+ rpc SetMirrorConfig(SetMirrorConfigRequest) returns (SetMirrorConfigResponse);
29+ // SetRepoMirror sets an override, even when global mirroring is disabled.
30+ rpc SetRepoMirror(SetRepoMirrorRequest) returns (SetRepoMirrorResponse);
31+ // RemoveRepoMirror removes the override so the global policy applies again.
32+ rpc RemoveRepoMirror(RemoveRepoMirrorRequest) returns (RemoveRepoMirrorResponse);
33+ // SetRepoMirrorExcluded excludes or includes a repository in the global
34+ // policy. An explicit URL override takes precedence.
35+ rpc SetRepoMirrorExcluded(SetRepoMirrorExcludedRequest) returns (SetRepoMirrorExcludedResponse);
36+ // GetMirrorStatus returns the resolved target and status of one repository.
37+ rpc GetMirrorStatus(GetMirrorStatusRequest) returns (GetMirrorStatusResponse);
38+ // ListMirrorStatus lists mirror statuses and needs owner rights.
39+ rpc ListMirrorStatus(ListMirrorStatusRequest) returns (ListMirrorStatusResponse);
40+ // SyncMirror syncs Repo or, with All, every repository. Paused mirrors need
41+ // Force. Manual requests without Force are at least 60 seconds apart.
42+ rpc SyncMirror(SyncMirrorRequest) returns (SyncMirrorResponse);
43+}
44+
45+// MirrorKey is the public SSH key used for push mirrors. Private key material
46+// is never returned.
47+message MirrorKey {
48+ // AuthorizedKey is the SSH authorized_keys line.
49+ string authorized_key = 1;
50+ // Fingerprint is the SHA256 fingerprint of the public key.
51+ string fingerprint = 2;
52+ // CreatedAt is when the key was created.
53+ google.protobuf.Timestamp created_at = 3;
54+}
55+
56+// GetMirrorKeyRequest selects the input of GetMirrorKey.
57+message GetMirrorKeyRequest {}
58+
59+// GetMirrorKeyResponse holds the result of GetMirrorKey.
60+message GetMirrorKeyResponse {
61+ // Key is the public mirror key.
62+ MirrorKey key = 1;
63+}
64+
65+// RotateMirrorKeyRequest selects the input of RotateMirrorKey.
66+message RotateMirrorKeyRequest {}
67+
68+// RotateMirrorKeyResponse holds the result of RotateMirrorKey.
69+message RotateMirrorKeyResponse {
70+ // Key is the public mirror key.
71+ MirrorKey key = 1;
72+ // PreviousFingerprint is the fingerprint of the replaced key.
73+ string previous_fingerprint = 2;
74+}
75+
76+// MirrorConfig is the server-wide push mirror policy.
77+message MirrorConfig {
78+ // Enabled says whether mirroring is enabled.
79+ bool enabled = 1;
80+ // URLTemplate is an SSH URL containing "{repo}", replaced with the
81+ // repository name.
82+ string url_template = 2;
83+ // Cron is a five-field cron expression in UTC, no more frequent than every
84+ // five minutes.
85+ string cron = 3;
86+ // AllBranches includes every branch rather than only the default branch.
87+ // Tags are always included.
88+ bool all_branches = 4;
89+ // UpdatedAt is when the configuration was last changed.
90+ google.protobuf.Timestamp updated_at = 5;
91+}
92+
93+// GetMirrorConfigRequest selects the input of GetMirrorConfig.
94+message GetMirrorConfigRequest {}
95+
96+// GetMirrorConfigResponse holds the result of GetMirrorConfig.
97+message GetMirrorConfigResponse {
98+ // Config is the global mirror policy.
99+ MirrorConfig config = 1;
100+}
101+
102+// SetMirrorConfigRequest selects the input of SetMirrorConfig.
103+message SetMirrorConfigRequest {
104+ // Enabled says whether mirroring is enabled. Unset means unchanged.
105+ optional bool enabled = 1;
106+ // URLTemplate is an SSH URL containing "{repo}", replaced with the
107+ // repository name. Unset means unchanged.
108+ optional string url_template = 2;
109+ // Cron is a five-field cron expression in UTC, no more frequent than every
110+ // five minutes. Unset means unchanged.
111+ optional string cron = 3;
112+ // AllBranches includes every branch rather than only the default branch.
113+ // Tags are always included. Unset means unchanged.
114+ optional bool all_branches = 4;
115+}
116+
117+// SetMirrorConfigResponse holds the result of SetMirrorConfig.
118+message SetMirrorConfigResponse {
119+ // Config is the global mirror policy.
120+ MirrorConfig config = 1;
121+}
122+
123+// MirrorSource says which configuration applies to a repository.
124+enum MirrorSource {
125+ // MIRROR_SOURCE_DISABLED: nothing applies; MIRROR_SOURCE_GLOBAL: the global
126+ // template; MIRROR_SOURCE_OVERRIDE: a per-repository URL;
127+ // MIRROR_SOURCE_EXCLUDED: opted out of the global template.
128+ MIRROR_SOURCE_UNSPECIFIED = 0;
129+ MIRROR_SOURCE_DISABLED = 1;
130+ MIRROR_SOURCE_GLOBAL = 2;
131+ MIRROR_SOURCE_OVERRIDE = 3;
132+ MIRROR_SOURCE_EXCLUDED = 4;
133+}
134+
135+// MirrorTarget is the resolved push mirror target of a repository.
136+message MirrorTarget {
137+ // Repo is the repository name.
138+ string repo = 1;
139+ // Enabled says whether mirroring is enabled.
140+ bool enabled = 2;
141+ // URL is the SSH destination of the mirror.
142+ string url = 3;
143+ // Cron is a five-field cron expression in UTC, no more frequent than every
144+ // five minutes.
145+ string cron = 4;
146+ // AllBranches includes every branch rather than only the default branch.
147+ // Tags are always included.
148+ bool all_branches = 5;
149+ // Source is the configuration used to resolve the target.
150+ MirrorSource source = 6;
151+}
152+
153+// SetRepoMirrorRequest selects the input of SetRepoMirror.
154+message SetRepoMirrorRequest {
155+ // Repo is the repository name.
156+ string repo = 1;
157+ // URL is the SSH destination of the mirror.
158+ string url = 2;
159+ // Cron is a five-field cron expression in UTC, no more frequent than every
160+ // five minutes.
161+ // Empty means inherit the global cron.
162+ string cron = 3;
163+ // AllBranches includes every branch rather than only the default branch.
164+ // Tags are always included. Unset means inherit the global setting.
165+ optional bool all_branches = 4;
166+}
167+
168+// SetRepoMirrorResponse holds the result of SetRepoMirror.
169+message SetRepoMirrorResponse {
170+ // Target is the resolved mirror target.
171+ MirrorTarget target = 1;
172+}
173+
174+// RemoveRepoMirrorRequest selects the input of RemoveRepoMirror.
175+message RemoveRepoMirrorRequest {
176+ // Repo is the repository name.
177+ string repo = 1;
178+}
179+
180+// RemoveRepoMirrorResponse holds the result of RemoveRepoMirror.
181+message RemoveRepoMirrorResponse {}
182+
183+// SetRepoMirrorExcludedRequest selects the input of SetRepoMirrorExcluded.
184+message SetRepoMirrorExcludedRequest {
185+ // Repo is the repository name.
186+ string repo = 1;
187+ // Excluded opts out of the global template, unless a URL override exists.
188+ bool excluded = 2;
189+}
190+
191+// SetRepoMirrorExcludedResponse holds the result of SetRepoMirrorExcluded.
192+message SetRepoMirrorExcludedResponse {
193+ // Target is the resolved mirror target.
194+ MirrorTarget target = 1;
195+}
196+
197+// MirrorState is the current state of a push mirror.
198+enum MirrorState {
199+ // MIRROR_STATE_OK: last sync succeeded; MIRROR_STATE_FAILING: retrying with
200+ // backoff; MIRROR_STATE_PAUSED: stopped until a forced sync or a config change.
201+ MIRROR_STATE_UNSPECIFIED = 0;
202+ MIRROR_STATE_DISABLED = 1;
203+ MIRROR_STATE_OK = 2;
204+ MIRROR_STATE_FAILING = 3;
205+ MIRROR_STATE_PAUSED = 4;
206+}
207+
208+// MirrorStatus describes a repository mirror and its last attempts.
209+message MirrorStatus {
210+ // Target is the resolved mirror target.
211+ MirrorTarget target = 1;
212+ // State is the current mirror state.
213+ MirrorState state = 2;
214+ // FailureClass is the failure category, empty when there is no failure.
215+ string failure_class = 3;
216+ // Reason explains the current state.
217+ string reason = 4;
218+ // Detail is the last error detail, returned only to callers with repo:admin
219+ // on the repository.
220+ string detail = 5;
221+ // ConsecutiveFailures counts failed attempts since the last success or
222+ // reset.
223+ int32 consecutive_failures = 6;
224+ // LastAttemptAt is when the last sync was attempted.
225+ google.protobuf.Timestamp last_attempt_at = 7;
226+ // LastSuccessAt is when the last sync succeeded, including a no-op.
227+ google.protobuf.Timestamp last_success_at = 8;
228+ // NextRetryAt is the next retry time while failing; otherwise unset.
229+ google.protobuf.Timestamp next_retry_at = 9;
230+ // NextRunAt is the next scheduled sync time, or the retry time while
231+ // failing.
232+ google.protobuf.Timestamp next_run_at = 10;
233+ // PausedAt is when the mirror was paused; otherwise unset.
234+ google.protobuf.Timestamp paused_at = 11;
235+}
236+
237+// GetMirrorStatusRequest selects the input of GetMirrorStatus.
238+message GetMirrorStatusRequest {
239+ // Repo is the repository name.
240+ string repo = 1;
241+}
242+
243+// GetMirrorStatusResponse holds the result of GetMirrorStatus.
244+message GetMirrorStatusResponse {
245+ // Status is the current mirror status.
246+ MirrorStatus status = 1;
247+}
248+
249+// ListMirrorStatusRequest selects the input of ListMirrorStatus.
250+message ListMirrorStatusRequest {}
251+
252+// ListMirrorStatusResponse holds the result of ListMirrorStatus.
253+message ListMirrorStatusResponse {
254+ // Statuses are the mirror statuses.
255+ repeated MirrorStatus statuses = 1;
256+}
257+
258+// SyncMirrorRequest selects the input of SyncMirror.
259+message SyncMirrorRequest {
260+ // Repo is the repository name.
261+ string repo = 1;
262+ // All selects every repository and needs owner rights. Repo must be empty
263+ // when true.
264+ bool all = 2;
265+ // Force lifts a pause and skips the no-op check and the 60-second manual
266+ // throttle. It never forces a Git push.
267+ bool force = 3;
268+}
269+
270+// SyncMirrorResult is the result of a sync request for one repository.
271+message SyncMirrorResult {
272+ // Repo is the repository name.
273+ string repo = 1;
274+ // Queued says another sync is running and this request was queued.
275+ bool queued = 2;
276+ // NoOp says the refs already matched the last successful snapshot.
277+ bool no_op = 3;
278+ // Status is the current mirror status.
279+ MirrorStatus status = 4;
280+ // Error is a human-readable failure of this repository's sync, empty on
281+ // success. It never contains the mirror URL.
282+ string error = 5;
283+}
284+
285+// SyncMirrorResponse holds the result of SyncMirror.
286+message SyncMirrorResponse {
287+ // Results are the per-repository sync results.
288+ repeated SyncMirrorResult results = 1;
289+}

Comments0 open / 0

no comments yet.

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
l
merge request: the list of files (j k move, enter opens, esc back)
M
merge request: the merge menu; there 1–3 pick the mode, D deletes the branch, enter merges
R
merge request: mark the head read
esc
close a dialog, leave a field

Branches 2

Tags 18

↑↓ moveenter openesc close