【Flutter】Flutterを使った開発でGitHub Actionsを活用する

CI/CDといえば CircleCI や Bitrise などのサービスが有名ですが、GitHub Actionsを使っているプロジェクトも増えてきています。

今回はFlutterを使った開発でGitHub Actionsを活用する方法を紹介します。

PRを作成、アップデートする度にAnalyzeを走らせる

types: [opened, synchronize] とPRの作成時、修正時に実行されるようにしています。

name: Flutter_Analyzer
on:
pull_request:
types: [opened, synchronize]
jobs:
flutter_analyze:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 1
- uses: subosito/flutter-action@v1
with:
channel: 'stable'
- run: flutter pub get
- run: flutter analyze

ちなみに私が普段使っている analysis_options.yaml は次のとおりです。freezedを使っているので関連するファイルの除外設定をしています。

include: package:flutter_lints/flutter.yaml
analyzer:
exclude:
- "**/*.g.dart" # for freezed
- "**/*.freezed.dart" # for freezed
errors:
missing_required_param: warning
missing_return: warning
invalid_annotation_target: ignore # for freezed
linter:
rules:
- non_constant_identifier_names

マージされたときにビルドして Firebase App Distribution にアップロードする

PRがmainブランチにマージされた時を想定していますが 、もちろんyamlファイルを書き換えることでdevelopブランチなど特定のブランチにマージされてときにも対応可能です。

iOS

p12ファイル、プロビジョニングファイルをBASE64化してGitHubActionsのSECRETに設定します。パスワードなども合わせてSECRETにします。

BASE64化する際はmacOSであれば次のように実行することでBASE64化してクリップボードにコピーされるので便利です。

AdHoc.p12 | base64 | pbcopy
name: auto build & deploy for iOS
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: macos-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Import Provisioning Profile
run: |
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
echo '${{ secrets.PROVISIONING_PROFILE }}' | openssl base64 -d -out ~/Library/MobileDevice/Provisioning\ Profiles/flutter_app_store.mobileprovision
- name: Import Code-Signing Certificates
uses: Apple-Actions/import-codesign-certs@v1
with:
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
- name: install flutter
uses: subosito/flutter-action@v1
with:
channel: 'stable'
- name: flutter dependencies install
run: flutter pub get
- name: build ipa
run: flutter build ios --release --no-codesign
- name: iOS Build Action
uses: yukiarrr/ios-build-action@v1.4.0
with:
project-path: ios/Runner.xcodeproj
export-method: ad-hoc
p12-base64: ${{ secrets.CERTIFICATES_P12 }}
mobileprovision-base64: ${{ secrets.PROVISIONING_PROFILE }}
code-signing-identity: Apple Distribution
team-id: ${{ secrets.TEAM_ID }}
workspace-path: ios/Runner.xcworkspace
certificate-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
output-path: app-release.ipa
- name: Upload artifact
uses: actions/upload-artifact@v1.0.0
with:
name: ios
path: app-release.ipa
deploy:
needs: [build]
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Download artifact
uses: actions/download-artifact@v1.0.0
with:
name: ios
- name: Firebase App Distribution
uses: wzieba/Firebase-Distribution-Github-Action@v1.3.2
with:
appId: ${{secrets.FIREBASE_APP_ID_IOS}}
token: ${{secrets.FIREBASE_TOKEN}}
groups: testers
file: ios/app-release.ipa
# Delete it because it is unnecessary after uploading.
- name: Delete artifact
uses: geekyeggo/delete-artifact@v1
with:
name: ios

Android

AndroidはKEYSTOREをBASE64化し、パスワード、エイリアス名をSECRETに設定します。

name: auto build & deploy for Andorid
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: install java 8.x
uses: actions/setup-java@v1
with:
java-version: '8.x'
- name: setup cache
uses: actions/cache@v1
with:
path: /Users/runner/hostedtoolcache/flutter
key: ${{ runner.OS }}-flutter-install-cache
- name: install flutter
uses: subosito/flutter-action@v1
with:
channel: 'stable'
- name: flutter dependencies install
run: flutter pub get
- name: build apk
run: |
echo '${{ secrets.KEYSTORE_BASE64 }}' | base64 -d > android/release.keystore
export KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}'
export KEY_ALIAS='${{ secrets.KEY_ALIAS }}'
export KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}'
flutter build apk --release
- name: Upload artifact
uses: actions/upload-artifact@v1.0.0
with:
name: android
path: build/app/outputs/flutter-apk/app-release.apk
deploy:
needs: [build]
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Download artifact
uses: actions/download-artifact@v1.0.0
with:
name: android
- name: upload artifact to Firebase App Distribution
uses: wzieba/Firebase-Distribution-Github-Action@v1.3.2
with:
appId: ${{secrets.FIREBASE_APP_ID_ANDROID}}
token: ${{secrets.FIREBASE_TOKEN}}
groups: testers
file: android/app-release.apk
# Delete it because it is unnecessary after uploading.
- name: Delete artifact
uses: geekyeggo/delete-artifact@v1
with:
name: android