#!/bin/bash # 🔍 Branch Analyzer - Análise Completa de Branches e PRs # Autor: Jonas Santos # Versão: 1.0 # Data: Janeiro 2025 set -e # Cores para output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' WHITE='\033[1;37m' NC='\033[0m' # No Color # Emojis ROCKET="🚀" CHECK="✅" WARNING="⚠️" INFO="ℹ️" FIRE="🔥" EYES="👀" CHART="📊" FILES="📁" COMMIT="💻" MERGE="🔀" TEST="🧪" SECURITY="🔒" PERFORMANCE="⚡" DOCS="📚" # Função para exibir cabeçalho print_header() { echo -e "${WHITE}================================================================${NC}" echo -e "${CYAN}${ROCKET} BRANCH ANALYZER - Análise Completa de Branch/PR${NC}" echo -e "${WHITE}================================================================${NC}" } # Função para exibir seção print_section() { echo "" echo -e "${YELLOW}$1${NC}" echo -e "${WHITE}$(printf '%.0s-' {1..50})${NC}" } # Função para exibir erro e sair error_exit() { echo -e "${RED}❌ Erro: $1${NC}" >&2 exit 1 } # Função para verificar se é um repositório git check_git_repo() { if ! git rev-parse --git-dir > /dev/null 2>&1; then error_exit "Este diretório não é um repositório Git." fi } # Função para verificar se a branch existe check_branch_exists() { local branch=$1 if ! git show-ref --verify --quiet refs/remotes/origin/$branch; then if ! git show-ref --verify --quiet refs/heads/$branch; then error_exit "Branch '$branch' não encontrada localmente nem no remoto." fi fi } # Função para obter informações básicas da branch get_branch_info() { local branch=$1 local base_branch=${2:-main} print_section "${INFO} INFORMAÇÕES BÁSICAS" # Último commit local last_commit=$(git log --format="%H" -n 1 origin/$branch 2>/dev/null || git log --format="%H" -n 1 $branch) local last_commit_short=$(git log --format="%h" -n 1 origin/$branch 2>/dev/null || git log --format="%h" -n 1 $branch) local author=$(git log --format="%an" -n 1 $last_commit) local date=$(git log --format="%ad" --date=format:'%d/%m/%Y %H:%M' -n 1 $last_commit) local message=$(git log --format="%s" -n 1 $last_commit) echo -e "${CHECK} ${GREEN}Branch:${NC} $branch" echo -e "${CHECK} ${GREEN}Último commit:${NC} $last_commit_short" echo -e "${CHECK} ${GREEN}Autor:${NC} $author" echo -e "${CHECK} ${GREEN}Data:${NC} $date" echo -e "${CHECK} ${GREEN}Mensagem:${NC} $message" # Status da branch local ahead_behind=$(git rev-list --left-right --count origin/$base_branch...origin/$branch 2>/dev/null || echo "0 0") local behind=$(echo $ahead_behind | cut -f1) local ahead=$(echo $ahead_behind | cut -f2) echo -e "${CHECK} ${GREEN}Commits à frente de $base_branch:${NC} $ahead" echo -e "${CHECK} ${GREEN}Commits atrás de $base_branch:${NC} $behind" if [ "$behind" -gt 0 ]; then echo -e "${WARNING} ${YELLOW}Branch está desatualizada! Considere fazer rebase.${NC}" fi } # Função para análise de commits analyze_commits() { local branch=$1 local base_branch=${2:-main} print_section "${COMMIT} ANÁLISE DE COMMITS" # Últimos commits da branch echo -e "${EYES} ${BLUE}Últimos 10 commits:${NC}" git log --oneline origin/$branch -10 2>/dev/null || git log --oneline $branch -10 # Commits únicos da branch local unique_commits=$(git rev-list --count origin/$base_branch..origin/$branch 2>/dev/null || git rev-list --count $base_branch..$branch) echo -e "\n${CHART} ${GREEN}Commits únicos nesta branch:${NC} $unique_commits" if [ "$unique_commits" -gt 0 ]; then echo -e "\n${EYES} ${BLUE}Commits únicos da branch:${NC}" git log --oneline origin/$base_branch..origin/$branch 2>/dev/null || git log --oneline $base_branch..$branch fi } # Função para análise de arquivos modificados analyze_files() { local branch=$1 local base_branch=${2:-main} print_section "${FILES} ANÁLISE DE ARQUIVOS" # Arquivos modificados local modified_files=$(git diff --name-only origin/$base_branch origin/$branch 2>/dev/null || git diff --name-only $base_branch $branch) local total_files=$(echo "$modified_files" | wc -l) if [ -z "$modified_files" ]; then echo -e "${INFO} ${BLUE}Nenhum arquivo modificado encontrado.${NC}" return fi echo -e "${CHART} ${GREEN}Total de arquivos modificados:${NC} $total_files" echo "" echo -e "${EYES} ${BLUE}Arquivos modificados:${NC}" echo "$modified_files" | while read file; do if [ -n "$file" ]; then echo " 📄 $file" fi done # Estatísticas detalhadas echo "" echo -e "${CHART} ${GREEN}Estatísticas de mudanças:${NC}" git diff --stat origin/$base_branch origin/$branch 2>/dev/null || git diff --stat $base_branch $branch } # Função para análise de tipos de arquivo analyze_file_types() { local branch=$1 local base_branch=${2:-main} print_section "${CHART} ANÁLISE POR TIPO DE ARQUIVO" local modified_files=$(git diff --name-only origin/$base_branch origin/$branch 2>/dev/null || git diff --name-only $base_branch $branch) if [ -z "$modified_files" ]; then return fi # Contar por extensão echo -e "${EYES} ${BLUE}Distribuição por tipo:${NC}" echo "$modified_files" | grep -E '\.' | sed 's/.*\.//' | sort | uniq -c | sort -nr | while read count ext; do echo " 📋 .$ext: $count arquivo(s)" done # Arquivos críticos echo "" echo -e "${SECURITY} ${RED}Arquivos críticos detectados:${NC}" local critical_found=false echo "$modified_files" | while read file; do if [ -n "$file" ]; then case "$file" in package.json|package-lock.json|yarn.lock) echo " 🔴 $file (Dependências)" critical_found=true ;; *.config.js|*.config.ts|angular.json|tsconfig*.json) echo " 🟡 $file (Configuração)" critical_found=true ;; *.env*|*secret*|*key*|*password*) echo " 🔴 $file (Segurança - ATENÇÃO!)" critical_found=true ;; *test*|*spec*|*.test.ts|*.spec.ts) echo " 🟢 $file (Testes)" ;; *.md|docs/*|documentation/*) echo " 📘 $file (Documentação)" ;; esac fi done } # Função para análise de complexidade analyze_complexity() { local branch=$1 local base_branch=${2:-main} print_section "${PERFORMANCE} ANÁLISE DE COMPLEXIDADE" # Linhas adicionadas/removidas local stats=$(git diff --numstat origin/$base_branch origin/$branch 2>/dev/null || git diff --numstat $base_branch $branch) if [ -z "$stats" ]; then echo -e "${INFO} ${BLUE}Nenhuma mudança detectada.${NC}" return fi local total_added=0 local total_removed=0 local files_count=0 while read added removed file; do if [ "$added" != "-" ] && [ "$removed" != "-" ]; then total_added=$((total_added + added)) total_removed=$((total_removed + removed)) files_count=$((files_count + 1)) fi done <<< "$stats" echo -e "${CHART} ${GREEN}Linhas adicionadas:${NC} +$total_added" echo -e "${CHART} ${RED}Linhas removidas:${NC} -$total_removed" echo -e "${CHART} ${BLUE}Total de mudanças:${NC} $((total_added + total_removed))" echo -e "${CHART} ${PURPLE}Arquivos afetados:${NC} $files_count" # Classificação de complexidade local total_changes=$((total_added + total_removed)) if [ $total_changes -lt 50 ]; then echo -e "${CHECK} ${GREEN}Complexidade: BAIXA (< 50 linhas)${NC}" elif [ $total_changes -lt 200 ]; then echo -e "${WARNING} ${YELLOW}Complexidade: MÉDIA (50-200 linhas)${NC}" elif [ $total_changes -lt 500 ]; then echo -e "${WARNING} ${YELLOW}Complexidade: ALTA (200-500 linhas)${NC}" else echo -e "${FIRE} ${RED}Complexidade: MUITO ALTA (> 500 linhas)${NC}" fi # Arquivos com mais mudanças echo "" echo -e "${EYES} ${BLUE}Top 5 arquivos com mais mudanças:${NC}" echo "$stats" | awk '$1 != "-" && $2 != "-" {print ($1+$2)" "$3}' | sort -nr | head -5 | while read changes file; do echo " 📊 $file: $changes mudanças" done } # Função para verificar conflitos potenciais check_potential_conflicts() { local branch=$1 local base_branch=${2:-main} print_section "${MERGE} VERIFICAÇÃO DE CONFLITOS" # Tentar fazer um merge dry-run echo -e "${INFO} ${BLUE}Verificando possíveis conflitos...${NC}" # Backup da branch atual local current_branch=$(git branch --show-current) # Criar uma branch temporária para teste local temp_branch="temp-merge-test-$(date +%s)" if git checkout -b $temp_branch origin/$base_branch >/dev/null 2>&1; then if git merge --no-commit --no-ff origin/$branch >/dev/null 2>&1; then echo -e "${CHECK} ${GREEN}Nenhum conflito detectado! Merge será limpo.${NC}" git merge --abort >/dev/null 2>&1 else echo -e "${WARNING} ${RED}Conflitos potenciais detectados!${NC}" echo -e "${INFO} ${YELLOW}Arquivos com possíveis conflitos:${NC}" git status --porcelain | grep "^UU\|^AA\|^DD" | while read status file; do echo " ⚠️ $file" done git merge --abort >/dev/null 2>&1 fi # Voltar para a branch original e limpar git checkout $current_branch >/dev/null 2>&1 git branch -D $temp_branch >/dev/null 2>&1 else echo -e "${WARNING} ${YELLOW}Não foi possível verificar conflitos automaticamente.${NC}" fi } # Função para análise de testes analyze_tests() { local branch=$1 local base_branch=${2:-main} print_section "${TEST} ANÁLISE DE TESTES" local modified_files=$(git diff --name-only origin/$base_branch origin/$branch 2>/dev/null || git diff --name-only $base_branch $branch) if [ -z "$modified_files" ]; then return fi # Arquivos de teste modificados local test_files=$(echo "$modified_files" | grep -E "\.(test|spec)\.(ts|js)$" || true) local test_count=$(echo "$test_files" | grep -v '^$' | wc -l || echo 0) echo -e "${CHART} ${GREEN}Arquivos de teste modificados:${NC} $test_count" if [ "$test_count" -gt 0 ]; then echo -e "${EYES} ${BLUE}Testes afetados:${NC}" echo "$test_files" | while read file; do if [ -n "$file" ]; then echo " 🧪 $file" fi done fi # Arquivos de código sem testes correspondentes echo "" echo -e "${WARNING} ${YELLOW}Verificando cobertura de testes:${NC}" local code_files=$(echo "$modified_files" | grep -E "\.(ts|js)$" | grep -v -E "\.(test|spec|config|d)\.(ts|js)$" || true) echo "$code_files" | while read file; do if [ -n "$file" ]; then local test_file1="${file%.ts}.spec.ts" local test_file2="${file%.ts}.test.ts" local test_file3="${file%.js}.spec.js" local test_file4="${file%.js}.test.js" if [ ! -f "$test_file1" ] && [ ! -f "$test_file2" ] && [ ! -f "$test_file3" ] && [ ! -f "$test_file4" ]; then echo " ⚠️ $file (sem teste correspondente)" fi fi done } # Função para sugestões de revisão review_suggestions() { local branch=$1 local base_branch=${2:-main} print_section "${DOCS} SUGESTÕES PARA REVISÃO" local modified_files=$(git diff --name-only origin/$base_branch origin/$branch 2>/dev/null || git diff --name-only $base_branch $branch) local total_changes=$(git diff --numstat origin/$base_branch origin/$branch 2>/dev/null | awk '{added+=$1; removed+=$2} END {print added+removed}' || echo 0) echo -e "${EYES} ${BLUE}Pontos importantes para revisar:${NC}" # Checklist baseado na análise echo "" echo -e "${CHECK} ${GREEN}CHECKLIST DE REVISÃO:${NC}" echo " 📋 Verificar se os commits têm mensagens claras" echo " 📋 Validar se as mudanças atendem aos requisitos" echo " 📋 Revisar lógica de negócio implementada" echo " 📋 Verificar padrões de código e nomenclatura" echo " 📋 Validar tratamento de erros" echo " 📋 Checar performance das mudanças" if echo "$modified_files" | grep -q -E "\.(test|spec)\.(ts|js)$"; then echo " 📋 Executar testes automatizados" echo " 📋 Verificar cobertura de testes" else echo -e " ${WARNING} Considerar adicionar testes" fi if echo "$modified_files" | grep -q "package.json"; then echo -e " ${WARNING} Revisar mudanças em dependências" echo " 📋 Verificar compatibilidade de versões" fi if echo "$modified_files" | grep -q -E "\.config\.(ts|js)$|angular\.json"; then echo -e " ${WARNING} Revisar mudanças de configuração" echo " 📋 Testar em diferentes ambientes" fi if [ "$total_changes" -gt 300 ]; then echo -e " ${WARNING} PR grande - considerar quebrar em partes menores" echo " 📋 Revisar com mais cuidado devido ao tamanho" fi echo "" echo -e "${PERFORMANCE} ${PURPLE}COMANDOS ÚTEIS PARA REVISÃO:${NC}" echo " git checkout $branch" echo " git diff origin/$base_branch..origin/$branch" echo " git log origin/$base_branch..origin/$branch --oneline" echo " npm test (para executar testes)" echo " npm run build (para verificar build)" } # Função para gerar relatório resumido generate_summary() { local branch=$1 local base_branch=${2:-main} print_section "${ROCKET} RESUMO EXECUTIVO" local modified_files=$(git diff --name-only origin/$base_branch origin/$branch 2>/dev/null || git diff --name-only $base_branch $branch) local total_files=$(echo "$modified_files" | grep -v '^$' | wc -l || echo 0) local total_commits=$(git rev-list --count origin/$base_branch..origin/$branch 2>/dev/null || git rev-list --count $base_branch..$branch) local stats=$(git diff --numstat origin/$base_branch origin/$branch 2>/dev/null || git diff --numstat $base_branch $branch) local total_added=$(echo "$stats" | awk '$1 != "-" {added+=$1} END {print added+0}') local total_removed=$(echo "$stats" | awk '$2 != "-" {removed+=$2} END {print removed+0}') local author=$(git log --format="%an" -n 1 origin/$branch 2>/dev/null || git log --format="%an" -n 1 $branch) echo "┌─────────────────────────────────────────────────┐" echo "│ RESUMO │" echo "├─────────────────────────────────────────────────┤" echo "│ Branch: $branch" echo "│ Autor: $author" echo "│ Base: $base_branch" echo "│ Commits: $total_commits" echo "│ Arquivos: $total_files" echo "│ Linhas: +$total_added/-$total_removed" echo "└─────────────────────────────────────────────────┘" } # Função principal main() { local branch_name="" local base_branch="main" local show_help=false # Parse dos argumentos while [[ $# -gt 0 ]]; do case $1 in -h|--help) show_help=true shift ;; -b|--base) base_branch="$2" shift 2 ;; *) if [ -z "$branch_name" ]; then branch_name="$1" fi shift ;; esac done # Mostrar ajuda if [ "$show_help" = true ]; then echo "🔍 Branch Analyzer - Análise Completa de Branches e PRs" echo "" echo "Uso: $0 [OPÇÕES] " echo "" echo "Opções:" echo " -h, --help Mostra esta ajuda" echo " -b, --base BRANCH Define a branch base para comparação (padrão: main)" echo "" echo "Exemplos:" echo " $0 feature/checkbox-vehicle" echo " $0 feature/new-feature -b develop" echo " $0 --help" exit 0 fi # Verificar se branch foi fornecida if [ -z "$branch_name" ]; then error_exit "Nome da branch é obrigatório. Use -h para ajuda." fi # Verificações iniciais check_git_repo check_branch_exists "$branch_name" # Executar análises print_header get_branch_info "$branch_name" "$base_branch" analyze_commits "$branch_name" "$base_branch" analyze_files "$branch_name" "$base_branch" analyze_file_types "$branch_name" "$base_branch" analyze_complexity "$branch_name" "$base_branch" check_potential_conflicts "$branch_name" "$base_branch" analyze_tests "$branch_name" "$base_branch" review_suggestions "$branch_name" "$base_branch" generate_summary "$branch_name" "$base_branch" echo "" echo -e "${GREEN}${CHECK} Análise concluída com sucesso!${NC}" echo "" } # Executar função principal com todos os argumentos main "$@"