Intermediate30 minModule 5 of 5

Scripting & CI/CD

Use JSON output, jq, and environment variables to automate deployments in GitHub Actions, GitLab CI, and shell scripts.

What you'll be able to do after this module

Wire the Skytells CLI into automated pipelines — deploy on every push, verify the result, and fail the build if something goes wrong.


Prerequisites

  • Modules 1–4 complete
  • Familiarity with your CI platform (GitHub Actions examples used throughout; the pattern translates directly to GitLab CI, CircleCI, etc.)

The foundation: JSON output

Every CLI command supports --json. In interactive mode you get formatted tables. In scripts, you want structured data you can parse.

# Human-readable
skytells apps ls

# Machine-readable
skytells apps ls --json

Add jq to process it:

# Just the app names
skytells apps ls --json | jq '.[].name'

# App IDs where status is running
skytells apps ls --json | jq '[.[] | select(.status == "running") | .id]'

# Latest deployment status for a specific app
skytells deployments ls --app my-api --limit 1 --json | jq -r '.[0].status'

Authenticating in CI

Never store credentials in code or config files. Use your CI platform's secrets manager.

The CLI picks up two environment variables automatically:

VariableWhat it is
SKYTELLS_TOKENYour personal access token (from console.skytells.ai/settings/tokens)
SKYTELLS_ACCESS_KEYThe project access key (from your project Settings in the Console)

When both are set, no skytells login step is needed. The CLI uses them directly.


GitHub Actions

Basic deploy workflow

Create .github/workflows/deploy.yml:

name: Deploy to Skytells

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Install Skytells CLI
        run: npm install -g @skytells/cli

      - name: Deploy
        env:
          SKYTELLS_TOKEN: ${{ secrets.SKYTELLS_TOKEN }}
          SKYTELLS_ACCESS_KEY: ${{ secrets.SKYTELLS_ACCESS_KEY }}
        run: skytells deploy my-api

      - name: Verify deployment
        env:
          SKYTELLS_TOKEN: ${{ secrets.SKYTELLS_TOKEN }}
          SKYTELLS_ACCESS_KEY: ${{ secrets.SKYTELLS_ACCESS_KEY }}
        run: |
          STATUS=$(skytells deployments ls --app my-api --limit 1 --json | jq -r '.[0].status')
          echo "Deployment status: $STATUS"
          if [ "$STATUS" != "success" ]; then
            echo "Deployment did not succeed"
            exit 1
          fi

In GitHub:

  1. Go to your repository → Settings → Secrets and variables → Actions.
  2. Add SKYTELLS_TOKEN — your personal access token.
  3. Add SKYTELLS_ACCESS_KEY — the access key from your project.

Deploy multiple apps in sequence

- name: Deploy API
  run: skytells deploy api-server

- name: Deploy Worker
  run: skytells deploy worker

- name: Verify all apps are running
  run: |
    STATUS=$(skytells status --json | jq -r '.apps[] | select(.status != "running") | .name')
    if [ -n "$STATUS" ]; then
      echo "Apps not running: $STATUS"
      exit 1
    fi
    echo "All apps running"

Update environment variables before deploying

- name: Update environment
  run: |
    skytells env set --app my-api \
      COMMIT_SHA=${{ github.sha }} \
      BUILD_NUMBER=${{ github.run_number }}

- name: Deploy
  run: skytells deploy my-api

GitLab CI

stages:
  - deploy

deploy:
  stage: deploy
  image: node:18
  before_script:
    - npm install -g @skytells/cli
  script:
    - skytells deploy my-api
    - |
      STATUS=$(skytells deployments ls --app my-api --limit 1 --json | jq -r '.[0].status')
      if [ "$STATUS" != "success" ]; then exit 1; fi
  variables:
    SKYTELLS_TOKEN: $SKYTELLS_TOKEN
    SKYTELLS_ACCESS_KEY: $SKYTELLS_ACCESS_KEY
  only:
    - main

Set SKYTELLS_TOKEN and SKYTELLS_ACCESS_KEY in Settings → CI/CD → Variables in GitLab, marked as masked and protected.


Shell scripting patterns

Deploy and wait for confirmation

The CLI's deploy command is non-blocking — it starts the build and exits. To wait for a result, poll the deployment list:

#!/bin/bash
set -euo pipefail

APP="my-api"

echo "Triggering deployment..."
skytells deploy "$APP"

echo "Waiting for deployment to complete..."
MAX_ATTEMPTS=20   # ~5 minutes at 15s intervals
ATTEMPTS=0

while [ "$ATTEMPTS" -lt "$MAX_ATTEMPTS" ]; do
  STATUS=$(skytells deployments ls --app "$APP" --limit 1 --json | jq -r '.[0].status')

  if [ "$STATUS" = "success" ]; then
    echo "Deployment succeeded"
    exit 0
  elif [ "$STATUS" = "failed" ]; then
    echo "Deployment failed"
    skytells logs "$APP" --type deployment --tail 50
    exit 1
  fi

  echo "Status: $STATUS — waiting..."
  sleep 15
  ATTEMPTS=$((ATTEMPTS + 1))
done

echo "Timed out waiting for deployment"
exit 1

Check whether a specific app is running before deploying

#!/bin/bash
APP_ID="abc123"

STATUS=$(skytells apps inspect "$APP_ID" --json | jq -r '.status')

if [ "$STATUS" = "stopped" ]; then
  echo "App is stopped — starting before deploy"
  skytells apps start "$APP_ID"
  sleep 5
fi

skytells deploy "$APP_ID"

Rotate a secret across all apps in a project

#!/bin/bash
NEW_SECRET=$(openssl rand -hex 32)
APP_IDS=$(skytells apps ls --json | jq -r '.[].id')

for ID in $APP_IDS; do
  echo "Updating SECRET_KEY for app $ID"
  skytells env set --app "$ID" SECRET_KEY="$NEW_SECRET"
done

echo "Redeploying all apps..."
for ID in $APP_IDS; do
  skytells apps redeploy "$ID"
done

echo "Done. Monitor logs with: skytells logs <app> --type deployment --follow"

Putting it all together: a production deploy checklist

#!/bin/bash
# Full production deploy with checks
set -euo pipefail

APP="my-api"

echo "=== Pre-deploy checks ==="
skytells status

echo "=== Updating build metadata ==="
skytells env set --app "$APP" DEPLOYED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"

echo "=== Deploying ==="
skytells deploy "$APP"

echo "=== Streaming deployment logs ==="
# Stream for up to 3 minutes, then exit regardless
timeout 180 skytells logs "$APP" --type deployment --follow || true

echo "=== Verifying status ==="
FINAL_STATUS=$(skytells deployments ls --app "$APP" --limit 1 --json | jq -r '.[0].status')

if [ "$FINAL_STATUS" = "success" ]; then
  echo "✓ Deployment succeeded"
  skytells status
else
  echo "✗ Deployment status: $FINAL_STATUS"
  exit 1
fi

Quick reference

What you wantCommand or pattern
JSON output for any command<command> --json
Parse with jq<command> --json | jq '<expression>'
CI authenticationSKYTELLS_TOKEN + SKYTELLS_ACCESS_KEY env vars
Deploy in CIskytells deploy <app>
Check latest deploy statusskytells deployments ls --app <id> --limit 1 --json | jq -r '.[0].status'
Stream logs (time-limited)timeout <seconds> skytells logs <app> --follow || true

You've completed the CLI path

You now know how to install and authenticate, manage projects and apps, deploy and debug, configure environment variables, and automate everything in CI pipelines.

Want to go deeper? Check out:

On this page