はじめに
前回はiOSをビルドできるようにスクリプトを組んでいましたが、今回はDevOpsパイプラインでAPKをビルドできるようにしてみました。
新卒が入ってきて自分も、先輩エンジニアになりましたが、まだ実感がないです。
中高時代の後輩とはまた違うので、距離感が難しいですね。中高時代の後輩にはお母さんみたいと言われていました。
なんでも話せていたのか、過干渉だったのか?
過干渉されるのは、私は大嫌いなので、人にやっていたら最悪ですが、多分よくクッキーを作っていたからかな?と推測してます。
自分が後輩なら「ス○ラおばさん」とあだ名つけますね。(最低)
本題に行きます。
このブログでわかること
- Flutterのバージョン管理にfvmを利用し、安定したFlutter SDK(3.24.3)を使ってAPKをビルド。
- Android SDKやJDK17をCI環境上でインストールし、ビルドに必要な環境を自動構築。
- keystoreやFirebaseの設定ファイルをAzure DevOpsのSecure Files機能から安全にダウンロードし、ビルドに利用
- Flutterのビルド時にObfuscate(難読化)やsplit-per-abi(ABI毎にAPK分割)を行い、リリース用の最適化を実施。
上記の内容を含んでいます。
必要な前提と準備
- Azure DevOpsの変数グループに、ビルドで使うAPIキーやドメインなどの環境変数を登録しておく必要があります。
Secure Filesに以下のファイルを登録しておく必要がある。
- key.properties (署名用設定ファイル)
- key.jks(Android署名用キーストア)
- stg_firebase_options.dart、(Firebase設定ファイル)※
- google-services.json(Firebase Android設定ファイル)※
※部分は開発で使用しているものに応じて変更してください。
ビルドの流れ
- Flutter SDKはリポジトリからクローンして直接インストールしますが、fvmを使いFlutterバージョンを固定・切り替えしています。
- Android SDKはコマンドラインツールから必要なビルドツールやプラットフォームをインストールしています。
Flutterのビルド時に--obfuscateや--split-debug-infoで難読化とデバッグ情報の分割を行い、リリースビルドとして最適化をしています。
セキュリティ・機密情報について
- key.jksやkey.propertiesはアプリの署名に使う重要情報なので、絶対にリポジトリに直接含めず、Azure DevOpsのSecure Filesからのみ取得しています。
Firebase設定ファイルやAPIキーも機密情報として扱い、直接公開しないようにしています。
スクリプト例
trigger:
- main
- develop
pr:
branches:
include:
- '*'
variables:
# Azure DevOpsの変数グループから機密情報を読み込み
- group: APP_RELEASE_STG
pool:
vmImage: 'ubuntu-latest'
jobs:
- job: BuildAndScan
pool:
vmImage: 'ubuntu-latest'
steps:
# --- Secure Files のダウンロード ---
# Android署名に使うkey.propertiesをSecure Filesから取得
- task: DownloadSecureFile@1
name: downloadKeyProperties
inputs:
secureFile: 'key.properties'
# Android署名用のキーストア(key.jks)をSecure Filesから取得
- task: DownloadSecureFile@1
name: downloadKeystore
inputs:
secureFile: 'key.jks'
# Firebase用のDart設定ファイル(stg環境)をSecure Filesから取得
- task: DownloadSecureFile@1
name: downloadStgFirebaseOptions
inputs:
secureFile: 'stg_firebase_options.dart'
# Firebase Android設定ファイル(google-services.json)をSecure Filesから取得
- task: DownloadSecureFile@1
name: downloadGoogleServices
inputs:
secureFile: 'google-services.json'
# --- ファイルの配置 ---
# FirebaseのDartファイルをlib/srcに配置(stg)
- script: |
mkdir -p lib/src
cp $(downloadStgFirebaseOptions.secureFilePath) lib/src/stg_firebase_options.dart
displayName: 'Copy stg_firebase_options.dart to source folder'
# google-services.jsonをandroid/app/srcに配置
- script: |
mkdir -p android/app/src
cp $(downloadGoogleServices.secureFilePath) android/app/src/google-services.json
displayName: 'Copy google-services.json to android/app/src'
# key.propertiesとkey.jksをandroidディレクトリに配置
- script: |
mkdir -p android
cp $(downloadKeyProperties.secureFilePath) android/key.properties
cp $(downloadKeystore.secureFilePath) android/key.jks
displayName: 'Copy key.properties and keystore to android directory'
# --- Flutter SDKのインストール ---
- script: |
# Flutterリポジトリをクローン(stableブランチ)
git clone https://github.com/flutter/flutter.git -b stable
echo "##vso[task.setvariable variable=FLUTTER_HOME]$(pwd)/flutter"
# PATHにFlutterのbinディレクトリを追加
echo "##vso[task.prependpath]$(pwd)/flutter/bin"
displayName: 'Install Flutter and set PATH'
# fvm (Flutter Version Manager) のインストール
- script: |
flutter pub global activate fvm
echo '##vso[task.prependpath]$(HOME)/.pub-cache/bin'
displayName: 'Install fvm'
# fvmでFlutter 3.24.3をインストールし利用設定
- script: |
fvm install 3.24.3
fvm use 3.24.3
displayName: 'Setup Flutter via fvm'
# --- JDK 17のインストール ---
- script: |
sudo apt-get update
sudo apt-get install -y openjdk-17-jdk
echo "##vso[task.setvariable variable=JAVA_HOME]/usr/lib/jvm/java-17-openjdk-amd64"
displayName: 'Install JDK 17'
# --- Android SDKのインストールとセットアップ ---
- script: |
sudo apt-get update
sudo apt-get install -y wget unzip
export ANDROID_SDK_ROOT=$HOME/android-sdk
mkdir -p $ANDROID_SDK_ROOT/cmdline-tools
# Android SDKコマンドラインツールのダウンロードと展開
wget https://dl.google.com/android/repository/commandlinetools-linux-10406996_latest.zip -O cmdline-tools.zip
unzip -q cmdline-tools.zip -d $ANDROID_SDK_ROOT/cmdline-tools
mv $ANDROID_SDK_ROOT/cmdline-tools/cmdline-tools $ANDROID_SDK_ROOT/cmdline-tools/latest
rm cmdline-tools.zip
# PATHにSDKツールを追加
export PATH=$PATH:$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/platform-tools
# ライセンスに同意
yes | $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --sdk_root=$ANDROID_SDK_ROOT --licenses
# 必要なSDKパッケージのインストール
$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --sdk_root=$ANDROID_SDK_ROOT "platform-tools" "platforms;android-34" "build-tools;34.0.0"
displayName: 'Install Android SDK & Build Tools'
# --- Flutter APKのビルド ---
- script: |
export FLUTTER_HOME=$(FLUTTER_HOME)
export ANDROID_SDK_ROOT=$HOME/android-sdk
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export PATH=$FLUTTER_HOME/bin:$PATH:$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/platform-tools
mkdir -p android
cp $(downloadKeyProperties.secureFilePath) android/key.properties
cp $(downloadKeystore.secureFilePath) android/key.jks
# SDKライセンス再確認
yes | $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --sdk_root=$ANDROID_SDK_ROOT --licenses
# Flutter packagesの取得
fvm flutter pub get
# build_runnerによるコード生成(競合ファイルを削除)
fvm flutter pub run build_runner build --delete-conflicting-outputs
# Flutterのローカライズコード生成
fvm flutter gen-l10n
# APKビルド(flavor=stgで、難読化・ABI分割・デバッグ情報分割を実施)
fvm flutter build apk --split-per-abi --obfuscate --split-debug-info=symbols/ --flavor=stg \
--dart-define=CMS_BASE_DOMAIN=$(CMS_BASE_DOMAIN) \
--dart-define=CMS_API_KEY=$(CMS_API_KEY) \
--dart-define=MY_PORTAL_DOMAIN=$(MY_PORTAL_DOMAIN) \
--dart-define=MY_API_DOMAIN=$(MY_API_DOMAIN) \
--dart-define=MY_API_KEY=$(MY_API_KEY) \
--verbose
displayName: 'Build Flutter APK'
まとめ
Build Flutter APKの部分はだいぶ割愛していますが、記述方法は開発スタイル次第なので、こちらは適宜変更してください。
リリース目的というよりは、脆弱性診断を行うためのスクリプトを組んでいて、APKビルドは通過点であるため、現状はビルドまでの公開となっています。
次回は、脆弱性診断を行うために、MobSFをDockerコンテナで起動し、ビルドしたAPKを自動でアップロード・スキャンし、スキャン結果をJSON形式で取得してアーティファクトとして保存するまでの構成を書きます。
Github Actionを利用した記事はありましたが、DevOpsの記事が古かったので、誰かの参考になれば幸いです。