> For the complete documentation index, see [llms.txt](https://docs.controltheory.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.controltheory.com/controltheory-documentation/dstl8-docs/code-graph/ci.md).

# CI Integration

Push code graphs from GitHub Actions, GitLab CI, or Jenkins

Run the code graph push on every merge to the default branch. The extractor is fast (seconds, even on large repos), the push is skipped when nothing changed, and every integration below is **fail-open** — a graph problem must never break a build.

## Getting a token

CI authenticates with a `dstl8_` API token:

1. In the portal, go to **Organization Settings → Tokens** and create a token (it's shown once — store it immediately in your CI secret store).
2. Or mint a service token via the API (org admin required):

```bash
curl -fsS -X POST "https://acme.app.dstl8.ai/api/tokens/service" \
  -H "Authorization: Bearer <an admin's dstl8_ API token>" \
  -H "Content-Type: application/json" \
  -d '{"name":"GitHub Actions CI","service_name":"github-actions","role":"user","expires_in":"365d","scopes":["*"]}'
```

The same token works for [`dstl8 events send`](/controltheory-documentation/dstl8-docs/events.md), so one secret covers both integrations.

Two rules apply everywhere:

* **Default branch only.** The server tracks one lineage per repo. Pushes from other branches are reported as a skip and exit 0, so a mis-wired trigger warns instead of failing.
* **Add a weekly cron.** Snapshot retention follows your log-data retention; a scheduled push keeps low-traffic repos inside the window. When nothing changed it's nearly free — the client skips the upload if the server copy is fresh, and the server just renews an aging identical snapshot.

## GitHub Actions

Use the [`control-theory/dstl8-codegraph-action`](https://github.com/control-theory/dstl8-codegraph-action) action. It installs a checksum-verified dstl8 CLI release and runs `dstl8 graph push`.

1. Create a token (above) and add it to the repository, or to your GitHub organization, as the secret `DSTL8_API_TOKEN`.
2. Add a variable `DSTL8_API_URL` with your org's API URL, for example `https://acme.app.dstl8.ai`.
3. Add the workflow:

```yaml
# .github/workflows/dstl8-codegraph.yml
name: dstl8 code graph
on:
  push:
    branches: [main]        # default branch only — other branches are skipped
  schedule:
    - cron: "17 6 * * 1"    # weekly refresh keeps low-traffic repos current
  workflow_dispatch:

permissions:
  contents: read

jobs:
  codegraph:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 50    # enough history to diff against the last push
      - uses: control-theory/dstl8-codegraph-action@v1
        with:
          api-url: ${{ vars.DSTL8_API_URL }}
          token: ${{ secrets.DSTL8_API_TOKEN }}
```

**Inputs:**

| Input           | Required | Default  | Description                                                                                             |
| --------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------- |
| `api-url`       | yes\*    |          | Org API base URL, e.g. `https://acme.app.dstl8.ai`                                                      |
| `token`         | yes\*    |          | `dstl8_` API token. Store it as a secret.                                                               |
| `version`       | no       | `latest` | dstl8 CLI release to use, e.g. `v0.2.10`. Pin it for reproducible builds.                               |
| `path`          | no       | `.`      | Directory to extract, for monorepos where the graph root isn't the repo root                            |
| `extra-args`    | no       |          | Extra `dstl8 graph` flags, shell-quoted, e.g. `--exclude 'gen/**' --service checkout='cmd/checkout/**'` |
| `dry-run`       | no       | `false`  | `"true"` builds the graph to a file (output `artifact-path`) and uploads nothing                        |
| `fail-on-error` | no       | `false`  | `"true"` fails the job when the push cannot complete                                                    |
| `label`         | no       |          | Name shown in log lines when one job pushes to several orgs                                             |

\* Not needed with `dry-run`.

**Outputs:** `status` (`pushed`, `refreshed`, `unchanged`, `skipped`, `dry-run` or `failed`), `repo`, `commit-sha`, `cli-version`, `artifact-path`.

**Fail-open by default:** every failure mode — CLI download failure, checksum mismatch, a wrong URL or token, a push error — emits a `::warning::` annotation and the job succeeds. Check the workflow's annotations if graphs stop arriving, or set `fail-on-error: "true"` to make problems fail the job.

**Audit before you push:** run the action once with `dry-run: "true"` and upload `artifact-path` with `actions/upload-artifact` to review exactly what would be sent.

**Runners:** Linux and macOS, x86-64 and arm64.

**Prefer not to use a third-party action?** The action is a thin wrapper; the same thing as a plain step:

```yaml
      - name: dstl8 code graph
        continue-on-error: true    # fail-open
        env:
          DSTL8_API_URL: ${{ vars.DSTL8_API_URL }}
          DSTL8_API_TOKEN: ${{ secrets.DSTL8_API_TOKEN }}
        run: |
          curl -fsSL https://install.dstl8.ai/script/dstl8-cli | sh
          dstl8 graph push --json
```

**Checkout depth:** `fetch-depth: 0` is *not* required. The diff against the previously pushed snapshot needs that snapshot's commit to exist locally, so `fetch-depth: 50` covers typical push cadence. A too-shallow clone degrades gracefully — the snapshot pushes without a diff.

{% hint style="success" %}
Pair it with a deploy marker: the code graph push on merge to main tells Dstl8 *what the code looks like*; a `deploy` [event](/controltheory-documentation/dstl8-docs/events.md) with the commit SHA at deploy time tells it *when that code shipped*. Together they give Möbius the full what-changed picture.
{% endhint %}

## GitLab CI

Plain CLI + env auth. `allow_failure: true` gives you the fail-open behavior; `GIT_DEPTH: "50"` covers the diff.

```yaml
# .gitlab-ci.yml
dstl8-codegraph:
  stage: build
  allow_failure: true
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
  variables:
    GIT_DEPTH: "50"
  script:
    - curl -fsSL https://install.dstl8.ai/script/dstl8-cli | sh
    - dstl8 graph push --json
```

Set `DSTL8_API_TOKEN` (masked) and `DSTL8_API_URL` as CI/CD variables — the CLI authenticates from the environment alone, no profile or login step. Add a [scheduled pipeline](https://docs.gitlab.com/ee/ci/pipelines/schedules.html) on the default branch for the weekly refresh.

## Jenkins

```groovy
stage('dstl8 code graph') {
  when { branch 'main' }
  environment {
    DSTL8_API_TOKEN = credentials('dstl8-api-token')
    DSTL8_API_URL   = 'https://acme.app.dstl8.ai'
  }
  steps {
    // fail-open: never break the build over a graph push
    sh 'curl -fsSL https://install.dstl8.ai/script/dstl8-cli | sh && dstl8 graph push --json || true'
  }
}
```

## Any other CI

The whole integration is one authenticated command from the repo checkout:

```bash
DSTL8_API_TOKEN="dstl8_…" DSTL8_API_URL="https://acme.app.dstl8.ai" dstl8 graph push --json
```

Gate it to the default branch, tolerate failure, and prefer a checkout with \~50 commits of history. In checkouts without a usable `.git` (source exports), pass the identity flags — see [gitless environments](/controltheory-documentation/dstl8-docs/code-graph/cli.md#gitless-environment-exported-tarball-no-git) in the CLI reference.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.controltheory.com/controltheory-documentation/dstl8-docs/code-graph/ci.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
