detached 751d351

feat(proto): ReleaseService, release:write scope and release events

commit
751d3518d858cfece95cd552f798e836e6ba4292
author
brice · · 2026-10-21 14:14 UTC
committer
brice · · 2026-10-21 17:42 UTC
parent
e4b332d
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
checks
2/2
refs
tag: v0.16.0
11 files · +3361 -47
M docs/services.md +26 -0
@@ -50,3 +50,29 @@ starts failing (ok or disabled to failing or paused, once per incident), and `mi
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
5252 a URL, reason or error detail (`MirrorFailed` field 4, `reason`, was removed).
53+
54+## ReleaseService
55+
56+Servers advertise `releases` in `GetVersionResponse.features`.
57+
58+| RPC | Behavior |
59+|---|---|
60+| `CreateRelease`, `GetRelease`, `ListReleases`, `UpdateRelease`, `DeleteRelease` | Manage releases; get selects by id, tag or latest. Lists are paginated, newest first. Updates use a field mask |
61+| `UploadReleaseAsset` | Client stream: metadata and nonnegative exact size first, then data in chunks of at most 1 MiB. `replace` replaces a taken name |
62+| `UpdateReleaseAsset`, `DeleteReleaseAsset`, `ListReleaseAssets` | Rename/relabel with a field mask, delete, or list uploaded assets by name |
63+| `DownloadReleaseAsset` | Select by asset id, tag/name or latest/name; metadata first, then chunks of at most 1 MiB. Resume with a byte offset |
64+| `GetReleaseUsage` | Repository usage including reserved uploads, quota, asset size and count limits |
65+
66+Published releases follow repository read access; drafts are visible only to
67+writers. Writes require `release:write` (`SCOPE_RELEASE_WRITE = 13`) or a job
68+token with `contents: write` for that repository. Branch-limited tokens cannot
69+write releases. Unreadable objects return `not_found`. Duplicate tags or asset
70+names return `already_exists`; limits return `resource_exhausted`. Publishing
71+requires an existing tag or `target_commitish` to create it; existing tags never
72+move. `make_latest` is a boolean pin, and updates use mask presence.
73+
74+`EventService.Watch` adds `release_published` (27), `release_updated` (28),
75+`release_deleted` (29) and `release_unpublished` (30), delivered on `repo:<name>`
76+and `home` subject to repository visibility. Payloads carry repository, release
77+id and tag; all except deletion also carry draft and prerelease flags. Tag
78+deletion turns a release into a draft without deleting its assets.
@@ -50,3 +50,29 @@ starts failing (ok or disabled to failing or paused, once per incident), and `mi
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, reason or error detail (`MirrorFailed` field 4, `reason`, was removed).52 a URL, reason or error detail (`MirrorFailed` field 4, `reason`, was removed).
53+
54+## ReleaseService
55+
56+Servers advertise `releases` in `GetVersionResponse.features`.
57+
58+| RPC | Behavior |
59+|---|---|
60+| `CreateRelease`, `GetRelease`, `ListReleases`, `UpdateRelease`, `DeleteRelease` | Manage releases; get selects by id, tag or latest. Lists are paginated, newest first. Updates use a field mask |
61+| `UploadReleaseAsset` | Client stream: metadata and nonnegative exact size first, then data in chunks of at most 1 MiB. `replace` replaces a taken name |
62+| `UpdateReleaseAsset`, `DeleteReleaseAsset`, `ListReleaseAssets` | Rename/relabel with a field mask, delete, or list uploaded assets by name |
63+| `DownloadReleaseAsset` | Select by asset id, tag/name or latest/name; metadata first, then chunks of at most 1 MiB. Resume with a byte offset |
64+| `GetReleaseUsage` | Repository usage including reserved uploads, quota, asset size and count limits |
65+
66+Published releases follow repository read access; drafts are visible only to
67+writers. Writes require `release:write` (`SCOPE_RELEASE_WRITE = 13`) or a job
68+token with `contents: write` for that repository. Branch-limited tokens cannot
69+write releases. Unreadable objects return `not_found`. Duplicate tags or asset
70+names return `already_exists`; limits return `resource_exhausted`. Publishing
71+requires an existing tag or `target_commitish` to create it; existing tags never
72+move. `make_latest` is a boolean pin, and updates use mask presence.
73+
74+`EventService.Watch` adds `release_published` (27), `release_updated` (28),
75+`release_deleted` (29) and `release_unpublished` (30), delivered on `repo:<name>`
76+and `home` subject to repository visibility. Payloads carry repository, release
77+id and tag; all except deletion also carry draft and prerelease flags. Tag
78+deletion turns a release into a draft without deleting its assets.
A gen/ermite/ermiteconnect/release.connect.go +441 -0
@@ -0,0 +1,441 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/release.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+ // ReleaseServiceName is the fully-qualified name of the ReleaseService service.
27+ ReleaseServiceName = "ermite.ReleaseService"
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+ // ReleaseServiceCreateReleaseProcedure is the fully-qualified name of the ReleaseService's
39+ // CreateRelease RPC.
40+ ReleaseServiceCreateReleaseProcedure = "/ermite.ReleaseService/CreateRelease"
41+ // ReleaseServiceGetReleaseProcedure is the fully-qualified name of the ReleaseService's GetRelease
42+ // RPC.
43+ ReleaseServiceGetReleaseProcedure = "/ermite.ReleaseService/GetRelease"
44+ // ReleaseServiceListReleasesProcedure is the fully-qualified name of the ReleaseService's
45+ // ListReleases RPC.
46+ ReleaseServiceListReleasesProcedure = "/ermite.ReleaseService/ListReleases"
47+ // ReleaseServiceUpdateReleaseProcedure is the fully-qualified name of the ReleaseService's
48+ // UpdateRelease RPC.
49+ ReleaseServiceUpdateReleaseProcedure = "/ermite.ReleaseService/UpdateRelease"
50+ // ReleaseServiceDeleteReleaseProcedure is the fully-qualified name of the ReleaseService's
51+ // DeleteRelease RPC.
52+ ReleaseServiceDeleteReleaseProcedure = "/ermite.ReleaseService/DeleteRelease"
53+ // ReleaseServiceUploadReleaseAssetProcedure is the fully-qualified name of the ReleaseService's
54+ // UploadReleaseAsset RPC.
55+ ReleaseServiceUploadReleaseAssetProcedure = "/ermite.ReleaseService/UploadReleaseAsset"
56+ // ReleaseServiceUpdateReleaseAssetProcedure is the fully-qualified name of the ReleaseService's
57+ // UpdateReleaseAsset RPC.
58+ ReleaseServiceUpdateReleaseAssetProcedure = "/ermite.ReleaseService/UpdateReleaseAsset"
59+ // ReleaseServiceDeleteReleaseAssetProcedure is the fully-qualified name of the ReleaseService's
60+ // DeleteReleaseAsset RPC.
61+ ReleaseServiceDeleteReleaseAssetProcedure = "/ermite.ReleaseService/DeleteReleaseAsset"
62+ // ReleaseServiceDownloadReleaseAssetProcedure is the fully-qualified name of the ReleaseService's
63+ // DownloadReleaseAsset RPC.
64+ ReleaseServiceDownloadReleaseAssetProcedure = "/ermite.ReleaseService/DownloadReleaseAsset"
65+ // ReleaseServiceListReleaseAssetsProcedure is the fully-qualified name of the ReleaseService's
66+ // ListReleaseAssets RPC.
67+ ReleaseServiceListReleaseAssetsProcedure = "/ermite.ReleaseService/ListReleaseAssets"
68+ // ReleaseServiceGetReleaseUsageProcedure is the fully-qualified name of the ReleaseService's
69+ // GetReleaseUsage RPC.
70+ ReleaseServiceGetReleaseUsageProcedure = "/ermite.ReleaseService/GetReleaseUsage"
71+)
72+
73+// ReleaseServiceClient is a client for the ermite.ReleaseService service.
74+type ReleaseServiceClient interface {
75+ // CreateRelease creates one release per tag (already_exists on duplication).
76+ // Publishing without an existing tag is failed_precondition, unless
77+ // target_commitish is supplied to create it; existing tags never move.
78+ CreateRelease(context.Context, *connect.Request[ermite.CreateReleaseRequest]) (*connect.Response[ermite.CreateReleaseResponse], error)
79+ // GetRelease selects by id, tag or latest (latest must be true).
80+ GetRelease(context.Context, *connect.Request[ermite.GetReleaseRequest]) (*connect.Response[ermite.GetReleaseResponse], error)
81+ // ListReleases lists newest first, including drafts only for writers.
82+ ListReleases(context.Context, *connect.Request[ermite.ListReleasesRequest]) (*connect.Response[ermite.ListReleasesResponse], error)
83+ // UpdateRelease changes masked fields. Publishing requires an existing tag
84+ // or target_commitish to create it; otherwise failed_precondition.
85+ UpdateRelease(context.Context, *connect.Request[ermite.UpdateReleaseRequest]) (*connect.Response[ermite.UpdateReleaseResponse], error)
86+ // DeleteRelease removes a release and its assets.
87+ DeleteRelease(context.Context, *connect.Request[ermite.DeleteReleaseRequest]) (*connect.Response[ermite.DeleteReleaseResponse], error)
88+ // UploadReleaseAsset reserves size before reading. The first message carries
89+ // metadata; subsequent messages carry only data, at most 1 MiB per message.
90+ // Size must be nonnegative (-1 is refused). An interrupted stream stores no
91+ // asset. A taken name gives already_exists unless replace is true. Uploading
92+ // to a release of another repo gives failed_precondition.
93+ UploadReleaseAsset(context.Context) *connect.ClientStreamForClient[ermite.UploadReleaseAssetRequest, ermite.UploadReleaseAssetResponse]
94+ // UpdateReleaseAsset changes masked name and label; a taken name gives already_exists.
95+ UpdateReleaseAsset(context.Context, *connect.Request[ermite.UpdateReleaseAssetRequest]) (*connect.Response[ermite.UpdateReleaseAssetResponse], error)
96+ // DeleteReleaseAsset removes an asset.
97+ DeleteReleaseAsset(context.Context, *connect.Request[ermite.DeleteReleaseAssetRequest]) (*connect.Response[ermite.DeleteReleaseAssetResponse], error)
98+ // DownloadReleaseAsset sends asset metadata first, then chunks of at most
99+ // 1 MiB. Resume with the offset after the last byte received. An offset
100+ // beyond the size gives out_of_range.
101+ DownloadReleaseAsset(context.Context, *connect.Request[ermite.DownloadReleaseAssetRequest]) (*connect.ServerStreamForClient[ermite.DownloadReleaseAssetResponse], error)
102+ // ListReleaseAssets lists uploaded assets, ordered by name.
103+ ListReleaseAssets(context.Context, *connect.Request[ermite.ListReleaseAssetsRequest]) (*connect.Response[ermite.ListReleaseAssetsResponse], error)
104+ // GetReleaseUsage returns usage (including reserved uploads) and limits.
105+ GetReleaseUsage(context.Context, *connect.Request[ermite.GetReleaseUsageRequest]) (*connect.Response[ermite.GetReleaseUsageResponse], error)
106+}
107+
108+// NewReleaseServiceClient constructs a client for the ermite.ReleaseService service. By default, it
109+// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
110+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
111+// connect.WithGRPCWeb() options.
112+//
113+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
114+// http://api.acme.com or https://acme.com/grpc).
115+func NewReleaseServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) ReleaseServiceClient {
116+ baseURL = strings.TrimRight(baseURL, "/")
117+ releaseServiceMethods := ermite.File_ermite_release_proto.Services().ByName("ReleaseService").Methods()
118+ return &releaseServiceClient{
119+ createRelease: connect.NewClient[ermite.CreateReleaseRequest, ermite.CreateReleaseResponse](
120+ httpClient,
121+ baseURL+ReleaseServiceCreateReleaseProcedure,
122+ connect.WithSchema(releaseServiceMethods.ByName("CreateRelease")),
123+ connect.WithClientOptions(opts...),
124+ ),
125+ getRelease: connect.NewClient[ermite.GetReleaseRequest, ermite.GetReleaseResponse](
126+ httpClient,
127+ baseURL+ReleaseServiceGetReleaseProcedure,
128+ connect.WithSchema(releaseServiceMethods.ByName("GetRelease")),
129+ connect.WithClientOptions(opts...),
130+ ),
131+ listReleases: connect.NewClient[ermite.ListReleasesRequest, ermite.ListReleasesResponse](
132+ httpClient,
133+ baseURL+ReleaseServiceListReleasesProcedure,
134+ connect.WithSchema(releaseServiceMethods.ByName("ListReleases")),
135+ connect.WithClientOptions(opts...),
136+ ),
137+ updateRelease: connect.NewClient[ermite.UpdateReleaseRequest, ermite.UpdateReleaseResponse](
138+ httpClient,
139+ baseURL+ReleaseServiceUpdateReleaseProcedure,
140+ connect.WithSchema(releaseServiceMethods.ByName("UpdateRelease")),
141+ connect.WithClientOptions(opts...),
142+ ),
143+ deleteRelease: connect.NewClient[ermite.DeleteReleaseRequest, ermite.DeleteReleaseResponse](
144+ httpClient,
145+ baseURL+ReleaseServiceDeleteReleaseProcedure,
146+ connect.WithSchema(releaseServiceMethods.ByName("DeleteRelease")),
147+ connect.WithClientOptions(opts...),
148+ ),
149+ uploadReleaseAsset: connect.NewClient[ermite.UploadReleaseAssetRequest, ermite.UploadReleaseAssetResponse](
150+ httpClient,
151+ baseURL+ReleaseServiceUploadReleaseAssetProcedure,
152+ connect.WithSchema(releaseServiceMethods.ByName("UploadReleaseAsset")),
153+ connect.WithClientOptions(opts...),
154+ ),
155+ updateReleaseAsset: connect.NewClient[ermite.UpdateReleaseAssetRequest, ermite.UpdateReleaseAssetResponse](
156+ httpClient,
157+ baseURL+ReleaseServiceUpdateReleaseAssetProcedure,
158+ connect.WithSchema(releaseServiceMethods.ByName("UpdateReleaseAsset")),
159+ connect.WithClientOptions(opts...),
160+ ),
161+ deleteReleaseAsset: connect.NewClient[ermite.DeleteReleaseAssetRequest, ermite.DeleteReleaseAssetResponse](
162+ httpClient,
163+ baseURL+ReleaseServiceDeleteReleaseAssetProcedure,
164+ connect.WithSchema(releaseServiceMethods.ByName("DeleteReleaseAsset")),
165+ connect.WithClientOptions(opts...),
166+ ),
167+ downloadReleaseAsset: connect.NewClient[ermite.DownloadReleaseAssetRequest, ermite.DownloadReleaseAssetResponse](
168+ httpClient,
169+ baseURL+ReleaseServiceDownloadReleaseAssetProcedure,
170+ connect.WithSchema(releaseServiceMethods.ByName("DownloadReleaseAsset")),
171+ connect.WithClientOptions(opts...),
172+ ),
173+ listReleaseAssets: connect.NewClient[ermite.ListReleaseAssetsRequest, ermite.ListReleaseAssetsResponse](
174+ httpClient,
175+ baseURL+ReleaseServiceListReleaseAssetsProcedure,
176+ connect.WithSchema(releaseServiceMethods.ByName("ListReleaseAssets")),
177+ connect.WithClientOptions(opts...),
178+ ),
179+ getReleaseUsage: connect.NewClient[ermite.GetReleaseUsageRequest, ermite.GetReleaseUsageResponse](
180+ httpClient,
181+ baseURL+ReleaseServiceGetReleaseUsageProcedure,
182+ connect.WithSchema(releaseServiceMethods.ByName("GetReleaseUsage")),
183+ connect.WithClientOptions(opts...),
184+ ),
185+ }
186+}
187+
188+// releaseServiceClient implements ReleaseServiceClient.
189+type releaseServiceClient struct {
190+ createRelease *connect.Client[ermite.CreateReleaseRequest, ermite.CreateReleaseResponse]
191+ getRelease *connect.Client[ermite.GetReleaseRequest, ermite.GetReleaseResponse]
192+ listReleases *connect.Client[ermite.ListReleasesRequest, ermite.ListReleasesResponse]
193+ updateRelease *connect.Client[ermite.UpdateReleaseRequest, ermite.UpdateReleaseResponse]
194+ deleteRelease *connect.Client[ermite.DeleteReleaseRequest, ermite.DeleteReleaseResponse]
195+ uploadReleaseAsset *connect.Client[ermite.UploadReleaseAssetRequest, ermite.UploadReleaseAssetResponse]
196+ updateReleaseAsset *connect.Client[ermite.UpdateReleaseAssetRequest, ermite.UpdateReleaseAssetResponse]
197+ deleteReleaseAsset *connect.Client[ermite.DeleteReleaseAssetRequest, ermite.DeleteReleaseAssetResponse]
198+ downloadReleaseAsset *connect.Client[ermite.DownloadReleaseAssetRequest, ermite.DownloadReleaseAssetResponse]
199+ listReleaseAssets *connect.Client[ermite.ListReleaseAssetsRequest, ermite.ListReleaseAssetsResponse]
200+ getReleaseUsage *connect.Client[ermite.GetReleaseUsageRequest, ermite.GetReleaseUsageResponse]
201+}
202+
203+// CreateRelease calls ermite.ReleaseService.CreateRelease.
204+func (c *releaseServiceClient) CreateRelease(ctx context.Context, req *connect.Request[ermite.CreateReleaseRequest]) (*connect.Response[ermite.CreateReleaseResponse], error) {
205+ return c.createRelease.CallUnary(ctx, req)
206+}
207+
208+// GetRelease calls ermite.ReleaseService.GetRelease.
209+func (c *releaseServiceClient) GetRelease(ctx context.Context, req *connect.Request[ermite.GetReleaseRequest]) (*connect.Response[ermite.GetReleaseResponse], error) {
210+ return c.getRelease.CallUnary(ctx, req)
211+}
212+
213+// ListReleases calls ermite.ReleaseService.ListReleases.
214+func (c *releaseServiceClient) ListReleases(ctx context.Context, req *connect.Request[ermite.ListReleasesRequest]) (*connect.Response[ermite.ListReleasesResponse], error) {
215+ return c.listReleases.CallUnary(ctx, req)
216+}
217+
218+// UpdateRelease calls ermite.ReleaseService.UpdateRelease.
219+func (c *releaseServiceClient) UpdateRelease(ctx context.Context, req *connect.Request[ermite.UpdateReleaseRequest]) (*connect.Response[ermite.UpdateReleaseResponse], error) {
220+ return c.updateRelease.CallUnary(ctx, req)
221+}
222+
223+// DeleteRelease calls ermite.ReleaseService.DeleteRelease.
224+func (c *releaseServiceClient) DeleteRelease(ctx context.Context, req *connect.Request[ermite.DeleteReleaseRequest]) (*connect.Response[ermite.DeleteReleaseResponse], error) {
225+ return c.deleteRelease.CallUnary(ctx, req)
226+}
227+
228+// UploadReleaseAsset calls ermite.ReleaseService.UploadReleaseAsset.
229+func (c *releaseServiceClient) UploadReleaseAsset(ctx context.Context) *connect.ClientStreamForClient[ermite.UploadReleaseAssetRequest, ermite.UploadReleaseAssetResponse] {
230+ return c.uploadReleaseAsset.CallClientStream(ctx)
231+}
232+
233+// UpdateReleaseAsset calls ermite.ReleaseService.UpdateReleaseAsset.
234+func (c *releaseServiceClient) UpdateReleaseAsset(ctx context.Context, req *connect.Request[ermite.UpdateReleaseAssetRequest]) (*connect.Response[ermite.UpdateReleaseAssetResponse], error) {
235+ return c.updateReleaseAsset.CallUnary(ctx, req)
236+}
237+
238+// DeleteReleaseAsset calls ermite.ReleaseService.DeleteReleaseAsset.
239+func (c *releaseServiceClient) DeleteReleaseAsset(ctx context.Context, req *connect.Request[ermite.DeleteReleaseAssetRequest]) (*connect.Response[ermite.DeleteReleaseAssetResponse], error) {
240+ return c.deleteReleaseAsset.CallUnary(ctx, req)
241+}
242+
243+// DownloadReleaseAsset calls ermite.ReleaseService.DownloadReleaseAsset.
244+func (c *releaseServiceClient) DownloadReleaseAsset(ctx context.Context, req *connect.Request[ermite.DownloadReleaseAssetRequest]) (*connect.ServerStreamForClient[ermite.DownloadReleaseAssetResponse], error) {
245+ return c.downloadReleaseAsset.CallServerStream(ctx, req)
246+}
247+
248+// ListReleaseAssets calls ermite.ReleaseService.ListReleaseAssets.
249+func (c *releaseServiceClient) ListReleaseAssets(ctx context.Context, req *connect.Request[ermite.ListReleaseAssetsRequest]) (*connect.Response[ermite.ListReleaseAssetsResponse], error) {
250+ return c.listReleaseAssets.CallUnary(ctx, req)
251+}
252+
253+// GetReleaseUsage calls ermite.ReleaseService.GetReleaseUsage.
254+func (c *releaseServiceClient) GetReleaseUsage(ctx context.Context, req *connect.Request[ermite.GetReleaseUsageRequest]) (*connect.Response[ermite.GetReleaseUsageResponse], error) {
255+ return c.getReleaseUsage.CallUnary(ctx, req)
256+}
257+
258+// ReleaseServiceHandler is an implementation of the ermite.ReleaseService service.
259+type ReleaseServiceHandler interface {
260+ // CreateRelease creates one release per tag (already_exists on duplication).
261+ // Publishing without an existing tag is failed_precondition, unless
262+ // target_commitish is supplied to create it; existing tags never move.
263+ CreateRelease(context.Context, *connect.Request[ermite.CreateReleaseRequest]) (*connect.Response[ermite.CreateReleaseResponse], error)
264+ // GetRelease selects by id, tag or latest (latest must be true).
265+ GetRelease(context.Context, *connect.Request[ermite.GetReleaseRequest]) (*connect.Response[ermite.GetReleaseResponse], error)
266+ // ListReleases lists newest first, including drafts only for writers.
267+ ListReleases(context.Context, *connect.Request[ermite.ListReleasesRequest]) (*connect.Response[ermite.ListReleasesResponse], error)
268+ // UpdateRelease changes masked fields. Publishing requires an existing tag
269+ // or target_commitish to create it; otherwise failed_precondition.
270+ UpdateRelease(context.Context, *connect.Request[ermite.UpdateReleaseRequest]) (*connect.Response[ermite.UpdateReleaseResponse], error)
271+ // DeleteRelease removes a release and its assets.
272+ DeleteRelease(context.Context, *connect.Request[ermite.DeleteReleaseRequest]) (*connect.Response[ermite.DeleteReleaseResponse], error)
273+ // UploadReleaseAsset reserves size before reading. The first message carries
274+ // metadata; subsequent messages carry only data, at most 1 MiB per message.
275+ // Size must be nonnegative (-1 is refused). An interrupted stream stores no
276+ // asset. A taken name gives already_exists unless replace is true. Uploading
277+ // to a release of another repo gives failed_precondition.
278+ UploadReleaseAsset(context.Context, *connect.ClientStream[ermite.UploadReleaseAssetRequest]) (*connect.Response[ermite.UploadReleaseAssetResponse], error)
279+ // UpdateReleaseAsset changes masked name and label; a taken name gives already_exists.
280+ UpdateReleaseAsset(context.Context, *connect.Request[ermite.UpdateReleaseAssetRequest]) (*connect.Response[ermite.UpdateReleaseAssetResponse], error)
281+ // DeleteReleaseAsset removes an asset.
282+ DeleteReleaseAsset(context.Context, *connect.Request[ermite.DeleteReleaseAssetRequest]) (*connect.Response[ermite.DeleteReleaseAssetResponse], error)
283+ // DownloadReleaseAsset sends asset metadata first, then chunks of at most
284+ // 1 MiB. Resume with the offset after the last byte received. An offset
285+ // beyond the size gives out_of_range.
286+ DownloadReleaseAsset(context.Context, *connect.Request[ermite.DownloadReleaseAssetRequest], *connect.ServerStream[ermite.DownloadReleaseAssetResponse]) error
287+ // ListReleaseAssets lists uploaded assets, ordered by name.
288+ ListReleaseAssets(context.Context, *connect.Request[ermite.ListReleaseAssetsRequest]) (*connect.Response[ermite.ListReleaseAssetsResponse], error)
289+ // GetReleaseUsage returns usage (including reserved uploads) and limits.
290+ GetReleaseUsage(context.Context, *connect.Request[ermite.GetReleaseUsageRequest]) (*connect.Response[ermite.GetReleaseUsageResponse], error)
291+}
292+
293+// NewReleaseServiceHandler builds an HTTP handler from the service implementation. It returns the
294+// path on which to mount the handler and the handler itself.
295+//
296+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
297+// and JSON codecs. They also support gzip compression.
298+func NewReleaseServiceHandler(svc ReleaseServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
299+ releaseServiceMethods := ermite.File_ermite_release_proto.Services().ByName("ReleaseService").Methods()
300+ releaseServiceCreateReleaseHandler := connect.NewUnaryHandler(
301+ ReleaseServiceCreateReleaseProcedure,
302+ svc.CreateRelease,
303+ connect.WithSchema(releaseServiceMethods.ByName("CreateRelease")),
304+ connect.WithHandlerOptions(opts...),
305+ )
306+ releaseServiceGetReleaseHandler := connect.NewUnaryHandler(
307+ ReleaseServiceGetReleaseProcedure,
308+ svc.GetRelease,
309+ connect.WithSchema(releaseServiceMethods.ByName("GetRelease")),
310+ connect.WithHandlerOptions(opts...),
311+ )
312+ releaseServiceListReleasesHandler := connect.NewUnaryHandler(
313+ ReleaseServiceListReleasesProcedure,
314+ svc.ListReleases,
315+ connect.WithSchema(releaseServiceMethods.ByName("ListReleases")),
316+ connect.WithHandlerOptions(opts...),
317+ )
318+ releaseServiceUpdateReleaseHandler := connect.NewUnaryHandler(
319+ ReleaseServiceUpdateReleaseProcedure,
320+ svc.UpdateRelease,
321+ connect.WithSchema(releaseServiceMethods.ByName("UpdateRelease")),
322+ connect.WithHandlerOptions(opts...),
323+ )
324+ releaseServiceDeleteReleaseHandler := connect.NewUnaryHandler(
325+ ReleaseServiceDeleteReleaseProcedure,
326+ svc.DeleteRelease,
327+ connect.WithSchema(releaseServiceMethods.ByName("DeleteRelease")),
328+ connect.WithHandlerOptions(opts...),
329+ )
330+ releaseServiceUploadReleaseAssetHandler := connect.NewClientStreamHandler(
331+ ReleaseServiceUploadReleaseAssetProcedure,
332+ svc.UploadReleaseAsset,
333+ connect.WithSchema(releaseServiceMethods.ByName("UploadReleaseAsset")),
334+ connect.WithHandlerOptions(opts...),
335+ )
336+ releaseServiceUpdateReleaseAssetHandler := connect.NewUnaryHandler(
337+ ReleaseServiceUpdateReleaseAssetProcedure,
338+ svc.UpdateReleaseAsset,
339+ connect.WithSchema(releaseServiceMethods.ByName("UpdateReleaseAsset")),
340+ connect.WithHandlerOptions(opts...),
341+ )
342+ releaseServiceDeleteReleaseAssetHandler := connect.NewUnaryHandler(
343+ ReleaseServiceDeleteReleaseAssetProcedure,
344+ svc.DeleteReleaseAsset,
345+ connect.WithSchema(releaseServiceMethods.ByName("DeleteReleaseAsset")),
346+ connect.WithHandlerOptions(opts...),
347+ )
348+ releaseServiceDownloadReleaseAssetHandler := connect.NewServerStreamHandler(
349+ ReleaseServiceDownloadReleaseAssetProcedure,
350+ svc.DownloadReleaseAsset,
351+ connect.WithSchema(releaseServiceMethods.ByName("DownloadReleaseAsset")),
352+ connect.WithHandlerOptions(opts...),
353+ )
354+ releaseServiceListReleaseAssetsHandler := connect.NewUnaryHandler(
355+ ReleaseServiceListReleaseAssetsProcedure,
356+ svc.ListReleaseAssets,
357+ connect.WithSchema(releaseServiceMethods.ByName("ListReleaseAssets")),
358+ connect.WithHandlerOptions(opts...),
359+ )
360+ releaseServiceGetReleaseUsageHandler := connect.NewUnaryHandler(
361+ ReleaseServiceGetReleaseUsageProcedure,
362+ svc.GetReleaseUsage,
363+ connect.WithSchema(releaseServiceMethods.ByName("GetReleaseUsage")),
364+ connect.WithHandlerOptions(opts...),
365+ )
366+ return "/ermite.ReleaseService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
367+ switch r.URL.Path {
368+ case ReleaseServiceCreateReleaseProcedure:
369+ releaseServiceCreateReleaseHandler.ServeHTTP(w, r)
370+ case ReleaseServiceGetReleaseProcedure:
371+ releaseServiceGetReleaseHandler.ServeHTTP(w, r)
372+ case ReleaseServiceListReleasesProcedure:
373+ releaseServiceListReleasesHandler.ServeHTTP(w, r)
374+ case ReleaseServiceUpdateReleaseProcedure:
375+ releaseServiceUpdateReleaseHandler.ServeHTTP(w, r)
376+ case ReleaseServiceDeleteReleaseProcedure:
377+ releaseServiceDeleteReleaseHandler.ServeHTTP(w, r)
378+ case ReleaseServiceUploadReleaseAssetProcedure:
379+ releaseServiceUploadReleaseAssetHandler.ServeHTTP(w, r)
380+ case ReleaseServiceUpdateReleaseAssetProcedure:
381+ releaseServiceUpdateReleaseAssetHandler.ServeHTTP(w, r)
382+ case ReleaseServiceDeleteReleaseAssetProcedure:
383+ releaseServiceDeleteReleaseAssetHandler.ServeHTTP(w, r)
384+ case ReleaseServiceDownloadReleaseAssetProcedure:
385+ releaseServiceDownloadReleaseAssetHandler.ServeHTTP(w, r)
386+ case ReleaseServiceListReleaseAssetsProcedure:
387+ releaseServiceListReleaseAssetsHandler.ServeHTTP(w, r)
388+ case ReleaseServiceGetReleaseUsageProcedure:
389+ releaseServiceGetReleaseUsageHandler.ServeHTTP(w, r)
390+ default:
391+ http.NotFound(w, r)
392+ }
393+ })
394+}
395+
396+// UnimplementedReleaseServiceHandler returns CodeUnimplemented from all methods.
397+type UnimplementedReleaseServiceHandler struct{}
398+
399+func (UnimplementedReleaseServiceHandler) CreateRelease(context.Context, *connect.Request[ermite.CreateReleaseRequest]) (*connect.Response[ermite.CreateReleaseResponse], error) {
400+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.CreateRelease is not implemented"))
401+}
402+
403+func (UnimplementedReleaseServiceHandler) GetRelease(context.Context, *connect.Request[ermite.GetReleaseRequest]) (*connect.Response[ermite.GetReleaseResponse], error) {
404+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.GetRelease is not implemented"))
405+}
406+
407+func (UnimplementedReleaseServiceHandler) ListReleases(context.Context, *connect.Request[ermite.ListReleasesRequest]) (*connect.Response[ermite.ListReleasesResponse], error) {
408+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.ListReleases is not implemented"))
409+}
410+
411+func (UnimplementedReleaseServiceHandler) UpdateRelease(context.Context, *connect.Request[ermite.UpdateReleaseRequest]) (*connect.Response[ermite.UpdateReleaseResponse], error) {
412+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.UpdateRelease is not implemented"))
413+}
414+
415+func (UnimplementedReleaseServiceHandler) DeleteRelease(context.Context, *connect.Request[ermite.DeleteReleaseRequest]) (*connect.Response[ermite.DeleteReleaseResponse], error) {
416+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.DeleteRelease is not implemented"))
417+}
418+
419+func (UnimplementedReleaseServiceHandler) UploadReleaseAsset(context.Context, *connect.ClientStream[ermite.UploadReleaseAssetRequest]) (*connect.Response[ermite.UploadReleaseAssetResponse], error) {
420+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.UploadReleaseAsset is not implemented"))
421+}
422+
423+func (UnimplementedReleaseServiceHandler) UpdateReleaseAsset(context.Context, *connect.Request[ermite.UpdateReleaseAssetRequest]) (*connect.Response[ermite.UpdateReleaseAssetResponse], error) {
424+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.UpdateReleaseAsset is not implemented"))
425+}
426+
427+func (UnimplementedReleaseServiceHandler) DeleteReleaseAsset(context.Context, *connect.Request[ermite.DeleteReleaseAssetRequest]) (*connect.Response[ermite.DeleteReleaseAssetResponse], error) {
428+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.DeleteReleaseAsset is not implemented"))
429+}
430+
431+func (UnimplementedReleaseServiceHandler) DownloadReleaseAsset(context.Context, *connect.Request[ermite.DownloadReleaseAssetRequest], *connect.ServerStream[ermite.DownloadReleaseAssetResponse]) error {
432+ return connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.DownloadReleaseAsset is not implemented"))
433+}
434+
435+func (UnimplementedReleaseServiceHandler) ListReleaseAssets(context.Context, *connect.Request[ermite.ListReleaseAssetsRequest]) (*connect.Response[ermite.ListReleaseAssetsResponse], error) {
436+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.ListReleaseAssets is not implemented"))
437+}
438+
439+func (UnimplementedReleaseServiceHandler) GetReleaseUsage(context.Context, *connect.Request[ermite.GetReleaseUsageRequest]) (*connect.Response[ermite.GetReleaseUsageResponse], error) {
440+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.GetReleaseUsage is not implemented"))
441+}
@@ -0,0 +1,441 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/release.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+ // ReleaseServiceName is the fully-qualified name of the ReleaseService service.
27+ ReleaseServiceName = "ermite.ReleaseService"
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+ // ReleaseServiceCreateReleaseProcedure is the fully-qualified name of the ReleaseService's
39+ // CreateRelease RPC.
40+ ReleaseServiceCreateReleaseProcedure = "/ermite.ReleaseService/CreateRelease"
41+ // ReleaseServiceGetReleaseProcedure is the fully-qualified name of the ReleaseService's GetRelease
42+ // RPC.
43+ ReleaseServiceGetReleaseProcedure = "/ermite.ReleaseService/GetRelease"
44+ // ReleaseServiceListReleasesProcedure is the fully-qualified name of the ReleaseService's
45+ // ListReleases RPC.
46+ ReleaseServiceListReleasesProcedure = "/ermite.ReleaseService/ListReleases"
47+ // ReleaseServiceUpdateReleaseProcedure is the fully-qualified name of the ReleaseService's
48+ // UpdateRelease RPC.
49+ ReleaseServiceUpdateReleaseProcedure = "/ermite.ReleaseService/UpdateRelease"
50+ // ReleaseServiceDeleteReleaseProcedure is the fully-qualified name of the ReleaseService's
51+ // DeleteRelease RPC.
52+ ReleaseServiceDeleteReleaseProcedure = "/ermite.ReleaseService/DeleteRelease"
53+ // ReleaseServiceUploadReleaseAssetProcedure is the fully-qualified name of the ReleaseService's
54+ // UploadReleaseAsset RPC.
55+ ReleaseServiceUploadReleaseAssetProcedure = "/ermite.ReleaseService/UploadReleaseAsset"
56+ // ReleaseServiceUpdateReleaseAssetProcedure is the fully-qualified name of the ReleaseService's
57+ // UpdateReleaseAsset RPC.
58+ ReleaseServiceUpdateReleaseAssetProcedure = "/ermite.ReleaseService/UpdateReleaseAsset"
59+ // ReleaseServiceDeleteReleaseAssetProcedure is the fully-qualified name of the ReleaseService's
60+ // DeleteReleaseAsset RPC.
61+ ReleaseServiceDeleteReleaseAssetProcedure = "/ermite.ReleaseService/DeleteReleaseAsset"
62+ // ReleaseServiceDownloadReleaseAssetProcedure is the fully-qualified name of the ReleaseService's
63+ // DownloadReleaseAsset RPC.
64+ ReleaseServiceDownloadReleaseAssetProcedure = "/ermite.ReleaseService/DownloadReleaseAsset"
65+ // ReleaseServiceListReleaseAssetsProcedure is the fully-qualified name of the ReleaseService's
66+ // ListReleaseAssets RPC.
67+ ReleaseServiceListReleaseAssetsProcedure = "/ermite.ReleaseService/ListReleaseAssets"
68+ // ReleaseServiceGetReleaseUsageProcedure is the fully-qualified name of the ReleaseService's
69+ // GetReleaseUsage RPC.
70+ ReleaseServiceGetReleaseUsageProcedure = "/ermite.ReleaseService/GetReleaseUsage"
71+)
72+
73+// ReleaseServiceClient is a client for the ermite.ReleaseService service.
74+type ReleaseServiceClient interface {
75+ // CreateRelease creates one release per tag (already_exists on duplication).
76+ // Publishing without an existing tag is failed_precondition, unless
77+ // target_commitish is supplied to create it; existing tags never move.
78+ CreateRelease(context.Context, *connect.Request[ermite.CreateReleaseRequest]) (*connect.Response[ermite.CreateReleaseResponse], error)
79+ // GetRelease selects by id, tag or latest (latest must be true).
80+ GetRelease(context.Context, *connect.Request[ermite.GetReleaseRequest]) (*connect.Response[ermite.GetReleaseResponse], error)
81+ // ListReleases lists newest first, including drafts only for writers.
82+ ListReleases(context.Context, *connect.Request[ermite.ListReleasesRequest]) (*connect.Response[ermite.ListReleasesResponse], error)
83+ // UpdateRelease changes masked fields. Publishing requires an existing tag
84+ // or target_commitish to create it; otherwise failed_precondition.
85+ UpdateRelease(context.Context, *connect.Request[ermite.UpdateReleaseRequest]) (*connect.Response[ermite.UpdateReleaseResponse], error)
86+ // DeleteRelease removes a release and its assets.
87+ DeleteRelease(context.Context, *connect.Request[ermite.DeleteReleaseRequest]) (*connect.Response[ermite.DeleteReleaseResponse], error)
88+ // UploadReleaseAsset reserves size before reading. The first message carries
89+ // metadata; subsequent messages carry only data, at most 1 MiB per message.
90+ // Size must be nonnegative (-1 is refused). An interrupted stream stores no
91+ // asset. A taken name gives already_exists unless replace is true. Uploading
92+ // to a release of another repo gives failed_precondition.
93+ UploadReleaseAsset(context.Context) *connect.ClientStreamForClient[ermite.UploadReleaseAssetRequest, ermite.UploadReleaseAssetResponse]
94+ // UpdateReleaseAsset changes masked name and label; a taken name gives already_exists.
95+ UpdateReleaseAsset(context.Context, *connect.Request[ermite.UpdateReleaseAssetRequest]) (*connect.Response[ermite.UpdateReleaseAssetResponse], error)
96+ // DeleteReleaseAsset removes an asset.
97+ DeleteReleaseAsset(context.Context, *connect.Request[ermite.DeleteReleaseAssetRequest]) (*connect.Response[ermite.DeleteReleaseAssetResponse], error)
98+ // DownloadReleaseAsset sends asset metadata first, then chunks of at most
99+ // 1 MiB. Resume with the offset after the last byte received. An offset
100+ // beyond the size gives out_of_range.
101+ DownloadReleaseAsset(context.Context, *connect.Request[ermite.DownloadReleaseAssetRequest]) (*connect.ServerStreamForClient[ermite.DownloadReleaseAssetResponse], error)
102+ // ListReleaseAssets lists uploaded assets, ordered by name.
103+ ListReleaseAssets(context.Context, *connect.Request[ermite.ListReleaseAssetsRequest]) (*connect.Response[ermite.ListReleaseAssetsResponse], error)
104+ // GetReleaseUsage returns usage (including reserved uploads) and limits.
105+ GetReleaseUsage(context.Context, *connect.Request[ermite.GetReleaseUsageRequest]) (*connect.Response[ermite.GetReleaseUsageResponse], error)
106+}
107+
108+// NewReleaseServiceClient constructs a client for the ermite.ReleaseService service. By default, it
109+// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
110+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
111+// connect.WithGRPCWeb() options.
112+//
113+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
114+// http://api.acme.com or https://acme.com/grpc).
115+func NewReleaseServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) ReleaseServiceClient {
116+ baseURL = strings.TrimRight(baseURL, "/")
117+ releaseServiceMethods := ermite.File_ermite_release_proto.Services().ByName("ReleaseService").Methods()
118+ return &releaseServiceClient{
119+ createRelease: connect.NewClient[ermite.CreateReleaseRequest, ermite.CreateReleaseResponse](
120+ httpClient,
121+ baseURL+ReleaseServiceCreateReleaseProcedure,
122+ connect.WithSchema(releaseServiceMethods.ByName("CreateRelease")),
123+ connect.WithClientOptions(opts...),
124+ ),
125+ getRelease: connect.NewClient[ermite.GetReleaseRequest, ermite.GetReleaseResponse](
126+ httpClient,
127+ baseURL+ReleaseServiceGetReleaseProcedure,
128+ connect.WithSchema(releaseServiceMethods.ByName("GetRelease")),
129+ connect.WithClientOptions(opts...),
130+ ),
131+ listReleases: connect.NewClient[ermite.ListReleasesRequest, ermite.ListReleasesResponse](
132+ httpClient,
133+ baseURL+ReleaseServiceListReleasesProcedure,
134+ connect.WithSchema(releaseServiceMethods.ByName("ListReleases")),
135+ connect.WithClientOptions(opts...),
136+ ),
137+ updateRelease: connect.NewClient[ermite.UpdateReleaseRequest, ermite.UpdateReleaseResponse](
138+ httpClient,
139+ baseURL+ReleaseServiceUpdateReleaseProcedure,
140+ connect.WithSchema(releaseServiceMethods.ByName("UpdateRelease")),
141+ connect.WithClientOptions(opts...),
142+ ),
143+ deleteRelease: connect.NewClient[ermite.DeleteReleaseRequest, ermite.DeleteReleaseResponse](
144+ httpClient,
145+ baseURL+ReleaseServiceDeleteReleaseProcedure,
146+ connect.WithSchema(releaseServiceMethods.ByName("DeleteRelease")),
147+ connect.WithClientOptions(opts...),
148+ ),
149+ uploadReleaseAsset: connect.NewClient[ermite.UploadReleaseAssetRequest, ermite.UploadReleaseAssetResponse](
150+ httpClient,
151+ baseURL+ReleaseServiceUploadReleaseAssetProcedure,
152+ connect.WithSchema(releaseServiceMethods.ByName("UploadReleaseAsset")),
153+ connect.WithClientOptions(opts...),
154+ ),
155+ updateReleaseAsset: connect.NewClient[ermite.UpdateReleaseAssetRequest, ermite.UpdateReleaseAssetResponse](
156+ httpClient,
157+ baseURL+ReleaseServiceUpdateReleaseAssetProcedure,
158+ connect.WithSchema(releaseServiceMethods.ByName("UpdateReleaseAsset")),
159+ connect.WithClientOptions(opts...),
160+ ),
161+ deleteReleaseAsset: connect.NewClient[ermite.DeleteReleaseAssetRequest, ermite.DeleteReleaseAssetResponse](
162+ httpClient,
163+ baseURL+ReleaseServiceDeleteReleaseAssetProcedure,
164+ connect.WithSchema(releaseServiceMethods.ByName("DeleteReleaseAsset")),
165+ connect.WithClientOptions(opts...),
166+ ),
167+ downloadReleaseAsset: connect.NewClient[ermite.DownloadReleaseAssetRequest, ermite.DownloadReleaseAssetResponse](
168+ httpClient,
169+ baseURL+ReleaseServiceDownloadReleaseAssetProcedure,
170+ connect.WithSchema(releaseServiceMethods.ByName("DownloadReleaseAsset")),
171+ connect.WithClientOptions(opts...),
172+ ),
173+ listReleaseAssets: connect.NewClient[ermite.ListReleaseAssetsRequest, ermite.ListReleaseAssetsResponse](
174+ httpClient,
175+ baseURL+ReleaseServiceListReleaseAssetsProcedure,
176+ connect.WithSchema(releaseServiceMethods.ByName("ListReleaseAssets")),
177+ connect.WithClientOptions(opts...),
178+ ),
179+ getReleaseUsage: connect.NewClient[ermite.GetReleaseUsageRequest, ermite.GetReleaseUsageResponse](
180+ httpClient,
181+ baseURL+ReleaseServiceGetReleaseUsageProcedure,
182+ connect.WithSchema(releaseServiceMethods.ByName("GetReleaseUsage")),
183+ connect.WithClientOptions(opts...),
184+ ),
185+ }
186+}
187+
188+// releaseServiceClient implements ReleaseServiceClient.
189+type releaseServiceClient struct {
190+ createRelease *connect.Client[ermite.CreateReleaseRequest, ermite.CreateReleaseResponse]
191+ getRelease *connect.Client[ermite.GetReleaseRequest, ermite.GetReleaseResponse]
192+ listReleases *connect.Client[ermite.ListReleasesRequest, ermite.ListReleasesResponse]
193+ updateRelease *connect.Client[ermite.UpdateReleaseRequest, ermite.UpdateReleaseResponse]
194+ deleteRelease *connect.Client[ermite.DeleteReleaseRequest, ermite.DeleteReleaseResponse]
195+ uploadReleaseAsset *connect.Client[ermite.UploadReleaseAssetRequest, ermite.UploadReleaseAssetResponse]
196+ updateReleaseAsset *connect.Client[ermite.UpdateReleaseAssetRequest, ermite.UpdateReleaseAssetResponse]
197+ deleteReleaseAsset *connect.Client[ermite.DeleteReleaseAssetRequest, ermite.DeleteReleaseAssetResponse]
198+ downloadReleaseAsset *connect.Client[ermite.DownloadReleaseAssetRequest, ermite.DownloadReleaseAssetResponse]
199+ listReleaseAssets *connect.Client[ermite.ListReleaseAssetsRequest, ermite.ListReleaseAssetsResponse]
200+ getReleaseUsage *connect.Client[ermite.GetReleaseUsageRequest, ermite.GetReleaseUsageResponse]
201+}
202+
203+// CreateRelease calls ermite.ReleaseService.CreateRelease.
204+func (c *releaseServiceClient) CreateRelease(ctx context.Context, req *connect.Request[ermite.CreateReleaseRequest]) (*connect.Response[ermite.CreateReleaseResponse], error) {
205+ return c.createRelease.CallUnary(ctx, req)
206+}
207+
208+// GetRelease calls ermite.ReleaseService.GetRelease.
209+func (c *releaseServiceClient) GetRelease(ctx context.Context, req *connect.Request[ermite.GetReleaseRequest]) (*connect.Response[ermite.GetReleaseResponse], error) {
210+ return c.getRelease.CallUnary(ctx, req)
211+}
212+
213+// ListReleases calls ermite.ReleaseService.ListReleases.
214+func (c *releaseServiceClient) ListReleases(ctx context.Context, req *connect.Request[ermite.ListReleasesRequest]) (*connect.Response[ermite.ListReleasesResponse], error) {
215+ return c.listReleases.CallUnary(ctx, req)
216+}
217+
218+// UpdateRelease calls ermite.ReleaseService.UpdateRelease.
219+func (c *releaseServiceClient) UpdateRelease(ctx context.Context, req *connect.Request[ermite.UpdateReleaseRequest]) (*connect.Response[ermite.UpdateReleaseResponse], error) {
220+ return c.updateRelease.CallUnary(ctx, req)
221+}
222+
223+// DeleteRelease calls ermite.ReleaseService.DeleteRelease.
224+func (c *releaseServiceClient) DeleteRelease(ctx context.Context, req *connect.Request[ermite.DeleteReleaseRequest]) (*connect.Response[ermite.DeleteReleaseResponse], error) {
225+ return c.deleteRelease.CallUnary(ctx, req)
226+}
227+
228+// UploadReleaseAsset calls ermite.ReleaseService.UploadReleaseAsset.
229+func (c *releaseServiceClient) UploadReleaseAsset(ctx context.Context) *connect.ClientStreamForClient[ermite.UploadReleaseAssetRequest, ermite.UploadReleaseAssetResponse] {
230+ return c.uploadReleaseAsset.CallClientStream(ctx)
231+}
232+
233+// UpdateReleaseAsset calls ermite.ReleaseService.UpdateReleaseAsset.
234+func (c *releaseServiceClient) UpdateReleaseAsset(ctx context.Context, req *connect.Request[ermite.UpdateReleaseAssetRequest]) (*connect.Response[ermite.UpdateReleaseAssetResponse], error) {
235+ return c.updateReleaseAsset.CallUnary(ctx, req)
236+}
237+
238+// DeleteReleaseAsset calls ermite.ReleaseService.DeleteReleaseAsset.
239+func (c *releaseServiceClient) DeleteReleaseAsset(ctx context.Context, req *connect.Request[ermite.DeleteReleaseAssetRequest]) (*connect.Response[ermite.DeleteReleaseAssetResponse], error) {
240+ return c.deleteReleaseAsset.CallUnary(ctx, req)
241+}
242+
243+// DownloadReleaseAsset calls ermite.ReleaseService.DownloadReleaseAsset.
244+func (c *releaseServiceClient) DownloadReleaseAsset(ctx context.Context, req *connect.Request[ermite.DownloadReleaseAssetRequest]) (*connect.ServerStreamForClient[ermite.DownloadReleaseAssetResponse], error) {
245+ return c.downloadReleaseAsset.CallServerStream(ctx, req)
246+}
247+
248+// ListReleaseAssets calls ermite.ReleaseService.ListReleaseAssets.
249+func (c *releaseServiceClient) ListReleaseAssets(ctx context.Context, req *connect.Request[ermite.ListReleaseAssetsRequest]) (*connect.Response[ermite.ListReleaseAssetsResponse], error) {
250+ return c.listReleaseAssets.CallUnary(ctx, req)
251+}
252+
253+// GetReleaseUsage calls ermite.ReleaseService.GetReleaseUsage.
254+func (c *releaseServiceClient) GetReleaseUsage(ctx context.Context, req *connect.Request[ermite.GetReleaseUsageRequest]) (*connect.Response[ermite.GetReleaseUsageResponse], error) {
255+ return c.getReleaseUsage.CallUnary(ctx, req)
256+}
257+
258+// ReleaseServiceHandler is an implementation of the ermite.ReleaseService service.
259+type ReleaseServiceHandler interface {
260+ // CreateRelease creates one release per tag (already_exists on duplication).
261+ // Publishing without an existing tag is failed_precondition, unless
262+ // target_commitish is supplied to create it; existing tags never move.
263+ CreateRelease(context.Context, *connect.Request[ermite.CreateReleaseRequest]) (*connect.Response[ermite.CreateReleaseResponse], error)
264+ // GetRelease selects by id, tag or latest (latest must be true).
265+ GetRelease(context.Context, *connect.Request[ermite.GetReleaseRequest]) (*connect.Response[ermite.GetReleaseResponse], error)
266+ // ListReleases lists newest first, including drafts only for writers.
267+ ListReleases(context.Context, *connect.Request[ermite.ListReleasesRequest]) (*connect.Response[ermite.ListReleasesResponse], error)
268+ // UpdateRelease changes masked fields. Publishing requires an existing tag
269+ // or target_commitish to create it; otherwise failed_precondition.
270+ UpdateRelease(context.Context, *connect.Request[ermite.UpdateReleaseRequest]) (*connect.Response[ermite.UpdateReleaseResponse], error)
271+ // DeleteRelease removes a release and its assets.
272+ DeleteRelease(context.Context, *connect.Request[ermite.DeleteReleaseRequest]) (*connect.Response[ermite.DeleteReleaseResponse], error)
273+ // UploadReleaseAsset reserves size before reading. The first message carries
274+ // metadata; subsequent messages carry only data, at most 1 MiB per message.
275+ // Size must be nonnegative (-1 is refused). An interrupted stream stores no
276+ // asset. A taken name gives already_exists unless replace is true. Uploading
277+ // to a release of another repo gives failed_precondition.
278+ UploadReleaseAsset(context.Context, *connect.ClientStream[ermite.UploadReleaseAssetRequest]) (*connect.Response[ermite.UploadReleaseAssetResponse], error)
279+ // UpdateReleaseAsset changes masked name and label; a taken name gives already_exists.
280+ UpdateReleaseAsset(context.Context, *connect.Request[ermite.UpdateReleaseAssetRequest]) (*connect.Response[ermite.UpdateReleaseAssetResponse], error)
281+ // DeleteReleaseAsset removes an asset.
282+ DeleteReleaseAsset(context.Context, *connect.Request[ermite.DeleteReleaseAssetRequest]) (*connect.Response[ermite.DeleteReleaseAssetResponse], error)
283+ // DownloadReleaseAsset sends asset metadata first, then chunks of at most
284+ // 1 MiB. Resume with the offset after the last byte received. An offset
285+ // beyond the size gives out_of_range.
286+ DownloadReleaseAsset(context.Context, *connect.Request[ermite.DownloadReleaseAssetRequest], *connect.ServerStream[ermite.DownloadReleaseAssetResponse]) error
287+ // ListReleaseAssets lists uploaded assets, ordered by name.
288+ ListReleaseAssets(context.Context, *connect.Request[ermite.ListReleaseAssetsRequest]) (*connect.Response[ermite.ListReleaseAssetsResponse], error)
289+ // GetReleaseUsage returns usage (including reserved uploads) and limits.
290+ GetReleaseUsage(context.Context, *connect.Request[ermite.GetReleaseUsageRequest]) (*connect.Response[ermite.GetReleaseUsageResponse], error)
291+}
292+
293+// NewReleaseServiceHandler builds an HTTP handler from the service implementation. It returns the
294+// path on which to mount the handler and the handler itself.
295+//
296+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
297+// and JSON codecs. They also support gzip compression.
298+func NewReleaseServiceHandler(svc ReleaseServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
299+ releaseServiceMethods := ermite.File_ermite_release_proto.Services().ByName("ReleaseService").Methods()
300+ releaseServiceCreateReleaseHandler := connect.NewUnaryHandler(
301+ ReleaseServiceCreateReleaseProcedure,
302+ svc.CreateRelease,
303+ connect.WithSchema(releaseServiceMethods.ByName("CreateRelease")),
304+ connect.WithHandlerOptions(opts...),
305+ )
306+ releaseServiceGetReleaseHandler := connect.NewUnaryHandler(
307+ ReleaseServiceGetReleaseProcedure,
308+ svc.GetRelease,
309+ connect.WithSchema(releaseServiceMethods.ByName("GetRelease")),
310+ connect.WithHandlerOptions(opts...),
311+ )
312+ releaseServiceListReleasesHandler := connect.NewUnaryHandler(
313+ ReleaseServiceListReleasesProcedure,
314+ svc.ListReleases,
315+ connect.WithSchema(releaseServiceMethods.ByName("ListReleases")),
316+ connect.WithHandlerOptions(opts...),
317+ )
318+ releaseServiceUpdateReleaseHandler := connect.NewUnaryHandler(
319+ ReleaseServiceUpdateReleaseProcedure,
320+ svc.UpdateRelease,
321+ connect.WithSchema(releaseServiceMethods.ByName("UpdateRelease")),
322+ connect.WithHandlerOptions(opts...),
323+ )
324+ releaseServiceDeleteReleaseHandler := connect.NewUnaryHandler(
325+ ReleaseServiceDeleteReleaseProcedure,
326+ svc.DeleteRelease,
327+ connect.WithSchema(releaseServiceMethods.ByName("DeleteRelease")),
328+ connect.WithHandlerOptions(opts...),
329+ )
330+ releaseServiceUploadReleaseAssetHandler := connect.NewClientStreamHandler(
331+ ReleaseServiceUploadReleaseAssetProcedure,
332+ svc.UploadReleaseAsset,
333+ connect.WithSchema(releaseServiceMethods.ByName("UploadReleaseAsset")),
334+ connect.WithHandlerOptions(opts...),
335+ )
336+ releaseServiceUpdateReleaseAssetHandler := connect.NewUnaryHandler(
337+ ReleaseServiceUpdateReleaseAssetProcedure,
338+ svc.UpdateReleaseAsset,
339+ connect.WithSchema(releaseServiceMethods.ByName("UpdateReleaseAsset")),
340+ connect.WithHandlerOptions(opts...),
341+ )
342+ releaseServiceDeleteReleaseAssetHandler := connect.NewUnaryHandler(
343+ ReleaseServiceDeleteReleaseAssetProcedure,
344+ svc.DeleteReleaseAsset,
345+ connect.WithSchema(releaseServiceMethods.ByName("DeleteReleaseAsset")),
346+ connect.WithHandlerOptions(opts...),
347+ )
348+ releaseServiceDownloadReleaseAssetHandler := connect.NewServerStreamHandler(
349+ ReleaseServiceDownloadReleaseAssetProcedure,
350+ svc.DownloadReleaseAsset,
351+ connect.WithSchema(releaseServiceMethods.ByName("DownloadReleaseAsset")),
352+ connect.WithHandlerOptions(opts...),
353+ )
354+ releaseServiceListReleaseAssetsHandler := connect.NewUnaryHandler(
355+ ReleaseServiceListReleaseAssetsProcedure,
356+ svc.ListReleaseAssets,
357+ connect.WithSchema(releaseServiceMethods.ByName("ListReleaseAssets")),
358+ connect.WithHandlerOptions(opts...),
359+ )
360+ releaseServiceGetReleaseUsageHandler := connect.NewUnaryHandler(
361+ ReleaseServiceGetReleaseUsageProcedure,
362+ svc.GetReleaseUsage,
363+ connect.WithSchema(releaseServiceMethods.ByName("GetReleaseUsage")),
364+ connect.WithHandlerOptions(opts...),
365+ )
366+ return "/ermite.ReleaseService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
367+ switch r.URL.Path {
368+ case ReleaseServiceCreateReleaseProcedure:
369+ releaseServiceCreateReleaseHandler.ServeHTTP(w, r)
370+ case ReleaseServiceGetReleaseProcedure:
371+ releaseServiceGetReleaseHandler.ServeHTTP(w, r)
372+ case ReleaseServiceListReleasesProcedure:
373+ releaseServiceListReleasesHandler.ServeHTTP(w, r)
374+ case ReleaseServiceUpdateReleaseProcedure:
375+ releaseServiceUpdateReleaseHandler.ServeHTTP(w, r)
376+ case ReleaseServiceDeleteReleaseProcedure:
377+ releaseServiceDeleteReleaseHandler.ServeHTTP(w, r)
378+ case ReleaseServiceUploadReleaseAssetProcedure:
379+ releaseServiceUploadReleaseAssetHandler.ServeHTTP(w, r)
380+ case ReleaseServiceUpdateReleaseAssetProcedure:
381+ releaseServiceUpdateReleaseAssetHandler.ServeHTTP(w, r)
382+ case ReleaseServiceDeleteReleaseAssetProcedure:
383+ releaseServiceDeleteReleaseAssetHandler.ServeHTTP(w, r)
384+ case ReleaseServiceDownloadReleaseAssetProcedure:
385+ releaseServiceDownloadReleaseAssetHandler.ServeHTTP(w, r)
386+ case ReleaseServiceListReleaseAssetsProcedure:
387+ releaseServiceListReleaseAssetsHandler.ServeHTTP(w, r)
388+ case ReleaseServiceGetReleaseUsageProcedure:
389+ releaseServiceGetReleaseUsageHandler.ServeHTTP(w, r)
390+ default:
391+ http.NotFound(w, r)
392+ }
393+ })
394+}
395+
396+// UnimplementedReleaseServiceHandler returns CodeUnimplemented from all methods.
397+type UnimplementedReleaseServiceHandler struct{}
398+
399+func (UnimplementedReleaseServiceHandler) CreateRelease(context.Context, *connect.Request[ermite.CreateReleaseRequest]) (*connect.Response[ermite.CreateReleaseResponse], error) {
400+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.CreateRelease is not implemented"))
401+}
402+
403+func (UnimplementedReleaseServiceHandler) GetRelease(context.Context, *connect.Request[ermite.GetReleaseRequest]) (*connect.Response[ermite.GetReleaseResponse], error) {
404+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.GetRelease is not implemented"))
405+}
406+
407+func (UnimplementedReleaseServiceHandler) ListReleases(context.Context, *connect.Request[ermite.ListReleasesRequest]) (*connect.Response[ermite.ListReleasesResponse], error) {
408+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.ListReleases is not implemented"))
409+}
410+
411+func (UnimplementedReleaseServiceHandler) UpdateRelease(context.Context, *connect.Request[ermite.UpdateReleaseRequest]) (*connect.Response[ermite.UpdateReleaseResponse], error) {
412+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.UpdateRelease is not implemented"))
413+}
414+
415+func (UnimplementedReleaseServiceHandler) DeleteRelease(context.Context, *connect.Request[ermite.DeleteReleaseRequest]) (*connect.Response[ermite.DeleteReleaseResponse], error) {
416+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.DeleteRelease is not implemented"))
417+}
418+
419+func (UnimplementedReleaseServiceHandler) UploadReleaseAsset(context.Context, *connect.ClientStream[ermite.UploadReleaseAssetRequest]) (*connect.Response[ermite.UploadReleaseAssetResponse], error) {
420+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.UploadReleaseAsset is not implemented"))
421+}
422+
423+func (UnimplementedReleaseServiceHandler) UpdateReleaseAsset(context.Context, *connect.Request[ermite.UpdateReleaseAssetRequest]) (*connect.Response[ermite.UpdateReleaseAssetResponse], error) {
424+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.UpdateReleaseAsset is not implemented"))
425+}
426+
427+func (UnimplementedReleaseServiceHandler) DeleteReleaseAsset(context.Context, *connect.Request[ermite.DeleteReleaseAssetRequest]) (*connect.Response[ermite.DeleteReleaseAssetResponse], error) {
428+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.DeleteReleaseAsset is not implemented"))
429+}
430+
431+func (UnimplementedReleaseServiceHandler) DownloadReleaseAsset(context.Context, *connect.Request[ermite.DownloadReleaseAssetRequest], *connect.ServerStream[ermite.DownloadReleaseAssetResponse]) error {
432+ return connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.DownloadReleaseAsset is not implemented"))
433+}
434+
435+func (UnimplementedReleaseServiceHandler) ListReleaseAssets(context.Context, *connect.Request[ermite.ListReleaseAssetsRequest]) (*connect.Response[ermite.ListReleaseAssetsResponse], error) {
436+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.ListReleaseAssets is not implemented"))
437+}
438+
439+func (UnimplementedReleaseServiceHandler) GetReleaseUsage(context.Context, *connect.Request[ermite.GetReleaseUsageRequest]) (*connect.Response[ermite.GetReleaseUsageResponse], error) {
440+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.ReleaseService.GetReleaseUsage is not implemented"))
441+}
M gen/ermite/event.pb.go +447 -43 generated file: collapsed
A gen/ermite/release.pb.go +2106 -0 generated file: collapsed
M gen/ermite/server.pb.go +2 -1 generated file: collapsed
M gen/ermite/token.pb.go +8 -2 generated file: collapsed
M gen_test.go +20 -0
@@ -19,6 +19,18 @@ 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.ReleaseServiceCreateReleaseProcedure, "/ermite.ReleaseService/CreateRelease"},
23+ {ermiteconnect.ReleaseServiceGetReleaseProcedure, "/ermite.ReleaseService/GetRelease"},
24+ {ermiteconnect.ReleaseServiceListReleasesProcedure, "/ermite.ReleaseService/ListReleases"},
25+ {ermiteconnect.ReleaseServiceUpdateReleaseProcedure, "/ermite.ReleaseService/UpdateRelease"},
26+ {ermiteconnect.ReleaseServiceDeleteReleaseProcedure, "/ermite.ReleaseService/DeleteRelease"},
27+ {ermiteconnect.ReleaseServiceUploadReleaseAssetProcedure, "/ermite.ReleaseService/UploadReleaseAsset"},
28+ {ermiteconnect.ReleaseServiceUpdateReleaseAssetProcedure, "/ermite.ReleaseService/UpdateReleaseAsset"},
29+ {ermiteconnect.ReleaseServiceDeleteReleaseAssetProcedure, "/ermite.ReleaseService/DeleteReleaseAsset"},
30+ {ermiteconnect.ReleaseServiceDownloadReleaseAssetProcedure, "/ermite.ReleaseService/DownloadReleaseAsset"},
31+ {ermiteconnect.ReleaseServiceListReleaseAssetsProcedure, "/ermite.ReleaseService/ListReleaseAssets"},
32+ {ermiteconnect.ReleaseServiceGetReleaseUsageProcedure, "/ermite.ReleaseService/GetReleaseUsage"},
33+
2234 {ermiteconnect.MirrorServiceGetMirrorKeyProcedure, "/ermite.MirrorService/GetMirrorKey"},
2335 {ermiteconnect.MirrorServiceRotateMirrorKeyProcedure, "/ermite.MirrorService/RotateMirrorKey"},
2436 {ermiteconnect.MirrorServiceGetMirrorConfigProcedure, "/ermite.MirrorService/GetMirrorConfig"},
@@ -615,3 +627,11 @@ func TestMirrorWire(t *testing.T) {
615627 }
616628 }
617629 }
630+
631+// TestReleaseSymbols pins the scope and event wrapper exports used by callers.
632+func TestReleaseSymbols(t *testing.T) {
633+ if ermite.Scope_SCOPE_RELEASE_WRITE != 13 {
634+ t.Fatal("release scope wire value changed")
635+ }
636+ var _ proto.Message = &ermite.Event{Kind: &ermite.Event_ReleasePublished{ReleasePublished: &ermite.ReleasePublished{}}}
637+}
@@ -19,6 +19,18 @@ 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.ReleaseServiceCreateReleaseProcedure, "/ermite.ReleaseService/CreateRelease"},
23+ {ermiteconnect.ReleaseServiceGetReleaseProcedure, "/ermite.ReleaseService/GetRelease"},
24+ {ermiteconnect.ReleaseServiceListReleasesProcedure, "/ermite.ReleaseService/ListReleases"},
25+ {ermiteconnect.ReleaseServiceUpdateReleaseProcedure, "/ermite.ReleaseService/UpdateRelease"},
26+ {ermiteconnect.ReleaseServiceDeleteReleaseProcedure, "/ermite.ReleaseService/DeleteRelease"},
27+ {ermiteconnect.ReleaseServiceUploadReleaseAssetProcedure, "/ermite.ReleaseService/UploadReleaseAsset"},
28+ {ermiteconnect.ReleaseServiceUpdateReleaseAssetProcedure, "/ermite.ReleaseService/UpdateReleaseAsset"},
29+ {ermiteconnect.ReleaseServiceDeleteReleaseAssetProcedure, "/ermite.ReleaseService/DeleteReleaseAsset"},
30+ {ermiteconnect.ReleaseServiceDownloadReleaseAssetProcedure, "/ermite.ReleaseService/DownloadReleaseAsset"},
31+ {ermiteconnect.ReleaseServiceListReleaseAssetsProcedure, "/ermite.ReleaseService/ListReleaseAssets"},
32+ {ermiteconnect.ReleaseServiceGetReleaseUsageProcedure, "/ermite.ReleaseService/GetReleaseUsage"},
33+
22 {ermiteconnect.MirrorServiceGetMirrorKeyProcedure, "/ermite.MirrorService/GetMirrorKey"},34 {ermiteconnect.MirrorServiceGetMirrorKeyProcedure, "/ermite.MirrorService/GetMirrorKey"},
23 {ermiteconnect.MirrorServiceRotateMirrorKeyProcedure, "/ermite.MirrorService/RotateMirrorKey"},35 {ermiteconnect.MirrorServiceRotateMirrorKeyProcedure, "/ermite.MirrorService/RotateMirrorKey"},
24 {ermiteconnect.MirrorServiceGetMirrorConfigProcedure, "/ermite.MirrorService/GetMirrorConfig"},36 {ermiteconnect.MirrorServiceGetMirrorConfigProcedure, "/ermite.MirrorService/GetMirrorConfig"},
@@ -615,3 +627,11 @@ func TestMirrorWire(t *testing.T) {
615 }627 }
616 }628 }
617 }629 }
630+
631+// TestReleaseSymbols pins the scope and event wrapper exports used by callers.
632+func TestReleaseSymbols(t *testing.T) {
633+ if ermite.Scope_SCOPE_RELEASE_WRITE != 13 {
634+ t.Fatal("release scope wire value changed")
635+ }
636+ var _ proto.Message = &ermite.Event{Kind: &ermite.Event_ReleasePublished{ReleasePublished: &ermite.ReleasePublished{}}}
637+}
M proto/ermite/event.proto +42 -0
@@ -145,6 +145,14 @@ message Event {
145145 // MergeRequestAutoMergeCancelled: the auto-merge of a merge request was
146146 // cancelled.
147147 MergeRequestAutoMergeCancelled merge_request_auto_merge_cancelled = 26;
148+ // ReleasePublished reports a release published.
149+ ReleasePublished release_published = 27;
150+ // ReleaseUpdated reports a release updated.
151+ ReleaseUpdated release_updated = 28;
152+ // ReleaseDeleted reports a release deleted.
153+ ReleaseDeleted release_deleted = 29;
154+ // ReleaseUnpublished reports a release turned into a draft after tag deletion.
155+ ReleaseUnpublished release_unpublished = 30;
148156 }
149157 }
150158
@@ -358,3 +366,37 @@ message MirrorRecovered {
358366 // Repo is the repository name.
359367 string repo = 1;
360368 }
369+
370+// ReleasePublished reports a release change on repo:<name> and home topics.
371+message ReleasePublished {
372+ string repo = 1;
373+ int64 id = 2;
374+ string tag = 3;
375+ bool draft = 4;
376+ bool prerelease = 5;
377+}
378+
379+// ReleaseUpdated reports a release change on repo:<name> and home topics.
380+message ReleaseUpdated {
381+ string repo = 1;
382+ int64 id = 2;
383+ string tag = 3;
384+ bool draft = 4;
385+ bool prerelease = 5;
386+}
387+
388+// ReleaseDeleted reports a release change on repo:<name> and home topics.
389+message ReleaseDeleted {
390+ string repo = 1;
391+ int64 id = 2;
392+ string tag = 3;
393+}
394+
395+// ReleaseUnpublished reports a release change on repo:<name> and home topics.
396+message ReleaseUnpublished {
397+ string repo = 1;
398+ int64 id = 2;
399+ string tag = 3;
400+ bool draft = 4;
401+ bool prerelease = 5;
402+}
@@ -145,6 +145,14 @@ message Event {
145 // MergeRequestAutoMergeCancelled: the auto-merge of a merge request was145 // MergeRequestAutoMergeCancelled: the auto-merge of a merge request was
146 // cancelled.146 // cancelled.
147 MergeRequestAutoMergeCancelled merge_request_auto_merge_cancelled = 26;147 MergeRequestAutoMergeCancelled merge_request_auto_merge_cancelled = 26;
148+ // ReleasePublished reports a release published.
149+ ReleasePublished release_published = 27;
150+ // ReleaseUpdated reports a release updated.
151+ ReleaseUpdated release_updated = 28;
152+ // ReleaseDeleted reports a release deleted.
153+ ReleaseDeleted release_deleted = 29;
154+ // ReleaseUnpublished reports a release turned into a draft after tag deletion.
155+ ReleaseUnpublished release_unpublished = 30;
148 }156 }
149 }157 }
150 158
@@ -358,3 +366,37 @@ message MirrorRecovered {
358 // Repo is the repository name.366 // Repo is the repository name.
359 string repo = 1;367 string repo = 1;
360 }368 }
369+
370+// ReleasePublished reports a release change on repo:<name> and home topics.
371+message ReleasePublished {
372+ string repo = 1;
373+ int64 id = 2;
374+ string tag = 3;
375+ bool draft = 4;
376+ bool prerelease = 5;
377+}
378+
379+// ReleaseUpdated reports a release change on repo:<name> and home topics.
380+message ReleaseUpdated {
381+ string repo = 1;
382+ int64 id = 2;
383+ string tag = 3;
384+ bool draft = 4;
385+ bool prerelease = 5;
386+}
387+
388+// ReleaseDeleted reports a release change on repo:<name> and home topics.
389+message ReleaseDeleted {
390+ string repo = 1;
391+ int64 id = 2;
392+ string tag = 3;
393+}
394+
395+// ReleaseUnpublished reports a release change on repo:<name> and home topics.
396+message ReleaseUnpublished {
397+ string repo = 1;
398+ int64 id = 2;
399+ string tag = 3;
400+ bool draft = 4;
401+ bool prerelease = 5;
402+}
A proto/ermite/release.proto +264 -0
@@ -0,0 +1,264 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "google/protobuf/field_mask.proto";
8+import "google/protobuf/timestamp.proto";
9+
10+// Release describes a tag and its notes. Drafts are visible only to writers.
11+message Release {
12+ int64 id = 1;
13+ string repo = 2;
14+ string tag = 3;
15+ string title = 4;
16+ string notes = 5;
17+ bool draft = 6;
18+ bool prerelease = 7;
19+ // Latest identifies the release returned by the latest selector, computed or pinned.
20+ bool latest = 8;
21+ // TargetCommitish is the tag's commit; empty for a draft without a tag.
22+ string target_commitish = 9;
23+ // Author is the label of the principal that created the release.
24+ string author = 10;
25+ google.protobuf.Timestamp created_at = 11;
26+ // PublishedAt is unset while draft.
27+ google.protobuf.Timestamp published_at = 12;
28+ google.protobuf.Timestamp updated_at = 13;
29+ // Assets contains uploaded assets only, ordered by name.
30+ repeated ReleaseAsset assets = 14;
31+}
32+
33+// ReleaseAsset describes an uploaded file, with a sha256:<hex> digest.
34+message ReleaseAsset {
35+ int64 id = 1;
36+ int64 release_id = 2;
37+ // Name is unique within the release, 1–255 characters, without slashes or controls.
38+ string name = 3;
39+ string label = 4;
40+ // ContentType defaults to application/octet-stream.
41+ string content_type = 5;
42+ int64 size = 6;
43+ string digest = 7;
44+ string uploader = 8;
45+ google.protobuf.Timestamp created_at = 9;
46+ google.protobuf.Timestamp updated_at = 10;
47+}
48+
49+// ReleaseService manages releases and their assets. Servers list "releases" in
50+// GetVersionResponse.features; other servers answer unimplemented.
51+// Published releases follow repository read access; drafts require write access.
52+// Unknown or unreadable repositories, releases and assets give not_found.
53+// Writes require release:write or a job token with contents: write for the repo;
54+// readers and branch-limited tokens receive permission_denied.
55+// Invalid names, media types or masks give invalid_argument. Size, asset count
56+// and repository quota limits give resource_exhausted and leave no stored upload.
57+service ReleaseService {
58+ // CreateRelease creates one release per tag (already_exists on duplication).
59+ // Publishing without an existing tag is failed_precondition, unless
60+ // target_commitish is supplied to create it; existing tags never move.
61+ rpc CreateRelease(CreateReleaseRequest) returns (CreateReleaseResponse);
62+ // GetRelease selects by id, tag or latest (latest must be true).
63+ rpc GetRelease(GetReleaseRequest) returns (GetReleaseResponse);
64+ // ListReleases lists newest first, including drafts only for writers.
65+ rpc ListReleases(ListReleasesRequest) returns (ListReleasesResponse);
66+ // UpdateRelease changes masked fields. Publishing requires an existing tag
67+ // or target_commitish to create it; otherwise failed_precondition.
68+ rpc UpdateRelease(UpdateReleaseRequest) returns (UpdateReleaseResponse);
69+ // DeleteRelease removes a release and its assets.
70+ rpc DeleteRelease(DeleteReleaseRequest) returns (DeleteReleaseResponse);
71+ // UploadReleaseAsset reserves size before reading. The first message carries
72+ // metadata; subsequent messages carry only data, at most 1 MiB per message.
73+ // Size must be nonnegative (-1 is refused). An interrupted stream stores no
74+ // asset. A taken name gives already_exists unless replace is true. Uploading
75+ // to a release of another repo gives failed_precondition.
76+ rpc UploadReleaseAsset(stream UploadReleaseAssetRequest) returns (UploadReleaseAssetResponse);
77+ // UpdateReleaseAsset changes masked name and label; a taken name gives already_exists.
78+ rpc UpdateReleaseAsset(UpdateReleaseAssetRequest) returns (UpdateReleaseAssetResponse);
79+ // DeleteReleaseAsset removes an asset.
80+ rpc DeleteReleaseAsset(DeleteReleaseAssetRequest) returns (DeleteReleaseAssetResponse);
81+ // DownloadReleaseAsset sends asset metadata first, then chunks of at most
82+ // 1 MiB. Resume with the offset after the last byte received. An offset
83+ // beyond the size gives out_of_range.
84+ rpc DownloadReleaseAsset(DownloadReleaseAssetRequest) returns (stream DownloadReleaseAssetResponse);
85+ // ListReleaseAssets lists uploaded assets, ordered by name.
86+ rpc ListReleaseAssets(ListReleaseAssetsRequest) returns (ListReleaseAssetsResponse);
87+ // GetReleaseUsage returns usage (including reserved uploads) and limits.
88+ rpc GetReleaseUsage(GetReleaseUsageRequest) returns (GetReleaseUsageResponse);
89+}
90+
91+// CreateReleaseRequest is the request of ReleaseService.CreateRelease.
92+message CreateReleaseRequest {
93+ string repo = 1;
94+ string tag = 2;
95+ string target_commitish = 3;
96+ string title = 4;
97+ string notes = 5;
98+ bool draft = 6;
99+ bool prerelease = 7;
100+ // MakeLatest pins this published non-prerelease as latest. False does not pin the release.
101+ bool make_latest = 8;
102+}
103+
104+// CreateReleaseResponse is the response of ReleaseService.CreateRelease.
105+message CreateReleaseResponse {
106+ Release release = 1;
107+}
108+
109+// ListReleasesRequest is the request of ReleaseService.ListReleases.
110+message ListReleasesRequest {
111+ string repo = 1;
112+ // PageSize is the maximum count; zero uses the server default.
113+ int32 page_size = 2;
114+ // PageToken is next_page_token from the preceding page, or empty initially.
115+ string page_token = 3;
116+}
117+
118+// ListReleasesResponse is the response of ReleaseService.ListReleases.
119+message ListReleasesResponse {
120+ repeated Release releases = 1;
121+ string next_page_token = 2;
122+}
123+
124+// UpdateReleaseRequest is the request of ReleaseService.UpdateRelease.
125+message UpdateReleaseRequest {
126+ string repo = 1;
127+ int64 id = 2;
128+ string tag = 3;
129+ string target_commitish = 4;
130+ string title = 5;
131+ string notes = 6;
132+ bool draft = 7;
133+ bool prerelease = 8;
134+ // MakeLatest pins this published non-prerelease as latest. False does not pin the release.
135+ bool make_latest = 9;
136+ // UpdateMask names the fields to change; unknown paths are invalid_argument.
137+ google.protobuf.FieldMask update_mask = 10;
138+}
139+
140+// UpdateReleaseResponse is the response of ReleaseService.UpdateRelease.
141+message UpdateReleaseResponse {
142+ Release release = 1;
143+}
144+
145+// DeleteReleaseRequest is the request of ReleaseService.DeleteRelease.
146+message DeleteReleaseRequest {
147+ string repo = 1;
148+ int64 id = 2;
149+}
150+
151+// DeleteReleaseResponse is the response of ReleaseService.DeleteRelease.
152+message DeleteReleaseResponse {
153+}
154+
155+// UploadReleaseAssetRequest is the request of ReleaseService.UploadReleaseAsset.
156+message UploadReleaseAssetRequest {
157+ string repo = 1;
158+ int64 release_id = 2;
159+ string name = 3;
160+ string label = 4;
161+ string content_type = 5;
162+ int64 size = 6;
163+ bool replace = 7;
164+ bytes data = 8;
165+}
166+
167+// UploadReleaseAssetResponse is the response of ReleaseService.UploadReleaseAsset.
168+message UploadReleaseAssetResponse {
169+ ReleaseAsset asset = 1;
170+}
171+
172+// UpdateReleaseAssetRequest is the request of ReleaseService.UpdateReleaseAsset.
173+message UpdateReleaseAssetRequest {
174+ string repo = 1;
175+ int64 asset_id = 2;
176+ string name = 3;
177+ string label = 4;
178+ // UpdateMask names the fields to change; unknown paths are invalid_argument.
179+ google.protobuf.FieldMask update_mask = 5;
180+}
181+
182+// UpdateReleaseAssetResponse is the response of ReleaseService.UpdateReleaseAsset.
183+message UpdateReleaseAssetResponse {
184+ ReleaseAsset asset = 1;
185+}
186+
187+// DeleteReleaseAssetRequest is the request of ReleaseService.DeleteReleaseAsset.
188+message DeleteReleaseAssetRequest {
189+ string repo = 1;
190+ int64 asset_id = 2;
191+}
192+
193+// DeleteReleaseAssetResponse is the response of ReleaseService.DeleteReleaseAsset.
194+message DeleteReleaseAssetResponse {
195+}
196+
197+// DownloadReleaseAssetResponse is the response of ReleaseService.DownloadReleaseAsset.
198+message DownloadReleaseAssetResponse {
199+ ReleaseAsset asset = 1;
200+ bytes chunk = 2;
201+}
202+
203+// ListReleaseAssetsRequest is the request of ReleaseService.ListReleaseAssets.
204+message ListReleaseAssetsRequest {
205+ string repo = 1;
206+ int64 release_id = 2;
207+}
208+
209+// ListReleaseAssetsResponse is the response of ReleaseService.ListReleaseAssets.
210+message ListReleaseAssetsResponse {
211+ repeated ReleaseAsset assets = 1;
212+}
213+
214+// GetReleaseUsageRequest is the request of ReleaseService.GetReleaseUsage.
215+message GetReleaseUsageRequest {
216+ string repo = 1;
217+}
218+
219+// GetReleaseUsageResponse is the response of ReleaseService.GetReleaseUsage.
220+message GetReleaseUsageResponse {
221+ int64 used_bytes = 1;
222+ int64 quota_bytes = 2;
223+ int64 asset_max_bytes = 3;
224+ int32 assets_max = 4;
225+}
226+
227+// GetReleaseRequest selects exactly one release.
228+message GetReleaseRequest {
229+ string repo = 1;
230+ oneof selector {
231+ int64 id = 2;
232+ string tag = 3;
233+ bool latest = 4;
234+ }
235+}
236+
237+// ReleaseAssetTag selects a named asset of a tag.
238+message ReleaseAssetTag {
239+ string tag = 1;
240+ string name = 2;
241+}
242+
243+// ReleaseAssetLatest selects a named asset of the latest release.
244+message ReleaseAssetLatest {
245+ // Latest must be true.
246+ bool latest = 1;
247+ string name = 2;
248+}
249+
250+// DownloadReleaseAssetRequest selects an asset and a resume offset.
251+message DownloadReleaseAssetRequest {
252+ string repo = 1;
253+ oneof selector {
254+ int64 asset_id = 2;
255+ ReleaseAssetTag tag = 3;
256+ ReleaseAssetLatest latest = 4;
257+ }
258+ int64 offset = 5;
259+}
260+
261+// GetReleaseResponse contains the selected release.
262+message GetReleaseResponse {
263+ Release release = 1;
264+}
@@ -0,0 +1,264 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "google/protobuf/field_mask.proto";
8+import "google/protobuf/timestamp.proto";
9+
10+// Release describes a tag and its notes. Drafts are visible only to writers.
11+message Release {
12+ int64 id = 1;
13+ string repo = 2;
14+ string tag = 3;
15+ string title = 4;
16+ string notes = 5;
17+ bool draft = 6;
18+ bool prerelease = 7;
19+ // Latest identifies the release returned by the latest selector, computed or pinned.
20+ bool latest = 8;
21+ // TargetCommitish is the tag's commit; empty for a draft without a tag.
22+ string target_commitish = 9;
23+ // Author is the label of the principal that created the release.
24+ string author = 10;
25+ google.protobuf.Timestamp created_at = 11;
26+ // PublishedAt is unset while draft.
27+ google.protobuf.Timestamp published_at = 12;
28+ google.protobuf.Timestamp updated_at = 13;
29+ // Assets contains uploaded assets only, ordered by name.
30+ repeated ReleaseAsset assets = 14;
31+}
32+
33+// ReleaseAsset describes an uploaded file, with a sha256:<hex> digest.
34+message ReleaseAsset {
35+ int64 id = 1;
36+ int64 release_id = 2;
37+ // Name is unique within the release, 1–255 characters, without slashes or controls.
38+ string name = 3;
39+ string label = 4;
40+ // ContentType defaults to application/octet-stream.
41+ string content_type = 5;
42+ int64 size = 6;
43+ string digest = 7;
44+ string uploader = 8;
45+ google.protobuf.Timestamp created_at = 9;
46+ google.protobuf.Timestamp updated_at = 10;
47+}
48+
49+// ReleaseService manages releases and their assets. Servers list "releases" in
50+// GetVersionResponse.features; other servers answer unimplemented.
51+// Published releases follow repository read access; drafts require write access.
52+// Unknown or unreadable repositories, releases and assets give not_found.
53+// Writes require release:write or a job token with contents: write for the repo;
54+// readers and branch-limited tokens receive permission_denied.
55+// Invalid names, media types or masks give invalid_argument. Size, asset count
56+// and repository quota limits give resource_exhausted and leave no stored upload.
57+service ReleaseService {
58+ // CreateRelease creates one release per tag (already_exists on duplication).
59+ // Publishing without an existing tag is failed_precondition, unless
60+ // target_commitish is supplied to create it; existing tags never move.
61+ rpc CreateRelease(CreateReleaseRequest) returns (CreateReleaseResponse);
62+ // GetRelease selects by id, tag or latest (latest must be true).
63+ rpc GetRelease(GetReleaseRequest) returns (GetReleaseResponse);
64+ // ListReleases lists newest first, including drafts only for writers.
65+ rpc ListReleases(ListReleasesRequest) returns (ListReleasesResponse);
66+ // UpdateRelease changes masked fields. Publishing requires an existing tag
67+ // or target_commitish to create it; otherwise failed_precondition.
68+ rpc UpdateRelease(UpdateReleaseRequest) returns (UpdateReleaseResponse);
69+ // DeleteRelease removes a release and its assets.
70+ rpc DeleteRelease(DeleteReleaseRequest) returns (DeleteReleaseResponse);
71+ // UploadReleaseAsset reserves size before reading. The first message carries
72+ // metadata; subsequent messages carry only data, at most 1 MiB per message.
73+ // Size must be nonnegative (-1 is refused). An interrupted stream stores no
74+ // asset. A taken name gives already_exists unless replace is true. Uploading
75+ // to a release of another repo gives failed_precondition.
76+ rpc UploadReleaseAsset(stream UploadReleaseAssetRequest) returns (UploadReleaseAssetResponse);
77+ // UpdateReleaseAsset changes masked name and label; a taken name gives already_exists.
78+ rpc UpdateReleaseAsset(UpdateReleaseAssetRequest) returns (UpdateReleaseAssetResponse);
79+ // DeleteReleaseAsset removes an asset.
80+ rpc DeleteReleaseAsset(DeleteReleaseAssetRequest) returns (DeleteReleaseAssetResponse);
81+ // DownloadReleaseAsset sends asset metadata first, then chunks of at most
82+ // 1 MiB. Resume with the offset after the last byte received. An offset
83+ // beyond the size gives out_of_range.
84+ rpc DownloadReleaseAsset(DownloadReleaseAssetRequest) returns (stream DownloadReleaseAssetResponse);
85+ // ListReleaseAssets lists uploaded assets, ordered by name.
86+ rpc ListReleaseAssets(ListReleaseAssetsRequest) returns (ListReleaseAssetsResponse);
87+ // GetReleaseUsage returns usage (including reserved uploads) and limits.
88+ rpc GetReleaseUsage(GetReleaseUsageRequest) returns (GetReleaseUsageResponse);
89+}
90+
91+// CreateReleaseRequest is the request of ReleaseService.CreateRelease.
92+message CreateReleaseRequest {
93+ string repo = 1;
94+ string tag = 2;
95+ string target_commitish = 3;
96+ string title = 4;
97+ string notes = 5;
98+ bool draft = 6;
99+ bool prerelease = 7;
100+ // MakeLatest pins this published non-prerelease as latest. False does not pin the release.
101+ bool make_latest = 8;
102+}
103+
104+// CreateReleaseResponse is the response of ReleaseService.CreateRelease.
105+message CreateReleaseResponse {
106+ Release release = 1;
107+}
108+
109+// ListReleasesRequest is the request of ReleaseService.ListReleases.
110+message ListReleasesRequest {
111+ string repo = 1;
112+ // PageSize is the maximum count; zero uses the server default.
113+ int32 page_size = 2;
114+ // PageToken is next_page_token from the preceding page, or empty initially.
115+ string page_token = 3;
116+}
117+
118+// ListReleasesResponse is the response of ReleaseService.ListReleases.
119+message ListReleasesResponse {
120+ repeated Release releases = 1;
121+ string next_page_token = 2;
122+}
123+
124+// UpdateReleaseRequest is the request of ReleaseService.UpdateRelease.
125+message UpdateReleaseRequest {
126+ string repo = 1;
127+ int64 id = 2;
128+ string tag = 3;
129+ string target_commitish = 4;
130+ string title = 5;
131+ string notes = 6;
132+ bool draft = 7;
133+ bool prerelease = 8;
134+ // MakeLatest pins this published non-prerelease as latest. False does not pin the release.
135+ bool make_latest = 9;
136+ // UpdateMask names the fields to change; unknown paths are invalid_argument.
137+ google.protobuf.FieldMask update_mask = 10;
138+}
139+
140+// UpdateReleaseResponse is the response of ReleaseService.UpdateRelease.
141+message UpdateReleaseResponse {
142+ Release release = 1;
143+}
144+
145+// DeleteReleaseRequest is the request of ReleaseService.DeleteRelease.
146+message DeleteReleaseRequest {
147+ string repo = 1;
148+ int64 id = 2;
149+}
150+
151+// DeleteReleaseResponse is the response of ReleaseService.DeleteRelease.
152+message DeleteReleaseResponse {
153+}
154+
155+// UploadReleaseAssetRequest is the request of ReleaseService.UploadReleaseAsset.
156+message UploadReleaseAssetRequest {
157+ string repo = 1;
158+ int64 release_id = 2;
159+ string name = 3;
160+ string label = 4;
161+ string content_type = 5;
162+ int64 size = 6;
163+ bool replace = 7;
164+ bytes data = 8;
165+}
166+
167+// UploadReleaseAssetResponse is the response of ReleaseService.UploadReleaseAsset.
168+message UploadReleaseAssetResponse {
169+ ReleaseAsset asset = 1;
170+}
171+
172+// UpdateReleaseAssetRequest is the request of ReleaseService.UpdateReleaseAsset.
173+message UpdateReleaseAssetRequest {
174+ string repo = 1;
175+ int64 asset_id = 2;
176+ string name = 3;
177+ string label = 4;
178+ // UpdateMask names the fields to change; unknown paths are invalid_argument.
179+ google.protobuf.FieldMask update_mask = 5;
180+}
181+
182+// UpdateReleaseAssetResponse is the response of ReleaseService.UpdateReleaseAsset.
183+message UpdateReleaseAssetResponse {
184+ ReleaseAsset asset = 1;
185+}
186+
187+// DeleteReleaseAssetRequest is the request of ReleaseService.DeleteReleaseAsset.
188+message DeleteReleaseAssetRequest {
189+ string repo = 1;
190+ int64 asset_id = 2;
191+}
192+
193+// DeleteReleaseAssetResponse is the response of ReleaseService.DeleteReleaseAsset.
194+message DeleteReleaseAssetResponse {
195+}
196+
197+// DownloadReleaseAssetResponse is the response of ReleaseService.DownloadReleaseAsset.
198+message DownloadReleaseAssetResponse {
199+ ReleaseAsset asset = 1;
200+ bytes chunk = 2;
201+}
202+
203+// ListReleaseAssetsRequest is the request of ReleaseService.ListReleaseAssets.
204+message ListReleaseAssetsRequest {
205+ string repo = 1;
206+ int64 release_id = 2;
207+}
208+
209+// ListReleaseAssetsResponse is the response of ReleaseService.ListReleaseAssets.
210+message ListReleaseAssetsResponse {
211+ repeated ReleaseAsset assets = 1;
212+}
213+
214+// GetReleaseUsageRequest is the request of ReleaseService.GetReleaseUsage.
215+message GetReleaseUsageRequest {
216+ string repo = 1;
217+}
218+
219+// GetReleaseUsageResponse is the response of ReleaseService.GetReleaseUsage.
220+message GetReleaseUsageResponse {
221+ int64 used_bytes = 1;
222+ int64 quota_bytes = 2;
223+ int64 asset_max_bytes = 3;
224+ int32 assets_max = 4;
225+}
226+
227+// GetReleaseRequest selects exactly one release.
228+message GetReleaseRequest {
229+ string repo = 1;
230+ oneof selector {
231+ int64 id = 2;
232+ string tag = 3;
233+ bool latest = 4;
234+ }
235+}
236+
237+// ReleaseAssetTag selects a named asset of a tag.
238+message ReleaseAssetTag {
239+ string tag = 1;
240+ string name = 2;
241+}
242+
243+// ReleaseAssetLatest selects a named asset of the latest release.
244+message ReleaseAssetLatest {
245+ // Latest must be true.
246+ bool latest = 1;
247+ string name = 2;
248+}
249+
250+// DownloadReleaseAssetRequest selects an asset and a resume offset.
251+message DownloadReleaseAssetRequest {
252+ string repo = 1;
253+ oneof selector {
254+ int64 asset_id = 2;
255+ ReleaseAssetTag tag = 3;
256+ ReleaseAssetLatest latest = 4;
257+ }
258+ int64 offset = 5;
259+}
260+
261+// GetReleaseResponse contains the selected release.
262+message GetReleaseResponse {
263+ Release release = 1;
264+}
M proto/ermite/server.proto +2 -1
@@ -64,7 +64,8 @@ message GetVersionResponse {
6464 // RepoSettings.allow_auto_merge, MergeRequest.auto_merge and auto_merged,
6565 // the EnableAutoMerge and CancelAutoMerge RPCs, the
6666 // MergeRequestAutoMergeEnabled and MergeRequestAutoMergeCancelled events
67- // and MergeRequestMerged.auto.
67+ // and MergeRequestMerged.auto. "releases" covers ReleaseService and the
68+ // release_published, release_updated, release_deleted and release_unpublished events.
6869 repeated string features = 2;
6970 }
7071
@@ -64,7 +64,8 @@ message GetVersionResponse {
64 // RepoSettings.allow_auto_merge, MergeRequest.auto_merge and auto_merged,64 // RepoSettings.allow_auto_merge, MergeRequest.auto_merge and auto_merged,
65 // the EnableAutoMerge and CancelAutoMerge RPCs, the65 // the EnableAutoMerge and CancelAutoMerge RPCs, the
66 // MergeRequestAutoMergeEnabled and MergeRequestAutoMergeCancelled events66 // MergeRequestAutoMergeEnabled and MergeRequestAutoMergeCancelled events
67- // and MergeRequestMerged.auto.67+ // and MergeRequestMerged.auto. "releases" covers ReleaseService and the
68+ // release_published, release_updated, release_deleted and release_unpublished events.
68 repeated string features = 2;69 repeated string features = 2;
69 }70 }
70 71
M proto/ermite/token.proto +3 -0
@@ -49,6 +49,9 @@ enum Scope {
4949 // SCOPE_MR_FORCE_MERGE allows merging a merge request whatever its checks
5050 // say (MergeMergeRequestRequest.ignore_checks). Wire name: mr:force-merge.
5151 SCOPE_MR_FORCE_MERGE = 12;
52+ // SCOPE_RELEASE_WRITE allows creating, editing and deleting releases and their assets.
53+ // Wire name: release:write.
54+ SCOPE_RELEASE_WRITE = 13;
5255 }
5356
5457 // Token describes a stored token. It never contains the secret nor its hash.
@@ -49,6 +49,9 @@ enum Scope {
49 // SCOPE_MR_FORCE_MERGE allows merging a merge request whatever its checks49 // SCOPE_MR_FORCE_MERGE allows merging a merge request whatever its checks
50 // say (MergeMergeRequestRequest.ignore_checks). Wire name: mr:force-merge.50 // say (MergeMergeRequestRequest.ignore_checks). Wire name: mr:force-merge.
51 SCOPE_MR_FORCE_MERGE = 12;51 SCOPE_MR_FORCE_MERGE = 12;
52+ // SCOPE_RELEASE_WRITE allows creating, editing and deleting releases and their assets.
53+ // Wire name: release:write.
54+ SCOPE_RELEASE_WRITE = 13;
52 }55 }
53 56
54 // Token describes a stored token. It never contains the secret nor its hash.57 // Token describes a stored token. It never contains the secret nor its hash.

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