diff --git a/.github/workflows/deploy-main.yml b/.github/workflows/deploy-main.yml new file mode 100644 index 0000000..37021f2 --- /dev/null +++ b/.github/workflows/deploy-main.yml @@ -0,0 +1,138 @@ +name: CI/CD Deploy Main + +on: + pull_request: + branches: ["main"] + paths: + - "backend/**" + - "frontend/**" + - ".github/workflows/deploy-main.yml" + + push: + branches: ["main"] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: optimesai/mallo-backend + +jobs: + backend: + name: Backend Test & Docker Build + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + + defaults: + run: + working-directory: backend + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: "21" + distribution: "temurin" + cache: "gradle" + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build and Test with Gradle + run: ./gradlew build + + - name: Login to GHCR + if: github.event_name == 'push' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and Push Docker Image + if: github.event_name == 'push' + uses: docker/build-push-action@v6 + with: + context: backend + file: backend/Dockerfile + push: true + tags: | + ghcr.io/${{ env.IMAGE_NAME }}:latest + ghcr.io/${{ env.IMAGE_NAME }}:${{ github.sha }} + labels: | + org.opencontainers.image.source=https://github.com/${{ github.repository }} + + frontend: + name: Frontend Build + runs-on: ubuntu-latest + + defaults: + run: + working-directory: frontend + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "22" + cache: "npm" + cache-dependency-path: frontend/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Build frontend + run: npm run build + + deploy: + name: Deploy to EC2 + runs-on: ubuntu-latest + needs: [backend, frontend] + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + + steps: + - name: Deploy to EC2 + uses: appleboy/ssh-action@v1 + with: + host: ${{ secrets.EC2_HOST }} + username: ${{ secrets.EC2_USER }} + key: ${{ secrets.EC2_SSH_KEY }} + script: | + set -e + + cd ~/mallo + git fetch origin main + git reset --hard origin/main + + cd ~/mallo/frontend + npm ci + npm run build + sudo rm -rf /var/www/html/* + sudo cp -r dist/* /var/www/html/ + + cd ~/mallo/backend + docker-compose pull app + docker-compose up -d --no-recreate mysql + + i=0 + until [ "$(docker inspect --format='{{.State.Health.Status}}' backend-mysql 2>/dev/null)" = "healthy" ]; do + i=$((i+1)) + if [ "$i" -ge 30 ]; then + docker-compose logs --tail=100 mysql + exit 1 + fi + echo "Waiting for mysql..." + sleep 3 + done + + docker-compose stop app || true + docker-compose rm -f app || true + docker-compose up -d --no-deps app + docker system prune -f diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml index bba6b74..4ea901f 100644 --- a/backend/docker-compose.yml +++ b/backend/docker-compose.yml @@ -1,3 +1,5 @@ +version: "2.4" + services: mysql: image: mysql:8.0 @@ -23,12 +25,9 @@ services: start_period: 5s app: - build: - context: . - dockerfile: Dockerfile - image: backend-app + image: ghcr.io/optimesai/mallo-backend:latest container_name: backend-app - restart: no + restart: always env_file: - .env - .secret.env @@ -41,4 +40,4 @@ services: - ./logs:/app/logs volumes: - mysql_data: + mysql_data: \ No newline at end of file