提交 70946588 authored 作者: 王鹏飞's avatar 王鹏飞

feat: update iOS publish script with enhanced options and error handling

- Bump version in pubspec.yaml from 1.0.1+4 to 1.0.1+5. - Refactor ios_publish.sh to improve usability and error handling. - Add options for skipping build, submitting only, and setting a timeout. - Implement checks for required commands and configuration files. - Enhance version and build number extraction from pubspec.yaml. - Improve output messages for better clarity during the publishing process.
上级 5345b482
......@@ -64,11 +64,11 @@
<true/>
</dict>
<key>NSCameraUsageDescription</key>
<string>清控紫荆数智学堂需要访问相机</string>
<string>用于拍摄并上传用户头像图片,例如在个人资料页面拍照设置头像。</string>
<key>NSMicrophoneUsageDescription</key>
<string>清控紫荆数智学堂需要访问麦克风</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>清控紫荆数智学堂需要访问照片</string>
<string>用于从相册选择并上传图片,例如设置用户头像、在笔记或讨论中添加图片内容。</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
......
......@@ -58,11 +58,11 @@ class MineController extends GetxController {
value: userInfo.integralNums.toString(),
link: Routes.point,
icon: 'assets/images/point.png'),
// ReadModel(
// name: '紫荆币',
// value: userInfo.beanNums.toString(),
// link: Routes.coin,
// icon: 'assets/images/coin.png'),
ReadModel(
name: '紫荆币',
value: userInfo.beanNums.toString(),
link: Routes.coin,
icon: 'assets/images/coin.png'),
ReadModel(
name: '订单',
value: userInfo.ordersNums.toString(),
......
......@@ -68,12 +68,15 @@ abstract class Access {
}
if (showExplanation && context != null && purpose.isNotEmpty) {
final bool? shouldRequest = await _showPermissionExplanationDialog(
if (!context.mounted) {
return false;
}
await _showPermissionExplanationDialog(
context: context,
permissionName: permissionName,
purpose: purpose,
);
if (shouldRequest != true) {
if (!context.mounted) {
return false;
}
}
......@@ -96,12 +99,12 @@ abstract class Access {
/// 显示权限使用目的说明对话框
/// 对话框不会自动消失,需要用户主动确认
static Future<bool?> _showPermissionExplanationDialog({
static Future<void> _showPermissionExplanationDialog({
required BuildContext context,
required String permissionName,
required String purpose,
}) async {
return await showDialog<bool>(
await showDialog<void>(
context: context,
barrierDismissible: false, // 不允许点击外部关闭
builder: (BuildContext dialogContext) {
......@@ -162,44 +165,21 @@ abstract class Access {
],
),
actions: <Widget>[
Row(
children: [
Expanded(
child: TextButton(
onPressed: () {
Navigator.of(dialogContext).pop(false);
},
child: Text(
'取消',
style: TextStyle(
fontSize: 16.w,
color: Colours.c6,
fontWeight: Fonts.medium,
),
),
SizedBox(
width: double.infinity,
child: TextButton(
onPressed: () {
Navigator.of(dialogContext).pop();
},
child: Text(
'继续',
style: TextStyle(
fontSize: 16.w,
color: Colours.cAB1941,
fontWeight: Fonts.boldSemi,
),
),
Container(
width: 1,
height: 40.w,
color: Colours.cLine,
),
Expanded(
child: TextButton(
onPressed: () {
Navigator.of(dialogContext).pop(true);
},
child: Text(
'确定',
style: TextStyle(
fontSize: 16.w,
color: Colours.cAB1941,
fontWeight: Fonts.boldSemi,
),
),
),
),
],
),
),
],
),
......
差异被折叠。
......@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.1+4
version: 1.0.1+5
environment:
sdk: '>=3.0.0 <4.0.0'
......
#!/bin/bash
set -e
#!/usr/bin/env bash
set -euo pipefail
# =============================================
# 清控紫荆数智学堂 — iOS 发布脚本
# 用法: ./scripts/ios_publish.sh [--submit]
# =============================================
# iOS App Store publish helper.
#
# Default behavior:
# 1. Validate local release inputs.
# 2. Run flutter pub get and pod install.
# 3. Build an App Store IPA.
# 4. Upload the IPA and wait for App Store Connect processing.
#
# Submit behavior:
# ./scripts/ios_publish.sh --submit
# Uploads, waits, updates App Review notes, then submits for review.
APP_ID="6476735372"
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
EXPORT_OPTIONS="$PROJECT_DIR/ios/ExportOptions.plist"
ASC_CONFIG="$PROJECT_DIR/.asc/config.json"
SUBMIT=false
SKIP_BUILD=false
SUBMIT_ONLY=false
TIMEOUT="45m"
# 从 pubspec.yaml 读取版本号
VERSION=$(grep '^version:' "$PROJECT_DIR/pubspec.yaml" | sed 's/version: //' | cut -d'+' -f1)
BUILD=$(grep '^version:' "$PROJECT_DIR/pubspec.yaml" | sed 's/version: //' | cut -d'+' -f2)
REVIEW_NOTES=$'App Review notes:\n\nIn-App Purchases can be found after logging in:\n我的 -> 紫荆币 -> 充值\n\nThe recharge page displays the In-App Purchase products, including 6元, 98元, 298元, and 598元. Select a product and tap 立即充值 to start the Apple In-App Purchase flow.\n\nThis build also updates the camera/photo permission flow: the pre-permission explanation no longer has a cancel button, and the iOS purpose strings now describe specific usage examples.'
# 解析参数
SUBMIT=false
for arg in "$@"; do
case $arg in
--submit) SUBMIT=true ;;
usage() {
cat <<EOF
Usage:
./scripts/ios_publish.sh [--submit] [--skip-build] [--submit-only] [--timeout 45m]
Options:
--submit Submit the processed build for App Review after upload.
--skip-build Reuse an existing IPA under build/ios/ipa/.
--submit-only Update App Review notes and submit an already uploaded build.
--timeout Upload/build processing timeout passed to asc. Default: 45m.
-h, --help Show this help.
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--submit)
SUBMIT=true
shift
;;
--skip-build)
SKIP_BUILD=true
shift
;;
--submit-only)
SUBMIT=true
SUBMIT_ONLY=true
shift
;;
--timeout)
TIMEOUT="${2:-}"
if [[ -z "$TIMEOUT" ]]; then
echo "Missing value for --timeout" >&2
exit 2
fi
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown argument: $1" >&2
usage
exit 2
;;
esac
done
echo "========================================="
echo " App: 清控紫荆数智学堂 ($APP_ID)"
echo " 版本: $VERSION (build $BUILD)"
echo "========================================="
require_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "Missing required command: $1" >&2
exit 1
fi
}
extract_json_id() {
jq -r '
if type == "object" and (.data | type) == "array" then
.data[0].id // empty
elif type == "object" and (.data | type) == "object" then
.data.id // empty
elif type == "object" and (.id | type) == "string" then
.id
else
empty
end
'
}
# 1. 安装依赖
echo "[1/4] 安装 Flutter 依赖..."
cd "$PROJECT_DIR"
flutter pub get
# 2. Pod install
echo "[2/4] 安装 CocoaPods 依赖..."
cd "$PROJECT_DIR/ios"
pod install
require_cmd flutter
require_cmd pod
require_cmd asc
require_cmd jq
# 3. 构建 IPA(Flutter 内置流程:build → archive → export)
echo "[3/4] 构建 IPA..."
cd "$PROJECT_DIR"
if [[ ! -f "$ASC_CONFIG" ]]; then
echo "Missing asc config: $ASC_CONFIG" >&2
exit 1
fi
# 生成 ExportOptions.plist(如果不存在)
EXPORT_OPTIONS="$PROJECT_DIR/ios/ExportOptions.plist"
if [ ! -f "$EXPORT_OPTIONS" ]; then
cat > "$EXPORT_OPTIONS" << 'XMLEOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
<key>destination</key>
<string>export</string>
<key>teamID</key>
<string>MYN43C5WGE</string>
<key>uploadBitcode</key>
<false/>
<key>uploadSymbols</key>
<true/>
<key>signingStyle</key>
<string>automatic</string>
<key>generateAppStoreInformation</key>
<true/>
</dict>
</plist>
XMLEOF
echo "已生成 ExportOptions.plist"
if [[ ! -f "$EXPORT_OPTIONS" ]]; then
echo "Missing export options plist: $EXPORT_OPTIONS" >&2
exit 1
fi
VERSION_LINE="$(grep -E '^version:[[:space:]]*[0-9]+\.[0-9]+\.[0-9]+\+[0-9]+[[:space:]]*$' pubspec.yaml || true)"
if [[ -z "$VERSION_LINE" ]]; then
echo "pubspec.yaml version must look like: version: 1.0.1+5" >&2
exit 1
fi
flutter build ipa --release \
--export-options-plist="$EXPORT_OPTIONS"
VERSION="${VERSION_LINE#version: }"
VERSION="${VERSION%%+*}"
BUILD="${VERSION_LINE##*+}"
BUILD="${BUILD//[[:space:]]/}"
IPA_PATH="$PROJECT_DIR/build/ios/ipa/flutter_book.ipa"
if [ ! -f "$IPA_PATH" ]; then
echo "错误: IPA 文件未生成: $IPA_PATH"
if [[ -z "$VERSION" || -z "$BUILD" ]]; then
echo "Failed to parse version/build from pubspec.yaml" >&2
exit 1
fi
echo "IPA 生成成功: $IPA_PATH"
echo "========================================="
echo " App: 清控紫荆数智学堂 ($APP_ID)"
echo " Version: $VERSION"
echo " Build: $BUILD"
echo " Submit: $SUBMIT"
echo " Submit only: $SUBMIT_ONLY"
echo "========================================="
# 4. 上传到 App Store
echo "[4/4] 上传到 App Store..."
if [ "$SUBMIT" = true ]; then
asc publish appstore \
--app "$APP_ID" \
--ipa "$IPA_PATH" \
--version "$VERSION" \
--submit --confirm
echo "[0/5] Checking App Store Connect auth..."
asc doctor >/dev/null
if [[ "$SUBMIT_ONLY" == true ]]; then
echo "[1-4/5] Submit-only mode; skipping build and upload."
elif [[ "$SKIP_BUILD" != true ]]; then
echo "[1/5] Installing Flutter dependencies..."
flutter pub get
echo "[2/5] Installing CocoaPods dependencies..."
(cd ios && pod install)
echo "[3/5] Building IPA..."
flutter build ipa --release --build-name="$VERSION" --build-number="$BUILD" \
--export-options-plist="$EXPORT_OPTIONS"
else
echo "[1-3/5] Skipping build; reusing existing IPA."
fi
if [[ "$SUBMIT_ONLY" != true ]]; then
mapfile -t IPA_FILES < <(find "$PROJECT_DIR/build/ios/ipa" -maxdepth 1 -name '*.ipa' -type f -print | sort)
if [[ "${#IPA_FILES[@]}" -ne 1 ]]; then
echo "Expected exactly one IPA under build/ios/ipa, found ${#IPA_FILES[@]}:" >&2
printf ' %s\n' "${IPA_FILES[@]}" >&2
exit 1
fi
IPA_PATH="${IPA_FILES[0]}"
echo "IPA: $IPA_PATH"
echo "[4/5] Uploading IPA and waiting for processing..."
asc builds upload \
--app "$APP_ID" \
--ipa "$IPA_PATH"
--ipa "$IPA_PATH" \
--version "$VERSION" \
--build-number "$BUILD" \
--wait \
--verify-timeout 5m
fi
echo ""
echo "========================================="
echo " 上传完成!"
echo " 版本: $VERSION (build $BUILD)"
if [ "$SUBMIT" = true ]; then
echo " 已自动提交审核"
if [[ "$SUBMIT" != true ]]; then
echo "[5/5] Upload complete. Build was not submitted."
echo "Submit later with: ./scripts/ios_publish.sh --submit-only"
exit 0
fi
echo "[5/5] Preparing App Review submission..."
BUILD_JSON="$(asc builds list --app "$APP_ID" --version "$VERSION" --build-number "$BUILD" --platform IOS --processing-state VALID --output json)"
BUILD_ID="$(printf '%s' "$BUILD_JSON" | extract_json_id)"
if [[ -z "$BUILD_ID" ]]; then
echo "Could not resolve processed build id for $VERSION ($BUILD)." >&2
echo "$BUILD_JSON" >&2
exit 1
fi
VERSION_JSON="$(asc versions list --app "$APP_ID" --version "$VERSION" --platform IOS --output json)"
VERSION_ID="$(printf '%s' "$VERSION_JSON" | extract_json_id)"
if [[ -z "$VERSION_ID" ]]; then
echo "Could not resolve App Store version id for $VERSION." >&2
echo "$VERSION_JSON" >&2
exit 1
fi
DETAIL_JSON="$(asc review details-for-version --version-id "$VERSION_ID" --output json || true)"
DETAIL_ID="$(printf '%s' "$DETAIL_JSON" | extract_json_id)"
if [[ -n "$DETAIL_ID" ]]; then
echo "Updating App Review notes..."
asc review details-update --id "$DETAIL_ID" --notes "$REVIEW_NOTES" >/dev/null
else
echo " 未提交审核,可用以下命令提交:"
echo " asc review status --app $APP_ID"
echo " 或运行: $0 --submit"
echo "Could not resolve review detail id; review notes were not updated." >&2
echo "Do not submit until App Review notes include the IAP path below:" >&2
printf '%s\n' "$REVIEW_NOTES" >&2
exit 1
fi
echo "Submitting for App Review..."
asc review submit \
--app "$APP_ID" \
--version "$VERSION" \
--build "$BUILD_ID" \
--confirm
echo "========================================="
echo "Submitted for App Review."
echo "Version: $VERSION ($BUILD)"
echo "========================================="
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论