init: fdroid setup

- `mobile/Makefile` -> Added `apk-fdroid` build target, used by
`fdroid/build-app.sh`. F-Droid requires an unsigned APK without Google
Play services.

  - `mobile/android/qt6/build.gradle` -> Added F-Droid gradle build
flavor, used during `apk-fdroid` target. Separates F-Droid builds from
Play Store builds and no signing configuration since it's supposed to be
unsigned.

  - `mobile/scripts/buildApp.sh` -> Updated to copy F-Droid APK instead
of usual locations from Jenkins build.

  - `mobile/scripts/buildOpenSSL.sh` -> Moved from iOS dir, shared
between iOS and F-Droid builds. Reusing the OpenSSL build logic that was
previously iOS-only but is now needed for F-Droid's from-source
requirement.

  - `mobile/scripts/Common.mk` -> Updated path to `buildOpenSSL.sh`
which is no longer inside iOS dir.

  - `mobile/scripts/openssl-patch.diff` -> Moved from iOS dir, applied
by `buildOpenSSL.sh`, so that the patch is co-located with the script
that uses it.

  - `ci/Jenkinsfile.fdroid` -> Had to make a new pipeline script because
integrating this into `ci/Jenkinsfile.android` was causing issues with
the generated `AppImage` and steps had to be fully sequential, so I
figured it's good to separate them out.

  - `fdroid/entrypoint-fdroid.sh` -> Docker entrypoint, used by
`fdroid/Dockerfile`. Sets up the container user/permissions before the
F-Droid build starts.

  - `fdroid/Dockerfile` -> A minimalistic Docker image for the F-Droid
Jenkins build environment, used by `ci/Jenkinsfile.fdroid`. I didn't add
to existing Qt Android `Dockerfile` because this one only needs a
minimal environment with Docker-in-Docker to run the `fdroidserver` VM.

  - `fdroid/build-app.sh` -> Called by `fdroiddata` metadata build step.
Sets up Qt and Android NDK environment, detects `JAVA_HOME`, configures
`Go`/`Nim` env vars, and runs the `make` targets for the F-Droid APK.

  - `fdroid/build-openssl.sh` -> F-Droid requires OpenSSL to be built
from source rather than using prebuilt binaries.

  - `fdroid/build-qt-android.sh` -> F-Droid requires Qt to be built from
source; this handles the Android cross-compilation.

  - `fdroid/build-qt-host.sh` -> Qt's cross-compilation requires host
tools (`moc`, `rcc`, etc.) to be built first.

  - `fdroid/cleanup-binaries.sh` -> Removes binaries flagged by `fdroid`
scanner, called by `fdroiddata` metadata `prebuild` step. F-Droid's
scanner rejects repos containing prebuilt binaries in test/vendor
directories.

  - `fdroid/cleanup-fdroiddata.sh` -> Removes stale `fdroiddata` clones,
called by `ci/Jenkinsfile.fdroid`. Prevents disk space issues from
accumulating `fdroiddata` clones across builds.

  - `fdroid/fdroid-container-build.sh` -> Runs `fdroid build` inside
Docker container, called by `scripts/fdroid-local-build.sh`. Isolates
the build in a container to match F-Droid's official build environment.

  - `scripts/fdroid-local-build.sh` -> A handy script to build F-Droid
locally, also called by `ci/Jenkinsfile.fdroid`. Manages the full
lifecycle of cloning `fdroiddata`, launching the `fdroidserver`, and
extracting the output APK.

  - `fdroid/generate-keystore.sh` -> Generates a single-use keystore for
signing the F-Droid APK. Sourced by `buildApp.sh` to export signing
credentials into the build environment.

