Skip to content

Fdroid release script #2904

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Create release App Bundle
name: Create release App Bundle and APKs

on:
workflow_dispatch:
Expand Down
2 changes: 2 additions & 0 deletions .idea/dictionaries/shared.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

129 changes: 129 additions & 0 deletions tools/github/download_all_github_artifacts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/usr/bin/env python3
#
# Copyright 2022 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import argparse
import hashlib
import json
import os
# Run `pip3 install requests` if not installed yet
import requests
# Run `pip3 install re` if not installed yet
import re
import time

# This script downloads artifacts from GitHub.
# Ref: https://docs.github.com/en/rest/actions/artifacts#get-an-artifact

error = False

### Arguments

parser = argparse.ArgumentParser(description='Download artifacts from GitHub.')
parser.add_argument('-t',
'--token',
required=True,
help='The GitHub token with read access.')
parser.add_argument('-r',
'--runUrl',
required=True,
help='the GitHub action run url.')
parser.add_argument('-d',
'--directory',
default="",
help='the target directory, where files will be downloaded. If not provided the runId will be used to create a directory.')
parser.add_argument('-v',
'--verbose',
help="increase output verbosity.",
action="store_true")
parser.add_argument('-s',
'--simulate',
help="simulate action, do not create folder or download any file.",
action="store_true")

args = parser.parse_args()

if args.verbose:
print("Argument:")
print(args)


# Split the artifact URL to get information
# Ex: https://github.com/element-hq/element-x-android/actions/runs/9065756777
runUrl = args.runUrl

url_regex = r"https://github.com/(.+?)/(.+?)/actions/runs/(.+)"
result = re.search(url_regex, runUrl)

if result is None:
print(
"❌ Invalid parameter --runUrl '%s'. Please check the format.\nIt should be something like: %s" %
(runUrl, 'https://github.com/element-hq/element-x-android/actions/runs/9065756777')
)
exit(1)

(gitHubRepoOwner, gitHubRepo, runId) = result.groups()

if args.verbose:
print("gitHubRepoOwner: %s, gitHubRepo: %s, runId: %s" % (gitHubRepoOwner, gitHubRepo, runId))

headers = {
'Authorization': "Bearer %s" % args.token,
'Accept': 'application/vnd.github+json'
}

base_url = "https://api.github.com/repos/%s/%s/actions/runs/%s" % (gitHubRepoOwner, gitHubRepo, runId)

### Fetch build state
status = ""
data = "{}"
while status != "completed":
r = requests.get(base_url, headers=headers)
data = json.loads(r.content.decode())

if args.verbose:
print("Json data:")
print(data)
status = data.get("status")

if data.get("status") == "completed":
if data.get("conclusion") != "success":
print("❌ The action %s is completed, but there is an error, the conclusion is: %s." % (runUrl, data.get("conclusion")))
exit(1)
else:
# Wait 1 minute
print("The action %s is not completed yet, waiting 1 minute..." % runUrl)
time.sleep(60)


artifacts_url = data.get("artifacts_url")
if args.verbose:
print("Artifacts url: %s" % artifacts_url)

r = requests.get(artifacts_url, headers=headers)
data = json.loads(r.content.decode())

if args.directory == "":
targetDir = runId
else:
targetDir = args.directory

