CI/CD with GitLab CI
Overview
Cosmonic Control workloads can be built, signed, and deployed from GitLab CI using the cosmonic-labs/ci-components/wash catalog — a set of reusable GitLab CI components that mirror the capabilities of the wasmCloud GitHub Actions toolkit covered on the CI/CD and GitOps page.
A complete production-style pipeline that uses every CI component together (i.e., merge-request workflow rules, security scanning, environments, Kubernetes deployment, and GitLab Releases) lives at cosmonic-labs/wasm-component-sample.
GitLab refers to units of CI activity as "components," which can lead to some confusion when we're talking about WebAssembly components. On this page, we use the term CI components to refer to GitLab's components, and Wasm components to refer to WebAssembly binaries that conform to the Component Model.
GitLab CI components
The catalog is split into a language-agnostic core and a Rust layer:
| Component | Purpose |
|---|---|
| Language-agnostic core | |
setup | Installs the wash CLI and exposes it on PATH for downstream jobs |
build | Runs wash build, validates the output, and copies the .wasm to a stable artifact path |
oci-publish | Pushes the Wasm component to an OCI registry under one or more tags; optional cosign keyless signing |
| Rust layer | |
setup-rust | Adds the wasm32-wasip2 Rust target and exposes a .rust-cache template |
setup-cargo-auditable | Configures wash build to use cargo auditable so Wasm components ship with an embedded SBOM |
attest-cargo-sbom | Extracts the CycloneDX SBOM and attaches it as a Sigstore attestation on the published image |
CI components are versioned via semver tags on the catalog repository and pinned through the include: component: reference (@~latest, @1, @1.2.3).
setup
The setup CI component installs wash and exposes WASH_BIN_DIR as a dotenv report so downstream jobs can pick it up:
include:
- component: $CI_SERVER_FQDN/cosmonic-labs/ci-components/wash/setup@~latest
inputs:
wash_version: "v2.2.1"
stage: validatebuild
The build CI component runs wash build and copies the resulting .wasm to a stable artifact path. It is language-agnostic; attach a language-specific cache (e.g. .rust-cache from setup-rust) via extends:.
include:
- component: $CI_SERVER_FQDN/cosmonic-labs/ci-components/wash/build@~latest
inputs:
stage: build
component_artifact_path: "build/greeter.wasm"oci-publish
The oci-publish CI component pushes the Wasm component to an OCI registry under one or more tags. With sign: "true" it runs cosign keyless signing against the published image using a GitLab-issued OIDC token; see Attestation below.
include:
- component: $CI_SERVER_FQDN/cosmonic-labs/ci-components/wash/oci-publish@~latest
inputs:
stage: publish
image_tags: "${CI_COMMIT_REF_SLUG},${CI_COMMIT_SHORT_SHA}"
sign: "true"setup-rust
The setup-rust CI component adds the wasm32-wasip2 Rust target and exposes a hidden .rust-cache job template that downstream jobs (clippy, wash-build) extend to share the cargo registry and target cache:
include:
- component: $CI_SERVER_FQDN/cosmonic-labs/ci-components/wash/setup-rust@~latest
inputs:
stage: validate
wash-build:
extends: .rust-cachesetup-cargo-auditable
The setup-cargo-auditable CI component rewrites .wash/config.yaml so that wash build invokes cargo auditable build. Every released Wasm component ships its full dependency graph embedded in the Wasm binary, ready for SBOM extraction.
include:
- component: $CI_SERVER_FQDN/cosmonic-labs/ci-components/wash/setup-cargo-auditable@~latest
inputs:
stage: validateattest-cargo-sbom
The attest-cargo-sbom CI component extracts the CycloneDX SBOM from the auditable build and attaches it as a Sigstore attestation to the published image. Like oci-publish with sign: "true", it uses a GitLab-issued OIDC token; no signing keys are stored.
include:
- component: $CI_SERVER_FQDN/cosmonic-labs/ci-components/wash/attest-cargo-sbom@~latest
inputs:
stage: publishAttestation
Sigstore keyless signing on GitLab is driven by the id_tokens: keyword, which mints a short-lived OIDC token scoped to the running job. cosign exchanges that token for a code-signing certificate from the Sigstore Fulcio CA, signs the artifact, and records the signature in the Rekor transparency log. There are no long-lived signing keys to manage or rotate.
The two attestation flows in this catalog are:
- Image signing —
oci-publishwithsign: "true"signs the published OCI image. Consumers verify withcosign verify --certificate-identity ... --certificate-oidc-issuer https://gitlab.com. - SBOM attestation —
attest-cargo-sbomextracts the CycloneDX SBOM from the auditable build and attaches it to the image as a Sigstore attestation, verifiable withcosign verify-attestation --type cyclonedx.
GitLab Ultimate users also get a parallel scanning path for free: publishing the CycloneDX SBOM as an artifacts:reports:cyclonedx report registers the dependencies in the project's Dependency List and runs them through GitLab's own SBOM vulnerability scanner against the GitLab Advisory Database. The sample pipeline layers Trivy on top as a third-party second opinion that gates on HIGH/CRITICAL findings.
Example: Build and publish pipeline
The following pipeline assembles the core and Rust layers to build a Rust-based Wasm component with auditable dependency metadata, publish it to the project's container registry with a cosign-signed image and SBOM attestation, and cache the cargo registry/target between runs:
stages:
- validate
- build
- publish
include:
- component: $CI_SERVER_FQDN/cosmonic-labs/ci-components/wash/setup@~latest
inputs:
wash_version: "v2.2.1"
stage: validate
- component: $CI_SERVER_FQDN/cosmonic-labs/ci-components/wash/setup-rust@~latest
inputs:
stage: validate
- component: $CI_SERVER_FQDN/cosmonic-labs/ci-components/wash/setup-cargo-auditable@~latest
inputs:
stage: validate
- component: $CI_SERVER_FQDN/cosmonic-labs/ci-components/wash/build@~latest
inputs:
stage: build
component_artifact_path: "build/greeter.wasm"
- component: $CI_SERVER_FQDN/cosmonic-labs/ci-components/wash/oci-publish@~latest
inputs:
stage: publish
image_tags: "${CI_COMMIT_REF_SLUG},${CI_COMMIT_SHORT_SHA}"
sign: "true"
- component: $CI_SERVER_FQDN/cosmonic-labs/ci-components/wash/attest-cargo-sbom@~latest
inputs:
stage: publish
# Attach the Rust cargo cache and auditable build outputs to wash-build.
wash-build:
extends: .rust-cache
needs:
- job: setup-wash
artifacts: true
- job: setup-rust
- job: setup-wash-cargo-auditable
artifacts: trueThe wasm-component-sample repository extends this with merge-request workflow rules, cargo fmt/cargo clippy validation, a Wasm component smoke test, GitLab Secret Detection, CycloneDX + Trivy SBOM scanning, a Kubernetes WorkloadDeployment apply job, and a GitLab Release job tied to semver tags. Clone it as a starting point for new projects.
Deploying to Cosmonic Control
The sample pipeline includes manifest:render, manifest:validate, and deploy:staging / deploy:production jobs that apply a WorkloadDeployment manifest against a Kubernetes cluster running Cosmonic Control. For HTTP-fronted workloads, you can also apply an HTTPTrigger manifest with the same job pattern — the CI plumbing is identical, only the rendered manifest changes.
For Cosmonic Control specifically, the sample's KUBECONFIG_STAGING / KUBECONFIG_PRODUCTION variables should point at clusters where Cosmonic Control is installed (see Production Installation). The manifest:validate job uses kubectl apply --dry-run=server against a cluster that has the runtime.wasmcloud.dev and control.cosmonic.io CRDs installed, so it catches schema violations before merge.
Required project configuration
For the pipeline above to run cleanly in a fresh project:
- Enable the GitLab container registry for the project (Settings → General → Visibility) so
oci-publishhas a registry to push to. - Mask any external registry credentials (e.g.
GHCR_TOKEN,ARTIFACTORY_TOKEN) as CI/CD variables if the publish target isn't the project registry.
For the Kubernetes deploy story shown in the sample repository, you'll additionally need:
- Protected environments (
staging,production) so that only the right people can deploy. - File-type CI/CD variables for cluster access pointing at Cosmonic Control clusters:
KUBECONFIG_DRYRUN— any cluster with Cosmonic Control's CRDs installed; used bymanifest:validateforkubectl apply --dry-run=server.KUBECONFIG_STAGING/KUBECONFIG_PRODUCTION— the Cosmonic Control clusters that will actually run the Wasm component.
No other configuration is required — the CI components use GitLab built-ins for the container registry, OIDC, and release tooling.
For closing the loop with Argo CD or Flux instead of kubectl apply directly from CI, see GitOps with Argo CD on the CI/CD and GitOps page.