ICTU/quality-time

View on GitHub
components/shared_code/src/shared_data_model/sources/sonarqube.py

Summary

Maintainability
B
7 hrs
Test Coverage
"""SonarQube source."""

from pydantic import HttpUrl

from shared_data_model.meta.base import StrEnum
from shared_data_model.meta.entity import Color, Entity, EntityAttribute, EntityAttributeType
from shared_data_model.meta.source import Configuration, Source
from shared_data_model.parameters import (
    URL,
    MultipleChoiceParameter,
    MultipleChoiceWithAdditionParameter,
    PrivateToken,
    Severities,
    SingleChoiceParameter,
    StringParameter,
    TestResult,
)


class Lines(StrEnum):
    """Line types that SonarQube can report on."""

    ALL = "all lines"
    CODE = "lines with code"


def violation_entity_attributes(
    include_security_warning_attributes=False,
    include_suppressed_violation_attributes=False,
) -> list[EntityAttribute]:
    """Return the violation entity attributes."""
    attributes = [
        EntityAttribute(name="Message"),
        EntityAttribute(
            name="Impact",
            key="impacts",
            color={
                "high impact on maintainability": Color.NEGATIVE,
                "high impact on reliability": Color.NEGATIVE,
                "high impact on security": Color.NEGATIVE,
                "medium impact on maintainability": Color.WARNING,
                "medium impact on reliability": Color.WARNING,
                "medium impact on security": Color.WARNING,
            },
        ),
        EntityAttribute(name="Clean code attribute", key="clean_code_attribute_category"),
    ]
    if include_security_warning_attributes:
        attributes.append(EntityAttribute(name="Warning type", key="security_type"))
        attributes.append(EntityAttribute(name="Hotspot status"))
        attributes.append(
            EntityAttribute(name="Review priority", color={"high": Color.NEGATIVE, "medium": Color.WARNING}),
        )
    if include_suppressed_violation_attributes:
        attributes.append(EntityAttribute(name="Resolution"))
        attributes.append(EntityAttribute(name="Rationale"))
    attributes.extend(
        [
            EntityAttribute(name="Component", url="url"),
            EntityAttribute(name="Tags"),
            EntityAttribute(name="Created", key="creation_date", type=EntityAttributeType.DATETIME),
            EntityAttribute(name="Updated", key="update_date", type=EntityAttributeType.DATETIME),
        ],
    )
    return attributes


PROJECT_METRICS = [
    "commented_out_code",
    "complex_units",
    "duplicated_lines",
    "loc",
    "long_units",
    "many_parameters",
    "remediation_effort",
    "security_warnings",
    "software_version",
    "source_up_to_dateness",
    "suppressed_violations",
    "tests",
    "todo_and_fixme_comments",
    "uncovered_branches",
    "uncovered_lines",
    "violations",
]

ALL_SONARQUBE_METRICS = sorted([*PROJECT_METRICS, "source_version"], key=str)

VIOLATION_ENTITY = Entity(name="violation", attributes=violation_entity_attributes())

ISSUE_SECURITY_TYPE = "issue with security impact"

