CI/CD Pipeline Validation
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
| Option | Type | Default | Purpose |
|---|---|---|---|
spec-path | string | mandrel-spec.yaml | Relative path to the specification file |
image-tag | string | latest | Tag of the Mandrel CLI container image |
strict | boolean | false | Escalate validation warnings to fatal pipeline errors |
allow-unclaimed-extensions | boolean | false | Permit 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
| Input | Type | Default | Description |
|---|---|---|---|
stage | string | lint | Pipeline stage in which to execute validation |
spec_path | string | mandrel-spec.yaml | Path to the specification file |
image_tag | string | latest | Image tag of the Mandrel CLI container |
strict | boolean | false | Treat warnings as fatal errors |
allow_unclaimed_extensions | boolean | false | Permit 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
| Name | Type | Default | Description |
|---|---|---|---|
specPath | string | mandrel-spec.yaml | Path to the specification relative to repository root |
imageTag | string | latest | CLI image tag to execute |
strict | boolean | false | Escalate warnings to fatal errors |
allowUnclaimedExtensions | boolean | false | Permit 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
| Name | Default | Description |
|---|---|---|
_MANDREL_SPEC_PATH | mandrel-spec.yaml | Path to the specification file |
_MANDREL_CLI_TAG | latest | CLI 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 Code | Constant | Meaning | Pipeline Behavior |
|---|---|---|---|
0 | ExitSuccess | Specification passed validation cleanly | Job passes |
1 | ExitGeneral | Operational failure (missing file, invalid flag syntax) | Job fails (infrastructure error) |
2 | ExitValidation | Specification violated schema constraints or governance policies | Job fails (spec invalid) |
3 | ExitAuth | Token missing or authentication failure | Job fails (auth error) |
4 | ExitPlugin | Plugin execution failed or crashed | Job 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."