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:
@@ -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
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user