#!/bin/bash # Copyright (C) 2011-2015 by Mike Gabriel # Copyright (C) 2015 by Mihai Moldovan # # 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_HOSTNAME="git.mydomain.org" RPMEMAIL="firstname.lastname@mydomain.org" RPMFULLNAME="Firstname Lastname" GPG_KEY="" RPM_DISTS_SUPPORTED="fedora epel opensuse sle" FEDORA_DISTROS="18,19,20,21,22,rawhide" EPEL_DISTROS="6,7" OPENSUSE_DISTROS="12.2,12.3,13.1,13.2" SLE_DISTROS="11.2,11.3,12.0" RPM_REPOS_BASE="/var/www/" RPM_WANT_EXTRA_REPOS="0" RPM_MOCK_CONFIG_DIR="/etc/mock" RPM_EXTRA_REPO_MOCK_CONFIG_BASE="" RPM_EXTRA_REPO_MOCK_CONFIG_FULL_NAME="" COMPONENT_MAIN="main" COMPONENT_NIGHTLY="nightly" COMPONENT_BUNDLES="bundle-release1 bundle-release2" REPOS_SERVER="packages.mydomain.org" PACKAGES_WITHOUT_OTHERMIRROR="keyring" GNUPGHOME="${HOME}/.gnupg" OPENSUSE_DOWNLOAD_URL="http://download.opensuse.org/distribution/#VERSION#/repo/oss/suse/" SLE_DOWNLOAD_URL="/srv/mirrors/non-public/sle/#VERSION#/rpms/" test -z "${1}" && { echo "usage: $(basename "${0}") [/] {main,main/,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; } : ${NO_DELAY:="no"} : ${FORCE_BUILD:="no"} : ${RPM_BUILD_FOR:="fedora:${FEDORA_DISTROS} epel:${EPEL_DISTROS} opensuse:${OPENSUSE_DISTROS} sle:${SLE_DISTROS}"} # These parts are not user-serviceable. TMP_MOCK_CFG_DIR="" TMP_MOCK_CFG_FILE="" TEMP_BASE="${HOME}/tmp/" 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 () { # We always assume the temporary mock config file is below the temporary config directory. if [ -n "${TMP_MOCK_CFG_DIR}" ] && [ -e "${TMP_MOCK_CFG_DIR}" ]; then case "${TMP_MOCK_CFG_DIR}" in ("${TEMP_BASE}"*) ;; (*) echo "Warning: mock temporary config directory is not matching the temporary file base dir ${TEMP_BASE}. Not doing cleanup." >&2 exit 1 ;; esac # Take care of the config file(s) first. typeset file="" for file in "${TMP_MOCK_CFG_DIR}/"*; do if [ ! -f "${file}" ]; then echo "Warning: mock temporary config file ${file} is not a regular file. Not unlinking." >&2 else # Remove and ignore errors. rm -- "${file}" || : fi done # And only later of the directory itself. if [ -e "${TMP_MOCK_CFG_DIR}" ]; then if [ -d "${TMP_MOCK_CFG_DIR}" ]; then rmdir -- "${TMP_MOCK_CFG_DIR}" || echo "Warning: unable to remove mock temporary config directory ${TMP_MOCK_CFG_DIR}. Is it non-empty?" >&2 else echo "Warning: mock temporary config directory ${TMP_MOCK_CFG_DIR} is not actually a directory. Not unlinking." >&2 fi else echo "Warning: mock temporary config directory ${TMP_MOCK_CFG_DIR} does not exist." >&2 fi elif [ -n "${TMP_MOCK_CFG_DIR}" ]; then echo "Warning: mock temporary config directory ${TMP_MOCK_CFG_DIR} defined but does not exist. Skipping cleanup." >&2 else echo "Warning: mock temporary config directory not defined. Skipping cleanup." >&2 fi # Cleanup the other temporary dirs afterwards. typeset temp_dir="" for temp_dir in "${temp_cleanup[@]}"; do if [ -n "${temp_dir}" ] && [ -d "${temp_dir}" ]; then rm -Rf -- "${temp_dir}" fi done } # Run cleanup() automatically. trap cleanup ERR EXIT SIGTERM SIGINT SIGHUP SIGPIPE SIGALRM SIGUSR1 SIGUSR2 # FIXME: this should really be in a common.(sh) file! function make_boolean () { typeset -l OPTION="${1}" case "${OPTION}" in ("0"|"no"|"false"|"") OPTION="0";; (*) OPTION="1";; esac printf "${OPTION}" return 0 } set_vars() { 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}")" # 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_MAIN}" ]; then CHECKOUT="${3:-build-main}" elif [ "x${COMPONENT}" = "x${COMPONENT_MAIN}-test" ]; then CHECKOUT="${3:-build-main-test}" COMPONENT="maintest" elif grep -qs "${COMPONENT}" <<< "${COMPONENT_RELEASES}"; 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}/${PROJECT}" PKGDIST="${HOME}/pkg-dist/${COMPONENT}/${PROJECT}" # lock file LOCK_FILE="${PROJECT_DIR}/../.${PROJECT}.lock" # creating paths mkdir -p -- "${PROJECT_DIR}" mkdir -p -- "${PKGDIST}" return 0 } # Check that mock version is at least the given version number. # Returns 0 if the mock version is greater or equal the specified input, # 1 otherwise. check_mock_version_atleast () { typeset MAJOR="${1:?"Error: no major version passed to ${FUNCNAME}()."}" typeset MINOR="${2:?"Error: no minor version passed to ${FUNCNAME}()."}" typeset PATCH="${3:?"Error: no patch version passed to ${FUNCNAME}()."}" # Check input parameters for sanity. typeset SANITY_CHECK_MAJOR="$(sed -e 's/^\([0-9][0-9]*\)$//' <<< "${MAJOR}")" typeset SANITY_CHECK_MINOR="$(sed -e 's/^\([0-9][0-9]*\)$//' <<< "${MINOR}")" typeset SANITY_CHECK_PATCH="$(sed -e 's/^\([0-9][0-9]*\)$//' <<< "${PATCH}")" if [ -n "${SANITY_CHECK_MAJOR}" ] || [ -n "${SANITY_CHECK_MINOR}" ] || [ -n "${SANITY_CHECK_PATCH}" ]; then echo "Error: input parameters of ${FUNCNAME}() are not pure integers and failed sanity check." >&2 exit 1 fi typeset MOCK_VER="$(mock --version)" # Sanitize ${MOCK_VER}: # Only take the first line into account. MOCK_VER="$(head -n 1 <<< "${MOCK_VER}")" # Only accept a string that has number.number.number somewhere. MOCK_VER="$(grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' <<< "${MOCK_VER}")" if [ -z "${MOCK_VER}" ]; then echo "Error: the reported mock version can not be handled by ${FUNCNAME}()." >&2 exit 1 fi # The reason for this weird [0-9][0-9]* construct is that POSIX BRE does only specify * # as a special character. POSIX ERE supports + as a special character, but sed is # specified by POSIX to only support BRE. GNU sed supports a \+ special character in # POSIX BRE standard mode, but this is a GNU extension. typeset MOCK_VER_MAJOR="$(sed -e 's/^\([0-9][0-9]*\)\.[0-9][0-9]*\.[0-9][0-9]*/\1/' <<< "${MOCK_VER}")" typeset MOCK_VER_MINOR="$(sed -e 's/^[0-9][0-9]*\.\([0-9][0-9]*\)\.[0-9][0-9]*/\1/' <<< "${MOCK_VER}")" typeset MOCK_VER_PATCH="$(sed -e 's/^[0-9][0-9]*\.[0-9][0-9]*\.\([0-9][0-9]*\)/\1/' <<< "${MOCK_VER}")" if [ -z "${MOCK_VER_MAJOR}" ] || [ -z "${MOCK_VER_MINOR}" ] || [ -z "${MOCK_VER_PATCH}" ]; then echo "Error: unable to parse mock version in ${FUNCNAME}()." >&2 exit 1 else typeset ret="1" if [ "${MOCK_VER_MAJOR}" -gt "${MAJOR}" ]; then ret="0" elif [ "${MOCK_VER_MAJOR}" -eq "${MAJOR}" ]; then if [ "${MOCK_VER_MINOR}" -gt "${MINOR}" ]; then ret="0" elif [ "${MOCK_VER_MINOR}" -eq "${MINOR}" ]; then if [ "${MOCK_VER_PATCH}" -gt "${PATCH}" ] || \ [ "${MOCK_VER_PATCH}" -eq "${PATCH}" ]; then ret="0" fi fi fi return "${ret}" fi } # Repeats an input string. # Returns the repeated input string. repeat_str () { # INPUT COUNT typeset INPUT="${1:?"Error: no input string passed to ${FUNCNAME}()."}" typeset COUNT="${2:?"Error: no count passed to ${FUNCNAME}()."}" typeset ret="" typeset -i i=0 while [ "${i}" -lt "${COUNT}" ]; do ret="${ret}$(printf "${INPUT}")" i=$(($i + 1)) done printf "${ret}" return 0 } # Creates a custom mock config file given a base config (taken from # /etc/mock/), the build component and the target specification ("full" or "base"). # Base refers to adding x2go-extras only, while full means also # adding the full X2Go upstream repository with all published files. # # Calling this function in a subshell is an error, as it NEEDS to modify # variables in global scope! create_mock_config () { # MOCK_BASE CUSTOM_REPO COMPONENT TARGET typeset MOCK_BASE="${1:?"Error: no mock base config passed to ${FUNCNAME}()."}" typeset COMPONENT="${2:?"Error: no component (X2Go release group) passed to ${FUNCNAME}()."}" typeset TARGET="${3:?"Error: no target (full or base) passed to ${FUNCNAME}()."}" # Check argument sanity. # Append .cfg is not already specified. typeset TMP_REGEX='^.*\.cfg$' if [[ ! "${MOCK_BASE}" =~ ${TMP_REGEX} ]]; then MOCK_BASE="${MOCK_BASE}.cfg" fi typeset DISTRO="" typeset VERSION="" # distribution - version - arch TMP_REGEX='^([[:alpha:]]+)-([[:alnum:]_]+)-[[:alnum:]_]+\.cfg$' if [[ ! "${MOCK_BASE}" =~ ${TMP_REGEX} ]]; then echo "Error: MOCK_BASE parameter not well formed. Must be: 'distro-version-arch.cfg'." >&2 exit 1 else DISTRO="${BASH_REMATCH[1]}" VERSION="${BASH_REMATCH[2]}" fi typeset CUSTOM_REPO="${RPM_EXTRA_REPO_MOCK_CONFIG_BASE}-${DISTRO}.repo" typeset CFG_FILE="" TMP_REGEX='/' for CFG_FILE in MOCK_BASE CUSTOM_REPO; do # Must be plain file. if [[ "${!CFG_FILE}" =~ ${TMP_REGEX} ]]; then echo "Error: (implicit) ${CFG_FILE} parameter must not be a path but a simple file name." >&2 exit 1 fi # Must exist and be readable. if [ ! -f "${RPM_MOCK_CONFIG_DIR}/${!CFG_FILE}" ] || [ ! -r "${RPM_MOCK_CONFIG_DIR}/${!CFG_FILE}" ]; then echo "Error: ${CFG_FILE} parameter must exist, be a regular file and readable." >&2 exit 1 fi done # Rename "main" to "release" and "heuler" to "nightly". if [ "${COMPONENT}" = "main" ]; then COMPONENT="release" elif [ "${COMPONENT}" = "heuler" ]; then COMPONENT="nightly" fi # Note: there is no way to check for the component's validity, # as LTS releases have "random" names assigned to them. if [ "${TARGET}" != "base" ] && [ "${TARGET}" != "full" ]; then echo "Error: TARGET parameter must be either full or base." >&2 exit 1 fi # Create temporary directory for our soon-to-be temporary mock config file, # if it does not already exist. if [ -z "${TMP_MOCK_CFG_DIR}" ]; then TMP_MOCK_CFG_DIR="$(mktemp -d --tmpdir="${TEMP_BASE}" "${RPM_EXTRA_REPO_MOCK_CONFIG_BASE}-mock-$(repeat_str "X" "24")")" if [ "$?" -ne "0" ]; then echo "Error: creating mock temporary config directory failed. Aborting." >&2 exit 1 fi fi TMP_MOCK_CFG_FILE="$(mktemp --tmpdir="${TEMP_BASE}" "$(basename "${TMP_MOCK_CFG_DIR}")/${MOCK_BASE%.cfg}-${RPM_EXTRA_REPO_MOCK_CONFIG_BASE}-${COMPONENT}-${TARGET}.$(repeat_str "X" "24").cfg")" if [ "$?" -ne "0" ]; then echo "Error: creating mock temporary config file failed. Aborting." >&2 exit 1 fi # Save old input field separator value and set it to newline only. typeset OLDIFS="${IFS}" IFS="$(printf '\n')" # Fetch the requested lines from ${CUSTOM_REPO} and store them in arrays. typeset -a extra_repo typeset -a full_repo typeset -i FETCH_EXTRA_SECTION=0 typeset -i FETCH_FULL_SECTION=0 typeset TMP_REGEX_EXTRA="^[[:space:]]*\[[[:space:]]*${RPM_EXTRA_REPO_MOCK_CONFIG_BASE}-extras-${DISTRO}[[:space:]]*\][[:space:]]*\$" typeset TMP_REGEX_FULL="^[[:space:]]*\[[[:space:]]*${RPM_EXTRA_REPO_MOCK_CONFIG_BASE}-${COMPONENT}-${DISTRO}[[:space:]]*\][[:space:]]*\$" typeset TMP_REGEX_OTHER='^[[:space:]]*\[.*\][[:space:]]*$' typeset line="" while read line; do if [[ "${line}" =~ ${TMP_REGEX_EXTRA} ]]; then FETCH_EXTRA_SECTION=1 FETCH_FULL_SECTION=0 elif [[ "${line}" =~ ${TMP_REGEX_FULL} ]]; then FETCH_FULL_SECTION=1 FETCH_EXTRA_SECTION=0 elif [[ "${line}" =~ ${TMP_REGEX_OTHER} ]]; then FETCH_FULL_SECTION=0 FETCH_EXTRA_SECTION=0 fi # Change some values like $releasever or gpgcheck. if [ "${FETCH_EXTRA_SECTION}" -eq "1" ] || [ "${FETCH_FULL_SECTION}" -eq "1" ]; then TMP_REGEX='^[[:space:]]*gpgcheck[[:space:]]*=[[:space:]]*1[[:space:]]*$' if [[ "${line}" =~ ${TMP_REGEX} ]]; then line="${line/1/0}" fi TMP_REGEX='^[[:space:]]*enabled[[:space:]]*=[[:space:]]*0[[:space:]]*$' if [[ "${line}" =~ ${TMP_REGEX} ]]; then line="${line/0/1}" fi case "${line}" in (*'$releasever'*) line="${line/'$releasever'/${VERSION}}" ;; (*) ;; esac fi if [ "${FETCH_EXTRA_SECTION}" -eq "1" ]; then extra_repo+=("${line}") elif [ "${FETCH_FULL_SECTION}" -eq "1" ]; then full_repo+=("${line}") fi done < "/etc/mock/${CUSTOM_REPO}" typeset -i REPO_START=0 TMP_REGEX='^[[:space:]]*config_opts\['"'"'yum\.conf'"'"'\][[:space:]]*=[[:space:]]*"""[[:space:]]*$' typeset TMP_REGEX_END='^[[:space:]]*"""[[:space:]]*$' while read line; do if [[ "${line}" =~ ${TMP_REGEX} ]]; then REPO_START=1 # Pass-through. echo "${line}" >> "${TMP_MOCK_CFG_FILE}" elif [ "${REPO_START}" -eq "1" ] && [[ "${line}" =~ ${TMP_REGEX_END} ]]; then # Time to insert whatever is required. printf '\n' >> "${TMP_MOCK_CFG_FILE}" # Extras repo. typeset -i i=0 for ((i = 0; i < ${#extra_repo[@]}; ++i)); do echo "${extra_repo[${i}]}" >> "${TMP_MOCK_CFG_FILE}" done # Full repo if required. if [ "${TARGET}" = "full" ]; then printf '\n' >> "${TMP_MOCK_CFG_FILE}" typeset -i i=0 for ((i = 0; i < ${#full_repo[@]}; ++i)); do echo "${full_repo[${i}]}" >> "${TMP_MOCK_CFG_FILE}" done fi echo "${line}" >> "${TMP_MOCK_CFG_FILE}" elif [ "${REPO_START}" -eq "0" ] && [[ "${line}" =~ ${TMP_REGEX_END} ]]; then echo "Error: Parsing mock base config file failed: unexpected end of yum configuration, no start found." >&2 exit 1 else # Pass-through. echo "${line}" >> "${TMP_MOCK_CFG_FILE}" fi done < "/etc/mock/${MOCK_BASE}" # Reset input field separator to original value. IFS="${OLDIFS}" return 0 } # Fetches extras repositories, if requested. # Takes the build system type (suse or redhat -- implicitly means OBS or mock), # the distribution, its version (either a real number or a codename), # the build component (X2Go release group), the package, the architecture # and an optional boolean value that determines whether to add any additional # repositories at all. # # Edits either OTHERMIRROR for type == suse or MOCK_CHROOT_CONFIG for type == redhat. # It is an error to execute this function in a subshell, as it MUST edit global variables. get_extra_repository () { typeset TYPE="${1:?"Error: no type passed to ${FUNCNAME}()."}" typeset DIST="${2:?"Error: no distribution passed to ${FUNCNAME}()."}" typeset CODENAME="${3:?"Error: no codename (distro 'version') passed to ${FUNCNAME}()."}" typeset COMPONENT="${4:?"Error: no component (X2Go release group) passed to ${FUNCNAME}()."}" typeset PACKAGE="${5:?"Error: no package passed to ${FUNCAME}()."}" typeset ARCH="${6:?"Error: no architecture passed to ${FUNCNAME}()."}" typeset WANT_EXTRA="$(make_boolean "${7}")" # Note: we always add the extras repo, because that's defined as "packages missing from the main repository", # unless explicitly overridden via ${WANT_EXTRA}. case "${TYPE}" in "suse") # FIXME: make this package repository consistent with our main ones. OTHERMIRROR="" if [ "${WANT_EXTRA}" -eq "1" ]; then OTHERMIRROR="--repo http://${REPOS_SERVER}/${DIST}/${CODENAME}/extras" if [ -z "${PACKAGE_WITHOUT_OTHERMIRROR}" ] || [ "${PACKAGE_WITHOUT_OTHERMIRROR}" != "${PACKAGE}" ]; then OTHERMIRROR="${OTHERMIRROR} --repo http://${REPOS_SERVER}/${DIST}/${CODENAME}/${COMPONENT}/${ARCH}" fi fi ;; "redhat") # Always use -r. MOCK_CHROOT_CONFIG="-r " # Itsy-bitsy problem here: mock versions prior to 1.2.0 are buggy in the sense that # they *always* prepend /etc/mock/ and append .cfg to any chroot file specified # via -r, even if the argument is an absolute path. # We have to work around that by specifying ../..//PATH/TO/MOCK-CONFIG and leaving # out the ".cfg" part. # Find out if we're using a buggy version. check_mock_version_atleast "1" "2" "0" && typeset MOCK_BUGGY="0" || typeset MOCK_BUGGY="1" [ "${MOCK_BUGGY}" -eq "1" ] && MOCK_CHROOT_CONFIG="${MOCK_CHROOT_CONFIG}../../" if [ "${WANT_EXTRA}" -eq "1" ]; then typeset WANT="base" if [ -z "${PACKAGE_WITHOUT_OTHERMIRROR}" ] || [ "${PACKAGE_WITHOUT_OTHERMIRROR}" != "${PACKAGE}" ]; then WANT="full" fi create_mock_config "${DIST}-${CODENAME}-${ARCH}" "${COMPONENT}" "${WANT}" # Remove the .cfg extension again... and maybe add it back later. MOCK_CHROOT_CONFIG="${MOCK_CHROOT_CONFIG}${TMP_MOCK_CFG_FILE%.cfg}" else MOCK_CHROOT_CONFIG="${MOCK_CHROOT_CONFIG}${RPM_MOCK_CONFIG_DIR}/${DIST}-${CODENAME}-${ARCH}" fi # Add .cfg extension. [ "${MOCK_BUGGY}" -eq "0" ] && MOCK_CHROOT_CONFIG="${MOCK_CHROOT_CONFIG}.cfg" ;; *) echo "Error: unknown type passed to ${FUNCNAME}()" >&2 echo "Valid values: suse, redhat." >&2 exit 1 ;; esac return 0 } prepare_workspace() { # make sure our local working copy is up to date... if [ -d "${PROJECT_DIR}/.git" ]; then cd "${PROJECT_DIR}" && git reset --hard git checkout --force "${CHECKOUT}" || git checkout --force -b "${CHECKOUT}" git pull origin "${CHECKOUT}" git fetch origin upstream:upstream || true git fetch origin pristine-tar:pristine-tar || true # and again, get the ${CHECKOUT} refspec in pure state git reset --hard git clean -df else cd "$(dirname "${PROJECT_DIR}")" git clone "git://${GIT_HOSTNAME}/${PROJECT_PATH}.git" cd "${PROJECT}" git checkout --force "${CHECKOUT}" || git checkout --force -b "${CHECKOUT}" git fetch origin upstream:upstream git fetch origin pristine-tar:pristine-tar || true git clean -df fi GIT_OBJECT_ID="$(git rev-parse --verify HEAD)" cd "${PROJECT_DIR}" if [ "x${ARGV2_CODENAME}" != "x" ]; then if grep -qs "${ARGV2_CODENAME}" <<< "${FEDORA_DISTROS}"; then RPM_BUILD_FOR="fedora:${ARGV2_CODENAME}" elif grep -qs "${ARGV2_CODENAME}" <<< "${EPEL_DISTROS}"; then RPM_BUILD_FOR="epel:${ARGV2_CODENAME}" fi fi return 0 } # Cleans up the pkgdist directory used for building packages. # Does not take parameters. # Does not "return" anything. clear_pkgdist() { # Do NOT spawn a subshell here. Functions like get_extra_repository() need to # change global variables in the main process. typeset -a rpm_build_for_arr typeset OLDIFS="${IFS}" IFS=" " read -a rpm_build_for_arr <<< "${RPM_BUILD_FOR}" IFS="${OLDIFS}" typeset line="" for line in "${rpm_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}" <<< "${RPM_DISTS_SUPPORTED}" && { for l_CODENAME in ${l_CODENAMES}; do test -z "${CODENAMES}" || grep "${CODENAMES}" <<< "${line}" || break # Yes, "SRPM" is technically not an architecture. for l_ARCH in x86_64 i386 SRPM; do if [ "x${SKIP_ARCH}" != "x${l_ARCH}" ]; then mkdir -p -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}" rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES/"* rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SRPMS/${PROJECT}-"*.src.rpm rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/"*.rpm fi done done } done return 0 } build_packages() { # Do NOT spawn a subshell here. Functions like get_extra_repository() need to # change global variables in the main process. typeset -a rpm_build_for_arr typeset OLDIFS="${IFS}" IFS=" " read -a rpm_build_for_arr <<< "${RPM_BUILD_FOR}" IFS="${OLDIFS}" typeset line="" for line in "${rpm_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}" <<< "${RPM_DISTS_SUPPORTED}" && { for l_CODENAME in ${l_CODENAMES}; do test -z "${CODENAMES}" || grep "${CODENAMES}" <<< "${line}" || break # FIXME: Builds currently break without this. This should really be merged/transformed into an arch loop. typeset l_ARCH="" # create rpmbuild subdirectories mkdir -p -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES" # tar upstream sources from our Git clone TEMP_DIR="$(mktemp -d --tmpdir="${TEMP_BASE}")" temp_cleanup+=("${TEMP_DIR}") mkdir -p -- "${TEMP_DIR}/${PROJECT}" chmod -Rf -- 2770 "${TEMP_DIR}" cd "$PROJECT_DIR" git clone "${PROJECT_DIR}" "${TEMP_DIR}/${PROJECT}/" cd "${TEMP_DIR}" GITREV="$(cd "${PROJECT}" && gitrevno && cd - 1>/dev/null)" # create git changelog immediately prior to building the SRPM package pushd "${PROJECT}" >/dev/null 2>&1 && \ git --no-pager log --since "2 years ago" --format="%ai %aN (%h) %n%n%x09*%w(68,0,10) %s%d%n" > ChangeLog.gitlog && \ popd >/dev/null 2>&1 rm -Rf -- "${PROJECT}/.git" mv -- "${PROJECT}/${PROJECT}.spec" . UPSTREAM_VERSION="$(grep -E -- "^Version:.*" "${PROJECT}.spec" | awk '{ print $2 }')" PKG_RELEASE="$(grep -E -- "^Release:.*" "${PROJECT}.spec" | awk '{ print $2 }')" PKG_SRCRELEASE="$(sed -e 's/%{?dist}//' <<< "${PKG_RELEASE}")" IS_NOARCH="$(grep -qsE -- "^BuildArch:.*noarch\$" "${PROJECT}.spec" && echo "yes" || echo "no")" if [ "${COMPONENT}" = "${COMPONENT_NIGHTLY}" ]; then IS_RELEASE="0" else IS_RELEASE="1" fi sed -i "${PROJECT}.spec" -e "s/%{?dist}/.${IS_RELEASE}.git${DATE}.${GITREV}.${COMPONENT}%{?dist}/" # apply patches from debian/patches/* so that they end up in the tarball # ... esp. relevant for NX (redistributed) if [ -f "${PROJECT}/debian/patches/series" ]; then ( cd "${PROJECT}" && QUILT_PATCHES=debian/patches quilt push -a && rm -Rf -- .pc/; ) fi grep -E "^Source[1-9]+:.*" "${PROJECT}.spec" | sed "s/%{name}/${PROJECT}/" | awk '{ print $2 }' | while read source_file; do find "${PROJECT}/rpm/${source_file}" -maxdepth 0 1> /dev/null && cp -- "${PROJECT}/rpm/${source_file}" "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES/" && continue find "${PROJECT}/${source_file}" -maxdepth 0 1> /dev/null && cp -- "${PROJECT}/${source_file}" "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES/" done mv -- "${TEMP_DIR}/${PROJECT}" "${TEMP_DIR}/${PROJECT}-${UPSTREAM_VERSION}" tar -czf "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES/${PROJECT}-${UPSTREAM_VERSION}.tar.gz" "${PROJECT}-${UPSTREAM_VERSION}" cp -- "${PROJECT}.spec" "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES" if [ "x${l_DIST}" = "xfedora" ] || [ "x${l_DIST}" = "xepel" ]; then while [ -d ~mock/"${l_DIST}-${l_CODENAME}-x86_64" ]; do echo "Waiting for some other build to finish..." sleep 30 done rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/build.log" # Obtain packages from our RPM repository. get_extra_repository "redhat" "${l_DIST}" "${l_CODENAME}" "${COMPONENT}" "${PROJECT}" "x86_64" "${RPM_WANT_EXTRA_REPOS}" if mock --buildsrpm \ ${MOCK_CHROOT_CONFIG} \ --resultdir="${PKGDIST}/${l_DIST}/${l_CODENAME}/rpmbuild/SRPMS" \ --spec "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES/${PROJECT}.spec" \ --sources "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES/"; then cat "${PKGDIST}/${l_DIST}/${l_CODENAME}/rpmbuild/SRPMS/build.log" rm -Rf -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/rpmbuild/SRPMS/build.log" else cat "${PKGDIST}/${l_DIST}/${l_CODENAME}/rpmbuild/SRPMS/build.log" exit 1 fi fi # clean up the Git clone from the temp folder cd && rm -Rf -- "${TEMP_DIR}/${PROJECT}" # modify changelog for this build ### TODO: add changelog entry for this automatic build mkdir -p -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/"{x86_64,i386,SRPM} if [ "x${SKIP_ARCH}" != "xx86_64" ] || [ "${IS_NOARCH}" = "yes" ]; then if [ "x${l_DIST}" = "xopensuse" ] || [ "x${l_DIST}" = "xsle" ]; then BUILD_RESULT="/home/abuild/rpmbuild/" if [ "x${l_DIST}" = "xopensuse" ]; then DOWNLOAD_URL="$(sed "s/#VERSION#/${l_CODENAME}/" <<< "${OPENSUSE_DOWNLOAD_URL}")" elif [ "x${l_DIST}" = "xsle" ]; then DOWNLOAD_URL="$(sed "s/#VERSION#/${l_CODENAME}/" <<< "${SLE_DOWNLOAD_URL}")" if [ "${l_CODENAME}" = "11.2" ] || [ "${l_CODENAME}" = "11.3" ]; then BUILD_RESULT="/usr/src/packages" fi fi while ps ax | grep -E "build.*/var/cache/obs-build/${l_DIST}/${l_CODENAME}/x86_64/" | grep "sudo obs"; do echo "Waiting for some other build to finish..." sleep 30 done # Obtain packages from our RPM repository. get_extra_repository "suse" "${l_DIST}" "${l_CODENAME}" "${COMPONENT}" "${PROJECT}" "x86_64" "${RPM_WANT_EXTRA_REPOS}" if sudo obs-build \ --nosignature \ ${OTHERMIRROR} \ --repo "${DOWNLOAD_URL}" \ --root "/var/cache/obs-build/${l_DIST}/${l_CODENAME}/x86_64/" \ --clean \ "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES/${PROJECT}.spec"; then mkdir -p -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/" # -r parameter to read: Backslashes may NOT escape any characters! # -d '': specifies the delimiter to be used - as '' resolves to an empty string followed # by a NUL character, the delimiter is set to this very NUL (\000) character. find "/var/cache/obs-build/${l_DIST}/${l_CODENAME}/x86_64/${BUILD_RESULT}/RPMS/" -type f \( -iname '*.rpm' -and -not -iname '*.src.rpm' \) -print0 | while read -r -d '' rpmfile; do cp "${rpmfile}" "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/" done rpmsign-unattended -D "%_gpg_name debian@x2go.org" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/"*.rpm # also copy and sign source RPM's # For information on why this weird -print0 | read -r -d '' construction works, # refer to the first instance of this in this script. find "/var/cache/obs-build/${l_DIST}/${l_CODENAME}/x86_64/${BUILD_RESULT}/SRPMS/" -type f -iname '*.rpm' -print0 | while read -r -d '' rpmfile; do cp "${rpmfile}" "$PKGDIST/${l_DIST}/${l_CODENAME}/SRPM/" done rpmsign-unattended -D "%_gpg_name debian@x2go.org" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/"*.rpm else exit 1 fi else rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/build.log" while [ -d ~mock/"${l_DIST}-${l_CODENAME}-x86_64" ]; do echo "Waiting for some other build to finish..." sleep 30 done # Obtain packages from our RPM repository. get_extra_repository "redhat" "${l_DIST}" "${l_CODENAME}" "${COMPONENT}" "${PROJECT}" "x86_64" "${RPM_WANT_EXTRA_REPOS}" # For information on why this weird -print0 | read -r -d '' construction works, # refer to the first instance of this in this script. find "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SRPMS/" -type 'f' -iname "${PROJECT}-${UPSTREAM_VERSION}-${PKG_SRCRELEASE}.${IS_RELEASE}.git${DATE}.${GITREV}.${COMPONENT}.*.src.rpm" -print0 | while read -r -d '' srpm; do if mock ${MOCK_CHROOT_CONFIG} --resultdir="${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64" "${srpm}"; then # copy and later sign source RPM cp "${srpm}" "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/" # Clean up source RPM files. We copy them manually. find "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64" -type 'f' -iname '*.src.rpm' -exec rm -f -- '{}' \; if [ "${l_DIST}" = "epel" ] && [ "${l_CODENAME}" = "5" ]; then # References: # /usr/lib/rpm/macros # http://adminotes.blogspot.fr/2011/12/centos-6-rpm-sign-problem-v4-signatures.html RPMMACRO_V3SIGN="%__gpg_sign_cmd %{__gpg} /usr/bin/gpg --force-v3-sigs --digest-algo=sha1 --batch --no-verbose --no-armor --passphrase-fd 3 --no-secmem-warning -u \"%{_gpg_name}\" -sbo %{__signature_filename} %{__plaintext_filename}" rpmsign-unattended -D "%_gpg_name debian@x2go.org" -D "${RPMMACRO_V3SIGN}" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/"*.rpm rpmsign-unattended -D "%_gpg_name debian@x2go.org" -D "${RPMMACRO_V3SIGN}" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/"*.rpm else rpmsign-unattended -D "%_gpg_name debian@x2go.org" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/"*.rpm rpmsign-unattended -D "%_gpg_name debian@x2go.org" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/"*.rpm fi cat "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/build.log" else cat "${PKGDIST}/${l_DIST}/${l_CODENAME}/x86_64/build.log" exit 1 fi done fi fi if [ "x${SKIP_ARCH}" != "xi386" ] && [ "${IS_NOARCH}" != "yes" ]; then if [ "x${l_DIST}" = "xopensuse" ] || [ "x${l_DIST}" = "xsle" ]; then BUILD_RESULT="/home/abuild/rpmbuild/" if [ "x$l_DIST" = "xopensuse" ]; then DOWNLOAD_URL="$(sed "s/#VERSION#/${l_CODENAME}/" <<< "${OPENSUSE_DOWNLOAD_URL}")" elif [ "x$l_DIST" = "xsle" ]; then DOWNLOAD_URL="$(sed "s/#VERSION#/${l_CODENAME}/" <<< "${SLE_DOWNLOAD_URL}")" if [ "${l_CODENAME}" = "11.2" ] || [ "${l_CODENAME}" = "11.3" ]; then BUILD_RESULT="/usr/src/packages" fi fi while ps ax | grep -E "build.*/var/cache/obs-build/${l_DIST}/${l_CODENAME}/i386/" | grep "sudo obs"; do echo "Waiting for some other build to finish..." sleep 30 done # Obtain packages from our RPM repository. get_extra_repository "suse" "${l_DIST}" "${l_CODENAME}" "${COMPONENT}" "${PROJECT}" "i386" "${RPM_WANT_EXTRA_REPOS}" if linux32 sudo obs-build \ --nosignature \ ${OTHERMIRROR} \ --repo "${DOWNLOAD_URL}" \ --root "/var/cache/obs-build/${l_DIST}/${l_CODENAME}/i386/" \ --clean \ "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SOURCES/${PROJECT}.spec"; then mkdir -p -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/" # For information on why this weird -print0 | read -r -d '' construction works, # refer to the first instance of this in this script. find "/var/cache/obs-build/${l_DIST}/${l_CODENAME}/i386/${BUILD_RESULT}/RPMS/" -type 'f' \( -iname '*.rpm' -and -not -iname '*.src.rpm' \) -print0 | while read -r -d '' rpmfile; do cp "${rpmfile}" "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/" done rpmsign-unattended -D "%_gpg_name debian@x2go.org" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/"*.rpm # copy and later sign source RPM's, if needed (that is, not already generated by x86_64/noarch code above) SEARCH_SRPM="$(find "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM" -type 'f' -iname "*.src.rpm" -print)" if [ -z "${SEARCH_SRPM}" ]; then # For information on why this weird -print0 | read -r -d '' construction works, # refer to the first instance of this in this script. find "/var/cache/obs-build/${l_DIST}/${l_CODENAME}/i386/${BUILD_RESULT}/SRPMS/" -type 'f' -iname '*.src.rpm' -print0 | while read -r -d '' rpmfile; do cp "${rpmfile}" "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/" done rpmsign-unattended -D "%_gpg_name debian@x2go.org" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/"*.rpm fi else exit 1 fi else while [ -d ~mock/"${l_DIST}-${l_CODENAME}-i386" ]; do echo "Waiting for some other build to finish..." sleep 30 done rm -f -- "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/build.log" # Obtain packages from our RPM repository. get_extra_repository "redhat" "${l_DIST}" "${l_CODENAME}" "${COMPONENT}" "${PROJECT}" "i386" "${RPM_WANT_EXTRA_REPOS}" # For information on why this weird -print0 | read -r -d '' construction works, # refer to the first instance of this in this script. find "${PKGDIST}/${l_DIST}/${l_CODENAME}/${l_ARCH}/rpmbuild/SRPMS/" -type 'f' -iname "${PROJECT}-${UPSTREAM_VERSION}-${PKG_SRCRELEASE}.${IS_RELEASE}.git${DATE}.${GITREV}.${COMPONENT}.*.src.rpm" -print0 | while read -r -d '' srpm; do if nice mock ${MOCK_CHROOT_CONFIG} --resultdir="${PKGDIST}/${l_DIST}/${l_CODENAME}/i386" "${srpm}"; then # only copy and sign source RPM if necessary SIGN_SRPM="0" if [ ! -e "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/$(basename "${srpm}")" ]; then cp "${srpm}" "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/" SIGN_SRPM="1" fi # Clean up source RPM files. We copy them manually. find "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386" -type 'f' -iname '*.src.rpm' -exec rm -f -- '{}' \; if [ "${l_DIST}" = "epel" ] && [ "${l_CODENAME}" = "5" ]; then RPMMACRO_V3SIGN="%__gpg_sign_cmd /usr/bin/gpg --force-v3-sigs --digest-algo=sha1 --batch --no-verbose --no-armor --passphrase-fd 3 --no-secmem-warning -u \"%_gpg_name\" -sbo %{__signature_filename} %{__plaintext_filename}" rpmsign-unattended -D "%_gpg_name debian@x2go.org" -D "${RPMMACRO_V3SIGN}" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/"*.rpm [ "x${SIGN_SRPM}" = "x1" ] && rpmsign-unattended -D "%_gpg_name debian@x2go.org" -D "${RPMMACRO_V3SIGN}" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/SRPM/"*.rpm else rpmsign-unattended -D "%_gpg_name debian@x2go.org" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/"*.rpm [ "x$SIGN_SRPM" = "x1" ] && rpmsign-unattended -D "%_gpg_name debian@x2go.org" --addsign "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/"*.rpm fi cat "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/build.log" else cat "${PKGDIST}/${l_DIST}/${l_CODENAME}/i386/build.log" exit 1 fi done fi fi done } done return 0 } # Uploads the build packages. # Has no parameters. # Does not "return" any value. upload_packages() { # Do NOT spawn a subshell here. Functions like get_extra_repository() need to # change global variables in the main process. typeset -a rpm_build_for_arr typeset OLDIFS="${IFS}" IFS=" " read -a rpm_build_for_arr <<< "${RPM_BUILD_FOR}" IFS="${OLDIFS}" typeset line="" for line in "${rpm_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 test -z "${CODENAMES}" || grep "${CODENAMES}" <<< "${line}" || break # Yes, "SRPM" is technically not an architecture. for l_ARCH in x86_64 i386 SRPM; do if [ "x${SKIP_ARCH}" != "x${l_ARCH}" ]; then # create remote directories in archive 0/dev/null 2>/dev/null 0/dev/null; then rm -f -- "${LOCK_FILE}" else echo "PROJECT directory is locked, sleeping for 10 seconds..." sleep 10 fi done } lock_workspace() { wait_for_lock echo "${$}" > "${LOCK_FILE}" } unlock_workspace() { rm -f -- "${LOCK_FILE}" } delay_build() { sleep $[ ( ( $RANDOM % 10 ) + 1 ) * 10 ]s } ### MAIN ### set_vars "$@" && { if [ "x$(basename "${0}")" = "x${PREFIX}-build-rpm-package" ] || [ "x$(basename "${0}")" = "x${PREFIX}-build+upload-rpm-package" ]; then FORCE_BUILD="$(make_boolean "${FORCE_BUILD}")" NO_DELAY="$(make_boolean "${NO_DELAY}")" # This indentation may look weird, but is actually needed because bash expects either a semicolon or a newline # between (the last) command and a closing curly brace. cd "${PROJECT_DIR}" && { pkgneedsbuild "${CHECKOUT}" || [ "${FORCE_BUILD}" -eq "1" ] } && { if [ "${FORCE_BUILD}" -eq "1" ] && [ "${NO_DELAY}" -eq "0" ]; then delay_build fi lock_workspace prepare_workspace && { unlock_workspace clear_pkgdist build_packages } unlock_workspace } fi if [ "x$(basename "${0}")" = "x${PREFIX}-upload-rpm-package" ] || [ "x$(basename "${0}")" = "x${PREFIX}-build+upload-rpm-package" ]; then upload_packages fi }