Flutter for WebをBitriseで自動デプロイしてみた

はじめに

この記事はFlutter Advent Calendar 2019 (#2) 20日目の記事です。 その1 も合わせて今年もFlutterの盛り上がりを感じますね!

やってみること

昨年のアドベンドカレンダーでは、ビルドスクリプトとかCI/CDについて書いてました。 CodeMagic が登場し、かなり慌てたことを覚えています。

blog.dr1009.com

1年経ったのでなにかしら新しいことに挑戦したい……不安定なものを触りたい……破壊的な変更に巻き込まれたい……でも調べるのが大変すぎるのはつらい……ということで、Flutter for WebのCI/CDに挑戦したいと思います。 よろしくお願いします。

開発環境

Flutterは先日リリースされた 1.12.13 を利用します。 Webもベータに格上げされたので、ちょうどいい感じです。

~/workspace » flutter --version
Flutter 1.12.13+hotfix.5 • channel stable •
https://github.com/flutter/flutter.git
Framework • revision 27321ebbad (7 days ago) • 2019-12-10 18:15:01 -0800
Engine • revision 2994f7e1e6
Tools • Dart 2.7.0

Flutter for Webの準備

まずはプロジェクトを作成していきます。 Web版のプロジェクトを作る場合は、まず下記の公式チュートリアルを確認するのが一番早く、確実です。

flutter.dev

2019年12月18日現在(このブログを書いている日です)、まだstable版では使えないのでbetaチャンネルに変更します。 stable版に格上げされたら、下のコードは不要になります。 enable-web は残るような気もするけど。

$ flutter channel beta
$ flutter upgrade
$ flutter config --enable-web

完了したら flutter devices を呼び出してみます。 筆者の環境では、ドキュメント通りChromeが表示されました。

~/workspace »  flutter devices
2 connected devices:

Chrome     • chrome     • web-javascript • Google Chrome 79.0.3945.79
Web Server • web-server • web-javascript • Flutter Tools

続いて、Githubリポジトリを作成します。 今回のプロジェクト名は flutter_web_deploy としました。

github.com

すでに flutter run -d chrome するとChromeで動作を確認できます。 また flutter build web することで /build/web/ ディレクトリに index.html などが作成され、公開する準備が整っています

ここで終わってしまうとあまりにもあっさりしてるので、次はBitriseと組み合わせてみましょう。

Bitrise上でFlutter for Webをビルドする

お馴染み、BitriseにログインしてGithubリポジトリと連携していきます。

www.bitrise.io

f:id:D_R_1009:20191217235056p:plain

そのあとは先ほどのステップをそのままBitrise上で再現してみると。

f:id:D_R_1009:20191218011347p:plain
ビルド成功

見事、Bitrise上でFlutter for Webのビルドを実現できました。 プロジェクトをPublicにしてあるので、興味のある方は見てみてください。

app.bitrise.io

bitrise.yml は以下の通りです。

---
format_version: '8'
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
project_type: flutter
trigger_map: []
workflows:
  deploy_web:
    steps:
    - activate-ssh-key@4.0.5:
        run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
    - git-clone@4.0.17: {}
    - flutter-installer@0.11.0:
        inputs:
        - version: beta
        - is_update: 'false'
    - script@1.1.5:
        title: flutter config
        inputs:
        - content: |-
            #!/usr/bin/env bash
            # fail if any commands fails
            set -e
            # debug log
            set -x

            flutter config --enable-web
    - script@1.1.5:
        title: Build flutter for web
        inputs:
        - content: |-
            #!/usr/bin/env bash
            # fail if any commands fails
            set -e
            # debug log
            set -x

            flutter build web
    - create-zip@0.9.0:
        inputs:
        - source_path: build/web
        - destination: "$BITRISE_DEPLOY_DIR"
    - deploy-to-bitrise-io@1.9.4: {}
app:
  envs:
  - opts:
      is_expand: false
    BITRISE_FLUTTER_PROJECT_LOCATION: "."
  - opts:
      is_expand: false
    BITRISE_PROJECT_PATH: ios/Runner.xcworkspace
  - opts:
      is_expand: false
    BITRISE_SCHEME: Runner
  - opts:
      is_expand: false
    BITRISE_EXPORT_METHOD: app-store

これでGithubのmasterブランチへのプッシュや、タグの作成に応じてFlutter for Webのビルドができるようになりました! Android/iOS版のCI/CDと組み合わせて実行できます。(ただ、実行時間的に課金プランになりますが)

12月25日にmonoさんが「FlutterでモバイルとWeb両対応のアプリを作るノウハウ」を書いてくださる予定です。 ソースの共通化をしたら、CI/CDも共通化するニーズが高まる気がするので、今後はこの手の構成が増えるのではないかなーと思っています。

デプロイも自動化したい

おわろうかと思ったのですが続きます。

上記のステップでは「zipファイルをダウンロードし、温かみのある手動アップロード」作業が必要になっています。 令和元年もそろそろ終わってしまう昨今、もう一歩自動化を進めたいところですね?

ということで、Firebase Hostingを利用した自動化に進みましょう。

firebase.google.com

Firebase Hostingは、ご存知Firebaseが提供するWebホストプロバイダーです。


Introducing Firebase Hosting

ドキュメントはこちら。

firebase.google.com

今回はCI/CD用の設定をする必要があるので、最初に手元の環境でトークンを取得しておきます。 手順は次のドキュメントを確認してください。

firebase.google.com

こちらでゲットしたトークンを、BitriseのSercretsに保存します。 (筆者は FIREBASE_TOKEN として追加しました。後ほど変数名として利用します。)

続いて、ローカルのプロジェクトでFirebase Hostingの設定を行います。 以下、コマンド処理のイメージです。

$ cd workspace/firebase_web_deploy/
$ firebase login
$ firebase init

上記のステップを踏むと firebase.json ファイルと public ディレクトリが、 firebase_web_deploy の下に生成されます。

public ディレクトリに追加したファイルが firebase deploy することでアップロードされる、という理解でほぼ大丈夫です。もしもいろいろと設定したい場合には、コマンドの途中で変更したりjsonファイルをいじってみてください。

さて、Firebaseのプロジェクトを新規作成か既存への追加を行ったことかと思います。 このときに指定した Project ID を、トークンと同じようにBItriseの環境変数に定義しましょう。 (筆者は FIREBSSE_PROJECT_ID として定義しました。)

あとは public ディレクトリの中身をFlutter for Webの成果物で置き換え、アップロードするだけです。 先ほどのzip処理の次に、簡単なスクリプトを追加します。

#!/bin/bash
curl -sL firebase.tools | bash

cp -rf build/web/* public/

firebase deploy --project ${FIREBASE_PROJECT_ID} --token ${FIREBASE_TOKEN}

ステップをワクワクしながら走らせ、完了後にHosting先を見てみると……お馴染みのアプリが動いています!

flutter-web-deploy-demo.firebaseapp.com

f:id:D_R_1009:20191218025138p:plain
デプロイ完了!

おめでとうございます!アップロード処理まで自動化できました。 Webにはストア審査がないので、これで1日に二桁回数のリリースもできちゃいますね!

ステップを一つ追加しただけですが、定義したymlファイルは下のようになります。

workflows:
  deploy_web_firebase:
    steps:
    - activate-ssh-key@4.0.5:
        run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
    - git-clone@4.0.17: {}
    - flutter-installer@0.11.0:
        inputs:
        - version: beta
        - is_update: 'false'
    - script@1.1.5:
        title: flutter config
        inputs:
        - content: |-
            #!/usr/bin/env bash
            # fail if any commands fails
            set -e
            # debug log
            set -x

            flutter config --enable-web
    - script@1.1.5:
        title: Build flutter for web
        inputs:
        - content: |-
            #!/usr/bin/env bash
            # fail if any commands fails
            set -e
            # debug log
            set -x

            flutter build web
    - create-zip@0.9.0:
        inputs:
        - source_path: build/web
        - destination: "$BITRISE_DEPLOY_DIR"
    - deploy-to-bitrise-io@1.9.4: {}
    - script@1.1.5:
        inputs:
        - content: |-
            #!/usr/bin/env bash
            # fail if any commands fails
            set -e
            # debug log
            set -x

            curl -sL firebase.tools | bash

            cp -rf build/web/* public/

            firebase deploy --project ${FIREBASE_PROJECT_ID} --token ${FIREBASE_TOKEN}
        title: Firebase Hosting

Firebase Hostingをする場合でも、後ほどなんらかの事情でWebアプリのロールバックする可能性もあるため、zipファイルをダウンロード可能な状態にして保存しておくのが良さそうな気もします。 この辺りは、Webアプリエンジニアの方の知見をいただきたいところですね。

おまけ

Github Actionsも試してみたところ、さっくりデプロイできました。

f:id:D_R_1009:20191219001846p:plain

Create main.yml · koji-1009/flutter_web_deploy@d0dfd12 · GitHub

利用したワークフローは下の通りです。 これで意図通りに動くのがすごい。

name: CI

on: 
  push:
    branches: 
      - master

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1
    - name: Install flutter (beta channel)
      uses: subosito/flutter-action@v1
      with:
        channel: 'beta'
    - name: Enable Flutter for Web
      run: flutter config --enable-web
    - name: Build Flutter for Web
      run: flutter build web
    - name: copy resource
      run: cp -rf build/web/* public/
    - name: Firebase GitHub Action
      uses: pizzafox/firebase-action@1.0.7
      env:
        PROJECT_ID: "flutter-web-deploy-demo"
        FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
      with:
        args: deploy

FlutterとFirebase CLIのActionsがあるので、そちらを適宜利用しています。 Flutterのビルドコマンドが整理されているため、全体的にすっきりしてますね。

コードは flutter_web_deploy/main.yml at master · koji-1009/flutter_web_deploy · GitHub にあるので、よければご確認ください。 実行速度もBitriseとほぼ変わりません。WebだけならGithub Actionsだけで対応できちゃいそうです。

終わりに

BitriseとGithub Actionsを利用して、Flutter for WebのFirebase Hostingを利用したデプロイを試してみました。 ささっと環境構築して、ささっと配信環境を作って、ささっとアプリ開発していけるようになり嬉しい限りです。

ぱっと考えてみるとCircleCIでも同じことができるので、手の空いたときにymlファイルを書いてみたいなと思います。 2年連続でCIについて書いたから、来年はCircleCIでなにか書けるといい気もする……。

それでは、Flutterを遊び倒していきましょう! 明日は akio_r さんの記事です。お楽しみに〜。