[iOS]尽可能使用Bitrise缓存来加速CI / CD


介绍

这篇文章是Cyber??Agent Developers Advent Calendar 2019的第4天文章!

今天,今年将加入负责Tapple iOS团队的19名新毕业生@Kazuma_Nagano。

这次,通过将Tapple的iOS应用程序的CI工作流更改为可以充分利用缓存的设置,
我想写一个故事,该故事成功地将Bitrise工作流程加快了40%至50%。

加速前后的工作流程

该团队目前正在对Bitrise进行自动化测试。
工作流程大致如下。

  • 储存库克隆
  • 自制程序包安装(Xcodegen,Linter等)
  • 安装Gem程序包(xcov,CocoaPods,危险,快速通道等)
  • 安装Cocoapods
  • 运行测试

以下是加速之前工作流的实际摘要(最后显示的执行时间)。

在加速之前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
+------------------------------------------------------------------------------+
|                               bitrise summary                                |
+---+---------------------------------------------------------------+----------+
|   | title                                                         | time (s) |
+---+---------------------------------------------------------------+----------+
| ? | activate-ssh-key                                              | 11.26 sec|
+---+---------------------------------------------------------------+----------+
| ? | git-clone                                                     | 1.5 min  |
+---+---------------------------------------------------------------+----------+
| ? | Set Env Path                                                  | 5.94 sec |
+---+---------------------------------------------------------------+----------+
| ? | cache-pull                                                    | 4.0 min  |
+---+---------------------------------------------------------------+----------+
| ? | Bundle Install                                                | 50.90 sec|
+---+---------------------------------------------------------------+----------+
| ? | Brew Install                                                  | 1.1 min  |
+---+---------------------------------------------------------------+----------+
| ? | XcodeGen                                                      | 15.0  sec|
+---+---------------------------------------------------------------+----------+
| ? | CocoaPods Install                                             | 4.1 min  |
+---+---------------------------------------------------------------+----------+
| ? | cache-push                                                    | 25.18 sec|
+---+---------------------------------------------------------------+----------+
| ? | github-status                                                 | 5.69 sec |
+---+---------------------------------------------------------------+----------+
| ? | Fastlane Test                                                 | 19.0 min |
+---+---------------------------------------------------------------+----------+
| ? | cache-push                                                    | 15.52 sec|
+---+---------------------------------------------------------------+----------+
| ? | github-status                                                 | 5.19 sec |
+---+---------------------------------------------------------------+----------+
| Total runtime: 31.9 min                                                      |
+------------------------------------------------------------------------------+

特别要检查Homebrew,Gem,Cocoapods(应用程序构建),
通过更多利用缓存,我们能够将总工作流程时间缩短40%到50%,如下所示。

加速后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
+------------------------------------------------------------------------------+
|                               bitrise summary                                |
+---+---------------------------------------------------------------+----------+
|   | title                                                         | time (s) |
+---+---------------------------------------------------------------+----------+
| ? | activate-ssh-key                                              | 10.15 sec|
+---+---------------------------------------------------------------+----------+
| ? | Git Config                                                    | 3.17 sec |
+---+---------------------------------------------------------------+----------+
| ? | git-clone                                                     | 29.22 sec|
+---+---------------------------------------------------------------+----------+
| ? | Set Env Path                                                  | 4.44 sec |
+---+---------------------------------------------------------------+----------+
| ? | cache-pull                                                    | 31.66 sec|
+---+---------------------------------------------------------------+----------+
| ? | Brew install                                                  | 8.13 sec |
+---+---------------------------------------------------------------+----------+
| ? | XcodeGen                                                      | 11.53 sec|
+---+---------------------------------------------------------------+----------+
| ? | rbenv Install                                                 | 4.48 sec |
+---+---------------------------------------------------------------+----------+
| ? | Bundle Install                                                | 5.07 sec |
+---+---------------------------------------------------------------+----------+
| ? | CocoaPods Install                                             | 38.11 sec|
+---+---------------------------------------------------------------+----------+
| ? | cache-push                                                    | 9.71 sec |
+---+---------------------------------------------------------------+----------+
| ? | github-status                                                 | 6.70 sec |            
+---+---------------------------------------------------------------+----------+
| ? | Fastlane Test                                                 | 13.6 min |
+---+---------------------------------------------------------------+----------+
| ? | github-status                                                 | 7.55 sec |
+---+---------------------------------------------------------------+----------+
| Total runtime: 16.4 min                                                      |
+------------------------------------------------------------------------------+

