CAT-BG3: mesh-build-go fetches the mesh's Go modules through the Go seat first, Go's public proxy second, the forge's git last

With go-package-registry bound, the fetch's GOPROXY is the Go seat, then proxy.golang.org, then direct (kept for
this milestone, E11: removed by the first change after mesh-sdk's and mesh-contracts' first numbered versions
are in the Go seat); no GOPRIVATE, and the checksum database off for the forge's paths (design 51 *Go packages*,
ADR 0605 §6). The module requires and binds go-package-registry, so its new build waits for the forge's build
that offers it (E2). Unbound, the fetch is as before.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
2026-11-04 12:00:35 +01:00
parent 4e318c8591
commit eeeb2056ae
6 changed files with 76 additions and 7 deletions
@@ -8,6 +8,7 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"path/filepath"
@@ -310,15 +311,58 @@ func (m *Module) goEnv() []string {
if m.GoModCache != "" {
modcache = m.GoModCache
}
return append([]string{
env := []string{
"GOTOOLCHAIN=local",
"GOFLAGS=-mod=mod",
"GOMODCACHE=" + modcache,
"GOCACHE=" + filepath.Join(m.Cache, "gobuild"),
"GOPRIVATE=git.novox.be",
"GIT_TERMINAL_PROMPT=0",
"GIT_SSH_COMMAND=ssh -o BatchMode=yes",
}, m.threadEnv()...)
}
if proxy := m.goSeat(); proxy != "" {
// The Go seat first, the public proxy second, the forge's git last (CAT-BG3, E11: direct goes in the first
// change after mesh-sdk's and mesh-contracts' first numbered versions are in the Go seat); no GOPRIVATE, and
// the checksum database off for the forge's paths, which go.sum and the builder's h1: digest guard instead.
env = append(env, "GOPROXY="+proxy+","+PublicGoProxy+",direct", "GONOSUMDB="+ForgeGoPaths, "GOPRIVATE=")
} else {
env = append(env, "GOPRIVATE="+ForgeGoPaths)
}
return append(append(env, "GIT_TERMINAL_PROMPT=0", "GIT_SSH_COMMAND=ssh -o BatchMode=yes"), m.threadEnv()...)
}
// PublicGoProxy is Go's public module proxy; ForgeGoPaths the module paths the forge serves.
const (
PublicGoProxy = "https://proxy.golang.org"
ForgeGoPaths = "git.novox.be"
)
// goSeat is the Go seat's proxy URL from its binding (`at`, and `serves` scheme, port and go-path), empty when the
// module holds no binding of it or the binding names no address.
func (m *Module) goSeat() string {
if m.GoSeatFile == "" {
return ""
}
raw, err := os.ReadFile(m.GoSeatFile)
if err != nil {
return ""
}
var b struct {
At string `json:"at"`
Serves struct {
Scheme string `json:"scheme"`
Port int `json:"port"`
GoPath string `json:"go-path"`
} `json:"serves"`
}
if json.Unmarshal(raw, &b) != nil || b.At == "" || b.Serves.GoPath == "" {
return ""
}
scheme := b.Serves.Scheme
if scheme == "" {
scheme = "http"
}
if b.Serves.Port == 0 {
return fmt.Sprintf("%s://%s%s", scheme, b.At, b.Serves.GoPath)
}
return fmt.Sprintf("%s://%s:%d%s", scheme, b.At, b.Serves.Port, b.Serves.GoPath)
}
// goEnvFor is goEnv for a command run in dir: a directory that vendors (vendor/modules.txt) is read from its vendor
@@ -145,6 +145,20 @@ func TestFetchAsksGitForTheSshRemoteAndDownloadsEachModule(t *testing.T) {
}
}
}
// CAT-BG3: with the Go seat bound, the fetch goes through it first, Go's public proxy second and the forge's git
// last, with no GOPRIVATE and the checksum database off for the forge's paths.
seat := filepath.Join(t.TempDir(), "go-package-registry.json")
if err := os.WriteFile(seat, []byte(`{"at":"forge.mesh","serves":{"scheme":"http","port":3000,"go-path":"/api/packages/novox/go"}}`), 0o600); err != nil {
t.Fatal(err)
}
m.GoSeatFile = seat
env := strings.Join(m.goEnv(), " ")
for _, want := range []string{"GOPROXY=http://forge.mesh:3000/api/packages/novox/go,https://proxy.golang.org,direct", "GONOSUMDB=git.novox.be", "GOPRIVATE= "} {
if !strings.Contains(env+" ", want) {
t.Errorf("with the Go seat bound the environment lacks %q: %s", want, env)
}
}
m.GoSeatFile = ""
if downloads != 2 || verifies != 2 {
t.Errorf("%d downloads and %d verifies, want 2 each (go.mod and sub/dir/go.mod):\n%s", downloads, verifies, lines)
}
@@ -10,6 +10,7 @@ import (
"encoding/json"
"os"
"path/filepath"
"slices"
"strings"
"testing"
@@ -145,8 +146,9 @@ func TestManifestRequiresForgeAccountKeepsKeysAndTakesFilesFromTemplates(t *test
m := readForgeManifest(t)
// artifact-store: the module pushes a done build's archive itself (ADR 0498 §2).
// node-tools: the module pulls its asks through the tool runner's seat pull (1911).
if len(m.Requires) != 4 || m.Requires[0] != "forge-account" || m.Requires[1] != "go-toolchain" || m.Requires[2] != "artifact-store" || m.Requires[3] != "node-tools" {
t.Errorf("requires = %v, want [forge-account go-toolchain artifact-store node-tools]", m.Requires)
// go-package-registry: the Go seat's read side, the fetch's first source (design 51, CAT-BG3, E2).
if want := []any{"forge-account", "go-toolchain", "go-package-registry", "artifact-store", "node-tools"}; !slices.Equal(m.Requires, want) {
t.Errorf("requires = %v, want %v", m.Requires, want)
}
keeps := false
for _, s := range m.State {
@@ -55,6 +55,7 @@ func main() {
}
m.Run = StepRunner(m.Home)
m.RegistryFile = m.Home + "/registry"
m.GoSeatFile = m.Home + "/go-package-registry.json"
m.Settings = m.Home + "/settings.json"
m.ReadKept = readOf(stdio.State(KeptState)).Get
m.ReadManifest = readOf(stdio.State(ManifestsState)).Get
@@ -70,6 +70,10 @@ type Module struct {
// RegistryFile holds the artifact store's address (host:port), placed by the manifest; Push puts a blob there
// (buildstep.PushBlob when nil; a test's fake store).
RegistryFile string
// GoSeatFile is the binding of the provision go-package-registry, the Go seat's read side (design 51 *Go
// packages*): bound, the mesh's Go modules are fetched through it first, Go's public proxy second, and the forge's
// git over the account's ssh last (Go's direct, kept for this milestone only); unbound, as before, directly.
GoSeatFile string
Push func(ctx context.Context, registry, repository, digest string, body []byte) error
// Executable is this program's path, which the integration level starts again shut off; os.Executable when empty.
Executable string
+4
View File
@@ -66,9 +66,13 @@
"requires": [
"forge-account",
"go-toolchain",
"go-package-registry",
"artifact-store",
"node-tools"
],
"binds": {
"go-package-registry": "${dir:state}/go-package-registry.json"
},
"own-secrets": {
"broker": "${dir:state}/broker"
},