-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathJenkinsfile
More file actions
240 lines (219 loc) · 8.43 KB
/
Copy pathJenkinsfile
File metadata and controls
240 lines (219 loc) · 8.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
pipeline {
agent any
options {
ansiColor('xterm')
timeout(time: 60, unit: 'MINUTES')
}
environment {
// === Gerrit settings ===
GERRIT_USER = "jenkins"
GERRIT_HOST = "10.175.2.49"
GERRIT_PORT = "29418"
TARGET_BRANCH = "master"
GERRIT_KEY = "/var/lib/jenkins/.ssh/gerrit_jenkins"
// Simulated PR metadata (for manual test)
CHANGE_ID = "101" // Fake PR ID
CHANGE_BRANCH = "feature/test-pr"
CHANGE_TARGET = "master"
// === GitHub repo details ===
// org/repo format
GITHUB_REPO = "sivaprakash123/python_gmail_smtp"
// === SonarQube (Jenkins Global Tool + Server) ===
// SONARQUBE_ENV = Jenkins "Server authentication token" name (Manage Jenkins > Configure System > SonarQube servers)
SONARQUBE_ENV = "sonarqube-server"
// SONAR_SCANNER = Jenkins Tool name for SonarScanner (Manage Jenkins > Global Tool Configuration)
SONAR_SCANNER = "SonarScanner"
// === Jira ===
JIRA_BASE = "https://karmayogibharat.atlassian.net"
}
stages {
stage('Preflight (PR context)') {
steps {
script {
// In Multibranch Pipeline, these exist only for PR builds
if (!env.CHANGE_ID) {
error "This job must run on a Pull Request build (env.CHANGE_ID is empty)."
}
echo "✅ PR detected: #${env.CHANGE_ID} | from ${env.CHANGE_BRANCH} -> ${env.CHANGE_TARGET}"
}
}
}
stage('Checkout PR code') {
steps {
// Multibranch already checks out the right ref; ensure we’re on the PR revision
checkout scm
sh 'git log -1 --pretty=oneline'
}
}
stage('SonarQube Analysis') {
steps {
script {
// Works for any tech using the standalone scanner
withSonarQubeEnv("${SONARQUBE_ENV}") {
def scannerHome = tool name: "${SONAR_SCANNER}", type: 'hudson.plugins.sonar.SonarRunnerInstallation'
// Tune paths as needed; default scans repo root
sh """
"/var/lib/jenkins/sonar-scanner-7.3.0.5189-linux-x64/bin/sonar-scanner" \
-Dsonar.projectKey=${GITHUB_REPO.replaceAll('/','_')} \
-Dsonar.projectName=${GITHUB_REPO} \
-Dsonar.sources=. \
-Dsonar.host.url=${SONAR_HOST_URL} \
-Dsonar.login=${SONAR_TOKEN} \
-Dsonar.pullrequest.key=${CHANGE_ID} \
-Dsonar.pullrequest.branch=${CHANGE_BRANCH} \
-Dsonar.pullrequest.base=${CHANGE_TARGET}
"""
}
}
}
}
stage('Enforce Sonar Quality Gate') {
steps {
// Wait up to ~10 min for the CE task
timeout(time: 10, unit: 'MINUTES') {
script {
def qg = waitForQualityGate() // requires SonarQube Scanner for Jenkins plugin
echo "🔎 Quality Gate: ${qg.status}"
if (qg.status != 'OK') {
error "❌ Failing build due to Quality Gate = ${qg.status}"
}
}
}
}
}
stage('Ensure Gerrit Change-Id') {
steps {
script {
echo "➡️ Checking Change-Id in last commit…"
def hasId = sh(script: "git log -1 | grep -E '^\\s*Change-Id:' || true", returnStdout: true).trim()
if (!hasId) {
echo "⚠️ No Change-Id in tip commit — amending with a generated one."
sh '''
git config user.name "Jenkins"
git config user.email "jenkins@local"
msg=$(git log -1 --pretty=%B)
git commit --amend -m "${msg}\n\nChange-Id: I$(uuidgen | tr -d '-')"
'''
} else {
echo "✅ ${hasId}"
}
}
}
}
stage('Push to Gerrit for Review') {
steps {
script {
echo "📤 Pushing patchset to Gerrit with topic=PR-${CHANGE_ID}"
sh """
git remote remove gerrit || true
git remote add gerrit ssh://${GERRIT_USER}@${GERRIT_HOST}:${GERRIT_PORT}/${GITHUB_REPO}.git
GIT_SSH_COMMAND="ssh -i ${GERRIT_KEY} -o StrictHostKeyChecking=no" \
git push gerrit HEAD:refs/for/${TARGET_BRANCH}%topic=PR-${CHANGE_ID}
"""
}
}
}
stage('Wait for Gerrit +2 / Verified +1') {
steps {
script {
echo "⏳ Waiting up to 30 minutes for Gerrit approvals on topic=PR-${CHANGE_ID}…"
timeout(time: 30, unit: 'MINUTES') {
waitUntil {
def json = sh(
script: """
ssh -p ${GERRIT_PORT} -i ${GERRIT_KEY} -o StrictHostKeyChecking=no \
${GERRIT_USER}@${GERRIT_HOST} \
"gerrit query --format=JSON topic:PR-${CHANGE_ID} --current-patch-set"
""",
returnStdout: true
).trim()
echo "📄 Gerrit query raw: ${json}"
// Extract highest votes
def codeReview = sh(
script: "echo '${json}' | jq -r '.currentPatchSet.approvals[]? | select(.type==\"Code-Review\") | .value' | sort -nr | head -1",
returnStdout: true
).trim()
def verified = sh(
script: "echo '${json}' | jq -r '.currentPatchSet.approvals[]? | select(.type==\"Verified\") | .value' | sort -nr | head -1",
returnStdout: true
).trim()
echo "🔎 Gerrit votes → Code-Review=${codeReview ?: '∅'} | Verified=${verified ?: '∅'}"
return (codeReview == "2" && verified == "1")
}
}
echo "✅ Gerrit approved (+2 / +1)."
}
}
}
stage('Merge GitHub PR') {
steps {
script {
echo "🔁 Merging GitHub PR #${CHANGE_ID}"
withCredentials([string(credentialsId: 'gerrit-github-token', variable: 'GITHUB_TOKEN')]) {
def http = sh(
script: """
curl -s -o /tmp/merge_resp.json -w "%{http_code}" \
-X PUT \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
-H "Accept: application/vnd.github.v3+json" \
-d '{ "merge_method": "squash" }' \
https://api.github.com/repos/${GITHUB_REPO}/pulls/${CHANGE_ID}/merge
""",
returnStdout: true
).trim()
echo "GitHub merge HTTP=${http}"
sh "echo '--- merge response ---'; cat /tmp/merge_resp.json || true"
if (http != "200") {
error "❌ GitHub merge failed (HTTP ${http})."
}
}
echo "🎉 PR #${CHANGE_ID} merged."
}
}
}
stage('Update Jira Ticket') {
steps {
script {
// Try to find JIRA key in PR title and latest commit message
def prTitle = sh(
script: "git log -1 --pretty=%s",
returnStdout: true
).trim()
def fromCommit = sh(
script: "git log -1 --pretty=%B | grep -oE '[A-Z]+-[0-9]+' || true",
returnStdout: true
).trim()
def fromTitle = sh(
script: "printf '%s' ${shQuote(prTitle)} | grep -oE '[A-Z]+-[0-9]+' || true",
returnStdout: true
).trim()
def jiraKey = (fromCommit ?: fromTitle)
if (jiraKey) {
echo "📎 Jira key detected: ${jiraKey}"
withCredentials([usernamePassword(credentialsId: 'jira-api-creds', usernameVariable: 'JIRA_USER', passwordVariable: 'JIRA_TOKEN')]) {
def basic = sh(script: "echo -n '${JIRA_USER}:${JIRA_TOKEN}' | base64", returnStdout: true).trim()
// Transition ID "31" must match your workflow (e.g., to 'Done' or 'Ready for QA')
sh """
curl -s -o /tmp/jira_resp.json -w "%{http_code}" \
-X POST \
-H "Authorization: Basic ${basic}" \
-H "Content-Type: application/json" \
-d '{ "transition": { "id": "31" } }' \
${JIRA_BASE}/rest/api/3/issue/${jiraKey}/transitions
"""
sh "echo '--- jira response ---'; cat /tmp/jira_resp.json || true"
}
echo "🎯 Jira ${jiraKey} transitioned."
} else {
echo "⚠️ No Jira key pattern found in commit subject/body or PR title."
}
}
}
}
}
post {
failure {
echo "❌ Pipeline failed. Check SonarQube/Gerrit/GitHub/Jira logs above."
}
}
}