-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontinuousIntegrationPipeline.groovy
More file actions
196 lines (166 loc) · 8.09 KB
/
continuousIntegrationPipeline.groovy
File metadata and controls
196 lines (166 loc) · 8.09 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
import static groovy.json.JsonOutput.toJson
import static groovy.json.JsonOutput.prettyPrint
import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
import java.util.regex.Pattern
def boolean onlyDocumentationFilesChangedIn(String workDirectory) {
if (!env.CHANGE_TARGET) {
echo "CHANGE_TARGET not set. Skipping check"
return false
}
def changedFiles = sh(script: "cd ${workDirectory} && git diff --name-only origin/${env.CHANGE_TARGET} origin/${env.BRANCH_NAME}", returnStdout: true).trim().split("\n")
echo "Changed files: ${changedFiles}" // Debug
return changedFiles && changedFiles.every { it.endsWith(".md") || it.endsWith(".txt") }
}
def call(Map pipelineParams = [:]) {
properties([
disableConcurrentBuilds(abortPrevious: true),
buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '2', daysToKeepStr: '', numToKeepStr: '5')),
gitLabConnection('gitlab.eclipse.org'),
[$class: 'RebuildSettings', autoRebuild: false, rebuildDisabled: false],
[$class: 'JobLocalConfiguration', changeReasonComment: '']
])
// Populate keys that are not set with default parameters
def defaultParameters = [
toolchain: [ jdk: "temurin-jdk17-latest", maven: "apache-maven-3.9.6" ],
buildType: "install",
sonar: [ enable: false, projectKey: null, tokenId: null, exclusions: null ],
pushArtifacts: true
]
pipelineParams = defaultParameters << pipelineParams
stage ("Pipeline parameters check") {
// Print effective pipeline parameters for debugging
echo "Pipeline parameters:"
println prettyPrint(toJson(pipelineParams))
// Check buildType is valid string, either "install" or "deploy"
assert pipelineParams.buildType instanceof String
assert pipelineParams.buildType.equals("install") || pipelineParams.buildType.equals("deploy")
// Check toolchain option is set and valid
def valid_jdks = [ "temurin-jdk17-latest" ]
def valid_mavens = [ "apache-maven-3.9.6" ]
assert pipelineParams.toolchain
assert pipelineParams.toolchain.jdk instanceof String
assert pipelineParams.toolchain.maven instanceof String
assert valid_jdks.contains(pipelineParams.toolchain.jdk)
assert valid_mavens.contains(pipelineParams.toolchain.maven)
// Check sonar configuration is set and valid
assert pipelineParams.sonar
assert pipelineParams.sonar.enable instanceof Boolean
// If sonar is enabled, ensure required fields are not empty
if (pipelineParams.sonar.enable) {
assert pipelineParams.sonar.projectKey instanceof String
assert pipelineParams.sonar.tokenId instanceof String
assert !pipelineParams.sonar.projectKey.isEmpty() : "sonar.projectKey cannot be empty when sonar is enabled"
assert !pipelineParams.sonar.tokenId.isEmpty() : "sonar.tokenId cannot be empty when sonar is enabled"
// Validate that projectKey contains only safe characters
assert pipelineParams.sonar.projectKey.matches(/^[a-zA-Z0-9_-]+$/) : "sonar.projectKey contains invalid characters. Only alphanumeric, underscores and hyphens are allowed"
// Validate that exclusions don't contain potentially dangerous characters
assert pipelineParams.sonar.exclusions instanceof String
assert !pipelineParams.sonar.exclusions.matches(/.*[;&|`$(){}\[\]\"].*/) : "sonar.exclusions contains potentially dangerous characters"
}
}
stage ("Checkout repository") {
dir("workdir") {
checkout scm
}
}
// Skip build if target branch is a documentation branch
if (env.CHANGE_TARGET && env.CHANGE_TARGET.startsWith("docs-")) {
echo "Skipping build for documentation branch"
currentBuild.result = 'SUCCESS'
return
}
// Skip build if only documentation files (i.e. *.md and *.txt) have changed
if (onlyDocumentationFilesChangedIn("workdir")) {
echo "Skipping build for documentation changes"
currentBuild.result = 'SUCCESS'
return
}
stage ("Build") {
// Only perform deploy if on main branch
def mavenBuildType = pipelineParams.buildType
if (!env.BRANCH_IS_PRIMARY && pipelineParams.buildType.equals("deploy")) {
echo "Skipping deploy for non-main branch"
mavenBuildType = "install"
}
timeout(time: 2, unit: 'HOURS') {
dir("workdir") {
withMaven(
jdk: pipelineParams.toolchain.jdk,
maven: pipelineParams.toolchain.maven,
options: [artifactsPublisher(disabled: true)]
) {
sh "mvn clean ${mavenBuildType}"
}
}
}
}
stage ("Deploy on Nexus Repository") {
// Call uploadPackages only if we are on the default branch,
// if we have DEB packages to upload and if the user has set the pushArtifacts parameter to true
if (env.BRANCH_IS_PRIMARY && pipelineParams.pushArtifacts) {
echo "Uploading DEB packages..."
def distribPom = readMavenPom file: 'workdir/distrib/pom.xml'
def repoDistribution = distribPom.properties['kura.repo.distribution']
def repoModule = distribPom.properties['kura.repo.module']
uploadPackages(repoDistribution, repoModule)
} else {
echo "Skipping DEB packages upload."
Utils.markStageSkippedForConditional(STAGE_NAME)
}
}
stage ("Archive artifacts") {
dir("workdir") {
archiveArtifacts allowEmptyArchive: true, artifacts: '**/*.deb'
}
}
stage ("Sonar scan") {
if (pipelineParams.sonar.enable) {
timeout(time: 2, unit: 'HOURS') {
dir("workdir") {
withMaven(jdk: 'temurin-jdk17-latest', maven: 'apache-maven-3.9.6', options: [artifactsPublisher(disabled: true)]) {
withSonarQubeEnv( credentialsId: pipelineParams.sonar.tokenId ) {
// Check if on primary branch
def analysisParameters = ""
if (isPullRequest()) {
analysisParameters = "-Dsonar.pullrequest.branch=${env.CHANGE_BRANCH} -Dsonar.pullrequest.base=${env.CHANGE_TARGET} -Dsonar.pullrequest.key=${env.CHANGE_ID}"
} else {
analysisParameters = "-Dsonar.branch.name=${env.BRANCH_NAME}"
}
sh """
mvn sonar:sonar \
-Dmaven.test.failure.ignore=true \
-Dsonar.organization=eclipse-kura \
-Dsonar.host.url=${SONAR_HOST_URL} \
-Dsonar.java.binaries='target/' \
${analysisParameters} \
-Dsonar.core.codeCoveragePlugin=jacoco \
-Dsonar.projectKey=${pipelineParams.sonar.projectKey} \
-Dsonar.exclusions=${pipelineParams.sonar.exclusions}
"""
}
}
}
}
} else {
echo "Sonar scan disabled. Skipping"
Utils.markStageSkippedForConditional(STAGE_NAME)
}
}
stage('Sonar quality gate') {
if (pipelineParams.sonar.enable) {
sleep(30) // Wait for Sonar to complete its scan
timeout(time: 1, unit: 'MINUTES') {
def qg = waitForQualityGate()
if (qg.status != 'OK') {
error "Pipeline aborted due to sonar quality gate failure: ${qg.status}"
}
}
} else {
echo "Sonar scan disabled. Skipping"
Utils.markStageSkippedForConditional(STAGE_NAME)
}
}
}
private Boolean isPullRequest(){
return env.CHANGE_ID
}