From c87c8117194594a6c9ca4a5e6dc4db502b58551c Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Sat, 28 Jan 2017 22:11:05 +0100 Subject: [PATCH] Make release script more modular and useful on other platforms --- make_release.sh | 352 ---------------------------- release-tool | 596 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 596 insertions(+), 352 deletions(-) delete mode 100755 make_release.sh create mode 100755 release-tool diff --git a/make_release.sh b/make_release.sh deleted file mode 100755 index 58f03534d..000000000 --- a/make_release.sh +++ /dev/null @@ -1,352 +0,0 @@ -#!/usr/bin/env bash -# -# KeePassXC Release Preparation Helper -# Copyright (C) 2017 KeePassXC team -# -# This program 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 2 or (at your option) -# version 3 of the License. -# -# This program 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, see . - -echo -e "\e[1m\e[32mKeePassXC\e[0m Release Preparation Helper" -echo -e "Copyright (C) 2017 KeePassXC Team \n" - - -# default values -RELEASE_NAME="" -APP_NAME="KeePassXC" -APP_NAME_LOWER="keepassxc" -SRC_DIR="." -GPG_KEY="CFB4C2166397D0D2" -GPG_GIT_KEY="" -OUTPUT_DIR="release" -BRANCH="" -RELEASE_BRANCH="master" -TAG_NAME="" -BUILD_SOURCES=false -DOCKER_IMAGE="" -DOCKER_CONTAINER_NAME="${APP_NAME_LOWER}-build-container" -CMAKE_OPTIONS="" -COMPILER="g++" -MAKE_OPTIONS="-j8" -BUILD_PLUGINS="autotype" -INSTALL_PREFIX="/usr/local" - -ORIG_BRANCH="$(git rev-parse --abbrev-ref HEAD 2> /dev/null)" -ORIG_CWD="$(pwd)" - - -# helper functions -printUsage() { - echo -e "\e[1mUsage:\e[0m $(basename $0) [options]" - cat << EOF - -Options: - -v, --version Release version number or name (required) - -a, --app-name Application name (default: '${APP_NAME}') - -s, --source-dir Source directory (default: '${SRC_DIR}') - -k, --gpg-key GPG key used to sign the release tarball - (default: '${GPG_KEY}') - -g, --gpg-git-key GPG key used to sign the merge commit and release tag, - leave empty to let Git choose your default key - (default: '${GPG_GIT_KEY}') - -o, --output-dir Output directory where to build the release - (default: '${OUTPUT_DIR}') - --develop-branch Development branch to merge from (default: 'release/VERSION') - --release-branch Target release branch to merge to (default: '${RELEASE_BRANCH}') - -t, --tag-name Override release tag name (defaults to version number) - -b, --build Build sources after exporting release - -d, --docker-image Use the specified Docker image to compile the application. - The image must have all required build dependencies installed. - This option has no effect if --build is not set. - --container-name Docker container name (default: '${DOCKER_CONTAINER_NAME}') - The container must not exist already - -c, --cmake-options Additional CMake options for compiling the sources - --compiler Compiler to use (default: '${COMPILER}') - -m, --make-options Make options for compiling sources (default: '${MAKE_OPTIONS}') - -i, --install-prefix Install prefix (default: '${INSTALL_PREFIX}') - -p, --plugins Space-separated list of plugins to build - (default: ${BUILD_PLUGINS}) - -h, --help Show this help - -EOF -} - -logInfo() { - echo -e "\e[1m[ \e[34mINFO\e[39m ]\e[0m $1" -} - -logError() { - echo -e "\e[1m[ \e[31mERROR\e[39m ]\e[0m $1" >&2 -} - -exitError() { - logError "$1" - if [ "" != "$ORIG_BRANCH" ]; then - git checkout "$ORIG_BRANCH" > /dev/null 2>&1 - fi - cd "$ORIG_CWD" - exit 1 -} - - -# parse command line options -while [ $# -ge 1 ]; do - arg="$1" - - case "$arg" in - -a|--app-name) - APP_NAME="$2" - shift ;; - - -s|--source-dir) - SRC_DIR"$2" - shift ;; - - -v|--version) - RELEASE_NAME="$2" - shift ;; - - -k|--gpg-key) - GPG_KEY="$2" - shift ;; - - -g|--gpg-git-key) - GPG_GIT_KEY="$2" - shift ;; - - -o|--output-dir) - OUTPUT_DIR="$2" - shift ;; - - --develop-branch) - BRANCH="$2" - shift ;; - - --release-branch) - RELEASE_BRANCH="$2" - shift ;; - - -t|--tag-name) - TAG_NAME="$2" - shift ;; - - -b|--build) - BUILD_SOURCES=true ;; - - -d|--docker-image) - DOCKER_IMAGE="$2" - shift ;; - - --container-name) - DOCKER_CONTAINER_NAME="$2" - shift ;; - - -c|--cmake-options) - CMAKE_OPTIONS="$2" - shift ;; - - -m|--make-options) - MAKE_OPTIONS="$2" - shift ;; - - --compiler) - COMPILER="$2" - shift ;; - - -p|--plugins) - BUILD_PLUGINS="$2" - shift ;; - - -h|--help) - printUsage - exit ;; - - *) - logError "Unknown option '$arg'\n" - printUsage - exit 1 ;; - esac - shift -done - - -if [ "" == "$RELEASE_NAME" ]; then - logError "Missing arguments, --version is required!\n" - printUsage - exit 1 -fi - -if [ "" == "$TAG_NAME" ]; then - TAG_NAME="$RELEASE_NAME" -fi -if [ "" == "$BRANCH" ]; then - BRANCH="release/${RELEASE_NAME}" -fi -APP_NAME_LOWER="$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]')" -APP_NAME_UPPER="$(echo "$APP_NAME" | tr '[:lower:]' '[:upper:]')" - -SRC_DIR="$(realpath "$SRC_DIR")" -OUTPUT_DIR="$(realpath "$OUTPUT_DIR")" -if [ ! -d "$SRC_DIR" ]; then - exitError "Source directory '${SRC_DIR}' does not exist!" -fi - -logInfo "Changing to source directory..." -cd "${SRC_DIR}" - -logInfo "Performing basic checks..." - -if [ -e "$OUTPUT_DIR" ]; then - exitError "Output directory '$OUTPUT_DIR' already exists. Please choose a different location!" -fi - -if [ ! -d .git ] || [ ! -f CHANGELOG ]; then - exitError "Source directory is not a valid Git repository!" -fi - -git tag | grep -q "$RELEASE_NAME" -if [ $? -eq 0 ]; then - exitError "Release '$RELEASE_NAME' already exists!" -fi - -git diff-index --quiet HEAD -- -if [ $? -ne 0 ]; then - exitError "Current working tree is not clean! Please commit or unstage any changes." -fi - -git checkout "$BRANCH" > /dev/null 2>&1 -if [ $? -ne 0 ]; then - exitError "Source branch '$BRANCH' does not exist!" -fi - -grep -q "${APP_NAME_UPPER}_VERSION \"${RELEASE_NAME}\"" CMakeLists.txt -if [ $? -ne 0 ]; then - exitError "${APP_NAME_UPPER}_VERSION version not updated to '${RELEASE_NAME}' in CMakeLists.txt!" -fi - -grep -q "${APP_NAME_UPPER}_VERSION_NUM \"${RELEASE_NAME}\"" CMakeLists.txt -if [ $? -ne 0 ]; then - exitError "${APP_NAME_UPPER}_VERSION_NUM version not updated to '${RELEASE_NAME}' in CMakeLists.txt!" -fi - -if [ ! -f CHANGELOG ]; then - exitError "No CHANGELOG file found!" -fi - -grep -qPzo "${RELEASE_NAME} \(\d{4}-\d{2}-\d{2}\)\n=+\n" CHANGELOG -if [ $? -ne 0 ]; then - exitError "CHANGELOG does not contain any information about the '${RELEASE_NAME}' release!" -fi - -git checkout "$RELEASE_BRANCH" > /dev/null 2>&1 -if [ $? -ne 0 ]; then - exitError "Release branch '$RELEASE_BRANCH' does not exist!" -fi - -logInfo "All checks pass, getting our hands dirty now!" - -logInfo "Merging '${BRANCH}' into '${RELEASE_BRANCH}'..." - -CHANGELOG=$(grep -Pzo "(?<=${RELEASE_NAME} \(\d{4}-\d{2}-\d{2}\)\n)=+\n\n?(?:.|\n)+?\n(?=\n)" \ - CHANGELOG | grep -Pzo '(?<=\n\n)(.|\n)+' | tr -d \\0) -COMMIT_MSG="Release ${RELEASE_NAME}" - -git merge "$BRANCH" --no-ff -m "$COMMIT_MSG" -m "${CHANGELOG}" "$BRANCH" -S"$GPG_GIT_KEY" - -logInfo "Creating tag '${RELEASE_NAME}'..." -if [ "" == "$GPG_GIT_KEY" ]; then - git tag -a "$RELEASE_NAME" -m "$COMMIT_MSG" -m "${CHANGELOG}" -s -else - git tag -a "$RELEASE_NAME" -m "$COMMIT_MSG" -m "${CHANGELOG}" -s -u "$GPG_GIT_KEY" -fi - -logInfo "Merge done, creating target directory..." -mkdir -p "$OUTPUT_DIR" - -if [ $? -ne 0 ]; then - exitError "Failed to create output directory!" -fi - -logInfo "Creating source tarball..." -TARBALL_NAME="${APP_NAME_LOWER}-${RELEASE_NAME}-src.tar.bz2" -git archive --format=tar "$RELEASE_BRANCH" --prefix="${APP_NAME_LOWER}-${RELEASE_NAME}/" \ - | bzip2 -9 > "${OUTPUT_DIR}/${TARBALL_NAME}" - - -if $BUILD_SOURCES; then - logInfo "Creating build directory..." - mkdir -p "${OUTPUT_DIR}/build-release" - mkdir -p "${OUTPUT_DIR}/bin-release" - cd "${OUTPUT_DIR}/build-release" - - logInfo "Configuring sources..." - for p in $BUILD_PLUGINS; do - CMAKE_OPTIONS="${CMAKE_OPTIONS} -DWITH_XC_$(echo $p | tr '[:lower:]' '[:upper:]')=On" - done - - if [ "$COMPILER" == "g++" ]; then - export CC=gcc - elif [ "$COMPILER" == "clang++" ]; then - export CC=clang - fi - export CXX="$COMPILER" - - if [ "" == "$DOCKER_IMAGE" ]; then - cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=Off $CMAKE_OPTIONS \ - -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" "$SRC_DIR" - - logInfo "Compiling sources..." - make $MAKE_OPTIONS - - logInfo "Installing to bin dir..." - make DESTDIR="${OUTPUT_DIR}/bin-release" install/strip - - logInfo "Creating AppImage..." - ${SRC_DIR}/AppImage-Recipe.sh "$APP_NAME" "$RELEASE_NAME" - else - logInfo "Launching Docker container to compile sources..." - - docker run --name "$DOCKER_CONTAINER_NAME" --rm \ - --cap-add SYS_ADMIN --device /dev/fuse \ - -e "CC=${CC}" -e "CXX=${CXX}" \ - -v "$(realpath "$SRC_DIR"):/keepassxc/src:ro" \ - -v "$(realpath "$OUTPUT_DIR"):/keepassxc/out:rw" \ - "$DOCKER_IMAGE" \ - bash -c "cd /keepassxc/out/build-release && \ - cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=Off $CMAKE_OPTIONS \ - -DCMAKE_INSTALL_PREFIX=\"${INSTALL_PREFIX}\" /keepassxc/src && \ - make $MAKE_OPTIONS && make DESTDIR=/keepassxc/out/bin-release install/strip && \ - /keepassxc/src/AppImage-Recipe.sh "$APP_NAME" "$RELEASE_NAME"" - - logInfo "Build finished, Docker container terminated." - fi - - cd .. - logInfo "Signing source tarball..." - gpg --output "${TARBALL_NAME}.sig" --armor --local-user "$GPG_KEY" --detach-sig "$TARBALL_NAME" - - logInfo "Signing AppImage..." - APPIMAGE_NAME="${APP_NAME}-${RELEASE_NAME}-x86_64.AppImage" - gpg --output "${APPIMAGE_NAME}.sig" --armor --local-user "$GPG_KEY" --detach-sig "$APPIMAGE_NAME" - - logInfo "Creating digests..." - sha256sum "$TARBALL_NAME" > "${TARBALL_NAME}.DIGEST" - sha256sum "$APPIMAGE_NAME" > "${APPIMAGE_NAME}.DIGEST" -fi - -logInfo "Leaving source directory..." -cd "$ORIG_CWD" -git checkout "$ORIG_BRANCH" > /dev/null 2>&1 - -logInfo "All done!" -logInfo "Please merge the release branch back into the develop branch now and then push your changes." -logInfo "Don't forget to also push the tags using \e[1mgit push --tags\e[0m." diff --git a/release-tool b/release-tool new file mode 100755 index 000000000..8e3285b1a --- /dev/null +++ b/release-tool @@ -0,0 +1,596 @@ +#!/usr/bin/env bash +# +# KeePassXC Release Preparation Helper +# Copyright (C) 2017 KeePassXC team +# +# This program 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 2 or (at your option) +# version 3 of the License. +# +# This program 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, see . + +echo -e "\e[1m\e[32mKeePassXC\e[0m Release Preparation Helper" +echo -e "Copyright (C) 2017 KeePassXC Team \n" + + +# ----------------------------------------------------------------------- +# global default values +# ----------------------------------------------------------------------- +RELEASE_NAME="" +APP_NAME="KeePassXC" +SRC_DIR="." +GPG_KEY="CFB4C2166397D0D2" +GPG_GIT_KEY="" +OUTPUT_DIR="release" +SOURCE_BRANCH="" +TARGET_BRANCH="master" +TAG_NAME="" +DOCKER_IMAGE="" +DOCKER_CONTAINER_NAME="keepassxc-build-container" +CMAKE_OPTIONS="" +COMPILER="g++" +MAKE_OPTIONS="-j8" +BUILD_PLUGINS="autotype" +INSTALL_PREFIX="/usr/local" +BUILD_SOURCE_TARBALL=true +ORIG_BRANCH="" +ORIG_CWD="$(pwd)" + +# ----------------------------------------------------------------------- +# helper functions +# ----------------------------------------------------------------------- +printUsage() { + local cmd + if [ "" == "$1" ] || [ "help" == "$1" ]; then + cmd="COMMAND" + elif [ "merge" == "$1" ] || [ "build" == "$1" ] || [ "sign" == "$1" ]; then + cmd="$1" + else + logError "Unknown command: '$1'" + echo + cmd="COMMAND" + fi + + echo -e "\e[1mUsage:\e[0m $(basename $0) $cmd [options]" + + if [ "COMMAND" == "$cmd" ]; then + cat << EOF + +Commands: + merge Merge release development branch into main branch + and create release tags + build Build and package binary release from sources + sign Sign compile release packages + help Show help for the given command +EOF + elif [ "merge" == "$cmd" ]; then + cat << EOF + +Options: + -v, --version Release version number or name (required) + -a, --app-name Application name (default: '${APP_NAME}') + -s, --source-dir Source directory (default: '${SRC_DIR}') + -g, --gpg-key GPG key used to sign the merge commit and release tag, + leave empty to let Git choose your default key + (default: '${GPG_GIT_KEY}') + -r, --release-branch Source release branch to merge from (default: 'release/VERSION') + --target-branch Target branch to merge to (default: '${TARGET_BRANCH}') + -t, --tag-name Override release tag name (defaults to version number) + -h, --help Show this help +EOF + elif [ "build" == "$cmd" ]; then + cat << EOF + +Options: + -v, --version Release version number or name (required) + -a, --app-name Application name (default: '${APP_NAME}') + -s, --source-dir Source directory (default: '${SRC_DIR}') + -o, --output-dir Output directory where to build the release + (default: '${OUTPUT_DIR}') + -t, --tag-name Release tag to check out (defaults to version number) + -b, --build Build sources after exporting release + -d, --docker-image Use the specified Docker image to compile the application. + The image must have all required build dependencies installed. + This option has no effect if --build is not set. + --container-name Docker container name (default: '${DOCKER_CONTAINER_NAME}') + The container must not exist already + -c, --cmake-options Additional CMake options for compiling the sources + --compiler Compiler to use (default: '${COMPILER}') + -m, --make-options Make options for compiling sources (default: '${MAKE_OPTIONS}') + -i, --install-prefix Install prefix (default: '${INSTALL_PREFIX}') + -p, --plugins Space-separated list of plugins to build + (default: ${BUILD_PLUGINS}) + -n, --no-source-tarball Don't build source tarball + -h, --help Show this help +EOF + elif [ "sign" == "$cmd" ]; then + cat << EOF + +Options: + -f, --files Files to sign (required) + -g, --gpg-key GPG key used to sign the files (default: '${GPG_KEY}') + -h, --help Show this help +EOF + fi +} + +logInfo() { + echo -e "\e[1m[ \e[34mINFO\e[39m ]\e[0m $1" +} + +logError() { + echo -e "\e[1m[ \e[31mERROR\e[39m ]\e[0m $1" >&2 +} + +init() { + ORIG_CWD="$(pwd)" + cd "$SRC_DIR" > /dev/null 2>&1 + ORIG_BRANCH="$(git rev-parse --abbrev-ref HEAD 2> /dev/null)" + cd "$ORIG_CWD" +} + +cleanup() { + logInfo "Checking out original branch..." + if [ "" != "$ORIG_BRANCH" ]; then + git checkout "$ORIG_BRANCH" > /dev/null 2>&1 + fi + logInfo "Leaving source directory..." + cd "$ORIG_CWD" +} + +exitError() { + logError "$1" + cleanup + exit 1 +} + +exitTrap() { + exitError "Existing upon user request..." +} + +checkSourceDirExists() { + if [ ! -d "$SRC_DIR" ]; then + exitError "Source directory '${SRC_DIR}' does not exist!" + fi +} + +checkOutputDirDoesNotExist() { + if [ -e "$OUTPUT_DIR" ]; then + exitError "Output directory '$OUTPUT_DIR' already exists. Please choose a different location!" + fi +} + +checkGitRepository() { + if [ ! -d .git ] || [ ! -f CHANGELOG ]; then + exitError "Source directory is not a valid Git repository!" + fi +} + +checkTagExists() { + git tag | grep -q "$TAG_NAME" + if [ $? -ne 0 ]; then + exitError "Tag '${TAG_NAME}' does not exist!" + fi +} + +checkReleaseDoesNotExist() { + git tag | grep -q "$TAG_NAME" + if [ $? -eq 0 ]; then + exitError "Release '$RELEASE_NAME' (tag: '$TAG_NAME') already exists!" + fi +} + +checkWorkingTreeClean() { + git diff-index --quiet HEAD -- + if [ $? -ne 0 ]; then + exitError "Current working tree is not clean! Please commit or unstage any changes." + fi +} + +checkSourceBranchExists() { + git rev-parse "$SOURCE_BRANCH" > /dev/null 2>&1 + if [ $? -ne 0 ]; then + exitError "Source branch '$SOURCE_BRANCH' does not exist!" + fi +} + +checkTargetBranchExists() { + git rev-parse "$TARGET_BRANCH" > /dev/null 2>&1 + if [ $? -ne 0 ]; then + exitError "Target branch '$TARGET_BRANCH' does not exist!" + fi +} + +checkVersionInCMake() { + local app_name_upper="$(echo "$APP_NAME" | tr '[:lower:]' '[:upper:]')" + + grep -q "${app_name_upper}_VERSION \"${RELEASE_NAME}\"" CMakeLists.txt + if [ $? -ne 0 ]; then + exitError "${app_name_upper}_VERSION version not updated to '${RELEASE_NAME}' in CMakeLists.txt!" + fi + + grep -q "${app_name_upper}_VERSION_NUM \"${RELEASE_NAME}\"" CMakeLists.txt + if [ $? -ne 0 ]; then + exitError "${app_name_upper}_VERSION_NUM version not updated to '${RELEASE_NAME}' in CMakeLists.txt!" + fi +} + +checkChangeLog() { + if [ ! -f CHANGELOG ]; then + exitError "No CHANGELOG file found!" + fi + + grep -qPzo "${RELEASE_NAME} \(\d{4}-\d{2}-\d{2}\)\n=+\n" CHANGELOG + if [ $? -ne 0 ]; then + exitError "CHANGELOG does not contain any information about the '${RELEASE_NAME}' release!" + fi +} + + +trap exitTrap SIGINT SIGTERM + + +# ----------------------------------------------------------------------- +# merge command +# ----------------------------------------------------------------------- +merge() { + while [ $# -ge 1 ]; do + local arg="$1" + case "$arg" in + -v|--version) + RELEASE_NAME="$2" + shift ;; + + -a|--app-name) + APP_NAME="$2" + shift ;; + + -s|--source-dir) + SRC_DIR="$2" + shift ;; + + -g|--gpg-key) + GPG_GIT_KEY="$2" + shift ;; + + -r|--release-branch) + SOURCE_BRANCH="$2" + shift ;; + + --target-branch) + TARGET_BRANCH="$2" + shift ;; + + -t|--tag-name) + TAG_NAME="$2" + shift ;; + + -h|--help) + printUsage "merge" + exit ;; + + *) + logError "Unknown option '$arg'\n" + printUsage "merge" + exit 1 ;; + esac + shift + done + + if [ "" == "$RELEASE_NAME" ]; then + logError "Missing arguments, --version is required!\n" + printUsage "merge" + exit 1 + fi + + if [ "" == "$TAG_NAME" ]; then + TAG_NAME="$RELEASE_NAME" + fi + + if [ "" == "$SOURCE_BRANCH" ]; then + SOURCE_BRANCH="release/${RELEASE_NAME}" + fi + + init + + SRC_DIR="$(realpath "$SRC_DIR")" + + logInfo "Performing basic checks..." + + checkSourceDirExists + + logInfo "Changing to source directory..." + cd "${SRC_DIR}" + + checkGitRepository + checkReleaseDoesNotExist + checkWorkingTreeClean + checkSourceBranchExists + checkTargetBranchExists + checkVersionInCMake + checkChangeLog + + logInfo "All checks pass, getting our hands dirty now!" + + logInfo "Checking out target branch '${TARGET_BRANCH}'..." + git checkout "$TARGET_BRANCH" + + logInfo "Merging '${SOURCE_BRANCH}' into '${TARGET_BRANCH}'..." + + CHANGELOG=$(grep -Pzo "(?<=${RELEASE_NAME} \(\d{4}-\d{2}-\d{2}\)\n)=+\n\n?(?:.|\n)+?\n(?=\n)" \ + CHANGELOG | grep -Pzo '(?<=\n\n)(.|\n)+' | tr -d \\0) + COMMIT_MSG="Release ${RELEASE_NAME}" + + git merge "$SOURCE_BRANCH" --no-ff -m "$COMMIT_MSG" -m "${CHANGELOG}" "$SOURCE_BRANCH" -S"$GPG_GIT_KEY" + + logInfo "Creating tag '${TAG_NAME}'..." + if [ "" == "$GPG_GIT_KEY" ]; then + git tag -a "$TAG_NAME" -m "$COMMIT_MSG" -m "${CHANGELOG}" -s + else + git tag -a "$TAG_NAME" -m "$COMMIT_MSG" -m "${CHANGELOG}" -s -u "$GPG_GIT_KEY" + fi + + cleanup + + logInfo "All done!" + logInfo "Please merge the release branch back into the develop branch now and then push your changes." + logInfo "Don't forget to also push the tags using \e[1mgit push --tags\e[0m." +} + +# ----------------------------------------------------------------------- +# build command +# ----------------------------------------------------------------------- +build() { + while [ $# -ge 1 ]; do + local arg="$1" + case "$arg" in + -v|--version) + RELEASE_NAME="$2" + shift ;; + + -a|--app-name) + APP_NAME="$2" + shift ;; + + -s|--source-dir) + SRC_DIR="$2" + shift ;; + + -o|--output-dir) + OUTPUT_DIR="$2" + shift ;; + + -t|--tag-name) + TAG_NAME="$2" + shift ;; + + -d|--docker-image) + DOCKER_IMAGE="$2" + shift ;; + + --container-name) + DOCKER_CONTAINER_NAME="$2" + shift ;; + + -c|--cmake-options) + CMAKE_OPTIONS="$2" + shift ;; + + --compiler) + COMPILER="$2" + shift ;; + + -m|--make-options) + MAKE_OPTIONS="$2" + shift ;; + + -i|--install-prefix) + INSTALL_PREFIX="$2" + shift ;; + + -p|--plugins) + BUILD_PLUGINS="$2" + shift ;; + + -n|--no-source-tarball) + BUILD_SOURCE_TARBALL=false ;; + + -h|--help) + printUsage "build" + exit ;; + + *) + logError "Unknown option '$arg'\n" + printUsage "build" + exit 1 ;; + esac + shift + done + + if [ "" == "$RELEASE_NAME" ]; then + logError "Missing arguments, --version is required!\n" + printUsage "build" + exit 1 + fi + + if [ "" == "$TAG_NAME" ]; then + TAG_NAME="$RELEASE_NAME" + fi + + init + + SRC_DIR="$(realpath "$SRC_DIR")" + OUTPUT_DIR="$(realpath "$OUTPUT_DIR")" + + logInfo "Performing basic checks..." + + checkSourceDirExists + + logInfo "Changing to source directory..." + cd "${SRC_DIR}" + + checkTagExists + checkGitRepository + checkWorkingTreeClean + checkOutputDirDoesNotExist + + logInfo "Checking out release tag '${TAG_NAME}'..." + git checkout "$TAG_NAME" + + logInfo "Creating output directory..." + mkdir -p "$OUTPUT_DIR" + + if [ $? -ne 0 ]; then + exitError "Failed to create output directory!" + fi + + if $BUILD_SOURCE_TARBALL; then + logInfo "Creating source tarball..." + local app_name_lower="$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]')" + TARBALL_NAME="${app_name_lower}-${RELEASE_NAME}-src.tar.xz" + git archive --format=tar "$TAG_NAME" --prefix="${app_name_lower}-${RELEASE_NAME}/" \ + | xz -6 > "${OUTPUT_DIR}/${TARBALL_NAME}" + fi + + logInfo "Creating build directory..." + mkdir -p "${OUTPUT_DIR}/build-release" + mkdir -p "${OUTPUT_DIR}/bin-release" + cd "${OUTPUT_DIR}/build-release" + + logInfo "Configuring sources..." + for p in $BUILD_PLUGINS; do + CMAKE_OPTIONS="${CMAKE_OPTIONS} -DWITH_XC_$(echo $p | tr '[:lower:]' '[:upper:]')=On" + done + + if [ "$COMPILER" == "g++" ]; then + export CC=gcc + elif [ "$COMPILER" == "clang++" ]; then + export CC=clang + fi + export CXX="$COMPILER" + + if [ "" == "$DOCKER_IMAGE" ]; then + cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=Off $CMAKE_OPTIONS \ + -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" "$SRC_DIR" + + logInfo "Compiling sources..." + make $MAKE_OPTIONS + + if [ "$(uname -i)" == "Msys" ]; then + logInfo "Bundling binary packages..." + make package + else + logInfo "Installing to bin dir..." + make DESTDIR="${OUTPUT_DIR}/bin-release" install/strip + + + logInfo "Creating AppImage..." + ${SRC_DIR}/AppImage-Recipe.sh "$APP_NAME" "$RELEASE_NAME" + fi + else + logInfo "Launching Docker container to compile sources..." + + docker run --name "$DOCKER_CONTAINER_NAME" --rm \ + --cap-add SYS_ADMIN --device /dev/fuse \ + -e "CC=${CC}" -e "CXX=${CXX}" \ + -v "$(realpath "$SRC_DIR"):/keepassxc/src:ro" \ + -v "$(realpath "$OUTPUT_DIR"):/keepassxc/out:rw" \ + "$DOCKER_IMAGE" \ + bash -c "cd /keepassxc/out/build-release && \ + cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=Off $CMAKE_OPTIONS \ + -DCMAKE_INSTALL_PREFIX=\"${INSTALL_PREFIX}\" /keepassxc/src && \ + make $MAKE_OPTIONS && make DESTDIR=/keepassxc/out/bin-release install/strip && \ + /keepassxc/src/AppImage-Recipe2.sh "$APP_NAME" "$RELEASE_NAME"" + + if [ 0 -ne $? ]; then + exitError "Docker build failed!" + fi + + logInfo "Build finished, Docker container terminated." + fi + + cleanup + + logInfo "All done!" +} + + +# ----------------------------------------------------------------------- +# sign command +# ----------------------------------------------------------------------- +sign() { + SIGN_FILES=() + + while [ $# -ge 1 ]; do + local arg="$1" + case "$arg" in + -f|--files) + while [ "${2:0:1}" != "-" ] && [ $# -ge 2 ]; do + SIGN_FILES+=("$2") + shift + done ;; + + -g|--gpg-key) + GPG_KEY="$2" + shift ;; + + -h|--help) + printUsage "sign" + exit ;; + + *) + logError "Unknown option '$arg'\n" + printUsage "sign" + exit 1 ;; + esac + shift + done + + if [ -z "$SIGN_FILES" ]; then + logError "Missing arguments, --files is required!\n" + printUsage "sign" + exit 1 + fi + + for f in "${SIGN_FILES[@]}"; do + if [ ! -f "$f" ]; then + exitError "File '${f}' does not exist!" + fi + + logInfo "Signing file '${f}'..." + gpg --output "${f}.sig" --armor --local-user "$GPG_KEY" --detach-sig "$f" + + if [ 0 -ne $? ]; then + exitError "Signing failed!" + fi + + logInfo "Creating digest for file '${f}'..." + sha256sum "$f" > "${f}.DIGEST" + done + + logInfo "All done!" +} + + +# ----------------------------------------------------------------------- +# parse global command line +# ----------------------------------------------------------------------- +MODE="$1" +shift +if [ "" == "$MODE" ]; then + logError "Missing arguments!\n" + printUsage + exit 1 +elif [ "help" == "$MODE" ]; then + printUsage "$1" + exit +elif [ "merge" == "$MODE" ] || [ "build" == "$MODE" ] || [ "sign" == "$MODE" ]; then + $MODE "$@" +fi