Skip to main content

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.

note

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.

Portal first, CLI second

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:

PartFieldWhat it does
Parameter schemaparameters.openAPIV3SchemaThe developer-facing inputs (repo URL, branch, timeout) — full OpenAPI v3 freedom
Run templaterunTemplateAn inline Argo Workflow manifest, with CEL ${...} injection, that composes the build steps
ResourcesresourcesExtra Kubernetes objects created in the workflow plane (e.g. an ExternalSecret for Git creds)
External refsexternalRefsReferences to external CRs (currently SecretReference) resolved at runtime into the CEL context
TTLttlAfterCompletionHow 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:

TypeSet byExampleWhere it lives
Developer-providedDevelopersrepository.url, branch, timeoutExposed in parameters.openAPIV3Schema, read via ${parameters.*}
Hard-codedYoutrivy-scan: "true", registry: "ghcr.io"Literal values in runTemplate — not in the schema, so developers can't change them
System-generatedThe platformrun name, namespace, component/project labelsInjected 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 WorkflowRun directly.
  • 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 via spec.workflow.name, and (3) is listed in a ComponentType's allowedWorkflows. There's no separate CRD — it's a Workflow that satisfies those three conditions.
warning

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:

ClusterWorkflowBuilds with
dockerfile-builderA provided Dockerfile/Containerfile
gcp-buildpacks-builderGo, Java, Node.js, Python, .NET
paketo-buildpacks-builderJava, Node.js, Python, Go, .NET, Ruby, PHP, and more
ballerina-buildpack-builderBallerina

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:

What's next

  • Component typesallowedWorkflows is where builds get governed.
  • Developers: builds and CI — the experience your Workflows produce.
  • Platform admin: workflow plane — the infrastructure these Workflows execute on.