name: Build & Deploy Tippspiel on: push: branches: [main] jobs: build: runs-on: self-hosted steps: - name: Setup tools run: apk add --no-cache curl python3 - name: Checkout run: | rm -rf workspace && mkdir workspace GIT_TERMINAL_PROMPT=0 git clone \ --depth 1 \ --branch main \ http://x-token:${{ secrets.DEPLOY_TOKEN }}@gitea:3000/mwf975_git/tippspiel.git \ workspace - name: Create build context run: | cd workspace tar cf /tmp/tippspiel-ci.tar \ --exclude='.git' \ --exclude='node_modules' \ --exclude='*.docx' \ --exclude='prototyp_*.html' \ . echo "Build context size: $(du -sh /tmp/tippspiel-ci.tar | cut -f1)" - name: Build Docker Image via Portainer run: | REGISTRY="git.home.rm-warpstation.de" IMAGE_TAG="${REGISTRY}/mwf975_git/tippspiel:latest" echo "Building image: $IMAGE_TAG" curl -s -k -X POST \ "https://192.168.1.60:9444/api/endpoints/2/docker/build?t=${IMAGE_TAG}&dockerfile=./Dockerfile&nocache=1" \ -H "X-API-Key: ${{ secrets.PORTAINER_TOKEN }}" \ -H "Content-Type: application/x-tar" \ --data-binary @/tmp/tippspiel-ci.tar \ --max-time 600 \ | tail -5 echo "Build completed." - name: Push to Gitea Registry run: | REGISTRY="git.home.rm-warpstation.de" IMAGE_TAG="${REGISTRY}/mwf975_git/tippspiel:latest" DEPLOY_TOKEN="${{ secrets.DEPLOY_TOKEN }}" AUTH_HEADER=$(python3 -c " import base64, json auth = json.dumps({'username': 'mwf975_git', 'password': '${DEPLOY_TOKEN}', 'serveraddress': 'https://${REGISTRY}'}) print(base64.urlsafe_b64encode(auth.encode()).decode()) ") echo "Pushing $IMAGE_TAG..." curl -s -k -X POST \ "https://192.168.1.60:9444/api/endpoints/2/docker/images/${IMAGE_TAG}/push" \ -H "X-API-Key: ${{ secrets.PORTAINER_TOKEN }}" \ -H "X-Registry-Auth: $AUTH_HEADER" \ --max-time 300 echo "" echo "Push completed." - name: Redeploy Stack via Portainer run: | REGISTRY="git.home.rm-warpstation.de" # Compose-File als separate Datei schreiben cat > /tmp/compose-deploy.yml << 'COMPOSE_EOF' services: tippspiel: image: git.home.rm-warpstation.de/mwf975_git/tippspiel:latest container_name: wm2026-tippspiel restart: unless-stopped ports: - "3301:3001" environment: - NODE_ENV=${NODE_ENV} - PORT=${PORT} - DATABASE_URL=${DATABASE_URL} - SUPABASE_URL=${SUPABASE_URL} - SUPABASE_SERVICE_ROLE_KEY=${SUPABASE_SERVICE_ROLE_KEY} - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - FOOTBALL_API_KEY=${FOOTBALL_API_KEY} - FOOTBALL_API_BASE_URL=${FOOTBALL_API_BASE_URL} - ELEVENLABS_API_KEY=${ELEVENLABS_API_KEY} - CORS_ORIGIN=${CORS_ORIGIN} - STAFFBASE_PUBLIC_KEY=${STAFFBASE_PUBLIC_KEY:-} - STAFFBASE_PLUGIN_ID=${STAFFBASE_PLUGIN_ID:-} healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:3001/health"] interval: 30s timeout: 5s start_period: 10s retries: 3 networks: - main-network networks: main-network: external: true COMPOSE_EOF # Env-Vars aus Portainer lesen ENV_VARS=$(curl -s -k \ "https://192.168.1.60:9444/api/stacks/115" \ -H "X-API-Key: ${{ secrets.PORTAINER_TOKEN }}" \ | python3 -c "import sys,json; print(json.dumps(json.load(sys.stdin).get('Env', [])))") # Stack-File lesen und Payload bauen STACK_CONTENT=$(cat /tmp/compose-deploy.yml) PAYLOAD=$(python3 -c " import json, sys compose = open('/tmp/compose-deploy.yml').read() env_vars = json.loads(sys.argv[1]) print(json.dumps({ 'stackFileContent': compose, 'env': env_vars, 'prune': True, 'pullImage': True })) " "$ENV_VARS") echo "Redeploying stack..." curl -s -k -X PUT \ "https://192.168.1.60:9444/api/stacks/115?endpointId=2" \ -H "X-API-Key: ${{ secrets.PORTAINER_TOKEN }}" \ -H "Content-Type: application/json" \ -d "$PAYLOAD" \ | python3 -c "import sys,json; d=json.load(sys.stdin); print('Stack:', d.get('Name'), '| Status:', d.get('Status'))" \ || echo "Stack redeploy triggered." - name: Verify deployment run: | sleep 20 STATUS=$(curl -s http://192.168.1.60:3301/health | python3 -c "import sys,json; print(d:=json.load(sys.stdin), d.get('status'))" 2>/dev/null || echo "unreachable") echo "Health check: $STATUS" - name: Cleanup if: always() run: rm -rf workspace /tmp/tippspiel-ci.tar /tmp/compose-deploy.yml