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}:{${this.lttngModulesCommitId}, ${this.lttngToolsCommitId}, ${this.lttngUstCommitId}}"
187 def LoadPreviousIdsFromWorkspace = { ondiskpath ->
190 File myFile = new File(ondiskpath);
191 def input = new ObjectInputStream(new FileInputStream(ondiskpath))
192 previousIds = input.readObject()
195 println("Failed to load previous ids from disk.")
200 def saveCurrentIdsToWorkspace = { currentIds, ondiskpath ->
202 File myFile = new File(ondiskpath);
203 myFile.createNewFile();
204 def out = new ObjectOutputStream(new FileOutputStream(ondiskpath))
205 out.writeObject(currentIds)
208 println("Failed to save previous ids from disk.")
212 def GetHeadCommits = { remoteRepo, branchesOfInterest ->
213 def remoteHeads = [:]
214 def remoteHeadRefs = Git.lsRemoteRepository()
217 .setRemote(remoteRepo).call()
219 remoteHeadRefs.each {
220 def branch = it.getName().replaceAll('refs/heads/', '')
221 if (branchesOfInterest.contains(branch))
222 remoteHeads[branch] = it.getObjectId().name()
228 def GetTagIds = { remoteRepo ->
230 def remoteTagRefs = Git.lsRemoteRepository()
233 .setRemote(remoteRepo).call()
236 // Exclude release candidate tags
237 if (!it.getName().contains('-rc')) {
238 remoteTags[it.getName().replaceAll('refs/tags/', '')] = it.getObjectId().name()
245 def GetLastTagOfBranch = { tagRefs, branch ->
246 def tagVersions = tagRefs.collect {new VanillaKVersion(it.key)}
247 def currMax = new VanillaKVersion('v0.0.0');
248 if (!branch.contains('master')){
249 def targetVersion = new VanillaKVersion(branch.replaceAll('linux-', 'v').replaceAll('.y', ''))
251 if (it.isSameStable(targetVersion)) {
259 if (!it.isInStableBranch() && currMax < it) {
264 return currMax.toString()
267 // Returns the latest tags of each of the branches passed in the argument
268 def GetLastTagIds = { remoteRepo, branchesOfInterest ->
269 def remoteHeads = GetHeadCommits(remoteRepo, branchesOfInterest)
270 def remoteTagRefs = GetTagIds(remoteRepo)
271 def remoteLastTagCommit = [:]
273 remoteTagRefs = remoteTagRefs.findAll { !it.key.contains("v2.") }
274 branchesOfInterest.each {
275 remoteLastTagCommit[it] = remoteTagRefs[GetLastTagOfBranch(remoteTagRefs, it)]
278 return remoteLastTagCommit
281 def CraftJobName = { jobType, runConfig ->
282 return "${jobType}_k${runConfig.linuxBranch}_l${runConfig.lttngBranch}"
285 def LaunchJob = { jobName, runConfig ->
286 def job = Hudson.instance.getJob(jobName)
288 for (paramdef in job.getProperty(ParametersDefinitionProperty.class).getParameterDefinitions()) {
289 params += paramdef.getDefaultParameterValue();
292 params.add(new StringParameterValue('tools_commit_id', runConfig.lttngToolsCommitId))
293 params.add(new StringParameterValue('modules_commit_id', runConfig.lttngModulesCommitId))
294 params.add(new StringParameterValue('ust_commit_id', runConfig.lttngUstCommitId))
295 params.add(new StringParameterValue('kernel_tag_id', runConfig.linuxTagId))
296 job.scheduleBuild2(0, new Cause.UpstreamCause(build), new ParametersAction(params))
297 println "Launching job: ${HyperlinkNote.encodeTo('/' + job.url, job.fullDisplayName)}"
300 def jobTypes = ['baremetal_tests', 'vm_tests', 'baremetal_benchmarks']
301 final String toolsRepo = "https://github.com/lttng/lttng-tools.git"
302 final String modulesRepo = "https://github.com/lttng/lttng-modules.git"
303 final String ustRepo = "https://github.com/lttng/lttng-ust.git"
304 final String linuxRepo = "git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git"
306 final String toolsOnDiskPath = build.getEnvironment(listener).get('WORKSPACE') + "/on-disk-tools-ref"
307 final String modulesOnDiskPath = build.getEnvironment(listener).get('WORKSPACE') + "/on-disk-modules-ref"
308 final String ustOnDiskPath = build.getEnvironment(listener).get('WORKSPACE') + "/on-disk-ust-ref"
309 final String linuxOnDiskPath = build.getEnvironment(listener).get('WORKSPACE') + "/on-disk-linux-ref"
311 def recentLttngBranchesOfInterest = ['master', 'stable-2.10', 'stable-2.9']
312 def recentLinuxBranchesOfInterest = ['master', 'linux-4.9.y', 'linux-4.4.y']
314 def legacyLttngBranchesOfInterest = ['stable-2.7']
315 def legacyLinuxBranchesOfInterest = ['linux-3.18.y', 'linux-4.4.y']
317 // Generate configurations of interest
318 def configurationOfInterest = [] as Set
320 recentLttngBranchesOfInterest.each { lttngBranch ->
321 recentLinuxBranchesOfInterest.each { linuxBranch ->
322 configurationOfInterest.add([lttngBranch, linuxBranch])
326 legacyLttngBranchesOfInterest.each { lttngBranch ->
327 legacyLinuxBranchesOfInterest.each { linuxBranch ->
328 configurationOfInterest.add([lttngBranch, linuxBranch])
332 def lttngBranchesOfInterest = recentLttngBranchesOfInterest + legacyLttngBranchesOfInterest
333 def linuxBranchesOfInterest = recentLinuxBranchesOfInterest + legacyLinuxBranchesOfInterest
335 // For Linux branches, we look for new non-RC tags
336 def toolsHeadCommits = GetHeadCommits(toolsRepo, lttngBranchesOfInterest)
337 def modulesHeadCommits = GetHeadCommits(modulesRepo, lttngBranchesOfInterest)
338 def ustHeadCommits = GetHeadCommits(ustRepo, lttngBranchesOfInterest)
340 // For LTTng branches, we look for new commits
341 def linuxLastTagIds = GetLastTagIds(linuxRepo, linuxBranchesOfInterest)
343 // Load previously build Linux tag ids
344 def oldLinuxTags = LoadPreviousIdsFromWorkspace(linuxOnDiskPath) as Set
346 // Load previously built LTTng commit ids
347 def oldToolsHeadCommits = LoadPreviousIdsFromWorkspace(toolsOnDiskPath) as Set
348 def oldModulesHeadCommits = LoadPreviousIdsFromWorkspace(modulesOnDiskPath) as Set
349 def oldUstHeadCommits = LoadPreviousIdsFromWorkspace(ustOnDiskPath) as Set
351 def newOldLinuxTags = oldLinuxTags
352 def newOldToolsHeadCommits = oldToolsHeadCommits
353 def newOldModulesHeadCommits = oldModulesHeadCommits
354 def newOldUstHeadCommits = oldUstHeadCommits
356 def canaryRunConfigs = [] as Set
357 canaryRunConfigs.add(
358 ['v4.4.9', '1a1a512b983108015ced1e7a7c7775cfeec42d8c', 'v2.8.1','d11e0db', '7fd9215', '514a87f'] as RunConfiguration)
360 def runConfigs = [] as Set
362 // For each top of branch kernel tags that were not seen before, schedule one
363 // job for each lttng/linux tracked configurations
364 linuxLastTagIds.each { linuxTag ->
365 if (!oldLinuxTags.contains(linuxTag.value)) {
366 lttngBranchesOfInterest.each { lttngBranch ->
367 if (configurationOfInterest.contains([lttngBranch, linuxTag.key])) {
368 runConfigs.add([linuxTag.key, linuxTag.value,
369 lttngBranch, toolsHeadCommits[lttngBranch],
370 modulesHeadCommits[lttngBranch], ustHeadCommits[lttngBranch]]
373 newOldLinuxTags.add(linuxTag.value)
379 // For each top of branch commits that were not seen before, schedule one job
380 // for each lttng/linux tracked configurations
381 toolsHeadCommits.each { toolsHead ->
382 if (!oldToolsHeadCommits.contains(toolsHead.value)) {
383 linuxLastTagIds.each { linuxTag ->
384 def lttngBranch = toolsHead.key
385 if (configurationOfInterest.contains([lttngBranch, linuxTag.key])) {
386 runConfigs.add([linuxTag.key, linuxTag.value,
387 lttngBranch, toolsHeadCommits[lttngBranch],
388 modulesHeadCommits[lttngBranch], ustHeadCommits[lttngBranch]]
391 newOldToolsHeadCommits.add(toolsHead.value)
397 // For each top of branch commits that were not seen before, schedule one job
398 // for each lttng/linux tracked configurations
399 modulesHeadCommits.each { modulesHead ->
400 if (!oldModulesHeadCommits.contains(modulesHead.value)) {
401 linuxLastTagIds.each { linuxTag ->
402 def lttngBranch = modulesHead.key
403 if (configurationOfInterest.contains([lttngBranch, linuxTag.key])) {
404 runConfigs.add([linuxTag.key, linuxTag.value,
405 lttngBranch, toolsHeadCommits[lttngBranch],
406 modulesHeadCommits[lttngBranch], ustHeadCommits[lttngBranch]]
409 newOldModulesHeadCommits.add(modulesHead.value)
415 // For each top of branch commits that were not seen before, schedule one job
416 // for each lttng/linux tracked configurations
417 ustHeadCommits.each { ustHead ->
418 if (!oldUstHeadCommits.contains(ustHead.value)) {
419 linuxLastTagIds.each { linuxTag ->
420 def lttngBranch = ustHead.key
421 if (configurationOfInterest.contains([lttngBranch, linuxTag.key])) {
422 runConfigs.add([linuxTag.key, linuxTag.value,
423 lttngBranch, toolsHeadCommits[lttngBranch],
424 modulesHeadCommits[lttngBranch], ustHeadCommits[lttngBranch]]
427 newOldUstHeadCommits.add(ustHead.value)
433 // Save the tag and commit IDs scheduled in the past and during this run to the workspace
434 saveCurrentIdsToWorkspace(newOldLinuxTags, linuxOnDiskPath)
435 saveCurrentIdsToWorkspace(newOldToolsHeadCommits, toolsOnDiskPath)
436 saveCurrentIdsToWorkspace(newOldModulesHeadCommits, modulesOnDiskPath)
437 saveCurrentIdsToWorkspace(newOldUstHeadCommits, ustOnDiskPath)
440 println("Schedule canary jobs once a day")
441 canaryRunConfigs.each { config ->
442 jobTypes.each { type ->
443 LaunchJob(type + '_canary', config)
447 if (runConfigs.size() > 0) {
448 println("Schedule jobs because of code changes.")
449 runConfigs.each { config ->
450 jobTypes.each { type ->
451 LaunchJob(CraftJobName(type, config), config);
454 // Jobs to run only on master branchs of both linux and lttng
455 if (config.linuxBranch.contains('master') &&
456 config.lttngBranch.contains('master')) {
457 LaunchJob(CraftJobName('vm_tests_fuzzing', config), config)
461 println("No new commit or tags, nothing more to do.")