Skip to content

docs: add SSH key troubleshooting guide for deployment issues #4

docs: add SSH key troubleshooting guide for deployment issues

docs: add SSH key troubleshooting guide for deployment issues #4

Workflow file for this run

name: Deploy to Staging
on:
push:
branches: [develop]
workflow_run:
workflows: ["Test Suite"]
types: [completed]
branches: [develop]
env:
NODE_VERSION: '18'
PYTHON_VERSION: '3.9'
jobs:
build-and-deploy:
name: Build and Deploy to Staging
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'push' }}
environment:
name: staging
url: https://dev.autoauthor.app
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Build frontend
working-directory: ./frontend
env:
NEXT_PUBLIC_API_URL: ${{ secrets.API_URL }}
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: ${{ secrets.CLERK_PUBLISHABLE_KEY }}
NEXT_PUBLIC_ENVIRONMENT: staging
run: |
npm ci
npm run build
- name: Create deployment package
run: |
# Create temporary directory
mkdir -p /tmp/deploy
# Copy necessary files (exclude development artifacts)
rsync -av --progress \
--exclude='node_modules' \
--exclude='.venv' \
--exclude='.next' \
--exclude='coverage' \
--exclude='*.log' \
--exclude='.env.local' \
--exclude='.env' \
--exclude='__pycache__' \
--exclude='*.pyc' \
--exclude='.pytest_cache' \
--exclude='test-results' \
--exclude='playwright-report' \
--exclude='.git' \
. /tmp/deploy/
# Create tar archive
cd /tmp/deploy
tar -czf /tmp/deploy-package.tar.gz .
- name: Setup SSH
run: |
mkdir -p ~/.ssh
printf "%s" "${{ secrets.SSH_KEY }}" > ~/.ssh/staging_key
chmod 600 ~/.ssh/staging_key
ssh-keyscan -H ${{ secrets.HOST }} >> ~/.ssh/known_hosts
- name: Upload package to server
run: |
scp -i ~/.ssh/staging_key /tmp/deploy-package.tar.gz \
${{ secrets.USER }}@${{ secrets.HOST }}:/tmp/
- name: Deploy to staging
run: |
ssh -i ~/.ssh/staging_key \
${{ secrets.USER }}@${{ secrets.HOST }} \
'bash -s' << 'ENDSSH'
set -euo pipefail
ENVIRONMENT="staging"
RELEASE_ID="$(date +%Y%m%d-%H%M%S)"
API_URL="${{ secrets.API_URL }}"
FRONTEND_URL="${{ secrets.FRONTEND_URL }}"
CLERK_PUBLISHABLE_KEY="${{ secrets.CLERK_PUBLISHABLE_KEY }}"
CLERK_SECRET_KEY="${{ secrets.CLERK_SECRET_KEY }}"
DEPLOY_BASE="/opt/auto-author"
RELEASE_DIR="$DEPLOY_BASE/releases/$RELEASE_ID"
CURRENT_DIR="$DEPLOY_BASE/current"
echo "==> Deploying $ENVIRONMENT environment (Release: $RELEASE_ID)"
# Create release directory
mkdir -p "$RELEASE_DIR"
# Extract uploaded package
echo "==> Extracting deployment package..."
tar -xzf /tmp/deploy-package.tar.gz -C "$RELEASE_DIR"
# Setup backend environment
echo "==> Setting up backend..."
cd "$RELEASE_DIR/backend"
# Install Python dependencies
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.cargo/bin:$PATH"
uv venv
source .venv/bin/activate
uv pip install -r requirements.txt
# Update .env file (merge with existing)
if [ -f "$CURRENT_DIR/backend/.env" ]; then
cp "$CURRENT_DIR/backend/.env" .env
fi
# Setup frontend environment
echo "==> Setting up frontend..."
cd "$RELEASE_DIR/frontend"
# Install Node dependencies (production only)
npm ci --production
# Create/update .env.production
cat > .env.production <<EOF
NEXT_PUBLIC_API_URL=$API_URL
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=$CLERK_PUBLISHABLE_KEY
CLERK_SECRET_KEY=$CLERK_SECRET_KEY
NEXT_PUBLIC_ENVIRONMENT=$ENVIRONMENT
PORT=3002
EOF
# Build frontend
echo "==> Building frontend..."
npm run build
# Update symlink atomically
echo "==> Switching to new release..."
ln -snf "$RELEASE_DIR" "$CURRENT_DIR.tmp"
mv -Tf "$CURRENT_DIR.tmp" "$CURRENT_DIR"
# Restart services
echo "==> Restarting services..."
pm2 restart auto-author-backend || pm2 start "$CURRENT_DIR/backend/.venv/bin/uvicorn" --name auto-author-backend -- app.main:app --host 0.0.0.0 --port 8000
pm2 restart auto-author-frontend || pm2 start npm --name auto-author-frontend -- start
# Wait for services to start
sleep 5
# Health checks
echo "==> Running health checks..."
curl -f http://localhost:8000/api/v1/health || {
echo "ERROR: Backend health check failed"
exit 1
}
curl -f http://localhost:3002 || {
echo "ERROR: Frontend health check failed"
exit 1
}
echo "==> Deployment successful!"
echo " Release: $RELEASE_ID"
echo " Backend: http://localhost:8000"
echo " Frontend: http://localhost:3002"
# Cleanup old releases (keep last 5)
cd "$DEPLOY_BASE/releases"
ls -t | tail -n +6 | xargs -r rm -rf
echo "==> Cleanup complete"
ENDSSH
- name: Health checks
run: |
# Wait for services to stabilize
sleep 10
# Backend health check
echo "Checking backend health..."
curl -f https://api.dev.autoauthor.app/api/v1/health || exit 1
# Frontend health check
echo "Checking frontend health..."
curl -f https://dev.autoauthor.app || exit 1
# CORS verification
echo "Checking CORS headers..."
curl -I -X OPTIONS https://api.dev.autoauthor.app/api/v1/books \
-H "Origin: https://dev.autoauthor.app" \
-H "Access-Control-Request-Method: GET" | grep -i "access-control-allow-origin" || exit 1
- name: Smoke tests
run: |
# Test API docs endpoint
curl -f https://api.dev.autoauthor.app/docs || exit 1
echo "✅ All smoke tests passed"
- name: Cleanup
if: always()
run: |
rm -f ~/.ssh/staging_key
rm -f /tmp/deploy-package.tar.gz
notify:
name: Notify Deployment Status
runs-on: ubuntu-latest
needs: [build-and-deploy]
if: always()
steps:
- name: Notify Slack
uses: 8398a7/action-slack@v3
with:
status: ${{ needs.build-and-deploy.result }}
text: |
🚀 Staging Deployment ${{ needs.build-and-deploy.result }}
Branch: ${{ github.ref_name }}
Commit: ${{ github.sha }}
Author: ${{ github.actor }}
URL: https://dev.autoauthor.app
API: https://api.dev.autoauthor.app
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}