From e930aac0395a92d6320b11291d2a6b3ee36548ed Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 19 Apr 2026 21:16:28 -0300 Subject: [PATCH] ci(gate): enforce sonar and supply-chain checks across suites --- Jenkinsfile | 92 ++++++++++++++++++++++- ci/Jenkinsfile.titan-iac | 92 ++++++++++++++++++++++- services/logging/Jenkinsfile.data-prepper | 65 ++++++++++++++++ 3 files changed, 245 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 47acd4f2..12c391ab 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -146,8 +146,96 @@ PY stage('Enforce quality gate') { steps { sh ''' - set -eu - test "$(cat build/quality-gate.rc 2>/dev/null || echo 1)" -eq 0 + set -euo pipefail + gate_rc="$(cat build/quality-gate.rc 2>/dev/null || echo 1)" + fail=0 + if [ "${gate_rc}" -ne 0 ]; then + echo "quality gate failed with rc=${gate_rc}" >&2 + fail=1 + fi + + enabled() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + 1|true|yes|on) return 0 ;; + *) return 1 ;; + esac + } + + if enabled "${QUALITY_GATE_SONARQUBE_ENFORCE:-1}"; then + sonar_status="$(python3 - <<'PY' +import json +from pathlib import Path + +path = Path("build/sonarqube-quality-gate.json") +if not path.exists(): + print("missing") + raise SystemExit(0) +try: + payload = json.loads(path.read_text(encoding="utf-8")) +except Exception: # noqa: BLE001 + print("error") + raise SystemExit(0) +status = (payload.get("status") or payload.get("projectStatus", {}).get("status") or payload.get("qualityGate", {}).get("status") or "").strip().lower() +print(status or "missing") +PY +)" + case "${sonar_status}" in + ok|pass|passed|success) ;; + *) + echo "sonarqube gate failed: ${sonar_status}" >&2 + fail=1 + ;; + esac + fi + + ironbank_required="${QUALITY_GATE_IRONBANK_REQUIRED:-0}" + if [ "${PUBLISH_IMAGES:-false}" = "true" ]; then + ironbank_required=1 + fi + if enabled "${QUALITY_GATE_IRONBANK_ENFORCE:-1}"; then + supply_status="$(python3 - <<'PY' +import json +from pathlib import Path + +path = Path("build/ironbank-compliance.json") +if not path.exists(): + print("missing") + raise SystemExit(0) +try: + payload = json.loads(path.read_text(encoding="utf-8")) +except Exception: # noqa: BLE001 + print("error") + raise SystemExit(0) +compliant = payload.get("compliant") +if compliant is True: + print("ok") +elif compliant is False: + print("failed") +else: + status = str(payload.get("status") or payload.get("result") or payload.get("compliance") or "").strip().lower() + print(status or "missing") +PY +)" + case "${supply_status}" in + ok|pass|passed|success|compliant) ;; + not_applicable|na|n/a) + if enabled "${ironbank_required}"; then + echo "supply chain gate required but status=${supply_status}" >&2 + fail=1 + fi + ;; + *) + if enabled "${ironbank_required}"; then + echo "supply chain gate failed: ${supply_status}" >&2 + fail=1 + else + echo "supply chain gate not passing (${supply_status}) but not required for this run" >&2 + fi + ;; + esac + fi + + exit "${fail}" ''' } } diff --git a/ci/Jenkinsfile.titan-iac b/ci/Jenkinsfile.titan-iac index 88b94106..ade27452 100644 --- a/ci/Jenkinsfile.titan-iac +++ b/ci/Jenkinsfile.titan-iac @@ -145,8 +145,96 @@ PY stage('Enforce quality gate') { steps { sh ''' - set -eu - test "$(cat build/quality-gate.rc 2>/dev/null || echo 1)" -eq 0 + set -euo pipefail + gate_rc="$(cat build/quality-gate.rc 2>/dev/null || echo 1)" + fail=0 + if [ "${gate_rc}" -ne 0 ]; then + echo "quality gate failed with rc=${gate_rc}" >&2 + fail=1 + fi + + enabled() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + 1|true|yes|on) return 0 ;; + *) return 1 ;; + esac + } + + if enabled "${QUALITY_GATE_SONARQUBE_ENFORCE:-1}"; then + sonar_status="$(python3 - <<'PY' +import json +from pathlib import Path + +path = Path("build/sonarqube-quality-gate.json") +if not path.exists(): + print("missing") + raise SystemExit(0) +try: + payload = json.loads(path.read_text(encoding="utf-8")) +except Exception: # noqa: BLE001 + print("error") + raise SystemExit(0) +status = (payload.get("status") or payload.get("projectStatus", {}).get("status") or payload.get("qualityGate", {}).get("status") or "").strip().lower() +print(status or "missing") +PY +)" + case "${sonar_status}" in + ok|pass|passed|success) ;; + *) + echo "sonarqube gate failed: ${sonar_status}" >&2 + fail=1 + ;; + esac + fi + + ironbank_required="${QUALITY_GATE_IRONBANK_REQUIRED:-0}" + if [ "${PUBLISH_IMAGES:-false}" = "true" ]; then + ironbank_required=1 + fi + if enabled "${QUALITY_GATE_IRONBANK_ENFORCE:-1}"; then + supply_status="$(python3 - <<'PY' +import json +from pathlib import Path + +path = Path("build/ironbank-compliance.json") +if not path.exists(): + print("missing") + raise SystemExit(0) +try: + payload = json.loads(path.read_text(encoding="utf-8")) +except Exception: # noqa: BLE001 + print("error") + raise SystemExit(0) +compliant = payload.get("compliant") +if compliant is True: + print("ok") +elif compliant is False: + print("failed") +else: + status = str(payload.get("status") or payload.get("result") or payload.get("compliance") or "").strip().lower() + print(status or "missing") +PY +)" + case "${supply_status}" in + ok|pass|passed|success|compliant) ;; + not_applicable|na|n/a) + if enabled "${ironbank_required}"; then + echo "supply chain gate required but status=${supply_status}" >&2 + fail=1 + fi + ;; + *) + if enabled "${ironbank_required}"; then + echo "supply chain gate failed: ${supply_status}" >&2 + fail=1 + else + echo "supply chain gate not passing (${supply_status}) but not required for this run" >&2 + fi + ;; + esac + fi + + exit "${fail}" ''' } } diff --git a/services/logging/Jenkinsfile.data-prepper b/services/logging/Jenkinsfile.data-prepper index 20ef9814..c39a9c2d 100644 --- a/services/logging/Jenkinsfile.data-prepper +++ b/services/logging/Jenkinsfile.data-prepper @@ -34,7 +34,10 @@ spec: environment { SUITE_NAME = 'data_prepper' PUSHGATEWAY_URL = 'http://platform-quality-gateway.monitoring.svc.cluster.local:9091' + QUALITY_GATE_SONARQUBE_ENFORCE = '1' QUALITY_GATE_SONARQUBE_REPORT = 'build/sonarqube-quality-gate.json' + QUALITY_GATE_IRONBANK_ENFORCE = '1' + QUALITY_GATE_IRONBANK_REQUIRED = '1' QUALITY_GATE_IRONBANK_REPORT = 'build/ironbank-compliance.json' } parameters { @@ -123,6 +126,68 @@ EOF } } } + stage('Enforce quality gate') { + steps { + container('git') { + sh ''' + set -euo pipefail + apk add --no-cache jq >/dev/null 2>&1 || true + fail=0 + enabled() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + 1|true|yes|on) return 0 ;; + *) return 1 ;; + esac + } + + if enabled "${QUALITY_GATE_SONARQUBE_ENFORCE:-1}"; then + sonar_status="$(jq -r '.status // .projectStatus.status // .qualityGate.status // empty' build/sonarqube-quality-gate.json 2>/dev/null | tr '[:upper:]' '[:lower:]')" + [ -n "${sonar_status}" ] || sonar_status="missing" + case "${sonar_status}" in + ok|pass|passed|success) ;; + *) + echo "sonarqube gate failed: ${sonar_status}" >&2 + fail=1 + ;; + esac + fi + + if enabled "${QUALITY_GATE_IRONBANK_ENFORCE:-1}"; then + ironbank_required="${QUALITY_GATE_IRONBANK_REQUIRED:-1}" + compliant="$(jq -r '.compliant // empty' build/ironbank-compliance.json 2>/dev/null || true)" + supply_status="" + if [ "${compliant}" = "true" ]; then + supply_status="ok" + elif [ "${compliant}" = "false" ]; then + supply_status="failed" + else + supply_status="$(jq -r '.status // .result // .compliance // empty' build/ironbank-compliance.json 2>/dev/null | tr '[:upper:]' '[:lower:]')" + fi + [ -n "${supply_status}" ] || supply_status="missing" + case "${supply_status}" in + ok|pass|passed|success|compliant) ;; + not_applicable|na|n/a) + if enabled "${ironbank_required}"; then + echo "supply chain gate required but status=${supply_status}" >&2 + fail=1 + fi + ;; + *) + if enabled "${ironbank_required}"; then + echo "supply chain gate failed: ${supply_status}" >&2 + fail=1 + else + echo "supply chain gate not passing (${supply_status}) but not required for this run" >&2 + fi + ;; + esac + fi + + exit "${fail}" + ''' + } + } + } stage('Build & Push') { steps { container('kaniko') {