Amazon Web Services ブログ

AWS CodeBuild for macOS が Fastlane のサポートを追加

macOS 環境向けの AWS CodeBuildFastlane がご利用いただけるようになりました。AWS CodeBuild は、ソースコードをコンパイルし、テストを実行して、すぐにデプロイできるソフトウェアパッケージを作成する、フルマネージド継続的インテグレーションサービスです。

Fastlane は、モバイルアプリ開発のさまざまな側面を自動化するように設計されたオープンソースツールスイートです。コード署名、スクリーンショット生成、ベータ版の配布、アプリケーションストアへの送信などのタスクを管理するための一元化された一連のツールをモバイルアプリデベロッパーに提供します。一般的な継続的インテグレーションおよび継続的デプロイ (CI/CD) プラットフォームと統合し、iOS と Android の両方の開発ワークフローをサポートします。Fastlane は優れたオートメーション機能を提供しますが、デベロッパーはセットアップとメンテナンス中に課題に直面する可能性があります。特に Ruby の構文とパッケージ管理システムに慣れていないチームは、Fastlane の設定が複雑であると感じる可能性があります。モバイルプラットフォームやサードパーティーサービスの更新によって既存のワークフローの調整が必要になる場合があるため、Fastlane とその依存関係を最新の状態に保つには継続的な作業が必要です。

2024 年 8 月に CodeBuild for macOS を導入したとき、お客様の課題の 1 つがビルド環境に Fastlane をインストールして維持することであることがわかりました。カスタムビルド環境に Fastlane を手動でインストールすることは可能でしたが、AWS はインフラストラクチャから差別化につながらない手間のかかる作業を取り除き、お客様がビジネスにとって重要な側面により多くの時間を費やせるようにします。本日より、Fastlane はデフォルトでインストールされ、buildspec.yaml ファイルで使い慣れたコマンド fastlane build を使用できるようになりました。

Fastlane とコード署名
App Store でアプリケーションを配布するには、デベロッパーは Apple Developer ポータルで生成されたプライベートキーを使用してバイナリに署名する必要があります。このプライベートキーと、それを検証する証明書は、ビルドプロセス中にアクセスできる必要があります。これは、開発チームにとって課題となる可能性があります。なぜなら、開発チームは、開発プライベートキー (特定のテストデバイスへのデプロイを可能にするもの) をチームメンバー間で共有する必要があるからです。さらに、App Store にバイナリをアップロードする前の署名プロセスでは、配布プライベートキー (App Store での公開を可能にするもの) が使用可能である必要があります。

Fastlane は、開発キーと配布キーおよび証明書の管理でもデベロッパーをサポートするという点で、多目的なビルドシステムです。デベロッパーは、fastlane match を使用して、チーム内で署名マテリアルを共有し、個々のデベロッパーのマシンと CI 環境で安全かつ簡単にこれらにアクセスできるようにすることができます。match を使用すると、プライベートキー、証明書、モバイルプロビジョニングプロファイルをセキュリティで保護された共有ストレージに保存できます。これにより、デベロッパーのノートパソコンであれ、クラウド内のサーバーマシンであれ、ローカルビルド環境が共有ストレージと同期された状態を維持できます。ビルド時に、アプリケーションに署名するために必要な証明書を安全にダウンロードし、codesign ユーティリティがそれらの証明書を取得できるようにビルドマシンを設定します。

match を使用すると、GitHub、GitLab、Google Cloud Storage、Azure DevOps、Amazon Simple Storage Service (Amazon S3) を通じて署名シークレットを共有できます。

これらのいずれかを既に使用していて、プロジェクトを CodeBuild に移行する場合、行うべきことはあまりありません。必要なのは、CodeBuild ビルド環境が共有ストレージにアクセスできることを確認することだけです (デモのステップ 3 をご覧ください)。

仕組みを見てみましょう
Fastlane または CodeBuild を初めて使用するお客様のために、その仕組みをご紹介します。

このデモでは、既存の iOS プロジェクトを使用して始めます。プロジェクトは、CodeBuild でビルドされるように既に設定されています。詳細については、以前のブログ記事「AWS CodeBuild を利用して、継続的インテグレーションパイプラインに macOS を追加する」をご覧ください。

3 つのステップで開始する方法を説明します:

  • 既存の署名マテリアルを共有プライベート GitHub リポジトリにインポートする
  • プロジェクトをビルドして署名するように fastlane を設定する
  • CodeBuild で fastlane を使用する

ステップ 1: 署名マテリアルをインポートする

