Skip to content

EBS Common - Staging/Prod Deploy #4

EBS Common - Staging/Prod Deploy

EBS Common - Staging/Prod Deploy #4

name: EBS Common - Staging/Prod Deploy
on:
workflow_dispatch:
inputs:
branch:
description: "Select a Branch to deploy"
required: true
default: "main"
environment:
description: "Select environment to deploy (staging or prod)"
required: true
default: "staging"
email:
description: "Admin email for deployment"
required: true
default: "saravana.k.r@gmail.com"
commit_id:
description: "Commit ID to deploy (leave blank for latest on branch)"
required: false
default: ""
env:
STAGING_HOST: staging.visualaiagentsbuilder.com
PROD_HOST: visualaiagentsbuilder.com
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout selected branch
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch }}
- name: Checkout specific commit if provided
if: ${{ github.event.inputs.commit_id != '' }}
run: |
git fetch --depth=1 origin ${{ github.event.inputs.commit_id }}
git checkout ${{ github.event.inputs.commit_id }}
echo "✅ Checked out commit ${{ github.event.inputs.commit_id }}"
- name: Determine Commit ID
id: commit
run: |
if [ -n "${{ github.event.inputs.commit_id }}" ]; then
echo "commit_id=${{ github.event.inputs.commit_id }}" >> $GITHUB_OUTPUT
else
if [ "${{ github.event.inputs.branch }}" = "main" ]; then
COMMIT_ID=$(git rev-parse HEAD)
else
COMMIT_ID=$(git log -1 --format="%H")
fi
echo "commit_id=$COMMIT_ID" >> $GITHUB_OUTPUT
fi
- name: Determine Branch Name
id: branch
run: |
BRANCH_NAME=$(echo "${{ github.event.inputs.branch }}" | tr '/' '-')
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
- name: Install sshpass
run: sudo apt-get update && sudo apt-get install -y sshpass
- name: Create GitHub Deployment
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
COMMIT_ID=${{ steps.commit.outputs.commit_id }}
ENVIRONMENT=${{ github.event.inputs.environment }}
BRANCH=${{ github.event.inputs.branch }}
ACTOR=${{ github.actor }}
echo "📦 Creating deployment for $ENVIRONMENT commit=$COMMIT_ID branch=$BRANCH"
DEPLOYMENT_ID=$(gh api repos/${{ github.repository }}/deployments \
-F ref=${{ github.event.inputs.branch }} \
-F environment=$ENVIRONMENT \
-F description="Deployment by $ACTOR from branch $BRANCH with commit $COMMIT_ID" \
-F auto_merge=false \
-F required_contexts[]=null --jq '.id')
echo "DEPLOYMENT_ID=$DEPLOYMENT_ID" >> $GITHUB_ENV
- name: Mark Deployment In Progress
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api repos/${{ github.repository }}/deployments/${{ env.DEPLOYMENT_ID }}/statuses \
-F state=in_progress \
-F description="Deployment started and is in progress"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: forwardemailforaifirstdesk
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build Docker Image (for staging)
if: ${{ github.event.inputs.environment == 'staging' }}
run: |
COMMIT_ID=${{ steps.commit.outputs.commit_id }}
BRANCH_NAME=${{ steps.branch.outputs.branch_name }}
echo "Using branch=$BRANCH_NAME commit=$COMMIT_ID"
echo "=== Building Staging Image ==="
DOCKER_BUILDKIT=1 \
VITE_AUTO_LOGIN=false \
VITE_CLERK_AUTH_ENABLED=true \
VITE_CLERK_PUBLISHABLE_KEY=${{ secrets.CLERK_PUBLISHABLE_KEY }} \
make docker_build TAG=langflow:staging-$BRANCH_NAME-$COMMIT_ID
docker tag langflow:staging-$BRANCH_NAME-$COMMIT_ID forwardemailforaifirstdesk/langflow:staging-$BRANCH_NAME-$COMMIT_ID
docker push forwardemailforaifirstdesk/langflow:staging-$BRANCH_NAME-$COMMIT_ID
echo "=== Building Prod Image ==="
DOCKER_BUILDKIT=1 \
VITE_AUTO_LOGIN=false \
VITE_CLERK_AUTH_ENABLED=true \
VITE_CLERK_PUBLISHABLE_KEY=${{ secrets.PROD_CLERK_PUBLISHABLE_KEY }} \
make docker_build TAG=langflow:prod-$BRANCH_NAME-$COMMIT_ID
docker tag langflow:prod-$BRANCH_NAME-$COMMIT_ID forwardemailforaifirstdesk/langflow:prod-$BRANCH_NAME-$COMMIT_ID
docker push forwardemailforaifirstdesk/langflow:prod-$BRANCH_NAME-$COMMIT_ID
- name: Cleanup old Docker Hub tags (skip for prod)
if: ${{ github.event.inputs.environment == 'staging' }}
run: |
#!/usr/bin/env bash
set -euo pipefail
DOCKERHUB_USERNAME="forwardemailforaifirstdesk"
DOCKERHUB_PASSWORD="${{ secrets.DOCKERHUB_TOKEN }}"
REPOSITORY="forwardemailforaifirstdesk/langflow"
echo "=== Getting Docker Hub token ==="
TOKEN=$(curl -s -H "Content-Type: application/json" -X POST \
-d '{"username": "'$DOCKERHUB_USERNAME'", "password": "'$DOCKERHUB_PASSWORD'"}' \
https://hub.docker.com/v2/users/login/ | jq -r .token)
if [ -z "$TOKEN" ] || [ "$TOKEN" == "null" ]; then
echo "❌ Failed to authenticate"
exit 1
fi
echo "✅ Authentication successful"
echo "=== Fetching tags ==="
RESPONSE=$(curl -s -H "Authorization: JWT $TOKEN" \
"https://hub.docker.com/v2/repositories/$REPOSITORY/tags?page_size=100")
TAGS=$(echo "$RESPONSE" | jq -r '.results[] | .name')
if [ -z "$TAGS" ]; then
echo "ℹ️ No tags found in repository $REPOSITORY. Nothing to delete."
exit 0
fi
echo "Found tags: $TAGS"
for TAG in $TAGS; do
COMMIT_ID=${{ steps.commit.outputs.commit_id }}
BRANCH_NAME=${{ steps.branch.outputs.branch_name }}
if [[ "$TAG" == "staging-$BRANCH_NAME-$COMMIT_ID" || "$TAG" == "prod-$BRANCH_NAME-$COMMIT_ID" ]]; then
echo "⏭️ Skipping current branch+commit tag: $TAG"
continue
fi
echo "Deleting old tag: $TAG"
DELETE_RESPONSE=$(curl -s -w "HTTPSTATUS:%{http_code}" \
-X DELETE -H "Authorization: JWT $TOKEN" \
"https://hub.docker.com/v2/repositories/$REPOSITORY/tags/$TAG/")
HTTP_CODE=$(echo $DELETE_RESPONSE | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
if [ "$HTTP_CODE" = "204" ]; then
echo " ✅ Successfully deleted tag $TAG"
else
echo " ❌ Failed to delete tag $TAG (HTTP: $HTTP_CODE)"
fi
sleep 1
done
echo "Note: Docker Hub will automatically clean up unreferenced images within 24-48 hours"
- name: Copy deployment files to VM
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ github.event.inputs.environment == 'staging' && env.STAGING_HOST || env.PROD_HOST }}
username: root
password: ${{ github.event.inputs.environment == 'staging' && secrets.VM_PASSWORD || secrets.PROD_VM_PASSWORD }}
source: "./deploy/ebs-staging-prod.sh,./.env-file"
target: /root/
- name: Run deployment script on VM
uses: appleboy/ssh-action@v1.1.0
with:
host: ${{ github.event.inputs.environment == 'staging' && env.STAGING_HOST || env.PROD_HOST }}
username: root
password: ${{ github.event.inputs.environment == 'staging' && secrets.VM_PASSWORD || secrets.PROD_VM_PASSWORD }}
script: |
chmod +x /root/deploy/ebs-staging-prod.sh
if [ "${{ github.event.inputs.environment }}" = "staging" ]; then
IMAGE="forwardemailforaifirstdesk/langflow:staging-${{ steps.branch.outputs.branch_name }}-${{ steps.commit.outputs.commit_id }}"
DOMAIN="${{env.STAGING_HOST}}"
EMAIL="${{ github.event.inputs.email }}"
ENV_FILE="/root/.env-file"
DB_USER="${{ secrets.POSTGRES_USER }}"
DB_PASSWORD="${{ secrets.POSTGRES_PASSWD }}"
DB_NAME="${{ secrets.POSTGRES_DB }}"
VOLUME_NAME="${{ vars.STAGING_VOLUME_NAME }}"
else
IMAGE="forwardemailforaifirstdesk/langflow:prod-${{ steps.branch.outputs.branch_name }}-${{ steps.commit.outputs.commit_id }}"
DOMAIN="${{env.PROD_HOST}}"
EMAIL="${{ github.event.inputs.email }}"
ENV_FILE="/root/.env-file"
DB_USER="${{ secrets.PROD_POSTGRES_USER }}"
DB_PASSWORD="${{ secrets.PROD_POSTGRES_PASSWD }}"
DB_NAME="${{ secrets.PROD_POSTGRES_DB }}"
VOLUME_NAME="${{ vars.PROD_VOLUME_NAME }}"
fi
/root/deploy/ebs-staging-prod.sh \
--image "$IMAGE" \
--domain "$DOMAIN" \
--email "$EMAIL" \
--env-file "$ENV_FILE" \
--db-user "$DB_USER" \
--db-password "$DB_PASSWORD" \
--db-name "$DB_NAME" \
--volume-name "$VOLUME_NAME"
- name: Copy deploy_status.env from VM
run: |
sshpass -p "${{ secrets.VM_PASSWORD }}" scp -o StrictHostKeyChecking=no \
root@staging.visualaiagentsbuilder.com:/root/deploy_status.env \
$GITHUB_WORKSPACE/deploy_status.env
- name: Update GitHub Deployment Status with Docker info
if: always()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
source "${GITHUB_WORKSPACE}/deploy_status.env"
# Encode container info for safe URL usage
CONTAINER_ENCODED=$(echo "$FINAL_CONTAINER" | jq -s -R -r @uri)
gh api repos/${{ github.repository }}/deployments/${{ env.DEPLOYMENT_ID }}/statuses \
-F state=$FINAL_STATUS \
-F description="$FINAL_DESCRIPTION" \
-F log_url="https://logs.${{ github.repository_owner }}.com/deploy/${{ env.DEPLOYMENT_ID }}?container=${CONTAINER_ENCODED}"