feat(android): add Flutter WebView app with Docker release builds

This commit is contained in:
2026-10-15 16:38:19 +08:00
parent 2745f2491b
commit 589937213f
43 changed files with 1608 additions and 0 deletions
Executable
+89
View File
@@ -0,0 +1,89 @@
#!/usr/bin/env bash
# DSH 工作台 APK 构建:Docker 内完成,宿主机无需安装 Flutter/Android SDK
# 用法:./build.sh URL --keystore PATH --alias NAME [--no-cache]
# 产出:build/app/outputs/flutter-apk/app-*.apk
set -euo pipefail
if [ "$#" -lt 1 ]; then
echo '用法:./build.sh https://你的域名 --keystore PATH --alias NAME [--no-cache]' >&2
exit 64
fi
DSH_SITE_URL=$1
shift
case "$DSH_SITE_URL" in
https://?*) ;;
*) echo '必须传入完整的 HTTPS 站点地址' >&2; exit 64 ;;
esac
case "$DSH_SITE_URL" in
*[[:space:]]*) echo '站点地址不能包含空白字符' >&2; exit 64 ;;
esac
REBUILD=false
KEYSTORE=''
DSH_KEY_ALIAS=''
while [ "$#" -gt 0 ]; do
case "$1" in
--keystore|--alias)
if [ "$#" -lt 2 ] || [ -z "$2" ]; then
echo "$1 缺少参数值" >&2; exit 64
fi
if [ "$1" = '--keystore' ]; then KEYSTORE=$2; else DSH_KEY_ALIAS=$2; fi
shift 2 ;;
--no-cache) REBUILD=true; shift ;;
*) echo "未知参数:$1" >&2; exit 64 ;;
esac
done
if [ -z "$KEYSTORE" ] || [ -z "$DSH_KEY_ALIAS" ]; then
echo '必须提供 --keystore PATH 和 --alias NAME;release 不使用 debug 签名' >&2
exit 64
fi
if [ ! -f "$KEYSTORE" ] || [ ! -r "$KEYSTORE" ]; then
echo '密钥库文件不存在或不可读' >&2; exit 64
fi
# 相对路径基于调用者的工作目录,不基于脚本目录。
KEYSTORE="$(cd -- "$(dirname -- "$KEYSTORE")" && pwd)/$(basename -- "$KEYSTORE")"
case "$KEYSTORE" in
*,*) echo '密钥库路径不能含逗号(Docker mount 分隔符)' >&2; exit 64 ;;
esac
if [ -z "${DSH_STORE_PASSWORD:-}" ]; then
if [ ! -t 0 ]; then
echo '非交互构建请通过受保护的环境变量提供 DSH_STORE_PASSWORD' >&2; exit 64
fi
read -r -s -p '密钥库密码:' DSH_STORE_PASSWORD
printf '\n'
fi
if [ -z "$DSH_STORE_PASSWORD" ]; then echo '密码不能为空' >&2; exit 64; fi
if [ -z "${DSH_KEY_PASSWORD:-}" ]; then
if [ -t 0 ]; then
read -r -s -p '私钥密码(回车使用密钥库密码):' DSH_KEY_PASSWORD
printf '\n'
fi
DSH_KEY_PASSWORD=${DSH_KEY_PASSWORD:-$DSH_STORE_PASSWORD}
fi
export DSH_STORE_PASSWORD DSH_KEY_PASSWORD DSH_KEY_ALIAS
cd "$(dirname "$0")"
IMAGE=dsh-appbuilder:3.47.7
if [ "$REBUILD" = true ] || ! docker image inspect "$IMAGE" > /dev/null 2>&1; then
echo "==> 构建镜像 $IMAGE(首次较慢,需下载 Flutter/Android SDK)"
BUILD_ARGS=()
if [ "$REBUILD" = true ]; then BUILD_ARGS+=(--no-cache); fi
docker build --build-arg http_proxy --build-arg https_proxy --build-arg no_proxy "${BUILD_ARGS[@]}" -t "$IMAGE" .
fi
echo "==> 在容器内构建 release APK"
docker run --rm \
-v "$PWD":/project \
-w /project \
--mount "type=bind,source=$KEYSTORE,target=/run/dsh-signing/release.keystore,readonly" \
-e DSH_KEYSTORE=/run/dsh-signing/release.keystore \
-e DSH_KEY_ALIAS -e DSH_STORE_PASSWORD -e DSH_KEY_PASSWORD \
-e http_proxy -e https_proxy -e no_proxy \
-e PUB_CACHE=/opt/pub-cache \
-e DSH_SITE_URL="$DSH_SITE_URL" \
"$IMAGE" \
sh -ec 'dart tool/validate_site_url.dart "$DSH_SITE_URL"; flutter pub get; flutter test --dart-define="DSH_SITE_URL=$DSH_SITE_URL"; flutter build apk --dart-define="DSH_SITE_URL=$DSH_SITE_URL" --release --split-per-abi --obfuscate --split-debug-info=build/symbols'
echo "==> 完成,产物:"
ls -lh build/app/outputs/flutter-apk/*.apk