私が読んだ fastlane に関するドキュメントのほとんどでは、開始するために新しいキーペアと新しい証明書を作成する方法が説明されています。これは新しいプロジェクトには確かに当てはまりますが、実際には、プロジェクトと署名キーは既に存在している可能性があります。したがって、最初のステップは、これらの既存の署名マテリアルをインポートすることです。

Apple App Store は、開発と配布に異なるキーと証明書を使用します (アドホック証明書とエンタープライズ証明書もありますが、これらはこの記事の範囲外です)。使用ごとに 3 つのファイルが必要です (合計 6 つのファイル):

  • Apple デベロッパーコンソールから作成してダウンロードできる .mobileprovision ファイル。プロビジョニングプロファイルは、ユーザーの ID、アプリケーション ID、アプリケーションが持つ可能性のある権限をリンクします。
  • Apple がプライベートキーを検証するために発行した証明書である .cer ファイル。これは、Apple Developer ポータルからダウンロードできます。証明書を選択し、[ダウンロード] を選択します。
  • プライベートキーを含む .p12 ファイル。キーは、Apple Developer ポータルで作成したときにダウンロードできます。ダウンロードしていないが、マシン上に保存されている場合は、Apple Keychain アプリケーションからエクスポートできます。KeyChain.app は macOS 15.x では非表示になっていることに留意してください。open /System/Library/CoreServices/Applications/Keychain\ Access.app で開くことができます。エクスポートするキーを選択し、右クリックして [エクスポート] を選択します。
キーチェーンから p12 ファイルをエクスポート

これらのファイルを作成したら、次の内容を含む fastlane/Matchfile ファイルを作成します:

git_url("https://github.com/sebsto/secret.git")
storage_mode("git")
type("development")
# または、appstore を使用して配布署名キーと証明書を使用します
# type("appstore")
Ruby

GitHub リポジトリの URL を置き換え、このリポジトリがプライベートであるようにしてください。これは、署名キーと証明書のストレージとして機能します。

その後、fastlane match import --type appstore コマンドを使用して、既存のファイルをインポートします。各環境 (appstoredevelopment) 用のコマンドを繰り返します。

初回は、fastlane から Apple ID のユーザー名とパスワードの入力を求められます。証明書の有効性を検証したり、必要に応じて新しい証明書を作成したりするために、App Store Connect に接続します。セッション Cookie は ~/.fastlane/spaceship/<your apple user id>/cookie に保存されます。

fastlane match からもパスワードの入力を求められます。このパスワードを使用して、ストレージ上の署名マテリアルを暗号化するためのキーを生成します。このパスワードは、ビルド時に署名マテリアルをビルドマシンにインポートするために使用されるため、忘れないようにしてください。

コマンドとその出力全体を次に示します:

 fastlane match import --type appstore

[] 🚀
[16:43:54]: Successfully loaded '~/amplify-ios-getting-started/code/fastlane/Matchfile' 📄

+-----------------------------------------------------+
| Detected Values from './fastlane/Matchfile'         |
+--------------+--------------------------------------+
| git_url.     | https://github.com/sebsto/secret.git |
| storage_mode | git                                  |
| type         | development                          |
+--------------+--------------------------------------+

