Skip to content

Commit 2f69572

Browse files
authored
Merge pull request #913 from jastang/785-extensions-release-process
Document release process for Crossplane extensions.
2 parents a6a74ef + f117b12 commit 2f69572

File tree

9 files changed

+1529
-1
lines changed

9 files changed

+1529
-1
lines changed
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
---
2+
title: Releasing Crossplane Extensions
3+
weight: 80
4+
description: "Configuring build pipelines for Crossplane extensions with GitHub
5+
Actions"
6+
---
7+
8+
## Distributing Crossplane extensions
9+
10+
Crossplane provides a packaging specification for extending a Crossplane
11+
instance with APIs and business logic for composing resources.
12+
13+
Building a Crossplane extension involves creating OCI images in the [xpkg]
14+
format. Authors and maintainers of Crossplane extensions must push their
15+
packages to an OCI registry before users can reference and use them.
16+
17+
The release process for Crossplane extensions grew organically in the community
18+
and developed its own conventions and common configurations. Authors of these
19+
extensions should follow this guide to enable automation for building
20+
and pushing their packages as part of their git workflow.
21+
22+
This guide provides step-by-step instructions for configuring automated
23+
CI pipelines in GitHub Actions for pushing your Crossplane extensions to
24+
`xpkg.crossplane.io`, the main registry that the Crossplane community
25+
uses today.
26+
27+
{{< hint "tip" >}}
28+
For more information about Crossplane packages, review the
29+
[xpkg concepts]({{<ref "../concepts/packages" >}}).
30+
{{< /hint >}}
31+
32+
## Typical workflow
33+
34+
A typical GitHub workflow definition to build and release an extension
35+
contains the following steps:
36+
37+
1. Fetching the source repository
38+
2. Authenticating to a remote registry
39+
3. Building and packaging artifacts
40+
4. Pushing (publishing) the artifact
41+
42+
{{< hint "warning" >}}
43+
The supplied credentials for the remote registry require read and write access
44+
as upload requests to the registry specify `push` authorization scope.
45+
{{< /hint >}}
46+
47+
## Quickstart: Releasing a Provider to `xpkg.crossplane.io`
48+
49+
### Prerequisites
50+
51+
- A GitHub repository, for example created from the
52+
[Upjet template](https://github.com/crossplane/upjet-provider-template)
53+
54+
### Steps
55+
56+
1. Create a new YAML file under `.github/workflows`. By convention, name this
57+
file `publish-provider-package.yaml`.
58+
2. Copy the following workflow definition into the file, replacing
59+
`<REPOSITORY NAME>` with the desired name of the repository in the registry.
60+
61+
```yaml
62+
name: Publish Provider Package
63+
64+
on:
65+
workflow_dispatch:
66+
inputs:
67+
version:
68+
description: "Version string to use while publishing the package (e.g. v1.0.0-alpha.1)"
69+
default: ''
70+
required: false
71+
go-version:
72+
description: 'Go version to use if building needs to be done'
73+
default: '1.23'
74+
required: false
75+
76+
jobs:
77+
publish-provider-package:
78+
uses: crossplane-contrib/provider-workflows/.github/workflows/publish-provider-non-family.yml@main
79+
with:
80+
repository: <REPOSITORY NAME>
81+
version: ${{ github.event.inputs.version }}
82+
go-version: ${{ github.event.inputs.go-version }}
83+
cleanup-disk: true
84+
secrets:
85+
GHCR_PAT: ${{ secrets.GITHUB_TOKEN }}
86+
```
87+
88+
3. Commit the workflow file to the default branch of the GitHub repository.
89+
4. The workflow should now be available to trigger via the GitHub UI in the
90+
`Actions` tab.
91+
5. Create a release branch with the `release-` prefix in the name in the GitHub UI. For example, `release-0.1`.
92+
6. Tag the desired commit on release branch with a valid semver release tag.
93+
For example, `v0.1.0`. By default, this is the inferred reference pushed to the registry.
94+
7. Manually run the workflow in the GitHub UI, targeting the release branch from step 5.
95+
96+
See [branching conventions](#branching-conventions) for more details on tagging
97+
practices and optionally overriding the inferred git tag version.
98+
99+
## Quickstart: Releasing a Function to `xpkg.crossplane.io`
100+
101+
The template repository for [functions] provides a functional GitHub Action
102+
YAML file that pushes to `xpkg.crossplane.io` without extra configuration.
103+
104+
To build and push a new release to the registry:
105+
106+
1. Cut a release branch with the `release-` prefix in the name in the GitHub UI. For example, `release-0.1`.
107+
2. Tag the desired commit on release branch with a valid semver release tag for a corresponding
108+
GitHub Release. For example, `v0.1.0`.
109+
3. Manually run the workflow in the GitHub UI, targeting the release branch from step 1.
110+
The workflow generates a default version string if user input isn't provided.
111+
112+
See [branching conventions](#branching-conventions) for more details on tagging
113+
practices and optionally overriding the inferred git tag version.
114+
115+
## Common Configuration
116+
117+
While the reusable workflows referenced in the quickstart guides are for
118+
convenience, users may choose to write their own custom GitHub Actions.
119+
120+
This and following sections provide more detailed information
121+
about common configuration options and conventions to implement the release
122+
process.
123+
124+
All workflows require references to credentials for a remote registry.
125+
Typically, users configure them as [GitHub Actions Secrets], and the workflow
126+
performs authentication via the`docker/login-action`
127+
[action](http://github.com/docker/login-action).
128+
129+
For example, adding the following step to a pipeline authenticates
130+
the job to `ghcr.io` using the workflow's ephemeral GitHub OIDC token.
131+
132+
```yaml
133+
- name: Login to GHCR
134+
uses: docker/login-action@v3
135+
with:
136+
registry: ghcr.io
137+
username: ${{ github.repository_owner }}
138+
password: ${{ secrets.GITHUB_TOKEN }}
139+
```
140+
141+
{{< hint "important" >}}
142+
By default, the job's OIDC token doesn't have permission to write packages
143+
to `ghcr.io`. Permissions are configurable in the GitHub repository's settings
144+
or declared
145+
[explicitly](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token)
146+
in the workflow definition YAML file.
147+
148+
Writing packages requires a `permissions` block with `packages: write` if it
149+
isn't configured elsewhere for the repository.
150+
{{< /hint >}}
151+
152+
For other registries, it's still best practice to reference credentials as
153+
custom Secret variables. For example:
154+
155+
```yaml
156+
- name: Login to Another Registry
157+
uses: docker/login-action@v3
158+
with:
159+
registry: my-registry.io
160+
username: ${{ env.REGISTRY_USER }}
161+
password: ${{ secrets.REGISTRY_PASSWORD }}
162+
```
163+
164+
## Branching conventions
165+
166+
Repositories for Crossplane extensions follow similar branching conventions
167+
to upstream Crossplane, where the release process assumes the workflow
168+
executing in branches with the `release-*` prefix. `main` is often included,
169+
though a conventional release process would not build and push off of tags on
170+
`main`.
171+
172+
```yaml
173+
on:
174+
push:
175+
branches:
176+
- main
177+
- release-*
178+
```
179+
180+
For example, when releasing `v0.1.0` of an extension, the conventional
181+
process is to cut a release branch `release-0.1` at the git commit
182+
where it builds from, and tag it as `v0.1.0`.
183+
184+
{{< hint "note" >}}
185+
Some custom workflows may accept an explicit input for the remote reference instead of
186+
inferring it from a git ref. The [`ci.yml`](https://github.com/crossplane-contrib/function-python/blob/main/.github/workflows/ci.yml)
187+
file for `crossplane-contrib/function-python` is a good example.
188+
{{< /hint >}}
189+
190+
## Configuring workflows for function packages
191+
192+
Function workflow definitions differ based on the base language the
193+
function implementation uses. For example, a Python function requires
194+
a Python environment in the GitHub Action runner:
195+
196+
```yaml
197+
- name: Setup Python
198+
uses: actions/setup-python@v5
199+
with:
200+
python-version: ${{ env.PYTHON_VERSION }}
201+
202+
- name: Setup Hatch
203+
run: pipx install hatch==1.7.0
204+
205+
- name: Lint
206+
run: hatch run lint:check
207+
```
208+
209+
While the template repository provides a working pipeline definition, users may
210+
choose to customize their environment with different tooling.
211+
212+
Functions also require a runtime image of the core business logic to
213+
build and embed into the Function package. The default workflow definition
214+
builds for two platforms: `linux/amd64` and `linux/arm64`.
215+
216+
```yaml
217+
- name: Build Runtime
218+
id: image
219+
uses: docker/build-push-action@v6
220+
with:
221+
context: .
222+
platforms: linux/${{ matrix.arch }}
223+
cache-from: type=gha
224+
cache-to: type=gha,mode=max
225+
target: image
226+
build-args:
227+
PYTHON_VERSION=${{ env.PYTHON_VERSION }}
228+
outputs: type=docker,dest=runtime-${{ matrix.arch }}.tar
229+
```
230+
231+
## Configuring workflows for provider packages
232+
233+
Providers, unlike Functions, use custom `make` targets in the [build submodule]
234+
for building and pushing Crossplane Provider packages.
235+
236+
Configuring the workflow for a specific registry involves two steps:
237+
238+
1. Updating the registry variables in the top-level `Makefile`.
239+
2. Referencing GitHub Actions Secrets for authorized credentials to the
240+
registry.
241+
242+
### Configure target registry
243+
244+
The provider template repository includes a top-level [`Makefile`](https://github.com/crossplane/upjet-provider-template/blob/main/Makefile).
245+
Edit the following variables to define the target registry:
246+
247+
1. `XPKG_REG_ORGS` - a space-delimited list of target repositories.
248+
2. `XPKG_REG_ORGS_NO_PROMOTE` - for registries that don't use or infer
249+
channel tags.
250+
251+
For example, the following dual-pushes to `xpkg.crossplane.io` as well as
252+
`index.docker.io`:
253+
254+
```make
255+
XPKG_REG_ORGS ?= xpkg.crossplane.io/crossplane-contrib index.docker.io/crossplanecontrib
256+
257+
XPKG_REG_ORGS_NO_PROMOTE ?= xpkg.crossplane.io/crossplane-contrib
258+
```
259+
260+
## Reusable workflows
261+
262+
The [crossplane-contrib/provider-workflows] repository provide reusable
263+
workflow definitions that are callable from a custom CI pipeline.
264+
265+
For example, the following snippet references the callable workflow to
266+
build and push the `provider-kubernetes` package to `xpkg.crossplane.io`:
267+
268+
```yaml
269+
jobs:
270+
publish-provider-package:
271+
uses: crossplane-contrib/provider-workflows/.github/workflows/publish-provider-non-family.yml@main
272+
with:
273+
repository: provider-kubernetes
274+
version: ${{ github.event.inputs.version }}
275+
go-version: ${{ github.event.inputs.go-version }}
276+
cleanup-disk: true
277+
secrets:
278+
GHCR_PAT: ${{ secrets.GITHUB_TOKEN }}
279+
```
280+
281+
{{< hint "tip" >}}
282+
The reusable workflows referenced here publish to `ghcr.io` by default.
283+
Ensure that the default GitHub Actions OIDC token inherits the
284+
`packages: write` permission.
285+
{{< /hint >}}
286+
287+
## Troubleshooting
288+
289+
{{< expand "Why is my workflow is failing with a 404 error code?" >}}
290+
Ensure the target repository exists in the registry. You need to create
291+
it if it doesn't already exist.
292+
{{</expand >}}
293+
294+
{{< expand "Why is my workflow failing with a 401 error code?" >}}
295+
Ensure the credentials used during the registry login step has authorization to
296+
pull and push, and that the `{{ secrets.* }}` variable substitutions match
297+
what's configured in GitHub.
298+
{{</expand >}}
299+
300+
<!-- Named Links -->
301+
[xpkg]: https://github.com/crossplane/crossplane/blob/main/contributing/specifications/xpkg.md
302+
[functions]: https://github.com/crossplane/function-template-go/blob/main/.github/workflows/ci.yml
303+
[GitHub Actions Secrets]: https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions
304+
[build submodule]: https://github.com/crossplane/build
305+
[crossplane-contrib/provider-workflows]: https://github.com/crossplane-contrib/provider-workflows/blob/main/.github/workflows

0 commit comments

Comments
 (0)