Skip to content
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

Build and release pipeline #152

Merged
merged 9 commits into from
Aug 16, 2023
65 changes: 63 additions & 2 deletions .github/workflows/build_apk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ on:
tar-url:
description: 'URL for Kolibri tar file'
required: true
release:
description: 'Is this a release asset?'
required: false
type: boolean
default: false
workflow_call:
inputs:
tar-file-name:
Expand All @@ -16,16 +21,50 @@ on:
description: 'A ref for this workflow to check out its own repo'
required: true
type: string
release:
description: 'Is this a release asset?'
required: false
type: boolean
default: false
secrets:
# A base64-encoded keystore that contains the key used to sign the app
# for development builds.
KOLIBRI_ANDROID_APP_DEVELOPER_KEYSTORE:
required: false
# The password for the keystore.
KOLIBRI_ANDROID_APP_DEVELOPER_KEYSTORE_PASSWORD:
required: false
# The password for the key in the keystore.
KOLIBRI_ANDROID_APP_DEVELOPER_KEYALIAS_PASSWORD:
required: false
# A base64-encoded keystore that contains the key used to sign the app
# for production builds.
KOLIBRI_ANDROID_APP_PRODUCTION_KEYSTORE:
required: false
# The password for the keystore.
KOLIBRI_ANDROID_APP_PRODUCTION_KEYSTORE_PASSWORD:
required: false
# The password for the key in the keystore.
KOLIBRI_ANDROID_APP_PRODUCTION_KEYALIAS_PASSWORD:
required: false
KOLIBRI_ANDROID_PLAY_STORE_API_SERVICE_ACCOUNT_JSON:
required: false
outputs:
apk-file-name:
description: "APK file name"
value: ${{ jobs.build_apk.outputs.apk-file-name }}
version-code:
description: "Version code"
value: ${{ jobs.build_apk.outputs.version-code }}

jobs:
build_apk:
runs-on: ubuntu-latest
env:
SERVICE_ACCOUNT_JSON: '${{ secrets.KOLIBRI_ANDROID_PLAY_STORE_API_SERVICE_ACCOUNT_JSON }}'
outputs:
apk-file-name: ${{ steps.get-apk-filename.outputs.apk-file-name }}
version-code: ${{ steps.get-version-code.outputs.version-code}}
steps:
- uses: actions/checkout@v3
if: ${{ !inputs.ref }}
Expand Down Expand Up @@ -84,12 +123,34 @@ jobs:
run: pip install -r requirements.txt
- name: Ensure that Android SDK dependencies are installed
run: make setup
- name: Build the app
- name: Build the aab
if: ${{ inputs.release == true || github.event.inputs.release == 'true' }}
env:
RELEASE_KEYALIAS: LE_RELEASE_KEY
RELEASE_KEYSTORE_PASSWD: ${{ secrets.KOLIBRI_ANDROID_APP_PRODUCTION_KEYSTORE_PASSWORD }}
RELEASE_KEYALIAS_PASSWD: ${{ secrets.KOLIBRI_ANDROID_APP_PRODUCTION_KEYALIAS_PASSWORD }}
run: |
make kolibri.apk.unsigned
echo -n "${{ secrets.KOLIBRI_ANDROID_APP_PRODUCTION_KEYSTORE }}" | base64 --decode --output production.keystore
echo "RELEASE_KEYSTORE=$(realpath production.keystore)" >> "$GITHUB_ENV"
make kolibri.aab
# Upload to Play Store - this will also download the universal APK into the dist folder
make playstore-upload
- name: Build the apk
if: ${{ inputs.release != true && github.event.inputs.release != 'true' }}
env:
RELEASE_KEYALIAS: LE_DEV_KEY
RELEASE_KEYSTORE_PASSWD: ${{ secrets.KOLIBRI_ANDROID_APP_DEVELOPER_KEYSTORE_PASSWORD }}
RELEASE_KEYALIAS_PASSWD: ${{ secrets.KOLIBRI_ANDROID_APP_DEVELOPER_KEYALIAS_PASSWORD }}
run: |
echo -n "${{ secrets.KOLIBRI_ANDROID_APP_DEVELOPER_KEYSTORE }}" | base64 --decode --output development.keystore
echo "RELEASE_KEYSTORE=$(realpath development.keystore)" >> "$GITHUB_ENV"
make kolibri.apk
- name: Get APK filename
id: get-apk-filename
run: echo "apk-file-name=$(ls dist | grep .apk | cat)" >> $GITHUB_OUTPUT
- name: Get versionCode
id: get-version-code
run: echo "version-code=$(cat .version-code)" >> $GITHUB_OUTPUT
- uses: actions/upload-artifact@v3
with:
name: ${{ steps.get-apk-filename.outputs.apk-file-name }}
Expand Down
51 changes: 51 additions & 0 deletions .github/workflows/release_apk.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Release Android App

