Skip to content

Commit 22a7443

Browse files
Add CLAUDE command and new check to review and lint the API changes
1 parent 90cd55a commit 22a7443

File tree

2 files changed

+681
-0
lines changed

2 files changed

+681
-0
lines changed

.claude/commands/api-lint-diff.md

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
---
2+
description: Validate API issues using kube-api-linter with diff-aware analysis
3+
---
4+
5+
# API Lint Diff
6+
7+
Validates API issues in `api/` directory using kube-api-linter with diff-aware analysis that distinguishes between NEW and PRE-EXISTING issues.
8+
9+
## Instructions for Claude AI
10+
11+
When this command is invoked, you MUST:
12+
13+
**CRITICAL:** The final output MUST be a comprehensive analysis report displayed directly in the conversation for the user to read. Do NOT just create temp files - output the full report as your response.
14+
15+
1. **Execute the shell script**
16+
```bash
17+
bash hack/api-lint-diff/run.sh
18+
```
19+
20+
2. **Understand the shell script's output**:
21+
- **False positives (IGNORED)**: Standard CRD scaffolding patterns that kube-api-linter incorrectly flags
22+
- **NEW issues (ERRORS)**: Introduced in current branch → MUST fix
23+
- **PRE-EXISTING issues (WARNINGS)**: Existed before changes → Can fix separately
24+
25+
3. **Filter false positives** - Operator projects scaffold standard Kubernetes CRD patterns that kube-api-linter incorrectly flags as errors, even with WhenRequired configuration.
26+
27+
**Scenario 1: optionalfields on Status field**
28+
```go
29+
Status MyResourceStatus `json:"status,omitzero"`
30+
```
31+
**Error reported:**
32+
```
33+
optionalfields: field Status has a valid zero value ({}), but the validation
34+
is not complete (e.g. min properties/adding required fields). The field should
35+
be a pointer to allow the zero value to be set. If the zero value is not a
36+
valid use case, complete the validation and remove the pointer.
37+
```
38+
**Why it's a FALSE POSITIVE:**
39+
- Status is NEVER a pointer in any Kubernetes API
40+
- Works correctly with `omitzero` tag
41+
- Validation incompleteness is expected - Status is controller-managed, not user-provided
42+
- **ACTION: IGNORE this error**
43+
44+
**Scenario 2: requiredfields on Spec field**
45+
```go
46+
Spec MyResourceSpec `json:"spec"`
47+
```
48+
**Error reported:**
49+
```
50+
requiredfields: field Spec has a valid zero value ({}), but the validation is
51+
not complete (e.g. min properties/adding required fields). The field should be
52+
a pointer to allow the zero value to be set. If the zero value is not a valid
53+
use case, complete the validation and remove the pointer.
54+
```
55+
**Why it's a FALSE POSITIVE:**
56+
- Spec is NEVER a pointer in Kubernetes APIs
57+
- Scaffolds are starting points - users add validation when they implement their business logic
58+
- **ACTION: IGNORE this error**
59+
60+
**Scenario 3: conditions markers on metav1.Condition**
61+
```go
62+
Conditions []metav1.Condition `json:"conditions,omitempty"`
63+
```
64+
**Error reported:**
65+
```
66+
conditions: Conditions field is missing the following markers:
67+
patchStrategy=merge, patchMergeKey=type
68+
```
69+
**Why it's a FALSE POSITIVE:**
70+
- `metav1.Condition` already handles patches correctly
71+
- Adding these markers is redundant for this standard Kubernetes type
72+
- **ACTION: IGNORE this error**
73+
74+
4. **For reported issues, provide intelligent analysis**:
75+
76+
a. **Categorize by fix complexity**:
77+
- NON-BREAKING: Marker replacements, adding listType, adding +required/+optional
78+
- BREAKING: Pointer conversions, type changes (int→int32)
79+
80+
b. **Search for actual usage** (REQUIRED FOR ALL ISSUES - NOT OPTIONAL):
81+
- **CRITICAL:** Do NOT just look at JSON tags - analyze actual code usage patterns
82+
- **Exception:** Deprecated marker replacements (`+kubebuilder:validation:Required``+required`) are straightforward - no usage analysis needed
83+
- **For all other issues:** MUST analyze actual usage before making recommendations
84+
- Use Grep to find ALL occurrences where each field is:
85+
* **Read/accessed**: `obj.Spec.FieldName`, `cat.Spec.Priority`
86+
* **Written/set**: `obj.Spec.FieldName = value`
87+
* **Checked for zero/nil**: `if obj.Spec.FieldName == ""`, `if ptr != nil`
88+
* **Used in conditionals**: Understand semantic meaning of zero values
89+
- Search in: controllers, reconcilers, internal packages, tests, examples
90+
- **Smart assessment based on usage patterns**:
91+
* Field ALWAYS set in code? → Should be **required**, no omitempty
92+
* Field SOMETIMES set? → Should be **optional** with omitempty
93+
* Code checks `if field == zero`? → May need **pointer** to distinguish zero vs unset
94+
* Zero value semantically valid? → Keep as value type with omitempty
95+
* Zero value semantically invalid? → Use pointer OR mark required
96+
* Field never read but only set by controller? → Likely Status field
97+
- **Example analysis workflow for a field**:
98+
```
99+
1. Grep for field usage: `CatalogFilter.Version`
100+
2. Found 5 occurrences:
101+
- controllers/extension.go:123: if filter.Version != "" { ... }
102+
- controllers/extension.go:456: result.Version = bundle.Version
103+
- tests/filter_test.go:89: Version: "1.2.3"
104+
3. Analysis: Version is checked for empty, sometimes set, sometimes omitted
105+
4. Recommendation: Optional with omitempty (current usage supports this)
106+
```
107+
108+
c. **Generate EXACT code fixes** grouped by file:
109+
- Show current code
110+
- Show replacement code (copy-pasteable)
111+
- **Explain why based on actual usage analysis** (not just JSON tags):
112+
* Include usage summary: "Found N occurrences"
113+
* Cite specific examples: "Used in resolve/catalog.go:163 as direct int32"
114+
* Explain semantic meaning: "Field distinguishes priority 0 vs unset"
115+
* Justify recommendation: "Since code checks for empty, should be optional"
116+
- Note breaking change impact with reasoning
117+
- **Each fix MUST include evidence from code usage**
118+
119+
d. **Prioritize recommendations**:
120+
- NEW issues first (must fix)
121+
- Group PRE-EXISTING by NON-BREAKING vs BREAKING
122+
123+
5. **Present actionable report directly to user**:
124+
- **IMPORTANT:** Output the full comprehensive analysis in the conversation (not just to a temp file)
125+
- Summary: False positives filtered, NEW count, PRE-EXISTING count
126+
- Group issues by file and fix type
127+
- Provide code snippets ready to apply (current code → fixed code)
128+
- **DO NOT include "Next Steps" or "Conclusion" sections** - just present the analysis
129+
130+
**Report Structure:**
131+
```
132+
# API Lint Diff Analysis Report
133+
134+
**Generated:** [date]
135+
**Baseline:** main branch
136+
**Current:** [branch name]
137+
**Status:** [status icon and message based on logic below]
138+
139+
**Status Logic:**
140+
- ✅ PASSED: 0 real issues (after filtering false positives)
141+
- ⚠️ WARN: 0 new issues but has pre-existing issues
142+
- ❌ FAIL: Has new issues that must be fixed
143+
144+
## Executive Summary
145+
- Total issues: X
146+
- False positives (IGNORED): Y
147+
- Real issues (NEED FIXING): Z
148+
- NEW issues: N
149+
- PRE-EXISTING issues: P
150+
151+
## REAL ISSUES - FIXES NEEDED (Z issues)
152+
153+
### Category 1: [Issue Type] (N issues) - [BREAKING/NON-BREAKING]
154+
155+
#### File: [filename]
156+
157+
**[Issue #]. Line X - [Field Name]**
158+
```go
159+
// CURRENT:
160+
[current code]
161+
162+
// FIX:
163+
[fixed code]
164+
```
165+
**Usage Analysis:**
166+
- Found N occurrences in codebase
167+
- [Specific usage example 1]: path/file.go:123
168+
- [Specific usage example 2]: path/file.go:456
169+
- Pattern: [always set / sometimes set / checked for zero / etc.]
170+
171+
**Why:** [Recommendation based on usage analysis with evidence]
172+
**Breaking:** [YES/NO] ([detailed reason with impact])
173+
174+
[Repeat for all issues]
175+
176+
## Summary of Breaking Changes
177+
[Table of breaking changes if any]
178+
```
179+
180+
## What This Command Does
181+
182+
1. **Scopes to API directory only**: Analyzes only Go files under `api/`
183+
2. **Diff-aware analysis**: Compares current branch against `main` to identify:
184+
- **NEW issues**: Appear only in current branch in changed files (FAIL)
185+
- **PRE-EXISTING issues**: Already exist on main or in unchanged files (WARN)
186+
3. **False positive filtering**: Ignores known CRD scaffold patterns:
187+
- `optionalfields` on Status field (Status is never a pointer in K8s)
188+
- `requiredfields` on Spec field (Spec is never a pointer in K8s)
189+
- `conditions` on `[]metav1.Condition` (patch semantics already correct)
190+
4. **Breaking change analysis**: For each issue, assesses if the fix would be breaking
191+
5. **Temporary configuration**: Never modifies tracked files
192+
193+
## Usage
194+
195+
```bash
196+
/api-lint-diff
197+
```
198+
199+
## Output Format
200+
201+
The command provides:
202+
203+
1. **Summary**
204+
- Count of NEW issues
205+
- Count of PRE-EXISTING issues
206+
- List of changed files under `api/`
207+
208+
2. **NEW Issues** (cause failure)
209+
- File + line number
210+
- Linter name and message
211+
- Explanation of the issue
212+
- How to fix it
213+
- Breaking-change assessment
214+
215+
3. **PRE-EXISTING Issues** (warnings only)
216+
- Same detailed structure as NEW issues
217+
- Clearly labeled as pre-existing with `[WARNING]` prefix
218+
- Includes context, analysis, recommendations, and breaking-change assessment
219+
220+
## Exit Codes
221+
222+
- `0`: Success (no NEW issues, or only PRE-EXISTING/ignored issues)
223+
- `1`: Failure (NEW issues found that need fixing)
224+
225+
## Implementation Steps
226+
227+
When executing this command, the script automatically:
228+
229+
1. **Builds custom golangci-lint with kube-api-linter plugin**
230+
- Finds base golangci-lint from bingo or bin/
231+
- Extracts version from `.bingo/Variables.mk`
232+
- Creates temporary `.custom-gcl.yml` config with version placeholder
233+
- Builds custom binary with kube-api-linter plugin using `golangci-lint custom`
234+
- All files created in temporary directory
235+
236+
2. **Configures linter**
237+
- Generates temporary `.golangci.yaml` config inline (no templates needed)
238+
- Configures kube-api-linter with WhenRequired pointer preference
239+
- Focuses only on `api/` directory
240+
- Excludes generated files (zz_generated)
241+
242+
3. **Analyzes current and baseline**
243+
- Runs custom linter on current branch
244+
- Creates temporary git worktree for `main` branch
245+
- Runs linter on baseline branch
246+
- Parses and categorizes results comparing file + linter + message (ignores line number changes)
247+
248+
4. **Filters false positives**
249+
- Silently ignores `optionalfields` on Status fields
250+
- Silently ignores `requiredfields` on Spec fields
251+
- Silently ignores `conditions` markers on `[]metav1.Condition`
252+
- No false positives shown in output
253+
254+
5. **Presents results**
255+
- Summary: NEW count, PRE-EXISTING count, changed files
256+
- NEW issues: Full analysis with `[ERROR]` prefix in red (causes exit code 1)
257+
- PRE-EXISTING issues: Full analysis with `[WARNING]` prefix in yellow (no failure)
258+
- Each issue includes: file location, code context, analysis, recommendations, breaking-change assessment
259+
260+
6. **Cleanup**
261+
- Removes all temporary files and directories
262+
- Removes git worktree
263+
- Leaves repository in clean state
264+
265+
## Requirements
266+
267+
- golangci-lint v2.7.2+ (installed via bingo or available in bin/)
268+
- Git repository with `main` branch as baseline
269+
- Go toolchain (for building custom golangci-lint)
270+
- Internet connection (to download kube-api-linter module during custom build)
271+
272+
## Related Documentation
273+
274+
- [Kubernetes API Conventions](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md)
275+
- [kube-api-linter](https://github.com/kubernetes/kubernetes/tree/master/staging/src/k8s.io/code-generator/cmd/kube-api-linter)
276+
- AGENTS.md in this repository for understanding operator patterns
277+
278+
## Notes
279+
280+
- This command is safe to run at any time - it makes no persistent changes
281+
- False positives are based on standard Kubernetes CRD scaffolding patterns
282+
- Analysis considers operator-specific patterns (see AGENTS.md)
283+
- Breaking change assessments follow Kubernetes API compatibility rules

0 commit comments

Comments
 (0)