feat: add pr flow #18
Workflow file for this run
This file contains 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: PR Check | |
on: | |
pull_request: | |
branches: [ main ] | |
permissions: | |
contents: read | |
pull-requests: write # Needed to comment on PRs | |
jobs: | |
test: | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Setup Node.js | |
uses: actions/setup-node@v4 | |
with: | |
node-version: '20' | |
cache: 'npm' | |
- name: Install Dependencies | |
run: | | |
npm run install:all | |
- name: Build Frontend | |
run: cd frontend && npm run build | |
- name: Build Backend | |
run: cd backend && npm run build | |
- name: Check for sensitive data | |
run: | | |
echo "Checking for sensitive data in source files..." | |
# First show all matches with context | |
echo "🔍 Scanning files for sensitive patterns..." | |
find frontend/src backend/src -type f -not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/.github/*" -exec grep -H -n "API_KEY\|SECRET\|PASSWORD\|PRIVATE_KEY" {} \; || true | |
# Then do the actual check | |
FOUND_FILES=$(find frontend/src backend/src -type f -not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/.github/*" -exec grep -l "API_KEY\|SECRET\|PASSWORD\|PRIVATE_KEY" {} \;) | |
if [ ! -z "$FOUND_FILES" ]; then | |
echo "⚠️ Warning: Possible sensitive data found in these files:" | |
echo "$FOUND_FILES" | |
echo "Please review the matches above and ensure no sensitive data is being committed." | |
exit 1 | |
else | |
echo "✅ No sensitive data patterns found" | |
fi | |
- name: Start Backend | |
run: | | |
cd backend | |
npm run dev & | |
echo "Waiting for backend to start..." | |
sleep 2 | |
- name: Start Frontend Dev Server | |
run: | | |
cd frontend | |
npm run dev & | |
echo "Waiting for frontend to start..." | |
sleep 2 | |
- name: Test Application | |
run: | | |
# Test backend health endpoint | |
BACKEND_HEALTH=$(curl -s http://localhost:3456/health) | |
if [[ $BACKEND_HEALTH != *"ok"* ]]; then | |
echo "❌ Backend health check failed" | |
exit 1 | |
fi | |
echo "✅ Backend is healthy" | |
# Test frontend is running | |
FRONTEND_RESPONSE=$(curl -s http://localhost:5173) | |
if [[ $FRONTEND_RESPONSE != *"<!DOCTYPE html>"* ]]; then | |
echo "❌ Frontend check failed" | |
exit 1 | |
fi | |
echo "✅ Frontend is running" | |
# Test Bitcoin price endpoint | |
PRICE_CHECK=$(curl -s http://localhost:3456/api/price/bitcoin) | |
if [[ $PRICE_CHECK != *"success"* ]]; then | |
echo "❌ Bitcoin price endpoint failed" | |
exit 1 | |
fi | |
echo "✅ Bitcoin price endpoint is working" | |
- name: Create Render Preview | |
env: | |
RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }} | |
run: | | |
# Create a unique name for this PR preview | |
PR_NAME="pr-${GITHUB_HEAD_REF:-$GITHUB_REF_NAME}-${{ github.event.number }}" | |
echo "Creating preview for $PR_NAME" | |
# Get owner ID first | |
echo "Getting owner ID..." | |
OWNER_INFO=$(curl -s -H "Authorization: Bearer $RENDER_API_KEY" https://api.render.com/v1/owners) | |
# Extract the owner ID from the nested structure | |
OWNER_ID=$(echo "$OWNER_INFO" | jq -r '.[0].owner.id') | |
if [ -z "$OWNER_ID" ] || [ "$OWNER_ID" = "null" ]; then | |
echo "❌ Failed to get owner ID" | |
exit 1 | |
fi | |
echo "Using Render team ID: ${OWNER_ID:0:8}..." # Only show first 8 chars | |
# Create backend service | |
echo "Creating backend service..." | |
# Create JSON payload using jq for proper escaping | |
BACKEND_JSON=$(jq -n \ | |
--arg name "$PR_NAME-backend" \ | |
--arg owner "$OWNER_ID" \ | |
--arg build_cmd "cd backend && npm install && npm run build" \ | |
--arg start_cmd "cd backend && npm run start" \ | |
'{ | |
name: $name, | |
type: "web_service", | |
runtime: "node", | |
buildCommand: $build_cmd, | |
startCommand: $start_cmd, | |
envVars: [ | |
{key: "PORT", value: "3456"} | |
], | |
ownerId: $owner, | |
serviceDetails: { | |
plan: "free", | |
pullRequestPreviewsEnabled: true, | |
region: "oregon", | |
env: "node", | |
rootDir: ".", | |
numInstances: 1, | |
healthCheckPath: "/health", | |
buildFilter: { | |
paths: ["backend/**"] | |
} | |
}, | |
autoDeploy: "no" | |
}') | |
# Debug: Print the JSON payload | |
echo "Debug: JSON payload for backend service:" | |
echo "$BACKEND_JSON" | jq '.' | |
# Validate JSON before sending | |
echo "Validating JSON..." | |
if ! echo "$BACKEND_JSON" | jq '.' > /dev/null 2>&1; then | |
echo "❌ Invalid JSON:" | |
echo "$BACKEND_JSON" | |
exit 1 | |
fi | |
echo "Sending request to create backend service..." | |
# Capture response headers and body separately | |
BACKEND_RESPONSE=$(curl -v -X POST https://api.render.com/v1/services \ | |
-H "Authorization: Bearer $RENDER_API_KEY" \ | |
-H "Content-Type: application/json" \ | |
-d "$BACKEND_JSON" 2>curl_stderr) | |
echo "Backend service creation response:" | |
echo "$BACKEND_RESPONSE" | |
# For debugging, show the curl output | |
echo "Curl debug output:" | |
cat curl_stderr | |
# Try to extract the ID, but first check if we can parse the response | |
if ! echo "$BACKEND_RESPONSE" | jq '.' > /dev/null 2>&1; then | |
echo "❌ Invalid JSON response from Render API:" | |
echo "Response: $BACKEND_RESPONSE" | |
exit 1 | |
fi | |
BACKEND_ID=$(echo "$BACKEND_RESPONSE" | jq -r '.service.id // empty') | |
if [ -z "$BACKEND_ID" ]; then | |
echo "❌ Failed to create backend service" | |
exit 1 | |
fi | |
echo "Waiting for backend service to be ready..." | |
for i in {1..30}; do | |
BACKEND_STATUS=$(curl -s -H "Authorization: Bearer $RENDER_API_KEY" \ | |
"https://api.render.com/v1/services/$BACKEND_ID" | jq -r '.service.status // empty') | |
if [ "$BACKEND_STATUS" = "live" ]; then | |
break | |
fi | |
echo "Backend status: $BACKEND_STATUS" | |
sleep 10 | |
done | |
# Get backend URL | |
BACKEND_INFO=$(curl -s -H "Authorization: Bearer $RENDER_API_KEY" \ | |
"https://api.render.com/v1/services/$BACKEND_ID") | |
BACKEND_URL=$(echo "$BACKEND_INFO" | jq -r '.service.url // empty') | |
if [ -z "$BACKEND_URL" ]; then | |
echo "❌ Failed to get backend URL" | |
echo "Backend info: $BACKEND_INFO" | |
exit 1 | |
fi | |
echo "Backend URL: $BACKEND_URL" | |
# Create frontend service | |
echo "Creating frontend service..." | |
# Create JSON payload using jq for proper escaping | |
FRONTEND_JSON=$(jq -n \ | |
--arg name "$PR_NAME-frontend" \ | |
--arg owner "$OWNER_ID" \ | |
--arg backend_url "$BACKEND_URL" \ | |
--arg build_cmd "cd frontend && npm install && npm run build" \ | |
'{ | |
name: $name, | |
type: "static_site", | |
buildCommand: $build_cmd, | |
publishPath: "frontend/dist", | |
envVars: [ | |
{key: "VITE_BACKEND_URL", value: $backend_url} | |
], | |
ownerId: $owner, | |
serviceDetails: { | |
plan: "free", | |
pullRequestPreviewsEnabled: true, | |
region: "oregon", | |
buildFilter: { | |
paths: ["frontend/**"] | |
} | |
}, | |
autoDeploy: "no" | |
}') | |
# Debug: Print the JSON payload | |
echo "Debug: JSON payload for frontend service:" | |
echo "$FRONTEND_JSON" | jq '.' | |
# Validate JSON before sending | |
echo "Validating JSON..." | |
if ! echo "$FRONTEND_JSON" | jq '.' > /dev/null 2>&1; then | |
echo "❌ Invalid JSON:" | |
echo "$FRONTEND_JSON" | |
exit 1 | |
fi | |
echo "Sending request to create frontend service..." | |
# Capture response headers and body separately | |
FRONTEND_RESPONSE=$(curl -v -X POST https://api.render.com/v1/services \ | |
-H "Authorization: Bearer $RENDER_API_KEY" \ | |
-H "Content-Type: application/json" \ | |
-d "$FRONTEND_JSON" 2>curl_stderr_frontend) | |
echo "Frontend service creation response:" | |
echo "$FRONTEND_RESPONSE" | |
# For debugging, show the curl output | |
echo "Curl debug output:" | |
cat curl_stderr_frontend | |
# Try to extract the ID, but first check if we can parse the response | |
if ! echo "$FRONTEND_RESPONSE" | jq '.' > /dev/null 2>&1; then | |
echo "❌ Invalid JSON response from Render API:" | |
echo "Response: $FRONTEND_RESPONSE" | |
exit 1 | |
fi | |
FRONTEND_ID=$(echo "$FRONTEND_RESPONSE" | jq -r '.service.id // empty') | |
if [ -z "$FRONTEND_ID" ]; then | |
echo "❌ Failed to create frontend service" | |
exit 1 | |
fi | |
echo "Waiting for frontend service to be ready..." | |
for i in {1..30}; do | |
FRONTEND_STATUS=$(curl -s -H "Authorization: Bearer $RENDER_API_KEY" \ | |
"https://api.render.com/v1/services/$FRONTEND_ID" | jq -r '.service.status // empty') | |
if [ "$FRONTEND_STATUS" = "live" ]; then | |
break | |
fi | |
echo "Frontend status: $FRONTEND_STATUS" | |
sleep 10 | |
done | |
# Get frontend URL | |
FRONTEND_INFO=$(curl -s -H "Authorization: Bearer $RENDER_API_KEY" \ | |
"https://api.render.com/v1/services/$FRONTEND_ID") | |
FRONTEND_URL=$(echo "$FRONTEND_INFO" | jq -r '.service.url // empty') | |
if [ -z "$FRONTEND_URL" ]; then | |
echo "❌ Failed to get frontend URL" | |
echo "Frontend info: $FRONTEND_INFO" | |
exit 1 | |
fi | |
echo "Frontend URL: $FRONTEND_URL" | |
# Save URLs for the PR comment | |
echo "FRONTEND_URL=$FRONTEND_URL" >> $GITHUB_ENV | |
echo "BACKEND_URL=$BACKEND_URL" >> $GITHUB_ENV | |
- name: Comment PR | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
let comment = '## PR Check Results\n\n'; | |
comment += '### Application Tests\n\n'; | |
comment += '✅ Backend started successfully\n'; | |
comment += '✅ Frontend started successfully\n'; | |
comment += '✅ Health checks passed\n\n'; | |
comment += '### Preview Deployment\n\n'; | |
comment += `🌐 Frontend Preview: ${process.env.FRONTEND_URL}\n`; | |
comment += `🔗 Backend API: ${process.env.BACKEND_URL}\n\n`; | |
comment += '⏳ Preview environment is being deployed and will be ready in a few minutes.\n\n'; | |
comment += '⚠️ Please test the preview deployment before merging!'; | |
await github.rest.issues.createComment({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: context.issue.number, | |
body: comment | |
}); |