# CI and automation

`cademi` runs without a terminal in CI pipelines, cron jobs, and containers. Give it a credential through an environment variable, pass `--yes` to commands that confirm, and read the result from the exit code and the JSON output.

## Install a pinned version

Pin the version so every run uses the same CLI, and leave the shell startup files unchanged:

```sh
curl -fsSL https://cli.cademi.dev/install.sh | CADEMI_VERSION=0.1.4 CADEMI_NO_MODIFY_PATH=1 bash
export PATH="$HOME/.cademi/bin:$PATH"
```

In GitHub Actions:

```yaml
- name: Install cademi
  run: |
    curl -fsSL https://cli.cademi.dev/install.sh | CADEMI_VERSION=0.1.4 CADEMI_NO_MODIFY_PATH=1 bash
    echo "$HOME/.cademi/bin" >> "$GITHUB_PATH"
```

Automatic updates are off whenever the `CI` variable is set, which most CI services do, and they never start without a terminal. See [Installation](https://cademi.dev/cli/installation.md#automatic-updates).

## Credentials

Store the credential secret in your CI secret store and expose it as `CADEMI_API_KEY`. The CLI then runs in autonomous mode, without profiles or a keychain:

```yaml
env:
  CADEMI_API_KEY: ${{ secrets.CADEMI_API_KEY }}
```

- Use a credential whose `auth_mode` allows autonomous mode (`autonomous` or `both`). A `human_required` credential cannot be used without an administrator session.
- Grant it only the permissions the job needs ([Permissions and scope](https://cademi.dev/api/access-control.md)).
- Use sandbox credentials (`ck_test_...`) for test pipelines ([Sandbox](https://cademi.dev/cli/sandbox.md)).
- Set `CADEMI_BASE_URL` only if you call a platform URL other than `https://api.cademi.com.br`.

Check the setup at the start of the job:

```sh
cademi credentials current --jq '.environment'
```

If `CADEMI_API_KEY` is empty or not set and the machine has no saved profile, the check exits with code `3`, like any other command that needs a credential ([Exit codes](https://cademi.dev/cli/exit-codes.md)).

## Non-interactive behavior

Without a terminal, the CLI never waits for input:

- Commands that ask for confirmation (`delete` commands, `cademi config apply`, `cademi sandbox reset`) fail with exit code `2` unless you pass `--yes`.
- Responses that would print as a table in a terminal print as JSON.
- Status messages, warnings, and progress go to standard error. Standard output carries only data.

## Reading results

Use `--json` or `--jq` to parse output, and the exit code to decide what to do next ([Exit codes](https://cademi.dev/cli/exit-codes.md)). With `--json`, `-o json`, or `--jq`, errors also print as JSON on standard error.

```sh
product_id=$(cademi products create -f name="Course A" -f format=course -f showcase_id=shw_01J8Z1... --jq .id)
cademi products publications create "$product_id" --wait --wait-timeout 30m
```

`--wait` makes the command wait for an asynchronous operation. It exits with `1` if the operation ends as `failed` or `canceled`, or if `--wait-timeout` runs out first. An operation that ends as `partially_succeeded` exits with `0` and a warning, so check `status` in the JSON output when partial failures matter. Without `--wait`, the job continues as soon as the operation is created.

Scripts and agents can describe the whole CLI in one call with `cademi commands --json`, or `cademi commands --brief --json` for a smaller index, instead of running `--help` on each command ([Commands](https://cademi.dev/cli/commands.md#finding-commands)).

## Safe retries

The CLI retries network errors and `429`, `500`, `502`, `503`, and `504` responses on its own, with the same `Idempotency-Key` ([Commands](https://cademi.dev/cli/commands.md#writes-retries-and-idempotency)). To make a rerun of a whole job safe as well, derive the key from something stable, such as the pipeline run and step:

```sh
cademi users create --idempotency-key "run-${GITHUB_RUN_ID}-create-ana" \
  -f name="Ana Souza" -f email=ana@example.com
```

A rerun within 48 hours with the same key and the same body returns the stored result instead of creating a second user. See [Asynchronous operations, batches, and idempotency](https://cademi.dev/api/operations.md#idempotency).

## Configuration as code

Plan on every change and apply on merge:

```sh
# review step
cademi config validate catalog.yaml
cademi config plan catalog.yaml --out plan.json

# deploy step
cademi config apply catalog.yaml --yes
```

`cademi config validate` exits with code `6` when the manifest is invalid, which fails the review step. `cademi config apply` computes a fresh plan, so it applies the manifest as it is at deploy time. To apply exactly the reviewed plan instead, pass it with `--plan plan.json`, within 24 hours and with the same credentials that created it. Pass the manifest as well when the plan contains secrets. See [Declarative configuration](https://cademi.dev/cli/config.md).

## Events in automated tests

`cademi listen --forward-to` works in CI to deliver events to a service under test. Without a profile, set `CADEMI_LISTEN_SECRET` so the signing secret matches the one your service expects:

```sh
export CADEMI_LISTEN_SECRET=whsec_local_...
cademi listen --forward-to localhost:3000/webhooks/cademi --json > events.jsonl &
cademi sandbox run scn_01J8Z3...
```

Unless you pass `--replay`, `cademi listen` receives only the events emitted after its event stream is created. Start it before the step that produces events, and wait for its `Ready!` line on standard error. See [Events with cademi listen](https://cademi.dev/cli/listen.md).
