vole
Clone
Read only, no credentials:
https://rodent.software/git/vole.git
Write over SSH with a registered key:
git clone ssh://git@0.0.0.0:2222/vole.git
A free and open-source forge that lives in your Git repo.
Let a job declare the network it needs
d79580247b8f33f7ffdf2f05847733cad12c187a · Keenan Nemetz · 6d ago · parent 4ac42680
Let a job declare the network it needs A runner's --container-network decided the network for every job it took, so a machine that could reach the network gave it to builds that never asked, and a job that did need one could be handed to a machine that had none. The docs said "add bridge only when a job needs it", which no workflow could express. A job now says `network: none` or `network: bridge`. The request is pinned with the run, and the lease only hands a bridge job to a runner that advertises the capability, so a job waits for a capable machine instead of failing on one that never could. The runner applies the job's request to its container, which means a bridge runner can still withhold the network from a job that asked for none. A job that declares nothing keeps the runner's own setting, which is what every workflow meant before this field existed. The local run applies the same rule and refuses a bridge job when it was started without one, so `vole workflow run` either matches CI or says what to change on the command line. The workflows page, the run page, and the runner's log line now say what a job asked for and what it ran with. verify.yaml and release.yaml are deliberately not in this change: the instance validates a pushed workflow with the binary it is running, so they can declare the field once that binary knows it. Closes #159.
docs/runner.md +28−5
| @@ -98,8 +98,10 @@ instance or kept on the runner, so each of those channels has a bound: | |||
| 98 | 98 | branch cannot seed the cache another contributor's branch builds from. A | |
| 99 | 99 | protected branch never shares a cache with a contributor branch. | |
| 100 | 100 | - **Container.** `--container-memory`, `--container-cpus` and `--container-pids` | |
| 101 | pass the runtime's own limits; `--container-network none` (the default) means a | ||
| 102 | step with no network. The runtime's storage driver, not Vole, is what bounds the | ||
| 101 | pass the runtime's own limits. `--container-network` is what this machine can | ||
| 102 | offer — `none` (the default) or `bridge` — and a job's own `network:` decides what | ||
| 103 | it gets, so a bridge runner can still run a job that asks for none. See | ||
| 104 | [Network](#network). The runtime's storage driver, not Vole, is what bounds the | ||
| 103 | 105 | container's writable layer, so size it on the host. | |
| 104 | 106 | ||
| 105 | 107 | ## Images and image builds | |
| @@ -114,8 +116,9 @@ was queued. It renders the definition to a Dockerfile, builds it with the | |||
| 114 | 116 | configured OCI runtime, and tags the result by the definition digest. A later job | |
| 115 | 117 | with the same definition reuses that tag. | |
| 116 | 118 | ||
| 117 | The image build uses the runtime's own default network. `--container-network` | ||
| 118 | applies to the job container, not to the image build. | ||
| 119 | The image build uses the runtime's own default network, so a base image and its | ||
| 120 | `run:` commands can fetch packages. The job's own network request applies to the job | ||
| 121 | container, not to the build. | ||
| 119 | 122 | ||
| 120 | 123 | An image definition is code. Its build runs on the runner through the OCI | |
| 121 | 124 | builder, so approving a contributor tip that changes a definition also approves | |
| @@ -131,7 +134,8 @@ one. | |||
| 131 | 134 | ||
| 132 | 135 | Container defaults are intentionally closed: | |
| 133 | 136 | ||
| 134 | - no network (`--container-network none`); add `bridge` only when a job needs it; | ||
| 137 | - no network unless the job asks for one with `network: bridge`, or the runner was | ||
| 138 | started with it and the job says nothing; | ||
| 135 | 139 | - no capabilities, `no-new-privileges`, and no host mounts except the job tree; | |
| 136 | 140 | - no Docker socket, SSH agent, or host environment; | |
| 137 | 141 | - `/tmp` is a writable, executable tmpfs (compilers write their output there and | |
| @@ -190,6 +194,25 @@ the runtime's mapping (container root is the runner user) or use a runtime with | |||
| 190 | 194 | The run record names the image, the definition digest when there is one, and the | |
| 191 | 195 | runtime, so the run page says what actually executed. | |
| 192 | 196 | ||
| 197 | ## Network | ||
| 198 | |||
| 199 | `--container-network` says what this machine can give a job: `none` (the default) or | ||
| 200 | `bridge`. It is a capability, not a setting for every job. The job's `network:` | ||
| 201 | decides what the container gets, and the runner applies it: | ||
| 202 | |||
| 203 | | Job says | This runner says `bridge` | This runner says `none` | | ||
| 204 | |---|---|---| | ||
| 205 | | nothing | bridge | none | | ||
| 206 | | `none` | none | none | | ||
| 207 | | `bridge` | bridge | the runner never receives the job | | ||
| 208 | |||
| 209 | So a machine with a network is not a machine that hands it to every build. A job | ||
| 210 | that asks for none is isolated on either kind of runner, and a job that asks for | ||
| 211 | bridge waits in the queue for a runner that advertises one instead of running | ||
| 212 | without the network it needs. The runner advertises this with every lease, the same | ||
| 213 | way it advertises `--label`, and the job's request is pinned with the run when it is | ||
| 214 | queued. | ||
| 215 | |||
| 193 | 216 | ## Caching | |
| 194 | 217 | ||
| 195 | 218 | A job starts with an empty container, so without help every run downloads its | |