Skip to main content
Version: v2

wasmCloud on GitLab CI

wasmCloud GitLab CI components

The cosmonic-labs/ci-components/wash project is a GitLab CI/CD Catalog of reusable CI components for building, publishing, signing, and attesting Wasm components. It mirrors the capabilities of the wasmCloud GitHub Actions toolkit for teams running on GitLab.

Note on vocabulary

GitLab refers to units of CI activity as "components," which can lead to some confusion when we're talking about operations related to WebAssembly components. On this page, we will always 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.

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.

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:

yaml
include:
  - component: $CI_SERVER_FQDN/cosmonic-labs/ci-components/wash/setup@~latest
    inputs:
      wash_version: "v2.2.0"
      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:.

yaml
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.

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

yaml
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.

yaml
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.

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

yaml
stages:
  - validate
  - build
  - publish

include:
  - component: $CI_SERVER_FQDN/cosmonic-labs/ci-components/wash/setup@~latest
    inputs:
      wash_version: "v2.2.0"
      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.

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:
    • KUBECONFIG_DRYRUN — any cluster with the runtime-operator CRDs installed; used by manifest:validate for kubectl apply --dry-run=server.
    • KUBECONFIG_STAGING / KUBECONFIG_PRODUCTION — clusters running the runtime-operator Helm chart 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, see GitOps with Argo CD.