Azure DevOps Monorepo iOS构建流水线耗时超50分钟优化求助
针对Monorepo+React技术栈项目在Azure DevOps上iOS构建总耗时57分钟、Xcode单步骤占39-43分钟的问题,按投入产出比从高到低排列可直接落地的优化项,全部配置完成后总耗时可压缩至15-20分钟区间。
一、核心瓶颈优化:Xcode构建步骤(预计压缩25-30分钟)
Xcode构建占总耗时70%左右,是优化优先级最高的环节,所有调整直接修改现有Xcode@5任务及前置配置即可:
- 关闭默认全量清理:在Xcode任务参数中添加
clean: false,日常QA构建不需要每次清空编译目录,仅正式发版构建时临时开启全量清理即可,单这一项可减少40%左右的重复编译工作量。 - 裁剪编译架构:QA环境仅需要真机arm64架构包,在Xcode任务
args参数中添加ARCHS=arm64 ONLY_ACTIVE_ARCH=YES,跳过不需要的x86_64模拟器架构编译,可压缩5-8分钟耗时。 - 精简调试符号生成:QA环境不需要生成dSYM符号表,在
args参数中添加DEBUG_INFORMATION_FORMAT=dwarf,可压缩2-3分钟的符号生成耗时,正式发布构建再改回生成dSYM即可。 - 配置全链路编译缓存:
- 前置安装ccache编译缓存工具,配置10G缓存空间,同时开启CPU全核心编译:
- script: | brew install ccache export CCACHE_DIR=$(Pipeline.Workspace)/.ccache ccache -M 10G defaults write com.apple.dt.Xcode IDEBuildOperationMaxNumberOfConcurrentCompileTasks $(sysctl -n hw.ncpu) displayName: 'Configure build acceleration'- 将Xcode DerivedData编译目录、ccache目录纳入Azure DevOps缓存,缓存命中时不需要重复编译未改动的代码:
- task: Cache@2 inputs: key: 'xcode-build | "$(Agent.OS)" | packages/res-feber-mobile/ios/Podfile.lock | yarn.lock' restoreKeys: | xcode-build | "$(Agent.OS)" path: | $(Pipeline.Workspace)/DerivedData $(Pipeline.Workspace)/.ccache- 在Xcode任务
args参数中指定DerivedData路径为缓存目录:-derivedDataPath $(Pipeline.Workspace)/DerivedData
- 替换M1架构构建机:如果Azure DevOps组织支持arm64构建资源,将池配置的
vmImage从macos-latest(Intel芯片)替换为macos-13-arm64,M1芯片的Xcode编译速度比同配置Intel机型快35%左右,可额外压缩8-10分钟耗时。
调整后Xcode任务完整配置参考:
- task: Xcode@5 displayName: 'Build IPA' inputs: actions: 'build' configuration: 'Qa' sdk: 'iphoneos' scheme: 'resfebermobileQa' xcWorkspacePath: '$(System.DefaultWorkingDirectory)/packages/res-feber-mobile/ios/resfebermobile.xcworkspace' packageApp: true exportPath: 'output/$(SDK)/$(Configuration)' exportMethod: 'enterprise' signingOption: 'manual' signingIdentity: '$(APPLE_CERTIFICATE_SIGNING_IDENTITY)' provisioningProfileUuid: '$(APPLE_PROV_PROFILE_UUID)' clean: false args: 'ARCHS=arm64 ONLY_ACTIVE_ARCH=YES DEBUG_INFORMATION_FORMAT=dwarf -derivedDataPath $(Pipeline.Workspace)/DerivedData'
注意:所有硬编码的绝对路径/Users/runner/work/1/s/统一替换为系统变量$(System.DefaultWorkingDirectory),避免Agent工作目录变更导致的路径错误重试。
二、依赖安装步骤优化(预计压缩6-8分钟)
现有yarn install、CocoaPods安装步骤每次全量拉取依赖,通过缓存可将单步耗时从分钟级压缩到秒级:
- Yarn依赖缓存:在yarn install步骤前添加缓存任务,依赖锁文件不变时直接读取本地缓存,不需要重新下载:
- task: Cache@2 displayName: 'Cache Yarn dependencies' inputs: key: 'yarn | "$(Agent.OS)" | yarn.lock | **/package.json' restoreKeys: | yarn | "$(Agent.OS)" | yarn.lock path: $(Yarn.CacheFolder)
同时修改yarn install命令为yarn install --frozen-lockfile --ignore-scripts,安装完成后单独执行移动端子包需要的postinstall脚本,跳过Monorepo下其他非移动端包的冗余脚本执行。
- CocoaPods依赖缓存:在pod install步骤前添加缓存任务,Podfile.lock不变时直接复用本地Pods目录:
- task: Cache@2 displayName: 'Cache CocoaPods dependencies' inputs: key: 'cocoapods | "$(Agent.OS)" | packages/res-feber-mobile/ios/Podfile.lock' path: '$(System.DefaultWorkingDirectory)/packages/res-feber-mobile/ios/Pods'
三、流水线逻辑冗余裁剪(预计压缩3-4分钟)
现有配置存在多处无意义的重复操作,直接删除或调整即可:
- 优化代码拉取配置:将checkout步骤的
clean: true改为clean: false,添加fetchDepth: 1参数,仅拉取最新一次提交的代码,不拉取全量Git历史,checkout步骤可从2-3分钟压缩到30秒以内:
- checkout: self persistCredentials: true clean: false fetchDepth: 1
- 精简版本号bump脚本:现有脚本中
git checkout $(Build.SourceBranchName)、git add *为冗余操作——构建Agent拉取代码后默认就在触发构建的分支上,版本号修改仅作用于本地构建环境,不需要执行Git暂存操作,精简后脚本如下:
- script: | yarn config set version-sign-git-tag false yarn config set version-git-tag false yarn config set version-commit-hooks false oldVer=$(jq -r ".version" package.json) yarn version --patch newVer=$(jq -r ".version" package.json) echo "##vso[task.setvariable variable=OLD_VERSION]$oldVer" echo "##vso[task.setvariable variable=NEW_VERSION]$newVer" displayName: 'Bump version and set variables'
- 优化Node安装配置:macOS构建机默认预装了Node 18.x版本,将NodeTool任务的
versionSpec从固定版本18.0.0改为18.x,优先使用本地预装版本,不需要每次重新下载Node安装包,可压缩1-2分钟的工具安装耗时。 - 关闭证书/描述文件后置删除:托管机构建完成后会自动销毁,不需要在安装证书时配置
deleteCert: true、removeProfile: true,跳过删除操作可节省几十秒耗时。
四、进阶并行优化(额外压缩2-3分钟)
将无依赖关系的步骤配置为并行执行:Node安装、ccache配置、缓存恢复、版本号修改、证书与描述文件安装这几个步骤没有前后依赖,可以拆分为同一Job下的并行步骤,不需要串行等待,进一步压缩流水线空等耗时。
注意:缓存任务第一次执行时因为没有存量缓存,耗时和原有配置基本一致,从第二次构建开始缓存命中后即可达到预期的耗时压缩效果。
内容的提问来源于stack exchange,提问作者Resfeber Eudaimonia

