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 network it needs
d79580247b8f33f7ffdf2f05847733cad12c187a · Keenan Nemetz · 6d 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/workflow_test.go +45
| @@ -424,6 +424,51 @@ jobs: | |||
| 424 | 424 | } | |
| 425 | 425 | } | |
| 426 | 426 | ||
| 427 | // network asks for the isolation a job needs. It is one of two values, and a value | ||
| 428 | // no runner could satisfy fails the push rather than leaving the job queued behind an | ||
| 429 | // error nobody sees. A job that says nothing keeps the pre-network behaviour. | ||
| 430 | func TestJobNetworkIsParsedAndValidated(t *testing.T) { | ||
| 431 | t.Parallel() | ||
| 432 | // Saying nothing is not the same as saying none: the runner's own default applies. | ||
| 433 | wf, problems := Parse("ci.yaml", `version: 1 | ||
| 434 | jobs: | ||
| 435 | build: | ||
| 436 | image: alpine:3.22 | ||
| 437 | steps: | ||
| 438 | - run: true | ||
| 439 | `) | ||
| 440 | if len(problems) > 0 { | ||
| 441 | t.Fatalf("problems: %v", problems) | ||
| 442 | } | ||
| 443 | if got := wf.Jobs[0].Network; got != "" { | ||
| 444 | t.Fatalf("network = %q, want empty for a job that does not ask", got) | ||
| 445 | } | ||
| 446 | |||
| 447 | for _, want := range []string{NetworkNone, NetworkBridge} { | ||
| 448 | wf, problems := Parse("ci.yaml", "version: 1\njobs:\n build:\n network: "+want+"\n"+ | ||
| 449 | " image: alpine:3.22\n steps:\n - run: true\n") | ||
| 450 | if len(problems) > 0 { | ||
| 451 | t.Fatalf("network %q: %v", want, problems) | ||
| 452 | } | ||
| 453 | if got := wf.Jobs[0].Network; got != want { | ||
| 454 | t.Fatalf("network = %q, want %q", got, want) | ||
| 455 | } | ||
| 456 | } | ||
| 457 | |||
| 458 | // Anything else is refused, and the message names the field so a typo is findable. | ||
| 459 | for _, bad := range []string{"Bridge", "host", "docker", "on"} { | ||
| 460 | src := "version: 1\njobs:\n build:\n network: " + bad + "\n" + | ||
| 461 | " image: alpine:3.22\n steps:\n - run: true\n" | ||
| 462 | _, problems := Parse("ci.yaml", src) | ||
| 463 | if len(problems) == 0 { | ||
| 464 | t.Fatalf("network %s was accepted", bad) | ||
| 465 | } | ||
| 466 | if !strings.Contains(strings.Join(problems, " "), "network") { | ||
| 467 | t.Fatalf("the problem does not name network: %v", problems) | ||
| 468 | } | ||
| 469 | } | ||
| 470 | } | ||
| 471 | |||
| 427 | 472 | // TestJobEnvironment: a job's env is how it opts in to things the runner provides | |
| 428 | 473 | // but does not decide, such as the cache at /cache. It is validated like the image | |
| 429 | 474 | // definition's env, and it may not replace what the sandbox itself sets. | |