diff --git a/.env.development b/.env.development index fb24b9b4..bdea487e 100644 --- a/.env.development +++ b/.env.development @@ -4,7 +4,7 @@ ENV = 'development' # VITE_APP_BASE_API_Url = 'https://aits.jlbank.com.cn:7001' -VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7002' +VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001' # VITE_APP_BASE_API_Url = 'http://192.168.247.200' # VITE_APP_BASE_API_Url = 'http://aitscdn.jlbank.com.cn:7001' # VITE_APP_BASE_API_Url = 'http://192.168.108.129' diff --git a/devops/iso/新的/AuthKey_ACRB9XFL9G.p8 b/devops/iso/新的/AuthKey_ACRB9XFL9G.p8 new file mode 100644 index 00000000..c209d906 --- /dev/null +++ b/devops/iso/新的/AuthKey_ACRB9XFL9G.p8 @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgZfELkUrcpqqAVlQg +UfKNlPbySAnafYL+vEndTe9/xoqgCgYIKoZIzj0DAQehRANCAATBz1/RZd+1AEgU +VfnoB1PNUMynS8FQ/zJdTcZ25qEuu6WVkWb82Ltmb1lIRSZpRWEbpQzL+7X6zQ95 +yjRmUksW +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/devops/iso/新的/build_ios.py b/devops/iso/新的/build_ios.py new file mode 100644 index 00000000..cc7f5ee5 --- /dev/null +++ b/devops/iso/新的/build_ios.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 +import os +import subprocess +import sys +import shutil +from datetime import datetime + +# 颜色定义 +RED = '\033[0;31m' +GREEN = '\033[0;32m' +YELLOW = '\033[1;33m' +BLUE = '\033[0;34m' +NC = '\033[0m' # 无颜色 + +def run_command(command, check=True, capture_output=False): + """执行系统命令并返回结果""" + try: + if capture_output: + result = subprocess.run( + command, + shell=True, + check=check, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + return result + else: + subprocess.run(command, shell=True, check=check) + return None + except subprocess.CalledProcessError as e: + print(f"{RED}❌ 命令执行失败: {e.stderr}{NC}") + sys.exit(1) + +def main(): + # 检查参数 + if len(sys.argv) < 4: + print(f"{RED}错误:参数不足!{NC}") + print(f"使用方法:{YELLOW}{sys.argv[0]} {NC}") + print(f"示例:{YELLOW}{sys.argv[0]} sit 1.0.0 1{NC}") + sys.exit(1) + + # 解析参数(第一个参数为target_branch,原参数顺延) + target_branch = sys.argv[1] + marketing_version = sys.argv[2] + current_project_version = sys.argv[3] + + # ============================================== + # 配置参数 - 请根据实际情况修改以下参数 + # ============================================== + # 钥匙串配置 + keychain_password = "1234567" # 钥匙串密码 + keychain_path = f"{os.path.expanduser('~')}/Library/Keychains/login.keychain-db" # 钥匙串路径 + + # 项目路径配置(可根据需要结合target_branch调整路径) + script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) + parent_dir = os.path.dirname(script_dir) + work_dir = os.path.join(parent_dir, "build", target_branch, 'Ios','HBuilder-Hello') + project_dir = os.path.abspath(work_dir) + project_name = "HBuilder-Hello.xcodeproj" # Xcode项目文件名 + scheme_name = "HBuilder" # 构建方案名称 + configuration = "Release" # 构建配置 + + + # 构建输出配置 + build_dir = f"{os.path.expanduser('~')}/Desktop/lx_uniapp/build" # 构建输出目录 + archive_name = "HBuilder.xcarchive" # 归档文件名 + ipa_dir_name = f"{target_branch}_ipa_production" # IPA输出目录名(带分支名) + derived_data_dir = f"{os.path.expanduser('~')}/Library/Developer/Xcode/DerivedData/*" # 衍生数据目录 + + # 签名配置 + code_sign_identity = "Apple Distribution: Bank of Jilin Co., Ltd. (2EP6SRRN43)" # 签名标识 + provisioning_profile = "e3487fe1-19dc-4fc7-90df-11caf27da0c6" # 配置文件UUID + development_team = "2EP6SRRN43" # 开发团队ID + code_signing_required = "YES" # 是否需要签名 + + # 导出配置 + export_options_plist = os.path.join(script_dir, "build_ios_config", "ExportOptions.plist") + # 转换为绝对路径(跨平台兼容) + export_options_plist = os.path.abspath(export_options_plist) + # ============================================== + + # 显示参数信息 + print(f"\n{BLUE}===== 构建参数信息 ====={NC}") + print(f"▶ 目标分支: {YELLOW}{target_branch}{NC}") + print(f"▶ 市场版本号: {YELLOW}{marketing_version}{NC}") + print(f"▶ 内部版本号: {YELLOW}{current_project_version}{NC}") + print(f"▶ 项目目录: {YELLOW}{project_dir}{NC}\n") + + # 0. 解锁钥匙串并授予权限 + print(f"{BLUE}🔑 解锁钥匙串...{NC}") + run_command(f"security unlock-keychain -p '{keychain_password}' '{keychain_path}'") + + # 1. 进入项目目录 + print(f"{BLUE}📂 进入项目目录: {project_dir}{NC}") + try: + os.chdir(project_dir) + except OSError as e: + print(f"{RED}❌ 无法进入项目目录: {e}{NC}") + sys.exit(1) + + # 2. 清理旧构建 + print(f"{BLUE}🧹 清理旧构建...{NC}") + run_command( + f"xcodebuild -project '{project_name}' " + f"-scheme '{scheme_name}' " + f"-configuration '{configuration}' clean" + ) + + # 3. 删除上次构建内容 + print(f"{BLUE}🗑️ 删除上次构建内容...{NC}") + # 拼接路径 + + ipa_dir_path = f"{build_dir}/{ipa_dir_name}" + archive_path = f"{build_dir}/{ipa_dir_name}/{archive_name}" + # 删除文件/目录 + for path in [ipa_dir_path, derived_data_dir]: + if os.path.exists(path) or '*' in path: # 处理通配符 + run_command(f"rm -rf '{path}'", check=False) + + # 4. 归档项目 + print(f"{BLUE}📦 开始归档项目...{NC}") + run_command( + f"xcodebuild -project '{project_name}' " + f"-scheme '{scheme_name}' " + f"-configuration '{configuration}' " + f"-archivePath '{archive_path}' " + f"MARKETING_VERSION='{marketing_version}' " + f"CURRENT_PROJECT_VERSION='{current_project_version}' " + f"-destination 'generic/platform=iOS' " + f"CODE_SIGN_IDENTITY='{code_sign_identity}' " + f"PROVISIONING_PROFILE='{provisioning_profile}' " + f"DEVELOPMENT_TEAM='{development_team}' " + f"CODE_SIGNING_REQUIRED='{code_signing_required}' " + f"OTHER_CFLAGS='' " + f"OTHER_CPLUSPLUS_FLAGS='' " + f"OTHER_SWIFT_FLAGS='' " + f"archive" + ) + + # 5. 导出IPA文件 + print(f"{BLUE}📤 导出IPA文件...{NC}") + run_command( + f"xcodebuild -exportArchive " + f"-archivePath '{archive_path}' " + f"-exportPath '{build_dir}/{ipa_dir_name}' " + f"-exportOptionsPlist '{export_options_plist}'" + ) + + print(f"\n{GREEN}🎉 构建完成!IPA文件已导出至: {build_dir}/{ipa_dir_name}{NC}") + # 6. 复制并重命名IPA文件到共享目录 + print(f"{BLUE}📋 复制IPA文件到共享目录...{NC}") + + # 定义共享目录路径 + share_dir = "/Users/Shared/lx_uniapp_build_for_share" + + # 确保共享目录存在(不存在则创建) + os.makedirs(share_dir, exist_ok=True) + + # 获取当前时间(格式:YYYYMMDDHHMM) + current_time = datetime.now().strftime("%Y%m%d%H%M") + + # 构建新文件名(jax-<分支名>-<版本号>-<当前时间>.ipa) + new_ipa_name = f"jax-{target_branch}-{current_project_version}-{current_time}.ipa" + + # 源IPA文件路径(基于之前的导出路径) + source_ipa = os.path.join(build_dir, ipa_dir_name, "HBuilder.ipa") + + # 目标IPA文件路径 + target_ipa = os.path.join(share_dir, new_ipa_name) + + # 复制并覆盖文件 + try: + shutil.copy2(source_ipa, target_ipa) # copy2 会保留文件元数据 + print(f"{GREEN}✅ IPA文件已复制至共享目录:{target_ipa}{NC}") + except FileNotFoundError: + print(f"{RED}❌ 源IPA文件不存在:{source_ipa}{NC}") + except Exception as e: + print(f"{RED}❌ 复制IPA文件失败:{str(e)}{NC}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/devops/iso/新的/build_ios_config/DistributionSummary.plist b/devops/iso/新的/build_ios_config/DistributionSummary.plist new file mode 100644 index 00000000..5587ce10 --- /dev/null +++ b/devops/iso/新的/build_ios_config/DistributionSummary.plist @@ -0,0 +1,59 @@ + + + + + HBuilder.ipa + + + architectures + + arm64 + + buildNumber + 114 + certificate + + SHA1 + D24C7C1BFC6616660E24A8893EF27E23D1C6109F + dateExpires + 2026/7/18 + type + Apple Distribution + + entitlements + + application-identifier + 2EP6SRRN43.cn.com.jlbank.aits + beta-reports-active + + com.apple.developer.team-identifier + 2EP6SRRN43 + get-task-allow + + + name + HBuilder.app + profile + + UUID + e3487fe1-19dc-4fc7-90df-11caf27da0c6 + dateExpires + 2026/7/18 + name + jiaixue-pro + + symbols + + team + + id + 2EP6SRRN43 + name + + + versionNumber + 0.2.1 + + + + diff --git a/devops/iso/新的/build_ios_config/ExportOptions.plist b/devops/iso/新的/build_ios_config/ExportOptions.plist new file mode 100644 index 00000000..5e2a3068 --- /dev/null +++ b/devops/iso/新的/build_ios_config/ExportOptions.plist @@ -0,0 +1,31 @@ + + + + + destination + export + generateAppStoreInformation + + manageAppVersionAndBuildNumber + + method + app-store-connect + provisioningProfiles + + cn.com.jlbank.aits + e3487fe1-19dc-4fc7-90df-11caf27da0c6 + + signingCertificate + Apple Distribution: Bank of Jilin Co., Ltd. (2EP6SRRN43) + signingStyle + manual + stripSwiftSymbols + + teamID + 2EP6SRRN43 + testFlightInternalTestingOnly + + uploadSymbols + + + diff --git a/devops/iso/新的/git_update_remote.sh b/devops/iso/新的/git_update_remote.sh new file mode 100644 index 00000000..8c7947db --- /dev/null +++ b/devops/iso/新的/git_update_remote.sh @@ -0,0 +1,88 @@ +#!/bin/bash +# 脚本名称:sync_remote_branch.sh +# 功能描述:修复origin/sit引用问题,强制同步远程分支到本地develop +# 使用方法:./sync_remote_branch.sh <远程分支名> + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +# 检查参数 +if [ $# -eq 0 ]; then + echo -e "${RED}错误:未传入分支名称参数!${NC}" + echo -e "使用方法:${YELLOW}$0 <远程分支名>${NC}" + exit 1 +fi +echo -e "▶ 先设置内网网络\n" +sudo route add -host 25.13.9.101 25.64.32.254 +# 配置参数 +TARGET_BRANCH="$1" +WORK_DIR="Desktop/lx_uniapp/build/git_work_code" +REMOTE_NAME="origin" +LOCAL_BRANCH="develop" + +echo -e "\n${YELLOW}===== 开始执行分支同步脚本 =====${NC}" +echo -e "▶ 远程目标分支:${YELLOW}${TARGET_BRANCH}${NC}" +echo -e "▶ 本地工作目录:${YELLOW}${WORK_DIR}${NC}\n" + +# 1. 进入工作目录 +echo -e "${YELLOW}1. 检查并进入工作目录...${NC}" +if [ ! -d "$WORK_DIR" ]; then + echo -e "${RED}错误:工作目录不存在!${NC}" + exit 1 +fi +cd "$WORK_DIR" || { + echo -e "${RED}错误:无法进入工作目录!${NC}" + exit 1 +} +echo -e "✅ 成功进入工作目录:$(pwd)\n" + +# 2. 检查Git仓库 +echo -e "${YELLOW}2. 检查Git仓库...${NC}" +if [ ! -d ".git" ]; then + echo -e "${RED}错误:当前目录不是Git仓库!${NC}" + exit 1 +fi +echo -e "✅ 确认是Git仓库\n" + +# 3. 关键修复:获取远程分支完整信息(不限制深度) +echo -e "${YELLOW}3. 完整获取远程所有分支完整信息...${NC}" +# 先移除可能存在的浅层克隆限制 +git config --unset core.depth +# 完整拉取所有分支信息 +if ! git fetch "$REMOTE_NAME" "+refs/heads/*:refs/remotes/$REMOTE_NAME/*"; then + echo -e "${RED}错误:拉取远程分支信息失败!${NC}" + exit 1 +fi +echo -e "✅ 远程分支信息拉取完成\n" + +# 4. 再次检查远程分支是否存在 +echo -e "${YELLOW}4. 验证远程 ${TARGET_BRANCH} 分支...${NC}" +if ! git show-ref --verify --quiet "refs/remotes/$REMOTE_NAME/$TARGET_BRANCH"; then + echo -e "${RED}错误:远程确实不存在 ${TARGET_BRANCH} 分支!${NC}" + echo -e "远程所有分支列表:" + git branch -r + exit 1 +fi +echo -e "✅ 确认远程存在 ${YELLOW}${TARGET_BRANCH}${NC} 分支\n" + +# 5. 同步到本地develop分支 +echo -e "${YELLOW}5. 同步远程 ${TARGET_BRANCH} 到本地 ${LOCAL_BRANCH}...${NC}" +echo -e "${YELLOW}⚠️ 警告:将覆盖本地develop分支所有内容!${NC}" + +# 确保本地有develop分支 +git checkout -B "$LOCAL_BRANCH" >/dev/null 2>&1 + +# 关键命令:使用完整引用路径 +if ! git reset --hard "refs/remotes/$REMOTE_NAME/$TARGET_BRANCH"; then + echo -e "${RED}错误:同步远程分支到本地失败!${NC}" + exit 1 +fi + +# 6. 完成提示 +echo -e "\n${GREEN}===== 操作完成 =====${NC}" +echo -e "✅ 本地 ${YELLOW}${LOCAL_BRANCH}${NC} 已同步为远程 ${YELLOW}${TARGET_BRANCH}${NC} 最新状态" +echo -e "当前版本:" +git log -1 --pretty=format:"%h - %an, %ar : %s" diff --git a/devops/iso/新的/ipa_server.py b/devops/iso/新的/ipa_server.py new file mode 100644 index 00000000..8c59bd61 --- /dev/null +++ b/devops/iso/新的/ipa_server.py @@ -0,0 +1,101 @@ +#!/opt/miniconda3/bin/python3 +import os +import sys +import socket +import http.server +import socketserver +import subprocess +from urllib.parse import urlparse + +# 配置基础路径和端口(推荐使用 1024-65535 之间的端口,避免系统预留端口) +BUILD_DIR = os.path.join(os.path.expanduser('~'), 'Desktop', 'lx_uniapp', 'build') +DEFAULT_PORT = 8000 # 若仍失败,可改为 8080、9000 等 + + +def check_port_available(port): + """检查端口是否真的可用(比 lsof 更可靠)""" + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + try: + s.bind(("", port)) # 尝试绑定端口,成功则可用 + return True + except OSError: + return False + + +def free_port(port): + """释放占用端口的进程""" + try: + result = subprocess.check_output( + f"lsof -i :{port} | grep LISTEN | awk '{{print $2}}'", + shell=True, + text=True, + stderr=subprocess.STDOUT # 忽略错误输出 + ) + pids = [pid.strip() for pid in result.split('\n') if pid.strip()] + if pids: + print(f"释放端口 {port} 占用的进程:{pids}") + for pid in pids: + subprocess.run(f"kill -9 {pid}", shell=True, check=True) + except subprocess.CalledProcessError: + pass # 无进程占用时正常 + except Exception as e: + print(f"释放端口警告:{e}(可忽略)") + + +class IPARequestHandler(http.server.SimpleHTTPRequestHandler): + # 省略与之前相同的 do_GET 实现(保持不变) + def do_GET(self): + parsed_path = urlparse(self.path) + target_branch = parsed_path.path.strip('/') + + if not target_branch: + self.send_response(400) + self.end_headers() + self.wfile.write("请指定分支名,例如: http://服务器IP:端口/sit".encode('utf-8')) + return + + ipa_dir_name = f"{target_branch}_ipa_production" + ipa_path = os.path.join(BUILD_DIR, ipa_dir_name, "HBuilder.ipa") + + if not os.path.exists(ipa_path): + self.send_response(404) + self.end_headers() + self.wfile.write(f"文件不存在: {ipa_path}".encode('utf-8')) + return + + self.send_response(200) + self.send_header('Content-Type', 'application/octet-stream') + self.send_header('Content-Disposition', f'attachment; filename="HBuilder_{target_branch}.ipa"') + self.end_headers() + + with open(ipa_path, 'rb') as f: + self.wfile.write(f.read()) + + +if __name__ == '__main__': + # 1. 先尝试释放端口 + free_port(DEFAULT_PORT) + + # 2. 检查端口是否真的可用 + if not check_port_available(DEFAULT_PORT): + print(f"端口 {DEFAULT_PORT} 仍被占用,尝试自动切换端口...") + # 自动切换到 8080 端口(若 8000 不可用) + DEFAULT_PORT = 8080 + free_port(DEFAULT_PORT) + if not check_port_available(DEFAULT_PORT): + print(f"端口 {DEFAULT_PORT} 也被占用,请手动指定其他端口(如 9000)") + sys.exit(1) + + # 3. 启动服务(增加错误捕获) + try: + # 禁用地址重用限制(解决 TIME_WAIT 状态导致的启动失败) + socketserver.TCPServer.allow_reuse_address = True + with socketserver.TCPServer(("", DEFAULT_PORT), IPARequestHandler) as httpd: + print(f"\nIPA 文件服务已启动,端口: {DEFAULT_PORT}") + print(f"访问格式: http://服务器IP:{DEFAULT_PORT}/目标分支名") + print(f"示例: http://25.64.32.152:{DEFAULT_PORT}/sit") + httpd.serve_forever() + except Exception as e: + print(f"服务启动失败:{str(e)}") + print("可能原因:权限不足(尝试非 1-1024 端口)或网络配置限制") + sys.exit(1) \ No newline at end of file diff --git a/devops/iso/新的/push_version.py b/devops/iso/新的/push_version.py new file mode 100644 index 00000000..41a9bead --- /dev/null +++ b/devops/iso/新的/push_version.py @@ -0,0 +1,402 @@ +#!/usr/bin/env python3 +import jwt +import time +import requests +import os +import sys +import datetime +from pathlib import Path + + +# ============================================== +# 重试配置参数(可按需修改) +# ============================================== +MAX_RETRIES = 10 # 查找构建的最大重试次数 +RETRY_DELAY = 30 # 查找构建的重试间隔(秒) +SUBMIT_INIT_WAIT = 60 # 提交前初始等待时间(秒) +SUBMIT_MAX_RETRIES = 20 # 提交审核的最大重试次数 +SUBMIT_BASE_DELAY = 30 # 提交审核的初始重试间隔(秒) +SUBMIT_DELAY_INCREMENT = 10 # 每次重试间隔递增(秒) + +# ============================================== +# 其他配置参数 +# ============================================== +def get_chinese_date_time(): + """ + 获取当前时间,以中文格式返回(如:8月5日 上午8点24分) + 包含月、日、上午/下午、时、分,不包含秒数 + """ + now = datetime.datetime.now() + month = now.month + day = now.day + hour_24 = now.hour + minute = now.minute + + if hour_24 < 12: + period = "上午" + hour_12 = hour_24 if hour_24 != 0 else 12 + else: + period = "下午" + hour_12 = hour_24 if hour_24 == 12 else hour_24 - 12 + + return f"{month}月{day}日 {period}{hour_12}点{minute}分" + + +# 获取参数 +if len(sys.argv) >= 2 and sys.argv[1]: + env_name = sys.argv[1] +else: + env_name = 'develop' + +if len(sys.argv) >= 3 and sys.argv[2]: + build_number_str = sys.argv[2] +else: + build_number_str = '68' + +# 验证第一个参数(枚举值) +valid_envs = ['develop', 'sit', 'uat'] +if env_name not in valid_envs: + print(f"错误:环境名称 '{env_name}' 无效") + print(f"有效环境名称:{', '.join(valid_envs)}") + sys.exit(1) + +# 验证第二个参数(数字字符串) +if not build_number_str.isdigit(): + print(f"错误:构建号 '{build_number_str}' 必须是纯数字字符串") + sys.exit(1) + +# 参数验证通过,继续处理 +TARGET_BUILD_NUMBER = build_number_str + +if env_name == 'develop': + BETA_GROUP_NAME = "外部自动化测试" + BETA_GROUP_NAME = "UAT" + TARGET_VERSION = "0.3.1" +elif env_name == 'sit': + BETA_GROUP_NAME = "SIT" + TARGET_VERSION = "0.2.1" +elif env_name == 'uat': + BETA_GROUP_NAME = "UAT" + TARGET_VERSION = "0.3.1" + +# 接收参数结束 + +# 配置参数 +API_KEY = "ACRB9XFL9G" +API_ISSUER = "69a6de7f-3378-47e3-e053-5b8c7c11a4d1" +APP_NAME = "吉AI学" +API_ENDPOINT = "https://api.appstoreconnect.apple.com/v1" + +if 'PYCHARM_HOSTED' in os.environ: + Auth_Key_Path = './' +else: + Auth_Key_Path = '/Users/jlbank/Desktop/lx_uniapp/auto_build' + +private_key_path = Path(f'{Auth_Key_Path}/AuthKey_{API_KEY}.p8') + +def generate_jwt_token(api_key, api_issuer, private_key_path): + """生成 JWT 令牌""" + with open(private_key_path, "r") as f: + private_key = f.read() + + current_time = int(time.time()) + payload = { + "iss": api_issuer, + "exp": current_time + 20 * 60, + "iat": current_time, + "aud": "appstoreconnect-v1" + } + + token = jwt.encode( + payload, + private_key, + algorithm="ES256", + headers={"kid": api_key} + ) + return token + + +def find_specific_build(app_id, headers): + """通过预发布版本ID查找指定构建版本,带重试机制""" + retries = 0 + + while retries < MAX_RETRIES: + try: + print(f"\n🔍 查找版本 {TARGET_VERSION} 构建号 {TARGET_BUILD_NUMBER} (尝试 {retries + 1}/{MAX_RETRIES})") + + print(" 正在获取预发布版本ID...") + pre_release_url = f"{API_ENDPOINT}/preReleaseVersions" + pre_release_params = { + "filter[version]": TARGET_VERSION, + "filter[app]": app_id + } + + pre_release_response = requests.get(pre_release_url, headers=headers, params=pre_release_params) + pre_release_response.raise_for_status() + pre_release_data = pre_release_response.json() + + if not pre_release_data["data"]: + print(f"❓ 未找到版本为 {TARGET_VERSION} 的预发布版本") + retries += 1 + if retries < MAX_RETRIES: + time.sleep(RETRY_DELAY) + continue + + pre_release_id = pre_release_data["data"][0]["id"] + print(f" 找到预发布版本ID: {pre_release_id}") + + builds_url = f"{API_ENDPOINT}/builds" + params = { + "filter[preReleaseVersion]": pre_release_id, + "sort": "-version", + "limit": "10", + "include": "buildBetaDetail,betaBuildLocalizations,preReleaseVersion,betaAppReviewSubmission,icons,buildBundles,betaGroups", + "limit[betaBuildLocalizations]": "40", + "limit[betaGroups]": "50" + } + + response = requests.get(builds_url, headers=headers, params=params) + + if response.status_code == 400: + print(f" 错误响应内容: {response.text}") + response.raise_for_status() + + builds_data = response.json() + + matching_builds = [ + b for b in builds_data["data"] + if b["attributes"].get("version") == TARGET_BUILD_NUMBER + ] + if not matching_builds: + print(f"❓ 未找到版本 {TARGET_VERSION} 构建号 {TARGET_BUILD_NUMBER} 的构建") + else: + valid_build = next(( + b for b in matching_builds + if b["attributes"]["processingState"] == "VALID" + ), None) + + if valid_build: + return valid_build + else: + states = [b["attributes"]["processingState"] for b in matching_builds] + print(f"⏳ 找到匹配的构建但状态为: {', '.join(states)},等待处理完成...") + + if retries < MAX_RETRIES - 1: + print(f"将在 {RETRY_DELAY} 秒后重试...") + time.sleep(RETRY_DELAY) + + retries += 1 + + except Exception as e: + print(f"查询过程中出错: {str(e)}") + retries += 1 + if retries < MAX_RETRIES: + time.sleep(RETRY_DELAY) + + return None + +# ============================================== +# 新增:带重试的提交审核函数 +# ============================================== +def submit_for_review_with_retry(build_id, headers): + """提交审核(带初始等待和动态递增间隔重试)""" + # 第一步:初始等待 + print(f"\n⌛ 提交审核前,先等待 {SUBMIT_INIT_WAIT} 秒(确保构建完全处理完成)...") + time.sleep(SUBMIT_INIT_WAIT) + + # 第二步:重试提交 + for retry in range(SUBMIT_MAX_RETRIES): + current_delay = SUBMIT_BASE_DELAY + (retry * SUBMIT_DELAY_INCREMENT) + try: + print(f"\n📋 提交构建版本审核 (尝试 {retry + 1}/{SUBMIT_MAX_RETRIES})") + print(f" 当前重试间隔:{current_delay} 秒") + + submit_url = f"{API_ENDPOINT}/betaAppReviewSubmissions" + submit_payload = { + "data": { + "type": "betaAppReviewSubmissions", + "relationships": { + "build": { + "data": {"id": build_id, "type": "builds"} + } + } + } + } + + submit_response = requests.post(submit_url, headers=headers, json=submit_payload) + submit_response.raise_for_status() + print("✅ 构建版本已成功提交审核") + return True # 提交成功,退出函数 + + except requests.exceptions.HTTPError as e: + # 处理已提交的情况(409冲突) + if submit_response.status_code == 409: + print("⚠️ 构建已提交审核,无需重复操作") + return True + else: + print(f"❌ 第 {retry + 1} 次提交审核失败: {e}") + print("错误详情:", submit_response.json()) + + # 最后一次重试失败则退出 + if retry == SUBMIT_MAX_RETRIES - 1: + print(f"\n❌ 已达到最大重试次数 ({SUBMIT_MAX_RETRIES}),提交审核失败") + return False + + # 非最后一次重试,等待后继续 + print(f" 将在 {current_delay} 秒后进行第 {retry + 2} 次重试...") + time.sleep(current_delay) + + except Exception as e: + print(f"❌ 第 {retry + 1} 次提交审核异常: {str(e)}") + + # 最后一次重试失败则退出 + if retry == SUBMIT_MAX_RETRIES - 1: + print(f"\n❌ 已达到最大重试次数 ({SUBMIT_MAX_RETRIES}),提交审核失败") + return False + + # 非最后一次重试,等待后继续 + print(f" 将在 {current_delay} 秒后进行第 {retry + 2} 次重试...") + time.sleep(current_delay) + + return False + +def main(): + try: + # 1. 生成 JWT 令牌 + if not private_key_path.exists(): + raise FileNotFoundError(f"私钥文件不存在: {private_key_path}") + + token = generate_jwt_token(API_KEY, API_ISSUER, private_key_path) + print("✅ JWT 令牌生成成功") + + # 2. 配置请求头 + headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json" + } + + # 3. 获取应用 ID + print(f"\n🔍 查找应用: {APP_NAME}") + apps_url = f"{API_ENDPOINT}/apps" + response = requests.get(apps_url, headers=headers) + response.raise_for_status() + + apps_data = response.json() + app = next((a for a in apps_data["data"] if a["attributes"]["name"] == APP_NAME), None) + if not app: + raise ValueError(f"未找到应用: {APP_NAME}") + + app_id = app["id"] + print(f"✅ 找到应用 ID: {app_id}") + + # 4. 获取指定版本和构建号的构建版本(带重试) + valid_build = find_specific_build(app_id, headers) + if not valid_build: + raise ValueError( + f"经过 {MAX_RETRIES} 次尝试后,仍未找到可用的构建版本: {TARGET_VERSION} ({TARGET_BUILD_NUMBER})") + + build_id = valid_build["id"] + build_version = TARGET_VERSION + build_number = valid_build["attributes"]["version"] + print(f"✅ 找到构建版本 ID: {build_id} (版本号: {build_version}, 构建号: {build_number})") + + # 5. 获取外部测试群组 ID + print(f"\n🔍 查找测试群组: {BETA_GROUP_NAME}") + groups_url = f"{API_ENDPOINT}/betaGroups" + response = requests.get(groups_url, headers=headers) + response.raise_for_status() + + groups_data = response.json() + beta_group = next((g for g in groups_data["data"] if + g["attributes"]["name"] == BETA_GROUP_NAME and + g["attributes"]["isInternalGroup"] is False), None) + if not beta_group: + raise ValueError(f"未找到外部测试群组: {BETA_GROUP_NAME}") + + beta_group_id = beta_group["id"] + print(f"✅ 找到外部测试群组 ID: {beta_group_id}") + + # 6. 添加构建到测试群组后,更新测试内容(betaBuildLocalizations) + print("\n📝 更新测试版本的本地化信息(whatsNew)...") + + # 7. 将构建版本添加到测试群组 + print(f"\n🔗 将构建 {build_id} 添加到测试群组 {beta_group_id}...") + add_build_url = f"{API_ENDPOINT}/betaGroups/{beta_group_id}/relationships/builds" + + add_build_payload = { + "data": [{"id": build_id, "type": "builds"}] + } + + add_response = requests.post(add_build_url, headers=headers, json=add_build_payload) + try: + add_response.raise_for_status() + except requests.exceptions.HTTPError as e: + if add_response.status_code == 409: + print("⚠️ 构建已在测试群组中,跳过添加步骤") + else: + print(f"❌ 添加构建到测试群组失败: {e}") + print("错误详情:", add_response.json()) + sys.exit(1) + else: + print("✅ 构建已成功添加到测试群组") + + # 8. 更新测试内容(whatsNew) + get_localizations_url = f"{API_ENDPOINT}/builds/{build_id}/betaBuildLocalizations" + response = requests.get(get_localizations_url, headers=headers) + + try: + response.raise_for_status() + except requests.exceptions.HTTPError as e: + print(f"❌ 获取本地化信息失败: {e}") + print("错误详情:", response.json()) + sys.exit(1) + localizations = response.json().get("data", []) + + print("\n📝 更新测试版本说明...") + if localizations: + localization_id = localizations[0]["id"] + update_url = f"{API_ENDPOINT}/betaBuildLocalizations/{localization_id}" + print(f"使用现有本地化信息 ID: {localization_id}") + else: + print("创建新的本地化信息...") + create_url = f"{API_ENDPOINT}/betaBuildLocalizations" + create_payload = { + "data": { + "type": "betaBuildLocalizations", + "attributes": {"locale": "zh-Hans", "whatsNew": ""}, + "relationships": { + "build": {"data": {"id": build_id, "type": "builds"}} + } + } + } + create_response = requests.post(create_url, headers=headers, json=create_payload) + create_response.raise_for_status() + localization_id = create_response.json()["data"]["id"] + update_url = f"{API_ENDPOINT}/betaBuildLocalizations/{localization_id}" + + now_time_text = get_chinese_date_time() + update_payload = { + "data": { + "id": localization_id, + "type": "betaBuildLocalizations", + "attributes": { + "whatsNew": f"当前版本: {TARGET_BUILD_NUMBER}\n提交时间:{now_time_text}\n自动化提交" + } + } + } + update_response = requests.patch(update_url, headers=headers, json=update_payload) + update_response.raise_for_status() + print("✅ 测试版本说明已更新") + + # 9. 提交审核(调用新增的带重试函数) + submit_success = submit_for_review_with_retry(build_id, headers) + if not submit_success: + sys.exit(1) + + except Exception as e: + print(e) + exit(1) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/devops/iso/新的/ready_build_ios.sh b/devops/iso/新的/ready_build_ios.sh new file mode 100644 index 00000000..bfe3a24b --- /dev/null +++ b/devops/iso/新的/ready_build_ios.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +# ============================================== +# 配置参数 - 请根据实际情况修改以下参数 +# ============================================== +# 钥匙串配置 +KEYCHAIN_PASSWORD="1234567" # 钥匙串密码 +KEYCHAIN_PATH="$HOME/Library/Keychains/login.keychain-db" # 钥匙串路径 + +# 项目路径配置 +PROJECT_DIR="$HOME/Desktop/lx_uniapp/build/git_work_code/Ios/HBuilder-Hello" # 项目目录 +PROJECT_NAME="HBuilder-Hello.xcodeproj" # Xcode项目文件名 +SCHEME_NAME="HBuilder" # 构建方案名称 +CONFIGURATION="Release" # 构建配置 + +# 构建输出配置 +BUILD_DIR="$HOME/Desktop/lx_uniapp/build" # 构建输出目录 +ARCHIVE_NAME="HBuilder.xcarchive" # 归档文件名 +IPA_DIR_NAME="ipa_production" # IPA输出目录名 +DERIVED_DATA_DIR="$HOME/Library/Developer/Xcode/DerivedData/*" # 衍生数据目录 + +# 版本配置 +MARKETING_VERSION="$1" # 市场版本号 +CURRENT_PROJECT_VERSION="$2" # 项目内部版本号 + +# 签名配置 +CODE_SIGN_IDENTITY="Apple Distribution: Bank of Jilin Co., Ltd. (2EP6SRRN43)" # 签名标识 +PROVISIONING_PROFILE="e3487fe1-19dc-4fc7-90df-11caf27da0c6" # 配置文件UUID +DEVELOPMENT_TEAM="2EP6SRRN43" # 开发团队ID +CODE_SIGNING_REQUIRED="YES" # 是否需要签名 + +# 导出配置 +EXPORT_OPTIONS_PLIST="$BUILD_DIR/ExportOptions_Production.plist" # 导出配置plist路径 +# ============================================== + +# 错误处理函数 +error_exit() { + echo "❌ $1" + exit 1 +} + +# 0. 解锁钥匙串并授予权限 +echo "🔑 解锁钥匙串..." +security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" || { + error_exit "钥匙串解锁失败" +} + +# 1. 进入项目目录 +echo "📂 进入项目目录: $PROJECT_DIR" +cd "$PROJECT_DIR" || { + error_exit "无法进入项目目录: $PROJECT_DIR" +} + +# 2. 清理旧构建 +echo "🧹 清理旧构建..." +xcodebuild -project "$PROJECT_NAME" -scheme "$SCHEME_NAME" -configuration "$CONFIGURATION" clean || { + error_exit "清理构建失败" +} + +# 3. 删除上次构建内容 +echo "🗑️ 删除上次构建内容..." +setopt rm_star_silent nullglob +rm -rf "$BUILD_DIR/$ARCHIVE_NAME" "$BUILD_DIR/$IPA_DIR_NAME" "$DERIVED_DATA_DIR" +unsetopt rm_star_silent nullglob # 恢复默认设置 + +# 4. 归档项目 +echo "📦 开始归档项目..." +xcodebuild -project "$PROJECT_NAME" \ + -scheme "$SCHEME_NAME" \ + -configuration "$CONFIGURATION" \ + -archivePath "$BUILD_DIR/$ARCHIVE_NAME" \ + MARKETING_VERSION="$MARKETING_VERSION" \ + CURRENT_PROJECT_VERSION="$CURRENT_PROJECT_VERSION" \ + -destination 'generic/platform=iOS' \ + CODE_SIGN_IDENTITY="$CODE_SIGN_IDENTITY" \ + PROVISIONING_PROFILE="$PROVISIONING_PROFILE" \ + DEVELOPMENT_TEAM="$DEVELOPMENT_TEAM" \ + CODE_SIGNING_REQUIRED="$CODE_SIGNING_REQUIRED" \ + OTHER_CFLAGS="" \ + OTHER_CPLUSPLUS_FLAGS="" \ + OTHER_SWIFT_FLAGS="" \ + archive || { + error_exit "项目归档失败" +} + +# 5. 导出IPA文件 +echo "📤 导出IPA文件..." +xcodebuild -exportArchive \ + -archivePath "$BUILD_DIR/$ARCHIVE_NAME" \ + -exportPath "$BUILD_DIR/$IPA_DIR_NAME" \ + -exportOptionsPlist "$EXPORT_OPTIONS_PLIST" || { + error_exit "IPA导出失败" +} + +echo "🎉 构建完成!IPA文件已导出至: $BUILD_DIR/$IPA_DIR_NAME" diff --git a/devops/iso/新的/show.py b/devops/iso/新的/show.py new file mode 100644 index 00000000..bf9570d0 --- /dev/null +++ b/devops/iso/新的/show.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 +import os +import sys +print(f"🔍 当前脚本使用的 Python 路径:{sys.executable}") +print(f"🔍 当前 Python 环境变量 PATH:{os.environ.get('PATH')[:500]}...") # 只打印前500字符,避免过长 +print(f"🔍 当前 Conda 环境:{os.environ.get('CONDA_DEFAULT_ENV', '未激活 Conda 环境')}\n") \ No newline at end of file diff --git a/devops/iso/新的/sync_remote_branch.py b/devops/iso/新的/sync_remote_branch.py new file mode 100644 index 00000000..343d8767 --- /dev/null +++ b/devops/iso/新的/sync_remote_branch.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 +import os +import subprocess +import sys + +# 颜色定义 +RED = '\033[0;31m' +GREEN = '\033[0;32m' +YELLOW = '\033[1;33m' +NC = '\033[0m' # 无颜色 + +def run_command(command, check=True, capture_output=False): + """执行系统命令并返回结果""" + try: + if capture_output: + result = subprocess.run( + command, + shell=True, + check=check, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + return result + else: + subprocess.run(command, shell=True, check=check) + return None + except subprocess.CalledProcessError as e: + print(f"{RED}错误:命令执行失败: {e}{NC}") + sys.exit(1) + +def main(): + # 检查参数 + if len(sys.argv) < 2: + print(f"{RED}错误:未传入分支名称参数!{NC}") + print(f"使用方法:{YELLOW}{sys.argv[0]} <远程分支名>{NC}") + sys.exit(1) + + print(f"▶ 先设置内网网络\n") + run_command("sudo route add -host 25.13.9.101 25.64.32.254") + + # 配置参数 + target_branch = sys.argv[1] + + # 关键修改:跨平台路径拼接(build + target_branch) + # 1. 获取脚本所在目录(作为相对路径基准,可选,根据实际需求调整) + script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) + parent_dir = os.path.dirname(script_dir) + # 2. 拼接路径:build/(相对脚本目录) + work_dir = os.path.join(parent_dir, "build", target_branch) + # 3. 转换为绝对路径(消除相对路径符号,更可靠) + work_dir = os.path.abspath(work_dir) + print(f"▶ 本地工作目录:{YELLOW}{work_dir}{NC}\n") + remote_name = "origin" + local_branch = "develop" + + print(f"\n{YELLOW}===== 开始执行分支同步脚本 ====={NC}") + print(f"▶ 远程目标分支:{YELLOW}{target_branch}{NC}") + + + # 1. 进入工作目录 + print(f"{YELLOW}1. 检查并进入工作目录...{NC}") + if not os.path.isdir(work_dir): + print(f"{RED}错误:工作目录不存在!{NC}") + sys.exit(1) + + try: + os.chdir(work_dir) + except OSError as e: + print(f"{RED}错误:无法进入工作目录!{e}{NC}") + sys.exit(1) + + print(f"✅ 成功进入工作目录:{os.getcwd()}\n") + + # 2. 检查Git仓库 + print(f"{YELLOW}2. 检查Git仓库...{NC}") + if not os.path.isdir(".git"): + print(f"{RED}错误:当前目录不是Git仓库!{NC}") + sys.exit(1) + print(f"✅ 确认是Git仓库\n") + + # 3. 关键修复:获取远程分支完整信息 + print(f"{YELLOW}3. 完整获取远程所有分支完整信息...{NC}") + # 完整拉取所有分支信息 + run_command(f"git fetch {remote_name} '+refs/heads/*:refs/remotes/{remote_name}/*'") + print(f"✅ 远程分支信息拉取完成\n") + + # 4. 验证远程分支是否存在 + print(f"{YELLOW}4. 验证远程 {target_branch} 分支...{NC}") + result = run_command( + f"git show-ref --verify --quiet 'refs/remotes/{remote_name}/{target_branch}'", + check=False, + capture_output=True + ) + + if result.returncode != 0: + print(f"{RED}错误:远程确实不存在 {target_branch} 分支!{NC}") + print("远程所有分支列表:") + run_command("git branch -r") + sys.exit(1) + + print(f"✅ 确认远程存在 {YELLOW}{target_branch}{NC} 分支\n") + + # 5. 同步到本地develop分支 + print(f"{YELLOW}5. 同步远程 {target_branch} 到本地 {local_branch}...{NC}") + print(f"{YELLOW}⚠️ 警告:将覆盖本地develop分支所有内容!{NC}") + + # 确保本地有develop分支 + run_command(f"git checkout -B {local_branch} >/dev/null 2>&1", check=False) + + # 关键命令:使用完整引用路径 + run_command(f"git reset --hard 'refs/remotes/{remote_name}/{target_branch}'") + + # 6. 完成提示 + print(f"\n{GREEN}===== 操作完成 ====={NC}") + print(f"✅ 本地 {YELLOW}{local_branch}{NC} 已同步为远程 {YELLOW}{target_branch}{NC} 最新状态") + print("当前版本:") + run_command("git log -1 --pretty=format:\"%h - %an, %ar : %s\"") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/devops/iso/新的/upload_to_appstore.py b/devops/iso/新的/upload_to_appstore.py new file mode 100644 index 00000000..166dceee --- /dev/null +++ b/devops/iso/新的/upload_to_appstore.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +import os +import subprocess +import sys +import tempfile + +# 颜色定义 +RED = '\033[0;31m' +GREEN = '\033[0;32m' +YELLOW = '\033[1;33m' +NC = '\033[0m' # 无颜色 + +def run_command(command, capture_output=False): + """执行系统命令并返回结果""" + try: + result = subprocess.run( + command, + shell=True, + check=True, + stdout=subprocess.PIPE if capture_output else None, + stderr=subprocess.STDOUT if capture_output else None, + text=True + ) + return result + except subprocess.CalledProcessError as e: + if capture_output: + return e # 返回错误信息供后续分析 + print(f"{RED}❌ 命令执行失败: {e.output}{NC}") + sys.exit(e.returncode) + +def main(): + # 检查参数 + if len(sys.argv) < 2: + print(f"{RED}错误:参数不足!{NC}") + print(f"使用方法:{YELLOW}{sys.argv[0]} {NC}") + print(f"示例:{YELLOW}{sys.argv[0]} sit{NC}") + sys.exit(1) + + # 解析参数(第一个参数为target_branch) + target_branch = sys.argv[1] + + # ============================================== + # 配置变量 - 根据实际情况修改以下参数 + # ============================================== + API_KEY = "ACRB9XFL9G" # App Store Connect API 密钥ID + API_ISSUER = "69a6de7f-3378-47e3-e053-5b8c7c11a4d1" # API 密钥对应的Issuer ID + APP_TYPE = "ios" # 应用类型(固定为ios) + + # 关键修改:根据target_branch拼接IPA路径 + build_dir = f"{os.path.expanduser('~')}/Desktop/lx_uniapp/build" # 构建输出目录 + ipa_dir_name = f"{target_branch}_ipa_production" # 带分支名的IPA目录 + IPA_PATH = os.path.join(build_dir, ipa_dir_name, "HBuilder.ipa") # 固定IPA文件名 + + # 显示参数信息 + print(f"{YELLOW}===== 上传配置信息 ====={NC}") + print(f"▶ 目标分支: {YELLOW}{target_branch}{NC}") + print(f"▶ IPA文件路径: {YELLOW}{IPA_PATH}{NC}\n") + + # ============================================== + # 验证IPA文件是否存在 + # ============================================== + if not os.path.isfile(IPA_PATH): + print(f"{RED}❌ 错误:未找到IPA文件!{NC}") + print(f" 查找路径:{IPA_PATH}") + print(f" 请确认该分支已成功构建IPA文件") + sys.exit(1) + + # ============================================== + # 1. 验证IPA文件合法性 + # ============================================== + print(f"{YELLOW}📋 开始验证IPA文件...{NC}") + print(f" 文件路径:{IPA_PATH}") + + # 创建临时文件保存输出 + with tempfile.NamedTemporaryFile(mode='w+', delete=False) as tmp_file: + tmp_filename = tmp_file.name + + # 执行验证命令 + validate_cmd = ( + f"xcrun altool --validate-app " + f"--type '{APP_TYPE}' " + f"--file '{IPA_PATH}' " + f"--apiKey '{API_KEY}' " + f"--apiIssuer '{API_ISSUER}' > '{tmp_filename}' 2>&1" + ) + + # 执行命令但不立即退出(需要处理特定错误) + result = run_command(validate_cmd, capture_output=True) + + # 读取并显示验证输出 + with open(tmp_filename, 'r') as f: + validate_output = f.read() + print(validate_output) + os.unlink(tmp_filename) # 清理临时文件 + + # 处理验证结果 + if isinstance(result, subprocess.CalledProcessError): + # 检查是否是版本号重复错误 + if "The bundle version must be higher than the previously uploaded version" in validate_output: + # 提取之前的版本号 + previous_version = None + if "previousBundleVersion = " in validate_output: + previous_version = validate_output.split("previousBundleVersion = ")[1].split()[0] + elif "version: ‘" in validate_output: + previous_version = validate_output.split("version: ‘")[1].split("’")[0] + + print(f"{RED}❌ 错误:版本号重复{NC}") + if previous_version: + print(f"{RED} 已上传的最新版本号为: {previous_version}{NC}") + try: + print(f"{RED} 请将版本号修改为 {int(previous_version) + 1} 或更高后重试{NC}") + except ValueError: + print(f"{RED} 请将版本号修改为更高版本后重试{NC}") + else: + print(f"{RED} 请将版本号修改为更高版本后重试{NC}") + sys.exit(result.returncode) + else: + print(f"{RED}❌ IPA文件验证失败,请查看上方错误信息{NC}") + sys.exit(result.returncode) + else: + print(f"{GREEN}✅ IPA文件验证成功,符合上传要求{NC}") + + # ============================================== + # 2. 上传IPA文件到App Store Connect + # ============================================== + print(f"\n{YELLOW}🚀 开始上传IPA文件...{NC}") + print(f" 文件路径:{IPA_PATH}") + + upload_cmd = ( + f"xcrun altool --upload-app " + f"--type '{APP_TYPE}' " + f"--file '{IPA_PATH}' " + f"--apiKey '{API_KEY}' " + f"--apiIssuer '{API_ISSUER}'" + ) + + run_command(upload_cmd) + + # 上传成功提示 + print(f"\n{GREEN}🎉 上传成功!可在App Store Connect中查看构建版本{NC}") + print(f" 提示:构建版本处理需要几分钟,请耐心等待") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/devops/iso/新的/validate_and_upload.sh b/devops/iso/新的/validate_and_upload.sh new file mode 100644 index 00000000..5bcab95d --- /dev/null +++ b/devops/iso/新的/validate_and_upload.sh @@ -0,0 +1,92 @@ +#!/bin/bash +set -e # 脚本执行过程中若有命令失败则立即退出 + +# ============================================== +# 颜色定义 +# ============================================== +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # 无颜色(重置) + +# ============================================== +# 配置变量 - 根据实际情况修改以下参数 +# ============================================== +API_KEY="ACRB9XFL9G" # App Store Connect API 密钥ID +API_ISSUER="69a6de7f-3378-47e3-e053-5b8c7c11a4d1" # API 密钥对应的Issuer ID +IPA_PATH="$HOME/Desktop/lx_uniapp/build/ipa_production/HBuilder.ipa" # IPA文件路径 +APP_TYPE="ios" # 应用类型(固定为ios) + +# ============================================== +# 验证IPA文件是否存在 +# ============================================== +if [ ! -f "$IPA_PATH" ]; then + echo -e "${RED}❌ 错误:未找到IPA文件,请检查路径是否正确${NC}" + echo -e " 查找路径:$IPA_PATH" + exit 1 +fi +# ============================================== +# 1. 验证IPA文件合法性 +# ============================================== +echo -e "\n${YELLOW}📋 开始验证IPA文件...${NC}" +echo -e " 文件路径:$IPA_PATH" + +# 创建临时文件保存输出 +TMP_FILE=$(mktemp) + +# 执行验证命令,直接输出到终端,同时重定向到临时文件 +xcrun altool --validate-app \ + --type "$APP_TYPE" \ + --file "$IPA_PATH" \ + --apiKey "$API_KEY" \ + --apiIssuer "$API_ISSUER" > "$TMP_FILE" 2>&1 +# 直接打印临时文件内容(确保错误信息显示) +cat "$TMP_FILE" + +# 获取退出码 +VALIDATE_EXIT_CODE=$? + +# 从临时文件读取内容用于分析 +VALIDATE_OUTPUT=$(cat "$TMP_FILE") +rm "$TMP_FILE" # 清理临时文件 + +# 验证结果处理 +if [ $VALIDATE_EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}✅ IPA文件验证成功,符合上传要求${NC}" +else + # 检查是否是版本号重复错误 + if echo "$VALIDATE_OUTPUT" | grep -q "The bundle version must be higher than the previously uploaded version"; then + # 提取之前的版本号 + PREVIOUS_VERSION=$(echo "$VALIDATE_OUTPUT" | grep -oE 'previousBundleVersion = [0-9]+' | awk '{print $3}') + if [ -z "$PREVIOUS_VERSION" ]; then + PREVIOUS_VERSION=$(echo "$VALIDATE_OUTPUT" | grep -oE 'version: ‘[0-9]+’' | sed "s/version: ‘//;s/’//") + fi + + echo -e "${RED}❌ 错误:版本号重复${NC}" + echo -e "${RED} 已上传的最新版本号为: $PREVIOUS_VERSION${NC}" + echo -e "${RED} 请将版本号修改为 $((PREVIOUS_VERSION + 1)) 或更高后重试${NC}" + else + echo -e "${RED}❌ IPA文件验证失败,请查看上方错误信息${NC}" + fi + exit $VALIDATE_EXIT_CODE +fi +# ============================================== +# 2. 上传IPA文件到App Store Connect +# ============================================== +echo -e "\n${YELLOW}🚀 开始上传IPA文件...${NC}" +echo -e " 文件路径:$IPA_PATH" + +xcrun altool --upload-app \ + --type "$APP_TYPE" \ + --file "$IPA_PATH" \ + --apiKey "$API_KEY" \ + --apiIssuer "$API_ISSUER" + +# 上传成功提示 +if [ $? -eq 0 ]; then + echo -e "\n${GREEN}🎉 上传成功!可在App Store Connect中查看构建版本${NC}" + echo -e " 提示:构建版本处理需要几分钟,请耐心等待" +else + echo -e "\n${RED}❌ 上传失败,请根据错误信息修复后重试${NC}" + exit 1 +fi diff --git a/devops/iso/老的/AuthKey_ACRB9XFL9G.p8 b/devops/iso/老的/AuthKey_ACRB9XFL9G.p8 new file mode 100644 index 00000000..c209d906 --- /dev/null +++ b/devops/iso/老的/AuthKey_ACRB9XFL9G.p8 @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgZfELkUrcpqqAVlQg +UfKNlPbySAnafYL+vEndTe9/xoqgCgYIKoZIzj0DAQehRANCAATBz1/RZd+1AEgU +VfnoB1PNUMynS8FQ/zJdTcZ25qEuu6WVkWb82Ltmb1lIRSZpRWEbpQzL+7X6zQ95 +yjRmUksW +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/devops/iso/老的/git_update_remote.sh b/devops/iso/老的/git_update_remote.sh new file mode 100644 index 00000000..8c7947db --- /dev/null +++ b/devops/iso/老的/git_update_remote.sh @@ -0,0 +1,88 @@ +#!/bin/bash +# 脚本名称:sync_remote_branch.sh +# 功能描述:修复origin/sit引用问题,强制同步远程分支到本地develop +# 使用方法:./sync_remote_branch.sh <远程分支名> + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +# 检查参数 +if [ $# -eq 0 ]; then + echo -e "${RED}错误:未传入分支名称参数!${NC}" + echo -e "使用方法:${YELLOW}$0 <远程分支名>${NC}" + exit 1 +fi +echo -e "▶ 先设置内网网络\n" +sudo route add -host 25.13.9.101 25.64.32.254 +# 配置参数 +TARGET_BRANCH="$1" +WORK_DIR="Desktop/lx_uniapp/build/git_work_code" +REMOTE_NAME="origin" +LOCAL_BRANCH="develop" + +echo -e "\n${YELLOW}===== 开始执行分支同步脚本 =====${NC}" +echo -e "▶ 远程目标分支:${YELLOW}${TARGET_BRANCH}${NC}" +echo -e "▶ 本地工作目录:${YELLOW}${WORK_DIR}${NC}\n" + +# 1. 进入工作目录 +echo -e "${YELLOW}1. 检查并进入工作目录...${NC}" +if [ ! -d "$WORK_DIR" ]; then + echo -e "${RED}错误:工作目录不存在!${NC}" + exit 1 +fi +cd "$WORK_DIR" || { + echo -e "${RED}错误:无法进入工作目录!${NC}" + exit 1 +} +echo -e "✅ 成功进入工作目录:$(pwd)\n" + +# 2. 检查Git仓库 +echo -e "${YELLOW}2. 检查Git仓库...${NC}" +if [ ! -d ".git" ]; then + echo -e "${RED}错误:当前目录不是Git仓库!${NC}" + exit 1 +fi +echo -e "✅ 确认是Git仓库\n" + +# 3. 关键修复:获取远程分支完整信息(不限制深度) +echo -e "${YELLOW}3. 完整获取远程所有分支完整信息...${NC}" +# 先移除可能存在的浅层克隆限制 +git config --unset core.depth +# 完整拉取所有分支信息 +if ! git fetch "$REMOTE_NAME" "+refs/heads/*:refs/remotes/$REMOTE_NAME/*"; then + echo -e "${RED}错误:拉取远程分支信息失败!${NC}" + exit 1 +fi +echo -e "✅ 远程分支信息拉取完成\n" + +# 4. 再次检查远程分支是否存在 +echo -e "${YELLOW}4. 验证远程 ${TARGET_BRANCH} 分支...${NC}" +if ! git show-ref --verify --quiet "refs/remotes/$REMOTE_NAME/$TARGET_BRANCH"; then + echo -e "${RED}错误:远程确实不存在 ${TARGET_BRANCH} 分支!${NC}" + echo -e "远程所有分支列表:" + git branch -r + exit 1 +fi +echo -e "✅ 确认远程存在 ${YELLOW}${TARGET_BRANCH}${NC} 分支\n" + +# 5. 同步到本地develop分支 +echo -e "${YELLOW}5. 同步远程 ${TARGET_BRANCH} 到本地 ${LOCAL_BRANCH}...${NC}" +echo -e "${YELLOW}⚠️ 警告:将覆盖本地develop分支所有内容!${NC}" + +# 确保本地有develop分支 +git checkout -B "$LOCAL_BRANCH" >/dev/null 2>&1 + +# 关键命令:使用完整引用路径 +if ! git reset --hard "refs/remotes/$REMOTE_NAME/$TARGET_BRANCH"; then + echo -e "${RED}错误:同步远程分支到本地失败!${NC}" + exit 1 +fi + +# 6. 完成提示 +echo -e "\n${GREEN}===== 操作完成 =====${NC}" +echo -e "✅ 本地 ${YELLOW}${LOCAL_BRANCH}${NC} 已同步为远程 ${YELLOW}${TARGET_BRANCH}${NC} 最新状态" +echo -e "当前版本:" +git log -1 --pretty=format:"%h - %an, %ar : %s" diff --git a/devops/iso/老的/push_version.py b/devops/iso/老的/push_version.py new file mode 100644 index 00000000..d9a35e82 --- /dev/null +++ b/devops/iso/老的/push_version.py @@ -0,0 +1,382 @@ +import jwt +import time +import requests +import os +import sys +import datetime +from pathlib import Path + + +def get_chinese_date_time(): + """ + 获取当前时间,以中文格式返回(如:8月5日 上午8点24分) + 包含月、日、上午/下午、时、分,不包含秒数 + """ + # 获取当前时间 + now = datetime.datetime.now() + + # 获取月份和日期 + month = now.month + day = now.day + + # 获取24小时制小时数和分钟数 + hour_24 = now.hour + minute = now.minute + + # 确定上午/下午 + if hour_24 < 12: + period = "上午" + # 转换为12小时制(0点特殊处理为12点) + hour_12 = hour_24 if hour_24 != 0 else 12 + else: + period = "下午" + # 转换为12小时制(12点保持12点,13点及以后减12) + hour_12 = hour_24 if hour_24 == 12 else hour_24 - 12 + + # 格式化返回字符串 + return f"{month}月{day}日 {period}{hour_12}点{minute}分" + + +# 获取参数 +# 处理第一个参数(环境),默认值为'develop' +if len(sys.argv) >= 2 and sys.argv[1]: + env_name = sys.argv[1] +else: + env_name = 'develop' + +# 处理第二个参数(构建号),默认值为'2' +if len(sys.argv) >= 3 and sys.argv[2]: + build_number_str = sys.argv[2] +else: + build_number_str = '68' + +# 验证第一个参数(枚举值) +valid_envs = ['develop', 'sit', 'uat'] +if env_name not in ['develop', 'sit', 'uat']: + print(f"错误:环境名称 '{env_name}' 无效有效") + print(f"有效环境名称:{', '.join(valid_envs)}") + sys.exit(1) + +# 验证第二个参数(数字字符串) +if not build_number_str.isdigit(): + print(f"错误:构建号 '{build_number_str}' 必须是纯数字字符串") + sys.exit(1) + +# 参数验证通过,继续处理 +TARGET_BUILD_NUMBER = build_number_str + +if env_name == 'develop': + BETA_GROUP_NAME = "外部自动化测试" # 测试群组 + BETA_GROUP_NAME = "UAT" # 测试群组 + TARGET_VERSION = "0.3.1" +elif env_name == 'sit': + BETA_GROUP_NAME = "SIT" + TARGET_VERSION = "0.2.1" +elif env_name == 'uat': + BETA_GROUP_NAME = "UAT" + TARGET_VERSION = "0.3.1" + +# 接收参数结束 + +# 配置参数 +API_KEY = "ACRB9XFL9G" +API_ISSUER = "69a6de7f-3378-47e3-e053-5b8c7c11a4d1" +APP_NAME = "吉AI学" # 应用名称 +API_ENDPOINT = "https://api.appstoreconnect.apple.com/v1" + + + +MAX_RETRIES = 10 # 最大重试次数 +RETRY_DELAY = 30 # 重试间隔(秒) +if 'PYCHARM_HOSTED' in os.environ: # 说明是pycharm + Auth_Key_Path = './' +else: + Auth_Key_Path = '/Users/jlbank/Desktop/lx_uniapp/auto_build' # p8私钥文件位置 + +private_key_path = Path(f'{Auth_Key_Path}/AuthKey_{API_KEY}.p8') + +def generate_jwt_token(api_key, api_issuer, private_key_path): + """生成 JWT 令牌""" + with open(private_key_path, "r") as f: + private_key = f.read() + + # 令牌有效期 20 分钟 + current_time = int(time.time()) + payload = { + "iss": api_issuer, + "exp": current_time + 20 * 60, + "iat": current_time, + "aud": "appstoreconnect-v1" + } + + # 生成 JWT 令牌 + token = jwt.encode( + payload, + private_key, + algorithm="ES256", + headers={"kid": api_key} + ) + return token + + +def find_specific_build(app_id, headers): + """通过预发布版本ID查找指定构建版本,带重试机制""" + retries = 0 + + while retries < MAX_RETRIES: + try: + print(f"\n🔍 查找版本 {TARGET_VERSION} 构建号 {TARGET_BUILD_NUMBER} (尝试 {retries + 1}/{MAX_RETRIES})") + + # 1. 先获取目标版本对应的预发布版本ID + print(" 正在获取预发布版本ID...") + pre_release_url = f"{API_ENDPOINT}/preReleaseVersions" + pre_release_params = { + "filter[version]": TARGET_VERSION, + "filter[app]": app_id + } + + pre_release_response = requests.get(pre_release_url, headers=headers, params=pre_release_params) + pre_release_response.raise_for_status() + pre_release_data = pre_release_response.json() + + if not pre_release_data["data"]: + print(f"❓ 未找到版本为 {TARGET_VERSION} 的预发布版本") + retries += 1 + if retries < MAX_RETRIES: + time.sleep(RETRY_DELAY) + continue + + pre_release_id = pre_release_data["data"][0]["id"] + print(f" 找到预发布版本ID: {pre_release_id}") + + # 2. 使用预发布版本ID筛选构建版本(修正参数错误) + builds_url = f"{API_ENDPOINT}/builds" + params = { + "filter[preReleaseVersion]": pre_release_id, + "sort": "-version", + "limit": "10", + # 移除了无效的ciBuildGroup关联 + "include": "buildBetaDetail,betaBuildLocalizations,preReleaseVersion,betaAppReviewSubmission,icons,buildBundles,betaGroups", + # 移除了无效的ciBuildGroups字段 + "limit[betaBuildLocalizations]": "40", + # 将betaGroups的limit调整为最大值50 + "limit[betaGroups]": "50" + } + + response = requests.get(builds_url, headers=headers, params=params) + + if response.status_code == 400: + print(f" 错误响应内容: {response.text}") + response.raise_for_status() + + builds_data = response.json() + + # 在返回结果中筛选出符合构建号的构建 + matching_builds = [ + b for b in builds_data["data"] + if b["attributes"].get("version") == TARGET_BUILD_NUMBER + ] + if not matching_builds: + print(f"❓ 未找到版本 {TARGET_VERSION} 构建号 {TARGET_BUILD_NUMBER} 的构建") + else: + # 检查是否有状态为VALID的构建 + valid_build = next(( + b for b in matching_builds + if b["attributes"]["processingState"] == "VALID" + ), None) + + if valid_build: + return valid_build + else: + # 显示找到的构建但状态不是VALID + states = [b["attributes"]["processingState"] for b in matching_builds] + print(f"⏳ 找到匹配的构建但状态为: {', '.join(states)},等待处理完成...") + + # 重试前等待 + if retries < MAX_RETRIES - 1: + print(f"将在 {RETRY_DELAY} 秒后重试...") + time.sleep(RETRY_DELAY) + + retries += 1 + + except Exception as e: + print(f"查询过程中出错: {str(e)}") + retries += 1 + if retries < MAX_RETRIES: + time.sleep(RETRY_DELAY) + + return None +def main(): + try: + # 1. 生成 JWT 令牌 + + if not private_key_path.exists(): + raise FileNotFoundError(f"私钥文件不存在: {private_key_path}") + + token = generate_jwt_token(API_KEY, API_ISSUER, private_key_path) + print("✅ JWT 令牌生成成功") + + # 2. 配置请求头 + headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json" + } + + # 3. 获取应用 ID + print(f"\n🔍 查找应用: {APP_NAME}") + apps_url = f"{API_ENDPOINT}/apps" + response = requests.get(apps_url, headers=headers) + response.raise_for_status() + + apps_data = response.json() + app = next((a for a in apps_data["data"] if a["attributes"]["name"] == APP_NAME), None) + if not app: + raise ValueError(f"未找到应用: {APP_NAME}") + + app_id = app["id"] + print(f"✅ 找到应用 ID: {app_id}") + + # 4. 获取指定版本和构建号的构建版本(带重试) + valid_build = find_specific_build(app_id, headers) + if not valid_build: + raise ValueError( + f"经过 {MAX_RETRIES} 次尝试后,仍未找到可用的构建版本: {TARGET_VERSION} ({TARGET_BUILD_NUMBER})") + + build_id = valid_build["id"] + build_version = TARGET_VERSION + build_number = valid_build["attributes"]["version"] + print(f"✅ 找到构建版本 ID: {build_id} (版本号: {build_version}, 构建号: {build_number})") + + # 5. 获取外部测试群组 ID + print(f"\n🔍 查找测试群组: {BETA_GROUP_NAME}") + groups_url = f"{API_ENDPOINT}/betaGroups" + response = requests.get(groups_url, headers=headers) + response.raise_for_status() + + groups_data = response.json() + beta_group = next((g for g in groups_data["data"] if + g["attributes"]["name"] == BETA_GROUP_NAME and + g["attributes"]["isInternalGroup"] is False), None) # 确保是外部群组 + if not beta_group: + raise ValueError(f"未找到外部测试群组: {BETA_GROUP_NAME}") + + beta_group_id = beta_group["id"] + print(f"✅ 找到外部测试群组 ID: {beta_group_id}") + + # 6. 添加构建到测试群组后,更新测试内容(betaBuildLocalizations) + print("\n📝 更新测试版本的本地化信息(whatsNew)...") + + # 7. 将构建版本添加到测试群组 + print(f"\n🔗 将构建 {build_id} 添加到测试群组 {beta_group_id}...") + add_build_url = f"{API_ENDPOINT}/betaGroups/{beta_group_id}/relationships/builds" + + # 准备添加构建的请求数据 + add_build_payload = { + "data": [{"id": build_id, "type": "builds"}] + } + + # 发送添加请求 + add_response = requests.post(add_build_url, headers=headers, json=add_build_payload) + try: + add_response.raise_for_status() + except requests.exceptions.HTTPError as e: + # 处理已存在的情况(409 Conflict 是正常现象,说明已添加) + if add_response.status_code == 409: + print("⚠️ 构建已在测试群组中,跳过添加步骤") + else: + print(f"❌ 添加构建到测试群组失败: {e}") + print("错误详情:", add_response.json()) + sys.exit(1) + else: + print("✅ 构建已成功添加到测试群组") + # 8. 更新测试内容(whatsNew) + # 先获取该构建的本地化信息 ID(如果已有) + # 构建 ID(build_id 是你要操作的构建版本 ID) + get_localizations_url = f"{API_ENDPOINT}/builds/{build_id}/betaBuildLocalizations" + response = requests.get(get_localizations_url, headers=headers) + + try: + response.raise_for_status() + except requests.exceptions.HTTPError as e: + print(f"❌ 获取本地化信息失败: {e}") + print("错误详情:", response.json()) + sys.exit(1) + localizations = response.json().get("data", []) + + print("\n📝 更新测试版本说明...") + if localizations: + # 使用现有本地化信息 + localization_id = localizations[0]["id"] + update_url = f"{API_ENDPOINT}/betaBuildLocalizations/{localization_id}" + print(f"使用现有本地化信息 ID: {localization_id}") + else: + # 创建新的本地化信息(默认简体中文) + print("创建新的本地化信息...") + create_url = f"{API_ENDPOINT}/betaBuildLocalizations" + create_payload = { + "data": { + "type": "betaBuildLocalizations", + "attributes": {"locale": "zh-Hans", "whatsNew": ""}, + "relationships": { + "build": {"data": {"id": build_id, "type": "builds"}} + } + } + } + create_response = requests.post(create_url, headers=headers, json=create_payload) + create_response.raise_for_status() + localization_id = create_response.json()["data"]["id"] + update_url = f"{API_ENDPOINT}/betaBuildLocalizations/{localization_id}" + now_time_text = get_chinese_date_time() + # 更新测试内容 + update_payload = { + "data": { + "id": localization_id, + "type": "betaBuildLocalizations", + "attributes": { + "whatsNew": f"当前版本: {TARGET_BUILD_NUMBER}\n提交时间:{now_time_text}\n自动化提交" + } + } + } + update_response = requests.patch(update_url, headers=headers, json=update_payload) + update_response.raise_for_status() + print("✅ 测试版本说明已更新") + + # 9. 提交审核(触发测试状态)- 修复API端点 + print("\n📋 提交构建版本审核...") + + # 正确的提交审核API端点 + submit_url = f"{API_ENDPOINT}/betaAppReviewSubmissions" + submit_payload = { + "data": { + "type": "betaAppReviewSubmissions", + "relationships": { + "build": { + "data": {"id": build_id, "type": "builds"} + } + } + } + } + + submit_response = requests.post(submit_url, headers=headers, json=submit_payload) + try: + submit_response.raise_for_status() + except requests.exceptions.HTTPError as e: + # 处理已提交的情况(409冲突) + if submit_response.status_code == 409: + print("⚠️ 构建已提交审核,无需重复操作") + else: + print(f"❌ 提交审核失败: {e}") + print("错误详情:", submit_response.json()) + sys.exit(1) + else: + print("✅ 构建版本已成功提交审核") + + + except Exception as e: + print(e) + exit(1) + + +if __name__ == "__main__": + + main() \ No newline at end of file diff --git a/devops/iso/老的/ready_build_ios.sh b/devops/iso/老的/ready_build_ios.sh new file mode 100644 index 00000000..bfe3a24b --- /dev/null +++ b/devops/iso/老的/ready_build_ios.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +# ============================================== +# 配置参数 - 请根据实际情况修改以下参数 +# ============================================== +# 钥匙串配置 +KEYCHAIN_PASSWORD="1234567" # 钥匙串密码 +KEYCHAIN_PATH="$HOME/Library/Keychains/login.keychain-db" # 钥匙串路径 + +# 项目路径配置 +PROJECT_DIR="$HOME/Desktop/lx_uniapp/build/git_work_code/Ios/HBuilder-Hello" # 项目目录 +PROJECT_NAME="HBuilder-Hello.xcodeproj" # Xcode项目文件名 +SCHEME_NAME="HBuilder" # 构建方案名称 +CONFIGURATION="Release" # 构建配置 + +# 构建输出配置 +BUILD_DIR="$HOME/Desktop/lx_uniapp/build" # 构建输出目录 +ARCHIVE_NAME="HBuilder.xcarchive" # 归档文件名 +IPA_DIR_NAME="ipa_production" # IPA输出目录名 +DERIVED_DATA_DIR="$HOME/Library/Developer/Xcode/DerivedData/*" # 衍生数据目录 + +# 版本配置 +MARKETING_VERSION="$1" # 市场版本号 +CURRENT_PROJECT_VERSION="$2" # 项目内部版本号 + +# 签名配置 +CODE_SIGN_IDENTITY="Apple Distribution: Bank of Jilin Co., Ltd. (2EP6SRRN43)" # 签名标识 +PROVISIONING_PROFILE="e3487fe1-19dc-4fc7-90df-11caf27da0c6" # 配置文件UUID +DEVELOPMENT_TEAM="2EP6SRRN43" # 开发团队ID +CODE_SIGNING_REQUIRED="YES" # 是否需要签名 + +# 导出配置 +EXPORT_OPTIONS_PLIST="$BUILD_DIR/ExportOptions_Production.plist" # 导出配置plist路径 +# ============================================== + +# 错误处理函数 +error_exit() { + echo "❌ $1" + exit 1 +} + +# 0. 解锁钥匙串并授予权限 +echo "🔑 解锁钥匙串..." +security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" || { + error_exit "钥匙串解锁失败" +} + +# 1. 进入项目目录 +echo "📂 进入项目目录: $PROJECT_DIR" +cd "$PROJECT_DIR" || { + error_exit "无法进入项目目录: $PROJECT_DIR" +} + +# 2. 清理旧构建 +echo "🧹 清理旧构建..." +xcodebuild -project "$PROJECT_NAME" -scheme "$SCHEME_NAME" -configuration "$CONFIGURATION" clean || { + error_exit "清理构建失败" +} + +# 3. 删除上次构建内容 +echo "🗑️ 删除上次构建内容..." +setopt rm_star_silent nullglob +rm -rf "$BUILD_DIR/$ARCHIVE_NAME" "$BUILD_DIR/$IPA_DIR_NAME" "$DERIVED_DATA_DIR" +unsetopt rm_star_silent nullglob # 恢复默认设置 + +# 4. 归档项目 +echo "📦 开始归档项目..." +xcodebuild -project "$PROJECT_NAME" \ + -scheme "$SCHEME_NAME" \ + -configuration "$CONFIGURATION" \ + -archivePath "$BUILD_DIR/$ARCHIVE_NAME" \ + MARKETING_VERSION="$MARKETING_VERSION" \ + CURRENT_PROJECT_VERSION="$CURRENT_PROJECT_VERSION" \ + -destination 'generic/platform=iOS' \ + CODE_SIGN_IDENTITY="$CODE_SIGN_IDENTITY" \ + PROVISIONING_PROFILE="$PROVISIONING_PROFILE" \ + DEVELOPMENT_TEAM="$DEVELOPMENT_TEAM" \ + CODE_SIGNING_REQUIRED="$CODE_SIGNING_REQUIRED" \ + OTHER_CFLAGS="" \ + OTHER_CPLUSPLUS_FLAGS="" \ + OTHER_SWIFT_FLAGS="" \ + archive || { + error_exit "项目归档失败" +} + +# 5. 导出IPA文件 +echo "📤 导出IPA文件..." +xcodebuild -exportArchive \ + -archivePath "$BUILD_DIR/$ARCHIVE_NAME" \ + -exportPath "$BUILD_DIR/$IPA_DIR_NAME" \ + -exportOptionsPlist "$EXPORT_OPTIONS_PLIST" || { + error_exit "IPA导出失败" +} + +echo "🎉 构建完成!IPA文件已导出至: $BUILD_DIR/$IPA_DIR_NAME" diff --git a/devops/iso/老的/validate_and_upload.sh b/devops/iso/老的/validate_and_upload.sh new file mode 100644 index 00000000..5bcab95d --- /dev/null +++ b/devops/iso/老的/validate_and_upload.sh @@ -0,0 +1,92 @@ +#!/bin/bash +set -e # 脚本执行过程中若有命令失败则立即退出 + +# ============================================== +# 颜色定义 +# ============================================== +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # 无颜色(重置) + +# ============================================== +# 配置变量 - 根据实际情况修改以下参数 +# ============================================== +API_KEY="ACRB9XFL9G" # App Store Connect API 密钥ID +API_ISSUER="69a6de7f-3378-47e3-e053-5b8c7c11a4d1" # API 密钥对应的Issuer ID +IPA_PATH="$HOME/Desktop/lx_uniapp/build/ipa_production/HBuilder.ipa" # IPA文件路径 +APP_TYPE="ios" # 应用类型(固定为ios) + +# ============================================== +# 验证IPA文件是否存在 +# ============================================== +if [ ! -f "$IPA_PATH" ]; then + echo -e "${RED}❌ 错误:未找到IPA文件,请检查路径是否正确${NC}" + echo -e " 查找路径:$IPA_PATH" + exit 1 +fi +# ============================================== +# 1. 验证IPA文件合法性 +# ============================================== +echo -e "\n${YELLOW}📋 开始验证IPA文件...${NC}" +echo -e " 文件路径:$IPA_PATH" + +# 创建临时文件保存输出 +TMP_FILE=$(mktemp) + +# 执行验证命令,直接输出到终端,同时重定向到临时文件 +xcrun altool --validate-app \ + --type "$APP_TYPE" \ + --file "$IPA_PATH" \ + --apiKey "$API_KEY" \ + --apiIssuer "$API_ISSUER" > "$TMP_FILE" 2>&1 +# 直接打印临时文件内容(确保错误信息显示) +cat "$TMP_FILE" + +# 获取退出码 +VALIDATE_EXIT_CODE=$? + +# 从临时文件读取内容用于分析 +VALIDATE_OUTPUT=$(cat "$TMP_FILE") +rm "$TMP_FILE" # 清理临时文件 + +# 验证结果处理 +if [ $VALIDATE_EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}✅ IPA文件验证成功,符合上传要求${NC}" +else + # 检查是否是版本号重复错误 + if echo "$VALIDATE_OUTPUT" | grep -q "The bundle version must be higher than the previously uploaded version"; then + # 提取之前的版本号 + PREVIOUS_VERSION=$(echo "$VALIDATE_OUTPUT" | grep -oE 'previousBundleVersion = [0-9]+' | awk '{print $3}') + if [ -z "$PREVIOUS_VERSION" ]; then + PREVIOUS_VERSION=$(echo "$VALIDATE_OUTPUT" | grep -oE 'version: ‘[0-9]+’' | sed "s/version: ‘//;s/’//") + fi + + echo -e "${RED}❌ 错误:版本号重复${NC}" + echo -e "${RED} 已上传的最新版本号为: $PREVIOUS_VERSION${NC}" + echo -e "${RED} 请将版本号修改为 $((PREVIOUS_VERSION + 1)) 或更高后重试${NC}" + else + echo -e "${RED}❌ IPA文件验证失败,请查看上方错误信息${NC}" + fi + exit $VALIDATE_EXIT_CODE +fi +# ============================================== +# 2. 上传IPA文件到App Store Connect +# ============================================== +echo -e "\n${YELLOW}🚀 开始上传IPA文件...${NC}" +echo -e " 文件路径:$IPA_PATH" + +xcrun altool --upload-app \ + --type "$APP_TYPE" \ + --file "$IPA_PATH" \ + --apiKey "$API_KEY" \ + --apiIssuer "$API_ISSUER" + +# 上传成功提示 +if [ $? -eq 0 ]; then + echo -e "\n${GREEN}🎉 上传成功!可在App Store Connect中查看构建版本${NC}" + echo -e " 提示:构建版本处理需要几分钟,请耐心等待" +else + echo -e "\n${RED}❌ 上传失败,请根据错误信息修复后重试${NC}" + exit 1 +fi diff --git a/src/api/request.js b/src/api/request.js index 9081ebc3..e9a30160 100644 --- a/src/api/request.js +++ b/src/api/request.js @@ -35,7 +35,7 @@ export const get_base_url = (url = '') => { } // #endif // #ifdef APP-PLUS - return 'https://aitstest.jlbank.com.cn:7002' || ENV.VITE_APP_BASE_API_Url + return ENV.VITE_APP_BASE_API_Url // #endif } else { // 其他环境,包括生产环境,sit环境,uat环境 diff --git a/src/components/points-and-badge/badge.vue b/src/components/points-and-badge/badge.vue index 0754586f..af0e637e 100644 --- a/src/components/points-and-badge/badge.vue +++ b/src/components/points-and-badge/badge.vue @@ -1,17 +1,304 @@ - + diff --git a/src/components/points-and-badge/check-in.vue b/src/components/points-and-badge/check-in.vue index 486b2e06..ec8fcadc 100644 --- a/src/components/points-and-badge/check-in.vue +++ b/src/components/points-and-badge/check-in.vue @@ -95,7 +95,7 @@ // 注意:如果天数从1开始(不是0),需要减1再计算 return Math.floor((currentDay - 1) / 7); }); - const emits = defineEmits(['checkInFun']) + const emits = defineEmits(['checkInFun']); // 切换显示状态 const switchStatus = () => { expand.value = !expand.value; @@ -109,14 +109,24 @@ const checkIn = () => { if (checkInLoading.value) return; checkInLoading.value = true; - addPointsByCheckIn({days:6}) + addPointsByCheckIn({ days: continuousDay.value }) .then((res) => { console.log('签到结果', res); checkInLoading.value = false; isSigned.value = true; continuousDay.value = continuousDay.value + 1; - common.msg('签到成功,请明日继续!') - emits('checkInFun', true) + // common.msg('签到成功,请明日继续!') + emits( + 'checkInFun', + true, + [{ changePoints: 5 }], + [ + { + pointsBadgeImg: '/traoss/tras3/show/S3F0250181220722025110713501700000661177', + pointsBadgeName: '黄金徽章' + } + ] + ); }) .catch((err) => { checkInLoading.value = false; @@ -195,11 +205,11 @@ return groupedList; } - - const open = (checkInToday,days) => { - isSigned.value = checkInToday==='Y'?true:false// 今日签到状态 - // isSigned.value = false// 今日签到状态 - continuousDay.value = days // 连续签到 + + const open = (checkInToday, days) => { + isSigned.value = checkInToday === 'Y' ? true : false; // 今日签到状态 + isSigned.value = false; // 今日签到状态 + continuousDay.value = days; // 连续签到 // 示例:X=10时,以"10天前"为第1天,生成35天列表 try { const X = continuousDay.value - (isSigned.value ? 1 : 0); @@ -207,18 +217,6 @@ console.log(`以${continuousDay.value}天`); console.log(`以${X}天前为第1天的35天分组日期:`); dateGroups.value = generateGroupedDateList(X); - - // dateGroups.value.forEach((group) => { - // console.log(`\n第${group.groupIndex + 1}组:`); - // console.log( - // group.days.map((day) => ({ - // 日期: day.date, - // 天数序号: day.index, // 显示当前是第几天 - // text: day.text, - // 类型: day.type - // })) - // ); - // }); } catch (err) { console.error(err.message); } diff --git a/src/components/points-and-badge/points.vue b/src/components/points-and-badge/points.vue index 02409e35..0b4aaadd 100644 --- a/src/components/points-and-badge/points.vue +++ b/src/components/points-and-badge/points.vue @@ -1,8 +1,171 @@ - - \ No newline at end of file + diff --git a/src/pages/me/index.vue b/src/pages/me/index.vue index 195bc114..4d507197 100644 --- a/src/pages/me/index.vue +++ b/src/pages/me/index.vue @@ -48,8 +48,8 @@ 工号:{{ user_info.loginName }} - - + + @@ -64,7 +64,7 @@ > - + 徽章墙 @@ -97,7 +97,7 @@ - + AI陪练记录 @@ -159,6 +159,8 @@ @confirm="avatarOnConfirm" > + + @@ -179,6 +181,8 @@ import useUpdateAvatar from '@/composables/useUpdateAvatar'; import { queryCurrentStatus, queryCheckIn } from '@/api/pointsAndRank.js'; import checkIn from '@/components/points-and-badge/check-in'; + import badge from '@/components/points-and-badge/badge'; + import points from '@/components/points-and-badge/points'; const { avatarCropperUrl, click_update_avatar, avatarCropperStatus, avatarOnCancel, avatarOnConfirm } = useUpdateAvatar({ success: (userData) => { user_info.imageAddr = userData.imageAddr; @@ -220,8 +224,10 @@ const socketStore = useSocketStore(); const badgeNumRef = ref(null); const checkInRef = ref(null); + const badgeRef = ref(null); + const pointsRef = ref(null); const currentPointsRef = ref(null); - + const user_info = reactive({ userName: '', userId: '', @@ -231,19 +237,28 @@ checkInDays: 0 // 连续签到天数 }); const goTest = () => { - console.log('goTest'); - // common.navigateTo('/pages/test/index'); + common.navigateTo('/pages/test/index'); + }; + const test = () => { + badgeRef.value.open(); }; // 签到打开 const checkInClick = () => { checkInRef.value.open(user_info.checkInToday, user_info.checkInDays); }; // 签到成功回调 - const checkInFun = (status) => { - if(status) { - getCheckInInfo() + const checkInFun = (status, points = [], pointsBadges = []) => { + if (status) { + getCheckInInfo(); } + pointsRef.value.open(points); + // badgeRef.value.open(points); }; + + // 积分确认 + const pointsConfirm = () => { + + } const click_loginout = () => { common.show('确定退出登录?').then(async (res) => { if (!res) return; diff --git a/src/static/images/pointsAndRank/like.png b/src/static/images/pointsAndRank/like.png new file mode 100644 index 00000000..3bbca0ae Binary files /dev/null and b/src/static/images/pointsAndRank/like.png differ