EBS Common - Staging/Prod Deploy #13
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: 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 }} \ | |
| VITE_CLERK_FRONTEND_API=${{ vars.STAGING_CLERK_FRONTEND_API }} \ | |
| 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 }} \ | |
| VITE_CLERK_FRONTEND_API=${{ vars.PROD_CLERK_FRONTEND_API }} \ | |
| 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}" |