Flutter moduleのAARをGitHubレポジトリにデプロイする

こんにちは、Mobility TechnologiesでFlutterエンジニアとして働いているTomiと申します。

以前ブログで書いた「Flutter moduleのAARをMaven + GitHub ActionsでGitHub Packagesにデプロイする」ではFlutter v3.3.0より前のバージョンで動作する方法を紹介しました。

今回はFlutter v3.3.0以降でも動作するFlutter moduleのAARをGitHubのレポジトリにデプロイする方法を解説します。

※ 今回の検証はFlutter v3.3.5で行なっています。

単語定義

  • 保管用GitHubレポジトリ
    • AARを保管しておく場所
  • FlutterのGitHubレポジトリ

事前準備

流れ

  1. AARを生成する
  2. AARをpushする
  3. GitHub Actionsを使って自動化する
  4. Android側から保管用GitHubレポジトリを参照して依存性を取得する

1. AARを生成する

Flutter moduleをAndroid側に組み込むためにAARを生成します。

flutter build aar

上のコマンドで実行すると/build/host/outputs/repoに下記が生成されます。

.
└── com
    └── example
        └── moduleproject
            ├── flutter
            │   ├── 1.0
            │   │   ├── flutter-1.0-debug.aar
            │   │   ├── flutter-1.0-debug.aar.md5
            │   │   ├── flutter-1.0-debug.aar.sha1
            │   │   ├── flutter-1.0-debug.aar.sha256
            │   │   ├── flutter-1.0-debug.aar.sha512
            │   │   ├── flutter-1.0-profile.aar
            │   │   ├── flutter-1.0-profile.aar.md5
            │   │   ├── flutter-1.0-profile.aar.sha1
            │   │   ├── flutter-1.0-profile.aar.sha256
            │   │   ├── flutter-1.0-profile.aar.sha512
            │   │   ├── flutter-1.0-release.aar
            │   │   ├── flutter-1.0-release.aar.md5
            │   │   ├── flutter-1.0-release.aar.sha1
            │   │   ├── flutter-1.0-release.aar.sha256
            │   │   ├── flutter-1.0-release.aar.sha512
            │   │   ├── flutter-1.0.module
            │   │   ├── flutter-1.0.module.md5
            │   │   ├── flutter-1.0.module.sha1
            │   │   ├── flutter-1.0.module.sha256
            │   │   ├── flutter-1.0.module.sha512
            │   │   ├── flutter-1.0.pom
            │   │   ├── flutter-1.0.pom.md5
            │   │   ├── flutter-1.0.pom.sha1
            │   │   ├── flutter-1.0.pom.sha256
            │   │   └── flutter-1.0.pom.sha512
            │   ├── maven-metadata.xml
            │   ├── maven-metadata.xml.md5
            │   ├── maven-metadata.xml.sha1
            │   ├── maven-metadata.xml.sha256
            │   └── maven-metadata.xml.sha512
            ├── flutter_debug
            │   ├── 1.0
            │   │   ├── flutter_debug-1.0.aar
            │   │   ├── flutter_debug-1.0.aar.md5
            │   │   ├── flutter_debug-1.0.aar.sha1
            │   │   ├── flutter_debug-1.0.aar.sha256
            │   │   ├── flutter_debug-1.0.aar.sha512
            │   │   ├── flutter_debug-1.0.module
            │   │   ├── flutter_debug-1.0.module.md5
            │   │   ├── flutter_debug-1.0.module.sha1
            │   │   ├── flutter_debug-1.0.module.sha256
            │   │   ├── flutter_debug-1.0.module.sha512
            │   │   ├── flutter_debug-1.0.pom
            │   │   ├── flutter_debug-1.0.pom.md5
            │   │   ├── flutter_debug-1.0.pom.sha1
            │   │   ├── flutter_debug-1.0.pom.sha256
            │   │   └── flutter_debug-1.0.pom.sha512
            │   ├── maven-metadata.xml
            │   ├── maven-metadata.xml.md5
            │   ├── maven-metadata.xml.sha1
            │   ├── maven-metadata.xml.sha256
            │   └── maven-metadata.xml.sha512
            ├── flutter_profile
            │   ├── 1.0
            │   │   ├── flutter_profile-1.0.aar
            │   │   ├── flutter_profile-1.0.aar.md5
            │   │   ├── flutter_profile-1.0.aar.sha1
            │   │   ├── flutter_profile-1.0.aar.sha256
            │   │   ├── flutter_profile-1.0.aar.sha512
            │   │   ├── flutter_profile-1.0.module
            │   │   ├── flutter_profile-1.0.module.md5
            │   │   ├── flutter_profile-1.0.module.sha1
            │   │   ├── flutter_profile-1.0.module.sha256
            │   │   ├── flutter_profile-1.0.module.sha512
            │   │   ├── flutter_profile-1.0.pom
            │   │   ├── flutter_profile-1.0.pom.md5
            │   │   ├── flutter_profile-1.0.pom.sha1
            │   │   ├── flutter_profile-1.0.pom.sha256
            │   │   └── flutter_profile-1.0.pom.sha512
            │   ├── maven-metadata.xml
            │   ├── maven-metadata.xml.md5
            │   ├── maven-metadata.xml.sha1
            │   ├── maven-metadata.xml.sha256
            │   └── maven-metadata.xml.sha512
            └── flutter_release
                ├── 1.0
                │   ├── flutter_release-1.0.aar
                │   ├── flutter_release-1.0.aar.md5
                │   ├── flutter_release-1.0.aar.sha1
                │   ├── flutter_release-1.0.aar.sha256
                │   ├── flutter_release-1.0.aar.sha512
                │   ├── flutter_release-1.0.module
                │   ├── flutter_release-1.0.module.md5
                │   ├── flutter_release-1.0.module.sha1
                │   ├── flutter_release-1.0.module.sha256
                │   ├── flutter_release-1.0.module.sha512
                │   ├── flutter_release-1.0.pom
                │   ├── flutter_release-1.0.pom.md5
                │   ├── flutter_release-1.0.pom.sha1
                │   ├── flutter_release-1.0.pom.sha256
                │   └── flutter_release-1.0.pom.sha512
                ├── maven-metadata.xml
                ├── maven-metadata.xml.md5
                ├── maven-metadata.xml.sha1
                ├── maven-metadata.xml.sha256
                └── maven-metadata.xml.sha512

