Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CI] Refactor Jenkins CI pipeline + migrate tests to Jenkins #4380

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9f3ef5e
[WIP] Refactor Jenkins CI pipeline
hcho3 Apr 17, 2019
56eba7a
Exclude symlinks in Python package when linting
hcho3 Apr 23, 2019
761f312
Fix Maven repo caching
hcho3 Apr 23, 2019
cdf2a4c
Purge build/ directory before building JVM packages
hcho3 Apr 23, 2019
d057135
Do not run JVM tests in build stage
hcho3 Apr 23, 2019
65c95b4
Create test JARs
hcho3 Apr 23, 2019
eeadece
Remove build/ before building JVM doc
hcho3 Apr 23, 2019
30653c6
Make test jar only for ml.dmlc.xgboost4j
hcho3 Apr 24, 2019
66c76ee
Re-enable tests in Build stage
hcho3 Apr 24, 2019
0d7e3b6
Add cross version test for ml.dmlc.xgboost4j
hcho3 Apr 24, 2019
33cde39
Clean workspace after stages
hcho3 Apr 24, 2019
a9aa24c
Use only 4 cores to test XGBoost4J-Spark
hcho3 Apr 24, 2019
6d5df09
Need to unstash for build-jvm-packages
hcho3 Apr 24, 2019
f9c7047
Add JDK version to container name
hcho3 Apr 24, 2019
5e3d6f4
Unstash 'srcs' for test-jvm-jdk*
hcho3 Apr 24, 2019
efe875b
Fix syntax on Dockerfile.jvm_cross
hcho3 Apr 24, 2019
841668c
Install maven in jvm_cross
hcho3 Apr 24, 2019
239011e
Install Python in jvm_cross
hcho3 Apr 24, 2019
9282834
Fix path of stashed JARs
hcho3 Apr 24, 2019
8a1ca44
Install libgomp1 in jvm_cross
hcho3 Apr 24, 2019
94ecf8c
Suppress download progress in CI
hcho3 Apr 24, 2019
330322f
Set correct Java version in JVM tests
hcho3 Apr 24, 2019
e1528d3
Run Spark integration tests in jvm_cross
hcho3 Apr 24, 2019
0ef14a0
Add build for CPU, GPU targets
hcho3 Apr 24, 2019
7d0e66e
Limit parallel compilation to nproc, to avoid memory shortage
hcho3 Apr 24, 2019
df47d72
Run integration test for x86 target
hcho3 Apr 24, 2019
3a4ae9f
Add more integration tests for JVM packages
hcho3 Apr 24, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
359 changes: 250 additions & 109 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,129 +3,270 @@
// Jenkins pipeline
// See documents at https://jenkins.io/doc/book/pipeline/jenkinsfile/

import groovy.transform.Field

/* Unrestricted tasks: tasks that do NOT generate artifacts */

// Command to run command inside a docker container
def dockerRun = 'tests/ci_build/ci_build.sh'
// Utility functions
@Field
def utils

def buildMatrix = [
[ "enabled": true, "os" : "linux", "withGpu": true, "withNccl": true, "withOmp": true, "pythonVersion": "2.7", "cudaVersion": "10.0", "multiGpu": true],
[ "enabled": true, "os" : "linux", "withGpu": true, "withNccl": true, "withOmp": true, "pythonVersion": "2.7", "cudaVersion": "9.2" ],
[ "enabled": true, "os" : "linux", "withGpu": true, "withNccl": true, "withOmp": true, "pythonVersion": "2.7", "cudaVersion": "8.0" ],
[ "enabled": true, "os" : "linux", "withGpu": true, "withNccl": false, "withOmp": true, "pythonVersion": "2.7", "cudaVersion": "8.0" ],
]
dockerRun = 'tests/ci_build/ci_build.sh'

