はじめに

はじめまして。TISのインシュアランスサービス部およびデザイン&エンジニアリング部に所属している瀧です。私はモバイルアプリの新規開発プロジェクトに2年間従事し、数十万ユーザー規模のアプリをReactNativeで開発・運用しました。 本記事では、実際にプロジェクトで使用したReactNative+Expoアプリケーションのための、Azure DevOpsを用いたCI/CDパイプラインの実装事例をご紹介します。

CI/CDプラットフォームの構成

本プロジェクトでは、ReactNativeとExpoを組み合わせたアーキテクチャを採用しています。CI/CDプラットフォームにはAzure DevOps Pipelinesを選定し、ソース管理にはAzure Reposを利用しています。ビルドおよびデプロイメントの自動化にはFastlaneを活用しています。

開発したアプリケーションはiOSとAndroidの両プラットフォームに対応しており、iOSアプリはApp Store ConnectのTestFlight、AndroidアプリはGoogle Play Consoleを通じて配布します。

技術選定の背景

ReactNative+Expoを採用した理由

ReactNativeによりiOSとAndroidの両プラットフォームに対して単一のコードベースで開発できるクロスプラットフォーム開発の効率性と、現在ReactNativeの開発で主流となっているExpoの豊富な機能を活用できることが主な理由です。また、TISにはReactNativeの豊富な開発実績と技術的知見があり、Fintanで公開されているノウハウを活用することで、効率的かつ高品質なアプリ開発を実現できると判断しました。

Azure DevOpsを採用した理由

バックエンドインフラにAzureを採用していたため、CI/CDプラットフォームも同じMicrosoft Azureエコシステム内のAzure DevOpsを選択しました。

本記事で紹介するパイプラインの特徴

  • CDパイプラインは実行時に環境名をパラメータとして受け取る設計になっています。なにも指定しない場合、デフォルト指定であるprodが実行されます(prod環境向けのCDパイプラインになります)。実行時にパラメータで渡す環境名を変え、指定した環境用の設定ファイルを用意することで、同一のパイプライン定義ファイルを再利用して複数環境(prodに加えて、dev・stagingなど)用のパイプラインを作成することができます。
  • 証明書やキーストア、APIキーなどの機密情報はAzure DevOpsのSecure FilesとPipeline Variablesで管理しています。
  • ビルド成果物(.ipa/.aab ファイル)をAzure Pipelinesのアーティファクトとして保存し、必要に応じて手動でダウンロード・配布できる仕組みを提供しています。
  • Azure DevOpsのYAMLに直接記述すると冗長になりがちな処理を、Fastlaneでシンプルなlaneにまとめるようにしています。特にTestFlightやGoogle Play Consoleへの複雑なAPI呼び出しを簡潔に記述することができます。
  • 記事で紹介しているコードの一部は、mobile-app-crib-notes(Apache License 2.0)のソースコードを参考に作成しました。

必要な準備

CI/CDパイプラインを構築する前に、以下の準備作業が必要です。

1.Secure Filesの設定

ビルド時に必要な認証情報や証明書ファイルなど、機密性の高いファイルを安全に管理するため、Azure PipelinesのLibraryからSecure Filesに以下のファイルをアップロードします。ファイル名は環境変数で設定するため任意のファイル名で問題ありません。

Android用

ファイル
キーストアファイル

例)prod.keystore

Firebase構成ファイル

例)google-services-prod.json

Google Play Console API アクセス用のサービスアカウントキー

例)fastlane-supply-prod.json

注意:取得手順はドキュメントを参照

iOS用

ファイル
配布証明書

例)DistributionCertificate.p12

Provisioning Profile

例)DistProfile-prod.mobileprovision

Firebase構成ファイル

例)GoogleService-Info-prod.plist

 

2.package.jsonスクリプトの準備

package.jsonに以下のスクリプトを定義します。これらのスクリプトはパイプライン実行時に利用します。

注意:各スクリプト内で利用されているコマンドのオプションは、本プロジェクトの構成や要件に応じて設定されています。プロジェクトごとに適宜調整してください。


{
	"scripts": {
		"build:plugin": "rimraf config/plugin/build && tsc --build config/plugin",
		"prebuild:prod": "run-s build:plugin && cross-env ENVIRONMENT=prod expo prebuild --clean --no-install",
		"lint:es": "eslint --ext .jsx,.js,.tsx,.ts .",
		"lint:tsc": "tsc --noEmit",
		"coverage": "jest --detectOpenHandles --runInBand --coverage --coverageReporters=cobertura"
	}
}

