diff --git a/.golangci-kal.yml b/.golangci-kal.yml new file mode 100644 index 0000000000..6e68e5ba6e --- /dev/null +++ b/.golangci-kal.yml @@ -0,0 +1,99 @@ +version: "2" +run: + go: "1.24" + allow-parallel-runners: true +linters: + default: none + enable: + - kubeapilinter # linter for Kube API conventions + settings: + custom: + kubeapilinter: + type: module + description: KAL is the Kube-API-Linter and lints Kube like APIs based on API conventions and best practices. + settings: + linters: + enable: + - "commentstart" # Ensure comments start with the serialized version of the field name. + - "conditions" # Ensure conditions have the correct json tags and markers. + - "duplicatemarkers" # Ensure there are no exact duplicate markers. for types and fields. + - "integers" # Ensure only int32 and int64 are used for integers. + - "jsontags" # Ensure every field has a json tag. + - "maxlength" # Ensure all strings and arrays have maximum lengths/maximum items. + - "nobools" # Bools do not evolve over time, should use enums instead. + - "nofloats" # Ensure floats are not used. + - "nomaps" # Ensure maps are not used. + - "optionalfields" # Ensure that all fields marked as optional adhere to being pointers and + # having the `omitempty` value in their `json` tag where appropriate. + - "optionalorrequired" # Every field should be marked as `+optional` or `+required`. + - "requiredfields" # Required fields should not be pointers, and should not have `omitempty`. + - "ssatags" # Ensure array fields have the appropriate listType markers + - "statusoptional" # Ensure all first children within status should be optional. + - "statussubresource" # All root objects that have a `status` field should have a status subresource. + - "notimestamp" # Prevents usage of 'Timestamp' fields + - "uniquemarkers" # Ensure that types and fields do not contain more than a single definition of a marker that should only be present once. + + # Per discussion in July 2024, we are keeping phase fields for now. + # See https://github.com/kubernetes-sigs/cluster-api/pull/10897#discussion_r1685929508 + # and https://github.com/kubernetes-sigs/cluster-api/pull/10897#discussion_r1685919394. + # - "nophase" # Phase fields are discouraged by the Kube API conventions, use conditions instead. + + # Linters below this line are disabled, pending conversation on how and when to enable them. + disable: + - "*" # We will manually enable new linters after understanding the impact. Disable all by default. + lintersConfig: + conditions: + isFirstField: Warn # Require conditions to be the first field in the status struct. + usePatchStrategy: Forbid # Forbid patchStrategy markers on the Conditions field. + useProtobuf: Forbid # We don't use protobuf, so protobuf tags are not required. + optionalfields: + pointers: + preference: WhenRequired # Always | WhenRequired # Whether to always require pointers, or only when required. Defaults to `Always`. + policy: SuggestFix # SuggestFix | Warn # The policy for pointers in optional fields. Defaults to `SuggestFix`. + omitempty: + policy: SuggestFix # SuggestFix | Warn | Ignore # The policy for omitempty in optional fields. Defaults to `SuggestFix`. + # jsontags: + # jsonTagRegex: "^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$" # The default regex is appropriate for our use case. + # optionalorrequired: + # preferredOptionalMarker: optional | kubebuilder:validation:Optional # The preferred optional marker to use, fixes will suggest to use this marker. Defaults to `optional`. + # preferredRequiredMarker: required | kubebuilder:validation:Required # The preferred required marker to use, fixes will suggest to use this marker. Defaults to `required`. + # requiredfields: + # pointerPolicy: Warn | SuggestFix # Defaults to `SuggestFix`. We want our required fields to not be pointers. +# ssatags: +# listTypeSetUsage: Warn | Ignore # The policy for listType=set usage on object arrays. Defaults to `Warn`. + + exclusions: + generated: strict + paths: + - zz_generated.*\.go$ + - vendored_openapi\.go$ + # We don't want to invest time to fix new linter findings in old API types. + - ".*_test.go" # Exclude test files. + rules: + ## KAL should only run on API folders. + - path-except: "apis/vmware/v1beta1//*" + linters: + - kubeapilinter + + ## Excludes for current apiVersions that can be removed once v1beta1 is removed. + # .status.deprecated.v1beta1.conditions fields are using v1beta1.Condition types. + - path: "apis/vmware/v1beta1" + text: "Conditions field must be a slice of metav1.Condition" + linters: + - kubeapilinter + - path: "apis/vmware/v1beta1" + text: "ssatags: Conditions should have a listType marker for proper Server-Side Apply behavior" + linters: + - kubeapilinter + + ## Removal of bool fields of existing types requires further discussion + - path: "apis/vmware/v1beta1" + text: "nobools" + linters: + - kubeapilinter + +issues: + max-same-issues: 0 + max-issues-per-linter: 0 + new-from-merge-base: main + #new-from-rev: 2b8b70e3 diff --git a/Makefile b/Makefile index 149a3d8460..acdf0b93ca 100644 --- a/Makefile +++ b/Makefile @@ -170,6 +170,10 @@ GOLANGCI_LINT_VER := $(shell cat .github/workflows/pr-golangci-lint.yaml | grep GOLANGCI_LINT := $(abspath $(TOOLS_BIN_DIR)/$(GOLANGCI_LINT_BIN)-$(GOLANGCI_LINT_VER)) GOLANGCI_LINT_PKG := github.com/golangci/golangci-lint/v2/cmd/golangci-lint +GOLANGCI_LINT_KAL_BIN := golangci-lint-kube-api-linter +GOLANGCI_LINT_KAL_VER := $(shell cat ./hack/tools/.custom-gcl.yaml | grep version: | sed 's/version: //') +GOLANGCI_LINT_KAL := $(abspath $(TOOLS_BIN_DIR)/$(GOLANGCI_LINT_KAL_BIN)) + GOVULNCHECK_BIN := govulncheck GOVULNCHECK_VER := v1.1.4 GOVULNCHECK := $(abspath $(TOOLS_BIN_DIR)/$(GOVULNCHECK_BIN)-$(GOVULNCHECK_VER)) @@ -475,6 +479,14 @@ lint: $(GOLANGCI_LINT) ## Lint the codebase lint-fix: $(GOLANGCI_LINT) ## Lint the codebase and run auto-fixers if supported by the linter GOLANGCI_LINT_EXTRA_ARGS=--fix $(MAKE) lint +.PHONY: lint-api +lint-api: $(GOLANGCI_LINT_KAL) + $(GOLANGCI_LINT_KAL) run -v --config $(ROOT_DIR)/.golangci-kal.yml $(GOLANGCI_LINT_EXTRA_ARGS) + +.PHONY: lint-api-fix +lint-api-fix: $(GOLANGCI_LINT_KAL) + GOLANGCI_LINT_EXTRA_ARGS=--fix $(MAKE) lint-api + APIDIFF_OLD_COMMIT ?= $(shell git rev-parse origin/main) .PHONY: apidiff @@ -1059,6 +1071,9 @@ $(GINKGO_BIN): $(GINKGO) ## Build a local copy of ginkgo. .PHONY: $(GOLANGCI_LINT_BIN) $(GOLANGCI_LINT_BIN): $(GOLANGCI_LINT) ## Build a local copy of golangci-lint. +$(GOLANGCI_LINT_KAL): $(GOLANGCI_LINT) # Build golangci-lint-kal from custom configuration. + cd $(TOOLS_DIR); $(GOLANGCI_LINT) custom + .PHONY: $(GOVULNCHECK_BIN) $(GOVULNCHECK_BIN): $(GOVULNCHECK) ## Build a local copy of govulncheck. diff --git a/hack/tools/.custom-gcl.yaml b/hack/tools/.custom-gcl.yaml new file mode 100644 index 0000000000..11b2097506 --- /dev/null +++ b/hack/tools/.custom-gcl.yaml @@ -0,0 +1,7 @@ +version: v2.1.0 +name: golangci-lint-kube-api-linter +destination: ./bin +plugins: + - module: 'sigs.k8s.io/kube-api-linter' + path: ../../../kube-api-linter + # using JoelSpeed/required-fields-v2 branch for now