Skip to content

Commit 93b2475

Browse files
authored
Merge pull request #130 from scanoss/fix/mdaloia/SP-2870-scanoss-py-fs-does-not-write-output-when-to-file
[SP-2870] fix: cyclonedx format output not writing to file
2 parents e65b58e + be91ae6 commit 93b2475

File tree

6 files changed

+55
-41
lines changed

6 files changed

+55
-41
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
### Added
1010
- Upcoming changes...
1111

12+
## [1.27.1] - 2025-07-09
13+
### Fixed
14+
- Fixed when running `folder-scan` with `--format cyclonedx` the output was not writing to file
15+
- Fixed when running `container-scan` with `--format cyclonedx` the output was not writing to file
16+
1217
## [1.27.0] - 2025-06-30
1318
### Added
1419
- Add directory hash calculation to folder hasher
@@ -577,3 +582,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
577582
[1.26.2]: https://github.com/scanoss/scanoss.py/compare/v1.26.1...v1.26.2
578583
[1.26.3]: https://github.com/scanoss/scanoss.py/compare/v1.26.2...v1.26.3
579584
[1.27.0]: https://github.com/scanoss/scanoss.py/compare/v1.26.3...v1.27.0
585+
[1.27.1]: https://github.com/scanoss/scanoss.py/compare/v1.27.0...v1.27.1

src/scanoss/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@
2222
THE SOFTWARE.
2323
"""
2424

25-
__version__ = '1.27.0'
25+
__version__ = '1.27.1'

src/scanoss/cyclonedx.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,13 @@
2222
THE SOFTWARE.
2323
"""
2424

25+
import datetime
2526
import json
2627
import os.path
2728
import sys
2829
import uuid
29-
import datetime
3030

3131
from . import __version__
32-
3332
from .scanossbase import ScanossBase
3433
from .spdxlite import SpdxLite
3534

@@ -49,7 +48,7 @@ def __init__(self, debug: bool = False, output_file: str = None):
4948
self.debug = debug
5049
self._spdx = SpdxLite(debug=debug)
5150

52-
def parse(self, data: json):
51+
def parse(self, data: json): # noqa: PLR0912, PLR0915
5352
"""
5453
Parse the given input (raw/plain) JSON string and return CycloneDX summary
5554
:param data: json - JSON object
@@ -58,7 +57,7 @@ def parse(self, data: json):
5857
if not data:
5958
self.print_stderr('ERROR: No JSON data provided to parse.')
6059
return None, None
61-
self.print_debug(f'Processing raw results into CycloneDX format...')
60+
self.print_debug('Processing raw results into CycloneDX format...')
6261
cdx = {}
6362
vdx = {}
6463
for f in data:
@@ -171,17 +170,22 @@ def produce_from_file(self, json_file: str, output_file: str = None) -> bool:
171170
success = self.produce_from_str(f.read(), output_file)
172171
return success
173172

174-
def produce_from_json(self, data: json, output_file: str = None) -> bool:
173+
def produce_from_json(self, data: json, output_file: str = None) -> tuple[bool, json]: # noqa: PLR0912
175174
"""
176-
Produce the CycloneDX output from the input data
177-
:param data: JSON object
178-
:param output_file: Output file (optional)
179-
:return: True if successful, False otherwise
175+
Produce the CycloneDX output from the raw scan results input data
176+
177+
Args:
178+
data (json): JSON object
179+
output_file (str, optional): Output file (optional). Defaults to None.
180+
181+
Returns:
182+
bool: True if successful, False otherwise
183+
json: The CycloneDX output
180184
"""
181185
cdx, vdx = self.parse(data)
182186
if not cdx:
183187
self.print_stderr('ERROR: No CycloneDX data returned for the JSON string provided.')
184-
return False
188+
return False, None
185189
self._spdx.load_license_data() # Load SPDX license name data for later reference
186190
#
187191
# Using CDX version 1.4: https://cyclonedx.org/docs/1.4/json/
@@ -264,7 +268,7 @@ def produce_from_json(self, data: json, output_file: str = None) -> bool:
264268
if output_file:
265269
file.close()
266270

267-
return True
271+
return True, data
268272

269273
def produce_from_str(self, json_str: str, output_file: str = None) -> bool:
270274
"""