3.ConfigPluginの準備

本プロジェクトでは、AndroidおよびiOSのリリースビルド設定を効率的かつ自動的に管理するために、以下のExpo Config Pluginを利用しています。

1.Androidリリースビルド設定:withAndroidAppBuildGradleForRelease

ExpoのCDパイプラインにおいて、キーストア情報などの機密データをgradle.propertiesファイルに書き込みます。

このwithAndroidAppBuildGradleForReleaseConfig Pluginは、Androidアプリのbuild.gradleファイルを拡張し、このgradle.properties内の値を参照して、署名付きのリリースビルドを可能にするための設定を自動で適用します。

2.iOSビルド設定と署名:withIosSetCredentials

このConfig Pluginは、Xcodeプロジェクトのビルド設定(具体的にはバンドルID、署名チームID、Provisioning Profileなど)を、Expoの設定ファイル(app.jsonapp.config.js)で定義された値に基づいて自動で書き換えるために利用されます。

これにより、iOSアプリのコード署名に必要な設定を手動でXcodeプロジェクトに適用する手間を省き、ビルドプロセスを自動化します。

Config Pluginのプロジェクトへの取り込みおよび利用方法の詳細については、公式ドキュメント、Create and use config plugins – Expo Documentationを参考にしてください。

4.Fastlaneの設定

ビルドおよびデプロイメントの自動化に利用しているFastlaneのlaneを定義するために、以下のディレクトリ構成に従ってfastlaneフォルダにFastfileファイルを配置します。androidiosフォルダは、Expoのprebuildコマンド実行時に自動生成されます。Gitリポジトリにはコミットされず、CI/CDパイプライン内で動的に作成されます。

(Expoプロジェクトのルートディレクトリ)
├── fastlane
│ └── Fastfile
├── src
├── assets
├── android
├── ios
├── app.config.js
├── package.json
└── その他の設定ファイル

Fastfile


platform :ios do

  desc "Prebuild Production Release"
  lane :prebuild_prod do
    # npmパッケージのインストールは、lane実行前に実施されている想定、かつExpoのprebuildだとbundle execを使用してCocoaPodsの依存パッケージをインストールしないため、
    # --no-installオプションを付けて依存パッケージはインストールしないようにする(このアプリでは、prebuildスクリプトの中で、--no-installオプションを付与している)
    # https://docs.fastlane.tools/actions/sh/
    sh("npm run prebuild:prod -- -p ios")
  end

  desc "Build Production Release"
  lane :build_prod do
    # CocoaPodsの依存パッケージをインストールする
    # https://docs.fastlane.tools/actions/cocoapods/
    cocoapods(
      repo_update: true,
      podfile: "ios/Podfile",
      # CI環境ではプロジェクト名などが変わってしまうため、Podfile.lockに差分が出てしまう
      deployment: false,
      silent: false,
    )

    bundle_id = ENV["BUNDLE_ID"]
    provisioning_profile_name = ENV["APPLE_DISTRIBUTION_PROVISIONING_PROFILE_SECURE_FILENAME"]
    workspace = ENV["WORKSPACE"]
    scheme = ENV["SCHEME"]

    # アプリのiOS向けビルド
    build_ios_app(
      # アプリ名が日本語の場合、prebuildした際に自動で生成されるscheme(設定ファイル)はapp、
      # workspace(フォルダ)はios/app.xcworkspaceになる
      workspace: workspace,
      scheme: scheme,
      configuration: "Release",
      output_directory: "ios/build",
      output_name: "app.ipa",
      export_options: {
        method: "app-store",
        provisioningProfiles: {
          bundle_id => provisioning_profile_name,
        },
      },
      silent: false,
      suppress_xcode_output: false,
    )
  end

  desc "Upload Production On TestFlight"
  lane :upload_prod_testflight do
    apple_id = ENV["APPLE_ID"]
    
    # ビルドしたアプリをTestFlightにアップロード
    upload_to_testflight(
      apple_id: "#{apple_id}",
      skip_submission: true,
      skip_waiting_for_build_processing: true,
      ipa: "ios/build/app.ipa",
    )
  end

end

