Skip to main content

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.

Note on vocabulary

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:

ComponentPurpose
Language-agnostic core
setupInstalls the wash CLI and exposes it on PATH for downstream jobs
buildRuns wash build, validates the output, and copies the .wasm to a stable artifact path
oci-publishPushes the Wasm component to an OCI registry under one or more tags; optional cosign keyless signing
Rust layer
setup-rustAdds the wasm32-wasip2 Rust target and exposes a .rust-cache template
setup-cargo-auditableConfigures wash build to use cargo auditable so Wasm components ship with an embedded SBOM
attest-cargo-sbomExtracts 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: validate

build

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-cache

setup-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: validate

attest-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: publish

Attestation

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 signingoci-publish with sign: "true" signs the published OCI image. Consumers verify with cosign verify --certificate-identity ... --certificate-oidc-issuer https://gitlab.com.
  • SBOM attestationattest-cargo-sbom extracts the CycloneDX SBOM from the auditable build and attaches it to the image as a Sigstore attestation, verifiable with cosign 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: true

The 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:

  1. Enable the GitLab container registry for the project (Settings → General → Visibility) so oci-publish has a registry to push to.
  2. 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:

  1. Protected environments (staging, production) so that only the right people can deploy.
  2. File-type CI/CD variables for cluster access pointing at Cosmonic Control clusters:
    • KUBECONFIG_DRYRUN — any cluster with Cosmonic Control's CRDs installed; used by manifest:validate for kubectl 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.