pipeline { agent any parameters { choice(name: 'ENV', choices: ['uat', 'production'], description: 'Choose Environment') choice(name: 'PORTAL_NAME', choices: ['ktdt', 'hnt'], description: 'Choose SITE NAME') string(name: 'CI_JOB_BUILD_NUMBER', defaultValue: '', description: 'Build number of CI Job') } environment { PROJECT_NAME = 'portal' CI_JOB_NAME_UAT = 'CI-UAT-FE-PORTAL' // tên của job build code môi trường uat CI_JOB_NAME_PRODUCTION = 'CI-PRODUCTION-FE-PORTAL' // tên của job build code môi trường prod CI_JOB_METADATA_FILENAME = "${env.ENV}_${env.PROJECT_NAME}_${params.PORTAL_NAME}_metadata.json" // tên file metadata đã được lưu từ job build code NEXUS_CREDENTIALS = credentials('Nexus_credential') ANSIBLE_SSH_CONNECTION = 'root@103.166.183.172 -p 24700' GIT_PAT_CREDENTIALS_ID = 'b03f36c4-bba3-4764-94ca-b620651b2a68' // 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 = 'v3' ANSIBLE_FOLDER_PATH = '/srv/ansible_v3' SLACK_TOKEN = credentials('SLACK_TOKEN') } stages { stage('Set up') { steps { script { if (params.ENV == 'uat') { env.ANSIBLE_INVENTORY_PATH = 'inventory/uat/hosts.ini' } else if (params.ENV == 'production') { env.ANSIBLE_INVENTORY_PATH = 'inventory/production/hosts.ini' } echo "ANSIBLE_INVENTORY_PATH is set to: ${env.ANSIBLE_INVENTORY_PATH}" if (params.PORTAL_NAME == 'ktdt') { env.ANSIBLE_PLAYBOOK_PATH = 'playbooks/frontend/deploy_ktdt_portal_frontend.yml' } else if (params.PORTAL_NAME == 'hnt') { env.ANSIBLE_PLAYBOOK_PATH = 'playbooks/frontend/deploy_hnt_portal_frontend.yml' } echo "ANSIBLE_PLAYBOOK_PATH is set to: ${env.ANSIBLE_PLAYBOOK_PATH}" } } } stage('Retrieve Artifact Metadata From CI Job') { steps { script { switch (params.ENV) { case 'uat': env.CI_JOB_NAME = env.CI_JOB_NAME_UAT break case 'production': env.CI_JOB_NAME = env.CI_JOB_NAME_PRODUCTION break default: error "Unsupported environment: ${params.ENV}" } def ciJobBuildNumber = params.CI_JOB_BUILD_NUMBER echo "Retrieving artifact from CI_JOB_BUILD_NUMBER: ${ciJobBuildNumber}" def metadata = retrieveArtifactMetadataFromCIJOB( env.CI_JOB_NAME, ciJobBuildNumber, env.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, env.NEXUS_URL, env.NEXUS_ARTIFACT_NAME, env.NEXUS_CREDENTIALS_USR, env.NEXUS_CREDENTIALS_PSW) } } } post { always { echo 'Pipeline execution finished.' } success { script { def message = "✅Deploy thành công : FRONTEND - Môi trường ${params.ENV} - Dự án ${env.PROJECT_NAME} - Portal_Name ${params.PORTAL_NAME}, ${currentBuild.fullDisplayName}\n${env.BUILD_URL}" sh """curl -X POST -H 'Content-type: application/json' --data '{"text":"${message}"}' https://hooks.slack.com/services/T08TS987F4N/B08TS9SC2QN/${SLACK_TOKEN}""" } } failure { script { def message = "❌Deploy thất bại: FRONTEND - Môi trường ${params.ENV} - Dự án ${env.PROJECT_NAME} - Portal_Name ${params.PORTAL_NAME}, ${currentBuild.fullDisplayName}\n${env.BUILD_URL}" sh """curl -X POST -H 'Content-type: application/json' --data '{"text":"${message}"}' https://hooks.slack.com/services/T08TS987F4N/B08TS9SC2QN/${SLACK_TOKEN}""" } } } } //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 } } //Hàm lấy artifact metadata file từ CI job def retrieveArtifactMetadataFromCIJOB(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 nexusUrl, String nexusArtifactName, String nexusUsername, String nexusPassword) { try { Integer result = sh( script: """ ssh ${sshAnsibleConnection} " cd ${ansibleansibleFolderPath} && ansible-playbook -i ${inventoryPath} ${playbookPath} \\ -e '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 } }