Skip to content

Commit 27e0bfc

Browse files
authored
Merge pull request #5 from SkySingh04/feature/cd-pipeline
feat : Deployment Pipeline on PB Server
2 parents 2728931 + 407381d commit 27e0bfc

8 files changed

Lines changed: 256 additions & 16 deletions

File tree

.github/workflows/cd.yml

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
name: CD
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request_target:
7+
branches: [ main ]
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: read
12+
packages: write
13+
14+
env:
15+
REGISTRY: ghcr.io
16+
17+
jobs:
18+
build-and-push:
19+
runs-on: ubuntu-latest
20+
outputs:
21+
backend_image: ${{ steps.tags.outputs.backend }}
22+
frontend_image: ${{ steps.tags.outputs.frontend }}
23+
# Only run on PRs originating from this repo to avoid exposing secrets to forks
24+
if: ${{ github.event_name != 'pull_request_target' || github.event.pull_request.head.repo.full_name == github.repository }}
25+
steps:
26+
- name: Select ref
27+
run: |
28+
if [ "${{ github.event_name }}" = "pull_request_target" ]; then
29+
echo "REF=${{ github.event.pull_request.head.sha }}" >> $GITHUB_ENV
30+
else
31+
echo "REF=${GITHUB_SHA}" >> $GITHUB_ENV
32+
fi
33+
- uses: actions/checkout@v4
34+
with:
35+
ref: ${{ env.REF }}
36+
37+
- name: Derive lowercase image repo
38+
run: |
39+
echo "IMAGE_REPO=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
40+
41+
- name: Log in to GHCR
42+
uses: docker/login-action@v3
43+
with:
44+
registry: ${{ env.REGISTRY }}
45+
username: ${{ github.actor }}
46+
password: ${{ secrets.GITHUB_TOKEN }}
47+
48+
- name: Set image tags
49+
id: tags
50+
run: |
51+
SHA_TAG=sha-${GITHUB_SHA::7}
52+
echo "backend=${{ env.REGISTRY }}/${IMAGE_REPO}-backend:${SHA_TAG}" >> $GITHUB_OUTPUT
53+
echo "backend_latest=${{ env.REGISTRY }}/${IMAGE_REPO}-backend:latest" >> $GITHUB_OUTPUT
54+
echo "frontend=${{ env.REGISTRY }}/${IMAGE_REPO}-frontend:${SHA_TAG}" >> $GITHUB_OUTPUT
55+
echo "frontend_latest=${{ env.REGISTRY }}/${IMAGE_REPO}-frontend:latest" >> $GITHUB_OUTPUT
56+
57+
- name: Build and push backend
58+
uses: docker/build-push-action@v6
59+
with:
60+
context: .
61+
file: backend/Dockerfile
62+
push: true
63+
tags: |
64+
${{ steps.tags.outputs.backend }}
65+
${{ steps.tags.outputs.backend_latest }}
66+
67+
- name: Build and push frontend
68+
uses: docker/build-push-action@v6
69+
with:
70+
context: frontend
71+
file: frontend/Dockerfile
72+
push: true
73+
tags: |
74+
${{ steps.tags.outputs.frontend }}
75+
${{ steps.tags.outputs.frontend_latest }}
76+
77+
- name: Show image tags
78+
run: |
79+
echo "Backend: ${{ steps.tags.outputs.backend }}"
80+
echo "Frontend: ${{ steps.tags.outputs.frontend }}"
81+
82+
83+
84+
deploy-compose:
85+
runs-on: ubuntu-latest
86+
needs: build-and-push
87+
if: ${{ secrets.SSH_HOST != '' && secrets.SSH_USER != '' && secrets.SSH_PRIVATE_KEY != '' && (github.event_name != 'pull_request_target' || github.event.pull_request.head.repo.full_name == github.repository) }}
88+
environment: production
89+
concurrency:
90+
group: prod-deploy
91+
cancel-in-progress: false
92+
steps:
93+
- name: Select ref
94+
run: |
95+
if [ "${{ github.event_name }}" = "pull_request_target" ]; then
96+
echo "REF=${{ github.event.pull_request.head.sha }}" >> $GITHUB_ENV
97+
else
98+
echo "REF=${GITHUB_SHA}" >> $GITHUB_ENV
99+
fi
100+
- uses: actions/checkout@v4
101+
with:
102+
ref: ${{ env.REF }}
103+
104+
- name: Upload docker-compose file
105+
uses: appleboy/scp-action@v0.1.7
106+
with:
107+
host: ${{ secrets.SSH_HOST }}
108+
username: ${{ secrets.SSH_USER }}
109+
key: ${{ secrets.SSH_PRIVATE_KEY }}
110+
source: "docker-compose.prod.yml"
111+
target: "/opt/edgeflow/docker-compose.yml"
112+
strip_components: 0
113+
114+
- name: Deploy via SSH
115+
uses: appleboy/ssh-action@v1.0.3
116+
with:
117+
host: ${{ secrets.SSH_HOST }}
118+
username: ${{ secrets.SSH_USER }}
119+
key: ${{ secrets.SSH_PRIVATE_KEY }}
120+
script: |
121+
set -euo pipefail
122+
APP_DIR=/opt/edgeflow
123+
sudo mkdir -p ${APP_DIR}
124+
cd ${APP_DIR}
125+
# Write .env with image tags
126+
cat > .env << 'EOF'
127+
BACKEND_IMAGE=${{ needs.build-and-push.outputs.backend_image }}
128+
FRONTEND_IMAGE=${{ needs.build-and-push.outputs.frontend_image }}
129+
EOF
130+
# Login to GHCR if credentials provided
131+
if [ -n "${{ secrets.GHCR_USERNAME }}" ] && [ -n "${{ secrets.GHCR_TOKEN }}" ]; then
132+
echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io -u "${{ secrets.GHCR_USERNAME }}" --password-stdin
133+
fi
134+
# Ensure docker compose is available
135+
(docker compose version || docker-compose version)
136+
# Pull and run
137+
(docker compose --env-file .env -f docker-compose.yml pull || docker-compose --env-file .env -f docker-compose.yml pull)
138+
(docker compose --env-file .env -f docker-compose.yml up -d || docker-compose --env-file .env -f docker-compose.yml up -d)
139+
docker image prune -f || true
140+
# Basic health checks
141+
curl -fsS http://localhost:8000/api/health || (echo 'Backend health failed' && exit 1)
142+
curl -fsS http://localhost:3000/ || (echo 'Frontend health failed' && exit 1)

