Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
285 changes: 285 additions & 0 deletions .github/workflows/deploy-testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
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:
build-and-push:
name: Build & Push Images
runs-on: ubuntu-latest
outputs:
image-tag: ${{ steps.meta.outputs.tag }}
steps:
- uses: actions/checkout@v4

- 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"

- 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:${{ steps.meta.outputs.tag }}
${{ env.REGISTRY }}/dbr-echo-server:testing
cache-from: type=gha
cache-to: type=gha,mode=max

- 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:${{ steps.meta.outputs.tag }}
${{ env.REGISTRY }}/dbr-echo-directus:testing
cache-from: type=gha
cache-to: type=gha,mode=max

update-gitops:
name: Update GitOps Repo
runs-on: ubuntu-latest
needs: [build-and-push]
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.build-and-push.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.build-and-push.outputs.image-tag }}"
git push origin main
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: YAML Formatting Causes Commit Failure

The git commit command will fail if there are no changes to commit, which can occur if the sed command on line 80 doesn't match the expected pattern in values-testing.yaml. The sed pattern assumes imageTag: ".*" format with double quotes, but if the YAML uses a different format (single quotes, no quotes, or different spacing), no changes will be staged and the commit will fail with exit code 1, breaking the workflow.

Fix in Cursor Fix in Web


wait-for-deployment:
name: Wait for ArgoCD Deployment
runs-on: ubuntu-latest
needs: [update-gitops, build-and-push]
steps:
- uses: actions/checkout@v4

- name: Wait for deployment
run: |
echo "⏳ Waiting for ArgoCD to sync and deploy..."
echo "Image tag: ${{ needs.build-and-push.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/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, build-and-push]
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 --timeout=600 --maxfail=1

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: [build-and-push, smoke-tests, e2e-tests]
if: always()
steps:
- name: Determine status
id: status
run: |
if [ "${{ needs.smoke-tests.result }}" == "success" ] && [ "${{ needs.e2e-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.build-and-push.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:*\n${{ needs.e2e-tests.result }}"
}
]
},
{
"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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Optional Notification Compromises Workflow Stability

The Slack notification step will fail when SLACK_WEBHOOK_URL secret is not configured, causing the entire workflow to fail even when all tests pass. The notify-slack job runs with if: always() but the notification step lacks a conditional check to skip execution when the webhook secret is missing. This contradicts the PR description stating notifications are "temporarily disabled until SLACK_WEBHOOK_URL secret is added."

Fix in Cursor Fix in Web


113 changes: 113 additions & 0 deletions .github/workflows/pr-testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: PR to Testing Branch

on:
pull_request:
branches:
- testing
types: [opened, synchronize, reopened]

env:
REGISTRY: registry.digitalocean.com/dbr-cr
PYTHON_VERSION: "3.11"

jobs:
lint-and-type-check:
name: Lint & Type Check
runs-on: ubuntu-latest
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

- name: Run Ruff Lint
run: |
cd echo/server
ruff check .

- name: Run Ruff Format Check
run: |
cd echo/server
ruff format --check .

- name: Run MyPy Type Check
run: |
cd echo/server
mypy dembrane/ --ignore-missing-imports

unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
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

- name: Run Unit Tests
run: |
cd echo/server
pytest tests/ -v -m "not integration and not slow" --maxfail=3

build-images:
name: Build Docker Images (validation only)
runs-on: ubuntu-latest
needs: [lint-and-type-check, unit-tests]
steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build API Server Image
uses: docker/build-push-action@v5
with:
context: ./echo/server
file: ./echo/server/Dockerfile
push: false
tags: ${{ env.REGISTRY }}/dbr-echo-server:pr-${{ github.event.pull_request.number }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Build Directus Image
uses: docker/build-push-action@v5
with:
context: ./echo/directus
file: ./echo/directus/Dockerfile
push: false
tags: ${{ env.REGISTRY }}/dbr-echo-directus:pr-${{ github.event.pull_request.number }}
cache-from: type=gha
cache-to: type=gha,mode=max

auto-merge:
name: Auto-merge PR
runs-on: ubuntu-latest
needs: [build-images]
permissions:
pull-requests: write
contents: write
steps:
- name: Auto-merge PR
uses: pascalgn/automerge-action@v0.16.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MERGE_LABELS: ""
MERGE_METHOD: "squash"
MERGE_COMMIT_MESSAGE: "pull-request-title"
MERGE_RETRIES: 3
MERGE_RETRY_SLEEP: 10000

Loading