# Skripte und CI

Nach dieser Anleitung läuft die CLI in einer Pipeline ohne Profildatei, Ihr Skript reagiert auf Exit-Codes statt auf Text, holt Listen vollständig, begrenzt Wartezeiten und wiederholt verändernde Aufrufe, ohne einen Vorgang doppelt anzustoßen.

> **Typ:** anleitung
> **Für wen:** Wer die CLI in einer CI-Pipeline oder einem Cron-Lauf betreibt
> **Voraussetzung:** die gebaute CLI im Lauf, die Basis-Adresse der Umgebung und ein API-Schlüssel `ak_…` mit den Rechten der Befehle — als Geheimnis der CI, nicht als Datei
> **Ort:** Kommandozeile

## Schritte

1. Legen Sie Adresse und Schlüssel als Geheimnisse Ihrer CI ab und reichen Sie sie als `SZENERA_BASE_URL` und `SZENERA_API_KEY` in den Lauf. Beide Variablen tragen zusammen ein vollständiges Profil; eine Datei auf der Platte ist nicht nötig, und das Profil ist nicht schreibgeschützt.

2. Rufen Sie die CLI direkt über die gebaute Datei auf, nie über `pnpm run` — sonst sieht Ihr Skript für jeden Fehler den Code `1`:

   ```bash
   export SZENERA_BASE_URL="https://<basis-adresse>"
   export SZENERA_API_KEY="$CI_GEHEIMNIS_SIGNAGE"
   szenera() { node packages/cli/dist/main.js "$@"; }
   ```

3. Prüfen Sie am Anfang jeder Pipeline die Umgebung und den Ausweis — zwei Aufrufe, die Netz und Schlüssel trennen:

   ```bash
   szenera health --json > /dev/null || { echo "Umgebung nicht bereit"; exit 1; }
   szenera whoami --json | jq -r '.tenantId'
   ```

4. Werten Sie den Exit-Code aus, nicht die Meldung. Die Standardausgabe trägt im Fehlerfall nichts, deshalb darf sie gefahrlos in eine Datei gehen:

   ```bash
   szenera asset upload --file=plakat.png --json > ergebnis.json
   case $? in
     0)     echo "fertig: $(jq -r '.id' ergebnis.json)" ;;
     8|10)  sleep 30; echo "später erneut versuchen" ;;   # Bremse oder kein Netz
     11)    echo "Vorgang endete rot — Befund lesen"; exit 1 ;;
     12)    echo "Zeitlimit — erst nachsehen, nicht blind wiederholen"; exit 1 ;;
     *)     exit 1 ;;
   esac
   ```

5. Verarbeiten Sie die JSON-Ausgabe mit `jq`. Listen tragen `items` und, solange Seiten folgen, `nextCursor`:

   ```bash
   szenera device list --json | jq -r '.items[] | select(.status=="active") | .id'
   ```

## Optionen

**Listen vollständig holen.** `--all` folgt allen Seiten, bis keine mehr kommt, und liefert eine zusammengehängte Liste ohne `nextCursor`. Bei großen Beständen kombinieren Sie es mit `--limit=200`, dem Deckel je Seite:

```bash
szenera scene list --all --limit=200 --json | jq -r '.items[].id'
```

**Wartezeiten begrenzen.** Befehle, die auf einen Vorgang warten — `asset upload`, `asset reprocess`, `device screenshot`, `device send-command --wait` —, nehmen `--timeout=<dauer>` (`90s`, `20m`, `2h` oder eine Zahl in Sekunden) und teils `--interval=<dauer>`. `--no-wait` stößt nur an und kommt sofort zurück, ohne den Ausgang zu kennen. Exit 12 heißt in jedem Fall: Ausgang unbekannt, der Vorgang läuft womöglich weiter.

**Wiederholungen absichern.** Beim Notausgang `api post|put|patch|delete` erzeugt die CLI je Aufruf einen frischen Idempotenz-Schlüssel; `--idempotency-key=<schluessel>` ist nur nötig, um eine abgebrochene Wiederholung fortzusetzen — dann mit demselben Wert wie beim ersten Versuch. Befehle, die von sich aus idempotent sind, sagen es in ihrer Hilfe: `player set-program` zweimal zu senden ist kein zweiter Vorgang.

```bash
szenera api post /v1/playlists @playlist.json --idempotency-key="import-${CI_LAUF_ID}"
```

**Schutz vor Unwiderruflichem.** Befehle, die etwas Unumkehrbares tun, verlangen `--yes` und brechen ohne die Flagge mit Exit 1 ab — `asset purge`, `asset delete`, `scene delete`, `api-key revoke` und weitere; `szenera <befehl> --help` nennt die Flagge. Für Profile in einer Datei bleibt `readOnly: true` der Riegel: Ein verändernder Befehl bricht dann mit Exit 2 ab, bevor ein Netzpaket entsteht. Ein Profil allein aus Umgebungsvariablen hat diesen Riegel nicht.

## Ergebnis prüfen

- `szenera health` und `szenera whoami` laufen am Anfang der Pipeline mit Exit 0; `whoami` nennt die erwartete Organisation unter `tenantId`.
- Ein absichtlich falscher Aufruf (`szenera device list --state=active`) endet mit Exit 1 und einer Meldung auf der Fehlerausgabe; die umgeleitete Datei bleibt leer.
- Nach einem Upload zeigt `szenera asset get <ast_…> --json` den Zustand `ready`.

## Wenn etwas nicht klappt

| Symptom | Bedeutung | Lösung |
|---|---|---|
| Jeder Fehler endet mit `1`, auch ein 401 | Der Aufruf läuft über `pnpm run`. | Die Datei direkt über `node` aufrufen (Schritt 2). |
| „Keine Konfiguration gefunden“ (Exit 2) | `SZENERA_BASE_URL` fehlt oder ist leer. | Beide Variablen im Lauf setzen; siehe [Profile und Ausweis](/de/cli/profiles-and-credentials). |
| Exit 3 bei jedem Befehl außer `health` | Der Schlüssel gilt nicht — widerrufen, abgelaufen oder für eine andere Umgebung ausgestellt. | Neuen Schlüssel erzeugen und das Geheimnis austauschen. |
| Exit 4 bei einem einzelnen Befehl | Der Schlüssel trägt das Recht nicht, oder das Recht ist für Schlüssel nicht vorgesehen. | Spalte „Schlüssel“ unter [Befehle für Ihre Organisation](/de/cli/commands-organization) prüfen; sonst Profil mit Kennwort-Anmeldung. |
| Exit 12 nach einem Upload | Das Zeitlimit war zu knapp; die Verarbeitung läuft weiter. | `--timeout=20m` setzen und vor einer Wiederholung mit `asset get` nachsehen. |

## Weiterführendes

- [Ausgabe als JSON und Exit-Codes](/de/cli/output-and-exit-codes)
- [Profile und Ausweis](/de/cli/profiles-and-credentials)
- [Idempotenz von Anfang an](/de/getting-started/idempotency)