[16:43:54]: Certificate (.cer) path:
./secrets/sebsto-apple-dist.cer
[16:44:07]: Private key (.p12) path:
./secrets/sebsto-apple-dist.p12
[16:44:12]: Provisioning profile (.mobileprovision or .provisionprofile) path or leave empty to skip
this file:
./secrets/amplifyiosgettingstarteddist.mobileprovision
[16:44:25]: Cloning remote git repo...
[16:44:25]: If cloning the repo takes too long, you can use the `clone_branch_directly` option in match.
[16:44:27]: Checking out branch master...
[16:44:27]: Enter the passphrase that should be used to encrypt/decrypt your certificates
[16:44:27]: This passphrase is specific per repository and will be stored in your local keychain
[16:44:27]: Make sure to remember the password, as you'll need it when you run match on a different machine
[16:44:27]: Passphrase for Match storage: ********
[16:44:30]: Type passphrase again: ********
security: SecKeychainAddInternetPassword <NULL>: The specified item already exists in the keychain.
[16:44:31]: 🔓 Successfully decrypted certificates repo
[16:44:31]: Repo is at: '/var/folders/14/nwpsn4b504gfp02_mrbyd2jr0000gr/T/d20250131-41830-z7b4ic'
[16:44:31]: Login to App Store Connect (sebsto@mac.com)
[16:44:33]: Enter the passphrase that should be used to encrypt/decrypt your certificates
[16:44:33]: This passphrase is specific per repository and will be stored in your local keychain
[16:44:33]: Make sure to remember the password, as you'll need it when you run match on a different machine
[16:44:33]: Passphrase for Match storage: ********
[16:44:37]: Type passphrase again: ********
security: SecKeychainAddInternetPassword <NULL>: The specified item already exists in the keychain.
[16:44:39]: 🔒 Successfully encrypted certificates repo
[16:44:39]: Pushing changes to remote git repo...
[16:44:40]: Finished uploading files to Git Repo [https://github.com/sebsto/secret.git]
Bash

Fastlane が署名マテリアルを Git リポジトリにインポートしたことを確認します。

Fastlane match - インポート後の GitHub リポジトリ

次のビルドでこれらの署名マテリアルを使用するようにローカルマシンを設定することもできます:

» fastlane match appstore 

[✔] 🚀 
[17:39:08]: Successfully loaded '~/amplify-ios-getting-started/code/fastlane/Matchfile' 📄

+-----------------------------------------------------+
|   Detected Values from './fastlane/Matchfile'       |
+--------------+--------------------------------------+
| git_url      | https://github.com/sebsto/secret.git |
| storage_mode | git                                  |
| type         | development                          |
+--------------+--------------------------------------+


+-------------------------------------------------------------------------------------------+
|                                 Summary for match 2.226.0                                 |
+----------------------------------------+--------------------------------------------------+
| type                                   | appstore                                         |
| readonly                               | false                                            |
| generate_apple_certs                   | true                                             |
| skip_provisioning_profiles             | false                                            |
| app_identifier                         | ["com.amazonaws.amplify.mobile.getting-started"] |
| username                               | xxxx@xxxxxxxxx                                   |
| team_id                                | XXXXXXXXXX                                       |
| storage_mode                           | git                                              |
| git_url                                | https://github.com/sebsto/secret.git             |
| git_branch                             | master                                           |
| shallow_clone                          | false                                            |
| clone_branch_directly                  | false                                            |
| skip_google_cloud_account_confirmation | false                                            |
| s3_skip_encryption                     | false                                            |
| gitlab_host                            | https://gitlab.com                               |
| keychain_name                          | login.keychain                                   |
| force                                  | false                                            |
| force_for_new_devices                  | false                                            |
| include_mac_in_profiles                | false                                            |
| include_all_certificates               | false                                            |
| force_for_new_certificates             | false                                            |
| skip_confirmation                      | false                                            |
| safe_remove_certs                      | false                                            |
| skip_docs                              | false                                            |
| platform                               | ios                                              |
| derive_catalyst_app_identifier         | false                                            |
| fail_on_name_taken                     | false                                            |
| skip_certificate_matching              | false                                            |
| skip_set_partition_list                | false                                            |
| force_legacy_encryption                | false                                            |
| verbose                                | false                                            |
+----------------------------------------+--------------------------------------------------+

[17:39:08]: Cloning remote git repo...
[17:39:08]: If cloning the repo takes too long, you can use the `clone_branch_directly` option in match.
[17:39:10]: Checking out branch master...
[17:39:10]: Enter the passphrase that should be used to encrypt/decrypt your certificates
[17:39:10]: This passphrase is specific per repository and will be stored in your local keychain
[17:39:10]: Make sure to remember the password, as you'll need it when you run match on a different machine
[17:39:10]: Passphrase for Match storage: ********
[17:39:13]: Type passphrase again: ********
security: SecKeychainAddInternetPassword <NULL>: The specified item already exists in the keychain.
[17:39:15]: 🔓  Successfully decrypted certificates repo
[17:39:15]: Verifying that the certificate and profile are still valid on the Dev Portal...
[17:39:17]: Installing certificate...

+-------------------------------------------------------------------------+
|                          Installed Certificate                          |
+-------------------+-----------------------------------------------------+
| User ID           | XXXXXXXXXX                                          |
| Common Name       | Apple Distribution: Sebastien Stormacq (XXXXXXXXXX) |
| Organisation Unit | XXXXXXXXXX                                          |
| Organisation      | Sebastien Stormacq                                  |
| Country           | US                                                  |
| Start Datetime    | 2024-10-29 09:55:43 UTC                             |
| End Datetime      | 2025-10-29 09:55:42 UTC                             |
+-------------------+-----------------------------------------------------+

[17:39:18]: Installing provisioning profile...

+-------------------------------------------------------------------------------------------------------------------+
|                                          Installed Provisioning Profile                                           |
+---------------------+----------------------------------------------+----------------------------------------------+
| Parameter           | Environment Variable                         | Value                                        |
+---------------------+----------------------------------------------+----------------------------------------------+
| App Identifier      |                                              | com.amazonaws.amplify.mobile.getting-starte  |
|                     |                                              | d                                            |
| Type                |                                              | appstore                                     |
| Platform            |                                              | ios                                          |
| Profile UUID        | sigh_com.amazonaws.amplify.mobile.getting-s  | 4e497882-d80f-4684-945a-8bfec1b310b9         |
|                     | tarted_appstore                              |                                              |
| Profile Name        | sigh_com.amazonaws.amplify.mobile.getting-s  | amplify-ios-getting-started-dist             |
|                     | tarted_appstore_profile-name                 |                                              |
| Profile Path        | sigh_com.amazonaws.amplify.mobile.getting-s  | /Users/stormacq/Library/MobileDevice/Provis  |
|                     | tarted_appstore_profile-path                 | ioning                                       |
|                     |                                              | Profiles/4e497882-d80f-4684-945a-8bfec1b310  |
|                     |                                              | b9.mobileprovision                           |
| Development Team ID | sigh_com.amazonaws.amplify.mobile.getting-s  | XXXXXXXXXX                                   |
|                     | tarted_appstore_team-id                      |                                              |
| Certificate Name    | sigh_com.amazonaws.amplify.mobile.getting-s  | Apple Distribution: Sebastien Stormacq       |
|                     | tarted_appstore_certificate-name             | (XXXXXXXXXX)                                 |
+---------------------+----------------------------------------------+----------------------------------------------+

[17:39:18]: All required keys, certificates and provisioning profiles are installed 🙌
Plain text

ステップ 2: プロジェクトに署名するように Fastlane を設定する

fastlane/Fastfile に Fastlane ビルド設定ファイルを作成します (fastlane init コマンドを使用して開始できます)。

default_platform(:ios)

platform :ios do
  before_all do
    setup_ci
  end

  desc "Build and Sign the binary"
  lane :build do
    match(type: "appstore", readonly: true)
    gym(
      scheme: "getting started",
      export_method: "app-store"
    )
  end
end
Ruby

match アクションが正しく機能するには、setup_ci アクションが Fastfilebefore_all セクションに追加されているようにしてください。このアクションは、適切な許可を持つ一時的な Fastlane キーチェーンを作成します。このステップを実行しないと、ビルドが失敗したり、一貫性のない結果が得られたりする可能性があります。

そして、ローカルビルドをコマンド fastlane build でテストします。キーと証明書をインポートする際に使用したパスワードを入力し、システムにプロジェクトのビルドと署名を実行させます。すべてが正しく設定されていると、同様の出力が生成されます。

...
[17:58:33]: Successfully exported and compressed dSYM file
[17:58:33]: Successfully exported and signed the ipa file:
[17:58:33]: ~/amplify-ios-getting-started/code/getting started.ipa

+---------------------------------------+
|           fastlane summary            |
+------+------------------+-------------+
| Step | Action           | Time (in s) |
+------+------------------+-------------+
| 1    | default_platform | 0           |
| 2    | setup_ci         | 0           |
| 3    | match            | 36          |
| 4    | gym              | 151         |
+------+------------------+-------------+

[17:58:33]: fastlane.tools finished successfully 🎉
Plain text

ステップ 3: Fastlane を利用するように CodeBuild を設定する

次に、CodeBuild でプロジェクトを作成します。それに役立つステップバイステップのガイドにはここでは立ち入りません。前回の記事または CodeBuild ドキュメントをご覧ください。

Fastlane 固有の設定は 1 つだけです。署名マテリアルにアクセスするには、Fastlane は環境変数として渡す 3 つのシークレットの値にアクセスする必要があります:

  • MATCH_PASSWORD: 署名マテリアルをインポートする際に入力するパスワード。Fastlane はこのパスワードを使用して、GitHub リポジトリ内の暗号化されたファイルを解読します
  • FASTLANE_SESSION: ~/.fastlane/spaceship/<your apple user id>/cookie にある Apple ID セッション Cookie の値。セッションの有効期間は数時間から数日間です。セッションの有効期限が切れたら、ノートパソコンからコマンド fastlane spaceauth を使用して再認証し、FASTLANE_SESSION の値を Cookie の新しい値で更新します。
  • MATCH_GIT_BASIC_AUTHORIZATION: GitHub ユーザー名の Base 64 エンコードで、コロンと個人認証トークン (PAT) が続きます。プライベート GitHub リポジトリにアクセスするためのものです。PAT は、GitHub コンソールの [プロファイル] > [設定] > [デベロッパーの設定] > [個人アクセストークン] で生成できます。この環境変数の値を生成するには、次のコマンドを使用します: echo -n my_git_username:my_git_pat | base64

これらの 3 つの値それぞれについて、AWS Secrets Manager のシークレットの Amazon リソースネーム (ARN) またはプレーンテキストの値を入力できることに留意してください。セキュリティ上重要な値を保存するには Secrets Manager を利用することを強くお勧めします

私はセキュリティを重視するユーザーなので、次のコマンドを使用して 3 つのシークレットを Secrets Manager に保存します:

aws --region $REGION secretsmanager create-secret --name /CodeBuild/MATCH_PASSWORD --secret-string MySuperSecretPassword
aws --region $REGION secretsmanager create-secret --name /CodeBuild/FASTLANE_SESSION --secret-string $(cat ~/.fastlane/spaceship/my_appleid_username/cookie)
aws --region $REGION secretsmanager create-secret --name /CodeBuild/MATCH_GIT_BASIC_AUTHORIZATION --secret-string $(echo -n my_git_username:my_git_pat | base64)

ビルドプロジェクトが Secrets Manager に保存されているシークレットを参照する場合、ビルドプロジェクトのサービスロールで secretsmanager:GetSecretValue アクションを許可する必要があります。プロジェクトの作成時に [新しいサービスロール] を選択した場合、CodeBuild はビルドプロジェクトのデフォルトのサービスロールにこのアクションを含めます。しかし、[既存のサービスロール] を選択した場合は、このアクションをサービスロールに別途含める必要があります。

このデモでは、次の AWS Identity and Access Management (IAM) ポリシーを使用します:

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Action": [
				"secretsmanager:GetSecretValue"
			],
			"Resource": [
				"arn:aws:secretsmanager:us-east-2:012345678912:secret:/CodeBuild/*"
			]
		}
	]
}
JSON

