ci: add backend quality gate and pushgateway status

This commit is contained in:
Brad Stein 2026-04-10 05:18:52 -03:00
parent 2a3ee95e74
commit 4447174108

117
Jenkinsfile vendored
View File

@ -44,6 +44,13 @@ spec:
mountPath: /root/.docker
- name: harbor-config
mountPath: /docker-config
- name: tester
image: python:3.12-slim
command: ["cat"]
tty: true
volumeMounts:
- name: workspace-volume
mountPath: /home/jenkins/agent
volumes:
- name: workspace-volume
emptyDir: {}
@ -67,6 +74,8 @@ spec:
BACK_IMAGE = "${REGISTRY}/bstein-dev-home-backend"
VERSION_TAG = 'dev'
SEMVER = 'dev'
SUITE_NAME = 'bstein-home'
PUSHGATEWAY_URL = 'http://platform-quality-gateway.monitoring.svc.cluster.local:9091'
}
options {
disableConcurrentBuilds()
@ -88,7 +97,7 @@ spec:
sh '''
set -euo pipefail
for attempt in 1 2 3 4 5; do
if apk add --no-cache bash git jq; then
if apk add --no-cache bash git jq curl; then
break
fi
if [ "$attempt" -eq 5 ]; then
@ -142,13 +151,26 @@ spec:
sleep 2
done
docker buildx create --name bstein-builder --driver docker-container \
--driver-opt image=registry.bstein.dev/bstein/buildkit:buildx-stable-1-arm64 \
--driver-opt image=moby/buildkit:buildx-stable-1 \
--bootstrap --use || docker buildx use bstein-builder
'''
}
}
}
stage('Backend unit tests') {
steps {
container('tester') {
sh '''
set -euo pipefail
mkdir -p build
python -m pip install --no-cache-dir -r backend/requirements.txt pytest pytest-mock
python -m pytest backend/tests -q --junitxml=build/junit-backend.xml
'''
}
}
}
stage('Build & push frontend') {
steps {
container('builder') {
@ -187,11 +209,102 @@ spec:
}
post {
success {
container('tester') {
sh '''
set -euo pipefail
export QUALITY_STATUS=ok
python - <<'PY'
import os
import re
import urllib.request
suite = os.environ.get("SUITE_NAME", "bstein-home")
status = os.environ.get("QUALITY_STATUS", "failed")
gateway = os.environ.get("PUSHGATEWAY_URL", "http://platform-quality-gateway.monitoring.svc.cluster.local:9091").rstrip("/")
text = urllib.request.urlopen(f"{gateway}/metrics", timeout=10).read().decode("utf-8", errors="replace")
def counter(name: str) -> float:
pattern = re.compile(
rf'^platform_quality_gate_runs_total\\{{[^}}]*job="platform-quality-ci"[^}}]*suite="{re.escape(suite)}"[^}}]*status="{name}"[^}}]*\\}}\\s+([0-9]+(?:\\.[0-9]+)?)$',
re.M,
)
match = pattern.search(text)
return float(match.group(1)) if match else 0.0
ok = counter("ok")
failed = counter("failed")
if status == "ok":
ok += 1
else:
failed += 1
payload = (
"# TYPE platform_quality_gate_runs_total counter\\n"
f'platform_quality_gate_runs_total{{suite="{suite}",status="ok"}} {int(ok)}\\n'
f'platform_quality_gate_runs_total{{suite="{suite}",status="failed"}} {int(failed)}\\n'
)
req = urllib.request.Request(
f"{gateway}/metrics/job/platform-quality-ci/suite/{suite}",
data=payload.encode("utf-8"),
method="POST",
headers={"Content-Type": "text/plain"},
)
urllib.request.urlopen(req, timeout=10).read()
PY
'''
}
}
failure {
container('tester') {
sh '''
set -euo pipefail
export QUALITY_STATUS=failed
python - <<'PY'
import os
import re
import urllib.request
suite = os.environ.get("SUITE_NAME", "bstein-home")
status = os.environ.get("QUALITY_STATUS", "failed")
gateway = os.environ.get("PUSHGATEWAY_URL", "http://platform-quality-gateway.monitoring.svc.cluster.local:9091").rstrip("/")
text = urllib.request.urlopen(f"{gateway}/metrics", timeout=10).read().decode("utf-8", errors="replace")
def counter(name: str) -> float:
pattern = re.compile(
rf'^platform_quality_gate_runs_total\\{{[^}}]*job="platform-quality-ci"[^}}]*suite="{re.escape(suite)}"[^}}]*status="{name}"[^}}]*\\}}\\s+([0-9]+(?:\\.[0-9]+)?)$',
re.M,
)
match = pattern.search(text)
return float(match.group(1)) if match else 0.0
ok = counter("ok")
failed = counter("failed")
if status == "ok":
ok += 1
else:
failed += 1
payload = (
"# TYPE platform_quality_gate_runs_total counter\\n"
f'platform_quality_gate_runs_total{{suite="{suite}",status="ok"}} {int(ok)}\\n'
f'platform_quality_gate_runs_total{{suite="{suite}",status="failed"}} {int(failed)}\\n'
)
req = urllib.request.Request(
f"{gateway}/metrics/job/platform-quality-ci/suite/{suite}",
data=payload.encode("utf-8"),
method="POST",
headers={"Content-Type": "text/plain"},
)
urllib.request.urlopen(req, timeout=10).read()
PY
'''
}
}
always {
script {
def props = fileExists('build.env') ? readProperties(file: 'build.env') : [:]
echo "Build complete for ${props['SEMVER'] ?: env.VERSION_TAG}"
}
archiveArtifacts artifacts: 'build/junit-backend.xml', allowEmptyArchive: true
}
}
}