fosslight-dependency 3.0.7__py3-none-any.whl → 4.1.30__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. fosslight_dependency/LICENSES/LICENSE +201 -0
  2. fosslight_dependency/LICENSES/LicenseRef-3rd_party_licenses.txt +1254 -0
  3. fosslight_dependency/__init__.py +0 -1
  4. fosslight_dependency/_analyze_dependency.py +130 -0
  5. fosslight_dependency/_graph_convertor.py +67 -0
  6. fosslight_dependency/_help.py +79 -0
  7. fosslight_dependency/_package_manager.py +397 -0
  8. fosslight_dependency/cli.py +127 -0
  9. fosslight_dependency/constant.py +57 -0
  10. fosslight_dependency/dependency_item.py +103 -0
  11. fosslight_dependency/package_manager/Android.py +90 -0
  12. fosslight_dependency/package_manager/Cargo.py +144 -0
  13. fosslight_dependency/package_manager/Carthage.py +130 -0
  14. fosslight_dependency/package_manager/Cocoapods.py +194 -0
  15. fosslight_dependency/package_manager/Go.py +179 -0
  16. fosslight_dependency/package_manager/Gradle.py +123 -0
  17. fosslight_dependency/package_manager/Helm.py +106 -0
  18. fosslight_dependency/package_manager/Maven.py +274 -0
  19. fosslight_dependency/package_manager/Npm.py +296 -0
  20. fosslight_dependency/package_manager/Nuget.py +368 -0
  21. fosslight_dependency/package_manager/Pnpm.py +155 -0
  22. fosslight_dependency/package_manager/Pub.py +241 -0
  23. fosslight_dependency/package_manager/Pypi.py +395 -0
  24. fosslight_dependency/package_manager/Swift.py +159 -0
  25. fosslight_dependency/package_manager/Unity.py +118 -0
  26. fosslight_dependency/package_manager/Yarn.py +231 -0
  27. fosslight_dependency/package_manager/__init__.py +0 -0
  28. fosslight_dependency/run_dependency_scanner.py +393 -0
  29. fosslight_dependency-4.1.30.dist-info/METADATA +213 -0
  30. fosslight_dependency-4.1.30.dist-info/RECORD +37 -0
  31. {fosslight_dependency-3.0.7.dist-info → fosslight_dependency-4.1.30.dist-info}/WHEEL +1 -1
  32. fosslight_dependency-4.1.30.dist-info/entry_points.txt +2 -0
  33. fosslight_dependency-4.1.30.dist-info/licenses/LICENSES/Apache-2.0.txt +201 -0
  34. fosslight_dependency-4.1.30.dist-info/licenses/LICENSES/LicenseRef-3rd_party_licenses.txt +1254 -0
  35. fosslight_dependency-4.1.30.dist-info/licenses/LICENSES/MIT.txt +21 -0
  36. fosslight_dependency/_version.py +0 -1
  37. fosslight_dependency/analyze_dependency.py +0 -1090
  38. fosslight_dependency/third_party/askalono/askalono.exe +0 -0
  39. fosslight_dependency/third_party/askalono/askalono_macos +0 -0
  40. fosslight_dependency/third_party/nomos/nomossa +0 -0
  41. fosslight_dependency-3.0.7.dist-info/3rd_party_licenses.txt +0 -726
  42. fosslight_dependency-3.0.7.dist-info/METADATA +0 -51
  43. fosslight_dependency-3.0.7.dist-info/RECORD +0 -13
  44. fosslight_dependency-3.0.7.dist-info/entry_points.txt +0 -3
  45. {fosslight_dependency-3.0.7.dist-info → fosslight_dependency-4.1.30.dist-info/licenses}/LICENSE +0 -0
  46. {fosslight_dependency-3.0.7.dist-info → fosslight_dependency-4.1.30.dist-info}/top_level.txt +0 -0
