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. These examples pin version 0.5.2:
curl -fsSL https://cli.cademi.dev/install.sh | CADEMI_VERSION=0.5.2 CADEMI_NO_MODIFY_PATH=1 bash
export PATH="$HOME/.cademi/bin:$PATH"In GitHub Actions:
- name: Install cademi
run: |
curl -fsSL https://cli.cademi.dev/install.sh | CADEMI_VERSION=0.5.2 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.
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:
env:
CADEMI_API_KEY: ${{ secrets.CADEMI_API_KEY }}- Grant it only the permissions the job needs (Permissions and scope).
- Use sandbox credentials (
ck_test_...) for test pipelines (Sandbox). - Set
CADEMI_BASE_URLonly if you call an API URL other thanhttps://api.cademi.com.br.
Check the setup at the start of the job:
cademi integrations 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).
Non-interactive behavior
Without a terminal, the CLI never waits for input:
- Commands that ask for confirmation (
deletecommands,cademi config apply,cademi sandbox reset) fail with exit code2unless 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). With --json, -o json, or --jq, errors also print as JSON on standard error.
product_id=$(cademi content products create -f name="Course A" -f format=course -f showcase_id=shw_01J8Z1... --jq .id)
cademi content products publish-all "$product_id" --wait --wait-timeout 30m--wait makes the command wait for an asynchronous operation, including the operation of an export or a processing attempt (Asynchronous operations). 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 CLI with cademi commands --json instead of running --help on each command. Start with cademi commands --groups --brief --json for a compact index of the domains, then ask for one area, such as cademi commands content products --json (Commands). cademi guide <topic> --help explains errors, input, output, and retries for automation.
Safe retries
The CLI retries network errors and 429, 500, 502, 503, and 504 responses on its own, with the same Idempotency-Key (Commands). A 503 that carries an API error code but no Retry-After header, such as storage_unavailable, is not retried and exits with code 8. To make a rerun of a whole job safe as well, derive the key from something stable, such as the pipeline run and step:
cademi users create --idempotency-key "run-${GITHUB_RUN_ID}-create-ana" \
-f name="Ana Souza" -f [email protected]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.
When a write fails after it was sent, the JSON error on standard error also carries the key the CLI used, in request.idempotency_key, next to request.method and request.path. To repeat that write in a later step, pass the key with --idempotency-key and keep the same body:
cademi users create --data @new-user.json --json
# If it fails, take request.idempotency_key from the JSON error on standard error:
cademi users create --data @new-user.json --json --idempotency-key '<returned-key>'An error raised before the request is sent, such as a validation error or a missing required flag with exit code 2, has no request object.
To keep a job within its time limit, lower the number of retries with CADEMI_MAX_RETRIES and the longest wait with CADEMI_MAX_RETRY_WAIT (Environment variables). A 429 that asks for a longer wait then exits with code 7 right away:
env:
CADEMI_MAX_RETRIES: "2"
CADEMI_MAX_RETRY_WAIT: 10sFinding a job's requests
Set CADEMI_CLIENT_REQUEST_ID to an identifier of the run. The CLI sends it with every request, and the API records it in the request log: the entries of cademi integrations requests list from that run show it in client_request_id (Requests).
env:
CADEMI_CLIENT_REQUEST_ID: gh-${{ github.run_id }}-${{ github.run_attempt }}The value has 1 to 64 characters: letters, digits, ., _, and -. With any other value, commands exit with code 2.
Configuration as code
Plan on every change and apply on merge:
# review step
cademi config validate catalog.yaml
cademi config plan catalog.yaml --out plan.json
# deploy step
cademi config apply catalog.yaml --yescademi 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.
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:
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.