#!/usr/bin/env bash set -Eeuo pipefail readonly project_dir="${CI_WORKSPACE:-${GITHUB_WORKSPACE:-$(pwd)}}" readonly build_dir="${project_dir}/build-ci/android" readonly output_dir="${project_dir}/dist/android" readonly qt_root="${QT_ROOT:-/opt/Qt}" readonly qt_version="${QT_VERSION:-6.11.1}" readonly android_ndk_version="${ANDROID_NDK_VERSION:-27.2.12479018}" readonly run_number="${CI_RUN_NUMBER:-${GITHUB_RUN_NUMBER:-1}}" readonly ref_name="${CI_REF_NAME:-${GITHUB_REF_NAME:-}}" readonly ref_type="${CI_REF_TYPE:-${GITHUB_REF_TYPE:-}}" readonly version_code="${ANDROID_VERSION_CODE:-${run_number}}" if [[ -n "${ANDROID_VERSION_NAME:-}" ]]; then version_name="${ANDROID_VERSION_NAME}" elif [[ "${ref_type}" == tag && -n "${ref_name}" ]]; then version_name="${ref_name#v}" else version_name="${PROJECT_VERSION:-0.1.0}-ci.${run_number}" fi readonly version_name sign_android=OFF keystore_path="" if [[ -n "${ANDROID_KEYSTORE_BASE64:-}" ]]; then keystore_path="${build_dir}/release.keystore" mkdir -p "${build_dir}" printf '%s' "${ANDROID_KEYSTORE_BASE64}" | base64 --decode > "${keystore_path}" trap 'rm -f -- "${keystore_path}"' EXIT elif [[ -n "${ANDROID_KEYSTORE_FILE:-}" ]]; then keystore_path="${ANDROID_KEYSTORE_FILE}" fi if [[ -n "${keystore_path}" ]]; then : "${ANDROID_KEYSTORE_ALIAS:?ANDROID_KEYSTORE_ALIAS is required when a keystore is provided}" : "${ANDROID_KEYSTORE_STORE_PASS:?ANDROID_KEYSTORE_STORE_PASS is required when a keystore is provided}" : "${ANDROID_KEYSTORE_KEY_PASS:?ANDROID_KEYSTORE_KEY_PASS is required when a keystore is provided}" export QT_ANDROID_KEYSTORE_PATH="${keystore_path}" export QT_ANDROID_KEYSTORE_ALIAS="${ANDROID_KEYSTORE_ALIAS}" export QT_ANDROID_KEYSTORE_STORE_PASS="${ANDROID_KEYSTORE_STORE_PASS}" export QT_ANDROID_KEYSTORE_KEY_PASS="${ANDROID_KEYSTORE_KEY_PASS}" sign_android=ON else echo "No Android keystore configured; producing unsigned CI packages." fi "${qt_root}/${qt_version}/android_arm64_v8a/bin/qt-cmake" \ --fresh \ -S "${project_dir}" \ -B "${build_dir}" \ -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DQT_HOST_PATH="${qt_root}/${qt_version}/gcc_64" \ -DANDROID_SDK_ROOT="${ANDROID_SDK_ROOT}" \ -DANDROID_NDK_ROOT="${ANDROID_SDK_ROOT}/ndk/${android_ndk_version}" \ -DQT_ANDROID_ABIS="arm64-v8a;armeabi-v7a" \ -DEVCHARGER_ANDROID_VERSION_CODE="${version_code}" \ -DEVCHARGER_ANDROID_VERSION_NAME="${version_name}" \ -DQT_ANDROID_SIGN_APK="${sign_android}" \ -DQT_ANDROID_SIGN_AAB="${sign_android}" # Both package targets use the same android-build staging directory. Running # them in one parallel Ninja invocation makes androiddeployqt race with itself. cmake --build "${build_dir}" --parallel cmake --build "${build_dir}" --parallel --target apk cmake --build "${build_dir}" --parallel --target aab mkdir -p "${output_dir}" if [[ "${sign_android}" == ON ]]; then apk_path="$(find "${build_dir}" -type f -path '*/outputs/apk/release/*.apk' ! -name '*unsigned*' -print -quit)" else apk_path="$(find "${build_dir}" -type f -path '*/outputs/apk/release/*.apk' -print -quit)" fi aab_path="$(find "${build_dir}" -type f -path '*/outputs/bundle/release/*.aab' -print -quit)" : "${apk_path:?The Android build did not produce an APK}" : "${aab_path:?The Android build did not produce an AAB}" cp "${apk_path}" "${output_dir}/evcharger-${version_name}-universal.apk" cp "${aab_path}" "${output_dir}/evcharger-${version_name}.aab" readonly output_apk="evcharger-${version_name}-universal.apk" readonly output_aab="evcharger-${version_name}.aab" for abi in arm64-v8a armeabi-v7a; do unzip -Z1 "${output_dir}/${output_apk}" "lib/${abi}/*" >/dev/null done if [[ "${sign_android}" == ON ]]; then "${ANDROID_SDK_ROOT}/build-tools/36.0.0/apksigner" verify --verbose --print-certs "${output_dir}/${output_apk}" jarsigner -verify "${output_dir}/${output_aab}" fi ( cd "${output_dir}" sha256sum "${output_apk}" "${output_aab}" > SHA256SUMS )