diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml deleted file mode 100644 index d17cb82..0000000 --- a/.github/workflows/cicd.yml +++ /dev/null @@ -1,131 +0,0 @@ -name: TodaySound Server CI/CD - -on: - push: - branches: - - main - - dev - workflow_dispatch: - -env: - IMAGE_NAME: todaysound-server - -jobs: - build-and-deploy: - runs-on: ubuntu-latest - - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: "17" - - - name: Cache Gradle - uses: actions/cache@v4 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - - name: Grant execute permission for gradlew - run: chmod +x ./gradlew - - - name: Build Spring Boot app (skip tests) - run: ./gradlew clean bootJar -x test -x asciidoctor - env: - SPRING_PROFILES_ACTIVE: ci - - - name: Log in to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Build and tag Docker image - run: | - GIT_SHA=${GITHUB_SHA::7} - docker build -t ${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:latest -t ${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:${GIT_SHA} . - - - name: Push Docker image - run: | - GIT_SHA=${GITHUB_SHA::7} - docker push ${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:latest - docker push ${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:${GIT_SHA} - - - name: Deploy to EC2 via SSH - uses: appleboy/ssh-action@v1.0.3 - env: - DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} - IMAGE_NAME: ${{ env.IMAGE_NAME }} - DB_URL: ${{ secrets.DB_URL }} - DB_USERNAME: ${{ secrets.DB_USERNAME }} - DB_PASSWORD: ${{ secrets.DB_PASSWORD }} - FCM_JSON: ${{ secrets.FCM_JSON }} - SENTRY_DSN: ${{ secrets.SENTRY_DSN }} - SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - with: - host: ${{ secrets.EC2_HOST }} - username: ${{ secrets.EC2_USERNAME }} - key: ${{ secrets.EC2_PRIVATE_KEY }} - script_stop: true - envs: DOCKER_USERNAME,IMAGE_NAME,DB_URL,DB_USERNAME,DB_PASSWORD,FCM_JSON, SENTRY_DSN, SENTRY_AUTH_TOKEN - script: | - set -e - - cd ~/todaysound-be - - cat > .env << EOF - DOCKER_USERNAME=${DOCKER_USERNAME} - DB_URL=${DB_URL} - DB_USERNAME=${DB_USERNAME} - DB_PASSWORD=${DB_PASSWORD} - FCM_JSON=${FCM_JSON} - SENTRY_DSN=${SENTRY_DSN} - SENTRY_AUTH_TOKEN=${SENTRY_AUTH_TOKEN} - EOF - - sudo docker-compose down - sudo docker-compose pull app - sudo docker-compose up -d - - docker image prune -f - - # Health check (local) - try basic endpoint first - echo "Waiting for app to start..." && sleep 45 - - # Show container status - APP_NAME="todaysound-server" - PORT=8080 - sudo docker ps -a | grep ${APP_NAME} || true - - # Try basic health check first (disable set -e for this section) - set +e - HEALTH_CHECK_PASSED=false - for i in {1..15}; do - HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:${PORT}/actuator/health 2>/dev/null) - if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "401" ] || [ "$HTTP_CODE" = "403" ]; then - echo "Health check passed! HTTP code: $HTTP_CODE" - HEALTH_CHECK_PASSED=true - break - else - echo "Attempt $i failed, HTTP code: $HTTP_CODE, retrying in 5s..." - sleep 5 - fi - done - set -e - - if [ "$HEALTH_CHECK_PASSED" = "false" ]; then - echo "Health check failed after 15 attempts" - sudo docker logs ${APP_NAME} --tail 50 || true - exit 1 - fi - - # Optional domain health check (non-blocking) - curl -f http://today-sound.com/ || echo "Domain check failed, but continuing..." diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..78d8c2e --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,62 @@ +name: Deploy to homeserver + +on: + push: + branches: [dev] + paths: + - "src/**" + - "build.gradle" + - "settings.gradle" + - "gradle/**" + - "Dockerfile" + - ".github/workflows/deploy.yml" + workflow_dispatch: + +concurrency: + group: deploy-homeserver-todaysound + cancel-in-progress: false + +jobs: + deploy: + runs-on: [self-hosted, homeserver] + timeout-minutes: 25 + + env: + REPO_DIR: /Users/parkparkjihyeon/homeserver/services/todaysound/repo + APP_DIR: /Users/parkparkjihyeon/homeserver/services/todaysound + + steps: + - name: Sync repo on host + run: | + cd "$REPO_DIR" + git fetch --all --prune + git reset --hard origin/dev + + - name: Build backend image + run: | + cd "$APP_DIR" + DOCKER_BUILDKIT=1 docker compose build + + - name: Roll out + run: | + cd "$APP_DIR" + docker compose up -d + docker image prune -f + + - name: Smoke test (e2e via caddy with correct Host) + run: | + for i in 1 2 3 4 5 6 7 8 9 10; do + code=$(docker exec caddy wget -S -q -O- \ + --header "Host: today-sound.com" \ + http://localhost/actuator/health 2>&1 \ + | awk '/HTTP\//{print $2; exit}') + if [ "$code" = "200" ] || [ "$code" = "401" ] || [ "$code" = "403" ]; then + echo "Backend OK ($code)" + exit 0 + fi + echo "Retry $i (code=$code)..." + sleep 6 + done + echo "Backend healthcheck failed" + docker logs todaysound-server --tail 100 || true + exit 1 diff --git a/Dockerfile b/Dockerfile index b252e82..26ab971 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,15 @@ -FROM eclipse-temurin:17-jre - -WORKDIR /app +# syntax=docker/dockerfile:1.7 -COPY build/libs/*SNAPSHOT*.jar /app/app.jar +FROM eclipse-temurin:17-jdk AS build +WORKDIR /src +COPY . . +RUN --mount=type=cache,target=/root/.gradle \ + chmod +x gradlew && \ + ./gradlew --no-daemon clean bootJar -x test -x asciidoctor +FROM eclipse-temurin:17-jre +WORKDIR /app +COPY --from=build /src/build/libs/*SNAPSHOT*.jar /app/app.jar ENV SPRING_PROFILES_ACTIVE=prod EXPOSE 8080 - ENTRYPOINT ["java","-jar","/app/app.jar"]