123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458 |
- #!/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)"
- 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}')
- --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 ;;
- -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
-
- CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_BUILD_TYPE=Release"
- logInfo "Checking out release tag '${TAG_NAME}'..."
- 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..."
- 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
|