|  | #!/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" | 
|  | } | 
|  |  | 
|  | 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 | 
|  | Build build | 
|  | String consoleUrl | 
|  |  | 
|  | GerritCheck(name, build) { | 
|  | this.uuid = "gerritforge:" + name.replaceAll("(bazel/)", "") + | 
|  | Globals.gerritRepositoryNameSha1Suffix | 
|  | this.build = build | 
|  | this.consoleUrl = "${build.url}console" | 
|  | } | 
|  |  | 
|  | def getCheckResultFromBuild() { | 
|  | def resultString = build.result.toString() | 
|  | if (resultString == 'SUCCESS') { | 
|  | return "SUCCESSFUL" | 
|  | } else if (resultString == 'NOT_BUILT' || resultString == 'ABORTED') { | 
|  | return "NOT_STARTED" | 
|  | } | 
|  |  | 
|  | // Remaining options: 'FAILURE' or 'UNSTABLE': | 
|  | return "FAILED" | 
|  | } | 
|  | } | 
|  |  | 
|  | def hasChangeNumber() { | 
|  | env.GERRIT_CHANGE_NUMBER?.trim() | 
|  | } | 
|  |  | 
|  | def postCheck(check) { | 
|  | gerritCheck(checks: [ "${check.uuid}" : "${check.getCheckResultFromBuild()}" ], url: "${check.consoleUrl}") | 
|  | } | 
|  |  | 
|  | def queryChangedFiles(url) { | 
|  | def queryUrl = "${url}changes/${env.GERRIT_CHANGE_NUMBER}/revisions/${env.GERRIT_PATCHSET_REVISION}/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 collectBuildModes() { | 
|  | Builds.modes = ["reviewdb", "notedb"] | 
|  | def changedFiles = queryChangedFiles(Globals.gerritUrl) | 
|  | 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="reviewdb", 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: "refs/changes/${env.BRANCH_NAME}"), | 
|  | string(name: 'BRANCH', value: env.GERRIT_PATCHSET_REVISION), | 
|  | string(name: 'CHANGE_URL', value: "${Globals.gerritUrl}c/${env.GERRIT_PROJECT}/+/${env.GERRIT_CHANGE_NUMBER}"), | 
|  | string(name: 'MODE', value: mode), | 
|  | string(name: 'TARGET_BRANCH', value: env.GERRIT_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 | 
|  | } | 
|  |  | 
|  | node ('master') { | 
|  |  | 
|  | if (hasChangeNumber()) { | 
|  | stage('Preparing'){ | 
|  | gerritReview labels: ['Verified': 0, 'Code-Style': 0] | 
|  | 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] | 
|  | postCheck(new GerritCheck("codestyle", 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] | 
|  |  | 
|  | Builds.verification.each { type, build -> postCheck( | 
|  | new GerritCheck(type, build) | 
|  | )} | 
|  |  | 
|  | setResult(resVerify, resCodeStyle) | 
|  | } | 
|  | } | 
|  | } |