From cc39f9ec2336615b502bc563273736fa226f7fff Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Wed, 29 Sep 2021 16:25:52 +0200 Subject: [PATCH] Update and improve release-tool - Exit and clean up on intermittent errors - Show colour output when building in Docker containers - Run builds in containers as current user - Remove obsolete libgpg-error workarounds - General cleanup --- release-tool | 149 +++++++++++++++++++++++---------------------------- 1 file changed, 68 insertions(+), 81 deletions(-) diff --git a/release-tool b/release-tool index adb1e4c61f..b838f87539 100755 --- a/release-tool +++ b/release-tool @@ -1,7 +1,7 @@ #!/usr/bin/env bash # # KeePassXC Release Preparation Helper -# Copyright (C) 2017 KeePassXC team +# Copyright (C) 2021 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 @@ -17,8 +17,9 @@ # along with this program. If not, see . printf "\e[1m\e[32mKeePassXC\e[0m Release Preparation Helper\n" -printf "Copyright (C) 2017 KeePassXC Team \n\n" +printf "Copyright (C) 2021 KeePassXC Team \n\n" +set -eE -o pipefail # ----------------------------------------------------------------------- # global default values @@ -52,7 +53,7 @@ TIMESTAMP_SERVER="http://timestamp.sectigo.com" # ----------------------------------------------------------------------- printUsage() { local cmd - if [ "" == "$1" ] || [ "help" == "$1" ]; then + if [ -z "$1" ] || [ "help" == "$1" ]; then cmd="COMMAND" elif [ "check" == "$1" ] || [ "merge" == "$1" ] || [ "build" == "$1" ] || [ "gpgsign" == "$1" ] || \ [ "appsign" == "$1" ] || [ "notarize" == "$1" ] || [ "appimage" == "$1" ] || [ "i18n" == "$1" ]; then @@ -212,17 +213,17 @@ logError() { } init() { - if [ "" == "$RELEASE_NAME" ]; then + if [ -z "$RELEASE_NAME" ]; then logError "Missing arguments, --version is required!\n" printUsage "check" exit 1 fi - if [ "" == "$TAG_NAME" ]; then + if [ -z "$TAG_NAME" ]; then TAG_NAME="$RELEASE_NAME" fi - if [ "" == "$SOURCE_BRANCH" ]; then + if [ -z "$SOURCE_BRANCH" ]; then SOURCE_BRANCH="release/${RELEASE_NAME}" fi @@ -285,29 +286,25 @@ checkGitRepository() { } checkReleaseDoesNotExist() { - git tag | $GREP -q "^$TAG_NAME$" - if [ $? -eq 0 ]; then + if ! git tag | $GREP -q "^$TAG_NAME$"; then exitError "Release '$RELEASE_NAME' (tag: '$TAG_NAME') already exists!" fi } checkWorkingTreeClean() { - git diff-index --quiet HEAD -- - if [ $? -ne 0 ]; then + if ! git diff-index --quiet HEAD --; 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 + if ! git rev-parse "$SOURCE_BRANCH" > /dev/null 2>&1; then exitError "Source branch '$SOURCE_BRANCH' does not exist!" fi } checkTargetBranchExists() { - git rev-parse "$TARGET_BRANCH" > /dev/null 2>&1 - if [ $? -ne 0 ]; then + if ! git rev-parse "$TARGET_BRANCH" > /dev/null 2>&1; then exitError "Target branch '$TARGET_BRANCH' does not exist!" fi } @@ -318,18 +315,15 @@ checkVersionInCMake() { local minor_num="$(echo ${RELEASE_NAME} | cut -f2 -d.)" local patch_num="$(echo ${RELEASE_NAME} | cut -f3 -d. | cut -f1 -d-)" - $GREP -q "${app_name_upper}_VERSION_MAJOR \"${major_num}\"" CMakeLists.txt - if [ $? -ne 0 ]; then + if ! $GREP -q "${app_name_upper}_VERSION_MAJOR \"${major_num}\"" CMakeLists.txt; then exitError "${app_name_upper}_VERSION_MAJOR not updated to '${major_num}' in CMakeLists.txt!" fi - $GREP -q "${app_name_upper}_VERSION_MINOR \"${minor_num}\"" CMakeLists.txt - if [ $? -ne 0 ]; then + if ! $GREP -q "${app_name_upper}_VERSION_MINOR \"${minor_num}\"" CMakeLists.txt; then exitError "${app_name_upper}_VERSION_MINOR not updated to '${minor_num}' in CMakeLists.txt!" fi - $GREP -q "${app_name_upper}_VERSION_PATCH \"${patch_num}\"" CMakeLists.txt - if [ $? -ne 0 ]; then + if ! $GREP -q "${app_name_upper}_VERSION_PATCH \"${patch_num}\"" CMakeLists.txt; then exitError "${app_name_upper}_VERSION_PATCH not updated to '${patch_num}' in CMakeLists.txt!" fi } @@ -339,8 +333,7 @@ checkChangeLog() { exitError "No CHANGELOG file found!" fi - $GREP -qPzo "## ${RELEASE_NAME} \(\d{4}-\d{2}-\d{2}\)\n" CHANGELOG.md - if [ $? -ne 0 ]; then + if ! $GREP -qPzo "## ${RELEASE_NAME} \(\d{4}-\d{2}-\d{2}\)\n" CHANGELOG.md; then exitError "'CHANGELOG.md' has not been updated to the '${RELEASE_NAME}' release!" fi } @@ -350,8 +343,7 @@ checkAppStreamInfo() { exitError "No AppStream info file found!" fi - $GREP -qPzo "" share/linux/org.keepassxc.KeePassXC.appdata.xml - if [ $? -ne 0 ]; then + if ! $GREP -qPzo "" share/linux/org.keepassxc.KeePassXC.appdata.xml; then exitError "'share/linux/org.keepassxc.KeePassXC.appdata.xml' has not been updated to the '${RELEASE_NAME}' release!" fi } @@ -362,13 +354,11 @@ checkSnapcraft() { return fi - $GREP -qPzo "version: ${RELEASE_NAME}" snap/snapcraft.yaml - if [ $? -ne 0 ]; then + if ! $GREP -qPzo "version: ${RELEASE_NAME}" snap/snapcraft.yaml; then exitError "'snapcraft.yaml' has not been updated to the '${RELEASE_NAME}' release!" fi - $GREP -qPzo "KEEPASSXC_BUILD_TYPE=Release" snap/snapcraft.yaml - if [ $? -ne 0 ]; then + if ! $GREP -qPzo "KEEPASSXC_BUILD_TYPE=Release" snap/snapcraft.yaml; then exitError "'snapcraft.yaml' is not set for a release build!" fi } @@ -464,7 +454,7 @@ if ! cmdExists realpath; then fi -trap exitTrap SIGINT SIGTERM +trap exitTrap SIGINT SIGTERM ERR # ----------------------------------------------------------------------- # check command @@ -551,11 +541,10 @@ merge() { if [ 0 -ne $? ]; then exitError "Updating translations failed!" fi - git diff-index --quiet HEAD -- - if [ $? -ne 0 ]; then + if ! git diff-index --quiet HEAD --; then git add -A ./share/translations/ logInfo "Committing changes..." - if [ "" == "$GPG_GIT_KEY" ]; then + if [ -z "$GPG_GIT_KEY" ]; then git commit -m "Update translations" else git commit -m "Update translations" -S"$GPG_GIT_KEY" @@ -574,7 +563,7 @@ merge() { 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 + if [ -z "$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" @@ -657,7 +646,7 @@ appimage() { appdir="$(realpath "$appdir")" local out="${OUTPUT_DIR}" - if [ "" == "$out" ]; then + if [ -z "$out" ]; then out="." fi mkdir -p "$out" @@ -674,12 +663,12 @@ appimage() { logInfo "Testing for AppImage tools..." local docker_test_cmd if [ "" != "$DOCKER_IMAGE" ]; then - docker_test_cmd="docker run --rm ${DOCKER_IMAGE}" + docker_test_cmd="docker run -it --user $(id -u):$(id -g) --rm ${DOCKER_IMAGE}" fi # Test if linuxdeploy and linuxdeploy-plugin-qt are installed # on the system or inside the Docker container - if ! ${docker_test_cmd} which ${linuxdeploy} &> /dev/null; then + if ! ${docker_test_cmd} which ${linuxdeploy} > /dev/null; then logInfo "Downloading linuxdeploy..." linuxdeploy="./linuxdeploy" linuxdeploy_cleanup="rm -f ${linuxdeploy}" @@ -688,7 +677,7 @@ appimage() { fi chmod +x "$linuxdeploy" fi - if ! ${docker_test_cmd} which ${linuxdeploy_plugin_qt} &> /dev/null; then + if ! ${docker_test_cmd} which ${linuxdeploy_plugin_qt} > /dev/null; then logInfo "Downloading linuxdeploy-plugin-qt..." linuxdeploy_plugin_qt="./linuxdeploy-plugin-qt" linuxdeploy_plugin_qt_cleanup="rm -f ${linuxdeploy_plugin_qt}" @@ -710,49 +699,46 @@ appimage() { fi # Create custom AppRun wrapper - cat << EOF > "${out_real}/KeePassXC-AppRun" + cat << 'EOF' > "${out_real}/KeePassXC-AppRun" #!/usr/bin/env bash -export PATH="\$(dirname \$0)/usr/bin:\${PATH}" -export LD_LIBRARY_PATH="\$(dirname \$0)/usr/lib:\${LD_LIBRARY_PATH}" +export PATH="$(dirname $0)/usr/bin:${PATH}" +export LD_LIBRARY_PATH="$(dirname $0)/usr/lib:${LD_LIBRARY_PATH}" -if [ "\${1}" == "cli" ]; then +if [ "$1" == "cli" ]; then shift - exec keepassxc-cli "\$@" -elif [ "\${1}" == "proxy" ]; then + exec keepassxc-cli "$@" +elif [ "$1" == "proxy" ]; then shift - exec keepassxc-proxy "\$@" + exec keepassxc-proxy "$@" elif [ -v CHROME_WRAPPER ] || [ -v MOZ_LAUNCHED_CHILD ]; then - exec keepassxc-proxy "\$@" + exec keepassxc-proxy "$@" else - exec keepassxc "\$@" + exec keepassxc "$@" fi EOF chmod +x "${out_real}/KeePassXC-AppRun" # Find .desktop files, icons, and binaries to deploy local desktop_file="$(find "$appdir" -name "org.keepassxc.KeePassXC.desktop" | head -n1)" - local icon="$(find "$appdir" -name 'keepassxc.png' | $GREP -P 'application/256x256/apps/keepassxc.png$' | head -n1)" - local executables="$(IFS=$'\n' find "$appdir" | $GREP -P '/usr/bin/keepassxc[^/]*$' | xargs -i printf " --executable={}")" + local icon="$(find "$appdir" -path '*/application/256x256/apps/keepassxc.png' | head -n1)" + local executables="$(find "$appdir" -type f -executable -path '*/bin/keepassxc*' -print0 | xargs -0 -i printf " --executable={}")" logInfo "Collecting libs and patching binaries..." - if [ "" == "$DOCKER_IMAGE" ]; then + if [ -z "$DOCKER_IMAGE" ]; then "$linuxdeploy" --verbosity=${verbosity} --plugin=qt --appdir="$appdir" --desktop-file="$desktop_file" \ - --custom-apprun="${out_real}/KeePassXC-AppRun" --icon-file="$icon" ${executables} \ - --library=$(ldconfig -p | $GREP x86-64 | $GREP -oP '/[^\s]+/libgpg-error\.so\.\d+$' | head -n1) + --custom-apprun="${out_real}/KeePassXC-AppRun" --icon-file="$icon" ${executables} else - desktop_file="${desktop_file//${appdir}/\/keepassxc\/AppDir}" - icon="${icon//${appdir}/\/keepassxc\/AppDir}" - executables="${executables//${appdir}/\/keepassxc\/AppDir}" - docker run --name "$DOCKER_CONTAINER_NAME" --rm \ - --cap-add SYS_ADMIN --security-opt apparmor:unconfined --device /dev/fuse \ - -v "${appdir}:/keepassxc/AppDir:rw" \ - -v "${out_real}:/keepassxc/out:rw" \ + --cap-add SYS_ADMIN --security-opt apparmor:unconfined --device /dev/fuse -it \ + -v "${out_real}:${out_real}:rw" \ + -v "${appdir}:${appdir}:rw" \ + -w "$out_real" \ + --user $(id -u):$(id -g) \ "$DOCKER_IMAGE" \ - bash -c "cd /keepassxc/out && ${linuxdeploy} --verbosity=${verbosity} --plugin=qt --appdir=/keepassxc/AppDir \ - --custom-apprun="/keepassxc/out/KeePassXC-AppRun" --desktop-file=${desktop_file} --icon-file=${icon} ${executables} \ - --library=\$(ldconfig -p | grep x86-64 | grep -oP '/[^\s]+/libgpg-error\.so\.\d+$' | head -n1)" + bash -c "${linuxdeploy} --verbosity=${verbosity} --plugin=qt \ + --appdir='${appdir}' --custom-apprun='${out_real}/KeePassXC-AppRun' \ + --desktop-file='${desktop_file}' --icon-file='${icon}' ${executables}" fi if [ $? -ne 0 ]; then @@ -900,7 +886,6 @@ build() { init - OUTPUT_DIR="$(realpath "$OUTPUT_DIR")" # Resolve appsign key to absolute path if under Windows if [[ "${build_key}" && "$(uname -o)" == "Msys" ]]; then build_key="$(realpath "${build_key}")" @@ -930,10 +915,13 @@ build() { git checkout "$TAG_NAME" > /dev/null 2>&1 fi - logInfo "Creating output directory..." - mkdir -p "$OUTPUT_DIR" + OUTPUT_DIR="$(realpath "$OUTPUT_DIR")" + if ! ${build_snapshot} && [ -d "$OUTPUT_DIR" ]; then + exitError "Output dir '${OUTPUT_DIR}' already exists." + fi - if [ $? -ne 0 ]; then + logInfo "Creating output directory..." + if ! mkdir -p "$OUTPUT_DIR"; then exitError "Failed to create output directory!" fi @@ -961,14 +949,6 @@ build() { $xz -6 "${OUTPUT_DIR}/${tarball_name}" fi - if ! ${build_snapshot} && [ -e "${OUTPUT_DIR}/build-release" ]; then - logInfo "Cleaning existing build directory..." - rm -rf "${OUTPUT_DIR}/build-release" 2> /dev/null - if [ $? -ne 0 ]; then - exitError "Failed to clean existing build directory, please do it manually." - fi - fi - logInfo "Creating build directory..." mkdir -p "${OUTPUT_DIR}/build-release" cd "${OUTPUT_DIR}/build-release" @@ -994,7 +974,7 @@ build() { fi export CXX="$COMPILER" - if [ "" == "$DOCKER_IMAGE" ]; then + if [ -z "$DOCKER_IMAGE" ]; then if [ "$(uname -s)" == "Darwin" ]; then # Building on macOS export MACOSX_DEPLOYMENT_TARGET @@ -1068,7 +1048,7 @@ build() { logInfo "Launching Docker contain to compile snapcraft..." - sudo docker run --name "$DOCKER_CONTAINER_NAME" --rm \ + sudo docker run --name "$DOCKER_CONTAINER_NAME" --rm -it --user $(id -u):$(id -g) \ -v "$(realpath "$SRC_DIR"):/keepassxc" -w "/keepassxc" \ "$DOCKER_IMAGE" snapcraft else @@ -1078,7 +1058,8 @@ build() { docker run --name "$DOCKER_CONTAINER_NAME" --rm \ --cap-add SYS_ADMIN --security-opt apparmor:unconfined --device /dev/fuse \ - -e "CC=${CC}" -e "CXX=${CXX}" \ + --user $(id -u):$(id -g) \ + -e "CC=${CC}" -e "CXX=${CXX}" -it \ -v "$(realpath "$SRC_DIR"):/keepassxc/src:ro" \ -v "$(realpath "$OUTPUT_DIR"):/keepassxc/out:rw" \ "$DOCKER_IMAGE" \ @@ -1108,7 +1089,7 @@ build() { docker_image_flag="-d ${DOCKER_IMAGE}" docker_container_name_flag="--container-name ${DOCKER_CONTAINER_NAME}" fi - appimage "-a" "${OUTPUT_DIR}/KeePassXC.AppDir" "-o" "${OUTPUT_DIR}" \ + appimage -a "${OUTPUT_DIR}/KeePassXC.AppDir" -o "${OUTPUT_DIR}" \ ${appsign_flag} ${appsign_key_flag} ${docker_image_flag} ${docker_container_name_flag} fi @@ -1376,7 +1357,7 @@ notarize() { exit 1 fi - if [ "$ac_username" == "" ]; then + if [ -z "$ac_username" ]; then logError "Missing arguments, --username is required!" printUsage "notarize" exit 1 @@ -1441,7 +1422,7 @@ notarize() { i18n() { local cmd="$1" - if [ "$cmd" == "" ]; then + if [ -z "$cmd" ]; then logError "No subcommand specified.\n" printUsage i18n exit 1 @@ -1452,12 +1433,16 @@ i18n() { fi shift + checkGitRepository + if [ "$cmd" == "lupdate" ]; then if [ ! -d share/translations ]; then logError "Command must be called from repository root directory." exit 1 fi + checkQt5LUpdateExists + logInfo "Updating source translation file..." LUPDATE=lupdate-qt5 if ! command -v $LUPDATE > /dev/null; then @@ -1469,6 +1454,8 @@ i18n() { return 0 fi + checkTransifexCommandExists + local branch="$(git branch --show-current 2>&1)" local real_branch="$branch" if [[ "$branch" =~ ^release/ ]]; then @@ -1504,8 +1491,8 @@ i18n() { # parse global command line # ----------------------------------------------------------------------- MODE="$1" -shift -if [ "" == "$MODE" ]; then +shift || true +if [ -z "$MODE" ]; then logError "Missing arguments!\n" printUsage exit 1