platform :android do

  desc "Prebuild Production Release"
  lane :prebuild_prod do
    # npmパッケージのインストールは、lane実行前に実施されている想定のため、
    # --no-installオプションを付けて依存パッケージはインストールしないようにする(このアプリでは、prebuildスクリプトの中で、--no-installオプションを付与している)
    # https://docs.fastlane.tools/actions/sh/
    sh("npm run prebuild:prod -- -p android")
  end

  desc "Build Production Release"
  lane :build_prod do
    # アプリのAndroid向けビルド
    build_android_app(task: "bundle", build_type: "Release", project_dir: "android")
  end

  desc "Upload Production On GooglePlay Console"
  lane :upload_google_play_console do |options|
    # ビルドしたアプリをGoogle Play Consoleにアップロード
    supply(
      aab: options[:aab],
      json_key: options[:json_key],
      package_name: options[:package_name],
      track: options[:track]
    )
  end

end

Azure DevOpsでのパイプライン構築手順

注意1回の手順で作成できるパイプラインは1つのみです。複数のパイプラインを作成する場合は、パイプラインの数だけ手順を繰り返してください。

1.パイプラインの構築

Azure Pipelinesのランディングページで「New pipeline」を選択します。

本プロジェクトではソースコード管理にAzure Reposを使用しているため、「Azure Repos Git」を選択します。

対象となるリポジトリを選択します。

「Starter pipeline」を選択します。
※今回はパイプラインのコードをコピーして貼り付けるため、どのテンプレートを選択しても問題ありません。

以下のCIパイプラインまたはCDパイプラインのソースコードをコピー&ペーストします。

CIパイプライン

このパイプラインは、developブランチへのプッシュやPR作成をトリガーに、ESLintとTypeScriptのリントを実行し、テストとカバレッジ計測を行います。最後に、Cobertura形式のカバレッジ結果とJUnit形式のテスト結果をAzure DevOpsに公開します。


trigger:
  - develop

pool:
  vmImage: ubuntu-latest

variables:
  - name: appDir
    value: '$(System.DefaultWorkingDirectory)'
  - name: npmConfigCache
    value: '$(Pipeline.Workspace)/.npm'

steps:
  # Node.jsをインストール
  - task: NodeTool@0
    inputs:
      versionSpec: ">=16.0.0"
    displayName: "Install Node.js"

  # npmのキャッシュを復元して依存関係をインストール
  - task: Cache@2
    displayName: Cache npm
    inputs:
      key: 'npm | "$(Agent.OS)" | "$(appDir)/package-lock.json"'
      restoreKeys: |
        npm | "$(Agent.OS)"
      path: $(npmConfigCache)

  - task: CmdLine@2
    displayName: 'npm ci --cache $(npmConfigCache)'
    inputs:
      script: 'npm ci --cache $(npmConfigCache)'
      workingDirectory: '$(appDir)'

  # ESLintによる静的解析
  - script: |
      npm run lint:es
    displayName: "npm run lint:es"

  # TypeScript型チェック
  - script: |
      npm run lint:tsc
    displayName: "npm run lint:tsc"

  # テストとカバレッジ計測
  - script: |
      npm run coverage
    displayName: "npm run coverage"

  # カバレッジ結果を出力
  - task: PublishCodeCoverageResults@1
    displayName: Publish Code Coverage
    inputs:
      codeCoverageTool: Cobertura
      summaryFileLocation: $(System.DefaultWorkingDirectory)/coverage/cobertura-coverage.xml

  # テスト結果を出力
  - task: PublishTestResults@2
    displayName: Publish Test Results
    inputs:
      testResultsFormat: "JUnit"
      testResultsFiles: $(System.DefaultWorkingDirectory)/coverage/jest-junit.xml

CDパイプライン

パイプラインの処理フローは以下の通りです。

  1. 環境準備(Ruby、Node.js、Java/Xcodeのバージョン設定)
  2. 依存関係の復元(npmキャッシュ活用による高速化)
  3. ビルドに必要な機密ファイルの配置(証明書、設定ファイルのダウンロードとコピー)
  4. Expo Prebuild(ネイティブプロジェクトの動的生成)
  5. ビルド実行(Fastlaneによる自動化)
  6. 配布(TestFlight/Google Play Consoleへの自動アップロード)

注意:プロジェクトでは以下のライブラリバージョンを利用しています。

Expo:49.0.16
React Native:0.72.6

Android

trigger: none

parameters:
  - name: environment
    default: prod

variables:
  - name: appDir
    value: "$(System.DefaultWorkingDirectory)"
  - name: npmConfigCache
    value: "$(Pipeline.Workspace)/.npm"

