name: Backend Services CI/CD on: push: branches: [ main, develop ] paths: - 'user/**' - 'meeting/**' - 'stt/**' - 'ai/**' - 'notification/**' - 'common/**' - '.github/**' pull_request: branches: [ main ] workflow_dispatch: inputs: ENVIRONMENT: description: 'Target environment' required: true default: 'dev' type: choice options: - dev - staging - prod SKIP_SONARQUBE: description: 'Skip SonarQube Analysis' required: false default: 'true' type: choice options: - 'true' - 'false' env: REGISTRY: acrdigitalgarage02.azurecr.io IMAGE_ORG: hgzero RESOURCE_GROUP: rg-digitalgarage-02 AKS_CLUSTER: aks-digitalgarage-02 NAMESPACE: hgzero jobs: build: name: Build and Test runs-on: ubuntu-latest outputs: image_tag: ${{ steps.set_outputs.outputs.image_tag }} environment: ${{ steps.set_outputs.outputs.environment }} steps: - name: Check out code uses: actions/checkout@v4 - name: Set up JDK 21 uses: actions/setup-java@v3 with: java-version: '21' distribution: 'temurin' cache: 'gradle' - name: Determine environment id: determine_env run: | # Use input parameter or default to 'dev' ENVIRONMENT="${{ github.event.inputs.ENVIRONMENT || 'dev' }}" echo "environment=$ENVIRONMENT" >> $GITHUB_OUTPUT - name: Load environment variables id: env_vars run: | ENV=${{ steps.determine_env.outputs.environment }} # Initialize variables with defaults REGISTRY="acrdigitalgarage02.azurecr.io" IMAGE_ORG="hgzero" RESOURCE_GROUP="rg-digitalgarage-02" AKS_CLUSTER="aks-digitalgarage-02" NAMESPACE="hgzero" # Read environment variables from .github/config file if [[ -f ".github/config/deploy_env_vars_${ENV}" ]]; then while IFS= read -r line || [[ -n "$line" ]]; do # Skip comments and empty lines [[ "$line" =~ ^#.*$ ]] && continue [[ -z "$line" ]] && continue # Extract key-value pairs key=$(echo "$line" | cut -d '=' -f1) value=$(echo "$line" | cut -d '=' -f2-) # Override defaults if found in config case "$key" in "resource_group") RESOURCE_GROUP="$value" ;; "cluster_name") AKS_CLUSTER="$value" ;; esac done < ".github/config/deploy_env_vars_${ENV}" fi # Export for other jobs echo "REGISTRY=$REGISTRY" >> $GITHUB_ENV echo "IMAGE_ORG=$IMAGE_ORG" >> $GITHUB_ENV echo "RESOURCE_GROUP=$RESOURCE_GROUP" >> $GITHUB_ENV echo "AKS_CLUSTER=$AKS_CLUSTER" >> $GITHUB_ENV - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle run: | ./gradlew build -x test - name: SonarQube Analysis & Quality Gate env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} run: | # Check if SonarQube should be skipped SKIP_SONARQUBE="${{ github.event.inputs.SKIP_SONARQUBE || 'true' }}" if [[ "$SKIP_SONARQUBE" == "true" ]]; then echo "⏭️ Skipping SonarQube Analysis (SKIP_SONARQUBE=$SKIP_SONARQUBE)" exit 0 fi # Define services array services=(user meeting stt ai notification) # Run tests, coverage reports, and SonarQube analysis for each service for service in "${services[@]}"; do ./gradlew :$service:test :$service:jacocoTestReport :$service:sonar \ -Dsonar.projectKey=hgzero-$service-${{ steps.determine_env.outputs.environment }} \ -Dsonar.projectName=hgzero-$service-${{ steps.determine_env.outputs.environment }} \ -Dsonar.host.url=$SONAR_HOST_URL \ -Dsonar.token=$SONAR_TOKEN \ -Dsonar.java.binaries=build/classes/java/main \ -Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml \ -Dsonar.exclusions=**/config/**,**/entity/**,**/dto/**,**/*Application.class,**/exception/** done - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: app-builds path: | user/build/libs/*.jar meeting/build/libs/*.jar stt/build/libs/*.jar ai/build/libs/*.jar notification/build/libs/*.jar - name: Set outputs id: set_outputs run: | # Generate timestamp for image tag IMAGE_TAG=$(date +%Y%m%d%H%M%S) echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT echo "environment=${{ steps.determine_env.outputs.environment }}" >> $GITHUB_OUTPUT release: name: Build and Push Docker Images needs: build runs-on: ubuntu-latest steps: - name: Check out code uses: actions/checkout@v4 - name: Download build artifacts uses: actions/download-artifact@v4 with: name: app-builds - name: Set environment variables from build job run: | echo "REGISTRY=${{ env.REGISTRY }}" >> $GITHUB_ENV echo "IMAGE_ORG=${{ env.IMAGE_ORG }}" >> $GITHUB_ENV echo "ENVIRONMENT=${{ needs.build.outputs.environment }}" >> $GITHUB_ENV echo "IMAGE_TAG=${{ needs.build.outputs.image_tag }}" >> $GITHUB_ENV - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub (prevent rate limit) uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - name: Login to Azure Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.ACR_USERNAME }} password: ${{ secrets.ACR_PASSWORD }} - name: Build and push Docker images for all services run: | # Define services array services=(user meeting stt ai notification) # Build and push each service image for service in "${services[@]}"; do echo "Building and pushing $service..." docker build \ --build-arg BUILD_LIB_DIR="$service/build/libs" \ --build-arg ARTIFACTORY_FILE="$service.jar" \ -f deployment/container/Dockerfile-backend \ -t ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/$service:${{ needs.build.outputs.environment }}-${{ needs.build.outputs.image_tag }} . docker push ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/$service:${{ needs.build.outputs.environment }}-${{ needs.build.outputs.image_tag }} done deploy: name: Deploy to Kubernetes needs: [build, release] runs-on: ubuntu-latest steps: - name: Check out code uses: actions/checkout@v4 - name: Set image tag environment variable run: | echo "IMAGE_TAG=${{ needs.build.outputs.image_tag }}" >> $GITHUB_ENV echo "ENVIRONMENT=${{ needs.build.outputs.environment }}" >> $GITHUB_ENV - name: Install Azure CLI run: | curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash - name: Azure Login uses: azure/login@v1 with: creds: ${{ secrets.AZURE_CREDENTIALS }} - name: Setup kubectl uses: azure/setup-kubectl@v3 - name: Get AKS Credentials run: | az aks get-credentials --resource-group ${{ env.RESOURCE_GROUP }} --name ${{ env.AKS_CLUSTER }} --overwrite-existing - name: Create namespace run: | kubectl create namespace ${{ env.NAMESPACE }} --dry-run=client -o yaml | kubectl apply -f - - name: Install Kustomize run: | curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash sudo mv kustomize /usr/local/bin/ - name: Update Kustomize images and deploy run: | # 환경별 디렉토리로 이동 cd .github/kustomize/overlays/${{ env.ENVIRONMENT }} # 각 서비스별 이미지 태그 업데이트 kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/user:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/meeting:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/stt:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/ai:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/notification:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} # 매니페스트 적용 kubectl apply -k . - name: Wait for deployments to be ready run: | echo "Waiting for deployments to be ready..." kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/user --timeout=300s || true kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/meeting --timeout=300s || true kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/stt --timeout=300s || true kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/ai --timeout=300s || true kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/notification --timeout=300s || true