Help us understand the problem. What is going on with this article?

バイトル(iOS)のCI/CDを大公開!!

このエントリは ディップ Advent Calendar 2019 の23日目の記事となります。
いろんな記事あがってますのでぜひ他の記事も読んでみてください!

はじめに

私たちディップ株式会社はバイトルを始めとして、様々なサービスを開発しています。

私の担当しているバイトルアプリでは、おおよそ月に1度のペースでリリースを実施しています。
そのリリースを支えているCI/CD基盤を大公開しちゃおうと思います!

CI/CDツール

弊社のモバイルアプリ開発のCI/CDはBitriseを使用しています。
モバイルアプリに特化したツールで、やれることもかなり多く超ハードな使用されています。

ちなみに弊社はBitriseの日本ユーザではかなり古参らしいです。
https://www.wantedly.com/companies/dip/post_articles/140845

Workflow大公開

バイトルでは大きく分けて2つのプロジェクト、3つの親Workflow、8個の子Workflowで形成されています。

親Workflow

名前 用途
Kaihatu-Test 総合テストまでのビルドに使用します。プルリクにも反応します。
Ukeire-Test 受入テストに回すときに使用します。
Release リリース申請を実施するときに使用します。

子Workflow

名前 用途
PullRequest_ReviewerAssign プルリクエストに対してレビュワーを登録します。
_Preparation-Before-Build ビルドの準備をします。
_Build ビルドを実施します。
_AppStore-Deploy App Storeにアプリをアップロードします。
_Post_Process ビルド後の後始末をします。
_Bitrise-Upload アプリをBitriseにアップロードします。
_AppCenter-Deploy App Centerでテスト配信します。
_Slack-Notification Slackにメッセージを通知します。

作業は子で定義して、親は使用した子を呼び出すだけになっています。
弊社はサービスが複数個あるので、サービス間でWorkflowを横展開することを考えて分割しました。

Untitled Diagram.png

PullRequest_ReviewerAssign

  • PullRequest Reviewer Assign(Script)
    • プルリク起動の場合、レビュワーをアサイン

_Preparation-Before-Build

  • activate-ssh-key
    • Bitriseで必須の作業
  • git-clone
    • Bitriseで必須の作業
  • Set Build Version(Script)
    • アプリのビルド番号を採番
  • ruby-script
    • redcarpet使っているのでインストール
  • cocoapods-install
    • CocoaPodsを使っているのでインストール

_Build

  • set-xcode-build-number
    • 採番したビルド番号をPlistにセット
  • ios-auto-provision
    • プロビジョニングプロファイルの設定
    • Autoにすることで面倒なことを全部やってくれる。
  • xcode-archive
    • Archiveする。

_AppStore-Deploy

  • deploy-to-itunesconnect-deliver
    • AppStoreにアプリのバイナリをアップロードする。

_Post_Process

  • Create Release Note(Script)
    • いろんなところに貼っておくリリースノートを作成する。

_Bitrise-Upload

  • deploy-to-bitrise-io
    • BitriseにArchiveの結果をアップロード(ミスっても後からAppStoreにあげられる)

_AppCenter-Deploy

  • nvm
    • AppCenter Cliを使うためにNVMのバージョンをあげておく。
  • Set AppCenter Info(Script)
    • AppCenterの向き先設定する。(Stageが複数ある)
  • appcenter-app-release
    • AppCenterにアプリをアップロードしてテスト配信する。
  • Get Latest AppCenter App Id(Script)
    • 後続の処理に使用するAppCenterにさっきあげたアプリのIDを取得する。
  • appcenter-dsym-upload
    • 難読化ファイルをアップロードする。(そのままじゃクラッシュ読めない)

_Slack-Notification

  • slack
    • Slackにビルド結果を通知

ソース

