diff --git a/.github/actions/prepare-release/action.yaml b/.github/actions/prepare-release/action.yaml
new file mode 100644
index 0000000..c79a54f
--- /dev/null
+++ b/.github/actions/prepare-release/action.yaml
@@ -0,0 +1,13 @@
+name: Prepare-Release
+
+runs:
+ using: composite
+ steps:
+ - uses: actions/setup-go@v6
+ with:
+ go-version: '1.26'
+ - name: prepare-release
+ shell: bash
+ run: |
+ set -eu
+ make generate
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 5e8973d..12f1ac2 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -16,6 +16,8 @@ jobs:
uses: gardener/cc-utils/.github/workflows/prepare.yaml@v1
with:
mode: ${{ inputs.mode }}
+ version-operation: ${{ inputs.release-version }}
+ version-commit-callback-action-path: .github/actions/prepare-release
permissions:
id-token: write
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index a635ce8..1c1f7a5 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -33,6 +33,7 @@ jobs:
with:
release-commit-target: branch
next-version: ${{ inputs.next-version }}
+ next-version-callback-action-path: .github/actions/prepare-release
assets: |
- name: gardener-landscape-kit-darwin-amd64
type: ocm-resource
diff --git a/Makefile b/Makefile
index 67a6037..adf12b5 100644
--- a/Makefile
+++ b/Makefile
@@ -59,6 +59,7 @@ generate: tools-for-generate $(GOIMPORTS) $(FLUX_CLI) $(YQ)
@REPO_ROOT=$(REPO_ROOT) GARDENER_HACK_DIR=$(GARDENER_HACK_DIR) $(HACK_DIR)/update-codegen.sh
@GARDENER_HACK_DIR=$(GARDENER_HACK_DIR) $(HACK_DIR)/update-github-templates.sh
@ARRAY_KEY=matchPackageNames NEEDLE='// GENERATOR-PIN' GARDENER_HACK_DIR=$(GARDENER_HACK_DIR) RENOVATE_CONFIG=$(REPO_ROOT)/.github/renovate.json5 bash $(GARDENER_HACK_DIR)/generate-renovate-ignore-deps.sh
+ @$(HACK_DIR)/sync-glk-version.sh
$(MAKE) format
.PHONY: check
diff --git a/componentvector/components.go b/componentvector/components.go
index 8965c5f..84c4759 100644
--- a/componentvector/components.go
+++ b/componentvector/components.go
@@ -37,4 +37,6 @@ const (
NameGardenerGardenerExtensionShootNetworkingProblemdetector = "github.com/gardener/gardener-extension-shoot-networking-problemdetector"
// NameGardenerGardenerExtensionShootOidcService is a constant for a component in the component vector with name 'github.com/gardener/gardener-extension-shoot-oidc-service'.
NameGardenerGardenerExtensionShootOidcService = "github.com/gardener/gardener-extension-shoot-oidc-service"
+ // NameGardenerGardenerLandscapeKit is a constant for a component in the component vector with name 'github.com/gardener/gardener-landscape-kit'.
+ NameGardenerGardenerLandscapeKit = "github.com/gardener/gardener-landscape-kit"
)
diff --git a/componentvector/components.yaml b/componentvector/components.yaml
index 24f1c3e..1c66521 100644
--- a/componentvector/components.yaml
+++ b/componentvector/components.yaml
@@ -1,4 +1,7 @@
components:
+- name: github.com/gardener/gardener-landscape-kit
+ sourceRepository: https://github.com/gardener/gardener-landscape-kit
+ version: v0.2.0-dev
- name: github.com/gardener/gardener
sourceRepository: https://github.com/gardener/gardener
version: v1.140.1
@@ -122,11 +125,11 @@ components:
helmChart:
repository: europe-docker.pkg.dev/gardener-project/releases/charts/gardener/extensions/shoot-dns-service-admission-runtime
shootDnsServiceAdmissionApplication:
- helmChart:
- repository: europe-docker.pkg.dev/gardener-project/releases/charts/gardener/extensions/shoot-dns-service-admission-application
+ helmChart:
+ repository: europe-docker.pkg.dev/gardener-project/releases/charts/gardener/extensions/shoot-dns-service-admission-application
shootDnsService:
- helmChart:
- repository: europe-docker.pkg.dev/gardener-project/releases/charts/gardener/extensions/shoot-dns-service
+ helmChart:
+ repository: europe-docker.pkg.dev/gardener-project/releases/charts/gardener/extensions/shoot-dns-service
- name: github.com/gardener/gardener-extension-shoot-oidc-service
sourceRepository: https://github.com/gardener/gardener-extension-shoot-oidc-service
version: v0.38.0
diff --git a/docs/api-reference/landscapekit-v1alpha1.md b/docs/api-reference/landscapekit-v1alpha1.md
index 9b23792..cc9b2fd 100644
--- a/docs/api-reference/landscapekit-v1alpha1.md
+++ b/docs/api-reference/landscapekit-v1alpha1.md
@@ -150,6 +150,23 @@ _Appears in:_
| `landscape` _string_ | Landscape is the relative path to the landscape directory within the Git repository. | | Required: \{\}
|
+#### VersionCheckMode
+
+_Underlying type:_ _string_
+
+VersionCheckMode controls the behavior when the tool version doesn't match the component version.
+
+
+
+_Appears in:_
+- [VersionConfiguration](#versionconfiguration)
+
+| Field | Description |
+| --- | --- |
+| `Strict` | VersionCheckModeStrict indicates that version mismatches should cause an error.
|
+| `Warning` | VersionCheckModeWarning indicates that version mismatches should only log a warning.
|
+
+
#### VersionConfiguration
@@ -164,5 +181,6 @@ _Appears in:_
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `defaultVersionsUpdateStrategy` _[DefaultVersionsUpdateStrategy](#defaultversionsupdatestrategy)_ | UpdateStrategy determines whether the versions in the default vector should be updated from the release branch on resolve.
Possible values are "Disabled" (default) and "ReleaseBranch". | | Optional: \{\}
|
+| `checkMode` _[VersionCheckMode](#versioncheckmode)_ | CheckMode determines the behavior when the tool version doesn't match the gardener-landscape-kit version in the component vector.
Possible values are "Strict" (default) and "Warning".
In strict mode, version mismatches cause errors. In warning mode, only warnings are logged. | | Optional: \{\}
|
diff --git a/example/20-componentconfig-glk.yaml b/example/20-componentconfig-glk.yaml
index 77b010d..26522e7 100644
--- a/example/20-componentconfig-glk.yaml
+++ b/example/20-componentconfig-glk.yaml
@@ -19,4 +19,5 @@ kind: LandscapeKitConfiguration
# - component-name
# versionConfig:
# defaultVersionsUpdateStrategy: ReleaseBranch
+# checkMode: Strict # or Warning
# mergeMode: Hint
diff --git a/hack/sync-glk-version.sh b/hack/sync-glk-version.sh
new file mode 100755
index 0000000..28522ec
--- /dev/null
+++ b/hack/sync-glk-version.sh
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+#
+# SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+# This script syncs the gardener-landscape-kit component version in
+# componentvector/components.yaml with the version specified in the VERSION file.
+# If the component doesn't exist, it adds it as the first entry.
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
+REPO_ROOT="$(dirname "$SCRIPT_DIR")"
+VERSION_FILE="$REPO_ROOT/VERSION"
+COMPONENTS_FILE="$REPO_ROOT/componentvector/components.yaml"
+
+# Read the VERSION file
+if [[ ! -f "$VERSION_FILE" ]]; then
+ echo "ERROR: VERSION file not found at $VERSION_FILE" >&2
+ exit 1
+fi
+
+VERSION=$(cat "$VERSION_FILE" | tr -d '[:space:]')
+
+if [[ -z "$VERSION" ]]; then
+ echo "ERROR: VERSION file is empty" >&2
+ exit 1
+fi
+
+# Check if components.yaml exists
+if [[ ! -f "$COMPONENTS_FILE" ]]; then
+ echo "ERROR: components.yaml not found at $COMPONENTS_FILE" >&2
+ exit 1
+fi
+
+GLK_COMPONENT_NAME="github.com/gardener/gardener-landscape-kit"
+REPO_URL="https://$GLK_COMPONENT_NAME"
+
+# Check if the GLK component already exists in the file
+if [[ $(yq eval '[.components[] | select(.name == "'"$GLK_COMPONENT_NAME"'")] | length' "$COMPONENTS_FILE") -gt 0 ]]; then
+ # Component exists - update its version
+ yq eval -i --indent 2 -c "(.components[] | select(.name == \"$GLK_COMPONENT_NAME\") | .version) = \"$VERSION\"" "$COMPONENTS_FILE"
+ echo "Updated $GLK_COMPONENT_NAME component version to $VERSION"
+else
+ # Component doesn't exist - prepend it as the first entry
+ yq eval -i --indent 2 -c ".components = [{\"name\": \"$GLK_COMPONENT_NAME\", \"sourceRepository\": \"$REPO_URL\", \"version\": \"$VERSION\"}] + .components" "$COMPONENTS_FILE"
+ echo "Added $GLK_COMPONENT_NAME component with version $VERSION as first entry"
+fi
diff --git a/pkg/apis/config/v1alpha1/types.go b/pkg/apis/config/v1alpha1/types.go
index a6c4207..2e0d075 100644
--- a/pkg/apis/config/v1alpha1/types.go
+++ b/pkg/apis/config/v1alpha1/types.go
@@ -117,12 +117,33 @@ var AllowedDefaultVersionsUpdateStrategies = []string{
string(DefaultVersionsUpdateStrategyDisabled),
}
+// VersionCheckMode controls the behavior when the tool version doesn't match the component version.
+type VersionCheckMode string
+
+const (
+ // VersionCheckModeStrict indicates that version mismatches should cause an error.
+ VersionCheckModeStrict VersionCheckMode = "Strict"
+ // VersionCheckModeWarning indicates that version mismatches should only log a warning.
+ VersionCheckModeWarning VersionCheckMode = "Warning"
+)
+
+// AllowedVersionCheckModes lists all allowed version check modes.
+var AllowedVersionCheckModes = []string{
+ string(VersionCheckModeStrict),
+ string(VersionCheckModeWarning),
+}
+
// VersionConfiguration contains configuration for versioning.
type VersionConfiguration struct {
// UpdateStrategy determines whether the versions in the default vector should be updated from the release branch on resolve.
// Possible values are "Disabled" (default) and "ReleaseBranch".
// +optional
DefaultVersionsUpdateStrategy *DefaultVersionsUpdateStrategy `json:"defaultVersionsUpdateStrategy,omitempty"`
+ // CheckMode determines the behavior when the tool version doesn't match the gardener-landscape-kit version in the component vector.
+ // Possible values are "Strict" (default) and "Warning".
+ // In strict mode, version mismatches cause errors. In warning mode, only warnings are logged.
+ // +optional
+ CheckMode *VersionCheckMode `json:"checkMode,omitempty"`
}
// MergeMode controls how operator overwrites are handled during three-way merge.
diff --git a/pkg/apis/config/v1alpha1/validation/validation.go b/pkg/apis/config/v1alpha1/validation/validation.go
index 704cee8..65a7b47 100644
--- a/pkg/apis/config/v1alpha1/validation/validation.go
+++ b/pkg/apis/config/v1alpha1/validation/validation.go
@@ -169,5 +169,9 @@ func ValidateVersionConfig(conf *configv1alpha1.VersionConfiguration, fldPath *f
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultVersionsUpdateStrategy"), *conf.DefaultVersionsUpdateStrategy, "allowed values are: "+strings.Join(configv1alpha1.AllowedDefaultVersionsUpdateStrategies, ", ")))
}
+ if conf.CheckMode != nil && !slices.Contains(configv1alpha1.AllowedVersionCheckModes, string(*conf.CheckMode)) {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("checkMode"), *conf.CheckMode, "allowed values are: "+strings.Join(configv1alpha1.AllowedVersionCheckModes, ", ")))
+ }
+
return allErrs
}
diff --git a/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go
index 3ce6502..02f7197 100644
--- a/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go
@@ -200,6 +200,11 @@ func (in *VersionConfiguration) DeepCopyInto(out *VersionConfiguration) {
*out = new(DefaultVersionsUpdateStrategy)
**out = **in
}
+ if in.CheckMode != nil {
+ in, out := &in.CheckMode, &out.CheckMode
+ *out = new(VersionCheckMode)
+ **out = **in
+ }
return
}
diff --git a/pkg/cmd/generate/base/base.go b/pkg/cmd/generate/base/base.go
index 48dd8eb..edc991f 100644
--- a/pkg/cmd/generate/base/base.go
+++ b/pkg/cmd/generate/base/base.go
@@ -56,6 +56,10 @@ func run(_ context.Context, opts *options.Options) error {
return fmt.Errorf("failed to register components: %w", err)
}
+ if err := version.CheckGLKComponentVersion(componentOpts.GetComponentVector(), opts.Config, opts.Log); err != nil {
+ return fmt.Errorf("version check failed: %w", err)
+ }
+
if err := reg.GenerateBase(componentOpts); err != nil {
return err
}
diff --git a/pkg/cmd/generate/landscape/landscape.go b/pkg/cmd/generate/landscape/landscape.go
index cc84505..82843ba 100644
--- a/pkg/cmd/generate/landscape/landscape.go
+++ b/pkg/cmd/generate/landscape/landscape.go
@@ -77,6 +77,10 @@ func run(_ context.Context, opts *options.Options) error {
return fmt.Errorf("failed to create component options: %w", err)
}
+ if err := version.CheckGLKComponentVersion(componentOpts.GetComponentVector(), opts.Config, opts.Log); err != nil {
+ return fmt.Errorf("version validation failed: %w", err)
+ }
+
reg := registry.New()
if err := registry.RegisterAllComponents(reg, opts.Config); err != nil {
return fmt.Errorf("failed to register components: %w", err)
diff --git a/pkg/components/gardener/landscape-kit/component.go b/pkg/components/gardener/landscape-kit/component.go
new file mode 100644
index 0000000..ec1631d
--- /dev/null
+++ b/pkg/components/gardener/landscape-kit/component.go
@@ -0,0 +1,10 @@
+// SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package landscapekit
+
+const (
+ // ComponentName is the name of the gardener-landscape-kit component.
+ ComponentName = "gardener-landscape-kit"
+)
diff --git a/pkg/utils/version/metadata.go b/pkg/utils/version/metadata.go
index 69a73c7..70d7963 100644
--- a/pkg/utils/version/metadata.go
+++ b/pkg/utils/version/metadata.go
@@ -12,10 +12,14 @@ import (
"strings"
"github.com/Masterminds/semver/v3"
+ "github.com/go-logr/logr"
"github.com/spf13/afero"
apimachineryversion "k8s.io/apimachinery/pkg/version"
componentbaseversion "k8s.io/component-base/version"
+ "github.com/gardener/gardener-landscape-kit/componentvector"
+ configv1alpha1 "github.com/gardener/gardener-landscape-kit/pkg/apis/config/v1alpha1"
+ utilscomponentvector "github.com/gardener/gardener-landscape-kit/pkg/utils/componentvector"
"github.com/gardener/gardener-landscape-kit/pkg/utils/files"
)
@@ -139,6 +143,39 @@ func ValidateLandscapeVersionCompatibility(targetPath string, fs afero.Afero) er
return ValidateVersionCompatibility(baseMetadata.Version, version.GitVersion)
}
+// CheckGLKComponentVersion validates that the tool version matches the gardener-landscape-kit
+// component version in the component vector.
+// The behavior depends on the checkMode in the configuration:
+// - If checkMode is "Strict" (or nil/default), returns an error on mismatch.
+// - If checkMode is "Warning", logs a warning on mismatch and returns nil.
+func CheckGLKComponentVersion(cv utilscomponentvector.Interface, config *configv1alpha1.LandscapeKitConfiguration, log logr.Logger) error {
+ toolVersion := version.GitVersion
+ componentVersion, found := cv.FindComponentVersion(componentvector.NameGardenerGardenerLandscapeKit)
+
+ if !found {
+ return fmt.Errorf("gardener-landscape-kit component not found in component vector - this should not happen as it's part of the default component vector")
+ }
+
+ if toolVersion != componentVersion {
+ // Determine the check mode (default to Strict)
+ checkMode := configv1alpha1.VersionCheckModeStrict
+ if config != nil && config.VersionConfig != nil && config.VersionConfig.CheckMode != nil {
+ checkMode = *config.VersionConfig.CheckMode
+ }
+
+ err := fmt.Errorf("version mismatch: tool version (%s) does not match gardener-landscape-kit component version (%s) in component vector - obtain the matching gardener-landscape-kit version or adjust the component vector", toolVersion, componentVersion)
+
+ if checkMode == configv1alpha1.VersionCheckModeWarning {
+ log.Error(err, "Precheck failed")
+ return nil
+ }
+
+ return err
+ }
+
+ return nil
+}
+
// Get returns the version of GLK.
func Get() apimachineryversion.Info {
return version
diff --git a/pkg/utils/version/metadata_test.go b/pkg/utils/version/metadata_test.go
index 49e929e..5ff57bd 100644
--- a/pkg/utils/version/metadata_test.go
+++ b/pkg/utils/version/metadata_test.go
@@ -8,10 +8,14 @@ import (
"encoding/json"
"path/filepath"
+ "github.com/go-logr/logr"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/spf13/afero"
+ "sigs.k8s.io/controller-runtime/pkg/log/zap"
+ configv1alpha1 "github.com/gardener/gardener-landscape-kit/pkg/apis/config/v1alpha1"
+ "github.com/gardener/gardener-landscape-kit/pkg/utils/componentvector"
"github.com/gardener/gardener-landscape-kit/pkg/utils/version"
)
@@ -231,4 +235,152 @@ var _ = Describe("Version Metadata", func() {
Expect(err).To(MatchError(ContainSubstring("is newer than base generation version")))
})
})
+
+ Describe("#CheckGLKComponentVersion", func() {
+ var log logr.Logger
+
+ BeforeEach(func() {
+ log = zap.New(zap.WriteTo(GinkgoWriter))
+ })
+
+ type testCase struct {
+ componentVersion string
+ checkMode *configv1alpha1.VersionCheckMode
+ expectError bool
+ errorContains []string
+ }
+
+ DescribeTable("version checking behavior",
+ func(tc testCase) {
+ baseYAML := []byte(`
+components:
+ - name: github.com/gardener/gardener-landscape-kit
+ sourceRepository: https://github.com/gardener/gardener-landscape-kit
+ version: ` + tc.componentVersion + `
+`)
+ cv, err := componentvector.NewWithOverride(baseYAML)
+ Expect(err).NotTo(HaveOccurred())
+
+ var config *configv1alpha1.LandscapeKitConfiguration
+ if tc.checkMode != nil {
+ config = &configv1alpha1.LandscapeKitConfiguration{
+ VersionConfig: &configv1alpha1.VersionConfiguration{
+ CheckMode: tc.checkMode,
+ },
+ }
+ }
+
+ err = version.CheckGLKComponentVersion(cv, config, log)
+
+ if tc.expectError {
+ Expect(err).To(HaveOccurred())
+ for _, substr := range tc.errorContains {
+ Expect(err.Error()).To(ContainSubstring(substr))
+ }
+ } else {
+ Expect(err).NotTo(HaveOccurred())
+ }
+ },
+ Entry("should pass when versions match with nil config (default strict)",
+ testCase{
+ componentVersion: version.Get().GitVersion,
+ checkMode: nil,
+ expectError: false,
+ }),
+ Entry("should pass when versions match in strict mode",
+ testCase{
+ componentVersion: version.Get().GitVersion,
+ checkMode: ptr(configv1alpha1.VersionCheckModeStrict),
+ expectError: false,
+ }),
+ Entry("should pass when versions match in warning mode",
+ testCase{
+ componentVersion: version.Get().GitVersion,
+ checkMode: ptr(configv1alpha1.VersionCheckModeWarning),
+ expectError: false,
+ }),
+ Entry("should fail when versions differ in strict mode",
+ testCase{
+ componentVersion: "v0.99.99-test",
+ checkMode: ptr(configv1alpha1.VersionCheckModeStrict),
+ expectError: true,
+ errorContains: []string{"version mismatch", version.Get().GitVersion, "v0.99.99-test"},
+ }),
+ Entry("should not fail when versions differ in warning mode",
+ testCase{
+ componentVersion: "v0.99.99-test",
+ checkMode: ptr(configv1alpha1.VersionCheckModeWarning),
+ expectError: false,
+ }),
+ Entry("should use exact string matching - v0.2.0-dev vs v0.2.0 in strict mode",
+ func() testCase {
+ currentVersion := version.Get().GitVersion
+ var differentButRelated string
+ if currentVersion == "v0.2.0-dev" {
+ differentButRelated = "v0.2.0"
+ } else {
+ differentButRelated = currentVersion + "-modified"
+ }
+ return testCase{
+ componentVersion: differentButRelated,
+ checkMode: ptr(configv1alpha1.VersionCheckModeStrict),
+ expectError: true,
+ errorContains: []string{"version mismatch"},
+ }
+ }()),
+ Entry("should use exact string matching - v0.2.0-dev vs v0.2.0 in warning mode",
+ func() testCase {
+ currentVersion := version.Get().GitVersion
+ var differentButRelated string
+ if currentVersion == "v0.2.0-dev" {
+ differentButRelated = "v0.2.0"
+ } else {
+ differentButRelated = currentVersion + "-modified"
+ }
+ return testCase{
+ componentVersion: differentButRelated,
+ checkMode: ptr(configv1alpha1.VersionCheckModeWarning),
+ expectError: false,
+ }
+ }()),
+ )
+
+ It("should fail when GLK component is not found in both modes", func() {
+ baseYAML := []byte(`
+components:
+ - name: github.com/gardener/other-component
+ sourceRepository: https://github.com/gardener/other-component
+ version: v1.0.0
+`)
+ cv, err := componentvector.NewWithOverride(baseYAML)
+ Expect(err).NotTo(HaveOccurred())
+
+ // Test strict mode
+ strictMode := configv1alpha1.VersionCheckModeStrict
+ strictConfig := &configv1alpha1.LandscapeKitConfiguration{
+ VersionConfig: &configv1alpha1.VersionConfiguration{
+ CheckMode: &strictMode,
+ },
+ }
+
+ err = version.CheckGLKComponentVersion(cv, strictConfig, log)
+ Expect(err).To(MatchError(ContainSubstring("gardener-landscape-kit component not found")))
+
+ // Test warning mode
+ warningMode := configv1alpha1.VersionCheckModeWarning
+ warningConfig := &configv1alpha1.LandscapeKitConfiguration{
+ VersionConfig: &configv1alpha1.VersionConfiguration{
+ CheckMode: &warningMode,
+ },
+ }
+
+ err = version.CheckGLKComponentVersion(cv, warningConfig, log)
+ Expect(err).To(MatchError(ContainSubstring("gardener-landscape-kit component not found")))
+ })
+ })
})
+
+// ptr is a helper function to create a pointer to a value
+func ptr[T any](v T) *T {
+ return &v
+}