| 1 | #!/usr/bin/env bash |
| 2 | # pr-issues.sh -- fetch Codacy issues for a PR and emit a clustered summary. |
| 3 | # |
| 4 | # Output: |
| 5 | # - Full JSON dump of issues: |
| 6 | # <repo>/.local/audits/codacy/pr-<NNN>-<timestamp>.json |
| 7 | # - TSV summary clustering by tool / pattern / severity / file |
| 8 | # printed to stdout. |
| 9 | |
| 10 | set -euo pipefail |
| 11 | |
| 12 | usage() { |
| 13 | cat <<'EOF' |
| 14 | pr-issues.sh <pr-number> [options] |
| 15 | |
| 16 | Fetches the Codacy v3 PR issues list for the given PR, paginating |
| 17 | through every result, and writes a JSON dump under |
| 18 | <repo>/.local/audits/codacy/. Emits a TSV summary on stdout |
| 19 | clustering issues by tool / pattern / severity / file. |
| 20 | |
| 21 | Options: |
| 22 | --output PATH explicit output path for the JSON dump |
| 23 | --by <dim> summary dimension: tool|pattern|severity|file|category |
| 24 | (default: pattern) |
| 25 | --top N top-N rows in the summary (default: 25) |
| 26 | -h, --help |
| 27 | |
| 28 | Required env: CODACY_TOKEN (see <repo>/.agents/ENV.md) |
| 29 | Defaults: org=netdata, repo=netdata, provider=gh |
| 30 | (override via CODACY_ORG / CODACY_REPO / CODACY_PROVIDER in .env) |
| 31 | EOF |
| 32 | } |
| 33 | |
| 34 | PR= |
| 35 | OUTPUT= |
| 36 | BY=pattern |
| 37 | TOP=25 |
| 38 | |
| 39 | while [ $# -gt 0 ]; do |
| 40 | case "$1" in |
| 41 | --output) OUTPUT="$2"; shift 2 ;; |
| 42 | --by) BY="$2"; shift 2 ;; |
| 43 | --top) TOP="$2"; shift 2 ;; |
| 44 | -h|--help) usage; exit 0 ;; |
| 45 | -*) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;; |
| 46 | *) |
| 47 | if [ -z "$PR" ]; then PR="$1" |
| 48 | else echo "Unexpected positional arg: $1" >&2; usage >&2; exit 2 |
| 49 | fi |
| 50 | shift |
| 51 | ;; |
| 52 | esac |
| 53 | done |
| 54 | |
| 55 | if [ -z "$PR" ]; then |
| 56 | usage >&2 |
| 57 | exit 2 |
| 58 | fi |
| 59 | |
| 60 | # shellcheck source=SCRIPTDIR/_lib.sh disable=SC1091 |
| 61 | source "$(cd "$(dirname "$0")" && pwd)/_lib.sh" |
| 62 | codacyaudit_load_env |
| 63 | |
| 64 | # --------------------------------------------------------------- |
| 65 | # Output path. |
| 66 | |
| 67 | if [ -z "$OUTPUT" ]; then |
| 68 | audit_dir="$(codacyaudit_audit_dir)" |
| 69 | OUTPUT="${audit_dir}/pr-${PR}-$(date -u +%Y%m%dT%H%M%SZ).json" |
| 70 | fi |
| 71 | |
| 72 | echo -e "${CA_GRAY}[pr-issues] fetching issues for PR #${PR} ...${CA_NC}" >&2 |
| 73 | |
| 74 | # Wrap raw issues array into an envelope with metadata. |
| 75 | issues_array="$(codacyaudit_pr_issues "$PR")" |
| 76 | total="$(printf '%s' "$issues_array" | jq 'length')" |
| 77 | issues_tmp="$(mktemp "${TMPDIR:-/tmp}/codacy-pr-issues-XXXXXX")" |
| 78 | trap 'rm -f "$issues_tmp"' EXIT |
| 79 | printf '%s' "$issues_array" > "$issues_tmp" |
| 80 | |
| 81 | jq -n \ |
| 82 | --arg pr "$PR" \ |
| 83 | --arg fetched_at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ |
| 84 | --argjson total "$total" \ |
| 85 | --slurpfile data "$issues_tmp" \ |
| 86 | '{ |
| 87 | pr: ($pr | tonumber), |
| 88 | fetched_at: $fetched_at, |
| 89 | total: $total, |
| 90 | data: $data[0] |
| 91 | }' > "$OUTPUT" |
| 92 | |
| 93 | echo -e "${CA_GREEN}[pr-issues]${CA_NC} wrote ${total} issue(s) to ${OUTPUT}" >&2 |
| 94 | echo "$OUTPUT" |
| 95 | |
| 96 | # --------------------------------------------------------------- |
| 97 | # Summary. |
| 98 | |
| 99 | if [ "$total" -eq 0 ]; then |
| 100 | echo |
| 101 | echo -e "${CA_GREEN}No issues on PR #${PR}.${CA_NC}" |
| 102 | exit 0 |
| 103 | fi |
| 104 | |
| 105 | field_for_dim() { |
| 106 | local dim="$1" |
| 107 | case "$dim" in |
| 108 | tool) echo '.commitIssue.toolInfo.name' ;; |
| 109 | pattern) echo '.commitIssue.patternInfo.id' ;; |
| 110 | severity) echo '.commitIssue.patternInfo.severityLevel' ;; |
| 111 | file) echo '.commitIssue.filePath' ;; |
| 112 | category) echo '.commitIssue.patternInfo.category' ;; |
| 113 | *) echo "Unknown --by '$dim'" >&2; exit 2 ;; |
| 114 | esac |
| 115 | } |
| 116 | |
| 117 | field_path="$(field_for_dim "$BY")" |
| 118 | |
| 119 | echo |
| 120 | echo -e "${CA_CYAN}Top ${TOP} by ${BY} (PR #${PR}, ${total} total issue(s)):${CA_NC}" |
| 121 | printf '%s\n' "------------------------------------------------------------" |
| 122 | |
| 123 | jq -r --argjson top "$TOP" " |
| 124 | .data |
| 125 | | group_by(${field_path} // \"(none)\") |
| 126 | | map({key: (.[0] | ${field_path} // \"(none)\"), count: length}) |
| 127 | | sort_by(-.count) |
| 128 | | .[:\$top] |
| 129 | | .[] |
| 130 | | [.count, .key] | @tsv |
| 131 | " "$OUTPUT" \ |
| 132 | | awk -F'\t' '{ printf "%8d %s\n", $1, $2 }' |
| 133 | |
| 134 | printf '%s\n' "------------------------------------------------------------" |