Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
1,536 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

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

This file's history is long; its oldest lines are credited to the oldest commit read.