detached acbb5c2

feat(proto): report per-repository sync errors, drop the reason from mirror_failed and tidy mirror docs

commit
acbb5c20351995c53de7df85d8c5f1880bbe3307
author
brice · · 2026-10-20 07:06 UTC
committer
brice · · 2026-10-20 07:06 UTC
parent
f5fe69a
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
checks
4/4
refs
tag: v0.12.0
6 files · +57 -67
M docs/services.md +2 -2
@@ -34,7 +34,7 @@ No new token scope is introduced.
3434 | `SetRepoMirrorExcluded` | `repo:admin` on the repository. Opts out of or into the global template; a URL override wins over exclusion |
3535 | `GetMirrorStatus` | Read access to the repository. Error `detail` is returned only with `repo:admin` |
3636 | `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 |
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 |
3838
3939 Cron expressions have five fields, run in UTC and must be at least five minutes
4040 apart. Mirrors push the default branch (or every branch with `all_branches`) and
@@ -49,4 +49,4 @@ starts failing (ok or disabled to failing or paused, once per incident), and `mi
4949 (field 24, `MirrorRecovered`) when a failing or paused mirror succeeds again.
5050 These are transition events, not events for every attempt. They are delivered on
5151 `repo:<name>` and `home`, subject to repository visibility. Neither event carries
52-a URL or error detail.
52+a URL, reason or error detail (`MirrorFailed` field 4, `reason`, was removed).
@@ -34,7 +34,7 @@ No new token scope is introduced.
34 | `SetRepoMirrorExcluded` | `repo:admin` on the repository. Opts out of or into the global template; a URL override wins over exclusion |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` |35 | `GetMirrorStatus` | Read access to the repository. Error `detail` is returned only with `repo:admin` |
36 | `ListMirrorStatus` | Owner rights |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 |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 38
39 Cron expressions have five fields, run in UTC and must be at least five minutes39 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`) and40 apart. Mirrors push the default branch (or every branch with `all_branches`) and
@@ -49,4 +49,4 @@ starts failing (ok or disabled to failing or paused, once per incident), and `mi
49 (field 24, `MirrorRecovered`) when a failing or paused mirror succeeds again.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 on50 These are transition events, not events for every attempt. They are delivered on
51 `repo:<name>` and `home`, subject to repository visibility. Neither event carries51 `repo:<name>` and `home`, subject to repository visibility. Neither event carries
52-a URL or error detail.52+a URL, reason or error detail (`MirrorFailed` field 4, `reason`, was removed).
M gen/ermite/event.pb.go +6 -16 generated file: collapsed
M gen/ermite/mirror.pb.go +27 -21
@@ -28,16 +28,14 @@ const (
2828 type MirrorSource int32
2929
3030 const (
31- // Unspecified.
31+ // MIRROR_SOURCE_DISABLED: nothing applies; MIRROR_SOURCE_GLOBAL: the global
32+ // template; MIRROR_SOURCE_OVERRIDE: a per-repository URL;
33+ // MIRROR_SOURCE_EXCLUDED: opted out of the global template.
3234 MirrorSource_MIRROR_SOURCE_UNSPECIFIED MirrorSource = 0
33- // Disabled.
34- MirrorSource_MIRROR_SOURCE_DISABLED MirrorSource = 1
35- // Global.
36- MirrorSource_MIRROR_SOURCE_GLOBAL MirrorSource = 2
37- // Override.
38- MirrorSource_MIRROR_SOURCE_OVERRIDE MirrorSource = 3
39- // Excluded.
40- MirrorSource_MIRROR_SOURCE_EXCLUDED MirrorSource = 4
35+ MirrorSource_MIRROR_SOURCE_DISABLED MirrorSource = 1
36+ MirrorSource_MIRROR_SOURCE_GLOBAL MirrorSource = 2
37+ MirrorSource_MIRROR_SOURCE_OVERRIDE MirrorSource = 3
38+ MirrorSource_MIRROR_SOURCE_EXCLUDED MirrorSource = 4
4139 )
4240
4341 // Enum value maps for MirrorSource.
@@ -89,16 +87,13 @@ func (MirrorSource) EnumDescriptor() ([]byte, []int) {
8987 type MirrorState int32
9088
9189 const (
92- // Unspecified.
90+ // MIRROR_STATE_OK: last sync succeeded; MIRROR_STATE_FAILING: retrying with
91+ // backoff; MIRROR_STATE_PAUSED: stopped until a forced sync or a config change.
9392 MirrorState_MIRROR_STATE_UNSPECIFIED MirrorState = 0
94- // Disabled.
95- MirrorState_MIRROR_STATE_DISABLED MirrorState = 1
96- // Ok.
97- MirrorState_MIRROR_STATE_OK MirrorState = 2
98- // Failing.
99- MirrorState_MIRROR_STATE_FAILING MirrorState = 3
100- // Paused.
101- MirrorState_MIRROR_STATE_PAUSED MirrorState = 4
93+ MirrorState_MIRROR_STATE_DISABLED MirrorState = 1
94+ MirrorState_MIRROR_STATE_OK MirrorState = 2
95+ MirrorState_MIRROR_STATE_FAILING MirrorState = 3
96+ MirrorState_MIRROR_STATE_PAUSED MirrorState = 4
10297 )
10398
10499 // Enum value maps for MirrorState.
@@ -1465,7 +1460,10 @@ type SyncMirrorResult struct {
14651460 // NoOp says the refs already matched the last successful snapshot.
14661461 NoOp bool `protobuf:"varint,3,opt,name=no_op,json=noOp,proto3" json:"no_op,omitempty"`
14671462 // Status is the current mirror status.
1468- Status *MirrorStatus `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"`
1463+ Status *MirrorStatus `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"`
1464+ // Error is a human-readable failure of this repository's sync, empty on
1465+ // success. It never contains the mirror URL.
1466+ Error string `protobuf:"bytes,5,opt,name=error,proto3" json:"error,omitempty"`
14691467 unknownFields protoimpl.UnknownFields
14701468 sizeCache protoimpl.SizeCache
14711469 }
@@ -1528,6 +1526,13 @@ func (x *SyncMirrorResult) GetStatus() *MirrorStatus {
15281526 return nil
15291527 }
15301528
1529+func (x *SyncMirrorResult) GetError() string {
1530+ if x != nil {
1531+ return x.Error
1532+ }
1533+ return ""
1534+}
1535+
15311536 // SyncMirrorResponse holds the result of SyncMirror.
15321537 type SyncMirrorResponse struct {
15331538 state protoimpl.MessageState `protogen:"open.v1"`
@@ -1659,12 +1664,13 @@ const file_ermite_mirror_proto_rawDesc = "" +
16591664 "\x11SyncMirrorRequest\x12\x12\n" +
16601665 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x10\n" +
16611666 "\x03all\x18\x02 \x01(\bR\x03all\x12\x14\n" +
1662- "\x05force\x18\x03 \x01(\bR\x05force\"\x81\x01\n" +
1667+ "\x05force\x18\x03 \x01(\bR\x05force\"\x97\x01\n" +
16631668 "\x10SyncMirrorResult\x12\x12\n" +
16641669 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
16651670 "\x06queued\x18\x02 \x01(\bR\x06queued\x12\x13\n" +
16661671 "\x05no_op\x18\x03 \x01(\bR\x04noOp\x12,\n" +
1667- "\x06status\x18\x04 \x01(\v2\x14.ermite.MirrorStatusR\x06status\"H\n" +
1672+ "\x06status\x18\x04 \x01(\v2\x14.ermite.MirrorStatusR\x06status\x12\x14\n" +
1673+ "\x05error\x18\x05 \x01(\tR\x05error\"H\n" +
16681674 "\x12SyncMirrorResponse\x122\n" +
16691675 "\aresults\x18\x01 \x03(\v2\x18.ermite.SyncMirrorResultR\aresults*\x9b\x01\n" +
16701676 "\fMirrorSource\x12\x1d\n" +
@@ -28,16 +28,14 @@ const (
28 type MirrorSource int3228 type MirrorSource int32
29 29
30 const (30 const (
31- // Unspecified.31+ // MIRROR_SOURCE_DISABLED: nothing applies; MIRROR_SOURCE_GLOBAL: the global
32+ // template; MIRROR_SOURCE_OVERRIDE: a per-repository URL;
33+ // MIRROR_SOURCE_EXCLUDED: opted out of the global template.
32 MirrorSource_MIRROR_SOURCE_UNSPECIFIED MirrorSource = 034 MirrorSource_MIRROR_SOURCE_UNSPECIFIED MirrorSource = 0
33- // Disabled.35+ MirrorSource_MIRROR_SOURCE_DISABLED MirrorSource = 1
34- MirrorSource_MIRROR_SOURCE_DISABLED MirrorSource = 136+ MirrorSource_MIRROR_SOURCE_GLOBAL MirrorSource = 2
35- // Global.37+ MirrorSource_MIRROR_SOURCE_OVERRIDE MirrorSource = 3
36- MirrorSource_MIRROR_SOURCE_GLOBAL MirrorSource = 238+ MirrorSource_MIRROR_SOURCE_EXCLUDED MirrorSource = 4
37- // Override.
38- MirrorSource_MIRROR_SOURCE_OVERRIDE MirrorSource = 3
39- // Excluded.
40- MirrorSource_MIRROR_SOURCE_EXCLUDED MirrorSource = 4
41 )39 )
42 40
43 // Enum value maps for MirrorSource.41 // Enum value maps for MirrorSource.
@@ -89,16 +87,13 @@ func (MirrorSource) EnumDescriptor() ([]byte, []int) {
89 type MirrorState int3287 type MirrorState int32
90 88
91 const (89 const (
92- // Unspecified.90+ // MIRROR_STATE_OK: last sync succeeded; MIRROR_STATE_FAILING: retrying with
91+ // backoff; MIRROR_STATE_PAUSED: stopped until a forced sync or a config change.
93 MirrorState_MIRROR_STATE_UNSPECIFIED MirrorState = 092 MirrorState_MIRROR_STATE_UNSPECIFIED MirrorState = 0
94- // Disabled.93+ MirrorState_MIRROR_STATE_DISABLED MirrorState = 1
95- MirrorState_MIRROR_STATE_DISABLED MirrorState = 194+ MirrorState_MIRROR_STATE_OK MirrorState = 2
96- // Ok.95+ MirrorState_MIRROR_STATE_FAILING MirrorState = 3
97- MirrorState_MIRROR_STATE_OK MirrorState = 296+ MirrorState_MIRROR_STATE_PAUSED MirrorState = 4
98- // Failing.
99- MirrorState_MIRROR_STATE_FAILING MirrorState = 3
100- // Paused.
101- MirrorState_MIRROR_STATE_PAUSED MirrorState = 4
102 )97 )
103 98
104 // Enum value maps for MirrorState.99 // Enum value maps for MirrorState.
@@ -1465,7 +1460,10 @@ type SyncMirrorResult struct {
1465 // NoOp says the refs already matched the last successful snapshot.1460 // NoOp says the refs already matched the last successful snapshot.
1466 NoOp bool `protobuf:"varint,3,opt,name=no_op,json=noOp,proto3" json:"no_op,omitempty"`1461 NoOp bool `protobuf:"varint,3,opt,name=no_op,json=noOp,proto3" json:"no_op,omitempty"`
1467 // Status is the current mirror status.1462 // Status is the current mirror status.
1468- Status *MirrorStatus `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"`1463+ Status *MirrorStatus `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"`
1464+ // Error is a human-readable failure of this repository's sync, empty on
1465+ // success. It never contains the mirror URL.
1466+ Error string `protobuf:"bytes,5,opt,name=error,proto3" json:"error,omitempty"`
1469 unknownFields protoimpl.UnknownFields1467 unknownFields protoimpl.UnknownFields
1470 sizeCache protoimpl.SizeCache1468 sizeCache protoimpl.SizeCache
1471 }1469 }
@@ -1528,6 +1526,13 @@ func (x *SyncMirrorResult) GetStatus() *MirrorStatus {
1528 return nil1526 return nil
1529 }1527 }
1530 1528
1529+func (x *SyncMirrorResult) GetError() string {
1530+ if x != nil {
1531+ return x.Error
1532+ }
1533+ return ""
1534+}
1535+
1531 // SyncMirrorResponse holds the result of SyncMirror.1536 // SyncMirrorResponse holds the result of SyncMirror.
1532 type SyncMirrorResponse struct {1537 type SyncMirrorResponse struct {
1533 state protoimpl.MessageState `protogen:"open.v1"`1538 state protoimpl.MessageState `protogen:"open.v1"`
@@ -1659,12 +1664,13 @@ const file_ermite_mirror_proto_rawDesc = "" +
1659 "\x11SyncMirrorRequest\x12\x12\n" +1664 "\x11SyncMirrorRequest\x12\x12\n" +
1660 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x10\n" +1665 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x10\n" +
1661 "\x03all\x18\x02 \x01(\bR\x03all\x12\x14\n" +1666 "\x03all\x18\x02 \x01(\bR\x03all\x12\x14\n" +
1662- "\x05force\x18\x03 \x01(\bR\x05force\"\x81\x01\n" +1667+ "\x05force\x18\x03 \x01(\bR\x05force\"\x97\x01\n" +
1663 "\x10SyncMirrorResult\x12\x12\n" +1668 "\x10SyncMirrorResult\x12\x12\n" +
1664 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +1669 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
1665 "\x06queued\x18\x02 \x01(\bR\x06queued\x12\x13\n" +1670 "\x06queued\x18\x02 \x01(\bR\x06queued\x12\x13\n" +
1666 "\x05no_op\x18\x03 \x01(\bR\x04noOp\x12,\n" +1671 "\x05no_op\x18\x03 \x01(\bR\x04noOp\x12,\n" +
1667- "\x06status\x18\x04 \x01(\v2\x14.ermite.MirrorStatusR\x06status\"H\n" +1672+ "\x06status\x18\x04 \x01(\v2\x14.ermite.MirrorStatusR\x06status\x12\x14\n" +
1673+ "\x05error\x18\x05 \x01(\tR\x05error\"H\n" +
1668 "\x12SyncMirrorResponse\x122\n" +1674 "\x12SyncMirrorResponse\x122\n" +
1669 "\aresults\x18\x01 \x03(\v2\x18.ermite.SyncMirrorResultR\aresults*\x9b\x01\n" +1675 "\aresults\x18\x01 \x03(\v2\x18.ermite.SyncMirrorResultR\aresults*\x9b\x01\n" +
1670 "\fMirrorSource\x12\x1d\n" +1676 "\fMirrorSource\x12\x1d\n" +
M gen_test.go +1 -1
@@ -598,7 +598,7 @@ func TestMirrorWire(t *testing.T) {
598598 for _, in := range []proto.Message{
599599 &ermite.SetMirrorConfigRequest{Enabled: proto.Bool(false), UrlTemplate: proto.String(""), Cron: proto.String(""), AllBranches: proto.Bool(false)},
600600 &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", Reason: "remote diverged"}}},
601+ &ermite.Event{Kind: &ermite.Event_MirrorFailed{MirrorFailed: &ermite.MirrorFailed{Repo: "repo", State: ermite.MirrorState_MIRROR_STATE_PAUSED, FailureClass: "diverged"}}},
602602 &ermite.Event{Kind: &ermite.Event_MirrorRecovered{MirrorRecovered: &ermite.MirrorRecovered{Repo: "repo"}}},
603603 } {
604604 b, err := proto.Marshal(in)
@@ -598,7 +598,7 @@ func TestMirrorWire(t *testing.T) {
598 for _, in := range []proto.Message{598 for _, in := range []proto.Message{
599 &ermite.SetMirrorConfigRequest{Enabled: proto.Bool(false), UrlTemplate: proto.String(""), Cron: proto.String(""), AllBranches: proto.Bool(false)},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)},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", Reason: "remote diverged"}}},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"}}},602 &ermite.Event{Kind: &ermite.Event_MirrorRecovered{MirrorRecovered: &ermite.MirrorRecovered{Repo: "repo"}}},
603 } {603 } {
604 b, err := proto.Marshal(in)604 b, err := proto.Marshal(in)
M proto/ermite/event.proto +6 -5
@@ -314,9 +314,9 @@ message JobUpdated {
314314
315315 // MirrorFailed reports the start of a failure incident: a transition from ok
316316 // 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" deliver it
318-// on "repo:<name>" and "home", subject to repository visibility. It carries no
319-// URL or error detail.
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.
320320 message MirrorFailed {
321321 // Repo is the repository name.
322322 string repo = 1;
@@ -324,8 +324,9 @@ message MirrorFailed {
324324 MirrorState state = 2;
325325 // FailureClass is the failure category.
326326 string failure_class = 3;
327- // Reason explains the failure without a URL or error detail.
328- string reason = 4;
327+
328+ reserved 4;
329+ reserved "reason";
329330 }
330331
331332 // MirrorRecovered reports a successful sync after failing or paused. Delivery
@@ -314,9 +314,9 @@ message JobUpdated {
314 314
315 // MirrorFailed reports the start of a failure incident: a transition from ok315 // 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 for316 // 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" deliver it317+// each failed attempt nor for failing to paused. Servers with feature "mirror"
318-// on "repo:<name>" and "home", subject to repository visibility. It carries no318+// deliver it on "repo:<name>" and "home", subject to repository visibility. It
319-// URL or error detail.319+// carries no URL, reason or error detail; read the status for those.
320 message MirrorFailed {320 message MirrorFailed {
321 // Repo is the repository name.321 // Repo is the repository name.
322 string repo = 1;322 string repo = 1;
@@ -324,8 +324,9 @@ message MirrorFailed {
324 MirrorState state = 2;324 MirrorState state = 2;
325 // FailureClass is the failure category.325 // FailureClass is the failure category.
326 string failure_class = 3;326 string failure_class = 3;
327- // Reason explains the failure without a URL or error detail.327+
328- string reason = 4;328+ reserved 4;
329+ reserved "reason";
329 }330 }
330 331
331 // MirrorRecovered reports a successful sync after failing or paused. Delivery332 // MirrorRecovered reports a successful sync after failing or paused. Delivery
M proto/ermite/mirror.proto +15 -22
@@ -6,18 +6,13 @@ package ermite;
66
77 import "google/protobuf/timestamp.proto";
88
9-// MirrorService configures the push mirrors of the server. Global settings
10-// and key
11-// rotation need the admin token or a token with every scope and no repository
12-// or
13-// branch limits; per-repository settings and SyncMirror need repo:admin on
14-// the
15-// repository; status needs read access. GetMirrorKey needs a non-anonymous
16-// caller.
17-// ListMirrorStatus and SyncMirror with All need owner rights. No new token
18-// scope
19-// is required. Servers implementing this service list "mirror" in
20-// GetVersionResponse.features.
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.
2116 service MirrorService {
2217 // GetMirrorKey returns the public mirror key, generating it on first use.
2318 // Needs a non-anonymous caller. Fails with failed_precondition without
@@ -127,15 +122,13 @@ message SetMirrorConfigResponse {
127122
128123 // MirrorSource says which configuration applies to a repository.
129124 enum MirrorSource {
130- // Unspecified.
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.
131128 MIRROR_SOURCE_UNSPECIFIED = 0;
132- // Disabled.
133129 MIRROR_SOURCE_DISABLED = 1;
134- // Global.
135130 MIRROR_SOURCE_GLOBAL = 2;
136- // Override.
137131 MIRROR_SOURCE_OVERRIDE = 3;
138- // Excluded.
139132 MIRROR_SOURCE_EXCLUDED = 4;
140133 }
141134
@@ -203,15 +196,12 @@ message SetRepoMirrorExcludedResponse {
203196
204197 // MirrorState is the current state of a push mirror.
205198 enum MirrorState {
206- // Unspecified.
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.
207201 MIRROR_STATE_UNSPECIFIED = 0;
208- // Disabled.
209202 MIRROR_STATE_DISABLED = 1;
210- // Ok.
211203 MIRROR_STATE_OK = 2;
212- // Failing.
213204 MIRROR_STATE_FAILING = 3;
214- // Paused.
215205 MIRROR_STATE_PAUSED = 4;
216206 }
217207
@@ -287,6 +277,9 @@ message SyncMirrorResult {
287277 bool no_op = 3;
288278 // Status is the current mirror status.
289279 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;
290283 }
291284
292285 // SyncMirrorResponse holds the result of SyncMirror.
@@ -6,18 +6,13 @@ package ermite;
6 6
7 import "google/protobuf/timestamp.proto";7 import "google/protobuf/timestamp.proto";
8 8
9-// MirrorService configures the push mirrors of the server. Global settings9+// MirrorService configures the push mirrors of the server. Global settings and
10-// and key10+// key rotation need the admin token or a token with every scope and no
11-// rotation need the admin token or a token with every scope and no repository11+// repository or branch limits; per-repository settings and SyncMirror need
12-// or12+// repo:admin on the repository; status needs read access. GetMirrorKey needs a
13-// branch limits; per-repository settings and SyncMirror need repo:admin on13+// non-anonymous caller. ListMirrorStatus and SyncMirror with All need owner
14-// the14+// rights. No new token scope is required. Servers implementing this service
15-// repository; status needs read access. GetMirrorKey needs a non-anonymous15+// list "mirror" in GetVersionResponse.features.
16-// caller.
17-// ListMirrorStatus and SyncMirror with All need owner rights. No new token
18-// scope
19-// is required. Servers implementing this service list "mirror" in
20-// GetVersionResponse.features.
21 service MirrorService {16 service MirrorService {
22 // GetMirrorKey returns the public mirror key, generating it on first use.17 // GetMirrorKey returns the public mirror key, generating it on first use.
23 // Needs a non-anonymous caller. Fails with failed_precondition without18 // Needs a non-anonymous caller. Fails with failed_precondition without
@@ -127,15 +122,13 @@ message SetMirrorConfigResponse {
127 122
128 // MirrorSource says which configuration applies to a repository.123 // MirrorSource says which configuration applies to a repository.
129 enum MirrorSource {124 enum MirrorSource {
130- // Unspecified.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.
131 MIRROR_SOURCE_UNSPECIFIED = 0;128 MIRROR_SOURCE_UNSPECIFIED = 0;
132- // Disabled.
133 MIRROR_SOURCE_DISABLED = 1;129 MIRROR_SOURCE_DISABLED = 1;
134- // Global.
135 MIRROR_SOURCE_GLOBAL = 2;130 MIRROR_SOURCE_GLOBAL = 2;
136- // Override.
137 MIRROR_SOURCE_OVERRIDE = 3;131 MIRROR_SOURCE_OVERRIDE = 3;
138- // Excluded.
139 MIRROR_SOURCE_EXCLUDED = 4;132 MIRROR_SOURCE_EXCLUDED = 4;
140 }133 }
141 134
@@ -203,15 +196,12 @@ message SetRepoMirrorExcludedResponse {
203 196
204 // MirrorState is the current state of a push mirror.197 // MirrorState is the current state of a push mirror.
205 enum MirrorState {198 enum MirrorState {
206- // Unspecified.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.
207 MIRROR_STATE_UNSPECIFIED = 0;201 MIRROR_STATE_UNSPECIFIED = 0;
208- // Disabled.
209 MIRROR_STATE_DISABLED = 1;202 MIRROR_STATE_DISABLED = 1;
210- // Ok.
211 MIRROR_STATE_OK = 2;203 MIRROR_STATE_OK = 2;
212- // Failing.
213 MIRROR_STATE_FAILING = 3;204 MIRROR_STATE_FAILING = 3;
214- // Paused.
215 MIRROR_STATE_PAUSED = 4;205 MIRROR_STATE_PAUSED = 4;
216 }206 }
217 207
@@ -287,6 +277,9 @@ message SyncMirrorResult {
287 bool no_op = 3;277 bool no_op = 3;
288 // Status is the current mirror status.278 // Status is the current mirror status.
289 MirrorStatus status = 4;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;
290 }283 }
291 284
292 // SyncMirrorResponse holds the result of SyncMirror.285 // SyncMirrorResponse holds the result of SyncMirror.

Keyboard shortcuts

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

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

Branches 2

Tags 18

↑↓ moveenter openesc close