Skip to content
1,394 linesCodeBlameRaw
1//! The actions service: GitHub Actions workflows, run on g1t as they are.
2//!
3//! A repository's `.g1t/workflows/*.yml`, in GitHub's format, are read
4//! from the commit an
5//! event is about (the default branch for issues, schedules and manual
6//! runs). Each workflow an event starts becomes a run; each job of the run
7//! (one per matrix combination) runs in a sandbox once the jobs it needs
8//! have finished. Jobs report their steps and logs back as they go, and a
9//! run on a pull request's head is a status on that pull request.
10//!
11//! Secrets and variables belong to a repository or to its workspace; a
12//! repository's override its workspace's of the same name. Secret values
13//! are sealed at rest and never returned.
14//!
15//! Mirrors `packages/contracts/src/actions.ts`.
16
17use serde::{Deserialize, Serialize};
18use serde_json::Value;
19
20use crate::repos::RepoPath;
21use crate::{User, Viewer};
22
23/// A note on something in a workflow that runs differently on g1t.
24#[derive(Clone, Debug, Serialize, Deserialize)]
25#[serde(rename_all = "camelCase")]
26pub struct WorkflowNote {
27 /// `info`, `warning` or `unsupported`.
28 pub severity: String,
29 pub job: Option<String>,
30 pub message: String,
31}
32
33#[derive(Clone, Debug, Serialize, Deserialize)]
34#[serde(rename_all = "camelCase")]
35pub struct Workflow {
36 pub id: String,
37 /// `.g1t/workflows/ci.yml`.
38 pub path: String,
39 pub name: String,
40 /// The events that start it, such as `push` and `pull_request`.
41 pub events: Vec<String>,
42 /// `active`, or `disabled` when a member turned it off.
43 pub state: String,
44 /// Why the file cannot be used, if it cannot.
45 pub error: Option<String>,
46 pub notes: Vec<WorkflowNote>,
47 /// `on.workflow_dispatch.inputs` as written, when it can be run by hand.
48 pub dispatch: Option<Value>,
49 pub last_run: Option<WorkflowRun>,
50}
51
52#[derive(Clone, Debug, Serialize, Deserialize)]
53#[serde(rename_all = "camelCase")]
54pub struct WorkflowRun {
55 pub id: String,
56 pub workflow_id: String,
57 pub path: String,
58 /// The workflow's name.
59 pub name: String,
60 /// `run-name`, or what started it: a commit's subject, a pull request's title.
61 pub title: String,
62 /// Counts the workflow's runs: 1, 2, 3…
63 pub number: u64,
64 pub attempt: u64,
65 /// The GitHub event: `push`, `pull_request`, `schedule`…
66 pub event: String,
67 #[serde(rename = "ref")]
68 pub git_ref: String,
69 pub sha: String,
70 /// The pull request it ran for, if any.
71 pub pull: Option<u32>,
72 /// `queued`, `in_progress` or `completed`.
73 pub status: String,
74 /// When completed: `success`, `failure`, `cancelled` or `skipped`.
75 pub conclusion: Option<String>,
76 /// Why it could not start, such as a workflow file that does not read.
77 pub error: Option<String>,
78 /// Username of whoever caused it.
79 pub actor: Option<String>,
80 pub created_at: String,
81 pub started_at: Option<String>,
82 pub finished_at: Option<String>,
83}
84
85#[derive(Clone, Debug, Default, Serialize, Deserialize)]
86#[serde(rename_all = "camelCase")]
87pub struct StepState {
88 /// From 1.
89 pub number: u32,
90 pub name: String,
91 /// `queued`, `in_progress` or `completed`.
92 pub status: String,
93 /// `success`, `failure`, `cancelled` or `skipped`.
94 pub conclusion: Option<String>,
95 pub started_at: Option<String>,
96 pub finished_at: Option<String>,
97}
98
99/// A message a step left with `::error::`, `::warning::` or `::notice::`.
100#[derive(Clone, Debug, Default, Serialize, Deserialize)]
101#[serde(rename_all = "camelCase")]
102pub struct Annotation {
103 /// `error`, `warning` or `notice`.
104 pub level: String,
105 pub message: String,
106 pub title: Option<String>,
107 pub file: Option<String>,
108 pub line: Option<u32>,
109}
110
111#[derive(Clone, Debug, Serialize, Deserialize)]
112#[serde(rename_all = "camelCase")]
113pub struct Job {
114 pub id: String,
115 pub run_id: String,
116 /// Its key under `jobs:`.
117 pub key: String,
118 /// With its matrix combination: `test (ubuntu-latest, 20)`.
119 pub name: String,
120 pub needs: Vec<String>,
121 /// `queued`, `waiting` (for the jobs it needs), `in_progress` or `completed`.
122 pub status: String,
123 pub conclusion: Option<String>,
124 pub steps: Vec<StepState>,
125 pub annotations: Vec<Annotation>,
126 /// Why it did not run, what stopped it, or what it waits for.
127 pub reason: Option<String>,
128 pub started_at: Option<String>,
129 pub finished_at: Option<String>,
130 /// The environment it names, once its needs are done (an expression
131 /// read by then). A job held by the environment's protection rules is
132 /// `pending` until they let it through.
133 #[serde(default)]
134 pub environment: Option<String>,
135 /// Its `runs-on` names self-hosted runners (see `runners`).
136 #[serde(default)]
137 pub self_hosted: bool,
138 /// The self-hosted runner that took it, by name.
139 #[serde(default)]
140 pub runner: Option<String>,
141 /// It was cancelled and is running its `if: always()` and `cancelled()`
142 /// steps and its post steps before it ends.
143 #[serde(default)]
144 pub cancelling: bool,
145 /// Where its deployment is (`environment.url`), for a job that deploys
146 /// and says; the current attempt's only.
147 #[serde(default, skip_serializing_if = "Option::is_none")]
148 pub environment_url: Option<String>,
149 /// For a job that calls a reusable workflow: that workflow's file. Its
150 /// jobs' keys start with this job's key and a `/`.
151 #[serde(default, skip_serializing_if = "Option::is_none")]
152 pub uses: Option<String>,
153}
154
155#[derive(Clone, Debug, Serialize, Deserialize)]
156#[serde(rename_all = "camelCase")]
157pub struct RunDetail {
158 pub run: WorkflowRun,
159 pub jobs: Vec<Job>,
160 /// The workflow's notes, as of the run's commit.
161 pub notes: Vec<WorkflowNote>,
162 /// For a run of a pull request from outside: whether it waits for, or
163 /// had, someone's approval (`status` is `action_required` while it waits).
164 #[serde(default)]
165 pub approval: Option<RunApproval>,
166 /// The environments whose protection rules hold its jobs, this attempt.
167 #[serde(default)]
168 pub pending_deployments: Vec<PendingDeployment>,
169 /// Every attempt of the run, oldest first, the one shown included.
170 /// `run.attempt` says which one `jobs` belong to.
171 #[serde(default)]
172 pub attempts: Vec<RunAttempt>,
173}
174
175/// One attempt of a run: the first, or a re-run.
176#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
177#[serde(rename_all = "camelCase")]
178pub struct RunAttempt {
179 /// From 1.
180 pub attempt: u64,
181 /// `queued`, `in_progress` or `completed`; earlier attempts are completed.
182 pub status: String,
183 pub conclusion: Option<String>,
184 /// Who started it: whoever caused the run for the first, whoever re-ran
185 /// it for the rest.
186 pub actor: Option<String>,
187 /// It ran with debug logging (`RUNNER_DEBUG=1`).
188 pub debug: bool,
189 pub started_at: Option<String>,
190 pub finished_at: Option<String>,
191}
192
193/// One job's summary: what its steps wrote to `$GITHUB_STEP_SUMMARY`, in
194/// Markdown, masked.
195#[derive(Clone, Debug, Serialize, Deserialize)]
196#[serde(rename_all = "camelCase")]
197pub struct JobSummary {
198 /// The job's id, as `RunDetail.jobs` gives it for the attempt.
199 pub job_id: String,
200 pub name: String,
201 pub steps: Vec<StepSummary>,
202}
203
204#[derive(Clone, Debug, Serialize, Deserialize)]
205#[serde(rename_all = "camelCase")]
206pub struct StepSummary {
207 /// The step, from 1 (post steps follow the job's own).
208 pub step: u32,
209 pub markdown: String,
210}
211
212/// A job's whole log, for downloading: its steps, to split the text by.
213#[derive(Clone, Debug, Serialize, Deserialize)]
214#[serde(rename_all = "camelCase")]
215pub struct JobLogText {
216 pub job_id: String,
217 pub name: String,
218 pub steps: Vec<StepState>,
219 pub chunks: Vec<LogChunk>,
220 /// Whether the job has finished.
221 pub done: bool,
222 /// Its log was left out: the run's logs reached `MAX_RUN_LOG_BYTES`.
223 #[serde(default)]
224 pub omitted: bool,
225}
226
227/// A run that needed approval before it started.
228#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
229#[serde(rename_all = "camelCase")]
230pub struct RunApproval {
231 /// `required` while it waits, then `approved`.
232 pub state: String,
233 /// Why it waits, in words.
234 pub reason: String,
235 /// Who approved it.
236 pub approved_by: Option<String>,
237}
238
239/// One person or team who may approve a job's deployment to an
240/// environment.
241#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
242pub struct EnvironmentReviewer {
243 /// `user` or `team`.
244 #[serde(rename = "type")]
245 pub kind: String,
246 /// A username, or a team's slug in the repository's workspace.
247 pub name: String,
248}
249
250/// A branch or tag pattern an environment lets deploy.
251#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
252pub struct BranchPattern {
253 /// fnmatch-style, as branch filters are: `main`, `release/*`, `v*`.
254 pub name: String,
255 /// `branch` or `tag`.
256 #[serde(rename = "type", default = "branch_kind")]
257 pub kind: String,
258}
259
260fn branch_kind() -> String {
261 "branch".to_owned()
262}
263
264/// The most reviewers an environment may have, as on GitHub.
265pub const MAX_ENVIRONMENT_REVIEWERS: usize = 6;
266/// The longest wait timer, in minutes: 30 days.
267pub const MAX_WAIT_MINUTES: u32 = 43_200;
268
269/// An environment and its protection rules. Jobs that name it with
270/// `environment:` wait until the rules let them through; only then does the
271/// job get the environment's secrets.
272#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
273#[serde(rename_all = "camelCase")]
274pub struct Environment {
275 /// Lowercase.
276 pub name: String,
277 /// Who may approve its jobs; none means no review is needed.
278 pub reviewers: Vec<EnvironmentReviewer>,
279 /// Whoever started a run may not approve its jobs, even as a reviewer.
280 pub prevent_self_review: bool,
281 /// Minutes each job waits before it may start.
282 pub wait_minutes: u32,
283 /// Which refs may deploy: `all`, `protected` (branches the rules
284 /// protect, the default branch included) or `selected` (`branch_patterns`).
285 pub branch_policy: String,
286 pub branch_patterns: Vec<BranchPattern>,
287 /// Admins may approve without being reviewers, which also skips the wait.
288 pub admins_bypass: bool,
289 /// Whether it has rules saved; false for one only named by a workflow,
290 /// a secret or a deployment.
291 pub protected: bool,
292 pub updated_at: Option<String>,
293 pub updated_by: Option<String>,
294}
295
296/// An environment holding a run's jobs, and where its rules stand.
297#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
298#[serde(rename_all = "camelCase")]
299pub struct PendingDeployment {
300 pub environment: String,
301 /// `waiting`, `approved` or `rejected`.
302 pub state: String,
303 /// Whether a reviewer must approve it before its jobs start.
304 pub needs_review: bool,
305 /// When its wait timer lets its jobs start, if it has one.
306 pub wait_until: Option<String>,
307 pub reviewers: Vec<EnvironmentReviewer>,
308 /// The jobs it holds, by name.
309 pub jobs: Vec<String>,
310 /// Whether the viewer may approve or reject it now.
311 #[serde(default)]
312 pub can_review: bool,
313 pub reviewed_by: Option<String>,
314 pub comment: Option<String>,
315 pub reviewed_at: Option<String>,
316}
317
318/// A repository's choices for its workflows.
319#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
320#[serde(rename_all = "camelCase")]
321pub struct ActionsSettings {
322 /// What a workflow without `permissions:` gets: `read` (contents and
323 /// packages read) or `write` (every permission). Unchosen, a repository
324 /// made before restricted tokens keeps `write`; a newer one takes its
325 /// workspace's default. Never more than the workspace's maximum.
326 pub default_permissions: String,
327 /// Whether the repository chose it, rather than taking it as above.
328 #[serde(default)]
329 pub default_chosen: bool,
330 /// The most the workspace lets a repository's default be.
331 #[serde(default = "write")]
332 pub max_permissions: String,
333 /// Which pull requests' runs wait for approval: `first_time_contributors`,
334 /// `outside_contributors` (the default) or `all_external_contributors`.
335 pub approval_policy: String,
336 /// Whether a job's token may open pull requests and approve them. Off
337 /// unless the repository turns it on, and only where the workspace
338 /// allows it.
339 #[serde(default)]
340 pub can_approve_pull_requests: bool,
341 /// Whether the workspace lets its repositories turn that on.
342 #[serde(default)]
343 pub workspace_allows_pull_requests: bool,
344 /// Who may use this repository's actions and reusable workflows from
345 /// their workflows, when it is private: `none` (only itself, the
346 /// default) or `organization` (private repositories of its workspace).
347 /// A public repository's are anyone's. See [`ACCESS_LEVELS`].
348 #[serde(default = "no_access")]
349 pub access_level: String,
350}
351
352fn no_access() -> String {
353 "none".to_owned()
354}
355
356/// The values of `access_level`. `user` is read as `organization`: a
357/// personal account's repositories are its own workspace's.
358pub const ACCESS_LEVELS: [&str; 2] = ["none", "organization"];
359
360/// `access_level` as given, as one of [`ACCESS_LEVELS`]; None when it is
361/// not one.
362pub fn access_level(given: &str) -> Option<&'static str> {
363 match given.trim().to_ascii_lowercase().as_str() {
364 "none" | "" => Some("none"),
365 "organization" | "user" | "workspace" => Some("organization"),
366 _ => None,
367 }
368}
369
370fn write() -> String {
371 "write".to_owned()
372}
373
374/// A workspace's policy for its repositories' tokens.
375#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
376#[serde(rename_all = "camelCase")]
377pub struct WorkspaceActionsSettings {
378 /// What a repository made from now on gets by default: `read` (the
379 /// default) or `write`.
380 pub default_permissions: String,
381 /// The most any repository's default may be: `write` (the default) or
382 /// `read`, which holds every repository to read-only.
383 pub max_permissions: String,
384 /// Whether its repositories may let jobs open and approve pull
385 /// requests. Off by default.
386 pub can_approve_pull_requests: bool,
387}
388
389/// `workspace_actions_settings`: members only. Returns
390/// `Outcome<WorkspaceActionsSettings>`.
391#[derive(Debug, Serialize, Deserialize)]
392pub struct WorkspaceActionsSettingsArgs {
393 pub viewer: Viewer,
394 pub workspace: String,
395}
396
397/// `set_workspace_actions_settings`: owners only. Fields left out stay as
398/// they are. Returns `Outcome<WorkspaceActionsSettings>`.
399#[derive(Debug, Serialize, Deserialize)]
400#[serde(rename_all = "camelCase")]
401pub struct SetWorkspaceActionsSettingsArgs {
402 pub actor: User,
403 pub workspace: String,
404 #[serde(default)]
405 pub default_permissions: Option<String>,
406 #[serde(default)]
407 pub max_permissions: Option<String>,
408 #[serde(default)]
409 pub can_approve_pull_requests: Option<bool>,
410}
411
412/// The approval policies, least strict first.
413pub const APPROVAL_POLICIES: [&str; 3] = ["first_time_contributors", "outside_contributors", "all_external_contributors"];
414
415/// `actions_settings`. Returns `Outcome<ActionsSettings>`; anyone who can
416/// read the repository may see them.
417#[derive(Debug, Serialize, Deserialize)]
418pub struct ActionsSettingsArgs {
419 pub viewer: Viewer,
420 pub repo: RepoPath,
421}
422
423/// `set_actions_settings`: Admins only. Fields left out stay as they are.
424/// Returns `Outcome<ActionsSettings>`.
425#[derive(Debug, Serialize, Deserialize)]
426#[serde(rename_all = "camelCase")]
427pub struct SetActionsSettingsArgs {
428 pub actor: User,
429 pub repo: RepoPath,
430 /// `read` or `write`; `inherit` goes back to the workspace's (or, for a
431 /// repository made before restricted tokens, `write`).
432 #[serde(default)]
433 pub default_permissions: Option<String>,
434 #[serde(default)]
435 pub approval_policy: Option<String>,
436 #[serde(default)]
437 pub can_approve_pull_requests: Option<bool>,
438 /// `none`, or `organization` (`user` reads the same). See
439 /// [`ActionsSettings::access_level`].
440 #[serde(default)]
441 pub access_level: Option<String>,
442}
443
444/// `environments`: every environment a repository's workflows, secrets,
445/// deployments or rules name, with its rules. `environment`: one, by
446/// `name`. Returns `Outcome<Vec<Environment>>` and `Outcome<Environment>`.
447#[derive(Debug, Serialize, Deserialize)]
448pub struct EnvironmentsArgs {
449 pub viewer: Viewer,
450 pub repo: RepoPath,
451 #[serde(default)]
452 pub name: Option<String>,
453}
454
455/// `set_environment`: create an environment's rules or change them. Fields
456/// left out stay as they are (none, for a new one). Admins only. Returns
457/// `Outcome<Environment>`.
458#[derive(Debug, Serialize, Deserialize)]
459#[serde(rename_all = "camelCase")]
460pub struct SetEnvironmentArgs {
461 pub actor: User,
462 pub repo: RepoPath,
463 pub name: String,
464 #[serde(default)]
465 pub reviewers: Option<Vec<EnvironmentReviewer>>,
466 #[serde(default)]
467 pub prevent_self_review: Option<bool>,
468 #[serde(default)]
469 pub wait_minutes: Option<u32>,
470 #[serde(default)]
471 pub branch_policy: Option<String>,
472 #[serde(default)]
473 pub branch_patterns: Option<Vec<BranchPattern>>,
474 #[serde(default)]
475 pub admins_bypass: Option<bool>,
476}
477
478/// `delete_environment`: its rules go; jobs naming it run without them.
479/// Its secrets' rows stay. Admins only. Returns `Outcome<bool>`.
480#[derive(Debug, Serialize, Deserialize)]
481pub struct DeleteEnvironmentArgs {
482 pub actor: User,
483 pub repo: RepoPath,
484 pub name: String,
485}
486
487/// `pending_deployments`: the environments holding a run's jobs. Returns
488/// `Outcome<Vec<PendingDeployment>>`.
489#[derive(Debug, Serialize, Deserialize)]
490pub struct PendingDeploymentsArgs {
491 pub viewer: Viewer,
492 pub repo: RepoPath,
493 pub id: String,
494}
495
496/// `review_deployments`: approve or reject a run's jobs for `environments`
497/// (every one waiting, if empty). Returns `Outcome<Vec<PendingDeployment>>`.
498#[derive(Debug, Serialize, Deserialize)]
499pub struct ReviewDeploymentsArgs {
500 pub actor: User,
501 pub repo: RepoPath,
502 pub id: String,
503 #[serde(default)]
504 pub environments: Vec<String>,
505 /// `approved` or `rejected`.
506 pub state: String,
507 #[serde(default)]
508 pub comment: Option<String>,
509}
510
511/// `repository_dispatch`: start the default branch's workflows that run
512/// `on: repository_dispatch` for `event_type`. Needs the Write role (a
513/// token's `code:write`). Returns `Outcome<u32>`: how many started.
514#[derive(Debug, Serialize, Deserialize)]
515#[serde(rename_all = "camelCase")]
516pub struct RepositoryDispatchArgs {
517 pub actor: User,
518 pub repo: RepoPath,
519 pub event_type: String,
520 #[serde(default)]
521 pub client_payload: Value,
522}
523
524#[derive(Clone, Debug, Serialize, Deserialize)]
525#[serde(rename_all = "camelCase")]
526pub struct LogChunk {
527 pub seq: u64,
528 /// The step it belongs to, from 1; 0 for the job's setup.
529 pub step: u32,
530 pub text: String,
531}
532
533#[derive(Clone, Debug, Serialize, Deserialize)]
534#[serde(rename_all = "camelCase")]
535pub struct JobLog {
536 pub chunks: Vec<LogChunk>,
537 /// Whether the job has finished, so no more will come.
538 pub done: bool,
539}
540
541/// Who may read a secret or variable: workflows (`secrets.*` and `vars.*`
542/// in GitHub Actions) and deployments (a deploy build's environment and the
543/// running app's bindings). Agents, checks and the merge queue read none.
544pub const CONSUMERS: [&str; 2] = ["workflows", "deployments"];
545
546/// One row of a repository's or workspace's secrets and variables, as
547/// Vercel lists environment variables: a key, its type, the environments
548/// it applies to and who reads it. A key may have one row per environment.
549/// Secrets' values are never returned.
550#[derive(Clone, Debug, Serialize, Deserialize)]
551#[serde(rename_all = "camelCase")]
552pub struct Setting {
553 #[serde(default)]
554 pub id: String,
555 pub name: String,
556 /// `secret`, or `variable` (shown as Config).
557 #[serde(default)]
558 pub kind: String,
559 /// A variable's value; secrets' are never returned.
560 pub value: Option<String>,
561 /// `project` (a repository's, which belong to its project) or
562 /// `workspace`.
563 pub scope: String,
564 pub updated_at: String,
565 /// `workflows` and/or `deployments`.
566 #[serde(default)]
567 pub available_to: Vec<String>,
568 /// The environments it applies to; empty is every environment.
569 #[serde(default)]
570 pub environments: Vec<String>,
571 /// A workspace's row: the projects it reaches, by slug; empty is every
572 /// project.
573 #[serde(default)]
574 pub projects: Vec<String>,
575 #[serde(default)]
576 pub note: Option<String>,
577 #[serde(default)]
578 pub updated_by: Option<String>,
579}
580
581// --- Methods ---------------------------------------------------------------
582
583/// `workflows`. Returns `Outcome<Vec<Workflow>>`.
584#[derive(Debug, Serialize, Deserialize)]
585pub struct WorkflowsArgs {
586 pub repo: RepoPath,
587 pub viewer: Viewer,
588}
589
590/// `runs`: newest first. Returns `Outcome<Vec<WorkflowRun>>`.
591#[derive(Debug, Serialize, Deserialize)]
592pub struct RunsArgs {
593 pub repo: RepoPath,
594 pub viewer: Viewer,
595 /// A workflow's id or file name.
596 #[serde(default)]
597 pub workflow: Option<String>,
598 #[serde(default)]
599 pub branch: Option<String>,
600 #[serde(default)]
601 pub event: Option<String>,
602 /// The pull request's number.
603 #[serde(default)]
604 pub pull: Option<u32>,
605 #[serde(default)]
606 pub sha: Option<String>,
607 #[serde(default)]
608 pub limit: Option<u32>,
609}
610
611/// `run`. Returns `Outcome<RunDetail>`.
612#[derive(Debug, Serialize, Deserialize)]
613pub struct RunArgs {
614 pub repo: RepoPath,
615 pub viewer: Viewer,
616 pub id: String,
617 /// An earlier attempt; the latest when absent.
618 #[serde(default)]
619 pub attempt: Option<u64>,
620}
621
622/// `summaries`: the job summaries of a run's attempt (the latest when
623/// `attempt` is absent), jobs in the run's order, those with none left
624/// out. Returns `Outcome<Vec<JobSummary>>`.
625#[derive(Debug, Serialize, Deserialize)]
626pub struct SummariesArgs {
627 pub repo: RepoPath,
628 pub viewer: Viewer,
629 pub id: String,
630 #[serde(default)]
631 pub attempt: Option<u64>,
632}
633
634/// `job_log_text`: one job's whole log, any attempt's (by the id the run
635/// gave the job). Returns `Outcome<JobLogText>`.
636#[derive(Debug, Serialize, Deserialize)]
637pub struct JobLogTextArgs {
638 pub repo: RepoPath,
639 pub viewer: Viewer,
640 pub job: String,
641}
642
643/// `run_logs`: every job's whole log for an attempt of a run (the latest
644/// when `attempt` is absent), until they add up to `MAX_RUN_LOG_BYTES`;
645/// jobs past it come `omitted`, with no chunks. Returns
646/// `Outcome<Vec<JobLogText>>`.
647#[derive(Debug, Serialize, Deserialize)]
648pub struct RunLogsArgs {
649 pub repo: RepoPath,
650 pub viewer: Viewer,
651 pub id: String,
652 #[serde(default)]
653 pub attempt: Option<u64>,
654}
655
656/// The most log `run_logs` returns at once, in bytes.
657pub const MAX_RUN_LOG_BYTES: usize = 24 * 1024 * 1024;
658
659/// `logs`: a job's log after `after`. Returns `Outcome<JobLog>`.
660#[derive(Debug, Serialize, Deserialize)]
661pub struct LogsArgs {
662 pub repo: RepoPath,
663 pub viewer: Viewer,
664 pub job: String,
665 #[serde(default)]
666 pub after: u64,
667}
668
669/// `dispatch`: run a workflow that has `workflow_dispatch`. Members only.
670/// Returns `Outcome<WorkflowRun>`.
671#[derive(Debug, Serialize, Deserialize)]
672pub struct DispatchArgs {
673 pub actor: User,
674 pub repo: RepoPath,
675 /// A workflow's id or file name.
676 pub workflow: String,
677 /// A branch or tag; the default branch when absent.
678 #[serde(default, rename = "ref")]
679 pub git_ref: Option<String>,
680 #[serde(default)]
681 pub inputs: serde_json::Map<String, Value>,
682}
683
684/// `cancel` and `rerun`: every job, with `failed_only` the ones that did
685/// not succeed, or with `job` that one job (by its id in the run's latest
686/// attempt); each with the jobs that need them. `debug` runs the new
687/// attempt with debug logging. Members only. Returns `Outcome<WorkflowRun>`.
688#[derive(Debug, Serialize, Deserialize)]
689pub struct RunActionArgs {
690 pub actor: User,
691 pub repo: RepoPath,
692 pub id: String,
693 #[serde(default)]
694 pub failed_only: bool,
695 #[serde(default)]
696 pub job: Option<String>,
697 #[serde(default)]
698 pub debug: bool,
699 /// `cancel`: stop running jobs outright, without their cleanup steps.
700 #[serde(default)]
701 pub force: bool,
702}
703
704/// `set_workflow_enabled`. Members only. Returns `Outcome<Workflow>`.
705#[derive(Debug, Serialize, Deserialize)]
706pub struct SetWorkflowEnabledArgs {
707 pub actor: User,
708 pub repo: RepoPath,
709 pub workflow: String,
710 pub enabled: bool,
711}
712
713/// Whose secrets or variables: a repository's, or with only `workspace`,
714/// a workspace's.
715#[derive(Clone, Debug, Serialize, Deserialize)]
716pub struct SettingsOwner {
717 #[serde(default)]
718 pub repo: Option<RepoPath>,
719 #[serde(default)]
720 pub workspace: Option<String>,
721}
722
723/// `settings`: the secrets (`kind: secret`) or variables (`kind: variable`)
724/// of a repository, with its workspace's, or of a workspace. Members only.
725/// Returns `Outcome<Vec<Setting>>`.
726#[derive(Debug, Serialize, Deserialize)]
727pub struct SettingsArgs {
728 pub actor: User,
729 #[serde(flatten)]
730 pub owner: SettingsOwner,
731 pub kind: String,
732}
733
734/// `set_setting`: add or replace one. A repository's need a member; a
735/// workspace's an owner. Returns `Outcome<Setting>`.
736#[derive(Debug, Serialize, Deserialize)]
737pub struct SetSettingArgs {
738 pub actor: User,
739 #[serde(flatten)]
740 pub owner: SettingsOwner,
741 /// `secret` or `variable`. Changing a variable's row to `secret` seals
742 /// it; a secret cannot become a variable.
743 pub kind: String,
744 pub name: String,
745 /// The row to change. Left out, the key's row for every environment, as
746 /// GitHub's API addresses a secret by name alone.
747 #[serde(default)]
748 pub id: Option<String>,
749 /// Needed for a new row; left out, an existing row keeps its value.
750 #[serde(default)]
751 pub value: Option<String>,
752 /// `workflows` and/or `deployments`; left out, unchanged (both, for a
753 /// new row).
754 // Named as callers send it: an `alias` is not honoured beside the
755 // flattened owner in the Worker's build.
756 #[serde(default, rename = "availableTo")]
757 pub available_to: Option<Vec<String>>,
758 /// The environments it applies to; empty is every one. Left out,
759 /// unchanged.
760 #[serde(default)]
761 pub environments: Option<Vec<String>>,
762 /// A workspace's row: project slugs; empty for every one.
763 #[serde(default)]
764 pub projects: Option<Vec<String>>,
765 #[serde(default)]
766 pub note: Option<String>,
767}
768
769/// `set_managed_workflow`: installs, or replaces, a managed workflow in a
770/// repository (`g1t_actions::managed`): one that runs as if it were in
771/// `.g1t/workflows/` without being committed there, under the path
772/// `managed/<name>.yml`. For g1t's own services; never reached from the API.
773/// Refused when `source` is not a workflow g1t can read. Returns `bool`.
774#[derive(Debug, Serialize, Deserialize)]
775#[serde(rename_all = "camelCase")]
776pub struct SetManagedWorkflowArgs {
777 pub repo_id: String,
778 /// Letters, digits and hyphens: the file name its path ends with.
779 pub name: String,
780 pub source: String,
781 /// What keeps it: `deployments`, or a workspace's id.
782 pub origin: String,
783}
784
785/// `set_managed_deploy`: installs, replaces or removes the deploy workflow
786/// a project's deployments run through (`g1t_actions::managed`), named
787/// `deploy-<project>`, from the project's settings. Nothing deploys (both
788/// `previews` and `production` off) removes it. For the deployments
789/// service only. Returns `bool`, whether one is installed now.
790#[derive(Debug, Serialize, Deserialize)]
791#[serde(rename_all = "camelCase")]
792pub struct SetManagedDeployArgs {
793 pub repo_id: String,
794 pub project: String,
795 pub production_branch: String,
796 #[serde(default)]
797 pub root_dir: String,
798 #[serde(default)]
799 pub build_command: String,
800 #[serde(default)]
801 pub output_dir: String,
802 pub previews: bool,
803 pub production: bool,
804}
805
806/// `remove_managed_workflow`: takes a managed workflow out of a repository.
807/// Its runs stay. Returns `bool`, whether there was one.
808#[derive(Debug, Serialize, Deserialize)]
809#[serde(rename_all = "camelCase")]
810pub struct RemoveManagedWorkflowArgs {
811 pub repo_id: String,
812 pub name: String,
813}
814
815/// `resolve_settings`: the secrets and variables one reader gets, for the
816/// services that hand them out (the deployments service). Returns
817/// `ResolvedSettings`.
818#[derive(Debug, Serialize, Deserialize)]
819#[serde(rename_all = "camelCase")]
820pub struct ResolveSettingsArgs {
821 pub repo_id: String,
822 pub repo: RepoPath,
823 /// The project being read for; its repository's primary project if left
824 /// out.
825 #[serde(default)]
826 pub project_id: Option<String>,
827 #[serde(default)]
828 pub project_slug: Option<String>,
829 /// `workflows` or `deployments`.
830 pub consumer: String,
831 /// The environment being read for, such as `production` or `preview`.
832 #[serde(default)]
833 pub environment: Option<String>,
834 /// Whether the run is trusted; an untrusted one gets no secrets.
835 pub trusted: bool,
836}
837
838#[derive(Debug, Default, Serialize, Deserialize)]
839pub struct ResolvedSettings {
840 pub secrets: serde_json::Map<String, serde_json::Value>,
841 pub variables: serde_json::Map<String, serde_json::Value>,
842}
843
844/// `delete_setting`. Returns `Outcome<bool>`.
845#[derive(Debug, Serialize, Deserialize)]
846pub struct DeleteSettingArgs {
847 pub actor: User,
848 #[serde(flatten)]
849 pub owner: SettingsOwner,
850 pub kind: String,
851 pub name: String,
852 /// One row; left out, every row of the key.
853 #[serde(default)]
854 pub id: Option<String>,
855}
856
857/// `job_spec` and `job_report`: the sandbox running a job, with the job's
858/// own token. `report` is one of:
859/// `{"kind": "step", "number", "status", "conclusion"}`,
860/// `{"kind": "log", "step", "text"}`,
861/// `{"kind": "annotation", "level", "message", "title", "file", "line"}`,
862/// `{"kind": "done", "conclusion", "outputs", "reason"}`.
863#[derive(Debug, Serialize, Deserialize)]
864pub struct JobCallArgs {
865 pub job: String,
866 pub token: String,
867 #[serde(default)]
868 pub report: Value,
869}
870
871/// What the runner needs to start a job's sandbox.
872#[derive(Debug, Serialize, Deserialize)]
873#[serde(rename_all = "camelCase")]
874pub struct StartJobArgs {
875 pub job: String,
876 pub token: String,
877 pub repo: RepoPath,
878 /// Minutes before the job is stopped.
879 pub timeout_minutes: u32,
880 /// The workflow file the job is in (`.g1t/workflows/deploy.yml`), for
881 /// the guardrails' workflow-only domains.
882 #[serde(default)]
883 pub workflow: Option<String>,
884 /// The environment the job names with `environment:`, when it names
885 /// one plainly (not with an expression).
886 #[serde(default)]
887 pub environment: Option<String>,
888 /// Whether its run is trusted: not a pull request from a fork. Only a
889 /// trusted run's jobs reach workflow-only domains.
890 #[serde(default)]
891 pub trusted: bool,
892 /// The machine its `runs-on` asked for, by label (`instance_for`):
893 /// `g1t-2core` or `g1t-4core`; absent, the standard one.
894 #[serde(default)]
895 pub instance: Option<String>,
896}
897
898/// A size of machine g1t runs workflow jobs on, asked for by a label in
899/// `runs-on`. Each is a Cloudflare Containers instance type; it costs what
900/// that instance costs g1t, plus the margin, like any sandbox time.
901#[derive(Clone, Copy, Debug, PartialEq)]
902pub struct InstanceType {
903 /// The `runs-on` label, or `standard` for the default.
904 pub label: &'static str,
905 /// The Containers instance type.
906 pub container: &'static str,
907 pub vcpu: f64,
908 pub memory_gib: f64,
909 pub disk_gb: f64,
910 /// What a second of it costs g1t as a multiple of the standard
911 /// machine's, with its vCPUs as busy (Cloudflare's list prices:
912 /// memory $0.0000025 a GiB-second, disk $0.00000007 a GB-second, vCPU
913 /// $0.00002 a second). Used to reserve before a job starts, and to
914 /// price a job that did not report its own CPU.
915 pub price_scale: f64,
916}
917
918/// The default: what `ubuntu-latest` and every other hosted label get.
919pub const STANDARD_INSTANCE: InstanceType =
920 InstanceType { label: "standard", container: "standard-1", vcpu: 0.5, memory_gib: 4.0, disk_gb: 8.0, price_scale: 1.0 };
921
922/// Every machine a workflow job can ask for, the default first.
923pub const INSTANCE_TYPES: [InstanceType; 3] = [
924 STANDARD_INSTANCE,
925 InstanceType { label: "g1t-2core", container: "standard-3", vcpu: 2.0, memory_gib: 8.0, disk_gb: 16.0, price_scale: 2.8 },
926 InstanceType { label: "g1t-4core", container: "standard-4", vcpu: 4.0, memory_gib: 12.0, disk_gb: 20.0, price_scale: 5.1 },
927];
928
929/// The machine a job's `runs-on` labels ask for: the largest named, or the
930/// standard one. Labels compare without regard to case.
931pub fn instance_for(labels: &[String]) -> InstanceType {
932 INSTANCE_TYPES
933 .iter()
934 .rev()
935 .find(|instance| instance.label != STANDARD_INSTANCE.label && labels.iter().any(|label| label.trim().eq_ignore_ascii_case(instance.label)))
936 .copied()
937 .unwrap_or(STANDARD_INSTANCE)
938}
939
940/// An instance type by its label, if it is one.
941pub fn instance_named(label: &str) -> Option<InstanceType> {
942 INSTANCE_TYPES.iter().find(|instance| instance.label.eq_ignore_ascii_case(label.trim())).copied()
943}
944
945// ── The cache (actions/cache) ─────────────────────────────────────────────
946//
947// Entries are kept in R2 by the API (the ACTIONS_CACHE bucket) and listed
948// here, by the actions service, which decides what is found, what fits and
949// what is evicted. A sandbox reaches these through the API with its job's
950// token: `/actions/jobs/{job}/cache` (see apps/api/src/blobs.rs).
951
952/// The largest one cache entry may be, compressed.
953pub const CACHE_MAX_ENTRY_BYTES: u64 = 2 * 1024 * 1024 * 1024;
954/// What one repository's entries may hold together. Saving past it evicts
955/// the entries restored longest ago.
956pub const CACHE_REPO_QUOTA_BYTES: u64 = 10 * 1024 * 1024 * 1024;
957/// An entry not restored for this long is deleted.
958pub const CACHE_UNUSED_DAYS: u64 = 7;
959/// An entry is deleted this long after it was saved, however often it is
960/// restored (the bucket's own lifecycle rule deletes objects at 30 days).
961pub const CACHE_MAX_AGE_DAYS: u64 = 28;
962/// An upload is sent in parts of this size (the last may be smaller).
963pub const CACHE_PART_BYTES: u64 = 32 * 1024 * 1024;
964/// What R2 charges g1t to store a GB for a month, in millionths of a
965/// dollar ($0.015): what the cache's storage is charged at, plus the margin.
966pub const CACHE_MICROS_PER_GB_MONTH: i64 = 15_000;
967
968/// `cache_lookup`: the entry a job restores: its key exactly, else the
969/// newest whose key starts with one of `restore`, in order.
970/// Returns `Outcome<Option<CacheHit>>`.
971#[derive(Debug, Serialize, Deserialize)]
972pub struct CacheLookupArgs {
973 pub job: String,
974 pub token: String,
975 pub key: String,
976 #[serde(default)]
977 pub restore: Vec<String>,
978 /// The entry's version, a hash of its paths and compression, as the
979 /// toolkit's client and g1t's runner both send it: only an entry of the
980 /// same version is found. `None` from runners that send none, whose
981 /// entries have none.
982 #[serde(default)]
983 pub version: Option<String>,
984}
985
986#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
987pub struct CacheHit {
988 pub key: String,
989 pub object: String,
990 pub size: u64,
991 /// When it was saved, RFC 3339.
992 #[serde(default)]
993 pub created_at: String,
994 /// A signed token for downloading it through the toolkit's blob
995 /// endpoint, when the lookup came with a version.
996 #[serde(default)]
997 pub blob: Option<String>,
998}
999
1000/// `cache_reserve`: a job about to save `size` bytes under `key`. Refused
1001/// when the key is taken (`conflict`: keys are written once) or the entry
1002/// is too large. Returns `Outcome<CacheReservation>`.
1003#[derive(Debug, Serialize, Deserialize)]
1004pub struct CacheReserveArgs {
1005 pub job: String,
1006 pub token: String,
1007 pub key: String,
1008 /// Its size, when known before it is sent (the toolkit's newer client
1009 /// says only when it finishes: 0 then).
1010 pub size: u64,
1011 /// As in `CacheLookupArgs`.
1012 #[serde(default)]
1013 pub version: Option<String>,
1014}
1015
1016#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1017pub struct CacheReservation {
1018 pub id: String,
1019 /// Where the API puts it in R2.
1020 pub object: String,
1021 /// The entry's number, which the toolkit's older protocol names it by.
1022 #[serde(default)]
1023 pub number: u64,
1024 /// Its R2 upload, once one is started.
1025 #[serde(default)]
1026 pub upload: Option<String>,
1027 /// A signed token for sending its parts through the toolkit's blob
1028 /// endpoint, once its upload is started.
1029 #[serde(default)]
1030 pub blob: Option<String>,
1031}
1032
1033/// `cache_upload`: an entry a job is still uploading, by its number or by
1034/// key and version. Returns `Outcome<CacheReservation>`, with `upload` and
1035/// `blob` set once its upload has been started.
1036#[derive(Debug, Serialize, Deserialize)]
1037pub struct CacheUploadArgs {
1038 pub job: String,
1039 pub token: String,
1040 #[serde(default)]
1041 pub number: Option<u64>,
1042 #[serde(default)]
1043 pub key: Option<String>,
1044 #[serde(default)]
1045 pub version: Option<String>,
1046}
1047
1048/// `cache_commit`: the upload of `id` is complete, at `size` bytes. Returns
1049/// `Outcome<CacheCommitted>`: the objects of entries it evicted, which the
1050/// API deletes from R2.
1051#[derive(Debug, Serialize, Deserialize)]
1052pub struct CacheCommitArgs {
1053 pub job: String,
1054 pub token: String,
1055 pub id: String,
1056 pub size: u64,
1057}
1058
1059#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1060pub struct CacheCommitted {
1061 pub evicted: Vec<String>,
1062}
1063
1064/// `cache_abort`: an upload that will not finish; its reservation goes.
1065/// Returns `Outcome<bool>`.
1066#[derive(Debug, Serialize, Deserialize)]
1067pub struct CacheAbortArgs {
1068 pub job: String,
1069 pub token: String,
1070 pub id: String,
1071}
1072
1073// ── Artifacts (actions/upload-artifact) ───────────────────────────────────
1074//
1075// Kept in R2 by the API (the ACTIONS_CACHE bucket, under `a/`) and listed
1076// here, by the actions service, which decides names, sizes and how long
1077// each is kept. A sandbox reaches them with its job's token
1078// (`/actions/jobs/{job}/artifacts…`) or, through the toolkit's protocol,
1079// with its runtime token (`ACTIONS_RUNTIME_TOKEN`); people through the
1080// REST API and the run's page.
1081
1082/// The largest one artifact may be.
1083pub const ARTIFACT_MAX_BYTES: u64 = 5 * 1024 * 1024 * 1024;
1084/// What one run's artifacts may hold together.
1085pub const RUN_ARTIFACTS_MAX_BYTES: u64 = 10 * 1024 * 1024 * 1024;
1086/// How long artifacts are kept unless a repository says otherwise.
1087pub const ARTIFACT_RETENTION_DEFAULT_DAYS: u32 = 14;
1088/// The longest a repository may keep them.
1089pub const ARTIFACT_RETENTION_MAX_DAYS: u32 = 90;
1090/// A native upload is sent in parts of this size (the last may be smaller).
1091pub const ARTIFACT_PART_BYTES: u64 = 32 * 1024 * 1024;
1092
1093/// An artifact, as the API and the site show it.
1094#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1095pub struct Artifact {
1096 pub id: u64,
1097 pub name: String,
1098 pub size: u64,
1099 /// `sha256:<hex>`, when the uploader said.
1100 pub digest: Option<String>,
1101 /// `zip`, or `tgz` for one an older runner sent.
1102 pub format: String,
1103 pub run_id: String,
1104 pub job_id: String,
1105 pub repo_id: String,
1106 /// Whether it has expired or been deleted (its bytes are gone).
1107 pub expired: bool,
1108 pub created_at: String,
1109 pub updated_at: String,
1110 pub expires_at: String,
1111 /// The run's branch and commit, for the REST shape.
1112 #[serde(default)]
1113 pub head_branch: Option<String>,
1114 #[serde(default)]
1115 pub head_sha: Option<String>,
1116}
1117
1118/// An artifact with where its bytes are, and a signed token for them.
1119#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1120pub struct ArtifactBlob {
1121 pub artifact: Artifact,
1122 pub object: String,
1123 /// For the toolkit's blob endpoint (`/actions/toolkit/blobs/{blob}`).
1124 pub blob: String,
1125}
1126
1127/// A page of artifacts, in GitHub's shape.
1128#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1129pub struct ArtifactList {
1130 pub total_count: u64,
1131 pub artifacts: Vec<Artifact>,
1132}
1133
1134/// `artifact_reserve`: a job about to upload an artifact. Refused when its
1135/// run has one of that name and `overwrite` is not set (`conflict`), or it
1136/// is too large. Returns `Outcome<ArtifactReservation>`.
1137#[derive(Debug, Default, Serialize, Deserialize)]
1138pub struct ArtifactReserveArgs {
1139 pub job: String,
1140 /// The job's token, or its runtime token.
1141 pub token: String,
1142 pub name: String,
1143 /// Its size, when known before it is sent (0 otherwise).
1144 #[serde(default)]
1145 pub size: u64,
1146 /// Days to keep it: 0 for the repository's default; at most the
1147 /// repository's setting.
1148 #[serde(default)]
1149 pub retention_days: u32,
1150 /// When to expire it, RFC 3339, as the toolkit says it (in place of
1151 /// `retention_days`).
1152 #[serde(default)]
1153 pub expires_at: Option<String>,
1154 #[serde(default)]
1155 pub overwrite: bool,
1156 /// `zip` (the default) or `tgz`.
1157 #[serde(default)]
1158 pub format: Option<String>,
1159}
1160
1161#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1162pub struct ArtifactReservation {
1163 pub id: u64,
1164 /// Where the API puts it in R2.
1165 pub object: String,
1166 /// The days it will be kept, and until when.
1167 pub retention_days: u32,
1168 pub expires_at: String,
1169}
1170
1171/// `artifact_commit`: its upload is complete, at `size` bytes. The artifact
1172/// is named by `id`, or by `name` in the job's run (the toolkit's way).
1173/// Returns `Outcome<Artifact>`.
1174#[derive(Debug, Default, Serialize, Deserialize)]
1175pub struct ArtifactCommitArgs {
1176 pub job: String,
1177 pub token: String,
1178 #[serde(default)]
1179 pub id: Option<u64>,
1180 #[serde(default)]
1181 pub name: Option<String>,
1182 pub size: u64,
1183 #[serde(default)]
1184 pub digest: Option<String>,
1185}
1186
1187/// `job_artifacts`: a running job listing the artifacts of its own run, or
1188/// of another run of its repository (`run_id`), narrowed by `name` or
1189/// `id`: `Outcome<Vec<Artifact>>`. `job_artifact` gives the one named, with
1190/// a token to download it: `Outcome<ArtifactBlob>`. `job_delete_artifact`
1191/// deletes one of its own run's: `Outcome<Artifact>`. `artifact_abort`
1192/// gives up an upload by `id`: `Outcome<bool>`.
1193#[derive(Debug, Default, Serialize, Deserialize)]
1194pub struct JobArtifactsArgs {
1195 pub job: String,
1196 pub token: String,
1197 #[serde(default)]
1198 pub run_id: Option<String>,
1199 #[serde(default)]
1200 pub name: Option<String>,
1201 #[serde(default)]
1202 pub id: Option<u64>,
1203}
1204
1205/// `artifacts`: a repository's artifacts, newest first, or one run's.
1206/// Anyone who can see the repository. Returns `Outcome<ArtifactList>`.
1207#[derive(Debug, Serialize, Deserialize)]
1208pub struct ArtifactsArgs {
1209 pub repo: RepoPath,
1210 pub viewer: Viewer,
1211 #[serde(default)]
1212 pub run: Option<String>,
1213 #[serde(default)]
1214 pub name: Option<String>,
1215 #[serde(default)]
1216 pub page: Option<u32>,
1217 #[serde(default)]
1218 pub per_page: Option<u32>,
1219}
1220
1221/// `artifact` (`Outcome<Artifact>`) and `artifact_download`
1222/// (`Outcome<ArtifactBlob>`, with a token good for a few minutes): one
1223/// artifact by `id`, or by `name` within `run`. Anyone who can see the
1224/// repository.
1225#[derive(Debug, Serialize, Deserialize)]
1226pub struct ArtifactArgs {
1227 pub repo: RepoPath,
1228 pub viewer: Viewer,
1229 #[serde(default)]
1230 pub id: Option<u64>,
1231 #[serde(default)]
1232 pub run: Option<String>,
1233 #[serde(default)]
1234 pub name: Option<String>,
1235}
1236
1237/// `delete_artifact`: needs the Write role. Returns `Outcome<Artifact>`.
1238#[derive(Debug, Serialize, Deserialize)]
1239pub struct DeleteArtifactArgs {
1240 pub actor: User,
1241 pub repo: RepoPath,
1242 pub id: u64,
1243}
1244
1245/// `artifact_retention`: anyone who can see the repository. With `days`,
1246/// sets it, which needs the Maintain role. Returns
1247/// `Outcome<ArtifactRetention>`.
1248#[derive(Debug, Serialize, Deserialize)]
1249pub struct ArtifactRetentionArgs {
1250 pub repo: RepoPath,
1251 pub viewer: Viewer,
1252 #[serde(default)]
1253 pub days: Option<u32>,
1254}
1255
1256/// GitHub's shape: the days artifacts are kept by default, and the most a
1257/// repository may choose.
1258#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1259pub struct ArtifactRetention {
1260 pub days: u32,
1261 pub maximum_allowed_days: u32,
1262}
1263
1264// ── The toolkit's protocols ───────────────────────────────────────────────
1265//
1266// Actions built on GitHub's toolkit (`@actions/cache`, `@actions/artifact`,
1267// `@actions/core`'s `getIDToken`) reach g1t with the job's runtime token,
1268// `ACTIONS_RUNTIME_TOKEN`: a JSON Web Token whose `scp` names the run and
1269// job, signed with a key derived from the job's own token, so the actions
1270// service checks it without keeping another secret. Cache and artifact
1271// operations above take it in place of the job's token.
1272
1273/// `runtime_auth`: which job a runtime token is, while it runs:
1274/// `Outcome<RuntimeJob>`. `oidc_claims` takes the same and returns
1275/// `Outcome<Value>`: the claims of the job's OIDC token, less `iss`, `aud`,
1276/// `jti` and the times, or `forbidden` when the job's `permissions` do not
1277/// give it `id-token: write`.
1278#[derive(Debug, Serialize, Deserialize)]
1279pub struct RuntimeAuthArgs {
1280 pub job: String,
1281 pub token: String,
1282}
1283
1284#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1285pub struct RuntimeJob {
1286 pub job: String,
1287 pub run: String,
1288 pub repo_id: String,
1289 pub namespace: String,
1290 /// `owner/name`.
1291 pub repository: String,
1292}
1293
1294/// What a signed blob token lets its holder do.
1295#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1296pub struct BlobGrant {
1297 /// `cache` or `artifact`.
1298 pub kind: String,
1299 /// The entry's id: a cache entry's `cache_…`, an artifact's number.
1300 pub id: String,
1301 pub object: String,
1302 /// The R2 upload it sends parts to; `None` for a download.
1303 pub upload: Option<String>,
1304 /// For a download: what to call the file, and its type.
1305 #[serde(default)]
1306 pub filename: Option<String>,
1307 #[serde(default)]
1308 pub content_type: Option<String>,
1309}
1310
1311/// `blob_sign`: a token for uploading an entry the job reserved, to the R2
1312/// upload the API started for it. Returns `Outcome<String>`.
1313#[derive(Debug, Serialize, Deserialize)]
1314pub struct BlobSignArgs {
1315 pub job: String,
1316 pub token: String,
1317 /// `cache` or `artifact`.
1318 pub kind: String,
1319 pub id: String,
1320 pub upload: String,
1321}
1322
1323/// `blob_open`: what a signed token grants, while it is good and its entry
1324/// is there: `Outcome<BlobGrant>`. `blob_part` records a part sent with an
1325/// upload token (`part`, `etag`, `size`): `Outcome<bool>`. `blob_parts`
1326/// gives the parts recorded, in order: `Outcome<Vec<BlobPart>>`, and
1327/// `blob_done` forgets them: `Outcome<bool>`.
1328#[derive(Debug, Default, Serialize, Deserialize)]
1329pub struct BlobArgs {
1330 pub blob: String,
1331 #[serde(default)]
1332 pub part: u32,
1333 #[serde(default)]
1334 pub etag: String,
1335 #[serde(default)]
1336 pub size: u64,
1337}
1338
1339#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1340pub struct BlobPart {
1341 pub part: u32,
1342 pub etag: String,
1343 pub size: u64,
1344}
1345
1346#[cfg(test)]
1347mod instance_tests {
1348 use super::*;
1349
1350 fn labels(given: &[&str]) -> Vec<String> {
1351 given.iter().map(|l| (*l).to_owned()).collect()
1352 }
1353
1354 #[test]
1355 fn runs_on_picks_the_machine() {
1356 assert_eq!(instance_for(&labels(&["ubuntu-latest"])).container, "standard-1");
1357 assert_eq!(instance_for(&labels(&[])).label, "standard");
1358 assert_eq!(instance_for(&labels(&["g1t-4core"])).container, "standard-4");
1359 assert_eq!(instance_for(&labels(&["ubuntu-latest", "G1T-2Core"])).container, "standard-3");
1360 // Both named: the larger.
1361 assert_eq!(instance_for(&labels(&["g1t-2core", "g1t-4core"])).label, "g1t-4core");
1362 assert_eq!(instance_named("g1t-4core").map(|i| i.vcpu), Some(4.0));
1363 assert_eq!(instance_named("standard"), Some(STANDARD_INSTANCE));
1364 assert_eq!(instance_named("g1t-64core"), None);
1365 }
1366
1367 #[test]
1368 fn start_args_from_older_callers_read() {
1369 let args: StartJobArgs = serde_json::from_value(serde_json::json!({
1370 "job": "job_1", "token": "t", "repo": { "namespace": "acme", "name": "web" }, "timeoutMinutes": 30
1371 }))
1372 .unwrap();
1373 assert!(args.workflow.is_none() && args.environment.is_none() && !args.trusted && args.instance.is_none());
1374 }
1375}
1376
1377#[cfg(test)]
1378mod setting_args_tests {
1379 use super::*;
1380
1381 #[test]
1382 fn who_reads_a_row_is_read_as_the_site_and_api_send_it() {
1383 let args: SetSettingArgs = serde_json::from_value(serde_json::json!({
1384 "actor": { "id": "usr_1", "username": "a" },
1385 "repo": { "namespace": "acme", "name": "web" },
1386 "kind": "secret",
1387 "name": "STRIPE_KEY",
1388 "availableTo": ["deployments"],
1389 "environments": ["production"],
1390 }))
1391 .unwrap();
1392 assert_eq!(args.available_to, Some(vec!["deployments".to_owned()]));
1393 }
1394}