This repository contains shared GitHub Actions workflows for Crossplane projects, focusing on quality validation and CI/CD automation.
A reusable workflow that performs the common setup and build steps shared by all other workflows. Running this workflow first populates the cache, allowing dependent workflows to skip redundant setup work.
When running multiple workflows (validate, test, e2e, publish) in parallel, each would independently perform the same setup steps: checkout, install tools, authenticate, and build. This duplicates work up to 10x depending on how many workflows run concurrently.
By calling setup first and making other workflows depend on it via needs, the cache is populated once and shared across all subsequent jobs. This significantly reduces total CI time and resource usage.
crossplane_version(optional): Crossplane CLI version to install (default:v2.0.2)ghcr_user(optional): GitHub Container Registry username for pulling images
GH_PAT(optional): Personal access token for GitHub Container Registry access (required ifghcr_useris provided)
Requires the following permissions in the calling workflow:
packages: read(for GHCR access)contents: read(for checking out code)
- Checks out the repository
- Restores/creates cache for Upbound and Crossplane CLI
- Installs and configures the Upbound CLI
- Installs Crossplane CLI
- Authenticates with GitHub Container Registry
- Builds the project using
up(populating the cache for dependent workflows)
Call setup first, then have other workflows depend on it:
name: CI
on:
pull_request:
branches: [main]
push:
branches: [main]
permissions:
contents: read
packages: read
jobs:
setup:
uses: unbounded-tech/workflows-crossplane/.github/workflows/setup.yaml@main
secrets:
GH_PAT: ${{ secrets.GH_PAT }}
validate:
needs: setup
uses: unbounded-tech/workflows-crossplane/.github/workflows/validate.yaml@main
with:
examples: '[{"example": "examples/claim.yaml"}]'
api_path: 'apis/myapi'
secrets:
GH_PAT: ${{ secrets.GH_PAT }}
test:
needs: setup
uses: unbounded-tech/workflows-crossplane/.github/workflows/test.yaml@main
secrets:
GH_PAT: ${{ secrets.GH_PAT }}
e2e:
needs: setup
uses: unbounded-tech/workflows-crossplane/.github/workflows/e2e.yaml@main
with:
aws: true
aws-use-oidc: true
aws-account-id: "123456789012"
secrets:
GH_PAT: ${{ secrets.GH_PAT }}In this example, setup runs first and populates the cache. Then validate, test, and e2e run in parallel, each benefiting from the pre-warmed cache instead of duplicating the build step.
A reusable workflow that validates Crossplane compositions and examples using the Upbound CLI and Crossplane beta validate command.
composition(optional): Composition YAML filename (default:composition.yaml)examples(required): Examples input. Accepts either a single example string or a JSON array. JSON arrays can be of strings (legacy) or objects (new format withobserved_resourcesand/orapi_path):- Single example:
"examples/claim.yaml" - Array of strings:
["examples/claim.yaml", "examples/another.yaml"] - Array of objects (single API):
[{"example": "examples/claim.yaml", "observed_resources": "examples/observed-resources/step-1/"}] - Array of objects (multi-API):
[{"example": "examples/foo/minimal.yaml", "api_path": "apis/foos"}, {"example": "examples/bar/minimal.yaml", "api_path": "apis/bars"}]
- Single example:
api_path(optional): Path to your API directory containing your XRD definitions. Used as fallback when an example does not specify its ownapi_path. Required if no examples specifyapi_path.crossplane_version(optional): Crossplane CLI version to install (default:v2.0.2)error_on_missing_schemas(optional): Whether to error on missing schemas during validation (default:true)ghcr_user(optional): GitHub Container Registry username for pushing images
GH_PAT(optional): Personal access token for GitHub Container Registry write access (required ifghcr_useris provided)
Requires the following permissions in the calling workflow:
packages: write(for GHCR access)contents: write(for checking out code)issues: write(for potential issue reporting)pull-requests: write(for pull request comments)
- Caches Upbound and Crossplane CLI installations
- Authenticates with Upbound (without logging into registry)
- Installs Crossplane CLI
- Logs into GitHub Container Registry (if
ghcr_userprovided) - Builds the project using
up(Upbound CLI) - Renders the composition example
- Validates the example against XRD schemas
- Performs additional validation on the rendered composition
To use this workflow in your Crossplane composition repository:
name: Validate
on:
pull_request:
branches: [ main ]
jobs:
validate:
uses: unbounded-tech/workflows-crossplane/.github/workflows/validate.yaml@main
with:
examples: '[{"example": "examples/claim.yaml"}]'
api_path: 'package'
secrets:
GH_PAT: ${{ secrets.GH_PAT }}For repositories that contain multiple XRDs in separate apis/<name>/ subdirectories, specify api_path per example instead of (or in addition to) the top-level input:
jobs:
validate:
uses: unbounded-tech/workflows-crossplane/.github/workflows/validate.yaml@main
with:
examples: |
[
{ "example": "examples/foos/minimal.yaml", "api_path": "apis/foos" },
{ "example": "examples/bars/minimal.yaml", "api_path": "apis/bars" }
]
secrets:
GH_PAT: ${{ secrets.GH_PAT }}Each matrix run resolves api_path per example: the per-example value takes precedence, otherwise the top-level inputs.api_path is used. The validation fails fast if neither is set for a given example.
- Your repository should contain Crossplane composition and XRD definitions
- Enable GitHub Container Registry access if pushing packages
- Ensure your examples are valid YAML and reference correct schemas
A reusable workflow that runs Crossplane tests using the Upbound CLI testing framework.
crossplane_version(optional): Crossplane CLI version to install (default:v2.0.2)ghcr_user(optional): GitHub Container Registry username for pulling/pushing images
GH_PAT(optional): Personal access token for GitHub Container Registry write access (required ifghcr_useris provided)
Requires the following permissions in the calling workflow:
packages: write(for GHCR access)contents: write(for checking out code)issues: write(for potential issue reporting)pull-requests: write(for pull request comments)
- Caches Upbound and Crossplane CLI installations
- Authenticates with Upbound (without logging into registry)
- Installs Crossplane CLI
- Logs into GitHub Container Registry (if
ghcr_userprovided) - Builds the project using
up(Upbound CLI) - Runs all tests located in the
tests/directory usingup test run
name: Test
on:
push:
branches: [ main ]
jobs:
test:
uses: unbounded-tech/workflows-crossplane/.github/workflows/test.yaml@main
secrets:
GH_PAT: ${{ secrets.GH_PAT }}A reusable workflow that runs end-to-end tests for Crossplane compositions using Kind clusters and real cloud providers.
crossplane_version(optional): Crossplane CLI version to install (default:v2.0.2)ghcr_user(optional): GitHub Container Registry username for pulling/pushing imagespattern(optional): Test file pattern (default:tests/e2e*)timeout-minutes(optional): Timeout in minutes for the e2e test step (default:20)cleanup-timeout-minutes(optional): Timeout in minutes for the cleanup step (default:10)github(optional): Enable GitHub credentials setup forprovider-upjet-github(default:false)github-auth-mode(optional): GitHub auth mode forprovider-upjet-githubcredentials:auto,app, ortoken(default:auto)gh-app-id(optional): GitHub App ID used whengithub-auth-modeisapp; install it on the target owner with repositoryAdministration: writegh-owner(optional): GitHub owner (org or user) forprovider-upjet-githubcredentialsaws(optional): Enable AWS credentials setup (default:false)aws-use-oidc(optional): Use GitHub OIDC to assume an AWS role (default:false)aws-role-name(optional): AWS IAM role name to assume via OIDC (default:hops-github-actions)aws-role-arn(optional): AWS IAM role ARN to assume via OIDC (overridesaws-role-name+aws-account-id)aws-account-id(optional): AWS account ID used to build role ARN whenaws-role-arnis not provided (quote values with leading zeros)aws-region(optional): AWS region for OIDC credential configuration (default:us-east-1)debug-resource-types(optional): JSON array of resource types to debug and delete on test failure. These resources will be logged for debugging and deleted during cleanup to handle sibling resources not removed by cascade deletion (e.g.,["network.hops.ops.com.ai"])debug-get-yaml(optional): Showkubectl get -o yamloutput for failed resources (default:false)debug-describe(optional): Showkubectl describeoutput for failed resources (default:true)debug-usages(optional): Showkubectl get usagesoutput for debugging (default:true)namespace(optional): Kubernetes namespace for test resources (default:default)
GH_PAT(optional): Personal access token for GitHub Container Registry write access (required ifghcr_useris provided)GH_PROVIDER_TOKEN(optional): GitHub token or PAT forprovider-upjet-githubE2E tests whengithub-auth-modeistoken; it needs repo admin access on the target owner (fine-grained:Administration: write, org-level resources may needadmin:org)GH_APP_KEY(optional): GitHub App private key forprovider-upjet-githubE2E tests whengithub-auth-modeisapp; the app installation should have repositoryAdministration: writeon the target ownerAWS_ACCESS_KEY_ID(optional): AWS Access Key ID for E2E tests (required ifawsistrueandaws-use-oidcisfalse)AWS_SECRET_ACCESS_KEY(optional): AWS Secret Access Key for E2E tests (required ifawsistrueandaws-use-oidcisfalse)AWS_SESSION_TOKEN(optional): AWS Session Token for E2E tests (required for OIDC/assumed roles when providing credentials via secrets)
Requires the following permissions in the calling workflow:
packages: read(for GHCR access)contents: read(for checking out code)id-token: write(for OIDC authentication with AWS, if using assumable roles)
- Caches Upbound and Crossplane CLI installations
- Authenticates with Upbound (without logging into registry)
- Installs Crossplane CLI
- Logs into GitHub Container Registry
- Builds the project using
up(Upbound CLI) - Creates GitHub credentials files for
provider-upjet-githubusing either app auth or a token (ifgithub: true) - Creates AWS credentials file (if
aws: true) - Runs e2e tests using
up test runwith--e2eflag - On test failure: logs debug info and deletes root resource type plus all
debug-resource-types - Waits for managed resources to be cleaned up
Basic usage without AWS:
name: E2E Tests
on:
push:
branches: [ main ]
jobs:
e2e:
uses: unbounded-tech/workflows-crossplane/.github/workflows/e2e.yaml@main
secrets:
GH_PAT: ${{ secrets.GH_PAT }}With provider-upjet-github credentials using a token:
jobs:
e2e:
uses: unbounded-tech/workflows-crossplane/.github/workflows/e2e.yaml@main
with:
github: true
github-auth-mode: token
gh-owner: hops-ops
secrets:
GH_PROVIDER_TOKEN: ${{ secrets.GH_PROVIDER_TOKEN }}With provider-upjet-github credentials using a GitHub App:
jobs:
e2e:
uses: unbounded-tech/workflows-crossplane/.github/workflows/e2e.yaml@main
with:
github: true
github-auth-mode: app
gh-app-id: ${{ vars.GH_APP_ID }}
gh-owner: hops-ops
secrets:
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}With AWS credentials using OIDC role assumption (recommended):
name: E2E Tests
on:
push:
branches: [ main ]
permissions:
id-token: write
contents: read
packages: read
jobs:
e2e:
uses: unbounded-tech/workflows-crossplane/.github/workflows/e2e.yaml@main
with:
aws: true
aws-use-oidc: true
aws-account-id: "123456789012"
aws-role-name: hops-github-actions
aws-region: us-east-1
secrets:
GH_PAT: ${{ secrets.GH_PAT }}With AWS credentials using OIDC assumable role via a separate job:
name: E2E Tests
on:
push:
branches: [ main ]
permissions:
id-token: write
contents: read
packages: read
jobs:
aws-credentials:
runs-on: ubuntu-latest
outputs:
aws_access_key_id: ${{ steps.creds.outputs.aws-access-key-id }}
aws_secret_access_key: ${{ steps.creds.outputs.aws-secret-access-key }}
aws_session_token: ${{ steps.creds.outputs.aws-session-token }}
steps:
- name: Configure AWS Credentials
id: creds
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-role
aws-region: us-east-1
output-credentials: true
e2e:
needs: aws-credentials
uses: unbounded-tech/workflows-crossplane/.github/workflows/e2e.yaml@main
with:
aws: true
secrets:
GH_PAT: ${{ secrets.GH_PAT }}
AWS_ACCESS_KEY_ID: ${{ needs.aws-credentials.outputs.aws_access_key_id }}
AWS_SECRET_ACCESS_KEY: ${{ needs.aws-credentials.outputs.aws_secret_access_key }}
AWS_SESSION_TOKEN: ${{ needs.aws-credentials.outputs.aws_session_token }}With static AWS credentials (if OIDC is not available):
name: E2E Tests
on:
push:
branches: [ main ]
jobs:
e2e:
uses: unbounded-tech/workflows-crossplane/.github/workflows/e2e.yaml@main
with:
aws: true
secrets:
GH_PAT: ${{ secrets.GH_PAT }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}The e2e-aws.yaml workflow is deprecated. To migrate to e2e.yaml, add aws: true to your inputs:
Before:
jobs:
e2e:
uses: unbounded-tech/workflows-crossplane/.github/workflows/e2e-aws.yaml@main
secrets:
GH_PAT: ${{ secrets.GH_PAT }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}After:
jobs:
e2e:
uses: unbounded-tech/workflows-crossplane/.github/workflows/e2e.yaml@main
with:
aws: true
secrets:
GH_PAT: ${{ secrets.GH_PAT }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}Deprecated: Use
e2e.yamlwithaws: trueinstead. See migration guide above.
This workflow is maintained for backwards compatibility only and will be removed in a future release.
A reusable workflow that publishes Crossplane packages to a registry using the Upbound CLI.
tag(required): Tag to use when publishing the packagecrossplane_version(optional): Crossplane CLI version to install (default:v2.0.2)ghcr_user(optional): GitHub Container Registry username for pushing packages
GH_PAT(optional): Personal access token for GitHub Container Registry write access (required ifghcr_useris provided)
Requires the following permissions in the calling workflow:
packages: write(for GHCR access)contents: write(for checking out code)issues: write(for potential issue reporting)pull-requests: write(for pull request comments)
- Caches Upbound and Crossplane CLI installations
- Authenticates with Upbound (without logging into registry)
- Installs Crossplane CLI
- Logs into GitHub Container Registry (if
ghcr_userprovided) - Builds the project using
up(Upbound CLI) - Publishes the built package to the configured registry with the specified tag
name: Publish
on:
release:
types: [ published ]
jobs:
publish:
uses: unbounded-tech/workflows-crossplane/.github/workflows/publish.yaml@main
with:
tag: ${{ github.event.release.tag_name }}
secrets:
GH_PAT: ${{ secrets.GH_PAT }}This repository uses Renovate for automated dependency updates on GitHub Actions versions and other dependencies. The configuration extends the recommended settings from the Renovate configuration preset.
Contributions to improve these workflows are welcome. Please ensure that new workflows follow GitHub Actions best practices and include comprehensive documentation.