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.

Code

392 commit(s) on main

Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys keenan · 26m ago 4b8c9e1d
source preview history blame raw 7.2 KiB

Image reference

This page lists every field Vole accepts in a .vole/images/*.yaml definition, with its defaults and limits. For how workflows reference these definitions, see Workflow reference § image-file. For the task-oriented introduction, see Workflows § Images.

Unknown keys are rejected. Every definition under .vole/images/ is parsed when a workflow is pushed, even if no job references it, so a broken definition fails the push that introduced it.

File location and limits

Item Value
Location Directly under .vole/images/; subdirectories are rejected
Extension .yaml or .yml
Reference image-file: <name>; the extension is optional in the reference
Maximum file size 64 KiB
Schema version 1
Maximum env entries 64
Maximum run entries 100
Maximum single command size 64 KiB after trimming
Maximum env value size 8 KiB

A definition is named by its filename without the extension:

.vole/images/go.yaml      -> image-file: go
.vole/images/go.yml       -> image-file: go
.vole/images/release.yaml -> image-file: release

Names are 1–64 characters: lowercase letters, digits, ., _ and -, starting with a letter or digit. Two files for one name (go.yaml and go.yml) are rejected as ambiguous.

Top-level fields

Field Type Required Default Meaning
version integer yes — Schema version. Must be 1.
from string yes — Base OCI image. A tag works; a digest (image@sha256:...) makes the build reproducible.
env mapping no none Environment variables set in the image.
run list of strings no none Build commands, applied in order.
version: 1
from: golang:1.27-bookworm
env:
  CGO_ENABLED: "0"
run:
  - apt-get update && apt-get install -y --no-install-recommends jq
  - go install example.com/tool@v1.2.3

version

The only supported value is 1:

version: 1

from

The base image for the definition. It uses the same image-reference validation as image: in a workflow:

  • Must be non-empty and at most 255 bytes.
  • Must start with a letter or digit.
  • May contain letters, digits, ., _, /, :, @, +, ~ and -.
  • A leading -, .., ://, or a file: prefix is rejected.
from: golang:1.27-bookworm@sha256:...

Pin the base image by digest when reproducibility matters. A mutable tag can change what a later build produces.

env

A mapping of environment variable names to values, applied to the image:

env:
  CGO_ENABLED: "0"
  MESSAGE: "hello world"
Constraint Value
Maximum entries 64
Key syntax ^[A-Za-z_][A-Za-z0-9_]*$
Maximum value size 8 KiB
Line endings Values must be one line; CR/LF are rejected

Keys are emitted in sorted order, so the generated build is deterministic.

run

Build commands, run in order after from and env:

run:
  - apt-get update && apt-get install -y --no-install-recommends jq
  - go install example.com/tool@v1.2.3
Constraint Value
Maximum entries 100
Empty commands Rejected
Maximum size 64 KiB after trimming
Line endings One line; CR/LF are rejected
Shell Each command runs through /bin/sh -c

Chain commands with && rather than using multiple lines:

run:
  - apt-get update && apt-get install -y --no-install-recommends git make

run entries are rendered as Dockerfile exec-form instructions:

RUN ["/bin/sh", "-c", "apt-get update && apt-get install -y --no-install-recommends git make"]

That keeps the first word of a command from being interpreted as a Dockerfile build flag such as --network=host.

What the definition can and cannot express

The format deliberately supports only from, env, and run. It does not support:

  • COPY, ADD, or a build context from the repository
  • multi-stage builds
  • ARG
  • WORKDIR, USER, ENTRYPOINT, CMD, HEALTHCHECK, or SHELL
  • build secrets or host mounts

The runner controls the job’s working directory (/workspace), user, entrypoint, and mounts. The image supplies the toolchain and the shell.

The image build uses the runtime’s default network. --container-network applies to the job container, not to the image build. The builder has no host mounts and no build secrets from Vole.

Execution and caching

The runner renders the definition to a Dockerfile:

FROM <from>
ENV KEY="value"
RUN ["/bin/sh", "-c", "<command>"]
...

It builds that image with the configured OCI runtime and tags it by the SHA-256 of the exact definition file. A later job with the same definition reuses the tag. --container-pull always rebuilds a custom image even when the tag exists.

An image definition is code: its build runs on the runner through the OCI builder, so approving a contributor tip that changes a definition also approves that build. Use rootless Podman where possible.

The resulting job image must contain /bin/sh and a POSIX sleep, because Vole runs each step with /bin/sh -c and keeps the container alive between steps. If run is non-empty, the base image used for from must also provide /bin/sh, because build commands are rendered that way.

Nothing a job writes inside the container survives the run, except what it puts in the cache: the runner mounts a directory at /cache and sets no variables for it, so env is where a definition opts in. Point the toolchain’s own variables at it and its downloads are reused between jobs:

env:
  GOMODCACHE: "/cache/go-mod"
  GOCACHE: "/cache/go-build"

A job may also set its own env, which is applied after the image’s and wins; it is the way to point at /cache when the image is a registry reference rather than a definition. /tmp is a memory-backed tmpfs and HOME points there for everything else; the XDG convention (XDG_CACHE_HOME: /cache/xdg) covers tools that follow it. See Runners § Caching. Check that anything calling getpwuid has a passwd entry for the uid the job runs as — see Runners § How a job runs.

Validation and pinning

  • version must be 1.
  • Unknown keys are errors.
  • The definition must live directly under .vole/images/, not in a subdirectory.
  • Both .yaml and .yml are accepted, but not two files for the same name.
  • Every definition is parsed at push time, even if unreferenced.
  • The exact definition text and its digest are pinned with each run.
  • --container-pull always rebuilds instead of reusing the digest tag.

Complete example

# .vole/images/release-toolchain.yaml
version: 1
from: golang:1.27-bookworm@sha256:...
env:
  CGO_ENABLED: "0"
  SOURCE_DATE_EPOCH: "0"
run:
  - apt-get update && apt-get install -y --no-install-recommends git make jq
  - go install example.com/release-tool@v1.4.0
# .vole/workflows/release.yaml
version: 1
on:
  push:
    tags: ["v*"]
jobs:
  release:
    runs-on: [linux, amd64]
    image-file: release-toolchain
    steps:
      - name: build
        run: make dist
    artifacts:
      - path: dist/*
        release: true