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.

Let a job declare the secrets it reads

4ac42680eca3181efc70099f65d6e2c9f45e9dd2 · Keenan Nemetz · 7d ago · parent 0c40a1d3

Let a job declare the secrets it reads

A job now names the repository secrets it needs, and the instance delivers
those values when the job is leased. The workflow carries names only, so the
queue row, the metadata branch and the run record never hold a value: the
record names the version of each value the job ran with, which is what makes a
result say which one it used.

    jobs:
      deploy:
        image: alpine:3.22
        secrets: [DEPLOY_KEY]

A name the repository has no value for is not a failure. The variable is left
unset rather than empty, the step's shell can tell the difference, and the
runner writes one line to the log saying which value was missing. That keeps
an optional secret optional and leaves "required" with the step, where someone
can write the message: ${DEPLOY_KEY:?DEPLOY_KEY is not set}.

The value reaches the step through a file the runner writes into the run
directory it already mounts at /run/vole for an event payload, and each step is
prefixed with `. /run/vole/secrets.env`. That is deliberately not an env-file
argument to the runtime, which cannot carry a line break, and not --env, which
puts the value in the process's argument list where every process on the host
can read it. A local `vole workflow run` writes the same file, reading the
values with the developer's own credential, and says which ones it could not
read.

The name rule and the 128 KiB limit moved to ci, next to the environment rules
they mirror, so the store refuses a name no workflow could declare and the
workflow refuses a name no process environment could carry.

Slice 2 of #154.

docs/workflows.md +56−1

@@ -250,6 +250,58 @@ push time. Everything else is the workflow's. This is how a job opts in to the
250 250 cache the runner mounts at `/cache`, and how a job using a registry image gets it
251 251 without an image definition.
252 252
253 ## Secrets
254
255 A repository can keep the values its jobs need out of its code: a deploy key, a
256 registry token, a staging password. They are stored encrypted on the instance and
257 set by the people who maintain the repository:
258
259 ```sh
260 vole secret set demo DEPLOY_TOKEN # the value is read from stdin
261 vole secret list demo
262 ```
263
264 A job asks for one by name, and the name is also the variable a step reads:
265
266 ```yaml
267 jobs:
268 deploy:
269 image: alpine:3.22
270 secrets: [DEPLOY_TOKEN]
271 steps:
272 - run: ./deploy.sh # reads $DEPLOY_TOKEN
273 ```
274
275 Declaring the name is what makes it present. A job that does not list a secret
276 does not receive it, so a repository's values are never ambient in its steps.
277
278 A name the repository has no value for does not fail the job: the variable is left
279 **unset**, not empty, and the log says which one it went without.
280
281 ```
282 vole: DEPLOY_TOKEN is not set for this repository, so this job runs without it.
283 ```
284
285 That is what makes an optional secret optional. When one is required, the step says
286 so, and the reader gets the message its author wrote:
287
288 ```yaml
289 steps:
290 - run: |
291 : "${DEPLOY_TOKEN:?DEPLOY_TOKEN is not set}"
292 ./deploy.sh
293 ```
294
295 A value is never copied into the workflow, into a container argument, or into a run
296 record: the record names the version of each value the job ran with, so a result
297 says which one it used. Values reach a step only as environment variables, and one
298 is at most 128 KiB, which is as much as one environment variable can carry.
299
300 Anybody who can run a runner for a repository can read the values its jobs declare,
301 because that is the machine they arrive on. Secrets belong to the accounts that
302 approve work and allow CI, and the settings page that lists them never shows one.
303 See [Accounts and access](accounts-and-access.md#roles).
304
253 305 ## Contributor branches
254 306
255 307 A push to `refs/heads/contrib/<name>/...` queues no work by itself. Those
@@ -314,7 +366,10 @@ variables: the image's `env:` (or the step) decides whether `/cache` is used. Se
314 366
315 367 A local run still ignores triggers and `runs-on`, publishes no artifacts,
316 368 and deploys no site. It sets `CI=true` and `VOLE_CI=1`, gives each step its
317 declared timeout, and reads no standard input. Jobs run in declaration order and
369 declared timeout, and reads no standard input. It reads the secrets its jobs declare
370 from the instance the checkout's remote names, with your own credential, so a job
371 that deploys behaves the same here as it does on a runner. Where it cannot — no
372 remote, or no value stored — the variable is unset and the note says which one. Jobs run in declaration order and
318 373 share the working tree, so a later job sees files an earlier one wrote; a pushed
319 374 run queues each job separately, starts it from a clean checkout, and has no
320 375 ordering between jobs. A local run is not an event, so it sets no `VOLE_EVENT*`