jobs:
  - job: build_${{parameters.environment}}
    displayName: "build ${{parameters.environment}}"
    pool:
      vmImage: macOS-13
    steps:
      # Ruby 3.3をインストール(Fastlane用)
      - task: UseRubyVersion@0
        inputs:
          versionSpec: "3.3"
          addToPath: true

      # Node.js 18.19.0をインストール
      - task: NodeTool@0
        displayName: "Use Node.js 18.19.0"
        inputs:
          versionSpec: "18.19.0"

      # Java 17をインストール(Android開発用)
      - task: JavaToolInstaller@0
        inputs:
          versionSpec: "17"
          jdkArchitectureOption: "x64"
          jdkSourceOption: "PreInstalled"

      # npmのキャッシュを復元して依存関係をインストール
      - task: Cache@2
        displayName: Cache npm
        inputs:
          key: 'npm | "$(Agent.OS)" | "$(appDir)/package-lock.json"'
          restoreKeys: |
            npm | "$(Agent.OS)"
          path: $(npmConfigCache)

      - task: CmdLine@2
        displayName: "npm ci --cache $(npmConfigCache)"
        inputs:
          script: "npm ci --cache $(npmConfigCache)"
          workingDirectory: "$(appDir)"

      # アプリ署名用のKeystoreファイルをダウンロード
      - task: DownloadSecureFile@1
        displayName: "Download Keystore file for Staging"
        inputs:
          secureFile: $(ANDROID_RELEASE_INHOUSE_KEYSTORE_SECURE_FILENAME)

      # Keystoreファイルをプロジェクトルートにコピー
      - task: CopyFiles@2
        displayName: "Copy Keystore file to: $(appDir)"
        inputs:
          SourceFolder: "$(Agent.TempDirectory)"
          Contents: $(ANDROID_RELEASE_INHOUSE_KEYSTORE_SECURE_FILENAME)
          TargetFolder: "$(appDir)"

      # 環境別のFirebase設定ファイルをダウンロード
      - task: DownloadSecureFile@1
        name: googleServicesJson${{parameters.environment}}
        displayName: "Download google-services.json file for ${{parameters.environment}}"
        inputs:
          secureFile: $(GOOGLE_SERVICES_JSON_SECURE_FILENAME)

      # Firebase設定ファイルをプロジェクトルートにコピー
      - task: CopyFiles@2
        displayName: "Copy google-services.json file to: $(appDir)"
        inputs:
          SourceFolder: "$(Agent.TempDirectory)"
          Contents: $(GOOGLE_SERVICES_JSON_SECURE_FILENAME)
          TargetFolder: "$(appDir)"

      # Firebase設定ファイルを標準名にリネーム
      - task: CmdLine@2
        displayName: "Rename google-services.json"
        inputs:
          script: |
            /bin/mv $(GOOGLE_SERVICES_JSON_SECURE_FILENAME) google-services.json
          workingDirectory: "$(appDir)"

      # Fastlane用のGoogle Play ConsoleサービスアカウントJSONファイルをダウンロード
      - task: DownloadSecureFile@1
        displayName: "Download fastlane-supply.json"
        inputs:
          secureFile: "$(FASTLANE_SUPPLY_JSON_FILE_NAME)"

      # Expo prebuildでネイティブAndroidプロジェクトを生成
      - task: CmdLine@2
        displayName: "Prebuild Android app for ${{parameters.environment}}"
        inputs:
          script: "fastlane android prebuild_${{parameters.environment}}"
          workingDirectory: "$(appDir)"

      # アプリ署名用のKeystore設定をgradle.propertiesに追加
      # build.gradle で gradle.properties の値を参照するようにするために、下記コンフィグプラグインを使用
      # https://github.com/Fintan-contents/mobile-app-crib-notes/blob/master/example-app/SantokuApp/config/plugin/src/android/withAndroidAppBuildGradleForRelease.ts
      - task: CmdLine@2
        displayName: "Add gradle.properties settings for ReleaseInHouse"
        inputs:
          script: |
            echo -e '\n# Keystore properties for ReleaseInHouse' >> gradle.properties
            echo 'SANTOKU_UPLOAD_KEYSTORE_FILE=$(appDir)/$(ANDROID_RELEASE_INHOUSE_KEYSTORE_SECURE_FILENAME)' >> gradle.properties
            echo 'SANTOKU_UPLOAD_KEYSTORE_PASSWORD=$(ANDROID_RELEASE_INHOUSE_KEYSTORE_PASSWORD)' >> gradle.properties
            echo 'SANTOKU_UPLOAD_KEY_ALIAS=$(ANDROID_RELEASE_INHOUSE_KEY_ALIAS)' >> gradle.properties
            echo 'SANTOKU_UPLOAD_KEY_PASSWORD=$(ANDROID_RELEASE_INHOUSE_KEY_PASSWORD)' >> gradle.properties
          workingDirectory: '$(appDir)/android'
	  
	  # GradleでAndroidアプリをビルド(AAB形式)
      - task: CmdLine@2
        displayName: "Build Android app for ${{parameters.environment}}"
        inputs:
          script: "fastlane android build_${{parameters.environment}}"
          workingDirectory: "$(appDir)"

      # ビルド成果物をパイプライン成果物として保存
      - task: PublishPipelineArtifact@1
        displayName: "Publish Artifact"
        inputs:
          targetPath: "$(appDir)/android/app/build/outputs"
          artifactType: "pipeline"
          artifactName: "${{parameters.environment}}.aab"
          parallel: true
        condition: succeeded()

      # Google Play Consoleにアップロード
      - task: CmdLine@2
        displayName: "Upload Android app to Google Play Console"
        inputs:
          script: |
            PACKAGE_NAME=$(node -p "require('./config/app.config.${{parameters.environment}}.js')().android.package")
            fastlane android upload_google_play_console \
              aab:"$(appDir)/android/app/build/outputs/bundle/release/app-release.aab" \
              json_key:"$(Agent.TempDirectory)/$(FASTLANE_SUPPLY_JSON_FILE_NAME)" \
              package_name:"$PACKAGE_NAME" \
              track:"internal"
          workingDirectory: "$(appDir)"