*根据一天中的时间和环境的不同,位上升摘要的总运行时间可能会增加或减少。
在同一时区中选择并比较了文章中的内容,但其他因素也可能会产生影响

补品376??9113

在上面加速之前的工作流程中,
1. ./Pod(如果Podfile.lock中有更改)
2. /Users/vagrant/Library/Caches/Homebrew
3. ~/Library/Developer/Xcode/DerivedData
被缓存了。
在加速后的工作流程中,一些无效,已被删除。

关于Bitrise中的缓存功能

官方的Bitrise文档描述了缓存的工作方式。
一些摘录是

  • 缓存对象将缓存所有缓存的目录和依赖项,并安全地存储在Amazon S3中
  • 如果指定了a/path/to/cache,则还将缓存/path/to/cache/.ignore-me

  • 缓存按分支进行管理
  • PR中没有缓存推送
  • 如果该分支上没有完成新的构建,它将过期并在7天后将其删除

如何使用

用法可以通过添加两个步骤来使用。

Bitrise.io高速缓存:拉动步骤以下载先前的高速缓存(如果有)。
Bitrise.io高速缓存:推送步骤以检查高速缓存的状态并根据需要上载。

通过在

bitrise的工作流程中编写以下内容,可以缓存在所选目录下。

bitrise.yml

1
2
3
4
5
6
7
8
9
10
11
12
workflows:
    workflow-a:
        steps:
        # 他のワークフロー
        - cache-pull: {} #pullはこれだけ
        # 他のワークフロー
        - cache-push:
        inputs:
        - cache_paths: |-
            $BITRISE_CACHE_DIR
       MyProject/Cache  #キャッシュしたいパス
            ./Pods -> ./Podfile.lock #Podfile.lockに変更があれば./Podsをキャッシュ

您可以像

Podfike.lock示例中那样指定一个文件来监视更改,但这并不意味着您未查看diff,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Cleaning paths
Done in 1.32941661s
Checking previous cache status
Previous cache info found at: /tmp/cache-info.json
Done in 171.741223ms
Checking for file changes
0 files needs to be removed
0 files has changed
0 files added
No files found in 37.785423ms
Total time: 1.539638885s
|                                                                              |
+---+---------------------------------------------------------------+----------+
| ? | cache-push                                                    | 8.99 sec |
+---+---------------------------------------------------------------+----------+

如果检查

日志,则可以看到正在检查删除,更改和添加。
因此,您可以通过指定一个文件来监视更改来简化此流程。

有效的缓存设置

我将介绍

之后有效的缓存方法。

自制缓存

官方文档-"缓存Homebrew安装程序"描述了以下方法。

Brew安装步骤支持缓存:如果启用了此选项,则将从brew --cache位置缓存所有下载的brew安装程序,缓存路径为?/ Library / Caches / Homebrew /。

