diff --git a/libraries/microsoft-agents-a365-notifications/setup.py b/libraries/microsoft-agents-a365-notifications/setup.py index a04ba9a5..1c812d3b 100644 --- a/libraries/microsoft-agents-a365-notifications/setup.py +++ b/libraries/microsoft-agents-a365-notifications/setup.py @@ -1,13 +1,28 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +import sys +from pathlib import Path from os import environ from setuptools import setup # Get version from environment variable set by CI/CD -# This will be set by setuptools-git-versioning in the CI pipeline package_version = environ.get("AGENT365_PYTHON_SDK_PACKAGE_VERSION", "0.0.0") +# Add versioning helper to path +helper_path = Path(__file__).parent.parent.parent / "versioning" / "helper" +sys.path.insert(0, str(helper_path)) + +from setup_utils import get_dynamic_dependencies + +# Use minimum version strategy: +# - Internal packages get: >= current_base_version (e.g., >= 0.1.0) +# - Automatically updates when you build new versions +# - Consumers can upgrade to any higher version setup( version=package_version, + install_requires=get_dynamic_dependencies( + use_compatible_release=False, # No upper bound + use_exact_match=False, # Not exact match + ), ) diff --git a/libraries/microsoft-agents-a365-observability-core/pyproject.toml b/libraries/microsoft-agents-a365-observability-core/pyproject.toml index 7f5b1596..a10b4662 100644 --- a/libraries/microsoft-agents-a365-observability-core/pyproject.toml +++ b/libraries/microsoft-agents-a365-observability-core/pyproject.toml @@ -30,7 +30,7 @@ dependencies = [ "opentelemetry-exporter-otlp >= 1.36.0", "pydantic >= 2.0.0", "typing-extensions >= 4.0.0", - "microsoft-agents-a365-runtime >= 0.1.0" + "microsoft-agents-a365-runtime >= 0.0.0" ] [project.urls] diff --git a/libraries/microsoft-agents-a365-observability-core/setup.py b/libraries/microsoft-agents-a365-observability-core/setup.py index a04ba9a5..1c812d3b 100644 --- a/libraries/microsoft-agents-a365-observability-core/setup.py +++ b/libraries/microsoft-agents-a365-observability-core/setup.py @@ -1,13 +1,28 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +import sys +from pathlib import Path from os import environ from setuptools import setup # Get version from environment variable set by CI/CD -# This will be set by setuptools-git-versioning in the CI pipeline package_version = environ.get("AGENT365_PYTHON_SDK_PACKAGE_VERSION", "0.0.0") +# Add versioning helper to path +helper_path = Path(__file__).parent.parent.parent / "versioning" / "helper" +sys.path.insert(0, str(helper_path)) + +from setup_utils import get_dynamic_dependencies + +# Use minimum version strategy: +# - Internal packages get: >= current_base_version (e.g., >= 0.1.0) +# - Automatically updates when you build new versions +# - Consumers can upgrade to any higher version setup( version=package_version, + install_requires=get_dynamic_dependencies( + use_compatible_release=False, # No upper bound + use_exact_match=False, # Not exact match + ), ) diff --git a/libraries/microsoft-agents-a365-observability-extensions-agentframework/pyproject.toml b/libraries/microsoft-agents-a365-observability-extensions-agentframework/pyproject.toml index fe1aa3ee..1e686d19 100644 --- a/libraries/microsoft-agents-a365-observability-extensions-agentframework/pyproject.toml +++ b/libraries/microsoft-agents-a365-observability-extensions-agentframework/pyproject.toml @@ -25,7 +25,7 @@ classifiers = [ license = "MIT" keywords = ["observability", "telemetry", "tracing", "opentelemetry", "agent-framework", "agents", "ai"] dependencies = [ - "microsoft-agents-a365-observability-core >= 0.1.0", + "microsoft-agents-a365-observability-core >= 0.0.0", "opentelemetry-api >= 1.36.0", "opentelemetry-sdk >= 1.36.0", "opentelemetry-instrumentation >= 0.47b0", diff --git a/libraries/microsoft-agents-a365-observability-extensions-agentframework/setup.py b/libraries/microsoft-agents-a365-observability-extensions-agentframework/setup.py index a04ba9a5..1c812d3b 100644 --- a/libraries/microsoft-agents-a365-observability-extensions-agentframework/setup.py +++ b/libraries/microsoft-agents-a365-observability-extensions-agentframework/setup.py @@ -1,13 +1,28 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +import sys +from pathlib import Path from os import environ from setuptools import setup # Get version from environment variable set by CI/CD -# This will be set by setuptools-git-versioning in the CI pipeline package_version = environ.get("AGENT365_PYTHON_SDK_PACKAGE_VERSION", "0.0.0") +# Add versioning helper to path +helper_path = Path(__file__).parent.parent.parent / "versioning" / "helper" +sys.path.insert(0, str(helper_path)) + +from setup_utils import get_dynamic_dependencies + +# Use minimum version strategy: +# - Internal packages get: >= current_base_version (e.g., >= 0.1.0) +# - Automatically updates when you build new versions +# - Consumers can upgrade to any higher version setup( version=package_version, + install_requires=get_dynamic_dependencies( + use_compatible_release=False, # No upper bound + use_exact_match=False, # Not exact match + ), ) diff --git a/libraries/microsoft-agents-a365-observability-extensions-langchain/pyproject.toml b/libraries/microsoft-agents-a365-observability-extensions-langchain/pyproject.toml index 2bbf8767..2892c97a 100644 --- a/libraries/microsoft-agents-a365-observability-extensions-langchain/pyproject.toml +++ b/libraries/microsoft-agents-a365-observability-extensions-langchain/pyproject.toml @@ -25,7 +25,7 @@ classifiers = [ license = {text = "MIT"} keywords = ["observability", "telemetry", "tracing", "opentelemetry", "langchain", "agents", "ai"] dependencies = [ - "microsoft-agents-a365-observability-core >= 0.1.0", + "microsoft-agents-a365-observability-core >= 0.0.0", "langchain >= 0.1.0", "langchain-core >= 0.1.0", "opentelemetry-api >= 1.36.0", diff --git a/libraries/microsoft-agents-a365-observability-extensions-langchain/setup.py b/libraries/microsoft-agents-a365-observability-extensions-langchain/setup.py index a04ba9a5..1c812d3b 100644 --- a/libraries/microsoft-agents-a365-observability-extensions-langchain/setup.py +++ b/libraries/microsoft-agents-a365-observability-extensions-langchain/setup.py @@ -1,13 +1,28 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +import sys +from pathlib import Path from os import environ from setuptools import setup # Get version from environment variable set by CI/CD -# This will be set by setuptools-git-versioning in the CI pipeline package_version = environ.get("AGENT365_PYTHON_SDK_PACKAGE_VERSION", "0.0.0") +# Add versioning helper to path +helper_path = Path(__file__).parent.parent.parent / "versioning" / "helper" +sys.path.insert(0, str(helper_path)) + +from setup_utils import get_dynamic_dependencies + +# Use minimum version strategy: +# - Internal packages get: >= current_base_version (e.g., >= 0.1.0) +# - Automatically updates when you build new versions +# - Consumers can upgrade to any higher version setup( version=package_version, + install_requires=get_dynamic_dependencies( + use_compatible_release=False, # No upper bound + use_exact_match=False, # Not exact match + ), ) diff --git a/libraries/microsoft-agents-a365-observability-extensions-openai/pyproject.toml b/libraries/microsoft-agents-a365-observability-extensions-openai/pyproject.toml index 602dc971..7e3ac396 100644 --- a/libraries/microsoft-agents-a365-observability-extensions-openai/pyproject.toml +++ b/libraries/microsoft-agents-a365-observability-extensions-openai/pyproject.toml @@ -25,7 +25,7 @@ classifiers = [ license = {text = "MIT"} keywords = ["observability", "telemetry", "tracing", "opentelemetry", "openai", "agents", "ai"] dependencies = [ - "microsoft-agents-a365-observability-core >= 0.1.0", + "microsoft-agents-a365-observability-core >= 0.0.0", "openai-agents >= 0.2.6", "opentelemetry-api >= 1.36.0", "opentelemetry-sdk >= 1.36.0", diff --git a/libraries/microsoft-agents-a365-observability-extensions-openai/setup.py b/libraries/microsoft-agents-a365-observability-extensions-openai/setup.py index a04ba9a5..1c812d3b 100644 --- a/libraries/microsoft-agents-a365-observability-extensions-openai/setup.py +++ b/libraries/microsoft-agents-a365-observability-extensions-openai/setup.py @@ -1,13 +1,28 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +import sys +from pathlib import Path from os import environ from setuptools import setup # Get version from environment variable set by CI/CD -# This will be set by setuptools-git-versioning in the CI pipeline package_version = environ.get("AGENT365_PYTHON_SDK_PACKAGE_VERSION", "0.0.0") +# Add versioning helper to path +helper_path = Path(__file__).parent.parent.parent / "versioning" / "helper" +sys.path.insert(0, str(helper_path)) + +from setup_utils import get_dynamic_dependencies + +# Use minimum version strategy: +# - Internal packages get: >= current_base_version (e.g., >= 0.1.0) +# - Automatically updates when you build new versions +# - Consumers can upgrade to any higher version setup( version=package_version, + install_requires=get_dynamic_dependencies( + use_compatible_release=False, # No upper bound + use_exact_match=False, # Not exact match + ), ) diff --git a/libraries/microsoft-agents-a365-observability-extensions-semantickernel/pyproject.toml b/libraries/microsoft-agents-a365-observability-extensions-semantickernel/pyproject.toml index 9456cb49..cd23567b 100644 --- a/libraries/microsoft-agents-a365-observability-extensions-semantickernel/pyproject.toml +++ b/libraries/microsoft-agents-a365-observability-extensions-semantickernel/pyproject.toml @@ -24,8 +24,10 @@ classifiers = [ ] license = {text = "MIT"} keywords = ["observability", "telemetry", "tracing", "opentelemetry", "semantic-kernel", "agents", "ai"] +# Note: Internal package versions (microsoft-agents-a365-*) are automatically set at build time +# The placeholder below will be replaced with >= {current_version} by setup.py dependencies = [ - "microsoft-agents-a365-observability-core >= 0.1.0", + "microsoft-agents-a365-observability-core >= 0.0.0", "semantic-kernel >= 1.0.0", "opentelemetry-api >= 1.36.0", "opentelemetry-sdk >= 1.36.0", diff --git a/libraries/microsoft-agents-a365-observability-extensions-semantickernel/setup.py b/libraries/microsoft-agents-a365-observability-extensions-semantickernel/setup.py index a04ba9a5..1c812d3b 100644 --- a/libraries/microsoft-agents-a365-observability-extensions-semantickernel/setup.py +++ b/libraries/microsoft-agents-a365-observability-extensions-semantickernel/setup.py @@ -1,13 +1,28 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +import sys +from pathlib import Path from os import environ from setuptools import setup # Get version from environment variable set by CI/CD -# This will be set by setuptools-git-versioning in the CI pipeline package_version = environ.get("AGENT365_PYTHON_SDK_PACKAGE_VERSION", "0.0.0") +# Add versioning helper to path +helper_path = Path(__file__).parent.parent.parent / "versioning" / "helper" +sys.path.insert(0, str(helper_path)) + +from setup_utils import get_dynamic_dependencies + +# Use minimum version strategy: +# - Internal packages get: >= current_base_version (e.g., >= 0.1.0) +# - Automatically updates when you build new versions +# - Consumers can upgrade to any higher version setup( version=package_version, + install_requires=get_dynamic_dependencies( + use_compatible_release=False, # No upper bound + use_exact_match=False, # Not exact match + ), ) diff --git a/libraries/microsoft-agents-a365-runtime/setup.py b/libraries/microsoft-agents-a365-runtime/setup.py index a04ba9a5..1c812d3b 100644 --- a/libraries/microsoft-agents-a365-runtime/setup.py +++ b/libraries/microsoft-agents-a365-runtime/setup.py @@ -1,13 +1,28 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +import sys +from pathlib import Path from os import environ from setuptools import setup # Get version from environment variable set by CI/CD -# This will be set by setuptools-git-versioning in the CI pipeline package_version = environ.get("AGENT365_PYTHON_SDK_PACKAGE_VERSION", "0.0.0") +# Add versioning helper to path +helper_path = Path(__file__).parent.parent.parent / "versioning" / "helper" +sys.path.insert(0, str(helper_path)) + +from setup_utils import get_dynamic_dependencies + +# Use minimum version strategy: +# - Internal packages get: >= current_base_version (e.g., >= 0.1.0) +# - Automatically updates when you build new versions +# - Consumers can upgrade to any higher version setup( version=package_version, + install_requires=get_dynamic_dependencies( + use_compatible_release=False, # No upper bound + use_exact_match=False, # Not exact match + ), ) diff --git a/libraries/microsoft-agents-a365-tooling-extensions-agentframework/pyproject.toml b/libraries/microsoft-agents-a365-tooling-extensions-agentframework/pyproject.toml index abfc4581..4a30ce8d 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-agentframework/pyproject.toml +++ b/libraries/microsoft-agents-a365-tooling-extensions-agentframework/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ ] license = {text = "MIT"} dependencies = [ - "microsoft-agents-a365-tooling >= 0.1.0", + "microsoft-agents-a365-tooling >= 0.0.0", "microsoft-agents-hosting-core >= 0.4.0, < 0.6.0", "agent-framework-azure-ai >= 0.1.0", "azure-identity >= 1.12.0", diff --git a/libraries/microsoft-agents-a365-tooling-extensions-agentframework/setup.py b/libraries/microsoft-agents-a365-tooling-extensions-agentframework/setup.py index a04ba9a5..1c812d3b 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-agentframework/setup.py +++ b/libraries/microsoft-agents-a365-tooling-extensions-agentframework/setup.py @@ -1,13 +1,28 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +import sys +from pathlib import Path from os import environ from setuptools import setup # Get version from environment variable set by CI/CD -# This will be set by setuptools-git-versioning in the CI pipeline package_version = environ.get("AGENT365_PYTHON_SDK_PACKAGE_VERSION", "0.0.0") +# Add versioning helper to path +helper_path = Path(__file__).parent.parent.parent / "versioning" / "helper" +sys.path.insert(0, str(helper_path)) + +from setup_utils import get_dynamic_dependencies + +# Use minimum version strategy: +# - Internal packages get: >= current_base_version (e.g., >= 0.1.0) +# - Automatically updates when you build new versions +# - Consumers can upgrade to any higher version setup( version=package_version, + install_requires=get_dynamic_dependencies( + use_compatible_release=False, # No upper bound + use_exact_match=False, # Not exact match + ), ) diff --git a/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/pyproject.toml b/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/pyproject.toml index c3e74db8..e89e3236 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/pyproject.toml +++ b/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ ] license = {text = "MIT"} dependencies = [ - "microsoft-agents-a365-tooling >= 0.1.0", + "microsoft-agents-a365-tooling >= 0.0.0", "azure-ai-projects >= 1.0.0", "azure-ai-agents >= 1.0.0b251001", "azure-identity >= 1.12.0", diff --git a/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/setup.py b/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/setup.py index a04ba9a5..1c812d3b 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/setup.py +++ b/libraries/microsoft-agents-a365-tooling-extensions-azureaifoundry/setup.py @@ -1,13 +1,28 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +import sys +from pathlib import Path from os import environ from setuptools import setup # Get version from environment variable set by CI/CD -# This will be set by setuptools-git-versioning in the CI pipeline package_version = environ.get("AGENT365_PYTHON_SDK_PACKAGE_VERSION", "0.0.0") +# Add versioning helper to path +helper_path = Path(__file__).parent.parent.parent / "versioning" / "helper" +sys.path.insert(0, str(helper_path)) + +from setup_utils import get_dynamic_dependencies + +# Use minimum version strategy: +# - Internal packages get: >= current_base_version (e.g., >= 0.1.0) +# - Automatically updates when you build new versions +# - Consumers can upgrade to any higher version setup( version=package_version, + install_requires=get_dynamic_dependencies( + use_compatible_release=False, # No upper bound + use_exact_match=False, # Not exact match + ), ) diff --git a/libraries/microsoft-agents-a365-tooling-extensions-openai/pyproject.toml b/libraries/microsoft-agents-a365-tooling-extensions-openai/pyproject.toml index 6b398b63..4a096db0 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-openai/pyproject.toml +++ b/libraries/microsoft-agents-a365-tooling-extensions-openai/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ ] license = {text = "MIT"} dependencies = [ - "microsoft-agents-a365-tooling >= 0.1.0", + "microsoft-agents-a365-tooling >= 0.0.0", "openai-agents", "asyncio-throttle", ] diff --git a/libraries/microsoft-agents-a365-tooling-extensions-openai/setup.py b/libraries/microsoft-agents-a365-tooling-extensions-openai/setup.py index a04ba9a5..1c812d3b 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-openai/setup.py +++ b/libraries/microsoft-agents-a365-tooling-extensions-openai/setup.py @@ -1,13 +1,28 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +import sys +from pathlib import Path from os import environ from setuptools import setup # Get version from environment variable set by CI/CD -# This will be set by setuptools-git-versioning in the CI pipeline package_version = environ.get("AGENT365_PYTHON_SDK_PACKAGE_VERSION", "0.0.0") +# Add versioning helper to path +helper_path = Path(__file__).parent.parent.parent / "versioning" / "helper" +sys.path.insert(0, str(helper_path)) + +from setup_utils import get_dynamic_dependencies + +# Use minimum version strategy: +# - Internal packages get: >= current_base_version (e.g., >= 0.1.0) +# - Automatically updates when you build new versions +# - Consumers can upgrade to any higher version setup( version=package_version, + install_requires=get_dynamic_dependencies( + use_compatible_release=False, # No upper bound + use_exact_match=False, # Not exact match + ), ) diff --git a/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/pyproject.toml b/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/pyproject.toml index 1f93b382..f495323a 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/pyproject.toml +++ b/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ ] license = {text = "MIT"} dependencies = [ - "microsoft-agents-a365-tooling >= 0.1.0", + "microsoft-agents-a365-tooling >= 0.0.0", "semantic-kernel >= 1.0.0", "aiohttp >= 3.8.0", ] diff --git a/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/setup.py b/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/setup.py index a04ba9a5..1c812d3b 100644 --- a/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/setup.py +++ b/libraries/microsoft-agents-a365-tooling-extensions-semantickernel/setup.py @@ -1,13 +1,28 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +import sys +from pathlib import Path from os import environ from setuptools import setup # Get version from environment variable set by CI/CD -# This will be set by setuptools-git-versioning in the CI pipeline package_version = environ.get("AGENT365_PYTHON_SDK_PACKAGE_VERSION", "0.0.0") +# Add versioning helper to path +helper_path = Path(__file__).parent.parent.parent / "versioning" / "helper" +sys.path.insert(0, str(helper_path)) + +from setup_utils import get_dynamic_dependencies + +# Use minimum version strategy: +# - Internal packages get: >= current_base_version (e.g., >= 0.1.0) +# - Automatically updates when you build new versions +# - Consumers can upgrade to any higher version setup( version=package_version, + install_requires=get_dynamic_dependencies( + use_compatible_release=False, # No upper bound + use_exact_match=False, # Not exact match + ), ) diff --git a/libraries/microsoft-agents-a365-tooling/setup.py b/libraries/microsoft-agents-a365-tooling/setup.py index a04ba9a5..1c812d3b 100644 --- a/libraries/microsoft-agents-a365-tooling/setup.py +++ b/libraries/microsoft-agents-a365-tooling/setup.py @@ -1,13 +1,28 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +import sys +from pathlib import Path from os import environ from setuptools import setup # Get version from environment variable set by CI/CD -# This will be set by setuptools-git-versioning in the CI pipeline package_version = environ.get("AGENT365_PYTHON_SDK_PACKAGE_VERSION", "0.0.0") +# Add versioning helper to path +helper_path = Path(__file__).parent.parent.parent / "versioning" / "helper" +sys.path.insert(0, str(helper_path)) + +from setup_utils import get_dynamic_dependencies + +# Use minimum version strategy: +# - Internal packages get: >= current_base_version (e.g., >= 0.1.0) +# - Automatically updates when you build new versions +# - Consumers can upgrade to any higher version setup( version=package_version, + install_requires=get_dynamic_dependencies( + use_compatible_release=False, # No upper bound + use_exact_match=False, # Not exact match + ), ) diff --git a/versioning/helper/__init__.py b/versioning/helper/__init__.py new file mode 100644 index 00000000..0f4930f9 --- /dev/null +++ b/versioning/helper/__init__.py @@ -0,0 +1,18 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +"""Agent365 Python SDK setup utilities.""" + +from .setup_utils import ( + get_package_version, + get_dynamic_dependencies, + get_base_version, + get_next_major_version, +) + +__all__ = [ + "get_package_version", + "get_dynamic_dependencies", + "get_base_version", + "get_next_major_version", +] diff --git a/versioning/helper/setup_utils.py b/versioning/helper/setup_utils.py new file mode 100644 index 00000000..0ae10711 --- /dev/null +++ b/versioning/helper/setup_utils.py @@ -0,0 +1,235 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +""" +Shared utilities for setup.py files across all Agent365 Python SDK packages. + +This module provides helper functions to dynamically set internal package versions +at build time, ensuring all packages in the monorepo use the exact same version. +""" + +from os import environ +from typing import List + + +def get_package_version() -> str: + """ + Get the package version from environment variable. + + Returns: + The version string from AGENT365_PYTHON_SDK_PACKAGE_VERSION environment variable, + or "0.0.0" if not set. + """ + return environ.get("AGENT365_PYTHON_SDK_PACKAGE_VERSION", "0.0.0") + + +def get_base_version(version_string: str) -> str: + """ + Extract the base version (major.minor.micro) from a version string, + removing any dev, alpha, beta, rc, or post-release suffixes. + + Uses the packaging library for robust PEP 440 compliant parsing. + + Examples: + "0.1.0.dev5" -> "0.1.0" + "0.2.0" -> "0.2.0" + "1.0.0a1" -> "1.0.0" + "1.2.3b2" -> "1.2.3" + "2.0.0rc1" -> "2.0.0" + "1.a.3" -> "1.0.3" (handles edge cases correctly) + + Args: + version_string: Version string to parse + + Returns: + Base version string in format "major.minor.micro" + """ + try: + from packaging.version import Version + + # Parse the version using packaging library (PEP 440 compliant) + parsed = Version(version_string) + + # Return base release version (major.minor.micro) + return f"{parsed.major}.{parsed.minor}.{parsed.micro}" + except Exception: + # Fallback to simple parsing if packaging fails or isn't available + # Remove known suffixes (less robust but works for simple cases) + import re + + # Match version up to dev/alpha/beta/rc suffixes + match = re.match(r"^(\d+\.\d+\.\d+)", version_string) + if match: + return match.group(1) + # Last resort: return as-is + return version_string + + +def get_next_major_version(base_version: str) -> str: + """ + Calculate the next major version for upper bound in compatible release. + + For 0.x.y versions, increments minor (0.1.0 -> 0.2.0) + For x.y.z versions where x > 0, increments major (1.2.3 -> 2.0.0) + + Args: + base_version: Base version string (e.g., "0.1.0" or "1.2.3") + + Returns: + Next major version string + """ + try: + from packaging.version import Version + + parsed = Version(base_version) + + if parsed.major == 0: + # For 0.x.y, increment minor version (0.1.0 -> 0.2.0) + return f"{parsed.major}.{parsed.minor + 1}.0" + else: + # For x.y.z where x > 0, increment major (1.2.3 -> 2.0.0) + return f"{parsed.major + 1}.0.0" + except Exception: + # Fallback to string parsing + parts = base_version.split(".") + if len(parts) >= 3: + major = int(parts[0]) + if major == 0: + minor = int(parts[1]) + return f"{major}.{minor + 1}.0" + else: + return f"{major + 1}.0.0" + return base_version + + +def get_dynamic_dependencies( + pyproject_path: str = "pyproject.toml", + use_exact_match: bool = False, + use_compatible_release: bool = False, +) -> List[str]: + """ + Read dependencies from pyproject.toml and update internal package versions. + + Internal packages (microsoft-agents-a365-*) can use different versioning strategies: + + 1. Minimum version (default, recommended): + >= base_version + Example: >= 0.1.0 + - Maximum flexibility for consumers + + 2. Compatible release: + >= base_version, < next_major_version + Example: >= 0.1.0, < 0.2.0 + - Allows updates within major version + + 3. Exact match: + == current_version + Example: == 0.1.0.dev5 + - Forces exact version match + + External packages keep their original version constraints. + + Args: + pyproject_path: Path to the pyproject.toml file (default: "pyproject.toml") + use_exact_match: If True, use == for internal packages + use_compatible_release: If True, use >= with < upper bound + + Returns: + List of dependency strings with updated internal package versions + + Raises: + FileNotFoundError: If pyproject.toml doesn't exist + ValueError: If pyproject.toml is invalid or missing required fields + """ + package_version = get_package_version() + + # Extract base version using robust parsing + base_version = get_base_version(package_version) + + # Load TOML library (Python 3.11+ has built-in, older versions need tomli) + try: + import tomllib # Python 3.11+ + except ImportError: + try: + import tomli as tomllib # Fallback for older Python + except ImportError: + raise ImportError( + "Failed to import TOML library. For Python < 3.11, please install tomli: " + "pip install tomli" + ) + + # Read and parse pyproject.toml with comprehensive error handling + try: + with open(pyproject_path, "rb") as f: + pyproject = tomllib.load(f) + except FileNotFoundError: + raise FileNotFoundError( + f"Could not find {pyproject_path}. " + f"Ensure the file exists in the expected location. " + f"Current working directory may be incorrect." + ) + except PermissionError: + raise PermissionError( + f"Permission denied reading {pyproject_path}. Check file permissions." + ) + except Exception as e: + # Catch TOML decode errors (attribute may vary by library) + if "TOML" in type(e).__name__ or "Decode" in type(e).__name__: + raise ValueError( + f"Invalid TOML syntax in {pyproject_path}: {e}. " + f"Please check the file for syntax errors." + ) + # Re-raise unexpected errors + raise RuntimeError(f"Unexpected error reading {pyproject_path}: {type(e).__name__}: {e}") + + # Validate pyproject.toml structure + if "project" not in pyproject: + raise ValueError( + f"{pyproject_path} is missing [project] section. " + f"This is required for PEP 621 compliant packages." + ) + + dependencies = pyproject.get("project", {}).get("dependencies", []) + + # Warn if no dependencies found (might be intentional, so don't fail) + if not dependencies: + # Note: Using print instead of logging to avoid additional dependencies + # In production, consider using logging module + import sys + + print( + f"Warning: No dependencies found in {pyproject_path}. " + f"This may be intentional for packages with no dependencies.", + file=sys.stderr, + ) + + # Update internal package versions dynamically + updated_dependencies = [] + for dep in dependencies: + if not isinstance(dep, str): + # Skip non-string dependencies (shouldn't happen, but be defensive) + print( + f"Warning: Skipping non-string dependency: {dep}", + file=sys.stderr, + ) + continue + + if dep.startswith("microsoft-agents-a365-"): + # Extract package name (everything before >=, ==, or other operators) + pkg_name = dep.split(">=")[0].split("==")[0].split("<")[0].strip() + + if use_exact_match: + # Exact match: == current_version + updated_dependencies.append(f"{pkg_name} == {package_version}") + elif use_compatible_release: + # Compatible release: >= base_version, < next_major + next_major = get_next_major_version(base_version) + updated_dependencies.append(f"{pkg_name} >= {base_version}, < {next_major}") + else: + # Minimum version (default): >= base_version + updated_dependencies.append(f"{pkg_name} >= {base_version}") + else: + # Keep external dependencies as-is + updated_dependencies.append(dep) + + return updated_dependencies