2017-01-28 16:11:05 -05:00
#!/usr/bin/env bash
2018-09-26 16:46:59 -04:00
#
2017-01-28 16:11:05 -05:00
# KeePassXC Release Preparation Helper
# Copyright (C) 2017 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
# 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 <http://www.gnu.org/licenses/>.
2017-02-11 11:05:28 -05:00
printf "\e[1m\e[32mKeePassXC\e[0m Release Preparation Helper\n"
printf "Copyright (C) 2017 KeePassXC Team <https://keepassxc.org/>\n\n"
2017-01-28 16:11:05 -05:00
# -----------------------------------------------------------------------
# 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=""
2019-01-30 15:00:47 -05:00
CPACK_GENERATORS="WIX;ZIP"
2017-01-28 16:11:05 -05:00
COMPILER="g++"
2021-01-29 18:39:36 -05:00
MAKE_OPTIONS="-j$(getconf _NPROCESSORS_ONLN)"
2017-11-27 22:46:03 -05:00
BUILD_PLUGINS="all"
2017-01-28 16:11:05 -05:00
INSTALL_PREFIX="/usr/local"
ORIG_BRANCH=""
ORIG_CWD="$(pwd)"
2019-11-09 18:08:20 -05:00
MACOSX_DEPLOYMENT_TARGET=10.12
GREP="grep"
2017-01-28 16:11:05 -05:00
# -----------------------------------------------------------------------
# helper functions
# -----------------------------------------------------------------------
printUsage() {
local cmd
if [ "" == "$1" ] || [ "help" == "$1" ]; then
cmd="COMMAND"
2018-09-26 16:46:59 -04:00
elif [ "check" == "$1" ] || [ "merge" == "$1" ] || [ "build" == "$1" ] \
2021-01-29 18:39:36 -05:00
|| [ "gpgsign" == "$1" ] || [ "appsign" == "$1" ] || [ "notarize" == "$1" ] || [ "appimage" == "$1" ]; then
2017-01-28 16:11:05 -05:00
cmd="$1"
else
2017-01-28 17:17:48 -05:00
logError "Unknown command: '$1'\n"
2017-01-28 16:11:05 -05:00
cmd="COMMAND"
fi
2017-12-13 16:38:44 -05:00
printf "\e[1mUsage:\e[0m $(basename $0) $cmd [options]\n"
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
if [ "COMMAND" == "$cmd" ]; then
cat << EOF
Commands:
2017-06-06 10:35:16 -04:00
check Perform a dry-run check, nothing is changed
2017-01-28 16:19:23 -05:00
merge Merge release branch into main branch and create release tags
2017-01-28 16:11:05 -05:00
build Build and package binary release from sources
2017-12-13 16:38:44 -05:00
gpgsign Sign previously compiled release packages with GPG
appsign Sign binaries with code signing certificates on Windows and macOS
2021-01-29 18:39:36 -05:00
notarize Submit macOS application DMG for notarization
2017-01-28 16:11:05 -05:00
help Show help for the given command
EOF
elif [ "merge" == "$cmd" ]; then
cat << EOF
2017-01-29 06:52:05 -05:00
Merge release branch into main branch and create release tags
2017-01-28 16:11:05 -05:00
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}')
2018-07-08 19:47:35 -04:00
-k, --key GPG key used to sign the merge commit and release tag,
2017-01-28 16:11:05 -05:00
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
2017-01-29 06:52:05 -05:00
Build and package binary release from sources
2017-01-28 16:11:05 -05:00
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
2018-05-16 22:39:39 -04:00
--snapcraft Create and use docker image to build snapcraft distribution.
This option has no effect if --docker-image is not set.
2018-09-26 16:46:59 -04:00
--appimage Build a Linux AppImage after compilation.
If this option is set, --install-prefix has no effect
2018-07-08 19:47:35 -04:00
--appsign Perform platform specific App Signing before packaging
-k, --key Specify the App Signing Key/Identity
2017-01-28 16:11:05 -05:00
-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}')
2018-07-10 21:43:01 -04:00
-g, --generators Additional CPack generators (default: '${CPACK_GENERATORS}')
2017-01-28 16:11:05 -05:00
-i, --install-prefix Install prefix (default: '${INSTALL_PREFIX}')
-p, --plugins Space-separated list of plugins to build
(default: ${BUILD_PLUGINS})
2017-11-27 22:46:03 -05:00
--snapshot Don't checkout the release tag
2017-01-28 16:11:05 -05:00
-n, --no-source-tarball Don't build source tarball
-h, --help Show this help
EOF
2017-12-13 16:38:44 -05:00
elif [ "gpgsign" == "$cmd" ]; then
2017-01-28 16:11:05 -05:00
cat << EOF
2017-12-13 16:38:44 -05:00
Sign previously compiled release packages with GPG
2017-01-29 06:52:05 -05:00
2017-01-28 16:11:05 -05:00
Options:
2017-10-05 18:04:41 -04:00
-f, --files Files to sign (required)
2018-07-08 19:47:35 -04:00
-k, --key GPG key used to sign the files (default: '${GPG_KEY}')
2017-12-13 16:38:44 -05:00
-h, --help Show this help
EOF
elif [ "appsign" == "$cmd" ]; then
cat << EOF
Sign binaries with code signing certificates on Windows and macOS
Options:
-f, --files Files to sign (required)
2019-11-09 18:08:20 -05:00
-k, --key, -i, --identity
Signing Key or Apple Developer ID (required)
2021-01-29 18:39:36 -05:00
-h, --help Show this help
EOF
elif [ "notarize" == "$cmd" ]; then
cat << EOF
Submit macOS application DMG for notarization
Options:
-f, --files Files to notarize (required)
-u, --username Apple username for notarization (required)
2019-11-09 18:08:20 -05:00
-c, --keychain Apple keychain entry name storing the notarization
app password (default: 'AC_PASSWORD')
2017-10-05 18:04:41 -04:00
-h, --help Show this help
2018-09-26 16:46:59 -04:00
EOF
elif [ "appimage" == "$cmd" ]; then
cat << EOF
Generate Linux AppImage from 'make install' AppDir
Options:
-a, --appdir Input AppDir (required)
-v, --version KeePassXC version
-o, --output-dir Output directory where to build the AppImage
(default: '${OUTPUT_DIR}')
-d, --docker-image Use the specified Docker image to build the AppImage.
The image must have all required build dependencies installed.
--container-name Docker container name (default: '${DOCKER_CONTAINER_NAME}')
The container must not exist already
--appsign Embed a PGP signature into the AppImage
-k, --key The PGP Signing Key
--verbosity linuxdeploy verbosity (default: 3)
-h, --help Show this help
2017-01-28 16:11:05 -05:00
EOF
fi
}
logInfo() {
2017-02-11 11:05:28 -05:00
printf "\e[1m[ \e[34mINFO\e[39m ]\e[0m $1\n"
2017-01-28 16:11:05 -05:00
}
2019-11-09 18:08:20 -05:00
logWarn() {
printf "\e[1m[ \e[33mWARNING\e[39m ]\e[0m $1\n"
}
2017-01-28 16:11:05 -05:00
logError() {
2017-02-11 11:05:28 -05:00
printf "\e[1m[ \e[31mERROR\e[39m ]\e[0m $1\n" >&2
2017-01-28 16:11:05 -05:00
}
init() {
2017-06-06 10:35:16 -04:00
if [ "" == "$RELEASE_NAME" ]; then
logError "Missing arguments, --version is required!\n"
printUsage "check"
exit 1
fi
if [ "" == "$TAG_NAME" ]; then
TAG_NAME="$RELEASE_NAME"
fi
2018-09-26 16:46:59 -04:00
2017-06-06 10:35:16 -04:00
if [ "" == "$SOURCE_BRANCH" ]; then
SOURCE_BRANCH="release/${RELEASE_NAME}"
fi
2017-01-28 16:11:05 -05:00
ORIG_CWD="$(pwd)"
2017-06-06 10:35:16 -04:00
SRC_DIR="$(realpath "$SRC_DIR")"
2017-01-28 16:11:05 -05:00
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..."
}
2018-09-26 16:46:59 -04:00
cmdExists() {
command -v "$1" &> /dev/null
}
2019-11-09 18:08:20 -05:00
checkGrepCompat() {
if ! grep -qPzo test <(echo test) 2> /dev/null; then
if [ -e /usr/local/opt/grep/libexec/gnubin/grep ]; then
GREP="/usr/local/opt/grep/libexec/gnubin/grep"
else
exitError "Incompatible grep implementation! If on macOS, please run 'brew install grep'."
fi
fi
}
2017-01-28 16:11:05 -05:00
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() {
2019-09-08 07:30:51 -04:00
if [ ! -d .git ] || [ ! -f CHANGELOG.md ]; then
2017-01-28 16:11:05 -05:00
exitError "Source directory is not a valid Git repository!"
fi
}
checkReleaseDoesNotExist() {
2019-11-09 18:08:20 -05:00
git tag | $GREP -q "^$TAG_NAME$"
2017-01-28 16:11:05 -05:00
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:]')"
2017-06-06 10:35:16 -04:00
local major_num="$(echo ${RELEASE_NAME} | cut -f1 -d.)"
local minor_num="$(echo ${RELEASE_NAME} | cut -f2 -d.)"
2018-02-17 11:46:19 -05:00
local patch_num="$(echo ${RELEASE_NAME} | cut -f3 -d. | cut -f1 -d-)"
2017-06-06 10:35:16 -04:00
2019-11-09 18:08:20 -05:00
$GREP -q "${app_name_upper}_VERSION_MAJOR \"${major_num}\"" CMakeLists.txt
2017-01-28 16:11:05 -05:00
if [ $? -ne 0 ]; then
2017-06-06 10:35:16 -04:00
exitError "${app_name_upper}_VERSION_MAJOR not updated to '${major_num}' in CMakeLists.txt!"
2017-01-28 16:11:05 -05:00
fi
2019-11-09 18:08:20 -05:00
$GREP -q "${app_name_upper}_VERSION_MINOR \"${minor_num}\"" CMakeLists.txt
2017-01-28 16:11:05 -05:00
if [ $? -ne 0 ]; then
2017-06-06 10:35:16 -04:00
exitError "${app_name_upper}_VERSION_MINOR not updated to '${minor_num}' in CMakeLists.txt!"
fi
2019-11-09 18:08:20 -05:00
$GREP -q "${app_name_upper}_VERSION_PATCH \"${patch_num}\"" CMakeLists.txt
2017-06-06 10:35:16 -04:00
if [ $? -ne 0 ]; then
exitError "${app_name_upper}_VERSION_PATCH not updated to '${patch_num}' in CMakeLists.txt!"
2017-01-28 16:11:05 -05:00
fi
}
checkChangeLog() {
2019-09-08 07:30:51 -04:00
if [ ! -f CHANGELOG.md ]; then
2017-01-28 16:11:05 -05:00
exitError "No CHANGELOG file found!"
fi
2018-09-26 16:46:59 -04:00
2019-11-09 18:08:20 -05:00
$GREP -qPzo "## ${RELEASE_NAME} \(\d{4}-\d{2}-\d{2}\)\n" CHANGELOG.md
2017-01-28 16:11:05 -05:00
if [ $? -ne 0 ]; then
2019-09-08 07:30:51 -04:00
exitError "'CHANGELOG.md' has not been updated to the '${RELEASE_NAME}' release!"
2017-01-28 16:11:05 -05:00
fi
}
2017-10-17 08:36:18 -04:00
checkAppStreamInfo() {
2017-10-26 07:00:50 -04:00
if [ ! -f share/linux/org.keepassxc.KeePassXC.appdata.xml ]; then
2017-10-17 08:36:18 -04:00
exitError "No AppStream info file found!"
2017-01-28 17:02:26 -05:00
fi
2017-06-06 10:35:16 -04:00
2019-11-09 18:08:20 -05:00
$GREP -qPzo "<release version=\"${RELEASE_NAME}\" date=\"\d{4}-\d{2}-\d{2}\">" share/linux/org.keepassxc.KeePassXC.appdata.xml
2017-10-17 08:36:18 -04:00
if [ $? -ne 0 ]; then
2017-10-26 07:00:50 -04:00
exitError "'share/linux/org.keepassxc.KeePassXC.appdata.xml' has not been updated to the '${RELEASE_NAME}' release!"
2017-06-06 10:35:16 -04:00
fi
}
checkSnapcraft() {
2019-11-11 11:44:12 -05:00
if [ ! -f snap/snapcraft.yaml ]; then
echo "Could not find snap/snapcraft.yaml!"
2017-06-06 10:35:16 -04:00
return
fi
2019-11-11 11:44:12 -05:00
$GREP -qPzo "version: ${RELEASE_NAME}" snap/snapcraft.yaml
2017-06-06 10:35:16 -04:00
if [ $? -ne 0 ]; then
2017-10-17 08:36:18 -04:00
exitError "'snapcraft.yaml' has not been updated to the '${RELEASE_NAME}' release!"
fi
2019-03-19 18:54:56 -04:00
2019-11-11 11:44:12 -05:00
$GREP -qPzo "KEEPASSXC_BUILD_TYPE=Release" snap/snapcraft.yaml
2019-03-19 18:54:56 -04:00
if [ $? -ne 0 ]; then
exitError "'snapcraft.yaml' is not set for a release build!"
fi
2017-10-17 08:36:18 -04:00
}
checkTransifexCommandExists() {
2018-09-26 16:46:59 -04:00
if ! cmdExists tx; then
2017-12-13 16:38:44 -05:00
exitError "Transifex tool 'tx' not installed! Please install it using 'pip install transifex-client'."
fi
}
2017-11-27 22:46:03 -05:00
checkSigntoolCommandExists() {
2018-09-26 16:46:59 -04:00
if ! cmdExists signtool; then
2017-11-27 22:46:03 -05:00
exitError "signtool command not found on the PATH! Add the Windows SDK binary folder to your PATH."
fi
}
2019-11-09 18:08:20 -05:00
checkXcodeSetup() {
if ! cmdExists xcrun; then
exitError "xcrun command not found on the PATH! Please check that you have correctly installed Xcode."
fi
if ! xcrun -f codesign > /dev/null 2>&1; then
exitError "codesign command not found. You may need to run 'sudo xcode-select -r' to set up Xcode."
fi
if ! xcrun -f altool > /dev/null 2>&1; then
exitError "altool command not found. You may need to run 'sudo xcode-select -r' to set up Xcode."
fi
if ! xcrun -f stapler > /dev/null 2>&1; then
exitError "stapler command not found. You may need to run 'sudo xcode-select -r' to set up Xcode."
2017-12-13 16:38:44 -05:00
fi
}
2017-10-17 08:54:11 -04:00
checkQt5LUpdateExists() {
2019-11-09 18:08:20 -05:00
if cmdExists lupdate && ! $(lupdate -version | $GREP -q "lupdate version 5\."); then
2018-09-26 16:46:59 -04:00
if ! cmdExists lupdate-qt5; then
2017-10-17 08:54:11 -04:00
exitError "Qt Linguist tool (lupdate-qt5) is not installed! Please install using 'apt install qttools5-dev-tools'"
fi
2017-06-06 10:35:16 -04:00
fi
}
performChecks() {
logInfo "Performing basic checks..."
2018-09-26 16:46:59 -04:00
2019-11-09 18:08:20 -05:00
checkGrepCompat
2017-06-06 10:35:16 -04:00
checkSourceDirExists
logInfo "Changing to source directory..."
cd "${SRC_DIR}"
2018-09-26 16:46:59 -04:00
2017-06-06 10:35:16 -04:00
logInfo "Validating toolset and repository..."
checkTransifexCommandExists
2017-10-17 08:54:11 -04:00
checkQt5LUpdateExists
2017-06-06 10:35:16 -04:00
checkGitRepository
checkReleaseDoesNotExist
checkWorkingTreeClean
checkSourceBranchExists
checkTargetBranchExists
2018-09-26 16:46:59 -04:00
2017-06-06 10:35:16 -04:00
logInfo "Checking out '${SOURCE_BRANCH}'..."
2021-01-29 18:39:36 -05:00
git checkout "$SOURCE_BRANCH" > /dev/null 2>&1
2018-09-26 16:46:59 -04:00
2017-06-06 10:35:16 -04:00
logInfo "Attempting to find '${RELEASE_NAME}' in various files..."
checkVersionInCMake
checkChangeLog
2017-10-17 08:36:18 -04:00
checkAppStreamInfo
2017-06-06 10:35:16 -04:00
checkSnapcraft
2018-09-26 16:46:59 -04:00
2017-06-06 10:35:16 -04:00
logInfo "\e[1m\e[32mAll checks passed!\e[0m"
2017-01-28 17:02:26 -05:00
}
2017-01-31 13:25:15 -05:00
# re-implement realpath for OS X (thanks mschrag)
2017-01-31 19:03:30 -05:00
# https://superuser.com/questions/205127/
2018-09-26 16:46:59 -04:00
if ! cmdExists realpath; then
2017-01-31 13:25:15 -05:00
realpath() {
pushd . > /dev/null
if [ -d "$1" ]; then
2017-01-31 19:03:30 -05:00
cd "$1"
dirs -l +0
else
cd "$(dirname "$1")"
cur_dir=$(dirs -l +0)
2018-09-26 16:46:59 -04:00
2017-01-31 13:25:15 -05:00
if [ "$cur_dir" == "/" ]; then
2017-01-31 19:03:30 -05:00
echo "$cur_dir$(basename "$1")"
2017-01-31 13:25:15 -05:00
else
2017-01-31 19:03:30 -05:00
echo "$cur_dir/$(basename "$1")"
2017-01-31 13:25:15 -05:00
fi
fi
popd > /dev/null
}
fi
2017-01-28 16:11:05 -05:00
trap exitTrap SIGINT SIGTERM
2017-06-06 10:35:16 -04:00
# -----------------------------------------------------------------------
# check command
# -----------------------------------------------------------------------
check() {
while [ $# -ge 1 ]; do
local arg="$1"
case "$arg" in
-v|--version)
RELEASE_NAME="$2"
shift ;;
esac
shift
done
init
performChecks
cleanup
logInfo "Congrats! You can successfully merge, build, and sign KeepassXC."
}
2017-01-28 16:11:05 -05:00
# -----------------------------------------------------------------------
# merge command
# -----------------------------------------------------------------------
2018-09-26 16:46:59 -04:00
merge() {
2017-01-28 16:11:05 -05:00
while [ $# -ge 1 ]; do
local arg="$1"
case "$arg" in
-v|--version)
RELEASE_NAME="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
-a|--app-name)
APP_NAME="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
-s|--source-dir)
SRC_DIR="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2018-07-08 19:47:35 -04:00
-k|--key|-g|--gpg-key)
2017-01-28 16:11:05 -05:00
GPG_GIT_KEY="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
-r|--release-branch)
SOURCE_BRANCH="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
--target-branch)
TARGET_BRANCH="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
-t|--tag-name)
TAG_NAME="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
-h|--help)
printUsage "merge"
exit ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
*)
logError "Unknown option '$arg'\n"
printUsage "merge"
exit 1 ;;
esac
shift
done
init
2017-06-06 10:35:16 -04:00
performChecks
2018-09-26 16:46:59 -04:00
2017-01-28 17:02:26 -05:00
logInfo "Updating language files..."
2018-01-20 11:05:29 -05:00
./share/translations/update.sh update
./share/translations/update.sh pull
2017-01-28 17:02:26 -05:00
if [ 0 -ne $? ]; then
exitError "Updating translations failed!"
fi
git diff-index --quiet HEAD --
if [ $? -ne 0 ]; then
2020-07-06 19:08:30 -04:00
git add -A ./share/translations/
2017-01-28 17:02:26 -05:00
logInfo "Committing changes..."
if [ "" == "$GPG_GIT_KEY" ]; then
git commit -m "Update translations"
else
git commit -m "Update translations" -S"$GPG_GIT_KEY"
fi
fi
2017-01-28 16:11:05 -05:00
2019-11-09 18:08:20 -05:00
CHANGELOG=$($GREP -Pzo "(?<=${RELEASE_NAME} \(\d{4}-\d{2}-\d{2}\)\n\n)\n?(?:.|\n)+?\n(?=## )" CHANGELOG.md \
2019-09-08 07:30:51 -04:00
| sed 's/^### //' | tr -d \\0)
2017-02-11 11:19:46 -05:00
COMMIT_MSG="Release ${RELEASE_NAME}"
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
logInfo "Checking out target branch '${TARGET_BRANCH}'..."
2021-01-29 18:39:36 -05:00
git checkout "$TARGET_BRANCH" > /dev/null 2>&1
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
logInfo "Merging '${SOURCE_BRANCH}' into '${TARGET_BRANCH}'..."
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
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
cleanup
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
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."
}
2018-09-26 16:46:59 -04:00
# -----------------------------------------------------------------------
# appimage command
# -----------------------------------------------------------------------
appimage() {
local appdir
local build_appsign=false
local build_key
local verbosity="1"
while [ $# -ge 1 ]; do
local arg="$1"
case "$arg" in
-v|--version)
RELEASE_NAME="$2"
shift ;;
-a|--appdir)
appdir="$2"
shift ;;
-o|--output-dir)
OUTPUT_DIR="$2"
shift ;;
-d|--docker-image)
DOCKER_IMAGE="$2"
shift ;;
--container-name)
DOCKER_CONTAINER_NAME="$2"
shift ;;
--appsign)
build_appsign=true ;;
--verbosity)
verbosity=$2
shift ;;
-k|--key)
build_key="$2"
shift ;;
-h|--help)
printUsage "appimage"
exit ;;
*)
logError "Unknown option '$arg'\n"
printUsage "appimage"
exit 1 ;;
esac
shift
done
if [ -z "${appdir}" ]; then
logError "Missing arguments, --appdir is required!\n"
printUsage "appimage"
exit 1
fi
if [ ! -d "${appdir}" ]; then
2020-01-20 16:46:50 -05:00
exitError "AppDir does not exist, please create one with 'make install'!"
2018-09-26 16:46:59 -04:00
elif [ -e "${appdir}/AppRun" ]; then
2020-01-20 16:46:50 -05:00
exitError "AppDir has already been run through linuxdeploy, please create a fresh AppDir with 'make install'."
2018-09-26 16:46:59 -04:00
fi
appdir="$(realpath "$appdir")"
local out="${OUTPUT_DIR}"
if [ "" == "$out" ]; then
out="."
fi
mkdir -p "$out"
local out_real="$(realpath "$out")"
cd "$out"
local linuxdeploy="linuxdeploy"
local linuxdeploy_cleanup
local linuxdeploy_plugin_qt="linuxdeploy-plugin-qt"
local linuxdeploy_plugin_qt_cleanup
local appimagetool="appimagetool"
local appimagetool_cleanup
logInfo "Testing for AppImage tools..."
local docker_test_cmd
if [ "" != "$DOCKER_IMAGE" ]; then
docker_test_cmd="docker run --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
logInfo "Downloading linuxdeploy..."
linuxdeploy="./linuxdeploy"
linuxdeploy_cleanup="rm -f ${linuxdeploy}"
2020-01-20 16:46:50 -05:00
if ! curl -Lf "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" > "$linuxdeploy"; then
exitError "linuxdeploy download failed."
fi
2018-09-26 16:46:59 -04:00
chmod +x "$linuxdeploy"
fi
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}"
2020-01-20 16:46:50 -05:00
if ! curl -Lf "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage" > "$linuxdeploy_plugin_qt"; then
exitError "linuxdeploy-plugin-qt download failed."
fi
2018-09-26 16:46:59 -04:00
chmod +x "$linuxdeploy_plugin_qt"
fi
# appimagetool is always run outside a Docker container, so we can access our GPG keys
if ! cmdExists ${appimagetool}; then
logInfo "Downloading appimagetool..."
appimagetool="./appimagetool"
appimagetool_cleanup="rm -f ${appimagetool}"
2020-01-20 16:46:50 -05:00
if ! curl -Lf "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" > "$appimagetool"; then
exitError "appimagetool download failed."
fi
2018-09-26 16:46:59 -04:00
chmod +x "$appimagetool"
fi
# Create custom AppRun wrapper
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}"
if [ "\${1}" == "cli" ]; then
shift
exec keepassxc-cli "\$@"
elif [ "\${1}" == "proxy" ]; then
shift
exec keepassxc-proxy "\$@"
elif [ -v CHROME_WRAPPER ] || [ -v MOZ_LAUNCHED_CHILD ]; then
exec keepassxc-proxy "\$@"
else
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)"
2019-11-09 18:08:20 -05:00
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={}")"
2018-09-26 16:46:59 -04:00
logInfo "Collecting libs and patching binaries..."
if [ "" == "$DOCKER_IMAGE" ]; then
"$linuxdeploy" --verbosity=${verbosity} --plugin=qt --appdir="$appdir" --desktop-file="$desktop_file" \
--custom-apprun="${out_real}/KeePassXC-AppRun" --icon-file="$icon" ${executables} \
2019-11-09 18:08:20 -05:00
--library=$(ldconfig -p | $GREP x86-64 | $GREP -oP '/[^\s]+/libgpg-error\.so\.\d+$' | head -n1)
2018-09-26 16:46:59 -04:00
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" \
"$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)"
fi
2020-01-20 16:46:50 -05:00
if [ $? -ne 0 ]; then
exitError "AppDir deployment failed."
fi
2018-09-26 16:46:59 -04:00
logInfo "Creating AppImage..."
local appsign_flag=""
local appsign_key_flag=""
if ${build_appsign}; then
appsign_flag="--sign"
appsign_key_flag="--sign-key ${build_key}"
fi
local appimage_name="KeePassXC-x86_64.AppImage"
if [ "" != "$RELEASE_NAME" ]; then
appimage_name="KeePassXC-${RELEASE_NAME}-x86_64.AppImage"
2020-06-10 05:31:08 -04:00
echo "X-AppImage-Version=${RELEASE_NAME}" >> "$desktop_file"
2018-09-26 16:46:59 -04:00
fi
# Run appimagetool to package (and possibly sign) AppImage
# --no-appstream is required, since it may crash on newer systems
# see: https://github.com/AppImage/AppImageKit/issues/856
2020-01-20 16:46:50 -05:00
if ! "$appimagetool" --updateinformation "gh-releases-zsync|keepassxreboot|keepassxc|latest|KeePassXC-*-x86_64.AppImage.zsync" \
${appsign_flag} ${appsign_key_flag} --no-appstream "$appdir" "${out_real}/${appimage_name}"; then
exitError "AppImage creation failed."
fi
2018-09-26 16:46:59 -04:00
logInfo "Cleaning up temporary files..."
${linuxdeploy_cleanup}
${linuxdeploy_plugin_qt_cleanup}
${appimagetool_cleanup}
rm -f "${out_real}/KeePassXC-AppRun"
}
2017-01-28 16:11:05 -05:00
# -----------------------------------------------------------------------
# build command
# -----------------------------------------------------------------------
2018-07-08 19:47:35 -04:00
build() {
local build_source_tarball=true
local build_snapshot=false
local build_snapcraft=false
2018-09-26 16:46:59 -04:00
local build_appimage=false
2018-07-08 19:47:35 -04:00
local build_generators=""
local build_appsign=false
local build_key=""
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
while [ $# -ge 1 ]; do
local arg="$1"
case "$arg" in
-v|--version)
RELEASE_NAME="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
-a|--app-name)
APP_NAME="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
-s|--source-dir)
SRC_DIR="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
-o|--output-dir)
OUTPUT_DIR="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
-t|--tag-name)
TAG_NAME="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
-d|--docker-image)
DOCKER_IMAGE="$2"
shift ;;
2018-07-08 19:47:35 -04:00
2018-09-26 16:46:59 -04:00
--container-name)
DOCKER_CONTAINER_NAME="$2"
shift ;;
2018-07-08 19:47:35 -04:00
--appsign)
build_appsign=true ;;
-k|--key)
build_key="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2018-05-16 22:39:39 -04:00
--snapcraft)
2018-07-08 19:47:35 -04:00
build_snapcraft=true ;;
2018-09-26 16:46:59 -04:00
--appimage)
build_appimage=true ;;
2017-01-28 16:11:05 -05:00
-c|--cmake-options)
CMAKE_OPTIONS="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
--compiler)
COMPILER="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
-m|--make-options)
MAKE_OPTIONS="$2"
shift ;;
2018-07-08 19:47:35 -04:00
-g|--generators)
2018-07-10 21:43:01 -04:00
build_generators="$2"
2018-07-08 19:47:35 -04:00
shift ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
-i|--install-prefix)
INSTALL_PREFIX="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
-p|--plugins)
BUILD_PLUGINS="$2"
shift ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
-n|--no-source-tarball)
2018-07-08 19:47:35 -04:00
build_source_tarball=false ;;
2017-11-27 22:46:03 -05:00
--snapshot)
2018-07-08 19:47:35 -04:00
build_snapshot=true ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
-h|--help)
printUsage "build"
exit ;;
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
*)
logError "Unknown option '$arg'\n"
printUsage "build"
exit 1 ;;
esac
shift
done
2017-06-06 10:35:16 -04:00
2017-01-28 16:11:05 -05:00
init
OUTPUT_DIR="$(realpath "$OUTPUT_DIR")"
2019-03-21 16:21:45 -04:00
# Resolve appsign key to absolute path if under Windows
if [[ "${build_key}" && "$(uname -o)" == "Msys" ]]; then
build_key="$(realpath "${build_key}")"
fi
2017-10-05 18:04:41 -04:00
2018-07-08 19:47:35 -04:00
if ${build_snapshot}; then
2017-11-27 22:46:03 -05:00
TAG_NAME="HEAD"
local branch=`git rev-parse --abbrev-ref HEAD`
logInfo "Using current branch ${branch} to build..."
RELEASE_NAME="${RELEASE_NAME}-snapshot"
2019-03-21 16:21:45 -04:00
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_BUILD_TYPE=Snapshot -DOVERRIDE_VERSION=${RELEASE_NAME}"
2017-11-27 22:46:03 -05:00
else
2019-11-09 18:08:20 -05:00
checkGrepCompat
2018-02-17 11:46:19 -05:00
checkWorkingTreeClean
2019-11-09 18:08:20 -05:00
if $(echo "$TAG_NAME" | $GREP -qP "\-(alpha|beta)\\d+\$"); then
2018-02-17 11:46:19 -05:00
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_BUILD_TYPE=PreRelease"
logInfo "Checking out pre-release tag '${TAG_NAME}'..."
else
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_BUILD_TYPE=Release"
logInfo "Checking out release tag '${TAG_NAME}'..."
fi
2021-01-29 18:39:36 -05:00
git checkout "$TAG_NAME" > /dev/null 2>&1
2017-11-27 22:46:03 -05:00
fi
2017-10-05 18:04:41 -04:00
2017-01-28 16:11:05 -05:00
logInfo "Creating output directory..."
mkdir -p "$OUTPUT_DIR"
if [ $? -ne 0 ]; then
exitError "Failed to create output directory!"
fi
2018-07-08 19:47:35 -04:00
if ${build_source_tarball}; then
2017-01-28 16:11:05 -05:00
logInfo "Creating source tarball..."
local app_name_lower="$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]')"
2018-02-22 15:53:29 -05:00
local prefix="${app_name_lower}-${RELEASE_NAME}"
local tarball_name="${prefix}-src.tar"
git archive --format=tar "$TAG_NAME" --prefix="${prefix}/" --output="${OUTPUT_DIR}/${tarball_name}"
2019-03-21 16:21:45 -04:00
# add .version and .gitrev files to tarball
mkdir "${prefix}"
echo -n ${RELEASE_NAME} > "${prefix}/.version"
echo -n `git rev-parse --short=7 HEAD` > "${prefix}/.gitrev"
tar --append --file="${OUTPUT_DIR}/${tarball_name}" "${prefix}/.version" "${prefix}/.gitrev"
rm "${prefix}/.version" "${prefix}/.gitrev"
rmdir "${prefix}" 2> /dev/null
2018-02-22 15:53:29 -05:00
2019-11-09 18:08:20 -05:00
local xz="xz"
if ! cmdExists xz; then
logWarn "xz not installed. Falling back to bz2..."
xz="bzip2"
fi
$xz -6 "${OUTPUT_DIR}/${tarball_name}"
2017-01-28 16:11:05 -05:00
fi
2018-02-17 11:46:19 -05:00
2018-07-08 19:47:35 -04:00
if ! ${build_snapshot} && [ -e "${OUTPUT_DIR}/build-release" ]; then
2018-02-17 11:46:19 -05:00
logInfo "Cleaning existing build directory..."
2020-01-20 16:46:50 -05:00
rm -rf "${OUTPUT_DIR}/build-release" 2> /dev/null
2018-02-17 11:46:19 -05:00
if [ $? -ne 0 ]; then
exitError "Failed to clean existing build directory, please do it manually."
fi
fi
2017-01-28 16:11:05 -05:00
logInfo "Creating build directory..."
mkdir -p "${OUTPUT_DIR}/build-release"
cd "${OUTPUT_DIR}/build-release"
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
logInfo "Configuring sources..."
2018-02-17 11:46:19 -05:00
for p in ${BUILD_PLUGINS}; do
2017-01-28 16:11:05 -05:00
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DWITH_XC_$(echo $p | tr '[:lower:]' '[:upper:]')=On"
done
2019-11-09 18:08:20 -05:00
if [ "$(uname -o 2> /dev/null)" == "GNU/Linux" ] && ${build_appimage}; then
2018-09-26 16:46:59 -04:00
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_DIST_TYPE=AppImage"
# linuxdeploy requires /usr as install prefix
INSTALL_PREFIX="/usr"
fi
2019-03-21 16:21:45 -04:00
# Do not build tests cases
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DWITH_TESTS=OFF"
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
if [ "$COMPILER" == "g++" ]; then
export CC=gcc
elif [ "$COMPILER" == "clang++" ]; then
export CC=clang
fi
export CXX="$COMPILER"
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
if [ "" == "$DOCKER_IMAGE" ]; then
2017-01-29 14:46:57 -05:00
if [ "$(uname -s)" == "Darwin" ]; then
2018-02-21 15:35:17 -05:00
# Building on macOS
2019-11-09 18:08:20 -05:00
export MACOSX_DEPLOYMENT_TARGET
2018-02-21 15:35:17 -05:00
2017-01-29 14:46:57 -05:00
logInfo "Configuring build..."
2018-02-21 15:35:17 -05:00
cmake -DCMAKE_BUILD_TYPE=Release \
2021-01-29 18:39:36 -05:00
-DCMAKE_OSX_ARCHITECTURES="$(uname -m)" -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \
-DCMAKE_PREFIX_PATH="/opt/homebrew/opt/qt/lib/cmake;/usr/local/opt/qt/lib/cmake" \
2018-02-21 15:35:17 -05:00
${CMAKE_OPTIONS} "$SRC_DIR"
2017-01-29 14:46:57 -05:00
logInfo "Compiling and packaging sources..."
2018-02-21 15:35:17 -05:00
make ${MAKE_OPTIONS} package
2018-09-26 16:46:59 -04:00
# Appsign the executables if desired
2019-03-19 21:12:39 -04:00
if ${build_appsign}; then
2018-09-26 16:46:59 -04:00
logInfo "Signing executable files"
appsign "-f" "./${APP_NAME}-${RELEASE_NAME}.dmg" "-k" "${build_key}"
fi
2021-01-29 18:39:36 -05:00
mv "./${APP_NAME}-${RELEASE_NAME}.dmg" "../${APP_NAME}-${RELEASE_NAME}-$(uname -m).dmg"
2017-01-29 14:46:57 -05:00
elif [ "$(uname -o)" == "Msys" ]; then
2018-02-21 15:35:17 -05:00
# Building on Windows with Msys2
2017-01-28 16:19:23 -05:00
logInfo "Configuring build..."
2019-03-21 16:21:45 -04:00
cmake -DCMAKE_BUILD_TYPE=Release -G"MSYS Makefiles" \
2018-07-08 19:47:35 -04:00
-DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" ${CMAKE_OPTIONS} "$SRC_DIR"
2018-09-26 16:46:59 -04:00
2017-01-28 16:45:07 -05:00
logInfo "Compiling and packaging sources..."
2018-02-21 15:35:17 -05:00
mingw32-make ${MAKE_OPTIONS} preinstall
2018-07-08 19:47:35 -04:00
# Appsign the executables if desired
2019-03-21 16:21:45 -04:00
if ${build_appsign} && [ -f "${build_key}" ]; then
2018-07-08 19:47:35 -04:00
logInfo "Signing executable files"
2019-11-09 18:08:20 -05:00
appsign "-f" $(find src | $GREP -P '\.exe$|\.dll$') "-k" "${build_key}"
2018-07-08 19:47:35 -04:00
fi
2017-12-09 09:02:57 -05:00
# Call cpack directly instead of calling make package.
# This is important because we want to build the MSI when making a
# release.
2018-07-08 19:47:35 -04:00
cpack -G "${CPACK_GENERATORS};${build_generators}"
# Inject the portable config into the zip build and rename
2020-06-04 08:10:07 -04:00
touch .portable
2018-07-08 19:47:35 -04:00
for filename in ${APP_NAME}-*.zip; do
logInfo "Creating portable zip file"
2018-09-26 16:46:59 -04:00
local folder=$(echo ${filename} | sed -r 's/(.*)\.zip/\1/')
2018-07-08 19:47:35 -04:00
python -c 'import zipfile,sys ; zipfile.ZipFile(sys.argv[1],"a").write(sys.argv[2],sys.argv[3])' \
2020-06-04 08:10:07 -04:00
${filename} .portable ${folder}/.portable
2018-07-08 19:47:35 -04:00
mv ${filename} ${folder}-portable.zip
done
2020-06-04 08:10:07 -04:00
rm .portable
2018-09-26 16:46:59 -04:00
2018-07-08 19:47:35 -04:00
mv "${APP_NAME}-"*.* ../
2017-01-29 14:46:57 -05:00
else
2018-09-26 16:46:59 -04:00
mkdir -p "${OUTPUT_DIR}/KeePassXC.AppDir"
2017-01-29 14:46:57 -05:00
# Building on Linux without Docker container
2017-01-28 16:19:23 -05:00
logInfo "Configuring build..."
2019-03-21 16:21:45 -04:00
cmake -DCMAKE_BUILD_TYPE=Release ${CMAKE_OPTIONS} \
2018-09-26 16:46:59 -04:00
-DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" "$SRC_DIR"
2017-01-28 16:19:23 -05:00
logInfo "Compiling sources..."
2018-09-26 16:46:59 -04:00
make ${MAKE_OPTIONS}
2017-01-28 16:11:05 -05:00
logInfo "Installing to bin dir..."
2018-09-26 16:46:59 -04:00
make DESTDIR="${OUTPUT_DIR}/KeePassXC.AppDir" install/strip
2017-01-28 16:11:05 -05:00
fi
else
2018-08-24 08:40:41 -04:00
if ${build_snapcraft}; then
2018-05-16 22:39:39 -04:00
logInfo "Building snapcraft docker image..."
2018-09-26 16:46:59 -04:00
2018-05-16 22:39:39 -04:00
sudo docker image build -t "$DOCKER_IMAGE" "$(realpath "$SRC_DIR")/ci/snapcraft"
logInfo "Launching Docker contain to compile snapcraft..."
2018-09-26 16:46:59 -04:00
2018-05-16 22:39:39 -04:00
sudo docker run --name "$DOCKER_CONTAINER_NAME" --rm \
-v "$(realpath "$SRC_DIR"):/keepassxc" -w "/keepassxc" \
2018-09-26 16:46:59 -04:00
"$DOCKER_IMAGE" snapcraft
2018-05-16 22:39:39 -04:00
else
2018-09-26 16:46:59 -04:00
mkdir -p "${OUTPUT_DIR}/KeePassXC.AppDir"
2018-05-16 22:39:39 -04:00
logInfo "Launching Docker container to compile sources..."
2018-09-26 16:46:59 -04:00
2018-05-16 22:39:39 -04:00
docker run --name "$DOCKER_CONTAINER_NAME" --rm \
--cap-add SYS_ADMIN --security-opt apparmor:unconfined --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 && \
2019-03-21 16:21:45 -04:00
cmake -DCMAKE_BUILD_TYPE=Release ${CMAKE_OPTIONS} \
2018-09-26 16:46:59 -04:00
-DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} /keepassxc/src && \
make ${MAKE_OPTIONS} && make DESTDIR=/keepassxc/out/KeePassXC.AppDir install/strip"
2018-05-16 22:39:39 -04:00
fi
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
if [ 0 -ne $? ]; then
exitError "Docker build failed!"
fi
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
logInfo "Build finished, Docker container terminated."
fi
2018-09-26 16:46:59 -04:00
2019-11-09 18:08:20 -05:00
if [ "$(uname -o 2> /dev/null)" == "GNU/Linux" ] && ${build_appimage}; then
2018-09-26 16:46:59 -04:00
local appsign_flag=""
local appsign_key_flag=""
local docker_image_flag=""
local docker_container_name_flag=""
if ${build_appsign}; then
appsign_flag="--appsign"
appsign_key_flag="-k ${build_key}"
fi
if [ "" != "${DOCKER_IMAGE}" ]; then
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}" \
${appsign_flag} ${appsign_key_flag} ${docker_image_flag} ${docker_container_name_flag}
fi
2017-01-28 16:11:05 -05:00
cleanup
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
logInfo "All done!"
}
# -----------------------------------------------------------------------
2017-12-13 16:38:44 -05:00
# gpgsign command
2017-01-28 16:11:05 -05:00
# -----------------------------------------------------------------------
2017-12-13 16:38:44 -05:00
gpgsign() {
local sign_files=()
2017-01-28 16:11:05 -05:00
while [ $# -ge 1 ]; do
local arg="$1"
case "$arg" in
-f|--files)
while [ "${2:0:1}" != "-" ] && [ $# -ge 2 ]; do
2017-12-13 16:38:44 -05:00
sign_files+=("$2")
2017-01-28 16:11:05 -05:00
shift
done ;;
2017-12-13 16:38:44 -05:00
2018-07-08 19:47:35 -04:00
-k|--key|-g|--gpg-key)
2017-01-28 16:11:05 -05:00
GPG_KEY="$2"
shift ;;
2017-10-05 18:04:41 -04:00
2017-01-28 16:11:05 -05:00
-h|--help)
2017-12-13 16:38:44 -05:00
printUsage "gpgsign"
2017-01-28 16:11:05 -05:00
exit ;;
2017-12-13 16:38:44 -05:00
2017-01-28 16:11:05 -05:00
*)
logError "Unknown option '$arg'\n"
2017-12-13 16:38:44 -05:00
printUsage "gpgsign"
2017-01-28 16:11:05 -05:00
exit 1 ;;
esac
shift
done
2018-09-26 16:46:59 -04:00
2017-12-13 16:38:44 -05:00
if [ -z "${sign_files}" ]; then
2017-01-28 16:11:05 -05:00
logError "Missing arguments, --files is required!\n"
2017-12-13 16:38:44 -05:00
printUsage "gpgsign"
2017-01-28 16:11:05 -05:00
exit 1
fi
2017-10-05 18:04:41 -04:00
2017-12-13 16:38:44 -05:00
for f in "${sign_files[@]}"; do
2017-01-28 16:11:05 -05:00
if [ ! -f "$f" ]; then
2017-12-13 16:38:44 -05:00
exitError "File '${f}' does not exist or is not a file!"
2017-01-28 16:11:05 -05:00
fi
2017-10-05 18:04:41 -04:00
logInfo "Signing file '${f}' using release key..."
2017-01-28 16:11:05 -05:00
gpg --output "${f}.sig" --armor --local-user "$GPG_KEY" --detach-sig "$f"
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
if [ 0 -ne $? ]; then
exitError "Signing failed!"
fi
2017-12-13 16:38:44 -05:00
2017-01-28 16:11:05 -05:00
logInfo "Creating digest for file '${f}'..."
2017-02-17 11:21:27 -05:00
local rp="$(realpath "$f")"
local bname="$(basename "$f")"
(cd "$(dirname "$rp")"; sha256sum "$bname" > "${bname}.DIGEST")
2017-01-28 16:11:05 -05:00
done
2018-09-26 16:46:59 -04:00
2017-01-28 16:11:05 -05:00
logInfo "All done!"
}
2017-12-13 16:38:44 -05:00
# -----------------------------------------------------------------------
# appsign command
# -----------------------------------------------------------------------
appsign() {
local sign_files=()
2018-07-08 19:47:35 -04:00
local key
2017-12-13 16:38:44 -05:00
while [ $# -ge 1 ]; do
local arg="$1"
case "$arg" in
-f|--files)
while [ "${2:0:1}" != "-" ] && [ $# -ge 2 ]; do
sign_files+=("$2")
shift
done ;;
2018-07-08 19:47:35 -04:00
-k|--key|-i|--identity)
key="$2"
2017-12-13 16:38:44 -05:00
shift ;;
-h|--help)
printUsage "appsign"
exit ;;
*)
logError "Unknown option '$arg'\n"
printUsage "appsign"
exit 1 ;;
esac
shift
done
2018-07-08 19:47:35 -04:00
if [ -z "${key}" ]; then
logError "Missing arguments, --key is required!\n"
printUsage "appsign"
exit 1
fi
2017-12-13 16:38:44 -05:00
if [ -z "${sign_files}" ]; then
logError "Missing arguments, --files is required!\n"
printUsage "appsign"
exit 1
fi
for f in "${sign_files[@]}"; do
2021-01-29 18:39:36 -05:00
if [ ! -e "${f}" ]; then
exitError "File '${f}' does not exist!"
2017-12-13 16:38:44 -05:00
fi
done
if [ "$(uname -s)" == "Darwin" ]; then
2019-11-09 18:08:20 -05:00
checkXcodeSetup
checkGrepCompat
2017-12-13 16:38:44 -05:00
local orig_dir="$(pwd)"
2019-11-09 18:08:20 -05:00
local real_src_dir="$(realpath "${SRC_DIR}")"
2017-12-13 16:38:44 -05:00
for f in "${sign_files[@]}"; do
if [[ ${f: -4} == '.dmg' ]]; then
logInfo "Unpacking disk image '${f}'..."
local tmp_dir="/tmp/KeePassXC_${RANDOM}"
2018-01-13 16:10:26 -05:00
mkdir -p ${tmp_dir}/mnt
2021-01-29 18:39:36 -05:00
if ! hdiutil attach -quiet -noautoopen -mountpoint ${tmp_dir}/mnt "${f}"; then
exitError "DMG mount failed!"
fi
2017-12-13 16:38:44 -05:00
cd ${tmp_dir}
2018-01-13 16:10:26 -05:00
cp -a ./mnt ./app
2017-12-13 16:38:44 -05:00
hdiutil detach -quiet ${tmp_dir}/mnt
2021-01-29 18:39:36 -05:00
local app_dir_tmp="./app/KeePassXC.app"
2017-12-13 16:38:44 -05:00
2021-01-29 18:39:36 -05:00
if [ ! -d "$app_dir_tmp" ]; then
2017-12-13 16:38:44 -05:00
cd "${orig_dir}"
exitError "Unpacking failed!"
fi
2021-01-29 18:39:36 -05:00
elif [[ ${f: -4} == '.app' ]]; then
local app_dir_tmp="$f"
else
logWarn "Skipping non-app file '${f}'..."
continue
fi
2017-12-13 16:38:44 -05:00
2021-01-29 18:39:36 -05:00
logInfo "Signing libraries and frameworks..."
if ! find "$app_dir_tmp" \( -name '*.dylib' -o -name '*.framework' \) -print0 | xargs -0 \
xcrun codesign --sign "${key}" --verbose --force --options runtime; then
cd "${orig_dir}"
exitError "Signing failed!"
fi
logInfo "Signing executables..."
if ! find "${app_dir_tmp}/Contents/MacOS" \( -type f -not -name KeePassXC \) -print0 | xargs -0 \
xcrun codesign --sign "${key}" --verbose --force --options runtime; then
cd "${orig_dir}"
exitError "Signing failed!"
fi
# Sign main executable with additional entitlements
if ! xcrun codesign --sign "${key}" --verbose --force --options runtime --entitlements \
"${real_src_dir}/share/macosx/keepassxc.entitlements" "${app_dir_tmp}/Contents/MacOS/KeePassXC"; then
cd "${orig_dir}"
exitError "Signing failed!"
fi
2017-12-13 16:38:44 -05:00
2021-01-29 18:39:36 -05:00
if [[ ${f: -4} == '.dmg' ]]; then
2018-01-13 16:10:26 -05:00
logInfo "Repacking disk image..."
hdiutil create \
-volname "KeePassXC" \
-size $((1000 * ($(du -sk ./app | cut -f1) + 5000))) \
-srcfolder ./app \
-fs HFS+ \
-fsargs "-c c=64,a=16,e=16" \
-format UDBZ \
"${tmp_dir}/$(basename "${f}")"
2019-11-09 18:08:20 -05:00
2017-12-13 16:38:44 -05:00
cd "${orig_dir}"
2018-01-13 16:10:26 -05:00
cp -f "${tmp_dir}/$(basename "${f}")" "${f}"
2017-12-13 16:38:44 -05:00
rm -Rf ${tmp_dir}
fi
2021-01-29 18:39:36 -05:00
logInfo "File '${f}' successfully signed."
2017-12-13 16:38:44 -05:00
done
elif [ "$(uname -o)" == "Msys" ]; then
2018-07-08 19:47:35 -04:00
if [[ ! -f "${key}" ]]; then
2017-12-13 16:38:44 -05:00
exitError "Key file was not found!"
fi
read -s -p "Key password: " password
echo
for f in "${sign_files[@]}"; do
2019-03-19 18:54:56 -04:00
ext=${f: -4}
if [[ $ext == ".msi" || $ext == ".exe" || $ext == ".dll" ]]; then
2017-11-27 22:46:03 -05:00
# Make sure we can find the signtool
checkSigntoolCommandExists
# osslsigncode does not succeed at signing MSI files at this time...
logInfo "Signing file '${f}' using Microsoft signtool..."
2021-01-29 18:39:36 -05:00
if ! signtool sign -f "${key}" -p "${password}" -d "KeePassXC" -td sha256 \
-fd sha256 -tr "http://timestamp.comodoca.com/authenticode" "${f}"; then
2017-11-27 22:46:03 -05:00
exitError "Signing failed!"
fi
2017-12-13 16:38:44 -05:00
else
2017-11-27 22:46:03 -05:00
logInfo "Skipping non-executable file '${f}'..."
2017-12-13 16:38:44 -05:00
fi
done
else
exitError "Unsupported platform for code signing!\n"
fi
logInfo "All done!"
}
2021-01-29 18:39:36 -05:00
# -----------------------------------------------------------------------
# notarize command
# -----------------------------------------------------------------------
notarize() {
local notarize_files=()
local ac_username
local ac_keychain="AC_PASSWORD"
while [ $# -ge 1 ]; do
local arg="$1"
case "$arg" in
-f|--files)
while [ "${2:0:1}" != "-" ] && [ $# -ge 2 ]; do
notarize_files+=("$2")
shift
done ;;
-u|--username)
ac_username="$2"
shift ;;
-c|--keychain)
ac_keychain="$2"
shift ;;
-h|--help)
printUsage "notarize"
exit ;;
*)
logError "Unknown option '$arg'\n"
printUsage "notarize"
exit 1 ;;
esac
shift
done
if [ "$(uname -s)" != "Darwin" ]; then
exitError "Notarization is only supported on macOS!"
fi
if [ -z "${notarize_files}" ]; then
logError "Missing arguments, --files is required!\n"
printUsage "notarize"
exit 1
fi
if [ "$ac_username" == "" ]; then
logError "Missing arguments, --username is required!"
printUsage "notarize"
exit 1
fi
for f in "${notarize_files[@]}"; do
if [[ ${f: -4} != '.dmg' ]]; then
logWarn "Skipping non-DMG file '${f}'..."
continue
fi
logInfo "Submitting disk image '${f}' for notarization..."
local status
status="$(xcrun altool --notarize-app \
--primary-bundle-id "org.keepassxc.keepassxc" \
--username "${ac_username}" \
--password "@keychain:${ac_keychain}" \
--file "${f}" 2> /dev/null)"
if [ 0 -ne $? ]; then
logError "Submission failed!"
exitError "Error message:\n${status}"
fi
local ticket="$(echo "${status}" | $GREP -oP "[a-f0-9-]+$")"
logInfo "Submission successful. Ticket ID: ${ticket}."
logInfo "Waiting for notarization to finish (this may take a while)..."
while true; do
echo -n "."
status="$(xcrun altool --notarization-info "${ticket}" \
--username "${ac_username}" \
--password "@keychain:${ac_keychain}" 2> /dev/null)"
if echo "$status" | $GREP -q "Status Code: 0"; then
logInfo "\nNotarization successful."
break
elif echo "$status" | $GREP -q "Status Code"; then
logError "\nNotarization failed!"
exitError "Error message:\n${status}"
fi
sleep 5
done
logInfo "Stapling ticket to disk image..."
xcrun stapler staple "${f}"
if [ 0 -ne $? ]; then
exitError "Stapling failed!"
fi
logInfo "Disk image successfully notarized."
done
}
2017-01-28 16:11:05 -05:00
# -----------------------------------------------------------------------
# parse global command line
# -----------------------------------------------------------------------
MODE="$1"
shift
if [ "" == "$MODE" ]; then
logError "Missing arguments!\n"
printUsage
exit 1
elif [ "help" == "$MODE" ]; then
printUsage "$1"
exit
2018-09-26 16:46:59 -04:00
elif [ "check" == "$MODE" ] || [ "merge" == "$MODE" ] || [ "build" == "$MODE" ] \
2021-01-29 18:39:36 -05:00
|| [ "gpgsign" == "$MODE" ] || [ "appsign" == "$MODE" ]|| [ "notarize" == "$MODE" ] \
|| [ "appimage" == "$MODE" ]; then
2017-12-13 16:38:44 -05:00
${MODE} "$@"
2017-01-28 17:17:48 -05:00
else
printUsage "$MODE"
2017-01-28 16:11:05 -05:00
fi