diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 000000000..7ed56b4ba --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,44 @@ +name: CD - Production (AWS + Azure) + +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + # ------------------------- + # Deploy to AWS + # ------------------------- + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ap-south-1 + + - name: Terraform Apply - AWS + run: | + cd infra/aws + terraform init + terraform apply -auto-approve + + # ------------------------- + # Deploy to Azure + # ------------------------- + - name: Azure Login + uses: azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Terraform Apply - Azure + run: | + cd infra/azure + terraform init + terraform apply -auto-approve diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..661602c9f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,114 @@ +name: CI - Develop (AWS + Azure) + +on: + push: + branches: + - develop + +# ✅ REQUIRED PERMISSIONS +permissions: + contents: read + +jobs: + build-test-push: + runs-on: ubuntu-latest + + env: + AWS_REGION: ${{ secrets.AWS_REGION }} + AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }} + AZURE_ACR_NAME: ${{ secrets.AZURE_ACR_NAME }} + + steps: + # ------------------------- + # Checkout + # ------------------------- + - name: Checkout code + uses: actions/checkout@v4 + + # ------------------------- + # Backend setup + # ------------------------- + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install backend dependencies + run: | + cd backend + pip install -r requirements.txt + + # ------------------------- + # Frontend setup + # ------------------------- + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Install frontend dependencies + run: | + cd frontend + npm install + + # ------------------------- + # Docker Build + # ------------------------- + - name: Build backend image + run: docker build -t backend:${{ github.sha }} ./backend + + - name: Build frontend image + run: docker build -t frontend:${{ github.sha }} ./frontend + + # ================================================= + # AWS ECR + # ================================================= + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Login to AWS ECR + run: | + aws ecr get-login-password --region $AWS_REGION \ + | docker login --username AWS --password-stdin \ + $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com + + - name: Ensure ECR repositories exist + run: | + for repo in backend frontend; do + aws ecr describe-repositories --repository-names $repo \ + || aws ecr create-repository --repository-name $repo + done + + - name: Push images to AWS ECR + run: | + for repo in backend frontend; do + docker tag $repo:${{ github.sha }} \ + $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$repo:${{ github.sha }} + docker push \ + $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$repo:${{ github.sha }} + done + + # ================================================= + # Azure ACR (FIXED) + # ================================================= + - name: Azure Login + uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Login to Azure ACR + run: | + az acr login --name $AZURE_ACR_NAME + + - name: Push images to Azure ACR + run: | + for repo in backend frontend; do + docker tag $repo:${{ github.sha }} \ + $AZURE_ACR_NAME.azurecr.io/$repo:${{ github.sha }} + docker push \ + $AZURE_ACR_NAME.azurecr.io/$repo:${{ github.sha }} + done diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..4ae1b400a --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Backend +backend/venv/ +backend/app/__pycache__/ +backend/*.pyc + +# Frontend +frontend/node_modules/ +frontend/.next/ + +# OS / editor junk +.DS_Store +*.log diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 000000000..e493522a2 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,44 @@ +# ========================= +# Stage 1: Build stage +# ========================= +FROM python:3.11-slim AS builder + +# Set working directory +WORKDIR /app + +# Install build dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir --upgrade pip \ + && pip install --no-cache-dir -r requirements.txt + +# Copy application code +COPY app app + +# ========================= +# Stage 2: Runtime stage +# ========================= +FROM python:3.11-slim + +# Create non-root user (security best practice) +RUN useradd -m appuser + +# Set working directory +WORKDIR /app + +# Copy only required files from builder stage +COPY --from=builder /usr/local/lib/python3.11 /usr/local/lib/python3.11 +COPY --from=builder /usr/local/bin /usr/local/bin +COPY app app + +# Switch to non-root user +USER appuser + +# Environment-based configuration +ENV HOST=0.0.0.0 +ENV PORT=8000 + +# Expose application port +EXPOSE 8000 + +# Start FastAPI using Uvicorn +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 000000000..bf8192e4f --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,47 @@ +# ========================= +# Stage 1: Build stage +# ========================= +FROM node:18-alpine AS builder + +# Set working directory +WORKDIR /app + +# Copy package files first (better caching) +COPY package.json package-lock.json ./ +RUN npm ci + +# Copy application source +COPY . . + +# Build Next.js app +RUN npm run build + +# ========================= +# Stage 2: Production stage +# ========================= +FROM node:18-alpine + +# Create non-root user +RUN addgroup -S appgroup && adduser -S appuser -G appgroup + +# Set working directory +WORKDIR /app + +# Copy only required build output +COPY --from=builder /app/package.json ./ +COPY --from=builder /app/package-lock.json ./ +COPY --from=builder /app/.next ./.next +# COPY --from=builder /app/public ./public +COPY --from=builder /app/node_modules ./node_modules + +# Switch to non-root user +USER appuser + +# Environment variable for backend URL +ENV NEXT_PUBLIC_API_URL=http://localhost:8000 + +# Expose Next.js port +EXPOSE 3000 + +# Start Next.js in production mode +CMD ["npm", "start"]