diff --git a/.readthedocs.yml b/.readthedocs.yml index a8a9b78e..a5e2546d 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,17 +1,23 @@ -# .readthedocs.yml -# Read the Docs configuration file +# Read the Docs configuration file for Sphinx projects # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 -# Build documentation in the docs/ directory with Sphinx +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.8" + +# Build documentation in the "docs/" directory with Sphinx sphinx: configuration: doc/conf.py - -# Optionally set the version of Python and requirements required to build your docs + +# Optional but recommended, declare the Python requirements required +# to build your documentation +# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: - version: 3.7 install: - method: pip path: . diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ecba4dfb..d196a172 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,18 @@ Change log ========== +0.19.19 (2023-11-22) +-------------------- + +Bug fixes +~~~~~~~~~ + +* **report:** support originalUriBaseIds fields for SARIF + (causing malfunction of underline comments to the Swarm) +* **doc:** update RTD (https://universum.readthedocs.io/en/latest/) config + (old config format no longer supported) + + 0.19.18 (2023-11-10) -------------------- diff --git a/tests/test_code_report.py b/tests/test_code_report.py index f73cd2db..e39d69c0 100644 --- a/tests/test_code_report.py +++ b/tests/test_code_report.py @@ -123,6 +123,52 @@ def finalize(self) -> str: } """ +sarif_report_split_uri = """ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "Checkstyle", + "semanticVersion": "8.43", + "version": "8.43" + } + }, + "results": [ + { + "level": "warning", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "my_file", + "uriBaseId": "ROOTPATH" + }, + "region": { + "startColumn": 1, + "startLine": 1 + } + } + } + ], + "message": { + "text": "Error!" + }, + "ruleId": "testRule" + } + ], + "originalUriBaseIds": { + "ROOTPATH": { + "uri": "my_path" + } + } + } + ] +} +""" + config_uncrustify = """ code_width = 120 input_tab_size = 2 @@ -142,6 +188,7 @@ def finalize(self) -> str: [[json_report], False], [[sarif_report_minimal], True], [[sarif_report], False], + [[sarif_report_split_uri], False], [[json_report_minimal, sarif_report_minimal], True], [[json_report, sarif_report], False], [[json_report_minimal, sarif_report], False], @@ -151,6 +198,7 @@ def finalize(self) -> str: 'json_issues_found', 'sarif_no_issues', 'sarif_issues_found', + 'sarif_split_uri_issues_found', 'both_tested_no_issues', 'both_tested_issues_in_both', 'both_tested_issues_in_sarif', diff --git a/universum/__init__.py b/universum/__init__.py index 0a8f27f4..82e38a0f 100644 --- a/universum/__init__.py +++ b/universum/__init__.py @@ -1,2 +1,2 @@ __title__ = "Universum" -__version__ = "0.19.18" +__version__ = "0.19.19" diff --git a/universum/modules/code_report_collector.py b/universum/modules/code_report_collector.py index edb2e795..2919b2cc 100644 --- a/universum/modules/code_report_collector.py +++ b/universum/modules/code_report_collector.py @@ -3,6 +3,7 @@ import os import urllib.parse from copy import deepcopy +from pathlib import Path from typing import Dict, List, Optional, TextIO, Tuple from . import artifact_collector, reporter @@ -53,6 +54,35 @@ def _report_as_pylint_json(self, report) -> int: self.reporter.code_report(path, message) return len(report) + def _process_one_sarif_issue(self, issue, root_uri_base_paths, who) -> None: + what: str = issue.get('message') + for location in issue.get('locations', []): + location_data: Dict[str, Dict[str, str]] = location.get('physicalLocation') + if not location_data: + continue + artifact_data = location_data.get('artifactLocation') + if not artifact_data: + if location_data.get('address'): + continue # binary artifact can't be processed + raise ValueError("Unexpected lack of artifactLocation tag") + if artifact_data.get('uriBaseId'): + uri = artifact_data.get('uri') + if not uri: + raise ValueError("Unexpected lack of uri tag") + uri_base_id = artifact_data.get('uriBaseId', '') + root_base_path = root_uri_base_paths.get(uri_base_id, '') + if uri_base_id and not root_base_path: + raise ValueError(f"Unexpected lack of 'originalUriBaseIds' value for {uri_base_id}") + path = str(Path(root_base_path, urllib.parse.unquote(uri))) + else: + path = urllib.parse.unquote(artifact_data.get('uri', '')) + region_data = location_data.get('region') + if not region_data: + continue # TODO: cover this case as comment to the file as a whole + message: reporter.ReportMessage = {"message": f"{who} : {what}", + "line": int(region_data.get('startLine', '0'))} + self.reporter.code_report(path, message) + def _report_as_sarif_json(self, report) -> int: version: str = report.get('version', '') if version != '2.1.0': @@ -61,25 +91,11 @@ def _report_as_sarif_json(self, report) -> int: for run in report.get('runs', []): analyzer_data: Dict[str, str] = run.get('tool').get('driver') # non-optional per definition who: str = f"{analyzer_data.get('name')} [{analyzer_data.get('version', '?')}]" + root_uri_base_paths: Dict[str, str] = {uri_base_id: urllib.parse.urlparse(root_path['uri']).path for + uri_base_id, root_path in run.get('originalUriBaseIds', {}).items()} for issue in run.get('results', []): issue_count += 1 - what: str = issue.get('message') - for location in issue.get('locations', []): - location_data: Dict[str, Dict[str, str]] = location.get('physicalLocation') - if not location_data: - continue - artifact_data = location_data.get('artifactLocation') - if not artifact_data: - if location_data.get('address'): - continue # binary artifact can't be processed - raise ValueError("Unexpected lack of artifactLocation tag") - path: str = urllib.parse.unquote(artifact_data.get('uri', '')) - region_data = location_data.get('region') - if not region_data: - continue # TODO: cover this case as comment to the file as a whole - message: reporter.ReportMessage = {"message": f"{who} : {what}", - "line": int(region_data.get('startLine', '0'))} - self.reporter.code_report(path, message) + self._process_one_sarif_issue(issue, root_uri_base_paths, who) return issue_count @make_block("Processing code report results")