src/scanoss/scanner.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,40 +22,40 @@
2222
THE SOFTWARE.
2323
"""
2424

25+
import datetime
2526
import json
2627
import os
27-
from pathlib import Path
2828
import sys
29-
import datetime
29+
from pathlib import Path
3030
from typing import Any, Dict, List, Optional
31-
import importlib_resources
3231

32+
import importlib_resources
3333
from progress.bar import Bar
3434
from progress.spinner import Spinner
3535
from pypac.parser import PACFile
3636

3737
from scanoss.file_filters import FileFilters
3838

39-
from .scanossapi import ScanossApi
40-
from .cyclonedx import CycloneDx
41-
from .spdxlite import SpdxLite
39+
from . import __version__
4240
from .csvoutput import CsvOutput
43-
from .threadedscanning import ThreadedScanning
41+
from .cyclonedx import CycloneDx
4442
from .scancodedeps import ScancodeDeps
45-
from .threadeddependencies import ThreadedDependencies, SCOPE
46-
from .scanossgrpc import ScanossGrpc
47-
from .scantype import ScanType
48-
from .scanossbase import ScanossBase
4943
from .scanoss_settings import ScanossSettings
44+
from .scanossapi import ScanossApi
45+
from .scanossbase import ScanossBase
46+
from .scanossgrpc import ScanossGrpc
5047
from .scanpostprocessor import ScanPostProcessor
51-
from . import __version__
48+
from .scantype import ScanType
49+
from .spdxlite import SpdxLite
50+
from .threadeddependencies import SCOPE, ThreadedDependencies
51+
from .threadedscanning import ThreadedScanning
5252

5353
FAST_WINNOWING = False
5454
try:
5555
from scanoss_winnowing.winnowing import Winnowing
5656

5757
FAST_WINNOWING = True
58-
except ModuleNotFoundError or ImportError:
58+
except (ModuleNotFoundError, ImportError):
5959
FAST_WINNOWING = False
6060
from .winnowing import Winnowing
6161

@@ -284,7 +284,7 @@ def is_dependency_scan(self):
284284
return True
285285
return False
286286

287-
def scan_folder_with_options(
287+
def scan_folder_with_options( # noqa: PLR0913
288288
self,
289289
scan_dir: str,
290290
deps_file: str = None,
@@ -332,7 +332,7 @@ def scan_folder_with_options(
332332
success = False
333333
return success
334334

335-
def scan_folder(self, scan_dir: str) -> bool:
335+
def scan_folder(self, scan_dir: str) -> bool: # noqa: PLR0912, PLR0915
336336
"""
337337
Scan the specified folder producing fingerprints, send to the SCANOSS API and return results
338338
@@ -400,7 +400,7 @@ def scan_folder(self, scan_dir: str) -> bool:
400400
scan_block += wfp
401401
scan_size = len(scan_block.encode('utf-8'))
402402
wfp_file_count += 1
403-
# If the scan request block (group of WFPs) or larger than the POST size or we have reached the file limit, add it to the queue
403+
# If the scan request block (group of WFPs) or larger than the POST size or we have reached the file limit, add it to the queue # noqa: E501
404404
if wfp_file_count > self.post_file_count or scan_size >= self.max_post_size:
405405
self.threaded_scan.queue_add(scan_block)
406406
queue_size += 1
@@ -484,7 +484,7 @@ def __finish_scan_threaded(self, file_map: Optional[Dict[Any, Any]] = None) -> b
484484
self.__log_result(json.dumps(results, indent=2, sort_keys=True))
485485
elif self.output_format == 'cyclonedx':
486486
cdx = CycloneDx(self.debug, self.scan_output)
487-
success = cdx.produce_from_json(results)
487+
success, _ = cdx.produce_from_json(results)
488488
elif self.output_format == 'spdxlite':
489489
spdxlite = SpdxLite(self.debug, self.scan_output)
490490
success = spdxlite.produce_from_json(results)
@@ -509,7 +509,7 @@ def _merge_scan_results(
509509
for response in scan_responses:
510510
if response is not None:
511511
if file_map:
512-
response = self._deobfuscate_filenames(response, file_map)
512+
response = self._deobfuscate_filenames(response, file_map) # noqa: PLW2901
513513
results.update(response)
514514

515515
dep_files = dep_responses.get('files', None) if dep_responses else None
@@ -532,7 +532,7 @@ def _deobfuscate_filenames(self, response: dict, file_map: dict) -> dict:
532532
deobfuscated[key] = value
533533
return deobfuscated
534534

535-
def scan_file_with_options(
535+
def scan_file_with_options( # noqa: PLR0913
536536
self,
537537
file: str,
538538
deps_file: str = None,
@@ -603,7 +603,7 @@ def scan_file(self, file: str) -> bool:
603603
success = False
604604
return success
605605

606-
def scan_files(self, files: []) -> bool:
606+
def scan_files(self, files: []) -> bool: # noqa: PLR0912, PLR0915
607607
"""
608608
Scan the specified list of files, producing fingerprints, send to the SCANOSS API and return results
609609
Please note that by providing an explicit list you bypass any exclusions that may be defined on the scanner
@@ -657,7 +657,7 @@ def scan_files(self, files: []) -> bool:
657657
file_count += 1
658658
if self.threaded_scan:
659659
wfp_size = len(wfp.encode('utf-8'))
660-
# If the WFP is bigger than the max post size and we already have something stored in the scan block, add it to the queue
660+
# If the WFP is bigger than the max post size and we already have something stored in the scan block, add it to the queue # noqa: E501
661661
if scan_block != '' and (wfp_size + scan_size) >= self.max_post_size:
662662
self.threaded_scan.queue_add(scan_block)
663663
queue_size += 1
@@ -666,7 +666,7 @@ def scan_files(self, files: []) -> bool:
666666
scan_block += wfp
667667
scan_size = len(scan_block.encode('utf-8'))
668668
wfp_file_count += 1
669-
# If the scan request block (group of WFPs) or larger than the POST size or we have reached the file limit, add it to the queue
669+
# If the scan request block (group of WFPs) or larger than the POST size or we have reached the file limit, add it to the queue # noqa: E501
670670
if wfp_file_count > self.post_file_count or scan_size >= self.max_post_size:
671671
self.threaded_scan.queue_add(scan_block)
672672
queue_size += 1
@@ -755,7 +755,7 @@ def scan_contents(self, filename: str, contents: bytes) -> bool:
755755
success = False
756756
return success
757757

758-
def scan_wfp_file(self, file: str = None) -> bool:
758+
def scan_wfp_file(self, file: str = None) -> bool: # noqa: PLR0912, PLR0915
759759
"""
760760
Scan the contents of the specified WFP file (in the current process)
761761
:param file: Scan the contents of the specified WFP file (in the current process)

src/scanoss/scanners/container_scanner.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -436,10 +436,12 @@ def _format_cyclonedx_output(self) -> str:
436436
scan_results = {}
437437
for f in self.scanner.decorated_scan_results['files']:
438438
scan_results[f['file']] = [f]
439-
if not cdx.produce_from_json(scan_results, self.output_file):
439+
success, cdx_output = cdx.produce_from_json(scan_results)
440+
if not success:
440441
error_msg = 'Failed to produce CycloneDX output'
441442
self.base.print_stderr(error_msg)
442-
raise ValueError(error_msg)
443+
return None
444+
return json.dumps(cdx_output, indent=2)
443445

444446
def _format_spdxlite_output(self) -> str:
445447
"""

src/scanoss/scanners/scanner_hfh.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ def _format_plain_output(self) -> str:
162162
else str(self.scanner.scan_results)
163163
)
164164

165-
def _format_cyclonedx_output(self) -> str:
165+
def _format_cyclonedx_output(self) -> str: # noqa: PLR0911
166166
if not self.scanner.scan_results:
167167
return ''
168168
try:
@@ -196,14 +196,16 @@ def _format_cyclonedx_output(self) -> str:
196196

197197
decorated_scan_results = self.scanner.client.get_dependencies(get_dependencies_json_request)
198198

199-
cdx = CycloneDx(self.base.debug, self.output_file)
199+
cdx = CycloneDx(self.base.debug)
200200
scan_results = {}
201201
for f in decorated_scan_results['files']:
202202
scan_results[f['file']] = [f]
203-
if not cdx.produce_from_json(scan_results, self.output_file):
203+
success, cdx_output = cdx.produce_from_json(scan_results)
204+
if not success:
204205
error_msg = 'ERROR: Failed to produce CycloneDX output'
205206
self.base.print_stderr(error_msg)
206-
raise ValueError(error_msg)
207+
return None
208+
return json.dumps(cdx_output, indent=2)
207209
except Exception as e:
208210
self.base.print_stderr(f'ERROR: Failed to get license information: {e}')
209211
return None

0 commit comments

Comments
 (0)