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 has | 1 | //! The actions service: GitHub Actions workflows, run on g1t as they are. |
| 2 | //! | |
| 3 | //! A repository's `.g1t/workflows/*.yml`, in GitHub's format, are read | |
| 4 | //! from the commit an | |
| 5 | //! event is about (the default branch for issues, schedules and manual | |
| 6 | //! runs). Each workflow an event starts becomes a run; each job of the run | |
| 7 | //! (one per matrix combination) runs in a sandbox once the jobs it needs | |
| 8 | //! have finished. Jobs report their steps and logs back as they go, and a | |
| 9 | //! run on a pull request's head is a status on that pull request. | |
| 10 | //! | |
| 11 | //! Secrets and variables belong to a repository or to its workspace; a | |
| 12 | //! repository's override its workspace's of the same name. Secret values | |
| 13 | //! are sealed at rest and never returned. | |
| 14 | //! | |
| 15 | //! Mirrors `packages/contracts/src/actions.ts`. | |
| 16 | ||
| 17 | use serde::{Deserialize, Serialize}; | |
| 18 | use serde_json::Value; | |
| 19 | ||
| 20 | use crate::repos::RepoPath; | |
| 21 | use 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")] | |
| 26 | pub 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")] | |
| 35 | pub 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 has | 53 | } |
| 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")] | |
| 58 | pub 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 has | 71 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 72 | #[serde(rename_all = "camelCase")] | |
| 73 | pub 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")] | |
| 106 | pub 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")] | |
| 121 | pub 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")] | |
| 132 | pub 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 has | 146 | 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 has | 154 | /// 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 way | 164 | /// 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 has | 172 | } |
| 173 | ||
| 174 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
| 175 | #[serde(rename_all = "camelCase")] | |
| 176 | pub 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")] | |
| 197 | pub 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")] | |
| 216 | pub 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")] | |
| 225 | pub 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")] | |
| 234 | pub 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")] | |
| 249 | pub 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)] | |
| 261 | pub 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)] | |
| 271 | pub 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 | ||
| 279 | fn branch_kind() -> String { | |
| 280 | "branch".to_owned() | |
| 281 | } | |
| 282 | ||
| 283 | /// The most reviewers an environment may have, as on GitHub. | |
| 284 | pub const MAX_ENVIRONMENT_REVIEWERS: usize = 6; | |
| 285 | /// The longest wait timer, in minutes: 30 days. | |
| 286 | pub 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")] | |
| 293 | pub 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 has | 313 | } |
| 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")] | |
| 318 | pub 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")] | |
| 340 | pub 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 timeouts | 363 | /// 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 | ||
| 371 | fn 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. | |
| 377 | pub 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. | |
| 381 | pub 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 | ||
| 389 | fn 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")] | |
| 396 | pub 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)] | |
| 411 | pub 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")] | |
| 420 | pub 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. | |
| 432 | pub 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)] | |
| 437 | pub 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")] | |
| 446 | pub 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 timeouts | 457 | /// `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)] | |
| 467 | pub 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")] | |
| 479 | pub 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)] | |
| 500 | pub 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)] | |
| 509 | pub 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)] | |
| 518 | pub 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")] | |
| 535 | pub 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 has | 543 | #[derive(Clone, Debug, Serialize, Deserialize)] |
| 544 | #[serde(rename_all = "camelCase")] | |
| 545 | pub 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")] | |
| 554 | pub 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. | |
| 563 | pub 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")] | |
| 571 | pub 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 has | 604 | } |
| 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. | |
| 608 | pub 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")] | |
| 615 | pub 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 has | 623 | // --- Methods --------------------------------------------------------------- |
| 624 | ||
| 625 | /// `workflows`. Returns `Outcome<Vec<Workflow>>`. | |
| 626 | #[derive(Debug, Serialize, Deserialize)] | |
| 627 | pub 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)] | |
| 634 | pub 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)] | |
| 655 | pub 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)] | |
| 668 | pub 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)] | |
| 679 | pub 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)] | |
| 690 | pub 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 has | 696 | } |
| 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. |
| 699 | pub const MAX_RUN_LOG_BYTES: usize = 24 * 1024 * 1024; | |
| 700 | ||
| The Packages page names every registry it has | 701 | /// `logs`: a job's log after `after`. Returns `Outcome<JobLog>`. |
| 702 | #[derive(Debug, Serialize, Deserialize)] | |
| 703 | pub 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)] | |
| 714 | pub 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 has | 730 | #[derive(Debug, Serialize, Deserialize)] |
| 731 | pub 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 has | 744 | } |
| 745 | ||
| 746 | /// `set_workflow_enabled`. Members only. Returns `Outcome<Workflow>`. | |
| 747 | #[derive(Debug, Serialize, Deserialize)] | |
| 748 | pub 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)] | |
| 758 | pub 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)] | |
| 769 | pub 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)] | |
| 779 | pub 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 has | 813 | } |
| 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")] | |
| 822 | pub 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")] | |
| 838 | pub 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")] | |
| 858 | pub 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)] | |
| 890 | pub 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")] | |
| 903 | pub 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)] | |
| 925 | pub 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")] | |
| 935 | pub struct RemoveManagedWorkflowArgs { | |
| 936 | pub repo_id: String, | |
| 937 | pub name: String, | |
| 938 | } | |
| 939 | ||
| The Packages page names every registry it has | 940 | /// `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")] | |
| 945 | pub 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)] | |
| 964 | pub 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)] | |
| 971 | pub 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)] | |
| 989 | pub 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-agents | 1000 | /// `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 has | 1007 | } |
| 1008 | ||
| 1009 | /// What the runner needs to start a job's sandbox. | |
| 1010 | #[derive(Debug, Serialize, Deserialize)] | |
| 1011 | #[serde(rename_all = "camelCase")] | |
| 1012 | pub 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 has | 1038 | } |
| 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 has | 1045 | #[derive(Clone, Copy, Debug, PartialEq)] |
| 1046 | pub 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 has | 1051 | 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 has | 1061 | pub price_scale: f64, |
| 1062 | } | |
| 1063 | ||
| 1064 | /// The default: what `ubuntu-latest` and every other hosted label get. | |
| 1065 | pub 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 has | 1067 | |
| 1068 | /// Every machine a workflow job can ask for, the default first. | |
| 1069 | pub 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 has | 1073 | ]; |
| 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. | |
| 1077 | pub 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. | |
| 1087 | pub 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. | |
| 1099 | pub 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. | |
| 1102 | pub const CACHE_REPO_QUOTA_BYTES: u64 = 10 * 1024 * 1024 * 1024; | |
| 1103 | /// An entry not restored for this long is deleted. | |
| 1104 | pub 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). | |
| 1107 | pub const CACHE_MAX_AGE_DAYS: u64 = 28; | |
| 1108 | /// An upload is sent in parts of this size (the last may be smaller). | |
| 1109 | pub 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. | |
| 1112 | pub 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)] | |
| 1118 | pub 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 R2 | 1128 | #[serde(default)] |
| 1129 | pub version: Option<String>, | |
| The Packages page names every registry it has | 1130 | } |
| 1131 | ||
| 1132 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] | |
| 1133 | pub 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 R2 | 1137 | /// 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 has | 1144 | } |
| 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)] | |
| 1150 | pub 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 R2 | 1154 | /// 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 has | 1156 | 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 R2 | 1158 | #[serde(default)] |
| 1159 | pub version: Option<String>, | |
| The Packages page names every registry it has | 1160 | } |
| 1161 | ||
| 1162 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] | |
| 1163 | pub 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 R2 | 1167 | /// 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 has | 1177 | } |
| 1178 | ||
| Actions: OIDC tokens, the toolkit's cache and artifact services, and artifacts in R2 | 1179 | /// `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)] | |
| 1183 | pub 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 has | 1194 | /// `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)] | |
| 1198 | pub 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)] | |
| 1206 | pub 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)] | |
| 1213 | pub 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 R2 | 1219 | // ── 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. | |
| 1229 | pub const ARTIFACT_MAX_BYTES: u64 = 5 * 1024 * 1024 * 1024; | |
| 1230 | /// What one run's artifacts may hold together. | |
| 1231 | pub const RUN_ARTIFACTS_MAX_BYTES: u64 = 10 * 1024 * 1024 * 1024; | |
| 1232 | /// How long artifacts are kept unless a repository says otherwise. | |
| 1233 | pub const ARTIFACT_RETENTION_DEFAULT_DAYS: u32 = 14; | |
| 1234 | /// The longest a repository may keep them. | |
| 1235 | pub const ARTIFACT_RETENTION_MAX_DAYS: u32 = 90; | |
| 1236 | /// A native upload is sent in parts of this size (the last may be smaller). | |
| 1237 | pub 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)] | |
| 1241 | pub 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)] | |
| 1266 | pub 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)] | |
| 1275 | pub 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)] | |
| 1284 | pub 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)] | |
| 1308 | pub 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)] | |
| 1321 | pub 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)] | |
| 1340 | pub 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)] | |
| 1354 | pub 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)] | |
| 1372 | pub 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)] | |
| 1385 | pub 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)] | |
| 1395 | pub 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)] | |
| 1405 | pub 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)] | |
| 1425 | pub struct RuntimeAuthArgs { | |
| 1426 | pub job: String, | |
| 1427 | pub token: String, | |
| 1428 | } | |
| 1429 | ||
| 1430 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] | |
| 1431 | pub 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)] | |
| 1442 | pub 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)] | |
| 1460 | pub 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)] | |
| 1475 | pub 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)] | |
| 1486 | pub struct BlobPart { | |
| 1487 | pub part: u32, | |
| 1488 | pub etag: String, | |
| 1489 | pub size: u64, | |
| 1490 | } | |
| 1491 | ||
| The Packages page names every registry it has | 1492 | #[cfg(test)] |
| 1493 | mod 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 has | 1503 | 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 has | 1506 | // 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 has | 1536 | 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)] | |
| 1546 | mod 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.