on:
workflow_dispatch:
# Inputs the workflow accepts.
inputs:
version-code:
description: 'The version code to promote to the open testing track'
required: true
type: string
workflow_call:
inputs:
version-code:
description: 'The version code to promote to the open testing track'
required: true
type: string
ref:
description: 'A ref for this workflow to check out its own repo'
required: true
type: string
secrets:
KOLIBRI_ANDROID_PLAY_STORE_API_SERVICE_ACCOUNT_JSON:
required: false

jobs:
release_apk:
runs-on: ubuntu-latest
env:
SERVICE_ACCOUNT_JSON: '${{ secrets.KOLIBRI_ANDROID_PLAY_STORE_API_SERVICE_ACCOUNT_JSON }}'
steps:
- uses: actions/checkout@v3
if: ${{ !inputs.ref }}
- uses: actions/checkout@v3
if: ${{ inputs.ref }}
with:
repository: learningequality/kolibri-android-installer
ref: ${{ inputs.ref }}
- name: Set up Python 3.9
uses: actions/setup-python@v4
with:
python-version: 3.9
- uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: pip install -r requirements.txt
- name: Release APK
run: python scripts/play_store_api.py release "${{ inputs.version-code }}"
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ android_root/

*.apk
.env
.version-code
*.keystore
49 changes: 27 additions & 22 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ get-tar: clean-tar
$(eval TARFILE = $(shell echo "${DLFILE}" | sed "s/\?.*//"))
[ "${DLFILE}" = "${TARFILE}" ] || mv "${DLFILE}" "${TARFILE}"

