Skip to content

CI integration

OpenComplAI runs in GitHub Actions and GitLab CI via the CLI and connector scripts, no dashboard account required: pip install opencomplai, run opencomplai init to write a system-manifest.json, then copy the workflow file for your platform below as a CI step — it runs opencomplai check on every push/PR and fails the build on a control failure. The scan needs no key; OPENCOMPLAI_API_KEY is only needed to publish results to the dashboard.

Optional — hosted dashboard: if you also use the dashboard, open /connect for your project (Projects → your project → Connect) instead of copying the snippet by hand — it generates the same file with your own dashboard host already filled in, so to publish you only need to add OPENCOMPLAI_API_KEY as a secret. The copies below use https://YOUR-DASHBOARD-HOST as a placeholder for that value; without it, drop the OPENCOMPLAI_DASHBOARD_URL/push step and rely on the CLI's own exit code as the CI gate.

Looking for a GitLab CI/CD component that calls opencomplai check directly, or an Azure Pipelines template? See CI for GitLab and Azure Pipelines.

GitHub Actions

Copy this file to .github/workflows/opencomplai-scan.yml in your AI system repository. Requires a system-manifest.json in your repo root (see the CLI's own opencomplai init).

To publish to the dashboard (the scan itself needs no key), add your API key as a repository secret: Settings → Secrets and variables → Actions → New repository secret — name it OPENCOMPLAI_API_KEY, value is the key you issued on the project's page in the dashboard.

YAML
# OpenComplAI compliance scan — GitHub Actions
#
# Copy this file to .github/workflows/opencomplai-scan.yml in your AI system
# repository. Requires a system-manifest.json in your repo root (see the
# CLI's own `opencomplai init`).
#
# Before this runs, add your API key as a repository secret:
#   Settings -> Secrets and variables -> Actions -> New repository secret
#   Name:  OPENCOMPLAI_API_KEY
#   Value: the key you issued on the project's page in this dashboard
name: OpenComplAI compliance scan

on:
  push:
    branches: [main]
  pull_request:

jobs:
  opencomplai-scan:
    name: OpenComplAI compliance scan
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7

      - name: Set up Python
        uses: actions/setup-python@v6
        with:
          python-version: "3.11"

      - name: Install OpenComplAI CLI
        run: pip install opencomplai

      - name: Run OpenComplAI compliance scan
        run: opencomplai-gha-connector
        env:
          # Exit-code contract (opencomplai check / opencomplai-gha-connector):
          #   0 = pass             scan passed (or degraded_complete in local scan mode)
          #   1 = control_fail     a required control failed
          #   2 = validation_fail  manifest or input validation error
          #   3 = policy_block     prohibited system (EU AI Act Article 5)
          #   4 = trap_detected    Article 25 substantial-modification freeze
          #
          # This dashboard's own ingest endpoint — safe to commit as plain
          # text, it is not a secret.
          OPENCOMPLAI_DASHBOARD_URL: https://YOUR-DASHBOARD-HOST/api/ingest
          # Set in this repository's (or organization's) Actions secrets —
          # never commit the key itself.
          OPENCOMPLAI_API_KEY: ${{ secrets.OPENCOMPLAI_API_KEY }}
#
# Optional scheduled re-run. To use it, remove the leading "# " from each line below
# and save the result as .github/workflows/opencomplai-rerun.yml.
# name: OpenComplAI re-run
#
# on:
#   schedule:
#     - cron: "0 6 * * 1,4"
#   workflow_dispatch:
#
# permissions:
#   contents: read
#
# jobs:
#   opencomplai-rerun:
#     runs-on: ubuntu-latest
#     steps:
#       - name: Checkout repository
#         uses: actions/checkout@v7
#
#       - name: Set up Python
#         uses: actions/setup-python@v6
#         with:
#           python-version: "3.11"
#
#       - name: Install OpenComplAI CLI
#         run: pip install --upgrade opencomplai
#
#       - name: Restore the last baseline
#         uses: actions/cache@v4
#         with:
#           path: baseline
#           key: opencomplai-baseline-${{ github.run_id }}
#           restore-keys: opencomplai-baseline-
#
#       - name: Check, diff against the baseline, keep the new baseline
#         run: |
#           CHECK_EXIT=0
#           opencomplai check --with-gaps || CHECK_EXIT=$?
#           DIFF_EXIT=0
#           if [ -f baseline/compliance-artifact.json ] && [ -f compliance-artifact.json ]; then
#             opencomplai diff baseline/compliance-artifact.json compliance-artifact.json --fail-on-regression || DIFF_EXIT=$?
#           fi
#           if [ -f compliance-artifact.json ]; then
#             mkdir -p baseline
#             cp compliance-artifact.json baseline/compliance-artifact.json
#           fi
#           if [ "$DIFF_EXIT" -ne 0 ]; then exit "$DIFF_EXIT"; fi
#           exit "$CHECK_EXIT"

GitLab CI

Copy this into your .gitlab-ci.yml (or include: it) in your AI system repository. Requires a system-manifest.json in your repo root (see the CLI's own opencomplai init).

To publish to the dashboard (the scan itself needs no key), add your API key as a masked CI/CD variable: Settings → CI/CD → Variables → Add variable — key OPENCOMPLAI_API_KEY, value is the key you issued on the project's page in the dashboard, flags Protect variable + Mask variable.

YAML
# OpenComplAI compliance scan — GitLab CI
#
# Copy this into your .gitlab-ci.yml (or `include:` it) in your AI system
# repository. Requires a system-manifest.json in your repo root (see the
# CLI's own `opencomplai init`).
#
# Before this runs, add your API key as a masked CI/CD variable:
#   Settings -> CI/CD -> Variables -> Add variable
#   Key:   OPENCOMPLAI_API_KEY
#   Value: the key you issued on the project's page in this dashboard
#   Flags: Protect variable, Mask variable
opencomplai-scan:
  image: python:3.11
  before_script:
    - pip install opencomplai
  script:
    - opencomplai-gitlab-connector
  artifacts:
    reports:
      junit: opencomplai-report.xml
    dotenv: opencomplai.env
  variables:
    # Exit-code contract (opencomplai check / opencomplai-gitlab-connector):
    #   0 = pass             scan passed (or degraded_complete in local scan mode)
    #   1 = control_fail     a required control failed
    #   2 = validation_fail  manifest or input validation error
    #   3 = policy_block     prohibited system (EU AI Act Article 5)
    #   4 = trap_detected    Article 25 substantial-modification freeze
    #
    # This dashboard's own ingest endpoint — safe to commit as plain text,
    # it is not a secret. OPENCOMPLAI_API_KEY is NOT declared here — it
    # comes from the masked/protected CI/CD variable set above, which
    # GitLab injects into the job environment automatically.
    OPENCOMPLAI_DASHBOARD_URL: "https://YOUR-DASHBOARD-HOST/api/ingest"
    GL_ENV_FILE: opencomplai.env

Any other CI platform

No dedicated connector script — run the CLI directly and let it push for you.

--sign needs a signing key: ~/.opencomplai/signing.key (created by opencomplai init) or SIGNING_KEY_PRIVATE (the base64-encoded PEM, stored as a CI secret). With neither, it exits 2.

Bash
pip install opencomplai
opencomplai check --sign
# Keyless CI: write the artifact without a signature and print a warning instead of exiting 2:
# opencomplai check --sign-if-available
CHECK_EXIT=$?

# Push the scan artifact to the dashboard via the CLI:
OPENCOMPLAI_API_KEY="$OPENCOMPLAI_API_KEY" \
OPENCOMPLAI_DASHBOARD_URL="https://YOUR-DASHBOARD-HOST/api/ingest" \
  opencomplai push

exit "$CHECK_EXIT"

A raw POST to the ingest endpoint is rejected (HTTP 422) because the CLI shapes the artifact first, so always use opencomplai push.

Recipes for other systems are in CI recipes for other systems:

The GitHub Actions and GitLab CI connectors choose the flag for you: --sign when SIGNING_KEY_PRIVATE is set, --sign-if-available otherwise.

Reports from check

opencomplai check can write three extra files from the same verdict as compliance-artifact.json, so any CI can show results without a connector:

  • --report-junit PATH writes JUnit XML (one test case, a failure when the check fails).
  • --sarif-output PATH writes SARIF 2.1.0 of the check verdict (failed controls and gap rows).
  • --summary-md PATH writes the Markdown job summary the GitHub connector shows.
Text Only
opencomplai check --with-gaps --report-junit report.xml --sarif-output check.sarif --summary-md summary.md

Hand them to your platform: in GitLab CI list report.xml under artifacts: reports: junit:; in GitHub Actions pass check.sarif to github/codeql-action/upload-sarif as sarif_file.

Each report states the signed state: the Signed row of the summary, the JUnit property opencomplai.signed and the SARIF run property signed, plus the signer key id (sha256: and 16 hex characters) when the run signed. "yes" means a signature is present, not that it was verified.

The exit code is the same with or without these flags. A report that cannot be written prints a WARN on stderr and the run carries on. The files hold no dates. With several --manifest values each system writes its own file, named <stem>.<system_id><ext> beside the path you gave (--report-junit report.xml writes report.alpha.xml and report.beta.xml), so point artifact globs at report.*.xml. A single-manifest run writes the exact path given.

Scope

v1 focuses on the EU AI Act. OpenComplAI produces structured evidence, not legal sign-off. Pipeline evaluators (safety, bias, data-leakage) require a customer-supplied EvalSampleSet JSON; when omitted, evals are skipped and rule checks still run.