Skip to content
1,562 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 Packages page names every registry it has1//! 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 Packages page names every registry it has53}
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 Packages page names every registry it has71#[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 Packages page names every registry it has146 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 Packages page names every registry it has154 /// 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 Packages page names every registry it has172}
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 Packages page names every registry it has313}
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 Packages page names every registry it has543#[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 Packages page names every registry it has604}
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 Packages page names every registry it has623// --- 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 Packages page names every registry it has696}
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 Packages page names every registry it has701/// `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 Packages page names every registry it has730#[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 Packages page names every registry it has744}
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 Packages page names every registry it has813}
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 Packages page names every registry it has940/// `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 Packages page names every registry it has1007}
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 Packages page names every registry it has1038}
1039
1040/// A size of machine g1t runs workflow jobs on, asked for by a label in
Every sandbox on g1t's runners has 20 GB of disk, the most Cloudflare Containers give one, as much or more than a GitHub-hosted ubuntu-latest's 14 GB: the standard machine (ubuntu-latest and every other hosted label, and all other work) becomes a custom instance type of 1 vCPU, 4 GiB and 20 GB, where standard-1 had half a vCPU and 8 GB, and g1t-2core a custom 2 vCPU, 8 GiB and 20 GB, where standard-3 had 16 GB; g1t-4core stays standard-4, which already had 20 GB. 1 vCPU is the smallest a custom type may have, and Cloudflare bills CPU only as it is used, so the doubled vCPU costs only what a job uses of it. The contracts' instance table, its TypeScript mirror and the billing keeper's standard sandbox say the same sizes; the price scales, worked out as before from Cloudflare's list prices with every vCPU busy and the Durable Object behind each, are now 1.9 for g1t-2core and 3.4 for g1t-4core, against the larger standard machine, and the fallbacks for a sandbox second (33 millionths of a dollar with its vCPU busy) and a build second (16) follow it. A test holds wrangler.jsonc's sizes to the contracts', and another the scales to the list prices. The docs' machine tables, the larger machines' prices and the Cloudflare deploy guide say so.1041/// `runs-on`. Each is a Cloudflare Containers instance type (named, or a
1042/// custom one: services/runner/wrangler.jsonc), every one with the 20 GB of
1043/// disk Containers allow at most; it costs what that instance costs g1t,
1044/// plus the margin, like any sandbox time.
The Packages page names every registry it has1045#[derive(Clone, Copy, Debug, PartialEq)]
1046pub struct InstanceType {
1047 /// The `runs-on` label, or `standard` for the default.
1048 pub label: &'static str,
Every sandbox on g1t's runners has 20 GB of disk, the most Cloudflare Containers give one, as much or more than a GitHub-hosted ubuntu-latest's 14 GB: the standard machine (ubuntu-latest and every other hosted label, and all other work) becomes a custom instance type of 1 vCPU, 4 GiB and 20 GB, where standard-1 had half a vCPU and 8 GB, and g1t-2core a custom 2 vCPU, 8 GiB and 20 GB, where standard-3 had 16 GB; g1t-4core stays standard-4, which already had 20 GB. 1 vCPU is the smallest a custom type may have, and Cloudflare bills CPU only as it is used, so the doubled vCPU costs only what a job uses of it. The contracts' instance table, its TypeScript mirror and the billing keeper's standard sandbox say the same sizes; the price scales, worked out as before from Cloudflare's list prices with every vCPU busy and the Durable Object behind each, are now 1.9 for g1t-2core and 3.4 for g1t-4core, against the larger standard machine, and the fallbacks for a sandbox second (33 millionths of a dollar with its vCPU busy) and a build second (16) follow it. A test holds wrangler.jsonc's sizes to the contracts', and another the scales to the list prices. The docs' machine tables, the larger machines' prices and the Cloudflare deploy guide say so.1049 /// The Containers instance type: a named one, or `custom` for one
1050 /// sized by its vCPU, memory and disk.
The Packages page names every registry it has1051 pub container: &'static str,
1052 pub vcpu: f64,
1053 pub memory_gib: f64,
1054 pub disk_gb: f64,
1055 /// What a second of it costs g1t as a multiple of the standard
1056 /// machine's, with its vCPUs as busy (Cloudflare's list prices:
1057 /// memory $0.0000025 a GiB-second, disk $0.00000007 a GB-second, vCPU
Every sandbox on g1t's runners has 20 GB of disk, the most Cloudflare Containers give one, as much or more than a GitHub-hosted ubuntu-latest's 14 GB: the standard machine (ubuntu-latest and every other hosted label, and all other work) becomes a custom instance type of 1 vCPU, 4 GiB and 20 GB, where standard-1 had half a vCPU and 8 GB, and g1t-2core a custom 2 vCPU, 8 GiB and 20 GB, where standard-3 had 16 GB; g1t-4core stays standard-4, which already had 20 GB. 1 vCPU is the smallest a custom type may have, and Cloudflare bills CPU only as it is used, so the doubled vCPU costs only what a job uses of it. The contracts' instance table, its TypeScript mirror and the billing keeper's standard sandbox say the same sizes; the price scales, worked out as before from Cloudflare's list prices with every vCPU busy and the Durable Object behind each, are now 1.9 for g1t-2core and 3.4 for g1t-4core, against the larger standard machine, and the fallbacks for a sandbox second (33 millionths of a dollar with its vCPU busy) and a build second (16) follow it. A test holds wrangler.jsonc's sizes to the contracts', and another the scales to the list prices. The docs' machine tables, the larger machines' prices and the Cloudflare deploy guide say so.1058 /// $0.00002 a second, and the Durable Object behind it at 128 MB,
1059 /// $0.0000125 a GB-second), to one decimal. Used to reserve before a
1060 /// job starts, and to price a job that did not report its own CPU.
The Packages page names every registry it has1061 pub price_scale: f64,
1062}
1063
1064/// The default: what `ubuntu-latest` and every other hosted label get.
1065pub const STANDARD_INSTANCE: InstanceType =
Every sandbox on g1t's runners has 20 GB of disk, the most Cloudflare Containers give one, as much or more than a GitHub-hosted ubuntu-latest's 14 GB: the standard machine (ubuntu-latest and every other hosted label, and all other work) becomes a custom instance type of 1 vCPU, 4 GiB and 20 GB, where standard-1 had half a vCPU and 8 GB, and g1t-2core a custom 2 vCPU, 8 GiB and 20 GB, where standard-3 had 16 GB; g1t-4core stays standard-4, which already had 20 GB. 1 vCPU is the smallest a custom type may have, and Cloudflare bills CPU only as it is used, so the doubled vCPU costs only what a job uses of it. The contracts' instance table, its TypeScript mirror and the billing keeper's standard sandbox say the same sizes; the price scales, worked out as before from Cloudflare's list prices with every vCPU busy and the Durable Object behind each, are now 1.9 for g1t-2core and 3.4 for g1t-4core, against the larger standard machine, and the fallbacks for a sandbox second (33 millionths of a dollar with its vCPU busy) and a build second (16) follow it. A test holds wrangler.jsonc's sizes to the contracts', and another the scales to the list prices. The docs' machine tables, the larger machines' prices and the Cloudflare deploy guide say so.1066 InstanceType { label: "standard", container: "custom", vcpu: 1.0, memory_gib: 4.0, disk_gb: 20.0, price_scale: 1.0 };
The Packages page names every registry it has1067
1068/// Every machine a workflow job can ask for, the default first.
1069pub const INSTANCE_TYPES: [InstanceType; 3] = [
1070 STANDARD_INSTANCE,
Every sandbox on g1t's runners has 20 GB of disk, the most Cloudflare Containers give one, as much or more than a GitHub-hosted ubuntu-latest's 14 GB: the standard machine (ubuntu-latest and every other hosted label, and all other work) becomes a custom instance type of 1 vCPU, 4 GiB and 20 GB, where standard-1 had half a vCPU and 8 GB, and g1t-2core a custom 2 vCPU, 8 GiB and 20 GB, where standard-3 had 16 GB; g1t-4core stays standard-4, which already had 20 GB. 1 vCPU is the smallest a custom type may have, and Cloudflare bills CPU only as it is used, so the doubled vCPU costs only what a job uses of it. The contracts' instance table, its TypeScript mirror and the billing keeper's standard sandbox say the same sizes; the price scales, worked out as before from Cloudflare's list prices with every vCPU busy and the Durable Object behind each, are now 1.9 for g1t-2core and 3.4 for g1t-4core, against the larger standard machine, and the fallbacks for a sandbox second (33 millionths of a dollar with its vCPU busy) and a build second (16) follow it. A test holds wrangler.jsonc's sizes to the contracts', and another the scales to the list prices. The docs' machine tables, the larger machines' prices and the Cloudflare deploy guide say so.1071 InstanceType { label: "g1t-2core", container: "custom", vcpu: 2.0, memory_gib: 8.0, disk_gb: 20.0, price_scale: 1.9 },
1072 InstanceType { label: "g1t-4core", container: "standard-4", vcpu: 4.0, memory_gib: 12.0, disk_gb: 20.0, price_scale: 3.4 },
The Packages page names every registry it has1073];
1074
1075/// The machine a job's `runs-on` labels ask for: the largest named, or the
1076/// standard one. Labels compare without regard to case.
1077pub fn instance_for(labels: &[String]) -> InstanceType {
1078 INSTANCE_TYPES
1079 .iter()
1080 .rev()
1081 .find(|instance| instance.label != STANDARD_INSTANCE.label && labels.iter().any(|label| label.trim().eq_ignore_ascii_case(instance.label)))
1082 .copied()
1083 .unwrap_or(STANDARD_INSTANCE)
1084}
1085
1086/// An instance type by its label, if it is one.
1087pub fn instance_named(label: &str) -> Option<InstanceType> {
1088 INSTANCE_TYPES.iter().find(|instance| instance.label.eq_ignore_ascii_case(label.trim())).copied()
1089}
1090
1091// ── The cache (actions/cache) ─────────────────────────────────────────────
1092//
1093// Entries are kept in R2 by the API (the ACTIONS_CACHE bucket) and listed
1094// here, by the actions service, which decides what is found, what fits and
1095// what is evicted. A sandbox reaches these through the API with its job's
1096// token: `/actions/jobs/{job}/cache` (see apps/api/src/blobs.rs).
1097
1098/// The largest one cache entry may be, compressed.
1099pub const CACHE_MAX_ENTRY_BYTES: u64 = 2 * 1024 * 1024 * 1024;
1100/// What one repository's entries may hold together. Saving past it evicts
1101/// the entries restored longest ago.
1102pub const CACHE_REPO_QUOTA_BYTES: u64 = 10 * 1024 * 1024 * 1024;
1103/// An entry not restored for this long is deleted.
1104pub const CACHE_UNUSED_DAYS: u64 = 7;
1105/// An entry is deleted this long after it was saved, however often it is
1106/// restored (the bucket's own lifecycle rule deletes objects at 30 days).
1107pub const CACHE_MAX_AGE_DAYS: u64 = 28;
1108/// An upload is sent in parts of this size (the last may be smaller).
1109pub const CACHE_PART_BYTES: u64 = 32 * 1024 * 1024;
1110/// What R2 charges g1t to store a GB for a month, in millionths of a
1111/// dollar ($0.015): what the cache's storage is charged at, plus the margin.
1112pub const CACHE_MICROS_PER_GB_MONTH: i64 = 15_000;
1113
1114/// `cache_lookup`: the entry a job restores: its key exactly, else the
1115/// newest whose key starts with one of `restore`, in order.
1116/// Returns `Outcome<Option<CacheHit>>`.
1117#[derive(Debug, Serialize, Deserialize)]
1118pub struct CacheLookupArgs {
1119 pub job: String,
1120 pub token: String,
1121 pub key: String,
1122 #[serde(default)]
1123 pub restore: Vec<String>,
Merge branch 'worktree-agent-a3abfcce648e87dca'1124 /// The entry's version, a hash of its paths and compression, as the
1125 /// toolkit's client and g1t's runner both send it: only an entry of the
1126 /// same version is found. `None` from runners that send none, whose
1127 /// entries have none.
Actions: OIDC tokens, the toolkit's cache and artifact services, and artifacts in R21128 #[serde(default)]
1129 pub version: Option<String>,
The Packages page names every registry it has1130}
1131
1132#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1133pub struct CacheHit {
1134 pub key: String,
1135 pub object: String,
1136 pub size: u64,
Actions: OIDC tokens, the toolkit's cache and artifact services, and artifacts in R21137 /// When it was saved, RFC 3339.
1138 #[serde(default)]
1139 pub created_at: String,
1140 /// A signed token for downloading it through the toolkit's blob
1141 /// endpoint, when the lookup came with a version.
1142 #[serde(default)]
1143 pub blob: Option<String>,
The Packages page names every registry it has1144}
1145
1146/// `cache_reserve`: a job about to save `size` bytes under `key`. Refused
1147/// when the key is taken (`conflict`: keys are written once) or the entry
1148/// is too large. Returns `Outcome<CacheReservation>`.
1149#[derive(Debug, Serialize, Deserialize)]
1150pub struct CacheReserveArgs {
1151 pub job: String,
1152 pub token: String,
1153 pub key: String,
Actions: OIDC tokens, the toolkit's cache and artifact services, and artifacts in R21154 /// Its size, when known before it is sent (the toolkit's newer client
1155 /// says only when it finishes: 0 then).
The Packages page names every registry it has1156 pub size: u64,
Merge branch 'worktree-agent-a3abfcce648e87dca'1157 /// As in `CacheLookupArgs`.
Actions: OIDC tokens, the toolkit's cache and artifact services, and artifacts in R21158 #[serde(default)]
1159 pub version: Option<String>,
The Packages page names every registry it has1160}
1161
1162#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1163pub struct CacheReservation {
1164 pub id: String,
1165 /// Where the API puts it in R2.
1166 pub object: String,
Actions: OIDC tokens, the toolkit's cache and artifact services, and artifacts in R21167 /// The entry's number, which the toolkit's older protocol names it by.
1168 #[serde(default)]
1169 pub number: u64,
1170 /// Its R2 upload, once one is started.
1171 #[serde(default)]
1172 pub upload: Option<String>,
1173 /// A signed token for sending its parts through the toolkit's blob
1174 /// endpoint, once its upload is started.
1175 #[serde(default)]
1176 pub blob: Option<String>,
The Packages page names every registry it has1177}
1178
Actions: OIDC tokens, the toolkit's cache and artifact services, and artifacts in R21179/// `cache_upload`: an entry a job is still uploading, by its number or by
1180/// key and version. Returns `Outcome<CacheReservation>`, with `upload` and
1181/// `blob` set once its upload has been started.
1182#[derive(Debug, Serialize, Deserialize)]
1183pub struct CacheUploadArgs {
1184 pub job: String,
1185 pub token: String,
1186 #[serde(default)]
1187 pub number: Option<u64>,
1188 #[serde(default)]
1189 pub key: Option<String>,
1190 #[serde(default)]
1191 pub version: Option<String>,
1192}
1193
The Packages page names every registry it has1194/// `cache_commit`: the upload of `id` is complete, at `size` bytes. Returns
1195/// `Outcome<CacheCommitted>`: the objects of entries it evicted, which the
1196/// API deletes from R2.
1197#[derive(Debug, Serialize, Deserialize)]
1198pub struct CacheCommitArgs {
1199 pub job: String,
1200 pub token: String,
1201 pub id: String,
1202 pub size: u64,
1203}
1204
1205#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1206pub struct CacheCommitted {
1207 pub evicted: Vec<String>,
1208}
1209
1210/// `cache_abort`: an upload that will not finish; its reservation goes.
1211/// Returns `Outcome<bool>`.
1212#[derive(Debug, Serialize, Deserialize)]
1213pub struct CacheAbortArgs {
1214 pub job: String,
1215 pub token: String,
1216 pub id: String,
1217}
1218
Actions: OIDC tokens, the toolkit's cache and artifact services, and artifacts in R21219// ── Artifacts (actions/upload-artifact) ───────────────────────────────────
1220//
1221// Kept in R2 by the API (the ACTIONS_CACHE bucket, under `a/`) and listed
1222// here, by the actions service, which decides names, sizes and how long
1223// each is kept. A sandbox reaches them with its job's token
1224// (`/actions/jobs/{job}/artifacts…`) or, through the toolkit's protocol,
1225// with its runtime token (`ACTIONS_RUNTIME_TOKEN`); people through the
1226// REST API and the run's page.
1227
1228/// The largest one artifact may be.
1229pub const ARTIFACT_MAX_BYTES: u64 = 5 * 1024 * 1024 * 1024;
1230/// What one run's artifacts may hold together.
1231pub const RUN_ARTIFACTS_MAX_BYTES: u64 = 10 * 1024 * 1024 * 1024;
1232/// How long artifacts are kept unless a repository says otherwise.
1233pub const ARTIFACT_RETENTION_DEFAULT_DAYS: u32 = 14;
1234/// The longest a repository may keep them.
1235pub const ARTIFACT_RETENTION_MAX_DAYS: u32 = 90;
1236/// A native upload is sent in parts of this size (the last may be smaller).
1237pub const ARTIFACT_PART_BYTES: u64 = 32 * 1024 * 1024;
1238
1239/// An artifact, as the API and the site show it.
1240#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1241pub struct Artifact {
1242 pub id: u64,
1243 pub name: String,
1244 pub size: u64,
1245 /// `sha256:<hex>`, when the uploader said.
1246 pub digest: Option<String>,
1247 /// `zip`, or `tgz` for one an older runner sent.
1248 pub format: String,
1249 pub run_id: String,
1250 pub job_id: String,
1251 pub repo_id: String,
1252 /// Whether it has expired or been deleted (its bytes are gone).
1253 pub expired: bool,
1254 pub created_at: String,
1255 pub updated_at: String,
1256 pub expires_at: String,
1257 /// The run's branch and commit, for the REST shape.
1258 #[serde(default)]
1259 pub head_branch: Option<String>,
1260 #[serde(default)]
1261 pub head_sha: Option<String>,
1262}
1263
1264/// An artifact with where its bytes are, and a signed token for them.
1265#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1266pub struct ArtifactBlob {
1267 pub artifact: Artifact,
1268 pub object: String,
1269 /// For the toolkit's blob endpoint (`/actions/toolkit/blobs/{blob}`).
1270 pub blob: String,
1271}
1272
1273/// A page of artifacts, in GitHub's shape.
1274#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1275pub struct ArtifactList {
1276 pub total_count: u64,
1277 pub artifacts: Vec<Artifact>,
1278}
1279
1280/// `artifact_reserve`: a job about to upload an artifact. Refused when its
1281/// run has one of that name and `overwrite` is not set (`conflict`), or it
1282/// is too large. Returns `Outcome<ArtifactReservation>`.
1283#[derive(Debug, Default, Serialize, Deserialize)]
1284pub struct ArtifactReserveArgs {
1285 pub job: String,
1286 /// The job's token, or its runtime token.
1287 pub token: String,
1288 pub name: String,
1289 /// Its size, when known before it is sent (0 otherwise).
1290 #[serde(default)]
1291 pub size: u64,
1292 /// Days to keep it: 0 for the repository's default; at most the
1293 /// repository's setting.
1294 #[serde(default)]
1295 pub retention_days: u32,
1296 /// When to expire it, RFC 3339, as the toolkit says it (in place of
1297 /// `retention_days`).
1298 #[serde(default)]
1299 pub expires_at: Option<String>,
1300 #[serde(default)]
1301 pub overwrite: bool,
1302 /// `zip` (the default) or `tgz`.
1303 #[serde(default)]
1304 pub format: Option<String>,
1305}
1306
1307#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1308pub struct ArtifactReservation {
1309 pub id: u64,
1310 /// Where the API puts it in R2.
1311 pub object: String,
1312 /// The days it will be kept, and until when.
1313 pub retention_days: u32,
1314 pub expires_at: String,
1315}
1316
1317/// `artifact_commit`: its upload is complete, at `size` bytes. The artifact
1318/// is named by `id`, or by `name` in the job's run (the toolkit's way).
1319/// Returns `Outcome<Artifact>`.
1320#[derive(Debug, Default, Serialize, Deserialize)]
1321pub struct ArtifactCommitArgs {
1322 pub job: String,
1323 pub token: String,
1324 #[serde(default)]
1325 pub id: Option<u64>,
1326 #[serde(default)]
1327 pub name: Option<String>,
1328 pub size: u64,
1329 #[serde(default)]
1330 pub digest: Option<String>,
1331}
1332
1333/// `job_artifacts`: a running job listing the artifacts of its own run, or
1334/// of another run of its repository (`run_id`), narrowed by `name` or
1335/// `id`: `Outcome<Vec<Artifact>>`. `job_artifact` gives the one named, with
1336/// a token to download it: `Outcome<ArtifactBlob>`. `job_delete_artifact`
1337/// deletes one of its own run's: `Outcome<Artifact>`. `artifact_abort`
1338/// gives up an upload by `id`: `Outcome<bool>`.
1339#[derive(Debug, Default, Serialize, Deserialize)]
1340pub struct JobArtifactsArgs {
1341 pub job: String,
1342 pub token: String,
1343 #[serde(default)]
1344 pub run_id: Option<String>,
1345 #[serde(default)]
1346 pub name: Option<String>,
1347 #[serde(default)]
1348 pub id: Option<u64>,
1349}
1350
1351/// `artifacts`: a repository's artifacts, newest first, or one run's.
1352/// Anyone who can see the repository. Returns `Outcome<ArtifactList>`.
1353#[derive(Debug, Serialize, Deserialize)]
1354pub struct ArtifactsArgs {
1355 pub repo: RepoPath,
1356 pub viewer: Viewer,
1357 #[serde(default)]
1358 pub run: Option<String>,
1359 #[serde(default)]
1360 pub name: Option<String>,
1361 #[serde(default)]
1362 pub page: Option<u32>,
1363 #[serde(default)]
1364 pub per_page: Option<u32>,
1365}
1366
1367/// `artifact` (`Outcome<Artifact>`) and `artifact_download`
1368/// (`Outcome<ArtifactBlob>`, with a token good for a few minutes): one
1369/// artifact by `id`, or by `name` within `run`. Anyone who can see the
1370/// repository.
1371#[derive(Debug, Serialize, Deserialize)]
1372pub struct ArtifactArgs {
1373 pub repo: RepoPath,
1374 pub viewer: Viewer,
1375 #[serde(default)]
1376 pub id: Option<u64>,
1377 #[serde(default)]
1378 pub run: Option<String>,
1379 #[serde(default)]
1380 pub name: Option<String>,
1381}
1382
1383/// `delete_artifact`: needs the Write role. Returns `Outcome<Artifact>`.
1384#[derive(Debug, Serialize, Deserialize)]
1385pub struct DeleteArtifactArgs {
1386 pub actor: User,
1387 pub repo: RepoPath,
1388 pub id: u64,
1389}
1390
1391/// `artifact_retention`: anyone who can see the repository. With `days`,
1392/// sets it, which needs the Maintain role. Returns
1393/// `Outcome<ArtifactRetention>`.
1394#[derive(Debug, Serialize, Deserialize)]
1395pub struct ArtifactRetentionArgs {
1396 pub repo: RepoPath,
1397 pub viewer: Viewer,
1398 #[serde(default)]
1399 pub days: Option<u32>,
1400}
1401
1402/// GitHub's shape: the days artifacts are kept by default, and the most a
1403/// repository may choose.
1404#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1405pub struct ArtifactRetention {
1406 pub days: u32,
1407 pub maximum_allowed_days: u32,
1408}
1409
1410// ── The toolkit's protocols ───────────────────────────────────────────────
1411//
1412// Actions built on GitHub's toolkit (`@actions/cache`, `@actions/artifact`,
1413// `@actions/core`'s `getIDToken`) reach g1t with the job's runtime token,
1414// `ACTIONS_RUNTIME_TOKEN`: a JSON Web Token whose `scp` names the run and
1415// job, signed with a key derived from the job's own token, so the actions
1416// service checks it without keeping another secret. Cache and artifact
1417// operations above take it in place of the job's token.
1418
1419/// `runtime_auth`: which job a runtime token is, while it runs:
1420/// `Outcome<RuntimeJob>`. `oidc_claims` takes the same and returns
1421/// `Outcome<Value>`: the claims of the job's OIDC token, less `iss`, `aud`,
1422/// `jti` and the times, or `forbidden` when the job's `permissions` do not
1423/// give it `id-token: write`.
1424#[derive(Debug, Serialize, Deserialize)]
1425pub struct RuntimeAuthArgs {
1426 pub job: String,
1427 pub token: String,
1428}
1429
1430#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1431pub struct RuntimeJob {
1432 pub job: String,
1433 pub run: String,
1434 pub repo_id: String,
1435 pub namespace: String,
1436 /// `owner/name`.
1437 pub repository: String,
1438}
1439
1440/// What a signed blob token lets its holder do.
1441#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1442pub struct BlobGrant {
1443 /// `cache` or `artifact`.
1444 pub kind: String,
1445 /// The entry's id: a cache entry's `cache_…`, an artifact's number.
1446 pub id: String,
1447 pub object: String,
1448 /// The R2 upload it sends parts to; `None` for a download.
1449 pub upload: Option<String>,
1450 /// For a download: what to call the file, and its type.
1451 #[serde(default)]
1452 pub filename: Option<String>,
1453 #[serde(default)]
1454 pub content_type: Option<String>,
1455}
1456
1457/// `blob_sign`: a token for uploading an entry the job reserved, to the R2
1458/// upload the API started for it. Returns `Outcome<String>`.
1459#[derive(Debug, Serialize, Deserialize)]
1460pub struct BlobSignArgs {
1461 pub job: String,
1462 pub token: String,
1463 /// `cache` or `artifact`.
1464 pub kind: String,
1465 pub id: String,
1466 pub upload: String,
1467}
1468
1469/// `blob_open`: what a signed token grants, while it is good and its entry
1470/// is there: `Outcome<BlobGrant>`. `blob_part` records a part sent with an
1471/// upload token (`part`, `etag`, `size`): `Outcome<bool>`. `blob_parts`
1472/// gives the parts recorded, in order: `Outcome<Vec<BlobPart>>`, and
1473/// `blob_done` forgets them: `Outcome<bool>`.
1474#[derive(Debug, Default, Serialize, Deserialize)]
1475pub struct BlobArgs {
1476 pub blob: String,
1477 #[serde(default)]
1478 pub part: u32,
1479 #[serde(default)]
1480 pub etag: String,
1481 #[serde(default)]
1482 pub size: u64,
1483}
1484
1485#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1486pub struct BlobPart {
1487 pub part: u32,
1488 pub etag: String,
1489 pub size: u64,
1490}
1491
The Packages page names every registry it has1492#[cfg(test)]
1493mod instance_tests {
1494 use super::*;
1495
1496 fn labels(given: &[&str]) -> Vec<String> {
1497 given.iter().map(|l| (*l).to_owned()).collect()
1498 }
1499
1500 #[test]
1501 fn runs_on_picks_the_machine() {
Every sandbox on g1t's runners has 20 GB of disk, the most Cloudflare Containers give one, as much or more than a GitHub-hosted ubuntu-latest's 14 GB: the standard machine (ubuntu-latest and every other hosted label, and all other work) becomes a custom instance type of 1 vCPU, 4 GiB and 20 GB, where standard-1 had half a vCPU and 8 GB, and g1t-2core a custom 2 vCPU, 8 GiB and 20 GB, where standard-3 had 16 GB; g1t-4core stays standard-4, which already had 20 GB. 1 vCPU is the smallest a custom type may have, and Cloudflare bills CPU only as it is used, so the doubled vCPU costs only what a job uses of it. The contracts' instance table, its TypeScript mirror and the billing keeper's standard sandbox say the same sizes; the price scales, worked out as before from Cloudflare's list prices with every vCPU busy and the Durable Object behind each, are now 1.9 for g1t-2core and 3.4 for g1t-4core, against the larger standard machine, and the fallbacks for a sandbox second (33 millionths of a dollar with its vCPU busy) and a build second (16) follow it. A test holds wrangler.jsonc's sizes to the contracts', and another the scales to the list prices. The docs' machine tables, the larger machines' prices and the Cloudflare deploy guide say so.1502 assert_eq!(instance_for(&labels(&["ubuntu-latest"])), STANDARD_INSTANCE);
The Packages page names every registry it has1503 assert_eq!(instance_for(&labels(&[])).label, "standard");
1504 assert_eq!(instance_for(&labels(&["g1t-4core"])).container, "standard-4");
Every sandbox on g1t's runners has 20 GB of disk, the most Cloudflare Containers give one, as much or more than a GitHub-hosted ubuntu-latest's 14 GB: the standard machine (ubuntu-latest and every other hosted label, and all other work) becomes a custom instance type of 1 vCPU, 4 GiB and 20 GB, where standard-1 had half a vCPU and 8 GB, and g1t-2core a custom 2 vCPU, 8 GiB and 20 GB, where standard-3 had 16 GB; g1t-4core stays standard-4, which already had 20 GB. 1 vCPU is the smallest a custom type may have, and Cloudflare bills CPU only as it is used, so the doubled vCPU costs only what a job uses of it. The contracts' instance table, its TypeScript mirror and the billing keeper's standard sandbox say the same sizes; the price scales, worked out as before from Cloudflare's list prices with every vCPU busy and the Durable Object behind each, are now 1.9 for g1t-2core and 3.4 for g1t-4core, against the larger standard machine, and the fallbacks for a sandbox second (33 millionths of a dollar with its vCPU busy) and a build second (16) follow it. A test holds wrangler.jsonc's sizes to the contracts', and another the scales to the list prices. The docs' machine tables, the larger machines' prices and the Cloudflare deploy guide say so.1505 assert_eq!(instance_for(&labels(&["ubuntu-latest", "G1T-2Core"])).label, "g1t-2core");
The Packages page names every registry it has1506 // Both named: the larger.
1507 assert_eq!(instance_for(&labels(&["g1t-2core", "g1t-4core"])).label, "g1t-4core");
1508 assert_eq!(instance_named("g1t-4core").map(|i| i.vcpu), Some(4.0));
1509 assert_eq!(instance_named("standard"), Some(STANDARD_INSTANCE));
1510 assert_eq!(instance_named("g1t-64core"), None);
1511 }
1512
1513 #[test]
Every sandbox on g1t's runners has 20 GB of disk, the most Cloudflare Containers give one, as much or more than a GitHub-hosted ubuntu-latest's 14 GB: the standard machine (ubuntu-latest and every other hosted label, and all other work) becomes a custom instance type of 1 vCPU, 4 GiB and 20 GB, where standard-1 had half a vCPU and 8 GB, and g1t-2core a custom 2 vCPU, 8 GiB and 20 GB, where standard-3 had 16 GB; g1t-4core stays standard-4, which already had 20 GB. 1 vCPU is the smallest a custom type may have, and Cloudflare bills CPU only as it is used, so the doubled vCPU costs only what a job uses of it. The contracts' instance table, its TypeScript mirror and the billing keeper's standard sandbox say the same sizes; the price scales, worked out as before from Cloudflare's list prices with every vCPU busy and the Durable Object behind each, are now 1.9 for g1t-2core and 3.4 for g1t-4core, against the larger standard machine, and the fallbacks for a sandbox second (33 millionths of a dollar with its vCPU busy) and a build second (16) follow it. A test holds wrangler.jsonc's sizes to the contracts', and another the scales to the list prices. The docs' machine tables, the larger machines' prices and the Cloudflare deploy guide say so.1514 fn every_machine_has_the_most_disk_containers_allow_and_fits_a_custom_type() {
1515 for instance in INSTANCE_TYPES {
1516 assert_eq!(instance.disk_gb, 20.0, "{}", instance.label);
1517 // Custom instance types: 1 to 4 vCPU, at most 12 GiB, at least
1518 // 3 GiB a vCPU.
1519 assert!((1.0..=4.0).contains(&instance.vcpu), "{}", instance.label);
1520 assert!(instance.memory_gib <= 12.0 && instance.memory_gib >= 3.0 * instance.vcpu, "{}", instance.label);
1521 }
1522 }
1523
1524 #[test]
1525 fn price_scales_follow_the_list_prices_with_every_vcpu_busy() {
1526 // Memory, disk and vCPU at Cloudflare's list prices, and the
1527 // Durable Object behind the container (128 MB), in micros a second.
1528 let busy = |i: &InstanceType| i.memory_gib * 2.5 + i.disk_gb * 0.07 + i.vcpu * 20.0 + 0.125 * 12.5;
1529 for instance in INSTANCE_TYPES {
1530 let scale = busy(&instance) / busy(&STANDARD_INSTANCE);
1531 assert!((instance.price_scale - (scale * 10.0).round() / 10.0).abs() < 1e-9, "{}: {scale}", instance.label);
1532 }
1533 }
1534
1535 #[test]
The Packages page names every registry it has1536 fn start_args_from_older_callers_read() {
1537 let args: StartJobArgs = serde_json::from_value(serde_json::json!({
1538 "job": "job_1", "token": "t", "repo": { "namespace": "acme", "name": "web" }, "timeoutMinutes": 30
1539 }))
1540 .unwrap();
1541 assert!(args.workflow.is_none() && args.environment.is_none() && !args.trusted && args.instance.is_none());
1542 }
1543}
1544
1545#[cfg(test)]
1546mod setting_args_tests {
1547 use super::*;
1548
1549 #[test]
1550 fn who_reads_a_row_is_read_as_the_site_and_api_send_it() {
1551 let args: SetSettingArgs = serde_json::from_value(serde_json::json!({
1552 "actor": { "id": "usr_1", "username": "a" },
1553 "repo": { "namespace": "acme", "name": "web" },
1554 "kind": "secret",
1555 "name": "STRIPE_KEY",
1556 "availableTo": ["deployments"],
1557 "environments": ["production"],
1558 }))
1559 .unwrap();
1560 assert_eq!(args.available_to, Some(vec!["deployments".to_owned()]));
1561 }
1562}

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