.PHONY: install-tar
# Extract the tar file
install-tar: clean
$(eval TARFILE = $(shell echo ""tar/kolibri*.tar.gz"" | sed "s/tar\///"))
Expand All @@ -89,6 +90,7 @@ install-tar: clean
rm -rf python-for-android/build/python-installs/kolibri/*/kolibri* | true
rm -rf python-for-android/build/other_builds/kolibri | true

.PHONY: create-strings
create-strings:
python scripts/create_strings.py

Expand All @@ -109,8 +111,8 @@ p4a_android_distro: needs-android-dirs check-android-clean
# Update the python-for-android project bootstrap, discarding any changes that are made to committed files
# this should be the usually run command in normal workflows.
.PHONY: p4a_android_project
p4a_android_project: install-tar p4a_android_distro create-strings needs-version
$(P4A) bootstrap $(ARCH_OPTIONS) --version=$(APK_VERSION) --numeric-version=$(BUILD_NUMBER)
p4a_android_project: install-tar p4a_android_distro create-strings
$(P4A) bootstrap $(ARCH_OPTIONS) --version="None" --numeric-version=1
Copy link
Member

@jredrejo jredrejo Aug 14, 2023

Choose a reason for hiding this comment

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

not sure about this change as I don't know p4a enough. Following the code it looks to me that both version and numeric-version are useless and the apk version is overseed by the version.upgrade file that's built for gradle, but glad to have more information to understand it if that's not corect.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, that's precisely it. P4A bakes this information into the build.gradle file during its bootstrapping of the build process, but I wanted to decouple from P4A as much as possible - mostly to make the Java project files stable, rather than continually changing based on P4A's builds (also it changes the build.gradle depending on whether it's a dev or production build, which didn't work well with committing these files to the repo).

# Stash any changes to our python-for-android directory
@git stash push --quiet --include-untracked -- python-for-android
$(MAKE) write-version
Expand All @@ -119,50 +121,53 @@ p4a_android_project: install-tar p4a_android_distro create-strings needs-version
# this command should only be run when it is known there is an update from the upstream p4a bootstrap
# that is needed, although it will probably normally be easier to manually vendor the changes.
.PHONY: update_project_from_p4a
update_project_from_p4a: install-tar p4a_android_distro create-strings needs-version
$(P4A) bootstrap $(ARCH_OPTIONS) --version=$(APK_VERSION) --numeric-version=$(BUILD_NUMBER)

.PHONY: needs-version
needs-version:
$(eval APK_VERSION ?= $(shell python3 scripts/version.py apk_version))
$(eval BUILD_NUMBER ?= $(shell python3 scripts/version.py build_number))
update_project_from_p4a: install-tar p4a_android_distro create-strings
$(P4A) bootstrap $(ARCH_OPTIONS) --version="None" --numeric-version=1

.version-code:
python3 scripts/version.py set_version_code

.PHONY: write-version
write-version: needs-version
python3 scripts/version.py write_version
write-version: .version-code
python3 scripts/version.py write_version_properties

.PHONY: kolibri.apk
# Build the signed version of the apk
kolibri.apk: p4a_android_project
$(MAKE) guard-P4A_RELEASE_KEYSTORE
$(MAKE) guard-P4A_RELEASE_KEYALIAS
$(MAKE) guard-P4A_RELEASE_KEYSTORE_PASSWD
$(MAKE) guard-P4A_RELEASE_KEYALIAS_PASSWD
$(MAKE) guard-RELEASE_KEYSTORE
$(MAKE) guard-RELEASE_KEYALIAS
$(MAKE) guard-RELEASE_KEYSTORE_PASSWD
$(MAKE) guard-RELEASE_KEYALIAS_PASSWD
@echo "--- :android: Build APK"
cd python-for-android/dists/kolibri && ./gradlew clean assembleRelease
mkdir -p dist
cp python-for-android/dists/kolibri/build/outputs/apk/release/kolibri-release.apk dist/kolibri-release-$(APK_VERSION).apk
cp python-for-android/dists/kolibri/build/outputs/apk/release/*.apk dist/

.PHONY: kolibri.apk.unsigned
# Build the unsigned debug version of the apk
kolibri.apk.unsigned: p4a_android_project
@echo "--- :android: Build APK (unsigned)"
cd python-for-android/dists/kolibri && ./gradlew clean assembleDebug
mkdir -p dist
cp python-for-android/dists/kolibri/build/outputs/apk/debug/kolibri-debug.apk dist/kolibri-debug-$(APK_VERSION).apk
cp python-for-android/dists/kolibri/build/outputs/apk/debug/*.apk dist/

.PHONY: kolibri.aab
# Build the signed version of the aab
kolibri.aab: p4a_android_project
$(MAKE) guard-P4A_RELEASE_KEYSTORE
$(MAKE) guard-P4A_RELEASE_KEYALIAS
$(MAKE) guard-P4A_RELEASE_KEYSTORE_PASSWD
$(MAKE) guard-P4A_RELEASE_KEYALIAS_PASSWD
$(MAKE) guard-RELEASE_KEYSTORE
$(MAKE) guard-RELEASE_KEYALIAS
$(MAKE) guard-RELEASE_KEYSTORE_PASSWD
$(MAKE) guard-RELEASE_KEYALIAS_PASSWD
@echo "--- :android: Build AAB"
cd python-for-android/dists/kolibri && ./gradlew clean bundleRelease
mkdir -p dist
cp python-for-android/dists/kolibri/build/outputs/bundle/release/kolibri-release.aab dist/kolibri-release-$(APK_VERSION).aab
cp python-for-android/dists/kolibri/build/outputs/bundle/release/*.aab dist/

.PHONY: playstore-upload
# Upload the aab to the play store
playstore-upload:
python3 scripts/play_store_api.py upload


# DOCKER BUILD

Expand Down
31 changes: 21 additions & 10 deletions python-for-android/dists/kolibri/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,34 +39,45 @@ android {
def code = versionProps['VERSION_CODE'].toInteger()
def name = versionProps['VERSION_NAME']

// If we are doing a debug build, we'll end up with -debug-debug
// so we strip that here.
// For release builds, we'll either have -dev-release
// or -official-release so it is more informative.
def nameNoDebug = name.replace("-debug", "")

defaultConfig {
minSdkVersion 23
targetSdkVersion 31
versionCode code
versionName name
manifestPlaceholders = [:]
multiDexEnabled true
setProperty("archivesBaseName", "kolibri-$nameNoDebug")
}


packagingOptions {
packagingOptions {
jniLibs {
useLegacyPackaging = true
}
doNotStrip '**/*.so'

}




exclude 'lib/**/gdbserver'
exclude 'lib/**/gdb.setup'
}

signingConfigs {
release {
storeFile file(System.getenv("RELEASE_KEYSTORE") ?: "empty")
keyAlias System.getenv("RELEASE_KEYALIAS") ?: ""
storePassword System.getenv("RELEASE_KEYSTORE_PASSWD") ?: ""
keyPassword System.getenv("RELEASE_KEYALIAS_PASSWD") ?: ""
}
}

buildTypes {
debug {
debuggable true
}
release {

signingConfig signingConfigs.release
}
}

Expand Down
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
cython~=0.29
virtualenv
git+https://github.com/learningequality/python-for-android@4a3c74caf67cad4495f2352ae56ba2b0f1b266c5#egg=python-for-android
google-api-python-client==2.96.0
google-auth==2.22.0
google-auth-httplib2==0.1.0
2 changes: 1 addition & 1 deletion scripts/create_strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def generate_loading_pages(output_dir):
"--output-dir",
output_dir,
"--version-text",
apk_version(),
apk_version().replace("-official", ""),
)


Expand Down
Loading