vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Let a job declare the network it needs
d79580247b8f33f7ffdf2f05847733cad12c187a · Keenan Nemetz · 7d ago · parent 4ac42680
Let a job declare the network it needs A runner's --container-network decided the network for every job it took, so a machine that could reach the network gave it to builds that never asked, and a job that did need one could be handed to a machine that had none. The docs said "add bridge only when a job needs it", which no workflow could express. A job now says `network: none` or `network: bridge`. The request is pinned with the run, and the lease only hands a bridge job to a runner that advertises the capability, so a job waits for a capable machine instead of failing on one that never could. The runner applies the job's request to its container, which means a bridge runner can still withhold the network from a job that asked for none. A job that declares nothing keeps the runner's own setting, which is what every workflow meant before this field existed. The local run applies the same rule and refuses a bridge job when it was started without one, so `vole workflow run` either matches CI or says what to change on the command line. The workflows page, the run page, and the runner's log line now say what a job asked for and what it ran with. verify.yaml and release.yaml are deliberately not in this change: the instance validates a pushed workflow with the binary it is running, so they can declare the field once that binary knows it. Closes #159.
ci/agent/sandbox.go +23
| @@ -24,6 +24,7 @@ import ( | |||
| 24 | 24 | "strings" | |
| 25 | 25 | "syscall" | |
| 26 | 26 | ||
| 27 | "github.com/rodent-software/vole/ci" | ||
| 27 | 28 | "github.com/rodent-software/vole/ci/container" | |
| 28 | 29 | ) | |
| 29 | 30 | ||
| @@ -63,6 +64,28 @@ func containerOptions(opts Options, cache string) (runnerBox, error) { | |||
| 63 | 64 | }, nil | |
| 64 | 65 | } | |
| 65 | 66 | ||
| 67 | // networkFor is the container settings for one job. A job that names a network gets | ||
| 68 | // exactly that: `network: none` withholds the network even on a runner that could | ||
| 69 | // give one, and `network: bridge` is refused here on a runner that cannot, because | ||
| 70 | // running it without the network it asked for would fail later and less clearly than | ||
| 71 | // saying so at the start. A job that names nothing gets the runner's own default, | ||
| 72 | // which is what every workflow meant before the field existed. | ||
| 73 | func (b runnerBox) networkFor(job *runnerJob) (container.Config, error) { | ||
| 74 | cfg := b.Config | ||
| 75 | if job.Network == "" { | ||
| 76 | return cfg, nil | ||
| 77 | } | ||
| 78 | if err := ci.ValidNetwork(job.Network); err != nil { | ||
| 79 | return cfg, fmt.Errorf("job %s: %w", job.ID, err) | ||
| 80 | } | ||
| 81 | if job.Network == ci.NetworkBridge && cfg.Network != ci.NetworkBridge { | ||
| 82 | return cfg, fmt.Errorf("job %s asks for network: bridge but this runner was started with "+ | ||
| 83 | "--container-network %s, so it cannot provide one", job.ID, cfg.Network) | ||
| 84 | } | ||
| 85 | cfg.Network = job.Network | ||
| 86 | return cfg, nil | ||
| 87 | } | ||
| 88 | |||
| 66 | 89 | // labels are how a container says which runner and run it belongs to. | |
| 67 | 90 | func (b runnerBox) labels(job *runnerJob) map[string]string { | |
| 68 | 91 | return map[string]string{ | |