diff --git a/.github/workflows/validate-crd.yml b/.github/workflows/validate-crd.yml new file mode 100644 index 0000000000..7786a3c3b2 --- /dev/null +++ b/.github/workflows/validate-crd.yml @@ -0,0 +1,64 @@ +name: Validate CRD Generation + +# This workflow validates that generated CRD files are up-to-date when tool +# dependencies change. It ensures that if go.tool.mod or go.tool.sum are updated, +# the corresponding generated files (CRDs and deepcopy code) are also regenerated +# and committed in the same PR. +# +# Why this is needed: +# - controller-gen (from go.tool.mod) generates CRD YAML and deepcopy Go code +# - Different versions of controller-gen may produce different output +# - When tool versions change, generated code must be regenerated +# - This prevents CI failures and runtime issues from stale generated code + +on: + pull_request: + paths: + - 'go.tool.mod' + - 'go.tool.sum' + - 'scripts/generate-crd.sh' + - '**/dnsendpoints.externaldns.k8s.io.yaml' + +permissions: + contents: read + +jobs: + validate-crd: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - name: Set up Go + uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + with: + go-version-file: 'go.mod' + + - name: Regenerate CRDs + run: ./scripts/generate-crd.sh + + - name: Check for uncommitted changes + id: check_changes + run: | + # Check if there are any changes to generated files + if ! git diff --quiet; then + echo "::error::Generated CRD files are out of sync with go.tool.mod" + echo "" + echo "The following files have uncommitted changes after running 'make crd':" + git diff . + echo "" + echo "This usually means:" + echo "1. go.tool.mod or go.tool.sum was updated (new controller-gen version)" + echo "2. The generated CRD files were not regenerated" + echo "" + echo "To fix this:" + echo " make crd" + echo " git diff ." + echo " commit, push and update your PR:" + exit 1 + fi + + - name: Success + if: success() + run: | + echo "✅ Generated CRD files are up-to-date" diff --git a/Makefile b/Makefile index 8615e4abee..dc0d00b259 100644 --- a/Makefile +++ b/Makefile @@ -16,10 +16,6 @@ .PHONY: cover cover-html .DEFAULT_GOAL := build -CONTROLLER_GEN := go tool -modfile=go.tool.mod controller-gen -YQ := go tool -modfile=go.tool.mod yq -YAMLFMT := go tool -modfile=go.tool.mod yamlfmt - cover: @go test -cover -coverprofile=cover.out -v ./... @@ -64,13 +60,12 @@ lint: licensecheck go-lint #? crd: Generates CRD using controller-gen and copy it into chart .PHONY: crd crd: - $(CONTROLLER_GEN) object crd:crdVersions=v1 paths="./endpoint/..." - $(CONTROLLER_GEN) object crd:crdVersions=v1 paths="./apis/..." output:crd:stdout | \ - $(YAMLFMT) - | \ - $(YQ) eval '.' --no-doc --split-exp '"./config/crd/standard/" + .metadata.name + ".yaml"' - $(YQ) eval '.metadata.annotations |= with_entries(select(.key | test("kubernetes\.io")))' \ - --no-doc --split-exp '"./charts/external-dns/crds/" + .metadata.name + ".yaml"' \ - ./config/crd/standard/*.yaml + @./scripts/generate-crd.sh + +# Required as long as dependabot does not support go.tool.mod https://github.com/dependabot/dependabot-core/issues/12050 +#? update-tools-deps: Update go tools defined in go.tool.mod to latest versions +update-tools-deps: + @go get -modfile=go.tool.mod tool #? test: The verify target runs tasks similar to the CI tasks, but without code coverage .PHONY: test diff --git a/go.tool.mod b/go.tool.mod index ea84b58e6f..9972a2a181 100644 --- a/go.tool.mod +++ b/go.tool.mod @@ -1,6 +1,6 @@ module sigs.k8s.io/external-dns/tools -go 1.25 +go 1.25.0 tool ( github.com/google/yamlfmt/cmd/yamlfmt diff --git a/scripts/generate-crd.sh b/scripts/generate-crd.sh new file mode 100755 index 0000000000..8907297d98 --- /dev/null +++ b/scripts/generate-crd.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +# Copyright 2026 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. + +# generate-crd.sh +# +# This script generates Kubernetes Custom Resource Definitions (CRDs) and related +# deepcopy code for external-dns using controller-gen from controller-tools. +# +## What this script does: +# 1. Generates DeepCopy methods for types in the endpoint package +# 2. Generates CRD manifests for API types in the apis package +# 3. Copies CRDs to the Helm chart directory +# +# Usage: +# ./scripts/generate-crd.sh +# make crd # calls this script + +set -euo pipefail + +# Get the script directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# Get the project root (parent of scripts directory) +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +cd "${PROJECT_ROOT}" + +# Define tool commands (using tools from go.tool.mod) +CONTROLLER_GEN="go tool -modfile=go.tool.mod controller-gen" +YQ="go tool -modfile=go.tool.mod yq" +YAMLFMT="go tool -modfile=go.tool.mod yamlfmt" + +echo " Generating CRDs using controller-gen..." + +# Step 1: Generate deepcopy methods for endpoint types +# This creates zz_generated.deepcopy.go with DeepCopy/DeepCopyInto/DeepCopyObject methods +# The 'object' generator adds these methods for types marked with +kubebuilder:object markers +echo " → Generating deepcopy for endpoint package..." +${CONTROLLER_GEN} object crd:crdVersions=v1 paths="./endpoint/..." + +# Clean up empty import statements from generated files +# controller-gen sometimes adds empty import() blocks which create noise in diffs +find ./endpoint -name "zz_generated.deepcopy.go" -exec gofmt -s -w {} \; + +# Step 2: Generate CRD manifests for API types +# - Generates CRDs from Go types with kubebuilder markers +# - Outputs to stdout, formats with yamlfmt, then splits into individual files +# - Each CRD is saved to config/crd/standard/.yaml +echo " → Generating CRDs for apis package..." +${CONTROLLER_GEN} object crd:crdVersions=v1 paths="./apis/..." output:crd:stdout | \ + ${YAMLFMT} - | \ + ${YQ} eval '.' --no-doc --split-exp '"./config/crd/standard/" + .metadata.name + ".yaml"' + +# Clean up empty import statements from generated files +find ./apis -name "zz_generated.deepcopy.go" -exec gofmt -s -w {} \; + +# Step 3: Copy CRDs to Helm chart with filtered annotations +# - Reads CRDs from config/crd/standard/ +# - Filters annotations to only keep kubernetes.io/* (removes controller-gen annotations) +# - Splits and saves to charts/external-dns/crds/ for Helm chart packaging +echo " → Copying CRDs to chart directory..." +${YQ} eval '.metadata.annotations |= with_entries(select(.key | test("kubernetes\.io")))' \ + --no-doc --split-exp '"./charts/external-dns/crds/" + .metadata.name + ".yaml"' \ + ./config/crd/standard/*.yaml + +echo -e " ✅ CRD generation complete"