Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

CI/CD Pipeline

Automate building, bundling, and deploying ST programs to target devices from your CI/CD system.

Pipeline Overview

┌─────────┐    ┌─────────┐    ┌──────────┐    ┌──────────┐    ┌────────┐
│  Build   │───►│  Check  │───►│  Bundle  │───►│  Deploy  │───►│ Verify │
│ st-cli   │    │ st-cli  │    │ st-cli   │    │ curl API │    │ curl   │
│          │    │ check   │    │ bundle   │    │ upload   │    │ status │
└─────────┘    └─────────┘    └──────────┘    └──────────┘    └────────┘

Example: GitHub Actions

name: Deploy PLC Program
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Build st-cli
        run: cargo build -p st-cli --release

      - name: Check for errors
        run: ./target/release/st-cli check

      - name: Create release bundle
        run: ./target/release/st-cli bundle --release -o dist/program.st-bundle

      - name: Upload to target
        run: |
          curl -sf -X POST \
            -H "Authorization: Bearer ${{ secrets.PLC_AGENT_TOKEN }}" \
            -F "file=@dist/program.st-bundle" \
            https://plc.internal:4840/api/v1/program/upload

      - name: Stop current program
        run: |
          curl -sf -X POST \
            -H "Authorization: Bearer ${{ secrets.PLC_AGENT_TOKEN }}" \
            https://plc.internal:4840/api/v1/program/stop || true

      - name: Start new program
        run: |
          curl -sf -X POST \
            -H "Authorization: Bearer ${{ secrets.PLC_AGENT_TOKEN }}" \
            https://plc.internal:4840/api/v1/program/start

      - name: Verify running
        run: |
          sleep 2
          STATUS=$(curl -sf \
            -H "Authorization: Bearer ${{ secrets.PLC_AGENT_TOKEN }}" \
            https://plc.internal:4840/api/v1/status | python3 -c "
          import sys, json
          d = json.load(sys.stdin)
          print(d['status'])
          ")
          if [ "$STATUS" != "running" ]; then
            echo "ERROR: Program is not running (status: $STATUS)"
            exit 1
          fi
          echo "Program deployed and running successfully"

Example: Shell Script

A simpler script for manual or cron-based deployment:

#!/bin/bash
# deploy.sh — Deploy to production PLC
set -euo pipefail

TARGET="plc@192.168.1.50"
AGENT="http://192.168.1.50:4840"
TOKEN="my-secret-token"
AUTH="-H 'Authorization: Bearer ${TOKEN}'"

echo "=== Building bundle ==="
st-cli bundle --release -o /tmp/deploy.st-bundle

echo "=== Stopping current program ==="
curl -sf -X POST ${AUTH} ${AGENT}/api/v1/program/stop || true

echo "=== Uploading new program ==="
curl -sf -X POST ${AUTH} \
  -F "file=@/tmp/deploy.st-bundle" \
  ${AGENT}/api/v1/program/upload

echo "=== Starting program ==="
curl -sf -X POST ${AUTH} ${AGENT}/api/v1/program/start

echo "=== Verifying ==="
sleep 2
curl -sf ${AUTH} ${AGENT}/api/v1/status | python3 -m json.tool

echo "=== Done ==="
rm /tmp/deploy.st-bundle

Key API Endpoints for CI/CD

StepMethodEndpointPurpose
UploadPOST/api/v1/program/uploadUpload .st-bundle (multipart)
StopPOST/api/v1/program/stopStop current program
StartPOST/api/v1/program/startStart the new program
VerifyGET/api/v1/statusCheck status == "running"
HealthGET/api/v1/healthSmoke test (200 = agent alive)
InfoGET/api/v1/program/infoVerify correct version deployed

Security for CI/CD

  • Use --release bundles in production pipelines (no source code in artifact)
  • Store the agent token in CI secrets (${{ secrets.PLC_AGENT_TOKEN }})
  • Use HTTPS or SSH tunnels for agent access from CI runners
  • Consider auth.read_only: true on production targets, with a separate deploy token for the CI pipeline