|
7 | 7 | # See https://aboutcode.org for more information about nexB OSS projects. |
8 | 8 | # |
9 | 9 |
|
10 | | -import os |
11 | | -import logging |
12 | 10 | import ast |
| 11 | +import json |
| 12 | +import logging |
| 13 | +import os |
13 | 14 | from collections import defaultdict |
14 | 15 | import re |
15 | 16 |
|
@@ -444,7 +445,58 @@ def assign_package_to_resources(cls, package, resource, codebase, package_adder) |
444 | 445 | ) |
445 | 446 |
|
446 | 447 |
|
447 | | -class BazelModuleHandler(models.DatafileHandler): |
| 448 | +class BaseBazelModuleHandler(models.DatafileHandler): |
| 449 | + @classmethod |
| 450 | + def assemble(cls, package_data, resource, codebase, package_adder): |
| 451 | + if resource.has_parent(): |
| 452 | + directory = resource.parent(codebase) |
| 453 | + else: |
| 454 | + directory = resource |
| 455 | + |
| 456 | + if not directory: |
| 457 | + yield from super().assemble( |
| 458 | + package_data=package_data, |
| 459 | + resource=resource, |
| 460 | + codebase=codebase, |
| 461 | + package_adder=package_adder, |
| 462 | + ) |
| 463 | + return |
| 464 | + |
| 465 | + if not codebase.has_single_resource: |
| 466 | + siblings = list(directory.children(codebase)) |
| 467 | + else: |
| 468 | + siblings = [directory] |
| 469 | + |
| 470 | + pkgdata_resources = [] |
| 471 | + for datafile_name in ("MODULE.bazel", "MODULE.bazel.lock"): |
| 472 | + for sibling in siblings: |
| 473 | + if sibling.name != datafile_name: |
| 474 | + continue |
| 475 | + |
| 476 | + for sibling_package_data in sibling.package_data: |
| 477 | + pkgdata_resources.append( |
| 478 | + (models.PackageData.from_dict(sibling_package_data), sibling) |
| 479 | + ) |
| 480 | + |
| 481 | + if pkgdata_resources: |
| 482 | + yield from cls.assemble_from_many( |
| 483 | + pkgdata_resources=pkgdata_resources, |
| 484 | + codebase=codebase, |
| 485 | + package_adder=package_adder, |
| 486 | + ignore_name_check=True, |
| 487 | + parent_resource=directory, |
| 488 | + ) |
| 489 | + return |
| 490 | + |
| 491 | + yield from super().assemble( |
| 492 | + package_data=package_data, |
| 493 | + resource=resource, |
| 494 | + codebase=codebase, |
| 495 | + package_adder=package_adder, |
| 496 | + ) |
| 497 | + |
| 498 | + |
| 499 | +class BazelModuleHandler(BaseBazelModuleHandler): |
448 | 500 | """ |
449 | 501 | Handle Bazel MODULE.bazel module manifest files used by Bzlmod. |
450 | 502 | See: https://bazel.build/external/module |
@@ -474,6 +526,11 @@ def parse(cls, location, package_only=False): |
474 | 526 | name = _extract_starlark_kwarg(block, "name") |
475 | 527 | version = _extract_starlark_kwarg(block, "version") |
476 | 528 |
|
| 529 | + # Root Bazel modules can omit module(), so fall back to the enclosing |
| 530 | + # directory name to keep the manifest assemblable. |
| 531 | + if not name: |
| 532 | + name = fileutils.file_name(fileutils.parent_directory(location)) |
| 533 | + |
477 | 534 | # --- Extract bazel_dep() declarations --- |
478 | 535 | dependencies = [] |
479 | 536 | for dep_match in re.finditer( |
@@ -510,3 +567,37 @@ def parse(cls, location, package_only=False): |
510 | 567 | version=version, |
511 | 568 | dependencies=dependencies, |
512 | 569 | ) |
| 570 | + |
| 571 | + |
| 572 | +class BazelModuleLockHandler(BaseBazelModuleHandler): |
| 573 | + """ |
| 574 | + Handle Bazel MODULE.bazel.lock files and merge their metadata with a |
| 575 | + sibling MODULE.bazel manifest during assembly. |
| 576 | + """ |
| 577 | + |
| 578 | + datasource_id = "bazel_module_lock" |
| 579 | + path_patterns = ("*/MODULE.bazel.lock",) |
| 580 | + default_package_type = "bazel" |
| 581 | + is_lockfile = True |
| 582 | + description = "Bazel MODULE.bazel lockfile" |
| 583 | + documentation_url = "https://bazel.build/external/lockfile" |
| 584 | + |
| 585 | + @classmethod |
| 586 | + def parse(cls, location, package_only=False): |
| 587 | + with open(location, encoding="utf-8", errors="replace") as f: |
| 588 | + parsed = json.load(f) |
| 589 | + |
| 590 | + registry_file_hashes = parsed.get("registryFileHashes") or {} |
| 591 | + module_extensions = parsed.get("moduleExtensions") or {} |
| 592 | + |
| 593 | + yield models.PackageData( |
| 594 | + datasource_id=cls.datasource_id, |
| 595 | + type=cls.default_package_type, |
| 596 | + extra_data={ |
| 597 | + "bazel_lockfile_version": parsed.get("lockFileVersion"), |
| 598 | + "bazel_registry_file_hashes_count": len(registry_file_hashes), |
| 599 | + "bazel_module_extensions": sorted(module_extensions), |
| 600 | + "bazel_selected_yanked_versions": parsed.get("selectedYankedVersions") |
| 601 | + or {}, |
| 602 | + }, |
| 603 | + ) |
0 commit comments