pipeline {
// Each stage specify its own agent
agent none
// Each stage specify its own agent
agent none

environment {
DOCKER_CACHE_REPO = '492475357299.dkr.ecr.us-west-2.amazonaws.com'
}
environment {
DOCKER_CACHE_REPO = '492475357299.dkr.ecr.us-west-2.amazonaws.com'
}

// Setup common job properties
options {
ansiColor('xterm')
timestamps()
timeout(time: 120, unit: 'MINUTES')
buildDiscarder(logRotator(numToKeepStr: '10'))
}
// Setup common job properties
options {
ansiColor('xterm')
timestamps()
timeout(time: 120, unit: 'MINUTES')
buildDiscarder(logRotator(numToKeepStr: '10'))
preserveStashes()
}

// Build stages
stages {
stage('Jenkins: Get sources') {
agent {
label 'unrestricted'
}
steps {
script {
utils = load('tests/ci_build/jenkins_tools.Groovy')
utils.checkoutSrcs()
}
stash name: 'srcs', excludes: '.git/'
milestone label: 'Sources ready', ordinal: 1
}
// Build stages
stages {
stage('Get sources') {
agent { label 'linux && cpu' }
steps {
script {
checkoutSrcs()
}
stage('Jenkins: Build & Test') {
steps {
script {
parallel (buildMatrix.findAll{it['enabled']}.collectEntries{ c ->
def buildName = utils.getBuildName(c)
utils.buildFactory(buildName, c, false, this.&buildPlatformCmake)
} + [ "clang-tidy" : { buildClangTidyJob() } ])
}
}
stash name: 'srcs'
milestone ordinal: 1
}
}
stage('Formatting Check') {
agent none
steps {
script {
parallel ([
'clang-tidy': { ClangTidy() },
'lint': { Lint() },
'sphinx-doc': { SphinxDoc() },
'doxygen': { Doxygen() }
])
}
milestone ordinal: 2
}
}
}

/**
* Build platform and test it via cmake.
*/
def buildPlatformCmake(buildName, conf, nodeReq, dockerTarget) {
def opts = utils.cmakeOptions(conf)
// Destination dir for artifacts
def distDir = "dist/${buildName}"
def dockerArgs = ""
if (conf["withGpu"]) {
dockerArgs = "--build-arg CUDA_VERSION=" + conf["cudaVersion"]
stage('Build') {
agent none
steps {
script {
parallel ([
'build-cpu': { BuildCPU() },
'build-gpu-cuda8.0': { BuildCUDA(cuda_version: '8.0') },
'build-gpu-cuda9.2': { BuildCUDA(cuda_version: '9.2') },
'build-gpu-cuda10.0': { BuildCUDA(cuda_version: '10.0') },
'build-jvm-packages': { BuildJVMPackages(spark_version: '2.4.1') },
'build-jvm-doc': { BuildJVMDoc() }
])
}
milestone ordinal: 3
}
}
def test_suite = conf["withGpu"] ? (conf["multiGpu"] ? "mgpu" : "gpu") : "cpu"
// Build node - this is returned result
retry(1) {
node(nodeReq) {
unstash name: 'srcs'
echo """
|===== XGBoost CMake build =====
| dockerTarget: ${dockerTarget}
| cmakeOpts : ${opts}
|=========================
""".stripMargin('|')
// Invoke command inside docker
sh """
${dockerRun} ${dockerTarget} ${dockerArgs} tests/ci_build/build_via_cmake.sh ${opts}
${dockerRun} ${dockerTarget} ${dockerArgs} tests/ci_build/test_${test_suite}.sh
"""
if (!conf["multiGpu"]) {
sh """
${dockerRun} ${dockerTarget} ${dockerArgs} bash -c "cd python-package; rm -f dist/*; python setup.py bdist_wheel --universal"
rm -rf "${distDir}"; mkdir -p "${distDir}/py"
cp xgboost "${distDir}"
cp -r python-package/dist "${distDir}/py"
# Test the wheel for compatibility on a barebones CPU container
${dockerRun} release ${dockerArgs} bash -c " \
pip install --user python-package/dist/xgboost-*-none-any.whl && \
pytest -v --fulltrace -s tests/python"
# Test the wheel for compatibility on CUDA 10.0 container
${dockerRun} gpu --build-arg CUDA_VERSION=10.0 bash -c " \
pip install --user python-package/dist/xgboost-*-none-any.whl && \
pytest -v -s --fulltrace -m '(not mgpu) and (not slow)' tests/python-gpu"
"""
}
stage('Test') {
agent none
steps {
script {
parallel ([
'test-python-cpu': { TestPythonCPU() },
'test-python-gpu-cuda8.0': { TestPythonGPU(cuda_version: '8.0') },
'test-python-gpu-cuda9.2': { TestPythonGPU(cuda_version: '9.2') },
'test-python-gpu-cuda10.0': { TestPythonGPU(cuda_version: '10.0') },
'test-cpp-gpu': { TestCppGPU(cuda_version: '10.0') },
'test-cpp-mgpu': { TestCppGPU(cuda_version: '10.0', multi_gpu: true) },
'test-jvm-jdk8': { CrossTestJVMwithJDK(jdk_version: '8') },
'test-jvm-jdk11': { CrossTestJVMwithJDK(jdk_version: '11') },
'test-r-3.4.4': { TestR(r_version: '3.4.4') },
'test-r-3.5.3': { TestR(r_version: '3.5.3') }
])
}
milestone ordinal: 4
}
}
}
}

/**
* Run a clang-tidy job on a GPU machine
*/
def buildClangTidyJob() {
def nodeReq = "linux && gpu && unrestricted"
node(nodeReq) {
unstash name: 'srcs'
echo "Running clang-tidy job..."
// Invoke command inside docker
// Install Google Test and Python yaml
dockerTarget = "clang_tidy"
dockerArgs = "--build-arg CUDA_VERSION=9.2"
sh """
${dockerRun} ${dockerTarget} ${dockerArgs} tests/ci_build/clang_tidy.sh
"""
// check out source code from git
def checkoutSrcs() {
retry(5) {
try {
timeout(time: 2, unit: 'MINUTES') {
checkout scm
sh 'git submodule update --init'
}
} catch (exc) {
deleteDir()
error "Failed to fetch source codes"
}
}
}

def ClangTidy() {
node('linux && cpu') {
unstash name: 'srcs'
echo "Running clang-tidy job..."
container_type = "clang_tidy"
docker_binary = "docker"
dockerArgs = "--build-arg CUDA_VERSION=9.2"
sh """
${dockerRun} ${container_type} ${docker_binary} ${dockerArgs} tests/ci_build/clang_tidy.sh
"""
deleteDir()
}
}

def Lint() {
node('linux && cpu') {
unstash name: 'srcs'
echo "Running lint..."
// commented out for now, until another PR to migrate lint to Python 3 gets merged
container_type = "lint"
docker_binary = "docker"
sh """
${dockerRun} ${container_type} ${docker_binary} make lint
"""
deleteDir()
}
}

def SphinxDoc() {
node('linux && cpu') {
unstash name: 'srcs'
echo "Running sphinx-doc..."
container_type = "lint"
docker_binary = "docker"
docker_extra_params = "CI_DOCKER_EXTRA_PARAMS_INIT='-e SPHINX_GIT_BRANCH=${BRANCH_NAME}'"
sh """#!/bin/bash
${docker_extra_params} ${dockerRun} ${container_type} ${docker_binary} make -C doc html
"""
deleteDir()
}
}

def Doxygen() {
node('linux && cpu') {
unstash name: 'srcs'
echo "Running doxygen..."
container_type = "lint"
docker_binary = "docker"
sh """
${dockerRun} ${container_type} ${docker_binary} tests/ci_build/doxygen.sh
"""
deleteDir()
}
}

def BuildCPU() {
node('linux && cpu') {
unstash name: 'srcs'
echo "Build CPU"
container_type = "lint"
docker_binary = "docker"
sh """
${dockerRun} ${container_type} ${docker_binary} tests/ci_build/build_via_cmake.sh
${dockerRun} ${container_type} ${docker_binary} build/testxgboost
"""
deleteDir()
}
}

def BuildCUDA(args) {
node('linux && cpu') {
unstash name: 'srcs'
echo "Build with CUDA ${args.cuda_version}"
container_type = "gpu_build"
docker_binary = "docker"
docker_args = "--build-arg CUDA_VERSION=${args.cuda_version}"
sh """
${dockerRun} ${container_type} ${docker_binary} ${docker_args} tests/ci_build/build_via_cmake.sh -DUSE_CUDA=ON
${dockerRun} ${container_type} ${docker_binary} ${docker_args} bash -c "cd python-package && rm -rf dist/* && python setup.py bdist_wheel --universal"
"""
// Only stash wheel for CUDA 8.0 target
if (args.cuda_version == '8.0') {
echo 'Stashing Python wheel...'
stash name: 'xgboost_whl', includes: 'python-package/dist/*.whl'
echo 'Stashing C++ test executable (testxgboost)...'
stash name: 'xgboost_cpp_tests', includes: 'bin/testxgboost'
}
deleteDir()
}
}

def BuildJVMPackages(args) {
node('linux && cpu') {
unstash name: 'srcs'
echo "Build XGBoost4J-Spark with Spark ${args.spark_version}"
container_type = "jvm"
docker_binary = "docker"
// Use only 4 CPU cores
docker_extra_params = "CI_DOCKER_EXTRA_PARAMS_INIT='--cpuset-cpus 0-3'"
sh """
${docker_extra_params} ${dockerRun} ${container_type} ${docker_binary} tests/ci_build/build_jvm_packages.sh
"""
echo 'Stashing XGBoost4J JAR...'
stash name: 'xgboost4j_jar', includes: 'jvm-packages/xgboost4j/target/*.jar,jvm-packages/xgboost4j-spark/target/*.jar,jvm-packages/xgboost4j-example/target/*.jar'
deleteDir()
}
}

def BuildJVMDoc() {
node('linux && cpu') {
unstash name: 'srcs'
echo "Building JVM doc..."
container_type = "jvm"
docker_binary = "docker"
sh """
${dockerRun} ${container_type} ${docker_binary} tests/ci_build/build_jvm_doc.sh ${BRANCH_NAME}
"""
archiveArtifacts artifacts: "jvm-packages/${BRANCH_NAME}.tar.bz2", allowEmptyArchive: true
echo 'Uploading doc...'
s3Upload file: "jvm-packages/${BRANCH_NAME}.tar.bz2", bucket: 'xgboost-docs', acl: 'PublicRead', path: "${BRANCH_NAME}.tar.bz2"
deleteDir()
}
}

def TestPythonCPU() {
node('linux && cpu') {
echo "Test Python CPU"
}
}

def TestPythonGPU(args) {
node('linux && cpu') {
echo "Test Python GPU: CUDA ${args.cuda_version}"
}
}

def TestCppGPU(args) {
node('linux && cpu') {
echo "Test C++, CUDA ${args.cuda_version}"
if (args.multi_gpu) {
echo "Using multiple GPUs"
}
}
}

def CrossTestJVMwithJDK(args) {
node('linux && cpu') {
unstash name: 'xgboost4j_jar'
unstash name: 'srcs'
echo "Test XGBoost4J on a machine with JDK ${args.jdk_version}"
container_type = "jvm_cross"
docker_binary = "docker"
docker_args = "--build-arg JDK_VERSION=${args.jdk_version}"
// Only run integration tests for JDK 8, as Spark doesn't support later JDKs yet
docker_extra_params = (args.jdk_version == '8') ? "CI_DOCKER_EXTRA_PARAMS_INIT='-e RUN_INTEGRATION_TEST=1'" : ""
sh """
${docker_extra_params} ${dockerRun} ${container_type} ${docker_binary} ${docker_args} tests/ci_build/test_jvm_cross.sh
"""
deleteDir()
}
}

def TestR(args) {
node('linux && cpu') {
echo "Test R package: R version ${args.r_version}"
}
}
Loading