APKファイルとIPAファイルのバージョン情報をコマンドラインから確認する
apkファイルとipaファイルのバージョン情報をコマンドラインで確認する方法を備忘録として残しておきます。
apkとipaのバージョン情報について
apkファイル及びipaファイルのバージョン情報は、ストア申請のときのバージョニング情報として利用されるものです。それぞれ公式に詳細が記載されています。
両OSともにx.y.z
というバージョンの文字列と、バージョンの一意性を表す数字が定義されています。微妙に呼び名が異なるので注意が必要です。
OS | バージョン名 | ビルド番号 |
---|---|---|
Android | versionName | versionCode |
iOS | version number | build string |
versionNameとversin numberがストアで表示されるときのバージョン番号ですね。
apkのversionNameとversionCodeをコマンドラインから確認する
apkのバージョン番号とビルド番号を確認するのは、Android Studioでapk analyzerにかければすぐにわかりますが、ここではコマンドラインを使う方法を紹介します。
コマンドとしてはAndroid SDKに含まれるaapt2
コマンドを使うことでAndroidManifest.xmlの情報が取得できます。それをgrepして加工してあげればほしい情報が出力できます。
$ aapt2 dump badging sample.apk | grep "versionName" | sed -e "s/.*versionName='//" -e "s/' .*//" 1.2.3 $ aapt2 dump badging sample.apk | grep "versionCode" | sed -e "s/.*versionCode='//" -e "s/' .*//" 100
ipaのversion numberとbuild stringをコマンドラインから確認する
ipaについてはBitriseが公開しているipa_analyzer
を使うのが便利です。
ipaのバージョン情報・ビルド番号はipaが持つinfo.plist
の中のCFBundleShortVersionString
およびCFBundleVersion
に記載されています。
ipa_analyzer
を使うとinfo.plist
の値をjson形式で情報を出力してくれるので、jqコマンドでフィルターをかければよいでしょう。
$ ipa_analyzer -i sample.ipa -p --info-plist --prov | jq -r '.info_plist | .content | .CFBundleShortVersionString' 1.2.3 $ ipa_analyzer -i sample.ipa -p --info-plist --prov | jq -r '.info_plist | .content | .CFBundleVersion' 100
CIマシンでバージョニングを自動化するときに、正しくバージョニングできているかを上記のコマンドを使って確認するとよいでしょう。
Flutterで使うAPI関連のDartコードをSwagger/OpenAPIで自動生成する
はじめに
Flutterの開発の中でSwagger/OpenAPIのYamlからコードを自動生成して使うことがあったのでまとめておきます。
Swagger/OpenAPIとは
OpenAPIはREST APIの仕様を記述するフォーマットで、yaml形式で仕様を定義することができます。
2.0系と3.0系があって、3.0が出てからしばらく立ちましたが、API Gatewayはまだ2.0系のみサポートしているところが多い印象です。
SwaggerとOpenAPIの関係は少しややこしいようで、加えて2.0系と3.0系で仕様に差分があるため、ここでは2.0系の形式を扱うこととします。
Swagger/OpenAPIには以下の3つのツールがあります。
今回はSwagger/OpenAPIのyamlを使ってAPI仕様書を見る手順と、Flutterで使うDartのコードを生成する手順をまとめます。
Swagger UIでAPI仕様書を見る
Swagger UIはDocker Imageが用意されているので、yamlを連携してあげれば簡単に起動することができます。
API_URL
でデフォルトで読み込むyamlを指定できるので、マウントした領域を読み込んだり、ネットワーク越しにしていすればよいでしょう。
$ docker run -d -p 81:8080 -v $HOME/git/swagger-dart-client/:/usr/share/nginx/html/yaml \ -e API_URL=yaml/petstore.yaml swaggerapi/swagger-ui
http://localhost:81
にアクセスすれば確認できます。
Swagger CodegenでDartのコードを自動生成する
FlutterでAPIアクセスをして画面を描画する時、APIのモデルデータの一式を定義するのは大変です。
PHPやJavaScriptであれば単なるArrayのMapで処理するだけなのですが、JavaやDartではきっちり型定義が必要なのでList型や入れ子のJSONを含む複雑なレスポンスを返すAPIの場合は定義がとても面倒です。
加えて、特にフロントエンドとバックエンドが並行して新規開発するケースでは、やっとの思いでモデルクラスを定義しきれたとしてもAPIの仕様が流動的なため頻繁な変更が入ります。Typoでミスが入り、バックエンドと繋ぐときにIFの不整合が多発する要因にもなります。
そこでSwagger/OpenAPIのyamlで仕様を定義し、クライアントコードを自動生成するという戦略をとることでその手間やリスクを減らすことがことできます。
ツールをインストールする
コードの自動生成ツールをは、Swagger/OpenAPIそれぞれ用意されているのですが今回はOpenAPIの方を利用します。
インストールは公式のガイドに従っていけばよいです。
npm、homebrew、docker、jar、bashの5種類用意されています。
それぞれが利用しやすいものを選べばよいと思いますが、今回はjarを利用することにします。
API定義のYamlを記述する
まず YamlはAPIの仕様の肝となり、プロダクションで利用するコードの自動生成にも利用するものなので、フロントエンドとバックエンドをつなぐ重要な設計成果物であることをお互いに認識しましょう。
これが非常に重要です。
「ファイルサーバのExcelの方が最新です」ということにならないように、Gitのレポジトリで管理され、かつバージョニングされていることが望ましいです。
Yamlの記述についてはSwagger Editorを使えばSyntaxを確認できますが、yamlが巨大になってくるとブラウザが悲鳴を上げてくるのでcliで実行するとよいでしょう。
$ java -jar openapi-generator-cli.jar validate -i petstore.yaml Validating spec (petstore.yaml) No validation issues detected.
Yamlを手動で記述するのはなかなか大変なので、記述しやすい別の設計ドキュメントから自動生成するようにしてもいいかもしれませんね(何とはいいません🙊🙊🙊)。
個人的にはバックエンドのソースコードからYamlを生成してもらえると実装と設計の一意性がより担保できると思ってます。
YamlからDartのコードを自動生成する
Yamlが出来上がったらあとはクライアントコードの自動生成をするだけです。
Flutterはdartで記述するのでdartのクライアントコードを生成するのですが、GeneratorのREADMEを読むとdart
とdart2
とあります。
Flutterが利用するdartのバージョンは2.x系なのでdart2を利用すればいいのねと思ってコマンドを実行したら
$ java -jar openapi-generator-cli.jar generate -i petstore.yaml -g dart2 -o build Can't load config class with name 'dart2' ... [error] Check the spelling of the generator's name and try again.
となってエラーで失敗しました。まだコマンドラインツール上ではdart2は対応していないようです。
githubを見に行くとdart2用のテンプレートは用意されているようなのでテンプレートを指定する形で利用すればよさそうです。
テンプレートは-t
オプションで指定することができます。
$ java -jar openapi-generator-cli.jar generate -i petstore.yaml -g dart -t template/dart2 -o build [main] INFO o.o.codegen.DefaultGenerator - Generating with dryRun=false [main] INFO o.o.c.ignore.CodegenIgnoreProcessor - No .openapi-generator-ignore file found. [main] INFO o.o.codegen.DefaultGenerator - OpenAPI Generator: dart (client) [main] INFO o.o.codegen.DefaultGenerator - Generator 'dart' is considered stable. [main] INFO o.o.c.languages.DartClientCodegen - Environment variable DART_POST_PROCESS_FILE not defined so the Dart code may not be properly formatted. To define it, try `export DART_POST_PROCESS_FILE="/usr/local/bin/dartfmt -w"` (Linux/Mac) [main] INFO o.o.c.languages.DartClientCodegen - NOTE: To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI). [main] INFO o.o.c.languages.DartClientCodegen - Dart version: 2.x [main] INFO o.o.codegen.DefaultGenerator - Model Pets not generated since it's an alias to array (without property) and `generateAliasAsModel` is set to false (default) [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build/lib/model/error.dart [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build/test/error_test.dart [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build/doc/Error.md [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build/lib/model/pet.dart [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build/test/pet_test.dart [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build/doc/Pet.md [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build/lib/api/pets_api.dart [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build/test/pets_api_test.dart [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build/doc/PetsApi.md [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build/pubspec.yaml [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build//lib/api_client.dart [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build//lib/api_exception.dart [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build//lib/api_helper.dart [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build//lib/api.dart [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build//lib/auth/authentication.dart [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build//lib/auth/http_basic_auth.dart [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build//lib/auth/api_key_auth.dart [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build//lib/auth/oauth.dart [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build/git_push.sh [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build/.gitignore [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build/README.md [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build/.travis.yml [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build/.openapi-generator-ignore [main] INFO o.o.codegen.AbstractGenerator - writing file /Users/takami228/git/swagger-dart-client/build/.openapi-generator/VERSION
生成コードをカスタマイズする
OpenAPIを使ったコード生成では何も指定しないとAPIクライアント、ドキュメント、テストコードなどが一式生成されます。
モデルクラスのみあればよいというケースでは、オプションを設定することで自動生成をスキップできます。
$ java -jar openapi-generator-cli.jar -DbrowserClient=false -DapiTests=false -DmodelTests=false \ generate -i petstore.yaml -g dart -t template/dart2 -o build
詳細は Selective Generation を見るとよいでしょう。
また自動生成されたコードがDartのLintルールに沿ってない場合は、テンプレートのmustacheファイルをいじってルールに沿うように修正するとよいです。もしテンプレートをいじる場合は忘れずにテンプレートファイルをバージョン管理しましょう。
一式の生成の流れがまとまったらレポジトリにまとめたり、API仕様書が変更されたときにFlutterのプロジェクトへどう修正を取り込むのかのパイプラインを考えるとよいでしょう。
Flutterの場合はpubspec.yaml経由でバージョンを指定してimportすることもできるようです。やや古いですが、こちらの記事が参考になります。
- OpenAPI Generator + golang + Flutter でアプリ開発 - ryuichi111stdの技術日記
- flutter + golang + openAPIで雑アプリ開発 - Qiita
まとめ
以上がSwagger/OpenAPIのYamlからコードを生成する流れになります。githubにサンプルもまとめておきました。
頻繁に変更されるAPI定義のモデルクラスの修正は、単純でありつつもミスがゆるされない作業になるため、自動生成の仕組みを上手く利用して効率化するとよいでしょう。
おまけ
Swaggerを使ったコードやドキュメントの自動生成の話はint128先生のJavaのSpringを使った例も参考になるので見てみてください。
Swaggerのテンプレートを魔改造した話 / Customize Swagger Templates - Speaker Deck
Visual Studio App Centerを使って内部向けに継続的にネイティブアプリを配信する(※追記あり)
はじめに
ネイティブアプリの内部テストを円滑にすすめるために、Visual Studio App Centerを使ってアプリを配信する仕組みを触る機会があったので手順をまとめておきます。
Visual Studio App Centerとは
Microsoftが提供しているネイティ・デスクトップアプリ開発向けの開発プラットフォームです。Azure DevOpsとの連携やアプリのデバイス上でのテスト実行、ストア配信などもサポートしており、ビルドやテストの実行に一定時間の無料枠があります。
今回はビルド済みのAndroidアプリおよびiOSアプリをテスト端末へ配信する手順をまとめます。
1. Visual Studio App Centerの準備をする
アカウントとプロジェクトを作成する
Visual Studio App Centerは以下のID連携を使ってアカウントを作成することができます。
以前はメールアドレスでのアカウント作成もできたようなのですが、今は上記のアカウントからしか利用できないようです。メールアドレスで作成済みのユーザはメールアドレスにてログインできるようです。
今回はGoogleアカウントで作成します。作成するときにOwner Nameを定義することができ、URLに利用されます。
ログインでの認証でIP制限を用いたいケースは、アカウント管理をAzureのIDアカウント管理に任せ、認証画面にFirewallを設定するとよさそうです。
ログインが完了したら、次にアプリのプロジェクトを作成します。
Android用のプロジェクトは右のAdd New appから作成できます。
今回はBetaとしました。他にも配信中のアプリや内部テストといったラベルがつけられるようです。
iOSも同様に作成します。
これで前準備は完了です。アカウントのOwner Name、プロジェクトIDをあとで利用するのでそれぞれ控えておきましょう。
APIトークンを発行する
以後APIにてApp Centerのリソースにアクセスするため、ログインしたユーザのAPIトークンを発行しておきましょう。APIトークンはユーザのAccount Settingsのメニューの中で発行できます。
2. apkファイル・ipaファイルを作成する
Visual Studio App Center上でもアプリのビルドは実施できますが、ここでは別環境にてビルドしたipaファイル・apkファイルを配布するというシナリオを説明します。
それぞれ個別に作成するのが面倒だったので、Flutterのプロジェクトを作成したときに最初に作られるサンプルアプリをベースにipaファイルとapkファイルを作成しておきます。こういうときはクロスプラットフォームは便利ですね。
apkの署名はデバッグプロファイルで、ipaは後でインストールする端末の情報を含んだprovisioning profileファイルで署名したものを用意します。
Flutterを使ってapk・ipaファイルを作成する手順はこちらを参考にしてください。
3. Visual Studio App Centerへapk・ipaファイルをアップロードする
公式のドキュメントを参考にアップロードに必要な処理を確認します。
ドキュメントにはcurlコマンドを使った手順がのっているので、今回はこれを参考にシェルスクリプトを組んで行きます。
なおnpmコマンド経由でもできるようですが、Node環境の準備が面倒なので今回は使いません。
追記(2021/10/18)
以下のアップロード方法ではアップロードに失敗するようになりました。代替手段としてappcenter cliを使う方法を記事に起こしましたのでそちらを参照ください。
手順を見ればわかりますが、やることは単純でアップロード用のURLを発行して、その発行したURLを使ってファイルをアップロードするだけです。ただしファイルをアップロードするだけだと配信は完了しないため、追加のAPIリクエストを実行して配信のリクエストも自動化してしまいましょう。
なおVisual Studio App Centerで表示されるバージョンはアプリの組み込まれているバージョンで、プロジェクト上の通番はアップロードごとに自動的にRelease IDとしてインクリメントされます。
各Release IDに対してrelease_note
という説明欄を利用できるのですが、これはファイルアップロードのIFでは利用できません。
そのため、リリースとタグ名を紐付けるためにはアップロードが完了したあとに最新のRelease IDを取得し、そのReleaseに対してリリースのタグ名を紐付けるようなPATCHリクエストを投げるとよいでしょう。
最後に配信対象のグループを指定して配信APIを実行すれば配信が開始されます。
以上をまとめると、以下のようなAPIリクエストを順番に実行すればよいです。
- ファイルアップロード用のURLを発行する
- ファイルをアップロードする
- 最新のRelease ID(2で作成されたRelease)を取得する
- 3のRelease IDに対応するRelease Noteを更新する
- 3のRelease IDの配信を対象のグループに対して開始する
シェルスクリプトでまとめると以下のようになります。
#!/bin/sh set -x set -e # タグ名 RELEASE_TAG_NAME=${1} # APK ファイルパス ANDROID_APK_FILE_PATH=${2} # ipa ファイルパス IOS_IPA_FILE_PATH=${3} # Visual Studio App Centerのパラメータ AZURE_APPCENTER_API_TOKEN='xxxxxxxx' AZURE_APPCENTER_API_BASE_URL='https://api.appcenter.ms/v0.1/apps' AZURE_APPCENTER_OWNER_NAME='takamii228' AZURE_APPCENTER_ANDROID_PROJECT_NAME='AndroidSample' AZURE_APPCENTER_IOS_PROJECT_NAME='iOSSample' AZURE_APPCENTER_DISTRIBUTE_GROUP_ID='00000000-0000-0000-0000-000000000000' # AppCenterにアップロードする一連の処理をまとめた関数 # # $1: AppCenterのプロジェクト名 # $2: アップロードファイルパス # $3: タグ名 # function upload_file_to_appcenter () { UPLOAD_API_BASE_URL=${AZURE_APPCENTER_API_BASE_URL}'/'${AZURE_APPCENTER_OWNER_NAME}'/'${1} # アップロード用のURLを発行する APP_CENTER_UPLOAD_URL=`curl -v -X POST -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'X-API-Token: '${AZURE_APPCENTER_API_TOKEN} \ ${UPLOAD_API_BASE_URL}'/release_uploads' | jq -r .upload_url` # ファイルをアップロードする curl -v -F "ipa=@${2}" ${APP_CENTER_UPLOAD_URL} # アップロードしたアプリのリリースIDを取得する RELEASE_ID=`curl -v -X GET -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'X-API-Token: '${AZURE_APPCENTER_API_TOKEN} \ ${UPLOAD_API_BASE_URL}'/releases' | jq 'max_by(.id) | .id'` # アップロードの中でRelease Noteは更新できないため、作成されたリリースIDに対してPATCHリクエストを投げる curl -v -X PATCH -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'X-API-Token: '${AZURE_APPCENTER_API_TOKEN} \ -d '{ "release_notes": "'"${3}"'" }' \ ${UPLOAD_API_BASE_URL}'/releases/'${RELEASE_ID} # アップロードしたアプリをCollaboratorsに配信する curl -v -X POST -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'X-API-Token: '${AZURE_APPCENTER_API_TOKEN} \ -d '{ "id": "'"${AZURE_APPCENTER_DISTRIBUTE_GROUP_ID}"'", "mandatory_update": false, "notify_testers": true }' \ ${UPLOAD_API_BASE_URL}'/releases/'${RELEASE_ID}'/groups' echo "App file ${2} was released in Visual Studio App Center in Project: ${1} for TAG: ${3}, Release ID: ${RELEASE_ID}" } # apkファイルをアップロードする upload_file_to_appcenter ${AZURE_APPCENTER_ANDROID_PROJECT_NAME} ${ANDROID_APK_FILE_PATH} ${RELEASE_TAG_NAME} # ipaファイルをアップロードする upload_file_to_appcenter ${AZURE_APPCENTER_IOS_PROJECT_NAME} ${IOS_IPA_FILE_PATH} ${RELEASE_TAG_NAME}
上記のシェルスクリプトをtag push契機で実行するようにCDパイプラインを組むとアップロードと配信を自動で実行できるようになります。
$ ./upload-app-to-appcenter.sh 1.0.0-dev app-release.apk Runner.ipa
4. Visual Studio App Centerからアプリをダウンロードする
配信が開始されると対象者にメール通知が飛んできます。 インストールしたい端末にてそのURLを開き、ログインをするとダウロードリンクを取得することができます。
ダウンロード画面のブラウザでの表示はデフォルトではビルド番号・バージョン番号順になっているため、開発中は固定にしている場合はファイルの識別が難しいです。なので3で説明したとおり、release_note
にタグ名を書いておくと識別しやすいでしょう。
Android上でapkファイルをインストールする
Androidの場合はそのままapkファイルをダウンロードすることでアプリを端末にインストールすることができます。
iOS上でipaファイルをインストールする
iOSの場合はprovisioning profileやiPhoneのApple IDのアカウントの設定にもよりますが、appcenterの構成プロファイルのインストールを端末上で求められるケースがあるようです。
まとめ
以上がVisual Studio App Center経由でapk/ipaファイルを配信する手順になります。
ipaファイル・apkファイルを作成するCDパイプラインとシームレスに繋げば、tag push契機でテスト用アプリを配信することができるようになりますね。
以前紹介したFlutterのCDパイプラインと合わせてみると以下のようなイメージになります。便利〜!
内部で手軽にアプリを配信できるような仕組みを用意しておくことで、モバイルアプリでもサーバサイドと同等レベルのよりEnd-to-Endの継続的デリバリーが実現でき、QAやUI確認等の開発のフィードバックライフサイクルをより短縮することができるでしょう。
余談
AppCenter便利やわ〜と思ってた矢先に、配信済みの資材が一斉に消えるという場面に遭遇しました。
え、やっぱ課金必要だったの!?とか焦ってたら公式から障害の連絡が出てました。
[Status] Investigating: App Center is experiencing issues in the Build service with GitHub repositories. We apologize for the inconvenience while we work to resolve this issue. https://t.co/eofRpcv64K
— VSAppCenter Status (@AppCenterStatus) 2020年4月23日
何があったかは書いてないですが、Distributeの部分に障害があったとのこと。焦りました。
App Center Status - Distribute is Experiencing Issues
サービスのステータスはこちらで確認できるようです。「あれ?」と思ったときはまずこちらを見てみるようにしましょう。