Commit dcd93804bc

dcd93804bc21a4095956e2d04fd87f66426c7cb1

parent: 24c714dba2

Verified · cmc ci/build: success ci/test: success

cmc <hello@cleberg.net> · 2026-09-28 08:32 UTC

runner: only a loopback runner's podman builds leave -remote

Ref #260

Layout: unified · split

.gitbay/wiki/Users.org +5 −5
@@ -564,11 +564,11 @@ a broken config surfaces as a failed =ci/config= status. Environment:
564=GITBAY_REPO=, 564=GITBAY_REPO=,
565=GITBAY_SHA=, =GITBAY_REF=, =GITBAY_JOB=, =CI=true=, and =GITBAY_SSH=, 565=GITBAY_SHA=, =GITBAY_REF=, =GITBAY_JOB=, =CI=true=, and =GITBAY_SSH=,
566the instance's ssh destination as the build reaches it: on the forge's 566the instance's ssh destination as the build reaches it: on the forge's
567own runner, =git@169.254.1.2=, pasta's address for the host; from a 567own runner, =git@169.254.1.2=, pasta's address for the host, with
568runner elsewhere, the instance's public address (=git@gitbay.org=). 568=:port= when the instance's ssh is not on 22; from any other runner,
569Either carries =:port= when the instance's ssh is not on 22, so use it 569the destination that runner polls (its =-remote=, such as
570as =ssh://$GITBAY_SSH/owner/name.git= or =ssh ssh://$GITBAY_SSH …=, 570=git@gitbay.org=). Use it as =ssh://$GITBAY_SSH/owner/name.git= or
571which work in both forms. A job that 571=ssh ssh://$GITBAY_SSH …=, which work in either form. A job that
572talks back to the instance — a release asset, a comment, a push to a 572talks back to the instance — a release asset, a comment, a push to a
573pages branch — uses =$GITBAY_SSH= with a key it holds as a secret; 573pages branch — uses =$GITBAY_SSH= with a key it holds as a secret;
574the build's container has no key of its own. Two things about that 574the build's container has no key of its own. Two things about that
cmd/gitbay-runner/env_test.go +4 −3
@@ -195,7 +195,7 @@ func TestSplitEnvKeepsMultilineOutOfTheFile(t *testing.T) {
195// address, so a runner polling over loopback gives its podman builds 195// address, so a runner polling over loopback gives its podman builds
196// 169.254.1.2, pasta's address for the host, with the claim's port when 196// 169.254.1.2, pasta's address for the host, with the claim's port when
197// it is not 22, and never the loopback address it polls. Any other runner 197// it is not 22, and never the loopback address it polls. Any other runner
198// passes on the claim's destination, or its own remote without one (#260). 198// passes on its own remote (#260).
199func TestStepEnvCarriesInstanceAddress(t *testing.T) { 199func TestStepEnvCarriesInstanceAddress(t *testing.T) {
200 env := stepEnv(job{}, "/tmp/buildhome", "git@gitbay.org") 200 env := stepEnv(job{}, "/tmp/buildhome", "git@gitbay.org")
201 if !containsEnv(env, "GITBAY_SSH=git@gitbay.org") { 201 if !containsEnv(env, "GITBAY_SSH=git@gitbay.org") {
@@ -211,11 +211,12 @@ func TestStepEnvCarriesInstanceAddress(t *testing.T) {
211 {"forge@::1", isolationPodman, "git@gitbay.org", "forge@169.254.1.2"}, 211 {"forge@::1", isolationPodman, "git@gitbay.org", "forge@169.254.1.2"},
212 {"127.0.0.1", isolationPodman, "git@gitbay.org", "git@169.254.1.2"}, 212 {"127.0.0.1", isolationPodman, "git@gitbay.org", "git@169.254.1.2"},
213 {"git@127.0.0.1", isolationPodman, "", "git@169.254.1.2"}, 213 {"git@127.0.0.1", isolationPodman, "", "git@169.254.1.2"},
214 {"git@127.0.0.1", isolationNone, "git@gitbay.org", "git@gitbay.org"}, 214 {"git@127.0.0.1", isolationNone, "git@gitbay.org", "git@127.0.0.1"},
215 {"git@127.0.0.1", isolationNone, "", "git@127.0.0.1"}, 215 {"git@127.0.0.1", isolationNone, "", "git@127.0.0.1"},
216 {"git@gitbay.org", isolationPodman, "git@other.test", "git@other.test"}, 216 {"git@gitbay.org", isolationPodman, "git@other.test", "git@gitbay.org"},
217 {"git@gitbay.org", isolationPodman, "", "git@gitbay.org"}, 217 {"git@gitbay.org", isolationPodman, "", "git@gitbay.org"},
218 {"gitbay.org", isolationPodman, "", "gitbay.org"}, 218 {"gitbay.org", isolationPodman, "", "gitbay.org"},
219 {"ci@forge.internal", isolationNone, "git@gitbay.org", "ci@forge.internal"},
219 } { 220 } {
220 r := &runner{remote: tc.remote, isolation: tc.isolation} 221 r := &runner{remote: tc.remote, isolation: tc.isolation}
221 if got := r.buildSSH(tc.public); got != tc.want { 222 if got := r.buildSSH(tc.public); got != tc.want {
cmd/gitbay-runner/main.go +4 −7
@@ -44,8 +44,8 @@ type job struct {
44 // server did not say: such a build gets no secrets and a home of its 44 // server did not say: such a build gets no secrets and a home of its
45 // own (#255). 45 // own (#255).
46 Trusted bool `json:"trusted"` 46 Trusted bool `json:"trusted"`
47 // SSH is the instance's public ssh destination, for a build whose 47 // SSH is the instance's public ssh destination; a runner polling
48 // runner polls over loopback (#260). 48 // over loopback takes its port for its builds (#260).
49 SSH string `json:"ssh"` 49 SSH string `json:"ssh"`
50 Secrets map[string]string `json:"secrets"` 50 Secrets map[string]string `json:"secrets"`
51} 51}
@@ -507,8 +507,8 @@ const hostAddr = "169.254.1.2"
507// instance's public name resolves to the container itself. A runner 507// instance's public name resolves to the container itself. A runner
508// polling over loopback runs on the daemon's host, and its podman builds 508// polling over loopback runs on the daemon's host, and its podman builds
509// get hostAddr with the user from -remote and the port from the claim's 509// get hostAddr with the user from -remote and the port from the claim's
510// destination when it is not 22. Otherwise a build uses the claim's 510// destination when it is not 22. Any other runner's -remote is the path
511// destination, or -remote when the claim carries none. 511// that reaches the forge from where it runs, so its builds get that.
512func (r *runner) buildSSH(public string) string { 512func (r *runner) buildSSH(public string) string {
513 if r.isolation == isolationPodman && r.loopbackRemote() { 513 if r.isolation == isolationPodman && r.loopbackRemote() {
514 user, _, ok := strings.Cut(r.remote, "@") 514 user, _, ok := strings.Cut(r.remote, "@")
@@ -525,9 +525,6 @@ func (r *runner) buildSSH(public string) string {
525 } 525 }
526 return user + "@" + dest 526 return user + "@" + dest
527 } 527 }
528 if public != "" {
529 return public
530 }
531 return r.remote 528 return r.remote
532} 529}
533 530
internal/control/build.go +7 −7
@@ -462,11 +462,11 @@ func runnerMayBuild(c *Ctx, key store.SSHKey, repoID int64) (bool, error) {
462const maxOrphanSkip = 50 462const maxOrphanSkip = 50
463 463
464// publicSSH is the instance's ssh destination as anyone outside reaches 464// publicSSH is the instance's ssh destination as anyone outside reaches
465// it. A runner on the daemon's own host polls over loopback and hands 465// it. A runner on the daemon's own host polls over loopback and takes
466// its builds this instead, so no build connects from the runner's source 466// the port from it for its builds' GITBAY_SSH, which names pasta's
467// address (#260). The port is added only when it is not 22: hutch and 467// address for the host (#260). The port is added only when it is not
468// orgo build ssh://$GITBAY_SSH/... URLs, valid in both forms. Empty when 468// 22: hutch and orgo build ssh://$GITBAY_SSH/... URLs, valid in both
469// site_url is not set. 469// forms. Empty when site_url is not set.
470func publicSSH(c *Ctx) string { 470func publicSSH(c *Ctx) string {
471 host := c.Cfg.SiteHost() 471 host := c.Cfg.SiteHost()
472 if host == "" { 472 if host == "" {
@@ -581,8 +581,8 @@ func runRunnerNext(c *Ctx, args []string) int {
581 // Trusted is always sent: a runner decides a build's home and 581 // Trusted is always sent: a runner decides a build's home and
582 // secrets from it, and reads a missing field as untrusted (#255). 582 // secrets from it, and reads a missing field as untrusted (#255).
583 Trusted bool `json:"trusted"` 583 Trusted bool `json:"trusted"`
584 // SSH is the instance's public destination for the build's 584 // SSH is the instance's public destination; a runner polling
585 // GITBAY_SSH when its runner polls over loopback (#260). 585 // over loopback takes its port for the build's GITBAY_SSH (#260).
586 SSH string `json:"ssh,omitempty"` 586 SSH string `json:"ssh,omitempty"`
587 Secrets map[string]string `json:"secrets,omitempty"` 587 Secrets map[string]string `json:"secrets,omitempty"`
588 }{ID: b.ID, Repo: repo.Path(), Number: b.Number, Job: b.Job, SHA: b.SHA, Ref: b.Ref, 588 }{ID: b.ID, Repo: repo.Path(), Number: b.Number, Job: b.Job, SHA: b.SHA, Ref: b.Ref,