Hotfix projects home when pin_order is unavailable #152
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: Deploy to Testing | |
| on: | |
| push: | |
| branches: | |
| - testing | |
| env: | |
| REGISTRY: registry.digitalocean.com/dbr-cr | |
| GITOPS_REPO: dembrane/echo-gitops | |
| PYTHON_VERSION: "3.11" | |
| jobs: | |
| generate-tag: | |
| name: Generate Image Tag | |
| runs-on: ubuntu-latest | |
| outputs: | |
| image-tag: ${{ steps.meta.outputs.tag }} | |
| steps: | |
| - name: Generate image tag | |
| id: meta | |
| run: | | |
| SHORT_SHA=$(echo ${{ github.sha }} | cut -c1-7) | |
| echo "tag=$SHORT_SHA" >> $GITHUB_OUTPUT | |
| echo "📦 Image tag: $SHORT_SHA" | |
| build-api: | |
| name: Build & Push API Server | |
| runs-on: ubuntu-latest | |
| needs: [generate-tag] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to DigitalOcean Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ secrets.DO_REGISTRY_TOKEN }} | |
| password: ${{ secrets.DO_REGISTRY_TOKEN }} | |
| - name: Build and Push API Server | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./echo/server | |
| file: ./echo/server/Dockerfile | |
| push: true | |
| tags: | | |
| ${{ env.REGISTRY }}/dbr-echo-server:${{ needs.generate-tag.outputs.image-tag }} | |
| ${{ env.REGISTRY }}/dbr-echo-server:testing | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| build-agent: | |
| name: Build & Push Agent Service | |
| runs-on: ubuntu-latest | |
| needs: [generate-tag] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to DigitalOcean Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ secrets.DO_REGISTRY_TOKEN }} | |
| password: ${{ secrets.DO_REGISTRY_TOKEN }} | |
| - name: Build and Push Agent Service | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./echo/agent | |
| file: ./echo/agent/Dockerfile | |
| push: true | |
| tags: | | |
| ${{ env.REGISTRY }}/dbr-echo-agent:${{ needs.generate-tag.outputs.image-tag }} | |
| ${{ env.REGISTRY }}/dbr-echo-agent:testing | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| build-directus: | |
| name: Build & Push Directus | |
| runs-on: ubuntu-latest | |
| needs: [generate-tag] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to DigitalOcean Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ secrets.DO_REGISTRY_TOKEN }} | |
| password: ${{ secrets.DO_REGISTRY_TOKEN }} | |
| - name: Build and Push Directus | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./echo/directus | |
| file: ./echo/directus/Dockerfile | |
| push: true | |
| tags: | | |
| ${{ env.REGISTRY }}/dbr-echo-directus:${{ needs.generate-tag.outputs.image-tag }} | |
| ${{ env.REGISTRY }}/dbr-echo-directus:testing | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| deploy-dashboard: | |
| name: Deploy Dashboard to Vercel | |
| runs-on: ubuntu-latest | |
| env: | |
| VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} | |
| VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID_DASHBOARD_TESTING }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10 | |
| run_install: false | |
| - name: Get pnpm store directory | |
| id: pnpm-cache | |
| shell: bash | |
| run: | | |
| echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT | |
| - name: Setup pnpm cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('echo/frontend/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store- | |
| - name: Install Dependencies | |
| run: pnpm install --frozen-lockfile | |
| working-directory: echo/frontend | |
| - name: Install Vercel CLI | |
| run: pnpm add --global vercel@latest | |
| working-directory: echo/frontend | |
| - name: Pull Vercel Environment Information | |
| run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} | |
| working-directory: echo/frontend | |
| - name: Debug Environment Variables | |
| run: | | |
| echo "=== Checking .vercel directory ===" | |
| ls -la .vercel/ || echo "No .vercel directory" | |
| echo "" | |
| echo "=== Production environment file ===" | |
| cat .vercel/.env.production.local || echo "No production env file" | |
| echo "" | |
| echo "=== Looking for VITE variables ===" | |
| grep -r "VITE_" .vercel/ || echo "No VITE vars found" | |
| working-directory: echo/frontend | |
| - name: Build Project Artifacts | |
| run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} | |
| working-directory: echo/frontend | |
| - name: Deploy to Vercel | |
| run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }} | |
| working-directory: echo/frontend | |
| deploy-portal: | |
| name: Deploy Portal to Vercel | |
| runs-on: ubuntu-latest | |
| env: | |
| VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} | |
| VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID_PORTAL_TESTING }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10 | |
| run_install: false | |
| - name: Get pnpm store directory | |
| id: pnpm-cache | |
| shell: bash | |
| run: | | |
| echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT | |
| - name: Setup pnpm cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('echo/frontend/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store- | |
| - name: Install Dependencies | |
| run: pnpm install --frozen-lockfile | |
| working-directory: echo/frontend | |
| - name: Install Vercel CLI | |
| run: pnpm add --global vercel@latest | |
| working-directory: echo/frontend | |
| - name: Pull Vercel Environment Information | |
| run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} | |
| working-directory: echo/frontend | |
| - name: Debug Environment Variables | |
| run: | | |
| echo "=== Checking .vercel directory ===" | |
| ls -la .vercel/ || echo "No .vercel directory" | |
| echo "" | |
| echo "=== Production environment file ===" | |
| cat .vercel/.env.production.local || echo "No production env file" | |
| echo "" | |
| echo "=== Looking for VITE variables ===" | |
| grep -r "VITE_" .vercel/ || echo "No VITE vars found" | |
| working-directory: echo/frontend | |
| - name: Build Project Artifacts | |
| run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} | |
| working-directory: echo/frontend | |
| - name: Deploy to Vercel | |
| run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }} | |
| working-directory: echo/frontend | |
| update-gitops: | |
| name: Update GitOps Repo | |
| runs-on: ubuntu-latest | |
| needs: [generate-tag, build-api, build-agent, build-directus] | |
| steps: | |
| - name: Checkout GitOps repo | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ${{ env.GITOPS_REPO }} | |
| token: ${{ secrets.GITOPS_REPO_TOKEN }} | |
| ref: main | |
| - name: Update image tag in values-testing.yaml | |
| run: | | |
| IMAGE_TAG="${{ needs.generate-tag.outputs.image-tag }}" | |
| echo "Updating imageTag to: $IMAGE_TAG" | |
| sed -i "s/imageTag: \".*\"/imageTag: \"$IMAGE_TAG\"/" helm/echo/values-testing.yaml | |
| echo "Updated values-testing.yaml:" | |
| grep "imageTag:" helm/echo/values-testing.yaml | |
| - name: Commit and push changes | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add helm/echo/values-testing.yaml | |
| git commit -m "Update testing image tag to ${{ needs.generate-tag.outputs.image-tag }}" | |
| git push origin main | |
| wait-for-deployment: | |
| name: Wait for ArgoCD Deployment | |
| runs-on: ubuntu-latest | |
| needs: [update-gitops, generate-tag] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Wait for deployment | |
| run: | | |
| echo "⏳ Waiting for ArgoCD to sync and deploy..." | |
| echo "Image tag: ${{ needs.generate-tag.outputs.image-tag }}" | |
| MAX_WAIT=600 # 10 minutes | |
| INTERVAL=15 | |
| ELAPSED=0 | |
| while [ $ELAPSED -lt $MAX_WAIT ]; do | |
| echo "Checking API health (${ELAPSED}s elapsed)..." | |
| if curl -f -s https://api.echo-testing.dembrane.com/api/health > /dev/null 2>&1; then | |
| echo "✅ API is healthy!" | |
| exit 0 | |
| fi | |
| sleep $INTERVAL | |
| ELAPSED=$((ELAPSED + INTERVAL)) | |
| done | |
| echo "❌ Deployment did not become healthy within ${MAX_WAIT}s" | |
| exit 1 | |
| smoke-tests: | |
| name: Run Smoke Tests | |
| runs-on: ubuntu-latest | |
| needs: [wait-for-deployment, generate-tag] | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Install dependencies | |
| run: | | |
| cd echo/server | |
| pip install -r requirements.lock | |
| pip install requests | |
| - name: Run Smoke Tests | |
| env: | |
| TEST_API_URL: https://api.echo-testing.dembrane.com | |
| TEST_DIRECTUS_URL: https://directus.echo-testing.dembrane.com | |
| run: | | |
| cd echo/server | |
| pytest tests/smoke/ -v --maxfail=1 -m smoke | |
| # TODO: Enable E2E tests once Playwright tests are created (Phase 4) | |
| # e2e-tests: | |
| # name: Run E2E Tests | |
| # runs-on: ubuntu-latest | |
| # needs: [smoke-tests] | |
| # timeout-minutes: 10 | |
| # steps: | |
| # - uses: actions/checkout@v4 | |
| # | |
| # - name: Setup Node.js | |
| # uses: actions/setup-node@v4 | |
| # with: | |
| # node-version: "20" | |
| # | |
| # - name: Install pnpm | |
| # uses: pnpm/action-setup@v2 | |
| # with: | |
| # version: 8 | |
| # | |
| # - name: Install dependencies | |
| # run: | | |
| # cd echo/frontend | |
| # pnpm install | |
| # | |
| # - name: Install Playwright Browsers | |
| # run: | | |
| # cd echo/frontend | |
| # pnpm exec playwright install --with-deps chromium | |
| # | |
| # - name: Run Playwright Tests | |
| # env: | |
| # TEST_BASE_URL: https://api.echo-testing.dembrane.com | |
| # run: | | |
| # cd echo/frontend | |
| # pnpm exec playwright test | |
| # | |
| # - name: Upload Playwright Report | |
| # if: always() | |
| # uses: actions/upload-artifact@v4 | |
| # with: | |
| # name: playwright-report | |
| # path: echo/frontend/playwright-report/ | |
| # retention-days: 7 | |
| notify-slack: | |
| name: Notify Slack | |
| runs-on: ubuntu-latest | |
| needs: [generate-tag, smoke-tests] | |
| if: always() | |
| steps: | |
| - name: Determine status | |
| id: status | |
| run: | | |
| if [ "${{ needs.smoke-tests.result }}" == "success" ]; then | |
| echo "result=success" >> $GITHUB_OUTPUT | |
| echo "emoji=✅" >> $GITHUB_OUTPUT | |
| echo "color=good" >> $GITHUB_OUTPUT | |
| else | |
| echo "result=failure" >> $GITHUB_OUTPUT | |
| echo "emoji=❌" >> $GITHUB_OUTPUT | |
| echo "color=danger" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Send Slack notification | |
| uses: slackapi/slack-github-action@v1.24.0 | |
| with: | |
| payload: | | |
| { | |
| "attachments": [ | |
| { | |
| "color": "${{ steps.status.outputs.color }}", | |
| "blocks": [ | |
| { | |
| "type": "header", | |
| "text": { | |
| "type": "plain_text", | |
| "text": "${{ steps.status.outputs.emoji }} Testing Deployment: ${{ steps.status.outputs.result }}" | |
| } | |
| }, | |
| { | |
| "type": "section", | |
| "fields": [ | |
| { | |
| "type": "mrkdwn", | |
| "text": "*Repository:*\necho" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": "*Branch:*\ntesting" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": "*Image Tag:*\n`${{ needs.generate-tag.outputs.image-tag }}`" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": "*Commit:*\n<https://github.com/${{ github.repository }}/commit/${{ github.sha }}|${{ github.sha }}>" | |
| } | |
| ] | |
| }, | |
| { | |
| "type": "section", | |
| "fields": [ | |
| { | |
| "type": "mrkdwn", | |
| "text": "*Smoke Tests:*\n${{ needs.smoke-tests.result }}" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": "*E2E Tests:*\nPending (Phase 4)" | |
| } | |
| ] | |
| }, | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": "*Links:*\n• <https://api.echo-testing.dembrane.com|API>\n• <https://directus.echo-testing.dembrane.com|Directus>\n• <https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}|GitHub Actions>" | |
| } | |
| }, | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": "${{ steps.status.outputs.result == 'failure' && '⚠️ *Rollback Instructions:*\n```\ncd echo-gitops\ngit revert HEAD\ngit push origin main\n```\nOr keep for debugging and fix forward.' || '🎉 All tests passed! Environment is stable.' }}" | |
| } | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| env: | |
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} | |
| SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK |