mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-30 09:46:30 -05:00
a4c6529d60
AppImage recipes are the legacy method for building AppImages, whereas linuxdeploy is meant to replace them. It takes care of finding and deploying all needed libraries and Qt plugins automagically, but it's still under heavy development, so some manual work using appimagetool and a few fixes are required to make it work for KeePassXC. This patch moves building of AppImages directly into the release-tool, both as a separate module (release-tool appimage) as well as a flag (--appimage) for the build module. The release and CI Dockerfiles were updated accordingly to support the new build process. The release Dockerfile also received a Qt update to version 5.10.1. In theory, it is now possible to use release-tool appsign for embedding PGP signatures into AppImages, but it fails in practice due to ELF header size limitations.
1240 lines
40 KiB
Bash
Executable File
1240 lines
40 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# 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/>.
|
|
|
|
printf "\e[1m\e[32mKeePassXC\e[0m Release Preparation Helper\n"
|
|
printf "Copyright (C) 2017 KeePassXC Team <https://keepassxc.org/>\n\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=""
|
|
CPACK_GENERATORS="NSIS;ZIP"
|
|
COMPILER="g++"
|
|
MAKE_OPTIONS="-j8"
|
|
BUILD_PLUGINS="all"
|
|
INSTALL_PREFIX="/usr/local"
|
|
ORIG_BRANCH=""
|
|
ORIG_CWD="$(pwd)"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# helper functions
|
|
# -----------------------------------------------------------------------
|
|
printUsage() {
|
|
local cmd
|
|
if [ "" == "$1" ] || [ "help" == "$1" ]; then
|
|
cmd="COMMAND"
|
|
elif [ "check" == "$1" ] || [ "merge" == "$1" ] || [ "build" == "$1" ] \
|
|
|| [ "gpgsign" == "$1" ] || [ "appsign" == "$1" ] || [ "appimage" == "$1" ]; then
|
|
cmd="$1"
|
|
else
|
|
logError "Unknown command: '$1'\n"
|
|
cmd="COMMAND"
|
|
fi
|
|
|
|
printf "\e[1mUsage:\e[0m $(basename $0) $cmd [options]\n"
|
|
|
|
if [ "COMMAND" == "$cmd" ]; then
|
|
cat << EOF
|
|
|
|
Commands:
|
|
check Perform a dry-run check, nothing is changed
|
|
merge Merge release branch into main branch and create release tags
|
|
build Build and package binary release from sources
|
|
gpgsign Sign previously compiled release packages with GPG
|
|
appsign Sign binaries with code signing certificates on Windows and macOS
|
|
help Show help for the given command
|
|
EOF
|
|
elif [ "merge" == "$cmd" ]; then
|
|
cat << EOF
|
|
|
|
Merge release branch into main branch and create release tags
|
|
|
|
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, --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
|
|
|
|
Build and package binary release from sources
|
|
|
|
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
|
|
--snapcraft Create and use docker image to build snapcraft distribution.
|
|
This option has no effect if --docker-image is not set.
|
|
--appimage Build a Linux AppImage after compilation.
|
|
If this option is set, --install-prefix has no effect
|
|
--appsign Perform platform specific App Signing before packaging
|
|
-k, --key Specify the App Signing Key/Identity
|
|
-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}')
|
|
-g, --generators Additional CPack generators (default: '${CPACK_GENERATORS}')
|
|
-i, --install-prefix Install prefix (default: '${INSTALL_PREFIX}')
|
|
-p, --plugins Space-separated list of plugins to build
|
|
(default: ${BUILD_PLUGINS})
|
|
--snapshot Don't checkout the release tag
|
|
-n, --no-source-tarball Don't build source tarball
|
|
-h, --help Show this help
|
|
EOF
|
|
elif [ "gpgsign" == "$cmd" ]; then
|
|
cat << EOF
|
|
|
|
Sign previously compiled release packages with GPG
|
|
|
|
Options:
|
|
-f, --files Files to sign (required)
|
|
-k, --key GPG key used to sign the files (default: '${GPG_KEY}')
|
|
-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)
|
|
-k, --key Signing Key or Apple Developer ID
|
|
-h, --help Show this help
|
|
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
|
|
EOF
|
|
fi
|
|
}
|
|
|
|
logInfo() {
|
|
printf "\e[1m[ \e[34mINFO\e[39m ]\e[0m $1\n"
|
|
}
|
|
|
|
logError() {
|
|
printf "\e[1m[ \e[31mERROR\e[39m ]\e[0m $1\n" >&2
|
|
}
|
|
|
|
init() {
|
|
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
|
|
|
|
if [ "" == "$SOURCE_BRANCH" ]; then
|
|
SOURCE_BRANCH="release/${RELEASE_NAME}"
|
|
fi
|
|
|
|
ORIG_CWD="$(pwd)"
|
|
SRC_DIR="$(realpath "$SRC_DIR")"
|
|
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..."
|
|
}
|
|
|
|
cmdExists() {
|
|
command -v "$1" &> /dev/null
|
|
}
|
|
|
|
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:]')"
|
|
local major_num="$(echo ${RELEASE_NAME} | cut -f1 -d.)"
|
|
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
|
|
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
|
|
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
|
|
exitError "${app_name_upper}_VERSION_PATCH not updated to '${patch_num}' 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' has not been updated to the '${RELEASE_NAME}' release!"
|
|
fi
|
|
}
|
|
|
|
checkAppStreamInfo() {
|
|
if [ ! -f share/linux/org.keepassxc.KeePassXC.appdata.xml ]; then
|
|
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
|
|
exitError "'share/linux/org.keepassxc.KeePassXC.appdata.xml' has not been updated to the '${RELEASE_NAME}' release!"
|
|
fi
|
|
}
|
|
|
|
checkSnapcraft() {
|
|
if [ ! -f snapcraft.yaml ]; then
|
|
echo "No snapcraft file found!"
|
|
return
|
|
fi
|
|
|
|
grep -qPzo "version: ${RELEASE_NAME}" snapcraft.yaml
|
|
if [ $? -ne 0 ]; then
|
|
exitError "'snapcraft.yaml' has not been updated to the '${RELEASE_NAME}' release!"
|
|
fi
|
|
}
|
|
|
|
checkTransifexCommandExists() {
|
|
if ! cmdExists tx; then
|
|
exitError "Transifex tool 'tx' not installed! Please install it using 'pip install transifex-client'."
|
|
fi
|
|
}
|
|
|
|
checkOsslsigncodeCommandExists() {
|
|
if ! cmdExists osslsigncode; then
|
|
exitError "osslsigncode command not found on the PATH! Please install it using 'pacman -S mingw-w64-osslsigncode'."
|
|
fi
|
|
}
|
|
|
|
checkSigntoolCommandExists() {
|
|
if ! cmdExists signtool; then
|
|
exitError "signtool command not found on the PATH! Add the Windows SDK binary folder to your PATH."
|
|
fi
|
|
}
|
|
|
|
checkCodesignCommandExists() {
|
|
if !cmdExists codesign; then
|
|
exitError "codesign command not found on the PATH! Please check that you have correctly installed Xcode."
|
|
fi
|
|
}
|
|
|
|
checkQt5LUpdateExists() {
|
|
if cmdExists lupdate && ! $(lupdate -version | grep -q "lupdate version 5\."); then
|
|
if ! cmdExists lupdate-qt5; then
|
|
exitError "Qt Linguist tool (lupdate-qt5) is not installed! Please install using 'apt install qttools5-dev-tools'"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
performChecks() {
|
|
logInfo "Performing basic checks..."
|
|
|
|
checkSourceDirExists
|
|
|
|
logInfo "Changing to source directory..."
|
|
cd "${SRC_DIR}"
|
|
|
|
logInfo "Validating toolset and repository..."
|
|
|
|
checkTransifexCommandExists
|
|
checkQt5LUpdateExists
|
|
checkGitRepository
|
|
checkReleaseDoesNotExist
|
|
checkWorkingTreeClean
|
|
checkSourceBranchExists
|
|
checkTargetBranchExists
|
|
|
|
logInfo "Checking out '${SOURCE_BRANCH}'..."
|
|
git checkout "$SOURCE_BRANCH"
|
|
|
|
logInfo "Attempting to find '${RELEASE_NAME}' in various files..."
|
|
|
|
checkVersionInCMake
|
|
checkChangeLog
|
|
checkAppStreamInfo
|
|
checkSnapcraft
|
|
|
|
logInfo "\e[1m\e[32mAll checks passed!\e[0m"
|
|
}
|
|
|
|
# re-implement realpath for OS X (thanks mschrag)
|
|
# https://superuser.com/questions/205127/
|
|
if ! cmdExists realpath; then
|
|
realpath() {
|
|
pushd . > /dev/null
|
|
if [ -d "$1" ]; then
|
|
cd "$1"
|
|
dirs -l +0
|
|
else
|
|
cd "$(dirname "$1")"
|
|
cur_dir=$(dirs -l +0)
|
|
|
|
if [ "$cur_dir" == "/" ]; then
|
|
echo "$cur_dir$(basename "$1")"
|
|
else
|
|
echo "$cur_dir/$(basename "$1")"
|
|
fi
|
|
fi
|
|
popd > /dev/null
|
|
}
|
|
fi
|
|
|
|
|
|
trap exitTrap SIGINT SIGTERM
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 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."
|
|
}
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 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 ;;
|
|
|
|
-k|--key|-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
|
|
|
|
init
|
|
|
|
performChecks
|
|
|
|
logInfo "Updating language files..."
|
|
./share/translations/update.sh update
|
|
./share/translations/update.sh pull
|
|
if [ 0 -ne $? ]; then
|
|
exitError "Updating translations failed!"
|
|
fi
|
|
git diff-index --quiet HEAD --
|
|
if [ $? -ne 0 ]; then
|
|
git add ./share/translations/*
|
|
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
|
|
|
|
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}"
|
|
|
|
logInfo "Checking out target branch '${TARGET_BRANCH}'..."
|
|
git checkout "$TARGET_BRANCH"
|
|
|
|
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
|
|
|
|
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."
|
|
}
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 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
|
|
logError "AppDir does not exist, please create one with 'make install'!\n"
|
|
exit 1
|
|
elif [ -e "${appdir}/AppRun" ]; then
|
|
logError "AppDir has already been run through linuxdeploy, please create a fresh AppDir with 'make install'.\n"
|
|
exit 1
|
|
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}"
|
|
curl -L "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" > "$linuxdeploy"
|
|
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}"
|
|
curl -L "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage" > "$linuxdeploy_plugin_qt"
|
|
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}"
|
|
curl -L "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" > "$appimagetool"
|
|
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)"
|
|
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={}")"
|
|
|
|
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} \
|
|
--library=$(ldconfig -p | grep x86-64 | grep -oP '/[^\s]+/libgpg-error\.so\.\d+$' | head -n1)
|
|
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
|
|
|
|
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"
|
|
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
|
|
"$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}"
|
|
|
|
logInfo "Cleaning up temporary files..."
|
|
${linuxdeploy_cleanup}
|
|
${linuxdeploy_plugin_qt_cleanup}
|
|
${appimagetool_cleanup}
|
|
rm -f "${out_real}/KeePassXC-AppRun"
|
|
}
|
|
|
|
# -----------------------------------------------------------------------
|
|
# build command
|
|
# -----------------------------------------------------------------------
|
|
build() {
|
|
local build_source_tarball=true
|
|
local build_snapshot=false
|
|
local build_snapcraft=false
|
|
local build_appimage=false
|
|
local build_generators=""
|
|
local build_appsign=false
|
|
local build_key=""
|
|
|
|
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 ;;
|
|
|
|
--appsign)
|
|
build_appsign=true ;;
|
|
|
|
-k|--key)
|
|
build_key="$2"
|
|
shift ;;
|
|
|
|
--snapcraft)
|
|
build_snapcraft=true ;;
|
|
|
|
--appimage)
|
|
build_appimage=true ;;
|
|
|
|
-c|--cmake-options)
|
|
CMAKE_OPTIONS="$2"
|
|
shift ;;
|
|
|
|
--compiler)
|
|
COMPILER="$2"
|
|
shift ;;
|
|
|
|
-m|--make-options)
|
|
MAKE_OPTIONS="$2"
|
|
shift ;;
|
|
|
|
-g|--generators)
|
|
build_generators="$2"
|
|
shift ;;
|
|
|
|
-i|--install-prefix)
|
|
INSTALL_PREFIX="$2"
|
|
shift ;;
|
|
|
|
-p|--plugins)
|
|
BUILD_PLUGINS="$2"
|
|
shift ;;
|
|
|
|
-n|--no-source-tarball)
|
|
build_source_tarball=false ;;
|
|
|
|
--snapshot)
|
|
build_snapshot=true ;;
|
|
|
|
-h|--help)
|
|
printUsage "build"
|
|
exit ;;
|
|
|
|
*)
|
|
logError "Unknown option '$arg'\n"
|
|
printUsage "build"
|
|
exit 1 ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
init
|
|
|
|
OUTPUT_DIR="$(realpath "$OUTPUT_DIR")"
|
|
|
|
if ${build_snapshot}; then
|
|
TAG_NAME="HEAD"
|
|
local branch=`git rev-parse --abbrev-ref HEAD`
|
|
logInfo "Using current branch ${branch} to build..."
|
|
RELEASE_NAME="${RELEASE_NAME}-snapshot"
|
|
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_BUILD_TYPE=Snapshot"
|
|
else
|
|
checkWorkingTreeClean
|
|
|
|
if $(echo "$TAG_NAME" | grep -qP "\-(alpha|beta)\\d+\$"); then
|
|
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
|
|
git checkout "$TAG_NAME"
|
|
fi
|
|
|
|
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:]')"
|
|
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}"
|
|
|
|
if ! ${build_snapshot}; then
|
|
# add .version file to tar
|
|
mkdir "${prefix}"
|
|
echo -n ${RELEASE_NAME} > "${prefix}/.version"
|
|
tar --append --file="${OUTPUT_DIR}/${tarball_name}" "${prefix}/.version"
|
|
rm "${prefix}/.version"
|
|
rmdir "${prefix}" 2> /dev/null
|
|
fi
|
|
|
|
xz -6 "${OUTPUT_DIR}/${tarball_name}"
|
|
fi
|
|
|
|
if ! ${build_snapshot} && [ -e "${OUTPUT_DIR}/build-release" ]; then
|
|
logInfo "Cleaning existing build directory..."
|
|
rm -r "${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"
|
|
|
|
logInfo "Configuring sources..."
|
|
for p in ${BUILD_PLUGINS}; do
|
|
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DWITH_XC_$(echo $p | tr '[:lower:]' '[:upper:]')=On"
|
|
done
|
|
if [ "$(uname -o)" == "GNU/Linux" ] && ${build_appimage}; then
|
|
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_DIST_TYPE=AppImage"
|
|
# linuxdeploy requires /usr as install prefix
|
|
INSTALL_PREFIX="/usr"
|
|
fi
|
|
|
|
if [ "$COMPILER" == "g++" ]; then
|
|
export CC=gcc
|
|
elif [ "$COMPILER" == "clang++" ]; then
|
|
export CC=clang
|
|
fi
|
|
export CXX="$COMPILER"
|
|
|
|
if [ "" == "$DOCKER_IMAGE" ]; then
|
|
if [ "$(uname -s)" == "Darwin" ]; then
|
|
# Building on macOS
|
|
export MACOSX_DEPLOYMENT_TARGET=10.10
|
|
|
|
logInfo "Configuring build..."
|
|
cmake -DCMAKE_BUILD_TYPE=Release \
|
|
-DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \
|
|
-DCMAKE_PREFIX_PATH="/usr/local/opt/qt/lib/cmake" \
|
|
${CMAKE_OPTIONS} "$SRC_DIR"
|
|
|
|
logInfo "Compiling and packaging sources..."
|
|
make ${MAKE_OPTIONS} package
|
|
|
|
# Appsign the executables if desired
|
|
if [[ ${build_appsign} && ! -z ${build_key} ]]; then
|
|
logInfo "Signing executable files"
|
|
appsign "-f" "./${APP_NAME}-${RELEASE_NAME}.dmg" "-k" "${build_key}"
|
|
fi
|
|
|
|
mv "./${APP_NAME}-${RELEASE_NAME}.dmg" ../
|
|
elif [ "$(uname -o)" == "Msys" ]; then
|
|
# Building on Windows with Msys2
|
|
logInfo "Configuring build..."
|
|
cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=Off -G"MSYS Makefiles" \
|
|
-DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" ${CMAKE_OPTIONS} "$SRC_DIR"
|
|
|
|
logInfo "Compiling and packaging sources..."
|
|
mingw32-make ${MAKE_OPTIONS} preinstall
|
|
|
|
# Appsign the executables if desired
|
|
if [[ ${build_appsign} && ! -z ${build_key} ]]; then
|
|
logInfo "Signing executable files"
|
|
appsign "-f" $(find src | grep '\.exe') "-k" "${build_key}"
|
|
fi
|
|
|
|
# Call cpack directly instead of calling make package.
|
|
# This is important because we want to build the MSI when making a
|
|
# release.
|
|
cpack -G "${CPACK_GENERATORS};${build_generators}"
|
|
|
|
# Inject the portable config into the zip build and rename
|
|
for filename in ${APP_NAME}-*.zip; do
|
|
logInfo "Creating portable zip file"
|
|
local folder=$(echo ${filename} | sed -r 's/(.*)\.zip/\1/')
|
|
python -c 'import zipfile,sys ; zipfile.ZipFile(sys.argv[1],"a").write(sys.argv[2],sys.argv[3])' \
|
|
${filename} ${SRC_DIR}/share/keepassxc.ini ${folder}/keepassxc.ini
|
|
mv ${filename} ${folder}-portable.zip
|
|
done
|
|
|
|
mv "${APP_NAME}-"*.* ../
|
|
else
|
|
mkdir -p "${OUTPUT_DIR}/KeePassXC.AppDir"
|
|
|
|
# Building on Linux without Docker container
|
|
logInfo "Configuring build..."
|
|
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}/KeePassXC.AppDir" install/strip
|
|
fi
|
|
else
|
|
if ${build_snapcraft}; then
|
|
logInfo "Building snapcraft docker image..."
|
|
|
|
sudo docker image build -t "$DOCKER_IMAGE" "$(realpath "$SRC_DIR")/ci/snapcraft"
|
|
|
|
logInfo "Launching Docker contain to compile snapcraft..."
|
|
|
|
sudo docker run --name "$DOCKER_CONTAINER_NAME" --rm \
|
|
-v "$(realpath "$SRC_DIR"):/keepassxc" -w "/keepassxc" \
|
|
"$DOCKER_IMAGE" snapcraft
|
|
else
|
|
mkdir -p "${OUTPUT_DIR}/KeePassXC.AppDir"
|
|
|
|
logInfo "Launching Docker container to compile sources..."
|
|
|
|
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 && \
|
|
cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=Off ${CMAKE_OPTIONS} \
|
|
-DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} /keepassxc/src && \
|
|
make ${MAKE_OPTIONS} && make DESTDIR=/keepassxc/out/KeePassXC.AppDir install/strip"
|
|
fi
|
|
|
|
if [ 0 -ne $? ]; then
|
|
exitError "Docker build failed!"
|
|
fi
|
|
|
|
logInfo "Build finished, Docker container terminated."
|
|
fi
|
|
|
|
if [ "$(uname -o)" == "GNU/Linux" ] && ${build_appimage}; then
|
|
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
|
|
|
|
cleanup
|
|
|
|
logInfo "All done!"
|
|
}
|
|
|
|
# -----------------------------------------------------------------------
|
|
# gpgsign command
|
|
# -----------------------------------------------------------------------
|
|
gpgsign() {
|
|
local 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 ;;
|
|
|
|
-k|--key|-g|--gpg-key)
|
|
GPG_KEY="$2"
|
|
shift ;;
|
|
|
|
-h|--help)
|
|
printUsage "gpgsign"
|
|
exit ;;
|
|
|
|
*)
|
|
logError "Unknown option '$arg'\n"
|
|
printUsage "gpgsign"
|
|
exit 1 ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [ -z "${sign_files}" ]; then
|
|
logError "Missing arguments, --files is required!\n"
|
|
printUsage "gpgsign"
|
|
exit 1
|
|
fi
|
|
|
|
for f in "${sign_files[@]}"; do
|
|
if [ ! -f "$f" ]; then
|
|
exitError "File '${f}' does not exist or is not a file!"
|
|
fi
|
|
|
|
logInfo "Signing file '${f}' using release key..."
|
|
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}'..."
|
|
local rp="$(realpath "$f")"
|
|
local bname="$(basename "$f")"
|
|
(cd "$(dirname "$rp")"; sha256sum "$bname" > "${bname}.DIGEST")
|
|
done
|
|
|
|
logInfo "All done!"
|
|
}
|
|
|
|
# -----------------------------------------------------------------------
|
|
# appsign command
|
|
# -----------------------------------------------------------------------
|
|
appsign() {
|
|
local sign_files=()
|
|
local key
|
|
|
|
while [ $# -ge 1 ]; do
|
|
local arg="$1"
|
|
case "$arg" in
|
|
-f|--files)
|
|
while [ "${2:0:1}" != "-" ] && [ $# -ge 2 ]; do
|
|
sign_files+=("$2")
|
|
shift
|
|
done ;;
|
|
|
|
-k|--key|-i|--identity)
|
|
key="$2"
|
|
shift ;;
|
|
|
|
-h|--help)
|
|
printUsage "appsign"
|
|
exit ;;
|
|
|
|
*)
|
|
logError "Unknown option '$arg'\n"
|
|
printUsage "appsign"
|
|
exit 1 ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [ -z "${key}" ]; then
|
|
logError "Missing arguments, --key is required!\n"
|
|
printUsage "appsign"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "${sign_files}" ]; then
|
|
logError "Missing arguments, --files is required!\n"
|
|
printUsage "appsign"
|
|
exit 1
|
|
fi
|
|
|
|
for f in "${sign_files[@]}"; do
|
|
if [ ! -f "${f}" ]; then
|
|
exitError "File '${f}' does not exist or is not a file!"
|
|
fi
|
|
done
|
|
|
|
if [ "$(uname -s)" == "Darwin" ]; then
|
|
checkCodesignCommandExists
|
|
|
|
local orig_dir="$(pwd)"
|
|
for f in "${sign_files[@]}"; do
|
|
if [[ ${f: -4} == '.dmg' ]]; then
|
|
logInfo "Unpacking disk image '${f}'..."
|
|
local tmp_dir="/tmp/KeePassXC_${RANDOM}"
|
|
mkdir -p ${tmp_dir}/mnt
|
|
hdiutil attach -quiet -noautoopen -mountpoint ${tmp_dir}/mnt "${f}"
|
|
cd ${tmp_dir}
|
|
cp -a ./mnt ./app
|
|
hdiutil detach -quiet ${tmp_dir}/mnt
|
|
|
|
if [ ! -d ./app/KeePassXC.app ]; then
|
|
cd "${orig_dir}"
|
|
exitError "Unpacking failed!"
|
|
fi
|
|
|
|
logInfo "Signing app using codesign..."
|
|
codesign --sign "${key}" --verbose --deep ./app/KeePassXC.app
|
|
|
|
if [ 0 -ne $? ]; then
|
|
cd "${orig_dir}"
|
|
exitError "Signing failed!"
|
|
fi
|
|
|
|
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}")"
|
|
cd "${orig_dir}"
|
|
cp -f "${tmp_dir}/$(basename "${f}")" "${f}"
|
|
rm -Rf ${tmp_dir}
|
|
else
|
|
logInfo "Skipping non-DMG file '${f}'..."
|
|
fi
|
|
done
|
|
|
|
elif [ "$(uname -o)" == "Msys" ]; then
|
|
checkOsslsigncodeCommandExists
|
|
|
|
if [[ ! -f "${key}" ]]; then
|
|
exitError "Key file was not found!"
|
|
fi
|
|
|
|
read -s -p "Key password: " password
|
|
echo
|
|
|
|
for f in "${sign_files[@]}"; do
|
|
if [[ ${f: -4} == ".exe" ]]; then
|
|
logInfo "Signing file '${f}' using osslsigncode..."
|
|
# output a signed exe; we have to use a different name due to osslsigntool limitations
|
|
osslsigncode sign -pkcs12 "${key}" -pass "${password}" -n "KeePassXC" \
|
|
-t "http://timestamp.comodoca.com/authenticode" -in "${f}" -out "${f}.signed"
|
|
|
|
if [ 0 -ne $? ]; then
|
|
rm -f "${f}.signed"
|
|
exitError "Signing failed!"
|
|
fi
|
|
|
|
# overwrite the original exe with the signed exe
|
|
mv -f "${f}.signed" "${f}"
|
|
elif [[ ${f: -4} == ".msi" ]]; then
|
|
# 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..."
|
|
signtool sign -f "${key}" -p "${password}" -d "KeePassXC" \
|
|
-t "http://timestamp.comodoca.com/authenticode" "${f}"
|
|
|
|
if [ 0 -ne $? ]; then
|
|
exitError "Signing failed!"
|
|
fi
|
|
else
|
|
logInfo "Skipping non-executable file '${f}'..."
|
|
fi
|
|
done
|
|
|
|
else
|
|
exitError "Unsupported platform for code signing!\n"
|
|
fi
|
|
|
|
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 [ "check" == "$MODE" ] || [ "merge" == "$MODE" ] || [ "build" == "$MODE" ] \
|
|
|| [ "gpgsign" == "$MODE" ] || [ "appsign" == "$MODE" ] || [ "appimage" == "$MODE" ]; then
|
|
${MODE} "$@"
|
|
else
|
|
printUsage "$MODE"
|
|
fi
|