iOS

trigger: none

parameters:
  - name: environment
    default: prod

variables:
  - name: appDir
    value: "$(System.DefaultWorkingDirectory)"
  - name: npmConfigCache
    value: "$(Pipeline.Workspace)/.npm"

jobs:
  - job: build_${{parameters.environment}}
    displayName: "build ${{parameters.environment}}"
    pool:
      vmImage: macOS-14
    steps:
      # Ruby 3.3をインストール
      - task: UseRubyVersion@0
        inputs:
          versionSpec: "3.3"
          addToPath: true

      # Apple Distribution証明書をインストール(App Store配布用)
      - task: InstallAppleCertificate@2
        displayName: "Install an Apple certificate for ${{parameters.environment}}"
        inputs:
          certSecureFile: $(APPLE_DISTRIBUTION_CERTIFICATE_SECURE_FILENAME)
          certPwd: $(APPLE_DISTRIBUTION_CERTIFICATE_PASSWORD)

      # App Store配布用Provisioning Profileをインストール
      - task: InstallAppleProvisioningProfile@1
        displayName: "Install an Apple provisioning profile for ${{parameters.environment}}"
        inputs:
          provProfileSecureFile: $(APPLE_DISTRIBUTION_PROVISIONING_PROFILE_SECURE_FILENAME)

      # Xcode 16.2を使用するように設定
      - task: CmdLine@2
        displayName: "Use Xcode 16.2"
        inputs:
          script: "sudo xcode-select --switch /Applications/Xcode_16.2.app"

      # Node.js 18.19.0をインストール
      - task: NodeTool@0
        displayName: "Use Node.js 18.19.0"
        inputs:
          versionSpec: "18.19.0"

      # npmのキャッシュを復元して依存関係をインストール
      - task: Cache@2
        displayName: Cache npm
        inputs:
          key: 'npm | "$(Agent.OS)" | $(appDir)/package-lock.json'
          restoreKeys: |
            npm | "$(Agent.OS)"
          path: $(npmConfigCache)

      - task: CmdLine@2
        displayName: "npm ci --cache $(npmConfigCache)"
        inputs:
          script: "npm ci --cache $(npmConfigCache)"
          workingDirectory: "$(appDir)"

      # Gemfile.lockの依存関係をインストール
      - task: CmdLine@2
        displayName: "bundle install"
        inputs:
          script: "bundle install"
          workingDirectory: "$(appDir)"

      # 環境別のFirebase設定ファイルをダウンロード
      - task: DownloadSecureFile@1
        displayName: "Download GoogleServices-Info.plist file for ${{parameters.environment}}"
        inputs:
          secureFile: $(GOOGLE_SERVICES_INFO_PLIST_SECURE_FILENAME)

      # Firebase設定ファイルをプロジェクトルートにコピー
      - task: CopyFiles@2
        displayName: "Copy GoogleService-Info.plist file to: $(appDir)"
        inputs:
          SourceFolder: "$(Agent.TempDirectory)"
          Contents: $(GOOGLE_SERVICES_INFO_PLIST_SECURE_FILENAME)
          TargetFolder: "$(appDir)"

      # Firebase設定ファイルを標準名にリネーム
      - task: CmdLine@2
        displayName: "Rename GoogleService-Info.plist"
        inputs:
          script: |
            /bin/mv $(GOOGLE_SERVICES_INFO_PLIST_SECURE_FILENAME) GoogleService-Info.plist
          workingDirectory: "$(appDir)"

      # Expo prebuildでネイティブiOSプロジェクトを生成
      - task: CmdLine@2
        displayName: "Prebuild iOS app for ${{parameters.environment}}"
        inputs:
          script: "fastlane ios prebuild_${{parameters.environment}}"
          workingDirectory: "$(appDir)"

      # XcodeでiOSアプリをビルド
      - task: CmdLine@2
        displayName: "Build iOS app for ${{parameters.environment}}"
        inputs:
          script: "fastlane ios build_${{parameters.environment}}"
          workingDirectory: "$(appDir)"

      # TestFlightにアップロード(内部テスト用)
      - task: CmdLine@2
        displayName: "Upload TestFlight for ${{parameters.environment}}"
        inputs:
          script: |
            export APPLE_ID=$(APPLE_ID)
            export FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD=$(FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD)
            fastlane ios upload_${{parameters.environment}}_testflight
          workingDirectory: "$(appDir)"

      # ビルド成果物をパイプライン成果物として保存
      - task: PublishPipelineArtifact@1
        displayName: "Publish Artifact"
        inputs:
          targetPath: "$(appDir)/ios/build"
          artifactType: "pipeline"
          artifactName: "app.ipa"
          parallel: true
        condition: succeeded()

