Skip to main content

Quality Gates Project

Build a comprehensive quality gates system that integrates all testing, code quality, and security measures into a unified quality assurance framework.

Project Overview

This hands-on project guides you through building a production-grade quality gates system that enforces quality standards across your entire CI/CD pipeline.

Project Goals

System Architecture

Project Setup

Prerequisites

1. Infrastructure Requirements

# docker-compose.quality-gates.yml
version: '3.8'

services:
# SonarQube for code quality
sonarqube:
image: sonarqube:9.9-community
ports:
- "9000:9000"
environment:
- SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true
- SONAR_JDBC_URL=jdbc:postgresql://postgres:5432/sonar
- SONAR_JDBC_USERNAME=sonar
- SONAR_JDBC_PASSWORD=sonar
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_logs:/opt/sonarqube/logs
- sonarqube_extensions:/opt/sonarqube/extensions

# PostgreSQL for SonarQube
postgres:
image: postgres:13
environment:
- POSTGRES_USER=sonar
- POSTGRES_PASSWORD=sonar
- POSTGRES_DB=sonar
volumes:
- postgres_data:/var/lib/postgresql/data

# Grafana for dashboards
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning

# InfluxDB for metrics
influxdb:
image: influxdb:2.7
ports:
- "8086:8086"
environment:
- INFLUXDB_DB=quality_metrics
- INFLUXDB_ADMIN_USER=admin
- INFLUXDB_ADMIN_PASSWORD=adminpass
volumes:
- influxdb_data:/var/lib/influxdb2

# Quality Gates Service
quality-gates-service:
build: ./quality-gates-service
ports:
- "8080:8080"
environment:
- SONARQUBE_URL=http://sonarqube:9000
- INFLUXDB_URL=http://influxdb:8086
- DATABASE_URL=postgresql://postgres:5432/quality_db
depends_on:
- sonarqube
- postgres
- influxdb

volumes:
sonarqube_data:
sonarqube_logs:
sonarqube_extensions:
postgres_data:
grafana_data:
influxdb_data:

2. Quality Gates Configuration

# quality-gates-config.yml
quality_gates:
name: "Production Quality Gate"
version: "1.0.0"

# Testing requirements
testing:
unit_tests:
required: true
min_coverage: 80
max_duration: 300 # 5 minutes

integration_tests:
required: true
min_coverage: 70
max_duration: 600 # 10 minutes

e2e_tests:
required: true
critical_paths_only: true
max_duration: 900 # 15 minutes

# Code quality requirements
code_quality:
sonarqube:
quality_gate: "Sonar way"
min_rating:
reliability: "A"
security: "A"
maintainability: "A"
max_issues:
blocker: 0
critical: 0
major: 5
max_code_smells: 50
max_duplications: 3.0 # percentage
max_technical_debt: 5 # days

code_coverage:
min_line_coverage: 80
min_branch_coverage: 70
min_function_coverage: 85

# Security requirements
security:
sast:
enabled: true
max_high_issues: 0
max_medium_issues: 5

dependencies:
max_critical: 0
max_high: 0
max_medium: 10

containers:
scan_required: true
max_critical: 0
max_high: 0

secrets:
scan_required: true
block_on_detection: true

# Performance requirements
performance:
load_testing:
required: true
max_response_time_p95: 2000 # ms
max_error_rate: 0.01 # 1%
min_throughput: 1000 # requests/second

build_performance:
max_build_time: 600 # 10 minutes
max_image_size: 500 # MB

# Compliance requirements
compliance:
license_check: true
allowed_licenses:
- "MIT"
- "Apache-2.0"
- "BSD-3-Clause"

code_signing: true
sbom_generation: true

# Notification settings
notifications:
on_failure:
slack:
enabled: true
channel: "#quality-alerts"
email:
enabled: true
recipients:
- "[email protected]"
- "[email protected]"

on_success:
slack:
enabled: true
channel: "#deployments"

Implementation

Quality Gates Service

1. Core Quality Gates Engine

// quality-gates-engine.js
const axios = require('axios');
const { InfluxDB } = require('@influxdata/influxdb-client');

