11116. Skip duplicate packages according to config file
12127. Package all whl files into tar.gz
1313
14+ When --loongsuite-release is used (Scheme 1):
15+ - util is built FIRST as loongsuite-util-genai
16+ - Instrumentation packages depending on opentelemetry-util-genai get dependency
17+ replaced to loongsuite-util-genai
18+ - Avoids conflict with upstream opentelemetry-util-genai on user's system
19+
1420Note: loongsuite-distro is not included as it is published separately to PyPI.
1521"""
1622
1723import argparse
1824import json
1925import logging
26+ import os
2027import subprocess
2128import sys
2229import tarfile
30+ from contextlib import contextmanager
2331from pathlib import Path
2432from typing import List , Set
2533
2634logging .basicConfig (level = logging .INFO , format = "%(levelname)s: %(message)s" )
2735logger = logging .getLogger (__name__ )
2836
37+ # Instrumentation packages that depend on opentelemetry-util-genai (need replacement in loongsuite-release)
38+ LOONGSUITE_UTIL_GENAI_DEPENDENTS = [
39+ "instrumentation-genai/opentelemetry-instrumentation-google-genai" ,
40+ "instrumentation-genai/opentelemetry-instrumentation-vertexai" ,
41+ "instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2" ,
42+ "instrumentation-loongsuite/loongsuite-instrumentation-mem0" ,
43+ "instrumentation-loongsuite/loongsuite-instrumentation-dashscope" ,
44+ "instrumentation-loongsuite/loongsuite-instrumentation-agentscope" ,
45+ ]
46+
2947
3048def load_skip_config (config_path : Path ) -> Set [str ]:
3149 """Load package names to skip from config file"""
@@ -73,8 +91,25 @@ def get_package_name_from_whl(whl_path: Path) -> str:
7391 return name
7492
7593
94+ @contextmanager
95+ def _patch_pyproject (pyproject_path : Path , replacements : List [tuple ]):
96+ """Temporarily patch pyproject.toml, restore on exit."""
97+ content = pyproject_path .read_text (encoding = "utf-8" )
98+ try :
99+ patched = content
100+ for old , new in replacements :
101+ patched = patched .replace (old , new )
102+ pyproject_path .write_text (patched , encoding = "utf-8" )
103+ yield
104+ finally :
105+ pyproject_path .write_text (content , encoding = "utf-8" )
106+
107+
76108def build_package (
77- package_dir : Path , dist_dir : Path , existing_whl_files : Set [Path ]
109+ package_dir : Path ,
110+ dist_dir : Path ,
111+ existing_whl_files : Set [Path ],
112+ env_extra : dict | None = None ,
78113) -> List [Path ]:
79114 """Build whl file for a single package"""
80115 pyproject_toml = package_dir / "pyproject.toml"
@@ -87,6 +122,10 @@ def build_package(
87122 # Record whl files before build
88123 before_whl_files = set (dist_dir .glob ("*.whl" ))
89124
125+ build_env = dict (os .environ )
126+ if env_extra :
127+ build_env .update (env_extra )
128+
90129 result = subprocess .run (
91130 [
92131 sys .executable ,
@@ -100,6 +139,7 @@ def build_package(
100139 check = True ,
101140 capture_output = True ,
102141 text = True ,
142+ env = build_env ,
103143 )
104144
105145 # Find newly generated whl files (exist after build but not before)
@@ -207,7 +247,121 @@ def collect_packages(
207247 all_whl_files .extend (whl_files )
208248 existing_whl_files .update (whl_files )
209249
210- # 6. Filter out packages that need to be skipped
250+ return _filter_and_dedupe_whl_files (all_whl_files , skip_packages )
251+
252+
253+ def collect_packages_loongsuite_release (
254+ base_dir : Path ,
255+ dist_dir : Path ,
256+ skip_packages : Set [str ],
257+ ) -> List [Path ]:
258+ """
259+ Build for Scheme 1 (loongsuite release): util first as loongsuite-util-genai,
260+ then instrumentation with dependency replacement.
261+ """
262+ all_whl_files = []
263+ existing_whl_files = set (dist_dir .glob ("*.whl" ))
264+
265+ util_genai_dir = base_dir / "util" / "opentelemetry-util-genai"
266+ if not (
267+ util_genai_dir .exists ()
268+ and (util_genai_dir / "pyproject.toml" ).exists ()
269+ ):
270+ raise FileNotFoundError (
271+ f"util/opentelemetry-util-genai not found at { util_genai_dir } "
272+ )
273+
274+ # 1. Build util FIRST as loongsuite-util-genai
275+ logger .info ("Building util as loongsuite-util-genai (Scheme 1)..." )
276+ with _patch_pyproject (
277+ util_genai_dir / "pyproject.toml" ,
278+ [('name = "opentelemetry-util-genai"' , 'name = "loongsuite-util-genai"' )],
279+ ):
280+ whl_files = build_package (
281+ util_genai_dir , dist_dir , existing_whl_files
282+ )
283+ all_whl_files .extend (whl_files )
284+ existing_whl_files .update (whl_files )
285+
286+ def _build_with_patch_if_needed (rel_path : str , package_dir : Path ) -> List [Path ]:
287+ """Build package, patching opentelemetry-util-genai -> loongsuite-util-genai if needed."""
288+ if rel_path in LOONGSUITE_UTIL_GENAI_DEPENDENTS :
289+ pyproject = package_dir / "pyproject.toml"
290+ with _patch_pyproject (
291+ pyproject ,
292+ [("opentelemetry-util-genai" , "loongsuite-util-genai" )],
293+ ):
294+ return build_package (
295+ package_dir , dist_dir , existing_whl_files
296+ )
297+ return build_package (package_dir , dist_dir , existing_whl_files )
298+
299+ # 2. Build instrumentation/ (no util dependency)
300+ instrumentation_dir = base_dir / "instrumentation"
301+ if instrumentation_dir .exists ():
302+ logger .info ("Building packages under instrumentation/..." )
303+ for package_dir in sorted (instrumentation_dir .iterdir ()):
304+ if (
305+ package_dir .is_dir ()
306+ and (package_dir / "pyproject.toml" ).exists ()
307+ ):
308+ whl_files = build_package (
309+ package_dir , dist_dir , existing_whl_files
310+ )
311+ all_whl_files .extend (whl_files )
312+ existing_whl_files .update (whl_files )
313+
314+ # 3. Build instrumentation-genai/ (with dependency patch)
315+ instrumentation_genai_dir = base_dir / "instrumentation-genai"
316+ if instrumentation_genai_dir .exists ():
317+ logger .info ("Building packages under instrumentation-genai/..." )
318+ for package_dir in sorted (instrumentation_genai_dir .iterdir ()):
319+ if (
320+ package_dir .is_dir ()
321+ and (package_dir / "pyproject.toml" ).exists ()
322+ ):
323+ rel_path = str (package_dir .relative_to (base_dir ))
324+ whl_files = _build_with_patch_if_needed (rel_path , package_dir )
325+ all_whl_files .extend (whl_files )
326+ existing_whl_files .update (whl_files )
327+
328+ # 4. Build instrumentation-loongsuite/ (with dependency patch)
329+ instrumentation_loongsuite_dir = base_dir / "instrumentation-loongsuite"
330+ if instrumentation_loongsuite_dir .exists ():
331+ logger .info ("Building packages under instrumentation-loongsuite/..." )
332+ for package_dir in sorted (instrumentation_loongsuite_dir .iterdir ()):
333+ if (
334+ package_dir .is_dir ()
335+ and (package_dir / "pyproject.toml" ).exists ()
336+ ):
337+ rel_path = str (package_dir .relative_to (base_dir ))
338+ whl_files = _build_with_patch_if_needed (rel_path , package_dir )
339+ all_whl_files .extend (whl_files )
340+ existing_whl_files .update (whl_files )
341+
342+ # 5. Build processor/loongsuite-processor-baggage/
343+ processor_baggage_dir = (
344+ base_dir / "processor" / "loongsuite-processor-baggage"
345+ )
346+ if (
347+ processor_baggage_dir .exists ()
348+ and (processor_baggage_dir / "pyproject.toml" ).exists ()
349+ ):
350+ logger .info ("Building processor/loongsuite-processor-baggage/..." )
351+ whl_files = build_package (
352+ processor_baggage_dir , dist_dir , existing_whl_files
353+ )
354+ all_whl_files .extend (whl_files )
355+ existing_whl_files .update (whl_files )
356+
357+ return _filter_and_dedupe_whl_files (all_whl_files , skip_packages )
358+
359+
360+ def _filter_and_dedupe_whl_files (
361+ all_whl_files : List [Path ],
362+ skip_packages : Set [str ],
363+ ) -> List [Path ]:
364+ """Filter skip list and deduplicate whl files."""
211365 filtered_whl_files = []
212366 skipped_count = 0
213367 seen_packages = {} # Used to detect duplicate packages
@@ -302,6 +456,12 @@ def main():
302456 default = "dev" ,
303457 help = "Version number (for output filename)" ,
304458 )
459+ parser .add_argument (
460+ "--loongsuite-release" ,
461+ action = "store_true" ,
462+ help = "Build for Scheme 1: util as loongsuite-util-genai, "
463+ "instrumentation deps replaced (for GitHub Release tar.gz)" ,
464+ )
305465
306466 args = parser .parse_args ()
307467
@@ -318,7 +478,12 @@ def main():
318478 skip_packages = load_skip_config (args .config )
319479
320480 # Collect and build all packages
321- whl_files = collect_packages (base_dir , dist_dir , skip_packages )
481+ if args .loongsuite_release :
482+ whl_files = collect_packages_loongsuite_release (
483+ base_dir , dist_dir , skip_packages
484+ )
485+ else :
486+ whl_files = collect_packages (base_dir , dist_dir , skip_packages )
322487
323488 if not whl_files :
324489 logger .error ("No whl files found, build failed" )
0 commit comments