Set up custom SonarQube quality gates with automated project analysis for multiple programming languages. Configure SonarScanner integration, webhook notifications, and CI/CD pipeline automation for continuous code quality monitoring.
Prerequisites
- Running SonarQube server
- PostgreSQL database
- Root or sudo access
- Internet connectivity for plugin downloads
What this solves
SonarQube quality gates provide automated code quality control by setting thresholds for metrics like code coverage, duplicated lines, and security vulnerabilities. This tutorial configures custom quality gates with multi-language project scanning, automated analysis workflows, and CI/CD integration to maintain consistent code quality across your development pipeline.
Prerequisites
You need a running SonarQube server and database. If you haven't set this up yet, follow our SonarQube installation guide first.
Verify SonarQube installation
Check that SonarQube is running and accessible before configuring quality gates.
sudo systemctl status sonarqube
curl -I http://localhost:9000
Step-by-step configuration
Install SonarScanner CLI
Download and install the SonarScanner command-line interface for automated project analysis.
cd /opt
sudo wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-5.0.1.3006-linux.zip
sudo unzip sonar-scanner-cli-5.0.1.3006-linux.zip
sudo mv sonar-scanner-5.0.1.3006-linux sonar-scanner
sudo chown -R root:root sonar-scanner
sudo chmod +x sonar-scanner/bin/sonar-scanner
Configure SonarScanner environment
Set up environment variables and PATH for system-wide SonarScanner access.
SONAR_SCANNER_HOME="/opt/sonar-scanner"
PATH="$PATH:/opt/sonar-scanner/bin"
source /etc/environment
sonar-scanner --version
Create authentication token
Generate a user token in SonarQube for secure scanner authentication.
curl -u admin:admin -X POST "http://localhost:9000/api/user_tokens/generate" \
-d "name=scanner-token" \
-d "type=USER_TOKEN"
Configure global SonarScanner properties
Set up default configuration for SonarScanner to connect to your SonarQube server.
# SonarQube server configuration
sonar.host.url=http://localhost:9000
sonar.login=your-generated-token-here
Default project settings
sonar.sourceEncoding=UTF-8
sonar.scm.provider=git
Multi-language analysis settings
sonar.java.binaries=target/classes
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.python.coverage.reportPaths=coverage.xml
sonar.php.coverage.reportPaths=coverage.xml
Create custom quality gate
Define a quality gate with specific thresholds for code quality metrics using the SonarQube API.
# Create new quality gate
curl -u admin:admin -X POST "http://localhost:9000/api/qualitygates/create" \
-d "name=Production Quality Gate"
Get quality gate ID (replace with actual ID from response)
QG_ID="1"
Add coverage condition
curl -u admin:admin -X POST "http://localhost:9000/api/qualitygates/create_condition" \
-d "gateId=${QG_ID}" \
-d "metric=coverage" \
-d "op=LT" \
-d "error=80"
Add duplicated lines condition
curl -u admin:admin -X POST "http://localhost:9000/api/qualitygates/create_condition" \
-d "gateId=${QG_ID}" \
-d "metric=duplicated_lines_density" \
-d "op=GT" \
-d "error=3"
Add security hotspots condition
curl -u admin:admin -X POST "http://localhost:9000/api/qualitygates/create_condition" \
-d "gateId=${QG_ID}" \
-d "metric=security_hotspots_reviewed" \
-d "op=LT" \
-d "error=100"
Configure webhook for CI/CD integration
Set up webhooks to notify external systems when quality gate status changes.
# Create webhook for Jenkins integration
curl -u admin:admin -X POST "http://localhost:9000/api/webhooks/create" \
-d "name=Jenkins Webhook" \
-d "url=http://jenkins.example.com:8080/sonarqube-webhook/" \
-d "secret=webhook-secret-key"
Create webhook for Slack notifications
curl -u admin:admin -X POST "http://localhost:9000/api/webhooks/create" \
-d "name=Slack Webhook" \
-d "url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"
Create project-specific analysis configuration
Set up analysis properties for different programming languages and project types.
# Java project configuration
sonar.projectKey=java-app
sonar.projectName=Java Application
sonar.projectVersion=1.0
Source and test directories
sonar.sources=src/main/java
sonar.tests=src/test/java
sonar.java.binaries=target/classes
sonar.java.test.binaries=target/test-classes
sonar.java.libraries=target/dependency/*.jar
Coverage reports
sonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml
sonar.junit.reportPaths=target/surefire-reports
Configure JavaScript/Node.js project analysis
Create configuration for JavaScript and Node.js projects with ESLint and test coverage.
# Node.js project configuration
sonar.projectKey=nodejs-app
sonar.projectName=Node.js Application
sonar.projectVersion=1.0
Source and test directories
sonar.sources=src
sonar.tests=test
sonar.exclusions=node_modules/,coverage/,dist/**
ESLint reports
sonar.eslint.reportPaths=reports/eslint-report.json
Test coverage
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.testExecutionReportPaths=reports/test-report.xml
Configure Python project analysis
Set up analysis configuration for Python projects with pytest and coverage reports.
# Python project configuration
sonar.projectKey=python-app
sonar.projectName=Python Application
sonar.projectVersion=1.0
Source and test directories
sonar.sources=src
sonar.tests=tests
sonar.exclusions=venv/,__pycache__/,*.pyc
Python coverage
sonar.python.coverage.reportPaths=coverage.xml
sonar.python.xunit.reportPath=test-results.xml
Pylint reports
sonar.python.pylint.reportPaths=pylint-report.txt
Create automated analysis script
Build a shell script to automate project analysis with quality gate validation.
#!/bin/bash
set -e
Configuration
PROJECT_DIR="${1:-.}"
SONAR_HOST="${SONAR_HOST:-http://localhost:9000}"
SONAR_TOKEN="${SONAR_TOKEN}"
if [ -z "$SONAR_TOKEN" ]; then
echo "Error: SONAR_TOKEN environment variable is required"
exit 1
fi
echo "Starting SonarQube analysis for project in: $PROJECT_DIR"
Change to project directory
cd "$PROJECT_DIR"
Run SonarScanner
sonar-scanner \
-Dsonar.host.url="$SONAR_HOST" \
-Dsonar.login="$SONAR_TOKEN"
Extract project key from sonar-project.properties
PROJECT_KEY=$(grep "sonar.projectKey" sonar-project.properties | cut -d'=' -f2)
echo "Analysis complete. Checking quality gate status..."
Wait for analysis to complete and get quality gate status
sleep 10
QG_STATUS=$(curl -s -u "$SONAR_TOKEN:" "$SONAR_HOST/api/qualitygates/project_status?projectKey=$PROJECT_KEY" | jq -r '.projectStatus.status')
echo "Quality Gate Status: $QG_STATUS"
if [ "$QG_STATUS" = "OK" ]; then
echo "✓ Quality gate passed!"
exit 0
else
echo "✗ Quality gate failed!"
exit 1
fi
sudo chmod +x /usr/local/bin/sonar-analyze.sh
Set up CI/CD integration script
Create a comprehensive script for CI/CD pipeline integration with multiple language support.
#!/bin/bash
set -e
Detect project type and configure analysis
detect_project_type() {
if [ -f "pom.xml" ] || [ -f "build.gradle" ]; then
echo "java"
elif [ -f "package.json" ]; then
echo "javascript"
elif [ -f "requirements.txt" ] || [ -f "setup.py" ]; then
echo "python"
elif [ -f "composer.json" ]; then
echo "php"
else
echo "generic"
fi
}
Generate coverage reports based on project type
generate_coverage() {
PROJECT_TYPE=$(detect_project_type)
case $PROJECT_TYPE in
"java")
echo "Running Maven tests with JaCoCo coverage"
mvn clean test jacoco:report
;;
"javascript")
echo "Running Jest tests with coverage"
npm test -- --coverage --coverageReporters=lcov
;;
"python")
echo "Running pytest with coverage"
python -m pytest --cov=. --cov-report=xml
;;
"php")
echo "Running PHPUnit with coverage"
./vendor/bin/phpunit --coverage-clover coverage.xml
;;
esac
}
Main execution
echo "Detected project type: $(detect_project_type)"
Generate coverage if requested
if [ "$1" = "--with-coverage" ]; then
generate_coverage
fi
Run SonarQube analysis
export SONAR_TOKEN="$SONAR_TOKEN"
/usr/local/bin/sonar-analyze.sh
sudo chmod +x /usr/local/bin/ci-sonar-analysis.sh
Install additional language analyzers
Install plugins for comprehensive multi-language support in SonarQube.
# Install Node.js and npm for JavaScript analysis
sudo apt update
sudo apt install -y nodejs npm
npm install -g eslint jshint
Install Python analysis tools
sudo apt install -y python3-pip
pip3 install pylint pytest pytest-cov
Install PHP analysis tools
sudo apt install -y php-cli composer
composer global require squizlabs/php_codesniffer
Configure systemd service for automated scanning
Create a systemd service to run scheduled project analysis automatically.
[Unit]
Description=SonarQube Scanner for %i
After=network.target
[Service]
Type=oneshot
User=sonarqube
Group=sonarqube
WorkingDirectory=/var/lib/sonarqube/projects/%i
Environment=SONAR_TOKEN=your-token-here
ExecStart=/usr/local/bin/ci-sonar-analysis.sh --with-coverage
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
Set up automated timer for periodic analysis
Configure systemd timer to run analysis on a schedule for continuous monitoring.
[Unit]
Description=Daily SonarQube Analysis Timer
Requires=sonar-scanner@.service
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl enable sonar-scanner-timer.timer
sudo systemctl start sonar-scanner-timer.timer
Verify your setup
Test the complete analysis workflow with a sample project.
# Check SonarScanner installation
sonar-scanner --version
Verify SonarQube connectivity
curl -u admin:admin "http://localhost:9000/api/system/status"
List quality gates
curl -u admin:admin "http://localhost:9000/api/qualitygates/list"
Check webhook configuration
curl -u admin:admin "http://localhost:9000/api/webhooks/list"
Test analysis script
export SONAR_TOKEN="your-token-here"
/usr/local/bin/sonar-analyze.sh /path/to/test/project
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Authentication failed | Invalid or expired token | Generate new token: curl -u admin:admin -X POST "http://localhost:9000/api/user_tokens/generate" |
| Quality gate not found | Incorrect quality gate ID | List gates: curl -u admin:admin "http://localhost:9000/api/qualitygates/list" |
| Coverage not reported | Missing coverage report files | Check coverage paths in sonar-project.properties and ensure tests run first |
| Scanner out of memory | Large project analysis | Set SONAR_SCANNER_OPTS="-Xmx2048m" environment variable |
| Webhook delivery failed | Network connectivity issues | Check webhook URL accessibility and firewall rules |
Quality gate best practices
Configure different quality gates for different environments and project types.
Create development quality gate
Set up a more lenient quality gate for development branches with lower thresholds.
# Create development quality gate
curl -u admin:admin -X POST "http://localhost:9000/api/qualitygates/create" \
-d "name=Development Quality Gate"
Add relaxed coverage condition (60% instead of 80%)
DEV_QG_ID="2"
curl -u admin:admin -X POST "http://localhost:9000/api/qualitygates/create_condition" \
-d "gateId=${DEV_QG_ID}" \
-d "metric=coverage" \
-d "op=LT" \
-d "error=60"
Add duplicated lines condition (5% instead of 3%)
curl -u admin:admin -X POST "http://localhost:9000/api/qualitygates/create_condition" \
-d "gateId=${DEV_QG_ID}" \
-d "metric=duplicated_lines_density" \
-d "op=GT" \
-d "error=5"
Next steps
- Configure SonarQube LDAP authentication for enterprise user management
- Set up SonarQube branch analysis and pull request decoration
- Integrate SonarQube with Kubernetes for container-based deployments
- Configure SonarQube security scanning with dependency check
- Set up SonarQube quality profiles for enterprise coding standards
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
SONAR_SCANNER_VERSION="5.0.1.3006"
SONAR_HOST="http://localhost:9000"
SONAR_USER="${1:-admin}"
SONAR_PASS="${2:-admin}"
INSTALL_DIR="/opt/sonar-scanner"
# Usage function
usage() {
echo "Usage: $0 [sonar_username] [sonar_password]"
echo "Default: admin admin"
exit 1
}
# Error handling
cleanup() {
local exit_code=$?
if [ $exit_code -ne 0 ]; then
echo -e "${RED}[ERROR] Installation failed. Cleaning up...${NC}"
sudo rm -rf /tmp/sonar-scanner-cli-*.zip 2>/dev/null || true
sudo rm -rf "$INSTALL_DIR" 2>/dev/null || true
fi
}
trap cleanup ERR
# Progress function
progress() {
echo -e "${BLUE}$1${NC}"
}
success() {
echo -e "${GREEN}$1${NC}"
}
warning() {
echo -e "${YELLOW}$1${NC}"
}
error() {
echo -e "${RED}$1${NC}"
}
# Check if running as root or with sudo
check_privileges() {
if [[ $EUID -ne 0 ]]; then
error "This script must be run as root or with sudo"
exit 1
fi
}
# Detect distribution and set package manager
detect_distro() {
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
;;
*)
error "Unsupported distro: $ID"
exit 1
;;
esac
else
error "Cannot detect distribution"
exit 1
fi
}
# Install required packages
install_dependencies() {
progress "[2/8] Installing required packages..."
$PKG_UPDATE
$PKG_INSTALL curl wget unzip jq
success "Dependencies installed successfully"
}
# Check SonarQube status
check_sonarqube() {
progress "[3/8] Checking SonarQube status..."
if ! systemctl is-active --quiet sonarqube 2>/dev/null; then
warning "SonarQube service not found or not running"
warning "Checking direct connection to $SONAR_HOST..."
fi
if ! curl -f -s -I "$SONAR_HOST" >/dev/null; then
error "SonarQube is not accessible at $SONAR_HOST"
error "Please ensure SonarQube is installed and running"
exit 1
fi
success "SonarQube is accessible"
}
# Download and install SonarScanner
install_sonarscanner() {
progress "[4/8] Installing SonarScanner CLI..."
cd /tmp
DOWNLOAD_URL="https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip"
wget -q "$DOWNLOAD_URL" -O "sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip"
sudo rm -rf "$INSTALL_DIR" 2>/dev/null || true
sudo unzip -q "sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip" -d /opt/
sudo mv "/opt/sonar-scanner-${SONAR_SCANNER_VERSION}-linux" "$INSTALL_DIR"
# Set proper ownership and permissions
sudo chown -R root:root "$INSTALL_DIR"
sudo chmod 755 "$INSTALL_DIR"
sudo chmod 755 "$INSTALL_DIR/bin/sonar-scanner"
sudo chmod 644 "$INSTALL_DIR/conf/"*
# Clean up
rm -f "sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip"
success "SonarScanner CLI installed"
}
# Configure environment
configure_environment() {
progress "[5/8] Configuring SonarScanner environment..."
# Create environment file
cat > /tmp/sonar-env.sh << EOF
export SONAR_SCANNER_HOME="$INSTALL_DIR"
export PATH="\$PATH:$INSTALL_DIR/bin"
EOF
sudo mv /tmp/sonar-env.sh /etc/profile.d/sonar-scanner.sh
sudo chown root:root /etc/profile.d/sonar-scanner.sh
sudo chmod 644 /etc/profile.d/sonar-scanner.sh
# Source environment
export SONAR_SCANNER_HOME="$INSTALL_DIR"
export PATH="$PATH:$INSTALL_DIR/bin"
success "Environment configured"
}
# Generate authentication token
generate_token() {
progress "[6/8] Generating authentication token..."
local token_response
token_response=$(curl -s -u "$SONAR_USER:$SONAR_PASS" -X POST "$SONAR_HOST/api/user_tokens/generate" \
-d "name=scanner-token-$(date +%s)" \
-d "type=USER_TOKEN" || true)
if [[ -z "$token_response" ]] || echo "$token_response" | grep -q "error"; then
warning "Could not generate new token, using credentials for now"
SONAR_TOKEN="$SONAR_USER:$SONAR_PASS"
else
SONAR_TOKEN=$(echo "$token_response" | jq -r '.token' 2>/dev/null || echo "$SONAR_USER:$SONAR_PASS")
fi
success "Authentication configured"
}
# Configure SonarScanner properties
configure_scanner() {
progress "[7/8] Configuring SonarScanner properties..."
cat > /tmp/sonar-scanner.properties << EOF
# SonarQube server configuration
sonar.host.url=$SONAR_HOST
sonar.login=$SONAR_TOKEN
# Default project settings
sonar.sourceEncoding=UTF-8
sonar.scm.provider=git
# Multi-language analysis settings
sonar.java.binaries=target/classes
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.python.coverage.reportPaths=coverage.xml
sonar.php.coverage.reportPaths=coverage.xml
sonar.go.coverage.reportPaths=coverage.out
sonar.cs.vstest.reportsPaths=TestResults/*.trx
sonar.cs.opencover.reportsPaths=TestResults/coverage.opencover.xml
EOF
sudo mv /tmp/sonar-scanner.properties "$INSTALL_DIR/conf/sonar-scanner.properties"
sudo chown root:root "$INSTALL_DIR/conf/sonar-scanner.properties"
sudo chmod 644 "$INSTALL_DIR/conf/sonar-scanner.properties"
success "SonarScanner properties configured"
}
# Create quality gate
create_quality_gate() {
progress "[8/8] Creating custom quality gate..."
# Create quality gate
local qg_response
qg_response=$(curl -s -u "$SONAR_USER:$SONAR_PASS" -X POST "$SONAR_HOST/api/qualitygates/create" \
-d "name=Production-Quality-Gate-$(date +%s)" || echo "")
if [[ -n "$qg_response" ]] && ! echo "$qg_response" | grep -q "error"; then
local qg_id
qg_id=$(echo "$qg_response" | jq -r '.id' 2>/dev/null || echo "")
if [[ -n "$qg_id" && "$qg_id" != "null" ]]; then
# Add coverage condition
curl -s -u "$SONAR_USER:$SONAR_PASS" -X POST "$SONAR_HOST/api/qualitygates/create_condition" \
-d "gateId=$qg_id" \
-d "metric=coverage" \
-d "op=LT" \
-d "error=80" >/dev/null || true
# Add duplicated lines condition
curl -s -u "$SONAR_USER:$SONAR_PASS" -X POST "$SONAR_HOST/api/qualitygates/create_condition" \
-d "gateId=$qg_id" \
-d "metric=duplicated_lines_density" \
-d "op=GT" \
-d "error=3" >/dev/null || true
success "Custom quality gate created (ID: $qg_id)"
else
warning "Could not extract quality gate ID"
fi
else
warning "Could not create custom quality gate - may already exist or insufficient permissions"
fi
}
# Verify installation
verify_installation() {
progress "Verifying installation..."
if [[ -x "$INSTALL_DIR/bin/sonar-scanner" ]]; then
local version_output
version_output=$("$INSTALL_DIR/bin/sonar-scanner" --version 2>&1 | head -1 || echo "")
if [[ -n "$version_output" ]]; then
success "✓ SonarScanner CLI installed: $version_output"
else
warning "SonarScanner installed but version check failed"
fi
else
error "SonarScanner CLI installation failed"
return 1
fi
if curl -f -s "$SONAR_HOST/api/system/status" >/dev/null; then
success "✓ SonarQube server is accessible"
else
warning "SonarQube server accessibility check failed"
fi
success "Installation completed successfully!"
echo ""
echo "Usage examples:"
echo " source /etc/profile.d/sonar-scanner.sh"
echo " sonar-scanner -Dsonar.projectKey=my-project -Dsonar.sources=."
echo ""
echo "Configuration file: $INSTALL_DIR/conf/sonar-scanner.properties"
}
# Main execution
main() {
echo -e "${GREEN}SonarQube Quality Gates and Scanner Installation${NC}"
echo "================================================"
progress "[1/8] Checking prerequisites..."
check_privileges
detect_distro
install_dependencies
check_sonarqube
install_sonarscanner
configure_environment
generate_token
configure_scanner
create_quality_gate
verify_installation
}
# Validate arguments
if [[ $# -gt 2 ]]; then
usage
fi
main "$@"
Review the script before running. Execute with: bash install.sh