Highlight
Xcode Cloud 通过 Webhook 和 App Store Connect API 与团队现有工具集成,配合 Swift Package Manager 依赖管理、自定义脚本和权限控制,让 CI/CD 流程融入团队已有的开发工作流。
核心内容
你的团队正在使用 Xcode Cloud,但构建信息和 issue tracker 是割裂的。开发者需要手动查看构建状态,构建产物散落在各处,代码风格也缺乏统一约束。
(01:34)Session 用 Food Truck 项目演示了如何把 Xcode Cloud 嵌入团队的完整开发流程。
与 Issue Tracker 集成
(01:46)团队使用 issue tracker 管理任务,但构建信息没有关联到具体的 ticket。解决方案是搭建一个 Swift on Server 服务,接收 Xcode Cloud 的 Webhook,自动在对应的 issue 下添加构建结果评论。
流程如下:
- Xcode Cloud 构建完成后发送 Webhook 到团队服务器
- 服务器解析 commit message,提取 issue 编号
- 调用 Xcode Cloud API 获取构建详情和问题列表
- 构造评论内容,调用 issue tracker API 发布
(03:01)Xcode Cloud API 属于 App Store Connect API 的一部分。如果已经有 App Store Connect API 的认证 token,可以直接访问 Xcode Cloud 数据。
关键端点:
CiBuildRuns:创建、取消、查询构建CiBuildActions:获取构建 action 详情CiArtifacts:获取构建产物下载链接
(04:00)从 App Store Connect 文档下载 OpenAPI 规范,用 OpenAPI Generator 自动生成 Swift 客户端:
# 下载 OpenAPI 规范后
openapi-generator generate -i app_store_connect_api.json \
-g swift -o xcodecloud-client
生成的客户端是一个 Swift Package,可以直接作为依赖引入 webhook 服务器项目。
(05:24)在 App Store Connect 中配置 Webhook:进入产品设置 > Webhooks,填入服务器 URL。每次构建完成后,Xcode Cloud 会向这个地址发送 POST 请求。
Webhook payload 包含构建 ID、action 列表、执行状态等信息。用 Vapor 框架处理:
import Vapor
struct WebhookPayload: Content {
let buildRun: CiBuildRun
let actions: [CiBuildAction]
}
func routes(_ app: Application) throws {
app.post("webhook") { req async throws in
let payload = try req.content.decode(WebhookPayload.self)
// 解析 commit message 中的 issue 编号
// 调用 API 获取构建问题
// 发布评论到 issue tracker
return HTTPStatus.ok
}
}
依赖管理
(08:25)Xcode Cloud 原生支持 Swift Package Manager。公开的 package 无需额外配置,私有 package 需要配置访问权限。
第三方依赖管理器如 CocoaPods 和 Carthage 也可以通过自定义脚本在 Xcode Cloud 中使用。具体配置方法参考 Xcode 文档。
构建完成后,在 Xcode 的 Report Navigator > Cloud 标签页查看构建日志。Xcode Cloud 会自动解析并下载 Swift Package 依赖。
代码质量检查
(09:50)用 SwiftLint 进行静态代码分析,通过自定义脚本集成到 Xcode Cloud:
#!/bin/bash
# ci_scripts/post_clone.sh
# 安装 SwiftLint
brew install swiftlint
# 在代码仓库目录运行检查
swiftlint lint --path "$CI_WORKSPACE"
关键点:
post_clone脚本在代码克隆后执行CI_WORKSPACE环境变量指向代码仓库目录- 脚本执行位置是
ci_scripts目录,需要显式指定工作目录 - SwiftLint 会输出违规数量和严重程度
(11:27)首次集成时可能发现大量违规。可以临时停用工作流,团队讨论确定代码规范后再修复并启用。
工作流权限控制
(12:13)团队规模扩大后,需要防止成员意外修改工作流配置。Xcode Cloud 支持限制工作流编辑权限:
- 右键工作流 > Restrict Editing
- 只有管理员、账户持有者和应用管理员可以设置和解除限制
- 限制后的工作流显示钥匙图标
- 锁定的工作流显示锁图标,普通成员无法编辑
多 Start Condition
(13:22)一个工作流可以配置多个 Start Condition,减少需要维护的工作流数量。
例如,同一个测试和归档工作流可以在以下情况触发:
- main 分支有变更
- release 分支有变更
- main 分支的定时构建
不需要创建三个独立的工作流,在一个工作流中添加三个 Start Condition 即可。
详细内容
Webhook 服务实现
(05:55)完整的 Webhook 处理流程:
import Vapor
// 定义 Webhook payload 结构
struct XcodeCloudWebhook: Content {
let buildRun: CiBuildRun
let actions: [CiBuildActionResponse]
}
// 扩展 API 客户端获取构建问题
extension CiBuildActionsAPI {
func getIssues(for actionId: String) async throws -> [CiIssue] {
let response = try await ciBuildActionsActionsIdIssuesGet(
id: actionId
)
return response.data
}
}
// Vapor 路由处理
func routes(_ app: Application) throws {
app.post("webhook") { req async throws -> HTTPStatus in
let payload = try req.content.decode(XcodeCloudWebhook.self)
// 只处理已完成的构建
guard payload.buildRun.attributes?.status == .complete else {
return .ok
}
// 从 commit message 提取 issue 编号
let commitMessage = payload.buildRun.attributes?.commitMessage ?? ""
guard let issueId = extractIssueId(from: commitMessage) else {
return .ok
}
// 构造评论内容
var comment = "Build #\(payload.buildRun.attributes?.number ?? 0)\n"
comment += "Commit: \(payload.buildRun.attributes?.commitHash ?? "")\n"
comment += "Author: \(payload.buildRun.attributes?.commitAuthor ?? "")\n"
// 获取每个 action 的问题
for action in payload.actions {
let issues = try await req.application.ciAPI.getIssues(for: action.id)
for issue in issues {
comment += "- [\(issue.attributes?.issueType ?? "")] \(issue.attributes?.message ?? "")\n"
}
}
// 发布到 issue tracker
try await postComment(to: issueId, content: comment, on: req)
return .ok
}
}
关键点:
- Webhook payload 包含构建和 action 的完整信息
- 通过 API 扩展获取每个 action 的问题列表
- 评论包含构建号、commit hash、作者和问题详情
- 服务部署在 App Store Connect 中配置的 Webhook URL 上
App Store Connect API 认证
(03:08)访问 Xcode Cloud API 使用与 App Store Connect API 相同的认证方式:
- 在 App Store Connect > Users and Access > Keys 中生成 API Key
- 下载私钥文件(.p8)
- 使用 JWT 生成认证 token
import JWT
struct AppStoreConnectToken {
let keyId: String
let issuerId: String
let privateKey: String
func generate() throws -> String {
let signer = JWTSigner.es256(privateKey: privateKey)
let claims = AppStoreConnectClaims(
iss: issuerId,
iat: Date(),
exp: Date().addingTimeInterval(1200)
)
let jwt = JWT(claims: claims)
return try signer.sign(jwt, kid: JWKIdentifier(string: keyId))
}
}
关键点:
- 同一个 token 同时用于 App Store Connect API 和 Xcode Cloud API
- Token 有效期最长 20 分钟
- 私钥文件只能下载一次,需要妥善保存
自定义脚本时机
Xcode Cloud 在每个 action 的多个阶段执行自定义脚本:
| 脚本名称 | 执行时机 |
|---|---|
pre_clone | 克隆代码仓库之前 |
post_clone | 克隆代码仓库之后 |
pre_build | 构建开始之前 |
post_build | 构建完成之后 |
pre_test | 测试开始之前 |
post_test | 测试完成之后 |
pre_archive | 归档开始之前 |
post_archive | 归档完成之后 |
pre_xcodebuild | xcodebuild 执行之前 |
post_xcodebuild | xcodebuild 执行之后 |
脚本放在项目的 ci_scripts 目录中,需要添加可执行权限。
核心启发
-
做什么:搭建 Xcode Cloud Webhook 服务,自动关联构建和 issue tracker
-
为什么值得做:开发者无需手动查看构建状态,issue 页面直接展示构建结果和问题列表
-
怎么开始:用 Vapor 搭建 Swift on Server 服务,配置 Webhook URL,解析 commit message 中的 issue 编号
-
做什么:用 OpenAPI Generator 自动生成 App Store Connect API 客户端
-
为什么值得做:避免手动维护 API 请求代码,类型安全,API 更新后重新生成即可
-
怎么开始:从文档下载 OpenAPI 规范,运行 generator 生成 Swift Package,作为依赖引入项目
-
做什么:在 Xcode Cloud 中集成 SwiftLint 进行代码风格检查
-
为什么值得做:保持团队代码风格一致,在 CI 阶段发现问题而不是 code review 时
-
怎么开始:创建
ci_scripts/post_clone.sh安装并运行 SwiftLint,根据团队规范配置.swiftlint.yml -
做什么:为团队建立分层的工作流权限管理
-
为什么值得做:防止成员意外修改关键工作流配置,Release 工作流只能由管理员编辑
-
怎么开始:对关键工作流右键选择 Restrict Editing,普通成员仍可手动触发构建
关联 Session
- Get the most out of Xcode Cloud — 优化 Xcode Cloud 用量和工作流
- Customize your advanced Xcode Cloud workflows — WWDC21 高级工作流定制
- Meet Xcode Cloud — WWDC21 Xcode Cloud 入门
评论
GitHub Issues · utterances