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:
564564=GITBAY_REPO=,
565565=GITBAY_SHA=, =GITBAY_REF=, =GITBAY_JOB=, =CI=true=, and =GITBAY_SSH=,
566566the 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
568runner elsewhere, the instance's public address (=git@gitbay.org=).
569Either carries =:port= when the instance's ssh is not on 22, so use it
570as =ssh://$GITBAY_SSH/owner/name.git= or =ssh ssh://$GITBAY_SSH …=,
571which work in both forms. A job that
567own runner, =git@169.254.1.2=, pasta's address for the host, with
568=:port= when the instance's ssh is not on 22; from any other runner,
569the destination that runner polls (its =-remote=, such as
570=git@gitbay.org=). Use it as =ssh://$GITBAY_SSH/owner/name.git= or
571=ssh ssh://$GITBAY_SSH …=, which work in either form. A job that
572572talks back to the instance — a release asset, a comment, a push to a
573573pages branch — uses =$GITBAY_SSH= with a key it holds as a secret;
574574the 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) {
195195// address, so a runner polling over loopback gives its podman builds
196196// 169.254.1.2, pasta's address for the host, with the claim's port when
197197// 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).
199199func TestStepEnvCarriesInstanceAddress(t *testing.T) {
200200 env := stepEnv(job{}, "/tmp/buildhome", "git@gitbay.org")
201201 if !containsEnv(env, "GITBAY_SSH=git@gitbay.org") {
@@ -211,11 +211,12 @@ func TestStepEnvCarriesInstanceAddress(t *testing.T) {
211211 {"forge@::1", isolationPodman, "git@gitbay.org", "forge@169.254.1.2"},
212212 {"127.0.0.1", isolationPodman, "git@gitbay.org", "git@169.254.1.2"},
213213 {"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"},
215215 {"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"},
217217 {"git@gitbay.org", isolationPodman, "", "git@gitbay.org"},
218218 {"gitbay.org", isolationPodman, "", "gitbay.org"},
219 {"ci@forge.internal", isolationNone, "git@gitbay.org", "ci@forge.internal"},
219220 } {
220221 r := &runner{remote: tc.remote, isolation: tc.isolation}
221222 if got := r.buildSSH(tc.public); got != tc.want {
cmd/gitbay-runner/main.go +4 −7
@@ -44,8 +44,8 @@ type job struct {
4444 // server did not say: such a build gets no secrets and a home of its
4545 // own (#255).
4646 Trusted bool `json:"trusted"`
47 // SSH is the instance's public ssh destination, for a build whose
48 // runner polls over loopback (#260).
47 // SSH is the instance's public ssh destination; a runner polling
48 // over loopback takes its port for its builds (#260).
4949 SSH string `json:"ssh"`
5050 Secrets map[string]string `json:"secrets"`
5151}
@@ -507,8 +507,8 @@ const hostAddr = "169.254.1.2"
507507// instance's public name resolves to the container itself. A runner
508508// polling over loopback runs on the daemon's host, and its podman builds
509509// 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
511// destination, or -remote when the claim carries none.
510// destination when it is not 22. Any other runner's -remote is the path
511// that reaches the forge from where it runs, so its builds get that.
512512func (r *runner) buildSSH(public string) string {
513513 if r.isolation == isolationPodman && r.loopbackRemote() {
514514 user, _, ok := strings.Cut(r.remote, "@")
@@ -525,9 +525,6 @@ func (r *runner) buildSSH(public string) string {
525525 }
526526 return user + "@" + dest
527527 }
528 if public != "" {
529 return public
530 }
531528 return r.remote
532529}
533530
internal/control/build.go +7 −7
@@ -462,11 +462,11 @@ func runnerMayBuild(c *Ctx, key store.SSHKey, repoID int64) (bool, error) {
462462const maxOrphanSkip = 50
463463
464464// 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
466// its builds this instead, so no build connects from the runner's source
467// address (#260). The port is added only when it is not 22: hutch and
468// orgo build ssh://$GITBAY_SSH/... URLs, valid in both forms. Empty when
469// site_url is not set.
465// it. A runner on the daemon's own host polls over loopback and takes
466// the port from it for its builds' GITBAY_SSH, which names pasta's
467// address for the host (#260). The port is added only when it is not
468// 22: hutch and orgo build ssh://$GITBAY_SSH/... URLs, valid in both
469// forms. Empty when site_url is not set.
470470func publicSSH(c *Ctx) string {
471471 host := c.Cfg.SiteHost()
472472 if host == "" {
@@ -581,8 +581,8 @@ func runRunnerNext(c *Ctx, args []string) int {
581581 // Trusted is always sent: a runner decides a build's home and
582582 // secrets from it, and reads a missing field as untrusted (#255).
583583 Trusted bool `json:"trusted"`
584 // SSH is the instance's public destination for the build's
585 // GITBAY_SSH when its runner polls over loopback (#260).
584 // SSH is the instance's public destination; a runner polling
585 // over loopback takes its port for the build's GITBAY_SSH (#260).
586586 SSH string `json:"ssh,omitempty"`
587587 Secrets map[string]string `json:"secrets,omitempty"`
588588 }{ID: b.ID, Repo: repo.Path(), Number: b.Number, Job: b.Job, SHA: b.SHA, Ref: b.Ref,