前言
在 2022 年年底,我从 0 到 1 主导负责公司的“商品开发”移动端项目,并在 2023 年中旬完成了该项目的第一期的需求开发。期间从 Flutter 基础学起,并在较短时间内完成相关业务功能开发,同时搭建起该项目的整个自动化构建流程,支持一键分发 ipa 与 apk,在这个过程中,我学到了 Flutter 的相关知识,同时也了解了整个应用程序的分发流程。在此,感谢在这条路上帮助我的各位网友以及同事,让我在极短时间内完成整个项目的第一期开发。在这篇文章中,你将了解如下内容:
Flutter 开发环境搭建
Fastlane 安装 & 编写 Fastlane 脚本
编写构建执行脚本
Jenkins 安装 & 编写 Jenkins 脚本
Flutter 开发环境搭建
公司下发的电脑是 MacBook Pro,因此,关于 Flutter 的开发环境搭建是基于 macOS 操作系统进行搭建的,可参考我过往写过的这篇文章:《Flutter 完整开发环境搭建》。
关于 windows 操作系统如何搭建 Flutter 开发环境,大家可参考技术胖过往发布的视频:《Flutter开发环境搭建windows版》。我记得我第一次接触 Flutter 时,就是从技术胖的博客网站的这篇文章看起的,相较于 Flutter 官网介绍的安装方式,技术胖所讲解的对于当时的我来说通俗易懂。
如果,你想了解接下来如何分发 Ipa 文件,我建议还是用 mac 最为合适。我记得在大学期间做老师的一个校企项目中了解到了一个比较流行的跨端框架,是 DCloud 团队推出的 MUI,那会用 Hbuilder 完成了 apk 和 ipa 文件的上传,但是还是会绕不过借助 mac 申请相关证书,才能在 Hbuilder 上去上传我们的移动端应用程序。当然,也有第三方的工具支持,或者黑苹果,但是我不是很建议,主要还是出于安全以及异常问题的处理考虑。
Fastlane 安装 & 编写 Fastlane 脚本
Fastlane是一个用于iOS和Android应用程序开发的工具集,旨在简化开发流程中的一些重复性任务。它提供了一组命令行工具,帮助开发者自动化构建、测试、部署和发布移动应用。Fastlane的功能包括自动化代码签名、生成和管理应用截图、自动化发布到App Store和Google Play等。它可以与持续集成/持续交付(CI/CD)系统集成,以进一步提高开发团队的效率和工作流程。我在项目中,主要是用该工具完成了 IOS 的自动化构建,同时将其分发至 App Store 或蒲公英。
安装
安装前先检查是否安装了 Ruby 和 Xcode 命令行工具(在参考链接中给出了相关安装教程):
# 1. 查看是否安装了 Ruby
ruby -v
# 2. 检查是否安装了 Xcode 命令行工具
xcode-select -p
# bundler
bundler --version
# fastlane
sudo gem install fastlane
# xcode
xcode-select --install
# bundler
sudo gem install bundler
这里我的安装方式是通过 Ruby + Bundler 的方式来安装。Fastlane 是通过 Ruby 编写的,故 Fastlane 对 Ruby 版本是有一定要求的,Fastlane 支持的 Ruby 版本是 2.5+。
如果,发现当前系统版本低于 2.5,建议对 Ruby 进行升级,相关升级文档已经贴在参考链接栏目中。
Bundler
Bundler 是一个用于管理 Ruby 程序的依赖关系的工具。当你开发一个 Ruby 项目时,通常会依赖于其他的 Ruby gem 包,而 Bundler 能够帮助你确保项目依赖的 gem 包能够正确地被安装和加载。它会根据你的项目中的 Gemfile 文件来安装和管理 gem 包。Gemfile 是一个指定了项目所需 gem 包及其版本的清单文件。
在 ios 根目录下新建 Gemfile 文件,并输入以下内容:
source "https://rubygems.org"
gem "fastlane"
这样明确定义依赖和版本,能加快 fastlane 的执行速度,当然,在执行 fastlane init 后也会自动生成。
Fastlane 配置
1. 初始化 fastlane 配置文件
# 进入项目的 ios 文件夹
cd ios
# 初始化 fastlane 配置,执行后会自动生成 fastlane 文件夹
fastlane init
执行 init 指令后,我们就能看到有几个重要的文件,分别是:Appfile、Fastfile、Pluginfile,从文件名上看,我们不免就能大致明白各个文件的作用是什么。
文件 | 作用 |
Appfile | Appfile 存储了跨所有 fastlane 工具都会用到的有用信息,比如你的 Apple ID 或应用程序 Bundle Identifier,以便更快地部署并根据项目需求进行定制。 |
Fastfile | Fastfile 存储了可以使用 fastlane 运行的自动化配置 |
Pluginfile | 通过 Pluginfile,可更方便地管理和维护 fastlane 中使用的各种插件,确保项目中的插件版本和依赖关系正确地配置和一致 |
2. Appfile 文件配置
# ios 的 Bundle id
app_identifier "******"
# apple developer 账号
apple_id "******"
3. Fastfile 文件配置
因为我们需要将 Ipa 分发到 App Store 和蒲公英上,因此会需要一系列的 key:
key_id:生成 .p8 文件时指定的 key_id
issuer_id
是用于标识你在 App Store Connect 中创建的 API 密钥的唯一标识符
Fastlane 可以使用你的 App Store Connect API 密钥来执行相关的操作
pgyer-api_key:蒲公英上的key
pgyer-password:从蒲公英上提取 App 时,需要键入密码
了解了当中的 key 之后,我们就可以编写出以下代码:
# fastlane 版本
fastlane_version = "2.212.2"
default_platform(:ios)
# 执行超时时间
ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "30"
# 行失败后重试的次数
ENV["FASTLANE_XCODEBUILD_SETTINGS_RETRIES"] = "20"
platform :ios do
desc "构建 IPA 生产脚本"
lane :ProductRelease do
time = Time.new.strftime("%Y%m%d")
version = get_version_number
ipaName = "ProductApp_Release_#{version}_#{time}.ipa"
gym(
clean: true,
output_directory: "./../build",
output_name: "#{ipaName}",
scheme: "ProductRelease",
configuration: "Release",
# include_bitcode:true,
# include_symbols:true,
export_method: "app-store",
export_xcargs: "-allowProvisioningUpdates"
)
notification(
app_icon: "./fastlane/successful.png",
title: "ProductApp Build Success",
subtitle: "打包成功,已导出安装包",
message: "准备发布中……"
)
# 配置上传到App Store connect的api_key
api_key = app_store_connect_api_key(
key_id: "*********",
issuer_id: "***************",
key_filepath: "./../buildConfig/ios/AuthKey_******.p8",
duration: 1200,
in_house: false
)
# 上传到testflight
upload_to_testflight(
api_key: api_key,
skip_waiting_for_build_processing: true,
ipa: "./../build/#{ipaName}",
skip_submission: true
)
notification(
app_icon: "icon.png",
title: "LoanManager",
subtitle: "IPA上传成功",
message: "自动打包完成!"
)
end
desc "构建 IPA AdHoc 脚本"
lane :ProductAdhoc do
time = Time.new.strftime("%Y%m%d")
version = get_version_number
ipaName = "ProductApp_Adhoc_#{version}_#{time}.ipa"
gym(
scheme: "ProductAdhoc",
configuration: "AdHoc",
output_directory: "./../build",
output_name: ipaName,
export_method: "ad-hoc",
export_options: {
uploadBitcode: false,
compileBitcode: false,
provisioningProfiles: {
"********": "product-Ad-hoc" # key 是你的 App bundlerId
}
}
)
notification(
app_icon: "./fastlane/successful.png",
title: "ProductApp Build Success",
subtitle: "打包成功,已导出安装包",
message: "准备发布中……"
)
pgyer(
api_key: "***************",
ipa: "./../build/#{ipaName}",
update_description: "ProductApp for test befoe upload app to testflight",
install_type: "2",
password: "*********"
)
notification(
app_icon: "icon.png",
title: "LoanManager",
subtitle: "IPA上传至蒲公英成功",
message: "自动打包完成!"
)
end
desc "仅上传 ipa 文件至 AppStore"
lane :UploadIpaToAppStore do
notification(
app_icon: "./fastlane/successful.png",
title: "ProductApp Build Success",
subtitle: "打包成功,已导出安装包",
message: "准备发布中……"
)
# 配置上传到App Store connect的api_key
api_key = app_store_connect_api_key(
key_id: "*********",
issuer_id: "***************", # AppConnect id
key_filepath: "./../buildConfig/ios/AuthKey_******.p8",
duration: 1200,
in_house: false
)
# 上传到testflight
upload_to_testflight(
api_key: api_key,
skip_waiting_for_build_processing: true,
ipa: "./../build/production/ipa/productApp.ipa",
skip_submission: true
)
notification(
app_icon: "icon.png",
title: "LoanManager",
subtitle: "IPA上传成功",
message: "自动打包完成!"
)
end
desc "仅上传 ipa 文件至蒲公英"
lane :UploadIpaToPgyer do
notification(
app_icon: "./fastlane/successful.png",
title: "ProductApp Build Success",
subtitle: "打包成功,已导出安装包",
message: "准备发布中……"
)
pgyer(
api_key: "***************", # 蒲公英的 key
ipa: "./../build/test/ipa/productApp.ipa",
update_description: "ProductApp for test befoe upload app to testflight",
install_type: "2",
password: "*********" # 在蒲公英上配置的密码
)
notification(
app_icon: "icon.png",
title: "LoanManager",
subtitle: "IPA上传至蒲公英成功",
message: "自动打包完成!"
)
end
end
我们执行 Fastlane 任务时,需要通过 Ruby 来编写相关配置,在 Fastlane 中,有 lane 的概念,lane 的作用是定义一系列的任务(tasks),用于执行特定的操作序列,例如构建应用程序、打包应用程序、上传到测试环境或发布到应用商店等。每个 lane 通常代表了一个特定的工作流程或者部署流程。通过定义不同的 lane,你可以在 fastlane 中轻松地管理和执行各种自动化任务,从而简化了应用程序的构建和部署流程,提高了开发效率。
我们在构建 Ipa 应用程序时,通常需要这样的文件:
Ad-hoc 分发是指将应用程序安装包(IPA 文件)通过特定的方式分发给受限制的用户群体,例如应用的测试人员或者内部员工。这种方式与通过应用商店进行正式发布不同,AdHoc 分发不会将应用程序提交到 App Store 审核流程中,而是直接将应用程序安装包分发给指定的用户。
Debug 配置用于开发过程中的调试目的。它通常包含了一些用于帮助开发者调试和排查问题的设置,比如启用调试符号、关闭优化等,除此之外,在 Debug 配置中,通常会允许开发者使用 Xcode 的调试工具,如断点调试、日志输出等,以便更轻松地发现和修复代码中的 bug。
Release配置用于应用程序发布阶段。它通常包含了一些用于优化应用程序性能和减小应用程序体积的设置,以及一些用于保护应用程序安全的设置。在 Release 配置中,通常会关闭调试符号和调试工具的输出,以减小应用程序的体积,并启用一些代码优化策略,以提高应用程序的运行性能。
除了以上 Profile 之外,还需要 .p12、.p8、.cer 等文件,其中,.p12 需要根据多环境要求申请不同的类型的 .p12 文件。
编写构建执行脚本
我们先看看手动执行构建,即在本地通过终端输入相关指令去构建应用程序。
1. 构建 APK
# 第一步
flutter pub get
# 第二步
flutter build apk --release --dart-define=ENVIROMENT=production
2. 构建 IPA
# 第一步
flutter pub get
# 第二步
cd /ios && arch -x86_64 pod install
# 第三步
cd ..
# 第四步
flutter build ios --release --dart-define=ENVIROMENT=production
# 第五步
xcodebuild -workspace $PWD/ios/Runner.xcworkspace -scheme Runner -sdk iphoneos -configuration Release archive -archivePath $PWD/build/Runner.xcarchive
在这个指令中,$PWD 是一个环境变量,代表当前工作目录的路径。$PWD 会被替换成执行该指令时所处的当前工作目录的路径。在这个命令中,$PWD 被用来指定工作空间和输出归档文件的路径。
至此,我们就知道如果在终端上构建应用程序了。接下来,我们需要编写 shell 脚本,然后通过 Jenkins 去调用 shell 脚本实现我们自动化构建的目的。下面是我编写的 shell 脚本指令,仅供参考:
# buildType -构建类型,值为 test 和 production
# isUpload -是否上传,值为 1 表示上传,0 表示不上传
# appType -APP的类型,值为 ipa、apk 和 all
for arg in "$@"
do
case "$arg" in
--buildType=*) buildType="${arg#*=}" ;;
--appType=*) appType="${arg#*=}" ;;
--isUpload=*) isUpload="${arg#*=}" ;;
esac
done
# 常量
readonly REMOVE_BUILD_FOLDER="删除build文件夹"
readonly PULL_NEW_CODE="从gitlab上拉取最新代码"
readonly FLUTTER_INSTALL="安装flutter最新依赖"
readonly BUILD_APK="构建APK"
readonly POD_INSTALL="安装IOS最新依赖"
readonly BUILD_RUNNER_APP="构建RunnerApp"
readonly BUILD_RUNNER_XCARCHIVE="构建RunnerXcarchive"
readonly BUILD_IPA="构建IPA"
readonly UPLOAD_APP_TO_APP_STORE="上传APP到AppStore"
readonly UPLOAD_APP_TO_PGY="上传APP到蒲公英"
CODER=0
# 状态
function getExecuteStatus() {
case "$1" in
"$REMOVE_BUILD_FOLDER")
CODER=10
;;
"$PULL_NEW_CODE")
CODER=20
;;
"$FLUTTER_INSTALL")
CODER=30
;;
"$BUILD_APK")
CODER=40
;;
"$POD_INSTALL")
CODER=50
;;
"$BUILD_RUNNER_APP")
CODER=60
;;
"$BUILD_RUNNER_XCARCHIVE")
CODER=70
;;
"$BUILD_IPA")
CODER=80
;;
"$UPLOAD_APP_TO_APP_STORE")
CODER=90
;;
"$UPLOAD_APP_TO_PGY")
CODER=100
;;
*)
CODER=0
;;
esac
}
# 异常捕捉
# $1 -脚本执行后的状态
# $2 -状态类型
function throwExecuteStatus() {
getExecuteStatus $2
tip=$2
if [ $1 -ne 0 ]; then
echo "ERROR: ${tip}失败"
exit $CODER
else
echo "🎉 💯 SUCCESS: ${tip}成功"
fi
}
# 包裹函数,统一输出
function wrapperFunction() {
printf "\n\n"
local function_desc=$1
local function_name=$2
echo "============= $function_desc Start ============="
printf "\n"
$function_name
printf "\n"
echo "============= $function_desc End ============="
}
# 如果只执行 sh build.sh 脚本,没有输入任何参数的话,则需要让用户输入指定的一些参数
function judgeBuildTypeIsNull() {
if [ -z "$buildType" ]; then
read -p "构建生产包,取值为:production(生产) 或 test(测试):" buildType
fi
if [ -z "$appType" ]; then
read -p "请选择构建类型,取值为 apk(Android)、ipa(IOS)、all(apk 和 ipa):" appType
fi
if [ -z "$isUpload" ]; then
read -p "是否将打包生成的应用程序上传,取值为:1(上传) 或 0(不上传):" isUpload
fi
}
# 打印输出的参数类型
function printParams() {
# 环境类型
if [ "$buildType" = "production" ]; then
targetUploadPlatform="App Store"
echo "环境类型:生产"
else
targetUploadPlatform="蒲公英"
echo "环境类型:测试"
fi
# 产物类型
if [ "$appType" = "all" ]; then
targetBuildAppType="ipa、apk"
echo "构建产物:Ipa、Apk"
elif [ "$appType" = "apk" ]; then
targetBuildAppType='apk'
echo "构建产物:Apk"
else
targetBuildAppType='ipa'
echo "构建产物:Ipa"
fi
# 是否上传 App Store 或 蒲公英
if [ "$isUpload" = "1" ]; then
echo "是否上传:是,构建成的 ${targetBuildAppType} 将会上传至 ${targetUploadPlatform}"
else
echo "是否上传:否"
fi
}
# 删除 build 文件夹
function removeBuildFolder() {
if [ -d "build" ]; then
rm -r build
throwExecuteStatus $? $REMOVE_BUILD_FOLDER
fi
}
# 拉取代码
function getLatestCode() {
git pull
throwExecuteStatus $? $PULL_NEW_CODE
}
# 安装 Flutter 依赖
function installFlutterRely() {
flutter pub get
throwExecuteStatus $? $FLUTTER_INSTALL
}
# 安装 IOS 依赖
function installIosRely() {
cd ios
pod install
throwExecuteStatus $? $POD_INSTALL
cd ..
}
# 上传 IPA 到 App Store
function uploadIpaToAppStore() {
cd ios/fastlane
fastlane UploadIpaToAppStore
echo "IPA 文件已成功上传至 App Store,请及时前往 https://appstoreconnect.apple.com/apps/***/testflight/ios 进行确认"
throwExecuteStatus $? $UPLOAD_APP_TO_APP_STORE
}
# 上传 IPA 到蒲公英
function uploadIpaToPgyer() {
cd ios/fastlane
fastlane UploadIpaToPgyer
echo "IPA 文件已成功上传至蒲公英,请及时前往 https://www.pgyer.com/manager/version/index/*** 进行确认"
echo "输入 123456 提取文件"
throwExecuteStatus $? $UPLOAD_APP_TO_PGY
}
# TODO 上传 APK
function uploadApk() {
echo "TODO upload Apk"
}
# 构建可以上传到 App Store 的 IPA 文件并自动上传
function buildIpaUploadAppStore() {
# 构建 Runner.app 文件
flutter build ios --release --dart-define=ENVIROMENT=production
throwExecuteStatus $? $BUILD_RUNNER_APP
# 生成 Runner.xcarchive 文件
xcodebuild -workspace $PWD/ios/Runner.xcworkspace -scheme Runner -sdk iphoneos -configuration Release archive -archivePath $PWD/build/Runner.xcarchive
throwExecuteStatus $? $BUILD_RUNNER_XCARCHIVE
# 生成 ipa 文件
xcodebuild -exportArchive -exportOptionsPlist $PWD/buildConfig/ios/ExportOptionsProduction.plist -archivePath $PWD/build/Runner.xcarchive -exportPath $PWD/build/production/ipa -allowProvisioningUpdates
throwExecuteStatus $? $BUILD_IPA
if [ "$isUpload" = "1" ]; then
wrapperFunction "上传 Ipa 文件到 AppStore" uploadIpaToAppStore
fi
}
# 构建可以上传到蒲公英的 IPA 文件并自动上传
function buildIpaUploadPgyer() {
# 构建 Runner.app 文件
flutter build ios --release --dart-define=ENVIROMENT=test
throwExecuteStatus $? $BUILD_RUNNER_APP
# 生成 Runner.xcarchive 文件
xcodebuild -workspace $PWD/ios/Runner.xcworkspace -scheme Runner -sdk iphoneos -configuration Release archive -archivePath $PWD/build/Runner.xcarchive
throwExecuteStatus $? $BUILD_RUNNER_XCARCHIVE
# 生成 ipa 文件
xcodebuild -exportArchive -exportOptionsPlist $PWD/buildConfig/ios/ExportOptionsAdhoc.plist -archivePath $PWD/build/Runner.xcarchive -exportPath $PWD/build/test/ipa -allowProvisioningUpdates
throwExecuteStatus $? $BUILD_IPA
if [ "$isUpload" = "1" ]; then
wrapperFunction "上传 Ipa 文件到蒲公英" uploadIpaToPgyer
fi
}
# 构建 IOS 应用程序
function buildIosApp() {
wrapperFunction "安装 IOS 依赖" installIosRely
echo "当前路径:$PWD"
if [ "$buildType" = "production" ]; then
wrapperFunction "构建 IPA 文件" buildIpaUploadAppStore
else
wrapperFunction "构建 IPA 文件" buildIpaUploadPgyer
fi
}
# 构建 APK 应用程序
function buildApkApp() {
if [ "$buildType" = "production" ]; then
output=$(flutter build apk --release --dart-define=ENVIROMENT=production | grep Built)
else
output=$(flutter build apk --release --dart-define=ENVIROMENT=test | grep Built)
fi
apkPath=$(echo "$output" | sed -n 's/.*Built \(.*\.apk\) .*/\1/p')
echo "APK存储路径:$apkPath"
throwExecuteStatus $? $BUILD_APK
}
# 开始构建
function startBuildTask() {
# 检查参数是否配置了比较的参数
judgeBuildTypeIsNull
wrapperFunction "构建参数" printParams
# 删除 build 文件夹
wrapperFunction "删除 build 文件夹" removeBuildFolder
# 拉取代码
# TODO 这个只有在上Jenkins调试前用于拉取最新代码,在 Jenkins 中可以通过 SCM 来执行
# wrapperFunction "拉取代码" getLatestCode
wait
# 安装依赖
wrapperFunction "安装 Flutter 依赖" installFlutterRely
wait
# 构建
if [ "$appType" = "all" ]; then
wrapperFunction "构建 APK" buildApkApp
wait
buildIosApp
elif [ "$appType" = "apk" ]; then
wrapperFunction "构建 APK" buildApkApp
else
buildIosApp
fi
wait
echo "build.sh脚本执行成功!"
}
startBuildTask
执行成果
Jenkins 安装 & 编写 Jenkins 脚本
为了协同操作,我们通常会将代码上传到 Gitlab,然后通过一系列的 hooks 来触发我们的 Jenkins 任务,在这个章节中,我将基于 macOS 操作系统搭建整个 Jenkins 环境,并通过编写 Jenkinsfile 文件,来执行我们的构建任务。
为什么选择 macOS 来搭建 Jenkins 呢?主要是因为经过我调研,目前暂无相关云上产品支持构建 IPA 文件,虽然有第三方的,但是在我看来都不太安全或者需要额外的收费,故我的方式是:用一台 mac 专门来构建,同时支持协同操作。下面,就详细介绍一下怎么开始与如何编写 Jenkinsfile 文件。
在开始之前,我先简单介绍一些基础概念:
Groovy 脚本:是一种基于Java虚拟机(JVM)的动态编程语言,它被广泛用于编写脚本、自动化任务和扩展应用程序的能力。
Pipeline 脚本:使用 Groovy 代码来执行各种任务和操作。
Jenkinsfile:用于定义 Pipeline 的文件,它采用了一种特定的语法格式。Jenkinsfile通常与代码存储在同一个代码库中,并与版本控制系统一起管理。
安装 Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 如果上述这个指令更新缓慢的话,可用下面这个指令
/usr/bin/ruby -e "$(curl -fsSL https://cdn.jsdelivr.net/gh/ineo6/homebrew-install/install)"
该脚本用了中科大镜像加速访问,仅修改仓库地址部分,不会产生安全隐患。
Jenkins
# 安装
brew install jenkins-lts
# 启动
brew services start jenkins-lts
# 重启
brew services restart jenkins-lts
# 停止
brew services stop jenkins-lts
# 查看管理员密码,登录时用到
sudo cat /Users/Shared/Jenkins/Home/secrets/initialAdminPassword
访问:http://locathost:8080
至此,我们就完成了 Jenkins 的安装,接下来就创建 Jenkins 工程
1. 创建流水线构建任务
2. 常规配置
解读:
Abort previous builds
在新的构建任务开始之前,Jenkins会自动中止先前正在执行的构建。这样可以确保只有一个构建任务在同一时间运行,避免并发构建
丢弃旧的构建
用于限制保存的构建历史数量,从而控制磁盘空间的使用,这里设置 5,则只保留 5 条构建历史记录
参数化构建过程
Git 参数,需要在 Jenkins 中安装 "Git Parameter" 插件,用于展示当前项目下的 Git 分支以供人工构建时选择
选项参数,该参数为自定义参数,这里配置的 BUILD_TYPE、APP_TYPE、IS_UPLOAD 三个参数是用于执行 Jenkins 任务时将选择的参数值传递到 build.sh 构建脚本中
当 master 重启后,不允许恢复流水线
在 Jenkins 的 master 节点(Jenkins 服务器)重启后,不希望自动恢复之前正在执行的流水线
3. 构建触发器
Build when a change is pushed to GitLab
实现自动构建、持续集成
4. 流水线
解读:特别是当你希望将Pipeline脚本与源代码存储在一起,并利用SCM的版本控制和协作功能来管理和更新Pipeline脚本时。这使得Pipeline脚本的维护和版本控制更加方便,并与应用程序代码的版本管理保持一致
5. 全局配置
"Git installations"(Git安装)用于配置Jenkins使用的Git工具
"Name":指定Git工具的名称,例如"Git"
"Path to Git executable":指定Git可执行文件的路径。这应该是在Jenkins服务器上安装的Git工具的路径。这里 配置了 git,则Jenkins将使用系统的环境变量来确定Git的路径。它会搜索系统的环境变量(如PATH变量)以找到Git可执行文件的位置。这样,Jenkins就可以使用在环境变量中配置的Git工具。
pipeline {
agent any
environment {
LANG = 'en_US.UTF-8'
LANGUAGE = 'en_US.UTF-8'
LC_ALL = 'en_US.UTF-8'
PUB_HOSTED_URL = 'https://pub.flutter-io.cn/'
FLUTTER_STORAGE_BASE_URL = 'https://storage.flutter-io.cn/'
}
parameters {
gitParameter(
name: 'Branch',
type: 'PT_BRANCH',
branchFilter: 'origin/(.*)',
defaultValue: 'master',
selectedValue: 'DEFAULT',
sortMode: 'DESCENDING_SMART',
description: '选择你的分支,默认master.'
)
choice(choices: ['test', 'production'], description: '构建类型,值为 test 和 production', name: 'BUILD_TYPE')
choice(choices: ['apk', 'ipa', 'all'], description: 'APP的类型,值为 ipa、apk 和 all', name: 'APP_TYPE')
booleanParam(name: 'IS_UPLOAD', defaultValue: false, description: '是否上传,值为 1 表示上传,0 表示不上传')
}
stages {
stage('比较环境监测') {
steps {
// 当前运行时的用户
sh 'whoami'
// flutter 版本
sh 'flutter --version'
// java SDK 版本
sh 'java --version'
// 检查是否安装 ruby
sh 'ruby --version'
// pod 版本
sh 'pod --version'
// xcode 版本
sh 'xcodebuild -version'
}
}
stage('拉取代码') {
steps {
checkout scm
}
}
stage('构建APP') {
steps {
script {
try {
def result = sh(
returnStatus: true,
script: "/bin/bash ./build.sh --buildType=\"${params.BUILD_TYPE}\" --appType=\"${params.APP_TYPE}\" --isUpload=\"${params.IS_UPLOAD ? 1 : 0}\""
)
if (result != 0) {
error("脚本执行失败")
} else {
echo "构建成功"
}
} catch (Exception e) {
currentBuild.result = 'FAILURE'
error("❌ 脚本执行失败,出错步骤:${e.getMessage()}")
}
}
}
}
}
}
8. 成果展示
参考链接
Getting started with fastlane for iOS:
https://docs.fastlane.tools/getting-started/ios/setup/
Installing Ruby
https://www.ruby-lang.org/en/documentation/installation/#homebrew
Update Ruby
https://www.ruby-lang.org/en/downloads/
使用 Fastlane 上传 App 到蒲公英
https://www.pgyer.com/doc/view/fastlane
Fastlane for flutter
https://docs.flutter.dev/deployment/cd#fastlane
macOS Installers for Jenkins LTS
https://www.jenkins.io/download/lts/macos/
Installing Jenkins on macOS
https://www.macminivault.com/installing-jenkins-on-macos/