Skip to content

Instantly share code, notes, and snippets.

@adamwg
Created October 31, 2025 19:40
Show Gist options
  • Select an option

  • Save adamwg/57c1ba73cab61a9fb041c4cc9aec18f9 to your computer and use it in GitHub Desktop.

Select an option

Save adamwg/57c1ba73cab61a9fb041c4cc9aec18f9 to your computer and use it in GitHub Desktop.
Crossplane DevEx: Core and Plugins

Crossplane DevEx: Core and Plugins

I've been thinking about the following two things:

  1. What is most valuable and unique in the Upbound DevEx? I think it's the concept of a Project as an on-disk development artifact encompassing both configuration (XRDs, compositions, operations) and code (functions, tests). Separating the development artifact (project) from the artifact that gets installed into a cluster (xpkg) is powerful.
  2. It sounds like a lot of folks have built their own test frameworks with overlapping but distinct feature sets. I don't think our (Upbound) implementation of tests is clearly better or worse than the others: the differences are partly a matter of taste and partly the result of different requirements.

Projects in the Core

We could standardize the concept of a Project in Crossplane, and implement the basic tooling for building a Project into a set of XPKGs into the core of the CLI. This would mean standardizing:

  • A metadata file describing a project (like the current upbound.yaml).
  • A basic directory layout for projects.
  • How a project gets built into a set of XPKGs (dependency structure etc.).

The core of the CLI would have a set of commands for working with projects, like:

  • crossplane project init
  • crossplane project build
  • crossplane project push
  • crossplane project test
  • crossplane project run

The build, test, and run commands would call out to plugins to build functions, run tests, and spin up clusters rather than having these features built into the CLI core. This makes the CLI extensible to suit different opinions and requirements while leveraging a common core to avoid duplicated work and help unify the ecosystem.

Plugins for the Details

The core would not include:

  • Code for building functions.
  • A test definition or code for running tests.
  • Code for spinning up test clusters.

These concerns, where there could be many different implementations, would be left to plugins. The core could still include libraries to help with plugin development (e.g., utilities for building compliant xpkgs, a library-only version of render), but actual implementation would be left to plugins.

A couple of examples of how this might work:

  • crossplane project build would call out to a plugin (probably a separate executable) to build each function. The contract between the core CLI and the plugin might be that the plugin writes a function runtime image to an OCI layout in a particular location, which the core CLI would then consume to build into an xpkg.
  • crossplane project test would call out to plugins to spin up a cluster (for e2e tests) and to execute tests; the plugin would be provided with the test configuration file (all or part of which could be specific to the plugin), and would write results to stdout in a standardized format for the CLI to consume and present to the user.
  • crossplane project run would call out to a plugin to spin up a cluster. The plugin would return a kubeconfig on stdout, which the core CLI would then use to install the packages.
@jcogilvie
Copy link

I mostly like this idea, although I think we should be sure to provide a default plugin set for testing and running if no override is provided.

As an ecosystem, I think it's okay to have an opinion about tests. And I think the proliferation of different implementations mostly speaks to the lack of a cohesive approach upstream when we were all implementing our own test stacks. If there were one golden thing, we'd probably all have gone with that and as a community extended it until it met our requirements.

Speaking for myself, I went with the helm -> helm postRender -> crossplane render pipeline because i was familiar with helm-unittest and it was the path of least resistance. But if a sensible upstream implementation had been the path of least resistance, I'd have used it instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment