一、 SDK 安装
1)Mac安装 Xcode command line tools:
打开终端输入命令
xcode-select --install
2)安装Fastlane
输入命令
sudo gem install fastlane -NV
//或者
brew install fastlane
//两个中任何一个都可以
3)初始化Fastlane
安装完成后就是在项目初始化Fastlane
cd到你的需要打包的项目目录,然后执行命令
fastlane init
效果如下:
4)安装安卓SDK
1、下载Android SDK
我们需要到官网去获取sdk的具体下载链接,然后下载到自己的服务器进行配置。
Android SDK官网地址:下载链接
SDK tools目前最新的下载地址:下载链接
2、配置环境变量
这里将下载好的文件进行解压存放,然后将解压后的路径配置到系统环境变量中:
vi /etc/profile
// 这里是个人的路径,请改为自己的路径
export ANDROID_HOME="/home/Android/android_sdk"
// 这里记得配置$PATH,不然其他的环境变量都会失效
export PATH="$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools:$PATH"
3、安装所需的Android编译版本
这里的安装可以使用编译工具tools的bin目录下面的sdkmanager命令进行,安装前可以通过sdkmanager --list查看可以安装的项目。
第四步、配置Jenkins的Android环境变量
系统Android SDK配置好后需要到Jenkins的系统管理 > 系统配置中增加Android的环境变量。
5)安装Gadle
这里不一定在系统中进行安装,可以直接在Jenkins中进行配置,也是可以完成安装的,这里就先简单介绍以下Centos配置Gradle环境。
//下载Gadle
wget https://services.gradle.org/distributions/gradle-6.5-all.zip
// 解压
unzip https://services.gradle.org/distributions/gradle-6.5-all.zip
// 配置环境变量
vi /etc/profile
// 配置gradle目录
export GRADLE_HOME="/home/Androd/gradle-6.5"
// 加入系统环境变量
export PATH="$PATH:$GRADLE_HOME/bin"
二、Jenkins 安装 和 配置
1)首先检查是否有Jenkins依赖的java环境
终端输入命令
java -version
出现java version "1.8.xx"说明已经安装了java
如果没有安装,去下面官网下载对应自己电脑系统的版本安装:下载链接
2)检查是否有安装HomeBrew
终端输入命令 brew -v
有显示Homebrew版本说明已经安装
如果没有则使用以下命令安装Homebrew
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
终端输入下面命令安装最新版稳定Jenkins,安装时间较长
brew install jenkins-lts
启动Jenkins服务
brew services start jenkins-lts
其他命令
//启动 Jenkins 服务:
brew services start jenkins-lts
//重启 Jenkins 服务
brew services restart jenkins-lts
安装完成后,打开链接 http://127.0.0.1:8080/job/iOS_Build,其配置如下:
三、 构建打包上传云平台以及TestFlight
1. iOS 构建脚本
source ~/.zshrc
cd client_iOS
[ -f Gemfile.lock ] && rm Gemfile.lock
# 获取版本号
VersionCode=$(bundle exec fastlane get_project_version | grep "Formatted project version" | awk '{print $NF}')
# React Native 构建
cd ../rn
./rn_build.sh ${VersionCode}
# Fastlane 打包
cd ../client_iOS
bundle install
$(bundle show fastlane)/bin/fastlane ${BuildType} branch_name:${Branch} rn_branch:${RN_Branch}
2. Android 构建脚本
export ANDROID_SDK_ROOT=/Users/xxx/android_sdk_applecpu
export JAVA_HOME=/opt/homebrew/opt/openjdk@17
ABranch=${AndBranch#origin/}
RBranch=${RnBranch#origin/}
java --version
echo "=== React native 构建 ==="
cd rn
node ./build_bundle.js -p=android -bigVersion="${BuildVersionCode}" -desc=jenkins打包 -env=test
mv ./build_entry/output/android.zip ../client_android/reactnative/src/main/assets
ip="http://$(ifconfig | grep 'inet ' | grep -v 127.0.0.1 | awk '{print $2}'):8080"
ip_pub="http://x.x.x.x:10xxx"
echo "=== Android 构建 ==="
cd ../client_android
chmod +x ./gradlew
./gradlew clean
if [ "$BuildBundle" == "true" ]; then
./gradlew "gacapp:bundleApp${BuildVersion}Release" \
-PVERSION_NAME=${BuildVersionName} \
-PVERSION_CODE=${BuildVersionCode} \
--no-build-cache \
--stacktrace
AAB_OUTPUT_DIR="./gacapp/build/outputs/bundle/app${BuildVersion}Release/"
AAB_FILE=$(find "$AAB_OUTPUT_DIR" -type f -name "*.aab")
# 局域网屏蔽--start--
# AAB_OBS_PATH="aab/client/${BuildVersion}/${BuildVersionCode}/app${BuildVersion}_${BuildVersionCode}_${BUILD_NUMBER}.aab"
# /Users/xx/obsutil/upload_apk_to_obs.sh "${AAB_FILE}" "${AAB_OBS_PATH}"
# QR_IMG="aab/client/${BuildVersion}/${BuildVersionCode}/app${BuildVersion}_${BuildVersionCode}_${BUILD_NUMBER}.png"
# OBS_URL="https://android-autobuild-files.xxx.cc/${AAB_OBS_PATH}"
# 局域网屏蔽--end--
# 局域网
mv "${AAB_FILE}" "/Users/xxx/Shared/Jenkins/AAB_Output/app${BuildVersion}_${BuildVersionCode}_${BUILD_NUMBER}.aab"
/opt/homebrew/bin/qrencode -m 8 -o "/Users/xx/Shared/Jenkins/AAB_Output/app${BuildVersion}_${BuildVersionCode}_${BUILD_NUMBER}.png" "${ip}/AAB_Output/app${BuildVersion}_${BuildVersionCode}_${BUILD_NUMBER}.aab"
QR_IMG="${ip}/AAB_Output/app${BuildVersion}_${BuildVersionCode}_${BUILD_NUMBER}.png"
OBS_URL="${ip_pub}/AAB_Output/app${BuildVersion}_${BuildVersionCode}_${BUILD_NUMBER}.aab"
else
./gradlew "gacapp:assembleApp${BuildVersion}Release" \
-PVERSION_NAME=${BuildVersionName} \
-PVERSION_CODE=${BuildVersionCode} \
--no-build-cache \
--stacktrace
APK_OUTPUT_DIR="./gacapp/build/outputs/apk/app${BuildVersion}/release"
APK_FILE=$(find "$APK_OUTPUT_DIR" -type f -name "*.apk")
# 局域网屏蔽--start--
# APK_OBS_PATH="apk/client/${BuildVersion}/${BuildVersionCode}/app${BuildVersion}_${BuildVersionCode}_${BUILD_NUMBER}.apk"
# QR_IMG="apk/client/${BuildVersion}/${BuildVersionCode}/app${BuildVersion}_${BuildVersionCode}_${BUILD_NUMBER}.png"
# /Users/flyme/obsutil/upload_apk_to_obs.sh "${APK_FILE}" "${APK_OBS_PATH}"
# OBS_URL="https://android-autobuild-files.xxxxx.cc/${APK_OBS_PATH}"
# 局域网屏蔽--end--
# 局域网
mv "${APK_FILE}" "/Users/xx/Shared/Jenkins/APK_Output/app${BuildVersion}_${BuildVersionCode}_${BUILD_NUMBER}.apk"
/opt/homebrew/bin/qrencode -m 8 -o "/Users/flyme/Shared/Jenkins/APK_Output/app${BuildVersion}_${BuildVersionCode}_${BUILD_NUMBER}.png" "${ip}/APK_Output/app${BuildVersion}_${BuildVersionCode}_${BUILD_NUMBER}.apk"
QR_IMG="${ip}/APK_Output/app${BuildVersion}_${BuildVersionCode}_${BUILD_NUMBER}.png"
OBS_URL="${ip_pub}/APK_Output/app${BuildVersion}_${BuildVersionCode}_${BUILD_NUMBER}.apk"
fi
# 局域网屏蔽--start--
# /opt/homebrew/bin/qrencode -m 8 -o ./qr_code.png "${OBS_URL}"
# /Users/xx/obsutil/upload_apk_to_obs.sh "./qr_code.png" "${QR_IMG}"
# /Users/flyme/obsutil/wx.sh "${BuildVersionName}" "${BuildVersionCode}" "${RBranch}" "${ABranch}" "${Describe}" "${OBS_URL}" "https://android-autobuild-files.lagride.ng/${QR_IMG}" "${BuildVersion}"
# 局域网屏蔽--end--
/Users/xx/obsutil/wx.sh "${BuildVersionName}" "${BuildVersionCode}" "${ABranch}" "${Describe}" "${OBS_URL}" "${QR_IMG}" "${BuildVersion}"
华为云 OBS 上传 (Ruby 实现)
def obs_upload(output_directory, ipaName, ftp_directory)
s3_client = Aws::S3::Client.new(
access_key_id: @access_key_id,
secret_access_key: @access_key_secret,
endpoint: "https://#{@obs_endpoint}",
region: 'af-south-1',
force_path_style: true
)
# 上传 IPA
s3_client.put_object(
bucket: @obs_bucket,
key: "#{ftp_directory}/#{ipaName}.ipa",
body: File.open("#{output_directory}/#{ipaName}.ipa", 'rb'),
content_type: 'application/octet-stream'
)
# 生成下载二维码
item_service_url = "itms-services://?action=download-manifest&url=#{obs_plist_url}"
qrcode = RQRCode::QRCode.new(item_service_url)
png = qrcode.as_png(size: 400)
IO.binwrite("#{output_directory}/#{ipaName}.png", png.to_s)
# 企业微信通知
wechat_work_push(qrcode_url, download_url, changelog)
end
4. TestFlight 上传脚本
def appStoreUpload(ipaPath)
api_key = app_store_connect_api_key(
key_id: "P8xxxxxxxx",
issuer_id: "xxx-xxxx-xxxx-xxxx-c5d2116a0xxx",
key_filepath: "./fastlane/AuthKey_P8XXXXXXXXX.p8"
)
upload_to_testflight(
api_key: api_key,
ipa: ipaPath,
skip_submission: true,
skip_waiting_for_build_processing: true
)
end
四、消息通知机制
def wechat_work_push(qrcode_url = nil, download_url = nil, changelog, upload_status, options)
ios_branch_name = get_current_branch(options)
rn_branch_name = get_rn_branch_name(options)
content = if upload_status == "TestFlight上传成功"
"### 客户端testFlight上传成功\n" \
"> #### 版本环境: #{@textEnv}\n" \
"> #### 版本号: #{@versionNum}\n" \
"> #### iOS分支: #{ios_branch_name}\n" \
"> #### RN分支: #{rn_branch_name}\n" \
"> #### 编译号: #{get_build_number()}\n" \
"> #### 修改如下:\n#{changelog}\n" \
">@所有人"
else
"### 客户端IPA上传成功\n" \
"> #### 版本环境: #{@textEnv}\n" \
"> #### 版本号: #{@versionNum}\n" \
"> #### iOS分支: #{ios_branch_name}\n" \
"> #### RN分支: #{rn_branch_name}\n" \
"> #### 编译号: #{get_build_number()}\n" \
"> #### 修改如下:\n#{changelog}\n" \
"> #### 二维码地址: \n" \
"> #### 下载地址: #{download_url}\n" \
">@所有人"
end
message = {
"msgtype": "markdown",
"markdown": {
"content": content
}
}
uri = URI(@webhook_url)
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
request = Net::HTTP::Post.new(uri.request_uri)
request.add_field('Content-Type', 'application/json')
request.body = message.to_json
response = https.request(request)
puts "企业微信推送响应: #{response.code} #{response.message}: #{response.body}"
end
五、安全最佳实践
1.密钥管理
🔒 使用环境变量存储华为云
access_key_id/secret_access_key
🔒 TestFlight API Key 通过加密文件存储 (.p8 文件)
2.权限控制
s3_client.put_bucket_acl(
bucket: @obs_bucket,
acl: 'public-read' # 按需开放权限
)
六、结果通知示例
1.iOS测试包通知
2.安卓测试包通知
3、上传TestFlight 通知
4、上传GP 通知
(内容与上方类似,略)