diff --git a/scripts/ci/quality_gate.sh b/scripts/ci/quality_gate.sh index 910312e..1dbbdbe 100755 --- a/scripts/ci/quality_gate.sh +++ b/scripts/ci/quality_gate.sh @@ -28,12 +28,17 @@ commit=${GIT_COMMIT:-} if [[ -z "${commit}" ]]; then commit=$(git -C "${ROOT_DIR}" rev-parse --short HEAD 2>/dev/null || echo unknown) fi +build_number=${BUILD_NUMBER:-unknown} +jenkins_job=${JOB_NAME:-lesavka} cat >"${METRICS_FILE}" < list[str]: proc = subprocess.run( @@ -259,6 +266,7 @@ def esc(value: str) -> str: return value.replace('\\', r'\\').replace('\n', r'\\n').replace('"', r'\"') labels = f'suite="lesavka",branch="{esc(branch)}",commit="{esc(commit)}"' +build_labels = f'{labels},build_number="{esc(build_number)}",jenkins_job="{esc(jenkins_job)}"' metrics = [] metrics.append('# HELP platform_quality_gate_runs_total Number of quality gate runs by result.') metrics.append('# TYPE platform_quality_gate_runs_total counter') @@ -266,6 +274,9 @@ status_label = 'ok' if not regressions and not contract_failures and not all_fil ok_value = 1 if status_label == 'ok' else 0 failed_value = 1 if status_label == 'failed' else 0 metrics.append(f'platform_quality_gate_runs_total{{{labels},status="{status_label}"}} 1') +metrics.append('# HELP platform_quality_gate_build_info Build metadata for the latest lesavka gate run.') +metrics.append('# TYPE platform_quality_gate_build_info gauge') +metrics.append(f'platform_quality_gate_build_info{{{build_labels}}} 1') metrics.append('# HELP platform_quality_gate_checks_total Check outcomes from the latest lesavka gate run.') metrics.append('# TYPE platform_quality_gate_checks_total gauge') metrics.append(f'platform_quality_gate_checks_total{{{labels},check="coverage",status="ok"}} {ok_value}') diff --git a/scripts/ci/test_gate.sh b/scripts/ci/test_gate.sh index 0d50213..12e5fbc 100755 --- a/scripts/ci/test_gate.sh +++ b/scripts/ci/test_gate.sh @@ -9,7 +9,7 @@ SUMMARY_JSON="${REPORT_DIR}/summary.json" SUMMARY_TXT="${REPORT_DIR}/summary.txt" METRICS_FILE="${REPORT_DIR}/metrics.prom" PUSHGATEWAY_URL=${QUALITY_GATE_PUSHGATEWAY_URL:-} -PUSHGATEWAY_JOB=${LESAVKA_TEST_GATE_PUSHGATEWAY_JOB:-lesavka-test-gate} +PUSHGATEWAY_JOB=${LESAVKA_TEST_GATE_PUSHGATEWAY_JOB:-platform-quality-ci} mkdir -p "${REPORT_DIR}" cd "${ROOT_DIR}" @@ -48,7 +48,9 @@ python3 - \ "${duration_seconds}" \ "${branch}" \ "${commit}" \ - "${build_url}" <<'PY' + "${build_url}" \ + "${BUILD_NUMBER:-unknown}" \ + "${JOB_NAME:-lesavka}" <<'PY' import json import pathlib import re @@ -64,6 +66,8 @@ duration_seconds = int(sys.argv[6]) branch = sys.argv[7] or 'unknown' commit = sys.argv[8] or 'unknown' build_url = sys.argv[9] +build_number = sys.argv[10] or 'unknown' +jenkins_job = sys.argv[11] or 'lesavka' result_re = re.compile( r'test result: (?:ok|FAILED)\. ' @@ -73,9 +77,46 @@ result_re = re.compile( r'(?P\d+) measured; ' r'(?P\d+) filtered out;' ) +running_re = re.compile(r'^\s*Running (?P.+?)(?: \(|$)') +test_re = re.compile(r'^test (?P.+?) \.\.\. (?Pok|FAILED|ignored)$') counts = {'passed': 0, 'failed': 0, 'ignored': 0, 'measured': 0, 'filtered': 0} +test_cases = [] +current_target = '' + +manifest_path = pathlib.Path('tests/test-taxonomy-manifest.json') +category_by_path = {} +if manifest_path.exists(): + for item in json.loads(manifest_path.read_text(encoding='utf-8')): + path = item.get('new', '') + category = item.get('category', '') + if path and category: + category_by_path[path] = category + +def category_for_target(target: str) -> str: + if target in category_by_path: + return category_by_path[target] + parts = pathlib.PurePosixPath(target).parts + if len(parts) >= 2 and parts[0] == 'tests': + return parts[1] + if target.startswith('src/'): + return 'unit' + return 'uncategorized' + for raw in log_path.read_text(encoding='utf-8', errors='replace').splitlines(): + running = running_re.search(raw) + if running: + current_target = running.group('target') + continue + test_match = test_re.search(raw.strip()) + if test_match: + raw_result = test_match.group('result') + test_cases.append({ + 'test': f'{current_target or "unknown"}::{test_match.group("name")}', + 'category': category_for_target(current_target or 'unknown'), + 'status': {'ok': 'passed', 'FAILED': 'failed', 'ignored': 'skipped'}[raw_result], + }) + continue match = result_re.search(raw) if not match: continue @@ -113,6 +154,10 @@ def label_value(value: str) -> str: return value.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n') labels = f'suite="lesavka",branch="{label_value(branch)}"' +build_labels = ( + f'suite="lesavka",branch="{label_value(branch)}",commit="{label_value(commit)}",' + f'build_number="{label_value(build_number)}",jenkins_job="{label_value(jenkins_job)}"' +) success = 1 if outcome == 'ok' else 0 failure = 1 - success lines = [ @@ -128,6 +173,9 @@ lines = [ for result, value in counts.items(): lines.append(f'lesavka_test_gate_tests{{{labels},result="{result}"}} {value}') lines.extend([ + '# HELP platform_quality_gate_build_info Build metadata for the latest lesavka gate run.', + '# TYPE platform_quality_gate_build_info gauge', + f'platform_quality_gate_build_info{{{build_labels}}} 1', '# HELP platform_quality_gate_tests_total Test result counts from the latest lesavka gate run.', '# TYPE platform_quality_gate_tests_total gauge', f'platform_quality_gate_tests_total{{{labels},result="passed"}} {counts["passed"]}', @@ -137,7 +185,15 @@ lines.extend([ '# TYPE platform_quality_gate_checks_total gauge', f'platform_quality_gate_checks_total{{{labels},check="tests",status="ok"}} {success}', f'platform_quality_gate_checks_total{{{labels},check="tests",status="failed"}} {failure}', + '# HELP platform_quality_gate_test_case_result Per-test result from the latest lesavka gate run.', + '# TYPE platform_quality_gate_test_case_result gauge', ]) +for case in test_cases: + case_labels = ( + f'{build_labels},category="{label_value(case["category"])}",' + f'test="{label_value(case["test"])}",status="{label_value(case["status"])}"' + ) + lines.append(f'platform_quality_gate_test_case_result{{{case_labels}}} 1') metrics_path.write_text('\n'.join(lines) + '\n', encoding='utf-8') PY @@ -149,7 +205,7 @@ publish_metrics() { curl --fail --silent --show-error \ --data-binary @"${METRICS_FILE}" \ - "${PUSHGATEWAY_URL%/}/metrics/job/${PUSHGATEWAY_JOB}/suite/lesavka" + "${PUSHGATEWAY_URL%/}/metrics/job/${PUSHGATEWAY_JOB}/suite/lesavka/gate/tests" } publish_status=0