fix and update

This commit is contained in:
lendry
2026-07-02 16:19:06 +03:00
parent 4306d0ce37
commit 2f76b28339
9 changed files with 262 additions and 9 deletions

View File

@@ -30,22 +30,39 @@ npm --workspace @lendry/tauri-app run ios:init
## Docker APK build
При запуске общего `docker compose up --build` сервис `tauri-apk-builder` собирает Android APK и кладёт файл сюда:
Сборка подписанного release APK через `install.sh` (Docker + Android SDK + release keystore):
```bash
./install.sh --build-apk
```
Или пункт **12** в интерактивном меню `./install.sh`.
Готовый файл:
```text
apps/frontend/public/downloads/lendry-id.apk
```
Keystore и пароли создаются автоматически при первой сборке и сохраняются в `tauri_app/.secrets/` (не коммитятся в git).
После этого кнопка «Скачать приложение» на странице входа скачивает APK через:
```text
/download/android
```
Собрать только APK:
Собрать только через Docker-образ (без install.sh):
```bash
docker compose up --build tauri-apk-builder
docker build -f tauri_app/Dockerfile.apk -t lendry-id-tauri-apk-builder .
mkdir -p apps/frontend/public/downloads tauri_app/.secrets
docker run --rm \
-v "$(pwd)/apps/frontend/public/downloads:/out" \
-v "$(pwd)/tauri_app/.secrets:/workspace/tauri_app/.secrets" \
-e PUBLIC_API_URL=https://sso.example.ru/idp-api \
-e PUBLIC_FRONTEND_URL=https://sso.example.ru \
lendry-id-tauri-apk-builder
```
## Переменные окружения

View File

@@ -50,17 +50,26 @@ echo "==> Настройка Android SDK (offline, без загрузки platf
echo "==> Настройка offline Gradle wrapper (если bundled zip доступен)"
/workspace/tauri_app/docker/install-offline-gradle.sh /workspace/tauri_app/src-tauri/gen/android
echo "==> Сборка Android APK"
echo "==> Подготовка keystore для release-подписи APK"
/workspace/tauri_app/docker/ensure-android-keystore.sh
/workspace/tauri_app/docker/patch-android-signing.sh /workspace/tauri_app/src-tauri/gen/android
echo "==> Сборка Android APK (release, signed)"
npm run tauri -- android build --apk
APK_PATH="$(find /workspace/tauri_app/src-tauri/gen/android -type f -name '*.apk' | sort | tail -n 1 || true)"
APK_PATH="$(find /workspace/tauri_app/src-tauri/gen/android -type f -name '*release*.apk' ! -name '*unsigned*' | sort | tail -n 1 || true)"
if [[ -z "${APK_PATH}" ]]; then
APK_PATH="$(find /workspace/tauri_app/src-tauri/gen/android -type f -name '*.apk' ! -name '*unsigned*' | sort | tail -n 1 || true)"
fi
if [ -z "${APK_PATH}" ] || [ ! -f "${APK_PATH}" ]; then
echo "APK не найден после сборки" >&2
find /workspace/tauri_app/src-tauri/gen/android -maxdepth 8 -type f | sort >&2 || true
find /workspace/tauri_app/src-tauri/gen/android -maxdepth 8 -type f -name '*.apk' | sort >&2 || true
exit 1
fi
/workspace/tauri_app/docker/verify-apk-signature.sh "${APK_PATH}"
cp "${APK_PATH}" /out/lendry-id.apk
chmod 0644 /out/lendry-id.apk

View File

