82ee9dc5ae
* stable-2.16: Use reliable check for empty env.GERRIT_CHANGE_NUMBER Do not build on stable-2.14 branch Change-Id: If60fc67b15f4b5090cbee6dd9c65f4dbe4b5fcba
334 lines
11 KiB
Groovy
334 lines
11 KiB
Groovy
#!/usr/bin/env groovy
|
|
|
|
// Copyright (C) 2019 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
import groovy.json.JsonSlurper
|
|
import groovy.json.JsonOutput
|
|
|
|
class Globals {
|
|
static final String gerritUrl = "https://gerrit-review.googlesource.com/"
|
|
static final String gerritCredentialsId = "gerrit-review.googlesource.com"
|
|
static final long curlTimeout = 10000
|
|
static final int waitForResultTimeout = 10000
|
|
static final String gerritRepositoryNameSha1Suffix = "-a6a0e4682515f3521897c5f950d1394f4619d928"
|
|
static final resTicks = [ 'ABORTED':'\u26aa', 'SUCCESS':'\u2705', 'FAILURE':'\u274c' ]
|
|
}
|
|
|
|
class Change {
|
|
static String sha1 = ""
|
|
static String number = ""
|
|
static String branch = ""
|
|
static String ref = ""
|
|
static String patchNum = ""
|
|
static String url = ""
|
|
}
|
|
|
|
class Build {
|
|
String url
|
|
String result
|
|
|
|
Build(url, result) {
|
|
this.url = url
|
|
this.result = result
|
|
}
|
|
}
|
|
|
|
class Builds {
|
|
static Set<String> modes = []
|
|
static Build codeStyle = null
|
|
static Map verification = [:]
|
|
}
|
|
|
|
class GerritCheck {
|
|
String uuid
|
|
String changeNum
|
|
String sha1
|
|
Build build
|
|
|
|
GerritCheck(name, changeNum, sha1, build) {
|
|
this.uuid = "gerritforge:" + name.replaceAll("(bazel/)", "") +
|
|
Globals.gerritRepositoryNameSha1Suffix
|
|
this.changeNum = changeNum
|
|
this.sha1 = sha1
|
|
this.build = build
|
|
}
|
|
|
|
def getCheckResultFromBuild() {
|
|
switch(build.result) {
|
|
case 'SUCCESS':
|
|
return "SUCCESSFUL"
|
|
case 'NOT_BUILT':
|
|
case 'ABORTED':
|
|
return "NOT_STARTED"
|
|
case 'FAILURE':
|
|
case 'UNSTABLE':
|
|
default:
|
|
return "FAILED"
|
|
}
|
|
}
|
|
|
|
def createCheckPayload() {
|
|
return JsonOutput.toJson([
|
|
checker_uuid: uuid,
|
|
state: getCheckResultFromBuild(),
|
|
url: "${build.url}consoleText"
|
|
])
|
|
}
|
|
}
|
|
|
|
def hasChangeNumber() {
|
|
env.GERRIT_CHANGE_NUMBER?.trim()
|
|
}
|
|
|
|
def postCheck(check) {
|
|
def gerritPostUrl = Globals.gerritUrl +
|
|
"a/changes/${check.changeNum}/revisions/${check.sha1}/checks"
|
|
|
|
try {
|
|
def json = check.createCheckPayload()
|
|
httpRequest(httpMode: 'POST', authentication: Globals.gerritCredentialsId,
|
|
contentType: 'APPLICATION_JSON', requestBody: json,
|
|
validResponseCodes: '200', url: gerritPostUrl)
|
|
echo "----------------------------------------------------------------------------"
|
|
echo "Gerrit Check: ${check.uuid}=" + check.build.result + " to change " +
|
|
check.changeNum + "/" + check.sha1
|
|
echo "----------------------------------------------------------------------------"
|
|
} catch(Exception e) {
|
|
echo "ERROR> Failed to post check results to Gerrit: ${e}"
|
|
}
|
|
}
|
|
|
|
def queryChangedFiles(url, changeNum, sha1) {
|
|
def queryUrl = "${url}changes/${Change.number}/revisions/${Change.sha1}/files/"
|
|
def response = httpRequest queryUrl
|
|
def files = response.getContent().substring(5)
|
|
def filesJson = new JsonSlurper().parseText(files)
|
|
return filesJson.keySet().findAll { it != "/COMMIT_MSG" }
|
|
}
|
|
|
|
def queryChange(){
|
|
def requestedChangeId = env.BRANCH_NAME.split('/')[1]
|
|
def queryUrl = "${Globals.gerritUrl}changes/${requestedChangeId}/?pp=0&O=3"
|
|
def response = httpRequest queryUrl
|
|
def jsonSlurper = new JsonSlurper()
|
|
return jsonSlurper.parseText(response.getContent().substring(5))
|
|
}
|
|
|
|
def getChangeMetaData(){
|
|
def changeJson = queryChange()
|
|
Change.sha1 = changeJson.current_revision
|
|
Change.number = changeJson._number
|
|
Change.branch = changeJson.branch
|
|
def revision = changeJson.revisions.get(Change.sha1)
|
|
Change.ref = revision.ref
|
|
Change.patchNum = revision._number
|
|
Change.url = Globals.gerritUrl + "#/c/" + Change.number + "/" + Change.patchNum
|
|
}
|
|
|
|
def collectBuildModes() {
|
|
Builds.modes = ["notedb"]
|
|
def changedFiles = queryChangedFiles(Globals.gerritUrl, Change.number, Change.sha1)
|
|
def polygerritFiles = changedFiles.findAll { it.startsWith("polygerrit-ui") ||
|
|
it.startsWith("lib/js") }
|
|
|
|
if(polygerritFiles.size() > 0) {
|
|
if(changedFiles.size() == polygerritFiles.size()) {
|
|
println "Only PolyGerrit UI changes detected, skipping other test modes..."
|
|
Builds.modes = ["polygerrit"]
|
|
} else {
|
|
println "PolyGerrit UI changes detected, adding 'polygerrit' validation..."
|
|
Builds.modes += "polygerrit"
|
|
}
|
|
} else if(changedFiles.contains("WORKSPACE")) {
|
|
println "WORKSPACE file changes detected, adding 'polygerrit' validation..."
|
|
Builds.modes += "polygerrit"
|
|
}
|
|
}
|
|
|
|
def prepareBuildsForMode(buildName, mode="notedb", retryTimes = 1) {
|
|
return {
|
|
stage("${buildName}/${mode}") {
|
|
def slaveBuild = null
|
|
for (int i = 1; i <= retryTimes; i++) {
|
|
try {
|
|
slaveBuild = build job: "${buildName}", parameters: [
|
|
string(name: 'REFSPEC', value: Change.ref),
|
|
string(name: 'BRANCH', value: Change.sha1),
|
|
string(name: 'CHANGE_URL', value: Change.url),
|
|
string(name: 'MODE', value: mode),
|
|
string(name: 'TARGET_BRANCH', value: Change.branch)
|
|
], propagate: false
|
|
} finally {
|
|
if (buildName == "Gerrit-codestyle"){
|
|
Builds.codeStyle = new Build(
|
|
slaveBuild.getAbsoluteUrl(), slaveBuild.getResult())
|
|
} else {
|
|
Builds.verification[mode] = new Build(
|
|
slaveBuild.getAbsoluteUrl(), slaveBuild.getResult())
|
|
}
|
|
if (slaveBuild.getResult() == "SUCCESS") {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
def collectBuilds() {
|
|
def builds = [:]
|
|
if (hasChangeNumber()) {
|
|
builds["Gerrit-codestyle"] = prepareBuildsForMode("Gerrit-codestyle")
|
|
Builds.modes.each {
|
|
builds["Gerrit-verification(${it})"] = prepareBuildsForMode("Gerrit-verifier-bazel", it)
|
|
}
|
|
} else {
|
|
builds["java8"] = { -> build "Gerrit-bazel-${env.BRANCH_NAME}" }
|
|
|
|
if (env.BRANCH_NAME == "master") {
|
|
builds["java11"] = { -> build "Gerrit-bazel-java11-${env.BRANCH_NAME}" }
|
|
}
|
|
}
|
|
return builds
|
|
}
|
|
|
|
def findFlakyBuilds() {
|
|
def flaky = Builds.verification.findAll { it.value.result == null ||
|
|
it.value.result != 'SUCCESS' }
|
|
|
|
if(flaky.size() == Builds.verification.size()) {
|
|
return []
|
|
}
|
|
|
|
def retryBuilds = []
|
|
flaky.each {
|
|
def mode = it.key
|
|
Builds.verification = Builds.verification.findAll { it.key != mode }
|
|
retryBuilds += mode
|
|
}
|
|
|
|
return retryBuilds
|
|
}
|
|
|
|
def getLabelValue(acc, res) {
|
|
if(res == null || res == 'ABORTED') {
|
|
return 0
|
|
}
|
|
switch(acc) {
|
|
case 0: return 0
|
|
case 1:
|
|
if(res == null) {
|
|
return 0;
|
|
}
|
|
switch(res) {
|
|
case 'SUCCESS': return +1;
|
|
case 'FAILURE': return -1;
|
|
default: return 0;
|
|
}
|
|
case -1: return -1
|
|
}
|
|
}
|
|
|
|
def setResult(resultVerify, resultCodeStyle) {
|
|
if (resultVerify == 0 || resultCodeStyle == 0) {
|
|
currentBuild.result = 'ABORTED'
|
|
} else if (resultVerify == -1 || resultCodeStyle == -1) {
|
|
currentBuild.result = 'FAILURE'
|
|
} else {
|
|
currentBuild.result = 'SUCCESS'
|
|
}
|
|
}
|
|
|
|
def findCodestyleFilesInLog(build) {
|
|
def codeStyleFiles = []
|
|
def needsFormatting = false
|
|
def response = httpRequest "${build.url}consoleText"
|
|
response.content.eachLine {
|
|
needsFormatting = needsFormatting || (it ==~ /.*Need Formatting.*/)
|
|
if(needsFormatting && it ==~ /\[.*\]/) {
|
|
codeStyleFiles += it.substring(1,it.length()-1)
|
|
}
|
|
}
|
|
|
|
return codeStyleFiles
|
|
}
|
|
|
|
def createCodeStyleMsgBody(build, label) {
|
|
def codeStyleFiles = findCodestyleFilesInLog(build)
|
|
def formattingMsg = label < 0 ? ('The following files need formatting:\n ' +
|
|
codeStyleFiles.join('\n ')) : 'All files are correctly formatted'
|
|
def url = build.url + "consoleText"
|
|
|
|
return "${Globals.resTicks[build.result]} $formattingMsg\n (${url})"
|
|
}
|
|
|
|
def createVerifyMsgBody(builds) {
|
|
def msgList = builds.collect { type, build -> [
|
|
'type': type, 'res': build.result, 'url': build.url + "consoleText" ]
|
|
}
|
|
|
|
return msgList.collect {
|
|
"${Globals.resTicks[it.res]} ${it.type} : ${it.res}\n (${it.url})"
|
|
} .join('\n')
|
|
}
|
|
|
|
node ('master') {
|
|
|
|
if (hasChangeNumber()) {
|
|
stage('Preparing'){
|
|
gerritReview labels: ['Verified': 0, 'Code-Style': 0]
|
|
|
|
getChangeMetaData()
|
|
collectBuildModes()
|
|
}
|
|
}
|
|
|
|
parallel(collectBuilds())
|
|
|
|
if (hasChangeNumber()) {
|
|
stage('Retry Flaky Builds'){
|
|
def flakyBuildsModes = findFlakyBuilds()
|
|
if (flakyBuildsModes.size() > 0){
|
|
parallel flakyBuildsModes.collectEntries {
|
|
["Gerrit-verification(${it})" :
|
|
prepareBuildsForMode("Gerrit-verifier-bazel", it, 3)]
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Report to Gerrit'){
|
|
resCodeStyle = getLabelValue(1, Builds.codeStyle.result)
|
|
gerritReview(
|
|
labels: ['Code-Style': resCodeStyle],
|
|
message: createCodeStyleMsgBody(Builds.codeStyle, resCodeStyle))
|
|
postCheck(new GerritCheck("codestyle", Change.number, Change.sha1, Builds.codeStyle))
|
|
|
|
def verificationResults = Builds.verification.collect { k, v -> v }
|
|
def resVerify = verificationResults.inject(1) {
|
|
acc, build -> getLabelValue(acc, build.result)
|
|
}
|
|
gerritReview(
|
|
labels: ['Verified': resVerify],
|
|
message: createVerifyMsgBody(Builds.verification))
|
|
|
|
Builds.verification.each { type, build -> postCheck(
|
|
new GerritCheck(type, Change.number, Change.sha1, build)
|
|
)}
|
|
|
|
setResult(resVerify, resCodeStyle)
|
|
}
|
|
}
|
|
}
|