WWDC Quick Look 💓 By SwiftGGTeam
Deep dive into Xcode Cloud for teams

Deep dive into Xcode Cloud for teams

观看原视频

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 下添加构建结果评论。

流程如下:

  1. Xcode Cloud 构建完成后发送 Webhook 到团队服务器
  2. 服务器解析 commit message,提取 issue 编号
  3. 调用 Xcode Cloud API 获取构建详情和问题列表
  4. 构造评论内容,调用 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 相同的认证方式:

  1. 在 App Store Connect > Users and Access > Keys 中生成 API Key
  2. 下载私钥文件(.p8)
  3. 使用 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_xcodebuildxcodebuild 执行之前
post_xcodebuildxcodebuild 执行之后

脚本放在项目的 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

评论

GitHub Issues · utterances