diff --git a/js/util/gh-dependabot-failure-stats.sh b/js/util/gh-dependabot-failure-stats.sh new file mode 100755 index 0000000000..57086f8259 --- /dev/null +++ b/js/util/gh-dependabot-failure-stats.sh @@ -0,0 +1,173 @@ +#!/bin/bash +# Displays the count of Dependabot PRs opened in the last 7 days +# and the number of failed Admin UI E2E and Account UI E2E checks. + +# Usage: `./gh-dependabot-failure-stats.sh ` + +# Sample output: +# Total Open Dependabot PRs: 18 +# Total Admin UI E2E failures: 1 +# Total Account UI E2E failures: 0 + +# --- Failure Details for PR #12345 --- +# Title: Bump typescript-eslint from 7.17.0 to 7.18.0 +# URL: https://github.com/keycloak/keycloak/pull/12345 +# Branch: dependabot/npm_and_yarn/typescript-eslint-7.18.0 +# More Info: +# - Admin UI E2E Check: Admin UI E2E (2, chrome) +# Details URL: https://github.com/keycloak/keycloak/actions/runs/56789/job/98765 +# Run ID: 56789 +# Run Status: completed +# Run Conclusion: failure +# Run HTML URL: https://github.com/keycloak/keycloak/actions/runs/56789 +# Run Attempt: 1 + +REPO_OWNER="keycloak" +REPO_NAME="keycloak" +total_prs=0 +admin_failure_counter=0 +account_failure_counter=0 +failure_details="" + +function get_dependabot_prs_last_7_days() { + local seven_days_ago + seven_days_ago=$([ "$(uname)" = Linux ] && date --date="7 days ago" +%Y-%m-%d || date -v -7d +%Y-%m-%d) + + gh pr list --repo "${REPO_OWNER}/${REPO_NAME}" --author "app/dependabot" --state all --json number,createdAt \ + | jq -r --arg seven_days_ago "$seven_days_ago" \ + '.[] | select(.createdAt > $seven_days_ago) | "\(.number)"' +} + +function get_pr_details() { + local pr_number=$1 + gh pr view "$pr_number" --repo "${REPO_OWNER}/${REPO_NAME}" --json number,title,headRefName,url +} + +function get_failed_runs() { + local pr_number=$1 + local commit_sha + commit_sha=$(gh pr view "$pr_number" --repo "${REPO_OWNER}/${REPO_NAME}" --json commits -q '.commits[-1].oid') + + gh api -X GET "/repos/${REPO_OWNER}/${REPO_NAME}/commits/${commit_sha}/check-runs" \ + --jq '.check_runs[] | select(.conclusion == "failure")' +} + +function get_run_details() { + local run_id=$1 + gh api -X GET "/repos/${REPO_OWNER}/${REPO_NAME}/actions/runs/${run_id}" \ + --jq '{run_id: .id, status: .status, conclusion: .conclusion, html_url: .html_url, run_attempt: .run_attempt, jobs_url: .jobs_url}' +} + +function get_failed_jobs_details() { + local jobs_url=$1 + gh api -X GET "$jobs_url" \ + --jq '.jobs[] | select(.conclusion == "failure" and (.name | contains("Admin UI E2E") or contains("Account UI E2E")))' +} + +function get_failed_dependabot_prs_details() { + local dependabot_prs_last_7_days + dependabot_prs_last_7_days=$(get_dependabot_prs_last_7_days) + total_prs=$(echo "$dependabot_prs_last_7_days" | wc -l) + admin_failure_counter=0 + account_failure_counter=0 + failure_details="" + + while read -r pr_number; do + local pr_details + local title + local url + local head_ref + local failed_runs + local relevant_failures="" + local admin_failure_added=0 + local account_failure_added=0 + + pr_details=$(get_pr_details "$pr_number") + title=$(echo "$pr_details" | jq -r '.title') + url=$(echo "$pr_details" | jq -r '.url') + head_ref=$(echo "$pr_details" | jq -r '.headRefName') + failed_runs=$(get_failed_runs "$pr_number") + + if [ -n "$failed_runs" ]; then + while read -r check; do + local check_name + local details_url + local run_id + local run_details + local run_status + local run_conclusion + local run_html_url + local run_attempt + local jobs_url + local failed_jobs + + check_name=$(echo "$check" | jq -r '.name') + details_url=$(echo "$check" | jq -r '.details_url') + run_id=$(echo "$details_url" | sed -E 's#.*/actions/runs/([0-9]+)/.*#\1#') + run_details=$(get_run_details "$run_id") + run_status=$(echo "$run_details" | jq -r '.status') + run_conclusion=$(echo "$run_details" | jq -r '.conclusion') + run_html_url=$(echo "$run_details" | jq -r '.html_url') + run_attempt=$(echo "$run_details" | jq -r '.run_attempt') + jobs_url=$(echo "$run_details" | jq -r '.jobs_url') + failed_jobs=$(get_failed_jobs_details "$jobs_url") + + if [ -n "$failed_jobs" ]; then + while read -r job; do + local job_name + local job_url + + job_name=$(echo "$job" | jq -r '.name') + job_url=$(echo "$job" | jq -r '.html_url') + + if [[ "$job_name" == *"Admin UI E2E"* && "$admin_failure_added" -eq 0 ]]; then + relevant_failures+=" - Admin UI E2E Check: $job_name\n" + relevant_failures+=" Details URL: $job_url\n" + relevant_failures+=" Run ID: $run_id\n" + relevant_failures+=" Run Status: $run_status\n" + relevant_failures+=" Run Conclusion: $run_conclusion\n" + relevant_failures+=" Run HTML URL: $run_html_url\n" + relevant_failures+=" Run Attempt: $run_attempt\n" + relevant_failures+="\n" + admin_failure_counter=$((admin_failure_counter + 1)) + admin_failure_added=1 + fi + + if [[ "$job_name" == *"Account UI E2E"* && "$account_failure_added" -eq 0 ]]; then + relevant_failures+=" - Account UI E2E Check: $job_name\n" + relevant_failures+=" Details URL: $job_url\n" + relevant_failures+=" Run ID: $run_id\n" + relevant_failures+=" Run Status: $run_status\n" + relevant_failures+=" Run Conclusion: $run_conclusion\n" + relevant_failures+=" Run HTML URL: $run_html_url\n" + relevant_failures+=" Run Attempt: $run_attempt\n" + relevant_failures+="\n" + account_failure_counter=$((account_failure_counter + 1)) + account_failure_added=1 + fi + done <<< "$(echo "$failed_jobs")" + fi + done <<< "$(echo "$failed_runs")" + + if [ -n "$relevant_failures" ]; then + failure_details+="--- Failure Details for PR #$pr_number ---\n" + failure_details+="Title: $title\n" + failure_details+="URL: $url\n" + failure_details+="Branch: $head_ref\n" + failure_details+="More Info:\n" + failure_details+="$relevant_failures" + fi + fi + done <<< "$dependabot_prs_last_7_days" +} + +function get_stats() { + echo "Total Opened Dependabot PRs: $total_prs" + echo "Total Admin UI E2E failures: $admin_failure_counter" + echo "Total Account UI E2E failures: $account_failure_counter" + echo "" + echo -e "$failure_details" +} + +get_failed_dependabot_prs_details +get_stats \ No newline at end of file