blob: 293cf2954935105b0bd1636626316663e61ae6f8 [file] [log] [blame]
#!groovy
//
//
// 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.
// Erlang version embedded in binary packages
ERLANG_VERSION = '24.3.4.10'
// Erlang version used for rebar in release process. CouchDB will not build from
// the release tarball on Erlang versions older than this
MINIMUM_ERLANG_VERSION = '24.3.4.10'
// We create parallel build / test / package stages for each OS using the metadata
// in this map. Adding a new OS should ideally only involve adding a new entry here.
meta = [
'centos7': [
name: 'CentOS 7',
spidermonkey_vsn: '1.8.5',
enable_nouveau: true,
image: "apache/couchdbci-centos:7-erlang-${ERLANG_VERSION}"
],
'centos8': [
name: 'CentOS 8',
spidermonkey_vsn: '60',
enable_nouveau: true,
image: "apache/couchdbci-centos:8-erlang-${ERLANG_VERSION}"
],
'bionic': [
name: 'Ubuntu 18.04',
spidermonkey_vsn: '1.8.5',
enable_nouveau: true,
image: "apache/couchdbci-ubuntu:bionic-erlang-${ERLANG_VERSION}"
],
'focal': [
name: 'Ubuntu 20.04',
spidermonkey_vsn: '68',
enable_nouveau: true,
image: "apache/couchdbci-ubuntu:focal-erlang-${ERLANG_VERSION}"
],
'jammy': [
name: 'Ubuntu 22.04',
spidermonkey_vsn: '91',
enable_nouveau: true,
image: "apache/couchdbci-ubuntu:jammy-erlang-${ERLANG_VERSION}"
],
'buster': [
name: 'Debian 10',
spidermonkey_vsn: '60',
enable_nouveau: true,
image: "apache/couchdbci-debian:buster-erlang-${ERLANG_VERSION}"
],
'bullseye-arm64': [
name: 'Debian 11 ARM',
spidermonkey_vsn: '78',
enable_nouveau: true,
image: "apache/couchdbci-debian:bullseye-erlang-${ERLANG_VERSION}",
node_label: 'arm64v8'
],
'bullseye-ppc64': [
name: 'Debian 11 POWER',
spidermonkey_vsn: '78',
enable_nouveau: true,
image: "apache/couchdbci-debian:bullseye-erlang-${ERLANG_VERSION}",
node_label: 'ppc64le'
],
'bullseye-s390x': [
name: 'Debian 11 s390x',
spidermonkey_vsn: '78',
enable_nouveau: true,
image: "apache/couchdbci-debian:bullseye-erlang-${ERLANG_VERSION}",
node_label: 's390x'
],
'bullseye': [
name: 'Debian 11',
spidermonkey_vsn: '78',
enable_nouveau: true,
image: "apache/couchdbci-debian:bullseye-erlang-${ERLANG_VERSION}"
],
// Skip freebsd builds for now as adviced by node owner
// 'freebsd': [
// name: 'FreeBSD',
// spidermonkey_vsn: '1.8.5',
// gnu_make: 'gmake'
// ],
'macos': [
name: 'macOS',
spidermonkey_vsn: '91',
enable_nouveau: false,
gnu_make: 'make'
]
]
def String configure(config) {
result = "./configure --skip-deps --spidermonkey-version ${config.spidermonkey_vsn}"
if (config.enable_nouveau) {
result += " --enable-nouveau"
}
return result
}
// Credit to https://stackoverflow.com/a/69222555 for this technique.
// We can use the scripted pipeline syntax to dynamically generate stages,
// and inject them into a map that we pass to the `parallel` step in a script.
// While the scripting approach is very flexible, it's not able to use some
// functionality specific to Declarative Pipelines, like the `agent` and `post`
// directives, so you'll see alternatives like try-catch-finally used for flow
// control and the nested `node` and `docker` blocks in the container stage to
// configure the worker environment.
// Returns a build stage suitable for non-containerized environments (currently
// macOS and FreeBSD). Coincidentally we do not currently support automated
// package generation on these platforms. This method in invoked when we create
// `parallelStagesMap` below.
def generateNativeStage(platform) {
return {
stage(platform) {
node(platform) {
timeout(time: 90, unit: "MINUTES") {
try {
// deleteDir is OK here because we're not inside of a Docker container!
deleteDir()
unstash 'tarball'
withEnv([
'HOME='+pwd(),
'PATH+USRLOCAL=/usr/local/bin',
'MAKE='+meta[platform].gnu_make
]) {
sh( script: "mkdir -p ${platform}/build", label: 'Create build directories' )
sh( script: "tar -xf apache-couchdb-*.tar.gz -C ${platform}/build --strip-components=1", label: 'Unpack release' )
dir( "${platform}/build" ) {
sh "${configure(meta[platform])}"
sh '$MAKE'
sh '$MAKE eunit'
sh '$MAKE elixir-suite'
sh '$MAKE exunit'
sh '$MAKE mango-test'
sh '$MAKE weatherreport-test'
sh '$MAKE nouveau-test'
}
}
}
catch (err) {
sh 'ls -l ${WORKSPACE}'
withEnv([
'HOME='+pwd(),
'PATH+USRLOCAL=/usr/local/bin',
'MAKE='+meta[platform].gnu_make
]) {
dir( "${platform}/build" ) {
sh 'ls -l'
sh '${MAKE} build-report'
}
}
error("Build step failed with error: ${err.getMessage()}")
}
finally {
junit '**/.eunit/*.xml, **/_build/*/lib/couchdbtest/*.xml, **/src/mango/nosetests.xml, **/test/javascript/junit.xml'
sh 'killall -9 beam.smp || true'
sh 'rm -rf ${WORKSPACE}/*'
}
}
}
}
}
}
// Returns a build stage suitable for container-based deployments. This method
// is invoked when we create the `parallelStagesMap` in the pipeline below.
def generateContainerStage(platform) {
return {
// Important: the stage name here must match the parallelStagesMap key for the
// Jenkins UI to render the pipeline stages correctly. Don't ask why. -APK
stage(platform) {
node(meta[platform].get('node_label', 'docker')) {
docker.withRegistry('https://docker.io/', 'dockerhub_creds') {
docker.image(meta[platform].image).inside("${DOCKER_ARGS}") {
timeout(time: 90, unit: "MINUTES") {
stage("${meta[platform].name} - build & test") {
try {
sh( script: "rm -rf ${platform} apache-couchdb-*", label: 'Clean workspace' )
unstash 'tarball'
sh( script: "mkdir -p ${platform}/build", label: 'Create build directories' )
sh( script: "tar -xf apache-couchdb-*.tar.gz -C ${platform}/build --strip-components=1", label: 'Unpack release' )
dir( "${platform}/build" ) {
sh "${configure(meta[platform])}"
sh 'make'
sh 'make eunit'
sh 'make elixir-suite'
sh 'make exunit'
sh 'make mango-test'
sh 'make weatherreport-test'
sh 'make nouveau-test'
}
}
catch (err) {
sh 'ls -l ${WORKSPACE}'
dir( "${platform}/build" ) {
sh 'ls -l'
sh 'make build-report'
}
error("Build step failed with error: ${err.getMessage()}")
}
finally {
junit '**/.eunit/*.xml, **/_build/*/lib/couchdbtest/*.xml, **/src/mango/nosetests.xml, **/test/javascript/junit.xml'
sh 'rm -rf ${WORKSPACE}/*'
}
}
stage("${meta[platform].name} - package") {
try {
unstash 'tarball'
sh( script: "mkdir -p ${platform}/couchdb", label: 'Create build directory' )
sh( script: "tar -xf apache-couchdb-*.tar.gz -C ${platform}/couchdb", label: 'Unpack release' )
sh( script: "cd ${platform} && git clone https://github.com/apache/couchdb-pkg", label: 'Clone packaging helper repo' )
dir( "${platform}/couchdb-pkg" ) {
sh( script: 'make', label: 'Build packages' )
}
sh( label: 'Stage package artifacts for archival', script: """
rm -rf pkgs/${platform}
mkdir -p pkgs/${platform}
mv ${platform}/rpmbuild/RPMS/\$(arch)/*rpm pkgs/${platform} || true
mv ${platform}/couchdb/*.deb pkgs/${platform} || true
""" )
archiveArtifacts artifacts: 'pkgs/**', fingerprint: true, onlyIfSuccessful: true
}
catch (err) {
sh 'ls -l ${WORKSPACE}'
error("Build step failed with error: ${err.getMessage()}")
}
finally {
sh 'rm -rf ${WORKSPACE}/*'
}
}
}
}
}
}
}
}
}
// Finally we have the actual Pipeline. It's mostly a Declarative Pipeline,
// except for the 'Test and Package' stage where we use the `script` step as an
// "escape hatch" to dynamically generate a set of parallel stages to execute.
pipeline {
// no top-level agent; agents must be declared for each stage
agent none
environment {
// Following fix an issue with git <= 2.6.5 where no committer
// name or email are present for reflog, required for git clone
GIT_COMMITTER_NAME = 'Jenkins User'
GIT_COMMITTER_EMAIL = 'couchdb@apache.org'
// https://github.com/jenkins-infra/jenkins.io/blob/master/Jenkinsfile#64
// We need the jenkins user mapped inside of the image
// npm config cache below deals with /home/jenkins not mapping correctly
// inside the image
DOCKER_ARGS = '-e npm_config_cache=npm-cache -e HOME=. -v=/etc/passwd:/etc/passwd -v /etc/group:/etc/group -v /root/.gradle:/root/.gradle'
}
options {
buildDiscarder(logRotator(numToKeepStr: '10', artifactNumToKeepStr: '10'))
preserveStashes(buildCount: 10)
timeout(time: 3, unit: 'HOURS')
timestamps()
}
stages {
stage('Build Release Tarball') {
agent {
docker {
label 'docker'
image "apache/couchdbci-debian:bullseye-erlang-${MINIMUM_ERLANG_VERSION}"
args "${DOCKER_ARGS}"
registryUrl 'https://docker.io/'
registryCredentialsId 'dockerhub_creds'
}
}
steps {
timeout(time: 15, unit: "MINUTES") {
sh (script: 'rm -rf apache-couchdb-*', label: 'Clean workspace of any previous release artifacts' )
sh "./configure --spidermonkey-version 78 --enable-nouveau"
sh 'make dist'
}
}
post {
success {
stash includes: 'apache-couchdb-*.tar.gz', name: 'tarball'
archiveArtifacts artifacts: 'apache-couchdb-*.tar.gz', fingerprint: true
}
failure {
sh 'ls -l ${WORKSPACE}'
}
cleanup {
// UGH see https://issues.jenkins-ci.org/browse/JENKINS-41894
sh 'rm -rf ${WORKSPACE}/*'
}
}
} // stage Build Release Tarball
stage('Test and Package') {
steps {
script {
// Including failFast: true in map fails the build immediately if any parallel step fails
parallelStagesMap = meta.collectEntries( [failFast: false] ) { key, values ->
if (values.image) {
["${key}": generateContainerStage(key)]
}
else {
["${key}": generateNativeStage(key)]
}
}
parallel parallelStagesMap
}
}
}
stage('Publish') {
when {
expression { return env.BRANCH_NAME ==~ /main|2.*.x|3.*.x|4.*.x|jenkins-.*/ }
}
agent {
docker {
image "apache/couchdbci-debian:bullseye-erlang-${ERLANG_VERSION}"
label 'docker'
args "${DOCKER_ARGS}"
registryUrl 'https://docker.io/'
registryCredentialsId 'dockerhub_creds'
}
}
options {
skipDefaultCheckout()
timeout(time: 90, unit: "MINUTES")
}
steps {
sh 'rm -rf ${WORKSPACE}/*'
unstash 'tarball'
unarchive mapping: ['pkgs/' : '.']
sh( label: 'Build Debian repo', script: '''
mkdir -p $BRANCH_NAME/debian $BRANCH_NAME/el7 $BRANCH_NAME/el8 $BRANCH_NAME/source
git clone https://github.com/apache/couchdb-pkg
for plat in buster bullseye focal
do
reprepro -b couchdb-pkg/repo includedeb $plat pkgs/$plat/*.deb
done
''' )
sh( label: 'Build CentOS repos', script: '''
#cp js/centos-7/*rpm pkgs/centos7
#cp js/centos-8/*rpm pkgs/centos8
cd pkgs/centos7 && createrepo_c --database .
cd ../centos8 && createrepo_c --database .
''' )
sh( label: 'Build unified repo', script: '''
mv couchdb-pkg/repo/pool $BRANCH_NAME/debian
mv couchdb-pkg/repo/dists $BRANCH_NAME/debian
mv pkgs/centos7/* $BRANCH_NAME/el7
mv pkgs/centos8/* $BRANCH_NAME/el8
mv apache-couchdb-*.tar.gz $BRANCH_NAME/source
cd $BRANCH_NAME/source
ls -1tr | head -n -10 | xargs -d '\n' rm -f --
cd ../..
''' )
} // steps
} // stage
} // stages
post {
success {
mail to: 'notifications@couchdb.apache.org',
replyTo: 'notifications@couchdb.apache.org',
subject: "[Jenkins] SUCCESS: ${currentBuild.fullDisplayName}",
body: "Yay, we passed. ${env.RUN_DISPLAY_URL}"
}
unstable {
mail to: 'notifications@couchdb.apache.org',
replyTo: 'notifications@couchdb.apache.org',
subject: "[Jenkins] SUCCESS: ${currentBuild.fullDisplayName}",
body: "Eep! Build is unstable... ${env.RUN_DISPLAY_URL}"
}
failure {
mail to: 'notifications@couchdb.apache.org',
replyTo: 'notifications@couchdb.apache.org',
subject: "[Jenkins] FAILURE: ${currentBuild.fullDisplayName}",
body: "Boo, we failed. ${env.RUN_DISPLAY_URL}"
}
}
} // pipeline