mm-auth-service/Jenkinsfile

288 lines
13 KiB
Groovy
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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
])
}
}
}
}