Androidプロジェクトのbuild.gradleに記載することで、生成されたAARを組み込むことができます。

repositories {
    maven {
        url '/Users/username/project_path/build/host/outputs/repo'
    }
    maven {
        url '$storageUrl/download.flutter.io'
    }
}

1人で開発するならローカルにあるAARファイルを使うこともありですが、チーム単位で開発する場合は、リモートにAARファイルをおくことが必須になると思います。

2. AARをpushする

それではAARファイルをリモート(保管用GitHubレポジトリ)にデプロイします。

// [1.] maven-repoディレクトリに既存にあるAARを持ってくる
$ git clone --depth 1 https://gitHub.com/{owner}/{repository name}.git maven-repo

// [2.] 既存にあるAAR(maven-repo)と新しく作ったAAR(build/host/outputs/repo/)を同期化する
$ rsync -a build/host/outputs/repo/ maven-repo/

// [3.] 同期化されたAARをpushする
$ cd maven-repo
$ git add .
$ git commit -m update
$ git pull --rebase
$ git push

https://github.com/{owner}/{repository name}.git : 保管用GitHubレポジトのURLで書き換えてください。

  1. maven-repoというディレクトリを作っておいて保管用GitHubレポジトリをcloneしておきます。
  2. cloneしたディレクトリ(pushしたことがあれば)に新しくビルドしたAARを書き換えします。
  3. 書き換えたAARを保管用Githubレポジトリにpushします。

上記のコマンドをスクリプト化します。(今回はFlutterの記事なのでDartスクリプトを書きました)

import 'dart:io';

const _githubUrl = 'https://github.com/{owner}/{repository name}.git';
const _directory = 'maven-repo';
const _repoPath = 'build/host/outputs/repo/';

void main(List<String> arguments) async {
  await _process('git clone --depth 1 $_githubUrl $_directory');
  await _process('rsync -a $_repoPath $_directory/');
  await _process('git add .', workingDirectory: _directory);

    // 変更がある場合
  if (await _hasChanged()) {
    await _process('git commit -m update', workingDirectory: _directory);
      await _process('git pull --rebase', workingDirectory: _directory);
      await _process('git push', workingDirectory: _directory);
  }
}

Future<bool> _hasChanged() async {
  final gitDiffProgress = await _process(
    'git diff --quiet HEAD',
    shouldIgnoreError: true,
    workingDirectory: _directory,
  );
  return gitDiffProgress.exitCode != 0;
}

