You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
498 lines
17 KiB
498 lines
17 KiB
#!/bin/bash
|
|
|
|
set -x
|
|
set -u
|
|
|
|
# make sure cowbuilder/pbuilder/... are available
|
|
PATH='/bin:/sbin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin'
|
|
|
|
echo "*** Starting $0 at $(date) ***"
|
|
start_seconds=$(cut -d . -f 1 /proc/uptime)
|
|
|
|
JENKINS_DEBIAN_GLUE_VERSION=$(dpkg --list jenkins-debian-glue 2>/dev/null | awk '/^ii/ {print $3}')
|
|
if [ -n "${JENKINS_DEBIAN_GLUE_VERSION:-}" ] ; then
|
|
echo "*** Running jenkins-debian-glue version $JENKINS_DEBIAN_GLUE_VERSION ***"
|
|
fi
|
|
|
|
checks_and_defaults() {
|
|
if [ -r /etc/jenkins/debian_glue ] ; then
|
|
. /etc/jenkins/debian_glue
|
|
fi
|
|
|
|
if [ -z "${JOB_NAME:-}" ] ; then
|
|
echo "Error: No JOB_NAME defined, please run it in jenkins." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "${architecture:-}" ] ; then
|
|
echo "*** No architecture defined. Consider running it with matrix configuration. ***"
|
|
architecture="$(dpkg-architecture -qDEB_HOST_ARCH)"
|
|
echo "*** Falling back to default, using host architecture ${architecture}. ***"
|
|
fi
|
|
|
|
if [ -z "${REPOSITORY:-}" ] ; then
|
|
REPOSITORY='/srv/repository'
|
|
fi
|
|
}
|
|
|
|
clean_workspace() {
|
|
echo "*** The following files have been noticed in the workspace [$(pwd)]: ***"
|
|
ls -la ./
|
|
|
|
# echo "*** Cleaning workspace in $(pwd) to make sure we're building from scratch. ***"
|
|
# rm -f ./* || true
|
|
}
|
|
|
|
# make sure we don't leave files for next run
|
|
bailout() {
|
|
[ -n "${1:-}" ] && EXIT="${1}" || EXIT=0
|
|
[ -n "${2:-}" ] && echo "$2" >&2
|
|
|
|
if [ -n "${sources:-}" ] && [ "${sources:-}" != "unset" ] ; then
|
|
echo "*** Removing sources file. ***"
|
|
rm -f "${sources}/"*
|
|
fi
|
|
|
|
echo "*** Getting rid of files in $WORKSPACE/binaries/ to avoid problems in next run. ***"
|
|
rm -f "$WORKSPACE"/binaries/*
|
|
|
|
[ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
|
|
echo "*** Finished execution of $0 at $(date) [running ${SECONDS} seconds] ***"
|
|
|
|
exit $EXIT
|
|
}
|
|
|
|
identify_package_name() {
|
|
# make sure we get rid of 'repos' and 'binaries' from Jenkins job name
|
|
PACKAGE=${JOB_NAME%-repos*}
|
|
PACKAGE=${PACKAGE%-binaries*}
|
|
|
|
if [ -n "${PACKAGE:-}" ] ; then
|
|
echo "*** Identified Debian package name $PACKAGE ***"
|
|
else
|
|
bailout 1 "Error: could not identify Debian package name based on job name ${JOB_NAME:-}."
|
|
fi
|
|
}
|
|
|
|
set_base_path() {
|
|
# when BASE_PATH is set in the build step then don't assume a default,
|
|
# this is useful when building on slave nodes, being used like:
|
|
# export BASE_PATH="$WORKSPACE/${JOB_NAME%-binaries/*}-source/"
|
|
|
|
if [ -n "${BASE_PATH:-}" ] ; then
|
|
echo "*** Using provided ${BASE_PATH} as BASE_PATH ***"
|
|
else
|
|
BASE_PATH="${WORKSPACE}"
|
|
echo "*** Using \$WORKSPACE [$BASE_PATH] as default BASE_PATH ***"
|
|
fi
|
|
|
|
}
|
|
|
|
build_info() {
|
|
if [ -n "${REPOS:-}" ] ; then
|
|
echo "*** Using supplied repository name $REPOS ***"
|
|
else
|
|
REPOS="${JOB_NAME%-binaries*}"
|
|
REPOS="${REPOS%-repos*}"
|
|
|
|
if [ -z "${distribution:-}" ]; then
|
|
echo "*** No repository supplied, using repository name $REPOS ***"
|
|
else
|
|
REPOS="${REPOS}-${distribution}"
|
|
echo "*** No repository supplied but distribution has been set, using repository name $REPOS ***"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
identify_sourcefile() {
|
|
if [ -n "${sources:-}" ]; then
|
|
echo "*** Variable \$sources set, using it for locating source files. ***"
|
|
if [ -z "${distribution:-}" ]; then
|
|
sourcefile=$(echo "${sources}/"*.dsc)
|
|
else
|
|
sourcefile=$(echo "${sources}/distribution=${distribution}/"*.dsc)
|
|
fi
|
|
|
|
if [ "$sourcefile" = 'sources/*.dsc' ] ; then
|
|
bailout 1 "Error: no sourcefile (*.dsc) found. Exiting."
|
|
fi
|
|
|
|
case "$sourcefile" in
|
|
*\ *) echo "*** Multiple source files (*.dsc) present in $(pwd): ***"
|
|
ls -la "${sources}/"
|
|
bailout 1 "Error: Please re-run source job to force clean rebuild."
|
|
;;
|
|
esac
|
|
p="$(basename $sourcefile .dsc)"
|
|
newest_version="${p#*_}"
|
|
SOURCE_PACKAGE="*" # FIXME: SOURCE_PACKAGE is used by identify_build_type
|
|
else
|
|
sources="unset"
|
|
echo "*** Identifying newest package version ***"
|
|
newest_version="0"
|
|
|
|
for file in "${BASE_PATH}/"*.dsc ; do
|
|
SOURCE_PACKAGE="$(awk '/^Source: / {print $2}' $file)"
|
|
p="$(basename $file .dsc)"
|
|
if [ "$p" = '*' ] ; then
|
|
bailout 1 "Error: No source package found (forgot to configure source files deployment?)"
|
|
fi
|
|
cur_version="${p#*_}"
|
|
if dpkg --compare-versions "${cur_version}" gt "${newest_version}" ; then
|
|
newest_version="${cur_version}"
|
|
else
|
|
base_version="${cur_version}"
|
|
fi
|
|
done
|
|
|
|
echo "*** Found package version $newest_version ***"
|
|
|
|
sourcefile="${BASE_PATH}/${SOURCE_PACKAGE}"_*"${newest_version}".dsc
|
|
fi
|
|
|
|
echo "*** Using $sourcefile (version: ${newest_version}) [sources: $sources]"
|
|
}
|
|
|
|
dist_and_arch_settings() {
|
|
if [ -n "${distribution:-}" ] ; then
|
|
DIST="-${distribution}"
|
|
fi
|
|
|
|
if [ -z "${architecture:-}" ] || [ "${architecture:-}" = "all" ] ; then
|
|
echo "*** No architecture set or architecture set to 'all', using system arch for cowbuilder ***"
|
|
ARCH="$(dpkg-architecture -qDEB_HOST_ARCH)"
|
|
BASE="/var/cache/pbuilder/base${DIST:-}.cow"
|
|
else
|
|
echo "*** Using cowbuilder base for architecture ${architecture} ***"
|
|
ARCH="${architecture}"
|
|
BASE="/var/cache/pbuilder/base${DIST:-}-${architecture}.cow"
|
|
fi
|
|
|
|
if [ -n "${PROVIDE_ONLY:-}" ] ; then
|
|
echo "*** Config variable 'PROVIDE_ONLY' is set, not considering COWBUILDER_DIST ***"
|
|
return 0
|
|
fi
|
|
|
|
if [ -n "${COWBUILDER_DIST:-}" ]; then
|
|
echo "*** COWBUILDER_DIST is set to $COWBUILDER_DIST - using it for base.cow if it does not exist yet. ***"
|
|
else
|
|
if [ -n "${distribution:-}" ]; then
|
|
echo "*** Using cowbuilder base for distribution ${distribution} ***"
|
|
COWBUILDER_DIST="${distribution}"
|
|
else
|
|
# default to the currently running distribution to avoid hardcoding
|
|
# a distribution which might not be supported by the running system
|
|
local distri_codename=$(lsb_release --short --codename 2>/dev/null)
|
|
[ -n "${distri_codename:-}" ] || distri_codename="sid" # fallback to "sid" iff lsb_release fails
|
|
echo "*** Neither COWBUILDER_DIST nor distribution set - using ${distri_codename} for base.cow if it does not exist yet. ***"
|
|
COWBUILDER_DIST="${distri_codename}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
cowbuilder_init() {
|
|
if [ ! -d "${BASE}" ]; then
|
|
echo "*** Creating cowbuilder base $BASE for arch $ARCH and distribution $COWBUILDER_DIST ***"
|
|
sudo cowbuilder --create --basepath "${BASE}" --distribution "${COWBUILDER_DIST}" \
|
|
--debootstrapopts --arch --debootstrapopts "$ARCH" \
|
|
--debootstrapopts --variant=buildd
|
|
[ $? -eq 0 ] || bailout 1 "Error: Failed to create cowbuilder base ${BASE}."
|
|
else
|
|
echo "*** Updating cowbuilder cow base ***"
|
|
sudo cowbuilder --update --basepath "${BASE}"
|
|
[ $? -eq 0 ] || bailout 1 "Error: Failed to update cowbuilder base ${BASE}."
|
|
fi
|
|
}
|
|
|
|
|
|
identify_build_type() {
|
|
# defaults
|
|
DEBBUILDOPTS="-sa"
|
|
SKIP_ARCH_BUILD=false
|
|
|
|
if [ "${architecture:-}" = "all" ] ; then
|
|
echo "*** \$architecture is set to 'all', skipping further identify_build_type checks. ***"
|
|
echo "*** Consider setting \$architecture to amd64, i386,... instead. ***"
|
|
return 0
|
|
fi
|
|
|
|
if [ -z "${MAIN_ARCHITECTURE:-}" ] ; then
|
|
if [ "$(dpkg-architecture -qDEB_HOST_ARCH)" = "${architecture:-}" ] ; then
|
|
echo "*** MAIN_ARCHITECTURE is unset. ***"
|
|
echo "*** Host architecture [$(dpkg-architecture -qDEB_HOST_ARCH)] matches \$architecture [${architecture:-}], using default ${DEBBUILDOPTS:-} buildoption ***"
|
|
return 0
|
|
else
|
|
echo "*** MAIN_ARCHITECTURE is unset. ***"
|
|
echo "*** Host architecture [$(dpkg-architecture -qDEB_HOST_ARCH)] does not match \$architecture [${architecture:-}] ... ***"
|
|
echo "*** ... setting binary only build and continuing with identify_build_type ***"
|
|
DEBBUILDOPTS="-B"
|
|
fi
|
|
else
|
|
if [ "${MAIN_ARCHITECTURE:-}" = "${architecture:-}" ] ;then
|
|
echo "*** MAIN_ARCHITECTURE is set [${MAIN_ARCHITECTURE:-}]. ***"
|
|
echo "*** MAIN_ARCHITECTURE matches \$architecture [${architecture:-}], using default ${DEBBUILDOPTS:-} buildoption ***"
|
|
return 0
|
|
else
|
|
echo "*** MAIN_ARCHITECTURE [${MAIN_ARCHITECTURE:-}] does not match \$architecture [${architecture:-}], setting binary only build and continuing with identify_build_type ***"
|
|
DEBBUILDOPTS="-B"
|
|
fi
|
|
fi
|
|
|
|
local TMPDIR=$(mktemp -d)
|
|
local old_dir=$(pwd)
|
|
cd "$TMPDIR"
|
|
for file in ${BASE_PATH}/${SOURCE_PACKAGE}_*.tar.* ; do
|
|
if tar atf "$file" 2>/dev/null | grep -q debian/control ; then
|
|
# might be source/debian/control - so let's identify the path to debian/control
|
|
local control_file=$(tar atf "$file" 2>/dev/null | grep 'debian/control$')
|
|
tar axf "$file" "$control_file" || bailout 1 "Error while looking at debian/control in source archive."
|
|
|
|
if grep -q '^Architecture: all' "$control_file" ; then
|
|
if grep -q '^Architecture: any' "$control_file" ; then
|
|
echo "*** Package provides arch 'all' + 'any', enabling -B buildoption for this architecture. ***"
|
|
# -B -> binary-only build, limited to architecture dependent packages
|
|
DEBBUILDOPTS="-B"
|
|
break
|
|
else
|
|
# only "Architecture: all", so no arch specific packages since
|
|
# we aren't building for $MAIN_ARCHITECTURE
|
|
SKIP_ARCH_BUILD=true
|
|
break
|
|
fi
|
|
fi
|
|
fi
|
|
done
|
|
cd "$old_dir"
|
|
|
|
rm -rf "${TMPDIR}"
|
|
}
|
|
|
|
cowbuilder_run() {
|
|
echo "*** cowbuilder build phase for arch $architecture ***"
|
|
mkdir -p "$WORKSPACE"/binaries/
|
|
|
|
# make sure we build arch specific packages only when necessary
|
|
identify_build_type
|
|
|
|
if $SKIP_ARCH_BUILD ; then
|
|
bailout 0 "Nothing to do, architecture all binary packages only for non-primary architecture."
|
|
fi
|
|
|
|
case "$architecture" in
|
|
i386)
|
|
linux32 sudo cowbuilder --buildresult "$WORKSPACE"/binaries/ \
|
|
--build $sourcefile \
|
|
--basepath $BASE --debbuildopts "${DEBBUILDOPTS:-}"
|
|
[ $? -eq 0 ] || bailout 1 "Error: Failed to build with cowbuilder."
|
|
;;
|
|
amd64|all|*)
|
|
sudo cowbuilder --buildresult "$WORKSPACE"/binaries/ \
|
|
--build $sourcefile \
|
|
--basepath $BASE --debbuildopts "${DEBBUILDOPTS:-}"
|
|
[ $? -eq 0 ] || bailout 1 "Error: Failed to build with cowbuilder."
|
|
;;
|
|
*)
|
|
bailout 1 "Error: Unsupported architecture: $architecture"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
remove_packages() {
|
|
if [ -n "${SKIP_REMOVAL:-}" ] ; then
|
|
echo "*** Skipping removal of existing packages as requested via SKIP_REMOVAL ***"
|
|
return 0
|
|
fi
|
|
|
|
echo "*** Removing source package version from repository ***"
|
|
${SUDO_CMD:-} reprepro -v -A source -b "${REPOSITORY}" --waitforlock 1000 remove "${REPOS}" "${SOURCE_PACKAGE}"
|
|
|
|
echo "*** Removing previous binary package versions from repository ***"
|
|
for p in $(dcmd "${WORKSPACE}/binaries/"*"${newest_version}_${ARCH}.changes" | grep '\.deb$') ; do
|
|
file="$(basename $p)"
|
|
binpackage="${file%%_*}"
|
|
binary_list="${binary_list:-} ${binpackage}"
|
|
|
|
# note: "removesrc" would remove foreign arch files (of different builds)
|
|
if echo "$file" | egrep -q '_all.deb$'; then
|
|
echo "*** Removing existing package ${binpackage} from repository ${REPOS} (arch all) ***"
|
|
${SUDO_CMD:-} reprepro -v -b "${REPOSITORY}" --waitforlock 1000 remove "${REPOS}" "${binpackage}"
|
|
else
|
|
echo "*** Removing existing package ${binpackage} from repository ${REPOS} for arch ${ARCH} ***"
|
|
${SUDO_CMD:-} reprepro -v -A "${ARCH}" -b "${REPOSITORY}" --waitforlock 1000 remove "${REPOS}" "${binpackage}"
|
|
fi
|
|
|
|
done
|
|
|
|
}
|
|
|
|
remove_missing_binary_packages() {
|
|
if [ -n "${SKIP_REMOVAL:-}" ] ; then
|
|
echo "*** Skipping removal of existing packages as requested via SKIP_REMOVAL ***"
|
|
return 0
|
|
fi
|
|
|
|
echo "*** Checking for missing binary packages to be considered for removal ***"
|
|
|
|
# In a binary-only build we don't get any arch-all (*_all.deb) packages and
|
|
# therefore they won't be listed in the changes file. As a result they would
|
|
# be reported as missing from the build and to be considered for removal.
|
|
# As we don't want to remove the arch-all package e.g. from the amd64 repos
|
|
# in the i386 run we've to skip the removal procedure then.
|
|
case "${DEBBUILDOPTS:-}" in
|
|
*-B*)
|
|
echo "*** Skipping removal of missing binaries as being a binary-only build ***"
|
|
return 0
|
|
;;
|
|
esac
|
|
|
|
for p in $(${SUDO_CMD:-} reprepro -v -b "${REPOSITORY}" --waitforlock 1000 --list-format '${package}\n' listmatched "${REPOS}" '*' | sort -u); do
|
|
echo " $binary_list " | grep -q " $p " || missing_packages="${missing_packages:-} $p"
|
|
done
|
|
|
|
if echo "${missing_packages:-}" | grep -q '.' ; then
|
|
echo "*** Binary package(s) found, missing in build version: ${missing_packages:-} ***"
|
|
|
|
for p in $missing_packages ; do
|
|
echo "*** Removing $p from $REPOS to avoid out-of-date data ***"
|
|
${SUDO_CMD:-} reprepro -v -b "${REPOSITORY}" --waitforlock 1000 remove "${REPOS}" "${p}"
|
|
done
|
|
fi
|
|
}
|
|
|
|
reprepro_wrapper() {
|
|
if ! [ -d "$REPOSITORY" ] ; then
|
|
bailout 1 "Error: repository ${REPOSITORY} does not exist."
|
|
fi
|
|
|
|
remove_packages
|
|
remove_missing_binary_packages
|
|
|
|
archall=false
|
|
case $architecture in
|
|
all) archall=true
|
|
architecture='*' # support as file expansion in reprepro cmdline
|
|
;;
|
|
esac
|
|
|
|
echo "*** Including packages in repository $REPOS ***"
|
|
${SUDO_CMD:-} reprepro -v -b "${REPOSITORY}" --waitforlock 1000 --ignore=wrongdistribution \
|
|
include "${REPOS}" "${WORKSPACE}/binaries/"*"${newest_version}"_${architecture}.changes
|
|
[ $? -eq 0 ] || bailout 1 "Error: Failed to include binary package in $REPOS repository."
|
|
}
|
|
|
|
trunk_release() {
|
|
# setting TRUNK_RELEASE=true enables release-trunk repository,
|
|
# to always get a copy of the package(s) to a central place
|
|
if [ -z "${TRUNK_RELEASE:-}" ] ; then
|
|
echo "*** TRUNK_RELEASE is not enabled ***"
|
|
elif [ "${IGNORE_RELEASE_TRUNK:-}" = "true" ] ; then
|
|
echo "*** IGNORE_RELEASE_TRUNK is set, ignoring request to add package(s) to $TRUNK_RELEASE repos ***"
|
|
else
|
|
echo "*** TRUNK_RELEASE is enabled ($TRUNK_RELEASE) ***"
|
|
|
|
generate-reprepro-codename "$TRUNK_RELEASE"
|
|
|
|
${SUDO_CMD:-} reprepro -v -b "${REPOSITORY}" --waitforlock 1000 --ignore=wrongdistribution copymatched "$TRUNK_RELEASE" "$REPOS" '*'
|
|
[ $? -eq 0 ] || bailout 1 "Error: Failed to copy packages from ${REPOS} to ${TRUNK_RELEASE}."
|
|
fi
|
|
}
|
|
|
|
release_repos() {
|
|
echo "*** Environment variable 'release' is set, running through release steps. ***"
|
|
|
|
local REPOSITORY="${REPOSITORY}/release/${release}"
|
|
|
|
mkdir -p "${REPOSITORY}/incoming/${release}"
|
|
mkdir -p "${REPOSITORY}/conf"
|
|
|
|
if [ -n "${SUDO_CMD:-}" ] ; then
|
|
${SUDO_CMD:-} mkdir -p "${REPOSITORY}/incoming/${release}"
|
|
${SUDO_CMD:-} mkdir -p "${REPOSITORY}/conf"
|
|
${SUDO_CMD:-} chown -R "$(id -un)" "${REPOSITORY}/conf"
|
|
${SUDO_CMD:-} chown -R "$(id -un)" "${REPOSITORY}/incoming/${release}"
|
|
fi
|
|
|
|
cp "${WORKSPACE}/binaries/"* "${REPOSITORY}/incoming/${release}/"
|
|
[ $? -eq 0 ] || bailout 1 "Error: Failed to copy binary packages to release directory."
|
|
|
|
REPOSITORY=$REPOSITORY generate-reprepro-codename "${release}"
|
|
|
|
if ! grep -q "^Name: $release$" "${REPOSITORY}/conf/incoming" 2>/dev/null ; then
|
|
cat >> "${REPOSITORY}/conf/incoming" << EOF
|
|
|
|
Name: $release
|
|
IncomingDir: incoming/$release
|
|
TempDir: tmp
|
|
LogDir: log
|
|
MorgueDir: ${REPOSITORY}/morgue
|
|
Allow: unstable>$release
|
|
Cleanup: unused_files on_deny on_error
|
|
|
|
EOF
|
|
fi
|
|
|
|
local old_dir=$(pwd)
|
|
cd "${REPOSITORY}/incoming/${release}"
|
|
${SUDO_CMD:-} reprepro -v -b "${REPOSITORY}" processincoming "${release}" "$(basename ${WORKSPACE}/binaries/*.changes)"
|
|
local RC=$?
|
|
cd "$old_dir"
|
|
|
|
if [ $RC -ne 0 ] ; then
|
|
bailout 1 "Error: Failed to execute processincoming for release ${release}."
|
|
fi
|
|
}
|
|
|
|
deploy_to_releases() {
|
|
if [ -n "${release:-}" ] && [ "$release" != "none" ] && [ "$release" != "trunk" ] && \
|
|
# '${release}' is a hidden bomb: when provided through predefined parameters
|
|
# from an upstream jenkins job (like foo-binaries receiving the parameters
|
|
# from foo-source) but the job (foo-binaries) gets triggered manually (without
|
|
# setting the predefined parameters therefore) then ${release} is set to
|
|
# '${release}' instead of being empty
|
|
[ "${release}" != '${release}' ] ; then
|
|
release_repos
|
|
else
|
|
reprepro_wrapper
|
|
trunk_release
|
|
fi
|
|
}
|
|
|
|
# make them available for the Jenkin's 'Archiving artifacts'
|
|
binaries_to_workspace() {
|
|
echo "*** Moving binaries files to workspace. ***"
|
|
mv "${WORKSPACE}/binaries/"* "${WORKSPACE}/"
|
|
rmdir "${WORKSPACE}/binaries/"
|
|
}
|
|
|
|
# main execution
|
|
trap bailout 1 2 3 6 9 14 15
|
|
|
|
checks_and_defaults
|
|
clean_workspace
|
|
identify_package_name
|
|
set_base_path
|
|
build_info
|
|
identify_sourcefile
|
|
dist_and_arch_settings
|
|
|
|
# do not run in repos job?
|
|
if [ -n "${PROVIDE_ONLY:-}" ] ; then
|
|
echo "*** Config variable 'PROVIDE_ONLY' is set, ignoring request to run cowbuilder. ***"
|
|
else
|
|
cowbuilder_init
|
|
cowbuilder_run
|
|
fi
|
|
|
|
# do not run in binaries job?
|
|
if [ -n "${BUILD_ONLY:-}" ] ; then
|
|
echo "*** Config variable 'BUILD_ONLY' is set, ignoring request to use local repository. ***"
|
|
else
|
|
deploy_to_releases
|
|
fi
|
|
|
|
binaries_to_workspace
|
|
|
|
bailout 0
|
|
|
|
# vim:foldmethod=marker ts=2 ft=sh ai expandtab sw=2
|