2.変数の設定

「Variables」を選択します。

「New variable」を選択します。

必要な環境変数を設定します。

AndroidのCDパイプラインに必要な環境変数

変数名 説明
ANDROID_RELEASE_INHOUSE_KEYSTORE_SECURE_FILENAME キーストアのファイル名
ANDROID_RELEASE_INHOUSE_KEYSTORE_PASSWORD キーストアのパスワード
ANDROID_RELEASE_INHOUSE_KEY_ALIAS キーのエイリアス名
ANDROID_RELEASE_INHOUSE_KEY_PASSWORD キーのパスワード
GOOGLE_SERVICES_JSON_SECURE_FILENAME Firebase構成ファイル名

 

iOSのCDパイプラインに必要な環境変数

変数名 説明
APPLE_DISTRIBUTION_CERTIFICATE_SECURE_FILENAME 配布証明書のファイル名
APPLE_DISTRIBUTION_CERTIFICATE_PASSWORD 証明書のパスワード
APPLE_DISTRIBUTION_PROVISIONING_PROFILE_SECURE_FILENAME Provisioning Profileのファイル名
GOOGLE_SERVICES_INFO_PLIST_SECURE_FILENAME Firebase構成ファイル名
APPLE_ID App Store Connectの [App情報] セクションのApple IDプロパティ
FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD アプリケーション固有のパスワード

注意:取得手順はドキュメントを参照

FASTLANE_USER App Store Connect/Apple Developer Portal ユーザーのメールアドレス
BUNDLE_ID バンドルID
FASTLANE_SUPPLY_JSON_FILE_NAME Google Play Console APIアクセス用のサービスアカウントキーのファイル名
WORKSPACE ワークスペース ファイルへのパス
SCHEME プロジェクトのスキーム

3.保存

パイプラインを保存します。作成したパイプラインは「azure-pipelines-1.yml」という名前でリポジトリにコミットされます。パイプラインに変更を加えたい場合は、リポジトリ内のこのファイルを編集することで、パイプラインの内容を更新できます。

運用時の注意点とメンテナンス

プロジェクト固有の値

設定ファイルはそのままコピー&ペーストで利用可能ですが、プロジェクト固有の値(Bundle ID、証明書名など)は適宜調整してください。

主要なメンテナンス項目

以下の項目は、定期的なメンテナンスが必要です。

  • 配布証明書:年 1 回の更新が必要
  • Provisioning Profile: 年 1 回の更新が必要
  • 実行環境:macOS、Xcode、Node.js、Java、Rubyのバージョンを定期アップデート

まとめ

本記事では、Azure DevOpsを活用したReact Native+ExpoアプリケーションのCI/CDパイプライン構築事例をご紹介しました。
React Native+Expoアプリの開発・運用において、このパイプライン設定が皆様のプロジェクトの参考になれば幸いです。

参考資料