shuhelohelo’s blog

Xamarin.Forms多めです.

App Center ビルドスクリプト

前回の記事でApp Centerを使ってPushのタイミングで自動ビルド(releaseビルド)まで行いました.

しかし,Syncfusionのライセンスキーをコード内に記述していないため,アプリ起動後に何も表示されずにアプリを利用できませんでした.

ソースコードに直接記述すれば動きはしますが,そのままGitHubなどにPushしてしまうとライセンスキーが公開されてしまうリスクが生じます.

ライセンスキーやDBへの接続文字列,APIキーなどの機密情報はソースコード内に記述したくはありません.

モバイルアプリ開発における解決方法の1つは,App Centerでビルドが実行される前にこれらの機密情報をソースコードに差し込む処理を自動で行われるようにする,というものです.

例えば,Covid19Radarであれば,settings.jsonファイルにこれらの情報をまとめて記述しておき,実行時に取り出すようになっていますが,ソースコード上では以下のように実際のキーは記述されていません.APP_VERSIONAPI_SECRET,ANDROID_SAFETYNETKEYといった部分がそれです.

{
  "appVersion": "APP_VERSION",
  "apiSecret": "API_SECRET",
  "apiUrlBase": "https://API_URL_BASE/api",
  "supportedRegions": "440",
  "blobStorageContainerName": "c19r",
  "androidSafetyNetApiKey": "ANDROID_SAFETYNETKEY",
  "cdnUrlBase": "https://CDN_URL_BASE/",
  "licenseUrl": "https://covid19radarjpnprod.z11.web.core.windows.net/license.html",
  "appStoreUrl": "https://itunes.apple.com/jp/app/id1516764458?mt=8",
  "googlePlayUrl": "https://play.google.com/store/apps/details?id=jp.go.mhlw.covid19radar",
  "supportEmail": "SUPPORT_EMAIL"
}

また,TurnipTracker(あつまれ動物の森のカブ情報をプレーヤー間で共有するためのアプリ)ではApp.xaml.csファイル内にまとめられています.

        public const string GetFriendsKey = "AC_GetFriendsKey";
        public const string GetFriendRequestsKey = "AC_GetFriendRequestsKey";
        public const string GetFriendRequestCountKey = "AC_GetFriendRequestCountKey";
        public const string PostRemoveFriendRequestKey = "AC_PostRemoveFriendRequestKey";
        public const string PostApproveFriendRequestKey = "AC_PostApproveFriendRequestKey";
        public const string DeleteRemoveFriendKey = "AC_DeleteRemoveFriendKey";
        public const string PostSubmitFriendRequestKey = "AC_PostSubmitFriendRequestKey";
        public const string PutUpdateProfileKey = "AC_PutUpdateProfileKey";
        public const string PutUpdateTurnipPricesKey = "AC_PutUpdateTurnipPricesKey";
        public const string PostCreateProfileKey = "AC_PostCreateProfileKey";

        public static bool IsStore => "AC_IsStore" == "true" ? true : false;

        const string AppCenteriOS = "AC_IOS";
        const string AppCenterAndroid = "AC_ANDROID";
        const string AppCenterUWP = "AC_UWP";
        const string SyncFusionKey = "AC_SYNC";

このように,ソースコードでは実際の機密情報を記述せずに,「置換の目印となる文字列」を記述しておき,App Centerでビルド前にこれらの文字列を機密情報に置換するという方法をとっています.

App Centerではこのようにビルド前(後)などに何らかの処理を行うスクリプトを実行することができます.

https://montemagno.com/vs-app-center-custom-build-scripts-for-production-apps/amp/?__twitter_impression=true

こちらの動画はTurnipTrackerの実装についてJames Montemagnoさんが紹介しているものですが,その46分39秒あたりでスクリプトを使った仕組みについて触れています.

youtu.be

長くなりましたが,今回はSyncfusionのライセンスキーをビルドスクリプトを使ってビルド時に差し込む方法についてです.

Syncfusionのライセンスキーの取得についてはSyncfusion公式サイトや以前の記事が参考になるかと思います.

JSONファイルの読み込み

やり方はなんでも良いですが,基本的には対象のJSONファイル(appsettings.jsonなど)をEmbedded resourceに設定して,Assemblyから文字列(もしくはstream)として読み込むのが簡単かと思います.

例えばCovid19Radarだと以下のように読み込んでいます.

そして読み込んだJSONをパースして必要な情報を取得していくといった感じです.

            var assembly = Assembly.GetExecutingAssembly();

            using var file = assembly.GetManifestResourceStream("Covid19Radar.settings.json");
            using var sr = new StreamReader(file);
            var json = sr.ReadToEnd();
            var j = JObject.Parse(json);

            AppVersion = j.Value<string>("appVersion");
            LicenseUrl = j.Value<string>("licenseUrl");
            AppStoreUrl = j.Value<string>("appStoreUrl");
            GooglePlayUrl = j.Value<string>("googlePlayUrl");
            ApiUrlBase = j.Value<string>("apiUrlBase");
            ApiSecret = j.Value<string>("apiSecret");
            CdnUrlBase = j.Value<string>("cdnUrlBase");
            BlobStorageContainerName = j.Value<string>("blobStorageContainerName");
            SupportedRegions = j.Value<string>("supportedRegions").ToUpperInvariant().Split(';', ',', ':');
            AndroidSafetyNetApiKey = j.Value<string>("androidSafetyNetApiKey");
            SupportEmail = j.Value<string>("supportEmail");

