From aa8521fec75136f6c62d699179dfa427d519db2c Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Fri, 27 Jun 2025 23:12:06 +0300 Subject: [PATCH] add docker-compose and CI/CD workflow for web and bot services --- .github/workflows/deploy.yml | 97 ++++++++++++++++++++++++++++++++++++ apps/bot/Dockerfile | 27 ++++++++++ apps/web/Dockerfile | 29 +++++++++++ docker-compose.yml | 42 ++++++++++++++++ 4 files changed, 195 insertions(+) create mode 100644 .github/workflows/deploy.yml create mode 100644 apps/bot/Dockerfile create mode 100644 apps/web/Dockerfile create mode 100644 docker-compose.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..5cc5316 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,97 @@ +name: Build & Deploy Web & Bot + +on: + push: + branches: + - main + +jobs: + build-and-push: + name: Build and Push to Docker Hub + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push web image + uses: docker/build-push-action@v5 + with: + context: . + file: ./apps/web/Dockerfile + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/zapishis-web:latest + + - name: Build and push bot image + uses: docker/build-push-action@v5 + with: + context: . + file: ./apps/bot/Dockerfile + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/zapishis-bot:latest + + deploy: + name: Deploy to VPS + needs: build-and-push + runs-on: ubuntu-latest + environment: production + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup SSH key + run: | + mkdir -p ~/.ssh + echo "${{ secrets.VPS_SSH_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + ssh-keyscan -H ${{ secrets.VPS_HOST }} >> ~/.ssh/known_hosts + + - name: Ensure zapishis directory exists on VPS + run: | + ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }} "mkdir -p /home/${{ secrets.VPS_USER }}/zapishis" + + - name: Create .env file + run: | + echo "BOT_TOKEN=${{ secrets.BOT_TOKEN }}" > .env + echo "LOGIN_GRAPHQL=${{ secrets.LOGIN_GRAPHQL }}" >> .env + echo "PASSWORD_GRAPHQL=${{ secrets.PASSWORD_GRAPHQL }}" >> .env + echo "URL_GRAPHQL=${{ secrets.URL_GRAPHQL }}" >> .env + echo "__DEV_TELEGRAM_ID=${{ secrets.__DEV_TELEGRAM_ID }}" >> .env + echo "NODE_ENV=production" >> .env + + - name: Copy .env to VPS via SCP + uses: appleboy/scp-action@master + with: + host: ${{ secrets.VPS_HOST }} + username: ${{ secrets.VPS_USER }} + key: ${{ secrets.VPS_SSH_KEY }} + port: 22 + source: '.env' + target: '/home/${{ secrets.VPS_USER }}/zapishis/' + + - name: Copy docker-compose.yml to VPS via SCP + uses: appleboy/scp-action@master + with: + host: ${{ secrets.VPS_HOST }} + username: ${{ secrets.VPS_USER }} + key: ${{ secrets.VPS_SSH_KEY }} + port: 22 + source: 'docker-compose.yml' + target: '/home/${{ secrets.VPS_USER }}/zapishis/' + + - name: Login and deploy on VPS + run: | + ssh ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }} " + cd /home/${{ secrets.VPS_USER }}/zapishis && \ + docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_TOKEN }} && \ + docker compose pull && \ + docker compose up -d + " \ No newline at end of file diff --git a/apps/bot/Dockerfile b/apps/bot/Dockerfile new file mode 100644 index 0000000..9696417 --- /dev/null +++ b/apps/bot/Dockerfile @@ -0,0 +1,27 @@ +FROM node:20-alpine AS base + +FROM base AS builder +RUN apk update +RUN apk add --no-cache libc6-compat +WORKDIR /app +RUN corepack enable && corepack prepare yarn@4.2.2 --activate +RUN yarn global add turbo +COPY . . +RUN turbo prune bot --docker + +FROM base AS installer +RUN apk update +RUN apk add --no-cache libc6-compat +WORKDIR /app +COPY --from=builder /app/out/json/ . +RUN yarn install --immutable +COPY --from=builder /app/out/full/ . +RUN yarn turbo build + +FROM base AS runner +WORKDIR /app +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 botuser +USER botuser +COPY --from=installer --chown=botuser:nodejs /app/apps/bot/dist ./apps/bot/dist +CMD node apps/bot/dist/src/index.js \ No newline at end of file diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile new file mode 100644 index 0000000..3f96967 --- /dev/null +++ b/apps/web/Dockerfile @@ -0,0 +1,29 @@ +FROM node:20-alpine AS base + +FROM base AS builder +RUN apk update +RUN apk add --no-cache libc6-compat +WORKDIR /app +RUN corepack enable && corepack prepare yarn@4.2.2 --activate +RUN yarn global add turbo +COPY . . +RUN turbo prune web --docker + +FROM base AS installer +RUN apk update +RUN apk add --no-cache libc6-compat +WORKDIR /app +COPY --from=builder /app/out/json/ . +RUN yarn install --immutable +COPY --from=builder /app/out/full/ . +RUN yarn turbo build + +FROM base AS runner +WORKDIR /app +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs +USER nextjs +COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/standalone ./ +COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static +COPY --from=installer --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public +CMD node apps/web/server.js \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3e5e638 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,42 @@ +version: "3" + +services: + web: + container_name: web + build: + context: . + dockerfile: ./apps/web/Dockerfile + restart: always + ports: + - 3000:3000 + environment: + BOT_TOKEN: ${BOT_TOKEN} + LOGIN_GRAPHQL: ${LOGIN_GRAPHQL} + PASSWORD_GRAPHQL: ${PASSWORD_GRAPHQL} + URL_GRAPHQL: ${URL_GRAPHQL} + __DEV_TELEGRAM_ID: ${__DEV_TELEGRAM_ID} + NODE_ENV: "production" + networks: + - app + - web + bot: + container_name: bot + build: + context: . + dockerfile: ./apps/bot/Dockerfile + restart: always + environment: + BOT_TOKEN: ${BOT_TOKEN} + LOGIN_GRAPHQL: ${LOGIN_GRAPHQL} + PASSWORD_GRAPHQL: ${PASSWORD_GRAPHQL} + URL_GRAPHQL: ${URL_GRAPHQL} + NODE_ENV: "production" + networks: + - app + - web + +networks: + app: + external: true + web: + external: true \ No newline at end of file