blob: fd2afe7d98be9877117e07cc9ac63f676b25f3e6 [file] [log] [blame]
#!/usr/bin/env groovy
/*******************************************************************************
* Copyright (C) 2020 EGit Committers and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.egit.jenkins
/**
* Collection of useful pipeline fragments for use in an EGit build.
*/
class Tools implements Serializable {
private final def script
Tools(def script) {
this.script = script
}
/**
* Constructs a GerritTrigger "gerritProjects" specification to trigger on the given
* {@code repo} and {@code branches}.
*
* @param match
* match type for {@code repo}
* @param repo
* to trigger on
* @param branches
* to trigger on; single entry or a Collection of entries, each entry either
* a simple string or a list containing two strings, the first one the match
* type and the second one the name or pattern. If empty or {@code null}, no
* branch filter will be added.
* @return the "gerritProjects" specification
*/
def Object projectsToBuild(String match, String repo, def branches = null) {
def branchSpecs = []
if (!branches) {
return [
[$class: "GerritProject", compareType: match, pattern: repo]
]
}
if (branches instanceof String) {
branchSpecs.add([$class: "Branch", compareType: "PLAIN", pattern: branches])
} else {
for (b in branches) {
if (b instanceof Collection && b.size() == 2) {
branchSpecs.add([$class: "Branch", compareType: b[0], pattern: b[1]])
} else {
branchSpecs.add([$class: "Branch", compareType: "PLAIN", pattern: b])
}
}
}
return [
[$class: "GerritProject", compareType: match, pattern: repo, branches: branchSpecs]
]
}
/**
* A complete "Checkout" stage for EGit projects, cloning a given {@code project} using
* the given {@code refSpec} and checking out a given {@code branch}.
*
* @param project
* to clone; for example "jgit/jgit"
* @param branch
* to check out
* @param refSpec
* to use
* @param extras
* map of extra parameters for the checkout() command
*/
def void cloneAndCheckout(String project, String branch, String refSpec, Map extras = [:]) {
def cfg = [
$class: 'GitSCM',
branches: [[name: branch]],
doGenerateSubmoduleConfigurations: false,
submoduleCfg: [],
userRemoteConfigs: [
[url : "https://git.eclipse.org/r/${project}", name : 'origin', refspec : refSpec]
]
]
for (extra in extras) {
cfg.put(extra.key, extra.value)
}
script.checkout(cfg)
}
/**
* Copies all content of a {@code sourceDirectory} to a {@code publishDirectory} on projects-storage.eclipse.org
* via ssh/scp. If the {@code publishDirectory} exists already, it is replaced.
*
* @param genie
* user name to log in at projects-storage.eclipse.org, typically "genie.egit"
* @param credentials
* Jenkins credential name for the ssh key to use
* @param sourceDirectory
* path relative to ${WORKSPACE} of the directory to copy
* @param publishDirectory
* path on projects-storage.eclipse.org to copy to
* @param extraSource
* to also copy to {@code publishDirectory}
*/
def void publishUpdateSite(String genie, String credentials, String sourceDirectory, String publishDirectory, String extraSource = null) {
def buildNumber = script.currentBuild.number;
script.sshagent ([credentials]) {
// Serialize concurrently running jobs to not overwrite the publishDirectory concurrently
def lockName = "${genie}-projects-storage-eclipse-org-" + publishDirectory.replace('/', '-')
script.lock(lockName) {
script.sh """
ssh ${genie}@projects-storage.eclipse.org rm -rf ${publishDirectory}-tmp${buildNumber}
ssh ${genie}@projects-storage.eclipse.org mkdir -p ${publishDirectory}-tmp${buildNumber}
scp -r ${sourceDirectory}/* ${genie}@projects-storage.eclipse.org:${publishDirectory}-tmp${buildNumber}
"""
if (extraSource) {
script.sh """
scp ${extraSource} ${genie}@projects-storage.eclipse.org:${publishDirectory}-tmp${buildNumber}/
"""
}
// Remove former -old directory. There shouldn't be one, but let's be sure.
// Ensure the publishDirectory exists before moving it to -old.
// Then rename -tmp and remove -old.
script.sh """
ssh ${genie}@projects-storage.eclipse.org rm -rf ${publishDirectory}-old
ssh ${genie}@projects-storage.eclipse.org mkdir -p ${publishDirectory}
ssh ${genie}@projects-storage.eclipse.org mv ${publishDirectory} ${publishDirectory}-old
ssh ${genie}@projects-storage.eclipse.org mv ${publishDirectory}-tmp${buildNumber} ${publishDirectory}
ssh ${genie}@projects-storage.eclipse.org rm -rf ${publishDirectory}-old
"""
}
}
}
/**
* Archives build artifacts. Screenshots from failed tests and Eclipse logs are added automatically.
*
* @param specificArtifacts
* Collection of ${WORKSPACE}-relative ant patterns defining the artifacts to archive;
* screenshots and Eclipse log files are added automatically
*/
def void archiveArtifacts(Collection specificArtifacts = []) {
def artifacts = []
artifacts.addAll(specificArtifacts)
artifacts.addAll([
'*/target/screenshots/*',
'*/target/work/data/.metadata/*log',
])
script.archiveArtifacts artifacts.join(',')
}
/**
* Standard EGit build reporting steps.
*/
def void reporting() {
// don't use ** if the number of directories is known, this is a huge performance problem
script.junit '*/target/surefire-reports/*.xml'
// TODO replace by warnings-next-generation once it is installed
script.findbugs pattern: '*/target/*bugsXml.xml', defaultEncoding: 'UTF-8'
script.dry defaultEncoding: 'UTF-8'
}
/**
* Sends an e-mail depending on the build outcome.
*
* @param to
* E-mail recipients; a whitespace-separated sequence of e-mail addresses
*/
def void sendMail(String to) {
if (script.currentBuild.result == null) {
script.currentBuild.result = script.currentBuild.currentResult
}
script.step([
$class: 'Mailer',
notifyEveryUnstableBuild: true,
recipients: to,
sendToIndividuals: true
])
}
/**
* Runs mvn with the given arguments, supplying the EGit default arguments automatically.
*
* @param arguments
* for mvn
* @param mvnVersion
* Jenkins tool identifier; defaults to the latest mvn version available
*/
def void maven(Collection arguments, String mvnVersion = 'apache-maven-latest') {
def args = []
args.addAll(arguments)
// General build setup
args.addAll([
// suppress progress output
'--batch-mode',
// show maven errors
'--errors',
// have a separate maven repo per job
"-Dmaven.repo.local=${script.env.WORKSPACE}/.repository",
// avoid flaky or not updated mirrors
'-Declipse.p2.mirrors=false',
// temporary directory for egit tests
"-Degit.test.tmpdir=${script.env.WORKSPACE}/tmp/egit.tmp/",
// temporary directory for java
"-Djava.io.tmpdir=${script.env.WORKSPACE}/tmp/",
])
// mvn logging setup
args.addAll([
// make eclipse log to the build log, not just into a file
'-Dtest.vmparams=-Declipse.consoleLog=true',
// enable timestamps in mvn logging
'-Dorg.slf4j.simpleLogger.showDateTime=true',
// set timestamp format
'-Dorg.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss',
// disable download progress output by allowing warning output only
'-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn',
// disable parallel maven build threadsafe warning by allowing error output only
'-Dorg.slf4j.simpleLogger.log.org.apache.maven.lifecycle.internal.builder.BuilderCommon=error'
])
def command = args.join(' ')
// get the path from the global Jenkins configuration
def mvnHome = script.tool mvnVersion
// invoke maven
if (script.isUnix()) {
script.sh "'${mvnHome}/bin/mvn' ${command}"
} else {
script.bat(/"${mvnHome}\bin\mvn" ${command}/)
}
}
}