Merge branch 'stable-2.16' into stable-3.0

* stable-2.16:
  Update highlight.js to include soy
  Update building docs for building highlight.js
  config-gerrit: Remove superfluous quote mark
  Stop query from executing if predicate is empty
  Highlight jinja2 syntax
  Highlight Jenkinsfile as groovy
  Update highlight.js
  Verify Gerrit changes also on NoteDb
  Don't sort messages returned by Jenkins
  Fix string formatting in Jenkinsfile
  Fix RejectedAccessException during posting checks in Jenkinsfile
  Remove unnecessary SCM checkout from Jenkinsfile
  Fix expected response code when posting checks in Jenkinsfile
  Use basic auth for posting checks
  Add Jenkinsfile for verification pipeline

Remove the reviewDb build from Jenkinsfile.

Change-Id: I48f1fd6c19f87a55cb838955c5edbfb40f7db68f
This commit is contained in:
David Pursehouse
2019-10-22 10:57:23 +09:00
8 changed files with 604 additions and 235 deletions

View File

@@ -9,7 +9,7 @@ config file that controls many host specific settings for Gerrit.
The contents of the `etc/gerrit.config` file are cached at startup
by Gerrit. For most properties, if they are modified in this file, Gerrit
needs to be restarted before it will use the new values. Some properties
support being link:#reloadConfig[`reloaded`]' without restart.
support being link:#reloadConfig[`reloaded`] without restart.
Sample `etc/gerrit.config`:
----

310
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,310 @@
#!/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 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 || changedFiles.contains("WORKSPACE")) {
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"
}
}
}
def prepareBuildsForMode(buildName, mode="notedb", retryTimes = 1) {
def propagate = retryTimes == 1 ? false : true
return {
stage("${buildName}/${mode}") {
catchError{
retry(retryTimes){
def 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: propagate
if (buildName == "Gerrit-codestyle"){
Builds.codeStyle = new Build(
slaveBuild.getAbsoluteUrl(), slaveBuild.getResult())
} else {
Builds.verification[mode] = new Build(
slaveBuild.getAbsoluteUrl(), slaveBuild.getResult())
}
}
}
}
}
}
def collectBuilds() {
def builds = [:]
builds["Gerrit-codestyle"] = prepareBuildsForMode("Gerrit-codestyle")
Builds.modes.each {
builds["Gerrit-verification(${it})"] = prepareBuildsForMode("Gerrit-verifier-bazel", it)
}
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.remove(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') {
stage('Preparing'){
gerritReview labels: ['Verified': 0, 'Code-Style': 0]
getChangeMetaData()
collectBuildModes()
}
parallel(collectBuilds())
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)
}
}

View File

@@ -22,6 +22,9 @@ languages included. Build it with the following:
$> # start in some temp directory
$> git clone https://github.com/highlightjs/highlight.js
$> cd highlight.js
$> git clone https://github.com/highlightjs/highlightjs-closure-templates
$> ln -s ../../highlightjs-closure-templates/soy.js src/languages/soy.js
$> mkdir test/detect/soy && ln -s ../../../highlightjs-closure-templates/test/detect/soy/default.txt test/detect/soy/default.txt
$> npm install
$> node tools/build.js -n

File diff suppressed because one or more lines are too long

View File

@@ -98,7 +98,7 @@
// All of the ops, with corresponding negations.
const SEARCH_OPERATORS_WITH_NEGATIONS =
SEARCH_OPERATORS.concat(SEARCH_OPERATORS.map(op => `-${op}`));
SEARCH_OPERATORS.concat(SEARCH_OPERATORS.map(op => `-${op}`));
const MAX_AUTOCOMPLETE_RESULTS = 10;
@@ -193,7 +193,16 @@
} else {
target.blur();
}
if (this._inputVal) {
const trimmedInput = this._inputVal && this._inputVal.trim();
if (trimmedInput) {
const predefinedOpOnlyQuery = SEARCH_OPERATORS_WITH_NEGATIONS.some(
op => {
return op.endsWith(':') && op === trimmedInput;
}
);
if (predefinedOpOnlyQuery) {
return;
}
this.dispatchEvent(new CustomEvent('handle-search', {
detail: {inputVal: this._inputVal},
}));

View File

@@ -91,6 +91,42 @@ limitations under the License.
assert.isFalse(searchSpy.called);
});
test('Predefined query op with no predication doesnt trigger nav', () => {
const searchSpy = sandbox.spy();
element.addEventListener('handle-search', searchSpy);
element.value = 'added:';
MockInteractions.pressAndReleaseKeyOn(element.$.searchInput.$.input, 13,
null, 'enter');
assert.isFalse(searchSpy.called);
});
test('predefined predicate query triggers nav', () => {
const searchSpy = sandbox.spy();
element.addEventListener('handle-search', searchSpy);
element.value = 'age:1week';
MockInteractions.pressAndReleaseKeyOn(element.$.searchInput.$.input, 13,
null, 'enter');
assert.isTrue(searchSpy.called);
});
test('undefined predicate query triggers nav', () => {
const searchSpy = sandbox.spy();
element.addEventListener('handle-search', searchSpy);
element.value = 'random:1week';
MockInteractions.pressAndReleaseKeyOn(element.$.searchInput.$.input, 13,
null, 'enter');
assert.isTrue(searchSpy.called);
});
test('empty undefined predicate query triggers nav', () => {
const searchSpy = sandbox.spy();
element.addEventListener('handle-search', searchSpy);
element.value = 'random:';
MockInteractions.pressAndReleaseKeyOn(element.$.searchInput.$.input, 13,
null, 'enter');
assert.isTrue(searchSpy.called);
});
test('keyboard shortcuts', () => {
const focusSpy = sandbox.spy(element.$.searchInput, 'focus');
const selectAllSpy = sandbox.spy(element.$.searchInput, 'selectAll');

View File

@@ -78,6 +78,7 @@
'text/x-scss': 'scss',
'text/x-scheme': 'scheme',
'text/x-shell': 'shell',
'text/x-soy': 'soy',
'text/x-spreadsheet': 'excel',
'text/x-sh': 'bash',
'text/x-sql': 'sql',

View File

@@ -100,8 +100,12 @@ HISTORY.md = text/x-gfm
in = text/x-properties
ini = text/x-properties
intr = text/x-dylan
j2 = text/x-jinja2
jade = text/x-pug
java = text/x-java
Jenkinsfile = text/x-groovy
jinja = text/x-jinja2
jinja2 = text/x-jinja2
jl = text/x-julia
jruby = text/x-ruby
js = text/javascript