@@ -1 +0,0 @@
1
- from ._version import __version__
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright (c) 2021 LG Electronics Inc.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ import os
7
+ import logging
8
+ import fosslight_dependency.constant as const
9
+ from fosslight_dependency._package_manager import deduplicate_dep_items
10
+ from fosslight_dependency.package_manager.Pypi import Pypi
11
+ from fosslight_dependency.package_manager.Npm import Npm
12
+ from fosslight_dependency.package_manager.Yarn import Yarn
13
+ from fosslight_dependency.package_manager.Maven import Maven
14
+ from fosslight_dependency.package_manager.Gradle import Gradle
15
+ from fosslight_dependency.package_manager.Pub import Pub
16
+ from fosslight_dependency.package_manager.Cocoapods import Cocoapods
17
+ from fosslight_dependency.package_manager.Android import Android
18
+ from fosslight_dependency.package_manager.Swift import Swift
19
+ from fosslight_dependency.package_manager.Carthage import Carthage
20
+ from fosslight_dependency.package_manager.Go import Go
21
+ from fosslight_dependency.package_manager.Nuget import Nuget
22
+ from fosslight_dependency.package_manager.Helm import Helm
23
+ from fosslight_dependency.package_manager.Unity import Unity
24
+ from fosslight_dependency.package_manager.Cargo import Cargo
25
+ from fosslight_dependency.package_manager.Pnpm import Pnpm
26
+ import fosslight_util.constant as constant
27
+
28
+ logger = logging.getLogger(constant.LOGGER_NAME)
29
+
30
+
31
+ def analyze_dependency(package_manager_name, input_dir, output_dir, pip_activate_cmd='', pip_deactivate_cmd='',
32
+ output_custom_dir='', app_name=const.default_app_name, github_token='', manifest_file_name=[],
33
+ direct=True):
34
+ ret = True
35
+ package_dep_item_list = []
36
+ cover_comment = ''
37
+ npm_fallback_to_yarn = False
38
+
39
+ if package_manager_name == const.PYPI:
40
+ package_manager = Pypi(input_dir, output_dir, pip_activate_cmd, pip_deactivate_cmd)
41
+ elif package_manager_name == const.NPM:
42
+ package_manager = Npm(input_dir, output_dir)
43
+ npm_fallback_to_yarn = True
44
+ elif package_manager_name == const.YARN:
45
+ package_manager = Yarn(input_dir, output_dir)
46
+ elif package_manager_name == const.MAVEN:
47
+ package_manager = Maven(input_dir, output_dir, output_custom_dir)
48
+ elif package_manager_name == const.GRADLE:
49
+ package_manager = Gradle(input_dir, output_dir, output_custom_dir)
50
+ elif package_manager_name == const.PUB:
51
+ package_manager = Pub(input_dir, output_dir)
52
+ elif package_manager_name == const.COCOAPODS:
53
+ package_manager = Cocoapods(input_dir, output_dir)
54
+ elif package_manager_name == const.ANDROID:
55
+ package_manager = Android(input_dir, output_dir, app_name)
56
+ elif package_manager_name == const.SWIFT:
57
+ package_manager = Swift(input_dir, output_dir, github_token)
58
+ elif package_manager_name == const.CARTHAGE:
59
+ package_manager = Carthage(input_dir, output_dir, github_token)
60
+ elif package_manager_name == const.GO:
61
+ package_manager = Go(input_dir, output_dir)
62
+ elif package_manager_name == const.NUGET:
63
+ package_manager = Nuget(input_dir, output_dir)
64
+ elif package_manager_name == const.HELM:
65
+ package_manager = Helm(input_dir, output_dir)
66
+ elif package_manager_name == const.UNITY:
67
+ package_manager = Unity(input_dir, output_dir)
68
+ elif package_manager_name == const.CARGO:
69
+ package_manager = Cargo(input_dir, output_dir)
70
+ elif package_manager_name == const.PNPM:
71
+ package_manager = Pnpm(input_dir, output_dir)
72
+ else:
73
+ logger.error(f"Not supported package manager name: {package_manager_name}")
74
+ ret = False
75
+ return ret, package_dep_item_list, cover_comment, package_manager_name
76
+
77
+ if manifest_file_name:
78
+ package_manager.set_manifest_file(manifest_file_name)
79
+
80
+ if direct:
81
+ package_manager.set_direct_dependencies(direct)
82
+ ret = package_manager.run_plugin()
83
+
84
+ if not ret and npm_fallback_to_yarn:
85
+ logger.warning("Npm analysis failed. Attempting to use Yarn as fallback...")
86
+ del package_manager
87
+ package_manager = Yarn(input_dir, output_dir)
88
+ package_manager_name = const.YARN
89
+
90
+ if manifest_file_name:
91
+ package_manager.set_manifest_file(manifest_file_name)
92
+ if direct:
93
+ package_manager.set_direct_dependencies(direct)
94
+
95
+ ret = package_manager.run_plugin()
96
+ if ret:
97
+ logger.info("Successfully switched to Yarn")
98
+ else:
99
+ logger.error("Yarn also failed")
100
+
101
+ if ret:
102
+ if direct:
103
+ package_manager.parse_direct_dependencies()
104
+
105
+ for f_name in package_manager.input_package_list_file:
106
+ logger.info(f"Parse oss information with file: {f_name}")
107
+
108
+ file_path = os.path.join(input_dir, f_name) if not os.path.isabs(f_name) else f_name
109
+ if os.path.isfile(file_path):
110
+ package_manager.parse_oss_information(f_name)
111
+ package_dep_item_list.extend(package_manager.dep_items)
112
+ else:
113
+ logger.error(f"Failed to open input file: {file_path}")
114
+ ret = False
115
+ if package_manager_name == const.PNPM:
116
+ logger.info("Parse oss information for pnpm")
117
+ package_manager.parse_oss_information_for_pnpm()
118
+ package_dep_item_list.extend(package_manager.dep_items)
119
+ if package_dep_item_list:
120
+ package_dep_item_list = deduplicate_dep_items(package_dep_item_list)
121
+ if ret:
122
+ logger.warning(f"### Complete to analyze: {package_manager_name}({input_dir}: {','.join(manifest_file_name)})")
123
+ if package_manager.cover_comment:
124
+ cover_comment = package_manager.cover_comment
125
+ else:
126
+ logger.error(f"### Fail to analyze: {package_manager_name}({input_dir}: {','.join(manifest_file_name)})")
127
+
128
+ del package_manager
129
+
130
+ return ret, package_dep_item_list, cover_comment, package_manager_name
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright (c) 2021 LG Electronics Inc.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+ from typing import Optional, Tuple
6
+ import igraph as ig
7
+ import matplotlib.pyplot as plt
8
+
9
+
10
+ class GraphConvertor:
11
+ def __init__(self, dep_items: Optional[list] = None):
12
+ self._verticies = {}
13
+ self._edges = []
14
+ if dep_items:
15
+ self.init_list(dep_items)
16
+
17
+ def init_list(self, dep_items: list):
18
+ """
19
+ Initialize dep_items to self._verticies and self._edges
20
+
21
+ Args:
22
+ dep_items : List containing package information
23
+ """
24
+ depend_on_package_dict = {}
25
+ for idx, file_item in enumerate(dep_items):
26
+ package_name = file_item.purl
27
+ depend_on_packages = file_item.depends_on
28
+ self._verticies[package_name] = idx
29
+ depend_on_package_dict[package_name] = depend_on_packages
30
+ else:
31
+ for package_name, depend_on_packages in depend_on_package_dict.items():
32
+ if not package_name:
33
+ pass
34
+ else:
35
+ package_idx = self._verticies[package_name]
36
+ for depend_on_package in depend_on_packages:
37
+ if not depend_on_package:
38
+ pass
39
+ else:
40
+ depend_on_package_idx = self._verticies[depend_on_package]
41
+ self._edges.append((package_idx, depend_on_package_idx))
42
+
43
+ def save(self, path: str, size: Tuple[(int, int)]):
44
+ g = ig.Graph((len(self._verticies)), (self._edges), directed=True)
45
+
46
+ g["title"] = "Dependency Graph"
47
+ g.vs["name"] = list(self._verticies.keys())
48
+
49
+ fig, ax = plt.subplots(figsize=(tuple(map((lambda x: x / 100), size))))
50
+ fig.tight_layout()
51
+
52
+ ig.plot(
53
+ g,
54
+ target=ax,
55
+ layout="kk",
56
+ vertex_size=15,
57
+ vertex_color=["#FFD2D2"],
58
+ vertex_label=(g.vs["name"]),
59
+ vertex_label_dist=1.5,
60
+ vertex_label_size=7.0,
61
+ edge_width=0.5,
62
+ edge_color=["#FFD2D2"],
63
+ edge_arrow_size=5,
64
+ edge_arrow_width=5,
65
+ )
66
+
67
+ fig.savefig(path)
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright (c) 2021 LG Electronics Inc.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+ from fosslight_util.help import PrintHelpMsg, print_package_version
6
+ from fosslight_util.output_format import SUPPORT_FORMAT
7
+
8
+ _HELP_MESSAGE_DEPENDENCY = f"""
9
+ Usage: fosslight_dependency [option1] <arg1> [option2] <arg2>...
10
+
11
+ FOSSLight Dependency Scanner is the tool that supports the analysis of dependencies for multiple package managers.
12
+ It detects the manifest file of package managers automatically and analyzes the dependencies with using open source tools.
13
+ Then, it generates the report file that contains OSS information of dependencies.
14
+
15
+ Currently, it supports the following package managers:
16
+ Gradle (Java)
17
+ Maven (Java)
18
+ NPM (Node.js)
19
+ PNPM (Node.js)
20
+ Yarn (Node.js)
21
+ PIP (Python)
22
+ Pub (Dart with flutter)
23
+ Cocoapods (Swift/Obj-C)
24
+ Swift (Swift)
25
+ Carthage (Swift/Obj-C)
26
+ Go (Go)
27
+ Nuget (.NET)
28
+ Helm (Kubernetes)
29
+ Unity (Unity)
30
+ Cargo (Rust)
31
+
32
+ Options:
33
+ Optional
34
+ -h\t\t\t\t Print help message.
35
+ -v\t\t\t\t Print the version of the script.
36
+ -m <package_manager>\t Enter the package manager.
37
+ \t(npm, maven, gradle, pypi, pub, cocoapods, android, swift, carthage,
38
+ \t go, nuget, helm, unity, cargo, pnpm, yarn)
39
+ -p <input_path>\t\t Enter the path where the script will be run.
40
+ -e <exclude_path>\t\t Enter the path where the analysis will not be performed (files and directories).
41
+ \t\t\t\t * IMPORTANT: Always wrap patterns in double quotes ("") to avoid shell expansion.
42
+ \t\t\t\t Example) fosslight_dependency -e "test/abc.py" "*.jar"
43
+ -o <output_path>\t\t Output path
44
+ \t\t\t\t\t(If you want to generate the specific file name, add the output path with file name.)
45
+ -f <format> [<format> ...]\t Output formats
46
+ \t\t\t\t \t({', '.join(SUPPORT_FORMAT)})
47
+ \t\t\t\t Multiple formats can be specified separated by space.
48
+ --graph-path <save_path> \t Enter the path where the graph image will be saved
49
+ \t\t\t\t\t(ex. /your/directory/path/filename.[pdf, jpg, png]) (recommend pdf extension)
50
+ --graph-size <width> <height> Enter the size of the graph image (The size unit is pixels)
51
+ \t\t\t\t\t--graph-path option is required
52
+ --direct\t\t\t Print the direct/transitive dependency type in comment.
53
+ \t\tChoice 'True' or 'False'. (default:True)
54
+ -r\t\t\t\t Recursive mode. Scan all subdirectories for manifest files.
55
+ --notice\t\t\t Print the open source license notice text.
56
+
57
+ Required only for swift, carthage
58
+ -t <token>\t\t\t Enter the github personal access token.
59
+
60
+ Optional only for pypi
61
+ -a <activate_cmd>\t\t Virtual environment activate command(ex, 'conda activate (venv name)')
62
+ -d <deactivate_cmd>\t\t Virtual environment deactivate command(ex, 'conda deactivate')
63
+
64
+ Optional only for gradle, maven
65
+ -c <dir_name>\t\t Enter the customized build output directory name
66
+ \t\t-Default name : 'build' for gradle, 'target' for maven
67
+
68
+ Optional only for android
69
+ -n <app_name>\t\t Enter the application directory name where the plugin output file is located(default: app)
70
+ """
71
+
72
+
73
+ def print_version(pkg_name: str) -> None:
74
+ print_package_version(pkg_name, "FOSSLight Dependency Scanner Version:")
75
+
76
+
77
+ def print_help_msg():
78
+ helpMsg = PrintHelpMsg(_HELP_MESSAGE_DEPENDENCY)
79
+ helpMsg.print_help_msg(True)
@@ -0,0 +1,397 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright (c) 2021 LG Electronics Inc.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ import os
7
+ import logging
8
+ import platform
9
+ import re
10
+ import base64
11
+ import subprocess
12
+ import shutil
13
+ import stat
14
+ from packageurl.contrib import url2purl
15
+ from askalono import identify
16
+ import fosslight_util.constant as constant
17
+ import fosslight_dependency.constant as const
18
+
19
+ try:
20
+ from github import Github
21
+ except Exception:
22
+ pass
23
+
24
+ logger = logging.getLogger(constant.LOGGER_NAME)
25
+
26
+ gradle_config = ['runtimeClasspath', 'runtime']
27
+ android_config = ['releaseRuntimeClasspath']
28
+ ASKALONO_THRESHOLD = 0.7
29
+
30
+
31
+ class PackageManager:
32
+ input_package_list_file = []
33
+ direct_dep = False
34
+ total_dep_list = []
35
+ direct_dep_list = []
36
+
37
+ def __init__(self, package_manager_name, dn_url, input_dir, output_dir):
38
+ self.input_package_list_file = []
39
+ self.direct_dep = False
40
+ self.total_dep_list = []
41
+ self.direct_dep_list = []
42
+ self.package_manager_name = package_manager_name
43
+ self.input_dir = input_dir
44
+ self.output_dir = output_dir
45
+ self.dn_url = dn_url
46
+ self.manifest_file_name = []
47
+ self.relation_tree = {}
48
+ self.package_name = ''
49
+ self.cover_comment = ''
50
+ self.dep_items = []
51
+
52
+ self.platform = platform.system()
53
+
54
+ def __del__(self):
55
+ self.input_package_list_file = []
56
+ self.direct_dep = False
57
+ self.total_dep_list = []
58
+ self.direct_dep_list = []
59
+ self.package_manager_name = ''
60
+ self.input_dir = ''
61
+ self.output_dir = ''
62
+ self.dn_url = ''
63
+ self.manifest_file_name = []
64
+ self.relation_tree = {}
65
+ self.package_name = ''
66
+ self.dep_items = []
67
+
68
+ def run_plugin(self):
69
+ ret = True
70
+ if self.package_manager_name == const.GRADLE or self.package_manager_name == const.ANDROID:
71
+ ret = self.run_gradle_task()
72
+ else:
73
+ logger.info(f"This package manager({self.package_manager_name}) skips the step to run plugin.")
74
+ return ret
75
+
76
+ def append_input_package_list_file(self, input_package_file):
77
+ self.input_package_list_file.append(input_package_file)
78
+
79
+ def set_manifest_file(self, manifest_file_name):
80
+ self.manifest_file_name = manifest_file_name
81
+
82
+ def set_direct_dependencies(self, direct):
83
+ self.direct_dep = direct
84
+
85
+ def parse_direct_dependencies(self):
86
+ pass
87
+
88
+ def run_gradle_task(self):
89
+ ret_task = True
90
+ if os.path.isfile(const.SUPPORT_PACKAE.get(self.package_manager_name)):
91
+ gradle_backup = f'{const.SUPPORT_PACKAE.get(self.package_manager_name)}_bk'
92
+
93
+ shutil.copy(const.SUPPORT_PACKAE.get(self.package_manager_name), gradle_backup)
94
+ ret_alldeps = self.add_allDeps_in_gradle()
95
+
96
+ ret_plugin = False
97
+ if (self.package_manager_name == const.ANDROID):
98
+ module_build_gradle = os.path.join(self.app_name, const.SUPPORT_PACKAE.get(self.package_manager_name))
99
+ module_gradle_backup = f'{module_build_gradle}_bk'
100
+ if os.path.isfile(module_build_gradle) and (not os.path.isfile(self.input_file_name)):
101
+ shutil.copy(module_build_gradle, module_gradle_backup)
102
+ ret_plugin = self.add_android_plugin_in_gradle(module_build_gradle)
103
+
104
+ if os.path.isfile('gradlew') or os.path.isfile('gradlew.bat'):
105
+ if self.platform == const.WINDOWS:
106
+ cmd_gradle = "gradlew.bat"
107
+ else:
108
+ cmd_gradle = "./gradlew"
109
+ else:
110
+ ret_task = False
111
+ self.set_direct_dependencies(False)
112
+ logger.warning('No gradlew file exists (Skip to find dependencies relationship.).')
113
+ if ret_plugin:
114
+ logger.warning('Also it cannot run android-dependency-scanning plugin.')
115
+ if ret_task:
116
+ current_mode = change_file_mode(cmd_gradle)
117
+ if ret_alldeps:
118
+ cmd = f"{cmd_gradle} allDeps"
119
+ try:
120
+ ret = subprocess.check_output(cmd, shell=True, encoding='utf-8')
121
+ if ret != 0:
122
+ self.parse_dependency_tree(ret)
123
+ else:
124
+ self.set_direct_dependencies(False)
125
+ logger.warning(f"Fail to run {cmd}")
126
+ except Exception as e:
127
+ self.set_direct_dependencies(False)
128
+ logger.warning(f"Cannot print 'depends on' information. (fail {cmd}: {e})")
129
+
130
+ if ret_plugin:
131
+ cmd = f"{cmd_gradle} generateLicenseTxt"
132
+ try:
133
+ ret = subprocess.check_output(cmd, shell=True, encoding='utf-8')
134
+ if ret == 0:
135
+ ret_task = False
136
+ logger.error(f'Fail to run {cmd}')
137
+ if os.path.isfile(self.input_file_name):
138
+ logger.info('Automatically run android-dependency-scanning plugin and generate output.')
139
+ self.plugin_auto_run = True
140
+ else:
141
+ logger.warning('Automatically run android-dependency-scanning plugin, but fail to generate output.')
142
+ except Exception as e:
143
+ logger.error(f'Fail to run {cmd}: {e}')
144
+ ret_task = False
145
+ change_file_mode(cmd_gradle, current_mode)
146
+
147
+ if os.path.isfile(gradle_backup):
148
+ os.remove(const.SUPPORT_PACKAE.get(self.package_manager_name))
149
+ shutil.move(gradle_backup, const.SUPPORT_PACKAE.get(self.package_manager_name))
150
+
151
+ if (self.package_manager_name == const.ANDROID):
152
+ if os.path.isfile(module_gradle_backup):
153
+ os.remove(module_build_gradle)
154
+ shutil.move(module_gradle_backup, module_build_gradle)
155
+ if os.path.isfile(self.input_file_name):
156
+ logger.info(f'Found {self.input_file_name}, skip to run plugin.')
157
+ self.set_direct_dependencies(False)
158
+ ret_task = True
159
+ return ret_task
160
+
161
+ def add_android_plugin_in_gradle(self, module_build_gradle):
162
+ ret = False
163
+ build_script = '''buildscript {
164
+ repositories {
165
+ mavenCentral()
166
+ }
167
+ dependencies {
168
+ //Android dependency scanning Plugin
169
+ classpath 'org.fosslight:android-dependency-scanning:+'
170
+ }
171
+ }'''
172
+ apply = "apply plugin: 'org.fosslight'\n"
173
+ try:
174
+ with open(const.SUPPORT_PACKAE.get(self.package_manager_name), 'r', encoding='utf-8') as original:
175
+ data = original.read()
176
+ with open(const.SUPPORT_PACKAE.get(self.package_manager_name), 'w', encoding='utf-8') as modified:
177
+ modified.write(f"{build_script}\n{data}")
178
+ ret = True
179
+ except Exception as e:
180
+ logging.warning(f"Cannot add the buildscript task in build.gradle: {e}")
181
+
182
+ try:
183
+ with open(module_build_gradle, 'a', encoding='utf-8') as modified:
184
+ modified.write(f'\n{apply}\n')
185
+ ret = True
186
+ except Exception as e:
187
+ logging.warning(f"Cannot add the apply plugin in {module_build_gradle}: {e}")
188
+ return ret
189
+
190
+ def add_allDeps_in_gradle(self):
191
+ ret = False
192
+ config = android_config if self.package_manager_name == 'android' else gradle_config
193
+ configuration = ','.join([f'project.configurations.{c}' for c in config])
194
+
195
+ allDeps = f'''allprojects {{
196
+ task allDeps(type: DependencyReportTask) {{
197
+ doFirst{{
198
+ try {{
199
+ configurations = [{configuration}] as Set }}
200
+ catch(UnknownConfigurationException) {{}}
201
+ }}
202
+ }}
203
+ }}'''
204
+ try:
205
+ with open(const.SUPPORT_PACKAE.get(self.package_manager_name), 'a', encoding='utf8') as f:
206
+ f.write(f'\n{allDeps}\n')
207
+ ret = True
208
+ except Exception as e:
209
+ logging.warning(f"Cannot add the allDeps task in build.gradle: {e}")
210
+
211
+ return ret
212
+
213
+ def create_dep_stack(self, dep_line, config):
214
+ packages_in_config = False
215
+ dep_stack = []
216
+ cur_flag = ''
217
+ dep_level = -1
218
+ dep_level_plus = False
219
+ for line in dep_line.split('\n'):
220
+ try:
221
+ if not packages_in_config:
222
+ filtered = next(filter(lambda c: re.findall(rf'^{c}\s\-', line), config), None)
223
+ if filtered:
224
+ packages_in_config = True
225
+ else:
226
+ if line == '':
227
+ packages_in_config = False
228
+ prev_flag = cur_flag
229
+ prev_dep_level = dep_level
230
+ dep_level = line.count("|")
231
+
232
+ re_result = re.findall(r'([\+|\\])\-\-\-\s([^\:\s]+\:[^\:\s]+)\:([^\:\s]+)', line)
233
+ if re_result:
234
+ cur_flag = re_result[0][0]
235
+ if (prev_flag == '\\') and (prev_dep_level == dep_level):
236
+ dep_level_plus = True
237
+ if dep_level_plus and (prev_flag == '\\') and (prev_dep_level != dep_level):
238
+ dep_level_plus = False
239
+ if dep_level_plus:
240
+ dep_level += 1
241
+ dep_name = f'{re_result[0][1]}({re_result[0][2]})'
242
+ dep_stack = dep_stack[:dep_level] + [dep_name]
243
+ yield dep_stack[:dep_level], dep_name
244
+ else:
245
+ cur_flag = ''
246
+ except Exception as e:
247
+ logger.warning(f"Failed to parse dependency tree: {e}")
248
+
249
+ def parse_dependency_tree(self, f_name):
250
+ config = android_config if self.package_manager_name == 'android' else gradle_config
251
+ try:
252
+ for stack, name in self.create_dep_stack(f_name, config):
253
+ self.total_dep_list.append(name)
254
+ if len(stack) == 0:
255
+ self.direct_dep_list.append(name)
256
+ else:
257
+ if stack[-1] not in self.relation_tree:
258
+ self.relation_tree[stack[-1]] = []
259
+ self.relation_tree[stack[-1]].append(name)
260
+ except Exception as e:
261
+ logger.warning(f'Fail to parse gradle dependency tree:{e}')
262
+
263
+
264
+ def get_url_to_purl(url, pkg_manager, oss_name='', oss_version=''):
265
+ purl_prefix = f'pkg:{pkg_manager}'
266
+ purl = str(url2purl.get_purl(url))
267
+ if not re.match(purl_prefix, purl):
268
+ match = re.match(constant.PKG_PATTERN.get(pkg_manager, 'not_support'), url)
269
+ try:
270
+ if match and (match != ''):
271
+ if pkg_manager == 'maven':
272
+ purl = f'{purl_prefix}/{match.group(1)}/{match.group(2)}@{match.group(3)}'
273
+ elif pkg_manager == 'pub':
274
+ purl = f'{purl_prefix}/{match.group(1)}@{match.group(2)}'
275
+ elif pkg_manager == 'cocoapods':
276
+ match = re.match(r'([^\/]+)\/?([^\/]*)', oss_name) # ex, GoogleUtilities/NSData+zlib
277
+ purl = f'{purl_prefix}/{match.group(1)}@{oss_version}'
278
+ if match.group(2):
279
+ purl = f'{purl}#{match.group(2)}'
280
+ elif pkg_manager == 'go':
281
+ purl = f'{purl_prefix}lang/{match.group(1)}@{match.group(2)}'
282
+ elif pkg_manager == 'cargo':
283
+ purl = f'{purl_prefix}/{oss_name}@{oss_version}'
284
+ else:
285
+ if pkg_manager == 'swift':
286
+ if oss_version:
287
+ purl = f'{purl_prefix}/{oss_name}@{oss_version}'
288
+ else:
289
+ purl = f'{purl_prefix}/{oss_name}'
290
+ elif pkg_manager == 'carthage':
291
+ if oss_version:
292
+ purl = f'{purl}@{oss_version}'
293
+ except Exception:
294
+ logger.debug('Fail to get purl. So use the link purl({purl}).')
295
+ return purl
296
+
297
+
298
+ def version_refine(oss_version):
299
+ version_cmp = oss_version.upper()
300
+
301
+ if version_cmp.find(".RELEASE") != -1:
302
+ oss_version = version_cmp.rstrip(".RELEASE")
303
+ elif version_cmp.find(".FINAL") != -1:
304
+ oss_version = version_cmp.rstrip(".FINAL")
305
+
306
+ return oss_version
307
+
308
+
309
+ def connect_github(github_token):
310
+ if len(github_token) > 0:
311
+ g = Github(github_token)
312
+ else:
313
+ g = Github()
314
+
315
+ return g
316
+
317
+
318
+ def get_github_license(g, github_repo):
319
+ license_name = ''
320
+
321
+ try:
322
+ repository = g.get_repo(github_repo)
323
+ except Exception:
324
+ logger.info("It cannot find the license name. Please use '-t' option with github token.")
325
+ logger.info("{0}{1}".format("refer:https://docs.github.com/en/github/authenticating-to-github/",
326
+ "keeping-your-account-and-data-secure/creating-a-personal-access-token"))
327
+ repository = ''
328
+
329
+ if repository != '':
330
+ try:
331
+ license_name = repository.get_license().license.spdx_id
332
+ if license_name == "" or license_name == "NOASSERTION":
333
+ try:
334
+ license_txt_data = base64.b64decode(repository.get_license().content).decode('utf-8')
335
+ license_name = check_license_name(license_txt_data)
336
+ except Exception:
337
+ logger.info("Cannot find the license name with askalono.")
338
+ except Exception:
339
+ logger.info("Cannot find the license name with github api.")
340
+
341
+ return license_name
342
+
343
+
344
+ def check_license_name(license_txt, is_filepath=False):
345
+ license_name = ''
346
+ if is_filepath:
347
+ with open(license_txt, 'r', encoding='utf-8') as f:
348
+ license_content = f.read()
349
+ else:
350
+ license_content = license_txt
351
+
352
+ detect_askalono = identify(license_content)
353
+ if detect_askalono.score > ASKALONO_THRESHOLD:
354
+ license_name = detect_askalono.name
355
+ return license_name
356
+
357
+
358
+ def change_file_mode(filepath, mode=''):
359
+ current_mode = ''
360
+
361
+ if not os.path.exists(filepath):
362
+ logger.debug(f"The file{filepath} does not exist.")
363
+ else:
364
+ current_mode = os.stat(filepath).st_mode
365
+ if not mode:
366
+ new_mode = current_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
367
+ else:
368
+ new_mode = mode
369
+ os.chmod(filepath, new_mode)
370
+ logger.debug(f"File mode of {filepath} has been changed to {oct(new_mode)}.")
371
+ return current_mode
372
+
373
+
374
+ def deduplicate_dep_items(dep_items):
375
+ if not dep_items:
376
+ return dep_items
377
+
378
+ unique_items = []
379
+ seen = set()
380
+
381
+ for item in dep_items:
382
+ first_oss = item.oss_items[0] if getattr(item, "oss_items", None) else None
383
+ oss_name = getattr(first_oss, "name", None) if first_oss else None
384
+ oss_ver = getattr(first_oss, "version", None) if first_oss else None
385
+ comment = getattr(first_oss, "comment", None) if first_oss else None
386
+
387
+ depends_on = None
388
+ if getattr(item, "depends_on", None):
389
+ depends_on = tuple(sorted(item.depends_on))
390
+
391
+ key = (getattr(item, "purl", None), oss_name, oss_ver, comment, depends_on)
392
+ if key in seen:
393
+ continue
394
+ seen.add(key)
395
+ unique_items.append(item)
396
+
397
+ return unique_items