288 lines
13 KiB
Groovy
288 lines
13 KiB
Groovy
// Refactored Jenkinsfile for mm-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 = 'mm-applications-project'
|
||
BUILD_NAME = 'mm-auth-app'
|
||
DEPLOYMENT = 'mm-auth-app'
|
||
|
||
OKD_PROJECT_DEV = 'mm-dev-applications-project'
|
||
BUILD_NAME_DEV = 'mm-dev-auth-apps'
|
||
DEPLOYMENT_DEV = 'mm-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
|
||
])
|
||
}
|
||
}
|
||
}
|
||
} |