mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Make release script more modular and useful on other platforms
This commit is contained in:
parent
b97024c8f6
commit
c87c811719
352
make_release.sh
352
make_release.sh
@ -1,352 +0,0 @@
|
|||||||
#!/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/>.
|
|
||||||
|
|
||||||
echo -e "\e[1m\e[32mKeePassXC\e[0m Release Preparation Helper"
|
|
||||||
echo -e "Copyright (C) 2017 KeePassXC Team <https://keepassxc.org/>\n"
|
|
||||||
|
|
||||||
|
|
||||||
# default values
|
|
||||||
RELEASE_NAME=""
|
|
||||||
APP_NAME="KeePassXC"
|
|
||||||
APP_NAME_LOWER="keepassxc"
|
|
||||||
SRC_DIR="."
|
|
||||||
GPG_KEY="CFB4C2166397D0D2"
|
|
||||||
GPG_GIT_KEY=""
|
|
||||||
OUTPUT_DIR="release"
|
|
||||||
BRANCH=""
|
|
||||||
RELEASE_BRANCH="master"
|
|
||||||
TAG_NAME=""
|
|
||||||
BUILD_SOURCES=false
|
|
||||||
DOCKER_IMAGE=""
|
|
||||||
DOCKER_CONTAINER_NAME="${APP_NAME_LOWER}-build-container"
|
|
||||||
CMAKE_OPTIONS=""
|
|
||||||
COMPILER="g++"
|
|
||||||
MAKE_OPTIONS="-j8"
|
|
||||||
BUILD_PLUGINS="autotype"
|
|
||||||
INSTALL_PREFIX="/usr/local"
|
|
||||||
|
|
||||||
ORIG_BRANCH="$(git rev-parse --abbrev-ref HEAD 2> /dev/null)"
|
|
||||||
ORIG_CWD="$(pwd)"
|
|
||||||
|
|
||||||
|
|
||||||
# helper functions
|
|
||||||
printUsage() {
|
|
||||||
echo -e "\e[1mUsage:\e[0m $(basename $0) [options]"
|
|
||||||
cat << EOF
|
|
||||||
|
|
||||||
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, --gpg-key GPG key used to sign the release tarball
|
|
||||||
(default: '${GPG_KEY}')
|
|
||||||
-g, --gpg-git-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}')
|
|
||||||
-o, --output-dir Output directory where to build the release
|
|
||||||
(default: '${OUTPUT_DIR}')
|
|
||||||
--develop-branch Development branch to merge from (default: 'release/VERSION')
|
|
||||||
--release-branch Target release branch to merge to (default: '${RELEASE_BRANCH}')
|
|
||||||
-t, --tag-name Override release tag name (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
|
|
||||||
-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}')
|
|
||||||
-i, --install-prefix Install prefix (default: '${INSTALL_PREFIX}')
|
|
||||||
-p, --plugins Space-separated list of plugins to build
|
|
||||||
(default: ${BUILD_PLUGINS})
|
|
||||||
-h, --help Show this help
|
|
||||||
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
logInfo() {
|
|
||||||
echo -e "\e[1m[ \e[34mINFO\e[39m ]\e[0m $1"
|
|
||||||
}
|
|
||||||
|
|
||||||
logError() {
|
|
||||||
echo -e "\e[1m[ \e[31mERROR\e[39m ]\e[0m $1" >&2
|
|
||||||
}
|
|
||||||
|
|
||||||
exitError() {
|
|
||||||
logError "$1"
|
|
||||||
if [ "" != "$ORIG_BRANCH" ]; then
|
|
||||||
git checkout "$ORIG_BRANCH" > /dev/null 2>&1
|
|
||||||
fi
|
|
||||||
cd "$ORIG_CWD"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# parse command line options
|
|
||||||
while [ $# -ge 1 ]; do
|
|
||||||
arg="$1"
|
|
||||||
|
|
||||||
case "$arg" in
|
|
||||||
-a|--app-name)
|
|
||||||
APP_NAME="$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
-s|--source-dir)
|
|
||||||
SRC_DIR"$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
-v|--version)
|
|
||||||
RELEASE_NAME="$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
-k|--gpg-key)
|
|
||||||
GPG_KEY="$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
-g|--gpg-git-key)
|
|
||||||
GPG_GIT_KEY="$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
-o|--output-dir)
|
|
||||||
OUTPUT_DIR="$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
--develop-branch)
|
|
||||||
BRANCH="$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
--release-branch)
|
|
||||||
RELEASE_BRANCH="$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
-t|--tag-name)
|
|
||||||
TAG_NAME="$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
-b|--build)
|
|
||||||
BUILD_SOURCES=true ;;
|
|
||||||
|
|
||||||
-d|--docker-image)
|
|
||||||
DOCKER_IMAGE="$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
--container-name)
|
|
||||||
DOCKER_CONTAINER_NAME="$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
-c|--cmake-options)
|
|
||||||
CMAKE_OPTIONS="$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
-m|--make-options)
|
|
||||||
MAKE_OPTIONS="$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
--compiler)
|
|
||||||
COMPILER="$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
-p|--plugins)
|
|
||||||
BUILD_PLUGINS="$2"
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
-h|--help)
|
|
||||||
printUsage
|
|
||||||
exit ;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
logError "Unknown option '$arg'\n"
|
|
||||||
printUsage
|
|
||||||
exit 1 ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
||||||
if [ "" == "$RELEASE_NAME" ]; then
|
|
||||||
logError "Missing arguments, --version is required!\n"
|
|
||||||
printUsage
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "" == "$TAG_NAME" ]; then
|
|
||||||
TAG_NAME="$RELEASE_NAME"
|
|
||||||
fi
|
|
||||||
if [ "" == "$BRANCH" ]; then
|
|
||||||
BRANCH="release/${RELEASE_NAME}"
|
|
||||||
fi
|
|
||||||
APP_NAME_LOWER="$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]')"
|
|
||||||
APP_NAME_UPPER="$(echo "$APP_NAME" | tr '[:lower:]' '[:upper:]')"
|
|
||||||
|
|
||||||
SRC_DIR="$(realpath "$SRC_DIR")"
|
|
||||||
OUTPUT_DIR="$(realpath "$OUTPUT_DIR")"
|
|
||||||
if [ ! -d "$SRC_DIR" ]; then
|
|
||||||
exitError "Source directory '${SRC_DIR}' does not exist!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
logInfo "Changing to source directory..."
|
|
||||||
cd "${SRC_DIR}"
|
|
||||||
|
|
||||||
logInfo "Performing basic checks..."
|
|
||||||
|
|
||||||
if [ -e "$OUTPUT_DIR" ]; then
|
|
||||||
exitError "Output directory '$OUTPUT_DIR' already exists. Please choose a different location!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -d .git ] || [ ! -f CHANGELOG ]; then
|
|
||||||
exitError "Source directory is not a valid Git repository!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
git tag | grep -q "$RELEASE_NAME"
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
exitError "Release '$RELEASE_NAME' already exists!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
git diff-index --quiet HEAD --
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
exitError "Current working tree is not clean! Please commit or unstage any changes."
|
|
||||||
fi
|
|
||||||
|
|
||||||
git checkout "$BRANCH" > /dev/null 2>&1
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
exitError "Source branch '$BRANCH' does not exist!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
grep -q "${APP_NAME_UPPER}_VERSION \"${RELEASE_NAME}\"" CMakeLists.txt
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
exitError "${APP_NAME_UPPER}_VERSION version not updated to '${RELEASE_NAME}' in CMakeLists.txt!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
grep -q "${APP_NAME_UPPER}_VERSION_NUM \"${RELEASE_NAME}\"" CMakeLists.txt
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
exitError "${APP_NAME_UPPER}_VERSION_NUM version not updated to '${RELEASE_NAME}' in CMakeLists.txt!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
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 does not contain any information about the '${RELEASE_NAME}' release!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
git checkout "$RELEASE_BRANCH" > /dev/null 2>&1
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
exitError "Release branch '$RELEASE_BRANCH' does not exist!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
logInfo "All checks pass, getting our hands dirty now!"
|
|
||||||
|
|
||||||
logInfo "Merging '${BRANCH}' into '${RELEASE_BRANCH}'..."
|
|
||||||
|
|
||||||
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}"
|
|
||||||
|
|
||||||
git merge "$BRANCH" --no-ff -m "$COMMIT_MSG" -m "${CHANGELOG}" "$BRANCH" -S"$GPG_GIT_KEY"
|
|
||||||
|
|
||||||
logInfo "Creating tag '${RELEASE_NAME}'..."
|
|
||||||
if [ "" == "$GPG_GIT_KEY" ]; then
|
|
||||||
git tag -a "$RELEASE_NAME" -m "$COMMIT_MSG" -m "${CHANGELOG}" -s
|
|
||||||
else
|
|
||||||
git tag -a "$RELEASE_NAME" -m "$COMMIT_MSG" -m "${CHANGELOG}" -s -u "$GPG_GIT_KEY"
|
|
||||||
fi
|
|
||||||
|
|
||||||
logInfo "Merge done, creating target directory..."
|
|
||||||
mkdir -p "$OUTPUT_DIR"
|
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
exitError "Failed to create output directory!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
logInfo "Creating source tarball..."
|
|
||||||
TARBALL_NAME="${APP_NAME_LOWER}-${RELEASE_NAME}-src.tar.bz2"
|
|
||||||
git archive --format=tar "$RELEASE_BRANCH" --prefix="${APP_NAME_LOWER}-${RELEASE_NAME}/" \
|
|
||||||
| bzip2 -9 > "${OUTPUT_DIR}/${TARBALL_NAME}"
|
|
||||||
|
|
||||||
|
|
||||||
if $BUILD_SOURCES; then
|
|
||||||
logInfo "Creating build directory..."
|
|
||||||
mkdir -p "${OUTPUT_DIR}/build-release"
|
|
||||||
mkdir -p "${OUTPUT_DIR}/bin-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 [ "$COMPILER" == "g++" ]; then
|
|
||||||
export CC=gcc
|
|
||||||
elif [ "$COMPILER" == "clang++" ]; then
|
|
||||||
export CC=clang
|
|
||||||
fi
|
|
||||||
export CXX="$COMPILER"
|
|
||||||
|
|
||||||
if [ "" == "$DOCKER_IMAGE" ]; then
|
|
||||||
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}/bin-release" install/strip
|
|
||||||
|
|
||||||
logInfo "Creating AppImage..."
|
|
||||||
${SRC_DIR}/AppImage-Recipe.sh "$APP_NAME" "$RELEASE_NAME"
|
|
||||||
else
|
|
||||||
logInfo "Launching Docker container to compile sources..."
|
|
||||||
|
|
||||||
docker run --name "$DOCKER_CONTAINER_NAME" --rm \
|
|
||||||
--cap-add SYS_ADMIN --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/bin-release install/strip && \
|
|
||||||
/keepassxc/src/AppImage-Recipe.sh "$APP_NAME" "$RELEASE_NAME""
|
|
||||||
|
|
||||||
logInfo "Build finished, Docker container terminated."
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd ..
|
|
||||||
logInfo "Signing source tarball..."
|
|
||||||
gpg --output "${TARBALL_NAME}.sig" --armor --local-user "$GPG_KEY" --detach-sig "$TARBALL_NAME"
|
|
||||||
|
|
||||||
logInfo "Signing AppImage..."
|
|
||||||
APPIMAGE_NAME="${APP_NAME}-${RELEASE_NAME}-x86_64.AppImage"
|
|
||||||
gpg --output "${APPIMAGE_NAME}.sig" --armor --local-user "$GPG_KEY" --detach-sig "$APPIMAGE_NAME"
|
|
||||||
|
|
||||||
logInfo "Creating digests..."
|
|
||||||
sha256sum "$TARBALL_NAME" > "${TARBALL_NAME}.DIGEST"
|
|
||||||
sha256sum "$APPIMAGE_NAME" > "${APPIMAGE_NAME}.DIGEST"
|
|
||||||
fi
|
|
||||||
|
|
||||||
logInfo "Leaving source directory..."
|
|
||||||
cd "$ORIG_CWD"
|
|
||||||
git checkout "$ORIG_BRANCH" > /dev/null 2>&1
|
|
||||||
|
|
||||||
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."
|
|
596
release-tool
Executable file
596
release-tool
Executable file
@ -0,0 +1,596 @@
|
|||||||
|
#!/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/>.
|
||||||
|
|
||||||
|
echo -e "\e[1m\e[32mKeePassXC\e[0m Release Preparation Helper"
|
||||||
|
echo -e "Copyright (C) 2017 KeePassXC Team <https://keepassxc.org/>\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=""
|
||||||
|
COMPILER="g++"
|
||||||
|
MAKE_OPTIONS="-j8"
|
||||||
|
BUILD_PLUGINS="autotype"
|
||||||
|
INSTALL_PREFIX="/usr/local"
|
||||||
|
BUILD_SOURCE_TARBALL=true
|
||||||
|
ORIG_BRANCH=""
|
||||||
|
ORIG_CWD="$(pwd)"
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# helper functions
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
printUsage() {
|
||||||
|
local cmd
|
||||||
|
if [ "" == "$1" ] || [ "help" == "$1" ]; then
|
||||||
|
cmd="COMMAND"
|
||||||
|
elif [ "merge" == "$1" ] || [ "build" == "$1" ] || [ "sign" == "$1" ]; then
|
||||||
|
cmd="$1"
|
||||||
|
else
|
||||||
|
logError "Unknown command: '$1'"
|
||||||
|
echo
|
||||||
|
cmd="COMMAND"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "\e[1mUsage:\e[0m $(basename $0) $cmd [options]"
|
||||||
|
|
||||||
|
if [ "COMMAND" == "$cmd" ]; then
|
||||||
|
cat << EOF
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
merge Merge release development branch into main branch
|
||||||
|
and create release tags
|
||||||
|
build Build and package binary release from sources
|
||||||
|
sign Sign compile release packages
|
||||||
|
help Show help for the given command
|
||||||
|
EOF
|
||||||
|
elif [ "merge" == "$cmd" ]; then
|
||||||
|
cat << EOF
|
||||||
|
|
||||||
|
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}')
|
||||||
|
-g, --gpg-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
|
||||||
|
|
||||||
|
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
|
||||||
|
-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}')
|
||||||
|
-i, --install-prefix Install prefix (default: '${INSTALL_PREFIX}')
|
||||||
|
-p, --plugins Space-separated list of plugins to build
|
||||||
|
(default: ${BUILD_PLUGINS})
|
||||||
|
-n, --no-source-tarball Don't build source tarball
|
||||||
|
-h, --help Show this help
|
||||||
|
EOF
|
||||||
|
elif [ "sign" == "$cmd" ]; then
|
||||||
|
cat << EOF
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-f, --files Files to sign (required)
|
||||||
|
-g, --gpg-key GPG key used to sign the files (default: '${GPG_KEY}')
|
||||||
|
-h, --help Show this help
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
logInfo() {
|
||||||
|
echo -e "\e[1m[ \e[34mINFO\e[39m ]\e[0m $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
logError() {
|
||||||
|
echo -e "\e[1m[ \e[31mERROR\e[39m ]\e[0m $1" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
ORIG_CWD="$(pwd)"
|
||||||
|
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..."
|
||||||
|
}
|
||||||
|
|
||||||
|
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:]')"
|
||||||
|
|
||||||
|
grep -q "${app_name_upper}_VERSION \"${RELEASE_NAME}\"" CMakeLists.txt
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
exitError "${app_name_upper}_VERSION version not updated to '${RELEASE_NAME}' in CMakeLists.txt!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
grep -q "${app_name_upper}_VERSION_NUM \"${RELEASE_NAME}\"" CMakeLists.txt
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
exitError "${app_name_upper}_VERSION_NUM version not updated to '${RELEASE_NAME}' 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 does not contain any information about the '${RELEASE_NAME}' release!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
trap exitTrap SIGINT SIGTERM
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# 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 ;;
|
||||||
|
|
||||||
|
-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
|
||||||
|
|
||||||
|
if [ "" == "$RELEASE_NAME" ]; then
|
||||||
|
logError "Missing arguments, --version is required!\n"
|
||||||
|
printUsage "merge"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "" == "$TAG_NAME" ]; then
|
||||||
|
TAG_NAME="$RELEASE_NAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "" == "$SOURCE_BRANCH" ]; then
|
||||||
|
SOURCE_BRANCH="release/${RELEASE_NAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
init
|
||||||
|
|
||||||
|
SRC_DIR="$(realpath "$SRC_DIR")"
|
||||||
|
|
||||||
|
logInfo "Performing basic checks..."
|
||||||
|
|
||||||
|
checkSourceDirExists
|
||||||
|
|
||||||
|
logInfo "Changing to source directory..."
|
||||||
|
cd "${SRC_DIR}"
|
||||||
|
|
||||||
|
checkGitRepository
|
||||||
|
checkReleaseDoesNotExist
|
||||||
|
checkWorkingTreeClean
|
||||||
|
checkSourceBranchExists
|
||||||
|
checkTargetBranchExists
|
||||||
|
checkVersionInCMake
|
||||||
|
checkChangeLog
|
||||||
|
|
||||||
|
logInfo "All checks pass, getting our hands dirty now!"
|
||||||
|
|
||||||
|
logInfo "Checking out target branch '${TARGET_BRANCH}'..."
|
||||||
|
git checkout "$TARGET_BRANCH"
|
||||||
|
|
||||||
|
logInfo "Merging '${SOURCE_BRANCH}' into '${TARGET_BRANCH}'..."
|
||||||
|
|
||||||
|
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}"
|
||||||
|
|
||||||
|
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."
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# build command
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
build() {
|
||||||
|
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 ;;
|
||||||
|
|
||||||
|
-c|--cmake-options)
|
||||||
|
CMAKE_OPTIONS="$2"
|
||||||
|
shift ;;
|
||||||
|
|
||||||
|
--compiler)
|
||||||
|
COMPILER="$2"
|
||||||
|
shift ;;
|
||||||
|
|
||||||
|
-m|--make-options)
|
||||||
|
MAKE_OPTIONS="$2"
|
||||||
|
shift ;;
|
||||||
|
|
||||||
|
-i|--install-prefix)
|
||||||
|
INSTALL_PREFIX="$2"
|
||||||
|
shift ;;
|
||||||
|
|
||||||
|
-p|--plugins)
|
||||||
|
BUILD_PLUGINS="$2"
|
||||||
|
shift ;;
|
||||||
|
|
||||||
|
-n|--no-source-tarball)
|
||||||
|
BUILD_SOURCE_TARBALL=false ;;
|
||||||
|
|
||||||
|
-h|--help)
|
||||||
|
printUsage "build"
|
||||||
|
exit ;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
logError "Unknown option '$arg'\n"
|
||||||
|
printUsage "build"
|
||||||
|
exit 1 ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "" == "$RELEASE_NAME" ]; then
|
||||||
|
logError "Missing arguments, --version is required!\n"
|
||||||
|
printUsage "build"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "" == "$TAG_NAME" ]; then
|
||||||
|
TAG_NAME="$RELEASE_NAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
init
|
||||||
|
|
||||||
|
SRC_DIR="$(realpath "$SRC_DIR")"
|
||||||
|
OUTPUT_DIR="$(realpath "$OUTPUT_DIR")"
|
||||||
|
|
||||||
|
logInfo "Performing basic checks..."
|
||||||
|
|
||||||
|
checkSourceDirExists
|
||||||
|
|
||||||
|
logInfo "Changing to source directory..."
|
||||||
|
cd "${SRC_DIR}"
|
||||||
|
|
||||||
|
checkTagExists
|
||||||
|
checkGitRepository
|
||||||
|
checkWorkingTreeClean
|
||||||
|
checkOutputDirDoesNotExist
|
||||||
|
|
||||||
|
logInfo "Checking out release tag '${TAG_NAME}'..."
|
||||||
|
git checkout "$TAG_NAME"
|
||||||
|
|
||||||
|
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:]')"
|
||||||
|
TARBALL_NAME="${app_name_lower}-${RELEASE_NAME}-src.tar.xz"
|
||||||
|
git archive --format=tar "$TAG_NAME" --prefix="${app_name_lower}-${RELEASE_NAME}/" \
|
||||||
|
| xz -6 > "${OUTPUT_DIR}/${TARBALL_NAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
logInfo "Creating build directory..."
|
||||||
|
mkdir -p "${OUTPUT_DIR}/build-release"
|
||||||
|
mkdir -p "${OUTPUT_DIR}/bin-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 [ "$COMPILER" == "g++" ]; then
|
||||||
|
export CC=gcc
|
||||||
|
elif [ "$COMPILER" == "clang++" ]; then
|
||||||
|
export CC=clang
|
||||||
|
fi
|
||||||
|
export CXX="$COMPILER"
|
||||||
|
|
||||||
|
if [ "" == "$DOCKER_IMAGE" ]; then
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=Off $CMAKE_OPTIONS \
|
||||||
|
-DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" "$SRC_DIR"
|
||||||
|
|
||||||
|
logInfo "Compiling sources..."
|
||||||
|
make $MAKE_OPTIONS
|
||||||
|
|
||||||
|
if [ "$(uname -i)" == "Msys" ]; then
|
||||||
|
logInfo "Bundling binary packages..."
|
||||||
|
make package
|
||||||
|
else
|
||||||
|
logInfo "Installing to bin dir..."
|
||||||
|
make DESTDIR="${OUTPUT_DIR}/bin-release" install/strip
|
||||||
|
|
||||||
|
|
||||||
|
logInfo "Creating AppImage..."
|
||||||
|
${SRC_DIR}/AppImage-Recipe.sh "$APP_NAME" "$RELEASE_NAME"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
logInfo "Launching Docker container to compile sources..."
|
||||||
|
|
||||||
|
docker run --name "$DOCKER_CONTAINER_NAME" --rm \
|
||||||
|
--cap-add SYS_ADMIN --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/bin-release install/strip && \
|
||||||
|
/keepassxc/src/AppImage-Recipe2.sh "$APP_NAME" "$RELEASE_NAME""
|
||||||
|
|
||||||
|
if [ 0 -ne $? ]; then
|
||||||
|
exitError "Docker build failed!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
logInfo "Build finished, Docker container terminated."
|
||||||
|
fi
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
|
||||||
|
logInfo "All done!"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# sign command
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
sign() {
|
||||||
|
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 ;;
|
||||||
|
|
||||||
|
-g|--gpg-key)
|
||||||
|
GPG_KEY="$2"
|
||||||
|
shift ;;
|
||||||
|
|
||||||
|
-h|--help)
|
||||||
|
printUsage "sign"
|
||||||
|
exit ;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
logError "Unknown option '$arg'\n"
|
||||||
|
printUsage "sign"
|
||||||
|
exit 1 ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$SIGN_FILES" ]; then
|
||||||
|
logError "Missing arguments, --files is required!\n"
|
||||||
|
printUsage "sign"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for f in "${SIGN_FILES[@]}"; do
|
||||||
|
if [ ! -f "$f" ]; then
|
||||||
|
exitError "File '${f}' does not exist!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
logInfo "Signing file '${f}'..."
|
||||||
|
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}'..."
|
||||||
|
sha256sum "$f" > "${f}.DIGEST"
|
||||||
|
done
|
||||||
|
|
||||||
|
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 [ "merge" == "$MODE" ] || [ "build" == "$MODE" ] || [ "sign" == "$MODE" ]; then
|
||||||
|
$MODE "$@"
|
||||||
|
fi
|
Loading…
Reference in New Issue
Block a user