diff --git a/Jenkins/Jenkinsfile-CD b/Jenkins/Jenkinsfile-CD
new file mode 100644
index 0000000000..17d6130344
--- /dev/null
+++ b/Jenkins/Jenkinsfile-CD
@@ -0,0 +1,62 @@
+pipeline {
+ agent any
+
+ parameters {
+ string(name: 'eureka-services', defaultValue: 'main')
+ string(name: 'admin-server', defaultValue: 'main')
+ string(name: 'zipkin', defaultValue: 'main')
+ string(name: 'api-gateway', defaultValue: 'main')
+ string(name: 'customers-services', defaultValue: 'main')
+ string(name: 'genai-services', defaultValue: 'main')
+ string(name: 'vets-services', defaultValue: 'main')
+ string(name: 'visits-services', defaultValue: 'main')
+ }
+
+ environment {
+ REPO_URL = 'https://github.com/vominh-source/spring-petclinic-microservices.git'
+ IMAGE_NAME = '22127475/devops-project02'
+ }
+
+
+
+ stages {
+ stage('Deploy services') {
+ steps {
+ script {
+ services = [
+ [name: 'eureka-services', branch: params.eureka-services],
+ [name: 'admin-server', branch: params.admin-server],
+ [name: 'zipkin', branch: params.zipkin],
+ [name: 'api-gateway', branch: params.api-gateway]
+ [name: 'customers-services', branch: params.customers-services],
+ [name: 'visits-services', branch: params.visits-services],
+ [name: 'vets-services', branch: params.vets-services],
+ [name: 'genai-services', branch: params.genai-services]
+ ]
+
+ tags = []
+ for (service in services) {
+ tags.append(tag.branch == 'main' ? 'latest' : sh(script: "git ls-remote ${REPO_URL} refs/heads/${services.branch} | cut -f1", returnStdout: true).trim())
+
+ echo "tags: ${tags[-1]}"
+ }
+ }
+ }
+ }
+
+ stage('Pull images') {
+ steps {
+ script {
+ for (tag in tags)
+ sh "docker image pull ${IMAGE_NAME}:${tag}"
+ echo "pulled image: ${IMAGE_NAME}:${tag}"
+ }
+ }
+ }
+
+
+
+ }
+
+
+}
diff --git a/Jenkins/Jenkinsfile-CI b/Jenkins/Jenkinsfile-CI
new file mode 100644
index 0000000000..c24a0fc8c0
--- /dev/null
+++ b/Jenkins/Jenkinsfile-CI
@@ -0,0 +1,94 @@
+// Jenkinsfile
+pipeline {
+ agent any
+ // {
+ // label '!built-in'
+ // }
+
+
+ options {
+ timeout(time: 90, unit: 'MINUTES')
+ buildDiscarder(logRotator(numToKeepStr: '10'))
+ disableConcurrentBuilds()
+ }
+
+ environment {
+ DOCKERHUB_CREDENTIALS_ID = 'dockerhub'
+ DOCKER_REGISTRY = '22127146'
+ }
+
+ stages {
+ stage('Prepare Workspace') {
+ steps {
+ echo 'Cleaning workspace...'
+ cleanWs()
+ checkout scm
+ }
+ }
+
+ stage('Initialize') {
+ steps {
+ script {
+ // Lấy commit ID
+ if (env.GIT_COMMIT) {
+ env.COMMIT_ID = env.GIT_COMMIT
+ } else {
+ //env.COMMIT_ID = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
+ env.COMMIT_ID = bat(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
+ }
+ // In thông tin
+ env.BRANCH_NAME = env.BRANCH_NAME
+ echo "Building branch: ${env.BRANCH_NAME}"
+ echo "Commit ID: ${env.COMMIT_ID}"
+ echo "Docker Hub Credentials ID: ${env.DOCKERHUB_CREDENTIALS_ID}"
+ echo "Docker Registry (for image prefix): ${env.DOCKER_REGISTRY}"
+ }
+ }
+ }
+
+ stage('Build and Push Images to Docker Hub') {
+ steps {
+ script{
+ docker.withRegistry("https://index.docker.io/v1/", env.DOCKERHUB_CREDENTIALS_ID){
+ try{
+
+ def commit_id = env.COMMIT_ID
+
+ if (env.BRANCH_NAME == 'main') {
+ commit_id = 'latest'
+ }
+
+ def mvnCommand = "./mvnw.cmd clean install -P buildDocker -DskipTests "+
+ "-Ddocker.image.prefix=${env.DOCKER_REGISTRY} "+
+ "-Ddocker.image.tag.commit=${commit_id} "+
+ "-Dcontainer.build.extraarg=\"--push\""
+
+ echo "Executing Maven command on Windows to build images: ${mvnCommand}"
+ //sh mvnCommand // Thực thi lệnh build
+ bat mvnCommand
+
+ echo "Maven build completed successfully."
+ }
+ catch (e) {
+ echo "Error building images via Maven: ${e.getMessage()}"
+ error(message: "Failed to build images via Maven")
+ }
+ }
+ }
+ }
+ }
+ }
+
+ post {
+ always {
+ echo 'CI Pipeline finished.'
+ }
+ success {
+ // Cập nhật thông báo thành công
+ echo "Successfully built, tagged (${env.COMMIT_ID}), and pushed images for branch ${env.BRANCH_NAME}"
+ }
+ failure {
+ echo "Pipeline failed for commit ${env.COMMIT_ID} on branch ${env.BRANCH_NAME}"
+ }
+ }
+}
diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000000..6d4f44291a
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,144 @@
+pipeline {
+ agent any
+
+ stages {
+ stage('Checkout') {
+ steps {
+ checkout scm
+ }
+ }
+
+ stage('Determine Build') {
+ steps {
+ script {
+ // Lấy danh sách các file đã thay đổi
+ env.CHANGED_FILES = sh(returnStdout: true, script: 'git diff --name-only HEAD^ HEAD').trim()
+ // Xác định service nào cần build/test
+ def servicesToBuild = determineService(env.CHANGED_FILES.readLines())
+
+ // Kiểm tra xem có thay đổi nào nằm ngoài các thư mục service không
+ def changedOutsideServices = env.CHANGED_FILES.readLines().any { filePath ->
+ !servicesToBuild.any { service -> filePath.contains(service) }
+ }
+
+ if (changedOutsideServices) {
+ env.SERVICES_TO_BUILD = "all" // Build tất cả nếu có thay đổi bên ngoài
+ } else {
+ env.SERVICES_TO_BUILD = servicesToBuild // Sử dụng kết quả từ hàm determineServices
+ }
+
+ echo "Services to build: ${env.SERVICES_TO_BUILD}" // In ra để kiểm tra
+ }
+ }
+ }
+
+ stage('Test') {
+ when {
+ expression { env.SERVICES_TO_BUILD != null }
+ }
+ steps {
+ script {
+ if (env.SERVICES_TO_BUILD instanceof String && env.SERVICES_TO_BUILD != 'all') {
+ // Test 1 service
+ echo "Testing service: ${env.SERVICES_TO_BUILD}"
+ sh "./mvnw -f ${env.SERVICES_TO_BUILD}/pom.xml test"
+
+ // JUnit report
+ junit(
+ testResults: "${env.SERVICES_TO_BUILD}/target/surefire-reports/*.xml",
+ allowEmptyResults: true
+ )
+
+ } else if (env.SERVICES_TO_BUILD == 'all'){
+ // Test all services
+ echo "Testing all services"
+ sh "./mvnw test"
+
+ // JUnit report
+ junit(
+ testResults: "**/target/surefire-reports/*.xml",
+ allowEmptyResults: true
+ )
+
+ }
+ }
+ }
+ }
+
+ stage('Code Coverage') {
+ when {
+ expression { env.SERVICES_TO_BUILD != null }
+ }
+ steps {
+ script {
+ if (env.SERVICES_TO_BUILD instanceof String && env.SERVICES_TO_BUILD != 'all') {
+ // Code coverage cho 1 service
+ echo "Generating code coverage for service: ${env.SERVICES_TO_BUILD}"
+ sh "./mvnw -f ${env.SERVICES_TO_BUILD}/pom.xml org.jacoco:jacoco-maven-plugin:report"
+ recordCoverage(
+ tools: [[parser: 'JACOCO', pattern: "${env.SERVICES_TO_BUILD}/target/site/jacoco/**/*.xml"]]
+ )
+ } else if(env.SERVICES_TO_BUILD == 'all'){
+ // Test all services
+ echo "Generating code coverage for all services"
+ sh "./mvnw org.jacoco:jacoco-maven-plugin:report"
+ recordCoverage(
+ tools: [[parser: 'JACOCO', pattern: "**/target/site/jacoco/**/*.xml"]]
+ )
+
+ }
+ }
+ }
+ }
+
+ stage('Build') {
+ when {
+ expression { env.SERVICES_TO_BUILD != null }
+ }
+ steps {
+ script {
+ if (env.SERVICES_TO_BUILD instanceof String && env.SERVICES_TO_BUILD != 'all') {
+ // Build 1 service
+ echo "Building service: ${env.SERVICES_TO_BUILD}"
+ sh "./mvnw -f ${env.SERVICES_TO_BUILD}/pom.xml clean install -DskipTests"
+ archiveArtifacts artifacts: "${env.SERVICES_TO_BUILD}/target/*.jar"
+
+ } else if (env.SERVICES_TO_BUILD == 'all'){
+ // Build all services
+ echo "Building all services"
+ sh "./mvnw clean install -DskipTests"
+ archiveArtifacts artifacts: "**/target/*.jar"
+
+ }
+ }
+ }
+ }
+ }
+
+ post {
+ always {
+ echo "Finished pipeline"
+ }
+ }
+}
+
+// Function to determine which services to build/test
+def determineService(changedFiles) {
+ println "Changed Files: ${changedFiles}"
+ def services = [
+ 'spring-petclinic-customers-service',
+ 'spring-petclinic-vets-service',
+ 'spring-petclinic-visits-service',
+ 'spring-petclinic-api-gateway',
+ 'spring-petclinic-admin-server'
+ ]
+ // Không cần danh sách servicesToBuild nữa, vì chỉ cần tìm một
+
+ for (service in services) {
+ if (changedFiles.any { it.contains(service) }) {
+ return service // Trả về ngay khi tìm thấy service đầu tiên
+ }
+ }
+
+ return null // Trả về null nếu không tìm thấy
+}
diff --git a/README.md b/README.md
index 34fe9a69d9..88a5044dea 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Distributed version of the Spring PetClinic Sample Application built with Spring Cloud and Spring AI
+# Update for webhook
[](https://github.com/spring-petclinic/spring-petclinic-microservices/actions/workflows/maven-build.yml)
[](https://opensource.org/licenses/Apache-2.0)
diff --git a/pom.xml b/pom.xml
index 27b99d2a4a..11ee731023 100644
--- a/pom.xml
+++ b/pom.xml
@@ -151,6 +151,25 @@
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.10
+
+
+
+ prepare-agent
+
+
+
+ report
+ verify
+
+ report
+
+
+
+
@@ -186,7 +205,7 @@
${container.platform}
${container.build.extraarg}
-t
- ${docker.image.prefix}/${project.artifactId}
+ ${docker.image.prefix}/${project.artifactId}:${docker.image.tag.commit}
${project.build.directory}