Source code for swh.osv.luigi.reachability

# Copyright (C) 2026  The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information

"""
Luigi tasks for computing reachability information
==================================================
"""

from os import PathLike
from typing import Dict, Iterator, Tuple

import luigi

from swh.graph.luigi.compressed_graph import LocalGraph
from swh.osv.luigi import DownloadVulnerabilities, MapPackagesToVcs
from swh.osv.luigi.utils import _BaseUploadToS3


[docs] class ComputeConnectedComponents(luigi.Task): """Computes the connected components of the subgraph spanned by commits referenced in vulnerability reports.""" local_graph_path = luigi.PathParameter() graph_name = luigi.StrParameter(default="graph") vulnerabilities_dir = luigi.PathParameter() include_vcs_matches = luigi.BoolParameter(default=True) @property def resources(self): """Declares the task uses every CPU available""" import socket hostname = socket.getfqdn() return { f"{hostname}_max_cpu": 1, }
[docs] def requires(self) -> Dict[str, luigi.Task]: """Returns an instance of :class:`swh.graph.luigi.compressed_graph.LocalGraph` and :class:`swh.osv.luigi.DownloadVulnerabilities`. If :attr:`include_vcs_matches` is True, also returns an instance of :class:`swh.osv.luigi.MapPackagesToVcs`.""" result: Dict[str, luigi.Task] = { "graph": LocalGraph(local_graph_path=self.local_graph_path), "sqlite": DownloadVulnerabilities( local_graph_path=self.local_graph_path, graph_name=self.graph_name, vulnerabilities_dir=self.vulnerabilities_dir, ), } if self.include_vcs_matches: result["packages_to_vcs"] = MapPackagesToVcs( local_graph_path=self.local_graph_path, graph_name=self.graph_name, vulnerabilities_dir=self.vulnerabilities_dir, ) return result
[docs] def output(self) -> luigi.LocalTarget: """``connected_components.wccs`` file containing the epserde-serialized subgraph connected components.""" return luigi.LocalTarget(self.vulnerabilities_dir / "connected_components.wccs")
[docs] def run(self) -> None: """Runs ``compute-connected-components``.""" from .shell import Rust # fmt: off args = [ "compute-connected-components", "--graph", self.local_graph_path / self.graph_name, "--db", self.input()["sqlite"], "--subgraphwccs-out", self.output(), ] if self.include_vcs_matches: args += [ "--package2vcs", self.input()["packages_to_vcs"], "--include-vcs-matches", ] Rust(*args).run()
# fmt: on
[docs] class UploadSubgraphWccsToS3(_BaseUploadToS3): """Uploads the connected components WCCS file to S3.""" include_vcs_matches = luigi.BoolParameter(default=True)
[docs] def requires(self) -> Dict[str, luigi.Task]: """Returns an instance of :class:`ComputeConnectedComponents`.""" return { "subgraphwccs": ComputeConnectedComponents( local_graph_path=self.local_graph_path, graph_name=self.graph_name, vulnerabilities_dir=self.vulnerabilities_dir, include_vcs_matches=self.include_vcs_matches, ), }
[docs] def output(self) -> luigi.Target: """``connected_components.wccs`` on S3.""" import luigi.contrib.s3 return luigi.contrib.s3.S3Target( f"{self._s3_prefix()}/connected_components.wccs" )
def _files_to_upload(self) -> Iterator[Tuple[PathLike, str]]: yield (self.input()["subgraphwccs"].path, "connected_components.wccs")