#!/usr/bin/env bash # # Copyright (C) 2011 Norbert Thiebaud # License: GPLv3 # if [ -n "$debug" ] ; then set -x fi BIN_DIR=$(dirname "$0") PAUSE_SECONDS=$((15*60)) PUSH_NIGHTLIES=0 PUSH_TO_BIBISECT_REPO=0 ARTIFACTDIR= SEND_MAIL="all" LOCK=1 MODE="tb" NICE= PROFILE_NAME= V= FORCE_REBUILD=0 METADATA_DIR="." WATCHDOG= GERRIT_REF= DO_NOT_CLEAN= GERRIT_HOST="logerrit" # load the internal functions. note that # tinderbuild_internals_.sh is sourced at the tail # end of tinbuild_internals.sh. this allow you do do # platform specific stuff. # after the platform specific internal script (if present) is # sourced, then tinbuild_phases.sh is sourced # this is where the standard build cycle unit functions are implemented # you can redefine these function in ~/.tinbuild/phases/.sh # which is sourced, if it exist, at the end of tinderbuild_phases.sh # # The build cycle, invoked in this script with do_build, consist # of 5 phases that are processed in this order: autogen, clean, make, test, push # each phase execution tries to invoke pre_, do_ and post_ # in this order, if the respective bash function are defined. # # In order to implement a specific step, pre_build for instance, you need # to implement a function pre_build() in ~/.tinbuild/config/phases/.sh # these function take no parameter at all, tough you can use any global variable # (the one in which a name in UPPERCASE in this script. all effort will be made to # maintain the existance of these variable # every step should also condition the entiere execution of the step by testing that # the variable $retval is equal to 0. if not the step must return $retval immediately # the step can do which ever actions that are deemded necessary.. # the step must collect the relevant messages in tb_${B}_.log # In case of error the step must set the error condition in the following manner # report_log=tb_${B}_.log # report_msgs=" failed - error is:" # retval=1 # with the convention that do_xxx steps are shortenen to just xxx in the above filenames # and messages when substitution . # # For example this is how do_make is implemented in tinderbuild_phases.sh # # do_make() # { # if [ "${retval}" = "0" ] ; then # if ! $NICE ${MAKE?} >tb_${B}_build.log 2>&1 ; then # report_log=tb_${B}_build.log # report_msgs="build failed - error is:" # retval=1 # fi # fi # } unset CDPATH source ${BIN_DIR?}/tinbuild_internals.sh display_setup_instruction() { cat << EOF In order to use $0 you need to: * create a directory ~/.tinbuild/config * create a file ~/.tinbuild/config/.cfg where is the argument you pased to the mandatory parameter -p that is sourced and define at least: SMTPHOST= SMTPUSER= SMTPPW= TINDER_NAME= OWNER= EOF } usage() { cat << EOF Usage $0 [options] -p Options: -h print help -a 'author'. This is for branch that are not under tb supervision. that is the email of the 'author' of the branch. email are sent to that email address for sucess and failure -c sanity check (verify that tou configuration is sane and send a test email) -d wrap make commands in a watchdog that will interrupt them if they get stuck for -g gerrit ref to build, like refs/changes/02/102/1 see gerrit download checkout information -i run with ionice -c3, can be combined with -n -k to not override autogen.lastrun if present (usefull with -z to do a quick test) -m [all,tb,owner,author,none] tb=mail progress to the tinderbox, all=tb + mail errors to the committer, general errors to the owner. owner= tb + any other mail to the owner only debug=any mail (progress or error) is sent to the owner author=send failure email to the authors + owner tb is not in the loop none=no mail If in doubt, use "all". -n run with nice, can be combined with -i -p profile to use. -r push the build nightly at a rate limited by (in kB/s) -t run the tests after a sucessfull build -v verbose. print more progress messages -w specify minimum wait between build attempt -x push the build (nightly?) into the bibisect repository -z bootstrap/prime the tinbuild EOF } load_profile() { local p=$1 local rc=0 if [ -z "$p" ] ; then echo "a profile is needed to run, see help" >&2 exit 1 else if [ ! -f $HOME/.tinbuild/config/${p}.cfg ] ; then echo "$HOME/.tinbuild/config/${p}.cfg can't be accessed" >&2 exit 1 else source "$HOME/.tinbuild/config/${p}.cfg" # add check to make sure that thing are setup correctly if [ -f "$HOME/.tinbuild/phases/${p}.sh" ] ; then source "$HOME/.tinbuild/phases/${p}.sh" fi fi fi } sanity_check() { g=$(git rev-parse --git-dir 2> /dev/null) if [ "$g" != ".git" ] ; then echo "Error: The current working directory must be the root git repo" exit 1 fi if [ ! -f configure.in -a ! -f configure.ac ] ; then echo "Error: The current working directory must be the root git repo" exit 1 fi if [ ! -d $HOME/.tinbuild ] ; then echo "Error: the directory $HOME/.tinbuild does not exist" >&2 display_setup_instruction exit 1 fi if [ ! -d $HOME/.tinbuild/config ] ; then echo "Error: the directory $HOME/.tinbuild/config does not exist" >&2 display_setup_instruction exit 1 fi if [ ! -f $HOME/.tinbuild/config/${PROFILE_NAME?}.cfg ] ; then echo "Error: the file $HOME/.tinbuild/config/${PROFILE_NAME?}.cfg does not exist" >&2 display_setup_instruction exit 1 fi if [ ! -f $HOME/.tinbuild/autogen/${PROFILE_NAME?}.autogen ] ; then if [ ! -f autogen.lastrun ] ; then echo "Error: Neither $HOME/.tinbuild/autogen/${PROFILE_NAME?}.autogen or autogen.lastrun exist" >&2 exit 1 else echo "Warning: the file $HOME/.tinbuild/autogen/${PROFILE_NAME?}.autogen does not exist. Will use autogen.lastrun :" >&2 cat autogen.lastrun fi fi source $HOME/.tinbuild/config/${PROFILE_NAME?}.cfg || (echo "Error sourcing $HOME/.tinbuild/config/${PROFILE_NAME?}.cfg" ; exit 1) rc=0 if [ -z "${SMTPHOST}" ] ; then echo "Error: missing SMTPHOST in config file" 1>&2 rc=1 fi if [ -z "${SMTPUSER}" ] ; then echo "Warning: missing SMTPUSER in config file (can work, depends on your smtp server)" 1>&2 fi if [ -n "${SMTPUSER}" -a -z "${SMTPPW}" ] ; then echo "Error: SMTPPW empty, but SMTPUSER set in config file" 1>&2 rc=1 fi if [ -z "${TINDER_NAME}" ] ; then echo "Error: missing TINDER_NAME in config file" 1>&2 rc=1 fi if [ -z "${OWNER}" ] ; then echo "Error: missing OWNER in config file" 1>&2 rc=1 fi if [ "$rc" != "0" ] ; then exit 1 fi local smtp_auth="" if [ -n "${SMTPUSER}" ] ; then smtp_auth="-xu ${SMTPUSER?} -xp ${SMTPPW?}" fi echo "test email from tinbuild" | ${BIN_DIR?}/sendEmail -f "$OWNER" -s "${SMTPHOST?}" $smtp_auth -t "$OWNER" -u "tinderbuild test" || (echo "Errot sending test email" 1>&2 ; exit 1) echo "Checking flock" ( do_flock -x 200 ) 200>${lock_file?} echo "Sanity checks OK." exit 0 } # # Main # while [ "${1}" != "" ]; do parm=${1%%=*} arg=${1#*=} has_arg= if [ "${1}" != "${parm?}" ] ; then has_arg=1 else arg="" fi # echo "parm=!${parm}!" # echo "arg=!${arg}!" case "${parm}" in -0) # avoid calling *_clean functions (incremental build) DO_NOT_CLEAN=1 ;; -a) # email to the branch's author for out-of-tinderbox branch buildbot if [ -z "${has_arg}" ] ; then shift; arg="$1" fi if [ -z "${arg}" ] ; then echo "Missing argument for option $parm" 1>&2 exit -1 else BRANCH_AUTHOR="$arg" fi ;; -b) # to override the branch name to use if [ -z "${has_arg}" ] ; then shift; arg="$1" fi if [ -z "${arg}" ] ; then echo "Missing argument for option $parm" 1>&2 exit -1 else BRANCH="$arg" fi ;; -c) # whether to invoke the sanity-check function sanity_check # do not return ;; -d) # wrap make in a watchdog if [ -z "${has_arg}" ] ; then shift; arg="$1" fi if [ -z "${arg}" ] ; then echo "Missing argument for option $parm" 1>&2 exit -1 else WATCHDOG="makewatchdog $arg -- " fi ;; -e) # whether to use flock to protect the build-cycle LOCK=0 ;; -f) # whether to force an initial rebuild on restart, even if the pull-info are unchanged FORCE_REBUILD=1 ;; -g) # gerit's ref to fetch. This imply a one time build, no memory if [ -z "${has_arg}" ] ; then shift; arg="$1" fi if [ -z "${arg}" ] ; then echo "Missing argument for option $parm" 1>&2 exit -1 else GERRIT_REF="${arg}" BRANCH="gerrit" SEND_MAIL="none" MODE="gerrit-patch" fi ;; -h) # display help usage; exit ;; -i) # be gentle on i/o NICE="$NICE ionice -c3" ;; -k) # do not override the local autogen.lastrun if present KEEP_AUTOGEN="YES" ;; -m) # which email to wend and to whom if [ -z "${has_arg}" ] ; then shift; arg="$1" fi if [ -z "${arg}" ] ; then echo "Missing argument for option $parm" 1>&2 exit -1 else SEND_MAIL="${arg}" fi ;; --mode) # operating mode of the tinbuild # tb,prime,gerrit,tb-gerrit,gerrit-tb if [ -z "${has_arg}" ] ; then shift; arg="$1" fi if [ -z "${arg}" ] ; then echo "Missing argument for option $parm" 1>&2 exit -1 else MODE="${arg}" fi ;; -n) # build 'nicely' :-) NICE="$NICE nice" ;; -p) # profile to use to deterine extra parameter (email mostly) and autogen arguments if [ -z "${has_arg}" ] ; then shift; arg="$1" fi if [ -z "${arg}" ] ; then echo "Missing argument for option $parm" 1>&2 exit -1 else PROFILE_NAME="${arg}" fi ;; -r) # whether to upload daily build. if [ -z "${has_arg}" ] ; then shift; arg="$1" fi if [ -z "${arg}" ] ; then echo "Missing argument for option $parm" 1>&2 exit -1 else PUSH_NIGHTLIES=1 BANDWIDTH="${arg}" fi ;; -s) # where to stage the build in case of -z with upload if [ -z "${has_arg}" ] ; then shift; arg="$1" fi if [ -z "${arg}" ] ; then echo "Missing argument for option $parm" 1>&2 exit -1 else STAGE_DIR="${arg}" fi ;; -t) # whether to run tests after the build DO_TESTS=1 ;; -v) # print more messages V=1 ;; -w) # cool-down time after a build, in seconds if [ -z "${has_arg}" ] ; then shift; arg="$1" fi if [ -z "${arg}" ] ; then echo "Missing argument for option $parm" 1>&2 exit -1 else PAUSE_SECONDS="${arg}" fi ;; -x) # Push builds to bibisect repo (per buildbot and major version) PUSH_TO_BIBISECT_REPO=1 ;; -z) # to run an initial build (without sending any email) to establish a 'baseline' MODE="prime" ;; -*) echo "Invalid option $1" 1>&2 usage; exit -1 ;; *) echo "Invalid argument $1" 1>&2 exit -1 ;; esac shift done load_profile "$PROFILE_NAME" if [ ! -d ${METADATA_DIR} ] ; then mkdir -p "${METADATA_DIR?}" || exit 1 fi if [ "${PUSH_TO_BIBISECT_REPO}" != "0" ] ; then if [ -z "${ARTIFACTDIR}" ] ; then echo "Error: to do bibisect you must define ARTIFACTDIR to point to your bibisect git repo" 1>&2 exit 1 else if [ ! -d "${ARTIFACTDIR}" -o ! -d "${ARTIFACTDIR}/.git" ] ; then echo "Error: ARTIFACTDIR:${ARTIFACTDIR} is not a git repository" 1>&2 exit 1 fi fi fi if [ "$MODE" = "gerrit-tb" -o "$MODE" = "tb-gerrit" -o "$MODE" = "gerrit" -o "$MODE" = "fair" ] ; then if [ -z "$GERRIT_PLATFORM" ] ; then os=$(uname) case "$os" in *Linux*) GERRIT_PLATFORM="LINUX" ;; Darwin) GERRIT_PLATFORM="MAC" ;; CYGWIN*) GERRIT_PLATFORM="WINDOWS" ;; esac fi if [ -z "$GERRIT_PLATFORM" ] ; then echo "Error: GERRIT_PLATFORM is required for mode involving gerrit" exit 1 fi if [ -z "$TINDER_ID" ] ; then echo "Error: TINDER_ID is required for mode involving gerrit" exit 1 fi fi # if we want email to be sent, we must make sure that the required parameters are set in the profile (or in the environment) case "$SEND_MAIL" in all|tb|owner|debug|author) if [ -n "${SEND_MAIL}" ] ; then rc=0 if [ -z "${SMTPHOST}" ] ; then echo "Error: missing SMTPHOST" 1>&2 rc=1 fi if [ -z "${SMTPUSER}" ] ; then echo "Warning: missing SMTPUSER (can work, depends on your smtp server)" 1>&2 fi if [ -n "${SMTPUSER}" -a -z "${SMTPPW}" ] ; then echo "Error: missing SMTPPW" 1>&2 rc=1 fi if [ -z "${TINDER_NAME}" ] ; then echo "Error: missing TINDER_NAME" 1>&2 rc=1 fi if [ -z "${OWNER}" ] ; then echo "Error: missing OWNER" 1>&2 rc=1 fi if [ "$rc" != "0" ] ; then exit 1 fi fi ;; none) SEND_MAIL="" ;; *) echo "Invalid -m argument:$SEND_MAIL" 1>&2 exit 1 ;; esac # where to report # right now we are limited to local branch that match what the tinderbox server expect # if [ -n "$BRANCH" ] ; then TINDER_BRANCH="$BRANCH" B="$BRANCH" else B=`git branch | grep '^\*' | sed 's/^..//' | sed 's/\//_/g'` TINDER_BRANCH= case "$B" in master) TINDER_BRANCH='MASTER' ;; libreoffice-3-4) TINDER_BRANCH=$B ;; libreoffice-3-5) TINDER_BRANCH=$B ;; libreoffice-3-6) TINDER_BRANCH=$B ;; libreoffice-4-0) TINDER_BRANCH=$B ;; *) if [ "${MODE}" = "prime" -o "${MODE}" = "gerrit-patch" -o "${MODE?}" = "gerrit" ] ; then TINDER_BRANCH=$B else if [ "$SEND_MAIL" = "all" -o "$SEND_MAIL" = "" ] ; then echo "Unknown, non-registered branch, please update tinbuild, and tinderbox.libreoffice.org."2>&1 ; exit 1; else TINDER_BRANCH=$B fi fi ;; esac fi # if we want to deliver bibisect make sure that bibisect is on the right branch # we will recheck that before each build just in case someone crazy were to mess # with bibisect while the tinderbox is running... but failing early is good if [ "${PUSH_TO_BIBISECT_REPO}" != "0" ] ; then if [ "${MODE?}" = "gerrit" -o "${MODE?}" = "gerrit-patch" -o "${MODE?}" = "prime" ] ; then echo "Warning: bibisect delivery is ignored for non tb build" 1>&2 PUSH_TO_BIBISECT_REPO=0 else position_bibisect_branch fi fi case "${MODE?}" in prime) if [ "$SEND_MAIL" != "owner" ] ; then SEND_MAIL="" # we don't want to notify the tinderbox fi if [ "$PUSH_NIGHTLIES" = "1" ] ; then rm -f "${METADATA_DIR?}/tb_${B?}_last-upload-day.txt" fi run_primer ;; tb) run_tb_loop ;; gerrit) run_gerrit_loop ;; tb-gerrit) run_tb_gerrit_loop "tb" ;; gerrit-tb) run_tb_gerrit_loop "gerrit" ;; fair) run_tb_gerrit_loop "fair" ;; gerrit-patch) run_gerrit_patch ;; esac