AWS マネジメントコンソールの CodeBuild セクションでプロジェクトを作成した後、3 つの環境変数を入力します。値は Secrets Manager のシークレットの名前であることに留意してください。

Codebuild - Fastlane match の環境変数

環境変数とその Secrets Manager シークレット名を buildpsec.yaml ファイルで定義することもできます。

次に、プロジェクトのルートにある buildspec.yaml ファイルを変更して、fastlane を使用してバイナリをビルドおよび署名します。私の buildspec.yaml ファイルは次のようになりました:

# buildspec.yml
version: 0.2
phases:
  install:
    commands:
      - code/ci_actions/00_install_rosetta.sh
  pre_build:
    commands:
      - code/ci_actions/02_amplify.sh
  build:
    commands:
      - (cd code && fastlane build)
artifacts:
  name: getting-started-$(date +%Y-%m-%d).ipa
  files:
    - 'getting started.ipa'
  base-directory: 'code'
YAML

バックエンドの Amplify 設定を受け取るには、Rosetta スクリプトと Amplify スクリプトが必要です。プロジェクトで AWS Amplify を利用しない場合、これらは必要ありません。

ビルドファイルには、署名キーをダウンロードしたり、ビルド環境でキーチェーンを準備したりするものがないことに留意してください。fastlane match がそれを実行してくれます。

新しい buildspec.yaml ファイルと ./fastlane ディレクトリを Git に追加します。これらのファイルをコミットしてプッシュします。git commit -m "add fastlane support" && git push

すべてがうまくいけば、CodeBuild でビルドが実行され、「成功」メッセージが表示されます。

Codebuild - 「成功」メッセージ

料金と利用可能なリージョン
CodeBuild for macOS が利用可能なすべてのリージョンにおいて、CodeBuild が使用するすべての macOS イメージに、追加料金なしで Fastlane がプリインストールされるようになりました。この記事の執筆時点では、これらのリージョンは、米国東部 (オハイオ、バージニア北部)、米国西部 (オレゴン)、アジアパシフィック (シドニー)、および欧州 (フランクフルト) です。

私の経験では、fastlane match を正しく設定するには少し時間がかかります。設定が完了したら、CodeBuild で動作させるのは非常に簡単です。これを CodeBuild で試す前に、ローカルマシンで動作することを確認してください。CodeBuild で問題が発生した場合は、環境変数の値を三重に確認し、CodeBuild が AWS Secrets Manager のシークレットにアクセスできることを確認してください。

さあ、(macOS で) ビルドしましょう!

原文はこちらです。