diff --git a/.claude/commands/api-lint-diff.md b/.claude/commands/api-lint-diff.md index f9d102d1e..549809bc4 100644 --- a/.claude/commands/api-lint-diff.md +++ b/.claude/commands/api-lint-diff.md @@ -4,7 +4,7 @@ description: Validate API issues using kube-api-linter with diff-aware analysis # API Lint Diff -Validates API issues in `api/` directory using kube-api-linter with diff-aware analysis that distinguishes between NEW and PRE-EXISTING issues. +Validates API issues in `api/` directory using kube-api-linter with diff-aware analysis that distinguishes between FIXED, NEW, and PRE-EXISTING issues. ## Instructions for Claude AI @@ -19,6 +19,7 @@ When this command is invoked, you MUST: 2. **Understand the shell script's output**: - **False positives (IGNORED)**: Standard CRD scaffolding patterns that kube-api-linter incorrectly flags + - **FIXED issues (SUCCESS)**: Issues that existed in baseline but were resolved in current branch → Celebrate! 🎉 - **NEW issues (ERRORS)**: Introduced in current branch → MUST fix - **PRE-EXISTING issues (WARNINGS)**: Existed before changes → Can fix separately @@ -132,23 +133,38 @@ When this command is invoked, you MUST: # API Lint Diff Analysis Report **Generated:** [date] - **Baseline:** main branch - **Current:** [branch name] + **Baseline:** main branch (X issues) + **Current:** [branch name] (Y issues) **Status:** [status icon and message based on logic below] **Status Logic:** - - ✅ PASSED: 0 real issues (after filtering false positives) + - ✅ PASSED: 0 new issues (fixed issues are OK) - ⚠️ WARN: 0 new issues but has pre-existing issues - ❌ FAIL: Has new issues that must be fixed ## Executive Summary - - Total issues: X - - False positives (IGNORED): Y - - Real issues (NEED FIXING): Z - - NEW issues: N - - PRE-EXISTING issues: P + - Baseline issues: X + - Current issues: Y + - **FIXED**: F (issues resolved in this branch) + - **NEW**: N (issues introduced in this branch) + - **PRE-EXISTING**: P (issues that still remain) + - False positives (IGNORED): Z - ## REAL ISSUES - FIXES NEEDED (Z issues) + ## FIXED ISSUES (F issues) + + [List of issues that were fixed in this branch - show the baseline line numbers] + + ## NEW ISSUES (N issues) + + [List of issues introduced in this branch - these MUST be fixed] + + ## PRE-EXISTING ISSUES (P issues) + + [List of issues that existed before and still exist - can be fixed separately] + + --- + + ## DETAILED ANALYSIS FOR ISSUES NEEDING FIXES ### Category 1: [Issue Type] (N issues) - [BREAKING/NON-BREAKING] diff --git a/hack/api-lint-diff/run.sh b/hack/api-lint-diff/run.sh index ca067d618..993ec9052 100755 --- a/hack/api-lint-diff/run.sh +++ b/hack/api-lint-diff/run.sh @@ -257,13 +257,14 @@ get_changed_files() { grep -v 'zz_generated' || true } -# Categorize issues as NEW or PRE-EXISTING +# Categorize issues as NEW, PRE-EXISTING, or FIXED categorize_issues() { local current_file="$1" local baseline_file="$2" local changed_files_file="$3" local new_issues_file="$4" local preexisting_issues_file="$5" + local fixed_issues_file="$6" # Read changed files into array local changed_files=() @@ -306,17 +307,81 @@ categorize_issues() { # Compare without line numbers since line numbers can change when code is added/removed # Format is: file:line:col:linter:message # We'll compare: file:linter:message - # Use f1,4,5- to capture field 5 and all remaining fields (handles colons in messages) + # Extract file (field 1), linter (field 4), and message (field 5+) from current issue local file_linter_msg file_linter_msg=$(echo "${line}" | cut -d: -f1,4,5-) - if grep -Fq "${file_linter_msg}" "${baseline_file}" 2>/dev/null; then + # Check if baseline has a matching issue (same file, linter, message but possibly different line number) + # We need to extract the same fields from baseline and compare + local found=false + if [[ -f "${baseline_file}" ]]; then + while IFS= read -r baseline_line; do + [[ -z "${baseline_line}" ]] && continue + local baseline_file_linter_msg + baseline_file_linter_msg=$(echo "${baseline_line}" | cut -d: -f1,4,5-) + if [[ "${file_linter_msg}" == "${baseline_file_linter_msg}" ]]; then + found=true + break + fi + done < "${baseline_file}" + fi + + if $found; then echo "${line}" >> "${preexisting_issues_file}" else echo "${line}" >> "${new_issues_file}" fi done < "${current_file}" fi + + # Find FIXED issues - issues in baseline that are NOT in current + if [[ -f "${baseline_file}" && -s "${baseline_file}" ]]; then + while IFS= read -r baseline_line; do + [[ -z "${baseline_line}" ]] && continue + + local file + file=$(echo "${baseline_line}" | cut -d: -f1) + + # Only check files that were changed + if [[ ${#changed_files[@]} -gt 0 ]]; then + local file_changed=false + for changed_file in "${changed_files[@]}"; do + if [[ "${file}" == "${changed_file}" ]]; then + file_changed=true + break + fi + done + + # Skip if file wasn't changed + if ! $file_changed; then + continue + fi + fi + + # Extract file:linter:message from baseline + local baseline_file_linter_msg + baseline_file_linter_msg=$(echo "${baseline_line}" | cut -d: -f1,4,5-) + + # Check if this issue still exists in current + local still_exists=false + if [[ -f "${current_file}" ]]; then + while IFS= read -r current_line; do + [[ -z "${current_line}" ]] && continue + local current_file_linter_msg + current_file_linter_msg=$(echo "${current_line}" | cut -d: -f1,4,5-) + if [[ "${baseline_file_linter_msg}" == "${current_file_linter_msg}" ]]; then + still_exists=true + break + fi + done < "${current_file}" + fi + + # If issue doesn't exist in current, it was fixed + if ! $still_exists; then + echo "${baseline_line}" >> "${fixed_issues_file}" + fi + done < "${baseline_file}" + fi } # Output issue (basic format) @@ -328,20 +393,41 @@ output_issue() { generate_report() { local new_issues_file="$1" local preexisting_issues_file="$2" + local fixed_issues_file="$3" + local baseline_file="$4" local new_count=0 local preexisting_count=0 + local fixed_count=0 + local baseline_count=0 [[ -f "${new_issues_file}" ]] && new_count=$(wc -l < "${new_issues_file}" | tr -d ' ') [[ -f "${preexisting_issues_file}" ]] && preexisting_count=$(wc -l < "${preexisting_issues_file}" | tr -d ' ') + [[ -f "${fixed_issues_file}" ]] && fixed_count=$(wc -l < "${fixed_issues_file}" | tr -d ' ') + [[ -f "${baseline_file}" ]] && baseline_count=$(wc -l < "${baseline_file}" | tr -d ' ') + + local current_total=$((new_count + preexisting_count)) - # Simple summary + # Summary header echo "API Lint Diff Results" - echo "Baseline: ${BASELINE_BRANCH}" + echo "=====================" + echo "Baseline (${BASELINE_BRANCH}): ${baseline_count} issues" + echo "Current branch: ${current_total} issues" + echo "" + echo "FIXED: ${fixed_count}" echo "NEW: ${new_count}" echo "PRE-EXISTING: ${preexisting_count}" echo "" + # Show FIXED issues + if [[ ${fixed_count} -gt 0 ]]; then + echo "=== FIXED ISSUES ===" + while IFS= read -r line; do + output_issue "${line}" + done < "${fixed_issues_file}" + echo "" + fi + # Show NEW issues if [[ ${new_count} -gt 0 ]]; then echo "=== NEW ISSUES ===" @@ -362,13 +448,17 @@ generate_report() { # Exit based on NEW issues count if [[ ${new_count} -eq 0 ]]; then - echo -e "${GREEN}NO NEW ISSUES found. Lint check passed.${NC}" + if [[ ${fixed_count} -gt 0 ]]; then + echo -e "${GREEN}SUCCESS: Fixed ${fixed_count} issue(s), no new issues introduced.${NC}" + else + echo -e "${GREEN}NO NEW ISSUES found. Lint check passed.${NC}" + fi if [[ ${preexisting_count} -gt 0 ]]; then - echo -e "${YELLOW}WARNING: Pre-existing issues detected. Please address them separately.${NC}" + echo -e "${YELLOW}WARNING: ${preexisting_count} pre-existing issue(s) remain. Please address them separately.${NC}" fi return 0 else - echo -e "${RED}FAILED: ${new_count} new issue(s)${NC}" + echo -e "${RED}FAILED: ${new_count} new issue(s) introduced${NC}" return 1 fi } @@ -414,18 +504,22 @@ main() { # Categorize issues touch "${TEMP_DIR}/new_issues.txt" touch "${TEMP_DIR}/preexisting_issues.txt" + touch "${TEMP_DIR}/fixed_issues.txt" categorize_issues \ "${TEMP_DIR}/current_parsed.txt" \ "${TEMP_DIR}/baseline_parsed.txt" \ "${TEMP_DIR}/changed_files.txt" \ "${TEMP_DIR}/new_issues.txt" \ - "${TEMP_DIR}/preexisting_issues.txt" + "${TEMP_DIR}/preexisting_issues.txt" \ + "${TEMP_DIR}/fixed_issues.txt" # Generate report generate_report \ "${TEMP_DIR}/new_issues.txt" \ - "${TEMP_DIR}/preexisting_issues.txt" + "${TEMP_DIR}/preexisting_issues.txt" \ + "${TEMP_DIR}/fixed_issues.txt" \ + "${TEMP_DIR}/baseline_parsed.txt" return $? }