2 * Copyright (C) 2016 - Michael Jeanson <mjeanson@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/>.
19 import hudson.AbortException
20 import hudson.console.HyperlinkNote
21 import java.util.concurrent.CancellationException
22 import org.eclipse.jgit.api.Git
23 import org.eclipse.jgit.lib.Ref
26 class kVersion implements Comparable<kVersion> {
32 Integer rc = Integer.MAX_VALUE;
45 this.rc = Integer.MAX_VALUE
47 def match = version =~ /^v(\d+)\.(\d+)(\.(\d+))?(\.(\d+))?(-rc(\d+))?$/
49 throw new Exception("Invalid kernel version: ${version}")
55 this.major = Integer.parseInt(match.group(1))
56 if (this.major <= 2) {
58 this.majorB = Integer.parseInt(match.group(2))
62 if (match.group(2 + offset) != null) {
63 this.minor = Integer.parseInt(match.group(2 + offset))
67 if (match.group(4 + offset) != null) {
68 this.patch = Integer.parseInt(match.group(4 + offset))
72 if (match.group(8) != null) {
73 this.rc = Integer.parseInt(match.group(8))
77 // Return true if this version is a release candidate
79 return this.rc != Integer.MAX_VALUE
82 @Override int compareTo(kVersion o) {
83 if (this.major != o.major) {
84 return Integer.compare(this.major, o.major);
86 if (this.majorB != o.majorB) {
87 return Integer.compare(this.majorB, o.majorB);
89 if (this.minor != o.minor) {
90 return Integer.compare(this.minor, o.minor);
92 if (this.patch != o.patch) {
93 return Integer.compare(this.patch, o.patch);
95 if (this.rc != o.rc) {
96 return Integer.compare(this.rc, o.rc);
104 String vString = "v${this.major}"
106 if (this.majorB > 0) {
107 vString = vString.concat(".${this.majorB}")
110 vString = vString.concat(".${this.minor}")
112 if (this.patch > 0) {
113 vString = vString.concat(".${this.patch}")
116 if (this.rc > 0 && this.rc < Integer.MAX_VALUE) {
117 vString = vString.concat("-rc${this.rc}")
124 // Retrieve parameters of the current build
125 def mversion = build.buildVariableResolver.resolve('mversion')
126 def maxConcurrentBuild = build.buildVariableResolver.resolve('maxConcurrentBuild')
127 def kgitrepo = build.buildVariableResolver.resolve('kgitrepo')
128 def kverfloor = new kVersion(build.buildVariableResolver.resolve('kverfloor'))
129 def job = Hudson.instance.getJob(build.buildVariableResolver.resolve('kbuildjob'))
130 def currentJobName = build.project.getFullDisplayName()
132 // Get the out variable
133 def config = new HashMap()
134 def bindings = getBinding()
135 config.putAll(bindings.getVariables())
136 def out = config['out']
138 def jlc = new jenkins.model.JenkinsLocationConfiguration()
139 def jenkinsUrl = jlc.url
141 // Get tags from git repository
142 def refs = Git.lsRemoteRepository().setTags(true).setRemote(kgitrepo).call();
144 // Get kernel versions to build
148 def match = ref.getName() =~ /^refs\/tags\/(v[\d\.]+(-rc(\d+))?)$/
151 def v = new kVersion(match.group(1))
153 if (v >= kverfloor) {
166 // If the last RC version is newer than the last stable, add it to the build list
167 if (kversionsRC.last() > kversions.last()) {
168 kversions.add(kversionsRC.last())
172 println "Building the following kernel versions:"
173 for (k in kversions) {
177 // Debug: Stop build here
178 //throw new InterruptedException()
180 def joburl = HyperlinkNote.encodeTo('/' + job.url, job.fullDisplayName)
183 def ongoingBuild = []
186 def similarJobQueued = 0;
188 // Loop while we have kernel versions remaining or jobs running
189 while ( kversions.size() != 0 || ongoingBuild.size() != 0 ) {
191 if(ongoingBuild.size() < maxConcurrentBuild.toInteger() && kversions.size() != 0) {
192 def kversion = kversions.pop()
194 new StringParameterValue('mversion', mversion),
195 new StringParameterValue('kversion', kversion.toString()),
196 new StringParameterValue('kgitrepo', kgitrepo),
199 // Launch the parametrized build
200 def param_build = job.scheduleBuild2(0, new Cause.UpstreamCause(build), new ParametersAction(job_params))
201 println "triggering ${joburl} for the ${mversion} branch on kernel ${kversion}"
203 // Add it to the ongoing build queue
204 ongoingBuild.push(param_build)
208 println "Waiting... Queued: " + kversions.size() + " Running: " + ongoingBuild.size()
212 if (e in InterruptedException) {
213 build.setResult(hudson.model.Result.ABORTED)
214 throw new InterruptedException()
220 // Check for queued similar job since we only want to run latest
221 // as Mathieu Desnoyers requirement
222 similarJobQueued = Hudson.instance.queue.items.count{it.task.getFullDisplayName() == currentJobName}
223 if ( similarJobQueued > 0 ) {
224 // Abort since new build is queued
225 build.setResult(hudson.model.Result.ABORTED)
226 throw new InterruptedException()
229 def i = ongoingBuild.iterator()
230 while ( i.hasNext() ) {
231 currentBuild = i.next()
232 if ( currentBuild.isCancelled() || currentBuild.isDone() ) {
237 def matrixParent = currentBuild.get()
238 allBuilds.add(matrixParent)
239 def kernelStr = matrixParent.buildVariableResolver.resolve("kversion")
240 println "${matrixParent.fullDisplayName} (${kernelStr}) completed with status ${matrixParent.result}"
242 // Process child runs of matrixBuild
243 def childRuns = matrixParent.getRuns()
244 for ( childRun in childRuns ) {
245 println "\t${childRun.fullDisplayName} (${kernelStr}) completed with status ${childRun.result}"
246 if (childRun.result != Result.SUCCESS) {
247 failedRuns.add(childRun)
256 // Get log of failed runs
257 for (failedRun in failedRuns) {
258 println "---START---"
259 failedRun.writeWholeLogTo(out)
263 println "---Build report---"
264 for (b in allBuilds) {
265 def kernelStr = b.buildVariableResolver.resolve("kversion")
266 println "${b.fullDisplayName} (${kernelStr}) completed with status ${b.result}"
271 // Mark this build failed if any child build has failed
273 build.getExecutor().interrupt(Result.FAILURE)