bitrise.yml
---
format_version: 1.1.0
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
workflows:
  Kaihatu-Test:
    after_run:
    - _PullRequest_Reviewer_Assign
    - _Preparation-Before-Build
    - _Build_no_AutoProvision
    - _Post_Process
    - _Bitrise-Upload
    - _AppCenter-Deploy
    - _Slack-Notification
    description: |-
      開発/結合/総合のときはこっちを回す。
      手動でビルドするときはMessageを忘れないこと!
    envs:
    - opts:
        is_expand: false
      STAGE: stage1
    - opts:
        is_expand: false
      RELEASE_SHORTVERSION: x.xx.x
  Ukeire-Test:
    before_run: 
    after_run:
    - _Preparation-Before-Build
    - _Build
    - _Post_Process
    - _Bitrise-Upload
    - _AppCenter-Deploy
    - _Slack-Notification
    description: |-
      開発/結合/総合のときはこっちを回す。
      手動でビルドするときはMessageを忘れないこと!
    envs:
    - opts:
        is_expand: false
      STAGE: stage2
    - opts:
        is_expand: false
      RELEASE_SHORTVERSION: x.xx.x
    - RELEASE_NOTES: 受入テスト/${STAGE}
  _AppCenter-Deploy:
    steps:
    - nvm:
        inputs:
        - node_version: 10.0.0
    - script:
        inputs:
        - content: "#!/usr/bin/env bash\n\nenvman add --key APPCENTER_NAME --value {AppCenterの向き先}"
        title: Set AppCenter Info
    - appcenter-app-release:
        inputs:
        - artifact_path: "$BITRISE_IPA_PATH"
        - notify_testers: 'true'
    - script@1.1.5:
        title: Get Latest AppCenter App Id
        inputs:
        - content: |-
            #!/usr/bin/env bash
            set -ex

            if hash appcenter 2>/dev/null; then
              echo "Microsoft AppCenter CLI already installed."
            else
              echo "Microsoft AppCenter CLI is not installed. Installing..."
              npm install -g appcenter-cli
            fi

            if hash jq 2>/dev/null; then
              echo "jq already installed."
            else
              echo "jq is not installed. Installing..."
              /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
              brew install jq
            fi

            APPCENTER_LATEST_APP_ID=$(appcenter distribute releases list --app ${APPCENTER_ORG}/${APPCENTER_NAME} --token ${APPCENTER_API_TOKEN} --output json | jq '. | sort_by(.uploadedAt) | reverse | .[0].id')

            envman add --key APPCENTER_LATEST_APP_ID --value $APPCENTER_LATEST_APP_ID
    - appcenter-dsym-upload:
        inputs:
        - api_token: "$APPCENTER_API_TOKEN"
        - app_id: "$APPCENTER_ORG/$APPCENTER_NAME"
    description: AppCenterへのテスト配信を実施します。
    envs:
    - opts:
        is_expand: false
      APPCENTER_ORG: xxx
    - opts:
        is_expand: false
      DISTRIBUTION_GROUPS: Collaborators
  _Slack-Notification:
    steps:
    - slack:
        inputs:
        - message: "${SLACK_MESSAGE}"
        - thumb_url_on_error: ''
        - message_on_error: ビルドに失敗しました。
        - icon_url: ''
        - from_username: ''
        - icon_url_on_error: ''
        - from_username_on_error: ''
        - pretext: |-
            ┌──────────────┐
            < ビルド成功してるよ  |
            └──────────────┘
        - pretext_on_error: |-
            ┌──────────────┐
            < なんかミスってない? |
            └──────────────┘
        - webhook_url: {Slackのペイロード}
    description: Slackにビルド結果を通知します。
    envs:
    - SLACK_MESSAGE: |-
        ■ リリースノート
        ${RELEASE_NOTES}

        ■ 付属情報
        GIT URL: ${GIT_REPOSITORY_URL}/commit/${GIT_CLONE_COMMIT_HASH}
        AppCenter URL: https://appcenter.ms/orgs/${APPCENTER_ORG}/apps/${APPCENTER_NAME}/distribute/releases/${APPCENTER_LATEST_APP_ID}
  _Preparation-Before-Build:
    steps:
    - activate-ssh-key:
        title: Activate App SSH key
        inputs:
        - ssh_key_save_path: "$HOME/.ssh/steplib_ssh_step_id_rsa"
    - git-clone: {}
    - script:
        inputs:
        - content: |
            export TZ='Asia/Tokyo'
            envman add --key AUTO_BUILDVERSION --value `date +"%y.%-m.%-d"`
        title: Set Build Version
    - ruby-script@2.1.0:
        inputs:
        - gemfile_content: |-
            source 'https://rubygems.org'

            # gem 'json', '~> 1.8', '>= 1.8.3'

            gem 'redcarpet'
        - ruby_content: puts "start"
    - cocoapods-install: {}
  _Build:
    steps:
    - set-xcode-build-number:
        inputs:
        - plist_path: "$APP_PLIST"
        - build_short_version_string: "$RELEASE_SHORTVERSION"
        - build_version: "$AUTO_BUILDVERSION.$BITRISE_BUILD_NUMBER"
        title: BaitoruApp Set Xcode Project Build Number
    - set-xcode-build-number:
        inputs:
        - plist_path: "$NOTIFICATION_PLIST"
        - build_short_version_string: "$RELEASE_SHORTVERSION"
        - build_version: "$AUTO_BUILDVERSION.$BITRISE_BUILD_NUMBER"
        title: NotificationServiceExtension Set Xcode Project Build Number
    - set-xcode-build-number:
        inputs:
        - plist_path: "$IMESSAGE_PLIST"
        - build_short_version_string: "$RELEASE_SHORTVERSION"
        - build_version: "$AUTO_BUILDVERSION.$BITRISE_BUILD_NUMBER"
        title: iMessageExtension Set Xcode Project Build Number
    - ios-auto-provision:
        inputs:
        - team_id: {オーガナイゼーションID}
        - distribution_type: ad-hoc
    - xcode-archive:
        title: 'Xcode: Create Archive'
        inputs:
        - scheme: "$BITRISE_SCHEME"
          opts:
            is_expand: true
        - configuration: ''
        - force_code_sign_identity: '{証明書}'
        - team_id: {オーガナイゼーションID}
        - export_method: ad-hoc
        - compile_bitcode: 'no'
        - upload_bitcode: 'no'
        - output_dir: "${BITRISE_DEPLOY_DIR}"
    envs:
    - BITRISE_SCHEME: "${STAGE}"
    - APP_PLIST: {PLISTの場所}
    - opts:
        is_expand: false
      BITRISE_PROJECT_PATH: {プロジェクトのPATH}
    - opts:
        is_expand: false
      NOTIFICATION_PLIST: {PLISTの場所}
    - opts:
        is_expand: false
      IMESSAGE_PLIST: {PLISTの場所}
  _Bitrise-Upload:
    steps:
    - deploy-to-bitrise-io: {}
  _PullRequest_Reviewer_Assign:
    before_run: 
    after_run: []
    envs:
    - opts:
        is_expand: false
      REVIEWER_CORE: {レビュワー}
    steps:
    - script@1.1.5:
        inputs:
        - content: |-
            #!/bin/bash

            # 申し送り事項
            # 1. ユーザ名は正しいものが設定されている前提です。
            # 2. 存在しないユーザ名を設定しても処理は正常に終了します。

            # プルリク起動でなければスキップする。
            if [ "${BITRISE_PULL_REQUEST}" = "" ] ; then
              echo "プルリク起動でないためスキップ"
              exit 0
            fi

            # GIT_REPOSITORY_URLからAPI用のURLを生成する。
            readonly GITHUB_API_BASE_URL=$(echo $GIT_REPOSITORY_URL | sed -e "s/git@/https:\/\//g" -e "s/:${BITRISEIO_GIT_REPOSITORY_OWNER}\/${BITRISEIO_GIT_REPOSITORY_SLUG}\.git/\/api\/v3/g")

            # Stringで定義されているレビュワーを配列に変換する。
            readonly REVIEWER_CORE_ARRAY=(${REVIEWER_CORE//,/ })

            # jqが必要なのでなければインストールする。
            if hash jq 2>/dev/null; then
              echo "jq already installed."
            else
              echo "jq is not installed. Installing..."
              /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
              brew install jq
            fi

            # すでにレビュワーが登録されていれば処理をスキップする。
            reviewer_count=$(curl -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" \
              ${GITHUB_API_BASE_URL}/repos/${BITRISEIO_GIT_REPOSITORY_OWNER}/${BITRISEIO_GIT_REPOSITORY_SLUG}/pulls/${BITRISE_PULL_REQUEST} | jq .requested_reviewers | jq length)
            if test $reviewer_count -gt 0 ; then
              echo "レビュワー登録済みのため処理をスキップ"
              exit 0
            fi

            # コアレビュワー指定
            reviewer_core=${REVIEWER_CORE_ARRAY[$(($RANDOM % ${#REVIEWER_CORE_ARRAY[*]}))]}
            echo "reviewer1: "$reviewer_core

            # レビュワーの登録
            curl -X POST -H 'Content-Type:application/json' -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" \
              ${GITHUB_API_BASE_URL}/repos/${BITRISEIO_GIT_REPOSITORY_OWNER}/${BITRISEIO_GIT_REPOSITORY_SLUG}/pulls/${BITRISE_PULL_REQUEST}/requested_reviewers \
              -d '{"reviewers": ["'${reviewer_core}'"]}'
        title: PullRequest Reviewer Assign
  _Post_Process:
    before_run: 
    after_run: []
    steps:
    - script@1.1.5:
        inputs:
        - content: |-
            #!/bin/bash

            # 最優先するのは設定されているリリースノート(環境変数)
            if [ "${RELEASE_NOTES}" != "" ] ; then
              echo "リリースノートが設定されているためスキップ"
              exit 0
            fi

            # プルリク起動でなければデフォルト文言を設定する。
            if [ "${BITRISE_PULL_REQUEST}" = "" ] ; then
              echo "プルリク起動でないためデフォルトを設定"
              envman add --key RELEASE_NOTES --value "結合_総合/${STAGE}"
              exit 0
            fi

            # GIT_REPOSITORY_URLからAPI用のURLを生成する。
            readonly GITHUB_API_BASE_URL=$(echo $GIT_REPOSITORY_URL | sed -e "s/git@/https:\/\//g" -e "s/:${BITRISEIO_GIT_REPOSITORY_OWNER}\/${BITRISEIO_GIT_REPOSITORY_SLUG}\.git/\/api\/v3/g")

            # jqが必要なのでなければインストールする。
            if hash jq 2>/dev/null; then
              echo "jq already installed."
            else
              echo "jq is not installed. Installing..."
              /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
              brew install jq
            fi

            # プルリクのタイトルをリリースノートとする。
            pull_request_title=$(curl -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" \
              ${GITHUB_API_BASE_URL}/repos/${BITRISEIO_GIT_REPOSITORY_OWNER}/${BITRISEIO_GIT_REPOSITORY_SLUG}/pulls/${BITRISE_PULL_REQUEST} | jq .title)

            echo $pull_request_title
            envman add --key RELEASE_NOTES --value $pull_request_title
        title: Create Release Note
meta:
  bitrise.io:
    machine_type: standard
trigger_map:
- push_branch: release/*
  workflow: Ukeire-Test
- pull_request_source_branch: feature/*
  pull_request_target_branch: develop*
  workflow: Kaihatu-Test

まとめ

これからBitriseを始める人たちの助けになればと思います!
みんなでBitriseを盛り上げていきましょ!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした