Skip to main content

SonarQube

通过我们的 SonarQube 集成(由Ocean 提供) ,您可以根据您的映射和定义,从 SonarQube 账户将 "项目"、"问题 "和 "分析 "导入 Port。

常见被用于情况

  • 映射 SonarQube 组织环境中的 "项目"、"问题 "和 "分析"。
  • 实时观察对象更改(创建/更新/删除),并自动将更改应用到 Port 中的实体。
  • 使用自助操作创建/删除 SonarQube 对象。

先决条件

To install the integration, you need a Kubernetes cluster that the integration's container chart will be deployed to.

Please make sure that you have kubectl and helm installed on your machine, and that your kubectl CLI is connected to the Kubernetes cluster where you plan to install the integration.

安装

从以下安装方法中选择一种:

使用该安装选项意味着集成将能使用 webhook 实时更新 Port。

本表总结了安装时可用的参数,请在下面的脚本中按自己的需要进行设置,然后复制并在终端运行:

ParameterDescriptionExampleRequired
port.clientIdYour port client id (How to get the credentials)
port.clientSecretYour port client secret (How to get the credentials)
integration.secrets.sonarApiTokenThe SonarQube API token
integration.config.sonarOrganizationIdThe SonarQube organization Key (Not required when using on-prem sonarqube instance)myOrganization
integration.config.appHostA URL bounded to the integration container that can be accessed by sonarqube. When used the integration will create webhooks on top of sonarqube to listen to any live changes in the datahttps://my-ocean-integration.com
integration.config.sonarUrlRequired if using On-Prem, Your SonarQube instance URLhttps://my-sonar-cloud-instance.com

Advanced configuration

ParameterDescription
integration.eventListener.typeThe event listener type. Read more about event listeners
integration.typeThe integration to be installed
scheduledResyncIntervalThe number of minutes between each resync. When not set the integration will resync for each event listener resync event. Read more about scheduledResyncInterval
initializePortResourcesDefault true, When set to true the integration will create default blueprints and the port App config Mapping. Read more about initializePortResources

To install the integration using Helm, run the following command:

helm repo add --force-update port-labs https://port-labs.github.io/helm-charts
helm upgrade --install my-sonarqube-integration port-labs/port-ocean \
--set port.clientId="PORT_CLIENT_ID" \
--set port.clientSecret="PORT_CLIENT_SECRET" \
--set initializePortResources=true \
--set scheduledResyncInterval=120 \
--set integration.identifier="my-sonarqube-integration" \
--set integration.type="sonarqube" \
--set integration.eventListener.type="POLLING" \
--set integration.secrets.sonarApiToken="MY_API_TOKEN" \
--set integration.config.sonarOrganizationId="MY_ORG_KEY"
高级集成配置

有关代理或自签名证书等高级配置,click here

接收 SonarQube 对象

SonarQube 集成使用 YAML 配置来描述将数据加载到开发者门户的过程。

下面是配置中的一个示例片段,演示了从 SonarQube 获取 "项目 "数据的过程:

resources:
- kind: projects
selector:
query: "true"
port:
entity:
mappings:
blueprint: '"sonarQubeProject"'
identifier: .key
title: .name
properties:
organization: .organization
link: .__link
lastAnalysisStatus: .__branch.status.qualityGateStatus
lastAnalysisDate: .__branch.analysisDate
numberOfBugs: .__measures[]? | select(.metric == "bugs") | .value
numberOfCodeSmells: .__measures[]? | select(.metric == "code_smells") | .value
numberOfVulnerabilities: .__measures[]? | select(.metric == "vulnerabilities") | .value
numberOfHotSpots: .__measures[]? | select(.metric == "security_hotspots") | .value
numberOfDuplications: .__measures[]? | select(.metric == "duplicated_files") | .value
coverage: .__measures[]? | select(.metric == "coverage") | .value
mainBranch: .__branch.name
tags: .tags

该集成利用JQ JSON processor 对 SonarQube 的 API 事件中的现有字段和 Values 进行选择、修改、连接、转换和其他操作。

Configuration structure

The integration configuration determines which resources will be queried from SonarQube, and which entities and properties will be created in Port.

Supported resources

The following resources can be used to map data from SonarQube, it is possible to reference any field that appears in the API responses linked below for the mapping configuration.

  • Project - represents a SonarQube project. Retrieves data from components, measures, and branches.
  • Issue
  • Analysis - represents a SonarQube analysis and latest activity.
