Workflows
A Workflow is a platform-engineer-defined template for running automation in the platform — most commonly a CI build that turns a developer's source code into a container image, but equally infrastructure provisioning, data pipelines, or end-to-end tests. Authoring Workflows is how you give developers a "build my app" button without letting them define arbitrary build pipelines: you write the steps and lock the policy, they supply a repo URL and a branch.
DevsPortal is built on the Control Plane Operator engine. Workflow,
ClusterWorkflow, WorkflowRun, the openchoreo.dev/v1alpha1 API group, and the occ CLI are
unchanged engine internals. Workflows execute on the workflow plane the
platform admin installs. Examples target the
Fedshi instance.
Author Workflows from the portal's Create… templates — they open the in-portal YAML editor,
pre-filled with the right kind. The occ/YAML form below is for scripting, review, and
GitOps. A Workflow wraps an Argo Workflow spec, so authoring is genuinely YAML work — the
portal gives you the editor and validation, not a no-YAML wizard.
Where you author them
In the portal: open Create… in the left nav and pick the ClusterWorkflow template
(scroll the Platform Resources section to find it), or the namespace-scoped Workflow
template. It opens the in-portal YAML editor seeded with the kind, where you fill in the
parameter schema and the inline Argo runTemplate described below.
Or with the occ CLI: write the manifest and apply it — see Managing them with the
CLI below.
Why you author them
A unified Workflow model covers both halves of CI/automation:
- Standardized, governed builds — every team building a Go service uses the same vetted pipeline (clone, scan, build, push), with security scanning and registry settings you hard-code, not options a developer can turn off.
- Self-service builds from source — developers point a component at a Workflow, push to Git, and the platform builds and deploys, with no Jenkinsfile and no per-team CI to maintain.
- General automation — the same mechanism runs provisioning, ETL, test suites, or package publishing that isn't tied to any component.
Workflow vs ClusterWorkflow
A ClusterWorkflow is the cluster-scoped variant — same spec, visible to every organization
namespace. Ship shared builders (the default Dockerfile and buildpack builders) as
ClusterWorkflows so teams everywhere reference one definition. Author a namespace-scoped
Workflow for an org-specific builder. Because a ClusterWorkflow is cluster-scoped, it can
only reference cluster-scoped workflow planes (ClusterWorkflowPlane).
How a Workflow is structured
A Workflow lives on the control plane and bridges to the workflow plane where execution happens. It has a few parts:
| Part | Field | What it does |
|---|---|---|
| Parameter schema | parameters.openAPIV3Schema | The developer-facing inputs (repo URL, branch, timeout) — full OpenAPI v3 freedom |
| Run template | runTemplate | An inline Argo Workflow manifest, with CEL ${...} injection, that composes the build steps |
| Resources | resources | Extra Kubernetes objects created in the workflow plane (e.g. an ExternalSecret for Git creds) |
| External refs | externalRefs | References to external CRs (currently SecretReference) resolved at runtime into the CEL context |
| TTL | ttlAfterCompletion | How long completed runs are retained before automatic cleanup |
The actual step logic — clone, scan, build, push — lives in Argo ClusterWorkflowTemplates in
the workflow plane, each a single reusable step. Your runTemplate composes those steps into a
pipeline by templateRef. You author the composition and the schema; you don't hand-create the
running Argo Workflow — the platform renders runTemplate into one per execution.
The three kinds of parameter
The most important authoring decision is who controls each value. Every parameter in your
runTemplate is one of three types:
| Type | Set by | Example | Where it lives |
|---|---|---|---|
| Developer-provided | Developers | repository.url, branch, timeout | Exposed in parameters.openAPIV3Schema, read via ${parameters.*} |
| Hard-coded | You | trivy-scan: "true", registry: "ghcr.io" | Literal values in runTemplate — not in the schema, so developers can't change them |
| System-generated | The platform | run name, namespace, component/project labels | Injected via ${metadata.*} |
Hard-coding is your governance lever: pin the scanner on, fix the registry, lock the builder image. Expose only what a developer legitimately needs to vary.
A representative example
A buildpacks builder. The schema exposes the repo and a couple of knobs; the runTemplate
injects developer params, system context, and hard-coded policy into an Argo Workflow that
composes cluster workflow templates.
apiVersion: openchoreo.dev/v1alpha1
kind: ClusterWorkflow
metadata:
name: gcp-buildpacks-builder
labels:
# Required for a component (CI) workflow — see CI governance below
openchoreo.dev/workflow-type: "component"
spec:
# Developer-provided parameters
parameters:
openAPIV3Schema:
type: object
required: [repository]
properties:
repository:
type: object
required: [url]
properties:
url:
type: string
description: "Git repository URL"
x-openchoreo-component-parameter-repository-url: true
revision:
type: object
properties:
branch:
type: string
default: main
x-openchoreo-component-parameter-repository-branch: true
appPath:
type: string
default: "."
x-openchoreo-component-parameter-repository-app-path: true
timeout:
type: string
default: "30m"
# The inline Argo Workflow rendered into the workflow plane per run
runTemplate:
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
name: ${metadata.workflowRunName} # system-generated
namespace: ${metadata.namespace}
spec:
serviceAccountName: workflow-sa # auto-created by the platform
entrypoint: build-workflow
arguments:
parameters:
- name: component-name
value: ${metadata.labels['openchoreo.dev/component']} # system
- name: repo-url
value: ${parameters.repository.url} # developer
- name: branch
value: ${parameters.repository.revision.branch} # developer
- name: trivy-scan
value: "true" # hard-coded
- name: registry
value: "ghcr.io" # hard-coded
templates:
- name: build-workflow
steps:
- - name: checkout-source
templateRef:
name: checkout-source # an Argo ClusterWorkflowTemplate
template: checkout
clusterScope: true
- - name: build-image
templateRef:
name: gcp-buildpacks-build
template: build-image
clusterScope: true
ttlAfterCompletion: "7d"
Notice the x-openchoreo-component-parameter-repository-* vendor extensions on the schema fields
— those are what make this a CI workflow that supports auto-build (covered next).
Component workflows vs generic workflows
The same Workflow CR serves two purposes:
- Generic workflows run standalone automation not tied to a component — infrastructure
provisioning, ETL, e2e tests, package publishing. A developer or process triggers them with a
WorkflowRundirectly. - Component (CI) workflows are bound to a Component and build it. A Workflow becomes a
component workflow when it (1) carries
openchoreo.dev/workflow-type: "component", (2) is referenced by a Component viaspec.workflow.name, and (3) is listed in a ComponentType'sallowedWorkflows. There's no separate CRD — it's a Workflow that satisfies those three conditions.
A WorkflowRun is imperative — it triggers an action rather than declaring desired state.
Don't commit WorkflowRuns to a GitOps repo; create them via Git webhooks (auto-build), the
console, or occ.
Auto-build and the vendor extensions
To enable auto-build — a Git webhook triggering a build on push — a component workflow must
tag its schema fields with x-openchoreo-component-parameter-repository-* extensions (URL,
branch, commit, app-path, secret-ref). The platform walks the schema for these to learn which
fields are the repo coordinates, regardless of nesting. They're required for auto-build and
otherwise enable richer UI behavior. Full table in the upstream CI-governance guide.
CI governance: allowedWorkflows
The primary governance lever for builds is on the ComponentType, not the Workflow:
allowedWorkflows lists exactly which Workflows a component of that type may use.
apiVersion: openchoreo.dev/v1alpha1
kind: ClusterComponentType
metadata:
name: backend
spec:
allowedWorkflows:
- kind: ClusterWorkflow
name: dockerfile-builder
- kind: ClusterWorkflow
name: gcp-buildpacks-builder
How it bites: when a developer points a component at a workflow that isn't in its ComponentType's
allowedWorkflows, the Component controller rejects it and the component enters a
WorkflowNotAllowed (Ready=False) state with a clear message — a guardrail surfacing as a
fixable error, not a silent escape hatch. This lets you say "all backend builds must use an
approved, scanning builder" and have the platform enforce it. See
Governance and guardrails
and, on the developer side, builds and CI.
Common patterns: one workflow (strict), several workflows (developer choice), or language-specific workflows (Dockerfile for compiled, buildpacks for interpreted).
The default builders
The platform ships four ClusterWorkflows you can allow as-is or use as references:
| ClusterWorkflow | Builds with |
|---|---|
dockerfile-builder | A provided Dockerfile/Containerfile |
gcp-buildpacks-builder | Go, Java, Node.js, Python, .NET |
paketo-buildpacks-builder | Java, Node.js, Python, Go, .NET, Ruby, PHP, and more |
ballerina-buildpack-builder | Ballerina |
Managing them with the CLI
occ clusterworkflow list
occ clusterworkflow get gcp-buildpacks-builder
occ workflow list -n fedshi
occ apply -f gcp-buildpacks-builder.yaml
Deep reference
Authoring the Argo steps, the full schema/CEL surface, external references, and the CI extension table are documented upstream:
- Workflows (overview, creating, CI governance).
- CEL reference — context variables and built-ins.
- API and CRDs — the DevsPortal pointer into the CRDs.
What's next
- Component types —
allowedWorkflowsis where builds get governed. - Developers: builds and CI — the experience your Workflows produce.
- Platform admin: workflow plane — the infrastructure these Workflows execute on.