-
Notifications
You must be signed in to change notification settings - Fork 46
feat(js): add JavaScript instrumentation watcher and initial registry #577
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
jaydeluca
merged 8 commits into
open-telemetry:main
from
MeloveGupta:feat/js-instrumentation-watcher
Jun 24, 2026
+2,272
−0
Merged
Changes from 4 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
4bf64b5
feat(js): add JavaScript instrumentation watcher and initial registry
MeloveGupta 6a51a0c
Merge branch 'main' into feat/js-instrumentation-watcher
MeloveGupta 7529475
chore: update uv.lock to include js-instrumentation-watcher
MeloveGupta 40a7597
chore: add missing copyright headers to js-instrumentation-watcher
MeloveGupta 078c907
fix(js): address review feedback on instrumentation watcher
MeloveGupta 285595d
Merge remote-tracking branch 'upstream/main' into feat/js-instrumenta…
MeloveGupta df1d634
Merge branch 'main' into feat/js-instrumentation-watcher
jaydeluca d6f2686
add guard and fix dependency
jaydeluca File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
44 changes: 44 additions & 0 deletions
44
ecosystem-automation/js-instrumentation-watcher/pyproject.toml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| # 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. | ||
|
|
||
| [project] | ||
| name = "js-instrumentation-watcher" | ||
| version = "0.1.0" | ||
| description = "Automation tool for watching and collecting OpenTelemetry JavaScript instrumentation metadata" | ||
| requires-python = ">=3.11" | ||
| dependencies = [ | ||
| "PyYAML>=6.0.1", | ||
| "GitPython>=3.1.40", | ||
| "semantic-version>=2.10.0", | ||
| "watcher-common", | ||
| ] | ||
|
|
||
| [project.scripts] | ||
| js-instrumentation-watcher = "js_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/js_instrumentation_watcher"] | ||
14 changes: 14 additions & 0 deletions
14
ecosystem-automation/js-instrumentation-watcher/src/js_instrumentation_watcher/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| # 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. | ||
| # | ||
|
jaydeluca marked this conversation as resolved.
|
||
19 changes: 19 additions & 0 deletions
19
ecosystem-automation/js-instrumentation-watcher/src/js_instrumentation_watcher/__main__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # 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 .main import main | ||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
|
jaydeluca marked this conversation as resolved.
|
||
122 changes: 122 additions & 0 deletions
122
...omation/js-instrumentation-watcher/src/js_instrumentation_watcher/instrumentation_sync.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| # 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 JS instrumentation metadata.""" | ||
|
|
||
| import logging | ||
| from pathlib import Path | ||
| from typing import Any | ||
|
|
||
| from .inventory_manager import InventoryManager | ||
| from .package_parser import PackageParser | ||
| from .package_scanner import PackageScanner | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| class InstrumentationSync: | ||
| """ | ||
| Orchestrates synchronization of JS instrumentation metadata. | ||
|
|
||
| Walks the js-contrib repository, parses each instrumentation package, | ||
| and writes per-package versioned YAML to the registry. | ||
|
|
||
| Unlike the Java watcher which has a single release version, JS packages | ||
| version independently. Each package is stored at its own version: | ||
| ecosystem-registry/javascript/{package}/v{version}.yaml | ||
| """ | ||
|
|
||
| def __init__( | ||
| self, | ||
| repo_path: Path, | ||
| inventory_manager: InventoryManager, | ||
| ): | ||
| """ | ||
| Args: | ||
| repo_path: Path to the cloned opentelemetry-js-contrib repository | ||
| inventory_manager: Inventory manager for writing registry files | ||
| """ | ||
| self.repo_path = repo_path | ||
| self.inventory_manager = inventory_manager | ||
| self.scanner = PackageScanner(repo_path) | ||
|
|
||
| def sync(self) -> dict[str, Any]: | ||
| """ | ||
| Synchronize all JS instrumentation packages to the registry. | ||
|
|
||
| For each package: | ||
| - If the current version already exists in the registry, skip it | ||
| - Otherwise parse and write the metadata | ||
|
|
||
| Returns: | ||
| Summary dict with counts of new, skipped, and failed packages | ||
| """ | ||
| summary: dict[str, Any] = { | ||
| "new": [], | ||
| "skipped": [], | ||
| "failed": [], | ||
| } | ||
|
|
||
| bundle_membership = self.scanner.load_bundle_membership() | ||
| logger.info("Loaded %d packages from auto-instrumentations-node", len(bundle_membership)) | ||
|
|
||
| component_owners = self.scanner.load_component_owners() | ||
| logger.info("Loaded owners for %d components", len(component_owners)) | ||
|
|
||
| packages = self.scanner.discover_packages() | ||
|
|
||
| for package_path in packages: | ||
| name = package_path.name | ||
| parser = PackageParser( | ||
| package_path=package_path, | ||
| bundle_membership=bundle_membership, | ||
| component_owners=component_owners, | ||
| ) | ||
|
|
||
| try: | ||
| data = parser.parse() | ||
| except Exception as e: | ||
| logger.warning("Failed to parse %s: %s", name, e) | ||
| summary["failed"].append(name) | ||
| continue | ||
|
jaydeluca marked this conversation as resolved.
|
||
|
|
||
| if data is None: | ||
| logger.warning("No data parsed for %s — skipping", name) | ||
| summary["failed"].append(name) | ||
| continue | ||
|
|
||
| version = data.get("version", "") | ||
| if not version: | ||
| logger.warning("No version found for %s — skipping", name) | ||
| summary["failed"].append(name) | ||
| continue | ||
|
|
||
| if self.inventory_manager.version_exists(name, version): | ||
| logger.debug("Already tracked: %s v%s", name, version) | ||
| summary["skipped"].append(f"{name}@{version}") | ||
| continue | ||
|
|
||
| self.inventory_manager.save(name, version, data) | ||
| logger.info("Saved: %s v%s", name, version) | ||
| summary["new"].append(f"{name}@{version}") | ||
|
|
||
| logger.info( | ||
| "Sync complete — new: %d, skipped: %d, failed: %d", | ||
| len(summary["new"]), | ||
| len(summary["skipped"]), | ||
| len(summary["failed"]), | ||
| ) | ||
|
|
||
| return summary | ||
91 changes: 91 additions & 0 deletions
91
...automation/js-instrumentation-watcher/src/js_instrumentation_watcher/inventory_manager.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,91 @@ | ||||||||
| # 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 manager for JS instrumentation registry storage.""" | ||||||||
|
|
||||||||
| import logging | ||||||||
| from pathlib import Path | ||||||||
|
|
||||||||
| import yaml | ||||||||
|
|
||||||||
| logger = logging.getLogger(__name__) | ||||||||
|
|
||||||||
|
|
||||||||
| class InventoryManager: | ||||||||
| """ | ||||||||
| Manages storage of JS instrumentation metadata in the registry. | ||||||||
|
|
||||||||
| Registry layout (per maintainer direction): | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| ecosystem-registry/javascript/{package-name}/v{version}.yaml | ||||||||
|
|
||||||||
| Each package is versioned independently — there is no single | ||||||||
| aggregated version file like the Java watcher uses. | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| """ | ||||||||
|
|
||||||||
| def __init__(self, registry_dir: str): | ||||||||
| """ | ||||||||
| Args: | ||||||||
| registry_dir: Base registry directory, e.g. 'ecosystem-registry/javascript' | ||||||||
| """ | ||||||||
| self.registry_dir = Path(registry_dir) | ||||||||
|
|
||||||||
| def version_exists(self, package_name: str, version: str) -> bool: | ||||||||
| """ | ||||||||
| Check if a specific package version already exists in the registry. | ||||||||
|
|
||||||||
| Args: | ||||||||
| package_name: Package directory name, e.g. 'instrumentation-express' | ||||||||
| version: Version string, e.g. '0.66.0' | ||||||||
|
|
||||||||
| Returns: | ||||||||
| True if the version file exists | ||||||||
| """ | ||||||||
| return self._version_path(package_name, version).exists() | ||||||||
|
|
||||||||
| def save(self, package_name: str, version: str, data: dict) -> None: | ||||||||
| """ | ||||||||
| Save a package version to the registry. | ||||||||
|
|
||||||||
| Args: | ||||||||
| package_name: Package directory name | ||||||||
| version: Version string | ||||||||
| data: Metadata dict to serialize as YAML | ||||||||
| """ | ||||||||
| path = self._version_path(package_name, version) | ||||||||
| path.parent.mkdir(parents=True, exist_ok=True) | ||||||||
|
|
||||||||
| with path.open("w") as f: | ||||||||
| yaml.dump( | ||||||||
| data, | ||||||||
| f, | ||||||||
| default_flow_style=False, | ||||||||
| sort_keys=True, | ||||||||
| allow_unicode=True, | ||||||||
| ) | ||||||||
|
|
||||||||
| logger.debug("Saved %s v%s to %s", package_name, version, path) | ||||||||
|
|
||||||||
| def _version_path(self, package_name: str, version: str) -> Path: | ||||||||
| """ | ||||||||
| Build the path for a package version file. | ||||||||
|
|
||||||||
| Args: | ||||||||
| package_name: Package directory name | ||||||||
| version: Version string | ||||||||
|
|
||||||||
| Returns: | ||||||||
| Path to the version YAML file | ||||||||
| """ | ||||||||
| return self.registry_dir / package_name / f"v{version}.yaml" | ||||||||
52 changes: 52 additions & 0 deletions
52
ecosystem-automation/js-instrumentation-watcher/src/js_instrumentation_watcher/main.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| # 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. | ||
| # | ||
|
|
||
| """Entry point for the JS instrumentation watcher.""" | ||
|
|
||
| import logging | ||
| import os | ||
|
|
||
| from .instrumentation_sync import InstrumentationSync | ||
| from .inventory_manager import InventoryManager | ||
| from .repository_manager import JsContribRepositoryManager | ||
|
|
||
| logging.basicConfig( | ||
| level=logging.INFO, | ||
| format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", | ||
| ) | ||
| logger = logging.getLogger(__name__) | ||
|
|
||
| REGISTRY_DIR = "ecosystem-registry/javascript" | ||
|
|
||
|
|
||
| def main() -> None: | ||
| """Main entry point for the JS instrumentation watcher.""" | ||
| base_dir = os.environ.get("JS_CONTRIB_REPOS_DIR", "tmp_repos") | ||
|
|
||
| logger.info("Starting JS instrumentation watcher...") | ||
|
|
||
|
|
||
| repo_manager = JsContribRepositoryManager(base_dir=base_dir) | ||
| repo_path = repo_manager.setup() | ||
|
|
||
| inventory_manager = InventoryManager(registry_dir=REGISTRY_DIR) | ||
|
|
||
| sync = InstrumentationSync( | ||
| repo_path=repo_path, | ||
| inventory_manager=inventory_manager, | ||
| ) | ||
|
|
||
| summary = sync.sync() | ||
|
|
||
| logger.info("Sync complete: %s", summary) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just removed the need for these dependencies from the other modules in #600 could you take a look and do the same thing for this new watcher?