CI/CD Pipeline Validation

Automate Mandrel specification validation across GitHub Actions, GitLab CI/CD, Azure DevOps, and Google Cloud Build.

Manual validation with the CLI catches errors locally, but team development requires automated gates at the pull request and merge request stage. Continuous integration ensures invalid risk profiles, malformed capabilities, and unverified extension blocks never merge into default branches.

The Mandrel project provides turnkey validation templates for major CI platforms. All templates execute the official zero-dependency CLI container:

registry.gitlab.com/the-mandrel-project/mandrel-containers/cli:latest

The container runs unprivileged under nonroot (uid 65532) on Google Distroless Debian 12. It contains static binaries for mandrel-cli and official plugins, requiring no host Go runtime or external package managers.


GitHub Actions

Add the validation action to your workflow file (for example, .github/workflows/validate-spec.yml):

name: Validate Specifications

on:
  push:
    branches: [main]
  pull_request:

jobs:
  validate-mandrel-spec:
    runs-on: ubuntu-latest
    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      - name: Validate Spec
        uses: the-mandrel-project/mandrel-containers/integrations/github@v0.0.1
        with:
          spec-path: "agent-spec.yaml"
          strict: "true"

Configuration Options

OptionTypeDefaultPurpose
spec-pathstringmandrel-spec.yamlRelative path to the specification file
image-tagstringlatestTag of the Mandrel CLI container image
strictbooleanfalseEscalate validation warnings to fatal pipeline errors
allow-unclaimed-extensionsbooleanfalsePermit undeclared spec.extensions blocks

GitLab CI/CD

Use the official component from the Mandrel CI/CD Catalog in your .gitlab-ci.yml:

stages:
  - lint
  - test

include:
  - component: $CI_SERVER_FQDN/the-mandrel-project/mandrel-ci/validate-spec@v2.4.0
    inputs:
      stage: lint
      spec_path: "agent-spec.yaml"
      strict: true

If including the template directly from a repository clone:

stages:
  - lint

include:
  - local: integrations/gitlab/validate-mandrel-spec.yml
    inputs:
      stage: lint
      spec_path: "agent-spec.yaml"

Configuration Inputs

InputTypeDefaultDescription
stagestringlintPipeline stage in which to execute validation
spec_pathstringmandrel-spec.yamlPath to the specification file
image_tagstringlatestImage tag of the Mandrel CLI container
strictbooleanfalseTreat warnings as fatal errors
allow_unclaimed_extensionsbooleanfalsePermit undeclared extension fields

Azure DevOps

Reference the step template in your azure-pipelines.yml:

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

steps:
  - checkout: self

  - task: Bash@3
    displayName: 'Validate Mandrel Specification'
    inputs:
      targetType: 'inline'
      script: |
        docker run --rm \
          -v "$(System.DefaultWorkingDirectory):/workspace:ro" \
          -w /workspace \
          registry.gitlab.com/the-mandrel-project/mandrel-containers/cli:latest \
          validate --strict agent-spec.yaml

When importing the reusable template file directly:

steps:
  - template: integrations/azure-devops/validate-mandrel-spec.yml
    parameters:
      specPath: 'agent-spec.yaml'
      strict: true

Parameters

NameTypeDefaultDescription
specPathstringmandrel-spec.yamlPath to the specification relative to repository root
imageTagstringlatestCLI image tag to execute
strictbooleanfalseEscalate warnings to fatal errors
allowUnclaimedExtensionsbooleanfalsePermit undeclared extension fields

Google Cloud Build

Include a validation step in your cloudbuild.yaml. Cloud Build executes containers natively and automatically mounts the workspace at /workspace:

steps:
  - id: 'validate-mandrel-spec'
    name: 'registry.gitlab.com/the-mandrel-project/mandrel-containers/cli:latest'
    args:
      - 'validate'
      - '--strict'
      - 'agent-spec.yaml'

Alternatively, invoke the standalone configuration via the Google Cloud CLI:

gcloud builds submit \
  --config=integrations/cloudbuild/validate-mandrel-spec.yaml \
  --substitutions=_MANDREL_SPEC_PATH=agent-spec.yaml,_MANDREL_CLI_TAG=latest

Substitutions

NameDefaultDescription
_MANDREL_SPEC_PATHmandrel-spec.yamlPath to the specification file
_MANDREL_CLI_TAGlatestCLI container image tag

Local Testing with Podman

To reproduce CI validation locally prior to opening a pull request, run the container with Podman:

podman run --rm \
  -v "$(pwd):/workspace:ro" \
  -w /workspace \
  registry.gitlab.com/the-mandrel-project/mandrel-containers/cli:latest \
  validate --strict agent-spec.yaml

Successful validation returns exit code 0:

✓ Spec 'my-specialist-agent' is valid.

Failed validation returns exit code 2 (ExitValidation) and diagnostic violation messages:

[ERROR] spec.runtime.timeout-seconds: timeout-seconds must be >= 0, got -5 [mandrel.io/core E002]
Error: validation failed with 1 error(s)

Exit Codes and Pipeline Handling

The Mandrel CLI returns deterministic exit codes to allow CI pipelines to distinguish between specification errors and operational or infrastructure failures:

Exit CodeConstantMeaningPipeline Behavior
0ExitSuccessSpecification passed validation cleanlyJob passes
1ExitGeneralOperational failure (missing file, invalid flag syntax)Job fails (infrastructure error)
2ExitValidationSpecification violated schema constraints or governance policiesJob fails (spec invalid)
3ExitAuthToken missing or authentication failureJob fails (auth error)
4ExitPluginPlugin execution failed or crashedJob fails (plugin error)

Advisory Linting in GitLab CI

Pipelines running in advisory mode can soft-fail on specification errors while continuing to fail hard on operational problems:

validate-mandrel-spec:
  allow_failure:
    exit_codes: [2]

GitHub Actions Exit Code Handling

GitHub workflows can evaluate exit codes or step outcomes to branch behavior:

      - name: Validate Mandrel Spec
        id: validate
        continue-on-error: true
        run: |
          docker run --rm -v "$GITHUB_WORKSPACE:/workspace:ro" -w /workspace \
            registry.gitlab.com/the-mandrel-project/mandrel-containers/cli:latest \
            validate --strict agent-spec.yaml
          echo "exit_code=$?" >> $GITHUB_OUTPUT

      - name: Report Specification Errors
        if: steps.validate.outputs.exit_code == '2'
        run: echo "Specification violated governance policies."