main 81ede3b

artifact.proto RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3syntax = "proto3";
4
5package ermite;
6
7import "google/protobuf/timestamp.proto";
8
9// Artifact is a file that a job of a run uploaded with actions/upload-artifact
10// (artifacts v4): in most cases a zip archive, or a single file uploaded as is
11// ("archive: false"). It belongs to a run, not to one attempt: every job of the
12// run, in any attempt, sees the artifacts of the run. Only finalized artifacts
13// are described; an upload in progress is invisible.
14message Artifact {
15  // Id identifies the artifact on the server: unique across repositories and
16  // runs, never reused, and higher for a later artifact. It is GitHub's
17  // "artifact id" (databaseId), the id of the REST API.
18  int64 id = 1;
19  // Repo is the name of the repository of the run.
20  string repo = 2;
21  // RunId is the run the artifact belongs to.
22  int64 run_id = 3;
23  // Attempt is the attempt of the run whose job uploaded it.
24  int32 attempt = 4;
25  // JobId is the Job.job_id of the job that uploaded it.
26  string job_id = 5;
27  // Name is the name of the artifact, unique among the artifacts of the run:
28  // 1 to 256 characters of valid UTF-8, without control characters, '"', ':',
29  // '<', '>', '|', '*', '?', '\' or '/' (the characters @actions/artifact
30  // refuses).
31  string name = 6;
32  // Size is the number of bytes of the content.
33  int64 size = 7;
34  // Digest is the SHA-256 of the content, "sha256:" and 64 lowercase
35  // hexadecimal characters, as GitHub gives it.
36  string digest = 8;
37  // MediaType is the media type of the content: "application/zip" for an
38  // archive, the type the uploader gave for a file uploaded as is.
39  string media_type = 9;
40  // CreatedAt is when the upload began.
41  google.protobuf.Timestamp created_at = 10;
42  // ExpiresAt is when the server deletes the artifact (see
43  // CreateJobArtifactRequest.expires_at).
44  google.protobuf.Timestamp expires_at = 11;
45}
46
47// JobArtifactService is the artifacts side of a CI runner: what ermite-runner
48// calls when the job's code uploads or downloads artifacts through the
49// results service the runner serves in the sandbox (Twirp
50// github.actions.results.api.v1.ArtifactService and Azure block blobs). The
51// runner translates that protocol; the server stores the content, enforces the
52// limits and decides what the job may see. It is not for people: owners use
53// ArtifactService.
54//
55// Every RPC needs the job token of the job ("Authorization: Bearer
56// ermitejob_..."), and job_id must be the job of the token, with the errors of
57// RunnerService (unauthenticated, permission_denied, and failed_precondition
58// with a JobEnded detail once the job has ended). The run is always the one of
59// the token: no request names a run, so a job can only reach the artifacts of
60// its own run, and the run and job ids that the job's code puts in its Twirp
61// requests are never passed on.
62//
63// An upload goes: CreateJobArtifact (a pending artifact and its id), any number
64// of UploadJobArtifactBlock (in any order and concurrently), one
65// CommitJobArtifact (the order of the blocks: the content), then
66// FinalizeJobArtifact (the artifact becomes visible). Every step is safe to
67// retry after a network error. A pending artifact that is not finalized when
68// its job ends is deleted with its blocks.
69//
70// Limits are settings of the server: the size of one artifact, the space of all
71// the artifacts of a repository (pending uploads included) and the longest
72// retention. A call that would go over a size limit fails with
73// resource_exhausted and a message that names the limit; nothing of the call
74// is kept.
75//
76// Servers that implement this service and ArtifactService list "ci.artifacts"
77// in GetVersionResponse.features; others answer unimplemented. A runner without
78// that feature serves no results service, and artifact actions fail as on a
79// GitHub Enterprise Server without artifacts v4.
80service JobArtifactService {
81  // CreateJobArtifact begins the upload of an artifact of the job's run and
82  // returns its id. Fails with invalid_argument if the name or expires_at break
83  // a rule, already_exists if an artifact of that name was finalized by a job
84  // of the same attempt of the run (the uploader deletes it first to replace
85  // it: DeleteJobArtifact), and resource_exhausted if the artifacts of the
86  // repository already use all their space. A pending artifact of the same
87  // job and name is discarded: a new create is a new upload.
88  rpc CreateJobArtifact(CreateJobArtifactRequest) returns (CreateJobArtifactResponse);
89  // UploadJobArtifactBlock stores one block of a pending artifact. It is a
90  // client stream: the first message names the job, the artifact and the
91  // block, every message may carry data, and the block is the data of all the
92  // messages in order. The block is stored when the client closes the stream
93  // and the server answers; a stream that breaks or fails stores nothing.
94  // Storing a block id again replaces the block, so a retry is safe. Blocks
95  // that the commit does not name are dropped.
96  //
97  // Fails with not_found if the artifact is not a pending artifact of this job,
98  // failed_precondition if it is already committed, invalid_argument if a rule
99  // of the messages is broken, and resource_exhausted if the blocks of the
100  // artifact would exceed the size of one artifact or the space of the
101  // repository.
102  rpc UploadJobArtifactBlock(stream UploadJobArtifactBlockRequest) returns (UploadJobArtifactBlockResponse);
103  // CommitJobArtifact sets the content of a pending artifact: the blocks named,
104  // in that order (Azure's Put Block List). An empty list is an empty content
105  // (a 0-byte upload). The server then knows the size and the digest, and
106  // drops the blocks. A repeat of the same call after a success returns the
107  // same result; a different list on a committed artifact is
108  // failed_precondition. Fails with not_found if the artifact is not a pending
109  // artifact of this job, and invalid_argument if a block id was never
110  // uploaded or the list has more than 50,000 ids.
111  rpc CommitJobArtifact(CommitJobArtifactRequest) returns (CommitJobArtifactResponse);
112  // FinalizeJobArtifact makes the latest committed artifact of the job with
113  // that name visible to the run, and returns it. An artifact of the same name
114  // from an earlier attempt of the run is deleted at the same moment (a re-run
115  // replaces what the first attempt uploaded). A repeat after a success
116  // returns the same artifact. Fails with not_found if the job has no pending
117  // artifact of that name, failed_precondition if it is not committed,
118  // invalid_argument if digest is set and differs from the content, and
119  // already_exists if a job of the same attempt finalized an artifact of that
120  // name in the meantime.
121  rpc FinalizeJobArtifact(FinalizeJobArtifactRequest) returns (FinalizeJobArtifactResponse);
122  // ListJobArtifacts lists the finalized, unexpired artifacts of the job's run,
123  // all attempts, by id. An empty list is not an error.
124  rpc ListJobArtifacts(ListJobArtifactsRequest) returns (ListJobArtifactsResponse);
125  // DownloadJobArtifact streams the content of an artifact of the job's run.
126  // The first message carries the artifact, then the chunks follow in order
127  // from offset. A client that loses the stream calls again with the offset
128  // after the last byte it received. Fails with not_found if the artifact is
129  // not a finalized artifact of the run (or has expired), and out_of_range if
130  // offset is beyond its size.
131  rpc DownloadJobArtifact(DownloadJobArtifactRequest) returns (stream DownloadJobArtifactResponse);
132  // DeleteJobArtifact deletes the finalized artifact of that name of the job's
133  // run (upload-artifact's "overwrite", and its merge action's
134  // "delete-merged"). Fails with not_found if there is none.
135  rpc DeleteJobArtifact(DeleteJobArtifactRequest) returns (DeleteJobArtifactResponse);
136}
137
138// CreateJobArtifactRequest begins an upload.
139message CreateJobArtifactRequest {
140  // JobId is the Job.job_id. It must be the job of the token.
141  string job_id = 1;
142  // Name is the name of the artifact, see Artifact.name.
143  string name = 2;
144  // MediaType is the media type of a file uploaded as is (the toolkit's
145  // mime_type), at most 255 characters. Empty means "application/zip".
146  string media_type = 3;
147  // ExpiresAt is when the artifact should be deleted (the toolkit's
148  // expires_at, from "retention-days"). Unset means the default retention of
149  // the server. The server keeps it between one day from now and its longest
150  // retention, and returns the time it kept.
151  google.protobuf.Timestamp expires_at = 4;
152}
153
154// CreateJobArtifactResponse identifies the pending artifact.
155message CreateJobArtifactResponse {
156  // ArtifactId is the id the artifact will have once finalized (Artifact.id).
157  int64 artifact_id = 1;
158  // ExpiresAt is the expiry the server kept.
159  google.protobuf.Timestamp expires_at = 2;
160}
161
162// UploadJobArtifactBlockRequest is one message of the stream of a block.
163message UploadJobArtifactBlockRequest {
164  // JobId is the Job.job_id, in the first message only. It must be the job of
165  // the token.
166  string job_id = 1;
167  // ArtifactId is the CreateJobArtifactResponse.artifact_id, in the first
168  // message only.
169  int64 artifact_id = 2;
170  // BlockId identifies the block within the artifact: 1 to 64 bytes (Azure's
171  // block id, decoded from base64), in the first message only.
172  bytes block_id = 3;
173  // Data is the next part of the block, at most 1 MiB (1,048,576 bytes). Any
174  // message may have none.
175  bytes data = 4;
176}
177
178// UploadJobArtifactBlockResponse confirms a stored block.
179message UploadJobArtifactBlockResponse {
180  // Size is the number of bytes of the block as stored.
181  int64 size = 1;
182}
183
184// CommitJobArtifactRequest sets the content of a pending artifact.
185message CommitJobArtifactRequest {
186  // JobId is the Job.job_id. It must be the job of the token.
187  string job_id = 1;
188  // ArtifactId is the CreateJobArtifactResponse.artifact_id.
189  int64 artifact_id = 2;
190  // BlockIds are the blocks of the content, in order, each one as given to
191  // UploadJobArtifactBlock. At most 50,000; the same id may appear more than
192  // once.
193  repeated bytes block_ids = 3;
194}
195
196// CommitJobArtifactResponse describes the committed content.
197message CommitJobArtifactResponse {
198  // Size is the number of bytes of the content.
199  int64 size = 1;
200  // Digest is the SHA-256 of the content, see Artifact.digest.
201  string digest = 2;
202}
203
204// FinalizeJobArtifactRequest ends an upload.
205message FinalizeJobArtifactRequest {
206  // JobId is the Job.job_id. It must be the job of the token.
207  string job_id = 1;
208  // Name is the name given to CreateJobArtifact. The toolkit finalizes by name.
209  string name = 2;
210  // Size is the size the uploader counted. Informational: the server keeps the
211  // size of the content it stored, and only logs a difference.
212  int64 size = 3;
213  // Digest is the digest the uploader computed ("sha256:<hex>"), or empty.
214  // When set it must be the digest of the content.
215  string digest = 4;
216}
217
218// FinalizeJobArtifactResponse describes the artifact now visible.
219message FinalizeJobArtifactResponse {
220  // Artifact is the finalized artifact.
221  Artifact artifact = 1;
222}
223
224// ListJobArtifactsRequest selects artifacts of the job's run.
225message ListJobArtifactsRequest {
226  // JobId is the Job.job_id. It must be the job of the token.
227  string job_id = 1;
228  // Name keeps only the artifact of that name. Empty means every name.
229  string name = 2;
230  // ArtifactId keeps only the artifact of that id. 0 means every id.
231  int64 artifact_id = 3;
232}
233
234// ListJobArtifactsResponse lists artifacts of the job's run.
235message ListJobArtifactsResponse {
236  // Artifacts are the artifacts, by id.
237  repeated Artifact artifacts = 1;
238}
239
240// DownloadJobArtifactRequest selects the content of an artifact.
241message DownloadJobArtifactRequest {
242  // JobId is the Job.job_id. It must be the job of the token.
243  string job_id = 1;
244  // ArtifactId is the Artifact.id of an artifact of the job's run.
245  int64 artifact_id = 2;
246  // Offset is the first byte to send, from 0 to the size of the artifact.
247  int64 offset = 3;
248}
249
250// DownloadJobArtifactResponse is one message of the content stream.
251message DownloadJobArtifactResponse {
252  // Artifact describes the artifact. Set in the first message of the stream
253  // only.
254  Artifact artifact = 1;
255  // Chunk is the next part of the content, at most 1 MiB, in order. The first
256  // message may have an empty chunk. The chunks of a stream add up to the size
257  // of the artifact minus the offset.
258  bytes chunk = 2;
259}
260
261// DeleteJobArtifactRequest names the artifact to delete.
262message DeleteJobArtifactRequest {
263  // JobId is the Job.job_id. It must be the job of the token.
264  string job_id = 1;
265  // Name is the name of a finalized artifact of the job's run.
266  string name = 2;
267}
268
269// DeleteJobArtifactResponse identifies the deleted artifact.
270message DeleteJobArtifactResponse {
271  // ArtifactId is the Artifact.id of the artifact that was deleted.
272  int64 artifact_id = 1;
273}
274
275// ArtifactService lets people list, download and delete the artifacts of the
276// runs of a repository (run page, `ermite run download`). Listing and
277// downloading need a signed-in caller with read access to the repository (the
278// repo:read scope for a token), even for a public repository: like GitHub,
279// artifacts are not served anonymously. Deleting needs the ci:write scope. A
280// repository or a run that does not exist or that the caller cannot read gives
281// not_found.
282//
283// Artifacts are deleted when they expire (Artifact.expires_at), with their run
284// (run retention) and with their repository.
285//
286// Only servers with the feature "ci.artifacts" implement it (unimplemented
287// otherwise).
288service ArtifactService {
289  // ListArtifacts lists the artifacts of a run, by id, with the space that the
290  // artifacts of the repository use.
291  rpc ListArtifacts(ListArtifactsRequest) returns (ListArtifactsResponse);
292  // DownloadArtifact streams the content of an artifact, as
293  // JobArtifactService.DownloadJobArtifact does. Fails with not_found if the
294  // artifact is not an artifact of the repository, and out_of_range if offset
295  // is beyond its size.
296  rpc DownloadArtifact(DownloadArtifactRequest) returns (stream DownloadArtifactResponse);
297  // DeleteArtifact deletes an artifact. Fails with not_found if it is not an
298  // artifact of the repository.
299  rpc DeleteArtifact(DeleteArtifactRequest) returns (DeleteArtifactResponse);
300}
301
302// ListArtifactsRequest selects the artifacts of a run.
303message ListArtifactsRequest {
304  // Repo is the repository name.
305  string repo = 1;
306  // RunId is the run.
307  int64 run_id = 2;
308}
309
310// ListArtifactsResponse lists the artifacts of a run.
311message ListArtifactsResponse {
312  // Artifacts are the artifacts of the run, all attempts, by id.
313  repeated Artifact artifacts = 1;
314  // UsedBytes is the space that all the artifacts of the repository use,
315  // uploads in progress included.
316  int64 used_bytes = 2;
317  // QuotaBytes is the space the artifacts of the repository may use.
318  int64 quota_bytes = 3;
319}
320
321// DownloadArtifactRequest selects the content of an artifact.
322message DownloadArtifactRequest {
323  // Repo is the repository name.
324  string repo = 1;
325  // ArtifactId is the Artifact.id.
326  int64 artifact_id = 2;
327  // Offset is the first byte to send, from 0 to the size of the artifact.
328  int64 offset = 3;
329}
330
331// DownloadArtifactResponse is one message of the content stream.
332message DownloadArtifactResponse {
333  // Artifact describes the artifact. Set in the first message of the stream
334  // only.
335  Artifact artifact = 1;
336  // Chunk is the next part of the content, at most 1 MiB, in order. The first
337  // message may have an empty chunk.
338  bytes chunk = 2;
339}
340
341// DeleteArtifactRequest names the artifact to delete.
342message DeleteArtifactRequest {
343  // Repo is the repository name.
344  string repo = 1;
345  // ArtifactId is the Artifact.id.
346  int64 artifact_id = 2;
347}
348
349// DeleteArtifactResponse is empty.
350message DeleteArtifactResponse {}

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