[bug-0zkpwxbsdxcn] Own canonical offline private Go module tooling #2

Merged
dfritz merged 7 commits from architect/bug-0zkpwxbsdxcn/private-module-tooling into main 2026-09-09 02:15:55 +00:00
4 changed files with 65 additions and 1 deletions
Showing only changes of commit 23729de1bb - Show all commits
+5
View File
@@ -35,6 +35,11 @@ client once, then run the generated command with `-write` to copy unchanged
generator itself enforces offline resolution and a local toolchain. It preserves generator itself enforces offline resolution and a local toolchain. It preserves
the complete original archives, including any licenses and notices. Normal Go the complete original archives, including any licenses and notices. Normal Go
downloads still validate their content against the consumer's `go.sum`. downloads still validate their content against the consumer's `go.sum`.
The generated proxy directory has an exact build-only nested `go.mod` marker.
Native Go module packaging excludes that directory from provider releases, so
downstream consumers never recursively bundle another provider's build inputs.
The checker requires the marker, and a native local-Git archive test proves the
exclusion without permitting Git network protocols.
Consumer resolution uses `GONOPROXY=none`, `GONOSUMDB=code.fritzlab.net`, Consumer resolution uses `GONOPROXY=none`, `GONOSUMDB=code.fritzlab.net`,
`GOFLAGS=-mod=readonly`, `GOTOOLCHAIN=local`, and `GOFLAGS=-mod=readonly`, `GOTOOLCHAIN=local`, and
+43
View File
@@ -1,8 +1,11 @@
import importlib.util import importlib.util
import pathlib import pathlib
import os
import json
import subprocess import subprocess
import tempfile import tempfile
import unittest import unittest
import zipfile
ROOT = pathlib.Path(__file__).resolve().parents[1] ROOT = pathlib.Path(__file__).resolve().parents[1]
SPEC = importlib.util.spec_from_file_location("exporter", ROOT / "tools/export-private-modules.py") SPEC = importlib.util.spec_from_file_location("exporter", ROOT / "tools/export-private-modules.py")
@@ -11,6 +14,46 @@ SPEC.loader.exec_module(MODULE)
class ExportTest(unittest.TestCase): class ExportTest(unittest.TestCase):
def test_native_go_archive_excludes_nested_build_artifacts(self):
with tempfile.TemporaryDirectory() as temporary:
root = pathlib.Path(temporary)
source = root / "source"
source.mkdir()
module = "example.invalid/fixture.git"
(source / "go.mod").write_text("module " + module + "\n\ngo 1.27.0\n")
(source / "provider.go").write_text("package fixture\n")
boundary = source / "third_party/go-proxy"
boundary.mkdir(parents=True)
(boundary / "go.mod").write_text("module example.invalid/build-artifacts\n\ngo 1.27.0\n")
(boundary / "provider.zip").write_bytes(b"must not enter the provider module archive")
subprocess.run(["git", "init", "-q", str(source)], check=True)
subprocess.run(["git", "-C", str(source), "add", "."], check=True)
subprocess.run(["git", "-C", str(source), "-c", "user.name=Fixture",
"-c", "user.email=fixture@example.invalid", "commit", "-qm", "fixture"], check=True)
subprocess.run(["git", "-C", str(source), "tag", "v0.1.0"], check=True)
config = root / "gitconfig"
config.write_text('[url "' + source.as_uri() + '"]\n'
'\tinsteadOf = https://example.invalid/fixture\n')
# Let Go select the synthetic VCS URL; Git still permits only the
# rewritten local file protocol, with no network scheme allowed.
env = dict(os.environ, GO111MODULE="on", GOTOOLCHAIN="local", GOFLAGS="", GOWORK="off",
GOPROXY="direct", GONOPROXY="none", GOSUMDB="off", GOVCS="*:git",
GOINSECURE="example.invalid",
GOMODCACHE=str(root / "cache"), GIT_CONFIG_GLOBAL=str(config),
GIT_CONFIG_NOSYSTEM="1", GIT_CONFIG_COUNT="0", GIT_ALLOW_PROTOCOL="file")
probe = subprocess.run(["git", "ls-remote", "https://example.invalid/fixture"], env=env, capture_output=True)
self.assertEqual(probe.returncode, 0, probe.stderr.decode())
# Native Git accepts only the local file protocol: a missed rewrite
# cannot turn this proof into a network or credential lookup.
result = subprocess.run(["go", "mod", "download", "-json", module + "@v0.1.0"],
cwd=root, env=env, capture_output=True)
self.assertEqual(result.returncode, 0, result.stdout.decode() + result.stderr.decode())
archive = json.loads(result.stdout)["Zip"]
with zipfile.ZipFile(archive) as zipped:
names = zipped.namelist()
self.assertIn(module + "@v0.1.0/provider.go", names)
self.assertFalse(any("third_party/go-proxy" in name for name in names))
def test_committed_bytes_and_provenance_are_exact(self): def test_committed_bytes_and_provenance_are_exact(self):
with tempfile.TemporaryDirectory() as temporary: with tempfile.TemporaryDirectory() as temporary:
base = pathlib.Path(temporary) base = pathlib.Path(temporary)
+9 -1
View File
@@ -21,6 +21,7 @@ import (
const bundleRoot = "third_party/go-proxy" const bundleRoot = "third_party/go-proxy"
const privatePrefix = "code.fritzlab.net/" const privatePrefix = "code.fritzlab.net/"
const archiveBoundary = "module code.fritzlab.net/action/image-build/private-module-artifacts\n\ngo 1.27.0\n"
type artifact struct { type artifact struct {
Path string `json:"path"` Path string `json:"path"`
@@ -79,6 +80,9 @@ func generate() error {
return err return err
} }
defer os.RemoveAll(temp) defer os.RemoveAll(temp)
if err := os.WriteFile(filepath.Join(temp, "go.mod"), []byte(archiveBoundary), 0444); err != nil {
return err
}
decoder := json.NewDecoder(bytes.NewReader(raw)) decoder := json.NewDecoder(bytes.NewReader(raw))
var total int64 var total int64
for { for {
@@ -226,7 +230,11 @@ func check() error {
if mod != m.GoModSHA256 || sum != m.GoSumSHA256 { if mod != m.GoModSHA256 || sum != m.GoSumSHA256 {
return errors.New("private module closure stale; regenerate after go.mod/go.sum changes") return errors.New("private module closure stale; regenerate after go.mod/go.sum changes")
} }
expected := map[string]bool{"manifest.json": true} boundary, err := os.ReadFile(filepath.Join(bundleRoot, "go.mod"))
if err != nil || string(boundary) != archiveBoundary {
return errors.New("private module archive boundary missing or modified")
}
expected := map[string]bool{"manifest.json": true, "go.mod": true}
last := "" last := ""
var total int64 var total int64
for _, m := range m.Modules { for _, m := range m.Modules {
+8
View File
@@ -27,9 +27,17 @@ func TestBundleRejectsStaleMissingModifiedAndUnexpectedArtifacts(t *testing.T) {
} }
b, _ := json.Marshal(m) b, _ := json.Marshal(m)
os.WriteFile(filepath.Join(bundleRoot, "manifest.json"), b, 0600) os.WriteFile(filepath.Join(bundleRoot, "manifest.json"), b, 0600)
os.WriteFile(filepath.Join(bundleRoot, "go.mod"), []byte(archiveBoundary), 0600)
if err := check(); err != nil { if err := check(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for _, boundary := range []string{"", "module unexpected.invalid/nested\n"} {
os.WriteFile(filepath.Join(bundleRoot, "go.mod"), []byte(boundary), 0600)
if check() == nil {
t.Fatal("missing or changed archive boundary accepted")
}
}
os.WriteFile(filepath.Join(bundleRoot, "go.mod"), []byte(archiveBoundary), 0600)
originalPath, originalVersion := m.Modules[0].Path, m.Modules[0].Version originalPath, originalVersion := m.Modules[0].Path, m.Modules[0].Version
for _, change := range []string{"path", "version"} { for _, change := range []string{"path", "version"} {
if change == "path" { if change == "path" {