#!/bin/bash # Should be run from the root dir, or SOURCE_DIR should be set. set -o errexit set -o nounset set -o pipefail # Default compiler : ${CC:="gcc"} # Paths : ${SOURCE_DIR:="."} : ${DIST_DIR:="${SOURCE_DIR}/dist"} : ${BUILD_DIR:="/tmp/build"} # GPG Configuration : ${GPG_PASSPHRASE:=""} # Make those paths absolute, and export them for the Python tests to consume. export SOURCE_DIR="$(readlink -f "${SOURCE_DIR}")" export DIST_DIR="$(readlink -f "${DIST_DIR}")" export BUILD_DIR="$(readlink -f "${BUILD_DIR}")" # Configuration : ${FORCE_SUBREAPER:="1"} export FORCE_SUBREAPER # Our build platform doesn't have those newer Linux flags, but we want Tini to have subreaper support # We also use those in our tests CFLAGS="${CFLAGS-} -DPR_SET_CHILD_SUBREAPER=36 -DPR_GET_CHILD_SUBREAPER=37" if [[ "${FORCE_SUBREAPER}" -eq 1 ]]; then # If FORCE_SUBREAPER is requested, then we set those CFLAGS for the Tini build export CFLAGS fi echo "CC=${CC}" echo "CFLAGS=${CFLAGS}" echo "MINIMAL=${MINIMAL-}" echo "ARCH_SUFFIX=${ARCH_SUFFIX-}" echo "ARCH_NATIVE=${ARCH_NATIVE-}" # Ensure Python output is not buffered (to make tests output clearer) export PYTHONUNBUFFERED=1 # Set path to prioritize our utils export REAL_PATH="${PATH}" export PATH="${SOURCE_DIR}/ci/util:${PATH}" # Build CMAKE_ARGS=(-B"${BUILD_DIR}" -H"${SOURCE_DIR}") if [[ -n "${MINIMAL:-}" ]]; then CMAKE_ARGS+=(-DMINIMAL=ON) fi cmake "${CMAKE_ARGS[@]}" pushd "${BUILD_DIR}" make clean make if [[ -n "${ARCH_NATIVE-}" ]]; then make package fi popd pkg_version="$(cat "${BUILD_DIR}/VERSION")" if [[ -n "${ARCH_NATIVE-}" ]]; then echo "Built native package (ARCH_NATIVE=${ARCH_NATIVE-})" echo "Running smoke and internal tests" BIN_TEST_DIR="${BUILD_DIR}/bin-test" mkdir -p "$BIN_TEST_DIR" export PATH="${BIN_TEST_DIR}:${PATH}" # Smoke tests (actual tests need Docker to run; they don't run within the CI environment) for tini in "${BUILD_DIR}/tini" "${BUILD_DIR}/tini-static"; do echo "Smoke test for ${tini}" "$tini" --version echo "Testing ${tini} --version" "$tini" --version | grep -q "tini version" echo "Testing ${tini} without arguments exits with 1" ! "$tini" 2>/dev/null echo "Testing ${tini} shows help message" { ! "$tini" 2>&1 } | grep -q "supervision of a valid init process" if [[ -n "${MINIMAL:-}" ]]; then echo "Testing $tini with: true" "${tini}" true echo "Testing $tini with: false" if "${tini}" false; then exit 1 fi echo "Testing ${tini} does not reference options that don't exist" ! { ! "$tini" 2>&1 } | grep -q "more verbose" # We try running binaries named after flags (both valid and invalid # flags) and test that they run. for flag in h s w x; do bin="-${flag}" echo "Testing $tini can run binary: ${bin}" cp "$(which true)" "${BIN_TEST_DIR}/${bin}" "$tini" "$bin" done echo "Testing $tini can run binary --version if args are given" cp "$(which true)" "${BIN_TEST_DIR}/--version" if "$tini" "--version" --foo | grep -q "tini version"; then exit 1 fi else echo "Testing ${tini} -h" "${tini}" -h echo "Testing $tini for license" "$tini" -l | diff - "${SOURCE_DIR}/LICENSE" echo "Testing $tini with: true" "${tini}" -vvv true echo "Testing $tini with: false" if "${tini}" -vvv false; then exit 1 fi echo "Testing ${tini} references options that exist" { ! "$tini" 2>&1 } | grep -q "more verbose" echo "Testing $tini with: -- true (should succeed)" "${tini}" -vvv -- true echo "Testing $tini with: -- -- true (should fail)" if "${tini}" -vvv -- -- true; then exit 1 fi fi echo "Testing ${tini} supports TINI_VERBOSITY" TINI_VERBOSITY=3 "$tini" true 2>&1 | grep -q 'Received SIGCHLD' echo "Testing ${tini} exits with 127 if the command does not exist" "$tini" foobar123 && rc="$?" || rc="$?" if [[ "$rc" != 127 ]]; then echo "Exit code was: ${rc}" exit 1 fi echo "Testing ${tini} exits with 126 if the command is not executable" "$tini" /etc && rc="$?" || rc="$?" if [[ "$rc" != 126 ]]; then echo "Exit code was: ${rc}" exit 1 fi # Test stdin / stdout are handed over to child echo "Testing ${tini} does not break pipes" echo "exit 0" | "${tini}" sh if [[ ! "$?" -eq "0" ]]; then echo "Pipe test failed" exit 1 fi echo "Checking hardening on $tini" hardening_skip=(--nopie --nostackprotector --nobindnow) if [[ "$CC" == "musl-gcc" ]]; then # FORTIFY_SOURCE is a glibc thing hardening_skip=("${hardening_skip[@]}" --nofortify) fi hardening-check "${hardening_skip[@]}" "${tini}" done # Quick package audit if which rpm >/dev/null; then echo "Contents for RPM:" rpm -qlp "${BUILD_DIR}/tini_${pkg_version}.rpm" echo "--" fi if which dpkg >/dev/null; then echo "Contents for DEB:" dpkg --contents "${BUILD_DIR}/tini_${pkg_version}.deb" echo "--" fi # Compile test code "${CC}" -o "${BUILD_DIR}/sigconf-test" "${SOURCE_DIR}/test/sigconf/sigconf-test.c" # Create virtual environment to run tests. # Accept system site packages for faster local builds. VENV="${BUILD_DIR}/venv" virtualenv --system-site-packages "${VENV}" # Don't use activate because it does not play nice with nounset export PATH="${VENV}/bin:${PATH}" export CFLAGS # We need them to build our test suite, regardless of FORCE_SUBREAPER # Install test dependencies CC=gcc python3 -m pip install psutil python-prctl bitmap # Run tests python3 "${SOURCE_DIR}/test/run_inner_tests.py" else echo "Not a native build, skipping smoke and internal tests" fi # Now, copy over files to DIST_DIR, with appropriate names depending on the # architecture. # Handle the DEB / RPM mkdir -p "${DIST_DIR}" DIST_TINIS=() SUFFIX="" if [[ -n "$ARCH_SUFFIX" ]]; then SUFFIX="-${ARCH_SUFFIX}" elif [[ -z "$ARCH_NATIVE" ]]; then echo "Refusing to publish a non-native build without suffix!" exit 1 fi for build_tini in tini tini-static; do dist_tini="${build_tini}${SUFFIX}" cp "${BUILD_DIR}/${build_tini}" "${DIST_DIR}/${dist_tini}" DIST_TINIS+=("$dist_tini") done if [[ -n "${ARCH_NATIVE-}" ]]; then for pkg_format in deb rpm; do build_tini="tini_${pkg_version}.${pkg_format}" dist_tini="tini_${pkg_version}${SUFFIX}.${pkg_format}" cp "${BUILD_DIR}/${build_tini}" "${DIST_DIR}/${dist_tini}" DIST_TINIS+=("$dist_tini") done fi echo "Tinis: ${DIST_TINIS[*]}" pushd "$DIST_DIR" for tini in "${DIST_TINIS[@]}"; do echo "${tini}:" for sum in sha1sum sha256sum; do "$sum" "$tini" | tee "${tini}.${sum}" done file "$tini" echo "--" done # If a signing key and passphrase are made available, then use it to sign the # binaries if [[ -n "$GPG_PASSPHRASE" ]] && [[ -f "${SOURCE_DIR}/sign.key" ]]; then echo "Signing tinis" GPG_SIGN_HOMEDIR="${BUILD_DIR}/gpg-sign" GPG_VERIFY_HOMEDIR="${BUILD_DIR}/gpg-verify" PGP_KEY_FINGERPRINT="595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7" PGP_KEYSERVER="ha.pool.sks-keyservers.net" mkdir "${GPG_SIGN_HOMEDIR}" "${GPG_VERIFY_HOMEDIR}" chmod 700 "${GPG_SIGN_HOMEDIR}" "${GPG_VERIFY_HOMEDIR}" gpg --homedir "${GPG_SIGN_HOMEDIR}" --import "${SOURCE_DIR}/sign.key" gpg --homedir "${GPG_VERIFY_HOMEDIR}" --keyserver "$PGP_KEYSERVER" --recv-keys "$PGP_KEY_FINGERPRINT" for tini in "${DIST_TINIS[@]}"; do echo "${GPG_PASSPHRASE}" | gpg --homedir "${GPG_SIGN_HOMEDIR}" --passphrase-fd 0 --armor --detach-sign "${tini}" gpg --homedir "${GPG_VERIFY_HOMEDIR}" --verify "${tini}.asc" done fi popd