note

The current version of the Sonarqube integration does not support the analysis kind for clients using on-premise Sonarqube installation.

  • The root key of the integration configuration is the resources key:
resources:
- kind: ONE_OF_THE_SUPPORTED_KINDS
selector:
...
  • The kind key is a specifier for a SonarQube object:
  resources:
- kind: ONE_OF_THE_SUPPORTED_KINDS
selector:
...
  • The selector and the query keys allow you to filter which objects of the specified kind will be ingested into your software catalog:
resources:
- kind: { props.customKind }
selector:
query: "true" # JQ boolean expression. If evaluated to false - this object will be skipped.
port:
  • The port, entity and the mappings keys are used to map the SonarQube object fields to Port entities. To create multiple mappings of the same kind, you can add another item in the resources array;
resources:
- kind: projects
selector:
query: "true"
port:
entity:
mappings:
blueprint: '"sonarQubeProject"'
identifier: .key
title: .name
properties:
organization: .organization
link: .__link
lastAnalysisStatus: .__branch.status.qualityGateStatus
lastAnalysisDate: .__branch.analysisDate
numberOfBugs: .__measures[]? | select(.metric == "bugs") | .value
numberOfCodeSmells: .__measures[]? | select(.metric == "code_smells") | .value
numberOfVulnerabilities: .__measures[]? | select(.metric == "vulnerabilities") | .value
numberOfHotSpots: .__measures[]? | select(.metric == "security_hotspots") | .value
numberOfDuplications: .__measures[]? | select(.metric == "duplicated_files") | .value
coverage: .__measures[]? | select(.metric == "coverage") | .value
mainBranch: .__branch.name
tags: .tags
- kind: projects # In this instance project is mapped again with a different filter
selector:
query: '.name == "MyProjectName"'
port:
entity:
mappings: ...
Blueprint key

