Skip to content

CI CD Integration

Marko Koljancic edited this page May 28, 2026 · 4 revisions

Home

CI/CD Integration

Batch validation and pipeline adapters - new in v0.6.0.

solarxy-cli can validate a whole directory of 3D assets in one run and emit the results in a format your CI/CD system understands natively. Wire it into a pipeline and a bad asset gets caught at commit time instead of mid-cook.

This page covers the validation command, then drop-in recipes for GitHub Actions, GitLab CI, Jenkins, and Perforce / Helix Core.

The validation command

Batch validation runs through solarxy-cli. The shape of every invocation:

solarxy-cli --mode analyze \
  --paths "assets/**/*.glb" "assets/**/*.gltf" \
  --config solarxy.toml \
  --adapter generic \
  --adapter-format junit-xml \
  --output validation-report.xml \
  --fail-on error

--paths requires --mode analyze. The default mode is view, which just launches the GUI - --paths is ignored there. Every batch-validation command must include --mode analyze (or the short -M analyze).

Flag Purpose
--mode analyze Required - selects the analyzer. With --paths present it runs the batch validator.
--paths <GLOB>… One or more quoted glob patterns. Repeatable. Quote them so your shell doesn't expand ** before the CLI sees it.
--config <PATH> A specific solarxy.toml. Omit to use discovery (which, for --paths, starts in the current working directory).
--adapter <NAME> generic or github-actions - see Adapters and formats.
--adapter-format <FORMAT> The output format. Defaults to the adapter's preferred format.
--output <PATH> Write the report to a file. - means stdout (also the default when omitted).
--fail-on <POLICY> Exit-code policy - error, warning, or never.

Validation policy - which checks run, the triangle budgets, filename classification - comes from solarxy.toml. See Configuration.

Exit codes and --fail-on

solarxy-cli returns a process exit code so the pipeline can gate on it:

--fail-on Exit non-zero when… Use for
error (default) Any asset has validation errors The standard merge gate.
warning Any asset has errors or warnings A strict gate.
never Never - always exits 0 Informational jobs; reports without blocking.

Exit 0 means the run passed the policy; a non-zero exit means it failed.

Adapters and formats

flowchart TD
    classDef gh fill:#1F2430,stroke:#FFC44C,color:#FFC44C
    classDef gen fill:#1F2430,stroke:#78A0EE,color:#78A0EE
    classDef format fill:#1F2430,stroke:#7FD962,color:#7FD962
    classDef start fill:#33415E,stroke:#FFC44C,color:#FFC44C

    Q[Which CI system?]:::start
    Q --> GH[GitHub Actions]:::gh
    Q --> Other[GitLab, Jenkins,<br/>Perforce, other]:::gen
    GH --> A1[--adapter github-actions]:::gh
    Other --> A2[--adapter generic]:::gen
    A1 --> F1[gha-commands<br/>PR annotations]:::format
    A1 --> F2[sarif<br/>Code Scanning]:::format
    A2 --> F3[junit-xml<br/>Tests tab]:::format
    A2 --> F4[json<br/>any consumer]:::format
    A2 --> F5[text or tap<br/>plain output]:::format
Loading

Adapter and format selection by CI system. gha-commands and sarif are GitHub-specific; the rest are CI-agnostic via the generic adapter.

An adapter wraps the output conventions of a target CI ecosystem. Two ship in v0.6.0:

Adapter Default format Built for
generic json CI-agnostic. Use it for GitLab, Jenkins, Perforce, or any system that consumes a file.
github-actions gha-commands GitHub Actions - emits workflow commands that annotate the PR, plus SARIF for Code Scanning.

Six --adapter-format values exist; not every adapter supports every format:

Format generic github-actions Notes
json Yes Yes Structured run report.
text Yes Yes Human-readable plain text.
tap Yes Yes Test Anything Protocol.
junit-xml Yes Yes JUnit XML - consumed by GitLab's artifacts:reports:junit and the Jenkins JUnit Plugin.
gha-commands No Yes GitHub Actions workflow commands (PR annotations).
sarif No Yes SARIF for GitHub Code Scanning.

Asking generic for gha-commands or sarif fails with an UnsupportedFormat error. The rendered report - including SARIF - is written to wherever --output points (or stdout); there are no separate side-artifact files.

The Docker image

Use the prebuilt CLI image when your CI runners don't have solarxy-cli natively installed, or when you need a fully reproducible, container-isolated run that pins the binary version. For bare-metal runners already provisioned with Solarxy, skip the image and call the binary directly.

A prebuilt CLI image is published to GitHub Container Registry on every release:

Tag Meaning
ghcr.io/marko-koljancic/solarxy-cli:latest Most recent stable release. Convenient, not reproducible.
ghcr.io/marko-koljancic/solarxy-cli:0.6 Latest patch of the 0.6.x line. Recommended for CI - picks up fixes, no breaking changes.
ghcr.io/marko-koljancic/solarxy-cli:0.6.0 An exact version. Use for fully reproducible builds.

The image is built from Dockerfile.cli. If your runners can't reach ghcr.io, mirror it into your internal registry:

docker pull ghcr.io/marko-koljancic/solarxy-cli:0.6
docker tag  ghcr.io/marko-koljancic/solarxy-cli:0.6 registry.internal/solarxy-cli:0.6
docker push registry.internal/solarxy-cli:0.6

GitHub Actions

This workflow runs on every pull request touching the assets/ directory or solarxy.toml, validates with the github-actions adapter, and surfaces findings as inline annotations on the files-changed view via the default gha-commands format.

