test: verify preview environment workflow #2
Workflow file for this run
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: Preview | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened, closed] | |
| jobs: | |
| deploy-preview: | |
| if: github.event.action != 'closed' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Deploy preview environment | |
| uses: appleboy/ssh-action@v1 | |
| with: | |
| host: ${{ secrets.SERVER_HOST }} | |
| username: ${{ secrets.SERVER_USER }} | |
| key: ${{ secrets.SERVER_SSH_KEY }} | |
| script: | | |
| set -e | |
| APP_NAME="${{ github.event.repository.name }}" | |
| PR_NUMBER="${{ github.event.number }}" | |
| PREVIEW_DOMAIN="pr-${PR_NUMBER}.preview.${{ secrets.PREVIEW_DOMAIN }}" | |
| APP_DB=$(echo "${APP_NAME}" | tr '-' '_')_db | |
| SCHEMA_NAME="pr_${PR_NUMBER}" | |
| cd /opt/apps/${APP_NAME} | |
| # Fetch and checkout the PR branch | |
| git fetch origin pull/${PR_NUMBER}/head:pr-${PR_NUMBER} --force | |
| git checkout pr-${PR_NUMBER} | |
| # Create preview schema in the app's database | |
| docker compose -f /opt/platform/docker-compose.yml exec -T postgres \ | |
| psql -U postgres -d ${APP_DB} -c "CREATE SCHEMA IF NOT EXISTS ${SCHEMA_NAME}" | |
| # Read the app's .env and generate a preview-specific version | |
| # with DATABASE_URL pointing to the PR schema | |
| if [ ! -f deploy/.env ]; then | |
| echo "ERROR: deploy/.env not found. Create it from deploy/env.template first." | |
| exit 1 | |
| fi | |
| # Generate preview .env with schema-aware DATABASE_URL | |
| sed "s|^DATABASE_URL=.*|DATABASE_URL=postgresql://postgres:$(grep POSTGRES_PASSWORD /opt/platform/.env | cut -d= -f2)@postgres:5432/${APP_DB}?options=-csearch_path%3D${SCHEMA_NAME}|" \ | |
| deploy/.env > deploy/.env.pr-${PR_NUMBER} | |
| # Build and start the app container (skip optional services like celery-worker) | |
| docker compose -p ${APP_NAME}-pr-${PR_NUMBER} -f deploy/docker-compose.yml \ | |
| --env-file deploy/.env.pr-${PR_NUMBER} up -d --build app | |
| # Run database migrations against the preview schema | |
| docker compose -p ${APP_NAME}-pr-${PR_NUMBER} -f deploy/docker-compose.yml \ | |
| exec -T app alembic -c app/alembic.ini upgrade head | |
| # Generate Caddyfile for preview domain | |
| cat > /opt/platform/caddy-apps/${APP_NAME}-pr-${PR_NUMBER}.caddy <<CADDYEOF | |
| ${PREVIEW_DOMAIN} { | |
| reverse_proxy ${APP_NAME}-pr-${PR_NUMBER}-app-1:8000 | |
| } | |
| CADDYEOF | |
| # Reload Caddy to pick up new route | |
| docker compose -f /opt/platform/docker-compose.yml exec -T caddy \ | |
| caddy reload --config /etc/caddy/Caddyfile | |
| # Switch back to main so production deploys aren't affected | |
| git checkout main | |
| - name: Post preview URL | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const previewDomain = `pr-${{ github.event.number }}.preview.${{ secrets.PREVIEW_DOMAIN }}`; | |
| const body = `Preview deployed: https://${previewDomain}\n\nHealth check: https://${previewDomain}/health`; | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| }); | |
| const existing = comments.find(c => c.body.startsWith('Preview deployed:')); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existing.id, | |
| body, | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body, | |
| }); | |
| } | |
| cleanup-preview: | |
| if: github.event.action == 'closed' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Clean up preview environment | |
| uses: appleboy/ssh-action@v1 | |
| with: | |
| host: ${{ secrets.SERVER_HOST }} | |
| username: ${{ secrets.SERVER_USER }} | |
| key: ${{ secrets.SERVER_SSH_KEY }} | |
| script: | | |
| set -e | |
| APP_NAME="${{ github.event.repository.name }}" | |
| PR_NUMBER="${{ github.event.number }}" | |
| APP_DB=$(echo "${APP_NAME}" | tr '-' '_')_db | |
| SCHEMA_NAME="pr_${PR_NUMBER}" | |
| # Stop and remove preview containers | |
| cd /opt/apps/${APP_NAME} | |
| docker compose -p ${APP_NAME}-pr-${PR_NUMBER} -f deploy/docker-compose.yml down --rmi local || true | |
| # Drop the preview schema | |
| docker compose -f /opt/platform/docker-compose.yml exec -T postgres \ | |
| psql -U postgres -d ${APP_DB} -c "DROP SCHEMA IF EXISTS ${SCHEMA_NAME} CASCADE" | |
| # Remove preview Caddyfile | |
| rm -f /opt/platform/caddy-apps/${APP_NAME}-pr-${PR_NUMBER}.caddy | |
| # Reload Caddy | |
| docker compose -f /opt/platform/docker-compose.yml exec -T caddy \ | |
| caddy reload --config /etc/caddy/Caddyfile | |
| # Clean up preview .env and branch | |
| rm -f deploy/.env.pr-${PR_NUMBER} | |
| git branch -D pr-${PR_NUMBER} || true |