fixes: https://github.com/status-im/status-app/issues/19741
This commit is contained in:
Siddarth Kumar
2026-03-14 20:54:15 +05:30
parent c5f102f921
commit 7afb60d1b9
17 changed files with 637 additions and 21 deletions
+124
View File
@@ -0,0 +1,124 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.40'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
/* Object to store public URLs for description. */
descLinks = [:]
pipeline {
agent {
dockerfile {
dir 'fdroid'
filename 'Dockerfile'
label 'linuxcontainer'
args '--entrypoint="" ' +
'--group-add docker ' +
'--volume=/nix:/nix ' +
'--volume=/etc/nix:/etc/nix ' +
'--volume=/var/run/docker.sock:/var/run/docker.sock '
}
}
parameters {
string(
name: 'BUILD_TARGET',
description: 'F-Droid build target in format appid:versioncode (leave empty for script default)',
defaultValue: ''
)
string(
name: 'FDROIDDATA_REPO',
description: 'Git repository URL for fdroiddata',
defaultValue: 'https://gitlab.com/siddarthkay/fdroiddata.git' // FIXME: point to org repo after https://gitlab.com/fdroid/fdroiddata/-/merge_requests/32193 is merged
)
string(
name: 'FDROIDDATA_BRANCH',
description: 'Branch of fdroiddata repository to use',
defaultValue: 'app.status.mobile'
)
}
options {
timestamps()
/* F-Droid builds can take many hours due to Qt compilation */
timeout(time: 48, unit: 'HOURS')
/* manage how many builds we keep */
buildDiscarder(logRotator(
numToKeepStr: '5',
daysToKeepStr: '30',
artifactNumToKeepStr: '2',
))
/* Allows combined build to copy */
copyArtifactPermission('/status-app/*')
/* Abort old PR builds. */
disableConcurrentBuilds(abortPrevious: isPRBuild)
disableRestartFromStage()
}
environment {
FDROIDDATA_PATH = "${env.WORKSPACE}/fdroiddata"
}
stages {
stage('Cleanup Workspace') {
steps { script {
sh './scripts/clean-git.sh'
} }
}
stage('Clone fdroiddata') {
steps {
sh './fdroid/cleanup-fdroiddata.sh'
sh "git clone --branch '${params.FDROIDDATA_BRANCH}' '${params.FDROIDDATA_REPO}' ${env.FDROIDDATA_PATH}"
/* Update metadata commit to current HEAD so we always build the checked-out revision */
sh "sed -i 's/commit: .*/commit: '`git rev-parse HEAD`'/' ${env.FDROIDDATA_PATH}/metadata/app.status.mobile.yml"
}
}
stage('Build F-Droid') {
steps {
sh "./scripts/fdroid-local-build.sh ${params.BUILD_TARGET ?: ''}"
}
}
stage('Package') {
steps {
script {
sh 'mkdir -p pkg'
sh "cp -v ${env.FDROIDDATA_PATH}/tmp/*.apk pkg/"
env.STATUS_FDROID_APK = utils.findFile("pkg/*.apk")
}
}
}
stage('Upload') {
steps {
script {
env.PKG_URL = s5cmd.upload(env.STATUS_FDROID_APK)
descLinks['F-Droid APK'] = env.PKG_URL
}
}
}
stage('Archive') {
steps {
archiveArtifacts artifacts: 'pkg/*.apk', fingerprint: true
}
}
}
post {
always { script {
jenkins.setBuildDesc(descLinks)
} }
success { script { github.notifyPR(true) } }
failure { script { github.notifyPR(false) } }
cleanup {
sh 'docker ps -aq --filter "name=fdroid-local-build" | xargs -r docker rm -f || true'
sh './fdroid/cleanup-fdroiddata.sh || true'
cleanWs(disableDeferredWipeout: true)
}
}
}
+16
View File
@@ -0,0 +1,16 @@
FROM harbor.status.im/infra/ci-build-containers:linux-base-1.0.1
USER root
# Install Docker CE client (newer than docker.io from apt)
RUN apt-get update && apt-get install -yq --no-install-recommends \
ca-certificates curl gnupg apksigner \
&& install -m 0755 -d /etc/apt/keyrings \
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
&& chmod a+r /etc/apt/keyrings/docker.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu jammy stable" > /etc/apt/sources.list.d/docker.list \
&& apt-get update \
&& apt-get install -yq --no-install-recommends docker-ce-cli \
&& rm -rf /var/lib/apt/lists/*
USER jenkins
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
set -eou pipefail
QT_VERSION="${QT_VERSION:-6.9.2}"
QT_BASE="$HOME/qt/$QT_VERSION"
ANDROID_ABI="arm64-v8a"
ANDROID_API="${ANDROID_API:-28}"
if [[ -z "${ANDROID_NDK_ROOT:-}" ]]; then
echo "Error: ANDROID_NDK_ROOT is not set" >&2
exit 1
fi
if [[ -z "${JAVA_HOME:-}" ]]; then
JAVA_HOME="$(dirname "$(dirname "$(readlink -f "$(which javac)")")")"
export JAVA_HOME
fi
NDK_TOOLCHAIN="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/$(uname -s | tr '[:upper:]' '[:lower:]')-x86_64"
export QT_HOST_PATH="$QT_BASE/gcc_64"
export QTDIR="$QT_BASE/android_arm64_v8a"
export CMAKE_PREFIX_PATH="$QT_BASE/android_arm64_v8a"
export QMAKE="$QT_BASE/android_arm64_v8a/bin/qmake"
export QT_ANDROID_DIR="$QT_BASE/android_arm64_v8a/src/android/java"
export ANDROID_ABI ANDROID_API
export PATH="$QT_BASE/gcc_64/bin:$QT_BASE/android_arm64_v8a/bin:$NDK_TOOLCHAIN/bin:$HOME/go/bin:$PATH"
cd "$BUILD_DIR"
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.34.1
ulimit -n 65536 || true
export USE_SYSTEM_NIM=1
export NIM_SDS_SOURCE_DIR="$BUILD_DIR/vendor/nim-sds"
make deps
make -C mobile apk-fdroid BUILD_VARIANT=release ARCH=arm64 V=3
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
#
# Build OpenSSL for Qt Android build
# This builds OpenSSL as a dependency for Qt compilation
# (separate from mobile/scripts/buildOpenSSL.sh which builds for the app)
#
set -eo pipefail
cd "$OPENSSL_SRCDIR"
if [[ -z "$ANDROID_NDK_ROOT" ]]; then
echo "Error: ANDROID_NDK_ROOT is not set" >&2
exit 1
fi
HOST_OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
export PATH="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/${HOST_OS}-x86_64/bin:$PATH"
ANDROID_API="${ANDROID_API:-28}"
./Configure android-arm64 -D__ANDROID_API__="$ANDROID_API" \
--prefix="$HOME/openssl" \
--openssldir="$HOME/openssl" \
no-shared no-tests
make -j"$(nproc)" 2>&1 | tail -100
make install_sw > /dev/null
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
set -eo pipefail
QT_VERSION="${QT_VERSION:-6.9.2}"
QT_MODULES=qtbase,qtdeclarative,qt5compat,qtmultimedia,qtshadertools,qtimageformats,qtwebview,qtscxml,qtsvg,qtconnectivity,qtwebsockets,qtpositioning,qtlottie,qtwebchannel
(cd "$QT_SRCDIR" && perl init-repository --module-subset="$QT_MODULES")
# Build Qt for host (required as cross-compilation toolchain for Android)
mkdir -p build_qt_host && cd build_qt_host
"$QT_SRCDIR"/configure \
-prefix "$HOME/qt/$QT_VERSION/gcc_64" \
-release \
-opensource \
-confirm-license \
-nomake examples \
-nomake tests \
-- \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_MESSAGE_LOG_LEVEL=WARNING \
-Wno-dev
cmake --build . --parallel "$(nproc)"
cmake --install . > /dev/null
cd "$BUILD_DIR"
rm -rf build_qt_host
# Build Qt for Android arm64-v8a
mkdir -p build_qt_android && cd build_qt_android
"$QT_SRCDIR"/configure \
-prefix "$HOME/qt/$QT_VERSION/android_arm64_v8a" \
-release \
-opensource \
-confirm-license \
-nomake examples \
-nomake tests \
-platform android-clang \
-android-ndk "$ANDROID_NDK_ROOT" \
-android-sdk "$ANDROID_SDK_ROOT" \
-qt-host-path "$HOME/qt/$QT_VERSION/gcc_64" \
-android-abis arm64-v8a \
-openssl-linked \
-- \
-DOPENSSL_ROOT_DIR="$HOME/openssl" \
-DCMAKE_MESSAGE_LOG_LEVEL=WARNING \
-Wno-dev
cmake --build . --parallel "$(nproc)"
cmake --install . > /dev/null
cd "$BUILD_DIR"
rm -rf build_qt_android
find "$QT_SRCDIR" -name "*.o" -delete 2>/dev/null || true
rm -rf "$QT_SRCDIR"/.git "$QT_SRCDIR"/qtwebengine 2>/dev/null || true
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -e
# Remove binary files that F-Droid scanner would flag.
find vendor mobile/vendors -type f -print0 | while IFS= read -r -d '' f; do
if file --mime "$f" | grep -q binary; then
rm -f "$f"
fi
done
# Target only corpora/corpus subdirs, not the fuzz/ dir itself: OpenSSL's configure
# walks fuzz/build.info and fails with "No such file or directory" if fuzz/ is gone.
find vendor mobile/vendors -type d \( -name 'corpora' -o -name 'corpus' \) -print0 | xargs -0 rm -rf
# Remove Cargo.toml files without lockfiles that the scanner flags.
find vendor/QR-Code-generator/rust mobile/vendors/openssl/cloudflare-quiche \
-name "Cargo.toml" -type f -delete
# Remove test/e2e artefacts not needed for the build.
rm -rf test/e2e
rm -f test/e2e_appium/package.json
+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
#
# Cleanup fdroiddata directory
# Uses Docker alpine to remove files owned by Docker user (vagrant/root)
# that Jenkins user cannot delete directly
#
set -e
FDROIDDATA_PATH="${FDROIDDATA_PATH:-${WORKSPACE}/fdroiddata}"
if [ -d "$FDROIDDATA_PATH" ]; then
echo "Removing fdroiddata directory: $FDROIDDATA_PATH"
docker run --rm -v "$(dirname "$FDROIDDATA_PATH"):/workspace" alpine:latest \
sh -c "rm -rf /workspace/$(basename "$FDROIDDATA_PATH")"
fi
+81
View File
@@ -0,0 +1,81 @@
#!/usr/bin/env bash
#
# F-Droid Container Build Script
# This script runs inside the fdroidserver Docker container
# Called by fdroid-local-build.sh
#
set -ex
export home_vagrant=/home/vagrant
export ANDROID_HOME=/opt/android-sdk
export CI_PROJECT_DIR=/fdroiddata
cd /fdroiddata
chown -R "$(whoami)" .
for d in logs tmp unsigned "$home_vagrant"/.android "$home_vagrant"/.gradle "$home_vagrant"/metadata "$home_vagrant"/build; do
test -d "$d" || mkdir -p "$d"
chown -R vagrant "$d" 2>/dev/null || true
done
export fdroidserver=/opt/fdroidserver
rm -rf "$fdroidserver"
mkdir -p "$fdroidserver"
curl -fsSL https://gitlab.com/fdroid/fdroidserver/-/archive/master/fdroidserver-master.tar.gz \
| tar -xz --directory="$fdroidserver" --strip-components=1
export PATH="$fdroidserver:$PATH"
export PYTHONPATH="$fdroidserver:$fdroidserver/examples"
export PYTHONUNBUFFERED=true
sdkmanager "platform-tools" "build-tools;31.0.0"
if [[ -d "$home_vagrant/gradlew-fdroid" ]]; then
git -C "$home_vagrant/gradlew-fdroid" pull || true
else
git clone https://gitlab.com/niccokunzmann/gradlew-fdroid.git "$home_vagrant/gradlew-fdroid" || true
fi
curl -fsSL 'https://gitlab.com/fdroid/fdroid-bootstrap-buildserver/-/raw/master/roles/production_hardening/files/gitconfig' >> /root/.gitconfig
ln -sf "$home_vagrant/.gradle" /fdroiddata/.gradle 2>/dev/null || true
ln -sf /fdroiddata/tmp "$home_vagrant/tmp" 2>/dev/null || true
ln -sf /fdroiddata/srclibs "$home_vagrant/srclibs" 2>/dev/null || true
sysctl fs.inotify.max_user_watches=524288 2>/dev/null || true
export GRADLE_USER_HOME="$home_vagrant/.gradle"
# Install Java 21 (required for Status app)
apt-get install -y sudo openjdk-21-jdk-headless
update-alternatives --set java /usr/lib/jvm/java-21-openjdk-amd64/bin/java || true
# Set up fdroid command with vagrant user
fdroid_cmd="sudo --preserve-env --user vagrant \
env PATH=$fdroidserver:\$PATH \
env PYTHONPATH=$fdroidserver:$fdroidserver/examples \
env PYTHONUNBUFFERED=true \
env TERM=$TERM \
env HOME=$home_vagrant \
fdroid"
appid="${BUILD_TARGET%:*}"
cp -R /fdroiddata/build "$home_vagrant/build" 2>/dev/null || mkdir -p "$home_vagrant/build"
[[ -d "/fdroiddata/metadata/$appid" ]] && cp -R "/fdroiddata/metadata/$appid" "$home_vagrant/metadata/"
cp -R "/fdroiddata/metadata/$appid.yml" "$home_vagrant/metadata/"
chown -R vagrant "$home_vagrant" /fdroiddata
pushd "$home_vagrant"
ln -sf /fdroiddata "$home_vagrant/fdroiddata"
ln -sf /root/.gitconfig "$home_vagrant/.gitconfig" 2>/dev/null || true
eval "$fdroid_cmd fetchsrclibs $BUILD_TARGET --verbose"
rm -f "$home_vagrant/fdroiddata" "$home_vagrant/.gitconfig"
# unset CI to prevent CI-specific behaviors
(unset CI; eval "$fdroid_cmd build --verbose --test --refresh-scanner --on-server --no-tarball $BUILD_TARGET")
popd
ls -la /fdroiddata/tmp/*.apk
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -e
# Generate a single-use keystore for signing the F-Droid APK.
# Intended to be sourced: `source generate-keystore.sh <keystore_path>`
# Exports: FDROID_STORE_FILE, FDROID_STORE_PASSWORD, FDROID_KEY_ALIAS, FDROID_KEY_PASSWORD
if [[ -z "${1:-}" ]]; then
echo "Usage: source generate-keystore.sh <keystore_path>" >&2
return 1 2>/dev/null || exit 1
fi
KEYSTORE_PATH="$1"
FDROID_KEY_ALIAS="status-fdroid"
FDROID_STORE_PASSWORD=$(openssl rand -base64 16)
FDROID_KEY_PASSWORD="$FDROID_STORE_PASSWORD"
mkdir -p "$(dirname "$KEYSTORE_PATH")"
rm -f "$KEYSTORE_PATH"
keytool -genkey -v \
-keyalg RSA \
-keysize 2048 \
-validity 10000 \
-deststoretype pkcs12 \
-dname "CN=Status, OU=Mobile, O=Status Research, L=Zug, S=Zug, C=CH" \
-keystore "$KEYSTORE_PATH" \
-alias "$FDROID_KEY_ALIAS" \
-storepass "$FDROID_STORE_PASSWORD" \
-keypass "$FDROID_KEY_PASSWORD" \
>&2
export FDROID_STORE_FILE="$KEYSTORE_PATH"
export FDROID_STORE_PASSWORD
export FDROID_KEY_ALIAS
export FDROID_KEY_PASSWORD
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
#
# Sign an unsigned APK with zipalign + apksigner.
#
# Called by mobile/scripts/buildApp.sh when Gradle produces an unsigned APK
# (e.g. fdroid builds where signing configs are stripped by fdroid's
# remove_signing_keys).
# See: https://gitlab.com/fdroid/fdroidserver/-/blob/master/fdroidserver/common.py#L3427
#
# Required environment variables:
# APK_OUT_UNSIGNED - path to the unsigned APK produced by Gradle
# APK_OUT - desired output path for the signed APK
# ANDROID_HOME - Android SDK root (for build-tools)
# FDROID_STORE_FILE - keystore path
# FDROID_KEY_ALIAS - key alias in the keystore
# FDROID_STORE_PASSWORD - keystore password
# FDROID_KEY_PASSWORD - key password
#
set -eou pipefail
ZIPALIGN=$(find "$ANDROID_HOME/build-tools" -name zipalign | sort -V | tail -1)
APKSIGNER=$(find "$ANDROID_HOME/build-tools" -name apksigner | sort -V | tail -1)
if [[ -z "$ZIPALIGN" || -z "$APKSIGNER" ]]; then
echo "Error: zipalign or apksigner not found in ANDROID_HOME=$ANDROID_HOME" >&2
exit 1
fi
APK_DIR="$(dirname "$APK_OUT")"
APK_ALIGNED="${APK_DIR}/$(basename "${APK_OUT_UNSIGNED%.apk}")-aligned.apk"
"$ZIPALIGN" -f 4 "$APK_OUT_UNSIGNED" "$APK_ALIGNED"
"$APKSIGNER" sign \
--ks "$FDROID_STORE_FILE" \
--ks-key-alias "$FDROID_KEY_ALIAS" \
--ks-pass "pass:$FDROID_STORE_PASSWORD" \
--key-pass "pass:$FDROID_KEY_PASSWORD" \
--out "$APK_OUT" \
"$APK_ALIGNED"
"$APKSIGNER" verify "$APK_OUT"
echo "APK signed and verified: $APK_OUT"
rm -f "$APK_ALIGNED" "$APK_OUT_UNSIGNED"
+8
View File
@@ -227,3 +227,11 @@ clean-status-desktop-rcc:
@rm -f $(STATUS_DESKTOP_RCC)
@rm -f $(STATUS_DESKTOP)/resources.rcc
@rm -f $(STATUS_DESKTOP)/ui/resources.qrc
.PHONY: apk-fdroid
apk-fdroid:
ifeq ($(OS),android)
@$(MAKE) GRADLE_TARGETS=assembleFdroid $(TARGET)
else
$(error apk-fdroid target is only available for Android)
endif
+6 -2
View File
@@ -93,8 +93,8 @@ android {
minSdkVersion qtMinSdkVersion
targetSdkVersion qtTargetSdkVersion
ndk.abiFilters = qtTargetAbiList.split(",")
// Generate unique versionCode based on minutes since epoch
versionCode = (System.currentTimeMillis() / 60000).toInteger()
// Use FDROID_VERSION_CODE env var if set, otherwise generate from timestamp
versionCode = System.getenv("FDROID_VERSION_CODE")?.toInteger() ?: (System.currentTimeMillis() / 60000).toInteger()
// Set applicationId and app name based on build variant
applicationId = buildVariant == 'release' ? "app.status.mobile" : "app.status.mobile.pr"
resValue "string", "app_name", buildVariant == 'release' ? "Status" : "Status PR"
@@ -117,6 +117,10 @@ android {
minifyEnabled = false
signingConfig signingConfigs.release
}
fdroid {
debuggable = false
minifyEnabled = false
}
debug {
debuggable = true
applicationIdSuffix ".debug"
+1 -1
View File
@@ -67,7 +67,7 @@ WRAPPER_APP_FILES := $(shell find $(WRAPPER_APP) -type f)
STATUS_Q_SCRIPT := $(SCRIPTS_PATH)/buildStatusQ.sh
STATUS_GO_SCRIPT := $(SCRIPTS_PATH)/buildStatusGo.sh
DOTHERSIDE_SCRIPT := $(SCRIPTS_PATH)/buildDOtherSide.sh
OPENSSL_SCRIPT := $(SCRIPTS_PATH)/ios/buildOpenSSL.sh
OPENSSL_SCRIPT := $(SCRIPTS_PATH)/buildOpenSSL.sh
QRCODEGEN_SCRIPT := $(SCRIPTS_PATH)/buildQRCodeGen.sh
STATUS_KEYCARD_QT_SCRIPT := $(SCRIPTS_PATH)/buildStatusKeycardQt.sh
NIM_STATUS_CLIENT_SCRIPT := $(SCRIPTS_PATH)/buildNimStatusClient.sh
+23 -3
View File
@@ -2,6 +2,7 @@
set -eo pipefail
CWD=$(realpath "$(dirname "$0")")
REPO_ROOT=$(realpath "$CWD/../..")
ARCH=${ARCH:-amd64}
SDK=${SDK:-iphonesimulator}
@@ -40,6 +41,11 @@ if [[ "${OS}" == "android" ]]; then
# Export BUILD_VARIANT for build.gradle to pick up
export BUILD_VARIANT
if [[ "$GRADLE_TARGETS" == *"Fdroid"* ]]; then
# shellcheck source=../../fdroid/generate-keystore.sh
source "$REPO_ROOT/fdroid/generate-keystore.sh" "$REPO_ROOT/status-fdroid.keystore"
fi
"$QMAKE_BIN" "$CWD/../wrapperApp/Status.pro" "${QMAKE_CONFIG[@]}" -spec android-clang \
ANDROID_ABIS="${ANDROID_ABI:-arm64-v8a}" VERSION="$VERSION" "${QMAKE_DEFINES[@]}" -after
@@ -64,17 +70,31 @@ if [[ "${OS}" == "android" ]]; then
# Determine build type from GRADLE_TARGETS
if [[ "$GRADLE_TARGETS" == *"Debug"* ]]; then
BUILD_TYPE="debug"
elif [[ "$GRADLE_TARGETS" == *"Fdroid"* ]]; then
BUILD_TYPE="fdroid"
else
BUILD_TYPE="release"
fi
# Gradle output paths
APK_OUT="build/outputs/apk/${BUILD_TYPE}/android-build-${BUILD_TYPE}.apk"
# Gradle output paths (may be signed or unsigned depending on environment)
APK_DIR="build/outputs/apk/${BUILD_TYPE}"
export APK_OUT="${APK_DIR}/android-build-${BUILD_TYPE}.apk"
export APK_OUT_UNSIGNED="${APK_DIR}/android-build-${BUILD_TYPE}-unsigned.apk"
AAB_OUT="build/outputs/bundle/${BUILD_TYPE}/android-build-${BUILD_TYPE}.aab"
# Build with specified gradle targets
# shellcheck disable=SC2086 # intentional word splitting for multiple gradle tasks
./gradlew ${GRADLE_TARGETS} --no-daemon
./gradlew ${GRADLE_TARGETS} --no-daemon --console=plain
echo "APK outputs:"
find build/outputs/apk -name '*.apk' 2>/dev/null || echo "No APKs found"
# If Gradle produced an unsigned APK (e.g. fdroid build where signing configs
# are stripped by fdroid's remove_signing_keys), sign it via the dedicated script.
if [[ ! -f "$APK_OUT" && -f "$APK_OUT_UNSIGNED" && -n "${FDROID_STORE_FILE:-}" ]]; then
echo "Signing unsigned APK..."
"$REPO_ROOT/fdroid/sign-apk.sh"
fi
# Copy whichever artifacts were built
BUILT=""
@@ -17,7 +17,8 @@ SSL_BUILD_DIR=${BUILD_DIR}/openssl-${TARGET}
CRYPTO_OUTPUT_LIB=${LIB_PATH}/libcrypto_3${LIB_EXT}
SSL_OUTPUT_LIB=${LIB_PATH}/libssl_3${LIB_EXT}
PLATFORM_ARGS=""
PLATFORM_CONFIG_ARGS=()
PLATFORM_BUILD_ARGS=()
if [[ "$OS" == "ios" ]]; then
if [[ "$SDK" == "iphonesimulator" ]]; then
@@ -38,30 +39,28 @@ if [[ "$OS" == "ios" ]]; then
fi
if [[ "$OS" == "android" ]]; then
PLATFORM_CONFIG_ARGS="-U__ANDROID_API__ -D__ANDROID_API__=${ANDROID_API}"
PLATFORM_BUILD_ARGS="SHLIB_VERSION_NUMBER="
PLATFORM_CONFIG_ARGS=("-U__ANDROID_API__" "-D__ANDROID_API__=${ANDROID_API}")
PLATFORM_BUILD_ARGS=("SHLIB_VERSION_NUMBER=")
cleanup() {
if [[ "$OS" == "android" ]]; then
patch -d ${OPENSSL} -R -p0 <"${SCRIPT_DIR}/openssl-patch.diff"
fi
patch -d "${OPENSSL}" -R -p0 <"${SCRIPT_DIR}/openssl-patch.diff"
}
trap cleanup EXIT
patch -d ${OPENSSL} -p0 <"${SCRIPT_DIR}/openssl-patch.diff"
patch -d "${OPENSSL}" -p0 <"${SCRIPT_DIR}/openssl-patch.diff"
fi
echo "Building OpenSSL for $TARGET with platform config args $PLATFORM_CONFIG_ARGS"
echo "Building OpenSSL for $TARGET with platform config args ${PLATFORM_CONFIG_ARGS[*]}"
mkdir -p ${SSL_BUILD_DIR}
mkdir -p "${SSL_BUILD_DIR}"
(
cd ${SSL_BUILD_DIR}
cd "${SSL_BUILD_DIR}"
# - no-module: Makes legacy provider built-in to libcrypto (not a separate module)
# - enable-legacy: Enables legacy algorithms including DES
# This is required for GlobalPlatform SCP02 which uses single-DES
# Reference: https://github.com/openssl/openssl/discussions/25793
# Platform-specific config
if [[ "$OS" == "ios" ]]; then
# iOS uses static libraries (.a files)
@@ -70,14 +69,14 @@ mkdir -p ${SSL_BUILD_DIR}
# Android uses shared libraries (.so files)
SHARED_FLAG="shared"
fi
${OPENSSL}/Configure --release "$TARGET" $PLATFORM_CONFIG_ARGS \
"${OPENSSL}"/Configure --release "$TARGET" "${PLATFORM_CONFIG_ARGS[@]}" \
no-module \
enable-legacy \
enable-des \
enable-md2 \
enable-rc5 \
$SHARED_FLAG \
"$SHARED_FLAG" \
no-tests \
no-ui-console
# Rebuilding isn't working with the default target, so we need to clean and build again
+123
View File
@@ -0,0 +1,123 @@
#!/usr/bin/env bash
set -e
show_help() {
cat << EOF
F-Droid Local Build Script
Runs the F-Droid build inside the fdroidserver Docker container.
USAGE:
$0 [OPTIONS] [appid:versioncode]
OPTIONS:
--no-cleanup Don't remove container after build (useful for debugging)
-h, --help Show this help message
EXAMPLES:
$0 # Build default (app.status.mobile:29500000)
$0 app.status.mobile:29500000 # Build specific version
$0 --no-cleanup app.status.mobile:29500000 # Keep container after build
ENVIRONMENT VARIABLES:
FDROIDDATA_PATH Path to fdroiddata repo (default: ./fdroiddata)
NOTE:
The Status app build requires many hours due to Qt compilation.
Make sure you have sufficient disk space (~100GB recommended).
EOF
}
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
DOCKER_IMAGE="registry.gitlab.com/fdroid/fdroidserver:buildserver-trixie"
FDROIDDATA_PATH="${FDROIDDATA_PATH:-$SCRIPT_DIR/fdroiddata}"
DEFAULT_BUILD="app.status.mobile:29500000"
CONTAINER_NAME="fdroid-local-build-$$"
ENTRYPOINT_SCRIPT="$SCRIPT_DIR/../fdroid/fdroid-container-build.sh"
log_info() { echo "[INFO] $1"; }
log_error() { echo "[ERROR] $1" >&2; }
# shellcheck disable=SC2317 # Used by trap
cleanup() {
log_info "Cleaning up container..."
docker rm -f "$CONTAINER_NAME" 2>/dev/null || true
}
trap cleanup EXIT
NO_CLEANUP=false
BUILD_TARGET=""
while [[ $# -gt 0 ]]; do
case $1 in
--no-cleanup)
NO_CLEANUP=true
shift
;;
-h|--help)
show_help
exit 0
;;
-*)
log_error "Unknown option: $1"
show_help
exit 1
;;
*)
BUILD_TARGET="$1"
shift
;;
esac
done
BUILD_TARGET="${BUILD_TARGET:-$DEFAULT_BUILD}"
if [[ ! -d "$FDROIDDATA_PATH" ]]; then
log_error "fdroiddata directory not found at: $FDROIDDATA_PATH"
log_error "Set FDROIDDATA_PATH environment variable to the correct path"
exit 1
fi
FDROIDDATA_PATH=$(cd "$FDROIDDATA_PATH" && pwd)
log_info "Using fdroiddata at: $FDROIDDATA_PATH"
APPID="${BUILD_TARGET%:*}"
if [[ ! -f "$FDROIDDATA_PATH/metadata/${APPID}.yml" ]]; then
log_error "Metadata file not found: $FDROIDDATA_PATH/metadata/${APPID}.yml"
exit 1
fi
log_info "Build target: $BUILD_TARGET"
log_info "Starting F-Droid build container..."
log_info "This may take a very long time (hours) for complex builds like Status app"
log_info "Container name: $CONTAINER_NAME"
if $NO_CLEANUP; then
trap - EXIT
fi
BUILD_EXIT_CODE=0
docker run -i \
--name "$CONTAINER_NAME" \
-v "$FDROIDDATA_PATH:/fdroiddata" \
-v "$ENTRYPOINT_SCRIPT:/entrypoint.sh:ro" \
-e "BUILD_TARGET=$BUILD_TARGET" \
-w /fdroiddata \
"$DOCKER_IMAGE" \
/bin/bash /entrypoint.sh || BUILD_EXIT_CODE=$?
if [[ $BUILD_EXIT_CODE -eq 0 ]]; then
log_info "Build completed successfully!"
log_info "Check $FDROIDDATA_PATH/tmp/ for output files"
else
log_error "Build failed with exit code: $BUILD_EXIT_CODE"
if $NO_CLEANUP; then
log_info "Container preserved for debugging: $CONTAINER_NAME"
log_info "Attach with: docker exec -it $CONTAINER_NAME /bin/bash"
fi
fi
exit $BUILD_EXIT_CODE