-
Notifications
You must be signed in to change notification settings - Fork 5
[fix/OPS-317] CI/CD 파이프라인 이벤트 분리 #66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The head ref may contain hidden characters: "fix/OPS-317-be-chore-ci-cd-\uD30C\uC774\uD504\uB77C\uC778-\uC774\uBCA4\uD2B8-\uBD84\uB9AC"
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,116 @@ | ||||||||||||||||||||||||||||||
| name: Spring CD (Test Server) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||||||||
| push: | ||||||||||||||||||||||||||||||
| branches: | ||||||||||||||||||||||||||||||
| - develop | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||
| cd-test: | ||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||
| - name: Checkout code | ||||||||||||||||||||||||||||||
| uses: actions/checkout@v4 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| - name: Deploy to Test Environment | ||||||||||||||||||||||||||||||
| uses: appleboy/[email protected] | ||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||
| host: ${{ secrets.TEST_SERVER_HOST }} | ||||||||||||||||||||||||||||||
| username: ec2-user | ||||||||||||||||||||||||||||||
| key: ${{ secrets.SSH_PRIVATE_KEY }} | ||||||||||||||||||||||||||||||
| script: | | ||||||||||||||||||||||||||||||
| # GHCR 로그인 | ||||||||||||||||||||||||||||||
| echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.repository_owner }}" --password-stdin | ||||||||||||||||||||||||||||||
| docker pull ghcr.io/${{ github.repository }}/zoopzoop:latest | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| NPM_HOST="localhost:81" | ||||||||||||||||||||||||||||||
| NPM_EMAIL="${{ secrets.NPM_ADMIN_EMAIL }}" | ||||||||||||||||||||||||||||||
| NPM_PASSWORD="${{ secrets.NPM_ADMIN_PASSWORD }}" | ||||||||||||||||||||||||||||||
| PROXY_HOST_ID="${{ secrets.NPM_PROXY_HOST_ID }}" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # NPM 토큰 | ||||||||||||||||||||||||||||||
| TOKEN=$(curl -s -X POST "http://${NPM_HOST}/api/tokens" \ | ||||||||||||||||||||||||||||||
| -H "Content-Type: application/json" \ | ||||||||||||||||||||||||||||||
| -d "{\"identity\":\"${NPM_EMAIL}\",\"secret\":\"${NPM_PASSWORD}\"}" | jq -r '.token') | ||||||||||||||||||||||||||||||
| if [ -z "$TOKEN" ] || [ "$TOKEN" == "null" ]; then | ||||||||||||||||||||||||||||||
| echo "❌ Failed to get NPM API token" | ||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| CURRENT_CONFIG=$(curl -s -H "Authorization: Bearer $TOKEN" \ | ||||||||||||||||||||||||||||||
| "http://${NPM_HOST}/api/nginx/proxy-hosts/${PROXY_HOST_ID}") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| CURRENT_TARGET=$(echo $CURRENT_CONFIG | jq -r '.[0].forward_host // .forward_host') | ||||||||||||||||||||||||||||||
| CURRENT_PORT=$(echo $CURRENT_CONFIG | jq -r '.[0].forward_port // .forward_port') | ||||||||||||||||||||||||||||||
| echo "Current NPM target: $CURRENT_TARGET:$CURRENT_PORT" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if [ "$(docker ps -q -f name=zoopzoop-blue)" ]; then | ||||||||||||||||||||||||||||||
| NEW_CONTAINER=zoopzoop-green | ||||||||||||||||||||||||||||||
| OLD_CONTAINER=zoopzoop-blue | ||||||||||||||||||||||||||||||
| NEW_PORT=8082 | ||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||
| NEW_CONTAINER=zoopzoop-blue | ||||||||||||||||||||||||||||||
| OLD_CONTAINER=zoopzoop-green | ||||||||||||||||||||||||||||||
| NEW_PORT=8081 | ||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| docker run -d --restart unless-stopped \ | ||||||||||||||||||||||||||||||
| -p $NEW_PORT:8080 \ | ||||||||||||||||||||||||||||||
| --name $NEW_CONTAINER \ | ||||||||||||||||||||||||||||||
| --network common \ | ||||||||||||||||||||||||||||||
| -e SPRING_DATASOURCE_URL="${{secrets.TEST_DB_URL}}" \ | ||||||||||||||||||||||||||||||
| -e SPRING_DATASOURCE_USERNAME="${{secrets.TEST_DB_USERNAME}}" \ | ||||||||||||||||||||||||||||||
| -e SPRING_DATASOURCE_PASSWORD="${{secrets.TEST_DB_PASSWORD}}" \ | ||||||||||||||||||||||||||||||
| ghcr.io/${{ github.repository }}/zoopzoop:latest | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # 헬스체크 | ||||||||||||||||||||||||||||||
| for i in {1..30}; do | ||||||||||||||||||||||||||||||
| if curl -s http://localhost:$NEW_PORT/actuator/health | grep -q '"status":"UP"'; then | ||||||||||||||||||||||||||||||
| echo "✅ New container is healthy!" | ||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||
| echo "Waiting for new container to be healthy..." | ||||||||||||||||||||||||||||||
| sleep 5 | ||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||
| if [ $i -eq 30 ]; then | ||||||||||||||||||||||||||||||
| echo "❌ Health check failed. Rolling back..." | ||||||||||||||||||||||||||||||
| docker stop $NEW_CONTAINER || true | ||||||||||||||||||||||||||||||
| docker rm $NEW_CONTAINER || true | ||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||
| done | ||||||||||||||||||||||||||||||
|
Comment on lines
+76
to
+82
|
||||||||||||||||||||||||||||||
| if [ $i -eq 30 ]; then | |
| echo "❌ Health check failed. Rolling back..." | |
| docker stop $NEW_CONTAINER || true | |
| docker rm $NEW_CONTAINER || true | |
| exit 1 | |
| fi | |
| done | |
| done | |
| # If the loop did not break, health check failed | |
| if ! curl -s http://localhost:$NEW_PORT/actuator/health | grep -q '"status":"UP"'; then | |
| echo "❌ Health check failed. Rolling back..." | |
| docker stop $NEW_CONTAINER || true | |
| docker rm $NEW_CONTAINER || true | |
| exit 1 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| # 워크플로우 이름 | ||
| name: Spring CI/CD Pipeline (Develop) | ||
|
|
||
| # develop 브랜치 PR에서만 실행 | ||
| on: | ||
| pull_request: | ||
| branches: | ||
| - develop | ||
| paths: | ||
| - 'src/**' | ||
| - 'build.gradle*' | ||
| - 'settings.gradle*' | ||
| - 'gradle/**' | ||
| - 'Dockerfile' | ||
| - '.github/workflows/**' | ||
|
|
||
| jobs: | ||
| # ================================== | ||
| # CI: Test and Build and Push Docker Image | ||
| # ================================== | ||
| ci: | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| # 1. 소스 코드 체크아웃 | ||
| - name: Checkout source code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| # 2. JDK 21 설치 | ||
| - name: Set up JDK 21 | ||
| uses: actions/setup-java@v4 | ||
| with: | ||
| java-version: '21' | ||
| distribution: 'temurin' | ||
|
|
||
| # 3. Gradle 캐시 설정 | ||
| - name: Cache Gradle packages | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: | | ||
| ~/.gradle/caches | ||
| ~/.gradle/wrapper | ||
| key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-gradle- | ||
|
|
||
| # 4. gradlew 실행 권한 부여 | ||
| - name: Grant execute permission for gradlew | ||
| run: chmod +x ./gradlew | ||
|
|
||
| # 5. application-secrets.yml 생성 | ||
| - name: Generate application-secrets.yml | ||
| run: | | ||
| mkdir -p src/main/resources | ||
| echo "${{ secrets.APPLICATION_SECRET_YML }}" > src/main/resources/application-secrets.yml | ||
| echo "OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}" >> src/main/resources/application-secrets.yml | ||
|
|
||
| # 6. application-secrets-server.yml 생성 | ||
| - name: Generate application-secrets-server.yml | ||
| run: | | ||
| mkdir -p src/main/resources | ||
| echo "${{ secrets.APPLICATION_SECRET_SERVER_YML }}" > src/main/resources/application-secrets-server.yml | ||
| echo "OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}" >> src/main/resources/application-secrets-server.yml | ||
|
|
||
| # 7. Gradle 테스트 실행 | ||
| - name: Test with Gradle | ||
| run: ./gradlew test | ||
|
|
||
| # 8. 테스트 결과 요약 출력 | ||
| - name: Show test results | ||
| run: | | ||
| echo "==== Test Results ====" | ||
| if compgen -G "build/test-results/test/TEST-*.xml" > /dev/null; then | ||
| total=$(grep '<testcase ' build/test-results/test/TEST-*.xml | wc -l) | ||
| failed=$(grep -h '<failure' build/test-results/test/TEST-*.xml | wc -l) | ||
| error=$(grep -h '<error' build/test-results/test/TEST-*.xml | wc -l) | ||
| passed=$((total - failed - error)) | ||
| echo "Total tests: $total" | ||
| echo "Passed tests: $passed" | ||
| echo "Failed tests: $failed" | ||
| echo "Errored tests: $error" | ||
| else | ||
| echo "No test results found." | ||
| fi | ||
|
|
||
| # 9. Gradle 빌드 실행 (테스트 성공 시) | ||
| - name: Build with Gradle | ||
| run: ./gradlew build | ||
|
|
||
| # 10. GHCR 로그인 | ||
| - name: Log in to GHCR | ||
| uses: docker/login-action@v2 | ||
| with: | ||
| registry: ghcr.io | ||
| username: ${{ github.repository_owner }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| # 11. Docker 이미지 빌드 & 푸시 | ||
| - name: Build & Push Docker Image | ||
| run: | | ||
| IMAGE_NAME=ghcr.io/${{ github.repository }}/zoopzoop | ||
|
|
||
| docker build -t $IMAGE_NAME:${GITHUB_SHA} . | ||
| docker tag $IMAGE_NAME:${GITHUB_SHA} $IMAGE_NAME:latest | ||
|
|
||
| docker push $IMAGE_NAME:${GITHUB_SHA} | ||
| docker push $IMAGE_NAME:latest | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent spacing in GitHub Actions expressions. Use consistent spacing like
${{ secrets.TEST_DB_URL }}with spaces around the expression.