name: Deploy on: push: branches: - "**" paths: - "scraper/**" - "ui/**" - "docker-compose.yml" pull_request: types: [closed] # tRPC API helper notes: # Mutations: POST /api/trpc/. # Body: {"0":{"json":{...input...}}} # Header: x-api-key: # Queries: GET /api/trpc/.?batch=1&input={"0":{"json":{...input...}}} # Header: x-api-key: # Response on success: HTTP 200, body: [{"result":{"data":{"json":{...}}}}] jobs: # ── production deploy (main/master only) ───────────────────────────────────── deploy-production: name: Deploy Production runs-on: ubuntu-latest if: > github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') steps: - name: Redeploy production stack run: | RESPONSE=$(curl -s -w "\n%{http_code}" \ -X POST "${{ secrets.DOKPLOY_URL }}/api/trpc/compose.redeploy" \ -H "x-api-key: ${{ secrets.DOKPLOY_TOKEN }}" \ -H "Content-Type: application/json" \ -d '{"0":{"json":{"composeId":"${{ secrets.DOKPLOY_COMPOSE_ID }}"}}}') HTTP_CODE=$(echo "$RESPONSE" | tail -1) BODY=$(echo "$RESPONSE" | head -1) echo "Status: $HTTP_CODE" echo "Body: $BODY" [ "$HTTP_CODE" = "200" ] || { echo "Redeploy failed"; exit 1; } # ── preview deploy (feature branches) ──────────────────────────────────────── deploy-preview: name: Deploy Preview runs-on: ubuntu-latest if: > github.event_name == 'push' && github.ref != 'refs/heads/main' && github.ref != 'refs/heads/master' steps: - name: Sanitize branch name id: branch run: | # Lowercase, replace non-alphanumeric with dashes, strip trailing dashes, max 20 chars SUFFIX=$(echo "${{ github.ref_name }}" \ | tr '[:upper:]' '[:lower:]' \ | sed 's/[^a-z0-9]/-/g' \ | cut -c1-20 \ | sed 's/-*$//') echo "suffix=$SUFFIX" >> $GITHUB_OUTPUT echo "Preview suffix: $SUFFIX" - name: Create or redeploy isolated preview stack run: | # compose.isolatedDeployment creates a new isolated copy of the compose stack # suffixed with the branch name. If the stack already exists it redeploys it. RESPONSE=$(curl -s -w "\n%{http_code}" \ -X POST "${{ secrets.DOKPLOY_URL }}/api/trpc/compose.isolatedDeployment" \ -H "x-api-key: ${{ secrets.DOKPLOY_TOKEN }}" \ -H "Content-Type: application/json" \ -d "{\"0\":{\"json\":{\"composeId\":\"${{ secrets.DOKPLOY_COMPOSE_ID }}\",\"suffix\":\"${{ steps.branch.outputs.suffix }}\"}}}") HTTP_CODE=$(echo "$RESPONSE" | tail -1) BODY=$(echo "$RESPONSE" | head -1) echo "Status: $HTTP_CODE" echo "Body: $BODY" [ "$HTTP_CODE" = "200" ] || { echo "Preview deploy failed"; exit 1; } # ── cleanup preview on PR close ─────────────────────────────────────────────── cleanup-preview: name: Cleanup Preview runs-on: ubuntu-latest if: github.event_name == 'pull_request' && github.event.action == 'closed' steps: - name: Sanitize branch name id: branch run: | SUFFIX=$(echo "${{ github.head_ref }}" \ | tr '[:upper:]' '[:lower:]' \ | sed 's/[^a-z0-9]/-/g' \ | cut -c1-20 \ | sed 's/-*$//') echo "suffix=$SUFFIX" >> $GITHUB_OUTPUT echo "Cleaning up preview suffix: $SUFFIX" - name: Search for preview compose stack by appName id: find run: | # compose.search is a tRPC query (GET). We search by appName pattern. # appName is set by Dokploy as "-" for isolated deployments. INPUT=$(python3 -c "import json,sys; print(json.dumps({'0':{'json':{'appName':'libnovel-${{ steps.branch.outputs.suffix }}','limit':5,'offset':0}}}))") RESPONSE=$(curl -s -w "\n%{http_code}" -G \ "${{ secrets.DOKPLOY_URL }}/api/trpc/compose.search" \ -H "x-api-key: ${{ secrets.DOKPLOY_TOKEN }}" \ --data-urlencode "batch=1" \ --data-urlencode "input=$INPUT") HTTP_CODE=$(echo "$RESPONSE" | tail -1) BODY=$(echo "$RESPONSE" | head -1) echo "Status: $HTTP_CODE" echo "Body: $BODY" # Extract the first composeId from the JSON response array COMPOSE_ID=$(echo "$BODY" | python3 -c " import json,sys data = json.load(sys.stdin) items = data[0]['result']['data']['json']['items'] print(items[0]['composeId'] if items else '') " 2>/dev/null || echo "") echo "composeId=$COMPOSE_ID" >> $GITHUB_OUTPUT echo "Found composeId: $COMPOSE_ID" - name: Delete preview stack if: steps.find.outputs.composeId != '' run: | RESPONSE=$(curl -s -w "\n%{http_code}" \ -X POST "${{ secrets.DOKPLOY_URL }}/api/trpc/compose.delete" \ -H "x-api-key: ${{ secrets.DOKPLOY_TOKEN }}" \ -H "Content-Type: application/json" \ -d "{\"0\":{\"json\":{\"composeId\":\"${{ steps.find.outputs.composeId }}\",\"deleteVolumes\":true}}}") HTTP_CODE=$(echo "$RESPONSE" | tail -1) BODY=$(echo "$RESPONSE" | head -1) echo "Status: $HTTP_CODE" echo "Body: $BODY" [ "$HTTP_CODE" = "200" ] || { echo "Delete failed"; exit 1; }