Skip to content

Conversation

@camilamacedo86
Copy link
Contributor

@camilamacedo86 camilamacedo86 commented Dec 19, 2025

What This Command Does

  1. Scopes to API directory only: Analyzes only Go files under api/
  2. Diff-aware analysis: Compares current branch against main to identify:
    • NEW issues: Appear only in current branch in changed files (FAIL)
    • PRE-EXISTING issues: Already exist on main or in unchanged files (WARN)
  3. False positive filtering: Ignores known CRD scaffold patterns:
  4. Breaking change analysis: For each issue, assesses if the fix would be breaking
  5. Temporary configuration: Never modifies tracked files

Why it matters

  • We ship CRDs as an API contract. New kube API convention violations can become breaking changes once released, making them harder (or impossible) to fix later without migrations.
  • This diff-aware lint check prevents new issues from slipping in while not blocking on legacy debt, keeping our API surface consistent with Kubernetes expectations.
  • Any issues not identified early can delay feature gate promotion and force late-stage fixes that are riskier and potentially breaking.

EXAMPLE - Result

  API Lint Diff Analysis Report                                                                         
                                                                                                        
  Generated: 2025-12-19                                                                                 
  Baseline: main branch                                                                                 
  Current: command-ai                                                                                   
  Status: ⚠️ WARN - 0 new issues but has pre-existing issues                                            
                                                                                                        
  Executive Summary                                                                                     
                                                                                                        
  - Total issues reported: 47                                                                           
  - False positives (IGNORED): 6                                                                        
  - Real issues (NEED FIXING): 41                                                                       
  - NEW issues: 0                                                                                       
  - PRE-EXISTING issues: 41                                                                             
                                                                                                        
  ---                                                                                                   
  FALSE POSITIVES - IGNORED (6 issues)                                                                  
                                                                                                        
  These are standard Kubernetes CRD scaffolding patterns that kube-api-linter incorrectly flags:        
                                                                                                        
  1. optionalfields on Status fields (3 occurrences)                                                    
                                                                                                        
  - api/v1/clustercatalog_types.go:73 - ClusterCatalog.Status                                           
  - api/v1/clusterextension_types.go:548 - ClusterExtension.Status                                      
  - api/v1/clusterextensionrevision_types.go:227 - ClusterExtensionRevision.Status                      
                                                                                                        
  Why FALSE POSITIVE: Status is NEVER a pointer in Kubernetes APIs. Works correctly with omitempty or omitzero tags. Validation incompleteness is expected - Status is controller-managed, not user-provided.
                                                                                                        
  2. nonpointerstructs on Spec fields (2 occurrences)                                                   
                                                                                                        
  - api/v1/clusterextension_types.go:544 - ClusterExtension.Spec                                        
  - api/v1/clusterextensionrevision_types.go:223 - ClusterExtensionRevision.Spec                        
                                                                                                        
  Why FALSE POSITIVE: Spec is NEVER a pointer in Kubernetes APIs. This is standard scaffolding.         
                                                                                                        
  3. conditions markers on metav1.Condition (3 occurrences)                                             
                                                                                                        
  - api/v1/clustercatalog_types.go:175 - ClusterCatalogStatus.Conditions                                
  - api/v1/clusterextension_types.go:501 - ClusterExtensionStatus.Conditions                            
  - api/v1/clusterextensionrevision_types.go:201 - ClusterExtensionRevisionStatus.Conditions            
                                                                                                        
  Why FALSE POSITIVE: metav1.Condition already handles patch semantics correctly. Adding patchStrategy=merge, patchMergeKey=type markers is redundant for this standard Kubernetes type.
                                                                                                        
  4. conditions markers on nested Conditions (1 occurrence)                                             
                                                                                                        
  - api/v1/clusterextension_types.go:472 - RevisionStatus.Conditions                                    
                                                                                                        
  Why FALSE POSITIVE: Same as above - standard metav1.Condition type.                                   
                                                                                                        
  ---                                                                                                   
  REAL ISSUES - FIXES NEEDED (41 issues)                                                                
                                                                                                        
  Category 1: Deprecated Marker Replacements (14 issues) - NON-BREAKING                                 
                                                                                                        
  These use the old +kubebuilder:validation:Required marker that should be replaced with +required:     
                                                                                                        
  File: api/v1/clustercatalog_types.go                                                                  
                                                                                                        
  Issue 1. Line 66 - ClusterCatalog.Spec                                                                
  // CURRENT:                                                                                           
  // spec is required and is the desired state of the ClusterCatalog.                                   
  // +kubebuilder:validation:Required                                                                   
  Spec ClusterCatalogSpec `json:"spec"`                                                                 
                                                                                                        
  // FIX:                                                                                               
  // spec is required and is the desired state of the ClusterCatalog.                                   
  // +required                                                                                          
  Spec ClusterCatalogSpec `json:"spec"`                                                                 
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  Issue 2. Line 109 - ClusterCatalogSpec.Source                                                         
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  Source CatalogSource `json:"source"`                                                                  
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  Source CatalogSource `json:"source"`                                                                  
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  Issue 3. Line 206 - ClusterCatalogURLs.Base                                                           
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  Base string `json:"base"`                                                                             
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  Base string `json:"base"`                                                                             
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  Issue 4. Line 224 - CatalogSource.Type                                                                
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  Type SourceType `json:"type"`                                                                         
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  Type SourceType `json:"type"`                                                                         
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  Issue 5. Line 245 - ResolvedCatalogSource.Type                                                        
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  Type SourceType `json:"type"`                                                                         
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  Type SourceType `json:"type"`                                                                         
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  Issue 6. Line 264 - ResolvedImageSource.Ref                                                           
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  Ref string `json:"ref"`                                                                               
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  Ref string `json:"ref"`                                                                               
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  Issue 7. Line 320 - ImageSource.Ref                                                                   
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  Ref string `json:"ref"`                                                                               
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  Ref string `json:"ref"`                                                                               
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  File: api/v1/clusterextension_types.go                                                                
                                                                                                        
  Issue 8. Line 67 - ClusterExtensionSpec.Namespace                                                     
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  Namespace string `json:"namespace"`                                                                   
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  Namespace string `json:"namespace"`                                                                   
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  Issue 9. Line 76 - ClusterExtensionSpec.ServiceAccount                                                
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  ServiceAccount ServiceAccountReference `json:"serviceAccount"`                                        
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  ServiceAccount ServiceAccountReference `json:"serviceAccount"`                                        
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  Issue 10. Line 92 - ClusterExtensionSpec.Source                                                       
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  Source SourceConfig `json:"source"`                                                                   
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  Source SourceConfig `json:"source"`                                                                   
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  Issue 11. Line 131 - SourceConfig.SourceType                                                          
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  SourceType string `json:"sourceType"`                                                                 
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  SourceType string `json:"sourceType"`                                                                 
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  Issue 12. Line 171 - ClusterExtensionConfig.ConfigType                                                
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  ConfigType string `json:"configType"`                                                                 
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  ConfigType string `json:"configType"`                                                                 
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  Issue 13. Line 214 - CatalogFilter.PackageName                                                        
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  PackageName string `json:"packageName"`                                                               
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  PackageName string `json:"packageName"`                                                               
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  Issue 14. Line 397 - ServiceAccountReference.Name                                                     
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  Name string `json:"name"`                                                                             
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  Name string `json:"name"`                                                                             
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  Issue 15. Line 425 - CRDUpgradeSafetyPreflightConfig.Enforcement                                      
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  Enforcement CRDUpgradeSafetyEnforcement `json:"enforcement"`                                          
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  Enforcement CRDUpgradeSafetyEnforcement `json:"enforcement"`                                          
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  Issue 16. Line 450 - BundleMetadata.Name                                                              
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  Name string `json:"name"`                                                                             
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  Name string `json:"name"`                                                                             
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  Issue 17. Line 457 - BundleMetadata.Version                                                           
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  Version string `json:"version"`                                                                       
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  Version string `json:"version"`                                                                       
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  Issue 18. Line 525 - ClusterExtensionInstallStatus.Bundle                                             
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  Bundle BundleMetadata `json:"bundle"`                                                                 
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  Bundle BundleMetadata `json:"bundle"`                                                                 
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  File: api/v1/clusterextensionrevision_types.go                                                        
                                                                                                        
  Issue 19. Line 67 - ClusterExtensionRevisionSpec.Revision                                             
  // CURRENT:                                                                                           
  // +kubebuilder:validation:Required                                                                   
  Revision int64 `json:"revision"`                                                                      
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  Revision int64 `json:"revision"`                                                                      
  Breaking: NO - Simple marker replacement                                                              
                                                                                                        
  ---                                                                                                   
  Category 2: Missing Optional/Required Markers (8 issues) - NON-BREAKING                               
                                                                                                        
  Fields that need to be marked as either +optional or +required:                                       
                                                                                                        
  File: api/v1/clustercatalog_types.go                                                                  
                                                                                                        
  Issue 20. Line 61 - ClusterCatalog.ObjectMeta                                                         
  // CURRENT:                                                                                           
  metav1.ObjectMeta `json:"metadata,omitempty"`                                                         
                                                                                                        
  // FIX:                                                                                               
  // +optional                                                                                          
  metav1.ObjectMeta `json:"metadata,omitempty"`                                                         
  Usage Analysis:                                                                                       
  - Standard Kubernetes embedded ObjectMeta                                                             
  - Always optional in resource definitions                                                             
                                                                                                        
  Why: Kubernetes convention - embedded ObjectMeta is always optional                                   
  Breaking: NO                                                                                          
                                                                                                        
  Issue 21. Line 248 - ResolvedCatalogSource.Image                                                      
  // CURRENT:                                                                                           
  Image *ResolvedImageSource `json:"image,omitempty"`                                                   
                                                                                                        
  // FIX:                                                                                               
  // +optional                                                                                          
  Image *ResolvedImageSource `json:"image,omitempty"`                                                   
  Usage Analysis:                                                                                       
  - Found 30+ occurrences in codebase                                                                   
  - Used in controllers: catalog.Status.ResolvedSource.Image.Ref                                        
  - Always checked for nil: if catalog.Status.ResolvedSource.Image == nil                               
  - Pattern: Status field, controller-managed                                                           
                                                                                                        
  Why: Field is a pointer with omitempty, indicating it's optional. Usage confirms it can be nil.       
  Breaking: NO                                                                                          
                                                                                                        
  File: api/v1/clusterextension_types.go                                                                
                                                                                                        
  Issue 22. Line 409 - PreflightConfig.CRDUpgradeSafety                                                 
  // CURRENT:                                                                                           
  CRDUpgradeSafety *CRDUpgradeSafetyPreflightConfig `json:"crdUpgradeSafety,omitempty"`                 
                                                                                                        
  // FIX:                                                                                               
  // +optional                                                                                          
  CRDUpgradeSafety *CRDUpgradeSafetyPreflightConfig `json:"crdUpgradeSafety,omitempty"`                 
  Usage Analysis:                                                                                       
  - Pointer with omitempty indicates optional                                                           
  - Preflight checks configuration                                                                      
                                                                                                        
  Why: Pointer with omitempty tag indicates optional field                                              
  Breaking: NO                                                                                          
                                                                                                        
  Issue 23. Line 463 - RevisionStatus.Name                                                              
  // CURRENT:                                                                                           
  Name string `json:"name"`                                                                             
                                                                                                        
  // FIX:                                                                                               
  // +required                                                                                          
  Name string `json:"name"`                                                                             
  Usage Analysis:                                                                                       
  - Found in: controllers/boxcutter_reconcile_steps.go:129 - ext.Status.ActiveRevisions = []ocv1.RevisionStatus{{Name: i.RevisionName}}
  - Always set when creating RevisionStatus                                                             
  - Never empty in observed usage                                                                       
                                                                                                        
  Why: Field is always set in code and has no omitempty tag, indicating it's required                   
  Breaking: NO                                                                                          
                                                                                                        
  Issue 24. Line 540 - ClusterExtension.ObjectMeta (missing godoc)                                      
  // CURRENT:                                                                                           
  metav1.ObjectMeta `json:"metadata,omitempty"`                                                         
                                                                                                        
  // FIX:                                                                                               
  // +optional                                                                                          
  metav1.ObjectMeta `json:"metadata,omitempty"`                                                         
  Why: Standard Kubernetes embedded ObjectMeta is always optional                                       
  Breaking: NO                                                                                          
                                                                                                        
  File: api/v1/clusterextensionrevision_types.go                                                        
                                                                                                        
  Issue 25. Line 55 - ClusterExtensionRevisionSpec.LifecycleState                                       
  // CURRENT:                                                                                           
  // +kubebuilder:default="Active"                                                                      
  // +kubebuilder:validation:Enum=Active;Archived                                                       
  // +kubebuilder:validation:XValidation:rule="oldSelf == 'Active' || oldSelf == 'Archived' && oldSelf == self", message="cannot un-archive"
  LifecycleState ClusterExtensionRevisionLifecycleState `json:"lifecycleState,omitempty"`               
                                                                                                        
  // FIX:                                                                                               
  // +kubebuilder:default="Active"                                                                      
  // +kubebuilder:validation:Enum=Active;Archived                                                       
  // +kubebuilder:validation:XValidation:rule="oldSelf == 'Active' || oldSelf == 'Archived' && oldSelf == self", message="cannot un-archive"
  // +optional                                                                                          
  LifecycleState ClusterExtensionRevisionLifecycleState `json:"lifecycleState,omitempty"`               
  Why: Has default value and omitempty tag, indicating it's optional                                    
  Breaking: NO                                                                                          
                                                                                                        
  Issue 26. Line 119 - ClusterExtensionRevisionPhase.Name                                               
  // CURRENT:                                                                                           
  // +kubebuilder:validation:MaxLength=63                                                               
  // +kubebuilder:validation:Pattern=`^[a-z]([-a-z0-9]*[a-z0-9])?$`                                     
  Name string `json:"name"`                                                                             
                                                                                                        
  // FIX:                                                                                               
  // +kubebuilder:validation:MaxLength=63                                                               
  // +kubebuilder:validation:Pattern=`^[a-z]([-a-z0-9]*[a-z0-9])?$`                                     
  // +required                                                                                          
  Name string `json:"name"`                                                                             
  Usage Analysis:                                                                                       
  - Found in tests creating phases: Phases: []ClusterExtensionRevisionPhase{{Name: "test"}}             
  - Always set when creating phases                                                                     
  - Documentation states "name is a required identifier for this phase"                                 
                                                                                                        
  Why: Documentation explicitly states it's required, always set in code                                
  Breaking: NO                                                                                          
                                                                                                        
  Issue 27. Line 136 - ClusterExtensionRevisionObject.Object                                            
  // CURRENT:                                                                                           
  // +kubebuilder:validation:EmbeddedResource                                                           
  // +kubebuilder:pruning:PreserveUnknownFields                                                         
  Object unstructured.Unstructured `json:"object"`                                                      
                                                                                                        
  // FIX:                                                                                               
  // +kubebuilder:validation:EmbeddedResource                                                           
  // +kubebuilder:pruning:PreserveUnknownFields                                                         
  // +required                                                                                          
  Object unstructured.Unstructured `json:"object"`                                                      
  Usage Analysis:                                                                                       
  - Found in controllers iterating objects: for _, obj := range phase.Objects                           
  - Documentation states "object is a required embedded Kubernetes object"                              
  - Always set when creating ClusterExtensionRevisionObject                                             
                                                                                                        
  Why: Documentation explicitly states it's required                                                    
  Breaking: NO                                                                                          
                                                                                                        
  Issue 28. Line 219 - ClusterExtensionRevision.ObjectMeta (missing godoc)                              
  // CURRENT:                                                                                           
  metav1.ObjectMeta `json:"metadata,omitempty"`                                                         
                                                                                                        
  // FIX:                                                                                               
  // +optional                                                                                          
  metav1.ObjectMeta `json:"metadata,omitempty"`                                                         
  Why: Standard Kubernetes embedded ObjectMeta is always optional                                       
  Breaking: NO                                                                                          
                                                                                                        
  ---                                                                                                   
  Category 3: Integer Type Issues (1 issue) - BREAKING                                                  
                                                                                                        
  Issue 29. Line 328 - ImageSource.PollIntervalMinutes                                                  
                                                                                                        
  File: api/v1/clustercatalog_types.go                                                                  
                                                                                                        
  // CURRENT:                                                                                           
  // pollIntervalMinutes is the interval in minutes at which the image source should be polled for new content.
  //                                                                                                    
  // The minimum value is 1.                                                                            
  // If the value is 0 or unspecified, the default polling interval is 5 minutes.                       
  //                                                                                                    
  // +kubebuilder:validation:Minimum:=0                                                                 
  // +optional                                                                                          
  PollIntervalMinutes *int `json:"pollIntervalMinutes,omitempty"`                                       
                                                                                                        
  // FIX:                                                                                               
  // pollIntervalMinutes is the interval in minutes at which the image source should be polled for new content.
  //                                                                                                    
  // The minimum value is 1.                                                                            
  // If the value is 0 or unspecified, the default polling interval is 5 minutes.                       
  //                                                                                                    
  // +kubebuilder:validation:Minimum:=0                                                                 
  // +optional                                                                                          
  PollIntervalMinutes *int32 `json:"pollIntervalMinutes,omitempty"`                                     
                                                                                                        
  Usage Analysis:                                                                                       
  - Found 17 occurrences in codebase                                                                    
  - Used in controller: time.Duration(*catalog.Spec.Source.Image.PollIntervalMinutes) * time.Minute     
  - Tests use: PollIntervalMinutes: ptr.To(1), ptr.To(5), ptr.To(7)                                     
  - All values are small positive integers (1-7 in tests)                                               
                                                                                                        
  Why: Kubernetes API conventions require int32 or int64 for pointer integers. Since this represents minutes and has validation minimum of 0, int32 is appropriate (max value 2,147,483,647 minutes = ~4,085 years).
                                                                                                        
  Breaking: YES - Field type change from *int to *int32 is a breaking change. Clients using the Go types will need to update their code.
                                                                                                        
  Impact: Moderate - requires updating all code that uses this field:                                   
  - internal/catalogd/controllers/core/clustercatalog_controller.go (4 occurrences)                     
  - Tests (10+ occurrences)                                                                             
                                                                                                        
  ---                                                                                                   
  Category 4: Field Validation Issues (5 issues) - NON-BREAKING to BREAKING                             
                                                                                                        
  File: api/v1/clustercatalog_types.go                                                                  
                                                                                                        
  Issue 30. Line 131 - ClusterCatalogSpec.Priority                                                      
  // CURRENT:                                                                                           
  // +kubebuilder:default:=0                                                                            
  // +kubebuilder:validation:minimum:=-2147483648                                                       
  // +kubebuilder:validation:maximum:=2147483647                                                        
  // +optional                                                                                          
  Priority int32 `json:"priority"`                                                                      
                                                                                                        
  // FIX:                                                                                               
  // +kubebuilder:default:=0                                                                            
  // +kubebuilder:validation:minimum:=-2147483648                                                       
  // +kubebuilder:validation:maximum:=2147483647                                                        
  // +optional                                                                                          
  Priority int32 `json:"priority,omitempty"`                                                            
                                                                                                        
  Usage Analysis:                                                                                       
  - Found 12 occurrences in codebase                                                                    
  - Used in resolve/catalog.go:163 as direct int32: foundBundle{&thisBundle, cat.GetName(), cat.Spec.Priority}
  - Tests use direct values: Priority: 1, Priority: 0                                                   
  - Has default value of 0                                                                              
  - Never checked for "not set" vs "set to 0"                                                           
                                                                                                        
  Why: Field has a default value and is optional, but lacks omitempty tag. The zero value (0) is semantically valid and is the default. However, since code never distinguishes between "not set" and "set to 0", and it has a default, it should have omitempty for proper marshaling.
                                                                                                        
  Breaking: NO - Adding omitempty doesn't change API behavior when default is 0                         
                                                                                                        
  Issue 31. Line 178 - ClusterCatalogStatus.ResolvedSource                                              
  // CURRENT:                                                                                           
  // +optional                                                                                          
  ResolvedSource *ResolvedCatalogSource `json:"resolvedSource,omitempty"`                               
                                                                                                        
  // FIX:                                                                                               
  // +optional                                                                                          
  ResolvedSource *ResolvedCatalogSource `json:"resolvedSource,omitzero"`                                
                                                                                                        
  Usage Analysis:                                                                                       
  - Found 30+ occurrences                                                                               
  - Controllers set to nil: status.ResolvedSource = nil                                                 
  - Controllers check for nil: if catalog.Status.ResolvedSource == nil                                  
  - Controller sets value: status.ResolvedSource = &ocv1.ResolvedCatalogSource{...}                     
                                                                                                        
  Why: The linter states "field does not allow the zero value. It must have the omitzero tag." This is a Status field that controllers explicitly set to nil to indicate "not resolved yet". The omitzero tag is more appropriate for this pattern.
                                                                                                        
  Breaking: NO - Behavioral change in JSON marshaling only, not in Go API                               
                                                                                                        
  File: api/v1/clusterextension_types.go                                                                
                                                                                                        
  Issue 32. Line 110 - ClusterExtensionSpec.Config                                                      
  // CURRENT:                                                                                           
  // +optional                                                                                          
  Config *ClusterExtensionConfig `json:"config,omitempty"`                                              
                                                                                                        
  // FIX:                                                                                               
  // +optional                                                                                          
  Config *ClusterExtensionConfig `json:"config,omitzero"`                                               
                                                                                                        
  Usage Analysis:                                                                                       
  - Found 5 occurrences in codebase                                                                     
  - Used in provider.go:141: if ext.Spec.Config != nil                                                  
  - Pattern: Checked for nil before access                                                              
  - Zero value (nil) is valid and meaningful (no config)                                                
                                                                                                        
  Why: Linter states field does not allow zero value, should use omitzero. However, nil is a valid state here. The current omitempty is actually correct for a pointer that can be nil. This might be a linter false positive, but using omitzero is more semantically correct for structs.
                                                                                                        
  Breaking: NO - Behavioral change in JSON marshaling only                                              
                                                                                                        
  Issue 33. Line 294 - CatalogFilter.Version                                                            
  // CURRENT:                                                                                           
  // version is an optional semver constraint (a specific version or range of versions). When not specified, the latest version will be used.
  //                                                                                                    
  // For information on the semver constraint syntax, please see https://github.com/blang/semver#ranges.
  //                                                                                                    
  // +optional                                                                                          
  Version string `json:"version,omitempty"`                                                             
                                                                                                        
  // FIX: No change recommended - keep as-is                                                            
                                                                                                        
  Usage Analysis:                                                                                       
  - Found 50+ occurrences in codebase                                                                   
  - Used in resolve/catalog.go:45: versionRange := ext.Spec.Source.Catalog.Version                      
  - Used in catalog.go:232: if rei.Version != "" {                                                      
  - Tests use: Version: "1.0.1"                                                                         
  - Pattern: Empty string means "any version", non-empty means specific version/range                   
                                                                                                        
  Why: The linter suggests making this a pointer or adding validation. However, the empty string "" is semantically valid (means "any version"). The current implementation is correct - empty string is a valid use case, and the code handles it appropriately.
                                                                                                        
  Recommendation: KEEP AS-IS. This is working correctly. The linter's suggestion to make it a pointer would add unnecessary complexity.
                                                                                                        
  Breaking: N/A - No change recommended                                                                 
                                                                                                        
  ---                                                                                                   
  Category 5: Server-Side Apply Issues (1 issue) - NON-BREAKING                                         
                                                                                                        
  Issue 34. Line 338 - CatalogFilter.Channels                                                           
                                                                                                        
  File: api/v1/clusterextension_types.go                                                                
                                                                                                        
  // CURRENT:                                                                                           
  // channels is an optional set of channel names that the bundle must be present in.                   
  //                                                                                                    
  // When specified, a bundle must belong to at least one of the specified channels.                    
  // When not specified, bundles are searched across all channels in the package.                       
  //                                                                                                    
  // +listType=set                                                                                      
  // +optional                                                                                          
  Channels []string `json:"channels,omitempty"`                                                         
                                                                                                        
  // FIX:                                                                                               
  // channels is an optional set of channel names that the bundle must be present in.                   
  //                                                                                                    
  // When specified, a bundle must belong to at least one of the specified channels.                    
  // When not specified, bundles are searched across all channels in the package.                       
  //                                                                                                    
  // +listType=set                                                                                      
  // +optional                                                                                          
  Channels []string `json:"channels,omitempty"`                                                         
                                                                                                        
  NOTE: The field already has +listType=set marker. The linter error appears to be incorrect or outdated.
                                                                                                        
  Usage Analysis:                                                                                       
  - Found 30+ occurrences                                                                               
  - Used in resolve/catalog.go:46: channels := ext.Spec.Source.Catalog.Channels                         
  - Pattern: Array of strings, can be empty or have multiple values                                     
  - Already has +listType=set marker                                                                    
                                                                                                        
  Why: Field already has the required listType=set marker. This appears to be a linter bug or the linter is not recognizing the marker properly.
                                                                                                        
  Recommendation: VERIFY the marker is present (it is), then this can be ignored or investigate linter version.
                                                                                                        
  Breaking: NO - Marker already present                                                                 
                                                                                                        
  ---                                                                                                   
  Category 6: Array of Structs Issues (2 issues) - NON-BREAKING but requires schema changes             
                                                                                                        
  These arrays of structs have no required fields, which can lead to ambiguous YAML configurations:     
                                                                                                        
  File: api/v1/clusterextension_types.go                                                                
                                                                                                        
  Issue 35. Line 514 - ClusterExtensionStatus.ActiveRevisions                                           
  // CURRENT:                                                                                           
  // +listType=map                                                                                      
  // +listMapKey=name                                                                                   
  // +optional                                                                                          
  ActiveRevisions []RevisionStatus `json:"activeRevisions,omitempty"`                                   
                                                                                                        
  // WHERE RevisionStatus is:                                                                           
  type RevisionStatus struct {                                                                          
      Name string `json:"name"`                                                                         
      // ... other fields                                                                               
      Conditions []metav1.Condition `json:"conditions,omitempty"`                                       
  }                                                                                                     
                                                                                                        
  // FIX:                                                                                               
  // Mark RevisionStatus.Name as required (already addressed in Issue 23)                               
                                                                                                        
  Usage Analysis:                                                                                       
  - Found in controllers/boxcutter_reconcile_steps.go:129: ext.Status.ActiveRevisions = []ocv1.RevisionStatus{{Name: i.RevisionName}}
  - Name is always set when creating RevisionStatus                                                     
  - listMapKey is "name", so Name must be unique and present                                            
                                                                                                        
  Why: The listMapKey is "name", which requires that field to be present and unique. This is already being addressed in Issue 23 by marking Name as required.
                                                                                                        
  Breaking: NO - Marking Name as required (Issue 23) resolves this                                      
                                                                                                        
  File: api/v1/clusterextensionrevision_types.go                                                        
                                                                                                        
  Issue 36. Line 89 - ClusterExtensionRevisionSpec.Phases                                               
  // CURRENT:                                                                                           
  // +listType=map                                                                                      
  // +listMapKey=name                                                                                   
  // +optional                                                                                          
  Phases []ClusterExtensionRevisionPhase `json:"phases,omitempty"`                                      
                                                                                                        
  // FIX:                                                                                               
  // Mark ClusterExtensionRevisionPhase.Name as required (already addressed in Issue 26)                
                                                                                                        
  Why: The listMapKey is "name", requiring that field to be present. Already addressed in Issue 26.     
                                                                                                        
  Breaking: NO - Marking Name as required (Issue 26) resolves this                                      
                                                                                                        
  Issue 37. Line 124 - ClusterExtensionRevisionPhase.Objects                                            
  // CURRENT:                                                                                           
  Objects []ClusterExtensionRevisionObject `json:"objects"`                                             
                                                                                                        
  // WHERE ClusterExtensionRevisionObject is:                                                           
  type ClusterExtensionRevisionObject struct {                                                          
      Object unstructured.Unstructured `json:"object"`                                                  
      CollisionProtection CollisionProtection `json:"collisionProtection,omitempty"`                    
  }                                                                                                     
                                                                                                        
  // FIX:                                                                                               
  // Mark ClusterExtensionRevisionObject.Object as required (already addressed in Issue 27)             
  // Add listType marker:                                                                               
                                                                                                        
  // objects is a required list of all Kubernetes objects that belong to this phase.                    
  //                                                                                                    
  // All objects in this list are applied to the cluster in no particular order.                        
  // +listType=atomic                                                                                   
  Objects []ClusterExtensionRevisionObject `json:"objects"`                                             
                                                                                                        
  Usage Analysis:                                                                                       
  - Found in controllers iterating: for _, obj := range phase.Objects                                   
  - Object field is always set (addressed in Issue 27)                                                  
  - Order doesn't matter, atomic updates make sense                                                     
                                                                                                        
  Why: Objects array needs a listType marker for Server-Side Apply. Since order doesn't matter and objects are treated as a unit, +listType=atomic is appropriate.
                                                                                                        
  Breaking: NO - Adding listType marker is non-breaking                                                 
                                                                                                        
  ---                                                                                                   
  Category 7: Comment Style Issues (2 issues) - NON-BREAKING                                            
                                                                                                        
  Issue 38. Line 477 - ClusterExtensionStatus.Conditions (commentstart)                                 
                                                                                                        
  File: api/v1/clusterextension_types.go                                                                
                                                                                                        
  // CURRENT:                                                                                           
  // Conditions holds a list of conditions describing the state of the ClusterExtension.                
  //                                                                                                    
  // The current condition types are Installed, Progressing, and Deprecated.                            
  //                                                                                                    
  // ... (more documentation)                                                                           
  Conditions []metav1.Condition `json:"conditions,omitempty"`                                           
                                                                                                        
  // FIX:                                                                                               
  // conditions holds a list of conditions describing the state of the ClusterExtension.                
  //                                                                                                    
  // The current condition types are Installed, Progressing, and Deprecated.                            
  //                                                                                                    
  // ... (more documentation)                                                                           
  Conditions []metav1.Condition `json:"conditions,omitempty"`                                           
                                                                                                        
  Why: Godoc convention requires field comments to start with the lowercase field name                  
  Breaking: NO - Documentation only                                                                     
                                                                                                        
  ---                                                                                                   
  Summary of Breaking Changes                                                                           
                                                                                                        
  | Issue | File                        | Field                           | Change Type   | Impact                                                    |
  |-------|-----------------------------|---------------------------------|---------------|-----------------------------------------------------------|
  | 29    | clustercatalog_types.go:328 | ImageSource.PollIntervalMinutes | *int → *int32 | Moderate - requires code updates in controllers and tests |
                                                                                                        
  All other issues (40 out of 41) are NON-BREAKING changes:                                             
  - 14 deprecated marker replacements                                                                   
  - 8 missing optional/required markers                                                                 
  - 5 validation tag improvements                                                                       
  - 1 SSA marker (already present)                                                                      
  - 2 array struct issues (resolved by other fixes)                                                     
  - 2 documentation style fixes                                                                         
  - 6 false positives (IGNORED)                                                                         
  - 3 keep as-is recommendations   

Copilot AI review requested due to automatic review settings December 19, 2025 16:45
@camilamacedo86 camilamacedo86 requested a review from a team as a code owner December 19, 2025 16:45
@openshift-ci
Copy link

openshift-ci bot commented Dec 19, 2025

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign grokspawn for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@netlify
Copy link

netlify bot commented Dec 19, 2025

Deploy Preview for olmv1 ready!

Name Link
🔨 Latest commit a625380
🔍 Latest deploy log https://app.netlify.com/projects/olmv1/deploys/694583f7e27e7d00082b9a20
😎 Deploy Preview https://deploy-preview-2399--olmv1.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new API linting workflow that validates Kubernetes API changes using kube-api-linter with diff-aware analysis. The implementation distinguishes between newly introduced issues and pre-existing problems, helping developers focus on fixing only the issues they've introduced.

Key Changes:

  • Adds a shell script that builds a custom golangci-lint with kube-api-linter plugin and performs diff-based analysis
  • Provides Claude AI integration with detailed instructions for intelligent issue analysis and false positive filtering
  • Implements git worktree-based comparison between current branch and main branch

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
hack/api-lint-diff/run.sh Shell script that orchestrates the linting workflow: builds custom golangci-lint, runs linter on current and baseline branches, categorizes issues as NEW or PRE-EXISTING, and generates a report
.claude/commands/api-lint-diff.md Documentation for Claude AI providing instructions for executing the script, filtering false positives, analyzing code usage patterns, and generating comprehensive reports
Comments suppressed due to low confidence (2)

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

  • The documentation states that false positives are "silently ignored" and filtered out, but the shell script doesn't actually implement any false positive filtering logic. The script only runs the linter and categorizes issues as NEW or PRE-EXISTING. The false positive filtering is expected to be done by Claude AI when processing the script's output, not by the script itself. This creates a discrepancy between what the documentation says the command does versus what the script actually does. Consider either implementing the filtering in the script or clarifying in the documentation that the filtering is performed by AI analysis, not by the script.
    .claude/commands/api-lint-diff.md:252
  • This section states that false positives are "silently ignored" and "No false positives shown in output", but the actual script implementation (run.sh) does not perform any false positive filtering. All issues detected by the linter are shown in the output and categorized only as NEW or PRE-EXISTING. The false positive filtering is documented to be performed by Claude AI after the script runs, not by the script itself. This mismatch could confuse users who expect the script output to already have false positives filtered out.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +253 to +300
while IFS= read -r line; do
[[ -z "${line}" ]] && continue

local file
file=$(echo "${line}" | cut -d: -f1)

# Check if file was changed
local file_changed=false
for changed_file in "${changed_files[@]}"; do
if [[ "${file}" == "${changed_file}" ]]; then
file_changed=true
break
fi
done

# If file wasn't changed, it's pre-existing
if [[ "${file_changed}" == "false" ]]; then
echo "${line}" >> "${preexisting_issues_file}"
continue
fi

# Check if issue exists in baseline
# 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
local file_linter_msg
file_linter_msg=$(echo "${line}" | cut -d: -f1,4,5)

if grep -F ":$(echo "${line}" | cut -d: -f4,5)" "${baseline_file}" 2>/dev/null | \
grep -Fq "$(echo "${line}" | cut -d: -f1):"; then
echo "${line}" >> "${preexisting_issues_file}"
else
echo "${line}" >> "${new_issues_file}"
fi
done < "${current_file}"
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If parse_linter_output produces no output (e.g., no issues found or grep returns no matches), the current_parsed.txt file will be empty. The while loop that reads from this file will handle this correctly by not executing the loop body, but it would be more defensive to ensure the file exists before attempting to read from it. Consider adding a check like 'if [[ -f "${current_file}" && -s "${current_file}" ]]; then' before the while loop to skip processing if the file doesn't exist or is empty.

Suggested change
while IFS= read -r line; do
[[ -z "${line}" ]] && continue
local file
file=$(echo "${line}" | cut -d: -f1)
# Check if file was changed
local file_changed=false
for changed_file in "${changed_files[@]}"; do
if [[ "${file}" == "${changed_file}" ]]; then
file_changed=true
break
fi
done
# If file wasn't changed, it's pre-existing
if [[ "${file_changed}" == "false" ]]; then
echo "${line}" >> "${preexisting_issues_file}"
continue
fi
# Check if issue exists in baseline
# 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
local file_linter_msg
file_linter_msg=$(echo "${line}" | cut -d: -f1,4,5)
if grep -F ":$(echo "${line}" | cut -d: -f4,5)" "${baseline_file}" 2>/dev/null | \
grep -Fq "$(echo "${line}" | cut -d: -f1):"; then
echo "${line}" >> "${preexisting_issues_file}"
else
echo "${line}" >> "${new_issues_file}"
fi
done < "${current_file}"
if [[ -f "${current_file}" && -s "${current_file}" ]]; then
while IFS= read -r line; do
[[ -z "${line}" ]] && continue
local file
file=$(echo "${line}" | cut -d: -f1)
# Check if file was changed
local file_changed=false
for changed_file in "${changed_files[@]}"; do
if [[ "${file}" == "${changed_file}" ]]; then
file_changed=true
break
fi
done
# If file wasn't changed, it's pre-existing
if [[ "${file_changed}" == "false" ]]; then
echo "${line}" >> "${preexisting_issues_file}"
continue
fi
# Check if issue exists in baseline
# 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
local file_linter_msg
file_linter_msg=$(echo "${line}" | cut -d: -f1,4,5)
if grep -F ":$(echo "${line}" | cut -d: -f4,5)" "${baseline_file}" 2>/dev/null | \
grep -Fq "$(echo "${line}" | cut -d: -f1):"; then
echo "${line}" >> "${preexisting_issues_file}"
else
echo "${line}" >> "${new_issues_file}"
fi
done < "${current_file}"
fi

Copilot uses AI. Check for mistakes.
# Get golangci-lint version from bingo
local golangci_version
golangci_version=$(get_golangci_version)

Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The golangci_version variable could be empty if get_golangci_version fails to extract the version from Variables.mk. This would result in an invalid config file with "version: " (empty value). Consider adding validation to ensure the version is not empty before using it in the config, or handle the error case explicitly.

Suggested change
if [[ -z "${golangci_version}" ]]; then
echo -e "${RED}Error: Failed to determine golangci-lint version from .bingo/Variables.mk${NC}" >&2
exit 1
fi

Copilot uses AI. Check for mistakes.
Comment on lines +281 to +295
if grep -F ":$(echo "${line}" | cut -d: -f4,5)" "${baseline_file}" 2>/dev/null | \
grep -Fq "$(echo "${line}" | cut -d: -f1):"; then
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable file_linter_msg is assigned on line 279 but never used. The comparison logic on lines 281-282 directly uses cut commands without referencing this variable. Either use this variable in the comparison or remove the assignment to improve code clarity and maintainability.

Suggested change
if grep -F ":$(echo "${line}" | cut -d: -f4,5)" "${baseline_file}" 2>/dev/null | \
grep -Fq "$(echo "${line}" | cut -d: -f1):"; then
if grep -Fq "${file_linter_msg}" "${baseline_file}" 2>/dev/null; then

Copilot uses AI. Check for mistakes.
@camilamacedo86 camilamacedo86 changed the title 🌱 Add CLAUDE command and new check to review and lint the API changes WIP: 🌱 Add CLAUDE command and new check to review and lint the API changes Dec 19, 2025
@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Dec 19, 2025
@codecov
Copy link

codecov bot commented Dec 19, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 73.05%. Comparing base (90cd55a) to head (a625380).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2399      +/-   ##
==========================================
+ Coverage   68.82%   73.05%   +4.22%     
==========================================
  Files         100      100              
  Lines        7641     7641              
==========================================
+ Hits         5259     5582     +323     
+ Misses       1947     1623     -324     
- Partials      435      436       +1     
Flag Coverage Δ
e2e 43.78% <ø> (-0.15%) ⬇️
experimental-e2e 48.77% <ø> (+35.09%) ⬆️
unit 57.11% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant