ci(portal): refresh Sonar after coverage

This commit is contained in:
codex 2026-05-21 16:49:12 -03:00
parent ce7bca1f6f
commit ce7ecfec21
2 changed files with 99 additions and 1 deletions

98
Jenkinsfile vendored
View File

@ -433,6 +433,104 @@ printf '%s\n' "${frontend_rc}" > ../build/frontend-tests.rc
}
}
stage('Refresh SonarQube evidence') {
when {
expression { fileExists('build/backend-coverage.xml') || fileExists('frontend/coverage/lcov.info') }
}
steps {
container('quality-tools') {
sh '''#!/usr/bin/env bash
set -euo pipefail
args=(
"-Dsonar.host.url=${SONARQUBE_HOST_URL}"
"-Dsonar.login=${SONARQUBE_TOKEN}"
"-Dsonar.projectKey=${SONARQUBE_PROJECT_KEY}"
"-Dsonar.projectName=${SONARQUBE_PROJECT_KEY}"
"-Dsonar.sources=."
"-Dsonar.exclusions=**/.git/**,**/build/**,**/dist/**,**/node_modules/**,**/.venv/**,**/__pycache__/**,**/coverage/**,**/test-results/**,**/playwright-report/**,frontend/public/media/**"
"-Dsonar.test.inclusions=**/tests/**,**/testing/**,**/*_test.go,**/*.test.ts,**/*.test.tsx,**/*.spec.ts,**/*.spec.tsx"
)
[ -f build/backend-coverage.xml ] && args+=("-Dsonar.python.coverage.reportPaths=build/backend-coverage.xml")
[ -f frontend/coverage/lcov.info ] && args+=("-Dsonar.javascript.lcov.reportPaths=frontend/coverage/lcov.info")
set +e
sonar-scanner "${args[@]}" | tee build/sonar-scanner.log
rc=${PIPESTATUS[0]}
set -e
printf '%s\n' "${rc}" > build/sonarqube-analysis.rc
'''
}
container('tester') {
sh '''
set -euo pipefail
python3 - <<'PY'
import base64
import json
import os
import time
import urllib.parse
import urllib.request
host = os.getenv('SONARQUBE_HOST_URL', '').strip().rstrip('/')
project_key = os.getenv('SONARQUBE_PROJECT_KEY', '').strip()
token = os.getenv('SONARQUBE_TOKEN', '').strip()
report_path = os.getenv('QUALITY_GATE_SONARQUBE_REPORT', 'build/sonarqube-quality-gate.json')
payload = {"status": "ERROR", "note": "missing SONARQUBE_HOST_URL and/or SONARQUBE_PROJECT_KEY"}
analysis_id = ""
def request_json(url: str) -> dict:
request = urllib.request.Request(url, method="GET")
if token:
encoded = base64.b64encode(f"{token}:".encode("utf-8")).decode("utf-8")
request.add_header("Authorization", f"Basic {encoded}")
with urllib.request.urlopen(request, timeout=12) as response:
return json.loads(response.read().decode("utf-8"))
if host and project_key:
task_path = next(
(
candidate
for candidate in (
os.path.join('.scannerwork', 'report-task.txt'),
os.path.join('build', '.scannerwork', 'report-task.txt'),
)
if os.path.exists(candidate)
),
"",
)
if os.path.exists(task_path):
ce_task_id = ""
with open(task_path, encoding="utf-8") as handle:
for line in handle:
if line.startswith("ceTaskId="):
ce_task_id = line.strip().split("=", 1)[1]
break
for _ in range(30):
if not ce_task_id:
break
query = urllib.parse.urlencode({"id": ce_task_id})
task = request_json(f"{host}/api/ce/task?{query}").get("task", {})
if task.get("status") == "SUCCESS":
analysis_id = str(task.get("analysisId") or "")
break
if task.get("status") in {"FAILED", "CANCELED"}:
break
time.sleep(2)
query = urllib.parse.urlencode({"analysisId": analysis_id} if analysis_id else {"projectKey": project_key})
try:
payload = request_json(f"{host}/api/qualitygates/project_status?{query}")
except Exception as exc: # noqa: BLE001
payload = {"status": "ERROR", "error": str(exc)}
with open(report_path, "w", encoding="utf-8") as handle:
json.dump(payload, handle, indent=2, sort_keys=True)
handle.write("\\n")
PY
'''
}
}
}
stage('Publish test metrics') {
steps {
container('tester') {

View File

@ -57,7 +57,7 @@ test("shows the overview and opens the diagram overlay", async ({ page }) => {
const firstCard = page.locator(".mermaid-card").first();
await expect(firstCard).toBeVisible();
await firstCard.getByRole("button", { name: "Full screen" }).click();
await firstCard.locator(".diagram").click();
await expect(page.locator(".overlay")).toBeVisible();
await page.keyboard.press("Escape");
await expect(page.locator(".overlay")).toHaveCount(0);