はじめに
今回、React Nativeで開発しているとあるアプリでAWS Amplifyを使ったのですがBitriseでビルドしたりチーム開発で使うにあたってちょっと困ったりしたのでメモ。
前提
もともと自分は以前にこちらの記事でも書いたようにAWS Amplifyについてはそんなにポジティブではないです。なのでこの記事以降も基本的にはあまりwatchしてきませんでした。なので今回ちょっと苦労したのはAmplify弱者だったことも理由かも知れない。
ただし、Amplify Consoleはいいぞ。これはプロダクションでも2,3使ってる。
また、React Nativeで開発したアプリはAndroid、iOSともにBitriseを使ってビルドをしています。これはリポジトリの特定ブランチにpushされたことをトリガーに毎回自動的に実行され、常に最新のビルドが確認可能という状況にしています。 なお、App Storeへの配布まではまだ自動化していないです。
なぜAmplifyを使ったか
先の自分のブログ記事にもあるように基本的にAmplifyはAppSyncとの組み合わせ以外で積極的に使う理由はないと思っています。
ではなぜ今回AWS Amplifyを使ったかというとS3に対してマルチパートアップロードを実装するにあたり、素のAWS SDKを使って実装するより楽に実装できそうだったからです。具体的にはこのあたりに書かれています。JavaScriptの場合です。
マルチパートアップロードを行おうと思うといろんなお作法が必要になってくるんですね。さらに一時停止とか再開とかそういうのも。
それがAWS Amplifyではユーティリティが用意されていてかんたんに実装できるんですね。このあたりの話です。
この発表は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.js
がenv
の内容が記載されているということで当然ながら開発時と本番では接続する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のプロジェクトのappId
でAWSのマネージメントコンソールやteam-provider-info.json
に記載があるのでそちらを指定。PROVIDER
およびAWSCLOUDFORMATIONCONFIG
で指定してるaccessKeyId
とsecretAccessKeyId
はAWSアカウントのクレデンシャル情報ですね。あとはFRONTEND
のところで今回はReact Nativeのプロジェクトなのでframework
をreact-native
と指定しています。ただ、このあたりはドキュメント読んでも正直よくわからない。
最後のamplify pull
コマンドで上で指定した値をすべてパラメータとして渡した上で--yes
というオプションを付与して実行します。この--yes
をつけることでプロンプトが表示されなくなるというものです。
とはいえ、このパラメータはちょっと面倒すぎるしもうちょっと説明が欲しいところ。面倒くさいぞAmplify CLI!
このscript
ステップをBitriseのWorkflow上の適当な位置に追加します。自分はこんな感じでCocoapod
のインストールが終わったあとに追加しましたがXCodeでのArchiveより手前であればどこでも大丈夫かと思います。
まとめ
というわけでAWS Amplifyを使ったアプリをBitriseのようなCI/CD環境でビルドしようとするとそのままだとaws-exports.js
が存在しないためビルドに失敗します。そのため、ビルド前にamplify pull
をして環境にあったaws-exports.js
を生成する必要があります。今回これはCI/CD環境でのビルドの話として書いたけど、これはチーム開発で別の人がリポジトリをcloneしたときに必ず発生する問題かと思います。
率直な感想としてやっぱりAmplifyは面倒だなと感じました。簡単にしようとして余計なことしてくれてる感じ。やっぱりそんなに便利には感じないな。
今回使った理由がS3のマルチパートアップロードのユーティリティを使いたいがためだけというものだったので余計にそう感じました。というかこのユーティリティをAWS SDK自体で用意してほしい。