diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 5fcd38a..f06b5ab 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -1,143 +1,274 @@ -name: Backend CI/CD Pipeline +name: Backend Services CI/CD -# Temporarily disabled -# on: -# push: -# branches: [ k8s ] - -env: - JAVA_VERSION: '21' - GRADLE_VERSION: '8.5' +on: + push: + #branches: [ cicd ] + paths: + - 'member/**' + - 'mysub/**' + - 'recommend/**' + - 'common/**' + - 'deployment/**' + - '.github/workflows/**' jobs: build: - name: Build Applications + name: Build and Test runs-on: ubuntu-latest + outputs: + image_tag: ${{ steps.set_outputs.outputs.image_tag }} steps: - - uses: actions/checkout@v4 + - name: Check out code + uses: actions/checkout@v4 - - name: Set up JDK - uses: actions/setup-java@v4 + - name: Set up JDK 21 + uses: actions/setup-java@v3 with: - java-version: ${{ env.JAVA_VERSION }} + java-version: '21' distribution: 'temurin' + cache: 'gradle' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - gradle-version: ${{ env.GRADLE_VERSION }} + - name: Load environment variables + id: env_vars + run: | + # Read environment variables from file + 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-) + + # Set GitHub environment variables + echo "$key=$value" >> $GITHUB_ENV + done < deployment/deploy_env_vars + + - name: Grant execute permission for gradlew + run: chmod +x gradlew - name: Build with Gradle run: | - chmod +x gradlew - ./gradlew clean :member:build :mysub-infra:build :recommend:build + ./gradlew :member:build :mysub-infra:build :recommend:build -x test - - name: Upload Build Artifacts + - name: Test with Gradle + run: | + ./gradlew :member:test :member:jacocoTestReport + ./gradlew :mysub-infra:test :mysub-infra:jacocoTestReport + ./gradlew :recommend:test :recommend:jacocoTestReport + + - name: SonarQube Analysis + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + 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 + + ./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 + + ./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 + + - name: Upload build artifacts uses: actions/upload-artifact@v4 with: - name: build-artifacts + name: app-builds path: | member/build/libs/*.jar mysub-infra/build/libs/*.jar recommend/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 + release: - name: Build and Push Images + name: Build and Push Docker Images needs: build runs-on: ubuntu-latest - outputs: - image_tag: ${{ steps.set_tag.outputs.tag }} steps: - - uses: actions/checkout@v4 + - name: Check out code + uses: actions/checkout@v4 - - name: Download Build Artifacts + - name: Download build artifacts uses: actions/download-artifact@v4 with: - name: build-artifacts + name: app-builds - - name: Load Environment Variables - run: source deployment/deploy_env_vars - - - name: Generate Image Tag - id: set_tag + - name: Load environment variables run: | - tag=$(date +'%Y%m%d%H%M%S') - echo "tag=${tag}" >> $GITHUB_OUTPUT + # Read environment variables from file + 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-) + + # Set GitHub environment variables + echo "$key=$value" >> $GITHUB_ENV + done < deployment/deploy_env_vars + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - name: Login to Azure Container Registry - uses: azure/docker-login@v1 + uses: docker/login-action@v3 with: - login-server: ${{ env.registry }} + registry: ${{ env.registry }} username: ${{ secrets.ACR_USERNAME }} password: ${{ secrets.ACR_PASSWORD }} - - name: Build and Push Images - run: | - for service in member mysub recommend; do - build_dir=$([[ "$service" == "mysub" ]] && echo "mysub-infra" || echo "$service") - jar_file=$([[ "$service" == "mysub" ]] && echo "mysub.jar" || echo "${service}.jar") - - docker build \ - --build-arg BUILD_LIB_DIR="${build_dir}/build/libs" \ - --build-arg ARTIFACTORY_FILE="${jar_file}" \ - -f deployment/Dockerfile \ - -t ${registry}/${image_org}/${service}:${{ steps.set_tag.outputs.tag }} . - - docker push ${registry}/${image_org}/${service}:${{ steps.set_tag.outputs.tag }} - done + - name: Build and push Member service image + uses: docker/build-push-action@v5 + with: + context: . + file: deployment/Dockerfile + push: true + tags: ${{ env.registry }}/${{ env.image_org }}/member:${{ needs.build.outputs.image_tag }} + build-args: | + BUILD_LIB_DIR=member/build/libs + ARTIFACTORY_FILE=member.jar + + - name: Build and push MySub service image + uses: docker/build-push-action@v5 + with: + context: . + file: deployment/Dockerfile + push: true + tags: ${{ env.registry }}/${{ env.image_org }}/mysub:${{ needs.build.outputs.image_tag }} + build-args: | + BUILD_LIB_DIR=mysub-infra/build/libs + ARTIFACTORY_FILE=mysub.jar + + - name: Build and push Recommend service image + uses: docker/build-push-action@v5 + with: + context: . + file: deployment/Dockerfile + push: true + tags: ${{ env.registry }}/${{ env.image_org }}/recommend:${{ needs.build.outputs.image_tag }} + build-args: | + BUILD_LIB_DIR=recommend/build/libs + ARTIFACTORY_FILE=recommend.jar deploy: - name: Deploy to AKS - needs: release + name: Deploy to Kubernetes + needs: [build, release] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - name: Check out code + uses: actions/checkout@v4 - - name: Load Environment Variables - run: source deployment/deploy_env_vars + - name: Load environment variables + run: | + # Read environment variables from file + 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-) + + # Set GitHub environment variables + echo "$key=$value" >> $GITHUB_ENV + done < deployment/deploy_env_vars + + - name: Set image tag environment variable + run: | + echo "IMAGE_TAG=${{ needs.build.outputs.image_tag }}" >> $GITHUB_ENV + + # Azure CLI 설치 단계 수정 + - 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: Set AKS Context - uses: azure/aks-set-context@v1 - with: - resource-group: ictcoe-edu - cluster-name: ${{ env.teamid }}-aks + - name: Setup kubectl + uses: azure/setup-kubectl@v3 - - name: Create Namespace + - name: Get AKS Credentials run: | - kubectl create namespace ${teamid}-${root_project}-ns --dry-run=client -o yaml | kubectl apply -f - + az aks get-credentials --resource-group ictcoe-edu --name ${{ env.teamid }}-aks --overwrite-existing - - name: Generate Manifest + - name: Create namespace run: | - export namespace=${teamid}-${root_project}-ns - export allowed_origins=${allowed_origins} - export jwt_secret_key=${jwt_secret_key} - export postgres_user=${postgres_user} - export postgres_password=${postgres_password} - export replicas=${replicas} - export resources_requests_cpu=${resources_requests_cpu} - export resources_requests_memory=${resources_requests_memory} - export resources_limits_cpu=${resources_limits_cpu} - export resources_limits_memory=${resources_limits_memory} - export member_image_path=${registry}/${image_org}/member:${{ needs.release.outputs.image_tag }} - export mysub_image_path=${registry}/${image_org}/mysub:${{ needs.release.outputs.image_tag }} - export recommend_image_path=${registry}/${image_org}/recommend:${{ needs.release.outputs.image_tag }} + kubectl create namespace ${{ env.namespace }} --dry-run=client -o yaml | kubectl apply -f - + + - name: Install envsubst + run: | + sudo apt-get update + sudo apt-get install -y gettext-base + + - name: Generate Kubernetes manifest + run: | + # Set environment variables for the deployment template + 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 dynamic 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 the manifest file using envsubst envsubst < deployment/deploy.yaml.template > deployment/deploy.yaml + + # Print manifest for debugging + echo "Generated Kubernetes manifest:" cat deployment/deploy.yaml - - name: Deploy to AKS + - name: Apply Kubernetes manifest run: | kubectl apply -f deployment/deploy.yaml - - echo "Waiting for deployments to be ready..." - kubectl -n ${namespace} wait --for=condition=available deployment/member --timeout=300s - kubectl -n ${namespace} wait --for=condition=available deployment/mysub --timeout=300s - kubectl -n ${namespace} wait --for=condition=available deployment/recommend --timeout=300s \ No newline at end of file + + - name: Wait for deployments to be ready + run: | + 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: Get service information + run: | + echo "Ingress IP: $(kubectl -n ${{ env.namespace }} get ingress lifesub -o jsonpath='{.status.loadBalancer.ingress[0].ip}')" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9eb9ece --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.gradle +build/ + +### IntelliJ IDEA ### +.idea/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin index f07e0ec..b09a2bf 100644 Binary files a/.gradle/buildOutputCleanup/outputFiles.bin and b/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe index f7a9d83..a8743e8 100644 Binary files a/.gradle/file-system.probe and b/.gradle/file-system.probe differ diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 264f470..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules/common/lifesub.common.main.iml b/.idea/modules/common/lifesub.common.main.iml deleted file mode 100644 index 0d2c1d2..0000000 --- a/.idea/modules/common/lifesub.common.main.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/modules/member/lifesub.member.main.iml b/.idea/modules/member/lifesub.member.main.iml deleted file mode 100644 index e208837..0000000 --- a/.idea/modules/member/lifesub.member.main.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/modules/mysub-biz/lifesub.mysub-biz.main.iml b/.idea/modules/mysub-biz/lifesub.mysub-biz.main.iml deleted file mode 100644 index 0c5afac..0000000 --- a/.idea/modules/mysub-biz/lifesub.mysub-biz.main.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/modules/mysub-infra/lifesub.mysub-infra.main.iml b/.idea/modules/mysub-infra/lifesub.mysub-infra.main.iml deleted file mode 100644 index bac25ea..0000000 --- a/.idea/modules/mysub-infra/lifesub.mysub-infra.main.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/modules/recommend/lifesub.recommend.main.iml b/.idea/modules/recommend/lifesub.recommend.main.iml deleted file mode 100644 index eb1b213..0000000 --- a/.idea/modules/recommend/lifesub.recommend.main.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ede8bcd --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ + +# 생활구독추천 서비스 + + diff --git a/build.gradle b/build.gradle index 46c8b65..1ea1702 100644 --- a/build.gradle +++ b/build.gradle @@ -92,6 +92,7 @@ subprojects { })) } } + } //-- Biz와 common 모듈이 아닌 경우 인프라 관련 라이브러리 추가 @@ -133,3 +134,4 @@ configure(subprojects.findAll { it.name.endsWith('-biz') || it.name == 'common' bootJar.enabled = false jar.enabled = true } + diff --git a/deployment/Jenkinsfile b/deployment/Jenkinsfile index d3329df..2e52aba 100644 --- a/deployment/Jenkinsfile +++ b/deployment/Jenkinsfile @@ -54,16 +54,16 @@ podTemplate( } } - stage('Build Applications') { + stage('Build Applications & SonarQube Analysis') { container('podman') { sh 'podman system service -t 0 unix:///run/podman/podman.sock & sleep 2' } container('gradle') { def testContainersConfig = '''docker.client.strategy=org.testcontainers.dockerclient.UnixSocketClientProviderStrategy - docker.host=unix:///run/podman/podman.sock - ryuk.container.privileged=true - testcontainers.reuse.enable=true''' +docker.host=unix:///run/podman/podman.sock +ryuk.container.privileged=true +testcontainers.reuse.enable=true''' sh """ # TestContainers 설정 @@ -71,11 +71,51 @@ podTemplate( echo '${testContainersConfig}' > member/src/test/resources/testcontainers.properties echo '${testContainersConfig}' > mysub-infra/src/test/resources/testcontainers.properties echo '${testContainersConfig}' > recommend/src/test/resources/testcontainers.properties - - # 빌드 실행 - chmod +x gradlew - ./gradlew clean :member:build :mysub-infra:build :recommend:build """ + + // Member 서비스 빌드 및 SonarQube 분석 + withSonarQubeEnv('SonarQube') { + sh """ + chmod +x gradlew + + # 빌드 실행 + ./gradlew :member:build :mysub-infra:build :recommend:build -x test + + # Member 서비스 + ./gradlew :member:test :member:jacocoTestReport :member:sonar \ + -Dsonar.projectKey=lifesub-member \ + -Dsonar.projectName=lifesub-member \ + -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/** + + # Recommend 서비스 + ./gradlew :recommend:test :recommend:jacocoTestReport :recommend:sonar \ + -Dsonar.projectKey=lifesub-recommend \ + -Dsonar.projectName=lifesub-recommend \ + -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/** + + # Mysub 서비스 (biz & infra 구조) + ./gradlew :mysub-infra:test :mysub-infra:jacocoTestReport :mysub-infra:sonar \ + -Dsonar.projectKey=lifesub-mysub \ + -Dsonar.projectName=lifesub-mysub \ + -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/** + """ + } + + } + } + + stage('Quality Gate') { + timeout(time: 10, unit: 'MINUTES') { + def qg = waitForQualityGate() + if (qg.status != 'OK') { + error "Pipeline aborted due to quality gate failure: ${qg.status}" + } } } @@ -143,4 +183,5 @@ podTemplate( } } } -} \ No newline at end of file +} + diff --git a/deployment/Jenkinsfile_ArgoCD b/deployment/Jenkinsfile_ArgoCD index 0a9ecd4..0e96ed8 100644 --- a/deployment/Jenkinsfile_ArgoCD +++ b/deployment/Jenkinsfile_ArgoCD @@ -10,43 +10,54 @@ podTemplate( label: "${PIPELINE_ID}", serviceAccount: 'jenkins', containers: [ - containerTemplate(name: 'podman', - image: "mgoltzsche/podman", - ttyEnabled: true, - command: 'cat', - privileged: true), + containerTemplate(name: 'podman', image: "mgoltzsche/podman", ttyEnabled: true, command: 'cat', privileged: true), containerTemplate(name: 'gradle', - image: 'gradle:jdk17', - ttyEnabled: true, - command: 'cat', - envVars: [ - envVar(key: 'DOCKER_HOST', value: 'unix:///run/podman/podman.sock'), - envVar(key: 'TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE', value: '/run/podman/podman.sock'), - envVar(key: 'TESTCONTAINERS_RYUK_DISABLED', value: 'true') - ]), - containerTemplate(name: 'git', - image: 'alpine:3.19', - command: 'cat', - ttyEnabled: true) + image: 'gradle:jdk17', + ttyEnabled: true, + command: 'cat', + envVars: [ + envVar(key: 'DOCKER_HOST', value: 'unix:///run/podman/podman.sock'), + envVar(key: 'TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE', value: '/run/podman/podman.sock'), + envVar(key: 'TESTCONTAINERS_RYUK_DISABLED', value: 'true') + ]), + containerTemplate(name: 'azure-cli', image: 'hiondal/azure-kubectl:latest', command: 'cat', ttyEnabled: true), + containerTemplate(name: 'envsubst', image: "hiondal/envsubst", command: 'sleep', args: '1h'), + containerTemplate(name: 'git', image: 'alpine/git:latest', command: 'cat', ttyEnabled: true) ], volumes: [ emptyDirVolume(mountPath: '/home/gradle/.gradle', memory: false), + emptyDirVolume(mountPath: '/root/.azure', memory: false), emptyDirVolume(mountPath: '/run/podman', memory: false) ] ) { node(PIPELINE_ID) { def props def imageTag = getImageTag() + def manifest = "deploy.yaml" + def namespace def services = ['member', 'mysub', 'recommend'] - def manifestRepo = 'cna-bootcamp/lifesub-manifest' - def manifestBranch = 'main' + def MANIFEST_REPO = "https://github.com/cna-bootcamp/lifesub-manifest.git" + def MANIFEST_BRANCH = "main" stage("Get Source") { checkout scm props = readProperties file: "deployment/deploy_env_vars" + namespace = "${props.namespace}" } - stage('Build Applications') { + stage("Setup AKS") { + container('azure-cli') { + withCredentials([azureServicePrincipal('azure-credentials')]) { + sh """ + az login --service-principal -u \$AZURE_CLIENT_ID -p \$AZURE_CLIENT_SECRET -t \$AZURE_TENANT_ID + az aks get-credentials --resource-group ictcoe-edu --name ${props.teamid}-aks --overwrite-existing + kubectl create namespace ${namespace} --dry-run=client -o yaml | kubectl apply -f - + """ + } + } + } + + stage('Build Applications & SonarQube Analysis') { container('podman') { sh 'podman system service -t 0 unix:///run/podman/podman.sock & sleep 2' } @@ -63,11 +74,50 @@ testcontainers.reuse.enable=true''' echo '${testContainersConfig}' > member/src/test/resources/testcontainers.properties echo '${testContainersConfig}' > mysub-infra/src/test/resources/testcontainers.properties echo '${testContainersConfig}' > recommend/src/test/resources/testcontainers.properties - - # 빌드 실행 - chmod +x gradlew - ./gradlew clean :member:build :mysub-infra:build :recommend:build """ + + // Member 서비스 빌드 및 SonarQube 분석 + withSonarQubeEnv('SonarQube') { + sh """ + chmod +x gradlew + + # 빌드 실행 + ./gradlew :member:build :mysub-infra:build :recommend:build -x test + + # Member 서비스 + ./gradlew :member:test :member:jacocoTestReport :member:sonar \ + -Dsonar.projectKey=lifesub-member \ + -Dsonar.projectName=lifesub-member \ + -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/** + + # Recommend 서비스 + ./gradlew :recommend:test :recommend:jacocoTestReport :recommend:sonar \ + -Dsonar.projectKey=lifesub-recommend \ + -Dsonar.projectName=lifesub-recommend \ + -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/** + + # Mysub 서비스 (biz & infra 구조) + ./gradlew :mysub-infra:test :mysub-infra:jacocoTestReport :mysub-infra:sonar \ + -Dsonar.projectKey=lifesub-mysub \ + -Dsonar.projectName=lifesub-mysub \ + -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/** + """ + } + } + } + + stage('Quality Gate') { + timeout(time: 10, unit: 'MINUTES') { + def qg = waitForQualityGate() + if (qg.status != 'OK') { + error "Pipeline aborted due to quality gate failure: ${qg.status}" + } } } @@ -85,10 +135,10 @@ testcontainers.reuse.enable=true''' def jarFile = service == 'mysub' ? 'mysub.jar' : "${service}.jar" sh """ - podman build \\ - --build-arg BUILD_LIB_DIR="${buildDir}/build/libs" \\ - --build-arg ARTIFACTORY_FILE="${jarFile}" \\ - -f deployment/Dockerfile \\ + podman build \ + --build-arg BUILD_LIB_DIR="${buildDir}/build/libs" \ + --build-arg ARTIFACTORY_FILE="${jarFile}" \ + -f deployment/Dockerfile \ -t ${props.registry}/${props.image_org}/${service}:${imageTag} . podman push ${props.registry}/${props.image_org}/${service}:${imageTag} @@ -98,47 +148,35 @@ testcontainers.reuse.enable=true''' } } - stage('Update Manifests') { + stage('Update Manifest Repository') { container('git') { - // git과 yq 설치 - sh ''' - apk add --no-cache git curl - curl -L https://github.com/mikefarah/yq/releases/download/v4.40.5/yq_linux_amd64 -o /usr/local/bin/yq - chmod +x /usr/local/bin/yq - ''' - - withCredentials([usernamePassword( - credentialsId: 'github-credentials', - usernameVariable: 'GIT_USERNAME', - passwordVariable: 'GIT_PASSWORD' - )]) { - sh ''' - git config --global user.email "jenkins@example.com" - git config --global user.name "Jenkins" - ''' + // 임시 디렉토리 생성 + sh "mkdir -p /tmp/manifests" + // Clone manifest repository + withCredentials([usernamePassword(credentialsId: 'github-credentials', usernameVariable: 'GIT_USERNAME', passwordVariable: 'GIT_PASSWORD')]) { sh """ - rm -rf lifesub-manifest - #git clone https://oauth2:${GIT_PASSWORD}@github.com/${manifestRepo}.git - git clone https://${GIT_USERNAME}:${GIT_PASSWORD}@github.com/${manifestRepo}.git + git config --global user.email "jenkins@example.com" + git config --global user.name "Jenkins Pipeline" + + git clone https://${GIT_USERNAME}:${GIT_PASSWORD}@github.com/cna-bootcamp/lifesub-manifest.git /tmp/manifests + cd /tmp/manifests + + # Update image tags in the appropriate YAML files + for service in ${services.join(' ')}; do + # 백엔드 서비스 이미지 태그 업데이트 + if [ -f lifesub/deployments/\${service}-deployment.yaml ]; then + sed -i "s|image: ${props.registry}/${props.image_org}/\${service}:.*|image: ${props.registry}/${props.image_org}/\${service}:${imageTag}|g" lifesub/deployments/\${service}-deployment.yaml + fi + done + + # Commit and push changes + git add . + git commit -m "Update backend service image tags to ${imageTag}" || true + git push """ - - dir('lifesub-manifest') { - services.each { service -> - def imagePath = "${props.registry}/${props.image_org}/${service}:${imageTag}" - sh """ - yq -i '.spec.template.spec.containers[0].image = "${imagePath}"' lifesub/deployments/${service}-deployment.yaml - """ - } - - sh """ - git add . - git diff-index --quiet HEAD || git commit -m "Update backend services images to ${imageTag}" - git push origin ${manifestBranch} - """ - } } } } } -} \ No newline at end of file +} diff --git a/deployment/deploy.yaml.template b/deployment/deploy.yaml.template index fa604ec..462f644 100644 --- a/deployment/deploy.yaml.template +++ b/deployment/deploy.yaml.template @@ -306,4 +306,41 @@ spec: ports: - port: 80 targetPort: 8083 - type: ClusterIP \ No newline at end of file + type: ClusterIP + +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: lifesub + namespace: ${namespace} + annotations: + kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/rewrite-target: /$2 + nginx.ingress.kubernetes.io/use-regex: "true" +spec: + ingressClassName: nginx + rules: + - http: + paths: + - path: /member(/|$)(.*) + pathType: ImplementationSpecific + backend: + service: + name: member + port: + number: 80 + - path: /mysub(/|$)(.*) + pathType: ImplementationSpecific + backend: + service: + name: mysub + port: + number: 80 + - path: /recommend(/|$)(.*) + pathType: ImplementationSpecific + backend: + service: + name: recommend + port: + number: 80 diff --git a/deployment/deploy_env_vars b/deployment/deploy_env_vars index d2c737c..494e576 100644 --- a/deployment/deploy_env_vars +++ b/deployment/deploy_env_vars @@ -8,8 +8,8 @@ registry=unicorncr.azurecr.io image_org=lifesub # Application Settings -replicas=2 -allowed_origins=http://4.230.156.229 +replicas=1 +allowed_origins=http://20.214.113.12 # Security Settings jwt_secret_key=8O2HQ13etL2BWZvYOiWsJ5uWFoLi6NBUG8divYVoCgtHVvlk3dqRksMl16toztDUeBTSIuOOPvHIrYq11G2BwQ diff --git a/deployment/manifest/configmaps/common-config.yaml b/deployment/manifest/configmaps/common-config.yaml index e9c5f74..37d4d22 100644 --- a/deployment/manifest/configmaps/common-config.yaml +++ b/deployment/manifest/configmaps/common-config.yaml @@ -5,4 +5,8 @@ metadata: data: JPA_DDL_AUTO: update JPA_SHOW_SQL: "true" +<<<<<<< HEAD ALLOWED_ORIGINS: "http://localhost:18080,http://localhost:18081,http://20.249.184.6" +======= + ALLOWED_ORIGINS: "http://localhost:18080,http://localhost:18081,http://20.214.113.12" +>>>>>>> 1ea06faa5d92eae36ec9d161158eb1314eaff732 diff --git a/deployment/manifest/deployments/mysub-deployment.yaml b/deployment/manifest/deployments/mysub-deployment.yaml index 55e3791..cb726a1 100644 --- a/deployment/manifest/deployments/mysub-deployment.yaml +++ b/deployment/manifest/deployments/mysub-deployment.yaml @@ -55,4 +55,4 @@ spec: path: /actuator/health/readiness port: 8082 initialDelaySeconds: 60 - periodSeconds: 15 \ No newline at end of file + periodSeconds: 15 diff --git a/member/src/main/java/com/unicorn/lifesub/member/service/MemberServiceImpl.java b/member/src/main/java/com/unicorn/lifesub/member/service/MemberServiceImpl.java index fca8527..8c65142 100644 --- a/member/src/main/java/com/unicorn/lifesub/member/service/MemberServiceImpl.java +++ b/member/src/main/java/com/unicorn/lifesub/member/service/MemberServiceImpl.java @@ -18,7 +18,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Collection; -import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -40,7 +39,7 @@ public class MemberServiceImpl implements MemberService { // 사용자의 권한 정보 생성 Collection authorities = member.getRoles().stream() .map(SimpleGrantedAuthority::new) - .collect(Collectors.toList()); + .toList(); return jwtTokenProvider.createToken(member, authorities); } diff --git a/member/src/main/resources/application.yml b/member/src/main/resources/application.yml index 9ac3e77..2c02376 100644 --- a/member/src/main/resources/application.yml +++ b/member/src/main/resources/application.yml @@ -7,7 +7,7 @@ spring: datasource: url: jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT:5432}/${POSTGRES_DB:member} username: ${POSTGRES_USER:admin} - password: ${POSTGRES_PASSWORD:Passw0rd} + password: ${POSTGRES_PASSWORD:Hi5Jessica!} driver-class-name: org.postgresql.Driver # JPA 설정 diff --git a/mysub-infra/src/main/resources/application.yml b/mysub-infra/src/main/resources/application.yml index 081428c..fe9bcaf 100644 --- a/mysub-infra/src/main/resources/application.yml +++ b/mysub-infra/src/main/resources/application.yml @@ -7,7 +7,7 @@ spring: datasource: url: jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT:5432}/${POSTGRES_DB:mysub} username: ${POSTGRES_USER:admin} - password: ${POSTGRES_PASSWORD:Passw0rd} + password: ${POSTGRES_PASSWORD:Hi5Jessica!} driver-class-name: org.postgresql.Driver # JPA 설정 jpa: diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/RecommendApplication.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/RecommendApplication.class index ac7f71a..560e99e 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/RecommendApplication.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/RecommendApplication.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/config/DataLoader.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/config/DataLoader.class index ab0390e..b67e6c6 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/config/DataLoader.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/config/DataLoader.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/config/SwaggerConfig.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/config/SwaggerConfig.class index 8d241b4..bf4e1be 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/config/SwaggerConfig.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/config/SwaggerConfig.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/config/jwt/JwtAuthenticationFilter.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/config/jwt/JwtAuthenticationFilter.class index 4ebf912..f796a2b 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/config/jwt/JwtAuthenticationFilter.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/config/jwt/JwtAuthenticationFilter.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/config/jwt/JwtTokenProvider.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/config/jwt/JwtTokenProvider.class index 11922ae..451d8b1 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/config/jwt/JwtTokenProvider.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/config/jwt/JwtTokenProvider.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/controller/RecommendController.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/controller/RecommendController.class index 169a5bb..c500547 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/controller/RecommendController.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/controller/RecommendController.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/domain/RecommendedCategory$RecommendedCategoryBuilder.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/domain/RecommendedCategory$RecommendedCategoryBuilder.class index f451fe4..ad2b49b 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/domain/RecommendedCategory$RecommendedCategoryBuilder.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/domain/RecommendedCategory$RecommendedCategoryBuilder.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/domain/RecommendedCategory.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/domain/RecommendedCategory.class index 78e5ad5..0028bc0 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/domain/RecommendedCategory.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/domain/RecommendedCategory.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/domain/SpendingCategory$SpendingCategoryBuilder.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/domain/SpendingCategory$SpendingCategoryBuilder.class index c0ae7e6..32c8e64 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/domain/SpendingCategory$SpendingCategoryBuilder.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/domain/SpendingCategory$SpendingCategoryBuilder.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/domain/SpendingCategory.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/domain/SpendingCategory.class index cd5a6c3..ff65eaf 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/domain/SpendingCategory.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/domain/SpendingCategory.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/dto/RecommendCategoryDTO$RecommendCategoryDTOBuilder.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/dto/RecommendCategoryDTO$RecommendCategoryDTOBuilder.class index 5a49ea0..604af4d 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/dto/RecommendCategoryDTO$RecommendCategoryDTOBuilder.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/dto/RecommendCategoryDTO$RecommendCategoryDTOBuilder.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/dto/RecommendCategoryDTO.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/dto/RecommendCategoryDTO.class index 876d31a..0ddf6ef 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/dto/RecommendCategoryDTO.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/dto/RecommendCategoryDTO.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/entity/RecommendedCategoryEntity$RecommendedCategoryEntityBuilder.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/entity/RecommendedCategoryEntity$RecommendedCategoryEntityBuilder.class index da3b037..dfbb106 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/entity/RecommendedCategoryEntity$RecommendedCategoryEntityBuilder.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/entity/RecommendedCategoryEntity$RecommendedCategoryEntityBuilder.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/entity/RecommendedCategoryEntity.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/entity/RecommendedCategoryEntity.class index 8066846..122189e 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/entity/RecommendedCategoryEntity.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/entity/RecommendedCategoryEntity.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/entity/SpendingEntity$SpendingEntityBuilder.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/entity/SpendingEntity$SpendingEntityBuilder.class index f16f095..85c7c82 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/entity/SpendingEntity$SpendingEntityBuilder.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/entity/SpendingEntity$SpendingEntityBuilder.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/entity/SpendingEntity.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/entity/SpendingEntity.class index f3d9574..0d2f750 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/entity/SpendingEntity.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/entity/SpendingEntity.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/jpa/RecommendRepository.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/jpa/RecommendRepository.class index 8d7cc9a..82c3d53 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/jpa/RecommendRepository.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/jpa/RecommendRepository.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/jpa/SpendingRepository.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/jpa/SpendingRepository.class index e88bad4..7e476f8 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/jpa/SpendingRepository.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/repository/jpa/SpendingRepository.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/service/RecommendService.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/service/RecommendService.class index a8dc8ab..e09e005 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/service/RecommendService.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/service/RecommendService.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/service/RecommendServiceImpl.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/service/RecommendServiceImpl.class index 563cc5c..823a806 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/service/RecommendServiceImpl.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/service/RecommendServiceImpl.class differ diff --git a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/service/SpendingAnalyzer.class b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/service/SpendingAnalyzer.class index 9ea7b14..55755f6 100644 Binary files a/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/service/SpendingAnalyzer.class and b/recommend/build/classes/java/main/com/unicorn/lifesub/recommend/service/SpendingAnalyzer.class differ diff --git a/recommend/build/resolvedMainClassName b/recommend/build/resolvedMainClassName deleted file mode 100644 index 5c86a2b..0000000 --- a/recommend/build/resolvedMainClassName +++ /dev/null @@ -1 +0,0 @@ -com.unicorn.lifesub.recommend.RecommendApplication \ No newline at end of file diff --git a/recommend/build/tmp/bootJar/MANIFEST.MF b/recommend/build/tmp/bootJar/MANIFEST.MF deleted file mode 100644 index 3194a35..0000000 --- a/recommend/build/tmp/bootJar/MANIFEST.MF +++ /dev/null @@ -1,12 +0,0 @@ -Manifest-Version: 1.0 -Main-Class: org.springframework.boot.loader.launch.JarLauncher -Start-Class: com.unicorn.lifesub.recommend.RecommendApplication -Spring-Boot-Version: 3.4.0 -Spring-Boot-Classes: BOOT-INF/classes/ -Spring-Boot-Lib: BOOT-INF/lib/ -Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx -Spring-Boot-Layers-Index: BOOT-INF/layers.idx -Build-Jdk-Spec: 21 -Implementation-Title: recommend -Implementation-Version: 1.0.0 - diff --git a/recommend/build/tmp/jar/MANIFEST.MF b/recommend/build/tmp/jar/MANIFEST.MF deleted file mode 100644 index 59499bc..0000000 --- a/recommend/build/tmp/jar/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/recommend/src/main/resources/application.yml b/recommend/src/main/resources/application.yml index cb921ae..3e6a0e7 100644 --- a/recommend/src/main/resources/application.yml +++ b/recommend/src/main/resources/application.yml @@ -7,7 +7,7 @@ spring: datasource: url: jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT:5432}/${POSTGRES_DB:recommend} username: ${POSTGRES_USER:admin} - password: ${POSTGRES_PASSWORD:Passw0rd} + password: ${POSTGRES_PASSWORD:Hi5Jessica!} driver-class-name: org.postgresql.Driver # JPA 설정 jpa: