Skip to content

Testing auto deployment using tailscale and portainer api #7

Testing auto deployment using tailscale and portainer api

Testing auto deployment using tailscale and portainer api #7

Workflow file for this run

name: Build and Deploy
on:
push:
branches: ["main"]
workflow_dispatch: # Allow manual triggering
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
name: Build and Push to GHCR
runs-on: [self-hosted, home]
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./deployments/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
build-frontend-image:
name: Build Frontend Container
runs-on: [self-hosted, home]
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for frontend
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-frontend
tags: |
type=ref,event=branch
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Frontend Docker image
uses: docker/build-push-action@v5
with:
context: ./frontend
file: ./frontend/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
deploy-via-portainer:
name: Deploy via Portainer
runs-on: [self-hosted, home]
needs: [build-and-push, build-frontend-image]
if: success() && github.ref == 'refs/heads/main'
steps:
- name: Install and Connect Tailscale
env:
TS_AUTH_KEY: ${{ secrets.TS_AUTH_KEY }}
run: |
# Install Tailscale (if not already installed)
if ! command -v tailscale &> /dev/null; then
echo "Installing Tailscale..."
curl -fsSL https://tailscale.com/install.sh | sh
fi
# Connect to Tailscale using auth key
echo "Connecting to Tailscale network..."
sudo tailscale up --auth-key="$TS_AUTH_KEY" --accept-routes --reset
# Wait for connection to establish
sleep 5
# Verify connection
echo "Tailscale status:"
sudo tailscale status
- name: Verify Portainer Connection
run: |
echo "Verifying connection to Portainer..."
# Test connection to Portainer
for i in {1..10}; do
if curl -f -s --connect-timeout 10 https://portainer.sankalpnarula.com/api/status > /dev/null; then
echo "✅ Successfully connected to Portainer via Tailscale"
break
fi
echo "⏳ Attempt $i/10 - Waiting for Portainer connection..."
sleep 3
done
# Final connection test
if ! curl -f -s --connect-timeout 10 https://portainer.sankalpnarula.com/api/status > /dev/null; then
echo "❌ Failed to connect to Portainer"
echo "Tailscale status:"
sudo tailscale status
exit 1
fi
- name: Trigger Portainer Stack Redeployment
env:
PORTAINER_URL: https://portainer.sankalpnarula.com
PORTAINER_TOKEN: ${{ secrets.PORTAINER_ACCESS_TOKEN }}
STACK_NAME: ocpp-chaos-sim # Adjust this to your actual stack name
run: |
set -e
echo "🚀 Starting deployment process..."
# Get stack ID
echo "🔍 Looking for stack: $STACK_NAME"
STACK_ID=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" \
"$PORTAINER_URL/api/stacks" | \
jq -r ".[] | select(.Name == \"$STACK_NAME\") | .Id")
if [ "$STACK_ID" = "null" ] || [ -z "$STACK_ID" ]; then
echo "❌ Stack '$STACK_NAME' not found. Available stacks:"
curl -s -H "X-API-Key: $PORTAINER_TOKEN" \
"$PORTAINER_URL/api/stacks" | jq -r '.[].Name'
exit 1
fi
echo "📋 Found stack ID: $STACK_ID"
# Get endpoint ID (usually 1 for local Docker, but let's be sure)
ENDPOINT_ID=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" \
"$PORTAINER_URL/api/stacks/$STACK_ID" | \
jq -r '.EndpointId')
echo "🎯 Using endpoint ID: $ENDPOINT_ID"
# Trigger stack update (pull latest images and redeploy)
echo "🔄 Triggering stack redeployment..."
RESPONSE=$(curl -s -w "%{http_code}" -X PUT \
-H "X-API-Key: $PORTAINER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"pullImage": true,
"prune": true
}' \
"$PORTAINER_URL/api/stacks/$STACK_ID?endpointId=$ENDPOINT_ID")
HTTP_CODE="${RESPONSE: -3}"
RESPONSE_BODY="${RESPONSE%???}"
if [ "$HTTP_CODE" = "200" ]; then
echo "✅ Stack redeployment triggered successfully!"
echo "📝 Response: $RESPONSE_BODY"
else
echo "❌ Deployment failed with HTTP $HTTP_CODE"
echo "📝 Response: $RESPONSE_BODY"
exit 1
fi
# Wait a moment and check deployment status
echo "⏳ Waiting for deployment to complete..."
sleep 10
# Check if stack is running
STACK_STATUS=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" \
"$PORTAINER_URL/api/stacks/$STACK_ID" | \
jq -r '.Status')
echo "📊 Stack status: $STACK_STATUS"
- name: Deployment Summary
if: always()
run: |
echo "🎯 **Portainer Deployment Summary**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ job.status }}" = "success" ]; then
echo "✅ **Status**: Deployment successful" >> $GITHUB_STEP_SUMMARY
echo "🔗 **Portainer**: [View Stack](https://portainer.sankalpnarula.com)" >> $GITHUB_STEP_SUMMARY
echo "📦 **Backend Image**: \`ghcr.io/${{ github.repository }}:latest\`" >> $GITHUB_STEP_SUMMARY
echo "📦 **Frontend Image**: \`ghcr.io/${{ github.repository }}-frontend:latest\`" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **Status**: Deployment failed" >> $GITHUB_STEP_SUMMARY
echo "🔍 **Check**: Review job logs for details" >> $GITHUB_STEP_SUMMARY
fi
- name: Cleanup Tailscale Connection
if: always()
run: |
echo "🔌 Cleaning up Tailscale connection..."
sudo tailscale logout || true
echo "✅ Tailscale disconnected"