Releasing Crossplane Extensions

This document is for a preview version of Crossplane.

This document applies to Crossplane v2.0-preview and not to the latest release v1.19.

Don't use Crossplane v2.0-preview in production.

Crossplane extensions are built as OCI images in the xpkg format. Authors and maintainers of Crossplane extensions must push their packages to an OCI registry before they can be used and referenced by Crossplane.

Tip
For more information about Crossplane packages, review the concepts here.

Typical Workflow

This guide covers configuring a GitHub Action for building Crossplane providers and functions and pushing them to an OCI registry such as ghcr.io.

A typical GitHub workflow definition contains the following steps:

  1. Fetching the source repository
  2. Authenticating to a remote registry
  3. Building and packaging artifacts
  4. Pushing (publishing) the artifact
Warning
The supplied credentials for the remote registry require read+write access, as subsequent requests to the registry will specify push authorization scope.

Fortunately, the template repositories for providers and functions provide a functional GitHub Action in .github/workflows/ci.yml. The following sections of this guide cover configuration options and conventions for each.

Common Configuration

All workflows require references to credentials for a remote registry. Typically, these are stored as GitHub Actions Secrets, and authentication is performed via thedocker/login-action action.

For example, adding the following step to a pipeline will authenticate the job to ghcr.io using the workflow’s ephemeral GitHub OIDC token.

1      - name: Login to GHCR
2        uses: docker/login-action@v3
3        with:
4          registry: ghcr.io
5          username: ${{ github.repository_owner }}
6          password: ${{ secrets.GITHUB_TOKEN }}
Important
By default, the job’s OIDC token will not have permission to write packages to ghcr.io. This can be configured in the GitHub repository’s settings, or declared explicitly in the workflow definition YAML file.

For other registries, it is still best practice to reference credentials as custom Secret variables. For example:

1      - name: Login to Upbound
2        uses: docker/login-action@v3
3        if: env.XPKG_ACCESS_ID != ''
4        with:
5          registry: xpkg.upbound.io
6          username: ${{ env.XPKG_ACCESS_ID }}
7          password: ${{ secrets.XPKG_TOKEN }}

Branching Conventions

Repositories for Crossplane extensions follow similar branching conventions to upstream Crossplane, where the release process is predicated on the workflow executing in branches with the release-* prefix. main is often included, though a conventional release process would not build and push off of tags on main.

1on:
2  push:
3    branches:
4      - main
5      - release-*

For example, when releasing v0.1.0 of an extension, the conventional process is to cut a release branch release-0.1 at the git commit where it will be built, and tag it as v0.1.0.

Note
Some custom workflows may accept an explicit input for the remote reference, which overrides inferring from a git ref or tag. The [ci.yml] file for crossplane-contrib/function-python is a good example.

Configuring Workflows for Functions

Function workflow definitions will differ based on the base language the function is implemented in. For example, a Python function will require a Python environment in the GitHub Action runner:

 1      - name: Setup Python
 2        uses: actions/setup-python@v5
 3        with:
 4          python-version: ${{ env.PYTHON_VERSION }}
 5
 6      - name: Setup Hatch
 7        run: pipx install hatch==1.7.0
 8
 9      - name: Lint
10        run: hatch run lint:check

While the template repository provides a working pipeline definition, users may choose to customize their environment with different tooling.

Functions also require a runtime image of the core business logic to be built and embedded into the Function package. The default workflow definition will build for two platforms: linux/amd64 and linux/arm64.

 1      - name: Build Runtime
 2        id: image
 3        uses: docker/build-push-action@v6
 4        with:
 5          context: .
 6          platforms: linux/${{ matrix.arch }}
 7          cache-from: type=gha
 8          cache-to: type=gha,mode=max
 9          target: image
10          build-args:
11            PYTHON_VERSION=${{ env.PYTHON_VERSION }}
12          outputs: type=docker,dest=runtime-${{ matrix.arch }}.tar

Configuring Workflows for Providers

Providers, unlike Functions, use custom make targets in the build submodule for building and pushing Crossplane Provider packages.

Configuring the workflow for a specific registry involves two steps:

  1. Updating the registry variables in the top-level Makefile.
  2. Referencing GitHub Actions Secrets for authorized credentials to the registry.

Configure Target Registry

The provider template repository includes a top-level Makefile. Edit the following variables to define the target registry:

  1. XPKG_REG_ORGS - a space-delimited list of target repositories.
  2. XPKG_REG_ORGS_NO_PROMOTE - for registries that do not use or infer channel tags, such as xpkg.upbound.io.

For example, the following dual-pushes to xpkg.upbound.io as well as index.docker.io:

1XPKG_REG_ORGS ?= xpkg.upbound.io/crossplane-contrib index.docker.io/crossplanecontrib
2
3XPKG_REG_ORGS_NO_PROMOTE ?= xpkg.upbound.io/crossplane-contrib

Reusable Workflows

The crossplane-contrib/provider-workflows repository provide reusable workflow definitions that can be called from a custom CI pipeline.

For example, the following snippet references the callable workflow to build and push the provider-kubernetes package to both ghcr.io and xpkg.upbound.io:

 1jobs:
 2  publish-provider-package:
 3    uses: crossplane-contrib/provider-workflows/.github/workflows/publish-provider-non-family.yml@main
 4    with:
 5      repository: provider-kubernetes
 6      version: ${{ github.event.inputs.version }}
 7      go-version: ${{ github.event.inputs.go-version }}
 8      cleanup-disk: true
 9    secrets:
10      GHCR_PAT: ${{ secrets.GITHUB_TOKEN }}
11      XPKG_UPBOUND_TOKEN: ${{ secrets.XPKG_UPBOUND_TOKEN }}
Tip
The reusable workflows referenced here will publish to ghcr.io by default. Ensure that the default GitHub Actions OIDC token is granted the packages: write permission.

Troubleshooting

Ensure the target repository exists in the registry. You will need to create it if it does not already exist.

Ensure the credentials used during the registry login step are authorized to pull and push, and that the {{ secrets.* }} variable substitutions match what is configured in GitHub.