Skip to content

Commit

Permalink
Add dependency graph generation feature (#214)
Browse files Browse the repository at this point in the history
Signed-off-by: fhdufhdu <[email protected]>
  • Loading branch information
fhdufhdu authored Aug 3, 2024
1 parent 2f0cc36 commit f04963f
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 3 deletions.
4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ fosslight_util>=1.4.47
PyGithub
requirements-parser
defusedxml
packageurl-python
packageurl-python
igraph
matplotlib
68 changes: 68 additions & 0 deletions src/fosslight_dependency/_graph_convertor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2021 LG Electronics Inc.
# SPDX-License-Identifier: Apache-2.0
from typing import Optional, Tuple
import igraph as ig
import matplotlib.pyplot as plt


class GraphConvertor:
def __init__(self, package_list: Optional[list] = None):
self._verticies = {}
self._edges = []
if package_list:
self.init_list(package_list)

def init_list(self, package_list: list):
"""
Initialize package_list to self._verticies and self._edges
Args:
package_list (list): List containing package information
"""
depend_on_package_dict = {}
for idx, package_info in enumerate(package_list):
package_name = package_info[0]
depend_on_packages_str = package_info[-1]
depend_on_packages = list(map((lambda x: x.strip()), depend_on_packages_str.split(",")))
self._verticies[package_name] = idx
depend_on_package_dict[package_name] = depend_on_packages
else:
for package_name, depend_on_packages in depend_on_package_dict.items():
if not package_name:
pass
else:
package_idx = self._verticies[package_name]
for depend_on_package in depend_on_packages:
if not depend_on_package:
pass
else:
depend_on_package_idx = self._verticies[depend_on_package]
self._edges.append((package_idx, depend_on_package_idx))

def save(self, path: str, size: Tuple[(int, int)]):
g = ig.Graph((len(self._verticies)), (self._edges), directed=True)

g["title"] = "Dependency Graph"
g.vs["name"] = list(self._verticies.keys())

fig, ax = plt.subplots(figsize=(tuple(map((lambda x: x / 100), size))))
fig.tight_layout()

ig.plot(
g,
target=ax,
layout="kk",
vertex_size=15,
vertex_color=["#FFD2D2"],
vertex_label=(g.vs["name"]),
vertex_label_dist=1.5,
vertex_label_size=7.0,
edge_width=0.5,
edge_color=["#FFD2D2"],
edge_arrow_size=5,
edge_arrow_width=5,
)

fig.savefig(path)
4 changes: 4 additions & 0 deletions src/fosslight_dependency/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
\t\t\t\t\t(If you want to generate the specific file name, add the output path with file name.)
-f <format> [<format> ...]\t Output formats (excel, csv, opossum, yaml, spdx-tag, spdx-yaml, spdx-json, spdx-xml)
\t\t\t\t Multiple formats can be specified separated by space.
--graph-path <save_path> \t Enter the path where the graph image will be saved
\t\t\t\t\t(ex. /your/directory/path/filename.{pdf, jpg, png}) (recommend pdf extension)
--graph-size <width> <height> Enter the size of the graph image (The size unit is pixels)
\t\t\t\t\t--graph-path option is required
--direct\t\t\t Print the direct/transitive dependency type in comment.
\t\tChoice 'True' or 'False'. (default:True)
--notice\t\t\t Print the open source license notice text.
Expand Down
24 changes: 22 additions & 2 deletions src/fosslight_dependency/run_dependency_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
if platform.system() != 'Windows':
from fosslight_util.write_spdx import write_spdx
from fosslight_util.cover import CoverItem
from fosslight_dependency._graph_convertor import GraphConvertor

# Package Name
_PKG_NAME = "fosslight_dependency"
Expand Down Expand Up @@ -92,7 +93,8 @@ def find_package_manager(input_dir, abs_path_to_exclude=[]):

def run_dependency_scanner(package_manager='', input_dir='', output_dir_file='', pip_activate_cmd='',
pip_deactivate_cmd='', output_custom_dir='', app_name=const.default_app_name,
github_token='', formats=[], direct=True, path_to_exclude=[]):
github_token='', formats=[], direct=True, path_to_exclude=[], graph_path='',
graph_size=(600, 600)):
global logger

ret = True
Expand Down Expand Up @@ -233,6 +235,15 @@ def run_dependency_scanner(package_manager='', input_dir='', output_dir_file='',
if cover_comment:
cover.comment += f', {cover_comment}'

if ret and graph_path:
graph_path = os.path.abspath(graph_path)
try:
converter = GraphConvertor(sheet_list[_sheet_name])
converter.save(graph_path, graph_size)
logger.info(f"Output graph image file: {graph_path}")
except Exception as e:
logger.error(f'Fail to make graph image: {e}')

combined_paths_and_files = [os.path.join(output_path, file) for file in output_files]
results = []
for i, output_extension in enumerate(output_extensions):
Expand Down Expand Up @@ -276,6 +287,8 @@ def main():
app_name = const.default_app_name
github_token = ''
format = ''
graph_path = ''
graph_size = (600, 600)
direct = True

parser = argparse.ArgumentParser(add_help=False)
Expand All @@ -291,6 +304,8 @@ def main():
parser.add_argument('-n', '--appname', nargs=1, type=str, required=False)
parser.add_argument('-t', '--token', nargs=1, type=str, required=False)
parser.add_argument('-f', '--format', nargs="*", type=str, required=False)
parser.add_argument('--graph-path', nargs=1, type=str, required=False)
parser.add_argument('--graph-size', nargs=2, type=int, metavar=("WIDTH", "HEIGHT"), required=False)
parser.add_argument('--direct', choices=('true', 'false'), default='True', required=False)
parser.add_argument('--notice', action='store_true', required=False)

Expand Down Expand Up @@ -324,6 +339,10 @@ def main():
github_token = ''.join(args.token)
if args.format: # -f option
format = list(args.format)
if args.graph_path:
graph_path = ''.join(args.graph_path)
if args.graph_size:
graph_size = args.graph_size
if args.direct: # --direct option
if args.direct == 'true':
direct = True
Expand All @@ -343,7 +362,8 @@ def main():
sys.exit(0)

run_dependency_scanner(package_manager, input_dir, output_dir, pip_activate_cmd, pip_deactivate_cmd,
output_custom_dir, app_name, github_token, format, direct, path_to_exclude)
output_custom_dir, app_name, github_token, format, direct, path_to_exclude,
graph_path, graph_size)


if __name__ == '__main__':
Expand Down

0 comments on commit f04963f

Please sign in to comment.