From c936458efb9c35df7794098c1e4673ff2340acd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86gir=20M=C3=A1ni=20Hauksson?= <54936225+sourcehawk@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:03:21 +0000 Subject: [PATCH 1/6] add compatibility tests and docs --- .github/workflows/compatibility.yml | 79 +++++++++++++++++++++++++++++ README.md | 6 ++- docs/compatibility.md | 69 +++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/compatibility.yml create mode 100644 docs/compatibility.md diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml new file mode 100644 index 00000000..fb354548 --- /dev/null +++ b/.github/workflows/compatibility.yml @@ -0,0 +1,79 @@ +name: Compatibility + +on: + schedule: + - cron: "0 8 * * 1" # Every Monday at 08:00 UTC + workflow_dispatch: + pull_request: + types: [labeled] + +jobs: + compatibility: + if: >- + github.event_name != 'pull_request' || + github.event.label.name == 'compatibility' + name: "CR v${{ matrix.controller-runtime }} / k8s v${{ matrix.k8s }}" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - controller-runtime: "0.23" + cr-version: "v0.23.3" + k8s: "0.35" + k8s-version: "v0.35.2" + current: true + - controller-runtime: "0.22" + cr-version: "v0.22.0" + k8s: "0.34" + k8s-version: "v0.34.0" + current: false + - controller-runtime: "0.21" + cr-version: "v0.21.0" + k8s: "0.33" + k8s-version: "v0.33.0" + current: false + - controller-runtime: "0.20" + cr-version: "v0.20.0" + k8s: "0.32" + k8s-version: "v0.32.0" + current: false + - controller-runtime: "0.19" + cr-version: "v0.19.0" + k8s: "0.31" + k8s-version: "v0.31.0" + current: false + - controller-runtime: "0.18" + cr-version: "v0.18.0" + k8s: "0.30" + k8s-version: "v0.30.0" + current: false + steps: + - name: Clone the code + uses: actions/checkout@v5 + + - name: Setup Go + uses: actions/setup-go@v6 + with: + go-version-file: go.mod + + - name: Swap dependencies + if: ${{ !matrix.current }} + run: | + go get \ + sigs.k8s.io/controller-runtime@${{ matrix.cr-version }} \ + k8s.io/api@${{ matrix.k8s-version }} \ + k8s.io/apimachinery@${{ matrix.k8s-version }} \ + k8s.io/client-go@${{ matrix.k8s-version }} \ + k8s.io/apiextensions-apiserver@${{ matrix.k8s-version }} \ + k8s.io/utils@latest + go mod tidy + + - name: Verify compilation + run: go build ./... + + - name: Build examples + run: make build-examples + + - name: Run tests + run: make test diff --git a/README.md b/README.md index 99892de6..8c7b69b1 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,8 @@ Controller go get github.com/sourcehawk/operator-component-framework ``` -Requires Go 1.25.6+ and a project using [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime). +Requires Go 1.25+ and [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) v0.18 or later. See +[Compatibility](docs/compatibility.md) for the full support matrix. ## Quick Start @@ -227,6 +228,7 @@ See the [Custom Resource Implementation Guide](docs/custom-resource.md) for a co | [Component Framework](docs/component.md) | Reconciliation lifecycle, condition model, grace periods, suspension | | [Resource Primitives](docs/primitives.md) | Primitive categories, Server-Side Apply, mutation system | | [Custom Resources](docs/custom-resource.md) | Implementing custom resource wrappers using the generic building blocks | +| [Compatibility](docs/compatibility.md) | Supported Kubernetes and controller-runtime versions, version policy | ## Contributing @@ -237,7 +239,7 @@ Contributions are welcome. Please open an issue to discuss significant changes b 3. Commit your changes 4. Open a pull request against `main` -All new code should include tests. The project uses [Ginkgo](https://github.com/onsi/ginkgo) and +All new code should include tests. The project uses [testify](https://github.com/stretchr/testify), [Ginkgo](https://github.com/onsi/ginkgo) and [Gomega](https://github.com/onsi/gomega) for testing. ```bash diff --git a/docs/compatibility.md b/docs/compatibility.md new file mode 100644 index 00000000..d6ae5659 --- /dev/null +++ b/docs/compatibility.md @@ -0,0 +1,69 @@ +# Compatibility + +## Supported Versions + +The framework is tested against the following version combinations: + +| Framework | controller-runtime | k8s.io/\* | Kubernetes | Go | Status | +| --------- | ------------------ | --------- | ---------- | ---- | ------- | +| main | v0.23.x | v0.35.x | 1.35 | 1.25 | Primary | +| main | v0.22.x | v0.34.x | 1.34 | 1.25 | Tested | +| main | v0.21.x | v0.33.x | 1.33 | 1.25 | Tested | +| main | v0.20.x | v0.32.x | 1.32 | 1.25 | Tested | +| main | v0.19.x | v0.31.x | 1.31 | 1.25 | Tested | +| main | v0.18.x | v0.30.x | 1.30 | 1.25 | Tested | + +**Primary** is the version combination used in `go.mod` and in the main CI pipeline. **Tested** versions are verified +weekly by the compatibility CI workflow. + +## Version Policy + +The framework targets the latest stable controller-runtime release as its primary dependency. Compatibility is tested +against the five prior controller-runtime minor versions, back to v0.18 (Kubernetes 1.30). When a new Kubernetes minor +version is released and controller-runtime publishes a matching release, the oldest tested version is dropped from the +matrix but may still be supported. + +## How Compatibility Is Tested + +The [compatibility workflow](../.github/workflows/compatibility.yml) runs weekly and on demand. For each version +combination in the matrix, it: + +1. Swaps the `controller-runtime` and `k8s.io/*` dependencies to the target versions using `go get`. +2. Runs `go mod tidy` to resolve transitive dependencies. +3. Verifies that the entire module compiles (`go build ./...`). +4. Builds all examples (`make build-examples`). +5. Runs the full unit and envtest test suite (`make test`). + +The Makefile automatically detects the correct envtest binary version from the `k8s.io/api` module version, so no manual +configuration is needed when testing against different Kubernetes versions. + +## Pinning Your Kubernetes and controller-runtime Versions + +When you `go get` this framework, Go's [Minimum Version Selection](https://go.dev/ref/mod#minimal-version-selection) +(MVS) will pull your `controller-runtime` and `k8s.io/*` dependencies up to at least the versions declared in the +framework's `go.mod`. If you are already on newer versions, Go will keep yours. But if you are on older versions, MVS +will bump them. + +To prevent this and stay on your current versions, pin them in your `go.mod` **after** adding the framework: + +```bash +# 1. Add the framework +go get github.com/sourcehawk/operator-component-framework@latest + +# 2. Pin your desired controller-runtime and k8s versions +go get sigs.k8s.io/controller-runtime@v0.19.0 \ + k8s.io/api@v0.31.0 \ + k8s.io/apimachinery@v0.31.0 \ + k8s.io/client-go@v0.31.0 \ + k8s.io/apiextensions-apiserver@v0.31.0 + +# 3. Clean up +go mod tidy +``` + +The `go get` calls in step 2 write explicit `require` directives into your `go.mod`, which override MVS for those +modules. As long as those directives remain, future `go get` of the framework will not bump them. + +This works because the framework's public API surface uses abstract interfaces (`client.Object`, `client.Client`) that +remain stable across controller-runtime minor versions. The compatibility CI verifies that this downgrade path compiles +and passes tests. From 6f10e93a4ae05fe7b6107c7d138ef5177507d774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86gir=20M=C3=A1ni=20Hauksson?= <54936225+sourcehawk@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:10:34 +0000 Subject: [PATCH 2/6] replace directives --- README.md | 4 ++-- docs/compatibility.md | 30 +++++++++++++----------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 8c7b69b1..ab2958a8 100644 --- a/README.md +++ b/README.md @@ -239,8 +239,8 @@ Contributions are welcome. Please open an issue to discuss significant changes b 3. Commit your changes 4. Open a pull request against `main` -All new code should include tests. The project uses [testify](https://github.com/stretchr/testify), [Ginkgo](https://github.com/onsi/ginkgo) and -[Gomega](https://github.com/onsi/gomega) for testing. +All new code should include tests. The project uses [testify](https://github.com/stretchr/testify), +[Ginkgo](https://github.com/onsi/ginkgo) and [Gomega](https://github.com/onsi/gomega) for testing. ```bash go test ./... diff --git a/docs/compatibility.md b/docs/compatibility.md index d6ae5659..ca8ae0cb 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -44,25 +44,21 @@ When you `go get` this framework, Go's [Minimum Version Selection](https://go.de framework's `go.mod`. If you are already on newer versions, Go will keep yours. But if you are on older versions, MVS will bump them. -To prevent this and stay on your current versions, pin them in your `go.mod` **after** adding the framework: - -```bash -# 1. Add the framework -go get github.com/sourcehawk/operator-component-framework@latest - -# 2. Pin your desired controller-runtime and k8s versions -go get sigs.k8s.io/controller-runtime@v0.19.0 \ - k8s.io/api@v0.31.0 \ - k8s.io/apimachinery@v0.31.0 \ - k8s.io/client-go@v0.31.0 \ - k8s.io/apiextensions-apiserver@v0.31.0 - -# 3. Clean up -go mod tidy +To prevent this, add `replace` directives to your `go.mod` that pin the versions you need: + +```go +// go.mod +replace ( + sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.19.0 + k8s.io/api => k8s.io/api v0.31.0 + k8s.io/apimachinery => k8s.io/apimachinery v0.31.0 + k8s.io/client-go => k8s.io/client-go v0.31.0 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.31.0 +) ``` -The `go get` calls in step 2 write explicit `require` directives into your `go.mod`, which override MVS for those -modules. As long as those directives remain, future `go get` of the framework will not bump them. +`replace` directives override MVS regardless of what the framework's `go.mod` declares. After adding the directives, run +`go mod tidy` to update the dependency graph. This works because the framework's public API surface uses abstract interfaces (`client.Object`, `client.Client`) that remain stable across controller-runtime minor versions. The compatibility CI verifies that this downgrade path compiles From 2e4c85ba10720b8b5e5d9d3f819981aa4f745017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86gir=20M=C3=A1ni=20Hauksson?= <54936225+sourcehawk@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:15:10 +0000 Subject: [PATCH 3/6] address comments --- .github/workflows/compatibility.yml | 3 +-- README.md | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml index fb354548..3ee7f539 100644 --- a/.github/workflows/compatibility.yml +++ b/.github/workflows/compatibility.yml @@ -65,8 +65,7 @@ jobs: k8s.io/api@${{ matrix.k8s-version }} \ k8s.io/apimachinery@${{ matrix.k8s-version }} \ k8s.io/client-go@${{ matrix.k8s-version }} \ - k8s.io/apiextensions-apiserver@${{ matrix.k8s-version }} \ - k8s.io/utils@latest + k8s.io/apiextensions-apiserver@${{ matrix.k8s-version }} go mod tidy - name: Verify compilation diff --git a/README.md b/README.md index ab2958a8..04168b27 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Controller go get github.com/sourcehawk/operator-component-framework ``` -Requires Go 1.25+ and [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) v0.18 or later. See +Requires Go 1.25.6+ and [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) v0.18 or later. See [Compatibility](docs/compatibility.md) for the full support matrix. ## Quick Start From 9d2abfe0b275abca1652a3d302218e5b03338735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86gir=20M=C3=A1ni=20Hauksson?= <54936225+sourcehawk@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:16:39 +0000 Subject: [PATCH 4/6] test the workflow --- .github/workflows/compatibility.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml index 3ee7f539..1a62dcca 100644 --- a/.github/workflows/compatibility.yml +++ b/.github/workflows/compatibility.yml @@ -5,13 +5,15 @@ on: - cron: "0 8 * * 1" # Every Monday at 08:00 UTC workflow_dispatch: pull_request: - types: [labeled] + # TODO: remove pull_request trigger after verifying the workflow, keep only labeled + # types: [labeled] jobs: compatibility: - if: >- - github.event_name != 'pull_request' || - github.event.label.name == 'compatibility' + # TODO: restore label check after verifying the workflow + # if: >- + # github.event_name != 'pull_request' || + # github.event.label.name == 'compatibility' name: "CR v${{ matrix.controller-runtime }} / k8s v${{ matrix.k8s }}" runs-on: ubuntu-latest strategy: From 5a0ce8b6512a17f1ea0e566322fade898b58c937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86gir=20M=C3=A1ni=20Hauksson?= <54936225+sourcehawk@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:44:00 +0000 Subject: [PATCH 5/6] compatibility is 0.22+ --- .github/workflows/compatibility.yml | 20 -------------------- README.md | 2 +- docs/compatibility.md | 23 +++++++++++------------ 3 files changed, 12 insertions(+), 33 deletions(-) diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml index 1a62dcca..e96b7fc3 100644 --- a/.github/workflows/compatibility.yml +++ b/.github/workflows/compatibility.yml @@ -30,26 +30,6 @@ jobs: k8s: "0.34" k8s-version: "v0.34.0" current: false - - controller-runtime: "0.21" - cr-version: "v0.21.0" - k8s: "0.33" - k8s-version: "v0.33.0" - current: false - - controller-runtime: "0.20" - cr-version: "v0.20.0" - k8s: "0.32" - k8s-version: "v0.32.0" - current: false - - controller-runtime: "0.19" - cr-version: "v0.19.0" - k8s: "0.31" - k8s-version: "v0.31.0" - current: false - - controller-runtime: "0.18" - cr-version: "v0.18.0" - k8s: "0.30" - k8s-version: "v0.30.0" - current: false steps: - name: Clone the code uses: actions/checkout@v5 diff --git a/README.md b/README.md index 04168b27..d5379812 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Controller go get github.com/sourcehawk/operator-component-framework ``` -Requires Go 1.25.6+ and [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) v0.18 or later. See +Requires Go 1.25.6+ and [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) v0.22 or later. See [Compatibility](docs/compatibility.md) for the full support matrix. ## Quick Start diff --git a/docs/compatibility.md b/docs/compatibility.md index ca8ae0cb..2f730378 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -8,10 +8,6 @@ The framework is tested against the following version combinations: | --------- | ------------------ | --------- | ---------- | ---- | ------- | | main | v0.23.x | v0.35.x | 1.35 | 1.25 | Primary | | main | v0.22.x | v0.34.x | 1.34 | 1.25 | Tested | -| main | v0.21.x | v0.33.x | 1.33 | 1.25 | Tested | -| main | v0.20.x | v0.32.x | 1.32 | 1.25 | Tested | -| main | v0.19.x | v0.31.x | 1.31 | 1.25 | Tested | -| main | v0.18.x | v0.30.x | 1.30 | 1.25 | Tested | **Primary** is the version combination used in `go.mod` and in the main CI pipeline. **Tested** versions are verified weekly by the compatibility CI workflow. @@ -19,9 +15,12 @@ weekly by the compatibility CI workflow. ## Version Policy The framework targets the latest stable controller-runtime release as its primary dependency. Compatibility is tested -against the five prior controller-runtime minor versions, back to v0.18 (Kubernetes 1.30). When a new Kubernetes minor -version is released and controller-runtime publishes a matching release, the oldest tested version is dropped from the -matrix but may still be supported. +against prior controller-runtime minor versions where transitive dependencies remain compatible. When a new Kubernetes +minor version is released and controller-runtime publishes a matching release, the matrix is updated accordingly. + +Versions v0.21 and below are incompatible due to multiple transitive dependency module path migrations in the Kubernetes +ecosystem (`structured-merge-diff` v4 to v6, `yaml` package path changes) that cannot be resolved by swapping direct +dependencies alone. ## How Compatibility Is Tested @@ -49,11 +48,11 @@ To prevent this, add `replace` directives to your `go.mod` that pin the versions ```go // go.mod replace ( - sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.19.0 - k8s.io/api => k8s.io/api v0.31.0 - k8s.io/apimachinery => k8s.io/apimachinery v0.31.0 - k8s.io/client-go => k8s.io/client-go v0.31.0 - k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.31.0 + sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.22.0 + k8s.io/api => k8s.io/api v0.34.0 + k8s.io/apimachinery => k8s.io/apimachinery v0.34.0 + k8s.io/client-go => k8s.io/client-go v0.34.0 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.34.0 ) ``` From 6f9a325c2d4578114c1f7244dbd159b83793eb0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86gir=20M=C3=A1ni=20Hauksson?= <54936225+sourcehawk@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:49:19 +0000 Subject: [PATCH 6/6] remove temp workflow check --- .github/workflows/compatibility.yml | 10 ++++------ docs/compatibility.md | 17 +++++++++-------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml index e96b7fc3..19ff819f 100644 --- a/.github/workflows/compatibility.yml +++ b/.github/workflows/compatibility.yml @@ -5,15 +5,13 @@ on: - cron: "0 8 * * 1" # Every Monday at 08:00 UTC workflow_dispatch: pull_request: - # TODO: remove pull_request trigger after verifying the workflow, keep only labeled - # types: [labeled] + types: [labeled] jobs: compatibility: - # TODO: restore label check after verifying the workflow - # if: >- - # github.event_name != 'pull_request' || - # github.event.label.name == 'compatibility' + if: >- + github.event_name != 'pull_request' || + github.event.label.name == 'compatibility' name: "CR v${{ matrix.controller-runtime }} / k8s v${{ matrix.k8s }}" runs-on: ubuntu-latest strategy: diff --git a/docs/compatibility.md b/docs/compatibility.md index 2f730378..08d60343 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -24,14 +24,15 @@ dependencies alone. ## How Compatibility Is Tested -The [compatibility workflow](../.github/workflows/compatibility.yml) runs weekly and on demand. For each version -combination in the matrix, it: - -1. Swaps the `controller-runtime` and `k8s.io/*` dependencies to the target versions using `go get`. -2. Runs `go mod tidy` to resolve transitive dependencies. -3. Verifies that the entire module compiles (`go build ./...`). -4. Builds all examples (`make build-examples`). -5. Runs the full unit and envtest test suite (`make test`). +The [compatibility workflow](../.github/workflows/compatibility.yml) runs weekly on a schedule, on manual dispatch, and +on pull requests labeled `compatibility`. For each version combination in the matrix, it: + +1. Swaps the `controller-runtime` and `k8s.io/*` dependencies to the target versions using `go get`, then runs + `go mod tidy` to resolve transitive dependencies. This step is skipped for the primary (current `go.mod`) entry, + which is tested as-is. +2. Verifies that the entire module compiles (`go build ./...`). +3. Builds all examples (`make build-examples`). +4. Runs the full unit and envtest test suite (`make test`). The Makefile automatically detects the correct envtest binary version from the `k8s.io/api` module version, so no manual configuration is needed when testing against different Kubernetes versions.