Skip to content

Commit f726b02

Browse files
authored
CI - C# quickstart test properly uses nuget (#3466)
# Description of Changes The quickstart smoketest was not correctly fudging our NuGet dependencies to use the local versions of the packages, so it was pulling them from NuGet. This ended up causing issues when we tried to modify local packages and then use them in a way that affected the quickstart (e.g. #3386). We had a few issues: we weren't ensuring that the local packages were built, we weren't using the right directory for those packages, and we weren't adding a "package source mapping" that forced those packages to be used from the local directory. # API and ABI breaking changes None. CI-only. # Expected complexity level and risk 2 # Testing - [x] CI passes when #3386 is merged together with this PR (it wasn't before). --------- Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
1 parent f8631cc commit f726b02

2 files changed

Lines changed: 73 additions & 16 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ jobs:
6767
- uses: actions/setup-python@v5
6868
with: { python-version: '3.12' }
6969
if: runner.os == 'Windows'
70-
- name: Install psycopg2
71-
run: python -m pip install psycopg2-binary
70+
- name: Install python deps
71+
run: python -m pip install psycopg2-binary xmltodict
7272
- name: Run smoketests
7373
# Note: clear_database and replication only work in private
7474
run: python -m smoketests ${{ matrix.smoketest_args }} -x clear_database replication

smoketests/tests/quickstart.py

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import shutil
44
from pathlib import Path
55
import tempfile
6+
import xmltodict
67

78
import smoketests
89
from .. import Smoketest, STDB_DIR, run_cmd, TEMPLATE_CARGO_TOML
@@ -43,17 +44,57 @@ def _parse_quickstart(doc_path: Path, language: str) -> str:
4344
# So we could have a different db for each language
4445
return "\n".join(blocks).replace("quickstart-chat", f"quickstart-chat-{language}") + end
4546

46-
47-
def _dotnet_add_package(project_path: Path, package_name: str, source_path: Path):
48-
"""Add a local NuGet package to a .NET project"""
49-
sources = run_cmd("dotnet", "nuget", "list", "source", cwd=project_path, capture_stderr=True)
50-
# Is the source already added?
51-
if package_name in sources:
52-
run_cmd("dotnet", "nuget", "remove", "source", package_name, cwd=project_path, capture_stderr=True)
53-
run_cmd("dotnet", "nuget", "add", "source", source_path, "--name", package_name, cwd=project_path,
54-
capture_stderr=True)
55-
run_cmd("dotnet", "add", "package", package_name, cwd=project_path, capture_stderr=True)
56-
47+
def load_nuget_config(p: Path):
48+
if p.exists():
49+
with p.open("rb") as f:
50+
return xmltodict.parse(f.read(), force_list=["add", "packageSource", "package"])
51+
return {}
52+
53+
def save_nuget_config(p: Path, doc: dict):
54+
# Write back (pretty, UTF-8, no BOM)
55+
xml = xmltodict.unparse(doc, pretty=True)
56+
p.write_text(xml, encoding="utf-8")
57+
58+
def add_source(doc: dict, *, key: str, path: str) -> None:
59+
cfg = doc.setdefault("configuration", {})
60+
sources = cfg.setdefault("packageSources", {})
61+
source_entries = sources.setdefault("add", [])
62+
source = {"@key": key, "@value": path}
63+
source_entries.append(source)
64+
65+
def add_mapping(doc: dict, *, key: str, pattern: str) -> None:
66+
cfg = doc.setdefault("configuration", {})
67+
68+
psm = cfg.setdefault("packageSourceMapping", {})
69+
mapping_sources = psm.setdefault("packageSource", [])
70+
71+
# Find or create the target <packageSource key="...">
72+
target = next((s for s in mapping_sources if s.get("@key") == key), None)
73+
if target is None:
74+
target = {"@key": key, "package": []}
75+
mapping_sources.append(target)
76+
77+
pkgs = target.setdefault("package", [])
78+
79+
existing = {pkg.get("@pattern") for pkg in pkgs if "@pattern" in pkg}
80+
if pattern not in existing:
81+
pkgs.append({"@pattern": pattern})
82+
83+
def override_nuget_package(*, project_dir: Path, package: str, source_dir: Path, build_subdir: str):
84+
"""Override nuget config to use a local NuGet package on a .NET project"""
85+
# Make sure the local package is built
86+
run_cmd("dotnet", "pack", cwd=source_dir)
87+
88+
p = Path(project_dir) / "nuget.config"
89+
doc = load_nuget_config(p)
90+
add_source(doc, key=package, path=source_dir/build_subdir)
91+
add_mapping(doc, key=package, pattern=package)
92+
# Fallback for other packages
93+
add_mapping(doc, key="nuget.org", pattern="*")
94+
save_nuget_config(p, doc)
95+
96+
# Clear any caches for nuget packages
97+
run_cmd("dotnet", "nuget", "locals", "--clear", "all", capture_stderr=True)
5798

5899
class BaseQuickstart(Smoketest):
59100
AUTOPUBLISH = False
@@ -246,11 +287,27 @@ def project_init(self, path: Path):
246287
run_cmd("dotnet", "new", "console", "--name", "QuickstartChatClient", "--output", path, capture_stderr=True)
247288

248289
def sdk_setup(self, path: Path):
249-
_dotnet_add_package(path, "SpacetimeDB.ClientSDK", (STDB_DIR / "sdks/csharp").absolute())
290+
override_nuget_package(
291+
project_dir=STDB_DIR/"sdks/csharp",
292+
package="SpacetimeDB.BSATN.Runtime",
293+
source_dir=(STDB_DIR / "crates/bindings-csharp/BSATN.Runtime").absolute(),
294+
build_subdir="bin/Release"
295+
)
296+
override_nuget_package(
297+
project_dir=path,
298+
package="SpacetimeDB.ClientSDK",
299+
source_dir=(STDB_DIR / "sdks/csharp").absolute(),
300+
build_subdir="bin~/Release"
301+
)
302+
run_cmd("dotnet", "add", "package", "SpacetimeDB.ClientSDK", cwd=path, capture_stderr=True)
250303

251304
def server_postprocess(self, server_path: Path):
252-
_dotnet_add_package(server_path, "SpacetimeDB.Runtime",
253-
(STDB_DIR / "crates/bindings-csharp/Runtime").absolute())
305+
override_nuget_package(
306+
project_dir=server_path,
307+
package="SpacetimeDB.Runtime",
308+
source_dir=(STDB_DIR / "crates/bindings-csharp/Runtime").absolute(),
309+
build_subdir="bin/Release"
310+
)
254311

255312
def test_quickstart(self):
256313
"""Run the C# quickstart guides for server and client."""

0 commit comments

Comments
 (0)