Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/compatibility.yml
Original file line number Diff line number Diff line change
@@ -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 }}"
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Matrix keys containing hyphens (e.g., controller-runtime) can't be accessed via dot-notation in GitHub Actions expressions. matrix.controller-runtime will be parsed incorrectly and the workflow will fail. Rename the matrix key(s) (e.g., controller_runtime) or use bracket notation like matrix['controller-runtime'].

Suggested change
name: "CR v${{ matrix.controller-runtime }} / k8s v${{ matrix.k8s }}"
name: "CR v${{ matrix['controller-runtime'] }} / k8s v${{ matrix.k8s }}"

Copilot uses AI. Check for mistakes.
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 }}
Comment on lines +44 to +48
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These expressions reference hyphenated matrix keys (cr-version, k8s-version) using dot-notation (e.g., matrix.cr-version), which will not evaluate correctly in GitHub Actions. Rename the keys (e.g., cr_version, k8s_version) or switch to bracket notation (matrix['cr-version'], matrix['k8s-version']) for all usages in this step.

Suggested change
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 }}
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'] }}

Copilot uses AI. Check for mistakes.
go mod tidy

- name: Verify compilation
run: go build ./...

- name: Build examples
run: make build-examples

- name: Run tests
run: make test
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand All @@ -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 ./...
Expand Down
65 changes: 65 additions & 0 deletions docs/compatibility.md
Original file line number Diff line number Diff line change
@@ -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.
Loading