name: generate-sh-checker description: Checks apis/ subdirectories for missing generate.sh and helps create PRs to add them, following the pattern in #7293. Use this when you want to ensure all KCC resources have a standardized generation script.
Generate.sh Checker
This skill helps maintain the generate.sh pattern across all apis/ subdirectories in the Config Connector codebase.
Workflow
Scan for missing scripts: Find subdirectories in
apis/(usuallyv1beta1orv1alpha1) that do not have agenerate.shfile.find apis -maxdepth 2 -type d \( -name "v1beta1" -o -name "v1alpha1" \) | while read dir; do if [ ! -f "$dir/generate.sh" ] && ls "$dir"/*_type*.go >/dev/null 2>&1; then echo "$dir"; fi; done(Note:
apis/refsis a special folder and does not correspond to a GCP service. Since it lacks*_types.gofiles, the above command naturally skips it, which is correct.)Gather Resource Information: For each identified directory, read
api_types.goandgroupversion_info.goto extract:PROTO_SERVICE: Look for// +kcc:spec:proto=or// +kcc:proto=markers inapi_types.go.GROUP: Look for// +groupName=ingroupversion_info.go.VERSION: The directory name (e.g.,v1beta1).RESOURCE_MAPPINGS: Mapping ofKind:ProtoMessagefrom// +kcc:spec:proto=markers.SERVICE_NAME: The parent directory name inapis/(e.g.,apigateway).
Note: If the directory does not contain any
*_type*.gofile (e.g., it only contains reference types likeservice_reference.go), there are no types or mappers to generate. In this case,generate.shis not required.Create generate.sh: Create a
generate.shfile in the directory. Ensure the year in the copyright header is current (2026).Template:
#!/bin/bash # Copyright 2026 Google LLC # # 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. set -o errexit set -o nounset set -o pipefail REPO_ROOT="$(git rev-parse --show-toplevel)" source "${REPO_ROOT}/dev/tools/goimports.sh" cd ${REPO_ROOT}/dev/tools/controllerbuilder ./generate-proto.sh go run . generate-types \ --service <PROTO_SERVICE> \ --api-version <GROUP>/<VERSION> \ --include-skipped-output \ --resource <KIND1>:<PROTO_MESSAGE1> \ [--resource <KIND2>:<PROTO_MESSAGE2> ...] go run . generate-mapper \ --service <PROTO_SERVICE> \ --api-version <GROUP>/<VERSION> \ --include-skipped-output cd ${REPO_ROOT} dev/tasks/generate-crds go run -mod=readonly golang.org/x/tools/cmd/goimports@${GOLANG_X_TOOLS_VERSION} -w pkg/controller/direct/<SERVICE_NAME>/Special Handling (Multi-version & Promotion/Consolidation):
- File Naming:
generate-typesexpects the main types file to be named<lowercase_proto_message_name>_types.go. If the existing file has a different name (e.g.,cluster_types.goinstead ofattachedcluster_types.go), rename it before running the generator. - Hand-written
types.generated.go: If atypes.generated.goalready exists but lacks the// Code generated by ... DO NOT EDIT.header, it was hand-written. Rename it totypes.goto prevent it from being overwritten. - Pointer Types: When preserving hand-written structs that correspond to proto messages, ensure their fields use pointers (e.g.,
*stringwith,omitemptyinstead ofstring) where the proto fields are optional. Otherwise,mapper.generated.gowill fail to compile with type assignment errors (e.g.,cannot use direct.LazyPtr(in.GetName()) ... as string value in assignment). - Multi-version resources: When generating the same resource in
v1alpha1andv1beta1, we should use// +kubebuilder:metadata:labels="internal.cloud.google.com/additional-versions=v1alpha1"on thev1beta1resource to generatev1alpha1fromv1beta1. Often this will mean we don't need av1alpha1folder at all. - Multiple generate.sh scripts: If both
v1alpha1andv1beta1directories exist and both need agenerate.shscript, only callgenerate-mapperin one of the version'sgenerate.sh(typicallyv1beta1/generate.shand not inv1alpha1/generate.sh). We still needgenerate-typesinv1alpha1/generate.sh.- If multiple API versions exist (e.g.
v1alpha1andv1beta1) and contain*_types.gofiles, use the--multiversionflag when callinggenerate-mapperto prevent naming collisions (e.g.,NotebookInstanceSpec_FromProtobecomingNotebookInstanceSpec_v1beta1_FromProto). This is becausegenerate-mapperscans the entireapis/<SERVICE>directory and will generate mappers for all versions it finds, causing naming collisions if the suffix is omitted. When using--multiversion, you will also need to update the direct controller (pkg/controller/direct/<SERVICE>/*_controller.goand*_fuzzer.go) to use the new version-suffixed mapper functions. If there are custom manual mapper functions in*_mappings.goor*_mappers.go, rename them to match the new version suffix (e.g.,_v1beta1_FromProtoor_v1alpha1_FromProto) sogenerate-mapperrecognizes them and skips generating duplicates.
- If multiple API versions exist (e.g.
- Promotion/Consolidation: If a
v1beta1directory is being updated and av1alpha1directory exists for the same service, check ifv1alpha1should be consolidated. - Following #7293, this involves:
- Removing the
v1alpha1directory entirely if all resources are inv1beta1. Or, if we generate the same resource inv1alpha1andv1beta1, we should use// +kubebuilder:metadata:labels="internal.cloud.google.com/additional-versions=v1alpha1"in thev1beta1type to generatev1alpha1fromv1beta1. Often this will mean we don't need av1alpha1folder at all. - Ensuring
v1beta1has// +kubebuilder:storageversion.
- Removing the
- If the existing types files are not named
<lowercasekind>_types.go(e.g.,cluster_types.goinstead ofworkstationcluster_types.go), rename them usinggit mvto match the expected pattern before runninggenerate.sh. Otherwise,controllerbuilder generate-typeswill fail to find the existing types and create duplicate files. - Multiple API Versions: If a service has multiple API versions (e.g.
v1alpha1andv1beta1) that map to the same proto service and cannot be consolidated (e.g. due to cross-references),generate-mappermust be called with--multiversion. Otherwise, identical mapper functions will be generated twice and cause compile errors. When using--multiversion, check existing handwritten controller code to ensure they call the updated multi-versioned mapper functions (e.g.KMSImportJobSpec_v1beta1_FromProto). - Different Proto Packages/Versions: If a resource (e.g. an alpha/beta resource like
ComputeFutureReservation) belongs to a different proto package/version (e.g.google.cloud.compute.v1beta) than the default--servicepackage (e.g.google.cloud.compute.v1), you can specify its fully-qualified name in the--resourceflag (e.g.--resource ComputeFutureReservation:google.cloud.compute.v1beta.FutureReservation). This allowsgenerate-typesto locate the correct message descriptors across different packages when compiling multiple API levels.
- File Naming:
Execute and Verify:
- Make
generate.shexecutable:chmod +x apis/<SERVICE>/<VERSION>/generate.sh. - Run it:
./apis/<SERVICE>/<VERSION>/generate.sh. - Verify that
types.generated.gois created in the API directory. - Verify that
pkg/controller/direct/<SERVICE>/mapper.generated.gois updated. - Verify that CRDs in
config/crds/resources/are updated. - Ref fields with acronyms: If you encounter
// MISSING: [Acronym]...(likeMISSING: KMSKeyorMISSING: CAPool) in the generatedmapper.generated.go, it might be because thegenerate-mappertool expects the field in the KRM struct to use the fully capitalized acronym (e.g.KMSKeyRefinstead ofKmsKeyRef,CAPoolRefinstead ofCaPoolRef). Rename the Go struct field to match the acronym (this won't break the yaml if thejsontag is unchanged), and update any references inmapper.goor[service]_controller.go. The generator should then automatically map theReffield properly.
- Make
Commit and PR: Create a branch, commit the changes, and propose a PR with a descriptive title like
chore: apis/<SERVICE> should follow generate.sh pattern.
Troubleshooting
See notes.md for troubleshooting uncommon edge cases.
- Deepcopy-gen errors (
invalid slice element type: invalid type): This typically happens ifgenerate-typesoutputs a struct name with a different capitalization than what is currently manually written in the*_types.gofile (e.g.PSCConfigvsPscConfig). To fix, rename the type and all its usages in the*_types.goandpkg/controller/direct/<SERVICE>/mapper.gofiles to match the generated capitalization, then run./generate.shagain.