Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

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.

Require an image for every CI job

70b702649b875d841e294f5a085ca784d43e5bc4 · Keenan Nemetz · 15d ago · parent 7da9111e

Require an image for every CI job

Runners now require an OCI runtime, and each workflow job must name either a registry image or an image-file definition under .vole/images. Definitions are validated at push time, pinned with the run, built locally by the runner, and cached by definition digest. Records and the run page carry the image and definition digest.

Split the CI guide into workflows and runners, and add reference pages for workflow and image definition fields.

docs/reference/image-reference.md +217

@@ -0,0 +1,217 @@
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`. |
43 | `from` | string | yes | — | Base OCI image. |
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
177 ## Validation and pinning
178
179 - `version` must be `1`.
180 - Unknown keys are errors.
181 - The definition must live directly under `.vole/images/`, not in a subdirectory.
182 - Both `.yaml` and `.yml` are accepted, but not two files for the same name.
183 - Every definition is parsed at push time, even if unreferenced.
184 - The exact definition text and its digest are pinned with each run.
185 - `--container-pull always` rebuilds instead of reusing the digest tag.
186
187 ## Complete example
188
189 ```yaml
190 # .vole/images/release-toolchain.yaml
191 version: 1
192 from: golang:1.27-bookworm@sha256:...
193 env:
194 CGO_ENABLED: "0"
195 SOURCE_DATE_EPOCH: "0"
196 run:
197 - apt-get update && apt-get install -y --no-install-recommends git make jq
198 - go install example.com/release-tool@v1.4.0
199 ```
200
201 ```yaml
202 # .vole/workflows/release.yaml
203 version: 1
204 on:
205 push:
206 tags: ["v*"]
207 jobs:
208 release:
209 runs-on: [linux, amd64]
210 image-file: release-toolchain
211 steps:
212 - name: build
213 run: make dist
214 artifacts:
215 - path: dist/*
216 release: true
217 ```