ci: switch to Gitea Container Registry for reliable deploys
Previous pipeline built images locally via Portainer Docker API, but Docker layer caching produced identical images. Now: - Build with nocache=1 - Push to Gitea registry (git.home.rm-warpstation.de) - Compose uses image: from registry instead of build: - Redeploy with pullImage: true forces fresh container Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+67
-16
@@ -4,6 +4,10 @@ on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
REGISTRY: git.home.rm-warpstation.de
|
||||
IMAGE: mwf975_git/tippspiel
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: self-hosted
|
||||
@@ -33,42 +37,89 @@ jobs:
|
||||
|
||||
- name: Build Docker Image via Portainer
|
||||
run: |
|
||||
echo "Starting Docker build on NAS..."
|
||||
IMAGE_TAG="${{ env.REGISTRY }}/${{ env.IMAGE }}:latest"
|
||||
echo "Building image: $IMAGE_TAG"
|
||||
curl -s -k -X POST \
|
||||
"https://192.168.1.60:9444/api/endpoints/2/docker/build?t=wm2026-tippspiel:latest&dockerfile=./Dockerfile" \
|
||||
"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 \
|
||||
| grep -E '(Successfully built|Successfully tagged|error|Error)' || true
|
||||
| grep -E '(Successfully|error|Error)' || true
|
||||
echo "Build completed."
|
||||
|
||||
- name: Push to Gitea Registry
|
||||
run: |
|
||||
IMAGE_TAG="${{ env.REGISTRY }}/${{ env.IMAGE }}:latest"
|
||||
|
||||
# Login to Gitea registry via Portainer Docker API
|
||||
LOGIN_PAYLOAD=$(python3 -c "import json; print(json.dumps({'username': 'mwf975_git', 'password': '${{ secrets.DEPLOY_TOKEN }}', 'serveraddress': 'https://${{ env.REGISTRY }}'}))")
|
||||
curl -s -k -X POST \
|
||||
"https://192.168.1.60:9444/api/endpoints/2/docker/auth" \
|
||||
-H "X-API-Key: ${{ secrets.PORTAINER_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$LOGIN_PAYLOAD" || true
|
||||
|
||||
# Push image to registry
|
||||
echo "Pushing $IMAGE_TAG..."
|
||||
AUTH_HEADER=$(python3 -c "import base64,json; print(base64.urlsafe_b64encode(json.dumps({'username':'mwf975_git','password':'${{ secrets.DEPLOY_TOKEN }}'}).encode()).decode())")
|
||||
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 || true
|
||||
echo "Push completed."
|
||||
|
||||
- name: Redeploy Stack via Portainer
|
||||
run: |
|
||||
echo "Fetching current stack config from Portainer..."
|
||||
|
||||
# Aktuelles Compose-File aus Portainer lesen
|
||||
STACK_FILE=$(curl -s -k \
|
||||
"https://192.168.1.60:9444/api/stacks/115/file" \
|
||||
-H "X-API-Key: ${{ secrets.PORTAINER_TOKEN }}" \
|
||||
| python3 -c "import sys,json; print(json.load(sys.stdin).get('StackFileContent',''))")
|
||||
|
||||
# Aktuelle Env-Vars aus Portainer lesen (enthält die echten Werte)
|
||||
# Aktuelle 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 mit bestehender Konfiguration neu deployen (keine Credentials im Workflow)
|
||||
# Stack mit Image-Pull neu deployen
|
||||
PAYLOAD=$(python3 -c "
|
||||
import json, sys
|
||||
stack_file = '''$STACK_FILE'''
|
||||
import json
|
||||
env_vars = $ENV_VARS
|
||||
print(json.dumps({
|
||||
'stackFileContent': stack_file,
|
||||
'stackFileContent': '''services:
|
||||
tippspiel:
|
||||
image: ${{ env.REGISTRY }}/${{ env.IMAGE }}: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''',
|
||||
'env': env_vars,
|
||||
'prune': True,
|
||||
'pullImage': False
|
||||
'pullImage': True
|
||||
}))
|
||||
")
|
||||
|
||||
@@ -84,7 +135,7 @@ jobs:
|
||||
|
||||
- name: Verify deployment
|
||||
run: |
|
||||
sleep 15
|
||||
sleep 20
|
||||
STATUS=$(curl -s http://192.168.1.60:3301/health | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('status'))" 2>/dev/null || echo "unreachable")
|
||||
echo "Health check: $STATUS"
|
||||
if [ "$STATUS" = "ok" ]; then
|
||||
|
||||
Reference in New Issue
Block a user