diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index f00b8d74..a83159c0 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -76,6 +76,11 @@ jobs: cd ecosystem-automation/explorer-db-builder uv run pytest tests/ --cov=explorer_db_builder --cov-report=term-missing --cov-report=json + - name: Run dotnet-instrumentation-watcher tests + run: | + cd ecosystem-automation/dotnet-instrumentation-watcher + uv run pytest tests/ --cov=dotnet_instrumentation_watcher --cov-report=term-missing --cov-report=json + test-ecosystem-explorer: runs-on: ubuntu-latest defaults: diff --git a/.github/workflows/nightly-registry-update.yml b/.github/workflows/nightly-registry-update.yml index 36bc4e97..2114932b 100644 --- a/.github/workflows/nightly-registry-update.yml +++ b/.github/workflows/nightly-registry-update.yml @@ -22,6 +22,7 @@ jobs: outputs: collector_result: ${{ steps.collector_watcher.outcome }} java_result: ${{ steps.java_instrumentation_watcher.outcome }} + dotnet_result: ${{ steps.dotnet_instrumentation_watcher.outcome }} configuration_result: ${{ steps.configuration_watcher.outcome }} steps: - name: Checkout code @@ -140,6 +141,12 @@ jobs: run: uv run java-instrumentation-watcher continue-on-error: true + - name: Run dotnet-instrumentation-watcher + id: dotnet_instrumentation_watcher + if: always() + run: uv run dotnet-instrumentation-watcher + continue-on-error: true + - name: Run configuration-watcher id: configuration_watcher if: always() @@ -286,3 +293,14 @@ jobs: with: success: ${{ needs.synchronize-inventory.outputs.configuration_result == 'success' }} watcher-name: "configuration-watcher" + + notify-dotnet: + permissions: + contents: read + issues: write + needs: [synchronize-inventory] + if: ${{ !cancelled() && needs.synchronize-inventory.result != 'skipped' }} + uses: ./.github/workflows/reusable-workflow-notification.yml + with: + success: ${{ needs.synchronize-inventory.outputs.dotnet_result == 'success' }} + watcher-name: "dotnet-instrumentation-watcher" diff --git a/ecosystem-automation/configuration-watcher/tests/__init__.py b/ecosystem-automation/configuration-watcher/tests/__init__.py deleted file mode 100644 index 131377bc..00000000 --- a/ecosystem-automation/configuration-watcher/tests/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# diff --git a/ecosystem-automation/dotnet-instrumentation-watcher/pyproject.toml b/ecosystem-automation/dotnet-instrumentation-watcher/pyproject.toml new file mode 100644 index 00000000..be967d77 --- /dev/null +++ b/ecosystem-automation/dotnet-instrumentation-watcher/pyproject.toml @@ -0,0 +1,30 @@ +[project] +name = "dotnet-instrumentation-watcher" +version = "0.1.0" +description = "Automation tool for watching and collecting OpenTelemetry .NET instrumentation metadata" +requires-python = ">=3.11" +dependencies = [ + "PyYAML>=6.0.1", + "requests>=2.31.0", + "semantic-version>=2.10.0", + "watcher-common", +] + +[project.scripts] +dotnet-instrumentation-watcher = "dotnet_instrumentation_watcher.main:main" + +[project.optional-dependencies] +dev = [ + "pytest>=8.0.0", + "pytest-cov>=4.1.0", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.uv.sources] +watcher-common = { workspace = true } + +[tool.hatch.build.targets.wheel] +packages = ["src/dotnet_instrumentation_watcher"] diff --git a/ecosystem-automation/collector-watcher/tests/__init__.py b/ecosystem-automation/dotnet-instrumentation-watcher/src/dotnet_instrumentation_watcher/__init__.py similarity index 92% rename from ecosystem-automation/collector-watcher/tests/__init__.py rename to ecosystem-automation/dotnet-instrumentation-watcher/src/dotnet_instrumentation_watcher/__init__.py index 232bc78c..9645902c 100644 --- a/ecosystem-automation/collector-watcher/tests/__init__.py +++ b/ecosystem-automation/dotnet-instrumentation-watcher/src/dotnet_instrumentation_watcher/__init__.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -"""Tests for collector-watcher.""" +"""Dotnet Instrumentation Watcher package.""" diff --git a/ecosystem-automation/dotnet-instrumentation-watcher/src/dotnet_instrumentation_watcher/dotnet_client.py b/ecosystem-automation/dotnet-instrumentation-watcher/src/dotnet_instrumentation_watcher/dotnet_client.py new file mode 100644 index 00000000..bc753cf5 --- /dev/null +++ b/ecosystem-automation/dotnet-instrumentation-watcher/src/dotnet_instrumentation_watcher/dotnet_client.py @@ -0,0 +1,184 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +"""NuGet API client for fetching .NET instrumentation data.""" + +import logging +from typing import Any, Dict, List + +import requests +from requests.adapters import HTTPAdapter +from urllib3 import Retry + +logger = logging.getLogger(__name__) + + +class NuGetAPIError(Exception): + """Custom exception for NuGet API errors.""" + + pass + + +class DotNetInstrumentationClient: + """Client for fetching .NET instrumentation metadata from NuGet.""" + + SERVICE_INDEX_URL = "https://api.nuget.org/v3/index.json" + OWNER = "OpenTelemetry" + TIMEOUT = 30 + + def __init__(self): + """Initialize the client.""" + self._session = requests.Session() + self._search_url = None + + retry_strategy = Retry( + total=3, + backoff_factor=1, + status_forcelist=[429, 500, 502, 503, 504], + ) + + adapter = HTTPAdapter(max_retries=retry_strategy) + self._session.mount("https://", adapter) + + def _get_search_url(self) -> str: + """Resolve the search URL from the NuGet service index. + + Raises: + NuGetAPIError: If the service index cannot be fetched or does not + contain a SearchQueryService resource. + """ + if self._search_url: + return self._search_url + + try: + response = self._session.get(self.SERVICE_INDEX_URL, timeout=self.TIMEOUT) + response.raise_for_status() + index_data = response.json() + except requests.RequestException as e: + raise NuGetAPIError(f"Error fetching NuGet service index: {e}") from e + + for resource in index_data.get("resources", []): + if resource.get("@type") == "SearchQueryService": + self._search_url = resource["@id"] + return self._search_url + + raise NuGetAPIError("NuGet service index did not contain a SearchQueryService resource") + + def fetch_instrumentation_list(self) -> Dict[str, Any]: + """ + Fetch instrumentation list by querying NuGet for packages owned by OpenTelemetry. + + The top-level ``version`` field in each search result entry is the latest + version of the package as reported by NuGet — no local sorting is needed. + """ + all_packages = self._fetch_all_packages_by_owner(self.OWNER) + modules = [] + + for pkg in all_packages: + package_id = pkg.get("id", "") + + # Skip packages flagged as deprecated by NuGet (includes Contrib packages). + if pkg.get("deprecation"): + logger.info(f" Skipping deprecated package: {package_id}") + continue + + # The top-level "version" field is the latest version returned by the + # NuGet search API — rely on the server ordering rather than sorting locally. + version = pkg.get("version", "") + description = pkg.get("description", "") + + # Filter and classify packages + if "Instrumentation" in package_id: + component_type = "instrumentation" + elif "Exporter" in package_id: + component_type = "exporter" + elif "Extensions" in package_id or "Resources" in package_id or "Sampler" in package_id: + component_type = "extension" + else: + # Skip core and unclassified packages. + continue + + modules.append( + { + "name": package_id, + "description": description or f"{package_id} for OpenTelemetry", + "type": component_type, + "version": version, + } + ) + + # Sort by name for deterministic registry output. + modules.sort(key=lambda x: x["name"]) + + return {"modules": modules} + + def get_core_version(self) -> str: + """Get the latest stable version of the core OpenTelemetry package. + + This is used as the 'ecosystem version' for the registry. + + Raises: + NuGetAPIError: If the version cannot be determined. + """ + params = { + "q": "PackageId:OpenTelemetry", + "prerelease": "false", + "semVerLevel": "2.0.0", + "take": 1, + } + try: + search_url = self._get_search_url() + response = self._session.get(search_url, params=params, timeout=self.TIMEOUT) + response.raise_for_status() + data = response.json() + results = data.get("data", []) + if not results: + raise NuGetAPIError("No results returned for core OpenTelemetry package") + return results[0]["version"] + except (KeyError, IndexError) as e: + raise NuGetAPIError(f"Unexpected response shape fetching core version: {e}") from e + except requests.RequestException as e: + raise NuGetAPIError(f"Error fetching core version: {e}") from e + + def _fetch_all_packages_by_owner(self, owner: str) -> List[Dict[str, Any]]: + """Fetch all packages for a specific owner using pagination.""" + packages = [] + skip = 0 + take = 20 + + while True: + params = { + "q": f"owner:{owner}", + "prerelease": "true", + "semVerLevel": "2.0.0", + "skip": skip, + "take": take, + } + try: + search_url = self._get_search_url() + response = self._session.get(search_url, params=params, timeout=self.TIMEOUT) + response.raise_for_status() + data = response.json() + + batch = data.get("data", []) + packages.extend(batch) + + if len(batch) < take: + break + + skip += take + except requests.RequestException as e: + raise NuGetAPIError(f"Error fetching packages from NuGet: {e}") from e + + return packages diff --git a/ecosystem-automation/dotnet-instrumentation-watcher/src/dotnet_instrumentation_watcher/instrumentation_sync.py b/ecosystem-automation/dotnet-instrumentation-watcher/src/dotnet_instrumentation_watcher/instrumentation_sync.py new file mode 100644 index 00000000..046eb7a0 --- /dev/null +++ b/ecosystem-automation/dotnet-instrumentation-watcher/src/dotnet_instrumentation_watcher/instrumentation_sync.py @@ -0,0 +1,141 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +"""Synchronization orchestration for .NET instrumentation metadata.""" + +import logging +from typing import Any + +from semantic_version import Version + +from .dotnet_client import DotNetInstrumentationClient +from .inventory_manager import InventoryManager + +logger = logging.getLogger(__name__) + + +class InstrumentationSync: + """Orchestrates synchronization of .NET instrumentation metadata.""" + + def __init__( + self, + client: DotNetInstrumentationClient, + inventory_manager: InventoryManager, + ): + """ + Args: + client: NuGet API client for fetching data + inventory_manager: Inventory manager for storing data + """ + self.client = client + self.inventory_manager = inventory_manager + + def sync(self) -> dict[str, Any]: + """ + Synchronize .NET instrumentation metadata. + + This will: + 1. Process the latest release (if new) + 2. Update the snapshot from main branch + + Returns: + Summary dictionary with processing results + """ + summary = { + "new_release": None, + "snapshot_updated": None, + } + + logger.info("Checking for latest release...") + new_release = self.process_latest_release() + if new_release: + summary["new_release"] = str(new_release) + logger.info(f"[*] Processed new release: {new_release}") + else: + logger.info("[*] Latest release already tracked") + + logger.info("Updating snapshot from main branch...") + snapshot_version = self.update_snapshot() + summary["snapshot_updated"] = str(snapshot_version) + logger.info(f"[*] Updated snapshot: {snapshot_version}") + + return summary + + def process_latest_release(self) -> Version | None: + """ + Process the latest release if not already tracked. + + Returns: + Version if newly processed, None if already exists + """ + version_string = self.client.get_core_version() + logger.info(f" Latest core package version: {version_string}") + + try: + version = Version(version_string) + except ValueError as e: + raise ValueError(f"Invalid core version string: {version_string!r}") from e + + if self.inventory_manager.version_exists(version): + return None + + logger.info(f" Fetching instrumentation list for version {version_string}...") + instrumentations = self.client.fetch_instrumentation_list() + + self.inventory_manager.save_versioned_inventory( + version=version, + instrumentations=instrumentations, + ) + + return version + + def update_snapshot(self) -> Version: + """ + Update snapshot version from NuGet data. + + This will: + 1. Determine next snapshot version + 2. Fetch from NuGet + 3. Clean up old snapshots + 4. Save new snapshot + + Returns: + The snapshot version + """ + latest_version_string = self.client.get_core_version() + try: + latest_version = Version(latest_version_string) + except ValueError as e: + raise ValueError(f"Could not resolve a valid core version: {latest_version_string!r}") from e + + snapshot_version = Version( + major=latest_version.major, + minor=latest_version.minor, + patch=latest_version.patch + 1, + prerelease=("SNAPSHOT",), + ) + + logger.info(" Fetching instrumentation list from NuGet...") + instrumentations = self.client.fetch_instrumentation_list() + + removed = self.inventory_manager.cleanup_snapshots() + if removed > 0: + logger.info(f" Removed {removed} old snapshot(s)") + + self.inventory_manager.save_versioned_inventory( + version=snapshot_version, + instrumentations=instrumentations, + ) + + return snapshot_version diff --git a/ecosystem-automation/dotnet-instrumentation-watcher/src/dotnet_instrumentation_watcher/inventory_manager.py b/ecosystem-automation/dotnet-instrumentation-watcher/src/dotnet_instrumentation_watcher/inventory_manager.py new file mode 100644 index 00000000..92c7c92b --- /dev/null +++ b/ecosystem-automation/dotnet-instrumentation-watcher/src/dotnet_instrumentation_watcher/inventory_manager.py @@ -0,0 +1,87 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +"""Inventory management for .NET instrumentation tracking.""" + +from typing import Any + +import yaml +from semantic_version import Version +from watcher_common.inventory_manager import BaseInventoryManager + + +class InventoryManager(BaseInventoryManager): + """Manages .NET instrumentation inventory storage and retrieval.""" + + FILE_NAME = "instrumentation.yaml" + + def __init__(self, inventory_dir: str = "ecosystem-registry/dotnet"): + """ + Args: + inventory_dir: Base directory for versioned metadata + """ + super().__init__(inventory_dir) + + def version_exists(self, version: Version) -> bool: + """ + Check if a specific version exists. + + Args: + version: Version to check + + Returns: + True if version directory and instrumentation file exist + """ + version_dir = self.get_version_dir(version) + return version_dir.exists() and (version_dir / self.FILE_NAME).exists() + + def save_versioned_inventory(self, version: Version, instrumentations: dict[str, Any]) -> None: + """ + Save inventory for a specific version. + + Args: + version: Version object + instrumentations: Instrumentation data dict + """ + version_dir = self.get_version_dir(version) + version_dir.mkdir(parents=True, exist_ok=True) + + file_path = version_dir / self.FILE_NAME + + inventory_data = { + **instrumentations, + } + + with open(file_path, "w") as f: + yaml.dump(inventory_data, f, default_flow_style=False, sort_keys=False, allow_unicode=True) + + def load_versioned_inventory(self, version: Version) -> dict[str, Any]: + """ + Load inventory for a specific version. + + Args: + version: Version object + + Returns: + Inventory dictionary with full structure, or empty structure if it doesn't exist + """ + version_dir = self.get_version_dir(version) + file_path = version_dir / self.FILE_NAME + + if not file_path.exists(): + return {"modules": []} + + with open(file_path) as f: + data = yaml.safe_load(f) or {} + return data diff --git a/ecosystem-automation/dotnet-instrumentation-watcher/src/dotnet_instrumentation_watcher/main.py b/ecosystem-automation/dotnet-instrumentation-watcher/src/dotnet_instrumentation_watcher/main.py new file mode 100644 index 00000000..c38d0b33 --- /dev/null +++ b/ecosystem-automation/dotnet-instrumentation-watcher/src/dotnet_instrumentation_watcher/main.py @@ -0,0 +1,82 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +"""Main entry point for dotnet instrumentation watcher.""" + +import argparse +import logging +import sys + +from .dotnet_client import DotNetInstrumentationClient +from .instrumentation_sync import InstrumentationSync +from .inventory_manager import InventoryManager + +logger = logging.getLogger(__name__) + + +def configure_logging(): + """Configure logging to output to stdout.""" + logging.basicConfig( + level=logging.INFO, + format="%(message)s", + handlers=[logging.StreamHandler(sys.stdout)], + ) + + +def main(): + """Synchronize dotnet instrumentation metadata to the registry.""" + configure_logging() + + parser = argparse.ArgumentParser( + description="Synchronize .NET instrumentation metadata to the registry", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "--inventory-dir", + default="ecosystem-registry/dotnet", + help="Directory path for the inventory", + ) + args = parser.parse_args() + + logger.info("=" * 60) + logger.info(".NET Instrumentation Watcher") + logger.info("=" * 60) + logger.info(f"Inventory directory: {args.inventory_dir}") + logger.info("") + + try: + client = DotNetInstrumentationClient() + inventory_manager = InventoryManager(inventory_dir=args.inventory_dir) + + sync = InstrumentationSync(client, inventory_manager) + summary = sync.sync() + + logger.info("") + logger.info("=" * 60) + logger.info("Sync Summary") + logger.info("=" * 60) + if summary["new_release"]: + logger.info(f"[*] New release processed: {summary['new_release']}") + else: + logger.info("[*] No new releases") + logger.info(f"[*] Snapshot updated: {summary['snapshot_updated']}") + logger.info("") + + except Exception as e: + logger.exception(f"Failed to sync: {e}") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/ecosystem-automation/dotnet-instrumentation-watcher/tests/test_dotnet_client.py b/ecosystem-automation/dotnet-instrumentation-watcher/tests/test_dotnet_client.py new file mode 100644 index 00000000..45d49884 --- /dev/null +++ b/ecosystem-automation/dotnet-instrumentation-watcher/tests/test_dotnet_client.py @@ -0,0 +1,126 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from unittest.mock import MagicMock, patch + +import pytest +from dotnet_instrumentation_watcher.dotnet_client import ( + DotNetInstrumentationClient, + NuGetAPIError, +) +from requests import RequestException + + +def _make_index_response(): + """Return a mock NuGet service index response.""" + mock = MagicMock() + mock.json.return_value = {"resources": [{"@id": "https://api.test/query", "@type": "SearchQueryService"}]} + return mock + + +def test_get_core_version_success(): + client = DotNetInstrumentationClient() + with patch.object(client._session, "get") as mock_get: + mock_get.side_effect = [ + _make_index_response(), + MagicMock(**{"json.return_value": {"data": [{"version": "1.15.3"}]}}), + ] + + version = client.get_core_version() + assert version == "1.15.3" + assert mock_get.call_count == 2 + + +def test_get_core_version_raises_on_api_error(): + """get_core_version should propagate NuGetAPIError instead of returning a fallback.""" + client = DotNetInstrumentationClient() + with patch.object(client._session, "get") as mock_get: + mock_get.side_effect = RequestException("API error") + with pytest.raises(NuGetAPIError): + client.get_core_version() + + +def test_get_search_url_raises_when_service_index_missing_resource(): + """_get_search_url should raise NuGetAPIError if no SearchQueryService is in the index.""" + client = DotNetInstrumentationClient() + with patch.object(client._session, "get") as mock_get: + mock_resp = MagicMock() + mock_resp.json.return_value = {"resources": []} # no SearchQueryService entry + mock_get.return_value = mock_resp + with pytest.raises(NuGetAPIError, match="SearchQueryService"): + client._get_search_url() + + +def test_fetch_instrumentation_list(): + """Packages are filtered by deprecation flag only; prerelease versions are supported.""" + client = DotNetInstrumentationClient() + with patch.object(client._session, "get") as mock_get: + mock_search_response = MagicMock() + mock_search_response.json.return_value = { + "data": [ + # Stable instrumentation + { + "id": "OpenTelemetry.Instrumentation.Test", + "version": "1.0.0", + "description": "Test instrumentation", + }, + # Stable exporter + { + "id": "OpenTelemetry.Exporter.Test", + "version": "1.1.0", + "description": "Test exporter", + }, + # Extension with a prerelease version (e.g. 1.2.3-preview.4) + { + "id": "OpenTelemetry.Extensions.Test", + "version": "1.2.3-preview.4", + "description": "Test extension", + }, + # Deprecated by NuGet — must be skipped + { + "id": "OpenTelemetry.Exporter.Deprecated", + "version": "0.1.0", + "deprecation": {"reasons": ["Legacy"]}, + }, + # Contrib package — deprecated in NuGet, so covered by the deprecation check + { + "id": "OpenTelemetry.Contrib.Instrumentation.Legacy", + "version": "1.0.0", + "description": "Legacy contrib package", + "deprecation": {"reasons": ["Legacy"]}, + }, + ] + } + mock_get.side_effect = [_make_index_response(), mock_search_response] + + result = client.fetch_instrumentation_list() + assert "modules" in result + # 3 valid packages (1 deprecated + 1 deprecated contrib are skipped) + assert len(result["modules"]) == 3 + + types = [m["type"] for m in result["modules"]] + assert "instrumentation" in types + assert "exporter" in types + assert "extension" in types + + # Sorted by name: Exporter.Test, Extensions.Test, Instrumentation.Test + assert result["modules"][0]["name"] == "OpenTelemetry.Exporter.Test" + assert result["modules"][0]["version"] == "1.1.0" + + # Extension carries a prerelease version — must be preserved as-is + assert result["modules"][1]["name"] == "OpenTelemetry.Extensions.Test" + assert result["modules"][1]["version"] == "1.2.3-preview.4" + + assert result["modules"][2]["name"] == "OpenTelemetry.Instrumentation.Test" + assert result["modules"][2]["version"] == "1.0.0" diff --git a/ecosystem-automation/dotnet-instrumentation-watcher/tests/test_instrumentation_sync.py b/ecosystem-automation/dotnet-instrumentation-watcher/tests/test_instrumentation_sync.py new file mode 100644 index 00000000..9ee42b54 --- /dev/null +++ b/ecosystem-automation/dotnet-instrumentation-watcher/tests/test_instrumentation_sync.py @@ -0,0 +1,67 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from unittest.mock import MagicMock + +import pytest +from dotnet_instrumentation_watcher.instrumentation_sync import InstrumentationSync +from semantic_version import Version + + +@pytest.fixture +def mock_client(): + client = MagicMock() + return client + + +@pytest.fixture +def mock_inventory(): + inventory = MagicMock() + return inventory + + +def test_process_latest_release(mock_client, mock_inventory): + mock_client.get_core_version.return_value = "1.2.3" + mock_inventory.version_exists.return_value = False + mock_client.fetch_instrumentation_list.return_value = {"modules": []} + + sync = InstrumentationSync(mock_client, mock_inventory) + result = sync.process_latest_release() + + assert result == Version("1.2.3") + mock_inventory.save_versioned_inventory.assert_called_once() + + +def test_process_latest_release_already_exists(mock_client, mock_inventory): + mock_client.get_core_version.return_value = "1.2.3" + mock_inventory.version_exists.return_value = True + + sync = InstrumentationSync(mock_client, mock_inventory) + result = sync.process_latest_release() + + assert result is None + mock_inventory.save_versioned_inventory.assert_not_called() + + +def test_update_snapshot(mock_client, mock_inventory): + mock_client.get_core_version.return_value = "1.2.3" + mock_client.fetch_instrumentation_list.return_value = {"modules": []} + mock_inventory.cleanup_snapshots.return_value = 1 + + sync = InstrumentationSync(mock_client, mock_inventory) + result = sync.update_snapshot() + + assert result == Version("1.2.4-SNAPSHOT") + mock_inventory.cleanup_snapshots.assert_called_once() + mock_inventory.save_versioned_inventory.assert_called_once() diff --git a/ecosystem-automation/dotnet-instrumentation-watcher/tests/test_inventory_manager.py b/ecosystem-automation/dotnet-instrumentation-watcher/tests/test_inventory_manager.py new file mode 100644 index 00000000..0024ee9a --- /dev/null +++ b/ecosystem-automation/dotnet-instrumentation-watcher/tests/test_inventory_manager.py @@ -0,0 +1,57 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +import os +import tempfile + +import pytest +from dotnet_instrumentation_watcher.inventory_manager import InventoryManager +from semantic_version import Version + + +@pytest.fixture +def temp_workspace(): + with tempfile.TemporaryDirectory() as temp_dir: + # Create mock workspace + registry_dir = os.path.join(temp_dir, "ecosystem-registry", "dotnet") + os.makedirs(registry_dir) + yield temp_dir + + +def test_has_version(temp_workspace): + manager = InventoryManager(inventory_dir=temp_workspace) + assert manager.version_exists(Version("1.0.0")) is False + + +def test_save_and_list_versions(temp_workspace): + manager = InventoryManager(inventory_dir=temp_workspace) + + mock_data = {"modules": [{"name": "test"}]} + manager.save_versioned_inventory(Version("1.0.0"), mock_data) + + assert manager.version_exists(Version("1.0.0")) is True + versions = manager.list_versions() + assert Version("1.0.0") in versions + + +def test_cleanup_snapshots(temp_workspace): + manager = InventoryManager(inventory_dir=temp_workspace) + + mock_data = {"modules": []} + manager.save_versioned_inventory(Version("1.0.0-SNAPSHOT"), mock_data) + manager.save_versioned_inventory(Version("1.0.1-SNAPSHOT"), mock_data) + + manager.cleanup_snapshots() + assert not manager.version_exists(Version("1.0.0-SNAPSHOT")) + assert not manager.version_exists(Version("1.0.1-SNAPSHOT")) diff --git a/ecosystem-automation/dotnet-instrumentation-watcher/tests/test_main.py b/ecosystem-automation/dotnet-instrumentation-watcher/tests/test_main.py new file mode 100644 index 00000000..5db1a84b --- /dev/null +++ b/ecosystem-automation/dotnet-instrumentation-watcher/tests/test_main.py @@ -0,0 +1,42 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from unittest.mock import patch + +import pytest +from dotnet_instrumentation_watcher.main import main + + +@patch("dotnet_instrumentation_watcher.main.DotNetInstrumentationClient") +@patch("dotnet_instrumentation_watcher.main.InventoryManager") +@patch("dotnet_instrumentation_watcher.main.InstrumentationSync") +def test_main_success(mock_sync, mock_inventory, mock_client): + mock_sync_instance = mock_sync.return_value + mock_sync_instance.sync.return_value = {"new_release": "1.0.0", "snapshot_updated": "1.0.1-SNAPSHOT"} + + with patch("sys.argv", ["dotnet-instrumentation-watcher"]): + assert main() is None + + +@patch("dotnet_instrumentation_watcher.main.DotNetInstrumentationClient") +@patch("dotnet_instrumentation_watcher.main.InventoryManager") +@patch("dotnet_instrumentation_watcher.main.InstrumentationSync") +def test_main_failure(mock_sync, mock_inventory, mock_client): + mock_sync_instance = mock_sync.return_value + mock_sync_instance.sync.side_effect = Exception("Test error") + + with patch("sys.argv", ["dotnet-instrumentation-watcher"]): + with pytest.raises(SystemExit) as excinfo: + main() + assert excinfo.value.code == 1 diff --git a/ecosystem-automation/explorer-db-builder/src/explorer_db_builder/instrumentation_transformer.py b/ecosystem-automation/explorer-db-builder/src/explorer_db_builder/instrumentation_transformer.py index 21402367..eed34e38 100644 --- a/ecosystem-automation/explorer-db-builder/src/explorer_db_builder/instrumentation_transformer.py +++ b/ecosystem-automation/explorer-db-builder/src/explorer_db_builder/instrumentation_transformer.py @@ -91,7 +91,7 @@ def _transform_0_1_to_0_2(inventory_data: dict[str, Any]) -> dict[str, Any]: Returns: Transformed inventory data in format 0.2 """ - if "libraries" not in inventory_data: + if "libraries" not in inventory_data or inventory_data["libraries"] is None: raise KeyError("Inventory data missing 'libraries' key") transformed_data = inventory_data.copy() @@ -143,6 +143,7 @@ def _transform_0_2_to_0_3(inventory_data: dict[str, Any]) -> dict[str, Any]: Returns: Transformed inventory data in format 0.3 """ + transformed_data = inventory_data.copy() if inventory_data.get("libraries") is not None: diff --git a/ecosystem-automation/explorer-db-builder/src/explorer_db_builder/main.py b/ecosystem-automation/explorer-db-builder/src/explorer_db_builder/main.py index f8aba690..43472143 100644 --- a/ecosystem-automation/explorer-db-builder/src/explorer_db_builder/main.py +++ b/ecosystem-automation/explorer-db-builder/src/explorer_db_builder/main.py @@ -188,7 +188,7 @@ def load_and_augment_inventory(version: Version) -> dict: logger.info(f" Files written: {stats['files_written']}") logger.info(f" Total size: {stats['total_bytes']:,} bytes ({total_mb:.2f} MB)") logger.info("") - logger.info("✓ Database build completed successfully") + logger.info("[*] Database build completed successfully") return 0 except ValueError as e: diff --git a/ecosystem-automation/explorer-db-builder/tests/__init__.py b/ecosystem-automation/explorer-db-builder/tests/__init__.py deleted file mode 100644 index 145bf306..00000000 --- a/ecosystem-automation/explorer-db-builder/tests/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -"""Tests for explorer-db-builder.""" diff --git a/ecosystem-automation/explorer-db-builder/tests/test_instrumentation_transformer.py b/ecosystem-automation/explorer-db-builder/tests/test_instrumentation_transformer.py index 562ba5c4..1e33575f 100644 --- a/ecosystem-automation/explorer-db-builder/tests/test_instrumentation_transformer.py +++ b/ecosystem-automation/explorer-db-builder/tests/test_instrumentation_transformer.py @@ -256,3 +256,10 @@ def test_missing_libraries_key_raises_error(self): with pytest.raises(KeyError, match="missing 'libraries' key"): _transform_0_1_to_0_2(data) + + def test_libraries_key_is_none_raises_error(self): + """libraries key present but None also raises KeyError.""" + data = {"file_format": 0.1, "libraries": None} + + with pytest.raises(KeyError, match="missing 'libraries' key"): + _transform_0_1_to_0_2(data) diff --git a/ecosystem-registry/dotnet/v1.15.3/instrumentation.yaml b/ecosystem-registry/dotnet/v1.15.3/instrumentation.yaml new file mode 100644 index 00000000..1ffcb08c --- /dev/null +++ b/ecosystem-registry/dotnet/v1.15.3/instrumentation.yaml @@ -0,0 +1,228 @@ +modules: +- name: OpenTelemetry.Api.ProviderBuilderExtensions + description: Contains extensions to register OpenTelemetry in applications using + Microsoft.Extensions.DependencyInjection + type: extension + version: 1.15.3 +- name: OpenTelemetry.AutoInstrumentation + description: OpenTelemetry Automatic Instrumentation package with all required components + to enable automatic instrumentation. + type: instrumentation + version: 1.16.0-beta.1 +- name: OpenTelemetry.AutoInstrumentation.AspNetCoreBootstrapper + description: ASP.NET Core Bootstrapper used by the OpenTelemetry.AutoInstrumentation + project. + type: instrumentation + version: 1.16.0-beta.1 +- name: OpenTelemetry.AutoInstrumentation.BuildTasks + description: Build tasks used by the OpenTelemetry.AutoInstrumentation project. + type: instrumentation + version: 1.16.0-beta.1 +- name: OpenTelemetry.AutoInstrumentation.Loader + description: Loader used by the OpenTelemetry.AutoInstrumentation project. + type: instrumentation + version: 1.16.0-beta.1 +- name: OpenTelemetry.AutoInstrumentation.Runtime.Managed + description: Managed components used by the OpenTelemetry.AutoInstrumentation project. + type: instrumentation + version: 1.16.0-beta.1 +- name: OpenTelemetry.AutoInstrumentation.Runtime.Native + description: Native runtime components used by the OpenTelemetry.AutoInstrumentation + project. + type: instrumentation + version: 1.16.0-beta.1 +- name: OpenTelemetry.AutoInstrumentation.StartupHook + description: StartupHook used by the OpenTelemetry.AutoInstrumentation project. + type: instrumentation + version: 1.16.0-beta.1 +- name: OpenTelemetry.Exporter.Console + description: Console exporter for OpenTelemetry .NET + type: exporter + version: 1.15.3 +- name: OpenTelemetry.Exporter.Geneva + description: An OpenTelemetry .NET exporter that exports to local ETW or UDS. + type: exporter + version: 1.15.2 +- name: OpenTelemetry.Exporter.InMemory + description: In-memory exporter for OpenTelemetry .NET + type: exporter + version: 1.15.3 +- name: OpenTelemetry.Exporter.InfluxDB + description: An OpenTelemetry .NET exporter that exports to InfluxDB. + type: exporter + version: 1.0.0-alpha.8 +- name: OpenTelemetry.Exporter.OneCollector + description: An OpenTelemetry .NET exporter that sends telemetry to Microsoft OneCollector. + type: exporter + version: 1.15.1 +- name: OpenTelemetry.Exporter.OpenTelemetryProtocol + description: OpenTelemetry protocol exporter for OpenTelemetry .NET + type: exporter + version: 1.15.3 +- name: OpenTelemetry.Exporter.Prometheus.AspNetCore + description: ASP.NET Core middleware for hosting OpenTelemetry .NET Prometheus Exporter + type: exporter + version: 1.15.3-beta.1 +- name: OpenTelemetry.Exporter.Prometheus.HttpListener + description: Stand-alone HttpListener for hosting OpenTelemetry .NET Prometheus + Exporter + type: exporter + version: 1.15.3-beta.1 +- name: OpenTelemetry.Exporter.Zipkin + description: Zipkin exporter for OpenTelemetry .NET + type: exporter + version: 1.15.3 +- name: OpenTelemetry.Extensions + description: OpenTelemetry .NET SDK preview features and extensions. + type: extension + version: 1.15.0-beta.1 +- name: OpenTelemetry.Extensions.AWS + description: OpenTelemetry extensions for AWS. + type: extension + version: 1.15.1 +- name: OpenTelemetry.Extensions.Enrichment + description: OpenTelemetry .NET SDK telemetry enrichment. + type: extension + version: 1.15.1-beta.1 +- name: OpenTelemetry.Extensions.Enrichment.AspNetCore + description: OpenTelemetry .NET SDK ASP.NET Core telemetry enrichment. + type: extension + version: 1.15.1-beta.2 +- name: OpenTelemetry.Extensions.Enrichment.Http + description: OpenTelemetry .NET SDK HTTP telemetry enrichment. + type: extension + version: 1.15.1-beta.2 +- name: OpenTelemetry.Extensions.Hosting + description: Contains extensions to start OpenTelemetry in applications using Microsoft.Extensions.Hosting + type: extension + version: 1.15.3 +- name: OpenTelemetry.Extensions.Propagators + description: OpenTelemetry Extensions Propagators + type: extension + version: 1.15.3 +- name: OpenTelemetry.Instrumentation.AWS + description: AWS client instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1 +- name: OpenTelemetry.Instrumentation.AWSLambda + description: AWS Lambda tracing wrapper for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1 +- name: OpenTelemetry.Instrumentation.AspNet + description: ASP.NET instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.2 +- name: OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule + description: A module that instruments incoming request with System.Diagnostics.Activity + and notifies listeners with DiagnosticsSource. + type: instrumentation + version: 1.15.2 +- name: OpenTelemetry.Instrumentation.AspNetCore + description: ASP.NET Core instrumentation for OpenTelemetry .NET + type: instrumentation + version: 1.15.2 +- name: OpenTelemetry.Instrumentation.Cassandra + description: OpenTelemetry Cassandra Instrumentation. + type: instrumentation + version: 1.0.0-beta.6 +- name: OpenTelemetry.Instrumentation.ConfluentKafka + description: Confluent.Kafka instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 0.1.0-alpha.6 +- name: OpenTelemetry.Instrumentation.ElasticsearchClient + description: Elasticsearch instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.EntityFrameworkCore + description: Microsoft.EntityFrameworkCore instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.EventCounters + description: OpenTelemetry Metrics instrumentation for .NET EventCounters. + type: instrumentation + version: 1.15.1-alpha.1 +- name: OpenTelemetry.Instrumentation.GrpcCore + description: .NET gRPC Core based client and server interceptors for OpenTelemetry. + type: instrumentation + version: 1.0.0-beta.11 +- name: OpenTelemetry.Instrumentation.GrpcNetClient + description: gRPC for .NET client instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.Hangfire + description: OpenTelemetry Hangfire Instrumentation. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.Http + description: HTTP instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1 +- name: OpenTelemetry.Instrumentation.Owin + description: OpenTelemetry instrumentation for OWIN. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.Process + description: dotnet process instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.Quartz + description: OpenTelemetry Quartz.NET Instrumentation. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.Runtime + description: .NET runtime instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1 +- name: OpenTelemetry.Instrumentation.ServiceFabricRemoting + description: ServiceFabric Remoting instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.SqlClient + description: SqlClient instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.2 +- name: OpenTelemetry.Instrumentation.StackExchangeRedis + description: StackExchange.Redis instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.Wcf + description: OpenTelemetry instrumentation for WCF. + type: instrumentation + version: 1.15.1-beta.2 +- name: OpenTelemetry.Resources.AWS + description: OpenTelemetry Resource Detectors for AWS ElasticBeanstalk, EC2, ECS, + EKS. + type: extension + version: 1.15.1 +- name: OpenTelemetry.Resources.Azure + description: OpenTelemetry Resource Detectors for Azure cloud environments. + type: extension + version: 1.15.1-beta.1 +- name: OpenTelemetry.Resources.Container + description: OpenTelemetry Resource Detectors for Container environment. + type: extension + version: 1.15.1-beta.1 +- name: OpenTelemetry.Resources.Gcp + description: OpenTelemetry Resource Detectors for Google Cloud Platform environments. + type: extension + version: 1.0.0-alpha.1 +- name: OpenTelemetry.Resources.Host + description: OpenTelemetry Resource Detectors for Host. + type: extension + version: 1.15.1-beta.1 +- name: OpenTelemetry.Resources.OperatingSystem + description: OpenTelemetry Resource Detectors for Operating System. + type: extension + version: 1.15.1-beta.1 +- name: OpenTelemetry.Resources.Process + description: OpenTelemetry Resource Detectors for Process. + type: extension + version: 1.15.1-beta.2 +- name: OpenTelemetry.Resources.ProcessRuntime + description: OpenTelemetry Resource Detectors for Process Runtime. + type: extension + version: 1.15.1-beta.1 +- name: OpenTelemetry.Sampler.AWS + description: OpenTelemetry remote sampler for AWS X-Ray. + type: extension + version: 0.1.0-alpha.9 diff --git a/ecosystem-registry/dotnet/v1.15.4-SNAPSHOT/instrumentation.yaml b/ecosystem-registry/dotnet/v1.15.4-SNAPSHOT/instrumentation.yaml new file mode 100644 index 00000000..1ffcb08c --- /dev/null +++ b/ecosystem-registry/dotnet/v1.15.4-SNAPSHOT/instrumentation.yaml @@ -0,0 +1,228 @@ +modules: +- name: OpenTelemetry.Api.ProviderBuilderExtensions + description: Contains extensions to register OpenTelemetry in applications using + Microsoft.Extensions.DependencyInjection + type: extension + version: 1.15.3 +- name: OpenTelemetry.AutoInstrumentation + description: OpenTelemetry Automatic Instrumentation package with all required components + to enable automatic instrumentation. + type: instrumentation + version: 1.16.0-beta.1 +- name: OpenTelemetry.AutoInstrumentation.AspNetCoreBootstrapper + description: ASP.NET Core Bootstrapper used by the OpenTelemetry.AutoInstrumentation + project. + type: instrumentation + version: 1.16.0-beta.1 +- name: OpenTelemetry.AutoInstrumentation.BuildTasks + description: Build tasks used by the OpenTelemetry.AutoInstrumentation project. + type: instrumentation + version: 1.16.0-beta.1 +- name: OpenTelemetry.AutoInstrumentation.Loader + description: Loader used by the OpenTelemetry.AutoInstrumentation project. + type: instrumentation + version: 1.16.0-beta.1 +- name: OpenTelemetry.AutoInstrumentation.Runtime.Managed + description: Managed components used by the OpenTelemetry.AutoInstrumentation project. + type: instrumentation + version: 1.16.0-beta.1 +- name: OpenTelemetry.AutoInstrumentation.Runtime.Native + description: Native runtime components used by the OpenTelemetry.AutoInstrumentation + project. + type: instrumentation + version: 1.16.0-beta.1 +- name: OpenTelemetry.AutoInstrumentation.StartupHook + description: StartupHook used by the OpenTelemetry.AutoInstrumentation project. + type: instrumentation + version: 1.16.0-beta.1 +- name: OpenTelemetry.Exporter.Console + description: Console exporter for OpenTelemetry .NET + type: exporter + version: 1.15.3 +- name: OpenTelemetry.Exporter.Geneva + description: An OpenTelemetry .NET exporter that exports to local ETW or UDS. + type: exporter + version: 1.15.2 +- name: OpenTelemetry.Exporter.InMemory + description: In-memory exporter for OpenTelemetry .NET + type: exporter + version: 1.15.3 +- name: OpenTelemetry.Exporter.InfluxDB + description: An OpenTelemetry .NET exporter that exports to InfluxDB. + type: exporter + version: 1.0.0-alpha.8 +- name: OpenTelemetry.Exporter.OneCollector + description: An OpenTelemetry .NET exporter that sends telemetry to Microsoft OneCollector. + type: exporter + version: 1.15.1 +- name: OpenTelemetry.Exporter.OpenTelemetryProtocol + description: OpenTelemetry protocol exporter for OpenTelemetry .NET + type: exporter + version: 1.15.3 +- name: OpenTelemetry.Exporter.Prometheus.AspNetCore + description: ASP.NET Core middleware for hosting OpenTelemetry .NET Prometheus Exporter + type: exporter + version: 1.15.3-beta.1 +- name: OpenTelemetry.Exporter.Prometheus.HttpListener + description: Stand-alone HttpListener for hosting OpenTelemetry .NET Prometheus + Exporter + type: exporter + version: 1.15.3-beta.1 +- name: OpenTelemetry.Exporter.Zipkin + description: Zipkin exporter for OpenTelemetry .NET + type: exporter + version: 1.15.3 +- name: OpenTelemetry.Extensions + description: OpenTelemetry .NET SDK preview features and extensions. + type: extension + version: 1.15.0-beta.1 +- name: OpenTelemetry.Extensions.AWS + description: OpenTelemetry extensions for AWS. + type: extension + version: 1.15.1 +- name: OpenTelemetry.Extensions.Enrichment + description: OpenTelemetry .NET SDK telemetry enrichment. + type: extension + version: 1.15.1-beta.1 +- name: OpenTelemetry.Extensions.Enrichment.AspNetCore + description: OpenTelemetry .NET SDK ASP.NET Core telemetry enrichment. + type: extension + version: 1.15.1-beta.2 +- name: OpenTelemetry.Extensions.Enrichment.Http + description: OpenTelemetry .NET SDK HTTP telemetry enrichment. + type: extension + version: 1.15.1-beta.2 +- name: OpenTelemetry.Extensions.Hosting + description: Contains extensions to start OpenTelemetry in applications using Microsoft.Extensions.Hosting + type: extension + version: 1.15.3 +- name: OpenTelemetry.Extensions.Propagators + description: OpenTelemetry Extensions Propagators + type: extension + version: 1.15.3 +- name: OpenTelemetry.Instrumentation.AWS + description: AWS client instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1 +- name: OpenTelemetry.Instrumentation.AWSLambda + description: AWS Lambda tracing wrapper for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1 +- name: OpenTelemetry.Instrumentation.AspNet + description: ASP.NET instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.2 +- name: OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule + description: A module that instruments incoming request with System.Diagnostics.Activity + and notifies listeners with DiagnosticsSource. + type: instrumentation + version: 1.15.2 +- name: OpenTelemetry.Instrumentation.AspNetCore + description: ASP.NET Core instrumentation for OpenTelemetry .NET + type: instrumentation + version: 1.15.2 +- name: OpenTelemetry.Instrumentation.Cassandra + description: OpenTelemetry Cassandra Instrumentation. + type: instrumentation + version: 1.0.0-beta.6 +- name: OpenTelemetry.Instrumentation.ConfluentKafka + description: Confluent.Kafka instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 0.1.0-alpha.6 +- name: OpenTelemetry.Instrumentation.ElasticsearchClient + description: Elasticsearch instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.EntityFrameworkCore + description: Microsoft.EntityFrameworkCore instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.EventCounters + description: OpenTelemetry Metrics instrumentation for .NET EventCounters. + type: instrumentation + version: 1.15.1-alpha.1 +- name: OpenTelemetry.Instrumentation.GrpcCore + description: .NET gRPC Core based client and server interceptors for OpenTelemetry. + type: instrumentation + version: 1.0.0-beta.11 +- name: OpenTelemetry.Instrumentation.GrpcNetClient + description: gRPC for .NET client instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.Hangfire + description: OpenTelemetry Hangfire Instrumentation. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.Http + description: HTTP instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1 +- name: OpenTelemetry.Instrumentation.Owin + description: OpenTelemetry instrumentation for OWIN. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.Process + description: dotnet process instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.Quartz + description: OpenTelemetry Quartz.NET Instrumentation. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.Runtime + description: .NET runtime instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1 +- name: OpenTelemetry.Instrumentation.ServiceFabricRemoting + description: ServiceFabric Remoting instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.SqlClient + description: SqlClient instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.2 +- name: OpenTelemetry.Instrumentation.StackExchangeRedis + description: StackExchange.Redis instrumentation for OpenTelemetry .NET. + type: instrumentation + version: 1.15.1-beta.1 +- name: OpenTelemetry.Instrumentation.Wcf + description: OpenTelemetry instrumentation for WCF. + type: instrumentation + version: 1.15.1-beta.2 +- name: OpenTelemetry.Resources.AWS + description: OpenTelemetry Resource Detectors for AWS ElasticBeanstalk, EC2, ECS, + EKS. + type: extension + version: 1.15.1 +- name: OpenTelemetry.Resources.Azure + description: OpenTelemetry Resource Detectors for Azure cloud environments. + type: extension + version: 1.15.1-beta.1 +- name: OpenTelemetry.Resources.Container + description: OpenTelemetry Resource Detectors for Container environment. + type: extension + version: 1.15.1-beta.1 +- name: OpenTelemetry.Resources.Gcp + description: OpenTelemetry Resource Detectors for Google Cloud Platform environments. + type: extension + version: 1.0.0-alpha.1 +- name: OpenTelemetry.Resources.Host + description: OpenTelemetry Resource Detectors for Host. + type: extension + version: 1.15.1-beta.1 +- name: OpenTelemetry.Resources.OperatingSystem + description: OpenTelemetry Resource Detectors for Operating System. + type: extension + version: 1.15.1-beta.1 +- name: OpenTelemetry.Resources.Process + description: OpenTelemetry Resource Detectors for Process. + type: extension + version: 1.15.1-beta.2 +- name: OpenTelemetry.Resources.ProcessRuntime + description: OpenTelemetry Resource Detectors for Process Runtime. + type: extension + version: 1.15.1-beta.1 +- name: OpenTelemetry.Sampler.AWS + description: OpenTelemetry remote sampler for AWS X-Ray. + type: extension + version: 0.1.0-alpha.9 diff --git a/pyproject.toml b/pyproject.toml index ef4853bc..58c83bea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,7 @@ dependencies = [ "collector-watcher", "configuration-watcher", "java-instrumentation-watcher", + "dotnet-instrumentation-watcher", "explorer-db-builder", "v1-registry-sync", ] @@ -22,6 +23,7 @@ members = [ collector-watcher = { workspace = true } configuration-watcher = { workspace = true } java-instrumentation-watcher = { workspace = true } +dotnet-instrumentation-watcher = { workspace = true } explorer-db-builder = { workspace = true } v1-registry-sync = { workspace = true } @@ -41,5 +43,6 @@ target-version = "py311" select = ["E", "F", "I", "N", "W"] [tool.pytest.ini_options] +addopts = "--import-mode=importlib" testpaths = ["ecosystem-automation"] python_files = ["test_*.py", "*_test.py"] \ No newline at end of file diff --git a/uv.lock b/uv.lock index 75acbe30..03d95711 100644 --- a/uv.lock +++ b/uv.lock @@ -6,9 +6,11 @@ requires-python = ">=3.11" members = [ "collector-watcher", "configuration-watcher", + "dotnet-instrumentation-watcher", "explorer-db-builder", "java-instrumentation-watcher", "opentelemetry-ecosystem-explorer", + "v1-registry-sync", "watcher-common", ] @@ -428,6 +430,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, ] +[[package]] +name = "dotnet-instrumentation-watcher" +version = "0.1.0" +source = { editable = "ecosystem-automation/dotnet-instrumentation-watcher" } +dependencies = [ + { name = "pyyaml" }, + { name = "requests" }, + { name = "semantic-version" }, + { name = "watcher-common" }, +] + +[package.optional-dependencies] +dev = [ + { name = "pytest" }, + { name = "pytest-cov" }, +] + +[package.metadata] +requires-dist = [ + { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, + { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.1.0" }, + { name = "pyyaml", specifier = ">=6.0.1" }, + { name = "requests", specifier = ">=2.31.0" }, + { name = "semantic-version", specifier = ">=2.10.0" }, + { name = "watcher-common", editable = "ecosystem-automation/watcher-common" }, +] +provides-extras = ["dev"] + [[package]] name = "explorer-db-builder" version = "0.1.0" @@ -562,8 +592,10 @@ source = { virtual = "." } dependencies = [ { name = "collector-watcher" }, { name = "configuration-watcher" }, + { name = "dotnet-instrumentation-watcher" }, { name = "explorer-db-builder" }, { name = "java-instrumentation-watcher" }, + { name = "v1-registry-sync" }, ] [package.dev-dependencies] @@ -578,8 +610,10 @@ dev = [ requires-dist = [ { name = "collector-watcher", editable = "ecosystem-automation/collector-watcher" }, { name = "configuration-watcher", editable = "ecosystem-automation/configuration-watcher" }, + { name = "dotnet-instrumentation-watcher", editable = "ecosystem-automation/dotnet-instrumentation-watcher" }, { name = "explorer-db-builder", editable = "ecosystem-automation/explorer-db-builder" }, { name = "java-instrumentation-watcher", editable = "ecosystem-automation/java-instrumentation-watcher" }, + { name = "v1-registry-sync", editable = "ecosystem-automation/v1-registry-sync" }, ] [package.metadata.requires-dev] @@ -944,6 +978,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] +[[package]] +name = "v1-registry-sync" +version = "0.1.0" +source = { editable = "ecosystem-automation/v1-registry-sync" } +dependencies = [ + { name = "collector-watcher" }, +] + +[package.optional-dependencies] +dev = [ + { name = "pytest" }, + { name = "pytest-cov" }, +] + +[package.metadata] +requires-dist = [ + { name = "collector-watcher", editable = "ecosystem-automation/collector-watcher" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, + { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.1.0" }, +] +provides-extras = ["dev"] + [[package]] name = "virtualenv" version = "21.2.4"