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 budget its whole run

e7aa0785cb69ace8c1338cc0b924113ea52a7abb · Keenan Nemetz · 6d ago · parent 2bc0ae74

Let a job budget its whole run

A step was capped at an hour and a job was not capped at all, so a job of
several slow steps held a runner for as long as they took, with no number saying
what the job was allowed to spend. And one long step — a session, a soak test, a
slow suite — could not be expressed, because the only clock was per step and its
ceiling was an hour.

A job may now declare `timeout: 2h`: a wall clock its steps share, starting with
the first step. A step that declares its own timeout is cut short when the budget
ends first, and a step that declares none runs with whatever is left, so one long
step needs only the job's number. A budget spent before a step starts stops the
job there instead of running the step on its own cap.

The budget is optional, pinned with the run, and spent by a local run the same
way, so nothing changes for a workflow that does not declare one. The step
ceiling rises from one hour to four to make a single long step possible; the
default stays ten minutes. The image build is not charged to the budget — it has
its own cap, so a slow pull cannot eat the time the steps were given.

A step timeout longer than its job's budget is refused at push time: the budget
would end first, so the step could never use what it asked for.

Closes #161.

# Conflicts:
#	ci/agent/agent.go
#	cmd/vole/workflowrun.go
#	docs/reference/workflow-reference.md

server/ci.go +12−1

@@ -219,6 +219,7 @@ func (s *Server) queue(m *repo.Meta, repoName string, spec queueSpec) int {
219 219 Secrets: job.Secrets,
220 220 Env: job.Env,
221 221 Network: job.Network,
222 Timeout: durationText(job.Timeout),
222 223 ReleaseArtifacts: releasePaths(job),
223 224 PagesPath: site,
224 225 Created: time.Now().UTC(),
@@ -409,11 +410,21 @@ func steps(job ci.Job) []runs.Step {
409 410 for _, st := range job.Steps {
410 411 // The step list is pinned at enqueue time, so editing the workflow after the
411 412 // push cannot change what an already-queued run executes.
412 out = append(out, runs.Step{Name: st.Name, Run: st.Run, Timeout: st.Timeout.String()})
413 out = append(out, runs.Step{Name: st.Name, Run: st.Run, Timeout: durationText(st.Timeout)})
413 414 }
414 415 return out
415 416 }
416 417
418 // durationText is how a pinned timeout is stored: empty when nothing was declared,
419 // otherwise the duration the reader wrote. Empty is what the runner reads as "no
420 // opinion", which is different from a timeout of zero.
421 func durationText(d time.Duration) string {
422 if d <= 0 {
423 return ""
424 }
425 return d.String()
426 }
427
417 428 // artifactPaths pins the declared patterns into the queued run, next to the pinned
418 429 // steps: a workflow edited after the push must not change what an already-queued
419 430 // run publishes.