for artifact in data.get("artifacts"):
if args.verbose:
print("Artifact:")
print(artifact)
# Invoke the script to download the artifact
os.system("python3 ./tools/github/download_github_artifacts.py -t %s -d %s -a %s" % (args.token, targetDir, artifact.get("url")))
30 changes: 21 additions & 9 deletions tools/github/download_github_artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
parser.add_argument('-d',
'--directory',
default="",
help='the target directory, where files will be downloaded. If not provided the build number will be used to create a directory.')
help='the target directory, where files will be downloaded. If not provided the artifactId will be used to create a directory.')
parser.add_argument('-v',
'--verbose',
help="increase output verbosity.",
Expand All @@ -71,15 +71,27 @@
# Ex: https://github.com/element-hq/element-x-android/actions/runs/7299827320/artifacts/1131077517
artifactUrl = args.artifactUrl

url_regex = r"https://github.com/(.+?)/(.+?)/actions/runs/.+?/artifacts/(.+)"
result = re.search(url_regex, artifactUrl)
# if artifactUrl starts with https://github.com
if artifactUrl.startswith('https://github.com'):
url_regex = r"https://github.com/(.+?)/(.+?)/actions/runs/.+?/artifacts/(.+)"
result = re.search(url_regex, artifactUrl)

if result is None:
print(
"❌ Invalid parameter --artifactUrl '%s'. Please check the format.\nIt should be something like: %s" %
(artifactUrl, 'https://github.com/element-hq/element-x-android/actions/runs/7299827320/artifacts/1131077517')
)
exit(1)
else:
url_regex = r"https://api.github.com/repos/(.+?)/(.+?)/actions/artifacts/(.+)"
result = re.search(url_regex, artifactUrl)
if result is None:
print(
"❌ Invalid parameter --artifactUrl '%s'. Please check the format.\nIt should be something like: %s" %
(artifactUrl, 'https://api.github.com/repos/element-hq/element-x-android/actions/artifacts/1131077517')
)
exit(1)

if result is None:
print(
"❌ Invalid parameter --artifactUrl '%s'. Please check the format.\nIt should be something like: %s" %
(artifactUrl, 'https://github.com/element-hq/element-x-android/actions/runs/7299827320/artifacts/1131077517')
)
exit(1)

(gitHubRepoOwner, gitHubRepo, artifactId) = result.groups()

Expand Down
119 changes: 93 additions & 26 deletions tools/release/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ version=${version:-${versionCandidate}}
versionMajor=`echo ${version} | cut -d "." -f1`
versionMinor=`echo ${version} | cut -d "." -f2`
versionPatch=`echo ${version} | cut -d "." -f3`
nextPatchVersion=$((versionPatch + 2))
nextPatchVersion=$((versionPatch + 1))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree to change this, just curious about why.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we do not really do hotfix release currently I always have to change the value manually. This is a temporary change, maybe one day I will revert it.


printf "\n================================================================================\n"
printf "Starting the release ${version}\n"
Expand All @@ -141,20 +141,13 @@ 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 "Building the bundle locally first...\n"
./gradlew clean app:bundleGplayRelease

printf "\n================================================================================\n"
printf "Running towncrier...\n"
yes | towncrier build --version "v${version}"

printf "\n================================================================================\n"
read -p "Check the file CHANGES.md consistency. It's possible to reorder items (most important changes first) or change their section if relevant. Also an opportunity to fix some typo, or rewrite things. Do not commit your change. Press enter to continue. "

# Get the changes to use it to create the GitHub release
changelogUrlEncoded=`git diff CHANGES.md | grep ^+ | tail -n +2 | cut -c2- | jq -sRr @uri | sed s/\(/%28/g | sed s/\)/%29/g`

printf "\n================================================================================\n"
printf "Committing...\n"
git commit -a -m "Changelog for version ${version}"
Expand Down Expand Up @@ -218,27 +211,98 @@ fi

printf "\n================================================================================\n"
printf "Wait for the GitHub action https://github.com/element-hq/element-x-android/actions/workflows/release.yml?query=branch%%3Amain to build the 'main' branch.\n"
read -p "After GHA is finished, please enter the artifact URL (for 'elementx-app-gplay-bundle-unsigned'): " artifactUrl
printf "Please enter the url of the github action (!!! WARNING: NOT THE URL OF THE ARTIFACT ANYMORE !!!)\n"
read -p "For instance https://github.com/element-hq/element-x-android/actions/runs/9065756777: " runUrl

printf "\n================================================================================\n"
printf "Downloading the artifact...\n"

# Download files
targetPath="./tmp/Element/${version}"

python3 ./tools/github/download_github_artifacts.py \
printf "\n================================================================================\n"
printf "Downloading the artifacts...\n"

python3 ./tools/github/download_all_github_artifacts.py \
--token ${gitHubToken} \
--artifactUrl ${artifactUrl} \
--directory ${targetPath} \
--ignoreErrors
--runUrl ${runUrl} \
--directory ${targetPath}

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 "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 \
--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 \
--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 \
--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 \
--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 artifact...\n"
printf "Unzipping the Gplay artifact...\n"

unzip ${targetPath}/elementx-app-gplay-bundle-unsigned.zip -d ${targetPath}
gplayTargetPath="${targetPath}/gplay"
unzip ${targetPath}/elementx-app-gplay-bundle-unsigned.zip -d ${gplayTargetPath}

unsignedBundlePath="${targetPath}/app-gplay-release.aab"
signedBundlePath="${targetPath}/app-gplay-release-signed.aab"
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"
Expand Down Expand Up @@ -274,15 +338,15 @@ doBuildApks=${doBuildApks:-yes}

if [ ${doBuildApks} == "yes" ]; then
printf "Building apks...\n"
bundletool build-apks --bundle=${signedBundlePath} --output=${targetPath}/elementx.apks \
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=${targetPath}/elementx.apks
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"
Expand All @@ -305,14 +369,17 @@ printf "You can then go to \"Publishing overview\" and send the new release for
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}&body=${changelogUrlEncoded}"
# Url encode for "<!-- Copy paste the section of the file CHANGES.md for this release here -->"
body="%3C%21--%20Copy%20paste%20the%20section%20of%20the%20file%20CHANGES.md%20for%20this%20release%20here%20--%3E"
githubCreateReleaseLink="https://github.com/element-hq/element-x-android/releases/new?tag=v${version}&title=Element%20X%20Android%20v${version}&body=${body}"
printf "Creating the release on gitHub.\n"
printf -- "Open this link: %s\n" ${githubCreateReleaseLink}
printf "Then\n"
printf " - copy paste the section of the file CHANGES.md for this release (if not there yet)\n"
printf " - click on the 'Generate releases notes' button\n"
printf " - copy paste the section of the file CHANGES.md for this release.\n"
printf " - click on the 'Generate releases notes' button.\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"
Expand Down
Loading