2 * Copyright (C) 2017 - Francis Deslauriers <francis.deslauriers@efficios.com>
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 import hudson.console.HyperlinkNote
21 import org.eclipse.jgit.api.Git
22 import org.eclipse.jgit.lib.Ref
24 class InvalidKVersionException extends Exception {
25 public InvalidKVersionException(String message) {
30 class EmptyKVersionException extends Exception {
31 public EmptyKVersionException(String message) {
36 class VanillaKVersion implements Comparable<VanillaKVersion> {
42 Integer rc = Integer.MAX_VALUE
43 Boolean inStable = false;
47 VanillaKVersion(version) {
51 static VanillaKVersion minKVersion() {
52 return new VanillaKVersion("v0.0.0")
55 static VanillaKVersion maxKVersion() {
56 return new VanillaKVersion("v" + Integer.MAX_VALUE + ".0.0")
59 static VanillaKVersion factory(version) {
60 return new VanillaKVersion(version)
68 this.rc = Integer.MAX_VALUE
71 throw new EmptyKVersionException("Empty kernel version")
74 def match = version =~ /^v(\d+)\.(\d+)(\.(\d+))?(\.(\d+))?(-rc(\d+))?$/
76 throw new InvalidKVersionException("Invalid kernel version: ${version}")
82 this.major = Integer.parseInt(match.group(1))
83 if (this.major <= 2) {
85 this.majorB = Integer.parseInt(match.group(2))
89 if (match.group(2 + offset) != null) {
90 this.minor = Integer.parseInt(match.group(2 + offset))
94 if (match.group(4 + offset) != null) {
95 this.patch = Integer.parseInt(match.group(4 + offset))
100 if (match.group(8) != null) {
101 this.rc = Integer.parseInt(match.group(8))
105 Boolean isInStableBranch() {
109 // Return true if both version are of the same stable branch
110 Boolean isSameStable(VanillaKVersion o) {
111 if (this.major != o.major) {
114 if (this.majorB != o.majorB) {
117 if (this.minor != o.minor) {
124 @Override int compareTo(VanillaKVersion o) {
125 if (this.major != o.major) {
126 return Integer.compare(this.major, o.major)
128 if (this.majorB != o.majorB) {
129 return Integer.compare(this.majorB, o.majorB)
131 if (this.minor != o.minor) {
132 return Integer.compare(this.minor, o.minor)
134 if (this.patch != o.patch) {
135 return Integer.compare(this.patch, o.patch)
137 if (this.rc != o.rc) {
138 return Integer.compare(this.rc, o.rc)
146 String vString = "v${this.major}"
148 if (this.majorB > 0) {
149 vString = vString.concat(".${this.majorB}")
152 vString = vString.concat(".${this.minor}")
154 if (this.patch > 0) {
155 vString = vString.concat(".${this.patch}")
158 if (this.rc > 0 && this.rc < Integer.MAX_VALUE) {
159 vString = vString.concat("-rc${this.rc}")
165 class RunConfiguration {
169 def lttngModulesCommitId
170 def lttngToolsCommitId
172 RunConfiguration(linuxBranch, linuxTagId, lttngBranch, lttngToolsCommitId,
173 lttngModulesCommitId, lttngUstCommitId) {
174 this.linuxBranch = linuxBranch
175 this.linuxTagId = linuxTagId
176 this.lttngBranch = lttngBranch
177 this.lttngModulesCommitId = lttngModulesCommitId
178 this.lttngToolsCommitId = lttngToolsCommitId
179 this.lttngUstCommitId = lttngUstCommitId
183 return "${this.linuxBranch}:{${this.linuxTagId}}, ${this.lttngBranch}" +
184 ":{${this.lttngModulesCommitId}, ${this.lttngToolsCommitId}," +
185 "${this.lttngUstCommitId}}"
189 def LoadPreviousIdsFromWorkspace = { ondiskpath ->
192 File myFile = new File(ondiskpath);
193 def input = new ObjectInputStream(new FileInputStream(ondiskpath))
194 previousIds = input.readObject()
197 println("Failed to load previous ids from disk.")
202 def saveCurrentIdsToWorkspace = { currentIds, ondiskpath ->
204 File myFile = new File(ondiskpath);
205 myFile.createNewFile();
206 def out = new ObjectOutputStream(new FileOutputStream(ondiskpath))
207 out.writeObject(currentIds)
210 println("Failed to save previous ids from disk.")
214 def GetHeadCommits = { remoteRepo, branchesOfInterest ->
215 def remoteHeads = [:]
216 def remoteHeadRefs = Git.lsRemoteRepository()
219 .setRemote(remoteRepo).call()
221 remoteHeadRefs.each {
222 def branch = it.getName().replaceAll('refs/heads/', '')
223 if (branchesOfInterest.contains(branch))
224 remoteHeads[branch] = it.getObjectId().name()
230 def GetTagIds = { remoteRepo ->
232 def remoteTagRefs = Git.lsRemoteRepository()
235 .setRemote(remoteRepo).call()
238 // Exclude release candidate tags
239 if (!it.getName().contains('-rc')) {
240 remoteTags[it.getName().replaceAll('refs/tags/', '')] = it.getObjectId().name()
247 def GetLastTagOfBranch = { tagRefs, branch ->
248 def tagVersions = tagRefs.collect {new VanillaKVersion(it.key)}
249 def currMax = new VanillaKVersion('v0.0.0');
250 if (!branch.contains('master')){
251 def targetVersion = new VanillaKVersion(branch.replaceAll('linux-', 'v').replaceAll('.y', ''))
253 if (it.isSameStable(targetVersion)) {
261 if (!it.isInStableBranch() && currMax < it) {
266 return currMax.toString()
269 // Returns the latest tags of each of the branches passed in the argument
270 def GetLastTagIds = { remoteRepo, branchesOfInterest ->
271 def remoteHeads = GetHeadCommits(remoteRepo, branchesOfInterest)
272 def remoteTagRefs = GetTagIds(remoteRepo)
273 def remoteLastTagCommit = [:]
275 remoteTagRefs = remoteTagRefs.findAll { !it.key.contains("v2.") }
276 branchesOfInterest.each {
277 remoteLastTagCommit[it] = remoteTagRefs[GetLastTagOfBranch(remoteTagRefs, it)]
280 return remoteLastTagCommit
283 def CraftJobName = { jobType, runConfig ->
284 return "${jobType}_k${runConfig.linuxBranch}_l${runConfig.lttngBranch}"
287 def LaunchJob = { jobName, runConfig ->
288 def job = Hudson.instance.getJob(jobName)
290 for (paramdef in job.getProperty(ParametersDefinitionProperty.class).getParameterDefinitions()) {
291 params += paramdef.getDefaultParameterValue();
294 params.add(new StringParameterValue('tools_commit_id', runConfig.lttngToolsCommitId))
295 params.add(new StringParameterValue('modules_commit_id', runConfig.lttngModulesCommitId))
296 params.add(new StringParameterValue('ust_commit_id', runConfig.lttngUstCommitId))
297 params.add(new StringParameterValue('kernel_tag_id', runConfig.linuxTagId))
298 job.scheduleBuild2(0, new Cause.UpstreamCause(build), new ParametersAction(params))
299 println "Launching job: ${HyperlinkNote.encodeTo('/' + job.url, job.fullDisplayName)}"
302 def jobTypes = ['baremetal_tests', 'vm_tests', 'baremetal_benchmarks']
303 final String toolsRepo = "https://github.com/lttng/lttng-tools.git"
304 final String modulesRepo = "https://github.com/lttng/lttng-modules.git"
305 final String ustRepo = "https://github.com/lttng/lttng-ust.git"
306 final String linuxRepo = "git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git"
308 final String toolsOnDiskPath = build.getEnvironment(listener).get('WORKSPACE') + "/on-disk-tools-ref"
309 final String modulesOnDiskPath = build.getEnvironment(listener).get('WORKSPACE') + "/on-disk-modules-ref"
310 final String ustOnDiskPath = build.getEnvironment(listener).get('WORKSPACE') + "/on-disk-ust-ref"
311 final String linuxOnDiskPath = build.getEnvironment(listener).get('WORKSPACE') + "/on-disk-linux-ref"
313 def recentLttngBranchesOfInterest = ['master', 'stable-2.10', 'stable-2.9']
314 def recentLinuxBranchesOfInterest = ['master', 'linux-4.9.y', 'linux-4.4.y']
316 def legacyLttngBranchesOfInterest = ['stable-2.7']
317 def legacyLinuxBranchesOfInterest = ['linux-3.18.y', 'linux-4.4.y']
319 // Generate configurations of interest
320 def configurationOfInterest = [] as Set
322 recentLttngBranchesOfInterest.each { lttngBranch ->
323 recentLinuxBranchesOfInterest.each { linuxBranch ->
324 configurationOfInterest.add([lttngBranch, linuxBranch])
328 legacyLttngBranchesOfInterest.each { lttngBranch ->
329 legacyLinuxBranchesOfInterest.each { linuxBranch ->
330 configurationOfInterest.add([lttngBranch, linuxBranch])
334 def lttngBranchesOfInterest = recentLttngBranchesOfInterest + legacyLttngBranchesOfInterest
335 def linuxBranchesOfInterest = recentLinuxBranchesOfInterest + legacyLinuxBranchesOfInterest
337 // For Linux branches, we look for new non-RC tags
338 def toolsHeadCommits = GetHeadCommits(toolsRepo, lttngBranchesOfInterest)
339 def modulesHeadCommits = GetHeadCommits(modulesRepo, lttngBranchesOfInterest)
340 def ustHeadCommits = GetHeadCommits(ustRepo, lttngBranchesOfInterest)
342 // For LTTng branches, we look for new commits
343 def linuxLastTagIds = GetLastTagIds(linuxRepo, linuxBranchesOfInterest)
345 // Load previously build Linux tag ids
346 def oldLinuxTags = LoadPreviousIdsFromWorkspace(linuxOnDiskPath) as Set
348 // Load previously built LTTng commit ids
349 def oldToolsHeadCommits = LoadPreviousIdsFromWorkspace(toolsOnDiskPath) as Set
350 def oldModulesHeadCommits = LoadPreviousIdsFromWorkspace(modulesOnDiskPath) as Set
351 def oldUstHeadCommits = LoadPreviousIdsFromWorkspace(ustOnDiskPath) as Set
353 def newOldLinuxTags = oldLinuxTags
354 def newOldToolsHeadCommits = oldToolsHeadCommits
355 def newOldModulesHeadCommits = oldModulesHeadCommits
356 def newOldUstHeadCommits = oldUstHeadCommits
358 def canaryRunConfigs = [] as Set
359 canaryRunConfigs.add(
360 ['v4.4.9', '1a1a512b983108015ced1e7a7c7775cfeec42d8c', 'v2.8.1','d11e0db', '7fd9215', '514a87f'] as RunConfiguration)
362 def runConfigs = [] as Set
364 // For each top of branch kernel tags that were not seen before, schedule one
365 // job for each lttng/linux tracked configurations
366 linuxLastTagIds.each { linuxTag ->
367 if (!oldLinuxTags.contains(linuxTag.value)) {
368 lttngBranchesOfInterest.each { lttngBranch ->
369 if (configurationOfInterest.contains([lttngBranch, linuxTag.key])) {
370 runConfigs.add([linuxTag.key, linuxTag.value,
371 lttngBranch, toolsHeadCommits[lttngBranch],
372 modulesHeadCommits[lttngBranch], ustHeadCommits[lttngBranch]]
375 newOldLinuxTags.add(linuxTag.value)
381 // For each top of branch commits of LTTng-Tools that were not seen before,
382 // schedule one job for each lttng/linux tracked configurations
383 toolsHeadCommits.each { toolsHead ->
384 if (!oldToolsHeadCommits.contains(toolsHead.value)) {
385 linuxLastTagIds.each { linuxTag ->
386 def lttngBranch = toolsHead.key
387 if (configurationOfInterest.contains([lttngBranch, linuxTag.key])) {
388 runConfigs.add([linuxTag.key, linuxTag.value,
389 lttngBranch, toolsHeadCommits[lttngBranch],
390 modulesHeadCommits[lttngBranch], ustHeadCommits[lttngBranch]]
393 newOldToolsHeadCommits.add(toolsHead.value)
399 // For each top of branch commits of LTTng-Modules that were not seen before,
400 // schedule one job for each lttng/linux tracked configurations
401 modulesHeadCommits.each { modulesHead ->
402 if (!oldModulesHeadCommits.contains(modulesHead.value)) {
403 linuxLastTagIds.each { linuxTag ->
404 def lttngBranch = modulesHead.key
405 if (configurationOfInterest.contains([lttngBranch, linuxTag.key])) {
406 runConfigs.add([linuxTag.key, linuxTag.value,
407 lttngBranch, toolsHeadCommits[lttngBranch],
408 modulesHeadCommits[lttngBranch], ustHeadCommits[lttngBranch]]
411 newOldModulesHeadCommits.add(modulesHead.value)
417 // For each top of branch commits of LTTng-UST that were not seen before,
418 // schedule one job for each lttng/linux tracked configurations
419 ustHeadCommits.each { ustHead ->
420 if (!oldUstHeadCommits.contains(ustHead.value)) {
421 linuxLastTagIds.each { linuxTag ->
422 def lttngBranch = ustHead.key
423 if (configurationOfInterest.contains([lttngBranch, linuxTag.key])) {
424 runConfigs.add([linuxTag.key, linuxTag.value,
425 lttngBranch, toolsHeadCommits[lttngBranch],
426 modulesHeadCommits[lttngBranch], ustHeadCommits[lttngBranch]]
429 newOldUstHeadCommits.add(ustHead.value)
435 // Save the tag and commit IDs scheduled in the past and during this run to the
437 saveCurrentIdsToWorkspace(newOldLinuxTags, linuxOnDiskPath)
438 saveCurrentIdsToWorkspace(newOldToolsHeadCommits, toolsOnDiskPath)
439 saveCurrentIdsToWorkspace(newOldModulesHeadCommits, modulesOnDiskPath)
440 saveCurrentIdsToWorkspace(newOldUstHeadCommits, ustOnDiskPath)
443 println("Schedule canary jobs once a day")
444 canaryRunConfigs.each { config ->
445 jobTypes.each { type ->
446 LaunchJob(type + '_canary', config)
450 if (runConfigs.size() > 0) {
451 println("Schedule jobs because of code changes.")
452 runConfigs.each { config ->
453 jobTypes.each { type ->
454 LaunchJob(CraftJobName(type, config), config);
457 // Jobs to run only on master branchs of both linux and lttng
458 if (config.linuxBranch.contains('master') &&
459 config.lttngBranch.contains('master')) {
460 LaunchJob(CraftJobName('vm_tests_fuzzing', config), config)
464 println("No new commit or tags, nothing more to do.")