SONARQUBE = Source(
    name="SonarQube",
    description="SonarQube is an open-source platform for continuous inspection of code quality to perform automatic "
    "reviews with static analysis of code to detect bugs, code smells, and security vulnerabilities on 20+ programming "
    "languages.",
    supported_versions_description="≥10.2",
    url=HttpUrl("https://www.sonarqube.org"),
    configuration={
        "commented_out_rules": Configuration(
            metrics=["commented_out_code"],
            name="Rules used to detect commented out code",
            value=[
                "Web:AvoidCommentedOutCodeCheck",
                "abap:S125",
                "c:S125",
                "cpp:S125",
                "csharpsquid:S125",
                "flex:CommentedCode",
                "java:S125",
                "javascript:S125",
                "kotlin:S125",
                "objc:S125",
                "php:S125",
                "plsql:S125",
                "python:S125",
                "scala:S125",
                "swift:S125",
                "tsql:S125",
                "typescript:S125",
                "xml:S125",
            ],
        ),
        "complex_unit_rules": Configuration(
            metrics=["complex_units"],
            name="Rules used to detect complex units",
            value=[
                "abap:S1541",
                "abap:S3776",
                "c:S1541",
                "c:S3776",
                "cpp:S1541",
                "cpp:S3776",
                "csharpsquid:S1541",
                "csharpsquid:S3776",
                "dart:S3776",
                "flex:FunctionComplexity",
                "go:S3776",
                "java:S1541",
                "java:S3776",
                "javascript:S1541",
                "javascript:S3776",
                "kotlin:S3776",
                "objc:S1541",
                "objc:S3776",
                "php:S1541",
                "php:S3776",
                "plsql:PlSql.FunctionAndProcedureComplexity",
                "python:FunctionComplexity",
                "python:S3776",
                "ruby:S3776",
                "scala:S3776",
                "swift:S1541",
                "swift:S3776",
                "typescript:S1541",
                "typescript:S3776",
                "vbnet:S1541",
                "vbnet:S3776",
            ],
        ),
        "many_parameter_rules": Configuration(
            metrics=["many_parameters"],
            name="Rules used to detect units with many parameters",
            value=[
                "c:S107",
                "cpp:S107",
                "csharpsquid:S107",
                "csharpsquid:S2436",
                "dart:S107",
                "flex:S107",
                "go:S107",
                "java:S107",
                "javascript:S107",
                "kotlin:S107",
                "objc:S107",
                "php:S107",
                "python:S107",
                "ruby:S107",
                "scala:S107",
                "swift:S107",
                "tsql:S107",
                "typescript:S107",
                "vbnet:S107",
            ],
        ),
        "long_unit_rules": Configuration(
            metrics=["long_units"],
            name="Rules used to detect long units",
            value=[
                "Web:FileLengthCheck",
                "Web:LongJavaScriptCheck",
                "abap:S104",
                "c:S104",
                "c:S138",
                "c:S1151",
                "cpp:S104",
                "cpp:S138",
                "cpp:S1151",
                "cpp:S1188",
                "cpp:S6184",
                "csharpsquid:S104",
                "csharpsquid:S138",
                "csharpsquid:S1151",
                "flex:S138",
                "flex:S1151",
                "go:S104",
                "go:S138",
                "go:S1151",
                "java:S104",
                "java:S138",
                "java:S1151",
                "java:S1188",
                "java:S2972",
                "java:S5612",
                "javascript:S104",
                "javascript:S138",
                "kotlin:S104",
                "kotlin:S138",
                "kotlin:S1151",
                "kotlin:S5612",
                "objc:S104",
                "objc:S138",
                "objc:S1151",
                "php:S104",
                "php:S138",
                "php:S1151",
                "php:S2042",
                "plsql:S104",
                "plsql:S1151",
                "python:S104",
                "python:S138",
                "ruby:S104",
                "ruby:S138",
                "ruby:S1151",
                "scala:S104",
                "scala:S138",
                "scala:S1151",
                "swift:S104",
                "swift:S138",
                "swift:S1151",
                "swift:S1188",
                "swift:S2042",
                "tsql:S104",
                "tsql:S138",
                "tsql:S1151",
                "typescript:S104",
                "typescript:S138",
                "vbnet:S104",
                "vbnet:S138",
                "vbnet:S1151",
            ],
        ),
        "suppression_rules": Configuration(
            metrics=["suppressed_violations"],
            name="Rules used to detect suppressed violations",
            value=[
                "c:S1291",
                "cpp:S1291",
                "csharpsquid:S1309",
                "java:NoSonar",
                "java:S1309",
                "java:S1310",
                "java:S1315",
                "objc:S1291",
                "php:NoSonar",
                "plsql:NoSonarCheck",
                "python:NoSonar",
                "tsql:NoSonar",
            ],
        ),
        "todo_and_fixme_comment_rules": Configuration(
            metrics=["todo_and_fixme_comments"],
            name="Rules used to detect todo and fixme comments",
            value=[
                "Web:S1134",
                "Web:S1135",
                "c:S1134",
                "c:S1135",
                "cloudformation:S1135",
                "cpp:S1134",
                "cpp:S1135",
                "csharpsquid:S1134",
                "csharpsquid:S1135",
                "dart:S1134",
                "dart:S1135",
                "docker:S1135",
                "go:S1134",
                "go:S1135",
                "java:S1134",
                "java:S1135",
                "javascript:S1134",
                "javascript:S1135",
                "kotlin:S1134",
                "kotlin:S1135",
                "kubernetes:S1135",
                "objc:S1134",
                "objc:S1135",
                "php:S1134",
                "php:S1135",
                "plsql:S1134",
                "plsql:S1135",
                "python:S1134",
                "python:S1135",
                "ruby:S1134",
                "ruby:S1135",
                "scala:S1134",
                "scala:S1135",
                "swift:S1134",
                "swift:S1135",
                "terraform:S1135",
                "tsql:S1134",
                "tsql:S1135",
                "typescript:S1134",
                "typescript:S1135",
                "vbnet:S1134",
                "vbnet:S1135",
                "xml:S1134",
                "xml:S1135",
            ],
        ),
    },
    parameters={
        "url": URL(
            name="URL",
            help="URL of the SonarQube instance, with port if necessary, but without path. For example, "
            "'https://sonarcloud.io'.",
            validate_on=["private_token"],
            metrics=ALL_SONARQUBE_METRICS,
        ),
        "private_token": PrivateToken(
            help_url=HttpUrl("https://docs.sonarqube.org/latest/user-guide/user-token/"),
            metrics=ALL_SONARQUBE_METRICS,
        ),
        "component": StringParameter(
            name="Project key",
            help="The project key can be found by opening the project in SonarQube and clicking 'Project Information'.",
            mandatory=True,
            metrics=PROJECT_METRICS,
        ),
        "branch": StringParameter(
            name="Branch (only supported by commercial SonarQube editions)",
            short_name="branch",
            help_url=HttpUrl("https://docs.sonarqube.org/latest/branches/overview/"),
            default_value="main",
            metrics=PROJECT_METRICS,
        ),
        "languages_to_ignore": MultipleChoiceWithAdditionParameter(
            name="Languages to ignore (regular expressions or language names)",
            short_name="languages to ignore",
            help_url=HttpUrl("https://docs.sonarqube.org/latest/analysis/languages/overview/"),
            metrics=["loc"],
        ),
        "lines_to_count": SingleChoiceParameter(
            name="Lines to count",
            help="Either count all lines including lines with comments or only count lines with code, excluding "
            "comments. Note: it's possible to ignore specific languages only when counting lines with code. "
            "This is a SonarQube limitation.",
            default_value=Lines.CODE,
            values=[Lines.ALL, Lines.CODE],
            api_values={Lines.ALL: "lines", Lines.CODE: "ncloc"},
            metrics=["loc"],
        ),
        "test_result": TestResult(values=["errored", "failed", "passed", "skipped"]),
        "impact_severities": Severities(
            name="Impact severities",
            placeholder="all impact severities",
            help_url=HttpUrl("https://docs.sonarsource.com/sonarqube/latest/user-guide/issues/#issue-severity"),
            values=["low", "medium", "high"],
            metrics=["security_warnings", "suppressed_violations", "violations"],
        ),
        "impacted_software_qualities": MultipleChoiceParameter(
            name="Impacted software qualities",
            placeholder="all impacted software qualities",
            help_url=HttpUrl("https://docs.sonarsource.com/sonarqube/latest/user-guide/clean-code/software-qualities/"),
            values=["maintainability", "reliability", "security"],
            metrics=["suppressed_violations", "violations"],
        ),
        "clean_code_attribute_categories": MultipleChoiceParameter(
            name="Clean code attribute categories",
            placeholder="all clean code attribute categories",
            help_url=HttpUrl("https://docs.sonarsource.com/sonarqube/latest/user-guide/clean-code/definition/"),
            values=["adaptable", "consistent", "intentional", "responsible"],
            metrics=["suppressed_violations", "violations"],
        ),
        "hotspot_statuses": MultipleChoiceParameter(
            name="Security hotspot statuses",
            short_name="hotspot statuses",
            help_url=HttpUrl("https://docs.sonarqube.org/latest/user-guide/security-hotspots/"),
            placeholder="acknowledged, to review",
            values=["to review", "acknowledged", "safe", "fixed"],
            default_value=["to review", "acknowledged"],
            metrics=["security_warnings"],
        ),
        "review_priorities": MultipleChoiceParameter(
            name="Security hotspot review priorities",
            short_name="review priorities",
            help_url=HttpUrl("https://docs.sonarqube.org/latest/user-guide/security-hotspots/"),
            placeholder="all review priorities",
            values=["low", "medium", "high"],
            metrics=["security_warnings"],
        ),
        "effort_types": MultipleChoiceParameter(
            name="Types of effort",
            short_name="effort types",
            placeholder="all effort types",
            help_url=HttpUrl("https://docs.sonarqube.org/latest/user-guide/metric-definitions/"),
            values=[
                "effort to fix all code smells",
                "effort to fix all bug issues",
                "effort to fix all vulnerabilities",
            ],
            api_values={
                "effort to fix all code smells": "sqale_index",
                "effort to fix all bug issues": "reliability_remediation_effort",
                "effort to fix all vulnerabilities": "security_remediation_effort",
            },
            metrics=["remediation_effort"],
        ),
        "security_types": MultipleChoiceParameter(
            name="Security issue types",
            placeholder=ISSUE_SECURITY_TYPE,
            help_url=HttpUrl("https://docs.sonarqube.org/latest/user-guide/rules/"),
            default_value=[ISSUE_SECURITY_TYPE],
            values=["security hotspot", ISSUE_SECURITY_TYPE],
            metrics=["security_warnings"],
        ),
        "tags": MultipleChoiceWithAdditionParameter(
            name="Tags to include",
            short_name="tags",
            placeholder="all tags",
            help_url=HttpUrl("https://docs.sonarsource.com/sonarqube/latest/user-guide/issues/"),
            metrics=["security_warnings", "violations"],
        ),
    },
    entities={
        "commented_out_code": VIOLATION_ENTITY,
        "complex_units": VIOLATION_ENTITY,
        "many_parameters": VIOLATION_ENTITY,
        "loc": Entity(
            name="language",
            measured_attribute="ncloc",
            attributes=[
                EntityAttribute(name="Language"),
                EntityAttribute(name="Number of lines", key="ncloc", type=EntityAttributeType.INTEGER),
                EntityAttribute(
                    name="Percentage of lines", key="ncloc_percentage", type=EntityAttributeType.INTEGER_PERCENTAGE
                ),
            ],
        ),
        "long_units": VIOLATION_ENTITY,
        "remediation_effort": Entity(
            name="effort type",
            measured_attribute="effort",
            attributes=[
                EntityAttribute(name="Effort type", url="url"),
                EntityAttribute(name="Effort (minutes)", key="effort", type=EntityAttributeType.INTEGER),
            ],
        ),
        "security_warnings": Entity(
            name="security warning",
            attributes=violation_entity_attributes(include_security_warning_attributes=True),
        ),
        "suppressed_violations": Entity(
            name="violation",
            attributes=violation_entity_attributes(include_suppressed_violation_attributes=True),
        ),
        "todo_and_fixme_comments": VIOLATION_ENTITY,
        "violations": VIOLATION_ENTITY,
    },
)