Skip to content

Commit 442c4f8

Browse files
committed
feat(workflows): add CI/CD workflows for backend and frontend
- Implement backend deployment to VPS with Node.js and PM2 management - Create frontend deployment to AWS S3 with CloudFront cache invalidation - Include installation and build steps for both backend and frontend projects - Set up workflow triggers on main and master branches for code changes
1 parent 47616ea commit 442c4f8

File tree

2 files changed

+206
-0
lines changed

2 files changed

+206
-0
lines changed

.github/workflows/backend.yml

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
name: Deploy Backend to VPS
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- master
8+
paths:
9+
- 'backend/**'
10+
workflow_dispatch:
11+
12+
jobs:
13+
deploy:
14+
name: Deploy to VPS
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v4
20+
21+
- name: Setup Node.js
22+
uses: actions/setup-node@v4
23+
with:
24+
node-version: '20'
25+
cache: 'npm'
26+
cache-dependency-path: backend/package-lock.json
27+
28+
- name: Install dependencies
29+
working-directory: ./backend
30+
run: npm ci
31+
32+
- name: Build TypeScript
33+
working-directory: ./backend
34+
run: npm run build
35+
36+
- name: Create deployment package
37+
run: |
38+
cd backend
39+
tar -czf ../backend-deploy.tar.gz \
40+
dist/ \
41+
package.json \
42+
package-lock.json \
43+
node_modules/
44+
45+
- name: Deploy to VPS
46+
uses: appleboy/scp-action@v0.1.7
47+
with:
48+
host: ${{ secrets.VPS_HOST }}
49+
username: ${{ secrets.VPS_USER }}
50+
key: ${{ secrets.VPS_SSH_KEY }}
51+
port: ${{ secrets.VPS_SSH_PORT || 22 }}
52+
source: "backend-deploy.tar.gz"
53+
target: "/tmp"
54+
55+
- name: Extract and restart service
56+
uses: appleboy/ssh-action@v1.0.3
57+
with:
58+
host: ${{ secrets.VPS_HOST }}
59+
username: ${{ secrets.VPS_USER }}
60+
key: ${{ secrets.VPS_SSH_KEY }}
61+
port: ${{ secrets.VPS_SSH_PORT || 22 }}
62+
script: |
63+
# Set deployment directory
64+
DEPLOY_DIR="${{ secrets.VPS_DEPLOY_PATH || '/root/copy-paste.space/backend' }}"
65+
66+
# Navigate to deployment directory
67+
cd "$DEPLOY_DIR"
68+
69+
# Backup current dist and node_modules if they exist
70+
if [ -d "dist" ] || [ -d "node_modules" ]; then
71+
BACKUP_DIR="backup-$(date +%Y%m%d-%H%M%S)"
72+
mkdir -p "../backups/$BACKUP_DIR"
73+
[ -d "dist" ] && mv dist "../backups/$BACKUP_DIR/"
74+
[ -d "node_modules" ] && mv node_modules "../backups/$BACKUP_DIR/"
75+
fi
76+
77+
# Extract new deployment
78+
tar -xzf /tmp/backend-deploy.tar.gz -C "$DEPLOY_DIR"
79+
80+
# Ensure .env file exists (copy from backup if needed)
81+
if [ ! -f ".env" ] && [ -d "../backups" ]; then
82+
LATEST_BACKUP=$(ls -td ../backups/backup-* 2>/dev/null | head -1)
83+
if [ -n "$LATEST_BACKUP" ] && [ -f "$LATEST_BACKUP/.env" ]; then
84+
cp "$LATEST_BACKUP/.env" .env
85+
fi
86+
fi
87+
88+
# Install production dependencies
89+
npm ci --production || npm install --production
90+
91+
# Restart PM2 service
92+
if pm2 describe cp-backend-production > /dev/null 2>&1; then
93+
# Process exists, reload it (zero-downtime restart)
94+
pm2 reload cp-backend-production --update-env
95+
else
96+
# Process doesn't exist, start it
97+
pm2 start dist/server.js \
98+
--name cp-backend-production \
99+
--cwd "$DEPLOY_DIR" \
100+
--update-env
101+
pm2 save
102+
fi
103+
104+
# Cleanup old backups (keep last 5)
105+
if [ -d "../backups" ]; then
106+
ls -td ../backups/backup-* 2>/dev/null | tail -n +6 | xargs rm -rf
107+
fi
108+
109+
# Cleanup temp file
110+
rm -f /tmp/backend-deploy.tar.gz
111+
112+
- name: Health check
113+
uses: appleboy/ssh-action@v1.0.3
114+
with:
115+
host: ${{ secrets.VPS_HOST }}
116+
username: ${{ secrets.VPS_USER }}
117+
key: ${{ secrets.VPS_SSH_KEY }}
118+
port: ${{ secrets.VPS_SSH_PORT || 22 }}
119+
script: |
120+
sleep 5
121+
# Check PM2 process status
122+
pm2 status cp-backend-production
123+
pm2 info cp-backend-production
124+