backend/Dockerfile

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
FROM python:3.11-slim
22

33
WORKDIR /app
4-
COPY requirements.txt /app/requirements.txt
4+
5+
# Install backend runtime deps
6+
COPY backend/requirements.txt /app/requirements.txt
57
RUN pip install --no-cache-dir -r /app/requirements.txt
68

7-
COPY . /app/backend
9+
# Copy backend app and core modules used by the API
10+
COPY backend /app/backend
11+
COPY edgeflowc.py /app/edgeflowc.py
12+
COPY parser /app/parser
13+
COPY optimizer.py /app/optimizer.py
814

915
EXPOSE 8000
1016
CMD ["uvicorn", "backend.app:app", "--host", "0.0.0.0", "--port", "8000"]

docker-compose.prod.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
version: '3.8'
2+
services:
3+
backend:
4+
image: ${BACKEND_IMAGE}
5+
container_name: edgeflow-backend
6+
restart: unless-stopped
7+
ports:
8+
- "8000:8000"
9+
environment:
10+
- PYTHONUNBUFFERED=1
11+
healthcheck:
12+
test: ["CMD", "curl", "-f", "http://localhost:8000/api/health"]
13+
interval: 10s
14+
timeout: 3s
15+
retries: 10
16+
networks:
17+
- edgeflow-net
18+
19+
frontend:
20+
image: ${FRONTEND_IMAGE}
21+
container_name: edgeflow-frontend
22+
restart: unless-stopped
23+
ports:
24+
- "3000:3000"
25+
depends_on:
26+
- backend
27+
networks:
28+
- edgeflow-net
29+
30+
networks:
31+
edgeflow-net:
32+
driver: bridge
33+