もしくはGeneric Hostを使ってASP.NET Coreのような設定,DIの仕組みを使う場合は以下のような方法でJSONの読み込みとデータの取得をすることもできます.

shuhelohelo.hatenablog.com

appsettings.jsonの編集

appsettings.jsonファイルを用意し,以下のようにします.

{
  "SyncfusionLicenceKey": "SYNCFUSION_LICENCE_KEY"
}

App Center側でこのSYNCFUSION_LICENCE_KEYという文字列を本物のライセンスキーに置換するようにします.

念の為,"SyncfusionLicenceKey"をキーにして値を取得できるか確認してみます.ここではIConfigurationを使った方法でやっています.

f:id:shuhelohelo:20200704220059p:plain

取得できています.

App Centerのビルドスクリプトの作成

次にApp Center側でビルド前に実行するスクリプトを作成します.

montemagno.com

docs.microsoft.com

ビルド前後の3つのタイミングで実行されるスクリプトをそれぞれ作成してリポジトリに含んでおくことで,App Centerでのビルド時にそれらが実行されます.

これらはbashスクリプトで名前が決まっています.

以下のタイミングでそれぞれ実行されます.

  • Post-clone(cloneの後) : appcenter-post-clone.sh -> リポジトリがcloneされた直後.

  • Pre-build(ビルド前) : appcenter-pre-build.sh -> ビルドの直前.

  • Post-build(ビルド後) : appcenter-post-build.sh ->ビルドの直後.

これらのスクリプトは用意されているビルトインの変数だけでなく,任意の環境変数を作成して利用することができたり,基本的には何でもできます.

これらのファイルをソリューション,プロジェクトのどこに置くのかというと,ソリューションファイル(sln)やプロジェクトファイル(csproj)と同じ階層ということです.

Place the scripts with the format specified below next to the project-level (.xcodeproj, .csproj, .sln, or package.json) file or module-level (build.gradle) file that you've selected in the build configuration and we'll run them as custom build steps.

Androidプロジェクトの直下にappcenter-post-clone.shを配置しました.

f:id:shuhelohelo:20200705102128p:plain

TurnipTrackerのビルドスクリプトを参考にして以下のように一箇所だけ置換するスクリプトを書きました.

#! /usr/bin/env bash

echo "Variables:"

APP_SETTINGS_FILE=$BUILD_REPOSITORY_LOCALPATH/XFMyDecode2020/XFMyDecode2020/appsettings.json

sed -i '' "s|SYNCFUSION_LICENCE_KEY|$SYNCFUSION_LICENCE_KEY|g" $APP_SETTINGS_FILE

# print out for reference
cat $APP_SETTINGS_FILE

$BUILD_REPOSITORY_LOCALPATHはビルトインの環境変数(?)なのでしょうか,どうやらこのリポジトリのルートフォルダのパスを表すようです.

sedコマンドで,指定したファイル(appsettings.json)内のSYNCFUSION_LICENCE_KEYという文字列を環境変数SYNCFUSION_LICENCE_KEYの値で置換する,という内容です.

あと,確認のために置換後にappsettings.jsonの内容を出力するようにしてあります.

App Centerに環境変数を追加する

App Centerを開き,以下のようにメニューのBuildを開きます.

f:id:shuhelohelo:20200705025607p:plain

右上に🔧のようなアイコンがあるので,これをクリックすると,ビルドの設定画面が開きます.

f:id:shuhelohelo:20200705030040p:plain

ここで,Environment varialesをOnにして,SYNCFUSION_LICENCE_KEYをキー,Syncfusionのライセンスキーを値とするペアを登録します.

f:id:shuhelohelo:20200705093632p:plain

Saveボタンを押して変更を保存します.

ちなみにこの時点でBuild scriptsNoneになっています.まだPushしてないので.

f:id:shuhelohelo:20200705030145p:plain

それではここまでの変更をGitHubにプッシュします.

f:id:shuhelohelo:20200705030323p:plain

プッシュの最中にビルドの設定画面を開くと,Build scriptsPost-cloneが表示されていました.ビルドスクリプトは認識されたようです.

f:id:shuhelohelo:20200705034314p:plain

ビルドは成功しましたが,ビルドスクリプトは実行されませんでした.

どうやら,

ということのようです.

このように,ビルドスクリプトを追加してpushした後,App Center側で設定を「保存」しなおす必要がありました.

なので,ビルドスクリプト追加後の1回目のビルドはすぐにキャンセルしてしまえばよいと思います.