#!/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-arg JAVA_TOOL_OPTIONS "${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 JAVA_TOOL_OPTIONS \ -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