要启用Brew安装程序的缓存:

  • 转到要缓存Brew安装的工作流程,然后选择Brew安装步骤。
  • 将"缓存"选项设置为"是"。
  • 与往常一样,单击"保存"。
  • 官员指定Homebrew的--cache选项,这不可避免地导致安装每次运行。

    这是二进制文件的直接链接,您可以避免重新安装已安装的公式。
    (~/Library/Caches/Homebrew不需要被缓存,因此不要使用官方步骤)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    - script:
            inputs:
            - content: |-
                # キャッシュするディレクトリを環境変数に追加
                envman add --key BREW_XCODEGEN --value "$(brew --cellar)/xcodegen" #パッケージ名を指定
                envman add --key BREW_OPT_XCODEGEN --value "/usr/local/opt/xcodegen"  #パッケージ名を指定
            title: Set Env Path
    - script:
            inputs:
            - content: |-
                #インストールは以下のコマンドを実行
                brew install xcodegen
                brew link xcodegen
            title: Brew install
    - cache-push:
            inputs:
            - cache_paths: |-
               $BITRISE_CACHE_DIR

               #以下の二つをcache_pathに追加
               $BREW_XCODEGEN
               $BREW_OPT_XCODEGEN

    缓存成功时记录

    1
    2
    3
    4
    5
    + brew install xcodegen
    Warning: xcodegen 2.10.1 is already installed, it's just not linked
    You can use `brew link xcodegen` to link this version.
    + brew link xcodegen
    Linking /usr/local/Cellar/xcodegen/2.10.1... 2 symlinks created

    这使brew install步骤减少了多达80%。

    在加速之前

    1
    2
    3
    +---+---------------------------------------------------------------+----------+
    | ? | Brew Install                                                  | 1.1 min  |
    +---+---------------------------------------------------------------+----------+

    加速后

    1
    2
    3
    +---+---------------------------------------------------------------+----------+
    | ? | Brew install                                                  | 8.13 sec |
    +---+---------------------------------------------------------------+----------+

    另外,使用此方法,brew cleanup将在每次运行一个或多个安装时使用运行时间。
    image.png

    可以通过在Homebrew的Github存储库中检查cleanup.rb并使用~/Library/Caches/Homebrew/.cleaned确定条件,从而将其添加到缓存目标中来避免。

    rubyruby

    rbenv

    官方文档-"缓存Ruby Gems"描述了以下方法。
    スクリーンショット 2019-12-05 0.04.36.png

    在当前环境中,缓存路径指向~/.rbenv/versions/2.6.3。这是rbenv的缓存规范。
    此设置有效,因为未在Bitrise提供的堆栈上安装ruby 2.6.3。

    另外,如果您在存储库中管理.ruby-version,则在推送时应在此处检查。

    ruby

    此外,通过缓存gem安装目录(tapple中的./vendor/bundler),
    您也可以避免在bundle install进行安装。
    由于更改反映在此处的Gemfile.lock中,因此请在推送时将其指定为检查对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    - script:
            title: Bundle Install
            inputs:
            - content: |-
                bundle install --path vendor/bundle
    - cache-push:
            inputs:
            - cache_paths: |-
               #以下の二つをcache_pathに追加
               $GEM_CACHE_PATH -> ./.ruby-version #公式ドキュメント↑の環境変数
               ./vendor -> ./Gemfile.lock #gemのインストール先ディレクトリを指定

    缓存成功时记录

    1
    2
    3
    4
    5
    6
    7
    + bundle install --path vendor/bundle
    Using rake 13.0.1
    Using CFPropertyList 3.0.1
    ...
    Using xcprofiler 0.6.3
    Bundle complete! 9 Gemfile dependencies, 109 gems now installed.
    Bundled gems are installed into `./vendor/bundle`

    这使bundle install减少了大约90%。

    在加速之前

    1
    2
    3
    +---+---------------------------------------------------------------+----------+
    | ? | Bundle Install                                                | 50.90 sec|
    +---+---------------------------------------------------------------+----------+

    加速后

    1
    2
    3
    +---+---------------------------------------------------------------+----------+
    | ? | Bundle Install                                                | 5.07 sec |
    +---+---------------------------------------------------------------+----------+

    可可粉

    官方文档-"缓存Ruby Gems"描述了以下方法。

    开始之前,请确保您的工作流程中具有最新版本的Cocoapods安装步骤。

  • 打开应用的工作流程编辑器。
  • 在Git克隆之后,但在Cocoapods安装步骤之前,插入Cache:Pull步骤。
    重要说明:确保您的Step为1.0.0或更高版本,对于较旧的版本,您必须手动指定缓存路径。
  • 插入缓存:将步骤推到工作流程的最后。
  • 似乎只能通过指定由Bitrise公式准备的Cocoapods Install来完成,但是
    就个人而言,在官方步骤中,
    image.png

    如果您检查日志,请在运行时
    -检查所选的Ruby是否已安装
    -$ gem install bundler --force
    -bundle install
    -pod install

    他正在使用

    进行多项工作,但是上一步的过程涉及其中一部分。
    所以我单独使用bundle exec pod install

    如果安装了正确的捆绑软件,则在该步骤之前安装了rbenv
    您可以通过简单地将Pods文件指定为缓存路径并检查Podfile.lock来缩短它。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    - script:
            inputs:
            - content: |-
                bundle exec pod install
            title: CocoaPods Install
    - cache-push:
            inputs:
            - cache_paths: |-
            ./Pods -> ./Podfile.lock ##キャッシュパスに追加

    在加速之前的流程中,每次都指定了--repo-update选项,但是可以缩小这种情况,或者
    通过始终保持有效的CocoaPods缓存,我们可以节省约85%的费用。

    在加速之前

    1
    2
    3
    +---+---------------------------------------------------------------+----------+
    | ? | CocoaPods Install                                             | 4.1 min  |
    +---+---------------------------------------------------------------+----------+

    加速后

    1
    2
    3
    +---+---------------------------------------------------------------+----------+
    | ? | CocoaPods Install                                             | 38.11 sec|
    +---+---------------------------------------------------------------+----------+

    缓存成功时记录

    1
    2
    3
    4
    5
    6
    7
    8
    9
    + bundle exec pod install
    Using FBSDKCoreKit
    Using FirebaseDynamicLinks
    Using Crashlytics
    Using FirebaseCoreDiagnosticsInterop
    Using FirebaseCore
    Using GoogleDataTransport
    Using GoogleDataTransportCCTSupport
    ???

    提供更快的速度

    在加速之前查看摘要时,Fastlane Test部分最明显。

    在加速之前

    1
    2
    3
    +---+---------------------------------------------------------------+----------+
    | ? | Fastlane Test                                                 | 19.0 min |
    +---+---------------------------------------------------------------+----------+

    此步骤

    • 编译应用
    • 测试
    • 危险
    • 通知Slack

    包含

    有什么方法可以使用缓存来加速此部分吗?

    cocoapods-binary

    不幸的是,Bitrise当前不支持Xcode的构建缓存,但是
    可以缓存使用cocoapods-binary的CocoaPods _Prebuild。

    参考:https://github.com/leavez/cocoapods-binary

    我将省略介绍方法,但是
    ./Pods/_Prebuild(因为它位于Pod下,所以未指定其他缓存路径)
    通过缓存Pod,您不必每次都构建Pod,因此
    我们能够大大减少CI的构建时间。
    它提高了30%,但是可以缩短到5.4m的时间是这里最大的。

    在加速之前

    1
    2
    3
    +---+---------------------------------------------------------------+----------+
    | ? | Fastlane Test                                                 | 19.0 min |
    +---+---------------------------------------------------------------+----------+

    加速后

    1
    2
    3
    +---+---------------------------------------------------------------+----------+
    | ? | Fastlane Test                                                 | 13.6 min |
    +---+---------------------------------------------------------------+----------+

    最后

    这次,我介绍了可以通过充分利用Bitrise缓存来加速的几点。
    但是请记住,存储和使用缓存始终会在某些地方带来不一致的风险。

    查看Bitrise的缓存日志,可以确认在拉入之前堆栈是相同的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    +------------------------------------------------------------------------------+

    | (4) cache-pull                                                               |
    +------------------------------------------------------------------------------+
    | id: cache-pull                                                               |
    | version: 2.1.2                                                               |
    | collection: https://github.com/bitrise-io/bitrise-steplib.git                |
    | toolkit: go                                                                  |
    | time: 2019-12-02T15:03:01Z                                                   |
    +------------------------------------------------------------------------------+
    |                                                                              |
    Config:
    - CacheAPIURL: [REDACTED]
    - DebugMode: false
    - StackID: osx-xcode-11.2.x
    Downloading remote cache archive
    Checking archive and current stacks
    current stack id: osx-xcode-11.2.x
    archive stack id: osx-xcode-11.2.x
    Extracting cache archive
    Done

    但是,提供TTL,例如在更新库时丢弃缓存。
    有必要采取诸如准备随时更新或使高速缓存无效的方法之类的措施。
    通过这种实现,我觉得建立依赖于缓存但又不依赖缓存的关系非常重要。

    参考

    • Bitrise官方文档
    • github.com/onmyway133/blog-如何缓存CocoaPods#196
    • 使用Bitrise的Qiita-Gem和Brew缓存
    • Sansan Builders Box-使用Bitrise的缓存将整体构建时间缩短25%的故事
    • github.com/leavez/cocoapods-binary