diff --git a/ci/Jenkinsfile.android b/ci/Jenkinsfile.android index 2d52ced9624..79792a7d653 100644 --- a/ci/Jenkinsfile.android +++ b/ci/Jenkinsfile.android @@ -1,5 +1,5 @@ #!/usr/bin/env groovy -library 'status-jenkins-lib@v1.9.33' +library 'status-jenkins-lib@fix-android-signing' /* Options section can't access functions in objects. */ def isPRBuild = utils.isPRBuild() @@ -11,7 +11,7 @@ pipeline { /* Image with Ubuntu 22.04, QT 6.9.2, Android SDK/NDK, Go, and Nim */ docker { label 'linuxcontainer' - image 'harbor.status.im/status-im/status-desktop-build:1.0.6-qt6.9.2-android' + image 'harbor.status.im/status-im/status-desktop-build:1.0.8-qt6.9.2-android' alwaysPull true args '--entrypoint="" ' + '--volume=/nix:/nix ' + @@ -70,8 +70,12 @@ pipeline { QMAKE = "/opt/qt/${env.QT_VERSION}/android_arm64_v8a/bin/qmake" /* override package type if set, otherwise auto-select based on branch */ PACKAGE_TYPE = "${params.PACKAGE_TYPE != 'auto' ? params.PACKAGE_TYPE : (isReleaseBranch ? 'aab' : 'apk')}" + /* BUILD_VARIANT controls package name and signing: pr = app.status.mobile.pr, release = app.status.mobile */ + BUILD_VARIANT = "${utils.isReleaseBuild() ? 'release' : 'pr'}" + /* App name derived from BUILD_VARIANT */ + STATUS_ANDROID_APP_NAME = "${['pr': 'StatusPR', 'release': 'Status'][env.BUILD_VARIANT]}" STATUS_ARTIFACT = "pkg/${utils.pkgFilename(ext: env.PACKAGE_TYPE, arch: 'arm64', version: env.VERSION, type: env.APP_TYPE)}" - STATUS_BINARY = "${WORKSPACE}/mobile/bin/android/qt6/Status.${env.PACKAGE_TYPE}" + STATUS_BINARY = "${WORKSPACE}/mobile/bin/android/qt6/${env.STATUS_ANDROID_APP_NAME}.${env.PACKAGE_TYPE}" NIM_SDS_SOURCE_DIR = "${env.WORKSPACE_TMP}/nim-sds" } diff --git a/mobile/android/qt6/AndroidManifest.xml b/mobile/android/qt6/AndroidManifest.xml index 7305b3591a8..2361c78aa50 100644 --- a/mobile/android/qt6/AndroidManifest.xml +++ b/mobile/android/qt6/AndroidManifest.xml @@ -1,9 +1,7 @@ + android:installLocation="auto"> @@ -60,6 +58,19 @@ + + + + + + + + + + + + diff --git a/mobile/android/qt6/settings.gradle b/mobile/android/qt6/settings.gradle new file mode 100644 index 00000000000..8483d53290a --- /dev/null +++ b/mobile/android/qt6/settings.gradle @@ -0,0 +1,9 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +rootProject.name = 'android-build' diff --git a/mobile/docker/Dockerfile b/mobile/docker/Dockerfile index b447e9250fc..6082390e844 100644 --- a/mobile/docker/Dockerfile +++ b/mobile/docker/Dockerfile @@ -337,6 +337,7 @@ ENV ARCH=arm64 RUN apt-get update && apt-get install -y --no-install-recommends \ jq \ + rsync \ s3cmd \ fuse \ tk-dev \ @@ -394,6 +395,13 @@ RUN apt update -yq && apt install -yq software-properties-common \ --slave /usr/bin/g++ g++ /usr/bin/g++-9 \ && apt-get -qq clean -ENV PATH="/root/go/bin:${PATH}" +ARG GRADLE_VERSION=8.11.1 +RUN wget -q https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip -O /tmp/gradle.zip \ + && unzip -q /tmp/gradle.zip -d /opt \ + && ln -s /opt/gradle-${GRADLE_VERSION} /opt/gradle \ + && rm /tmp/gradle.zip + +ENV GRADLE_HOME=/opt/gradle +ENV PATH="/opt/gradle/bin:/root/go/bin:${PATH}" LABEL description="Build image for the Status Android APK." diff --git a/mobile/scripts/buildApp.sh b/mobile/scripts/buildApp.sh index 36ead6c33f5..c11ba544908 100755 --- a/mobile/scripts/buildApp.sh +++ b/mobile/scripts/buildApp.sh @@ -5,146 +5,75 @@ CWD=$(realpath "$(dirname "$0")") ARCH=${ARCH:-amd64} SDK=${SDK:-iphonesimulator} -JAVA_HOME=${JAVA_HOME:-} -BIN_DIR=${BIN_DIR:-"$CWD/../bin/ios"} BUILD_DIR=${BUILD_DIR:-"$CWD/../build"} -ANDROID_ABI=${ANDROID_ABI:-"arm64-v8a"} BUILD_TYPE=${BUILD_TYPE:-"apk"} - -# BUILD_VARIANT controls bundle ID: "pr" = app.status.mobile.pr, "release" = app.status.mobile -export BUILD_VARIANT=${BUILD_VARIANT:-"release"} +BUILD_VARIANT=${BUILD_VARIANT:-"release"} QMAKE_BIN="${QMAKE:-qmake}" -QMAKE_CONFIG="CONFIG+=device CONFIG+=release" +QMAKE_CONFIG=("CONFIG+=device" "CONFIG+=release") -PRO_FILE="$CWD/../wrapperApp/Status.pro" +# Derive names from variant: pr -> StatusPR, release -> Status +OUTPUT_NAME="Status" +[[ "$BUILD_VARIANT" == "pr" ]] && OUTPUT_NAME="StatusPR" -echo "Building wrapperApp for ${OS}, ${ANDROID_ABI}" -echo "Using project file: $PRO_FILE" +echo "Building $OUTPUT_NAME for ${OS}, variant: ${BUILD_VARIANT}" mkdir -p "${BUILD_DIR}" cd "${BUILD_DIR}" STATUS_DESKTOP=${STATUS_DESKTOP:-"../vendors/status-desktop"} -DESKTOP_VERSION=$(eval cd "$STATUS_DESKTOP" && git describe --tags --dirty="-dirty" --always | cut -d- -f1 | cut -d. -f1-3 | sed 's/^v//') - -TIMESTAMP=$(($(date +%s) * 1000 / 60000)) - -if [[ -n "${CHANGE_ID:-}" ]]; then - BUILD_VERSION="${CHANGE_ID}.${TIMESTAMP}" -else - BUILD_VERSION="${TIMESTAMP}" -fi +VERSION=$(cd "$STATUS_DESKTOP" && git describe --tags --always | cut -d- -f1 | cut -d. -f1-3 | sed 's/^v//') +BUILD_VERSION="${CHANGE_ID:+${CHANGE_ID}.}$(($(date +%s) / 60))" -echo "Using version: $DESKTOP_VERSION; build version: $BUILD_VERSION" +echo "Version: $VERSION, build: $BUILD_VERSION" if [[ "${OS}" == "android" ]]; then - if [[ -z "${JAVA_HOME}" ]]; then - echo "JAVA_HOME is not set. Please set JAVA_HOME to the path of your JDK 11 or later." - exit 1 - fi + [[ -z "${JAVA_HOME}" ]] && { echo "JAVA_HOME is not set"; exit 1; } - echo "Building for Android 35" - ANDROID_PLATFORM=android-35 + GRADLE_FLAVOR="production" + [[ "$BUILD_VARIANT" == "pr" ]] && GRADLE_FLAVOR="pr" - "$QMAKE_BIN" "$PRO_FILE" "$QMAKE_CONFIG" -spec android-clang ANDROID_ABIS="$ANDROID_ABI" APP_VARIANT="${APP_VARIANT}" VERSION="$DESKTOP_VERSION" -after + "$QMAKE_BIN" "$CWD/../wrapperApp/Status.pro" "${QMAKE_CONFIG[@]}" -spec android-clang \ + ANDROID_ABIS="${ANDROID_ABI:-arm64-v8a}" VERSION="$VERSION" -after - # Build the app make -j"$(nproc)" apk_install_target - if [[ "$BUILD_TYPE" == "aab" ]]; then - if [[ -z "$KEYSTORE_PATH" || -z "$KEYSTORE_PASSWORD" || -z "$KEY_ALIAS" || -z "$KEY_PASSWORD" ]]; then - echo "Error: AAB builds require signing credentials" - echo "Required: KEYSTORE_PATH, KEYSTORE_PASSWORD, KEY_ALIAS, KEY_PASSWORD" - exit 1 - fi - - if [[ ! -f "$KEYSTORE_PATH" ]]; then - echo "Error: Keystore file not found at $KEYSTORE_PATH" - exit 1 - fi - - echo "Building AAB..." - androiddeployqt \ - --input "$BUILD_DIR/android-Status-deployment-settings.json" \ - --output "$BUILD_DIR/android-build" \ - --aab \ - --release \ - --android-platform "$ANDROID_PLATFORM" - - OUTPUT_FILE="$BUILD_DIR/android-build/build/outputs/bundle/release/android-build-release.aab" - if [[ ! -f "$OUTPUT_FILE" ]]; then - echo "Error: Could not find generated AAB file at $OUTPUT_FILE" - exit 1 - fi - - # Sign the AAB file (androiddeployqt --sign does not work for AAB files) - "$CWD/android/sign.sh" "$OUTPUT_FILE" - - ANDROID_OUTPUT_DIR="bin/android/qt6" - BIN_DIR_ANDROID=${BIN_DIR:-"$CWD/$ANDROID_OUTPUT_DIR"} - mkdir -p "$BIN_DIR_ANDROID" - cp "$OUTPUT_FILE" "$BIN_DIR_ANDROID/Status.aab" - echo "Build succeeded. Signed AAB is available at $BIN_DIR_ANDROID/Status.aab" - else - # APK build - NEEDS_SIGNING="false" - if [[ -n "$KEYSTORE_PATH" && -n "$KEYSTORE_PASSWORD" && -n "$KEY_ALIAS" && -n "$KEY_PASSWORD" ]]; then - if [[ -f "$KEYSTORE_PATH" ]]; then - NEEDS_SIGNING="true" - fi - fi - - if [[ "$NEEDS_SIGNING" == "true" ]]; then - echo "Building signed APK..." - androiddeployqt \ - --input "$BUILD_DIR/android-Status-deployment-settings.json" \ - --output "$BUILD_DIR/android-build" \ - --apk "$BUILD_DIR/android-build/Status.apk" \ - --release \ - --android-platform "$ANDROID_PLATFORM" \ - --sign "$KEYSTORE_PATH" "$KEY_ALIAS" \ - --storepass "$KEYSTORE_PASSWORD" \ - --keypass "$KEY_PASSWORD" - else - echo "Building unsigned APK..." - androiddeployqt \ - --input "$BUILD_DIR/android-Status-deployment-settings.json" \ - --output "$BUILD_DIR/android-build" \ - --apk "$BUILD_DIR/android-build/Status.apk" \ - --android-platform "$ANDROID_PLATFORM" - fi - - ANDROID_OUTPUT_DIR="bin/android/qt6" - BIN_DIR_ANDROID=${BIN_DIR:-"$CWD/$ANDROID_OUTPUT_DIR"} - mkdir -p "$BIN_DIR_ANDROID" - cp ./android-build/Status.apk "$BIN_DIR_ANDROID/Status.apk" - - if [[ "$NEEDS_SIGNING" == "true" ]]; then - echo "Build succeeded. Signed APK is available at $BIN_DIR_ANDROID/Status.apk" - else - echo "Build succeeded. Unsigned APK is available at $BIN_DIR_ANDROID/Status.apk" - fi - fi + androiddeployqt \ + --input "$BUILD_DIR/android-${OUTPUT_NAME}-deployment-settings.json" \ + --output "$BUILD_DIR/android-build" \ + --android-platform android-35 \ + --verbose --aux-mode + + # Copy custom Android files, preserve Qt-generated libs.xml + cp "$CWD/../android/qt${QT_MAJOR}"/{AndroidManifest.xml,build.gradle,settings.gradle,gradle.properties} "$BUILD_DIR/android-build/" + rsync -a --exclude='libs.xml' "$CWD/../android/qt${QT_MAJOR}/res/" "$BUILD_DIR/android-build/res/" 2>/dev/null || true + rsync -a "$CWD/../android/qt${QT_MAJOR}/src/" "$BUILD_DIR/android-build/src/" 2>/dev/null || true + + cd "$BUILD_DIR/android-build" + + # Build: aab -> bundle, apk -> assemble + TASK="assemble"; [[ "$BUILD_TYPE" == "aab" ]] && TASK="bundle" + gradle "${TASK}${GRADLE_FLAVOR^}Release" --no-daemon + + OUTPUT_FILE="build/outputs/apk/${GRADLE_FLAVOR}/release/android-build-${GRADLE_FLAVOR}-release.apk" + [[ "$BUILD_TYPE" == "aab" ]] && OUTPUT_FILE="build/outputs/bundle/${GRADLE_FLAVOR}Release/android-build-${GRADLE_FLAVOR}-release.aab" + [[ ! -f "$OUTPUT_FILE" ]] && { echo "Error: $OUTPUT_FILE not found"; exit 1; } + + BIN_DIR=${BIN_DIR:-"$CWD/../bin/android/qt6"} + mkdir -p "$BIN_DIR" + cp "$OUTPUT_FILE" "$BIN_DIR/${OUTPUT_NAME}.${BUILD_TYPE}" + echo "Build succeeded: $BIN_DIR/${OUTPUT_NAME}.${BUILD_TYPE}" + else - "$QMAKE_BIN" "$PRO_FILE" "$QMAKE_CONFIG" -spec macx-ios-clang CONFIG+="$SDK" VERSION="$DESKTOP_VERSION" -after - - if [[ "$BUILD_VARIANT" == "pr" ]]; then - TARGET_NAME="StatusPR" - else - TARGET_NAME="Status" - fi - - # Compile resources - xcodebuild -configuration Release -target "Qt Preprocess" -sdk "$SDK" -arch "$ARCH" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO CURRENT_PROJECT_VERSION=$BUILD_VERSION | xcbeautify - # Compile the app - xcodebuild -configuration Release -target "$TARGET_NAME" install -sdk "$SDK" -arch "$ARCH" DSTROOT="$BIN_DIR" INSTALL_PATH="/" TARGET_BUILD_DIR="$BIN_DIR" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO CURRENT_PROJECT_VERSION=$BUILD_VERSION | xcbeautify - - if [[ ! -e "${BIN_DIR}/${TARGET_NAME}.app/Info.plist" ]]; then - echo "Build failed -> ${BIN_DIR}/${TARGET_NAME}.app not found" - exit 1 - fi - - # Note: iOS signing is handled by fastlane - echo "Build succeeded! unsigned app ready for fastlane signing" + "$QMAKE_BIN" "$CWD/../wrapperApp/Status.pro" "${QMAKE_CONFIG[@]}" -spec macx-ios-clang CONFIG+="$SDK" VERSION="$VERSION" -after + + XCODE_FLAGS=(-configuration Release -sdk "$SDK" -arch "$ARCH" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO CURRENT_PROJECT_VERSION="$BUILD_VERSION") + BIN_DIR=${BIN_DIR:-"$CWD/../bin/ios"} + + xcodebuild "${XCODE_FLAGS[@]}" -target "Qt Preprocess" | xcbeautify + xcodebuild "${XCODE_FLAGS[@]}" -target "$OUTPUT_NAME" install DSTROOT="$BIN_DIR" INSTALL_PATH="/" TARGET_BUILD_DIR="$BIN_DIR" | xcbeautify + + [[ ! -e "$BIN_DIR/${OUTPUT_NAME}.app/Info.plist" ]] && { echo "Build failed"; exit 1; } + + echo "Build succeeded: $BIN_DIR/${OUTPUT_NAME}.app" fi diff --git a/mobile/wrapperApp/Status.pro b/mobile/wrapperApp/Status.pro index ef9bf115622..ee7be68b55e 100644 --- a/mobile/wrapperApp/Status.pro +++ b/mobile/wrapperApp/Status.pro @@ -2,6 +2,11 @@ TEMPLATE = app QT += quick gui qml webview svg widgets multimedia +BUILD_VARIANT = $$(BUILD_VARIANT) +TARGET = Status +equals(BUILD_VARIANT, "pr"): TARGET = StatusPR +message("Building $$BUILD_VARIANT variant: TARGET = $$TARGET") + equals(QT_MAJOR_VERSION, 6) { message("qt 6 config!!") QT += core5compat core @@ -50,19 +55,12 @@ ios { QMAKE_ASSET_CATALOGS += $$PWD/../ios/Images.xcassets QMAKE_IOS_LAUNCH_SCREEN = $$PWD/../ios/launch-image-universal.storyboard - # Bundle identifier configuration based on BUILD_VARIANT environment variable - # - PR builds (BUILD_VARIANT=pr): app.status.mobile.pr - # - Release/Local dev (BUILD_VARIANT unset or "release"): app.status.mobile - BUILD_VARIANT_ENV = $$(BUILD_VARIANT) - equals(BUILD_VARIANT_ENV, "pr") { - TARGET = StatusPR + # Bundle identifier: pr -> app.status.mobile.pr, release -> app.status.mobile + QMAKE_TARGET_BUNDLE_PREFIX = app.status + QMAKE_BUNDLE = mobile + equals(BUILD_VARIANT, "pr") { QMAKE_TARGET_BUNDLE_PREFIX = app.status.mobile QMAKE_BUNDLE = pr - } else { - # Default for local development and release builds - TARGET = Status - QMAKE_TARGET_BUNDLE_PREFIX = app.status - QMAKE_BUNDLE = mobile } LIBS += -L$$PWD/../lib/$$LIB_PREFIX -lnim_status_client -lDOtherSideStatic -lstatusq -lstatus -lsds -lssl_3 -lcrypto_3 -lqzxing -lresolv -lqrcodegen