Future<ProcessResult> _process(String command, {
  String? workingDirectory,
  bool shouldIgnoreError = false,
}) async {
  final splitCommands = command.split(' ');
  final executable = splitCommands.first;
  final arguments = splitCommands.length > 1 ? splitCommands.sublist(1) : <
      String>[];
  final process = await Process.run(
    executable,
    arguments,
    workingDirectory: workingDirectory,
  );

  if (!shouldIgnoreError && process.exitCode != 0) {
    throw Exception(process.stderr);
  }
  return process;
}
  • _githubUrl : こちらに保管用GitHubレポジトリのURLを入れてください。

これでGitHub Actionsで使用するものの準備が整いました。

3. GitHub Actionsを使って自動化する

name: deploy

on:
  push:
    branches:
      - main

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    permissions:
      contents: read
      packages: write

    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: flutter action
      uses: subosito/flutter-action@v2

    - name: Add path
      run: echo "$(pwd)/flutter/bin" >> $GITHUB_PATH

    - name: Get dependencies
      run: flutter pub get

    - name: build aar
      run: |
        flutter build aar
    
    - name: SBT credentials
      run: |
        mkdir -p ~/.sbt
        cat > ~/.sbt/.credentials <<EOL
        realm=
        host=raw.githubusercontent.com
        user=_
        password=${{ secrets.MACHINE_USER_PAT }}
        EOL

    - name: Setup git
      run: |
        git config --global user.name github-actions
        git config --global user.email github-actions@github.com
        git config --global url."https://${{ secrets.MACHINE_USER_PAT }}:x-oauth-basic@github.com/".insteadOf "https://github.com/"    
        
        - name: Deploy to maven-repo
          run: |
            dart run bin/deploy_script.dart

上記のyamlファイルを設定しておくとmainブランチをpushした際に、GitHub Actionsが動いてAARが生成され、保管用GitHubレポジトリにpushされます。

- name: Deploy
  run: |
    dart run bin/deploy_script.dart

こちらは「2. AARをpushする」で作成したスクリプトを実行するstepです。このスクリプトにはGitを使ってリモートにpushするコマンドが含まれているため、Gitの認証情報が必要です。

- name: SBT credentials
  run: |
    mkdir -p ~/.sbt
    cat > ~/.sbt/.credentials <<EOL
    realm=
    host=raw.githubusercontent.com
    user=_
    password=${{ secrets.MACHINE_USER_PAT }}
    EOL

- name: Setup git
  run: |
    git config --global user.name username
    git config --global user.email github-actions@github.com
    git config --global url."https://$
  • secrets.MACHINE_USER_PAT
    • 事前準備で生成したPersonal access tokenです。

上記のSBT credentialsとSetup GitステップをDeployステップより前に設定します。

4. Android側で保管用GitHubレポジトリを参照して依存性を取得する

保管用GitHubレポジトリから依存性を取得する前にMavenの設定が必要です。

String storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?: "https://storage.googleapis.com"
repositories {
    maven {
        name = "GitHubPackages"
        url = uri("https://raw.githubusercontent.com/{owner}/{保管用レポジトリ名}/{brunch名}/")
        credentials {
            username = {owner}
            password = {token}
        }
        authentication {
            basic(BasicAuthentication)
        }
    }
        maven {
        url "$storageUrl/download.flutter.io"
    }
}
  • https://raw.githubusercontent.com/{owner}/{保管用レポジトリ名}/{brunch}/
    • 保管用GitHubレポジトリのURLを入力してください。
  • {owner}
  • {token}
    • 事前に準備したPersonal access tokenを入力してください。

上記のコードをandroidプロジェクトのapp/build.gradleに記載しておくと、保管用GitHubレポジトリにあるAARを取得することができます。

dependencies {
  debugImplementation 'com.example.app:flutter_debug:1.0.0'
  profileImplementation 'com.example.app:flutter_profile:1.0.0'
  releaseImplementation 'com.example.app:flutter_release:1.0.0'
}

Android側で依存性を取得してFlutter moduleを使うことができるようになりました。

おわり

今回解説した方法はFlutterバージョンの影響を受けないメリットがありますが、バージョンが増えるほどデプロイ時間が伸びるデメリットもあります。

個人的にはGitHub Packagesにデプロイする方が良いと思います。 しかしながら、Add-to-appの機能に関しては、Flutter側の優先順位が高くなさそうなので、引き続きFlutterのバージョンごとで問題が発生するかもしれません。 その場合は、今回紹介した方法を選択するのも良いかと思います!

ここまで読んでいただき、ありがとうございました!