name: Backend CI/CD Pipeline on: push: branches: - cicd paths: - 'member/**' - 'mysub/**' - 'recommend/**' - 'common/**' - 'deployment/**' - '.github/workflows/**' jobs: build: name: Build and Test runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up JDK 21 uses: actions/setup-java@v3 with: java-version: '21' distribution: 'temurin' cache: gradle - name: Grant execute permission for gradlew run: chmod +x ./gradlew - name: Build with Gradle run: ./gradlew :member:build :mysub-infra:build :recommend:build -x test - name: Run tests run: | ./gradlew :member:test :member:jacocoTestReport ./gradlew :mysub-infra:test :mysub-infra:jacocoTestReport ./gradlew :recommend:test :recommend:jacocoTestReport - name: SonarQube Analysis - Member env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} run: | ./gradlew :member:sonar \ -Dsonar.projectKey=lifesub-member \ -Dsonar.projectName=lifesub-member \ -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/** - name: SonarQube Analysis - Recommend env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} run: | ./gradlew :recommend:sonar \ -Dsonar.projectKey=lifesub-recommend \ -Dsonar.projectName=lifesub-recommend \ -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/** - name: SonarQube Analysis - Mysub env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} run: | ./gradlew :mysub-infra:sonar \ -Dsonar.projectKey=lifesub-mysub \ -Dsonar.projectName=lifesub-mysub \ -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/** - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: build-artifacts path: | member/build/libs mysub-infra/build/libs recommend/build/libs deployment/ release: name: Build and Push Container Images runs-on: ubuntu-latest needs: build outputs: image_tag: ${{ steps.set-image-tag.outputs.image_tag }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Download build artifacts uses: actions/download-artifact@v4 with: name: build-artifacts path: . - name: Read deployment variables run: | source deployment/deploy_env_vars echo "REGISTRY=$registry" >> $GITHUB_ENV echo "IMAGE_ORG=$image_org" >> $GITHUB_ENV echo "NAMESPACE=$namespace" >> $GITHUB_ENV - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Azure Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.ACR_USERNAME }} password: ${{ secrets.ACR_PASSWORD }} - name: Generate image tag id: set-image-tag run: | IMAGE_TAG=$(date +'%Y%m%d%H%M%S') echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV - name: Build and push Member image uses: docker/build-push-action@v5 with: context: . push: true file: deployment/Dockerfile build-args: | BUILD_LIB_DIR=member/build/libs ARTIFACTORY_FILE=member.jar tags: ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/member:${{ env.IMAGE_TAG }} - name: Build and push Mysub image uses: docker/build-push-action@v5 with: context: . push: true file: deployment/Dockerfile build-args: | BUILD_LIB_DIR=mysub-infra/build/libs ARTIFACTORY_FILE=mysub.jar tags: ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/mysub:${{ env.IMAGE_TAG }} - name: Build and push Recommend image uses: docker/build-push-action@v5 with: context: . push: true file: deployment/Dockerfile build-args: | BUILD_LIB_DIR=recommend/build/libs ARTIFACTORY_FILE=recommend.jar tags: ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/recommend:${{ env.IMAGE_TAG }} deploy: name: Deploy to Kubernetes runs-on: ubuntu-latest needs: release steps: - name: Checkout code uses: actions/checkout@v4 - name: Download build artifacts uses: actions/download-artifact@v4 with: name: build-artifacts path: . - name: Setup envsubst run: sudo apt-get install gettext-base - name: Read deployment variables run: | source deployment/deploy_env_vars echo "TEAMID=$teamid" >> $TEAMID echo "NAMESPACE=$namespace" >> $GITHUB_ENV echo "REGISTRY=$registry" >> $GITHUB_ENV echo "IMAGE_ORG=$image_org" >> $GITHUB_ENV echo "ALLOWED_ORIGINS=$allowed_origins" >> $GITHUB_ENV echo "JWT_SECRET_KEY=$jwt_secret_key" >> $GITHUB_ENV echo "POSTGRES_USER=$postgres_user" >> $GITHUB_ENV echo "POSTGRES_PASSWORD=$postgres_password" >> $GITHUB_ENV echo "REPLICAS=$replicas" >> $GITHUB_ENV echo "RESOURCES_REQUESTS_CPU=$resources_requests_cpu" >> $GITHUB_ENV echo "RESOURCES_REQUESTS_MEMORY=$resources_requests_memory" >> $GITHUB_ENV echo "RESOURCES_LIMITS_CPU=$resources_limits_cpu" >> $GITHUB_ENV echo "RESOURCES_LIMITS_MEMORY=$resources_limits_memory" >> $GITHUB_ENV - name: Generate deployment manifest env: IMAGE_TAG: ${{ needs.release.outputs.image_tag }} run: | export namespace=${{ env.NAMESPACE }} export allowed_origins=${{ env.ALLOWED_ORIGINS }} export jwt_secret_key=${{ env.JWT_SECRET_KEY }} export postgres_user=${{ env.POSTGRES_USER }} export postgres_password=${{ env.POSTGRES_PASSWORD }} export replicas=${{ env.REPLICAS }} export resources_requests_cpu=${{ env.RESOURCES_REQUESTS_CPU }} export resources_requests_memory=${{ env.RESOURCES_REQUESTS_MEMORY }} export resources_limits_cpu=${{ env.RESOURCES_LIMITS_CPU }} export resources_limits_memory=${{ env.RESOURCES_LIMITS_MEMORY }} # Set image paths with the released tag export member_image_path=${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/member:${{ env.IMAGE_TAG }} export mysub_image_path=${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/mysub:${{ env.IMAGE_TAG }} export recommend_image_path=${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/recommend:${{ env.IMAGE_TAG }} # Generate manifest envsubst < deployment/deploy.yaml.template > deployment/deploy.yaml # Print manifest for debugging echo "Generated Kubernetes manifest:" cat deployment/deploy.yaml - name: Set up kubectl uses: azure/setup-kubectl@v3 - name: Set up AKS context uses: azure/aks-set-context@v3 with: resource-group: ictcoe-edu cluster-name: ${{ env.TEAMID }}-aks admin: false use-kubelogin: true env: AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} - name: Create namespace if not exists run: | kubectl create namespace ${{ env.NAMESPACE }} --dry-run=client -o yaml | kubectl apply -f - - name: Deploy to Kubernetes run: | kubectl apply -f deployment/deploy.yaml echo "Waiting for deployments to be ready..." kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/member --timeout=300s kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/mysub --timeout=300s kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/recommend --timeout=300s - name: Verify deployment run: | echo "Getting services:" kubectl get svc -n ${{ env.NAMESPACE }} echo "Getting ingress:" kubectl get ingress -n ${{ env.NAMESPACE }}