From 4fbb1fa23de6b7753989d49ae6a25f8f223b4dfd Mon Sep 17 00:00:00 2001 From: codex Date: Wed, 20 May 2026 01:10:02 -0300 Subject: [PATCH] ci: refresh sonarqube after coverage xml --- Jenkinsfile | 87 ++++++++++++++++++++++++++++++++++++++++++++ requirements-dev.txt | 1 + 2 files changed, 88 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 2e0f2d1..7c5360a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -350,6 +350,10 @@ if [ "${install_rc}" -eq 0 ]; then --fail-under "${COVERAGE_MIN}" \ -m pytest -ra -vv --durations=20 --junitxml "${JUNIT_XML}" tests_rc=$? + if [ -f "${COVERAGE_JSON}" ]; then + python -m coverage run --source ariadne -m pytest -q >/tmp/ariadne-coverage-xml.log + python -m coverage xml -o build/coverage.xml + fi python -c "import json; payload=json.load(open('build/coverage.json', encoding='utf-8')); percent=(payload.get('summary') or {}).get('percent_covered'); print(f'Coverage summary: {percent:.2f}%' if percent is not None else 'Coverage summary unavailable')" || true if [ -f "${COVERAGE_JSON}" ] && [ -f scripts/check_coverage_contract.py ]; then python scripts/check_coverage_contract.py "${COVERAGE_JSON}" --source-root ariadne --threshold "${COVERAGE_MIN}" @@ -375,6 +379,89 @@ printf '%s\n' "${gate_rc}" > build/quality-gate.rc } } + stage('Refresh SonarQube evidence') { + when { + expression { fileExists('build/coverage.xml') } + } + 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/**" + "-Dsonar.test.inclusions=**/tests/**,**/testing/**,**/*_test.go,**/*.test.ts,**/*.test.tsx,**/*.spec.ts,**/*.spec.tsx" + "-Dsonar.python.coverage.reportPaths=build/coverage.xml" + ) + 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"} + + +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 = os.path.join('build', '.scannerwork', 'report-task.txt') + 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") in {"SUCCESS", "FAILED", "CANCELED"}: + break + time.sleep(2) + query = urllib.parse.urlencode({"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') { diff --git a/requirements-dev.txt b/requirements-dev.txt index 4dd4f74..f03a4ce 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,5 @@ pytest==8.3.5 pytest-mock==3.14.0 slipcover==1.0.17 +coverage==7.6.10 ruff==0.14.13