.github/workflows/frontend.yml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: Deploy Frontend to AWS S3 + CloudFront
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- master
8+
paths:
9+
- 'frontend/**'
10+
workflow_dispatch:
11+
12+
jobs:
13+
deploy:
14+
name: Deploy to AWS S3 and CloudFront
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v4
20+
21+
- name: Setup Node.js
22+
uses: actions/setup-node@v4
23+
with:
24+
node-version: '18'
25+
cache: 'npm'
26+
cache-dependency-path: frontend/package-lock.json
27+
28+
- name: Install dependencies
29+
working-directory: ./frontend
30+
run: npm ci
31+
32+
- name: Build frontend
33+
working-directory: ./frontend
34+
env:
35+
VITE_BACKEND_URI: ${{ secrets.VITE_BACKEND_URI }}
36+
VITE_POSTHOG_API_KEY: ${{ secrets.VITE_POSTHOG_API_KEY }}
37+
VITE_POSTHOG_SECURE_PATH: ${{ secrets.VITE_POSTHOG_SECURE_PATH }}
38+
VITE_PUBLIC_POSTHOG_HOST: ${{ secrets.VITE_PUBLIC_POSTHOG_HOST || 'https://us.i.posthog.com' }}
39+
run: npm run build
40+
41+
- name: Configure AWS credentials
42+
uses: aws-actions/configure-aws-credentials@v4
43+
with:
44+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
45+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
46+
aws-region: ${{ secrets.AWS_REGION || 'us-east-1' }}
47+
48+
- name: Deploy to S3
49+
working-directory: ./frontend
50+
run: |
51+
aws s3 sync dist/ s3://${{ secrets.AWS_S3_BUCKET_NAME }} \
52+
--delete \
53+
--cache-control "public, max-age=31536000, immutable" \
54+
--exclude "*.html" \
55+
--exclude "*.json"
56+
57+
# Upload HTML files with no-cache
58+
aws s3 sync dist/ s3://${{ secrets.AWS_S3_BUCKET_NAME }} \
59+
--delete \
60+
--cache-control "no-cache, no-store, must-revalidate" \
61+
--exclude "*" \
62+
--include "*.html" \
63+
--include "*.json"
64+
65+
- name: Invalidate CloudFront cache
66+
env:
67+
CLOUDFRONT_DIST_ID: ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID }}
68+
run: |
69+
if [ -n "$CLOUDFRONT_DIST_ID" ]; then
70+
aws cloudfront create-invalidation \
71+
--distribution-id "$CLOUDFRONT_DIST_ID" \
72+
--paths "/*"
73+
else
74+
echo "Skipping CloudFront invalidation: Distribution ID not set"
75+
fi
76+
77+
- name: Deployment summary
78+
run: |
79+
echo "✅ Frontend deployed successfully!"
80+
echo "S3 Bucket: ${{ secrets.AWS_S3_BUCKET_NAME }}"
81+
echo "CloudFront Distribution: ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID }}"
82+

0 commit comments

Comments
 (0)