@@ -0,0 +1,79 @@
#!/usr/bin/env bash
# Создаёт release keystore для подписи APK, если его ещё нет.
set -euo pipefail
SECRETS_DIR="${ANDROID_SECRETS_DIR:-/workspace/tauri_app/.secrets}"
KEYSTORE_FILE="${ANDROID_KEYSTORE_FILE:-${SECRETS_DIR}/lendry-id-release.keystore}"
KEY_ALIAS="${ANDROID_KEY_ALIAS:-lendry-id}"
CREDENTIALS_FILE="${SECRETS_DIR}/signing-credentials.env"
KEYSTORE_PROPS_TEMPLATE="${SECRETS_DIR}/keystore.properties.template"
mkdir -p "${SECRETS_DIR}"
chmod 700 "${SECRETS_DIR}" || true
if [[ -f "${KEYSTORE_FILE}" ]]; then
echo "==> Android keystore уже существует: ${KEYSTORE_FILE}"
if [[ ! -f "${PROPS_TEMPLATE}" && -f "${CREDENTIALS_FILE}" ]]; then
# shellcheck disable=SC1090
source "${CREDENTIALS_FILE}"
cat > "${PROPS_TEMPLATE}" <<EOF
storePassword=${ANDROID_KEYSTORE_PASSWORD}
keyPassword=${ANDROID_KEY_PASSWORD}
keyAlias=${ANDROID_KEY_ALIAS}
storeFile=../../../.secrets/lendry-id-release.keystore
EOF
chmod 600 "${PROPS_TEMPLATE}" || true
echo "==> Восстановлен keystore.properties.template из signing-credentials.env"
fi
exit 0
fi
if ! command -v keytool >/dev/null 2>&1; then
echo "keytool не найден — нужен JDK для генерации keystore" >&2
exit 1
fi
gen_secret() {
if command -v openssl >/dev/null 2>&1; then
openssl rand -base64 32 | tr -dc 'A-Za-z0-9' | head -c 24
else
date +%s%N | sha256sum | head -c 24
fi
}
STORE_PASSWORD="$(gen_secret)"
KEY_PASSWORD="$(gen_secret)"
echo "==> Генерация release keystore для подписи APK..."
keytool -genkeypair -v \
-storetype PKCS12 \
-keystore "${KEYSTORE_FILE}" \
-alias "${KEY_ALIAS}" \
-keyalg RSA \
-keysize 2048 \
-validity 10000 \
-storepass "${STORE_PASSWORD}" \
-keypass "${KEY_PASSWORD}" \
-dname "CN=Lendry ID, OU=Mobile, O=Lendry, C=RU"
chmod 600 "${KEYSTORE_FILE}" || true
cat > "${CREDENTIALS_FILE}" <<EOF
# Сгенерировано $(date -u +"%Y-%m-%dT%H:%M:%SZ") — храните в безопасном месте.
ANDROID_KEYSTORE_FILE=${KEYSTORE_FILE}
ANDROID_KEY_ALIAS=${KEY_ALIAS}
ANDROID_KEYSTORE_PASSWORD=${STORE_PASSWORD}
ANDROID_KEY_PASSWORD=${KEY_PASSWORD}
EOF
chmod 600 "${CREDENTIALS_FILE}" || true
cat > "${KEYSTORE_PROPS_TEMPLATE}" <<EOF
storePassword=${STORE_PASSWORD}
keyPassword=${KEY_PASSWORD}
keyAlias=${KEY_ALIAS}
storeFile=../../../.secrets/lendry-id-release.keystore
EOF
chmod 600 "${KEYSTORE_PROPS_TEMPLATE}" || true
echo "==> Keystore создан: ${KEYSTORE_FILE}"
echo "==> Пароли сохранены: ${CREDENTIALS_FILE}"

View File

@@ -0,0 +1,61 @@
#!/usr/bin/env bash
# Подключает release-подпись APK через keystore.properties + Gradle script.
set -euo pipefail
ANDROID_GEN="${1:-/workspace/tauri_app/src-tauri/gen/android}"
SECRETS_DIR="${ANDROID_SECRETS_DIR:-/workspace/tauri_app/.secrets}"
KEYSTORE_FILE="${ANDROID_KEYSTORE_FILE:-${SECRETS_DIR}/lendry-id-release.keystore}"
APP_GRADLE="${ANDROID_GEN}/app/build.gradle.kts"
KEYSTORE_PROPS="${ANDROID_GEN}/keystore.properties"
SIGNING_SCRIPT="${ANDROID_GEN}/app/release-signing.gradle.kts"
PROPS_TEMPLATE="${SECRETS_DIR}/keystore.properties.template"
if [[ ! -d "${ANDROID_GEN}" ]]; then
echo "Android project not found: ${ANDROID_GEN}" >&2
exit 1
fi
if [[ ! -f "${KEYSTORE_FILE}" ]]; then
echo "Keystore не найден: ${KEYSTORE_FILE}. Запустите ensure-android-keystore.sh" >&2
exit 1
fi
if [[ -f "${PROPS_TEMPLATE}" ]]; then
cp "${PROPS_TEMPLATE}" "${KEYSTORE_PROPS}"
else
echo "keystore.properties.template не найден в ${SECRETS_DIR}" >&2
exit 1
fi
chmod 600 "${KEYSTORE_PROPS}" || true
cat > "${SIGNING_SCRIPT}" <<'EOF'
import com.android.build.api.dsl.ApplicationExtension
import java.util.Properties
val keystoreProperties = Properties().apply {
rootProject.file("keystore.properties").inputStream().use { load(it) }
}
extensions.configure<ApplicationExtension>("android") {
signingConfigs {
create("release") {
keyAlias = keystoreProperties.getProperty("keyAlias")
keyPassword = keystoreProperties.getProperty("keyPassword")
storeFile = rootProject.file(keystoreProperties.getProperty("storeFile"))
storePassword = keystoreProperties.getProperty("storePassword")
}
}
buildTypes {
getByName("release") {
signingConfig = signingConfigs.getByName("release")
}
}
}
EOF
if ! grep -q 'release-signing.gradle.kts' "${APP_GRADLE}"; then
printf '\napply(from = "release-signing.gradle.kts")\n' >> "${APP_GRADLE}"
fi
echo "==> Android release signing patched (${KEYSTORE_PROPS})"

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail
APK_PATH="${1:-}"
ANDROID_HOME="${ANDROID_HOME:-/opt/android-sdk}"
if [[ -z "${APK_PATH}" || ! -f "${APK_PATH}" ]]; then
echo "APK не найден для проверки подписи: ${APK_PATH}" >&2
exit 1
fi
APKSIGNER="$(find "${ANDROID_HOME}/build-tools" -name apksigner -type f 2>/dev/null | sort -V | tail -n 1 || true)"
if [[ -z "${APKSIGNER}" ]]; then
echo "apksigner не найден в ${ANDROID_HOME}/build-tools" >&2
exit 1
fi
echo "==> Проверка подписи APK: ${APK_PATH}"
"${APKSIGNER}" verify --verbose "${APK_PATH}"
echo "==> Подпись APK подтверждена (apksigner verify)"