From 1f08a80c728e432298cf88635b85a3000bd3821c Mon Sep 17 00:00:00 2001 From: Mohammad Sajad Pourajam Date: Sat, 21 Mar 2026 17:33:39 +0330 Subject: [PATCH] CI/CD --- .dockerignore | 70 +++-------------- .gitea/workflows/frontend.yml | 17 ++++- .github/workflows/frontend.yml | 134 +++++++++++++++------------------ Dockerfile | 99 +++++++++++------------- docker-compose-prod.yml | 13 ++++ 5 files changed, 145 insertions(+), 188 deletions(-) create mode 100644 docker-compose-prod.yml diff --git a/.dockerignore b/.dockerignore index a3e5323..0c29ff5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,65 +1,19 @@ -# Dependencies node_modules -.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# Testing -coverage - -# Next.js .next -out -build - -# Production -dist - -# Misc -.DS_Store -*.pem - -# Debug +.git +.github +.env +.env.local +.env.*.local npm-debug.log* yarn-debug.log* yarn-error.log* -.pnpm-debug.log* - -# Local env files -.env -.env*.local - -# Vercel -.vercel - -# TypeScript -*.tsbuildinfo -next-env.d.ts - -# IDE -.vscode -.idea -*.swp -*.swo -*~ - -# Git -.git -.gitignore - -# Docker +.DS_Store +*.pem +coverage +out +build Dockerfile -.dockerignore docker-compose*.yml - - - - - - - - +.dockerignore +README.md diff --git a/.gitea/workflows/frontend.yml b/.gitea/workflows/frontend.yml index 2f8bced..a5b77f7 100644 --- a/.gitea/workflows/frontend.yml +++ b/.gitea/workflows/frontend.yml @@ -86,6 +86,19 @@ jobs: run: | npm run build --if-present - - name: Run Lint + + - name: Setup SSH key run: | - npm run lint --if-present + mkdir -p ~/.ssh + echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + ssh-keyscan -p ${{ secrets.SERVER_SSH_PORT }} -H ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts + + - name: Deploy + run: | + ssh ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }} -p ${{ secrets.SERVER_SSH_PORT }} << 'EOF' + cd application/Frontend + git pull origin production + docker compose -f docker-compose-prod.yml down + docker compose -f docker-compose-prod.yml up -d --build + EOF diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index c7acf6b..9baff00 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -1,72 +1,62 @@ - name: Frontend CI/CD - - on: - push: - branches: [main] - paths: - - 'frontend/**' - - 'frontend/.github/workflows/frontend.yml' - pull_request: - branches: [main] - paths: - - 'frontend/**' - - 'frontend/.github/workflows/frontend.yml' - - defaults: - run: - working-directory: frontend - - jobs: - build: - name: Build, Lint & Test - runs-on: ubuntu-latest - strategy: - fail-fast: true - matrix: - node-version: ['20'] - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - - name: Cache npm dependencies - uses: actions/cache@v4 - with: - path: ~/.npm - key: ${{ runner.os }}-npm-frontend-${{ hashFiles('frontend/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-npm-frontend- - - - name: Install dependencies - run: npm ci - - - name: Build - run: npm run build - - - name: Lint - run: npm run lint --if-present - - - name: Test - run: npm test --if-present - - deploy: - name: Deploy Frontend - needs: build - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - runs-on: ubuntu-latest - steps: - - name: Deploy via SSH - uses: appleboy/ssh-action@v1 - with: - host: ${{ secrets.SSH_HOST }} - username: ${{ secrets.SSH_USER }} - port: ${{ secrets.SSH_PORT }} - key: ${{ secrets.SSH_PRIVATE_KEY }} - script: | - cd /opt/myproject/frontend - git pull origin main - sudo systemctl restart frontend +name: Frontend CI/CD + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + name: Build & Lint + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Cache npm dependencies + uses: actions/cache@v4 + with: + path: ~/.npm + key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }} + restore-keys: | + ${{ runner.os }}-npm- + + - name: Install dependencies + run: npm ci + + - name: Lint + run: npm run lint --if-present + + - name: Build + run: npm run build + + deploy: + name: Deploy Frontend + needs: build + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup SSH key + run: | + mkdir -p ~/.ssh + echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + ssh-keyscan -p ${{ secrets.SERVER_SSH_PORT }} -H ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts + + - name: Deploy + run: | + ssh ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }} -p ${{ secrets.SERVER_SSH_PORT }} << 'EOF' + cd ${{ secrets.PROJECT_PATH }}/frontend + git pull origin main + docker compose -f docker-compose-prod.yml down + docker compose -f docker-compose-prod.yml up -d --build + EOF diff --git a/Dockerfile b/Dockerfile index fb90974..10f60d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,80 +1,67 @@ -# Stage 1: Dependencies -FROM node:20-alpine AS deps -RUN npm config set registry https://package-mirror.liara.ir/repository/npm/ -# Install OpenSSL for Prisma -RUN apk add --no-cache openssl libc6-compat +FROM docker.iranserver.com/node:20-bookworm-slim AS base + +ENV NEXT_TELEMETRY_DISABLED=1 + +# Debian mirrors (Iranian) +RUN rm -f /etc/apt/sources.list /etc/apt/sources.list.d/* && \ + printf '%s\n' \ + 'deb https://mirror-linux.runflare.com/debian/ bookworm main contrib non-free non-free-firmware' \ + 'deb https://mirror-linux.runflare.com/debian/ bookworm-updates main contrib non-free non-free-firmware' \ + 'deb https://mirror-linux.runflare.com/debian-security/ bookworm-security main contrib non-free non-free-firmware' \ + '' \ + 'deb [trusted=yes] https://mirror2.chabokan.net/debian bookworm main contrib non-free non-free-firmware' \ + 'deb [trusted=yes] https://mirror2.chabokan.net/debian-security bookworm-security main contrib non-free non-free-firmware' \ + '' \ + 'deb http://mirror.iranserver.com/debian/ bookworm main contrib non-free non-free-firmware' \ + 'deb-src http://mirror.iranserver.com/debian/ bookworm main contrib non-free non-free-firmware' \ + > /etc/apt/sources.list + +# npm mirrors (Iranian) +RUN npm config set registry https://package-mirror.liara.ir/repository/npm/ && \ + npm config set @runflare:registry https://mirror-npm.runflare.com/ && \ + npm config set @chabokan:registry https://mirror2.chabokan.net/npm/ && \ + npm config set strict-ssl false && \ + npm config set fetch-retries 5 && \ + npm config set fetch-retry-mintimeout 20000 + +# ---- deps stage ---- +FROM base AS deps + WORKDIR /app -# Copy package files -COPY package.json ./ +COPY package.json package-lock.json ./ +RUN npm ci -# Install dependencies (skip scripts to avoid postinstall which needs source code) -RUN npm install --ignore-scripts +# ---- build stage ---- +FROM base AS builder -# Stage 2: Builder -FROM node:20-alpine AS builder -RUN npm config set registry https://package-mirror.liara.ir/repository/npm/ -# Install OpenSSL for Prisma -RUN apk add --no-cache openssl libc6-compat WORKDIR /app -# Copy dependencies from deps stage COPY --from=deps /app/node_modules ./node_modules -COPY --from=deps /app/package.json ./package.json - -# Copy source code COPY . . -# Set environment variables for build -ARG NODE_ENV=production -ARG NEXT_PUBLIC_APP_URL -ARG NEXT_PUBLIC_DOCS_URL -ARG API_URL -ARG BASEPATH -ARG MAPBOX_ACCESS_TOKEN -ARG DATABASE_URL +ENV NODE_ENV=production -ENV NODE_ENV=$NODE_ENV -ENV NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL -ENV NEXT_PUBLIC_DOCS_URL=$NEXT_PUBLIC_DOCS_URL -ENV API_URL=$API_URL -ENV BASEPATH=$BASEPATH -ENV MAPBOX_ACCESS_TOKEN=$MAPBOX_ACCESS_TOKEN -ENV DATABASE_URL=$DATABASE_URL - -# Generate Prisma Client and build icons (postinstall script) -# These commands need the source code to be present -RUN npm run build:icons - -# Build the application RUN npm run build -# Stage 3: Runner -FROM node:20-alpine AS runner -RUN npm config set registry https://package-mirror.liara.ir/repository/npm/ +# ---- production stage ---- +FROM base AS runner + WORKDIR /app ENV NODE_ENV=production -ENV PORT=9031 -ENV NEXT_TELEMETRY_DISABLED=1 +ENV PORT=3000 +ENV HOSTNAME=0.0.0.0 -# Install OpenSSL for Prisma runtime -RUN apk add --no-cache openssl libc6-compat +RUN addgroup --system --gid 1001 nodejs && \ + adduser --system --uid 1001 nextjs -# Create a non-root user -RUN addgroup --system --gid 1001 nodejs -RUN adduser --system --uid 1001 nextjs - -# Copy necessary files from standalone build +COPY --from=builder /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static -COPY --from=builder --chown=nextjs:nodejs /app/public ./public USER nextjs -EXPOSE 9031 - -ENV PORT=9031 -ENV HOSTNAME="0.0.0.0" +EXPOSE 3000 CMD ["node", "server.js"] diff --git a/docker-compose-prod.yml b/docker-compose-prod.yml new file mode 100644 index 0000000..2bfecfd --- /dev/null +++ b/docker-compose-prod.yml @@ -0,0 +1,13 @@ +services: + frontend: + build: + context: . + dockerfile: Dockerfile + container_name: croplogic-frontend + restart: unless-stopped + ports: + - "${PORT:-9031}:3000" + env_file: + - .env + environment: + - NODE_ENV=production