12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472 |
- #!/usr/bin/env bash
- #
- # KeePassXC Release Preparation Helper
- # 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
- # 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) 2021 KeePassXC Team <https://keepassxc.org/>\n\n"
- set -eE -o pipefail
- if [ "$(uname -s)" == "Linux" ]; then
- OS_LINUX="1"
- elif [ "$(uname -s)" == "Darwin" ]; then
- OS_MACOS="1"
- elif [ "$(uname -o)" == "Msys" ]; then
- OS_WINDOWS="1"
- fi
- # -----------------------------------------------------------------------
- # global default values
- # -----------------------------------------------------------------------
- RELEASE_NAME=""
- APP_NAME="KeePassXC"
- SRC_DIR="."
- GPG_KEY="CFB4C2166397D0D2"
- GPG_GIT_KEY=""
- OUTPUT_DIR="release"
- SOURCE_BRANCH=""
- TAG_NAME=""
- DOCKER_IMAGE=""
- DOCKER_CONTAINER_NAME="keepassxc-build-container"
- CMAKE_GENERATOR="Unix Makefiles"
- CMAKE_OPTIONS=""
- CPACK_GENERATORS="WIX;ZIP"
- COMPILER="g++"
- MAKE_OPTIONS="-j$(getconf _NPROCESSORS_ONLN)"
- BUILD_PLUGINS="all"
- INSTALL_PREFIX="/usr/local"
- ORIG_BRANCH=""
- ORIG_CWD="$(pwd)"
- MACOSX_DEPLOYMENT_TARGET=10.15
- TIMESTAMP_SERVER="http://timestamp.sectigo.com"
- # -----------------------------------------------------------------------
- # helper functions
- # -----------------------------------------------------------------------
- printUsage() {
- local cmd
- 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
- 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
- notarize Submit macOS application DMG for notarization
- help Show help for the given command
- i18n Update translation files and pull from or push to Transifex
- 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')
- -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
- --timestamp Explicitly set the timestamp server to use for appsign (default: '${TIMESTAMP_SERVER}')
- --vcpkg Specify VCPKG toolchain file (example: ~/vcpkg/scripts/buildsystems/vcpkg.cmake)
- -k, --key Specify the App Signing Key/Identity
- --cmake-generator Override the default CMake generator (Default: Ninja)
- -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, -i, --identity
- Signing Key or Apple Developer ID (required)
- --timestamp Explicitly set the timestamp server to use for appsign (default: '${TIMESTAMP_SERVER}')
- -u, --username Apple username for notarization (required on macOS)
- -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)
- -c, --keychain Apple keychain entry name storing the notarization
- app password (default: 'AC_PASSWORD')
- -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
- elif [ "i18n" == "$cmd" ]; then
- cat << EOF
- Update translation files and pull from or push to Transifex
- Subcommands:
- tx-push Push source translation file to Transifex
- tx-pull Pull updated translations from Transifex
- lupdate Update source translation file from C++ sources
- EOF
- fi
- }
- logInfo() {
- printf "\e[1m[ \e[34mINFO\e[39m ]\e[0m $1\n"
- }
- logWarn() {
- printf "\e[1m[ \e[33mWARNING\e[39m ]\e[0m $1\n"
- }
- logError() {
- printf "\e[1m[ \e[31mERROR\e[39m ]\e[0m $1\n" >&2
- }
- init() {
- if [ -z "$RELEASE_NAME" ]; then
- logError "Missing arguments, --version is required!\n"
- printUsage "check"
- exit 1
- fi
- if [ -z "$TAG_NAME" ]; then
- TAG_NAME="$RELEASE_NAME"
- fi
- if [ -z "$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() {
- cleanup
- logError "$1"
- exit 1
- }
- 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.md ]; then
- exitError "Source directory is not a valid Git repository!"
- fi
- }
- checkReleaseDoesNotExist() {
- if [ $(git tag -l $TAG_NAME) ]; then
- exitError "Release '$RELEASE_NAME' (tag: '$TAG_NAME') already exists!"
- fi
- }
- checkWorkingTreeClean() {
- if ! git diff-index --quiet HEAD --; then
- exitError "Current working tree is not clean! Please commit or unstage any changes."
- fi
- }
- checkSourceBranchExists() {
- if ! git rev-parse "$SOURCE_BRANCH" > /dev/null 2>&1; then
- exitError "Source branch '$SOURCE_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-)"
- 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
- 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
- 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
- }
- checkChangeLog() {
- if [ ! -f CHANGELOG.md ]; then
- exitError "No CHANGELOG file found!"
- fi
- if ! grep -qEzo "## ${RELEASE_NAME} \([0-9]{4}-[0-9]{2}-[0-9]{2}\)" CHANGELOG.md; then
- exitError "'CHANGELOG.md' 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
- if ! grep -qEzo "<release version=\"${RELEASE_NAME}\" date=\"[0-9]{4}-[0-9]{2}-[0-9]{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
- }
- checkTransifexCommandExists() {
- if ! cmdExists tx; then
- exitError "Transifex tool 'tx' not installed! Please install it using 'pip install transifex-client'."
- fi
- }
- checkSigntoolCommandExists() {
- if ! cmdExists signtool; then
- exitError "signtool command not found on the PATH! Add the Windows SDK binary folder to your PATH."
- fi
- }
- 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."
- 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
- logInfo "Checking out '${SOURCE_BRANCH}'..."
- git checkout "$SOURCE_BRANCH" > /dev/null 2>&1
- logInfo "Attempting to find '${RELEASE_NAME}' in various files..."
- checkVersionInCMake
- checkChangeLog
- checkAppStreamInfo
- 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 'exitError "Exited upon user request."' SIGINT SIGTERM
- trap 'exitError "Error occurred!"' ERR
- # -----------------------------------------------------------------------
- # 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 ;;
- --timestamp)
- TIMESTAMP_SERVER="$2"
- shift ;;
- -r|--release-branch)
- SOURCE_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
- # Update translations
- i18n lupdate
- i18n tx-pull
- if [ 0 -ne $? ]; then
- exitError "Updating translations failed!"
- fi
- if ! git diff-index --quiet HEAD --; then
- git add -A ./share/translations/
- logInfo "Committing changes..."
- if [ -z "$GPG_GIT_KEY" ]; then
- git commit -m "Update translations"
- else
- git commit -m "Update translations" -S"$GPG_GIT_KEY"
- fi
- fi
- local flags="-Pzo"
- if [ -n "$OS_MACOS" ]; then
- flags="-Ezo"
- fi
- CHANGELOG=$(grep ${flags} "## ${RELEASE_NAME} \([0-9]{4}-[0-9]{2}-[0-9]{2}\)\n\n(.|\n)+?\n\n## " CHANGELOG.md \
- | tail -n+3 | sed '$d' | sed 's/^### //')
- COMMIT_MSG="Release ${RELEASE_NAME}"
- logInfo "Creating tag '${TAG_NAME}'..."
- 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"
- fi
- logInfo "Advancing 'latest' tag..."
- if [ -z "$GPG_GIT_KEY" ]; then
- git tag -sf -a "latest" -m "Latest stable release"
- else
- git tag -sf -u "$GPG_GIT_KEY" -a "latest" -m "Latest stable release"
- fi
- cleanup
- logInfo "All done!"
- logInfo "Don't forget to 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
- exitError "AppDir does not exist, please create one with 'make install'!"
- elif [ -e "${appdir}/AppRun" ]; then
- exitError "AppDir has already been run through linuxdeploy, please create a fresh AppDir with 'make install'."
- fi
- appdir="$(realpath "$appdir")"
- local out="${OUTPUT_DIR}"
- if [ -z "$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 -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
- logInfo "Downloading linuxdeploy..."
- linuxdeploy="./linuxdeploy"
- linuxdeploy_cleanup="rm -f ${linuxdeploy}"
- if ! curl -Lf "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" > "$linuxdeploy"; then
- exitError "linuxdeploy download failed."
- fi
- 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}"
- 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
- 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}"
- if ! curl -Lf "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" > "$appimagetool"; then
- exitError "appimagetool download failed."
- fi
- 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" -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 [ -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}
- else
- docker run --name "$DOCKER_CONTAINER_NAME" --rm \
- --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 "${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
- exitError "AppDir deployment failed."
- 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"
- echo "X-AppImage-Version=${RELEASE_NAME}" >> "$desktop_file"
- 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
- 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
- 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=""
- local build_vcpkg=""
- 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 ;;
- --timestamp)
- TIMESTAMP_SERVER="$2"
- shift ;;
- -k|--key)
- build_key="$2"
- shift ;;
- --snapcraft)
- build_snapcraft=true ;;
- --appimage)
- build_appimage=true ;;
- --cmake-generator)
- CMAKE_GENERATOR="$2"
- shift ;;
- -c|--cmake-options)
- CMAKE_OPTIONS="$2"
- shift ;;
- --compiler)
- COMPILER="$2"
- shift ;;
- --vcpkg)
- build_vcpkg="$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
- # Resolve appsign key to absolute path if under Windows
- if [[ "${build_key}" && -n "$OS_WINDOWS" ]]; then
- build_key="$(realpath "${build_key}")"
- fi
- if [[ -f ${build_vcpkg} ]]; then
- CMAKE_OPTIONS="${CMAKE_OPTIONS} -DCMAKE_TOOLCHAIN_FILE=${build_vcpkg} -DX_VCPKG_APPLOCAL_DEPS_INSTALL=ON"
- fi
- 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 -DOVERRIDE_VERSION=${RELEASE_NAME}"
- else
- checkWorkingTreeClean
- if echo "$TAG_NAME" | grep -qE '\-(alpha|beta)[0-9]+$'; 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
- if ! git checkout "$TAG_NAME" > /dev/null 2>&1; then
- exitError "Failed to check out target branch."
- fi
- fi
- if ! ${build_snapshot} && [ -d "$OUTPUT_DIR" ]; then
- exitError "Output dir '${OUTPUT_DIR}' already exists."
- fi
- logInfo "Creating output directory..."
- if ! mkdir -p "$OUTPUT_DIR"; then
- exitError "Failed to create output directory!"
- fi
- OUTPUT_DIR="$(realpath "$OUTPUT_DIR")"
- 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}"
- # 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
- local xz="xz"
- if ! cmdExists xz; then
- logWarn "xz not installed. Falling back to bz2..."
- xz="bzip2"
- fi
- $xz -6 -f "${OUTPUT_DIR}/${tarball_name}"
- 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 [ -n "$OS_LINUX" ] && ${build_appimage}; then
- CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_DIST_TYPE=AppImage"
- # linuxdeploy requires /usr as install prefix
- INSTALL_PREFIX="/usr"
- fi
- if [ -n "$OS_MACOS" ]; then
- type brew &> /dev/null 2>&1
- if [ $? -eq 0 ]; then
- INSTALL_PREFIX=$(brew --prefix)
- fi
- fi
- # Do not build tests cases
- CMAKE_OPTIONS="${CMAKE_OPTIONS} -DWITH_TESTS=OFF"
- if [ "$COMPILER" == "g++" ]; then
- export CC=gcc
- elif [ "$COMPILER" == "clang++" ]; then
- export CC=clang
- else
- export CC="$COMPILER"
- fi
- export CXX="$COMPILER"
- if [ -z "$DOCKER_IMAGE" ]; then
- if [ -n "$OS_MACOS" ]; then
- # Building on macOS
- export MACOSX_DEPLOYMENT_TARGET
- logInfo "Configuring build..."
- cmake -G "${CMAKE_GENERATOR}" -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="$(uname -m)" \
- -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" ${CMAKE_OPTIONS} "$SRC_DIR"
- logInfo "Compiling and packaging sources..."
- cmake --build . -- ${MAKE_OPTIONS}
- cpack -G "DragNDrop"
- # Appsign the executables if desired
- if ${build_appsign}; then
- logInfo "Signing executable files"
- appsign "-f" "./${APP_NAME}-${RELEASE_NAME}.dmg" "-k" "${build_key}"
- fi
- mv "./${APP_NAME}-${RELEASE_NAME}.dmg" "../${APP_NAME}-${RELEASE_NAME}-$(uname -m).dmg"
- elif [ -n "$OS_WINDOWS" ]; then
- # Building on Windows with Msys2
- logInfo "Configuring build..."
- cmake -DCMAKE_BUILD_TYPE=Release -G "${CMAKE_GENERATOR}" -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \
- ${CMAKE_OPTIONS} "$SRC_DIR"
- logInfo "Compiling and packaging sources..."
- cmake --build . --config "Release" -- ${MAKE_OPTIONS}
- # Appsign the executables if desired
- if ${build_appsign} && [ -f "${build_key}" ]; then
- logInfo "Signing executable files"
- appsign "-f" $(find src | grep -Ei 'keepassxc.*(\.exe|\.dll)$') "-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}"
- mv "${APP_NAME}-"*.* ../
- else
- mkdir -p "${OUTPUT_DIR}/KeePassXC.AppDir"
- # Building on Linux without Docker container
- logInfo "Configuring build..."
- cmake -DCMAKE_BUILD_TYPE=Release ${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 -it --user $(id -u):$(id -g) \
- -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 \
- --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" \
- bash -c "cd /keepassxc/out/build-release && \
- cmake -DCMAKE_BUILD_TYPE=Release ${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 [ -n "$OS_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 [ ! -e "${f}" ]; then
- exitError "File '${f}' does not exist!"
- fi
- done
- if [ -n "$OS_MACOS" ]; then
- checkXcodeSetup
- local orig_dir="$(pwd)"
- local real_src_dir="$(realpath "${SRC_DIR}")"
- 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
- if ! hdiutil attach -quiet -noautoopen -mountpoint ${tmp_dir}/mnt "${f}"; then
- exitError "DMG mount failed!"
- fi
- cd ${tmp_dir}
- cp -a ./mnt ./app
- hdiutil detach -quiet ${tmp_dir}/mnt
- local app_dir_tmp="./app/KeePassXC.app"
- if [ ! -d "$app_dir_tmp" ]; then
- cd "${orig_dir}"
- exitError "Unpacking failed!"
- fi
- elif [[ ${f: -4} == '.app' ]]; then
- local app_dir_tmp="$f"
- else
- logWarn "Skipping non-app file '${f}'..."
- continue
- fi
- logInfo "Signing libraries and frameworks..."
- if ! find "$app_dir_tmp" \( -name '*.dylib' -o -name '*.so' -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
- if [[ ${f: -4} == '.dmg' ]]; then
- 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}
- fi
- logInfo "File '${f}' successfully signed."
- done
- elif [ -n "$OS_WINDOWS" ]; then
- if [[ ! -f "${key}" ]]; then
- exitError "Appsign key file was not found! (${key})"
- fi
- logInfo "Using appsign key ${key}."
- IFS=$'\n' read -s -r -p "Key password: " password
- echo
- for f in "${sign_files[@]}"; do
- ext=${f: -4}
- if [[ $ext == ".msi" || $ext == ".exe" || $ext == ".dll" ]]; 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" -td sha256 \
- -fd sha256 -tr "${TIMESTAMP_SERVER}" "${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!"
- }
- # -----------------------------------------------------------------------
- # 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 [ -z "$OS_MACOS" ]; 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 [ -z "$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}")"
- if [ 0 -ne $? ]; then
- logError "Submission failed!"
- exitError "Error message:\n${status}"
- fi
- local ticket="$(echo "${status}" | grep -oE '[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
- }
- # -----------------------------------------------------------------------
- # i18n command
- # -----------------------------------------------------------------------
- i18n() {
- local cmd="$1"
- if [ -z "$cmd" ]; then
- logError "No subcommand specified.\n"
- printUsage i18n
- exit 1
- elif [ "$cmd" != "tx-push" ] && [ "$cmd" != "tx-pull" ] && [ "$cmd" != "lupdate" ]; then
- logError "Unknown subcommand: '${cmd}'\n"
- printUsage i18n
- exit 1
- 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
- LUPDATE=lupdate
- fi
- $LUPDATE -no-ui-lines -disable-heuristic similartext -locations none -extensions c,cpp,h,js,mm,qrc,ui \
- -no-obsolete src -ts share/translations/keepassxc_en.ts $@
- return 0
- fi
- checkTransifexCommandExists
- local branch="$(git branch --show-current 2>&1)"
- local real_branch="$branch"
- if [[ "$branch" =~ ^release/ ]]; then
- logInfo "Release branch, setting language resource to master branch."
- branch="master"
- elif [ "$branch" != "develop" ] && [ "$branch" != "master" ]; then
- logError "Must be on master or develop branch!"
- exit 1
- fi
- local resource="keepassxc.share-translations-keepassxc-en-ts--${branch}"
- if [ "$cmd" == "tx-push" ]; then
- echo -e "This will push the \e[1m'en'\e[0m source file from the current branch to Transifex:\n" >&2
- echo -e " \e[1m${real_branch}\e[0m -> \e[1m${resource}\e[0m\n" >&2
- echo -n "Continue? [y/N] " >&2
- read -r yesno
- if [ "$yesno" != "y" ] && [ "$yesno" != "Y" ]; then
- logError "Push aborted."
- exit 1
- fi
- logInfo "Pushing source translation file to Transifex..."
- tx push -s --use-git-timestamps -r "$resource" $@
- elif [ "$cmd" == "tx-pull" ]; then
- logInfo "Pulling updated translations from Transifex..."
- tx pull -af --minimum-perc=60 -r "$resource" $@
- fi
- }
- # -----------------------------------------------------------------------
- # parse global command line
- # -----------------------------------------------------------------------
- MODE="$1"
- shift || true
- if [ -z "$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" ]|| [ "notarize" == "$MODE" ] \
- || [ "appimage" == "$MODE" ]|| [ "i18n" == "$MODE" ]; then
- ${MODE} "$@"
- else
- printUsage "$MODE"
- fi
|