Skip to content
Closed
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
157 changes: 157 additions & 0 deletions .github/workflows/pr-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
name: PR Preview Deploy

on:
pull_request:
types: [opened, synchronize, reopened, closed]

env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}

jobs:
deploy-preview:
if: github.event.action != 'closed'
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Railway CLI
run: |
curl -fsSL https://railway.app/install.sh | sh
echo "$HOME/.railway/bin" >> $GITHUB_PATH

- name: Setup PR Environment with PostgreSQL
id: setup
run: |
# Set environment name based on PR number
ENV_NAME="pr-${{ github.event.pull_request.number }}"
echo "env_name=$ENV_NAME" >> $GITHUB_OUTPUT

# Check if environment already exists, create if not
EXISTING_ENV=$(railway environment list 2>/dev/null | grep -w "$ENV_NAME" || true)

if [ -z "$EXISTING_ENV" ]; then
echo "Creating new environment: $ENV_NAME"
railway environment create "$ENV_NAME"
else
echo "Environment $ENV_NAME already exists"
fi

# Switch to the PR environment
railway environment "$ENV_NAME"

# Check if PostgreSQL service exists in this environment
SERVICES=$(railway service list 2>/dev/null || echo "")

if ! echo "$SERVICES" | grep -q "postgres"; then
echo "Provisioning PostgreSQL for $ENV_NAME..."

# Create PostgreSQL service from Railway's template
railway service create --name "postgres-$ENV_NAME" <<EOF
{
"source": {
"image": "postgres:16-alpine"
},
"variables": {
"POSTGRES_USER": "postgres",
"POSTGRES_PASSWORD": "$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 24)",
"POSTGRES_DB": "thinking_partner"
}
}
EOF

# Wait for PostgreSQL to be ready
echo "Waiting for PostgreSQL to initialize..."
sleep 30
fi

- name: Deploy Application
run: |
ENV_NAME="pr-${{ github.event.pull_request.number }}"
railway environment "$ENV_NAME"

# Link the app to PostgreSQL and deploy
railway up --detach

# Get the deployment URL
DEPLOY_URL=$(railway status --json 2>/dev/null | jq -r '.deploymentUrl // empty' || echo "")
echo "Deployment URL: $DEPLOY_URL"

- name: Comment PR with deployment URL
uses: actions/github-script@v7
with:
script: |
const envName = `pr-${{ github.event.pull_request.number }}`;
const body = `## 🚀 Preview Deployment Ready

Your PR preview environment has been deployed with:
- ✅ Application service
- ✅ PostgreSQL database (auto-provisioned)

**Environment:** \`${envName}\`

The DATABASE_URL has been automatically configured for this preview environment.

> Note: Preview environments are automatically cleaned up when the PR is closed.`;

// Find existing comment
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});

const botComment = comments.data.find(c =>
c.user.type === 'Bot' && c.body.includes('Preview Deployment Ready')
);

if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body
});
}

cleanup-preview:
if: github.event.action == 'closed'
runs-on: ubuntu-latest
steps:
- name: Install Railway CLI
run: |
curl -fsSL https://railway.app/install.sh | sh
echo "$HOME/.railway/bin" >> $GITHUB_PATH

- name: Cleanup PR Environment
run: |
ENV_NAME="pr-${{ github.event.pull_request.number }}"

echo "Cleaning up environment: $ENV_NAME"

# Delete the PR environment (this removes all services including PostgreSQL)
railway environment delete "$ENV_NAME" --yes 2>/dev/null || echo "Environment may already be deleted"

echo "Cleanup complete for $ENV_NAME"

- name: Comment PR about cleanup
uses: actions/github-script@v7
with:
script: |
const body = `## 🧹 Preview Environment Cleaned Up

The preview environment for this PR has been deleted along with its PostgreSQL database.`;

await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body
});
13 changes: 13 additions & 0 deletions railway.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "https://railway.app/railway.schema.json",
"build": {
"builder": "NIXPACKS"
},
"deploy": {
"startCommand": "cd backend && python -m uvicorn main:app --host 0.0.0.0 --port $PORT",
"healthcheckPath": "/health",
"healthcheckTimeout": 100,
"restartPolicyType": "ON_FAILURE",
"restartPolicyMaxRetries": 3
}
}
43 changes: 43 additions & 0 deletions railway.template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"$schema": "https://railway.app/railway.schema.json",
"name": "ThoughtsAI",
"description": "AI-powered thinking partner with PostgreSQL database",
"services": [
{
"name": "app",
"source": {
"repo": "SLatz18/thoughtsAI"
},
"build": {
"builder": "NIXPACKS"
},
"deploy": {
"startCommand": "cd backend && python -m uvicorn main:app --host 0.0.0.0 --port $PORT",
"healthcheckPath": "/health",
"healthcheckTimeout": 100,
"restartPolicyType": "ON_FAILURE",
"restartPolicyMaxRetries": 3
},
"variables": {
"DATABASE_URL": "${{postgres.DATABASE_URL}}",
"ANTHROPIC_API_KEY": {
"description": "Anthropic API key for Claude",
"required": true
},
"OPENAI_API_KEY": {
"description": "OpenAI API key for Whisper transcription",
"required": false
},
"DEEPGRAM_API_KEY": {
"description": "Deepgram API key for streaming transcription",
"required": false
}
}
},
{
"name": "postgres",
"plugin": "postgresql",
"variables": {}
}
]
}
9 changes: 9 additions & 0 deletions railway.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,12 @@ healthcheckPath = "/health"
healthcheckTimeout = 100
restartPolicyType = "on_failure"
restartPolicyMaxRetries = 3

# Service name for Railway project linking
[service]
name = "app"

# Database reference - Railway will auto-inject DATABASE_URL
# when a PostgreSQL service named "postgres" exists in the project
[variables]
DATABASE_URL = "${{postgres.DATABASE_URL}}"
73 changes: 73 additions & 0 deletions scripts/cleanup-railway-env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/bin/bash
#
# Cleanup Railway PR Preview Environment
#
# This script deletes a Railway environment and all its services
# including the PostgreSQL database.
#
# Usage:
# ./scripts/cleanup-railway-env.sh <environment-name>
#
# Example:
# ./scripts/cleanup-railway-env.sh pr-123

set -e

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}

log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}

log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}

# Parse arguments
ENV_NAME="${1:-}"

if [ -z "$ENV_NAME" ]; then
log_error "Environment name is required"
echo "Usage: $0 <environment-name>"
echo "Example: $0 pr-123"
exit 1
fi

# Safety check - don't delete production environments
if [ "$ENV_NAME" = "production" ] || [ "$ENV_NAME" = "prod" ] || [ "$ENV_NAME" = "main" ]; then
log_error "Refusing to delete protected environment: $ENV_NAME"
exit 1
fi

log_info "Cleaning up Railway environment: $ENV_NAME"

# Check if environment exists
if ! railway environment list 2>/dev/null | grep -qw "$ENV_NAME"; then
log_warn "Environment $ENV_NAME does not exist or already deleted"
exit 0
fi

# Confirm deletion (skip in CI)
if [ -z "$CI" ] && [ -z "$RAILWAY_TOKEN" ]; then
read -p "Are you sure you want to delete environment '$ENV_NAME' and all its data? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_info "Cancelled"
exit 0
fi
fi

# Delete the environment
log_info "Deleting environment: $ENV_NAME"
railway environment delete "$ENV_NAME" --yes

log_info "Environment $ENV_NAME has been deleted"
log_info "All services including PostgreSQL database have been removed"
Loading
Loading