2026-09-07 14:52:03 +00:00
|
|
|
import importlib.util
|
|
|
|
|
import pathlib
|
2026-09-07 15:08:18 +00:00
|
|
|
import os
|
|
|
|
|
import json
|
2026-09-07 14:52:03 +00:00
|
|
|
import subprocess
|
|
|
|
|
import tempfile
|
|
|
|
|
import unittest
|
2026-09-07 15:08:18 +00:00
|
|
|
import zipfile
|
2026-09-07 14:52:03 +00:00
|
|
|
|
|
|
|
|
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
|
|
|
|
SPEC = importlib.util.spec_from_file_location("exporter", ROOT / "tools/export-private-modules.py")
|
|
|
|
|
MODULE = importlib.util.module_from_spec(SPEC)
|
|
|
|
|
SPEC.loader.exec_module(MODULE)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ExportTest(unittest.TestCase):
|
2026-09-07 15:08:18 +00:00
|
|
|
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))
|
|
|
|
|
|
2026-09-07 14:52:03 +00:00
|
|
|
def test_committed_bytes_and_provenance_are_exact(self):
|
|
|
|
|
with tempfile.TemporaryDirectory() as temporary:
|
|
|
|
|
base = pathlib.Path(temporary)
|
|
|
|
|
source, target = base / "source", base / "target"
|
|
|
|
|
source.mkdir()
|
|
|
|
|
target.mkdir()
|
|
|
|
|
subprocess.run(["git", "init", "-q", str(source)], check=True)
|
|
|
|
|
for path in MODULE.FILES:
|
|
|
|
|
file = source / path
|
|
|
|
|
file.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
file.write_text("package main\n")
|
|
|
|
|
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)
|
|
|
|
|
revision = MODULE.committed(source, "rev-parse", "HEAD").decode().strip()
|
|
|
|
|
(source / next(iter(MODULE.FILES))).write_text("dirty worktree must be ignored")
|
|
|
|
|
MODULE.export(source, target, revision)
|
|
|
|
|
MODULE.export(source, target, revision, True)
|
|
|
|
|
subprocess.run(["sha256sum", "-c", "tools/private-modules.sha256"],
|
|
|
|
|
cwd=target, check=True, stdout=subprocess.DEVNULL)
|
|
|
|
|
destination = target / "tools/private-modules.go"
|
|
|
|
|
self.assertEqual(destination.read_text(), "package main\n")
|
|
|
|
|
destination.write_text("changed")
|
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
|
MODULE.export(source, target, revision, True)
|
|
|
|
|
destination.unlink()
|
|
|
|
|
destination.symlink_to(source / next(iter(MODULE.FILES)))
|
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
|
MODULE.export(source, target, revision)
|
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
|
MODULE.export(source, target, "HEAD")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
unittest.main()
|