diff --git a/Dockerfile b/Dockerfile index 4514f41..5cfb166 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,16 @@ FROM codenarc/codenarc:3.6.0-groovy3.0.23 RUN DEBIAN_FRONTEND=noninteractive \ -apt-get update && \ -apt-get install --no-install-recommends -y wget git && \ -apt-get clean && rm -rf /var/lib/apt/lists/* + apt-get update && \ + apt-get install --no-install-recommends -y wget git jq && \ + apt-get clean && rm -rf /var/lib/apt/lists/* ENV REVIEWDOG_VERSION=v0.13.0 SHELL ["/bin/bash", "-o", "pipefail", "-c"] -RUN wget -O - -q https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh| sh -s -- -b /usr/local/bin/ ${REVIEWDOG_VERSION} +RUN wget -O - -q https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b /usr/local/bin/ ${REVIEWDOG_VERSION} COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh index 5d353fd..d4b74db 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,241 +1,229 @@ #!/bin/sh set -e -CODENARC_RESULT="result.txt" -LINE_VIOLATIONS="line_violations.txt" -FILE_VIOLATIONS="file_violations.txt" -VIOLATIONS_FLAG="/tmp/found_violations.txt" +CODENARC_JSON="result.json" +CODENARC_COMPACT="result.txt" ALL_DIFF="/tmp/all_diff.txt" CHANGED_LINES_CACHE="/tmp/changed_lines.txt" CHANGED_FILES_CACHE="/tmp/changed_files.txt" cleanup_temp_files() { - rm -f "$CODENARC_RESULT" "$LINE_VIOLATIONS" "$FILE_VIOLATIONS" "$VIOLATIONS_FLAG" \ - "$ALL_DIFF" "$CHANGED_LINES_CACHE" "$CHANGED_FILES_CACHE" \ - "${FILE_VIOLATIONS}.formatted" >/dev/null 2>&1 + rm -f "$CODENARC_JSON" "$CODENARC_COMPACT" "$ALL_DIFF" \ + "$CHANGED_LINES_CACHE" "$CHANGED_FILES_CACHE" >/dev/null 2>&1 } trap 'cleanup_temp_files' EXIT run_codenarc() { - report="${INPUT_REPORT:-compact:stdout}" includes_arg="" - [ -n "$INPUT_SOURCE_FILES" ] && includes_arg="-includes=${INPUT_SOURCE_FILES}" - - echo "🔍 Executando CodeNarc..." + + echo "" + echo "🔍 Executando CodeNarc para análise estática..." java -jar /lib/codenarc-all.jar \ - -report="$report" \ + -report="json:${CODENARC_JSON}" \ -rulesetfiles="${INPUT_RULESETFILES}" \ -basedir="." \ - $includes_arg \ - > "$CODENARC_RESULT" - - echo "" - echo "" - echo "📋 Saída do CodeNarc:" - echo "" - echo "" - cat "$CODENARC_RESULT" - echo "" - echo "" -} - -run_reviewdog_with_config() { - input_file="$1" - efm="$2" - reporter="$3" - name="$4" - filter_mode="$5" - level="$6" + $includes_arg >/dev/null 2>&1 - < "$input_file" reviewdog \ - -efm="$efm" \ - -reporter="$reporter" \ - -name="$name" \ - -filter-mode="$filter_mode" \ - -fail-on-error="false" \ - -level="$level" \ - ${INPUT_REVIEWDOG_FLAGS} || true + echo "" + echo "📋 Processando violações encontradas:" + convert_json_to_compact + cat "$CODENARC_COMPACT" } -separate_violations() { - grep -E ':[0-9]+:' "$CODENARC_RESULT" > "$LINE_VIOLATIONS" || true - grep -E ':null:|\|\|' "$CODENARC_RESULT" > "$FILE_VIOLATIONS" || true +convert_json_to_compact() { + jq -r ' + .packages[]? | + .path as $pkg_path | + .files[]? | + ($pkg_path // "") as $rawpath | + .name as $filename | + (if $rawpath == "" then $filename else ($rawpath | ltrimstr("/")) + "/" + $filename end) as $file | + ($file | ltrimstr("/")) as $cleanfile | + .violations[]? | + if .lineNumber then + "\($cleanfile):\(.lineNumber):\(.ruleName) \(.message // "") [P\(.priority)]" + else + "\($cleanfile)::\(.ruleName) \(.message // "") [P\(.priority)]" + end + ' "$CODENARC_JSON" > "$CODENARC_COMPACT" 2>/dev/null || true } run_reviewdog() { + [ ! -s "$CODENARC_COMPACT" ] && return + echo "" echo "📤 Enviando resultados para reviewdog..." - separate_violations - - if [ -s "$LINE_VIOLATIONS" ]; then - echo "📤 Enviando violações line-based (${INPUT_REPORTER:-github-pr-check})..." - run_reviewdog_with_config "$LINE_VIOLATIONS" "%f:%l:%m" \ - "${INPUT_REPORTER:-github-pr-check}" "codenarc" \ - "${INPUT_FILTER_MODE}" "${INPUT_LEVEL}" - fi - - if [ -s "$FILE_VIOLATIONS" ]; then - true > "${FILE_VIOLATIONS}.formatted" - while read -r violation; do - if echo "$violation" | grep -q '||'; then - echo "$violation" | sed 's/||/::/' - else - echo "$violation" | sed 's/:null:/::/' - fi - done < "$FILE_VIOLATIONS" > "${FILE_VIOLATIONS}.formatted" - - if [ "${INPUT_REPORTER}" = "local" ]; then - echo "📤 Enviando violações file-based (local)..." - run_reviewdog_with_config "${FILE_VIOLATIONS}.formatted" "%f::%m" \ - "local" "codenarc" "nofilter" "${INPUT_LEVEL}" - else - echo "📤 Enviando violações file-based (github-pr-check)..." - run_reviewdog_with_config "${FILE_VIOLATIONS}.formatted" "%f::%m" \ - "github-pr-check" "codenarc" "nofilter" "warning" + if [ "${INPUT_REPORTER}" = "local" ]; then + < "$CODENARC_COMPACT" reviewdog \ + -efm="%f:%l:%m" \ + -efm="%f::%m" \ + -reporter="local" \ + -name="codenarc" \ + -filter-mode="${INPUT_FILTER_MODE}" \ + -level="${INPUT_LEVEL}" \ + ${INPUT_REVIEWDOG_FLAGS} >/dev/null || true + else + line_violations=$(grep -E ':[0-9]+:' "$CODENARC_COMPACT" || true) + if [ -n "$line_violations" ]; then + echo "$line_violations" | reviewdog \ + -efm="%f:%l:%m" \ + -reporter="github-pr-review" \ + -name="codenarc" \ + -filter-mode="${INPUT_FILTER_MODE}" \ + -level="${INPUT_LEVEL}" \ + ${INPUT_REVIEWDOG_FLAGS} >/dev/null || true + fi + file_violations=$(grep -E '::' "$CODENARC_COMPACT" || true) + if [ -n "$file_violations" ]; then + echo "$file_violations" | reviewdog \ + -efm="%f::%m" \ + -reporter="github-pr-check" \ + -name="codenarc" \ + -filter-mode="nofilter" \ + -level="warning" \ + ${INPUT_REVIEWDOG_FLAGS} >/dev/null || true fi - fi - - # fallback se nao houver violacoes categorizadas - if [ ! -s "$LINE_VIOLATIONS" ] && [ ! -s "$FILE_VIOLATIONS" ]; then - echo "📝 Executando reviewdog padrão..." - run_reviewdog_with_config "$CODENARC_RESULT" "%f:%l:%m" \ - "${INPUT_REPORTER:-github-pr-check}" "codenarc" \ - "${INPUT_FILTER_MODE}" "${INPUT_LEVEL}" fi } generate_git_diff() { if [ -n "$GITHUB_BASE_SHA" ] && [ -n "$GITHUB_HEAD_SHA" ]; then - git fetch origin "$GITHUB_BASE_SHA" --depth=1 2>/dev/null || true - git fetch origin "$GITHUB_HEAD_SHA" --depth=1 2>/dev/null || true + git fetch origin "$GITHUB_BASE_SHA" --depth=1 >/dev/null 2>&1 || true + git fetch origin "$GITHUB_HEAD_SHA" --depth=1 >/dev/null 2>&1 || true git diff -U0 "$GITHUB_BASE_SHA" "$GITHUB_HEAD_SHA" -- '*.groovy' else git diff -U0 HEAD~1 -- '*.groovy' fi } -parse_diff_range() { - range="$1" - if echo "$range" | grep -q ","; then - echo "$(echo "$range" | cut -d',' -f1) $(echo "$range" | cut -d',' -f2)" - else - echo "$range 1" - fi -} - build_changed_lines_cache() { - true > "$CHANGED_LINES_CACHE" true > "$CHANGED_FILES_CACHE" - - generate_git_diff > "$ALL_DIFF" 2>/dev/null || true - [ ! -s "$ALL_DIFF" ] && return 0 - - current_file="" - while read -r line; do - case "$line" in - "diff --git"*) - current_file=$(echo "$line" | sed 's|^diff --git a/\(.*\) b/.*|\1|') - [ -n "$current_file" ] && echo "$current_file" >> "$CHANGED_FILES_CACHE" - ;; - "@@"*) - [ -z "$current_file" ] && continue - range=$(echo "$line" | sed 's/.*+\([0-9,]*\).*/\1/') - range_info=$(parse_diff_range "$range") - start=$(echo "$range_info" | cut -d' ' -f1) - count=$(echo "$range_info" | cut -d' ' -f2) - - case "$start" in ''|*[!0-9]*) continue ;; esac - case "$count" in ''|*[!0-9]*) continue ;; esac - - i="$start" - while [ "$i" -lt "$((start + count))" ]; do - echo "$current_file:$i" >> "$CHANGED_LINES_CACHE" - i=$((i + 1)) - done - ;; - esac - done < "$ALL_DIFF" -} - -get_p1_count() { - p1_count=$(grep -Eo "p1=[0-9]+" "$CODENARC_RESULT" | cut -d'=' -f2 | head -1) - echo "${p1_count:-0}" -} + true > "$CHANGED_LINES_CACHE" -get_allowed_patterns() { - [ -n "$INPUT_SOURCE_FILES" ] && echo "$INPUT_SOURCE_FILES" | tr ',' '\n' | sed 's/\*\*/.*/g' + generate_git_diff > "$ALL_DIFF" 2>/dev/null || return + [ ! -s "$ALL_DIFF" ] && return + + awk ' + /^diff --git/ { + file = $3 + sub(/^a\//, "", file) + print file >> "'"$CHANGED_FILES_CACHE"'" + } + /^@@/ { + match($0, /\+([0-9]+)(,([0-9]+))?/) + range = substr($0, RSTART, RLENGTH) + sub(/^\+/, "", range) + split(range, parts, ",") + start = parts[1] + count = parts[2] + if (count == "") count = 1 + for (i = start; i < start + count; i++) + print file ":" i >> "'"$CHANGED_LINES_CACHE"'" + } + ' "$ALL_DIFF" } -file_matches_patterns() { +is_changed() { file="$1" - patterns="$2" + line="$2" - [ -z "$patterns" ] && return 0 + if [ -z "$line" ]; then + [ -f "$CHANGED_FILES_CACHE" ] && grep -qF "$file" "$CHANGED_FILES_CACHE" && return 0 + return 1 + fi - for pattern in $patterns; do - echo "$file" | grep -Eq "$pattern" && return 0 - done + [ -f "$CHANGED_LINES_CACHE" ] && grep -qF "${file}:${line}" "$CHANGED_LINES_CACHE" && return 0 return 1 } -is_line_changed() { - grep -q "^$2:$1$" "$CHANGED_LINES_CACHE" -} - -is_file_changed() { - grep -q "^$1$" "$CHANGED_FILES_CACHE" +extract_p1_violations() { + jq -r ' + .packages[]? | + .path as $pkg_path | + .files[]? | + ($pkg_path // "") as $rawpath | + .name as $filename | + (if $rawpath == "" then $filename else ($rawpath | ltrimstr("/")) + "/" + $filename end) as $file | + ($file | ltrimstr("/")) as $cleanfile | + .violations[]? | select(.priority == 1) | + if .lineNumber then + "\($cleanfile):\(.lineNumber):\(.ruleName) \(.message // "")" + else + "\($cleanfile)::\(.ruleName) \(.message // "")" + end + ' "$CODENARC_JSON" 2>/dev/null } check_blocking_rules() { - echo "🔎 Verificando violações bloqueantes (priority 1)..." - - [ ! -f "$CODENARC_RESULT" ] && echo "❌ Resultado não encontrado" && return 1 + echo "" + echo "🔎 Verificando violações bloqueantes (P1)..." + [ ! -f "$CODENARC_JSON" ] && echo "❌ Erro: Resultado do CodeNarc não encontrado. Não é possível verificar P1s." && return 1 - p1_count=$(get_p1_count) + p1_violations=$(extract_p1_violations) + if [ -z "$p1_violations" ]; then + echo "✅ Nenhuma violação P1 detectada → merge permitido" + return 0 + fi + + p1_count=$(echo "$p1_violations" | wc -l | tr -d ' ') echo "📊 Total de P1 encontradas: $p1_count" - - [ "$p1_count" -eq 0 ] && echo "✅ Nenhuma P1 detectada → merge permitido" && return 0 - - echo "⚠️ Verificando P1s em linhas alteradas..." + echo "⛔ Violações P1:" + echo "$p1_violations" + + if [ "${INPUT_REPORTER}" = "local" ]; then + echo "" + echo "🏠 Modo de execução local: todas as violações P1 são bloqueantes." + echo "💡 Corrija as violações antes de prosseguir." + exit 1 + fi + + echo "" + echo "⚠️ Analisando se as P1s estão em linhas alteradas..." build_changed_lines_cache + + if [ ! -s "$ALL_DIFF" ]; then + echo "" + echo "⚠️ Diff vazio: Sem informações de linhas alteradas. Todas as P1s são consideradas bloqueantes." + echo "💡 Corrija as violações ou use um bypass autorizado." + exit 1 + fi - allowed_patterns=$(get_allowed_patterns) - [ -n "$allowed_patterns" ] && echo "🧩 Analisando apenas arquivos filtrados por INPUT_SOURCE_FILES" - - echo "0" > "$VIOLATIONS_FLAG" - - grep -E ':[0-9]+:|:null:|\|\|' "$CODENARC_RESULT" | while IFS=: read -r file line rest; do - if echo "$file" | grep -q '||'; then - file=$(echo "$file" | cut -d'|' -f1) - line="" - fi + found_blocking=0 + while IFS=: read -r file line rest; do [ -z "$file" ] && continue - file_matches_patterns "$file" "$allowed_patterns" || continue - if [ -z "$line" ] || [ "$line" = "null" ]; then - if is_file_changed "$file"; then - echo "📍 Violação file-based em arquivo alterado: $file" - echo "1" > "$VIOLATIONS_FLAG" && break + if [ -z "$line" ]; then + if is_changed "$file" ""; then + echo "🚨 BLOQUEADO: Violação P1 a nível de arquivo encontrada no arquivo alterado: $file" + found_blocking=1 + break + fi + else + if is_changed "$file" "$line"; then + echo "🚨 BLOQUEADO: Violação P1 encontrada na linha alterada: $file:$line" + found_blocking=1 + break fi - elif is_line_changed "$line" "$file"; then - echo "📍 Violação em linha alterada: $file:$line" - echo "1" > "$VIOLATIONS_FLAG" && break fi - done - - if [ "$(cat "$VIOLATIONS_FLAG")" -eq 1 ]; then - echo "⛔ P1s existem E há violações em linhas alteradas" - echo "💡 Corrija as violacoes ou use o bypass autorizado pelo coordenador." + done < + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://codenarc.org/ruleset/1.0 http://codenarc.org/ruleset-schema.xsd" + xsi:noNamespaceSchemaLocation="http://codenarc.org/ruleset-schema.xsd"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/testdata/test.groovy b/testdata/test.groovy index 0897f5c..3e777fa 100644 --- a/testdata/test.groovy +++ b/testdata/test.groovy @@ -1,13 +1,15 @@ -package test +package com.test -class Test { - - boolean before() { - return true +class TestCase2 { + + void existingMethod() { + println "existing" } - - boolean after() { true } - - void after() { + + // P1 + String insecureUrl = "http://api.example.com" + + void callApi() { + // usar insecureUrl } -} +} \ No newline at end of file