#!/bin/bash # Copyright (C) 2011-2015 by Mike Gabriel # # This programme 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 programme 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, write to the # Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. export PATH="${HOME}/bin:${PATH}" GIT_USER="gituser" GIT_TRANSPORT="https" GIT_HOSTNAME="git.mydomain.org" DEBEMAIL="firstname.lastname@mydomain.org" DEBFULLNAME="Firstname Lastname" GPG_KEY="" DEB_DISTS_SUPPORTED="debian ubuntu" DEBIAN_DISTROS="jessie,stretch,buster,bullseye,sid" UBUNTU_DISTROS="xenial,bionic,disco,eoan" COMPONENT_RELEASE="release" COMPONENT_NIGHTLY="nightly" COMPONENT_BUNDLES="bundle-release1 bundle-release2" REPOS_SERVER="packages.mydomain.org" REPOS_BASE="/" PACKAGES_WITHOUT_OTHERMIRROR="keyring" GNUPGHOME="${HOME}/.gnupg" TIMESTAMP=$(date +%s%N) test -z "${1}" && { echo "usage: $(basename "${0}") [/] {release,release/,nightly,nightly/} []"; exit 1; } PREFIX="$(cut -d"-" -f1 <<< "$(basename "${0}")")" test -f "${HOME}/.buildscripts/${PREFIX}.conf" && . "${HOME}/.buildscripts/${PREFIX}.conf" || { echo "${0} has no valid context prefix..." >&2; exit 1; } : ${FORCE_BUILD:="no"} : ${DEB_BUILD_FOR:="debian:${DEBIAN_DISTROS} ubuntu:${UBUNTU_DISTROS}"} # These parts are not user-serviceable. typeset -ag temp_cleanup="" # End of non-user-serviceable part. set -ex # Cleans up temporary directories and files. # RFC SHOULD be called by trap handlers. cleanup () { typeset temp_dir="" for temp_dir in "${temp_cleanup[@]}"; do if [ -d "${temp_dir}" ]; then rm -Rf -- "${temp_dir}" fi done } # Run cleanup() automatically. trap cleanup ERR EXIT SIGTERM SIGINT SIGHUP SIGPIPE SIGALRM SIGUSR1 SIGUSR2 set_vars() { SBUILD="sbuild" TEMP_BASE="${HOME}/tmp/" mkdir -p -- "${TEMP_BASE}" chmod 2770 "${TEMP_BASE}" # first argv is the name of the Git project PROJECT_PATH="${1}" PROJECT_PATH="${PROJECT_PATH/%.git/}" PROJECT="$(basename "${PROJECT_PATH}")" DEBSRCPKG="${PROJECT}" # grab repository component area from command line (2nd argv) or guess it ARGV2_COMPONENT="$(cut -d"/" -f1 <<< "${2}/")" ARGV2_CODENAME="$(cut -d"/" -f2 <<< "${2}/")" COMPONENT="${ARGV2_COMPONENT:-${COMPONENT:-$COMPONENT_NIGHTLY}}" CODENAMES="${ARGV2_CODENAME:-${CODENAMES}}" [ -n "${ARGV2_CODENAME}" ] && FORCE_BUILD="yes" || true DATE="${DATE:-$(date +%Y%m%d)}" if [ "x${COMPONENT}" = "x${COMPONENT_RELEASE}" ]; then CHECKOUT="${3:-release-builds}" elif [ "x${COMPONENT}" = "x${COMPONENT_RELEASE}-test" ]; then CHECKOUT="${3:-release-testbuilds}" elif grep -qs "$COMPONENT" <<< "${COMPONENT_BUNDLES}"; then CHECKOUT="${3:-build-$COMPONENT}" elif [ "x${COMPONENT}" = "x${COMPONENT_NIGHTLY}" ]; then CHECKOUT="${3:-master}" else echo "error: no such package component area for this Git project. Aborting..." exit 1 fi # the DATE might be given as ,,today'' from the command line [ "x${DATE}" = "xtoday" ] && DATE="$(date +%Y%m%d)" # setting paths PROJECT_DIR="${HOME}/build/${COMPONENT}/${TIMESTAMP}/${PROJECT}" temp_cleanup+=("${PROJECT_DIR}") temp_cleanup+=("$(dirname ${PROJECT_DIR})") PKGDIST="${HOME}/pkg-dist/${COMPONENT}/${PROJECT}" # creating paths mkdir -p -- "${PROJECT_DIR}" mkdir -p -- "${PKGDIST}" # by default we build for all current debian versions if [ "x${ARGV2_CODENAME}" != "x" ]; then if grep -qs "${ARGV2_CODENAME}" <<< "${DEBIAN_DISTROS}"; then DEB_BUILD_FOR="debian:${ARGV2_CODENAME}" elif grep -qs "${ARGV2_CODENAME}" <<< "${UBUNTU_DISTROS}"; then DEB_BUILD_FOR="ubuntu:${ARGV2_CODENAME}" fi fi return 0 } prepare_workspace() { # make sure our local working copy is up to date... my_DIST="$1" my_CODENAME="$2" my_WORKDIR="$(dirname ${PROJECT_DIR})" cd ${my_WORKDIR} if [ ! -d ${PROJECT}/.git ]; then git clone --no-hardlinks --no-local "${GIT_TRANSPORT}://${GIT_HOSTNAME}/${PROJECT_PATH}.git" cd "${PROJECT}" git fetch origin "${CHECKOUT}" || true git checkout --force "${CHECKOUT}" || git checkout --force -b "${CHECKOUT}" GIT_OBJECT_ID="$(git rev-parse --verify HEAD)" fi cd "${PROJECT_DIR}" # extract Debian source package name from debian/changelog if [ -e debian/changelog ]; then DEBSRCPKG="$(dpkg-parsechangelog -S Source)" fi return 0 } clear_pkgdist() { # pkgdist directory cleanup # Do NOT spawn a subshell here. # Allow changing global variables in the main process. typeset -a deb_build_for_arr typeset OLDIFS="${IFS}" IFS=" " read -a deb_build_for_arr <<< "${DEB_BUILD_FOR}" IFS="${OLDIFS}" typeset line="" for line in "${deb_build_for_arr[@]}"; do l_DIST="$(cut -d":" -f1 <<< "${line/: /:}" | tr [:upper:] [:lower:])" l_CODENAMES="${CODENAMES:-$(cut -d":" -f2- <<< "${line/: /:}" | sed -e 's/,/ /g' | tr [:upper:] [:lower:])}" grep -qs "${l_DIST}" <<< "${DEB_DISTS_SUPPORTED}" && { for l_CODENAME in ${l_CODENAMES}; do # in case we build a special CODENAME (squeeze, wheezy, lucid, ...) do skip # the wrong distribution here... test -z "${CODENAMES}" || grep "${CODENAMES}" <<< "${line}" || break for l_ARCH in amd64 i386; do [ "x${SKIP_ARCH}" != "x${l_ARCH}" ] && { mkdir -p -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}" rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/dupload.conf" rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/"*_*.changes rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/"*_*.upload rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/"*_*.build rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/"*_*.dsc rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/"*_*.tar.gz rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/"*.deb } done done } done return 0 } build_packages() { # use pbuilder for building all variants of this package # Do NOT spawn a subshell here. # Allow changing global variables in the main process. typeset -a deb_build_for_arr typeset OLDIFS="${IFS}" IFS=" " read -a deb_build_for_arr <<< "${DEB_BUILD_FOR}" IFS="${OLDIFS}" typeset line="" for line in "${deb_build_for_arr[@]}"; do l_DIST="$(cut -d":" -f1 <<< "${line/: /:}" | tr [:upper:] [:lower:])" l_CODENAMES="${CODENAMES:-$(cut -d":" -f2- <<< "${line/: /:}" | sed -e 's/,/ /g' | tr [:upper:] [:lower:])}" grep -qs "${l_DIST}" <<< "${DEB_DISTS_SUPPORTED}" && { for l_CODENAME in ${l_CODENAMES}; do # prepare workspace prepare_workspace "${l_DIST}" "${l_CODENAME}" # in case we build a special CODENAME (squeeze, wheezy, lucid, ...) do skip # the wrong distribution here... test -z "${CODENAMES}" || grep "${CODENAMES}" <<< "${line}" || break TEMP_DIR="$(mktemp -d --tmpdir=${TEMP_BASE})" temp_cleanup+=("${TEMP_DIR}") mkdir -p -- "${TEMP_DIR}/${PROJECT}" chmod 2770 -Rf -- "${TEMP_DIR}" cd "${PROJECT_DIR}" git clone --no-hardlinks --no-local "${PROJECT_DIR}" "${TEMP_DIR}/${PROJECT}/" cd "${TEMP_DIR}/${PROJECT}" git checkout "${CHECKOUT}" || git checkout master if [ -e .gitmodules ]; then git submodule init git submodule update --force --checkout --recursive fi find "${PROJECT_DIR}/../" -maxdepth 0 -mindepth 0 -type f | grep -qs "${PROJECT}_"*.orig.tar.gz && cp -- "${PROJECT_DIR}/../${PROJECT}_"*.orig.tar.gz .. GITREV="$(gitrevno)" # we always build native packages for our repos SA_OPTION="" test -f "debian/source/format" && grep -Eqs '^3.0.*\(quilt\)$' "debian/source/format" && { git fetch origin upstream:upstream UPSTREAM_VERSION="$(dpkg-parsechangelog | grep "Version:" | cut -d " " -f2 | sed -e 's/-.*//' -e 's/^.*://')" REVISION="$(dpkg-parsechangelog | grep "Version:" | cut -d " " -f2 | sed -e 's/.*-//')" git archive --prefix="${PROJECT}-${UPSTREAM_VERSION}/" -o "../${PROJECT}_${UPSTREAM_VERSION}.orig.tar.gz" "upstream/${UPSTREAM_VERSION}" && { SA_OPTION="--debbuildopts=\"-sa\"" } || echo "1.0" > "debian/source/format" } # for Ubuntu version is the codename of the distribution release if [ -n "${BASH_VERSINFO[0]}" ] && [ "${BASH_VERSINFO[0]}" -gt 3 ]; then typeset -l codename="${l_CODENAME}" else typeset codename="$(tr '[:upper:]' '[:lower:]' <<< "${l_CODENAME}")" fi # translate the version name for Debian releases [ "x${l_CODENAME}" = "xsid" ] && codename="unstable" #[ "x$l_CODENAME" = "xjessie" ] && codename=testing #[ "x$l_CODENAME" = "xwheezy" ] && codename=stable #[ "x$l_CODENAME" = "xoldstable" ] && codename=oldstable typeset numerical_version="" typeset -i tmp_ret="1" typeset pretty_dist="" if [ -n "${l_DIST}" ] && [ "${l_DIST}" = "debian" ]; then pretty_dist="Debian" numerical_version="$("debian-codename-to-version.sh" "${codename}")" tmp_ret="${?}" fi if [ -n "${l_DIST}" ] && [ "${l_DIST}" = "ubuntu" ]; then pretty_dist="Ubuntu" numerical_version="$("ubuntu-codename-to-version.sh" "${codename}")" tmp_ret="${?}" fi if [ "${tmp_ret}" -ne "0" ] || [ -z "${numerical_version}" ]; then echo "Error: unable to map code name \"${codename}\" to Debian or Ubuntu numerical versions. Unknown code name or not applicable to distribution \"${dist_pretty}\"? Aborting." >&2 exit 1 fi # modify the section for non-release package builds [ "x${COMPONENT}" != "xrelease" ] && { mv -- "debian/control" "debian/control.tmp" sed "s,Section:[\ ]*\(.*\),Section: ${COMPONENT}/\1,g" debian/control.tmp > debian/control } # modify changelog for this build if [ "${COMPONENT}" != "${COMPONENT_NIGHTLY}" ]; then dch --distribution "${codename}" --force-distribution -l "+git${DATE}.${GITREV}+${numerical_version}.${COMPONENT}." "Auto-built ${pretty_dist} ${l_CODENAME} package for ${REPOS_SERVER} repository (Git commit: ${GIT_OBJECT_ID})." else dch --distribution "${codename}" --force-distribution -l "+git${DATE}.${GITREV}+${numerical_version}.${COMPONENT}." "Development-Snapshot!!! Auto-built ${pretty_dist} ${l_CODENAME} package for ${REPOS_SERVER} repository (Git commit: ${GIT_OBJECT_ID})." fi mkdir -p -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/"{amd64,i386} OTHERMIRROR="" if [ "x${COMPONENT}" = "x${COMPONENT_NIGHTLY}" ]; then grep -qs "${PROJECT}" <<< "${PACKAGE_WITHOUT_OTHERMIRROR}" || OTHERMIRROR="deb http://${REPOS_SERVER}${REPOS_BASE}${l_DIST}-nightly ${l_CODENAME} main" elif echo "${COMPONENT_BUNDLES}" | grep -q "${COMPONENT}" 1>/dev/null; then grep -qs "${PROJECT}" <<< "${PACKAGE_WITHOUT_OTHERMIRROR}" || OTHERMIRROR="deb http://${REPOS_SERVER}${REPOS_BASE}${l_DIST}-${COMPONENT} ${l_CODENAME} main" else grep "${PROJECT}" <<< "${PACKAGE_WITHOUT_OTHERMIRROR}" || OTHERMIRROR="deb http://${REPOS_SERVER}${REPOS_BASE}${l_DIST} ${l_CODENAME} main" fi # create git changelog immediately prior to building the package git --no-pager log --since "2 years ago" --format="%ai %aN (%h) %n%n%x09*%w(68,0,10) %s%d%n" > ChangeLog # build the source package dpkg-buildpackage -uc -us -S -d cd .. DSCFILE="$(pwd)/$(ls -1 "${DEBSRCPKG}_"*.dsc | head -n1)" SBUILD_OPTIONS="-n -j2 -sAd ${codename} -k ${GPG_KEY} --build-dep-resolver=aptitude" curl http://${REPOS_SERVER}${REPOS_BASE}/archive.key 2>/dev/null 1> ${TEMP_DIR}/extra-archive.key if head -n1 ${TEMP_DIR}/extra-archive.key | grep -q -E '\-----BEGIN PGP PUBLIC KEY BLOCK-----' && tail -n1 ${TEMP_DIR}/extra-archive.key | grep -q -E '\-----END PGP PUBLIC KEY BLOCK-----'; then SBUILD_OPTIONS="${SBUILD_OPTIONS} --extra-repository-key=${TEMP_DIR}/extra-archive.key" fi SBUILD_OPTIONS_64="${SBUILD_OPTIONS} -c ${PREFIX}-${l_CODENAME}" SBUILD_OPTIONS_32="${SBUILD_OPTIONS} -c ${PREFIX}-${l_CODENAME}-i386 --arch=i386 --debbuildopts=-B" if [ -n "${SA_OPTION}" ]; then SBUILD_OPTIONS_64=${SBUILD_OPTIONS}" ${SA_OPTION}" fi [ "x${SKIP_ARCH}" != "xamd64" ] && grep -Eqs 'Architecture.*(all|any|amd64)' "${TEMP_DIR}/${PROJECT}/debian/control" && { cd "${PKGDIST}/${l_DIST}/${l_CODENAME}/amd64" tac ${DSCFILE} | while read line; do if echo $line | grep -E "^Files:" 1>/dev/null; then break; fi # each line contains a file that is part of the src:package filename="$(echo $line | cut -d" " -f3-)" if [ -n "$filename" ]; then cp "${TEMP_DIR}/${filename}" .; fi done if [ -z "${OTHERMIRROR}" ]; then nice ${SBUILD} ${SBUILD_OPTIONS_64} "${DSCFILE}" else nice ${SBUILD} ${SBUILD_OPTIONS_64} --extra-repository="${OTHERMIRROR}" "${DSCFILE}" fi } [ "x${SKIP_ARCH}" != "xi386" ] && grep -Eqs 'Architecture.*(any|i386)' "${TEMP_DIR}/${PROJECT}/debian/control" && { cd "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386" tac ${DSCFILE} | while read line; do if echo $line | grep -E "^Files:" 1>/dev/null; then break; fi # each line contains a file that is part of the src:package filename="$(echo $line | cut -d" " -f3-)" if [ -n "$filename" ]; then cp "${TEMP_DIR}/${filename}" .; fi done if [ -z "${OTHERMIRROR}" ]; then nice ${SBUILD} ${SBUILD_OPTIONS_32} "${DSCFILE}" else nice ${SBUILD} ${SBUILD_OPTIONS_32} --extra-repository="${OTHERMIRROR}" "${DSCFILE}" fi } done } done return 0 } upload_packages() { # dupload the new packages to the reprepro repository # Do NOT spawn a subshell here. # Allow changing global variables in the main process. typeset -a deb_build_for_arr typeset OLDIFS="${IFS}" IFS=" " read -a deb_build_for_arr <<< "${DEB_BUILD_FOR}" IFS="${OLDIFS}" typeset line="" for line in "${deb_build_for_arr[@]}"; do l_DIST="$(cut -d":" -f1 <<< "${line/: /:}" | tr [:upper:] [:lower:])" l_CODENAMES="${CODENAMES:-$(cut -d":" -f2- <<< "${line/: /:}" | sed -e 's/,/ /g' | tr [:upper:] [:lower:])}" for l_CODENAME in ${l_CODENAMES}; do # in case we build a special CODENAME (squeeze, wheezy, lucid, ...) do skip # the wrong distribution here... test -z "${CODENAMES}" || grep "${CODENAMES}" <<< "${line}" || break for l_ARCH in amd64 i386; do [ "x${SKIP_ARCH}" != "x${l_ARCH}" ] && { cd "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}" test -f "./dupload.conf" || ln -s -- "${HOME}/.dupload.conf.${PREFIX}" "./dupload.conf" && true ls -- "${DEBSRCPKG}_"*.changes >/dev/null 2>&1 && dupload --to "${PREFIX}-${l_DIST}-${COMPONENT}" "${DEBSRCPKG}_"*.changes 0<&- } done done done return 0 } ### MAIN ### set_vars "$@" && { if [ "x$(basename "${0}")" = "x${PREFIX}-build-deb-package" ] || [ "x$(basename "${0}")" = "x${PREFIX}-build+upload-deb-package" ]; then # Treat any value other than "no" and "0" as true. cd "${PROJECT_DIR}" && pkgneedsbuild "${CHECKOUT}" || ( [ "x${FORCE_BUILD}" != "xno" ] && [ "x${FORCE_BUILD}" != "x0" ] ) && { clear_pkgdist build_packages } fi if [ "x$(basename "${0}")" = "x${PREFIX}-upload-deb-package" ] || [ "x$(basename "${0}")" = "x${PREFIX}-build+upload-deb-package" ]; then upload_packages clear_pkgdist fi }