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. | |