class QualityGatesEngine {
constructor(config) {
this.config = config;
this.results = {
testing: {},
codeQuality: {},
security: {},
performance: {},
compliance: {},
overall: {
passed: false,
score: 0,
failures: []
}
};

this.influx = new InfluxDB({
url: process.env.INFLUXDB_URL,
token: process.env.INFLUXDB_TOKEN
});
}

async evaluateQualityGates(projectKey, buildNumber) {
console.log(`Evaluating quality gates for ${projectKey} #${buildNumber}`);

try {
// Run all checks in parallel
await Promise.all([
this.checkTesting(projectKey, buildNumber),
this.checkCodeQuality(projectKey),
this.checkSecurity(projectKey),
this.checkPerformance(projectKey, buildNumber),
this.checkCompliance(projectKey)
]);

// Calculate overall result
this.calculateOverallResult();

// Store metrics
await this.storeMetrics(projectKey, buildNumber);

// Send notifications
await this.sendNotifications();

return this.results;
} catch (error) {
console.error('Quality gates evaluation failed:', error);
throw error;
}
}

async checkTesting(projectKey, buildNumber) {
console.log('Checking testing requirements...');

const testResults = await this.fetchTestResults(projectKey, buildNumber);

// Check unit tests
if (this.config.quality_gates.testing.unit_tests.required) {
const unitCoverage = testResults.unit.coverage;
const minCoverage = this.config.quality_gates.testing.unit_tests.min_coverage;

if (unitCoverage < minCoverage) {
this.results.testing.unitTests = {
passed: false,
coverage: unitCoverage,
required: minCoverage,
message: `Unit test coverage ${unitCoverage}% is below ${minCoverage}%`
};
this.results.overall.failures.push('Unit test coverage below threshold');
} else {
this.results.testing.unitTests = {
passed: true,
coverage: unitCoverage
};
}
}

// Check integration tests
if (this.config.quality_gates.testing.integration_tests.required) {
const integrationCoverage = testResults.integration.coverage;
const minCoverage = this.config.quality_gates.testing.integration_tests.min_coverage;

if (integrationCoverage < minCoverage) {
this.results.testing.integrationTests = {
passed: false,
coverage: integrationCoverage,
required: minCoverage
};
this.results.overall.failures.push('Integration test coverage below threshold');
} else {
this.results.testing.integrationTests = {
passed: true,
coverage: integrationCoverage
};
}
}

// Check E2E tests
if (this.config.quality_gates.testing.e2e_tests.required) {
const e2eResults = testResults.e2e;

if (e2eResults.failed > 0) {
this.results.testing.e2eTests = {
passed: false,
failedTests: e2eResults.failed,
totalTests: e2eResults.total
};
this.results.overall.failures.push(`${e2eResults.failed} E2E tests failed`);
} else {
this.results.testing.e2eTests = {
passed: true,
totalTests: e2eResults.total
};
}
}
}

async checkCodeQuality(projectKey) {
console.log('Checking code quality requirements...');

const sonarData = await this.fetchSonarQubeMetrics(projectKey);

const requirements = this.config.quality_gates.code_quality.sonarqube;

// Check quality gate status
if (sonarData.qualityGateStatus !== 'OK') {
this.results.codeQuality.qualityGate = {
passed: false,
status: sonarData.qualityGateStatus
};
this.results.overall.failures.push('SonarQube quality gate failed');
}

// Check ratings
const ratings = {
reliability: sonarData.reliabilityRating,
security: sonarData.securityRating,
maintainability: sonarData.maintainabilityRating
};

Object.entries(ratings).forEach(([key, value]) => {
const required = requirements.min_rating[key];
if (this.compareRating(value, required) < 0) {
this.results.codeQuality[key] = {
passed: false,
actual: value,
required: required
};
this.results.overall.failures.push(`${key} rating ${value} is below ${required}`);
} else {
this.results.codeQuality[key] = {
passed: true,
actual: value
};
}
});

// Check issues
if (sonarData.blockerIssues > requirements.max_issues.blocker) {
this.results.codeQuality.blockerIssues = {
passed: false,
count: sonarData.blockerIssues,
max: requirements.max_issues.blocker
};
this.results.overall.failures.push(`${sonarData.blockerIssues} blocker issues found`);
}

if (sonarData.criticalIssues > requirements.max_issues.critical) {
this.results.codeQuality.criticalIssues = {
passed: false,
count: sonarData.criticalIssues,
max: requirements.max_issues.critical
};
this.results.overall.failures.push(`${sonarData.criticalIssues} critical issues found`);
}

// Check code coverage
const coverage = sonarData.coverage;
const minCoverage = this.config.quality_gates.code_quality.code_coverage.min_line_coverage;

if (coverage < minCoverage) {
this.results.codeQuality.coverage = {
passed: false,
actual: coverage,
required: minCoverage
};
this.results.overall.failures.push(`Code coverage ${coverage}% is below ${minCoverage}%`);
} else {
this.results.codeQuality.coverage = {
passed: true,
actual: coverage
};
}
}

async checkSecurity(projectKey) {
console.log('Checking security requirements...');

const securityData = await this.fetchSecurityMetrics(projectKey);

// Check SAST results
if (this.config.quality_gates.security.sast.enabled) {
const highIssues = securityData.sast.high || 0;
const maxHigh = this.config.quality_gates.security.sast.max_high_issues;

if (highIssues > maxHigh) {
this.results.security.sast = {
passed: false,
highIssues: highIssues,
maxAllowed: maxHigh
};
this.results.overall.failures.push(`${highIssues} high severity SAST issues found`);
} else {
this.results.security.sast = {
passed: true,
highIssues: highIssues
};
}
}

// Check dependency vulnerabilities
const depVulns = securityData.dependencies;
const depRequirements = this.config.quality_gates.security.dependencies;

if (depVulns.critical > depRequirements.max_critical) {
this.results.security.dependencies = {
passed: false,
critical: depVulns.critical,
maxAllowed: depRequirements.max_critical
};
this.results.overall.failures.push(`${depVulns.critical} critical dependency vulnerabilities`);
}

// Check container security
if (this.config.quality_gates.security.containers.scan_required) {
const containerVulns = securityData.containers;
const containerRequirements = this.config.quality_gates.security.containers;

if (containerVulns.critical > containerRequirements.max_critical) {
this.results.security.containers = {
passed: false,
critical: containerVulns.critical,
maxAllowed: containerRequirements.max_critical
};
this.results.overall.failures.push(`${containerVulns.critical} critical container vulnerabilities`);
}
}
}

async checkPerformance(projectKey, buildNumber) {
console.log('Checking performance requirements...');

if (this.config.quality_gates.performance.load_testing.required) {
const perfData = await this.fetchPerformanceMetrics(projectKey, buildNumber);

const requirements = this.config.quality_gates.performance.load_testing;

// Check response time
if (perfData.responseTimeP95 > requirements.max_response_time_p95) {
this.results.performance.responseTime = {
passed: false,
p95: perfData.responseTimeP95,
maxAllowed: requirements.max_response_time_p95
};
this.results.overall.failures.push(`P95 response time ${perfData.responseTimeP95}ms exceeds ${requirements.max_response_time_p95}ms`);
}

// Check error rate
if (perfData.errorRate > requirements.max_error_rate) {
this.results.performance.errorRate = {
passed: false,
actual: perfData.errorRate,
maxAllowed: requirements.max_error_rate
};
this.results.overall.failures.push(`Error rate ${perfData.errorRate} exceeds ${requirements.max_error_rate}`);
}

// Check throughput
if (perfData.throughput < requirements.min_throughput) {
this.results.performance.throughput = {
passed: false,
actual: perfData.throughput,
minRequired: requirements.min_throughput
};
this.results.overall.failures.push(`Throughput ${perfData.throughput} below ${requirements.min_throughput} req/s`);
}
}
}

async checkCompliance(projectKey) {
console.log('Checking compliance requirements...');

const complianceData = await this.fetchComplianceData(projectKey);

// Check licenses
if (this.config.quality_gates.compliance.license_check) {
const allowedLicenses = this.config.quality_gates.compliance.allowed_licenses;
const forbiddenLicenses = complianceData.licenses.filter(
license => !allowedLicenses.includes(license)
);

if (forbiddenLicenses.length > 0) {
this.results.compliance.licenses = {
passed: false,
forbidden: forbiddenLicenses
};
this.results.overall.failures.push(`Forbidden licenses found: ${forbiddenLicenses.join(', ')}`);
} else {
this.results.compliance.licenses = {
passed: true
};
}
}
}

calculateOverallResult() {
const allChecks = [
...Object.values(this.results.testing),
...Object.values(this.results.codeQuality),
...Object.values(this.results.security),
...Object.values(this.results.performance),
...Object.values(this.results.compliance)
];

const passedChecks = allChecks.filter(check => check.passed !== false).length;
const totalChecks = allChecks.length;

this.results.overall.score = (passedChecks / totalChecks) * 100;
this.results.overall.passed = this.results.overall.failures.length === 0;

console.log(`Quality Gates Result: ${this.results.overall.passed ? 'PASSED' : 'FAILED'}`);
console.log(`Score: ${this.results.overall.score.toFixed(2)}%`);
console.log(`Failures: ${this.results.overall.failures.length}`);
}

async storeMetrics(projectKey, buildNumber) {
const writeApi = this.influx.getWriteApi('quality-metrics', 'quality-data');

const point = {
measurement: 'quality_gates',
tags: {
project: projectKey,
build: buildNumber
},
fields: {
score: this.results.overall.score,
passed: this.results.overall.passed ? 1 : 0,
failures: this.results.overall.failures.length,
// Testing metrics
unit_coverage: this.results.testing.unitTests?.coverage || 0,
integration_coverage: this.results.testing.integrationTests?.coverage || 0,
// Code quality metrics
code_coverage: this.results.codeQuality.coverage?.actual || 0,
blocker_issues: this.results.codeQuality.blockerIssues?.count || 0,
// Security metrics
critical_vulns: (this.results.security.dependencies?.critical || 0) +
(this.results.security.containers?.critical || 0)
},
timestamp: new Date()
};

writeApi.writePoint(point);
await writeApi.close();
}

async sendNotifications() {
if (!this.results.overall.passed) {
await this.sendFailureNotification();
} else {
await this.sendSuccessNotification();
}
}

async sendFailureNotification() {
const notification = {
title: '❌ Quality Gates Failed',
message: `Quality gates failed with score ${this.results.overall.score.toFixed(2)}%`,
failures: this.results.overall.failures,
timestamp: new Date().toISOString()
};

// Send to Slack
if (this.config.notifications.on_failure.slack.enabled) {
await this.sendSlackNotification(
this.config.notifications.on_failure.slack.channel,
notification
);
}

// Send email
if (this.config.notifications.on_failure.email.enabled) {
await this.sendEmailNotification(
this.config.notifications.on_failure.email.recipients,
notification
);
}
}

async sendSuccessNotification() {
const notification = {
title: '✅ Quality Gates Passed',
message: `Quality gates passed with score ${this.results.overall.score.toFixed(2)}%`,
timestamp: new Date().toISOString()
};

if (this.config.notifications.on_success.slack.enabled) {
await this.sendSlackNotification(
this.config.notifications.on_success.slack.channel,
notification
);
}
}

// Helper methods
compareRating(actual, required) {
const ratings = ['E', 'D', 'C', 'B', 'A'];
return ratings.indexOf(actual) - ratings.indexOf(required);
}

async fetchTestResults(projectKey, buildNumber) {
// Implementation to fetch test results
return {
unit: { coverage: 85, passed: 150, failed: 0 },
integration: { coverage: 75, passed: 50, failed: 0 },
e2e: { total: 20, failed: 0 }
};
}

async fetchSonarQubeMetrics(projectKey) {
const response = await axios.get(
`${process.env.SONARQUBE_URL}/api/measures/component`,
{
params: {
component: projectKey,
metricKeys: 'coverage,bugs,vulnerabilities,code_smells,sqale_rating,reliability_rating,security_rating'
},
headers: {
Authorization: `Bearer ${process.env.SONARQUBE_TOKEN}`
}
}
);

// Parse and return metrics
return this.parseSonarQubeMetrics(response.data);
}

async fetchSecurityMetrics(projectKey) {
// Implementation to fetch security metrics from various tools
return {
sast: { high: 0, medium: 2 },
dependencies: { critical: 0, high: 0, medium: 3 },
containers: { critical: 0, high: 0 }
};
}

async fetchPerformanceMetrics(projectKey, buildNumber) {
// Implementation to fetch performance test results
return {
responseTimeP95: 1500,
errorRate: 0.005,
throughput: 1200
};
}

async fetchComplianceData(projectKey) {
// Implementation to fetch compliance data
return {
licenses: ['MIT', 'Apache-2.0']
};
}
}

