mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 18:28:13 +00:00 
			
		
		
		
	Add linter to check if api docs match field tag names
This commit is contained in:
		
							
								
								
									
										105
									
								
								cmd/fieldnamedocscheck/field_name_docs_check.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								cmd/fieldnamedocscheck/field_name_docs_check.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| /* | ||||
| Copyright 2023 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
|  | ||||
| 	flag "github.com/spf13/pflag" | ||||
| 	kruntime "k8s.io/apimachinery/pkg/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/util/sets" | ||||
| 	"k8s.io/klog/v2" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	typeSrc = flag.StringP("type-src", "s", "", "From where we are going to read the types") | ||||
| 	re      = regexp.MustCompile("`(\\b\\w+\\b)`") | ||||
| ) | ||||
|  | ||||
| // kubeTypesMap is a map from field name to its tag name and doc. | ||||
| type kubeTypesMap map[string]kruntime.Pair | ||||
|  | ||||
| func main() { | ||||
| 	flag.Parse() | ||||
|  | ||||
| 	if *typeSrc == "" { | ||||
| 		klog.Fatalf("Please define -s flag as it is the api type file") | ||||
| 	} | ||||
|  | ||||
| 	docsForTypes := kruntime.ParseDocumentationFrom(*typeSrc) | ||||
| 	rc := false | ||||
|  | ||||
| 	for _, ks := range docsForTypes { | ||||
| 		typesMap := make(kubeTypesMap) | ||||
|  | ||||
| 		for _, p := range ks[1:] { | ||||
| 			// skip the field with no tag name | ||||
| 			if p.Name != "" { | ||||
| 				typesMap[strings.ToLower(p.Name)] = p | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		structName := ks[0].Name | ||||
|  | ||||
| 		rc = checkFieldNameAndDoc(structName, "", ks[0].Doc, typesMap) | ||||
| 		for _, p := range ks[1:] { | ||||
| 			rc = checkFieldNameAndDoc(structName, p.Name, p.Doc, typesMap) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if rc { | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func checkFieldNameAndDoc(structName, fieldName, doc string, typesMap kubeTypesMap) bool { | ||||
| 	rc := false | ||||
| 	visited := sets.Set[string]{} | ||||
|  | ||||
| 	// The rule is: | ||||
| 	// 1. Get all back-tick quoted names in the doc | ||||
| 	// 2. Skip the name which is already found mismatched. | ||||
| 	// 3. Skip the name whose lowercase is different from the lowercase of tag names, | ||||
| 	//    because some docs use back-tick to quote field value or nil | ||||
| 	// 4. Check if the name is different from its tag name | ||||
|  | ||||
| 	// TODO: a manual pass adding back-ticks to the doc strings, then update the linter to | ||||
| 	// check the existence of back-ticks | ||||
| 	nameGroups := re.FindAllStringSubmatch(doc, -1) | ||||
| 	for _, nameGroup := range nameGroups { | ||||
| 		name := nameGroup[1] | ||||
| 		if visited.Has(name) { | ||||
| 			continue | ||||
| 		} | ||||
| 		if p, ok := typesMap[strings.ToLower(name)]; ok && p.Name != name { | ||||
| 			rc = true | ||||
| 			visited.Insert(name) | ||||
|  | ||||
| 			fmt.Fprintf(os.Stderr, "Error: doc for %s", structName) | ||||
| 			if fieldName != "" { | ||||
| 				fmt.Fprintf(os.Stderr, ".%s", fieldName) | ||||
| 			} | ||||
|  | ||||
| 			fmt.Fprintf(os.Stderr, " contains: %s, which should be: %s\n", name, p.Name) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return rc | ||||
| } | ||||
							
								
								
									
										1
									
								
								hack/.fieldname_docs_failures
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								hack/.fieldname_docs_failures
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| ./staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1 | ||||
							
								
								
									
										71
									
								
								hack/verify-fieldname-docs.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										71
									
								
								hack/verify-fieldname-docs.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| #!/usr/bin/env bash | ||||
|  | ||||
| # Copyright 2023 The Kubernetes Authors. | ||||
| # | ||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| # you may not use this file except in compliance with the License. | ||||
| # You may obtain a copy of the License at | ||||
| # | ||||
| #     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| # | ||||
| # Unless required by applicable law or agreed to in writing, software | ||||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| # See the License for the specific language governing permissions and | ||||
| # limitations under the License. | ||||
|  | ||||
| # This script checks API-related files for mismatch in docs and field names, | ||||
| # and outputs a list of fields that their docs and field names are mismatched. | ||||
| # Usage: `hack/verify-fieldname-docs.sh`. | ||||
|  | ||||
| set -o errexit | ||||
| set -o nounset | ||||
| set -o pipefail | ||||
|  | ||||
| KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. | ||||
| source "${KUBE_ROOT}/hack/lib/init.sh" | ||||
| source "${KUBE_ROOT}/hack/lib/util.sh" | ||||
|  | ||||
| kube::golang::setup_env | ||||
|  | ||||
| make -C "${KUBE_ROOT}" WHAT=cmd/fieldnamedocscheck | ||||
|  | ||||
| # Find binary | ||||
| fieldnamedocscheck=$(kube::util::find-binary "fieldnamedocscheck") | ||||
|  | ||||
| result=0 | ||||
|  | ||||
| find_files() { | ||||
|   find . -not \( \ | ||||
|       \( \ | ||||
|         -wholename './output' \ | ||||
|         -o -wholename './_output' \ | ||||
|         -o -wholename './_gopath' \ | ||||
|         -o -wholename './release' \ | ||||
|         -o -wholename './target' \ | ||||
|         -o -wholename '*/third_party/*' \ | ||||
|         -o -wholename '*/vendor/*' \ | ||||
|         -o -wholename './pkg/*' \ | ||||
|       \) -prune \ | ||||
|     \) \ | ||||
|     \( -wholename './staging/src/k8s.io/api/*/v*/types.go' \ | ||||
|        -o -wholename './staging/src/k8s.io/kube-aggregator/pkg/apis/*/v*/types.go' \ | ||||
|        -o -wholename './staging/src/k8s.io/apiextensions-apiserver/pkg/apis/*/v*/types.go' \ | ||||
|     \) | ||||
| } | ||||
|  | ||||
| versioned_api_files=$(find_files) || true | ||||
|  | ||||
| failure_file="${KUBE_ROOT}/hack/.fieldname_docs_failures" | ||||
| failing_groups=() | ||||
| while IFS='' read -r line; do failing_groups+=("${line}"); done < <(cat "${failure_file}") | ||||
|  | ||||
| for file in ${versioned_api_files}; do | ||||
| 	package="${file%"/types.go"}" | ||||
|     if ! kube::util::array_contains "${package}" "${failing_groups[@]}"; then | ||||
|         echo "Checking ${package}" | ||||
|         ${fieldnamedocscheck} -s "${file}" || result=$? | ||||
|     fi | ||||
| done | ||||
|  | ||||
| exit ${result} | ||||
		Reference in New Issue
	
	Block a user
	 Hao Ruan
					Hao Ruan