ubuntu-latest follows GitHub: 4 vCPU in a public repository, 2 in a private one, billed as the machine it ran on (Closes #169)
GitHub's ubuntu-latest gives a public repository 4 vCPU and 16 GB, and a private one 2 vCPU and 8 GB. g1t gave every repository the 2 vCPU machine, so g1t's own workflows asked for g1t-4core by name, and its bill showed two machines. - `instance_for` takes whether the repository is public. With no machine named (`ubuntu-latest`, any other Linux label), a public repository gets the 4-core machine (standard-4: 4 vCPU, 12 GiB, 20 GB) and a private one the standard machine (standard-3: 2 vCPU, 8 GiB, 16 GB). A machine named in `runs-on` (`g1t-2core`, `g1t-4core`) is that machine in any repository. - 12 GiB is the most a Containers instance has without a raised account limit; GitHub's public machine has 16 GB. - The actions service reads visibility from the run's own event (`repository.private`, which every event carries), so it makes no extra call. An event that doesn't say is taken as private. - Billing: the label the plan resolves is the one the runner keeps on the job's meter, and the one billing prices the job by (`instance_named`). A public repository's ubuntu-latest job is charged as 4-core at $0.007 a minute. Usage stays owed until billing confirms it and is recorded once, as before. - g1t's workflows run on ubuntu-latest throughout. In this public repository that is 4 vCPU, as on GitHub. - The machine tables in the Actions, usage and billing, compute, Cloudflare and limitations docs say so. The open-source pool and the trial now say how many minutes they cover on each machine. Checked: - cargo test -p g1t-contracts -p g1t-billing -p g1t-actions-service (new: runs_on_picks_the_machine for public and private; a_runs_repository_is_public_only_when_its_event_says_so). - The runner's and the deploy tool's tests. - npx prettier --check .
- G1t-Session:
- ses_01m57b7r6jew3aqgyfmhw6yah6
| 266 | 266 | name: The desktop app's Linux build | |
| 267 | 267 | needs: look | |
| 268 | 268 | if: ${{ needs.look.outputs.desktop-linux == 'true' }} | |
| 269 | − | # The larger machine: linking Tauri on the small one dies with a bus error. | |
| 270 | − | runs-on: g1t-4core | |
| 269 | + | # ubuntu-latest is 4 vCPU in this public repository, as on GitHub; linking | |
| 270 | + | # Tauri needs it (on 2 vCPU it dies with a bus error). | |
| 271 | + | runs-on: ubuntu-latest | |
| 271 | 272 | timeout-minutes: 60 | |
| 272 | 273 | steps: | |
| 273 | 274 | - uses: actions/checkout@v5 | |
| ⋯ | |||
| 310 | 311 | name: The desktop app's Windows build | |
| 311 | 312 | needs: look | |
| 312 | 313 | if: ${{ needs.look.outputs.desktop-windows == 'true' && vars.DESKTOP_WINDOWS_FROM_LINUX == 'true' && vars.DESKTOP_WINDOWS_RUNNER != 'true' }} | |
| 313 | − | runs-on: g1t-4core | |
| 314 | + | runs-on: ubuntu-latest | |
| 314 | 315 | timeout-minutes: 60 | |
| 315 | 316 | steps: | |
| 316 | 317 | - uses: actions/checkout@v5 | |
| ⋯ | |||
| 374 | 375 | needs: look | |
| 375 | 376 | if: ${{ needs.look.outputs.cli == 'true' }} | |
| 376 | 377 | # The builder runs in Docker, which g1t's own machines give each job. | |
| 377 | − | runs-on: g1t-4core | |
| 378 | + | runs-on: ubuntu-latest | |
| 378 | 379 | timeout-minutes: 60 | |
| 379 | 380 | steps: | |
| 380 | 381 | - uses: actions/checkout@v5 | |
| 38 | 38 | jobs: | |
| 39 | 39 | rust: | |
| 40 | 40 | name: Rust | |
| 41 | − | runs-on: g1t-4core | |
| 41 | + | runs-on: ubuntu-latest | |
| 42 | 42 | timeout-minutes: 45 | |
| 43 | 43 | env: | |
| 44 | 44 | # Line tables, not full debug info: a panic still names its file and | |
| ⋯ | |||
| 158 | 158 | ||
| 159 | 159 | desktop: | |
| 160 | 160 | name: Desktop app | |
| 161 | − | # The larger machine, as the Rust job: linking Tauri on the small one | |
| 162 | − | # ran out of room and the linker died with a bus error. | |
| 163 | − | runs-on: g1t-4core | |
| 161 | + | # ubuntu-latest is 4 vCPU in this public repository, as on GitHub; linking | |
| 162 | + | # Tauri needs it (on 2 vCPU it ran out of room and died with a bus error). | |
| 163 | + | runs-on: ubuntu-latest | |
| 164 | 164 | timeout-minutes: 30 | |
| 165 | 165 | steps: | |
| 166 | 166 | - uses: actions/checkout@v5 | |
| 11 | 11 | # Every platform builds in one job, in the rust-cross/cargo-zigbuild image, | |
| 12 | 12 | # pinned by digest in scripts/cli-release.mjs: the macOS builds link Apple's | |
| 13 | 13 | # Security framework (the keychain), and that image carries its SDK. It | |
| 14 | − | # runs on g1t's own machines (g1t-4core), whose jobs have a Docker Engine of | |
| 14 | + | # runs on g1t's own machines (ubuntu-latest, 4 vCPU here), whose jobs have a Docker Engine of | |
| 15 | 15 | # their own. | |
| 16 | 16 | name: CLI release | |
| 17 | 17 | ||
| ⋯ | |||
| 34 | 34 | jobs: | |
| 35 | 35 | build: | |
| 36 | 36 | name: Build every platform | |
| 37 | − | runs-on: g1t-4core | |
| 37 | + | runs-on: ubuntu-latest | |
| 38 | 38 | timeout-minutes: 60 | |
| 39 | 39 | steps: | |
| 40 | 40 | - uses: actions/checkout@v5 | |
| 156 | 156 | # Runs when nothing before it failed: a migrate job skipped for having | |
| 157 | 157 | # nothing to apply is not a failure. | |
| 158 | 158 | if: ${{ !failure() && !cancelled() && needs.plan.outputs.has_core == 'true' && inputs.dry_run != true }} | |
| 159 | − | # Rust builds and the runner's image get 4 vCPUs; everything else the | |
| 160 | − | # standard machine. | |
| 161 | − | runs-on: ${{ (matrix.rust || matrix.image) && 'g1t-4core' || 'ubuntu-latest' }} | |
| 159 | + | # ubuntu-latest: 4 vCPU in this public repository, as on GitHub. | |
| 160 | + | runs-on: ubuntu-latest | |
| 162 | 161 | environment: | |
| 163 | 162 | name: production | |
| 164 | 163 | url: https://g1t.sh | |
| ⋯ | |||
| 306 | 305 | name: edge (${{ matrix.group }}) | |
| 307 | 306 | needs: [check, plan, migrate, core] | |
| 308 | 307 | if: ${{ !failure() && !cancelled() && needs.plan.outputs.has_edge == 'true' && inputs.dry_run != true }} | |
| 309 | − | runs-on: ${{ (matrix.rust || matrix.image) && 'g1t-4core' || 'ubuntu-latest' }} | |
| 308 | + | runs-on: ubuntu-latest | |
| 310 | 309 | environment: | |
| 311 | 310 | name: production | |
| 312 | 311 | url: https://g1t.sh | |
| ⋯ | |||
| 321 | 320 | name: front (${{ matrix.group }}) | |
| 322 | 321 | needs: [check, plan, migrate, core, edge] | |
| 323 | 322 | if: ${{ !failure() && !cancelled() && needs.plan.outputs.has_front == 'true' && inputs.dry_run != true }} | |
| 324 | − | runs-on: ${{ (matrix.rust || matrix.image) && 'g1t-4core' || 'ubuntu-latest' }} | |
| 323 | + | runs-on: ubuntu-latest | |
| 325 | 324 | environment: | |
| 326 | 325 | name: production | |
| 327 | 326 | url: https://g1t.sh | |
| 63 | 63 | linux: | |
| 64 | 64 | name: Linux | |
| 65 | 65 | needs: version | |
| 66 | − | # The larger machine: linking Tauri on the small one dies with a bus error. | |
| 67 | − | runs-on: g1t-4core | |
| 66 | + | # ubuntu-latest is 4 vCPU in this public repository, as on GitHub; linking | |
| 67 | + | # Tauri needs it (on 2 vCPU it dies with a bus error). | |
| 68 | + | runs-on: ubuntu-latest | |
| 68 | 69 | # Production's secrets, without a deployment: a release is not a deploy of | |
| 69 | 70 | # g1t.sh, and would otherwise show on its card as one in progress. | |
| 70 | 71 | environment: | |
| ⋯ | |||
| 168 | 169 | name: Windows, built on Linux | |
| 169 | 170 | needs: version | |
| 170 | 171 | if: vars.DESKTOP_WINDOWS_FROM_LINUX == 'true' && vars.DESKTOP_WINDOWS_RUNNER != 'true' | |
| 171 | − | runs-on: g1t-4core | |
| 172 | + | runs-on: ubuntu-latest | |
| 172 | 173 | # Production's secrets, without a deployment: a release is not a deploy of | |
| 173 | 174 | # g1t.sh, and would otherwise show on its card as one in progress. | |
| 174 | 175 | environment: | |
| 11 | 11 | # to make the release key the first time; CONTRIBUTING.md ("Deploying") | |
| 12 | 12 | # says how a release is made. | |
| 13 | 13 | # | |
| 14 | − | # The image jobs run on g1t's own machines (g1t-4core), whose jobs have a | |
| 14 | + | # The image jobs run on g1t's own machines (ubuntu-latest, 4 vCPU here), whose jobs have a | |
| 15 | 15 | # Docker Engine of their own (crates/runner/src/docker/engine.rs). arm64 | |
| 16 | 16 | # images need QEMU's emulators registered with the kernel; each image job | |
| 17 | 17 | # tries that, and when the machine does not allow it, builds amd64 alone | |
| ⋯ | |||
| 126 | 126 | image: | |
| 127 | 127 | name: Container image | |
| 128 | 128 | needs: binaries | |
| 129 | − | runs-on: g1t-4core | |
| 129 | + | runs-on: ubuntu-latest | |
| 130 | 130 | # Production's secrets, without a deployment: a release is not a deploy of | |
| 131 | 131 | # g1t.sh, and would otherwise show on its card as one in progress. | |
| 132 | 132 | environment: | |
| ⋯ | |||
| 183 | 183 | sandbox: | |
| 184 | 184 | name: Sandbox images | |
| 185 | 185 | needs: [binaries, image] | |
| 186 | − | runs-on: g1t-4core | |
| 186 | + | runs-on: ubuntu-latest | |
| 187 | 187 | timeout-minutes: 60 | |
| 188 | 188 | steps: | |
| 189 | 189 | - uses: actions/checkout@v5 | |
| 351 | 351 | ||
| 352 | 352 | ### Machine sizes, time and storage | |
| 353 | 353 | ||
| 354 | − | | Limit | Value | | |
| 355 | − | | ------------------------- | ----------------------------------------------------------------------------------------------------- | | |
| 356 | − | | Largest machine | 4 vCPUs, 12 GiB of memory, 20 GB of disk (`g1t-4core`). No GPUs. | | |
| 357 | − | | Standard machine | 2 vCPUs, 8 GiB of memory, 16 GB of disk (`ubuntu-latest`, every other hosted label, and `g1t-2core`). | | |
| 358 | − | | One job on g1t's machines | 60 minutes. On a self-hosted runner, 24 hours. | | |
| 359 | − | | One cache entry | 2 GiB, compressed. A larger one is not saved. | | |
| 360 | − | | A repository's caches | 10 GiB together. Past it, the entries restored longest ago are removed. | | |
| 361 | − | | One artifact | 5 GiB, zipped. Kept 14 days unless the repository says otherwise, at most 90. | | |
| 362 | − | | A run's artifacts | 10 GiB together. | | |
| 354 | + | | Limit | Value | | |
| 355 | + | | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | |
| 356 | + | | Largest machine | 4 vCPUs, 12 GiB of memory, 20 GB of disk (`g1t-4core`, and `ubuntu-latest` in a public repository). No GPUs. GitHub's machine for public repositories has 16 GB of memory. | | |
| 357 | + | | Standard machine | 2 vCPUs, 8 GiB of memory, 16 GB of disk (`ubuntu-latest` and every other hosted label in a private repository, and `g1t-2core`). | | |
| 358 | + | | One job on g1t's machines | 60 minutes. On a self-hosted runner, 24 hours. | | |
| 359 | + | | One cache entry | 2 GiB, compressed. A larger one is not saved. | | |
| 360 | + | | A repository's caches | 10 GiB together. Past it, the entries restored longest ago are removed. | | |
| 361 | + | | One artifact | 5 GiB, zipped. Kept 14 days unless the repository says otherwise, at most 90. | | |
| 362 | + | | A run's artifacts | 10 GiB together. | | |
| 363 | 363 | ||
| 364 | 364 | The machine sizes are Cloudflare Containers' instance sizes. For more, use a | |
| 365 | 365 | [self-hosted runner](/guides/self-hosted-runners/). See |
| 74 | 74 | `runner` defines three Container classes, picked by `runs-on`, each a | |
| 75 | 75 | named Cloudflare instance type: | |
| 76 | 76 | ||
| 77 | − | | `runs-on` | Class | Size | | |
| 78 | − | | ---------------------------------------------------- | ---------------- | ----------------------------------------------- | | |
| 79 | − | | Any other hosted label, and every other kind of work | `AttemptSandbox` | `standard-3`: 2 vCPU, 8 GiB memory, 16 GB disk | | |
| 80 | − | | `g1t-2core` | `Sandbox2Core` | `standard-3`: 2 vCPU, 8 GiB memory, 16 GB disk | | |
| 81 | − | | `g1t-4core` | `Sandbox4Core` | `standard-4`: 4 vCPU, 12 GiB memory, 20 GB disk | | |
| 77 | + | | `runs-on` | Class | Size | | |
| 78 | + | | ---------------------------------------------------------------------------- | ---------------- | ----------------------------------------------- | | |
| 79 | + | | Any other hosted label in a private repository, and every other kind of work | `AttemptSandbox` | `standard-3`: 2 vCPU, 8 GiB memory, 16 GB disk | | |
| 80 | + | | `g1t-2core` | `Sandbox2Core` | `standard-3`: 2 vCPU, 8 GiB memory, 16 GB disk | | |
| 81 | + | | `g1t-4core`, and any other hosted label in a public repository | `Sandbox4Core` | `standard-4`: 4 vCPU, 12 GiB memory, 20 GB disk | | |
| 82 | 82 | ||
| 83 | + | `ubuntu-latest` follows GitHub's hosted runners: the actions service | |
| 84 | + | resolves it to `g1t-4core` when the run's repository is public (its | |
| 85 | + | event's `repository.private`), and the job is billed as that machine. | |
| 83 | 86 | `g1t-2core` is kept as a name of the standard machine, so workflows that | |
| 84 | 87 | ask for it keep working; it keeps its class and its own `max_instances` | |
| 85 | 88 | in `services/runner/wrangler.jsonc`. The contracts' instance table |
| 554 | 554 | ||
| 555 | 555 | ### Machine sizes | |
| 556 | 556 | ||
| 557 | − | A job runs on the standard machine unless its `runs-on` is `g1t-4core`: | |
| 557 | + | `ubuntu-latest` gets what it gets on GitHub: 4 vCPUs in a public | |
| 558 | + | repository, 2 in a private one. Any other Linux label is the same. | |
| 558 | 559 | ||
| 559 | − | | `runs-on` | vCPUs | Memory | Disk | A minute | | |
| 560 | − | | -------------------------------------------- | ----- | ------ | ----- | -------- | | |
| 561 | − | | `ubuntu-latest`, or any other Linux label | 2 | 8 GiB | 16 GB | $0.004 | | |
| 562 | − | | `g1t-2core` (a name of the standard machine) | 2 | 8 GiB | 16 GB | $0.004 | | |
| 563 | − | | `g1t-4core` | 4 | 12 GiB | 20 GB | $0.007 | | |
| 560 | + | | `runs-on` | vCPUs | Memory | Disk | A minute | | |
| 561 | + | | ----------------------------------------------------------------- | ----- | ------ | ----- | -------- | | |
| 562 | + | | `ubuntu-latest`, or any other Linux label, in a public repository | 4 | 12 GiB | 20 GB | $0.007 | | |
| 563 | + | | `ubuntu-latest`, or any other Linux label, in a private one | 2 | 8 GiB | 16 GB | $0.004 | | |
| 564 | + | | `g1t-4core`, in any repository | 4 | 12 GiB | 20 GB | $0.007 | | |
| 565 | + | | `g1t-2core`, in any repository | 2 | 8 GiB | 16 GB | $0.004 | | |
| 564 | 566 | ||
| 567 | + | GitHub's machine for public repositories has 16 GB of memory; g1t's has | |
| 568 | + | 12 GiB, the most its machines have. | |
| 569 | + | ||
| 570 | + | To choose a machine whatever the repository's visibility, name it: | |
| 571 | + | ||
| 565 | 572 | ```yaml | |
| 566 | 573 | jobs: | |
| 567 | 574 | build: | |
| 568 | 575 | runs-on: g1t-4core | |
| 569 | 576 | ``` | |
| 570 | 577 | ||
| 571 | − | `g1t-4core` is for heavier jobs: builds that compile, such as Rust or a | |
| 572 | − | large TypeScript project, image builds and databases. `g1t-2core` is kept | |
| 573 | − | as a name of the standard machine, so workflows that ask for it keep | |
| 574 | − | working. The label can come from the matrix or the run's inputs | |
| 578 | + | `g1t-4core` suits heavier jobs in a private repository: builds that | |
| 579 | + | compile, such as Rust or a large TypeScript project, image builds and | |
| 580 | + | databases. `g1t-2core` keeps a public repository's light jobs on the | |
| 581 | + | smaller machine. The label can come from the matrix or the run's inputs | |
| 575 | 582 | (`runs-on: ${{ matrix.big && 'g1t-4core' || 'ubuntu-latest' }}`). | |
| 576 | 583 | ||
| 577 | 584 | Each job is charged its time rounded up to the whole minute (61 seconds |
| 340 | 340 | ||
| 341 | 341 | The standard sandbox is the instance type `standard-3` (2 vCPU, 8 GiB, | |
| 342 | 342 | 16 GB), and so is `runs-on: g1t-2core`, in a class of its own; jobs that | |
| 343 | − | ask for `runs-on: g1t-4core` get `standard-4` (4 vCPU, 12 GiB, 20 GB). At | |
| 343 | + | ask for `runs-on: g1t-4core`, and `ubuntu-latest` jobs in a public | |
| 344 | + | repository, get `standard-4` (4 vCPU, 12 GiB, 20 GB). At | |
| 344 | 345 | the `max_instances` in `services/runner/wrangler.jsonc` (20, 10 and 10), | |
| 345 | 346 | 40 sandboxes at once hold 100 vCPU, 360 GiB of memory and 680 GB of disk, | |
| 346 | 347 | which your account's Containers limits for live instances must allow. |
| 102 | 102 | - **$10 of usage each month**, used first: everything g1t runs, at the | |
| 103 | 103 | [prices below](#what-it-costs) (runner minutes at their flat rate per | |
| 104 | 104 | machine, everything else at cost plus 20%). That is 2,500 runner minutes on the standard machine, or | |
| 105 | − | about 1,428 on `g1t-4core`. | |
| 105 | + | about 1,428 on `g1t-4core` (which `ubuntu-latest` gets in a public | |
| 106 | + | repository). | |
| 106 | 107 | - **Everyone in the workspace** at one price, never per person. | |
| 107 | 108 | - **Unlimited** projects, previews and repositories. | |
| 108 | 109 | - Workflows, the merge queue, | |
| ⋯ | |||
| 244 | 245 | ||
| 245 | 246 | Once its card is checked, a workspace gets **$5 of trial credit**, once. It | |
| 246 | 247 | pays for usage at the plan's prices: runner minutes for checks, workflows | |
| 247 | − | and the merge queue (1,250 on the standard machine), and semantic search. It never pays for deployments. | |
| 248 | + | and the merge queue (1,250 on the standard machine, about 714 on the | |
| 249 | + | 4-core one), and semantic search. It never pays for deployments. | |
| 248 | 250 | ||
| 249 | 251 | - Each card gets one trial, whichever workspace it is checked in. Prepaid | |
| 250 | 252 | cards start none. | |
| ⋯ | |||
| 261 | 263 | ## The open-source pool | |
| 262 | 264 | ||
| 263 | 265 | g1t sets aside **$25 a month** for public repositories, at most **$2 a | |
| 264 | − | month** for any one repository: 6,250 and 500 runner minutes on the | |
| 265 | − | standard machine. It pays for **checks, workflows and the merge queue on | |
| 266 | + | month** for any one repository: about 3,571 and 285 minutes of | |
| 267 | + | workflow jobs, which run on the 4-core machine in a public repository, | |
| 268 | + | or 6,250 and 500 minutes of checks and merge queue runs on the standard | |
| 269 | + | machine. It pays for **checks, workflows and the merge queue on | |
| 266 | 270 | public repositories**, after a card check. It does not pay | |
| 267 | 271 | for deployments, storage or embeddings. | |
| 268 | 272 | ||
| ⋯ | |||
| 362 | 366 | queue run) is charged by the minute, at a fixed rate for the machine it | |
| 363 | 367 | ran on: | |
| 364 | 368 | ||
| 365 | − | | `runs-on` | Machine | vCPUs | Memory | Disk | A minute | | |
| 366 | − | | ------------------------------------------------------ | ------------------- | ----- | ------ | ----- | -------- | | |
| 367 | − | | `ubuntu-latest`, any other Linux label, or `g1t-2core` | Standard (`2-core`) | 2 | 8 GiB | 16 GB | $0.004 | | |
| 368 | − | | `g1t-4core` | `4-core` | 4 | 12 GiB | 20 GB | $0.007 | | |
| 369 | + | | `runs-on` | Machine | vCPUs | Memory | Disk | A minute | | |
| 370 | + | | -------------------------------------------------------------------------------- | ------------------- | ----- | ------ | ----- | -------- | | |
| 371 | + | | `ubuntu-latest` or any other Linux label in a private repository, or `g1t-2core` | Standard (`2-core`) | 2 | 8 GiB | 16 GB | $0.004 | | |
| 372 | + | | `ubuntu-latest` or any other Linux label in a public repository, or `g1t-4core` | `4-core` | 4 | 12 GiB | 20 GB | $0.007 | | |
| 369 | 373 | ||
| 370 | − | Checks and the merge queue run on the standard machine. `g1t-2core` is | |
| 374 | + | `ubuntu-latest` gets what it gets on GitHub: 4 vCPUs in a public | |
| 375 | + | repository, 2 in a private one. A job is charged as the machine it ran | |
| 376 | + | on. Checks and the merge queue run on the standard machine. `g1t-2core` is | |
| 371 | 377 | kept as a name of the standard machine, so workflows that ask for it keep | |
| 372 | 378 | working, at its rate. | |
| 373 | 379 | ||
| 1089 | 1089 | /// Minutes before the job is stopped. | |
| 1090 | 1090 | pub timeout_minutes: u32, | |
| 1091 | 1091 | /// The machine its `runs-on` asked for, by label (`instance_for`): | |
| 1092 | − | /// `g1t-2core` or `g1t-4core`; absent, the standard one. | |
| 1092 | + | /// `g1t-2core` or `g1t-4core`, which is also what `ubuntu-latest` | |
| 1093 | + | /// gets in a public repository; absent, the standard one. | |
| 1093 | 1094 | #[serde(default)] | |
| 1094 | 1095 | pub instance: Option<String>, | |
| 1095 | 1096 | /// Whether the job may be given brokered secrets, so the runner sends | |
| ⋯ | |||
| 1116 | 1117 | /// 16 GB of disk), `g1t-2core` is kept as a name of it so workflows that | |
| 1117 | 1118 | /// ask for it keep working, and `g1t-4core` is standard-4 (4 vCPU, 12 GiB, | |
| 1118 | 1119 | /// 20 GB). | |
| 1120 | + | /// | |
| 1121 | + | /// `ubuntu-latest` and every other label not named here follow GitHub's | |
| 1122 | + | /// hosted runners ([`instance_for`]): in a public repository the 4-core | |
| 1123 | + | /// machine (GitHub's is 4 vCPU and 16 GB; 12 GiB is the most a Containers | |
| 1124 | + | /// instance has without a raised limit), in a private one the standard | |
| 1125 | + | /// machine (GitHub's is 2 vCPU and 8 GB). | |
| 1119 | 1126 | #[derive(Clone, Copy, Debug, PartialEq)] | |
| 1120 | 1127 | pub struct InstanceType { | |
| 1121 | 1128 | /// The `runs-on` label, or `standard` for the default. | |
| ⋯ | |||
| 1154 | 1161 | } | |
| 1155 | 1162 | } | |
| 1156 | 1163 | ||
| 1157 | − | /// The default: what `ubuntu-latest` and every other hosted label get. | |
| 1164 | + | /// The default in a private repository: what `ubuntu-latest` and every | |
| 1165 | + | /// other hosted label get there, as on GitHub. Checks and the merge queue | |
| 1166 | + | /// run on it too. | |
| 1158 | 1167 | pub const STANDARD_INSTANCE: InstanceType = InstanceType { | |
| 1159 | 1168 | label: "standard", | |
| 1160 | 1169 | machine: "2-core", | |
| ⋯ | |||
| 1207 | 1216 | if seconds <= 0 { 0 } else { (seconds + 59) / 60 } | |
| 1208 | 1217 | } | |
| 1209 | 1218 | ||
| 1219 | + | /// The label of the machine `ubuntu-latest` gets in a public repository: | |
| 1220 | + | /// GitHub gives public repositories 4 vCPU. | |
| 1221 | + | pub const PUBLIC_DEFAULT_LABEL: &str = "g1t-4core"; | |
| 1222 | + | ||
| 1210 | 1223 | /// The machine a job's `runs-on` labels ask for: the dearest named (the | |
| 1211 | − | /// later row when two cost the same), or the standard one. Labels compare | |
| 1212 | − | /// without regard to case. | |
| 1213 | − | pub fn instance_for(labels: &[String]) -> InstanceType { | |
| 1224 | + | /// later row when two cost the same). With none named (`ubuntu-latest`, | |
| 1225 | + | /// say), what GitHub gives: the 4-core machine in a public repository, | |
| 1226 | + | /// the standard one in a private one. Labels compare without regard to | |
| 1227 | + | /// case. | |
| 1228 | + | pub fn instance_for(labels: &[String], public: bool) -> InstanceType { | |
| 1214 | 1229 | INSTANCE_TYPES | |
| 1215 | 1230 | .iter() | |
| 1216 | 1231 | .filter(|instance| instance.label != STANDARD_INSTANCE.label && labels.iter().any(|label| label.trim().eq_ignore_ascii_case(instance.label))) | |
| 1217 | 1232 | .max_by_key(|instance| instance.micros_per_minute) | |
| 1218 | 1233 | .copied() | |
| 1219 | − | .unwrap_or(STANDARD_INSTANCE) | |
| 1234 | + | .unwrap_or_else(|| if public { instance_named(PUBLIC_DEFAULT_LABEL).unwrap_or(STANDARD_INSTANCE) } else { STANDARD_INSTANCE }) | |
| 1220 | 1235 | } | |
| 1221 | 1236 | ||
| 1222 | 1237 | /// An instance type by its label, if it is one. | |
| ⋯ | |||
| 1717 | 1732 | ||
| 1718 | 1733 | #[test] | |
| 1719 | 1734 | fn runs_on_picks_the_machine() { | |
| 1720 | − | assert_eq!(instance_for(&labels(&["ubuntu-latest"])), STANDARD_INSTANCE); | |
| 1721 | − | assert_eq!(instance_for(&labels(&[])).label, "standard"); | |
| 1722 | − | assert_eq!(instance_for(&labels(&["g1t-4core"])).label, "g1t-4core"); | |
| 1723 | − | assert_eq!(instance_for(&labels(&["ubuntu-latest", "G1T-2Core"])).label, "g1t-2core"); | |
| 1735 | + | // As on GitHub: ubuntu-latest is 2 vCPU in a private repository, 4 in a public one. | |
| 1736 | + | assert_eq!(instance_for(&labels(&["ubuntu-latest"]), false), STANDARD_INSTANCE); | |
| 1737 | + | assert_eq!(instance_for(&labels(&["ubuntu-latest"]), true).machine, "4-core"); | |
| 1738 | + | assert_eq!(instance_for(&labels(&["ubuntu-24.04"]), true).vcpu, 4.0); | |
| 1739 | + | assert_eq!(instance_for(&labels(&[]), false).label, "standard"); | |
| 1740 | + | assert_eq!(instance_for(&labels(&[]), true).label, PUBLIC_DEFAULT_LABEL); | |
| 1741 | + | // A machine asked for by name is that machine, public or not. | |
| 1742 | + | assert_eq!(instance_for(&labels(&["g1t-4core"]), false).label, "g1t-4core"); | |
| 1743 | + | assert_eq!(instance_for(&labels(&["ubuntu-latest", "G1T-2Core"]), false).label, "g1t-2core"); | |
| 1744 | + | assert_eq!(instance_for(&labels(&["ubuntu-latest", "g1t-2core"]), true).machine, "2-core"); | |
| 1724 | 1745 | // Both named: the later one. | |
| 1725 | − | assert_eq!(instance_for(&labels(&["g1t-2core", "g1t-4core"])).label, "g1t-4core"); | |
| 1746 | + | assert_eq!(instance_for(&labels(&["g1t-2core", "g1t-4core"]), false).label, "g1t-4core"); | |
| 1726 | 1747 | assert_eq!(instance_named("g1t-2core").map(|i| i.vcpu), Some(2.0)); | |
| 1727 | 1748 | assert_eq!(instance_named("g1t-4core").map(|i| i.vcpu), Some(4.0)); | |
| 1728 | 1749 | assert_eq!(instance_named("standard"), Some(STANDARD_INSTANCE)); | |
| 41 | 41 | { label: "g1t-4core", machine: "4-core", sandboxClass: "Sandbox4Core", container: "standard-4", vcpu: 4, memoryGib: 12, diskGb: 20, microsPerMinute: 7000 }, | |
| 42 | 42 | ]; | |
| 43 | 43 | ||
| 44 | − | /** The default: what `ubuntu-latest` and every other hosted label get. */ | |
| 44 | + | /** The default in a private repository: what `ubuntu-latest` and every other hosted label get there, as on GitHub (`instance_for` in crates/contracts/src/actions.rs gives a public repository `g1t-4core`). */ | |
| 45 | 45 | export const STANDARD_INSTANCE: InstanceType = INSTANCE_TYPES[0]!; | |
| 46 | 46 | ||
| 47 | 47 | /** An instance type by its label; the standard one for anything else. */ |
| 505 | 505 | env.filter(|env| env.deploys) | |
| 506 | 506 | } | |
| 507 | 507 | ||
| 508 | + | /// Whether a run's repository was public when the run was made, from its | |
| 509 | + | /// event's `repository.private`: private when the event doesn't say. | |
| 510 | + | fn run_is_public(run: &RunRow) -> bool { | |
| 511 | + | run.info().event.get("repository").and_then(|repository| repository.get("private")).and_then(Value::as_bool) == Some(false) | |
| 512 | + | } | |
| 513 | + | ||
| 508 | 514 | /// The machine a job's `runs-on` asks for (`instance_for`), for the | |
| 509 | − | /// runner: none for the standard one. | |
| 515 | + | /// runner: none for the standard one. `ubuntu-latest` follows GitHub: the | |
| 516 | + | /// 4-core machine in a public repository. Billing charges the job as the | |
| 517 | + | /// machine named here, so what it ran on is what it pays for. | |
| 510 | 518 | fn start_instance(run: &RunRow, job: &JobRow) -> Option<String> { | |
| 511 | 519 | let spec = match job.callee() { | |
| 512 | 520 | Some((_, spec, _)) => spec, | |
| ⋯ | |||
| 526 | 534 | }, | |
| 527 | 535 | _ => Vec::new(), | |
| 528 | 536 | }; | |
| 529 | − | let instance = g1t_contracts::actions::instance_for(&labels); | |
| 537 | + | let instance = g1t_contracts::actions::instance_for(&labels, run_is_public(run)); | |
| 530 | 538 | (instance != g1t_contracts::actions::STANDARD_INSTANCE).then(|| instance.label.to_owned()) | |
| 531 | 539 | } | |
| 532 | 540 | ||
| ⋯ | |||
| 3253 | 3261 | assert_eq!(runner["name"], "g1t"); | |
| 3254 | 3262 | } | |
| 3255 | 3263 | } | |
| 3264 | + | ||
| 3265 | + | #[cfg(test)] | |
| 3266 | + | mod machines { | |
| 3267 | + | use serde_json::json; | |
| 3268 | + | ||
| 3269 | + | use super::{RunInfo, RunRow, run_is_public}; | |
| 3270 | + | ||
| 3271 | + | fn run(event: serde_json::Value) -> RunRow { | |
| 3272 | + | let info = serde_json::to_string(&RunInfo { repository: "acme/web".into(), event, ..RunInfo::default() }).unwrap(); | |
| 3273 | + | serde_json::from_value(json!({ | |
| 3274 | + | "id": "run_1", "workflow_id": "wf_1", "repo_id": "rep_1", "repo": "acme/web", "path": ".g1t/workflows/ci.yml", "name": "CI", | |
| 3275 | + | "title": "CI", "number": 1, "attempt": 1, "event": "push", "git_ref": "refs/heads/main", "sha": "abc", "status": "queued", | |
| 3276 | + | "source": "", "info": info, "inputs": "{}", "trusted": 1, "created_at": "2026-10-18T00:00:00Z", | |
| 3277 | + | })) | |
| 3278 | + | .unwrap() | |
| 3279 | + | } | |
| 3280 | + | ||
| 3281 | + | #[test] | |
| 3282 | + | fn a_runs_repository_is_public_only_when_its_event_says_so() { | |
| 3283 | + | assert!(run_is_public(&run(json!({ "repository": { "private": false } })))); | |
| 3284 | + | assert!(!run_is_public(&run(json!({ "repository": { "private": true } })))); | |
| 3285 | + | // An event that doesn't say is taken as private: the standard machine. | |
| 3286 | + | assert!(!run_is_public(&run(json!({})))); | |
| 3287 | + | } | |
| 3288 | + | } | |
Comments
Sign in to comment.