name: Validate Assets
on:
  pull_request:
    paths:
      - "assets/**"
      - "solarxy.toml"

jobs:
  validate:
    runs-on: ubuntu-latest
    container: ghcr.io/marko-koljancic/solarxy-cli:0.6
    steps:
      - uses: actions/checkout@v4
      - name: Validate assets
        run: |
          solarxy-cli --mode analyze \
            --paths "assets/**/*.glb" "assets/**/*.gltf" \
            --config solarxy.toml \
            --adapter github-actions \
            --fail-on error

SARIF + Code Scanning

To surface findings in the repository's Security → Code scanning tab, emit SARIF and upload it:

      - name: Validate assets (SARIF)
        run: |
          solarxy-cli --mode analyze \
            --paths "assets/**/*.glb" \
            --adapter github-actions --adapter-format sarif \
            --output solarxy.sarif \
            --fail-on never
      - name: Upload SARIF
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: solarxy.sarif

--fail-on never keeps the validation step green so the upload always runs; if: always() makes the upload survive a failing earlier step. Code Scanning then surfaces the findings and tracks them across commits.

GitLab CI

This job runs on every merge request that changes asset files, validates with the generic adapter and junit-xml format, and surfaces findings in the merge-request Tests tab via GitLab's native artifacts:reports:junit.

validate-assets:
  image: ghcr.io/marko-koljancic/solarxy-cli:0.6
  script:
    - solarxy-cli --mode analyze
        --paths "assets/**/*.glb" "assets/**/*.gltf"
        --config solarxy.toml
        --adapter generic
        --adapter-format junit-xml
        --output validation-report.xml
        --fail-on error
  artifacts:
    when: always
    reports:
      junit: validation-report.xml
    paths:
      - validation-report.xml
    expire_in: 30 days
  rules:
    - changes:
        - "assets/**/*"
        - "solarxy.toml"

Keep both reports:junit: (renders the Tests tab) and the literal path under paths: (keeps the XML browsable as an artifact). The rules:changes: block skips the job entirely for code-only commits.

To gate merges but keep branch builds informational, split the job: run --fail-on never on branch pushes and --fail-on error on $CI_PIPELINE_SOURCE == "merge_request_event".

Jenkins

This stage runs as part of an asset-build pipeline, validates with the generic adapter and junit-xml format, and surfaces findings in the build's Test Results panel via the JUnit Plugin's junit step.

stage('Validate Assets') {
  agent {
    docker {
      image 'ghcr.io/marko-koljancic/solarxy-cli:0.6'
      args  '-v $WORKSPACE:/workspace -w /workspace'
    }
  }
  steps {
    sh '''
      solarxy-cli --mode analyze \
        --paths "Content/**/*.glb" "Content/**/*.gltf" \
        --config solarxy.toml \
        --adapter generic \
        --adapter-format junit-xml \
        --output validation-report.xml \
        --fail-on error
    '''
  }
  post {
    always {
      junit testResults: 'validation-report.xml', allowEmptyResults: true
    }
  }
}

On Windows agents, swap sh for bat, use solarxy-cli.exe, and continue lines with ^ instead of \. Install the CLI on bare-metal agents with winget install Koljam.Solarxy or the portable .zip (see Installation).

Stage placement: run validation before any cook / build / package stage - per-asset validation takes seconds, a cook takes minutes. --fail-on error aborts the pipeline before the expensive stages run.

Warning-only assets stay green in the Test Results panel by design; warning detail surfaces in each testcase's <system-out>. Use --fail-on warning if you want warnings to mark the build red.

Perforce / Helix Core

For Perforce, validation runs as a change-submit trigger - a Python script that rejects a p4 submit when it contains a bad asset. The script and a full setup guide ship in the repo at docs/integrations/perforce/.

Outline:

  1. Copy change-submit.py to a stable path on the P4D server (e.g. /opt/solarxy/).

  2. Register it in the trigger table (p4 triggers), scoped to your depot path:

    solarxy-validate change-submit //depot/Project/... "/usr/bin/python3 /opt/solarxy/change-submit.py %changelist%"
    
  3. The script extracts pending model files, runs solarxy-cli --mode analyze against them, and returns:

    Exit Meaning Submit
    0 No model files, or validation passed Accepted
    1 Validation found errors Rejected - the artist sees the findings
    2 Tool error (script crash, CLI not found, …) Rejected - the artist is told to contact an admin

Requirements: Helix Core 2024.2+, Python 3.10+ on the trigger host, and solarxy-cli on the trigger user's PATH. To disable the trigger without removing it, prefix its line in the trigger table with #.

Troubleshooting

no model files matched / empty result. The glob ran but matched nothing. Quote your --paths patterns - an unquoted ** is expanded by the shell before solarxy-cli sees it. Remember globs resolve relative to the current working directory; run the CLI from your repository root.

--paths seems to do nothing / the GUI launches. You omitted --mode analyze. With the default --mode view, solarxy-cli tries to launch the GUI and ignores --paths.

UnsupportedFormat error. You asked the generic adapter for gha-commands or sarif. Those two formats are github-actions-only - see the format support matrix.

Findings missing from the MR / build UI. The JUnit XML must be wired into the CI system's native test reporter (artifacts:reports:junit for GitLab, the junit step for Jenkins). Producing the file is not enough - the reporter has to ingest it.

Wrong solarxy.toml picked up. For --paths runs, config discovery starts in the current working directory, not the asset directory. Pass --config explicitly, or run from the repo root. See Configuration → Discovery order.

See also: CLI Reference · Configuration · Installation · User Guide

Clone this wiki locally