mm-auth-service/Jenkinsfile

288 lines
13 KiB
Plaintext
Raw Normal View History

2025-07-25 11:00:13 +00:00
// Refactored Jenkinsfile for tte-auth-app
// Standardized to match the latest CI/CD template.
import groovy.transform.Field
import groovy.json.JsonOutput
import java.text.SimpleDateFormat
// A field to accumulate log messages for the final notification.
@Field
String pipelineLog = ''
/**
* Appends a formatted message to the pipeline log for inclusion in the final notification.
* @param message The string message to log.
*/
def appendLog(String message) {
echo "[LOG] ${message}"
pipelineLog += "\n${message.trim()}"
}
/**
* Sends a rich, formatted notification to a Discord webhook using embeds.
* @param params A map of parameters including status, jobName, buildUrl, etc.
*/
def sendDiscordNotification(Map params) {
// Parameter validation
['status', 'jobName', 'buildNumber', 'buildUrl', 'webhookCredentialId'].each { key ->
if (!params[key]) {
echo "⚠️ sendDiscordNotification: Missing required parameter '${key}'"
return
}
}
def colorValue, statusEmoji
def statusText = params.status.toUpperCase()
def embedTitle
// Determine color and emoji based on build status
switch (statusText) {
case 'SUCCESS': colorValue = 0x28A745; statusEmoji = "✅"; embedTitle = "${statusEmoji} Build Successful: ${params.jobName.split('/').last()}"; break
case 'FAILURE': colorValue = 0xDC3545; statusEmoji = "❌"; embedTitle = "${statusEmoji} Build Failed: ${params.jobName.split('/').last()}"; break
case 'UNSTABLE': colorValue = 0xFFC107; statusEmoji = "⚠️"; embedTitle = "${statusEmoji} Build Unstable: ${params.jobName.split('/').last()}"; break
case 'ABORTED': colorValue = 0x6C757D; statusEmoji = "🚫"; embedTitle = "${statusEmoji} Build Aborted: ${params.jobName.split('/').last()}"; break
case 'APPROVAL_REQUEST': colorValue = 0x007BFF; statusEmoji = "🔔"; embedTitle = "${statusEmoji} Approval Required: ${params.jobName.split('/').last()}"; break
default: colorValue = 0x17A2B8; statusEmoji = ""; embedTitle = "${statusEmoji} Build Notification (${statusText}): ${params.jobName.split('/').last()}"; break
}
// Special highlighting for the production branch
if (params.branchName == 'main') {
if (!['FAILURE', 'ABORTED'].contains(statusText.toUpperCase())) {
colorValue = 0xFFD700 // Gold color for production
}
embedTitle = "❗ PRODUCTION: ${embedTitle}"
}
// Build the main description text
def descriptionLines = []
if (params.customMessage) {
descriptionLines.add(params.customMessage.stripIndent().trim())
descriptionLines.add("")
}
descriptionLines.add("**Project:** `${params.jobName.replaceAll("/", " / ")}`")
if (params.branchName) {
descriptionLines.add("**Branch:** `${params.branchName}`")
}
descriptionLines.add("**Build:** [#${params.buildNumber}](${params.buildUrl}display/redirect) - **${statusText.toUpperCase()}**")
if (params.nodeName) {
descriptionLines.add("**Node:** `${params.nodeName}`")
}
if (params.triggeredBy) {
descriptionLines.add("**Triggered by:** ${params.triggeredBy}")
}
// Add commit details
def commitDetails = ""
if (params.processedCommits && !params.processedCommits.isEmpty()) {
def commitMessages = params.processedCommits.collect { c -> " - `${c.commitId}`: ${c.message} (_${c.author}_)" }
commitDetails = "\n\n📜 **Commits:**\n" + commitMessages.join('\n')
}
descriptionLines.add(commitDetails)
def description = descriptionLines.join('\n').take(4000)
// Footer with timestamp
def footerTimeZone = TimeZone.getTimeZone(params.footerTimeZone ?: 'UTC')
def footerTimestamp = new Date(params.buildTimestamp ?: System.currentTimeMillis()).format("yyyy-MM-dd HH:mm:ss z", footerTimeZone)
// Construct the final embed payload
def embed = [
title: embedTitle.take(250),
description: description,
color: colorValue,
timestamp: new Date(params.buildTimestamp ?: System.currentTimeMillis()).format("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZone.getTimeZone('UTC')),
url: "${params.buildUrl}display/redirect",
footer: [ text: "Jenkins Build #${params.buildNumber} • ${footerTimestamp}", icon_url: params.footerIconUrl ?: params.avatarUrl ]
]
if (params.customFields) { embed.fields = params.customFields }
def payloadMap = [embeds: [embed]]
if (params.username) { payloadMap.username = params.username.take(80) }
if (params.avatarUrl) { payloadMap.avatar_url = params.avatarUrl }
def jsonPayload = groovy.json.JsonOutput.toJson(payloadMap)
withCredentials([string(credentialsId: params.webhookCredentialId, variable: 'DISCORD_WEBHOOK_URL')]) {
try {
httpRequest(url: DISCORD_WEBHOOK_URL, httpMode: 'POST', contentType: 'APPLICATION_JSON', requestBody: jsonPayload, quiet: true, timeout: 20, validResponseCodes: '200:204')
echo "Discord notification sent successfully for status: ${statusText}."
} catch (Exception e) {
echo "⚠️ Error sending Discord notification: ${e.getMessage()}"
}
}
}
pipeline {
agent any
environment {
// --- Configuration for the auth application ---
OKD_PROJECT = 'tte-applications-project'
BUILD_NAME = 'tte-auth-app'
DEPLOYMENT = 'tte-auth-app'
OKD_PROJECT_DEV = 'tte-dev-applications-project'
BUILD_NAME_DEV = 'tte-dev-auth-apps'
DEPLOYMENT_DEV = 'tte-dev-auth-apps'
// Notification settings
DISCORD_WEBHOOK_CREDENTIAL_ID = 'discord-webhook-auth'
DISCORD_BOT_USERNAME = "Jenkins CI"
DISCORD_AVATAR_URL = "https://www.jenkins.io/images/logos/jenkins/jenkins.png"
FOOTER_TIMEZONE = "Asia/Jakarta"
}
options {
timestamps()
timeout(time: 1, unit: 'HOURS')
buildDiscarder(logRotator(numToKeepStr: '10'))
}
stages {
stage('Build App for Development') {
when { branch 'dev' }
steps {
script {
appendLog("🚀 **Building App for Development** (`${env.BUILD_NAME_DEV}`)...")
try {
withCredentials([file(credentialsId: 'kubeconfig-file', variable: 'KUBECONFIG_PATH')]) {
sh """
export KUBECONFIG="\${KUBECONFIG_PATH}"
oc start-build "${BUILD_NAME_DEV}" --follow --wait=true -n "${OKD_PROJECT_DEV}"
"""
}
appendLog("✅ **Development App Build** completed successfully.")
} catch (Exception e) {
currentBuild.result = 'FAILURE'
appendLog("❌ **Development App Build FAILED**.")
error("OpenShift build for Development failed: ${e.getMessage()}")
}
}
}
}
stage('Build App for Production') {
when { branch 'main' }
steps {
script {
appendLog("🚀 **Building App for Production** (`${env.BUILD_NAME}`)...")
try {
withCredentials([file(credentialsId: 'kubeconfig-file', variable: 'KUBECONFIG_PATH')]) {
sh """
export KUBECONFIG="\${KUBECONFIG_PATH}"
oc start-build "${BUILD_NAME}" --follow --wait=true -n "${OKD_PROJECT}"
"""
}
appendLog("✅ **Production App Build** completed successfully.")
} catch (Exception e) {
currentBuild.result = 'FAILURE'
appendLog("❌ **Production App Build FAILED**.")
error("OpenShift build for Production failed: ${e.getMessage()}")
}
}
}
}
stage('Deploy to Development') {
when {
branch 'dev'
expression { currentBuild.resultIsBetterOrEqualTo('SUCCESS') }
}
steps {
script {
appendLog("🚚 **Deploy to Development** starting for `${env.DEPLOYMENT_DEV}`...")
try {
withCredentials([file(credentialsId: 'kubeconfig-file', variable: 'KUBECONFIG_PATH')]) {
sh """
export KUBECONFIG="\${KUBECONFIG_PATH}"
oc rollout restart deployment/"${DEPLOYMENT_DEV}" -n "${OKD_PROJECT_DEV}"
oc rollout status deployment/"${DEPLOYMENT_DEV}" --watch -n "${OKD_PROJECT_DEV}"
"""
}
appendLog("✅ **Deploy to Development** succeeded.")
} catch (Exception e) {
currentBuild.result = 'FAILURE'
appendLog("❌ **Deploy to Development FAILED**.")
error("Deployment to Development failed: ${e.getMessage()}")
}
}
}
}
stage('Deploy to Production') {
when {
branch 'main'
expression { currentBuild.resultIsBetterOrEqualTo('SUCCESS') }
}
steps {
script {
appendLog("🚀 **Deploy to Production** starting for `${env.DEPLOYMENT}`...")
try {
withCredentials([file(credentialsId: 'kubeconfig-file', variable: 'KUBECONFIG_PATH')]) {
sh """
export KUBECONFIG="\${KUBECONFIG_PATH}"
oc rollout restart deployment/"${DEPLOYMENT}" -n "${OKD_PROJECT}"
oc rollout status deployment/"${DEPLOYMENT}" --watch -n "${OKD_PROJECT}"
"""
}
appendLog("✅ **Deploy to Production** succeeded.")
} catch (Exception e) {
currentBuild.result = 'FAILURE'
appendLog("❌ **Deploy to Production FAILED**.")
error("Deployment to Production failed: ${e.getMessage()}")
}
}
}
}
}
post {
always {
script {
def finalStatus = currentBuild.currentResult ?: 'UNKNOWN'
def processedCommitsList = []
try {
for (changeLogSet in currentBuild.changeSets) {
for (entry in changeLogSet.items) {
processedCommitsList.add([
commitId: entry.commitId?.take(7) ?: "N/A",
message: (entry.msg?.split('\n')[0] ?: "No commit message").take(80),
author: entry.author?.displayName ?: "Unknown"
])
}
}
} catch (e) { echo "Could not get changeset: ${e.getMessage()}" }
def customFieldsList = []
if (pipelineLog) {
customFieldsList.add([
name: "📋 Log Highlights",
value: "```\n${pipelineLog.trim().take(1000)}\n```",
inline: false
])
}
def triggeredBy = "Unknown"
try { triggeredBy = currentBuild.rawBuild.getCauses().collect { it.getShortDescription() }.join(", ") } catch (e) { /* ignore */ }
sendDiscordNotification([
status: finalStatus,
jobName: env.JOB_NAME,
buildNumber: env.BUILD_NUMBER,
buildUrl: env.BUILD_URL,
webhookCredentialId: env.DISCORD_WEBHOOK_CREDENTIAL_ID,
branchName: env.BRANCH_NAME ?: "N/A",
nodeName: env.NODE_NAME ?: 'N/A',
processedCommits: processedCommitsList,
triggeredBy: triggeredBy,
username: "${env.DISCORD_BOT_USERNAME} (${env.JOB_NAME.split('/').last()})",
avatarUrl: env.DISCORD_AVATAR_URL,
footerTimeZone: env.FOOTER_TIMEZONE,
buildTimestamp: currentBuild.startTimeInMillis
])
}
}
}
}