diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml new file mode 100644 index 00000000..19ff819f --- /dev/null +++ b/.github/workflows/compatibility.yml @@ -0,0 +1,58 @@ +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 + 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 }} + 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..d5379812 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.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 @@ -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,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 [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 new file mode 100644 index 00000000..08d60343 --- /dev/null +++ b/docs/compatibility.md @@ -0,0 +1,65 @@ +# 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 | + +**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 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 + +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. + +## 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, 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.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 +) +``` + +`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 +and passes tests.