288 lines
13 KiB
Plaintext
288 lines
13 KiB
Plaintext
|
|
// 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
|
|||
|
|
])
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|