module.exports = QualityGatesEngine;

2. Jenkins Pipeline Integration

// Jenkinsfile with quality gates
@Library('quality-gates-library') _

pipeline {
agent any

environment {
PROJECT_KEY = 'myapp'
QUALITY_GATES_SERVICE = 'http://quality-gates-service:8080'
}

stages {
stage('Checkout') {
steps {
checkout scm
}
}

stage('Build') {
steps {
sh 'npm install'
sh 'npm run build'
}
}

stage('Unit Tests') {
steps {
sh 'npm run test:unit -- --coverage'
}
post {
always {
junit 'test-results/unit/*.xml'
publishHTML([
reportDir: 'coverage/unit',
reportFiles: 'index.html',
reportName: 'Unit Test Coverage'
])
}
}
}

stage('Integration Tests') {
steps {
sh 'npm run test:integration -- --coverage'
}
post {
always {
junit 'test-results/integration/*.xml'
publishHTML([
reportDir: 'coverage/integration',
reportFiles: 'index.html',
reportName: 'Integration Test Coverage'
])
}
}
}

stage('E2E Tests') {
steps {
sh 'npm run test:e2e'
}
post {
always {
junit 'test-results/e2e/*.xml'
}
}
}

stage('Code Quality Analysis') {
steps {
script {
def scannerHome = tool 'SonarQubeScanner'
withSonarQubeEnv('SonarQube') {
sh """
${scannerHome}/bin/sonar-scanner \
-Dsonar.projectKey=${PROJECT_KEY} \
-Dsonar.sources=src \
-Dsonar.tests=test \
-Dsonar.javascript.lcov.reportPaths=coverage/lcov.info
"""
}
}
}
}

stage('Security Scans') {
parallel {
stage('Dependency Scan') {
steps {
sh 'npm audit --json > npm-audit.json || true'
sh 'snyk test --json > snyk-report.json || true'
}
}

stage('SAST') {
steps {
sh 'semgrep --config=auto --json > semgrep-report.json src/ || true'
}
}

stage('Secret Scan') {
steps {
sh 'gitleaks detect --report-path gitleaks-report.json || true'
}
}
}
}

stage('Container Build & Scan') {
steps {
script {
def image = docker.build("${PROJECT_KEY}:${BUILD_NUMBER}")

sh """
trivy image \
--format json \
--output trivy-report.json \
${PROJECT_KEY}:${BUILD_NUMBER}
"""
}
}
}

stage('Performance Tests') {
steps {
sh 'k6 run --out json=k6-results.json tests/load-test.js'
}
}

stage('Quality Gates Evaluation') {
steps {
script {
def response = sh(
script: """
curl -X POST ${QUALITY_GATES_SERVICE}/api/evaluate \
-H "Content-Type: application/json" \
-d '{
"projectKey": "${PROJECT_KEY}",
"buildNumber": "${BUILD_NUMBER}"
}'
""",
returnStdout: true
)

def result = readJSON text: response

echo "Quality Gates Score: ${result.overall.score}%"
echo "Status: ${result.overall.passed ? 'PASSED' : 'FAILED'}"

if (!result.overall.passed) {
echo "Failures:"
result.overall.failures.each { failure ->
echo " - ${failure}"
}
error "Quality gates failed!"
}
}
}
}

stage('Deploy') {
when {
expression { currentBuild.result != 'FAILURE' }
}
steps {
sh 'kubectl apply -f k8s/deployment.yaml'
}
}
}

post {
always {
// Archive all reports
archiveArtifacts artifacts: '**/*-report.json', allowEmptyArchive: true

// Clean workspace
cleanWs()
}

failure {
emailext(
subject: "Quality Gates Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: "Quality gates failed. Check console output for details.",
to: "${env.CHANGE_AUTHOR_EMAIL},[email protected]"
)
}

success {
slackSend(
channel: '#deployments',
color: 'good',
message: "✅ Quality Gates Passed: ${env.JOB_NAME} #${env.BUILD_NUMBER} deployed successfully"
)
}
}
}

