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.
|
70b70264
Keenan Nemetz
15d ago
|
1 | # Image reference |
| 2 | ||
| 3 | This page lists every field Vole accepts in a `.vole/images/*.yaml` definition, | |
| 4 | with its defaults and limits. For how workflows reference these definitions, see | |
| 5 | [Workflow reference § image-file](workflow-reference.md#image-file). For the | |
| 6 | task-oriented introduction, see [Workflows § Images](../workflows.md#images). | |
| 7 | ||
| 8 | Unknown keys are rejected. Every definition under `.vole/images/` is parsed when a | |
| 9 | workflow is pushed, even if no job references it, so a broken definition fails the | |
| 10 | push that introduced it. | |
| 11 | ||
| 12 | ## File location and limits | |
| 13 | ||
| 14 | | Item | Value | | |
| 15 | |---|---| | |
| 16 | | Location | Directly under `.vole/images/`; subdirectories are rejected | | |
| 17 | | Extension | `.yaml` or `.yml` | | |
| 18 | | Reference | `image-file: <name>`; the extension is optional in the reference | | |
| 19 | | Maximum file size | 64 KiB | | |
| 20 | | Schema version | `1` | | |
| 21 | | Maximum `env` entries | 64 | | |
| 22 | | Maximum `run` entries | 100 | | |
| 23 | | Maximum single command size | 64 KiB after trimming | | |
| 24 | | Maximum env value size | 8 KiB | | |
| 25 | ||
| 26 | A definition is named by its filename without the extension: | |
| 27 | ||
| 28 | ```text | |
| 29 | .vole/images/go.yaml -> image-file: go | |
| 30 | .vole/images/go.yml -> image-file: go | |
| 31 | .vole/images/release.yaml -> image-file: release | |
| 32 | ``` | |
| 33 | ||
| 34 | Names are 1–64 characters: lowercase letters, digits, `.`, `_` and `-`, starting | |
| 35 | with a letter or digit. Two files for one name (`go.yaml` and `go.yml`) are | |
| 36 | rejected as ambiguous. | |
| 37 | ||
| 38 | ## Top-level fields | |
| 39 | ||
| 40 | | Field | Type | Required | Default | Meaning | | |
| 41 | |---|---|---|---|---| | |
| 42 | | `version` | integer | yes | — | Schema version. Must be `1`. | | |
|
6bfc4294
Keenan Nemetz
12d ago
|
43 | | `from` | string | yes | — | Base OCI image. A tag works; a digest (`image@sha256:...`) makes the build reproducible. | |
|
70b70264
Keenan Nemetz
15d ago
|
44 | | `env` | mapping | no | none | Environment variables set in the image. | |
| 45 | | `run` | list of strings | no | none | Build commands, applied in order. | | |
| 46 | ||
| 47 | ```yaml | |
| 48 | version: 1 | |
| 49 | from: golang:1.27-bookworm | |
| 50 | env: | |
| 51 | CGO_ENABLED: "0" | |
| 52 | run: | |
| 53 | - apt-get update && apt-get install -y --no-install-recommends jq | |
| 54 | - go install example.com/tool@v1.2.3 | |
| 55 | ``` | |
| 56 | ||
| 57 | ### `version` | |
| 58 | ||
| 59 | The only supported value is `1`: | |
| 60 | ||
| 61 | ```yaml | |
| 62 | version: 1 | |
| 63 | ``` | |
| 64 | ||
| 65 | ### `from` | |
| 66 | ||
| 67 | The base image for the definition. It uses the same image-reference validation as | |
| 68 | `image:` in a workflow: | |
| 69 | ||
| 70 | - Must be non-empty and at most 255 bytes. | |
| 71 | - Must start with a letter or digit. | |
| 72 | - May contain letters, digits, `.`, `_`, `/`, `:`, `@`, `+`, `~` and `-`. | |
| 73 | - A leading `-`, `..`, `://`, or a `file:` prefix is rejected. | |
| 74 | ||
| 75 | ```yaml | |
| 76 | from: golang:1.27-bookworm@sha256:... | |
| 77 | ``` | |
| 78 | ||
| 79 | Pin the base image by digest when reproducibility matters. A mutable tag can | |
| 80 | change what a later build produces. | |
| 81 | ||
| 82 | ### `env` | |
| 83 | ||
| 84 | A mapping of environment variable names to values, applied to the image: | |
| 85 | ||
| 86 | ```yaml | |
| 87 | env: | |
| 88 | CGO_ENABLED: "0" | |
| 89 | MESSAGE: "hello world" | |
| 90 | ``` | |
| 91 | ||
| 92 | | Constraint | Value | | |
| 93 | |---|---| | |
| 94 | | Maximum entries | 64 | | |
| 95 | | Key syntax | `^[A-Za-z_][A-Za-z0-9_]*$` | | |
| 96 | | Maximum value size | 8 KiB | | |
| 97 | | Line endings | Values must be one line; `CR`/`LF` are rejected | | |
| 98 | ||
| 99 | Keys are emitted in sorted order, so the generated build is deterministic. | |
| 100 | ||
| 101 | ### `run` | |
| 102 | ||
| 103 | Build commands, run in order after `from` and `env`: | |
| 104 | ||
| 105 | ```yaml | |
| 106 | run: | |
| 107 | - apt-get update && apt-get install -y --no-install-recommends jq | |
| 108 | - go install example.com/tool@v1.2.3 | |
| 109 | ``` | |
| 110 | ||
| 111 | | Constraint | Value | | |
| 112 | |---|---| | |
| 113 | | Maximum entries | 100 | | |
| 114 | | Empty commands | Rejected | | |
| 115 | | Maximum size | 64 KiB after trimming | | |
| 116 | | Line endings | One line; `CR`/`LF` are rejected | | |
| 117 | | Shell | Each command runs through `/bin/sh -c` | | |
| 118 | ||
| 119 | Chain commands with `&&` rather than using multiple lines: | |
| 120 | ||
| 121 | ```yaml | |
| 122 | run: | |
| 123 | - apt-get update && apt-get install -y --no-install-recommends git make | |
| 124 | ``` | |
| 125 | ||
| 126 | `run` entries are rendered as Dockerfile exec-form instructions: | |
| 127 | ||
| 128 | ```dockerfile | |
| 129 | RUN ["/bin/sh", "-c", "apt-get update && apt-get install -y --no-install-recommends git make"] | |
| 130 | ``` | |
| 131 | ||
| 132 | That keeps the first word of a command from being interpreted as a Dockerfile | |
| 133 | build flag such as `--network=host`. | |
| 134 | ||
| 135 | ## What the definition can and cannot express | |
| 136 | ||
| 137 | The format deliberately supports only `from`, `env`, and `run`. It does not | |
| 138 | support: | |
| 139 | ||
| 140 | - `COPY`, `ADD`, or a build context from the repository | |
| 141 | - multi-stage builds | |
| 142 | - `ARG` | |
| 143 | - `WORKDIR`, `USER`, `ENTRYPOINT`, `CMD`, `HEALTHCHECK`, or `SHELL` | |
| 144 | - build secrets or host mounts | |
| 145 | ||
| 146 | The runner controls the job's working directory (`/workspace`), user, entrypoint, | |
| 147 | and mounts. The image supplies the toolchain and the shell. | |
| 148 | ||
| 149 | The image build uses the runtime's default network. `--container-network` applies | |
| 150 | to the job container, not to the image build. The builder has no host mounts and | |
| 151 | no build secrets from Vole. | |
| 152 | ||
| 153 | ## Execution and caching | |
| 154 | ||
| 155 | The runner renders the definition to a Dockerfile: | |
| 156 | ||
| 157 | ```dockerfile | |
| 158 | FROM <from> | |
| 159 | ENV KEY="value" | |
| 160 | RUN ["/bin/sh", "-c", "<command>"] | |
| 161 | ... | |
| 162 | ``` | |
| 163 | ||
| 164 | It builds that image with the configured OCI runtime and tags it by the SHA-256 of | |
| 165 | the exact definition file. A later job with the same definition reuses the tag. | |
| 166 | `--container-pull always` rebuilds a custom image even when the tag exists. | |
| 167 | ||
| 168 | An image definition is code: its build runs on the runner through the OCI builder, | |
| 169 | so approving a contributor tip that changes a definition also approves that build. | |
| 170 | Use rootless Podman where possible. | |
| 171 | ||
| 172 | The resulting job image must contain `/bin/sh` and a POSIX `sleep`, because Vole | |
| 173 | runs each step with `/bin/sh -c` and keeps the container alive between steps. If | |
| 174 | `run` is non-empty, the base image used for `from` must also provide `/bin/sh`, | |
| 175 | because build commands are rendered that way. | |
| 176 | ||
|
3b39a84c
Keenan Nemetz
14d ago
|
177 | Nothing a job writes inside the container survives the run, except what it puts in |
| 178 | the cache: the runner mounts a directory at `/cache` and sets no variables for it, | |
| 179 | so `env` is where a definition opts in. Point the toolchain's own variables at it | |
| 180 | and its downloads are reused between jobs: | |
| 181 | ||
| 182 | ```yaml | |
| 183 | env: | |
| 184 | GOMODCACHE: "/cache/go-mod" | |
| 185 | GOCACHE: "/cache/go-build" | |
| 186 | ``` | |
| 187 | ||
| 188 | A job may also set its own `env`, which is applied after the image's and wins; it | |
| 189 | is the way to point at `/cache` when the image is a registry reference rather than a | |
| 190 | definition. `/tmp` is a memory-backed tmpfs and `HOME` points there for everything | |
| 191 | else; the XDG convention (`XDG_CACHE_HOME: /cache/xdg`) covers tools that follow it. | |
| 192 | See [Runners § Caching](../runner.md#caching). | |
| 193 | Check that anything calling `getpwuid` has a passwd entry for the uid the job runs | |
| 194 | as — see [Runners § How a job runs](../runner.md#how-a-job-runs). | |
| 195 | ||
|
70b70264
Keenan Nemetz
15d ago
|
196 | ## Validation and pinning |
| 197 | ||
| 198 | - `version` must be `1`. | |
| 199 | - Unknown keys are errors. | |
| 200 | - The definition must live directly under `.vole/images/`, not in a subdirectory. | |
| 201 | - Both `.yaml` and `.yml` are accepted, but not two files for the same name. | |
| 202 | - Every definition is parsed at push time, even if unreferenced. | |
| 203 | - The exact definition text and its digest are pinned with each run. | |
| 204 | - `--container-pull always` rebuilds instead of reusing the digest tag. | |
| 205 | ||
| 206 | ## Complete example | |
| 207 | ||
| 208 | ```yaml | |
| 209 | # .vole/images/release-toolchain.yaml | |
| 210 | version: 1 | |
| 211 | from: golang:1.27-bookworm@sha256:... | |
| 212 | env: | |
| 213 | CGO_ENABLED: "0" | |
| 214 | SOURCE_DATE_EPOCH: "0" | |
| 215 | run: | |
| 216 | - apt-get update && apt-get install -y --no-install-recommends git make jq | |
| 217 | - go install example.com/release-tool@v1.4.0 | |
| 218 | ``` | |
| 219 | ||
| 220 | ```yaml | |
| 221 | # .vole/workflows/release.yaml | |
| 222 | version: 1 | |
| 223 | on: | |
| 224 | push: | |
| 225 | tags: ["v*"] | |
| 226 | jobs: | |
| 227 | release: | |
| 228 | runs-on: [linux, amd64] | |
| 229 | image-file: release-toolchain | |
| 230 | steps: | |
| 231 | - name: build | |
| 232 | run: make dist | |
| 233 | artifacts: | |
| 234 | - path: dist/* | |
| 235 | release: true | |
| 236 | ``` |