Add signed Android release build scripts
This commit is contained in:
1 parent
3166ee7a41
commit
2260719ff7
6 files changed
+155
-14
No files matched your search
@@ -7,3 +7,6 @@ build_headless/
|
||||
.idea/
|
||||
local.properties
|
||||
.cxx/
|
||||
|
||||
# Локальный ключ релизной подписи и пароли.
|
||||
/signing/
|
||||
@@ -155,8 +155,24 @@ sdkmanager "platforms;android-36" "build-tools;36.0.0" "ndk;29.0.14206865"
|
||||
./build.sh # clean + сборка + установка на все подключённые устройства
|
||||
./build.sh noinstall # только сборка, без прошивки
|
||||
./build.sh <SN> # clean + сборка + прошивка на конкретный девайс
|
||||
./create_signing_key.sh # однократно создать ключ релизной подписи
|
||||
./build.sh --release noinstall # clean + сборка подписанного релизного APK
|
||||
./build_release.sh # clean + подписанный релиз + установка на все подключённые устройства
|
||||
./build_release.sh <SN> # то же, установка на конкретный девайс
|
||||
```
|
||||
|
||||
Ключ RSA 3072 в PKCS12 (`signing/release.p12`) и случайный пароль
|
||||
(`signing/keystore.properties`) создаются автоматически, без ввода пароля в командную строку.
|
||||
Каталог `signing/` исключён из Git; права каталога — 700, файлов — 600.
|
||||
Сохранить резервную копию **всего каталога `signing/`**: этот ключ нужен для дальнейших обновлений приложения.
|
||||
Повторное создание поверх существующего каталога запрещено скриптом.
|
||||
Для генерации нужны JDK 17+ (`keytool`, учитывается `JAVA_HOME`) и `openssl`.
|
||||
|
||||
Релизный APK: `app/build/outputs/apk/release/app-release.apk`.
|
||||
Прямой вызов `./gradlew :app:assembleRelease` также подписывает APK этим ключом;
|
||||
без настроек подписи release завершается ошибкой. Debug не требует релизного ключа.
|
||||
`./build.sh --release <SN>` собирает и устанавливает релиз на выбранное устройство.
|
||||
|
||||
### APK (Android)
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
import java.util.Properties
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("org.jetbrains.kotlin.plugin.compose")
|
||||
}
|
||||
|
||||
// Ключ и пароли создаёт create_signing_key.sh; пути считаются от каталога настроек.
|
||||
val releaseSigningFile = rootProject.file("signing/keystore.properties")
|
||||
val releaseSigning = Properties().apply {
|
||||
if (releaseSigningFile.isFile) releaseSigningFile.inputStream().use { load(it) }
|
||||
}
|
||||
val validateReleaseSigning by tasks.registering {
|
||||
doLast {
|
||||
check(releaseSigningFile.isFile) {
|
||||
"Release signing settings missing: $releaseSigningFile. Run ./create_signing_key.sh first."
|
||||
}
|
||||
for (key in listOf("storeFile", "storePassword", "keyAlias", "keyPassword")) {
|
||||
check(!releaseSigning.getProperty(key).isNullOrBlank()) { "Release signing: missing $key in $releaseSigningFile" }
|
||||
}
|
||||
val keystore = releaseSigningFile.parentFile.resolve(releaseSigning.getProperty("storeFile"))
|
||||
check(keystore.isFile) { "Release signing keystore not found: $keystore" }
|
||||
logger.lifecycle("Release signing: keystore={}, alias={}", keystore, releaseSigning.getProperty("keyAlias"))
|
||||
}
|
||||
}
|
||||
|
||||
// Фон берём из desktop-чатгуи; drawable-nodpi сохраняет исходный размер плитки.
|
||||
val prepareChatBackground by tasks.registering(Copy::class) {
|
||||
from("../../chatgui/resources/bg.png")
|
||||
@@ -68,12 +89,26 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
create("release") {
|
||||
if (releaseSigningFile.isFile) {
|
||||
storeFile = releaseSigning.getProperty("storeFile")?.let { releaseSigningFile.parentFile.resolve(it) }
|
||||
storePassword = releaseSigning.getProperty("storePassword")
|
||||
keyAlias = releaseSigning.getProperty("keyAlias")
|
||||
keyPassword = releaseSigning.getProperty("keyPassword")
|
||||
storeType = "PKCS12"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks.matching { it.name == "preReleaseBuild" }.configureEach { dependsOn(validateReleaseSigning) }
|
||||
|
||||
// Настоящий JNI callback с отказами JVM; Linux host нужен для ELF --gc-sections, NDK ABI отдельно проверяет сборка APK.
|
||||
val testJniLog by tasks.registering(Exec::class) {
|
||||
|
||||
@@ -1,24 +1,60 @@
|
||||
#!/bin/bash
|
||||
set -eo pipefail
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
export ANDROID_HOME="${ANDROID_HOME:-/home/user/android}"
|
||||
APK=app/build/outputs/apk/debug/app-debug.apk
|
||||
|
||||
echo "Building..."
|
||||
TMP=$(mktemp)
|
||||
./gradlew clean > /dev/null 2>&1
|
||||
if ! ./gradlew assembleDebug > "$TMP" 2>&1; then
|
||||
cat "$TMP"
|
||||
rm -f "$TMP"
|
||||
BUILD_TYPE=Debug
|
||||
NOINSTALL=0
|
||||
DEVICE=""
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--release) BUILD_TYPE=Release ;;
|
||||
noinstall|--no-install) NOINSTALL=1 ;;
|
||||
--help|-h)
|
||||
echo "Usage: $0 [--release] [noinstall | DEVICE_SERIAL]"
|
||||
echo "Default: clean debug build and install on all connected devices."
|
||||
echo "Release: create the key once with ./create_signing_key.sh; then use --release noinstall."
|
||||
exit 0
|
||||
;;
|
||||
-*) echo "ERROR: Unknown option: $arg. Use --help." >&2; exit 1 ;;
|
||||
*)
|
||||
if [[ -n "$DEVICE" ]]; then
|
||||
echo "ERROR: Only one device serial may be specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
DEVICE="$arg"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
if [[ "$NOINSTALL" == 1 && -n "$DEVICE" ]]; then
|
||||
echo "ERROR: Specify either noinstall or a device serial." >&2
|
||||
exit 1
|
||||
fi
|
||||
rm -f "$TMP"
|
||||
echo "Build OK"
|
||||
if [[ "$BUILD_TYPE" == Release && ! -f signing/keystore.properties ]]; then
|
||||
echo "ERROR: Release signing settings missing. Run ./create_signing_key.sh first." >&2
|
||||
exit 1
|
||||
fi
|
||||
VARIANT="${BUILD_TYPE,,}"
|
||||
APK="app/build/outputs/apk/$VARIANT/app-$VARIANT.apk"
|
||||
trap 'echo "ERROR: Android build/install failed at line $LINENO." >&2' ERR
|
||||
|
||||
[ "$1" = "noinstall" ] && exit 0
|
||||
echo "Building $BUILD_TYPE APK..."
|
||||
TMP=$(mktemp)
|
||||
trap 'rm -f "$TMP"' EXIT
|
||||
if ! ./gradlew clean ":app:assemble$BUILD_TYPE" > "$TMP" 2>&1; then
|
||||
cat "$TMP"
|
||||
echo "ERROR: Gradle $BUILD_TYPE build failed." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "$APK" ]]; then
|
||||
echo "ERROR: Gradle succeeded but APK not found: $APK" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Build OK: $PWD/$APK"
|
||||
|
||||
if [ -n "$1" ]; then
|
||||
DEVICES=("$1")
|
||||
[[ "$NOINSTALL" == 1 ]] && exit 0
|
||||
|
||||
if [[ -n "$DEVICE" ]]; then
|
||||
DEVICES=("$DEVICE")
|
||||
else
|
||||
mapfile -t DEVICES < <(adb devices | awk 'NR>1 && $2=="device"{print $1}')
|
||||
fi
|
||||
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
# Сборка и установка подписанного релиза; аргументы передаются общему скрипту сборки.
|
||||
set -euo pipefail
|
||||
exec "$(dirname "$0")/build.sh" --release "$@"
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
# Однократно создаёт ключ релизной подписи и локальные настройки Gradle.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
if [[ "${1:-}" == "--help" ]]; then
|
||||
echo "Usage: $0"
|
||||
echo "Creates signing/release.p12 and signing/keystore.properties (excluded from Git)."
|
||||
exit 0
|
||||
fi
|
||||
if [[ $# -ne 0 ]]; then
|
||||
echo "ERROR: Unexpected arguments. Use $0 --help." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
KEYTOOL="${JAVA_HOME:+$JAVA_HOME/bin/}keytool"
|
||||
for tool in "$KEYTOOL" openssl; do
|
||||
if ! command -v "$tool" > /dev/null; then
|
||||
echo "ERROR: Required tool not found: $tool (install JDK 17+ and OpenSSL)." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
if [[ -e signing || -L signing ]]; then
|
||||
echo "ERROR: signing already exists; refusing to replace the release key or its settings." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
umask 077
|
||||
mkdir signing
|
||||
trap 'echo "ERROR: Key creation failed at line $LINENO; inspect signing/ before retrying." >&2' ERR
|
||||
export UTUN_KEYSTORE_PASSWORD
|
||||
UTUN_KEYSTORE_PASSWORD=$(openssl rand -hex 32)
|
||||
# Сохраняем пароль до генерации ключа, чтобы ошибка записи не оставила ключ без пароля.
|
||||
cat > signing/keystore.properties <<EOF
|
||||
storeFile=release.p12
|
||||
storePassword=$UTUN_KEYSTORE_PASSWORD
|
||||
keyAlias=utun-release
|
||||
keyPassword=$UTUN_KEYSTORE_PASSWORD
|
||||
EOF
|
||||
echo "Creating release signing key: signing/release.p12 (RSA 3072, validity 10000 days)..."
|
||||
"$KEYTOOL" -genkeypair -noprompt -keystore signing/release.p12 -storetype PKCS12 \
|
||||
-alias utun-release -keyalg RSA -keysize 3072 -sigalg SHA256withRSA -validity 10000 \
|
||||
-dname "CN=uTun Android Release" -storepass:env UTUN_KEYSTORE_PASSWORD -keypass:env UTUN_KEYSTORE_PASSWORD
|
||||
unset UTUN_KEYSTORE_PASSWORD
|
||||
echo "Release signing key and settings created; permissions: directory 700, files 600."
|
||||
echo "Back up the entire signing/ directory securely; keep it for all future application updates."
|
||||
echo "Build a signed APK: ./build.sh --release noinstall"
|
||||
Reference in new issue
Block a user