Quality Dashboard

1. Grafana Dashboard Configuration

{
"dashboard": {
"title": "Quality Gates Dashboard",
"tags": ["quality", "cicd"],
"timezone": "browser",
"panels": [
{
"id": 1,
"title": "Quality Score Trend",
"type": "graph",
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 0},
"targets": [
{
"query": "from(bucket: \"quality-metrics\")\n |> range(start: -30d)\n |> filter(fn: (r) => r._measurement == \"quality_gates\")\n |> filter(fn: (r) => r._field == \"score\")\n |> aggregateWindow(every: 1d, fn: mean)",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "percent",
"min": 0,
"max": 100,
"thresholds": {
"mode": "absolute",
"steps": [
{"color": "red", "value": 0},
{"color": "yellow", "value": 70},
{"color": "green", "value": 90}
]
}
}
}
},
{
"id": 2,
"title": "Pass/Fail Rate",
"type": "stat",
"gridPos": {"h": 8, "w": 6, "x": 12, "y": 0},
"targets": [
{
"query": "from(bucket: \"quality-metrics\")\n |> range(start: -30d)\n |> filter(fn: (r) => r._measurement == \"quality_gates\")\n |> filter(fn: (r) => r._field == \"passed\")\n |> mean()",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "percentunit",
"thresholds": {
"mode": "absolute",
"steps": [
{"color": "red", "value": 0},
{"color": "yellow", "value": 0.8},
{"color": "green", "value": 0.95}
]
}
}
}
},
{
"id": 3,
"title": "Code Coverage Trend",
"type": "graph",
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 8},
"targets": [
{
"query": "from(bucket: \"quality-metrics\")\n |> range(start: -30d)\n |> filter(fn: (r) => r._measurement == \"quality_gates\")\n |> filter(fn: (r) => r._field == \"code_coverage\" or r._field == \"unit_coverage\" or r._field == \"integration_coverage\")",
"refId": "A"
}
]
},
{
"id": 4,
"title": "Security Vulnerabilities",
"type": "graph",
"gridPos": {"h": 8, "w": 12, "x": 12, "y": 8},
"targets": [
{
"query": "from(bucket: \"quality-metrics\")\n |> range(start: -30d)\n |> filter(fn: (r) => r._measurement == \"quality_gates\")\n |> filter(fn: (r) => r._field == \"critical_vulns\")",
"refId": "A"
}
]
}
],
"refresh": "30s"
}
}

Project Summary

Key Achievements

Comprehensive Quality System

  • Multi-dimensional quality checks
  • Automated enforcement
  • Real-time feedback

Integration with Tools

  • SonarQube for code quality
  • Snyk/Trivy for security
  • K6 for performance

Metrics and Dashboards

  • Historical trending
  • Real-time monitoring
  • Actionable insights

Automated Notifications

  • Slack integration
  • Email alerts
  • Custom webhooks

Best Practices Demonstrated

  1. Automated Quality Enforcement
  2. Fail-Fast Approach
  3. Comprehensive Metrics Collection
  4. Clear Feedback Loops
  5. Continuous Improvement

Congratulations! You've built a production-grade quality gates system. Continue to Chapter 5: Deployment Strategies to learn about modern deployment patterns.


This quality gates project demonstrates how to integrate all quality and security measures into a unified system that ensures only high-quality code reaches production.