From 219d4fb21428c8275871e4cefaa6683bc2c4631b Mon Sep 17 00:00:00 2001 From: Admin Date: Fri, 6 Mar 2026 15:50:06 +0500 Subject: [PATCH] ci: add deploy workflow with correct Dokploy tRPC API calls - Production deploy: POST /api/trpc/compose.redeploy on push to main - Preview deploy: POST /api/trpc/compose.isolatedDeployment on feature branches - Preview cleanup: compose.search + compose.delete on PR close - Uses x-api-key auth and tRPC v10 batch JSON body format --- .gitea/workflows/deploy.yaml | 134 +++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 .gitea/workflows/deploy.yaml diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml new file mode 100644 index 0000000..91ad599 --- /dev/null +++ b/.gitea/workflows/deploy.yaml @@ -0,0 +1,134 @@ +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; }