Note the value of the blueprint key - if you want to use a hardcoded string, you need to encapsulate it in 2 sets of quotes, for example use a pair of single-quotes (') and then another pair of double-quotes (")

Ingest data into Port

To ingest SonarQube objects using the integration configuration, you can follow the steps below:

  1. Go to the DevPortal Builder page.
  2. Select a blueprint you want to ingest using SonarQube.
  3. Choose the Ingest Data option from the menu.
  4. Select SonarQube under the Code quality & security providers category.
  5. Modify the configuration according to your needs.
  6. Click Resync.

示例

蓝图和相关集成配置示例:

项目

Projects blueprint
{
"identifier": "sonarQubeProject",
"title": "SonarQube Project",
"icon": "sonarqube",
"schema": {
"properties": {
"organization": {
"type": "string",
"title": "Organization",
"icon": "TwoUsers"
},
"link": {
"type": "string",
"format": "url",
"title": "Link",
"icon": "Link"
},
"lastAnalysisDate": {
"type": "string",
"format": "date-time",
"icon": "Clock",
"title": "Last Analysis Date"
},
"numberOfBugs": {
"type": "number",
"title": "Number Of Bugs"
},
"numberOfCodeSmells": {
"type": "number",
"title": "Number Of CodeSmells"
},
"numberOfVulnerabilities": {
"type": "number",
"title": "Number Of Vulnerabilities"
},
"numberOfHotSpots": {
"type": "number",
"title": "Number Of HotSpots"
},
"numberOfDuplications": {
"type": "number",
"title": "Number Of Duplications"
},
"coverage": {
"type": "number",
"title": "Coverage"
},
"mainBranch": {
"type": "string",
"icon": "Git",
"title": "Main Branch"
},
"tags": {
"type": "array",
"title": "Tags"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {}
}
Integration configuration
createMissingRelatedEntities: true
deleteDependentEntities: true
resources:
- kind: projects
selector:
query: "true"
port:
entity:
mappings:
blueprint: '"sonarQubeProject"'
identifier: .key
title: .name
properties:
organization: .organization
link: .__link
lastAnalysisStatus: .__branch.status.qualityGateStatus
lastAnalysisDate: .__branch.analysisDate
numberOfBugs: .__measures[]? | select(.metric == "bugs") | .value
numberOfCodeSmells: .__measures[]? | select(.metric == "code_smells") | .value
numberOfVulnerabilities: .__measures[]? | select(.metric == "vulnerabilities") | .value
numberOfHotSpots: .__measures[]? | select(.metric == "security_hotspots") | .value
numberOfDuplications: .__measures[]? | select(.metric == "duplicated_files") | .value
coverage: .__measures[]? | select(.metric == "coverage") | .value
mainBranch: .__branch.name
tags: .tags

问题

Issue blueprint
{
"identifier": "sonarQubeIssue",
"title": "SonarQube Issue",
"icon": "sonarqube",
"schema": {
"properties": {
"type": {
"type": "string",
"title": "Type",
"enum": ["CODE_SMELL", "BUG", "VULNERABILITY"]
},
"severity": {
"type": "string",
"title": "Severity",
"enum": ["MAJOR", "INFO", "MINOR", "CRITICAL", "BLOCKER"],
"enumColors": {
"MAJOR": "orange",
"INFO": "green",
"CRITICAL": "red",
"BLOCKER": "red",
"MINOR": "yellow"
}
},
"link": {
"type": "string",
"format": "url",
"icon": "Link",
"title": "Link"
},
"status": {
"type": "string",
"title": "Status",
"enum": ["OPEN", "CLOSED", "RESOLVED", "REOPENED", "CONFIRMED"]
},
"assignees": {
"title": "Assignees",
"type": "string",
"icon": "TwoUsers"
},
"tags": {
"type": "array",
"title": "Tags"
},
"createdAt": {
"type": "string",
"format": "date-time",
"title": "Created At"
}
}
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {
"sonarQubeProject": {
"target": "sonarQubeProject",
"required": false,
"title": "SonarQube Project",
"many": false
}
}
}
Integration configuration
createMissingRelatedEntities: true
deleteDependentEntities: true
resources:
- kind: issues
selector:
query: "true"
port:
entity:
mappings:
blueprint: '"sonarQubeIssue"'
identifier: .key
title: .message
properties:
type: .type
severity: .severity
link: .__link
status: .status
assignees: .assignee
tags: .tags
createdAt: .creationDate
relations:
sonarQubeProject: .project

分析

Analysis blueprint
{
"identifier": "sonarQubeAnalysis",
"title": "SonarQube Analysis",
"icon": "sonarqube",
"schema": {
"properties": {
"branch": {
"type": "string",
"title": "Branch",
"icon": "GitVersion"
},
"fixedIssues": {
"type": "number",
"title": "Fixed Issues"
},
"newIssues": {
"type": "number",
"title": "New Issues"
},
"coverage": {
"title": "Coverage",
"type": "number"
},
"duplications": {
"type": "number",
"title": "Duplications"
},
"createdAt": {
"type": "string",
"format": "date-time",
"title": "Created At"
}
}
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {
"sonarQubeProject": {
"target": "sonarQubeProject",
"required": false,
"title": "SonarQube Project",
"many": false
}
}
}
Integration configuration
createMissingRelatedEntities: true
deleteDependentEntities: true
resources:
- kind: analysis
selector:
query: "true"
port:
entity:
mappings:
blueprint: '"sonarQubeAnalysis"'
identifier: .analysisId
title: .__commit.message // .analysisId
properties:
branch: .__branchName
fixedIssues: .measures.violations_fixed
newIssues: .measures.violations_added
coverage: .measures.coverage_change
duplications: .measures.duplicated_lines_density_change
createdAt: .__analysisDate
relations:
sonarQubeProject: .__project

让我们来测试一下

本节包括为保证质量而扫描代码库时 SonarQube 的响应数据示例。 此外,还包括根据上一节提供的 Ocean 配置从重新同步事件中创建的实体。

有效载荷

下面是 SonarQube 提供的有效载荷结构示例:

Project response data
{
"organization": "peygis",
"key": "PeyGis_Chatbot_For_Social_Media_Transaction",
"name": "Chatbot_For_Social_Media_Transaction",
"isFavorite": true,
"tags": [],
"visibility": "public",
"eligibilityStatus": "COMPLETED",
"eligible": true,
"isNew": false,
"analysisDateAllBranches": "2023-09-09T03:03:20+0200",
"__measures": [
{
"metric": "bugs",
"value": "6",
"bestValue": false
},
{
"metric": "code_smells",
"value": "216",
"bestValue": false
},
{
"metric": "duplicated_files",
"value": "2",
"bestValue": false
},
{
"metric": "vulnerabilities",
"value": "1",
"bestValue": false
},
{
"metric": "security_hotspots",
"value": "8",
"bestValue": false
}
],
"__branch": {
"name": "master",
"isMain": true,
"type": "LONG",
"status": {
"qualityGateStatus": "ERROR",
"bugs": 6,
"vulnerabilities": 1,
"codeSmells": 216
},
"analysisDate": "2023-09-07T14:38:41+0200",
"commit": {
"sha": "5b01b6dcb200df0bfd1c66df65be30f9ea5423d8",
"author": {
"name": "Username",
"login": "Username@github",
"avatar": "9df2ac1caa70b0a67ff0561f7d0363e5"
},
"date": "2023-09-07T14:38:36+0200",
"message": "Merge pull request #21 from PeyGis/test-sonar"
}
},
"__link": "https://sonarcloud.io/project/overview?id=PeyGis_Chatbot_For_Social_Media_Transaction"
}
Issue response data
{
"key": "AYhnRlhI0rLhE5EBPGHW",
"rule": "xml:S1135",
"severity": "INFO",
"component": "PeyGis_Chatbot_For_Social_Media_Transaction:node_modules/json-schema/draft-zyp-json-schema-04.xml",
"project": "PeyGis_Chatbot_For_Social_Media_Transaction",
"line": 313,
"hash": "8346d5371c3d1b0d1d57937c7b967090",
"textRange": {
"startLine": 313,
"endLine": 313,
"startOffset": 3,
"endOffset": 56
},
"flows": [],
"status": "OPEN",
"message": "Complete the task associated to this \"TODO\" comment.",
"effort": "0min",
"debt": "0min",
"assignee": "Username@github",
"author": "[email protected]",
"tags": [],
"creationDate": "2018-04-06T02:44:46+0200",
"updateDate": "2023-05-29T13:30:14+0200",
"type": "CODE_SMELL",
"organization": "peygis",
"cleanCodeAttribute": "COMPLETE",
"cleanCodeAttributeCategory": "INTENTIONAL",
"impacts": [
{
"softwareQuality": "MAINTAINABILITY",
"severity": "LOW"
}
],
"__link": "https://sonarcloud.io/project/issues?open=AYhnRlhI0rLhE5EBPGHW&id=PeyGis_Chatbot_For_Social_Media_Transaction"
}
Analysis response data
{
"analysisId": "AYpvptJNv89mE9ClYP-q",
"firstAnalysis": false,
"measures": {
"violations_added": "0",
"violations_fixed": "0",
"coverage_change": "0.0",
"duplicated_lines_density_change": "0.0",
"ncloc_change": "0"
},
"branch": {
"analysisDate": "2023-09-07T12:38:41.279Z",
"isMain": true,
"name": "master",
"commit": {
"sha": "5b01b6dcb200df0bfd1c66df65be30f9ea5423d8",
"author": {
"avatar": "9df2ac1caa70b0a67ff0561f7d0363e5",
"login": "Username@github",
"name": "Username"
},
"date": "2023-09-07T12:38:36Z",
"message": "Merge pull request #21 from PeyGis/test-sonar"
},
"type": "LONG",
"status": {
"qualityGateStatus": "ERROR"
}
},
"__branchName": "master",
"__analysisDate": "2023-09-07T12:38:41.279Z",
"__commit": {
"sha": "5b01b6dcb200df0bfd1c66df65be30f9ea5423d8",
"author": {
"avatar": "9df2ac1caa70b0a67ff0561f7d0363e5",
"login": "Username@github",
"name": "Username"
},
"date": "2023-09-07T12:38:36Z",
"message": "Merge pull request #21 from PeyGis/test-sonar"
},
"__project": "PeyGis_Chatbot_For_Social_Media_Transaction"
}

映射结果

结合样本有效载荷和 Ocean 配置,可生成以下 Port 实体:

Project entity in Port
{
"identifier": "PeyGis_Chatbot_For_Social_Media_Transaction",
"title": "Chatbot_For_Social_Media_Transaction",
"blueprint": "sonarQubeProject",
"team": [],
"properties": {
"organization": "peygis",
"link": "https://sonarcloud.io/project/overview?id=PeyGis_Chatbot_For_Social_Media_Transaction",
"lastAnalysisDate": "2023-09-07T12:38:41.000Z",
"numberOfBugs": 6,
"numberOfCodeSmells": 216,
"numberOfVulnerabilities": 1,
"numberOfHotSpots": 8,
"numberOfDuplications": 2,
"mainBranch": "master",
"tags": []
},
"relations": {},
"icon": "sonarqube"
}
Issue entity in Port
{
"identifier": "AYhnRlhI0rLhE5EBPGHW",
"title": "Complete the task associated to this \"TODO\" comment.",
"blueprint": "sonarQubeIssue",
"team": [],
"properties": {
"type": "CODE_SMELL",
"severity": "INFO",
"link": "https://sonarcloud.io/project/issues?open=AYhnRlhI0rLhE5EBPGHW&id=PeyGis_Chatbot_For_Social_Media_Transaction",
"status": "OPEN",
"assignees": "Username@github",
"tags": [],
"createdAt": "2018-04-06T00:44:46.000Z"
},
"relations": {
"sonarQubeProject": "PeyGis_Chatbot_For_Social_Media_Transaction"
},
"icon": "sonarqube"
}
Analysis entity in Port
{
"identifier": "AYpvptJNv89mE9ClYP-q",
"title": "Merge pull request #21 from PeyGis/test-sonar",
"blueprint": "sonarQubeAnalysis",
"team": [],
"properties": {
"branch": "master",
"fixedIssues": 0,
"newIssues": 0,
"coverage": 0,
"duplications": 0,
"createdAt": "2023-09-07T12:38:41.279Z"
},
"relations": {
"sonarQubeProject": "PeyGis_Chatbot_For_Social_Media_Transaction"
},
"icon": "sonarqube"
}

通过 webhook 进行替代安装

虽然上述海洋集成是推荐的安装方法,但您可能更喜欢使用 webhook 从 SonarQube 引用数据。 如果是这样,请使用以下说明:

Webhook installation (click to expand)

在本示例中,您将在SonarQube's SonarCloud 和 Port 之间创建一个 webhook 集成,用于接收 SonarQube 代码质量 "分析 "实体。

Port configuration

创建以下蓝图定义:

SonarQube analysis blueprint
{
"identifier": "sonarCloudAnalysis",
"description": "This blueprint represents a SonarCloud Analysis in our software catalog",
"title": "SonarCloud Analysis",
"icon": "sonarqube",
"schema": {
"properties": {
"serverUrl": {
"type": "string",
"format": "url",
"title": "Server URL"
},
"projectName": {
"type": "string",
"title": "Project name"
},
"projectUrl": {
"type": "string",
"format": "url",
"title": "Project URL"
},
"branchName": {
"type": "string",
"title": "Branch Name"
},
"branchType": {
"type": "string",
"title": "Branch Type"
},
"branchUrl": {
"type": "string",
"format": "url",
"title": "Branch URL"
},
"qualityGateName": {
"type": "string",
"title": "Quality Gate Name"
},
"qualityGateStatus": {
"type": "string",
"title": "Quality Gate Status",
"description": "General status of quality checks"
},
"qualityGateConditions": {
"type": "array",
"items": {
"type": "object"
},
"title": "Quality Gate Conditions",
"description": "Conditions of the qaulity gate"
},
"status": {
"type": "string",
"title": "General Status"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {}
}

创建以下 webhook 配置using Port's UI :

SonarQube analysis webhook configuration
  1. 基本信息 选项卡 - 填写以下详细信息: 1.title: SonarQube mapper; 2.标识符 : sonarqube_mapper; 3.Description : 将 SonarQube 警报映射到 Port 的 webhook 配置; 4.图标 : sonarqube
  2. 集成配置选项卡 - 填写以下JQ映射:
    [
    {
    "blueprint": "sonarCloudAnalysis",
    "entity": {
    "identifier": ".body.taskId",
    "title": ".body.project.name + \"-\" + .body.taskId",
    "properties": {
    "serverUrl": ".body.serverUrl",
    "status": ".body.status",
    "projectName": ".body.project.name",
    "projectUrl": ".body.project.url",
    "branchName": ".body.branch.name",
    "branchType": ".body.branch.type",
    "branchUrl": ".body.branch.url",
    "qualityGateName": ".body.qualityGate.name",
    "qualityGateStatus": ".body.qualityGate.status",
    "qualityGateConditions": ".body.qualityGate.conditions"
    }
    }
    }
    ]

3.向下滚动到 高级设置,输入以下详细信息:

  1. secret: WEBHOOK_SECRET; 2.签名头名称: x-sonar-webhook-hmac-sha256; 3.签名算法: 从下拉选项中选择 sha256; 4.点击页面底部的保存。 记住将 WEBHOOK_SECRET 替换为您在 SonarCloud 中创建 webhook 时指定的真实secret。

Create a webhook in SonarCloud

  1. 进入SonarCloud ,选择要配置 webhook 的项目;
  2. 点击页面左下角的管理,然后选择Webhooks
  3. 点击创建
  4. 输入以下详细信息:
    1. Name - 被用于一个有意义的名称,如 Port Webhook;
    2. URL - 输入您在创建 webhook 配置后收到的 url 密钥的值;
    3. Secret - 输入您在创建 webhook 时指定的 secret 值; 5.单击页面底部的创建
为了查看 SonarQube webhook 中可用的不同有效载荷和事件、look here

完成!您运行的任何新分析(例如,对新 PR 或对 PR 的更改)都将触发 webhook 事件,SonarCloud 将把该事件发送到 Port 提供的 webhook URL。 Port 将根据映射解析事件,并相应地更新目录实体。

Let's Test It

本节包括当扫描代码库以保证质量时从 SonarQube 发送的 webhook 事件示例。 此外,还包括根据上一节提供的 webhook 配置从事件中创建的实体。

Payload

下面是扫描 SonarQube 资源库时发送到 webhook URL 的有效载荷结构示例:

Webhook event payload
{
"serverUrl": "https://sonarcloud.io",
"taskId": "AYi_1w1fcGD_RU1S5-r_",
"status": "SUCCESS",
"analysedAt": "2023-06-15T16:15:05+0000",
"revision": "575718d8287cd09630ff0ff9aa4bb8570ea4ef29",
"changedAt": "2023-06-15T16:15:05+0000",
"project": {
"key": "Username_Test_Python_App",
"name": "Test_Python_App",
"url": "https://sonarcloud.io/dashboard?id=Username_Test_Python_App"
},
"branch": {
"name": "master",
"type": "LONG",
"isMain": true,
"url": "https://sonarcloud.io/dashboard?id=Username_Test_Python_App"
},
"qualityGate": {
"name": "My Quality Gate",
"status": "ERROR",
"conditions": [
{
"metric": "code_smells",
"operator": "GREATER_THAN",
"value": "217",
"status": "ERROR",
"errorThreshold": "5"
},
{
"metric": "ncloc",
"operator": "GREATER_THAN",
"value": "8435",
"status": "ERROR",
"errorThreshold": "20"
},
{
"metric": "new_branch_coverage",
"operator": "LESS_THAN",
"status": "NO_VALUE",
"errorThreshold": "1"
},
{
"metric": "new_sqale_debt_ratio",
"operator": "GREATER_THAN",
"value": "1.0303030303030303",
"status": "OK",
"errorThreshold": "5"
},
{
"metric": "new_violations",
"operator": "GREATER_THAN",
"value": "3",
"status": "ERROR",
"errorThreshold": "1"
}
]
},
"properties": {}
}

Mapping Result

结合示例有效载荷和 webhook 配置可生成以下 Port 实体:

{
"identifier": "AYi_1w1fcGD_RU1S5-r_",
"title": "Test_Python_App-AYi_1w1fcGD_RU1S5-r_",
"blueprint": "sonarCloudAnalysis",
"properties": {
"serverUrl": "https://sonarcloud.io",
"status": "SUCCESS",
"projectName": "Test_Python_App",
"projectUrl": "https://sonarcloud.io/dashboard?id=Username_Test_Python_App",
"branchName": "master",
"branchType": "LONG",
"branchUrl": "https://sonarcloud.io/dashboard?id=Username_Test_Python_App",
"qualityGateName": "My Quality Gate",
"qualityGateStatus": "ERROR",
"qualityGateConditions": [
{
"metric": "code_smells",
"operator": "GREATER_THAN",
"value": "217",
"status": "ERROR",
"errorThreshold": "5"
},
{
"metric": "ncloc",
"operator": "GREATER_THAN",
"value": "8435",
"status": "ERROR",
"errorThreshold": "20"
},
{
"metric": "new_branch_coverage",
"operator": "LESS_THAN",
"status": "NO_VALUE",
"errorThreshold": "1"
},
{
"metric": "new_sqale_debt_ratio",
"operator": "GREATER_THAN",
"value": "1.0303030303030303",
"status": "OK",
"errorThreshold": "5"
},
{
"metric": "new_violations",
"operator": "GREATER_THAN",
"value": "3",
"status": "ERROR",
"errorThreshold": "1"
}
]
},
"relations": {}
}