#! /usr/bin/env bash
repository="$1"
workdir="$2"
fossilCIDir="$3"
if [ -z "${repository}" -o -z "${workdir}" ]; then
echo "Usage: autobuild <repository> <workdir> [<fossil-ci-dir>]" >&2
exit 1
fi
if [ -z "${fossilCIDir}" ]; then
fossilCIDir='.fossil-ci'
fi
# 0. Resolve paths
repository="$(cd "$(dirname "${repository}")" && echo "$(pwd)/$(basename "${repository}")")" || exit 1
workdir="$(mkdir -p "${workdir}" && cd "${workdir}" && pwd)" || exit 1
# 1. Register cleanup
tmpdir=''
function cleanup() {
if [ -n "${tmpdir}" ]; then
rm -rf "${tmpdir}"
fi
}
trap cleanup EXIT
# 2. Setup a sane environment
## 2.a. Abort on errors
set -e
## 2.b. Universal timezone
export TZ=UTC
## 2.c. Basic locale
unset $(locale | cut -f 1 -d =)
## 2.d. Sane umask
umask 022
# 3. Update the Fossil repository
fossil config -R "${repository}" pull all >/dev/null 2>/dev/null || :
fossil sync -R "${repository}" >/dev/null 2>/dev/null || :
# 4. Get a list of branches
branches=( $(fossil branch -R "${repository}" list) )
# 5. Get Fossil CI configuration from the trunk branch
## 5.a. Set default config
excludedBranches=()
includedBranches=('')
buildCommands=('./autogen.sh || :' ./configure make)
initCommands=()
postCommands=()
branchPostCommands=()
testCommands=()
builderID=''
projectName="$(fossil info -R "${repository}" | awk '/^project-name:/{ sub(/^project-name: */, ""); gsub(/ /, ""); print; }')"
tagSuffix=''
tagPrefix=''
tagOmitRedundant='0'
tagOmitPass='0'
## 5.b. Read config
config="$(fossil cat -R "${repository}" -r trunk "${fossilCIDir}/config" 2>/dev/null)" || :
## 5.c Load config
if [ -f ~/.fossil-ci/config ]; then
. ~/.fossil-ci/config
fi
eval "${config}"
if [ -f ~/.fossil-ci/"${projectName}"/config ]; then
. ~/.fossil-ci/"${projectName}"/config
fi
## 5.d. Post-process config
### 5.d.i. Add builderID as a tag suffix if none was given
if [ -z "${tagSuffix}" ]; then
if [ -n "${builderID}" ]; then
tagSuffix="-${builderID}"
fi
fi
# 6. Perform any configured initialization
for cmd in "${initCommands[@]}"; do
eval "${cmd}" >/dev/null 2>/dev/null || :
done
# 7. For each branch, attempt to build
for branch in "${branches[@]}"; do
## 7.a. Only process specific branches
ignoreBranch='1'
for testBranch in "${includedBranches[@]}"; do
if echo "${branch}" | grep "${testBranch}" >/dev/null; then
ignoreBranch='0'
break
fi
done
for testBranch in "${excludedBranches[@]}"; do
if echo "${branch}" | grep "${testBranch}" >/dev/null; then
ignoreBranch='1'
break
fi
done
if [ "${ignoreBranch}" = '1' ]; then
continue
fi
## 7.b. Find the branch working directory
branchdir="$(echo "${branch}" | sed 's@[^A-Za-z0-9-]@_@g')"
builddir="${workdir}/build-output/branch-${branchdir}"
branchdir="${workdir}/working-copies/branch-${branchdir}"
mkdir -p "${branchdir}"
## 7.c. Determine current checkout ID
checkout="$(fossil info -R "${repository}" "${branch}" | awk '/^uuid:/{ print $3 "T" $4 "-" $2 }')"
builddir="${builddir}/checkout-${checkout}"
## 7.d. Determine if build even needs to occur
if [ -d "${builddir}" ]; then
continue
fi
mkdir -p "${builddir}"
## 7.e. Check-out or update the branch
(
cd "${branchdir}" || exit 1
if [ ! -f '.fslckout' ]; then
fossil open "${repository}" "${branch}" --nested
else
fossil update "${branch}"
fi
) > "${builddir}/update.log" 2>&1 || continue
## 7.f. Build and log the results, if there are any
build_pass='1'
(
cd "${branchdir}" || exit 1
for cmd in "${buildCommands[@]}"; do
( eval "set -x; ${cmd}" ) || exit 1
done
) > "${builddir}/build.log" 2>&1 || build_pass='0'
### 7.g. Test the branch
if [ "${build_pass}" = '1' ]; then
tests_pass='1'
(
cd "${branchdir}" || exit 1
for cmd in "${testCommands[@]}"; do
( eval "set -x; ${cmd}" ) || exit 1
done
) > "${builddir}/test.log" 2>&1 || tests_pass='0'
fi
### 7.h. Tag the branch with
tagsToAdd=()
if [ "${build_pass}" = '1' ]; then
if [ "${tagOmitRedundant}" != '1' -a "${tagOmitPass}" != '1' ]; then
tagsToAdd=("${tagsToAdd[@]}" build-pass)
fi
if [ "${tests_pass}" = '1' ]; then
if [ "${tagOmitPass}" != '1' ]; then
tagsToAdd=("${tagsToAdd[@]}" tests-pass)
fi
else
tagsToAdd=("${tagsToAdd[@]}" tests-fail)
fi
else
tagsToAdd=("${tagsToAdd[@]}" build-fail)
fi
tagsToAddOpts=()
for tag in "${tagsToAdd[@]}"; do
tagsToAddOpts=("${tagsToAddOpts[@]}" --tag "${tagPrefix}${tag}${tagSuffix}")
done
fossil amend -R "${repository}" "${branch}" "${tagsToAddOpts[@]}" > "${builddir}/update.log" 2>&1
### 7.i. Upload the logs somewhere if requested
#### XXX:TODO
### 7.j. Upload build artifacts somewhere if requested
#### XXX:TODO
### 7.k. Get a list of tags
tags=( $(fossil tag -R "${repository}" list "${branch}") )
# 8. Perform post-build work (e.g., for release engineering)
(
cd "${branchdir}" || exit 1
for cmd in "${branchPostCommands[@]}"; do
( eval "set -x; ${cmd}" ) || exit 1
done
) > "${builddir}/post.log" 2>&1 || :
done
# 9. Clean up any branches that no longer exist
## XXX:TODO