commit 6b2bc45f8321787346f8ff1b720c0b222c98b2f2 Author: thienqb123456 Date: Mon Dec 23 16:57:36 2024 +0700 nsg_jenkins vpress first commit diff --git a/be/be-acp/cd-acp-backend.Jenkinsfile b/be/be-acp/cd-acp-backend.Jenkinsfile new file mode 100644 index 0000000..4bb595c --- /dev/null +++ b/be/be-acp/cd-acp-backend.Jenkinsfile @@ -0,0 +1,229 @@ +pipeline { + agent any + parameters { + choice(name: 'ENV', choices: ['uat', 'prod'], description: 'Choose Environment') + string(name: 'CI_JOB_BUILD_NUMBER', defaultValue: '', description: 'Build number of CI Job') + } + environment { + PROJECT_NAME = 'acp' + CI_JOB_NAME = 'CI_BE_ACP' // tên của job build code + CI_JOB_METADATA_FILENAME = "${PROJECT_NAME}_metadata.json" // tên file metadata đã được lưu từ ci job + + GIT_PAT_CREDENTIALS_ID = 'd3de261f-8f1e-470b-b6d1-2fb4965e0129' // Id của Personal Access Token lưu trên jenkins + GIT_ANSIBLE_URL = 'work.gct.com.vn/thienvv/nsg_ansible.git' + GIT_ANSIBLE_BRANCH = 'v2' + + NEXUS_CREDENTIALS = credentials('Nexus_credential') + ANSIBLE_SSH_CONNECTION = 'root@103.166.183.172 -p 24700' + + ANSIBLE_FOLDER_PATH = '/srv/ansible_v2' + ANSIBLE_INVENTORY_PATH = "inventory/${params.ENV}.ini" + ANSIBLE_PLAYBOOK_PATH = 'playbooks/deploy_be.yml' + + + TELEGRAM_CHAT_ID = -4678013464 + TELEGRAM_BOT_TOKEN = credentials('TELEGRAM_BOT_TOKEN') + } + + stages { + stage('Retrieve Artifact From CI Job') { + steps { + script { + def ciJobBuildNumber = params.CI_JOB_BUILD_NUMBER + echo "Retrieving artifact from CI_JOB_BUILD_NUMBER: ${ciJobBuildNumber}" + + def metadata = handleArtifactAndMetadata(env.CI_JOB_NAME, ciJobBuildNumber, CI_JOB_METADATA_FILENAME) + + env.NEXUS_URL = metadata.nexusUrl + echo "env.NEXUS_URL : ${env.NEXUS_URL}" + + env.NEXUS_ARTIFACT_NAME = metadata.nexusArtifactName + echo "env.NEXUS_ARTIFACT_NAME : ${env.NEXUS_ARTIFACT_NAME}" + } + } + } + + stage('Checkout Ansible Code Git ') { + steps { + script { + checkoutFromGit( + env.ANSIBLE_SSH_CONNECTION, + env.GIT_ANSIBLE_URL, + env.GIT_PAT_CREDENTIALS_ID, + env.GIT_ANSIBLE_BRANCH, + env.ANSIBLE_FOLDER_PATH + ) + } + } + } + + stage('Deploy with Ansible') { + steps { + triggerAnsible( + env.ANSIBLE_SSH_CONNECTION, + env.ANSIBLE_FOLDER_PATH, + env.ANSIBLE_INVENTORY_PATH, + env.ANSIBLE_PLAYBOOK_PATH, + params.ENV, + env.PROJECT_NAME, + env.NEXUS_URL, + env.NEXUS_ARTIFACT_NAME, + env.NEXUS_CREDENTIALS_USR, + env.NEXUS_CREDENTIALS_PSW) + } + } + } + + post { + always { + script { + def message = "Build : API - Môi trường ${params.ENV} - Dự án ${env.PROJECT_NAME}, ${currentBuild.fullDisplayName}\n${env.BUILD_URL}" + sh "curl -s -X POST https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage -d chat_id=${env.TELEGRAM_CHAT_ID} -d text=\"${message}\"" + } + } + success { + script { + def message = "Build thành công : API - Môi trường ${params.ENV} - Dự án ${env.PROJECT_NAME}, ${currentBuild.fullDisplayName}\n${env.BUILD_URL}" + sh "curl -s -X POST https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage -d chat_id=${env.TELEGRAM_CHAT_ID} -d text=\"${message}\"" + } + } + failure { + script { + def message = "Build thất bại: API - Môi trường ${params.ENV} - Dự án ${env.PROJECT_NAME}, ${currentBuild.fullDisplayName}\n${env.BUILD_URL}" + sh "curl -s -X POST https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage -d chat_id=${env.TELEGRAM_CHAT_ID} -d text=\"${message}\"" + } + } + } + + +} + +//Thienvv- hàm checkout git code ansible về máy ansible +def checkoutFromGit( + String ansibleSSHConnection, + String gitUrl, + String gitAnsibleCredentialsId, + String branch, + String ansibleFolderPath) { + try { + // kiểm tra đã tồn tại code ansible trên thư mục của máy ansible + Boolean repoExists = sh( + script: """ + ssh -v ${ansibleSSHConnection} \\ + ' [ -d ${ansibleFolderPath}/.git ] && echo '1' || echo '0' ' + """, + returnStdout: true + ).trim() == '1' + + withCredentials([usernamePassword( + credentialsId: gitAnsibleCredentialsId, + usernameVariable: 'GIT_USERNAME', + passwordVariable: 'GIT_PAT')]) { + if (repoExists) { // nếu tồn tại source code ansible rồi thì pull về + echo "Repository already exists on ${ansibleFolderPath}. Pulling latest changes..." + + Integer result = sh(script: """ + ssh -v ${ansibleSSHConnection} \\ + 'cd ${ansibleFolderPath} && \\ + git reset --hard && \\ + git clean -fd && \\ + git fetch origin && \\ + git checkout ${branch} && \\ + git pull origin ${branch} -vvvv ' + """, returnStatus: true) + + if (result == 0) { + echo 'Pull ansible code successfully' + } else { + error "Pull ansible code failed, status: ${result}" + } + } else { //nếu chưa tồn tại code ansible + Integer result = sh(script: """ + ssh -v ${ansibleSSHConnection} \\ + 'git clone --branch ${branch} http://${GIT_USERNAME}@${gitUrl} ${ansibleFolderPath} -vvvv ' + """, returnStatus: true) + + if (result == 0) { + echo 'Clone ansible code successfully' + } else { + error "Clone ansible code failed, status: ${result}" + } + } + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } + } + +def handleArtifactAndMetadata(String ciJobName, String ciJobBuildNumber, String metadataFilename) { + try { + def selector = null + + if (ciJobBuildNumber != '') { // nếu ciJobBuildNumber khác rỗng thì + selector = [$class: 'SpecificBuildSelector', buildNumber: ciJobBuildNumber] // selector = ciJobBuildNumber + } else { //còn nếu ciJobBuildNumber = rỗng thì + selector = lastSuccessful() //selector = build thành công gần nhất + } + // Sao chép artifact từ dự án khác + copyArtifacts projectName: ciJobName, + selector: selector, + filter: metadataFilename, + target: '.' + + // Kiểm tra sự tồn tại của file metadata + if (!fileExists(metadataFilename)) { + error "File ${metadataFilename} not found after copyArtifacts!" + } else { + echo "File ${metadataFilename} successfully copied." + } + + // Đọc dữ liệu từ file JSON + def metadata = readJSON file: metadataFilename + + return metadata + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +def triggerAnsible( + String sshAnsibleConnection, + String ansibleansibleFolderPath, + String inventoryPath, + String playbookPath, + String deployENV, + String projectName, + String nexusUrl, + String nexusArtifactName, + String nexusUsername, + String nexusPassword) { + + try { + Integer result = sh( + script: """ + ssh ${sshAnsibleConnection} " + cd ${ansibleansibleFolderPath} && + ansible-playbook -i ${inventoryPath} ${playbookPath} \\ + -e 'deploy_env=${deployENV} \\ + project_name=${projectName} \\ + nexus_url=${nexusUrl} \\ + artifact_name=${nexusArtifactName} \\ + nexus_username=${nexusUsername} \\ + nexus_password=${nexusPassword}' -vvvv + " + """, + returnStatus: true + ) + + if (result == 0) { + echo 'triggerAnsible successfully' + } else { + error "triggerAnsible failed, status: ${result}" + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } + } diff --git a/be/be-acp/ci-acp-backend.Jenkinsfile b/be/be-acp/ci-acp-backend.Jenkinsfile new file mode 100644 index 0000000..037ab91 --- /dev/null +++ b/be/be-acp/ci-acp-backend.Jenkinsfile @@ -0,0 +1,370 @@ +pipeline { + agent any + environment { + GIT_CREDENTIALSID = 'd3de261f-8f1e-470b-b6d1-2fb4965e0129' + GIT_URL = 'http://work.gct.com.vn/anhln/ACP_2025.git' + GIT_BRANCH = 'main' + + PROJECT_NAME = 'acp' + + METADATA_FILENAME = "${PROJECT_NAME}_metadata.json" + TRIGGER_JOB_NAME = 'CD_BE_ACP' + + NUGET_CONFIG_PATH = 'NuGet.config' + APPLICATIONCORE_PATH = 'Packages' + JENKINS_BUILD_FOLDER_PATH = 'Acp.WebApi' + JENKINS_PUBLISH_FOLDER_PATH = 'publish' + + COMPRESSED_FILE_NAME = "${PROJECT_NAME}_publish.zip" // tên file nén + COMPRESSED_FILE_PATH = "${env.WORKSPACE }/${COMPRESSED_FILE_NAME}" + + NEXUS_CREDENTIALS = credentials('Nexus_credential') + + NEXUS_REPO_URL = 'https://nexus.gct.com.vn/repository/acp-backend' + GROUP_ID = 'vn.kinhtedothi' + ARTIFACT_ID = 'acp-backend-api' + PACKAGING = 'zip' + VERSION = '1.0.0' // Phiên bản cơ bản + + TIMESTAMP = new Date().format('yyyyMMdd.HH', TimeZone.getTimeZone('UTC')) + FULL_VERSION = "${env.VERSION}-${env.TIMESTAMP}-${env.BUILD_NUMBER}" // Tạo phiên bản hoàn chỉnh + } + + stages { + stage('Checkout') { + steps { + // Checkout mã nguồn từ Gitea + checkoutFromGit(env.GIT_URL as String, env.GIT_CREDENTIALSID as String, env.GIT_BRANCH as String) + } + } + + stage('Clear Nuget caches') { + steps { + // Xóa cache của NuGet + clearNuGetCaches() + } + } + + stage('Setup Nuget.Config') { + steps { + // Thiết lập tệp NuGet.config + setupNuGetConfig(env.APPLICATIONCORE_PATH as String) + } + } + + stage('Restore') { + steps { + // Gọi hàm để thực hiện restore + restoreNuGetPackages(env.JENKINS_BUILD_FOLDER_PATH as String , env.NUGET_CONFIG_PATH as String) + } + } + + stage('Clean & Build') { + steps { + script { + cleanProject(env.JENKINS_BUILD_FOLDER_PATH as String) + buildProject(env.JENKINS_BUILD_FOLDER_PATH as String) + } + } + } + + stage('Publish') { + steps { + publishProject(env.JENKINS_BUILD_FOLDER_PATH as String, env.JENKINS_PUBLISH_FOLDER_PATH as String) + } + } + + stage('Package (Compress Publish Files)') { + steps { + compressItems(env.COMPRESSED_FILE_PATH, env.JENKINS_PUBLISH_FOLDER_PATH) + } + } + + stage('Upload to Nexus') { + steps { + script { + String groupPath = env.GROUP_ID.replace('.', '/') + env.NEXUS_URL = "${env.NEXUS_REPO_URL}/${groupPath}/${env.ARTIFACT_ID}/${env.VERSION}" + env.NEXUS_ARTIFACT_NAME = "${env.ARTIFACT_ID}-${env.FULL_VERSION}.${env.PACKAGING}" + + String nexusUploadUrl = "${env.NEXUS_URL}/${env.NEXUS_ARTIFACT_NAME}" + + uploadToNexus( + env.NEXUS_CREDENTIALS_USR, + env.NEXUS_CREDENTIALS_PSW, + env.COMPRESSED_FILE_PATH, + nexusUploadUrl) + } + } + } + + stage('Create Metadata File') { + steps { + createMetadataFile( + env.METADATA_FILENAME, + env.GROUP_ID, + env.ARTIFACT_ID, + env.VERSION, + env.NEXUS_URL, + env.NEXUS_ARTIFACT_NAME) + echo "metadataFileName: ${env.METADATA_FILENAME}" + archiveArtifacts artifacts: "${env.METADATA_FILENAME}", allowEmptyArchive: false + } + } + } + + post { + always { + echo 'Pipeline execution finished.' + } + success { + echo "Job '${env.JOB_NAME}' completed successfully. Attempting to trigger Job '${TRIGGER_JOB_NAME}'..." + script { + try { + def buildResult = build job: "${TRIGGER_JOB_NAME}", parameters:[ + string(name: 'ENV', value: 'uat'), + string(name: 'CI_JOB_BUILD_NUMBER', value: env.BUILD_NUMBER) + ], + propagate: false + if (buildResult.result != 'SUCCESS') { + echo "[WARNING] Job 2 failed with result: ${buildResult.result}" + } + } + catch (Exception e) { + echo "[ERROR] Failed to trigger job: ${TRIGGER_JOB_NAME}. Error: ${e.message}" + throw e + } + } + } + } +} + +//Thienvv- hàm checkout git +void checkoutFromGit(String gitUrl, String credentialsId, String branch) { + try { + echo 'Start checkoutFromGit' + + checkout([$class : 'GitSCM', + userRemoteConfigs: [[url: gitUrl, credentialsId: credentialsId]], + branches : [[name: "*/${branch}"]]]) + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +//Thienvv- clear nuget caches +void clearNuGetCaches() { + try { + // Xóa cache của NuGet + Integer result = sh(script: 'dotnet nuget locals all --clear', returnStatus: true) + + echo "result:${result}" + + // Kiểm tra trạng thái trả về + if (result == 0) { + echo 'NuGet caches cleared successfully.' + } else { + error "Failed to clear NuGet caches with status: ${result}" + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +//Thienvv - Hàm set up file Nuget.config để restore các package dependencies +void setupNuGetConfig(String applicationCorePath) { + try { + writeFile file: 'NuGet.config', text: """ + + + + + +""" + + // Kiểm tra xem tệp đã được ghi thành công + if (fileExists('NuGet.config')) { + echo 'NuGet.config has been created successfully.' + } else { + error 'Failed to create NuGet.config.' + } + } + catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +// Thienvv - Định nghĩa hàm để restore các gói NuGet +// Thienvv - Định nghĩa hàm để restore các gói NuGet +void restoreNuGetPackages(String restorePath, String nugetConfigPath) { + echo 'Starting NuGet packages restore...' + try { + if (!fileExists(restorePath)) { + error "restorePath is not exist : ${restorePath}" + } + if (!fileExists(nugetConfigPath)) { + error "nugetConfigPath is not exist : ${nugetConfigPath}" + } + + // Thực hiện restore các gói NuGet cần thiết + Integer result = sh(script: "dotnet restore ${restorePath} --configfile ${nugetConfigPath}", returnStatus: true) + + // Kiểm tra kết quả trả về + if (result == 0) { + echo 'NuGet packages restored successfully.' + } else { + error "Restore NuGet packages failed with exit code: ${result}. Check NuGet config and restore path." + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +//Thienvv - clean project dotnet +void cleanProject(String buildPath) { + echo 'Start cleanProject' + try { + if (!fileExists(buildPath)) { + error "buildPath is not exist : ${buildPath}" + } + + Integer result = sh(script: "dotnet clean ${buildPath}", returnStatus: true) + + echo "result:${result}" + + if (result == 0) { + echo 'Clean Project successfully' + } else { + error 'Clean Projectfailed' + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +//Thienvv - build project dotnet +void buildProject(String buildPath) { + echo 'Start buildProject' + try { + if (!fileExists(buildPath)) { + error "buildPath is not exist : ${buildPath}" + } + Integer result = sh(script: "dotnet build ${buildPath} -c Release", returnStatus: true) + + echo "result:${result}" + + if (result == 0) { + echo 'Build Project successfully' + } else { + error 'Build Project failed' + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +//Thienvv - package/publish project dotnet +void publishProject(String buildPath, String publishFolderPath) { + echo 'Start publishProject' + try { + if (!fileExists(buildPath)) { + error "buildPath is not exist : ${buildPath}" + } + + Integer result = sh(script: "dotnet publish ${buildPath} -c Release -o ${publishFolderPath}", + returnStatus: true) + + echo "result : ${result}" + + if (result == 0) { + echo 'Publish Project successfully' + } else { + error 'Publish Project failed' + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +//Thienvv - nén file +void compressItems(String compressedFilePath, String parentFolderPath) { + echo "Starting compression of the entire folder into a zip file : ${compressedFilePath}" + try { + if (!fileExists(parentFolderPath)) { + error "parentFolderPath is not exist : ${parentFolderPath}" + } + + dir(parentFolderPath) { + // Thực hiện lệnh zip để nén tất cả các file trong thư mục + Integer result = sh(script: "zip -r ${compressedFilePath} * ", returnStatus: true) + + // Kiểm tra kết quả + if (result == 0) { + echo "Compression completed successfully: ${compressedFilePath}" + } else { + error "Compression failed with exit code: ${result}." + } + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +void uploadToNexus(String nexusUsername, String nexusPassword, String compressedFilePath, String uploadUrl) { + echo "Starting upload ${compressedFilePath} To Nexus" + try { + // Truyền các biến môi trường vào lệnh sh mà không lộ ra trong log + String result = sh(script: """ + curl -u ${nexusUsername}:${nexusPassword} --upload-file ${compressedFilePath} ${uploadUrl} -w '%{http_code}' + """, returnStdout: true).trim() + + if (result == '200' || result == '201') { + echo 'uploadToNexus successfully' + } else { + error "uploadToNexus failed, HTTP status: ${result}" + } + } + catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +def createMetadataFile( + String metadataFileName, + String groupId, + String artifactId, + String version, + String nexusUrl, + String nexusArtifactName) { + echo 'Starting create metadata file' + try { + // Tạo metadata + metadata = [ + groupId: groupId, + artifactId: artifactId, + version: version, + nexusUrl: nexusUrl, + nexusArtifactName: nexusArtifactName + ] + + // Ghi metadata vào file JSON + writeJSON file: "${metadataFileName}", json: metadata + + if (!fileExists(metadataFileName)) { + error "metadataFileName is not exist : ${metadataFileName}" + } + } + catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } + } diff --git a/be/be-portal/cd-portal-backend.Jenkinsfile b/be/be-portal/cd-portal-backend.Jenkinsfile new file mode 100644 index 0000000..d7265b5 --- /dev/null +++ b/be/be-portal/cd-portal-backend.Jenkinsfile @@ -0,0 +1,228 @@ +pipeline { + agent any + parameters { + choice(name: 'ENV', choices: ['uat', 'prod'], description: 'Choose Environment') + string(name: 'CI_JOB_BUILD_NUMBER', defaultValue: '', description: 'Build number of CI Job') + } + environment { + PROJECT_NAME = 'portal' + + CI_JOB_NAME = 'CI_BE_PORTAL' // tên của job CI + CI_JOB_METADATA_FILENAME = "${PROJECT_NAME}_metadata.json" // tên file metadata đã được lưu từ ci job + + NEXUS_CREDENTIALS = credentials('Nexus_credential') + + ANSIBLE_SSH_CONNECTION = 'root@103.166.183.172 -p 24700' + + GIT_PAT_CREDENTIALS_ID = 'd3de261f-8f1e-470b-b6d1-2fb4965e0129' // Id của Personal Access Token lưu trên jenkins + GIT_ANSIBLE_URL = 'work.gct.com.vn/thienvv/nsg_ansible.git' + GIT_ANSIBLE_BRANCH = 'v2' + + ANSIBLE_FOLDER_PATH = '/srv/ansible_v2' + ANSIBLE_INVENTORY_PATH = "inventory/${params.ENV}.ini" + ANSIBLE_PLAYBOOK_PATH = 'playbooks/deploy_be.yml' + + TELEGRAM_CHAT_ID = -4678013464 + TELEGRAM_BOT_TOKEN = credentials('TELEGRAM_BOT_TOKEN') + } + + stages { + stage('Retrieve Artifact From CI Job') { + steps { + script { + def ciJobBuildNumber = params.CI_JOB_BUILD_NUMBER + echo "Retrieving artifact from CI_JOB_BUILD_NUMBER: ${ciJobBuildNumber}" + + def metadata = handleArtifactAndMetadata(env.CI_JOB_NAME, ciJobBuildNumber, CI_JOB_METADATA_FILENAME) + + env.NEXUS_URL = metadata.nexusUrl + echo "env.NEXUS_URL : ${env.NEXUS_URL}" + + env.NEXUS_ARTIFACT_NAME = metadata.nexusArtifactName + echo "env.NEXUS_ARTIFACT_NAME : ${env.NEXUS_ARTIFACT_NAME}" + } + } + } + + stage('Checkout Ansible Code Git ') { + steps { + script { + checkoutFromGit( + env.ANSIBLE_SSH_CONNECTION, + env.GIT_ANSIBLE_URL, + env.GIT_PAT_CREDENTIALS_ID, + env.GIT_ANSIBLE_BRANCH, + env.ANSIBLE_FOLDER_PATH + ) + } + } + } + + stage('Deploy with Ansible') { + steps { + triggerAnsible( + env.ANSIBLE_SSH_CONNECTION, + env.ANSIBLE_FOLDER_PATH, + env.ANSIBLE_INVENTORY_PATH, + env.ANSIBLE_PLAYBOOK_PATH, + params.ENV, + env.PROJECT_NAME, + env.NEXUS_URL, + env.NEXUS_ARTIFACT_NAME, + env.NEXUS_CREDENTIALS_USR, + env.NEXUS_CREDENTIALS_PSW) + } + } + } + + post { + always { + script { + def message = "Build : API - Môi trường ${params.ENV} - Dự án ${env.PROJECT_NAME}, ${currentBuild.fullDisplayName}\n${env.BUILD_URL}" + sh "curl -s -X POST https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage -d chat_id=${env.TELEGRAM_CHAT_ID} -d text=\"${message}\"" + } + } + success { + script { + def message = "Build thành công : API - Môi trường ${params.ENV} - Dự án ${env.PROJECT_NAME}, ${currentBuild.fullDisplayName}\n${env.BUILD_URL}" + sh "curl -s -X POST https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage -d chat_id=${env.TELEGRAM_CHAT_ID} -d text=\"${message}\"" + } + } + failure { + script { + def message = "Build thất bại: API - Môi trường ${params.ENV} - Dự án ${env.PROJECT_NAME}, ${currentBuild.fullDisplayName}\n${env.BUILD_URL}" + sh "curl -s -X POST https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage -d chat_id=${env.TELEGRAM_CHAT_ID} -d text=\"${message}\"" + } + } + } +} + +def handleArtifactAndMetadata(String ciJobName, String ciJobBuildNumber, String metadataFilename) { + try { + def selector = null + + if (ciJobBuildNumber != '') { // nếu ciJobBuildNumber khác rỗng thì + selector = [$class: 'SpecificBuildSelector', buildNumber: ciJobBuildNumber] // selector = ciJobBuildNumber + } else { //còn nếu ciJobBuildNumber = rỗng thì + selector = lastSuccessful() //selector = build thành công gần nhất + } + // Sao chép artifact từ dự án khác + copyArtifacts projectName: ciJobName, + selector: selector, + filter: metadataFilename, + target: '.' + + // Kiểm tra sự tồn tại của file metadata + if (!fileExists(metadataFilename)) { + error "File ${metadataFilename} not found after copyArtifacts!" + } else { + echo "File ${metadataFilename} successfully copied." + } + + // Đọc dữ liệu từ file JSON + def metadata = readJSON file: metadataFilename + + return metadata + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +//Thienvv- hàm checkout git code ansible về máy ansible +def checkoutFromGit( + String ansibleSSHConnection, + String gitUrl, + String gitAnsibleCredentialsId, + String branch, + String ansibleFolderPath) { + try { + // kiểm tra đã tồn tại code ansible trên thư mục của máy ansible + Boolean repoExists = sh( + script: """ + ssh -v ${ansibleSSHConnection} \\ + ' [ -d ${ansibleFolderPath}/.git ] && echo '1' || echo '0' ' + """, + returnStdout: true + ).trim() == '1' + + withCredentials([usernamePassword( + credentialsId: gitAnsibleCredentialsId, + usernameVariable: 'GIT_USERNAME', + passwordVariable: 'GIT_PAT')]) { + if (repoExists) { // nếu tồn tại source code ansible rồi thì pull về + echo "Repository already exists on ${ansibleFolderPath}. Pulling latest changes..." + + Integer result = sh(script: """ + ssh -v ${ansibleSSHConnection} \\ + 'cd ${ansibleFolderPath} && \\ + git reset --hard && \\ + git clean -fd && \\ + git fetch origin && \\ + git checkout ${branch} && \\ + git pull origin ${branch} -vvvv ' + """, returnStatus: true) + + if (result == 0) { + echo 'Pull ansible code successfully' + } else { + error "Pull ansible code failed, status: ${result}" + } + } else { //nếu chưa tồn tại code ansible + Integer result = sh(script: """ + ssh -v ${ansibleSSHConnection} \\ + 'git clone --branch ${branch} http://${GIT_USERNAME}@${gitUrl} ${ansibleFolderPath} -vvvv ' + """, returnStatus: true) + + if (result == 0) { + echo 'Clone ansible code successfully' + } else { + error "Clone ansible code failed, status: ${result}" + } + } + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } + } + +def triggerAnsible( + String sshAnsibleConnection, + String ansibleansibleFolderPath, + String inventoryPath, + String playbookPath, + String deployENV, + String projectName, + String nexusUrl, + String nexusArtifactName, + String nexusUsername, + String nexusPassword) { + + try { + Integer result = sh( + script: """ + ssh ${sshAnsibleConnection} " + cd ${ansibleansibleFolderPath} && + ansible-playbook -i ${inventoryPath} ${playbookPath} \\ + -e 'deploy_env=${deployENV} \\ + project_name=${projectName} \\ + nexus_url=${nexusUrl} \\ + artifact_name=${nexusArtifactName} \\ + nexus_username=${nexusUsername} \\ + nexus_password=${nexusPassword}' -vvvv + " + """, + returnStatus: true + ) + + if (result == 0) { + echo 'triggerAnsible successfully' + } else { + error "triggerAnsible failed, status: ${result}" + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } + } diff --git a/be/be-portal/ci-portal-backend.Jenkinsfile b/be/be-portal/ci-portal-backend.Jenkinsfile new file mode 100644 index 0000000..ce9e3ec --- /dev/null +++ b/be/be-portal/ci-portal-backend.Jenkinsfile @@ -0,0 +1,375 @@ +pipeline { + agent any + environment { + GIT_CREDENTIALSID = 'd3de261f-8f1e-470b-b6d1-2fb4965e0129' + GIT_URL = 'http://work.gct.com.vn/anhln/ACP_2025.git' + GIT_BRANCH = 'main' + + PROJECT_NAME = 'portal' + + TRIGGER_JOB_NAME = 'CD_BE_PORTAL' + + METADATA_FILENAME = "${PROJECT_NAME}_metadata.json" + + NUGET_CONFIG_PATH = 'NuGet.config' + APPLICATIONCORE_PATH = 'Packages' + JENKINS_BUILD_FOLDER_PATH = 'Portal.WebApi' + JENKINS_PUBLISH_FOLDER_PATH = 'publish' + + COMPRESSED_FILE_NAME = "${PROJECT_NAME}_publish.zip" // tên file nén + COMPRESSED_FILE_PATH = "${env.WORKSPACE }/${COMPRESSED_FILE_NAME}" + + NEXUS_CREDENTIALS = credentials('Nexus_credential') + + NEXUS_REPO_URL = "https://nexus.gct.com.vn/repository/${PROJECT_NAME}-backend" + GROUP_ID = 'vn.kinhtedothi' + ARTIFACT_ID = "${PROJECT_NAME}-backend-api" + PACKAGING = 'zip' + VERSION = '1.0.0' // Phiên bản cơ bản + + TIMESTAMP = new Date().format('yyyyMMdd.HH', TimeZone.getTimeZone('UTC')) + FULL_VERSION = "${env.VERSION}-${env.TIMESTAMP}-${env.BUILD_NUMBER}" // Tạo phiên bản hoàn chỉnh + } + + stages { + stage('Checkout') { + steps { + // Checkout mã nguồn từ Gitea + checkoutFromGit(env.GIT_URL as String, env.GIT_CREDENTIALSID as String, env.GIT_BRANCH as String) + } + } + + stage('Clear Nuget caches') { + steps { + // Xóa cache của NuGet + clearNuGetCaches() + } + } + + stage('Setup Nuget.Config') { + steps { + // Thiết lập tệp NuGet.config + setupNuGetConfig(env.APPLICATIONCORE_PATH as String) + } + } + + stage('Restore') { + steps { + // Gọi hàm để thực hiện restore + restoreNuGetPackages(env.JENKINS_BUILD_FOLDER_PATH as String , env.NUGET_CONFIG_PATH as String) + } + } + + stage('Clean & Build') { + steps { + script { + cleanProject(env.JENKINS_BUILD_FOLDER_PATH as String) + buildProject(env.JENKINS_BUILD_FOLDER_PATH as String) + } + } + } + + stage('Publish') { + steps { + publishProject(env.JENKINS_BUILD_FOLDER_PATH as String, env.JENKINS_PUBLISH_FOLDER_PATH as String) + } + } + + stage('Package (Compress Publish Files)') { + steps { + compressItems(env.COMPRESSED_FILE_PATH, env.JENKINS_PUBLISH_FOLDER_PATH) + } + } + + stage('Upload to Nexus') { + steps { + script { + String groupPath = env.GROUP_ID.replace('.', '/') + env.NEXUS_URL = "${env.NEXUS_REPO_URL}/${groupPath}/${env.ARTIFACT_ID}/${env.VERSION}" + env.NEXUS_ARTIFACT_NAME = "${env.ARTIFACT_ID}-${env.FULL_VERSION}.${env.PACKAGING}" + + String nexusUploadUrl = "${env.NEXUS_URL}/${env.NEXUS_ARTIFACT_NAME}" + + uploadToNexus( + env.NEXUS_CREDENTIALS_USR, + env.NEXUS_CREDENTIALS_PSW, + env.COMPRESSED_FILE_PATH, + nexusUploadUrl) + } + } + } + + stage('Create Metadata File') { + steps { + createMetadataFile( + env.METADATA_FILENAME, + env.GROUP_ID, + env.ARTIFACT_ID, + env.VERSION, + env.NEXUS_URL, + env.NEXUS_ARTIFACT_NAME) + + echo "metadataFileName: ${env.METADATA_FILENAME}" + archiveArtifacts artifacts: "${env.METADATA_FILENAME}", allowEmptyArchive: false + } + } + } + + post { + always { + echo 'Pipeline execution finished.' + } + success { + echo "Job '${env.JOB_NAME}' completed successfully. Attempting to trigger Job '${TRIGGER_JOB_NAME}'..." + script { + try { + def buildResult = build job: "${TRIGGER_JOB_NAME}", parameters:[ + string(name: 'ENV', value: 'uat'), + string(name: 'CI_JOB_BUILD_NUMBER', value: env.BUILD_NUMBER) + ], + propagate: false + if (buildResult.result != 'SUCCESS') { + echo "[WARNING] Job 2 failed with result: ${buildResult.result}" + } + } + catch (Exception e) { + echo "[ERROR] Failed to trigger job: ${TRIGGER_JOB_NAME}. Error: ${e.message}" + throw e + } + } + } + + failure { + echo 'Pipeline failed!' + } + } +} + +//Thienvv- hàm checkout git +void checkoutFromGit(String gitUrl, String credentialsId, String branch) { + try { + echo 'Start checkoutFromGit' + + checkout([$class : 'GitSCM', + userRemoteConfigs: [[url: gitUrl, credentialsId: credentialsId]], + branches : [[name: "*/${branch}"]]]) + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +//Thienvv- clear nuget caches +void clearNuGetCaches() { + try { + // Xóa cache của NuGet + Integer result = sh(script: 'dotnet nuget locals all --clear', returnStatus: true) + + echo "result:${result}" + + // Kiểm tra trạng thái trả về + if (result == 0) { + echo 'NuGet caches cleared successfully.' + } else { + error "Failed to clear NuGet caches with status: ${result}" + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +//Thienvv - Hàm set up file Nuget.config để restore các package dependencies +void setupNuGetConfig(String applicationCorePath) { + try { + writeFile file: 'NuGet.config', text: """ + + + + + +""" + + // Kiểm tra xem tệp đã được ghi thành công + if (fileExists('NuGet.config')) { + echo 'NuGet.config has been created successfully.' + } else { + error 'Failed to create NuGet.config.' + } + } + catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +// Thienvv - Định nghĩa hàm để restore các gói NuGet +void restoreNuGetPackages(String restorePath, String nugetConfigPath) { + echo 'Starting NuGet packages restore...' + try { + if (!fileExists(restorePath)) { + error "restorePath is not exist : ${restorePath}" + } + if (!fileExists(nugetConfigPath)) { + error "nugetConfigPath is not exist : ${nugetConfigPath}" + } + + // Thực hiện restore các gói NuGet cần thiết + Integer result = sh(script: "dotnet restore ${restorePath} --configfile ${nugetConfigPath}", returnStatus: true) + + // Kiểm tra kết quả trả về + if (result == 0) { + echo 'NuGet packages restored successfully.' + } else { + error "Restore NuGet packages failed with exit code: ${result}. Check NuGet config and restore path." + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +//Thienvv - clean project dotnet +void cleanProject(String buildPath) { + echo 'Start cleanProject' + try { + if (!fileExists(buildPath)) { + error "buildPath is not exist : ${buildPath}" + } + + Integer result = sh(script: "dotnet clean ${buildPath}", returnStatus: true) + + echo "result:${result}" + + if (result == 0) { + echo 'Clean Project successfully' + } else { + error 'Clean Projectfailed' + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +//Thienvv - build project dotnet +void buildProject(String buildPath) { + echo 'Start buildProject' + try { + if (!fileExists(buildPath)) { + error "buildPath is not exist : ${buildPath}" + } + Integer result = sh(script: "dotnet build ${buildPath} -c Release", returnStatus: true) + + echo "result:${result}" + + if (result == 0) { + echo 'Build Project successfully' + } else { + error 'Build Project failed' + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +//Thienvv - package/publish project dotnet +void publishProject(String buildPath, String publishFolderPath) { + echo 'Start publishProject' + try { + if (!fileExists(buildPath)) { + error "buildPath is not exist : ${buildPath}" + } + + Integer result = sh(script: "dotnet publish ${buildPath} -c Release -o ${publishFolderPath}", + returnStatus: true) + + echo "result : ${result}" + + if (result == 0) { + echo 'Publish Project successfully' + } else { + error 'Publish Project failed' + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +//Thienvv - nén file +void compressItems(String compressedFilePath, String parentFolderPath) { + echo "Starting compression of the entire folder into a zip file : ${compressedFilePath}" + try { + if (!fileExists(parentFolderPath)) { + error "parentFolderPath is not exist : ${parentFolderPath}" + } + + dir(parentFolderPath) { + // Thực hiện lệnh zip để nén tất cả các file trong thư mục + Integer result = sh(script: "zip -r ${compressedFilePath} * ", returnStatus: true) + + // Kiểm tra kết quả + if (result == 0) { + echo "Compression completed successfully: ${compressedFilePath}" + } else { + error "Compression failed with exit code: ${result}." + } + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +void uploadToNexus(String nexusUsername, String nexusPassword, String compressedFilePath, String uploadUrl) { + echo "Starting upload ${compressedFilePath} To Nexus" + try { + // Truyền các biến môi trường vào lệnh sh mà không lộ ra trong log + String result = sh(script: """ + curl -u ${nexusUsername}:${nexusPassword} --upload-file ${compressedFilePath} ${uploadUrl} -w '%{http_code}' + """, returnStdout: true).trim() + + if (result == '200' || result == '201') { + echo 'uploadToNexus successfully' + } else { + error "uploadToNexus failed, HTTP status: ${result}" + } + } + catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +def createMetadataFile( + String metadataFileName, + String groupId, + String artifactId, + String version, + String nexusUrl, + String nexusArtifactName) { + echo 'Starting create metadata file' + try { + // Tạo metadata + metadata = [ + groupId: groupId, + artifactId: artifactId, + version: version, + nexusUrl: nexusUrl, + nexusArtifactName: nexusArtifactName + ] + + // Ghi metadata vào file JSON + writeJSON file: "${metadataFileName}", json: metadata + + if (!fileExists(metadataFileName)) { + error "metadataFileName is not exist : ${metadataFileName}" + } + } + catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } + } diff --git a/fe/fe-acp/cd-acp-frontend.Jenkinsfile b/fe/fe-acp/cd-acp-frontend.Jenkinsfile new file mode 100644 index 0000000..d3c76ef --- /dev/null +++ b/fe/fe-acp/cd-acp-frontend.Jenkinsfile @@ -0,0 +1,213 @@ +pipeline { + agent any + parameters { + choice(name: 'ENV', choices: ['uat', 'prod'], description: 'Choose Environment') + } + environment { + JOB_BUILD_NAME = 'CI_FE_ACP' // tên của job build code + METADATA_FILENAME = "${params.ENV}_metadata.json" // tên file metadata đã được lưu từ job build code + + GIT_PAT_CREDENTIALS_ID = 'd3de261f-8f1e-470b-b6d1-2fb4965e0129' // Id của Personal Access Token lưu trên jenkins + GIT_ANSIBLE_URL = 'work.gct.com.vn/thienvv/nsg_ansible.git' + GIT_ANSIBLE_BRANCH = 'v2' + + NEXUS_CREDENTIALS = credentials('Nexus_credential') + PROJECT_NAME = 'acp' + ANSIBLE_SSH_CONNECTION = 'root@103.166.183.172 -p 24700' + + ANSIBLE_FOLDER_PATH = '/srv/ansible_v2' + ANSIBLE_INVENTORY_PATH = "inventory/${params.ENV}.ini" + ANSIBLE_PLAYBOOK_PATH = 'playbooks/deploy_fe.yml' + + TELEGRAM_CHAT_ID = -4678013464 + TELEGRAM_BOT_TOKEN = credentials('TELEGRAM_BOT_TOKEN') + } + + stages { + stage('Fetch Metadata File From Job Build') { + steps { + script { + def metadata = handleArtifactAndMetadata(env.JOB_BUILD_NAME, env.METADATA_FILENAME) + env.NEXUS_URL = metadata.nexusUrl + echo "env.NEXUS_URL : ${env.NEXUS_URL}" + + env.NEXUS_ARTIFACT_NAME = metadata.nexusArtifactName + echo "env.NEXUS_ARTIFACT_NAME : ${env.NEXUS_ARTIFACT_NAME}" + } + } + } + + stage('Checkout Ansible Code Git ') { + steps { + script { + checkoutFromGit( + env.ANSIBLE_SSH_CONNECTION, + env.GIT_ANSIBLE_URL, + env.GIT_PAT_CREDENTIALS_ID, + env.GIT_ANSIBLE_BRANCH, + env.ANSIBLE_FOLDER_PATH + ) + } + } + } + + stage('Deploy with Ansible') { + steps { + triggerAnsible( + env.ANSIBLE_SSH_CONNECTION, + env.ANSIBLE_FOLDER_PATH, + env.ANSIBLE_INVENTORY_PATH, + env.ANSIBLE_PLAYBOOK_PATH, + params.ENV, + env.PROJECT_NAME, + env.NEXUS_URL, + env.NEXUS_ARTIFACT_NAME, + env.NEXUS_CREDENTIALS_USR, + env.NEXUS_CREDENTIALS_PSW) + } + } + } + + post { + always { + script { + def message = "Build : API - Môi trường ${params.ENV} - Dự án ${env.PROJECT_NAME}, ${currentBuild.fullDisplayName}\n${env.BUILD_URL}" + sh "curl -s -X POST https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage -d chat_id=${env.TELEGRAM_CHAT_ID} -d text=\"${message}\"" + } + } + success { + script { + def message = "Build thành công : API - Môi trường ${params.ENV} - Dự án ${env.PROJECT_NAME}, ${currentBuild.fullDisplayName}\n${env.BUILD_URL}" + sh "curl -s -X POST https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage -d chat_id=${env.TELEGRAM_CHAT_ID} -d text=\"${message}\"" + } + } + failure { + script { + def message = "Build thất bại: API - Môi trường ${params.ENV} - Dự án ${env.PROJECT_NAME}, ${currentBuild.fullDisplayName}\n${env.BUILD_URL}" + sh "curl -s -X POST https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage -d chat_id=${env.TELEGRAM_CHAT_ID} -d text=\"${message}\"" + } + } + } + + +} + +//Thienvv- hàm checkout git code ansible về máy ansible +def checkoutFromGit( + String ansibleSSHConnection, + String gitUrl, + String gitAnsibleCredentialsId, + String branch, + String ansibleFolderPath) { + try { + // kiểm tra đã tồn tại code ansible trên thư mục của máy ansible + Boolean repoExists = sh( + script: """ + ssh -v ${ansibleSSHConnection} \\ + ' [ -d ${ansibleFolderPath}/.git ] && echo '1' || echo '0' ' + """, + returnStdout: true + ).trim() == '1' + + withCredentials([usernamePassword( + credentialsId: gitAnsibleCredentialsId, + usernameVariable: 'GIT_USERNAME', + passwordVariable: 'GIT_PAT')]) { + if (repoExists) { // nếu tồn tại source code ansible rồi thì pull về + echo "Repository already exists on ${ansibleFolderPath}. Pulling latest changes..." + + Integer result = sh(script: """ + ssh -v ${ansibleSSHConnection} \\ + 'cd ${ansibleFolderPath} && \\ + git reset --hard && \\ + git clean -fd && \\ + git fetch origin && \\ + git checkout ${branch} && \\ + git pull origin ${branch} -vvvv ' + """, returnStatus: true) + + if (result == 0) { + echo 'Pull ansible code successfully' + } else { + error "Pull ansible code failed, status: ${result}" + } + } else { //nếu chưa tồn tại code ansible + Integer result = sh(script: """ + ssh -v ${ansibleSSHConnection} \\ + 'git clone --branch ${branch} http://${GIT_USERNAME}@${gitUrl} ${ansibleFolderPath} -vvvv ' + """, returnStatus: true) + + if (result == 0) { + echo 'Clone ansible code successfully' + } else { + error "Clone ansible code failed, status: ${result}" + } + } + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } + } + +def handleArtifactAndMetadata(String jobBuildName, String metadataFilename) { + try { + // Sao chép artifact từ dự án khác + copyArtifacts projectName: jobBuildName, + filter: metadataFilename, + target: '.' + + // Kiểm tra sự tồn tại của file metadata + if (!fileExists(metadataFilename)) { + error "File '${metadataFilename}' after copyArtifacts!" + } + + // Đọc dữ liệu từ file JSON + def metadata = readJSON file: metadataFilename + + return metadata + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +def triggerAnsible( + String sshAnsibleConnection, + String ansibleansibleFolderPath, + String inventoryPath, + String playbookPath, + String deployENV, + String projectName, + String nexusUrl, + String nexusArtifactName, + String nexusUsername, + String nexusPassword) { + + try { + Integer result = sh( + script: """ + ssh ${sshAnsibleConnection} " + cd ${ansibleansibleFolderPath} && + ansible-playbook -i ${inventoryPath} ${playbookPath} \\ + -e 'deploy_env=${deployENV} \\ + project_name=${projectName} \\ + nexus_url=${nexusUrl} \\ + artifact_name=${nexusArtifactName} \\ + nexus_username=${nexusUsername} \\ + nexus_password=${nexusPassword}' -vvvv + " + """, + returnStatus: true + ) + + if (result == 0) { + echo 'triggerAnsible successfully' + } else { + error "triggerAnsible failed, status: ${result}" + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } + } diff --git a/fe/fe-acp/ci-acp-frontend.Jenkinsfile b/fe/fe-acp/ci-acp-frontend.Jenkinsfile new file mode 100644 index 0000000..0f2a19f --- /dev/null +++ b/fe/fe-acp/ci-acp-frontend.Jenkinsfile @@ -0,0 +1,258 @@ +pipeline { + agent any + parameters { + choice(name: 'ENV', choices: ['uat', 'prod'], description: 'Choose Environment') + } + environment { + GIT_CREDENTIALSID = 'b03f36c4-bba3-4764-94ca-b620651b2a68' + GIT_URL = 'http://work.gct.com.vn/anhln/NSG_2025.git' + GIT_BRANCH = 'main' + + TRIGGER_JOB_NAME = 'CD_FE_ACP' + METADATA_FILENAME = "${params.ENV}_metadata.json" + + NUXT_BUILD_FOLDER_PATH = "${env.WORKSPACE}" + OUTPUT_FOLDER_PATH = '.output' // đường dẫn đến .output (sau khi build xong) + COMMAND_NUXT_INSTALL = 'npm install' //command install dependencies + COMMAND_NUXT_BUILD_UAT = 'yarn linux-builder' // command build + COMMAND_NUXT_BUILD_PROD = 'yarn linux-builder:production' // command build + + COMPRESSED_FILE_NAME = "${params.ENV}_output.zip" // tên file nén + COMPRESSED_FILE_PATH = "${env.WORKSPACE }/${COMPRESSED_FILE_NAME}" + + NEXUS_CREDENTIALS = credentials('Nexus_credential') + + NEXUS_REPO_URL = 'https://nexus.gct.com.vn/repository/acp-frontend' + GROUP_ID = 'vn.kinhtedothi' + ARTIFACT_ID = "${params.ENV}-acp-frontend" + PACKAGING = 'zip' + VERSION = '1.0.0' // Phiên bản cơ bản + + TIMESTAMP = new Date().format('yyyyMMdd.HH', TimeZone.getTimeZone('UTC')) + FULL_VERSION = "${env.VERSION}-${env.TIMESTAMP}-${env.BUILD_NUMBER}" // Tạo phiên bản hoàn chỉnh + } + + stages { + stage('Checkout') { + steps { + // Checkout mã nguồn từ Gitea + checkoutFromGit(env.GIT_URL as String, env.GIT_CREDENTIALSID as String, env.GIT_BRANCH as String) + } + } + + stage('Install Dependencies') { + steps { + script { + installDependencies(env.NUXT_BUILD_FOLDER_PATH as String, env.COMMAND_NUXT_INSTALL as String) + } + } + } + + stage('Build') { + steps { + script { + switch (params.ENV) { + case 'uat': + env.COMMAND_NUXT_BUILD = COMMAND_NUXT_BUILD_UAT + break + case 'prod': + env.COMMAND_NUXT_BUILD = COMMAND_NUXT_BUILD_PROD + break + default: + error "Unsupported environment: ${params.ENV}" + } + buildProject(env.NUXT_BUILD_FOLDER_PATH as String, env.COMMAND_NUXT_BUILD as String) + } + } + } + + stage('Package (Compress Publish Files)') { + steps { + compressItems(env.COMPRESSED_FILE_PATH, env.JENKINS_PUBLISH_FOLDER_PATH) + } + } + + stage('Upload to Nexus') { + steps { + script { + String groupPath = env.GROUP_ID.replace('.', '/') + env.NEXUS_URL = "${env.NEXUS_REPO_URL}/${groupPath}/${env.ARTIFACT_ID}/${env.VERSION}" + env.NEXUS_ARTIFACT_NAME = "${env.ARTIFACT_ID}-${env.FULL_VERSION}.${env.PACKAGING}" + + String nexusUploadUrl = "${env.NEXUS_URL}/${env.NEXUS_ARTIFACT_NAME}" + + uploadToNexus( + env.NEXUS_CREDENTIALS_USR, + env.NEXUS_CREDENTIALS_PSW, + env.COMPRESSED_FILE_PATH, + nexusUploadUrl) + } + } + } + + stage('Create Metadata File') { + steps { + createMetadataFile( + env.METADATA_FILENAME, + env.GROUP_ID, + env.ARTIFACT_ID, + env.VERSION, + env.NEXUS_URL, + env.NEXUS_ARTIFACT_NAME) + } + } + } + + post { + success { + echo "metadataFileName: ${env.METADATA_FILENAME}" + archiveArtifacts artifacts: "${env.METADATA_FILENAME}", allowEmptyArchive: false + + build job: "${TRIGGER_JOB_NAME}", parameters: [ + string(name: 'ENV', value: 'uat') ] } + + failure { + echo 'Pipeline failed!' + } + always { + echo 'Pipeline execution finished.' + } + } +} + +//Thienvv- hàm checkout git +void checkoutFromGit(String gitUrl, String credentialsId, String branch) { + echo 'Start checkoutFromGit' + try { + checkout([$class : 'GitSCM', + userRemoteConfigs: [[url: gitUrl, credentialsId: credentialsId]], + branches : [[name: "*/${branch}"]]]) + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +// Thienvv - hàm installDependencies +void installDependencies(String buildFolderPath, String command) { + echo 'Start installDependencies' + try { + if (!fileExists(buildFolderPath)) { + error "buildFolderPath is not exist : ${buildFolderPath}" + } + + dir("${buildFolderPath}") { + Integer result = sh(script: "${command}", returnStatus: true) + + echo "result:${result}" + + if (result == 0) { + echo 'install Dependencies successfully.' + } else { + error "install Dependencies failed with status: ${result}" + } + } + } catch (Exception e) { + error "Restore failed: ${e.message}" + } +} + +//Thienvv - build project +void buildProject(String buildFolderPath, String command) { + echo 'Start buildProject' + try { + if (!fileExists(buildFolderPath)) { + error "buildFolderPath is not exist : ${buildFolderPath}" + } + // Build dự án Nuxt.js + dir("${buildFolderPath}") { + Integer result = sh(script: "${command}", returnStatus: true) + + echo "result:${result}" + + if (result == 0) { + echo 'Build Project successfully' + } else { + error "Build Project failed, status: ${result}" + } + } + } catch (Exception e) { + error "Build Project failed: ${e.message}" + } +} + +//Thienvv - nén file +void compressItems(String compressedFilePath, String parentFolderPath) { + echo "Starting compression of the entire folder into a zip file : ${compressedFilePath}" + try { + if (!fileExists(parentFolderPath)) { + error "parentFolderPath is not exist : ${parentFolderPath}" + } + + dir(parentFolderPath) { + // Thực hiện lệnh zip để nén tất cả các file trong thư mục + Integer result = sh(script: "zip -r ${compressedFilePath} * ", returnStatus: true) + + // Kiểm tra kết quả + if (result == 0) { + echo "compressItems completed successfully: ${compressedFilePath}" + } else { + error "compressItems failed with exit code: ${result}." + } + } + } catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +void uploadToNexus(String nexusUsername, String nexusPassword, String compressedFilePath, String uploadUrl) { + echo "Starting upload ${compressedFilePath} To Nexus" + try { + // Truyền các biến môi trường vào lệnh sh mà không lộ ra trong log + String result = sh(script: """ + curl -u ${nexusUsername}:${nexusPassword} --upload-file ${compressedFilePath} ${uploadUrl} -w '%{http_code}' + """, returnStdout: true).trim() + + if (result == '200' || result == '201') { + echo 'uploadToNexus successfully' + } else { + error "uploadToNexus failed, HTTP status: ${result}" + } + } + catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +} + +void createMetadataFile( + String metadataFileName, + String groupId, + String artifactId, + String version, + String nexusUrl, + String nexusArtifactName) { + echo 'Starting create metadata file' + try { + // Tạo metadata + metadata = [ + groupId: groupId, + artifactId: artifactId, + version: version, + nexusUrl: nexusUrl, + nexusArtifactName: nexusArtifactName + ] + + // Ghi metadata vào file JSON + writeJSON file: "${metadataFileName}", json: metadata + + if (!fileExists(metadataFileName)) { + error "metadataFileName is not exist : ${metadataFileName}" + } + } + catch (Exception e) { + echo "[ERROR] Unexpected error: ${e.message}" + throw e + } +}