Using portainer webhook to trigger deployment #11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Build and Deploy | |
on: | |
push: | |
branches: ["main"] | |
workflow_dispatch: # Allow manual triggering | |
env: | |
REGISTRY: ghcr.io | |
IMAGE_NAME: ${{ github.repository }} | |
PORTAINER_WEBHOOK_URL: ${{ secrets.PORTAINER_WEBHOOK_URL }} | |
jobs: | |
check-changes: | |
name: Check What Changed | |
runs-on: [self-hosted, home] | |
outputs: | |
backend-changed: ${{ steps.changes.outputs.backend }} | |
frontend-changed: ${{ steps.changes.outputs.frontend }} | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 2 # Need at least 2 commits to compare | |
- name: Check for changes | |
id: changes | |
run: | | |
# Check if this is the first commit (no previous commit to compare) | |
if git rev-parse HEAD~1 >/dev/null 2>&1; then | |
# Compare with previous commit | |
BACKEND_CHANGED=$(git diff --name-only HEAD~1 HEAD | grep -E '^(cmd/|internal/|pkg/|go\.|configs/|deployments/|\.github/workflows/deploy\.yml)' || echo '') | |
FRONTEND_CHANGED=$(git diff --name-only HEAD~1 HEAD | grep -E '^frontend/' || echo '') | |
else | |
# First commit - build everything | |
BACKEND_CHANGED="initial" | |
FRONTEND_CHANGED="initial" | |
fi | |
# Set outputs | |
if [ -n "$BACKEND_CHANGED" ]; then | |
echo "backend=true" >> $GITHUB_OUTPUT | |
echo "π Backend changes detected: $BACKEND_CHANGED" | |
else | |
echo "backend=false" >> $GITHUB_OUTPUT | |
echo "β No backend changes detected" | |
fi | |
if [ -n "$FRONTEND_CHANGED" ]; then | |
echo "frontend=true" >> $GITHUB_OUTPUT | |
echo "π Frontend changes detected: $FRONTEND_CHANGED" | |
else | |
echo "frontend=false" >> $GITHUB_OUTPUT | |
echo "β No frontend changes detected" | |
fi | |
build-and-push: | |
name: Build and Push Backend | |
runs-on: [self-hosted, home] | |
needs: check-changes | |
if: needs.check-changes.outputs.backend-changed == 'true' | |
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] | |
needs: check-changes | |
if: needs.check-changes.outputs.frontend-changed == 'true' | |
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: [check-changes, build-and-push, build-frontend-image] | |
if: always() && github.ref == 'refs/heads/main' && | |
(needs.build-and-push.result == 'success' || needs.build-and-push.result == 'skipped') && | |
(needs.build-frontend-image.result == 'success' || needs.build-frontend-image.result == 'skipped') && | |
(needs.check-changes.outputs.backend-changed == 'true' || needs.check-changes.outputs.frontend-changed == 'true') | |
steps: | |
- name: Deployment Info | |
run: | | |
echo "π **Deployment Information**" >> $GITHUB_STEP_SUMMARY | |
echo "" >> $GITHUB_STEP_SUMMARY | |
echo "- **Backend Changed**: ${{ needs.check-changes.outputs.backend-changed }}" >> $GITHUB_STEP_SUMMARY | |
echo "- **Frontend Changed**: ${{ needs.check-changes.outputs.frontend-changed }}" >> $GITHUB_STEP_SUMMARY | |
echo "- **Backend Build**: ${{ needs.build-and-push.result }}" >> $GITHUB_STEP_SUMMARY | |
echo "- **Frontend Build**: ${{ needs.build-frontend-image.result }}" >> $GITHUB_STEP_SUMMARY | |
- name: Verify Portainer Connection | |
run: | | |
echo "π Verifying connection to Portainer..." | |
if curl -f -s --connect-timeout 10 https://portainer.sankalpnarula.com/api/status > /dev/null; then | |
echo "β Successfully connected to Portainer" | |
else | |
echo "β Failed to connect to Portainer" | |
exit 1 | |
fi | |
- name: Trigger Portainer Stack Redeployment | |
env: | |
PORTAINER_WEBHOOK_URL: ${{ secrets.PORTAINER_WEBHOOK_URL }} | |
BACKEND_CHANGED: ${{ needs.check-changes.outputs.backend-changed }} | |
FRONTEND_CHANGED: ${{ needs.check-changes.outputs.frontend-changed }} | |
run: | | |
echo "π Starting deployment process..." | |
echo "π Backend changed: $BACKEND_CHANGED" | |
echo "π Frontend changed: $FRONTEND_CHANGED" | |
# Trigger redeployment via webhook | |
echo "π Triggering stack redeployment via webhook..." | |
RESPONSE=$(curl -s -w "%{http_code}" -X POST \ | |
-H "Content-Type: application/json" \ | |
-d '{"pullImage": true}' \ | |
"$PORTAINER_WEBHOOK_URL") | |
HTTP_CODE="${RESPONSE: -3}" | |
RESPONSE_BODY="${RESPONSE%???}" | |
case $HTTP_CODE in | |
200|204) | |
echo "β Stack redeployment triggered successfully!" | |
[ -n "$RESPONSE_BODY" ] && echo "π Response: $RESPONSE_BODY" | |
;; | |
*) | |
echo "β Deployment webhook failed with HTTP $HTTP_CODE" | |
echo "π Response: $RESPONSE_BODY" | |
echo "π Check Portainer for more details" | |
exit 1 | |
;; | |
esac | |
echo "β³ Deployment initiated via webhook - check Portainer for progress" | |
echo "π Monitor at: https://portainer.sankalpnarula.com" | |
- 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 | |
if [ "${{ needs.check-changes.outputs.backend-changed }}" = "true" ]; then | |
echo "π¦ **Backend Image**: \`ghcr.io/${{ github.repository }}:latest\` (updated)" >> $GITHUB_STEP_SUMMARY | |
else | |
echo "π¦ **Backend Image**: \`ghcr.io/${{ github.repository }}:latest\` (unchanged)" >> $GITHUB_STEP_SUMMARY | |
fi | |
if [ "${{ needs.check-changes.outputs.frontend-changed }}" = "true" ]; then | |
echo "π¦ **Frontend Image**: \`ghcr.io/${{ github.repository }}-frontend:latest\` (updated)" >> $GITHUB_STEP_SUMMARY | |
else | |
echo "π¦ **Frontend Image**: \`ghcr.io/${{ github.repository }}-frontend:latest\` (unchanged)" >> $GITHUB_STEP_SUMMARY | |
fi | |
echo "" >> $GITHUB_STEP_SUMMARY | |
echo "**What was deployed:**" >> $GITHUB_STEP_SUMMARY | |
if [ "${{ needs.check-changes.outputs.backend-changed }}" = "true" ]; then | |
echo "- β Backend: Built and deployed new image" >> $GITHUB_STEP_SUMMARY | |
else | |
echo "- βοΈ Backend: No changes, using existing image" >> $GITHUB_STEP_SUMMARY | |
fi | |
if [ "${{ needs.check-changes.outputs.frontend-changed }}" = "true" ]; then | |
echo "- β Frontend: Built and deployed new image" >> $GITHUB_STEP_SUMMARY | |
else | |
echo "- βοΈ Frontend: No changes, using existing image" >> $GITHUB_STEP_SUMMARY | |
fi | |
else | |
echo "β **Status**: Deployment failed" >> $GITHUB_STEP_SUMMARY | |
echo "π **Check**: Review job logs for details" >> $GITHUB_STEP_SUMMARY | |
echo "π **Portainer**: [Check Stack Status](https://portainer.sankalpnarula.com)" >> $GITHUB_STEP_SUMMARY | |
fi |