diff --git a/.github/workflows/boolock-dev-cicd.yml b/.github/workflows/boolock-dev-cicd.yml index 51cd35b..b4b6902 100644 --- a/.github/workflows/boolock-dev-cicd.yml +++ b/.github/workflows/boolock-dev-cicd.yml @@ -1,8 +1,10 @@ -name: ci/cd action +name: dev ci/cd action on: push: - branches: ['main'] + branches: ['dev'] + pull_request: + branches: ['dev'] jobs: build: @@ -10,110 +12,91 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '20.x' - - - name: Install Pnpm - run: npm install -g pnpm - - name: Package install with pnpm - run: pnpm install:all + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + buildkitd-flags: --debug - name: Set BE .env run: | - echo "MONGO_URI=${{ secrets.MONGO_URI }}" > apps/server/.env - echo "SSH_HOST=${{ secrets.SSH_HOST }}" >> apps/server/.env - echo "SSH_PORT=${{ secrets.SSH_PORT }}" >> apps/server/.env - echo "SSH_USER=${{ secrets.SSH_USER }}" >> apps/server/.env - echo "SSH_PASSWORD=${{ secrets.SSH_PASSWORD }}" >> apps/server/.env - echo "SSH_DATABASE_HOST=${{ secrets.SSH_DATABASE_HOST }}" >> apps/server/.env - echo "SSH_DATABASE_PORT=${{ secrets.SSH_DATABASE_PORT }}" >> apps/server/.env - echo "LOCAL_PORT=${{ secrets.LOCAL_PORT }}" >> apps/server/.env - echo "IS_LOCAL=true" >> apps/server/.env - echo "SERVER_CORS_ACCEPT=http://localhost:5173" >> apps/server/.env + echo "MONGO_URI=${{ secrets.TEST_MONGO_URI }}" > apps/server/.env + echo "IS_LOCAL=false" >> apps/server/.env + echo "SERVER_CORS_ACCEPT=${{ secrets.TEST_SERVER_CORS_ACCEPT }}" >> apps/server/.env echo "S3_ACCESS_KEY=${{ secrets.S3_ACCESS_KEY }}" >> apps/server/.env echo "S3_SECRET_KEY=${{ secrets.S3_SECRET_KEY }}" >> apps/server/.env echo "S3_BUCKET_NAME=${{ secrets.S3_BUCKET_NAME }}" >> apps/server/.env - + echo "NODE_ENV=production" >> apps/server/.env + - name: Set FE .env run: | - echo "VITE_SERVER_URL=http://localhost:3000" > apps/client/.env + echo "VITE_SERVER_URL=${{ secrets.DEPLOY_VITE_SERVER_URL }}" > apps/client/.env echo "VITE_MIXPANEL_TOKEN=${{ secrets.VITE_MIXPANEL_TOKEN }}" >> apps/client/.env echo "VITE_STATIC_STORAGE_URL=${{ secrets.VITE_STATIC_STORAGE_URL }}" >> apps/client/.env - - name: Set Nginx SSL files - run: | - mkdir -p apps/client/ssl - echo "${{ secrets.SSL_FULLCHAIN }}" > apps/client/ssl/fullchain.pem - echo "${{ secrets.SSL_PRIVKEY }}" > apps/client/ssl/privkey.pem - - - name: Buld Frontend - run: | - cd apps/client - pnpm run build - - - name: Build Backend - run: | - cd apps/server - pnpm run swagger-auto - pnpm run build - - - name: Build base image - run: | - docker build . --file Dockerfile.base --tag ${{ secrets.DOCKERHUB_USERNAME }}/boolock_base_test:1.0 - docker tag ${{ secrets.DOCKERHUB_USERNAME }}/boolock_base_test:1.0 base-image - - - name: Build frontend and backend images - run: | - docker build . --file apps/client/Dockerfile --tag ${{ secrets.DOCKERHUB_USERNAME }}/boolock_client_test:1.0 - docker build . --file apps/server/Dockerfile --tag ${{ secrets.DOCKERHUB_USERNAME }}/boolock_server_test:1.0 - - name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_PASSWORD }} + password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }} - - name: Docker Hub push - run: | - docker push ${{ secrets.DOCKERHUB_USERNAME }}/boolock_base_test:1.0 - docker push ${{ secrets.DOCKERHUB_USERNAME }}/boolock_client_test:1.0 - docker push ${{ secrets.DOCKERHUB_USERNAME }}/boolock_server_test:1.0 + - name: Build and Push Base Image + uses: docker/build-push-action@v4 + with: + context: . + file: Dockerfile.base + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/base-image-test:latest + cache-from: type=gha,scope=base-image-test + cache-to: type=gha,mode=max,scope=base-image-test + + - name: Build and Push Frontend Image + uses: docker/build-push-action@v4 + with: + context: . + file: apps/client/Dockerfile.test + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/boolock_client_test:latest + cache-from: type=gha,scope=frontend-test + cache-to: type=gha,mode=max,scope=frontend-test + build-args: | + DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }} + TYPE=test + + - name: Build and Push Backend Image + uses: docker/build-push-action@v4 + with: + context: . + file: apps/server/Dockerfile + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/boolock_server_test:latest + cache-from: type=gha,scope=backend-test + cache-to: type=gha,mode=max,scope=backend-test + build-args: | + DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }} + TYPE=test deploy: + if: github.event_name == 'push' needs: build runs-on: ubuntu-latest steps: - - name: Login to Docker Hub - run: sudo docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_PASSWORD }} - - name: Deploy with docker uses: appleboy/ssh-action@v0.1.6 with: - host: ${{ secrets.SSH_HOST }} - username: ${{ secrets.SSH_USER }} - password: ${{ secrets.SSH_PASSWORD }} - port: ${{ secrets.SSH_PORT }} + host: ${{ secrets.TEST_SSH_HOST }} + username: ${{ secrets.TEST_SSH_USER }} + password: ${{ secrets.TEST_SSH_PASSWORD }} + port: ${{ secrets.TEST_SSH_PORT }} script: | cd boolock/refactor-web31-BooLock - git checkout main - git pull origin main - - echo "MONGO_URI=${{ secrets.DEPLOY_MONGO_URI }}" > apps/server/.env - echo "IS_LOCAL=false" >> apps/server/.env - echo "SERVER_CORS_ACCEPT=${{ secrets.DEPLOY_SERVER_CORS_ACCEPT }}" >> apps/server/.env - echo "S3_ACCESS_KEY=${{ secrets.S3_ACCESS_KEY }}" >> apps/server/.env - echo "S3_SECRET_KEY=${{ secrets.S3_SECRET_KEY }}" >> apps/server/.env - echo "S3_BUCKET_NAME=${{ secrets.S3_BUCKET_NAME }}" >> apps/server/.env - echo "NODE_ENV=production" >> apps/server/.env + git fetch origin + git checkout dev + git pull origin dev - echo "VITE_SERVER_URL=${{ secrets.DEPLOY_VITE_SERVER_URL }}" > apps/client/.env - echo "VITE_MIXPANEL_TOKEN=${{ secrets.VITE_MIXPANEL_TOKEN }}" >> apps/client/.env - echo "VITE_STATIC_STORAGE_URL=${{ secrets.VITE_STATIC_STORAGE_URL }}" >> apps/client/.env + echo "DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }}" > .env + echo "TYPE=test" >> .env + echo "${{ secrets.DOCKERHUB_ACCESS_TOKEN }}" | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin sudo docker compose pull - sudo docker compose down - sudo docker compose up -d --build - sudo docker image prune -f + sudo docker compose up -d --force-recreate --remove-orphans diff --git a/.github/workflows/boolock-main-cicd.yml b/.github/workflows/boolock-main-cicd.yml new file mode 100644 index 0000000..3a16639 --- /dev/null +++ b/.github/workflows/boolock-main-cicd.yml @@ -0,0 +1,108 @@ +name: main ci/cd action + +on: + push: + branches: ['main'] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + buildkitd-flags: --debug + + - name: Set BE .env + run: | + echo "MONGO_URI=${{ secrets.DEPLOY_MONGO_URI }}" > apps/server/.env + echo "IS_LOCAL=false" >> apps/server/.env + echo "SERVER_CORS_ACCEPT=${{ secrets.DEPLOY_SERVER_CORS_ACCEPT }}" >> apps/server/.env + echo "S3_ACCESS_KEY=${{ secrets.S3_ACCESS_KEY }}" >> apps/server/.env + echo "S3_SECRET_KEY=${{ secrets.S3_SECRET_KEY }}" >> apps/server/.env + echo "S3_BUCKET_NAME=${{ secrets.S3_BUCKET_NAME }}" >> apps/server/.env + echo "NODE_ENV=production" >> apps/server/.env + + - name: Set FE .env + run: | + echo "VITE_SERVER_URL=${{ secrets.DEPLOY_VITE_SERVER_URL }}" > apps/client/.env + echo "VITE_MIXPANEL_TOKEN=${{ secrets.VITE_MIXPANEL_TOKEN }}" >> apps/client/.env + echo "VITE_STATIC_STORAGE_URL=${{ secrets.VITE_STATIC_STORAGE_URL }}" >> apps/client/.env + + - name: Set Nginx SSL files + run: | + mkdir -p apps/client/ssl + echo "${{ secrets.SSL_FULLCHAIN }}" > apps/client/ssl/fullchain.pem + echo "${{ secrets.SSL_PRIVKEY }}" > apps/client/ssl/privkey.pem + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }} + + - name: Build and Push Base Image + uses: docker/build-push-action@v4 + with: + context: . + file: Dockerfile.base + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/base-image-prod:latest + cache-from: type=gha,scope=base-image-prod + cache-to: type=gha,mode=max,scope=base-image-prod + + - name: Build and Push Frontend Image + uses: docker/build-push-action@v4 + with: + context: . + file: apps/client/Dockerfile + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/boolock_client_prod:latest + cache-from: type=gha,scope=frontend + cache-to: type=gha,mode=max,scope=frontend + build-args: | + DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }} + TYPE=prod + + - name: Build and Push Backend Image + uses: docker/build-push-action@v4 + with: + context: . + file: apps/server/Dockerfile + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/boolock_server_prod:latest + cache-from: type=gha,scope=backend + cache-to: type=gha,mode=max,scope=backend + build-args: | + DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }} + TYPE=prod + + deploy: + needs: build + runs-on: ubuntu-latest + steps: + - name: Login to Docker Hub + run: sudo docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Deploy with docker + uses: appleboy/ssh-action@v0.1.6 + with: + host: ${{ secrets.SSH_HOST }} + username: ${{ secrets.SSH_USER }} + password: ${{ secrets.SSH_PASSWORD }} + port: ${{ secrets.SSH_PORT }} + script: | + cd boolock/refactor-web31-BooLock + git fetch origin + git checkout main + git pull origin main + + echo "DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }}" > .env + echo "TYPE=prod" >> .env + echo "${{ secrets.DOCKERHUB_ACCESS_TOKEN }}" | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin + + sudo docker compose pull + sudo docker compose up -d --force-recreate --remove-orphans \ No newline at end of file diff --git a/apps/client/Dockerfile b/apps/client/Dockerfile index 6f9915d..c4b6118 100644 --- a/apps/client/Dockerfile +++ b/apps/client/Dockerfile @@ -1,4 +1,6 @@ -FROM base-image AS frontend-build +ARG DOCKERHUB_USERNAME +ARG TYPE +FROM ${DOCKERHUB_USERNAME}/base-image-${TYPE} AS frontend-build WORKDIR /app/apps/client COPY ./apps/client . @@ -7,7 +9,7 @@ RUN pnpm install --offline --frozen-lockfile &&\ FROM nginx:alpine AS frontend -COPY /apps/client/nginx.conf /etc/nginx/conf.d/default.conf +COPY /apps/client/production/nginx.conf /etc/nginx/conf.d/default.conf COPY /apps/client/ssl /etc/nginx/ssl RUN chmod 644 /etc/nginx/ssl/fullchain.pem &&\ chmod 600 /etc/nginx/ssl/privkey.pem &&\ diff --git a/apps/client/Dockerfile.test b/apps/client/Dockerfile.test new file mode 100644 index 0000000..1270603 --- /dev/null +++ b/apps/client/Dockerfile.test @@ -0,0 +1,19 @@ +ARG DOCKERHUB_USERNAME +ARG TYPE +FROM ${DOCKERHUB_USERNAME}/base-image-${TYPE} AS frontend-build + +WORKDIR /app/apps/client +COPY ./apps/client . +RUN pnpm install --offline --frozen-lockfile &&\ + pnpm run build + +FROM nginx:alpine AS frontend + +COPY /apps/client/test/nginx.conf /etc/nginx/conf.d/default.conf +RUN chown -R nginx:nginx /usr/share/nginx/html + +COPY --from=frontend-build /app/apps/client/dist /usr/share/nginx/html/ +RUN chmod -R 755 /usr/share/nginx/html + +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/apps/client/nginx.conf b/apps/client/production/nginx.conf similarity index 100% rename from apps/client/nginx.conf rename to apps/client/production/nginx.conf diff --git a/apps/client/test/nginx.conf b/apps/client/test/nginx.conf new file mode 100644 index 0000000..2b4f390 --- /dev/null +++ b/apps/client/test/nginx.conf @@ -0,0 +1,28 @@ +server { + listen 80; + + location / { + root /usr/share/nginx/html; + try_files $uri $uri/ /index.html; + } + + location /api/ { + proxy_pass http://backend:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_cache_bypass $http_upgrade; + proxy_set_header X-Forwarded-Proto $scheme; + } + + add_header X-Content-Type-Options nosniff; + add_header X-Frame-Options SAMEORIGIN; + add_header X-XSS-Protection "1; mode=block"; + + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + gzip_min_length 256; +} diff --git a/apps/server/Dockerfile b/apps/server/Dockerfile index 13d8ccd..116e845 100644 --- a/apps/server/Dockerfile +++ b/apps/server/Dockerfile @@ -1,4 +1,6 @@ -FROM base-image AS backend-build +ARG DOCKERHUB_USERNAME +ARG TYPE +FROM ${DOCKERHUB_USERNAME}/base-image-${TYPE} AS backend-build WORKDIR /app/apps/server COPY ./apps/server . diff --git a/docker-compose.yml b/docker-compose.yml index 391d29c..107bfea 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,30 +1,13 @@ services: - base: - build: - context: . - dockerfile: Dockerfile.base - image: base-image - command: ['echo', 'Base image built'] - frontend: - build: - context: . - dockerfile: ./apps/client/Dockerfile - cache_from: - - base-image - depends_on: - - base + image: ${DOCKERHUB_USERNAME}/boolock_client_${TYPE}:latest + pull_policy: always ports: - '80:80' - - '443:443' + - "${TYPE:-} == 'prod' ? '443:443' : ''" backend: - build: - context: . - dockerfile: ./apps/server/Dockerfile - cache_from: - - base-image - depends_on: - - base + image: ${DOCKERHUB_USERNAME}/boolock_server_${TYPE}:latest + pull_policy: always ports: - '3000:3000'