docker-compose.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
version: '3.8'
21
services:
32
backend:
4-
build: ./backend
3+
build:
4+
context: .
5+
dockerfile: backend/Dockerfile
56
ports:
67
- "8000:8000"
78
volumes:
@@ -17,7 +18,6 @@ services:
1718
ports:
1819
- "3000:3000"
1920
environment:
20-
- NEXT_PUBLIC_API_URL=http://localhost:8000
21+
- NEXT_PUBLIC_API_URL=http://backend:8000
2122
depends_on:
2223
- backend
23-

frontend/Dockerfile

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
1-
FROM node:18-alpine
1+
FROM node:18-alpine AS deps
22
WORKDIR /app
3-
COPY package.json /app/package.json
4-
RUN npm install --legacy-peer-deps || npm install
5-
COPY . /app
3+
COPY package.json package-lock.json ./
4+
RUN npm ci
5+
6+
FROM node:18-alpine AS builder
7+
WORKDIR /app
8+
COPY --from=deps /app/node_modules ./node_modules
9+
COPY . .
10+
RUN npm run build
11+
12+
FROM node:18-alpine AS runner
13+
WORKDIR /app
14+
ENV NODE_ENV=production
15+
COPY --from=builder /app/.next ./.next
16+
COPY --from=builder /app/node_modules ./node_modules
17+
COPY --from=builder /app/public ./public
18+
COPY --from=builder /app/package.json ./package.json
19+
COPY --from=builder /app/next.config.js ./next.config.js
620
EXPOSE 3000
7-
CMD ["npm", "run", "dev"]
21+
CMD ["npm", "run", "start"]

frontend/next.config.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
/** @type {import('next').NextConfig} */
22
const nextConfig = {
33
reactStrictMode: true,
4+
async rewrites() {
5+
// Proxy API requests to backend service when running behind Docker Compose
6+
return [
7+
{
8+
source: '/api/:path*',
9+
destination: 'http://backend:8000/api/:path*',
10+
},
11+
];
12+
},
413
};
514

615
module.exports = nextConfig;
7-

frontend/src/services/api.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import axios from 'axios';
22

3-
const api = axios.create({
4-
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000',
5-
});
3+
// Default to relative path so Next.js rewrites can proxy to backend in Docker Compose
4+
const api = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL || '' });
65

76
export const compileConfig = (config_file: string, filename: string) =>
87
api.post('/api/compile', { config_file, filename }).then(r => r.data);
@@ -19,4 +18,3 @@ export const benchmarkModels = (original_model: string, optimized_model: string)
1918
export const getVersion = () => api.get('/api/version').then(r => r.data);
2019
export const getHelp = () => api.get('/api/help').then(r => r.data);
2120
export const getHealth = () => api.get('/api/health').then(r => r.data);
22-

k8s/frontend-deployment.yaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: edgeflow-frontend
5+
labels:
6+
app: edgeflow-frontend
7+
spec:
8+
replicas: 1
9+
selector:
10+
matchLabels:
11+
app: edgeflow-frontend
12+
template:
13+
metadata:
14+
labels:
15+
app: edgeflow-frontend
16+
spec:
17+
imagePullSecrets:
18+
- name: ghcr-pull-secret
19+
containers:
20+
- name: frontend
21+
image: ghcr.io/owner/repo-frontend:latest
22+
imagePullPolicy: IfNotPresent
23+
ports:
24+
- containerPort: 3000
25+
env:
26+
- name: NEXT_PUBLIC_API_URL
27+
value: http://edgeflow-backend:8000
28+
readinessProbe:
29+
httpGet:
30+
path: /
31+
port: 3000
32+
initialDelaySeconds: 5
33+
periodSeconds: 10
34+
livenessProbe:
35+
httpGet:
36+
path: /
37+
port: 3000
38+
initialDelaySeconds: 10
39+
periodSeconds: 20

0 commit comments

Comments
 (0)