summaryrefslogtreecommitdiffstats
path: root/contrib/ci
diff options
context:
space:
mode:
authorNikolai Kondrashov <Nikolai.Kondrashov@redhat.com>2014-03-25 12:01:00 +0200
committerJakub Hrozek <jhrozek@redhat.com>2014-09-02 10:43:21 +0200
commit3ce85a5f5264e7118beb6524e120fd8b53a13da4 (patch)
treeec46ff937a47518ff9ed0f4dd67ebf2157fbc4bf /contrib/ci
parent6b5044001e4b0a0caf971a2cf5f27674e0d270f4 (diff)
downloadsssd-3ce85a5f5264e7118beb6524e120fd8b53a13da4.tar.gz
sssd-3ce85a5f5264e7118beb6524e120fd8b53a13da4.tar.xz
sssd-3ce85a5f5264e7118beb6524e120fd8b53a13da4.zip
Add basic support for CI test execution
Add basic support for executing continuous integration (CI) tests on RHEL6, RHEL7, Fedora 20, Fedora Rawhide and Debian Testing. This adds two front-end scripts which can be executed either locally by developers, or on a CI server: contrib/ci/run and contrib/ci/clean. The first one will run the tests and the second will wipe out the artifacts. See contrib/ci/README.md for further details. Reviewed-by: Michal Židek <mzidek@redhat.com> Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
Diffstat (limited to 'contrib/ci')
-rw-r--r--contrib/ci/README.md62
-rwxr-xr-xcontrib/ci/clean25
-rw-r--r--contrib/ci/configure.sh50
-rw-r--r--contrib/ci/deps.sh119
-rw-r--r--contrib/ci/distro.sh86
-rwxr-xr-xcontrib/ci/make-check-wrap52
-rw-r--r--contrib/ci/misc.sh116
-rwxr-xr-xcontrib/ci/rpm-spec-builddeps35
-rwxr-xr-xcontrib/ci/run371
-rwxr-xr-xcontrib/ci/valgrind-condense128
10 files changed, 1044 insertions, 0 deletions
diff --git a/contrib/ci/README.md b/contrib/ci/README.md
new file mode 100644
index 000000000..6c87200db
--- /dev/null
+++ b/contrib/ci/README.md
@@ -0,0 +1,62 @@
+Continuous integration
+======================
+
+The executables and modules in this directory implement continuous integration
+(CI) tests, which can be run to verify SSSD code quality and validity.
+
+Supported host distros are Fedora 20 and later, RHEL 6.5 and later, and Debian
+Testing.
+
+The tests are executed by running `contrib/ci/run` from the source tree root.
+It accepts options to choose from three test sets: "essential", "moderate" and
+"rigorous" (-e/-m/-r), with the essential set selected by default.
+
+Essential tests include building everything and running the built-in test
+suite under Valgrind, completing in under 5 minutes. Valgrind failures are
+ignored for now.
+
+Moderate tests include essential tests, plus a distcheck target build and mock
+package builds for Fedora and RHEL on Red Hat distros. They complete in about
+15 minutes.
+
+Rigorous tests include moderate tests, plus a pass with Clang static analyzer
+over the whole build and test execution with code coverage collection and
+verification, completing in 30 minutes. Static analyzer failures are ignored
+for now.
+
+Use `contrib/ci/clean` to remove test results from the source tree.
+
+
+Setup
+-----
+
+CI requires `lsb_release` command to be available in order to determine host
+distro version. On Red Hat distros it is contained in the `redhat-lsb-core`
+package and on Debian in `lsb-release`.
+
+The rest of the required packages CI will attempt to install itself, using
+the distribution's package manager invoked through sudo.
+
+A sudo rule can be employed to selectively avoid password prompts on Red Hat
+distros:
+
+ <USER> ALL=(ALL:ALL) NOPASSWD: /usr/bin/yum --assumeyes install -- *
+
+and Debian-based distros:
+
+ <USER> ALL=(ALL:ALL) NOPASSWD: /usr/bin/apt-get --yes install -- *
+
+Where `<USER>` is the user invoking CI.
+
+On Red Hat distros a repository carrying dependencies missing from some
+distros needs to be added to yum configuration. See instructions on the
+[Copr project page](http://copr-fe.cloud.fedoraproject.org/coprs/lslebodn/sssd-deps/).
+That repository is also automatically used by CI during mock builds.
+
+Package installation can be disabled with the -n/--no-deps option, e.g. for
+manual dependency management, or for shaving off a few seconds of execution
+time, when dependency changes are not expected.
+
+On Red Hat distros, where mock builds are ran, it is better to have the
+invoking user added to the `mock` group. Otherwise mock builds will be
+executed through sudo.
diff --git a/contrib/ci/clean b/contrib/ci/clean
new file mode 100755
index 000000000..ee18c1070
--- /dev/null
+++ b/contrib/ci/clean
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Clean source tree after a run of integration tests.
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set -o nounset -o pipefail -o errexit
+export PATH=`dirname "\`readlink -f \"\$0\"\`"`:$PATH
+
+. misc.sh
+
+rm_rf_ro ci-*
diff --git a/contrib/ci/configure.sh b/contrib/ci/configure.sh
new file mode 100644
index 000000000..b92b09441
--- /dev/null
+++ b/contrib/ci/configure.sh
@@ -0,0 +1,50 @@
+#
+# Configure argument management.
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+if [ -z ${_CONFIGURE_SH+set} ]; then
+declare -r _CONFIGURE_SH=
+
+. distro.sh
+
+# List of "configure" arguments.
+declare -a CONFIGURE_ARG_LIST=(
+ "--disable-dependency-tracking"
+ "--disable-rpath"
+ "--disable-static"
+ "--enable-ldb-version-check"
+ "--with-syslog=journald"
+)
+
+
+if [[ "$DISTRO_BRANCH" == -redhat-redhatenterprise*-6.*- ]]; then
+ CONFIGURE_ARG_LIST+=(
+ "--disable-cifs-idmap-plugin"
+ "--with-syslog=syslog"
+ )
+fi
+
+if [[ "$DISTRO_BRANCH" == -debian-* ]]; then
+ CONFIGURE_ARG_LIST+=(
+ # TODO Remove once libini_config >= 1.1.0 becomes available
+ "--without-samba"
+ )
+fi
+
+declare -r -a CONFIGURE_ARG_LIST
+
+fi # _CONFIGURE_SH
diff --git a/contrib/ci/deps.sh b/contrib/ci/deps.sh
new file mode 100644
index 000000000..ba59f7848
--- /dev/null
+++ b/contrib/ci/deps.sh
@@ -0,0 +1,119 @@
+#
+# Dependency management.
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+if [ -z ${_DEPS_SH+set} ]; then
+declare -r _DEPS_SH=
+
+. distro.sh
+
+# Dependency list
+declare -a DEPS_LIST=(
+ lcov
+ valgrind
+ xqilla
+)
+
+# True, if all test dependencies are satisfied by the package list
+declare DEPS_TESTS_SATISFIED=true
+
+if [[ "$DISTRO_BRANCH" == -redhat-* ]]; then
+ declare _DEPS_LIST_SPEC
+ DEPS_LIST+=(
+ clang-analyzer
+ mock
+ rpm-build
+ )
+ _DEPS_LIST_SPEC=`
+ sed -e 's/@PACKAGE_VERSION@/0/g' \
+ -e 's/@PACKAGE_NAME@/package-name/g' \
+ -e 's/@PRERELEASE_VERSION@//g' contrib/sssd.spec.in |
+ rpm-spec-builddeps /dev/stdin`
+ readarray -t -O "${#DEPS_LIST[@]}" DEPS_LIST <<<"$_DEPS_LIST_SPEC"
+ if [[ "$DISTRO_BRANCH" == *-redhatenterprise*-6.*- ]]; then
+ DEPS_TESTS_SATISFIED=false
+ fi
+fi
+
+if [[ "$DISTRO_BRANCH" == -debian-* ]]; then
+ DEPS_LIST+=(
+ autoconf
+ automake
+ autopoint
+ check
+ cifs-utils
+ clang
+ dh-apparmor
+ dnsutils
+ docbook-xml
+ docbook-xsl
+ gettext
+ krb5-config
+ libaugeas-dev
+ libc-ares-dev
+ libcmocka-dev
+ libcollection-dev
+ libdbus-1-dev
+ libdhash-dev
+ libglib2.0-dev
+ libini-config-dev
+ libkeyutils-dev
+ libkrb5-dev
+ libldap2-dev
+ libldb-dev
+ libltdl-dev
+ libnl-3-dev
+ libnl-route-3-dev
+ libnspr4-dev
+ libnss3-dev
+ libpam0g-dev
+ libpcre3-dev
+ libpopt-dev
+ libsasl2-dev
+ libselinux1-dev
+ libsemanage1-dev
+ libsmbclient-dev
+ libsystemd-journal-dev
+ libtalloc-dev
+ libtdb-dev
+ libtevent-dev
+ libtool
+ libxml2-utils
+ python-dev
+ samba-dev
+ systemd
+ xml-core
+ xsltproc
+ )
+fi
+
+declare -a -r DEPS_LIST
+declare -r DEPS_TESTS_SATISFIED
+
+# Install dependencies.
+function deps_install()
+{
+ distro_pkg_install "${DEPS_LIST[@]}"
+}
+
+# Remove dependencies.
+function deps_remove()
+{
+ distro_pkg_remove "${DEPS_LIST[@]}"
+}
+
+fi # _DEPS_SH
diff --git a/contrib/ci/distro.sh b/contrib/ci/distro.sh
new file mode 100644
index 000000000..5416bfff3
--- /dev/null
+++ b/contrib/ci/distro.sh
@@ -0,0 +1,86 @@
+#
+# Distribution version discovery
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+if [ -z ${_DISTRO_SH+set} ]; then
+declare -r _DISTRO_SH=
+
+# Distribution family (lowercase)
+declare DISTRO_FAMILY=
+# Distribution ID (lowercase)
+declare DISTRO_ID=
+# Distribution release (lowercase)
+declare DISTRO_RELEASE=
+
+if [ -e /etc/redhat-release ]; then
+ DISTRO_FAMILY=redhat
+elif [ -e /etc/debian_version ]; then
+ DISTRO_FAMILY=debian
+else
+ DISTRO_FAMILY=unknown
+fi
+declare -r DISTRO_FAMILY
+
+DISTRO_ID=`lsb_release --id | sed -e 's/^[^:]*:\s*\(.*\)$/\L\1\E/'`
+declare -r DISTRO_ID
+DISTRO_RELEASE=`lsb_release --release | sed -e 's/^[^:]*:\s*\(.*\)$/\L\1\E/'`
+declare -r DISTRO_RELEASE
+
+# Distribution branch (lowercase)
+declare -r DISTRO_BRANCH="-$DISTRO_FAMILY-$DISTRO_ID-$DISTRO_RELEASE-"
+
+
+# Install packages.
+# Args: [pkg_name...]
+function distro_pkg_install()
+{
+ declare prompt=$'Need root permissions to install packages.\n'
+ prompt+="Enter sudo password for $USER: "
+ if [[ "$DISTRO_BRANCH" == -redhat-* ]]; then
+ [ $# != 0 ] && sudo -p "$prompt" yum --assumeyes install -- "$@" |&
+ # Pass input to output, fail if a missing package is reported
+ # TODO Remove and switch to DNF once
+ # https://bugzilla.redhat.com/show_bug.cgi?id=1128139 is fixed
+ awk 'BEGIN {s=0}
+ /^No package .* available.$/ {s=1}
+ {print}
+ END {exit s}'
+ elif [[ "$DISTRO_BRANCH" == -debian-* ]]; then
+ [ $# != 0 ] && sudo -p "$prompt" apt-get --yes install -- "$@"
+ else
+ echo "Cannot install packages on $DISTRO_BRANCH" >&2
+ exit 1
+ fi
+}
+
+# Remove packages.
+# Args: [pkg_name...]
+function distro_pkg_remove()
+{
+ declare prompt=$'Need root permissions to remove packages.\n'
+ prompt+="Enter sudo password for $USER: "
+ if [[ "$DISTRO_BRANCH" == -redhat-* ]]; then
+ [ $# != 0 ] && sudo -p "$prompt" yum --assumeyes remove -- "$@"
+ elif [[ "$DISTRO_BRANCH" == -debian-* ]]; then
+ [ $# != 0 ] && sudo -p "$prompt" apt-get --yes remove -- "$@"
+ else
+ echo "Cannot remove packages on $DISTRO_BRANCH" >&2
+ exit 1
+ fi
+}
+
+fi # _DISTRO_SH
diff --git a/contrib/ci/make-check-wrap b/contrib/ci/make-check-wrap
new file mode 100755
index 000000000..4ad323e5f
--- /dev/null
+++ b/contrib/ci/make-check-wrap
@@ -0,0 +1,52 @@
+#!/bin/bash
+#
+# Build a make check target, prepending test commands with specified command
+# and arguments.
+# Args: [make_arg...] [-- wrap_cmd wrap_arg...]
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set -o nounset -o pipefail -o errexit
+
+declare -a argv=("make")
+declare arg
+
+while [ $# != 0 ]; do
+ arg="$1"
+ shift
+ if [ "$arg" == "--" ]; then
+ break
+ fi
+ argv+=("$arg")
+done
+
+if [ $# != 0 ]; then
+ # If Makefile supports LOG_COMPILER
+ if grep -q -w LOG_COMPILER Makefile; then
+ printf -v arg 'LOG_COMPILER=%q' $1
+ argv+=("$arg")
+ shift
+ if [ $# != 0 ]; then
+ printf -v arg ' %q' "$@"
+ argv+=("LOG_FLAGS=$arg")
+ fi
+ else
+ printf -v arg ' %q' "$@"
+ argv+=("AUX_TESTS_ENVIRONMENT=$arg")
+ fi
+fi
+
+"${argv[@]}"
diff --git a/contrib/ci/misc.sh b/contrib/ci/misc.sh
new file mode 100644
index 000000000..9af848c76
--- /dev/null
+++ b/contrib/ci/misc.sh
@@ -0,0 +1,116 @@
+#
+# Miscellaneous routines.
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+if [ -z ${_MISC_SH+set} ]; then
+declare -r _MISC_SH=
+
+# Remove files and directories recursively, forcing write permissions on
+# directories.
+# Args: path...
+function rm_rf_ro()
+{
+ chmod -Rf u+w -- "$@" || true
+ rm -Rf -- "$@"
+}
+
+# Run "scan-build" with output to a single specified directory, instead of
+# a subdirectory.
+# Args: dir [scan_build_arg...]
+function scan_build_single()
+{
+ declare -r dir="$1"; shift
+ declare entry
+ declare subdir
+ declare status
+
+ set +o errexit
+ scan-build -o "$dir" "$@"
+ status="$?"
+ set -o errexit
+
+ for entry in "$dir/"*; do
+ if [ -n "${subdir+set}" ] || ! [ -d "$entry" ]; then
+ echo 'Unexpected entries in scan-build output directory' >&2
+ exit 1
+ fi
+ subdir="$entry"
+ done
+
+ mv "$subdir/"* "$dir"
+ rmdir "$subdir"
+ return "$status"
+}
+
+# Check if a scan-build result directory has any non-empty .plist files.
+# Args: dir
+function scan_check()
+{
+ declare -r dir="$1"
+ declare f
+ for f in "$dir"/*.plist; do
+ if [ "`xqilla -i \"\$f\" /dev/stdin \
+ <<<'count(/plist/dict/array[count(*) > 0])'`" != 0 ]; then
+ return 1
+ fi
+ done
+ return 0
+}
+
+# Extract line and function coverage percentage from a "genhtml" or "lcov
+# --summary" output.
+# Input: "genhtml" or "lcov --summary" output
+# Output: lines funcs
+function lcov_summary()
+{
+ sed -ne 's/^ *\(lines\|functions\)\.*: \([0-9]\+\).*$/ \2/p' |
+ tr -d '\n'
+ echo
+}
+
+# Check if a "genhtml" or "lcov --summary" output has a minimum coverage
+# percentage of lines and functions.
+# Input: "genhtml" or "lcov --summary" output
+# Args: min_lines min_funcs
+function lcov_check()
+{
+ declare -r min_lines="$1"; shift
+ declare -r min_funcs="$1"; shift
+ declare lines
+ declare funcs
+
+ read -r lines funcs < <(lcov_summary)
+ ((lines >= min_lines && funcs >= min_funcs)) && return 0 || return 1
+}
+
+# Check if the current user belongs to a group.
+# Args: group_name
+function memberof()
+{
+ declare -r group_name="$1"
+ declare group_id
+ declare id
+ group_id=`getent group "$group_name" | cut -d: -f3` || return 1
+ for id in "${GROUPS[@]}"; do
+ if [ "$id" == "$group_id" ]; then
+ return 0
+ fi
+ done
+ return 1
+}
+
+fi # _MISC_SH
diff --git a/contrib/ci/rpm-spec-builddeps b/contrib/ci/rpm-spec-builddeps
new file mode 100755
index 000000000..b73ed2083
--- /dev/null
+++ b/contrib/ci/rpm-spec-builddeps
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+#
+# Extract build dependencies from an RPM .spec file.
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+import re
+import rpm
+
+def usage(file):
+ file.write(("Usage: %s SPEC\n" +
+ "Extract build dependencies from an RPM .spec file.\n") %
+ re.match(".*?([^/]+)$", sys.argv[0]).group(1))
+
+if len(sys.argv) != 2:
+ usage(sys.stderr)
+ sys.exit(1)
+
+spec = rpm.spec(sys.argv[1])
+for d in rpm.ds(spec.sourceHeader, 'requires'):
+ print d.DNEVR()[2:]
diff --git a/contrib/ci/run b/contrib/ci/run
new file mode 100755
index 000000000..b8a296aea
--- /dev/null
+++ b/contrib/ci/run
@@ -0,0 +1,371 @@
+#!/bin/bash
+#
+# Run continuous integration tests.
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set -o nounset -o pipefail -o errexit
+export PATH=`dirname "\`readlink -f \"\$0\"\`"`:$PATH
+export LC_ALL=C
+
+. deps.sh
+. distro.sh
+. configure.sh
+. misc.sh
+
+declare -r DEBUG_CFLAGS="-g3 -O2"
+declare -r COVERAGE_CFLAGS="-g3 -O0 --coverage"
+declare -r ARCH=`uname -m`
+declare -r CPU_NUM=`getconf _NPROCESSORS_ONLN`
+declare -r TITLE_WIDTH=24
+declare -r RESULT_WIDTH=18
+
+# Minimum percentage of code lines covered by tests
+declare -r COVERAGE_MIN_LINES=15
+# Minimum percentage of code functions covered by tests
+declare -r COVERAGE_MIN_FUNCS=0
+
+declare BASE_PFX=""
+declare DEPS=true
+declare BASE_DIR=`pwd`
+declare MODERATE=false
+declare RIGOROUS=false
+
+# Output program usage information.
+function usage()
+{
+ cat <<EOF
+Usage: `basename "$0"` [OPTION...]
+Run continuous integration tests.
+
+Options:
+ -h, --help Output this help message and exit.
+ -p, --prefix=STRING Use STRING as the prefix to prepend to file and
+ directory paths in output.
+ -n, --no-deps Don't attempt to install dependencies.
+ -e, --essential Run the essential subset of tests.
+ -m, --moderate Run the moderate subset of tests.
+ -r, --rigorous,
+ -f, --full Run the rigorous (full) set of tests.
+
+Default options: --essential
+EOF
+}
+
+# Output a file display path: a path relocated from base directory (BASE_DIR)
+# to base prefix (BASE_PFX).
+# Args: path
+function disppath()
+{
+ declare -r path=`readlink -f "$1"`
+ printf "%s" "$BASE_PFX${path:${#BASE_DIR}+1}"
+}
+
+# Run a stage.
+# Args: id cmd [arg...]
+function stage()
+{
+ declare -r id="$1"; shift
+ declare -r log="ci-$id.log"
+ declare status
+ declare start
+ declare end
+ declare duration
+
+ printf "%-${TITLE_WIDTH}s" "$id:"
+
+ {
+ printf "Start: "
+ start=`date +%s`
+ date --date="@$start"
+ set +o errexit
+ (
+ set -o errexit -o xtrace
+ "$@"
+ )
+ status=$?
+ set -o errexit
+ printf "End: "
+ end=`date +%s`
+ date --date="@$end"
+ } &> "$log"
+
+ duration=$((end - start))
+
+ if [ "$status" == 0 ]; then
+ printf 'success '
+ else
+ printf 'failure '
+ fi
+ printf "%02u:%02u:%02u " \
+ $((duration / (60 * 60))) \
+ $((duration / 60 % 60)) \
+ $((duration % 60))
+ disppath "$log"
+ printf "\n"
+
+ return "$status"
+}
+
+# Execute mock as is, or, if the user is not in the "mock" group, under sudo,
+# which has password prompt/input on the console, instead of stderr/stdin.
+# Args: [mock_arg...]
+function mock_privileged()
+{
+ if memberof mock; then
+ mock "$@"
+ else
+ declare prompt=$'Not a "mock" group member.\n'
+ prompt+="To run mock enter sudo password for $USER: "
+ sudo -p "$prompt" mock "$@"
+ fi
+}
+
+# Execute mock_privileged with extra chroot configuration added.
+# Args: chroot [mock_arg...]
+# Input: extra configuration
+function mock_privileged_conf()
+{
+ declare -r chroot="$1"; shift
+ declare conf_dir
+
+ conf_dir=`mktemp --tmpdir --directory mock-config.XXXXXXXX`
+ trap 'trap - RETURN; rm -R "$conf_dir";' RETURN
+ cp -r /etc/mock/* "$conf_dir"/
+ cat >> "${conf_dir}/${chroot}.cfg"
+ mock_privileged --configdir="$conf_dir" --root="$chroot" "$@"
+}
+
+# Execute mock_privileged with dependency package source configuration added.
+# Args: chroot [mock_arg...]
+function mock_privileged_deps()
+{
+ declare -r chroot="$1"; shift
+ declare repo
+
+ if [[ "$chroot" == fedora-* ]]; then
+ repo='fedora-$releasever-$basearch'
+ elif [[ "$chroot" =~ epel-([0-9]+) ]]; then
+ repo="epel-${BASH_REMATCH[1]}-\$basearch"
+ else
+ echo "Unknown chroot config: $chroot" >&2
+ exit 1
+ fi
+
+ mock_privileged_conf "$chroot" "$@" <<<"
+config_opts['yum.conf'] += '''
+[sssd-deps]
+name=Extra SSSD dependencies
+baseurl=http://copr-be.cloud.fedoraproject.org/results/lslebodn/sssd-deps/$repo/
+skip_if_unavailable=true
+gpgcheck=0
+enabled=1
+'''
+"
+}
+
+# Run debug build checks.
+function build_debug()
+{
+ export CFLAGS="$DEBUG_CFLAGS"
+ declare test_dir
+ declare test_dir_distcheck
+ declare distcheck_configure_args
+ declare status
+
+ test_dir=`mktemp --directory /dev/shm/ci-test-dir.XXXXXXXX`
+ stage configure "$BASE_DIR/configure" \
+ "${CONFIGURE_ARG_LIST[@]}" \
+ --with-test-dir="$test_dir"
+
+ # Not building "tests" due to https://fedorahosted.org/sssd/ticket/2350
+ stage make-tests make-check-wrap -j $CPU_NUM check -- true
+
+ # Ignored until issues found by Valgrind are fixed
+ status=0
+ CK_FORK=no \
+ stage make-check-valgrind \
+ make-check-wrap -j $CPU_NUM check -- \
+ libtool --mode=execute \
+ valgrind-condense 99 '!(*.py)' -- \
+ --vgdb=no \
+ --trace-children=yes \
+ --trace-children-skip='*/bin/*,*/sbin/*' \
+ --leak-check=full ||
+ status=$?
+ mv "$test_dir" ci-test-dir
+
+ if "$MODERATE"; then
+ test_dir_distcheck=`mktemp --directory /dev/shm/ci-test-dir.XXXXXXXX`
+ # Single thread due to https://fedorahosted.org/sssd/ticket/2354
+ status=0
+ printf -v distcheck_configure_args " %q" \
+ "${CONFIGURE_ARG_LIST[@]}" \
+ "--with-test-dir=$test_dir_distcheck"
+ stage make-distcheck make distcheck \
+ AUX_DISTCHECK_CONFIGURE_FLAGS=" \
+ $distcheck_configure_args" ||
+ status=$?
+ mv "$test_dir_distcheck" ci-test-dir-distcheck
+ ((status == 0))
+
+ if [[ "$DISTRO_BRANCH" == -redhat-* ]]; then
+ stage make-srpm env -u CFLAGS -- make srpm
+ stage mock-epel6 mock_privileged_deps "epel-6-$ARCH" \
+ --resultdir ci-mock-result-epel6 \
+ rpmbuild/SRPMS/*.src.rpm
+ stage mock-fedora20 mock_privileged_deps "fedora-20-$ARCH" \
+ --resultdir ci-mock-result-fedora20 \
+ rpmbuild/SRPMS/*.src.rpm
+ fi
+ fi
+
+ unset CFLAGS
+}
+
+# Run coverage build checks.
+function build_coverage()
+{
+ declare -r scan_report_dir="ci-report-scan"
+ declare -r coverage_report_dir="ci-report-coverage"
+ declare test_dir
+
+ export CFLAGS="$COVERAGE_CFLAGS"
+
+ test_dir=`mktemp --directory /dev/shm/ci-test-dir.XXXXXXXX`
+ stage configure scan-build "$BASE_DIR/configure" \
+ "${CONFIGURE_ARG_LIST[@]}" \
+ --with-test-dir="$test_dir"
+
+ # Build everything, including tests
+ # Not building "tests" due to https://fedorahosted.org/sssd/ticket/2350
+ stage scan-make-tests scan_build_single \
+ "$scan_report_dir" \
+ -plist-html \
+ --html-title="sssd - scan-build report" \
+ make-check-wrap -j $CPU_NUM check -- true
+ printf "%-$((TITLE_WIDTH + RESULT_WIDTH))s%s\n" \
+ "scan report:" \
+ "`disppath \"\$scan_report_dir/index.html\"`"
+ # Ignored until issues found by the scanner are fixed
+ stage scan-check scan_check "$scan_report_dir" ||
+ true
+
+ stage lcov-pre lcov --capture --initial --directory . \
+ --base-directory "$BASE_DIR" \
+ --output-file ci-base.info
+ # Run tests
+ stage make-check scan-build make -j $CPU_NUM check || true
+ mv "$test_dir" ci-test-dir
+
+ stage lcov-post lcov --capture --directory . \
+ --base-directory "$BASE_DIR" \
+ --output-file ci-check.info
+ stage lcov-merge lcov --add-tracefile ci-base.info \
+ --add-tracefile ci-check.info \
+ --output-file ci-dirty.info
+ stage lcov-clean lcov --remove ci-dirty.info \
+ "/usr/*" "src/tests/*" \
+ --output-file ci.info
+ stage genhtml eval 'genhtml --output-directory \
+ "$coverage_report_dir" \
+ --title "sssd" --show-details \
+ --legend --prefix "$BASE_DIR" \
+ ci.info |& tee ci-genhtml.out'
+ printf "%-$((TITLE_WIDTH + RESULT_WIDTH))s%s\n" \
+ "coverage report:" \
+ "`disppath \"\$coverage_report_dir/index.html\"`"
+
+ # If dependencies for all tests are satisfied
+ # and so all the tests should have been built and ran.
+ if "$DEPS_TESTS_SATISFIED"; then
+ stage lcov-check eval 'lcov_check "$COVERAGE_MIN_LINES" \
+ "$COVERAGE_MIN_FUNCS" \
+ < ci-genhtml.out'
+ fi
+
+ unset CFLAGS
+}
+
+# Run a build inside a sub-directory.
+# Args: id cmd [arg...]
+function run_build()
+{
+ declare -r id="$1"; shift
+ declare -r dir="ci-build-$id"
+
+ mkdir "$dir"
+ printf "%-$((TITLE_WIDTH + RESULT_WIDTH))s%s\n" \
+ "${id^^} BUILD:" "`disppath \"\$dir\"`"
+
+ cd "$dir"
+ "$@"
+ cd ..
+}
+
+#
+# Main routine
+#
+declare args_expr
+args_expr=`getopt --name \`basename "\$0"\` \
+ --options hp:nemrf \
+ --longoptions help,prefix:,no-deps \
+ --longoptions essential,moderate,rigorous,full \
+ -- "$@"`
+eval set -- "$args_expr"
+
+while true; do
+ case "$1" in
+ -h|--help)
+ usage; exit 0;;
+ -p|--prefix)
+ BASE_PFX="$2"; shift 2;;
+ -n|--no-deps)
+ DEPS=false; shift;;
+ -e|--essential)
+ MODERATE=false; RIGOROUS=false; shift;;
+ -m|--moderate)
+ MODERATE=true; RIGOROUS=false; shift;;
+ -r|--rigorous|-f|--full)
+ MODERATE=true; RIGOROUS=true; shift;;
+ --)
+ shift; break;;
+ *)
+ echo "Unknown option: $1" >&2
+ exit 1;;
+ esac
+done
+
+if [ $# != 0 ]; then
+ echo "Positional arguments are not accepted." >&2
+ usage >&2
+ exit 1
+fi
+
+trap 'echo FAILURE' EXIT
+rm_rf_ro ci-*
+export V=1
+if "$DEPS"; then
+ stage install-deps deps_install
+fi
+stage autoreconf autoreconf --install --force
+run_build debug build_debug
+if "$RIGOROUS"; then
+ run_build coverage build_coverage
+fi
+unset V
+trap - EXIT
+echo SUCCESS
diff --git a/contrib/ci/valgrind-condense b/contrib/ci/valgrind-condense
new file mode 100755
index 000000000..b838039e2
--- /dev/null
+++ b/contrib/ci/valgrind-condense
@@ -0,0 +1,128 @@
+#!/bin/bash
+#
+# Run Valgrind, condensing logged reports into an exit code.
+#
+# Copyright (C) 2014 Red Hat
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set -o nounset -o pipefail -o errexit
+shopt -s extglob
+
+function usage()
+{
+ cat <<EOF
+Usage: `basename "$0"` ERROR_EXITCODE [PATH_PATTERN...] [-- VALGRIND_ARG...]
+Run Valgrind, condensing logged reports into an exit code.
+
+Arguments:
+ ERROR_EXITCODE An exit code to return if at least one error is found in
+ Valgrind log files.
+ PATH_PATTERN An extended glob pattern matching (original) paths to
+ programs to execute under Valgrind. Execution is skipped
+ and success is returned for non-matching programs. Without
+ patterns, all programs match.
+ VALGRIND_ARG An argument to pass to Valgrind after the arguments
+ specified by `basename "$0"`.
+
+The first non-option VALGRIND_ARG will be considered the path to the program
+to execute under Valgrind and will be used in naming Valgrind log files as
+such:
+
+ PROGRAM_NAME.PID.valgrind.log
+
+where PROGRAM_NAME is the filename portion of the program path and PID is the
+executed process ID. If the last directory of the program path is ".libs" and
+the filename begins with "lt-", both are removed to match the name of libtool
+frontend script. All files matching PROGRAM_NAME.*.valgrind.log are removed
+before invoking Valgrind.
+
+If an error is found in Valgrind log files, ERROR_EXITCODE is returned,
+otherwise Valgrind exit code is returned.
+EOF
+}
+
+
+if [[ $# == 0 ]]; then
+ echo "Invalid number of arguments." >&2
+ usage >&2
+ exit 1
+fi
+
+declare error_exitcode="$1"; shift
+declare -a path_pattern_list=()
+declare arg
+declare got_dash_dash
+declare program_path
+declare path_pattern
+declare match
+declare program_name
+declare status=0
+
+# Extract path patterns
+while [[ $# != 0 ]]; do
+ arg="$1"
+ shift
+ if [[ "$arg" == "--" ]]; then
+ break
+ else
+ path_pattern_list+=("$arg")
+ fi
+done
+
+# Find program path argument
+got_dash_dash=false
+for arg in "$@"; do
+ if [[ "$arg" == "--" ]]; then
+ got_dash_dash=true
+ elif "$got_dash_dash" || [[ "$arg" != -* ]]; then
+ program_path="$arg"
+ break
+ fi
+done
+
+if [[ -z "${program_path+set}" ]]; then
+ echo "Program path not specified." >&2
+ usage >&2
+ exit 1
+fi
+
+# Match against path patterns, if any
+if [[ ${#path_pattern_list[@]} != 0 ]]; then
+ match=false
+ for path_pattern in "${path_pattern_list[@]}"; do
+ if [[ "$program_path" == $path_pattern ]]; then
+ match=true
+ fi
+ done
+ if ! $match; then
+ exit 0
+ fi
+fi
+
+# Generate original path from libtool path
+program_path=`sed -e 's/^\(.*\/\)\?\.libs\/lt-\([^\/]\+\)$/\1\2/' \
+ <<<"$program_path"`
+
+program_name=`basename "$program_path"`
+
+rm -f "$program_name".*.valgrind.log
+valgrind --log-file="$program_name.%p.valgrind.log" "$@" || status=$?
+
+if grep -q '^==[0-9]\+== *ERROR SUMMARY: *[1-9]' \
+ "$program_name".*.valgrind.log; then
+ exit "$error_exitcode"
+else
+ exit "$status"
+fi