AWS Amplifyを使ったReact NativeなアプリをBitriseでビルド

はじめに

今回、React Nativeで開発しているとあるアプリでAWS Amplifyを使ったのですがBitriseでビルドしたりチーム開発で使うにあたってちょっと困ったりしたのでメモ。

前提

もともと自分は以前にこちらの記事でも書いたようにAWS Amplifyについてはそんなにポジティブではないです。なのでこの記事以降も基本的にはあまりwatchしてきませんでした。なので今回ちょっと苦労したのはAmplify弱者だったことも理由かも知れない。

www.keisuke69.net

ただし、Amplify Consoleはいいぞ。これはプロダクションでも2,3使ってる。

また、React Nativeで開発したアプリはAndroidiOSともにBitriseを使ってビルドをしています。これはリポジトリの特定ブランチにpushされたことをトリガーに毎回自動的に実行され、常に最新のビルドが確認可能という状況にしています。 なお、App Storeへの配布まではまだ自動化していないです。

なぜAmplifyを使ったか

先の自分のブログ記事にもあるように基本的にAmplifyはAppSyncとの組み合わせ以外で積極的に使う理由はないと思っています。

ではなぜ今回AWS Amplifyを使ったかというとS3に対してマルチパートアップロードを実装するにあたり、素のAWS SDKを使って実装するより楽に実装できそうだったからです。具体的にはこのあたりに書かれています。JavaScriptの場合です。

docs.aws.amazon.com

マルチパートアップロードを行おうと思うといろんなお作法が必要になってくるんですね。さらに一時停止とか再開とかそういうのも。

それがAWS Amplifyではユーティリティが用意されていてかんたんに実装できるんですね。このあたりの話です。

aws.amazon.com

この発表はAmplify for JavaScriptとなっていますが、React Nativeでも使えます。ドキュメントはこのあたり。

https://docs.amplify.aws/sdk/storage/transfer-utility/q/platform/ios/#upload-a-file

圧倒的に楽そう。ということでAWS Amplifyを使うことにしたのです。

Bitriseでビルドする

AWS Amplifyを使った実装自体はそんなにこれといった話はないのですが、いざある程度出来上がったのでリポジトリにpushしてBitriseでのビルドパイプラインに載せます。

そうすると、aws-exports.jsというファイルがなくてビルドでエラーになります。実はこれはBitriseだけの話ではなく、Amplifyを初期セットアップしたときのままGitとかで管理していて他の環境でcloneしたときにも同様の問題はおきます。

原因はこのaws-exports.jsというファイルが.gitignoreに追加されていてコミットされていないからです。

ではこのファイルは何なのかというと、AmplifyのEnvごとのAWSリソースのリソース名とかが記述されたファイルです。つまり、アップロード先のS3バケットの名前とかが書かれている。ではなぜこのファイルが標準では.gitignoreに追加されていてコミットされないようになっているのか。当初、リソース名が記載されている、つまりクレデンシャル自体の記述はないもののいわゆる秘匿情報みたいなものとして扱われていて事故防止のためかと思いました。

ですが、同じように利用するAWSのリソース名のような情報が記載されているamplify/team-provider-info.json.gitignoreに含まれていません。どうやら、AmplifyにはenvというGitのブランチのような機能があり、aws-exports.jsはこのenvが切り替わるたびに中身が自動的に書き換えられるみたいなんですね。だからコミットの対象外にされているだけのようです。

というわけで今回の問題の対処はリポジトリには含まれていないにも関わらずソースコード内からは参照されるaws-exports.jsをcloneしたあとにどうやって生成するかになります。

ではどう対応するか

Amplifyをチーム開発する場合にどう使っていけばいいのかの知見がなくて、このあたりよくわかっていませんでした。またaws-exports.jsenvの内容が記載されているということで当然ながら開発時と本番では接続するAWSアカウントも違えばAWSリソースも異なるのでその切替も必要になるのでどうしたものかと悩んでたものの、Twitterで聞いたところいろんなアイデアが得られました。

いくつかやり取りした結果、aws-exports.jsの中身が単なるAWSリソース情報がベタ書きされていて、オブジェクトをexportしているだけのものであるということから同様の値を環境変数で設定し、それを読み取って自分でオブジェクトとして組み立ててAmplify.configure()に渡す形で実装を変更しようかなと考えました。これなら開発と本番(≒Bitriseでビルドしたもの)で切り替えるのも容易かなと思ったわけです。

とはいえ、Amplifyのためにこんな実装入れるのもなーとか思ったり、セットする環境変数の数も多いしなーということでなかなか前向きではなかったんですね。

ここでそもそもチーム開発しているときにこの辺どうやればいいのかを調べたところ、どうやらリポジトリをcloneしてamplify pullをすればこのあたりのファイルも生成されるということがわかりました。

