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
This commit is contained in:
Janek Bevendorff 2021-09-29 16:25:52 +02:00 committed by Janek Bevendorff
parent c90ab2b9cb
commit cc39f9ec23

View File

@ -1,7 +1,7 @@
#!/usr/bin/env bash
#
# KeePassXC Release Preparation Helper
# Copyright (C) 2017 KeePassXC team <https://keepassxc.org/>
# Copyright (C) 2021 KeePassXC team <https://keepassxc.org/>
#
# 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 <http://www.gnu.org/licenses/>.
printf "\e[1m\e[32mKeePassXC\e[0m Release Preparation Helper\n"
printf "Copyright (C) 2017 KeePassXC Team <https://keepassxc.org/>\n\n"
printf "Copyright (C) 2021 KeePassXC Team <https://keepassxc.org/>\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 "<release version=\"${RELEASE_NAME}\" date=\"\d{4}-\d{2}-\d{2}\">" share/linux/org.keepassxc.KeePassXC.appdata.xml
if [ $? -ne 0 ]; then
if ! $GREP -qPzo "<release version=\"${RELEASE_NAME}\" date=\"\d{4}-\d{2}-\d{2}\">" 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