You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
418 lines
19 KiB
418 lines
19 KiB
#!/usr/bin/env bash |
|
|
|
# Copyright 2023-2024 New Vector Ltd. |
|
# |
|
# SPDX-License-Identifier: AGPL-3.0-only |
|
# Please see LICENSE in the repository root for full details. |
|
|
|
# do not exit when any command fails (issue with git flow) |
|
set +e |
|
|
|
printf "\n================================================================================\n" |
|
printf "| Welcome to the release script! |\n" |
|
printf "================================================================================\n" |
|
|
|
printf "Checking environment...\n" |
|
envError=0 |
|
|
|
# Check that bundletool is installed |
|
if ! command -v bundletool &> /dev/null |
|
then |
|
printf "Fatal: bundletool is not installed. You can install it running \`brew install bundletool\`\n" |
|
envError=1 |
|
fi |
|
|
|
# Path of the key store (it's a file) |
|
keyStorePath="${ELEMENT_X_KEYSTORE_PATH}" |
|
if [[ -z "${keyStorePath}" ]]; then |
|
printf "Fatal: ELEMENT_X_KEYSTORE_PATH is not defined in the environment.\n" |
|
envError=1 |
|
fi |
|
# Keystore password |
|
keyStorePassword="${ELEMENT_X_KEYSTORE_PASSWORD}" |
|
if [[ -z "${keyStorePassword}" ]]; then |
|
printf "Fatal: ELEMENT_X_KEYSTORE_PASSWORD is not defined in the environment.\n" |
|
envError=1 |
|
fi |
|
# Key password |
|
keyPassword="${ELEMENT_X_KEY_PASSWORD}" |
|
if [[ -z "${keyPassword}" ]]; then |
|
printf "Fatal: ELEMENT_X_KEY_PASSWORD is not defined in the environment.\n" |
|
envError=1 |
|
fi |
|
# GitHub token |
|
gitHubToken="${ELEMENT_GITHUB_TOKEN}" |
|
if [[ -z "${gitHubToken}" ]]; then |
|
printf "Fatal: ELEMENT_GITHUB_TOKEN is not defined in the environment.\n" |
|
envError=1 |
|
fi |
|
# Android home |
|
androidHome="${ANDROID_HOME}" |
|
if [[ -z "${androidHome}" ]]; then |
|
printf "Fatal: ANDROID_HOME is not defined in the environment.\n" |
|
envError=1 |
|
fi |
|
# @elementbot:matrix.org matrix token / Not mandatory |
|
elementBotToken="${ELEMENT_BOT_MATRIX_TOKEN}" |
|
if [[ -z "${elementBotToken}" ]]; then |
|
printf "Warning: ELEMENT_BOT_MATRIX_TOKEN is not defined in the environment.\n" |
|
fi |
|
|
|
if [ ${envError} == 1 ]; then |
|
exit 1 |
|
fi |
|
|
|
minSdkVersion=24 |
|
buildToolsVersion="35.0.0" |
|
buildToolsPath="${androidHome}/build-tools/${buildToolsVersion}" |
|
|
|
if [[ ! -d ${buildToolsPath} ]]; then |
|
printf "Fatal: ${buildToolsPath} folder not found, ensure that you have installed the SDK version ${buildToolsVersion}.\n" |
|
exit 1 |
|
fi |
|
|
|
# Check if git flow is enabled |
|
gitFlowDevelop=$(git config gitflow.branch.develop) |
|
if [[ ${gitFlowDevelop} != "" ]] |
|
then |
|
printf "Git flow is initialized\n" |
|
else |
|
printf "Git flow is not initialized. Initializing...\n" |
|
./tools/gitflow/gitflow-init.sh |
|
fi |
|
|
|
printf "OK\n" |
|
|
|
printf "\n================================================================================\n" |
|
printf "Ensuring main and develop branches are up to date...\n" |
|
|
|
git checkout main |
|
git pull |
|
git checkout develop |
|
git pull |
|
|
|
printf "\n================================================================================\n" |
|
# Guessing version to propose a default version |
|
versionsFile="./plugins/src/main/kotlin/Versions.kt" |
|
versionMajorCandidate=$(grep "val versionMajor" ${versionsFile} | cut -d " " -f6) |
|
versionMinorCandidate=$(grep "val versionMinor" ${versionsFile} | cut -d " " -f6) |
|
versionPatchCandidate=$(grep "val versionPatch" ${versionsFile} | cut -d " " -f6) |
|
versionCandidate="${versionMajorCandidate}.${versionMinorCandidate}.${versionPatchCandidate}" |
|
|
|
read -p "Please enter the release version (example: ${versionCandidate}). Just press enter if ${versionCandidate} is correct. " version |
|
version=${version:-${versionCandidate}} |
|
|
|
# extract major, minor and patch for future use |
|
versionMajor=$(echo "${version}" | cut -d "." -f1) |
|
versionMinor=$(echo "${version}" | cut -d "." -f2) |
|
versionPatch=$(echo "${version}" | cut -d "." -f3) |
|
nextPatchVersion=$((versionPatch + 1)) |
|
|
|
printf "\n================================================================================\n" |
|
printf "Starting the release ${version}\n" |
|
git flow release start "${version}" |
|
|
|
# Note: in case the release is already started and the script is started again, checkout the release branch again. |
|
ret=$? |
|
if [[ $ret -ne 0 ]]; then |
|
printf "Mmh, it seems that the release is already started. Checking out the release branch...\n" |
|
git checkout "release/${version}" |
|
fi |
|
|
|
# Ensure version is OK |
|
versionsFileBak="${versionsFile}.bak" |
|
cp ${versionsFile} ${versionsFileBak} |
|
sed "s/private const val versionMajor = .*/private const val versionMajor = ${versionMajor}/" ${versionsFileBak} > ${versionsFile} |
|
sed "s/private const val versionMinor = .*/private const val versionMinor = ${versionMinor}/" ${versionsFile} > ${versionsFileBak} |
|
sed "s/private const val versionPatch = .*/private const val versionPatch = ${versionPatch}/" ${versionsFileBak} > ${versionsFile} |
|
rm ${versionsFileBak} |
|
|
|
# This commit may have no effect because generally we do not change the version during the release. |
|
git commit -a -m "Setting version for the release ${version}" |
|
|
|
printf "\n================================================================================\n" |
|
printf "Creating fastlane file...\n" |
|
printf -v versionMajor2Digits "%02d" "${versionMajor}" |
|
printf -v versionMinor2Digits "%02d" "${versionMinor}" |
|
printf -v versionPatch2Digits "%02d" "${versionPatch}" |
|
fastlaneFile="4${versionMajor2Digits}${versionMinor2Digits}${versionPatch2Digits}0.txt" |
|
fastlanePathFile="./fastlane/metadata/android/en-US/changelogs/${fastlaneFile}" |
|
printf "Main changes in this version: TODO.\nFull changelog: https://github.com/element-hq/element-x-android/releases" > "${fastlanePathFile}" |
|
|
|
read -p "I have created the file ${fastlanePathFile}, please edit it and press enter to continue. " |
|
git add "${fastlanePathFile}" |
|
git commit -a -m "Adding fastlane file for version ${version}" |
|
|
|
printf "\n================================================================================\n" |
|
printf "OK, finishing the release...\n" |
|
git flow release finish "${version}" |
|
|
|
printf "\n================================================================================\n" |
|
read -p "Done, push the branch 'main' and the new tag (yes/no) default to yes? " doPush |
|
doPush=${doPush:-yes} |
|
|
|
if [ "${doPush}" == "yes" ]; then |
|
printf "Pushing branch 'main' and tag 'v${version}'...\n" |
|
git push origin main |
|
git push origin "v${version}" |
|
else |
|
printf "Not pushing, do not forget to push manually!\n" |
|
fi |
|
|
|
printf "\n================================================================================\n" |
|
printf "Checking out develop...\n" |
|
git checkout develop |
|
|
|
# Set next version |
|
printf "\n================================================================================\n" |
|
printf "Setting next version on file '${versionsFile}'...\n" |
|
cp ${versionsFile} ${versionsFileBak} |
|
sed "s/private const val versionPatch = .*/private const val versionPatch = ${nextPatchVersion}/" ${versionsFileBak} > ${versionsFile} |
|
rm ${versionsFileBak} |
|
|
|
printf "\n================================================================================\n" |
|
read -p "I have updated the versions to prepare the next release, please check that the change are correct and press enter so I can commit. " |
|
|
|
printf "Committing...\n" |
|
git commit -a -m 'version++' |
|
|
|
printf "\n================================================================================\n" |
|
printf "The GitHub action https://github.com/element-hq/element-x-android/actions/workflows/release.yml?query=branch%%3Amain should have start a new run.\n" |
|
read -p "Please enter the url of the run, no need to wait for it to complete (example: https://github.com/element-hq/element-x-android/actions/runs/9065756777): " runUrl |
|
|
|
targetPath="./tmp/Element/${version}" |
|
|
|
printf "\n================================================================================\n" |
|
printf "Downloading the artifacts...\n" |
|
|
|
ret=1 |
|
|
|
while [[ $ret -ne 0 ]]; do |
|
python3 ./tools/github/download_all_github_artifacts.py \ |
|
--token "${gitHubToken}" \ |
|
--runUrl "${runUrl}" \ |
|
--directory "${targetPath}" |
|
|
|
ret=$? |
|
if [[ $ret -ne 0 ]]; then |
|
read -p "Error while downloading the artifacts. You may want to fix the issue and retry. Retry (yes/no) default to yes? " doRetry |
|
doRetry=${doRetry:-yes} |
|
if [ "${doRetry}" == "no" ]; then |
|
exit 1 |
|
fi |
|
fi |
|
done |
|
|
|
printf "\n================================================================================\n" |
|
printf "Unzipping the F-Droid artifact...\n" |
|
|
|
fdroidTargetPath="${targetPath}/fdroid" |
|
unzip "${targetPath}"/elementx-app-fdroid-apks-unsigned.zip -d "${fdroidTargetPath}" |
|
|
|
printf "\n================================================================================\n" |
|
printf "Patching the FDroid APKs using inplace-fix.py...\n" |
|
|
|
inplaceFixScript="./tools/release/inplace-fix.py" |
|
python3 "${inplaceFixScript}" --page-size 16 fix-pg-map-id "${fdroidTargetPath}"/app-fdroid-arm64-v8a-release.apk '0000000' |
|
python3 "${inplaceFixScript}" --page-size 16 fix-pg-map-id "${fdroidTargetPath}"/app-fdroid-armeabi-v7a-release.apk '0000000' |
|
python3 "${inplaceFixScript}" --page-size 16 fix-pg-map-id "${fdroidTargetPath}"/app-fdroid-x86-release.apk '0000000' |
|
python3 "${inplaceFixScript}" --page-size 16 fix-pg-map-id "${fdroidTargetPath}"/app-fdroid-x86_64-release.apk '0000000' |
|
|
|
printf "\n================================================================================\n" |
|
printf "Signing the FDroid APKs...\n" |
|
|
|
cp "${fdroidTargetPath}"/app-fdroid-arm64-v8a-release.apk \ |
|
"${fdroidTargetPath}"/app-fdroid-arm64-v8a-release-signed.apk |
|
"${buildToolsPath}"/apksigner sign \ |
|
-v \ |
|
--alignment-preserved true \ |
|
--ks "${keyStorePath}" \ |
|
--ks-pass pass:"${keyStorePassword}" \ |
|
--ks-key-alias elementx \ |
|
--key-pass pass:"${keyPassword}" \ |
|
--min-sdk-version ${minSdkVersion} \ |
|
"${fdroidTargetPath}"/app-fdroid-arm64-v8a-release-signed.apk |
|
|
|
cp "${fdroidTargetPath}"/app-fdroid-armeabi-v7a-release.apk \ |
|
"${fdroidTargetPath}"/app-fdroid-armeabi-v7a-release-signed.apk |
|
"${buildToolsPath}"/apksigner sign \ |
|
-v \ |
|
--alignment-preserved true \ |
|
--ks "${keyStorePath}" \ |
|
--ks-pass pass:"${keyStorePassword}" \ |
|
--ks-key-alias elementx \ |
|
--key-pass pass:"${keyPassword}" \ |
|
--min-sdk-version ${minSdkVersion} \ |
|
"${fdroidTargetPath}"/app-fdroid-armeabi-v7a-release-signed.apk |
|
|
|
cp "${fdroidTargetPath}"/app-fdroid-x86-release.apk \ |
|
"${fdroidTargetPath}"/app-fdroid-x86-release-signed.apk |
|
"${buildToolsPath}"/apksigner sign \ |
|
-v \ |
|
--alignment-preserved true \ |
|
--ks "${keyStorePath}" \ |
|
--ks-pass pass:"${keyStorePassword}" \ |
|
--ks-key-alias elementx \ |
|
--key-pass pass:"${keyPassword}" \ |
|
--min-sdk-version ${minSdkVersion} \ |
|
"${fdroidTargetPath}"/app-fdroid-x86-release-signed.apk |
|
|
|
cp "${fdroidTargetPath}"/app-fdroid-x86_64-release.apk \ |
|
"${fdroidTargetPath}"/app-fdroid-x86_64-release-signed.apk |
|
"${buildToolsPath}"/apksigner sign \ |
|
-v \ |
|
--alignment-preserved true \ |
|
--ks "${keyStorePath}" \ |
|
--ks-pass pass:"${keyStorePassword}" \ |
|
--ks-key-alias elementx \ |
|
--key-pass pass:"${keyPassword}" \ |
|
--min-sdk-version ${minSdkVersion} \ |
|
"${fdroidTargetPath}"/app-fdroid-x86_64-release-signed.apk |
|
|
|
printf "\n================================================================================\n" |
|
printf "Please check the information below:\n" |
|
|
|
printf "File app-fdroid-arm64-v8a-release-signed.apk:\n" |
|
"${buildToolsPath}"/aapt dump badging "${fdroidTargetPath}"/app-fdroid-arm64-v8a-release-signed.apk | grep package |
|
printf "File app-fdroid-armeabi-v7a-release-signed.apk:\n" |
|
"${buildToolsPath}"/aapt dump badging "${fdroidTargetPath}"/app-fdroid-armeabi-v7a-release-signed.apk | grep package |
|
printf "File app-fdroid-x86-release-signed.apk:\n" |
|
"${buildToolsPath}"/aapt dump badging "${fdroidTargetPath}"/app-fdroid-x86-release-signed.apk | grep package |
|
printf "File app-fdroid-x86_64-release-signed.apk:\n" |
|
"${buildToolsPath}"/aapt dump badging "${fdroidTargetPath}"/app-fdroid-x86_64-release-signed.apk | grep package |
|
|
|
printf "\n" |
|
read -p "Does it look correct? Press enter when it's done. " |
|
|
|
printf "\n================================================================================\n" |
|
printf "The APKs in ${fdroidTargetPath} have been signed!\n" |
|
|
|
printf "\n================================================================================\n" |
|
printf "Unzipping the Gplay artifact...\n" |
|
|
|
gplayTargetPath="${targetPath}/gplay" |
|
unzip "${targetPath}"/elementx-app-gplay-bundle-unsigned.zip -d "${gplayTargetPath}" |
|
|
|
unsignedBundlePath="${gplayTargetPath}/app-gplay-release.aab" |
|
signedBundlePath="${gplayTargetPath}/app-gplay-release-signed.aab" |
|
|
|
printf "\n================================================================================\n" |
|
printf "Signing file ${unsignedBundlePath} with build-tools version ${buildToolsVersion} for min SDK version ${minSdkVersion}...\n" |
|
|
|
cp "${unsignedBundlePath}" "${signedBundlePath}" |
|
|
|
"${buildToolsPath}"/apksigner sign \ |
|
-v \ |
|
--ks "${keyStorePath}" \ |
|
--ks-pass pass:"${keyStorePassword}" \ |
|
--ks-key-alias elementx \ |
|
--key-pass pass:"${keyPassword}" \ |
|
--min-sdk-version ${minSdkVersion} \ |
|
"${signedBundlePath}" |
|
|
|
printf "\n================================================================================\n" |
|
printf "Please check the information below:\n" |
|
|
|
printf "Version code: " |
|
bundletool dump manifest --bundle="${signedBundlePath}" --xpath=/manifest/@android:versionCode |
|
printf "Version name: " |
|
bundletool dump manifest --bundle="${signedBundlePath}" --xpath=/manifest/@android:versionName |
|
|
|
printf "\n" |
|
read -p "Does it look correct? Press enter to continue. " |
|
|
|
printf "\n================================================================================\n" |
|
printf "The file ${signedBundlePath} has been signed and can be uploaded to the PlayStore!\n" |
|
|
|
printf "\n================================================================================\n" |
|
read -p "Do you want to build the APKs from the app bundle? You need to do this step if you want to install the application to your device. (yes/no) default to no " doBuildApks |
|
doBuildApks=${doBuildApks:-no} |
|
|
|
if [ "${doBuildApks}" == "yes" ]; then |
|
printf "Building apks...\n" |
|
bundletool build-apks --bundle="${signedBundlePath}" --output="${gplayTargetPath}"/elementx.apks \ |
|
--ks=./app/signature/debug.keystore --ks-pass=pass:android --ks-key-alias=androiddebugkey --key-pass=pass:android \ |
|
--overwrite |
|
|
|
read -p "Do you want to install the application to your device? Make sure there is one (and only one!) connected device first. (yes/no) default to yes " doDeploy |
|
doDeploy=${doDeploy:-yes} |
|
if [ "${doDeploy}" == "yes" ]; then |
|
printf "Installing apk for your device...\n" |
|
bundletool install-apks --apks="${gplayTargetPath}"/elementx.apks |
|
read -p "Please run the application on your phone to check that the upgrade went well. Press enter to continue. " |
|
else |
|
printf "APK will not be deployed!\n" |
|
fi |
|
else |
|
printf "APKs will not be generated!\n" |
|
fi |
|
|
|
printf "\n================================================================================\n" |
|
printf "Create the open testing release on GooglePlay.\n" |
|
|
|
printf "On GooglePlay console, go the the open testing section and click on \"Create new release\" button, then:\n" |
|
printf " - upload the file ${signedBundlePath}.\n" |
|
printf " - copy the release note from the fastlane file.\n" |
|
printf " - download the universal APK, to be able to provide it to the GitHub release: click on the right arrow next to the \"App bundle\", then click on the \"Download\" tab, and download the \"Signed, universal APK\".\n" |
|
printf " - submit the release.\n" |
|
read -p "Press enter to continue. " |
|
|
|
printf "You can then go to \"Publishing overview\" and send the new release for a review by Google.\n" |
|
read -p "Press enter to continue. " |
|
|
|
printf "\n================================================================================\n" |
|
githubCreateReleaseLink="https://github.com/element-hq/element-x-android/releases/new?tag=v${version}&title=Element%20X%20Android%20v${version}" |
|
printf "Creating the release on gitHub.\n" |
|
printf -- "Open this link: %s\n" "${githubCreateReleaseLink}" |
|
printf "Then\n" |
|
printf " - Click on the 'Generate releases notes' button.\n" |
|
printf " - Optionally reorder items and fix typos.\n" |
|
printf " - Add the file ${signedBundlePath} to the GitHub release.\n" |
|
printf " - Add the universal APK, downloaded from the GooglePlay console to the GitHub release.\n" |
|
printf " - Add the 4 signed APKs for F-Droid, located at ${fdroidTargetPath} to the GitHub release.\n" |
|
read -p ". Press enter to continue. " |
|
|
|
printf "\n================================================================================\n" |
|
printf "Update the project release notes:\n\n" |
|
|
|
read -p "Copy the content of the release note generated by GitHub to the file CHANGES.md and press enter to commit the change. " |
|
|
|
printf "\n================================================================================\n" |
|
printf "Committing...\n" |
|
git commit -a -m "Changelog for version ${version}" |
|
|
|
printf "\n================================================================================\n" |
|
read -p "Done, push the branch 'develop' (yes/no) default to yes? (A rebase may be necessary in case develop got new commits) " doPush |
|
doPush=${doPush:-yes} |
|
|
|
if [ "${doPush}" == "yes" ]; then |
|
printf "Pushing branch 'develop'...\n" |
|
git push origin develop |
|
else |
|
printf "Not pushing, do not forget to push manually!\n" |
|
fi |
|
|
|
printf "\n================================================================================\n" |
|
printf "Message for the Android internal room:\n\n" |
|
message="@room Element X Android ${version} is ready to be tested. You can get it from https://github.com/element-hq/element-x-android/releases/tag/v${version}. You can install the universal APK. If you want to install the application from the app bundle, you can follow instructions [here](https://github.com/element-hq/element-x-android/blob/develop/docs/install_from_github_release.md). Please report any feedback. Thanks!" |
|
printf "${message}\n\n" |
|
|
|
if [[ -z "${elementBotToken}" ]]; then |
|
read -p "ELEMENT_BOT_MATRIX_TOKEN is not defined in the environment. Cannot send the message for you. Please send it manually, and press enter to continue. " |
|
else |
|
read -p "Send this message to the room (yes/no) default to yes? " doSend |
|
doSend=${doSend:-yes} |
|
if [ "${doSend}" == "yes" ]; then |
|
printf "Sending message...\n" |
|
transactionId=$(openssl rand -hex 16) |
|
# Element Android internal |
|
matrixRoomId="!LiSLXinTDCsepePiYW:matrix.org" |
|
curl -X PUT --data "{\"msgtype\":\"m.text\",\"body\":\"${message}\"}" -H "Authorization: Bearer ${elementBotToken}" https://matrix-client.matrix.org/_matrix/client/r0/rooms/${matrixRoomId}/send/m.room.message/\$local."${transactionId}" |
|
else |
|
printf "Message not sent, please send it manually!\n" |
|
fi |
|
fi |
|
|
|
printf "\n================================================================================\n" |
|
printf "Congratulation! Kudos for using this script! Have a nice day!\n" |
|
printf "================================================================================\n"
|
|
|