ですが、これをやるにはAWSのクレデンシャルやらが必要なのでamplify initやらをしなければいけないのかな?と思っていたところどうやらAmplify CLIのヘッドレスモードだと大丈夫そうということでこの方向でやることにします。

Bitriseでどうやるか

とりあえずリポジトリをcloneしたらAmplify CLIを使ってpullすればいいということでこれをBitriseのWorkflow上で実現していきます。

まず、その前にプロジェクト上で開発と本番それぞれのenvを作成しておきます。自分の場合、もともと開発初期のセットアップ時にdevというenvを用意していてこれが開発用のAWSアカウントに紐付いていました。AWSリソースもこの開発用AWSアカウント上で作られています。

なので、まずは本番環境用のenvとしてprodというものを作っておきます。このときAWSアカウントごとわけられるのか気になったのですが、結果的には問題なくできているようです。実際にはamplify env add prodをするときにAWSアカウントを聞かれたので本番環境のAWSのアカウントで作成したクレデンシャル情報(Acesss KeyとSecret Access Key)を指定することができたんですね。で、envを作成したらamplify pushをして反映しておきます。実際に本番環境のAWSアカウント上に各種リソースが作成されたことが確認できました。

というわけでようやくここからBitriseの設定をしていきます。

実際にはWorkflowでビルドのステップの前にscriptのステップを追加し、ここでenvの情報をpullするという処理を入れます。というわけで実際に入れた処理がこちら。

#!/usr/bin/env bash
set -e
set -x

npm install -g @aws-amplify/cli

AMPLIFY="{\
\"appId\":\"<appId>\",\
\"envName\":\"prod\",\
\"defaultEditor\":\"code\"\
}"

AWSCLOUDFORMATIONCONFIG="{\
\"configLevel\":\"project\",\
\"useProfile\":false,\
\"accessKeyId\":\"<AWS_ACCESS_KEY_ID>\",\
\"secretAccessKey\":\"<AWS_SECRET_ACCESS_KEY_ID>\",\
\"region\":\"ap-northeast-1\"\
}"

PROVIDERS="{\
\"awscloudformation\":$AWSCLOUDFORMATIONCONFIG\
}"

REACTNATIVECONFIG="{\
\"SourceDir\":\"src\",\
\"DistributionDir\":\"build\",\
\"BuildCommand\":\"yarn build\",\
\"StartCommand\":\"yarn start\"\
}"

FRONTEND="{\
\"frontend\":\"javascript\",\
\"framework\":\"react-native\",\
\"config\":$REACTNATIVECONFIG\
}"

amplify pull --amplify $AMPLIFY --frontend $fRONTEND --providers $PROVIDERS --yes

冒頭でAmplify CLIをBitriseの環境にインストールしています。ちなみにyarnでAmplify CLIをインストールしようとすると変なエラーに悩まされることが多いのでnpmで入れたほうが無難。自分はこれで数時間溶かしました。

各値は各々の環境の値で置き換えてください。

補足すると、AMPLIFY内で指定しているappIdはAmplifyのプロジェクトのappIdAWSのマネージメントコンソールやteam-provider-info.jsonに記載があるのでそちらを指定。PROVIDERおよびAWSCLOUDFORMATIONCONFIGで指定してるaccessKeyIdsecretAccessKeyIdAWSアカウントのクレデンシャル情報ですね。あとはFRONTENDのところで今回はReact Nativeのプロジェクトなのでframeworkreact-nativeと指定しています。ただ、このあたりはドキュメント読んでも正直よくわからない。

最後のamplify pullコマンドで上で指定した値をすべてパラメータとして渡した上で--yesというオプションを付与して実行します。この--yesをつけることでプロンプトが表示されなくなるというものです。

とはいえ、このパラメータはちょっと面倒すぎるしもうちょっと説明が欲しいところ。面倒くさいぞAmplify CLI!

このscriptステップをBitriseのWorkflow上の適当な位置に追加します。自分はこんな感じでCocoapodのインストールが終わったあとに追加しましたがXCodeでのArchiveより手前であればどこでも大丈夫かと思います。

f:id:Keisuke69:20220209103713p:plain

まとめ

というわけでAWS Amplifyを使ったアプリをBitriseのようなCI/CD環境でビルドしようとするとそのままだとaws-exports.jsが存在しないためビルドに失敗します。そのため、ビルド前にamplify pullをして環境にあったaws-exports.jsを生成する必要があります。今回これはCI/CD環境でのビルドの話として書いたけど、これはチーム開発で別の人がリポジトリをcloneしたときに必ず発生する問題かと思います。

率直な感想としてやっぱりAmplifyは面倒だなと感じました。簡単にしようとして余計なことしてくれてる感じ。やっぱりそんなに便利には感じないな。

今回使った理由がS3のマルチパートアップロードのユーティリティを使いたいがためだけというものだったので余計にそう感じました。というかこのユーティリティをAWS SDK自体で用意してほしい。

©Keisuke Nishitani, 2020   プライバシーポリシー