#!/bin/bash ## # Care API - Coverage Monitor # # Monitora tendências de cobertura e gera alertas # # @package Care_API # @author Descomplicar® Crescimento Digital # @version 1.0.0 # @since 2025-09-14 ## set -euo pipefail # Configurações readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly PROJECT_DIR="$(dirname "$SCRIPT_DIR")" readonly HISTORY_FILE="$PROJECT_DIR/.coverage-history.json" readonly THRESHOLD_MIN=70 readonly THRESHOLD_WARN=60 readonly THRESHOLD_CRITICAL=50 # Cores readonly RED='\033[0;31m' readonly GREEN='\033[0;32m' readonly YELLOW='\033[1;33m' readonly BLUE='\033[0;34m' readonly NC='\033[0m' log() { local level="$1" shift local message="$*" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') case "$level" in "INFO") echo -e "${GREEN}[INFO]${NC} [$timestamp] $message" ;; "WARN") echo -e "${YELLOW}[WARN]${NC} [$timestamp] $message" ;; "ERROR") echo -e "${RED}[ERROR]${NC} [$timestamp] $message" >&2 ;; "DEBUG") echo -e "${BLUE}[DEBUG]${NC} [$timestamp] $message" ;; esac } # Função para extrair coverage actual get_current_coverage() { local clover_file="$PROJECT_DIR/coverage-reports/clover.xml" if [[ ! -f "$clover_file" ]]; then log "WARN" "Arquivo clover.xml não encontrado - executando coverage" cd "$PROJECT_DIR" ./bin/generate-coverage.sh quick >/dev/null 2>&1 fi if [[ -f "$clover_file" ]]; then php -r " \$xml = simplexml_load_file('$clover_file'); if (\$xml === false) { echo '0'; exit; } \$elements = (int) \$xml->project->metrics['elements']; \$covered = (int) \$xml->project->metrics['coveredelements']; echo \$elements > 0 ? round((\$covered / \$elements) * 100, 2) : 0; " else echo "0" fi } # Função para carregar histórico load_history() { if [[ -f "$HISTORY_FILE" ]]; then cat "$HISTORY_FILE" else echo '[]' fi } # Função para salvar histórico save_history() { local new_entry="$1" local history=$(load_history) # Manter apenas os últimos 30 registos local updated_history=$(echo "$history" | jq --argjson new "$new_entry" ' . + [$new] | sort_by(.timestamp) | reverse | .[0:30] ') echo "$updated_history" > "$HISTORY_FILE" } # Função para calcular tendência calculate_trend() { local history=$(load_history) local count=$(echo "$history" | jq 'length') if [[ $count -lt 2 ]]; then echo "0" return fi # Calcular diferença entre os 2 últimos registos echo "$history" | jq ' if length >= 2 then (.[0].coverage - .[1].coverage) | round * 100 else 0 end ' | awk '{print $1/100}' } # Função para gerar alerta generate_alert() { local coverage="$1" local trend="$2" local alert_level local alert_message if (( $(echo "$coverage < $THRESHOLD_CRITICAL" | bc -l) )); then alert_level="CRITICAL" alert_message="🚨 COVERAGE CRÍTICO: $coverage% (< $THRESHOLD_CRITICAL%)" elif (( $(echo "$coverage < $THRESHOLD_WARN" | bc -l) )); then alert_level="WARNING" alert_message="⚠️ COVERAGE BAIXO: $coverage% (< $THRESHOLD_WARN%)" elif (( $(echo "$coverage < $THRESHOLD_MIN" | bc -l) )); then alert_level="INFO" alert_message="ℹ️ COVERAGE ABAIXO DO ALVO: $coverage% (< $THRESHOLD_MIN%)" else alert_level="SUCCESS" alert_message="✅ COVERAGE OK: $coverage% (≥ $THRESHOLD_MIN%)" fi # Adicionar informação de tendência if (( $(echo "$trend > 0" | bc -l) )); then alert_message="$alert_message 📈 Tendência: +$trend%" elif (( $(echo "$trend < 0" | bc -l) )); then alert_message="$alert_message 📉 Tendência: $trend%" else alert_message="$alert_message ➡️ Tendência: estável" fi log "$alert_level" "$alert_message" # Retornar código apropriado case "$alert_level" in "CRITICAL") return 2 ;; "WARNING") return 1 ;; *) return 0 ;; esac } # Função para gerar relatório detalhado generate_detailed_report() { local coverage="$1" local trend="$2" local history=$(load_history) echo "" log "INFO" "📊 RELATÓRIO DETALHADO DE COVERAGE" echo "=====================================" echo "Current Coverage: $coverage%" echo "Trend: $(printf "%.2f" "$trend")%" echo "Threshold Min: $THRESHOLD_MIN%" echo "Threshold Warn: $THRESHOLD_WARN%" echo "Threshold Critical: $THRESHOLD_CRITICAL%" echo "" log "INFO" "📈 HISTÓRICO RECENTE:" echo "$history" | jq -r ' if length > 0 then .[] | "\(.timestamp) - \(.coverage)% (\(.git_commit[0:7]))" else "Nenhum histórico disponível" end ' | head -10 echo "" # Análise de classes com baixa cobertura local clover_file="$PROJECT_DIR/coverage-reports/clover.xml" if [[ -f "$clover_file" ]]; then log "INFO" "🎯 CLASSES COM BAIXA COBERTURA:" php << 'PHP' xpath('//file') as $file) { $filename = (string) $file['name']; $metrics = $file->metrics; if ($metrics) { $elements = (int) $metrics['elements']; $covered = (int) $metrics['coveredelements']; $coverage = $elements > 0 ? ($covered / $elements) * 100 : 0; if ($coverage < 70 && $elements > 10) { // Focar em arquivos relevantes $lowCoverage[] = [ 'file' => basename($filename), 'coverage' => round($coverage, 1), 'uncovered' => $elements - $covered ]; } } } usort($lowCoverage, function($a, $b) { return $a['coverage'] <=> $b['coverage']; }); foreach (array_slice($lowCoverage, 0, 5) as $item) { echo sprintf(" %-40s %5.1f%% (%d não cobertas)\n", $item['file'], $item['coverage'], $item['uncovered']); } PHP fi echo "" } # Função para integração com webhooks send_webhook_alert() { local coverage="$1" local trend="$2" local webhook_url="${COVERAGE_WEBHOOK_URL:-}" if [[ -z "$webhook_url" ]]; then return 0 fi local payload=$(cat << EOF { "project": "Care API", "coverage": $coverage, "trend": $trend, "threshold_min": $THRESHOLD_MIN, "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", "git_commit": "$(git rev-parse HEAD 2>/dev/null || echo 'unknown')", "git_branch": "$(git branch --show-current 2>/dev/null || echo 'unknown')" } EOF ) if command -v curl >/dev/null 2>&1; then curl -X POST \ -H "Content-Type: application/json" \ -d "$payload" \ "$webhook_url" \ --silent --show-error || { log "WARN" "Falha ao enviar webhook" } fi } # Função principal main() { local mode="${1:-monitor}" log "INFO" "🏥 Care API - Coverage Monitor iniciado" cd "$PROJECT_DIR" case "$mode" in "monitor") # Obter coverage actual local current_coverage=$(get_current_coverage) log "INFO" "Coverage actual: $current_coverage%" # Preparar entrada do histórico local git_commit=$(git rev-parse HEAD 2>/dev/null || echo 'unknown') local git_branch=$(git branch --show-current 2>/dev/null || echo 'unknown') local timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ) local new_entry=$(cat << EOF { "timestamp": "$timestamp", "coverage": $current_coverage, "git_commit": "$git_commit", "git_branch": "$git_branch" } EOF ) # Salvar no histórico save_history "$new_entry" # Calcular tendência local trend=$(calculate_trend) log "INFO" "Tendência: $(printf "%.2f" "$trend")%" # Gerar alertas generate_alert "$current_coverage" "$trend" local alert_code=$? # Enviar webhook se configurado send_webhook_alert "$current_coverage" "$trend" exit $alert_code ;; "report") local current_coverage=$(get_current_coverage) local trend=$(calculate_trend) generate_detailed_report "$current_coverage" "$trend" ;; "history") log "INFO" "📊 Histórico de Coverage:" load_history | jq -r '.[] | "\(.timestamp) - \(.coverage)% - \(.git_commit[0:7]) (\(.git_branch))"' | head -20 ;; "clean") log "INFO" "🧹 Limpando histórico de coverage" rm -f "$HISTORY_FILE" log "INFO" "Histórico limpo" ;; *) echo "Uso: $0 {monitor|report|history|clean}" echo "" echo " monitor - Monitorizar coverage actual e gerar alertas" echo " report - Gerar relatório detalhado" echo " history - Mostrar histórico de coverage" echo " clean - Limpar histórico" echo "" echo "Variáveis de ambiente:" echo " COVERAGE_WEBHOOK_URL - URL para webhooks de alerta" exit 1 ;; esac } # Executar se chamado directamente if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi